├── .chglog ├── CHANGELOG.tpl.md └── config.yml ├── .codecov.yml ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── config.yml │ └── enhancement_proposal.md ├── dependabot.yml ├── no-response.yml ├── pull_request_template.md └── workflows │ ├── README.md │ ├── changelog.yml │ ├── codeql.yml │ ├── docker-publish.yml │ ├── gh-pages.yaml │ ├── go.yml │ ├── image-reuse.yaml │ ├── pr-title-check.yml │ ├── release.yaml │ ├── stale-issues-pr.yml │ ├── testing-results.yml │ ├── testing.yaml │ └── waiting-issues.yml ├── .gitignore ├── .golangci.yml ├── .readthedocs.yaml ├── CHANGELOG-v0.1.0_v1.1.0.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── Dockerfile.dev ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── USERS.md ├── VERSION ├── analysis ├── analysis.go ├── analysis_test.go ├── controller.go ├── controller_test.go └── sync.go ├── cmd ├── kubectl-argo-rollouts │ └── main.go └── rollouts-controller │ └── main.go ├── controller ├── controller.go ├── controller_test.go ├── healthz.go ├── healthz_test.go ├── metrics │ ├── analysis.go │ ├── analysis_test.go │ ├── client.go │ ├── client_test.go │ ├── experiment_test.go │ ├── experiments.go │ ├── metrics.go │ ├── metrics_test.go │ ├── prommetrics.go │ ├── rollout_test.go │ └── rollouts.go └── profiling.go ├── docs ├── CONTRIBUTING.md ├── FAQ.md ├── analysis │ ├── cloudwatch.md │ ├── datadog.md │ ├── graphite.md │ ├── influxdb.md │ ├── job.md │ ├── kayenta.md │ ├── newrelic.md │ ├── plugins.md │ ├── prometheus.md │ ├── skywalking.md │ ├── wavefront.md │ └── web.md ├── architecture-assets │ ├── argo-rollout-architecture.png │ └── internal-architecture.png ├── architecture.md ├── assets │ ├── logo.png │ ├── versions.css │ └── versions.js ├── best-practices.md ├── concepts-assets │ ├── blue-green-deployments.png │ └── canary-deployments.png ├── concepts.md ├── contributing-assets │ └── plugin-loading.png ├── dashboard.md ├── dashboard │ ├── rollout-ui.png │ └── rollouts-list.png ├── features │ ├── analysis.md │ ├── anti-affinity │ │ ├── anti-affinity.md │ │ └── images │ │ │ ├── solution.png │ │ │ ├── step-0.png │ │ │ ├── step-1.png │ │ │ ├── step-2.png │ │ │ └── step-3.png │ ├── bluegreen.md │ ├── canary │ │ ├── index.md │ │ └── plugins.md │ ├── controller-metrics-assets │ │ ├── argo-rollouts-metrics.png │ │ └── prometheus-target.png │ ├── controller-metrics.md │ ├── ephemeral-metadata.md │ ├── experiment.md │ ├── helm.md │ ├── hpa-support.md │ ├── kubectl-plugin.md │ ├── kubectl-plugin │ │ └── kubectl-get-rollout.png │ ├── kustomize.md │ ├── kustomize │ │ ├── example │ │ │ ├── kustomization.yaml │ │ │ └── rollouts-demo.yaml │ │ ├── rollout-transform-kustomize-v5.yaml │ │ ├── rollout-transform.yaml │ │ └── rollout_cr_schema.json │ ├── notifications.md │ ├── restart.md │ ├── rollback.md │ ├── scaledown-aborted-rs.md │ ├── specification.md │ ├── traffic-management │ │ ├── alb.md │ │ ├── ambassador.md │ │ ├── apisix.md │ │ ├── google-cloud.md │ │ ├── index.md │ │ ├── istio-service-metrics.png │ │ ├── istio-workload-metrics.png │ │ ├── istio.md │ │ ├── kong.md │ │ ├── mixed.md │ │ ├── nginx.md │ │ ├── plugins.md │ │ ├── smi-diagram.jpg │ │ ├── smi.md │ │ └── traefik.md │ └── vpa-support.md ├── generated │ ├── kubectl-argo-rollouts │ │ ├── kubectl-argo-rollouts.md │ │ ├── kubectl-argo-rollouts_abort.md │ │ ├── kubectl-argo-rollouts_completion.md │ │ ├── kubectl-argo-rollouts_create.md │ │ ├── kubectl-argo-rollouts_create_analysisrun.md │ │ ├── kubectl-argo-rollouts_dashboard.md │ │ ├── kubectl-argo-rollouts_get.md │ │ ├── kubectl-argo-rollouts_get_experiment.md │ │ ├── kubectl-argo-rollouts_get_rollout.md │ │ ├── kubectl-argo-rollouts_lint.md │ │ ├── kubectl-argo-rollouts_list.md │ │ ├── kubectl-argo-rollouts_list_experiments.md │ │ ├── kubectl-argo-rollouts_list_rollouts.md │ │ ├── kubectl-argo-rollouts_notifications.md │ │ ├── kubectl-argo-rollouts_notifications_template.md │ │ ├── kubectl-argo-rollouts_notifications_template_get.md │ │ ├── kubectl-argo-rollouts_notifications_template_notify.md │ │ ├── kubectl-argo-rollouts_notifications_trigger.md │ │ ├── kubectl-argo-rollouts_notifications_trigger_get.md │ │ ├── kubectl-argo-rollouts_notifications_trigger_run.md │ │ ├── kubectl-argo-rollouts_pause.md │ │ ├── kubectl-argo-rollouts_promote.md │ │ ├── kubectl-argo-rollouts_restart.md │ │ ├── kubectl-argo-rollouts_retry.md │ │ ├── kubectl-argo-rollouts_retry_experiment.md │ │ ├── kubectl-argo-rollouts_retry_rollout.md │ │ ├── kubectl-argo-rollouts_set.md │ │ ├── kubectl-argo-rollouts_set_image.md │ │ ├── kubectl-argo-rollouts_status.md │ │ ├── kubectl-argo-rollouts_terminate.md │ │ ├── kubectl-argo-rollouts_terminate_analysisrun.md │ │ ├── kubectl-argo-rollouts_terminate_experiment.md │ │ ├── kubectl-argo-rollouts_undo.md │ │ └── kubectl-argo-rollouts_version.md │ └── notification-services │ │ ├── alertmanager.md │ │ ├── awssqs.md │ │ ├── email.md │ │ ├── github.md │ │ ├── googlechat.md │ │ ├── grafana.md │ │ ├── mattermost.md │ │ ├── newrelic.md │ │ ├── opsgenie.md │ │ ├── overview.md │ │ ├── pagerduty.md │ │ ├── pagerduty_v2.md │ │ ├── pushover.md │ │ ├── rocketchat.md │ │ ├── slack.md │ │ ├── teams.md │ │ ├── telegram.md │ │ ├── webex.md │ │ └── webhook.md ├── getting-started.md ├── getting-started │ ├── alb │ │ ├── alb-listener-rules.png │ │ ├── index.md │ │ ├── ingress.yaml │ │ ├── paused-rollout-alb.png │ │ ├── rollout-alb.png │ │ ├── rollout.yaml │ │ └── services.yaml │ ├── ambassador │ │ └── index.md │ ├── appmesh │ │ └── index.md │ ├── basic │ │ ├── aborted-rollout.png │ │ ├── healthy-rollout-rev4.png │ │ ├── initial-rollout.png │ │ ├── paused-rollout-rev3.png │ │ ├── paused-rollout.png │ │ ├── promoted-rollout.png │ │ ├── rollout.yaml │ │ └── service.yaml │ ├── istio │ │ ├── gateway.yaml │ │ ├── index.md │ │ ├── multipleVirtualsvc.yaml │ │ ├── paused-rollout-istio.png │ │ ├── rollout-istio.png │ │ ├── rollout.yaml │ │ ├── services.yaml │ │ └── virtualsvc.yaml │ ├── mixed │ │ ├── index.md │ │ ├── ingress.yaml │ │ ├── rollout.yaml │ │ └── services.yaml │ ├── nginx │ │ ├── index.md │ │ ├── ingress.yaml │ │ ├── paused-rollout-nginx.png │ │ ├── rollout-nginx.png │ │ ├── rollout.yaml │ │ └── services.yaml │ ├── setup │ │ ├── index.md │ │ ├── ingress-nginx-controller-metrics-scrape.yaml │ │ ├── rollout-istio.png │ │ ├── values-grafana-istio.yaml │ │ ├── values-grafana-nginx.yaml │ │ └── values-prometheus.yaml │ └── smi │ │ ├── index.md │ │ ├── ingress.yaml │ │ ├── rollout.yaml │ │ └── services.yaml ├── index.md ├── installation.md ├── migrating.md ├── plugins.md ├── proposals │ └── step-plugin.md ├── releasing.md ├── requirements.txt └── security │ ├── security.md │ └── signed-release-assets.md ├── examples ├── ambassador │ ├── echo-mapping.yaml │ ├── echo-service.yaml │ ├── resolver.yaml │ └── rollout.yaml ├── analysis-templates.yaml ├── apisix │ ├── rollout.yaml │ ├── route.yaml │ └── services.yaml ├── appmesh │ ├── canary-rollout.yaml │ └── canary-service.yaml ├── cluster-analysis-templates.yaml ├── dashboard.json ├── experiment-with-analysis.yaml ├── helm-blue-green │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── rollout.yaml │ │ └── services.yaml │ └── values.yaml ├── istio │ ├── istio-mirror.yaml │ └── istio-subset-split.yaml ├── notifications │ ├── configmap.yaml │ ├── kustomization.yaml │ └── rollout-canary.yaml ├── rollout-analysis-step.yaml ├── rollout-background-analysis.yaml ├── rollout-baseline-vs-canary.yaml ├── rollout-bluegreen.yaml ├── rollout-canary-preview.yaml ├── rollout-canary.yaml ├── rollout-experiment-step.yaml ├── rollout-rolling-update.yaml ├── rollout-secret.yaml ├── rollout-template-ref.yaml ├── traffic-management │ ├── job.yaml │ └── rollout-linkerd.yaml ├── traffic-routing-experiment-step │ ├── istio-host-split-experiment-step.yaml │ ├── rollout-alb-experiment-step.yaml │ └── rollout-smi-experiment-step.yaml └── workload-ref │ └── workload-ref.yaml ├── experiments ├── analysisrun_test.go ├── conditions.go ├── conditions_test.go ├── controller.go ├── controller_test.go ├── experiment.go ├── experiment_test.go ├── replicaset.go ├── replicaset_test.go └── service.go ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── build-release-plugins.sh ├── custom-boilerplate.go.txt ├── gen-crd-spec │ └── main.go ├── gen-docs │ └── main.go ├── installers │ ├── install-codegen-go-tools.sh │ ├── install-dev-tools.sh │ └── install-protoc.sh ├── swagger-codegen.sh ├── tools.go ├── trigger-release.sh ├── update-codegen.sh ├── update-k8s-dependencies.sh ├── update-manifests.sh ├── update-mocks.sh └── verify-codegen.sh ├── ingress ├── alb.go ├── alb_test.go ├── ingress.go └── ingress_test.go ├── manifests ├── README.md ├── base │ ├── argo-rollouts-aggregate-roles.yaml │ ├── argo-rollouts-config.yaml │ ├── argo-rollouts-deployment.yaml │ ├── argo-rollouts-metrics-service.yaml │ ├── argo-rollouts-notification-secret.yaml │ ├── argo-rollouts-sa.yaml │ └── kustomization.yaml ├── cluster-install │ ├── argo-rollouts-clusterrolebinding.yaml │ └── kustomization.yaml ├── crds │ ├── analysis-run-crd.yaml │ ├── analysis-template-crd.yaml │ ├── cluster-analysis-template-crd.yaml │ ├── experiment-crd.yaml │ ├── kustomization.yaml │ └── rollout-crd.yaml ├── dashboard-install.yaml ├── dashboard-install │ ├── dashboard-clusterrole.yaml │ ├── dashboard-clusterrolebinding.yaml │ ├── dashboard-deployment.yaml │ ├── dashboard-service.yaml │ ├── dashboard-serviceaccount.yaml │ └── kustomization.yaml ├── install.yaml ├── namespace-install.yaml ├── namespace-install │ ├── add-namespaced-flag.yaml │ ├── argo-rollouts-rolebinding.yaml │ ├── clusterrole-to-role.yaml │ └── kustomization.yaml ├── notifications-install.yaml ├── notifications │ ├── argo-rollouts-notification-configmap.yaml │ ├── kustomization.yaml │ ├── on-analysis-run-error.yaml │ ├── on-analysis-run-failed.yaml │ ├── on-analysis-run-running.yaml │ ├── on-rollout-aborted.yaml │ ├── on-rollout-completed.yaml │ ├── on-rollout-paused.yaml │ ├── on-rollout-step-completed.yaml │ ├── on-rollout-updated.yaml │ └── on-scaling-replica-set.yaml └── role │ ├── argo-rollouts-clusterrole.yaml │ └── kustomization.yaml ├── metric └── provider.go ├── metricproviders ├── cloudwatch │ ├── cloudwatch.go │ ├── cloudwatch_test.go │ └── mock_test.go ├── datadog │ ├── datadog.go │ ├── datadogV1_test.go │ ├── datadogV2_test.go │ ├── datadog_test.go │ ├── finders.go │ └── finders_test.go ├── graphite │ ├── api.go │ ├── api_test.go │ ├── graphite.go │ ├── graphite_test.go │ └── mock_test.go ├── influxdb │ ├── influxdb.go │ ├── influxdb_test.go │ └── mock_test.go ├── job │ ├── job.go │ └── job_test.go ├── kayenta │ ├── kayenta.go │ └── kayenta_test.go ├── metricproviders.go ├── mocks │ └── Provider.go ├── newrelic │ ├── mock_test.go │ ├── newrelic.go │ └── newrelic_test.go ├── plugin │ ├── client │ │ └── client.go │ ├── plugin.go │ └── rpc │ │ ├── rpc.go │ │ ├── rpc_test.go │ │ └── rpc_test_implementation.go ├── prometheus │ ├── headers_round_tripper.go │ ├── headers_round_tripper_test.go │ ├── mock_test.go │ ├── prometheus.go │ └── prometheus_test.go ├── skywalking │ ├── mock_test.go │ ├── skywalking.go │ └── skywalking_test.go ├── wavefront │ ├── mock_test.go │ ├── wavefront.go │ └── wavefront_test.go └── webmetric │ ├── webmetric.go │ └── webmetric_test.go ├── mkdocs.yml ├── pkg ├── apiclient │ └── rollout │ │ ├── forwarder_overwrite.go │ │ ├── rollout.pb.go │ │ ├── rollout.pb.gw.go │ │ ├── rollout.proto │ │ └── rollout.swagger.json ├── apis │ ├── api-rules │ │ └── violation_exceptions.list │ └── rollouts │ │ ├── register.go │ │ ├── v1alpha1 │ │ ├── analysis_types.go │ │ ├── doc.go │ │ ├── experiment_types.go │ │ ├── generated.pb.go │ │ ├── generated.proto │ │ ├── openapi_generated.go │ │ ├── register.go │ │ ├── types.go │ │ ├── types_test.go │ │ └── zz_generated.deepcopy.go │ │ └── validation │ │ ├── validation.go │ │ ├── validation_references.go │ │ ├── validation_references_test.go │ │ └── validation_test.go ├── client │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── rollouts │ │ │ └── v1alpha1 │ │ │ ├── analysisrun.go │ │ │ ├── analysistemplate.go │ │ │ ├── clusteranalysistemplate.go │ │ │ ├── doc.go │ │ │ ├── experiment.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_analysisrun.go │ │ │ ├── fake_analysistemplate.go │ │ │ ├── fake_clusteranalysistemplate.go │ │ │ ├── fake_experiment.go │ │ │ ├── fake_rollout.go │ │ │ └── fake_rollouts_client.go │ │ │ ├── generated_expansion.go │ │ │ ├── rollout.go │ │ │ └── rollouts_client.go │ ├── informers │ │ └── externalversions │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── internalinterfaces │ │ │ └── factory_interfaces.go │ │ │ └── rollouts │ │ │ ├── interface.go │ │ │ └── v1alpha1 │ │ │ ├── analysisrun.go │ │ │ ├── analysistemplate.go │ │ │ ├── clusteranalysistemplate.go │ │ │ ├── experiment.go │ │ │ ├── interface.go │ │ │ └── rollout.go │ └── listers │ │ └── rollouts │ │ └── v1alpha1 │ │ ├── analysisrun.go │ │ ├── analysistemplate.go │ │ ├── clusteranalysistemplate.go │ │ ├── expansion_generated.go │ │ ├── experiment.go │ │ └── rollout.go ├── kubectl-argo-rollouts │ ├── cmd │ │ ├── abort │ │ │ ├── abort.go │ │ │ └── abort_test.go │ │ ├── cmd.go │ │ ├── cmd_test.go │ │ ├── completion │ │ │ ├── completion.go │ │ │ └── completion_test.go │ │ ├── create │ │ │ ├── create.go │ │ │ ├── create_test.go │ │ │ └── testdata │ │ │ │ ├── analysis-template-no-name.yaml │ │ │ │ ├── analysis-template.json │ │ │ │ ├── analysis-template.yaml │ │ │ │ └── cluster-analysis-template.yaml │ │ ├── dashboard │ │ │ └── dashboard.go │ │ ├── get │ │ │ ├── get.go │ │ │ ├── get_experiment.go │ │ │ ├── get_rollout.go │ │ │ └── get_test.go │ │ ├── lint │ │ │ ├── lint.go │ │ │ ├── lint_test.go │ │ │ └── testdata │ │ │ │ ├── invalid-empty-rollout-vsvc.yml │ │ │ │ ├── invalid-ingress-smi-multi.yml │ │ │ │ ├── invalid-multiple-docs.yml │ │ │ │ ├── invalid-nginx-canary.yml │ │ │ │ ├── invalid-ping-pong.yml │ │ │ │ ├── invalid-service-labels.yml │ │ │ │ ├── invalid-unknown-field.yml │ │ │ │ ├── invalid.json │ │ │ │ ├── invalid.yml │ │ │ │ ├── valid-alb-canary.yml │ │ │ │ ├── valid-blue-green.yml │ │ │ │ ├── valid-ingress-smi-multi.yml │ │ │ │ ├── valid-ingress-smi.yml │ │ │ │ ├── valid-istio-v1alpha3.yml │ │ │ │ ├── valid-istio-v1beta1-mulitiple-virtualsvcs.yml │ │ │ │ ├── valid-istio-v1beta1.yml │ │ │ │ ├── valid-nginx-basic-canary.yml │ │ │ │ ├── valid-nginx-canary.yml │ │ │ │ ├── valid-nginx-smi-with-vsvc.yaml │ │ │ │ ├── valid-with-another-empty-object.yml │ │ │ │ ├── valid-workload-ref.yaml │ │ │ │ ├── valid.json │ │ │ │ └── valid.yml │ │ ├── list │ │ │ ├── list.go │ │ │ ├── list_experiments.go │ │ │ ├── list_rollouts.go │ │ │ ├── list_test.go │ │ │ └── rollloutinfo.go │ │ ├── pause │ │ │ ├── pause.go │ │ │ └── pause_test.go │ │ ├── promote │ │ │ ├── promote.go │ │ │ └── promote_test.go │ │ ├── restart │ │ │ ├── restart.go │ │ │ └── restart_test.go │ │ ├── retry │ │ │ ├── retry.go │ │ │ └── retry_test.go │ │ ├── set │ │ │ ├── set.go │ │ │ ├── set_image.go │ │ │ └── set_test.go │ │ ├── signals │ │ │ └── signal.go │ │ ├── status │ │ │ ├── status.go │ │ │ └── status_test.go │ │ ├── terminate │ │ │ ├── terminate.go │ │ │ └── terminate_test.go │ │ ├── undo │ │ │ ├── undo.go │ │ │ └── undo_test.go │ │ └── version │ │ │ ├── version.go │ │ │ └── version_test.go │ ├── info │ │ ├── analysisrun_info.go │ │ ├── experiment_info.go │ │ ├── info.go │ │ ├── info_test.go │ │ ├── pod_info.go │ │ ├── replicaset_info.go │ │ ├── rollout_info.go │ │ └── testdata │ │ │ ├── blue-green │ │ │ ├── active-pod1.yaml │ │ │ ├── active-pod2.yaml │ │ │ ├── active-pod3.yaml │ │ │ ├── active-rs.yaml │ │ │ ├── bluegreen-rollout.yaml │ │ │ ├── old-rs.yaml │ │ │ ├── preview-pod1.yaml │ │ │ ├── preview-pod2.yaml │ │ │ ├── preview-pod3.yaml │ │ │ └── preview-rs.yaml │ │ │ ├── canary │ │ │ ├── canary-pod.yaml │ │ │ ├── canary-ref-deploy.yaml │ │ │ ├── canary-rollout.yaml │ │ │ ├── canary-rollout2.yaml │ │ │ ├── canary-rollout3.yaml │ │ │ ├── canary-rollout4.yaml │ │ │ ├── canary-rollout5.yaml │ │ │ ├── canary-rollout6.yaml │ │ │ ├── canary-rs.yaml │ │ │ ├── old-rs.yaml │ │ │ ├── stable-pod1.yaml │ │ │ ├── stable-pod2.yaml │ │ │ ├── stable-pod3.yaml │ │ │ ├── stable-pod4.yaml │ │ │ ├── stable-pod5.yaml │ │ │ └── stable-rs.yaml │ │ │ ├── experiment-analysis │ │ │ ├── canary-analysis.yaml │ │ │ ├── canary-pod.yaml │ │ │ ├── canary-rs.yaml │ │ │ ├── experiment-rs1-pod.yaml │ │ │ ├── experiment-rs1.yaml │ │ │ ├── experiment-rs2-pod.yaml │ │ │ ├── experiment-rs2.yaml │ │ │ ├── experiment.yaml │ │ │ ├── rollout-experiment-analysis.yaml │ │ │ ├── stable-pod1.yaml │ │ │ ├── stable-pod2.yaml │ │ │ ├── stable-pod3.yaml │ │ │ └── stable-rs.yaml │ │ │ ├── experiment-step │ │ │ ├── canary-demo.yaml │ │ │ ├── canary-exp-analysis-job-pod.yaml │ │ │ ├── canary-exp-analysis-job.yaml │ │ │ ├── canary-exp-analysis.yaml │ │ │ ├── canary-exp-pod.yaml │ │ │ ├── canary-exp-rs.yaml │ │ │ ├── canary-exp.yaml │ │ │ ├── canary-rs.yaml │ │ │ ├── stable-pod1.yaml │ │ │ ├── stable-pod2.yaml │ │ │ ├── stable-pod3.yaml │ │ │ ├── stable-pod4.yaml │ │ │ ├── stable-pod5.yaml │ │ │ └── stable-rs.yaml │ │ │ ├── rollout-aborted │ │ │ ├── analysisrun.yaml │ │ │ ├── pod1.yaml │ │ │ ├── rollout.yaml │ │ │ ├── rs1.yaml │ │ │ └── rs2.yaml │ │ │ ├── rollout-invalid │ │ │ └── rollout-invalid.yaml │ │ │ └── testdata.go │ ├── options │ │ ├── fake │ │ │ └── fakeoptions.go │ │ ├── options.go │ │ └── options_test.go │ ├── util │ │ └── completion │ │ │ ├── completion.go │ │ │ └── completion_test.go │ └── viewcontroller │ │ ├── viewcontroller.go │ │ └── viewcontroller_test.go └── signals │ ├── signal.go │ ├── signal_posix.go │ └── signal_windows.go ├── rollout ├── analysis.go ├── analysis_test.go ├── bluegreen.go ├── bluegreen_test.go ├── canary.go ├── canary_test.go ├── context.go ├── controller.go ├── controller_test.go ├── ephemeralmetadata.go ├── ephemeralmetadata_test.go ├── experiment.go ├── experiment_test.go ├── mocks │ └── TrafficRoutingReconciler.go ├── pause.go ├── pause_test.go ├── replicaset.go ├── replicaset_test.go ├── restart.go ├── restart_test.go ├── scale_utils.go ├── scale_utils_test.go ├── service.go ├── service_test.go ├── stepplugin.go ├── stepplugin_test.go ├── steps │ └── plugin │ │ ├── client │ │ └── client.go │ │ ├── mocks │ │ ├── Resolver.go │ │ └── StepPlugin.go │ │ ├── plugin.go │ │ ├── plugin_test.go │ │ ├── resolver.go │ │ └── rpc │ │ ├── mocks │ │ └── StepPlugin.go │ │ ├── rpc.go │ │ ├── rpc_test.go │ │ └── rpc_test_implementation.go ├── sync.go ├── sync_test.go ├── templateref.go ├── templateref_test.go ├── trafficrouting.go ├── trafficrouting │ ├── alb │ │ ├── alb.go │ │ └── alb_test.go │ ├── ambassador │ │ ├── ambassador.go │ │ └── ambassador_test.go │ ├── apisix │ │ ├── apisix.go │ │ ├── apisix_test.go │ │ └── mocks │ │ │ └── apisix.go │ ├── appmesh │ │ ├── appmesh.go │ │ ├── appmesh_test.go │ │ └── resource_client.go │ ├── istio │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── istio.go │ │ ├── istio_test.go │ │ └── istio_types.go │ ├── nginx │ │ ├── nginx.go │ │ └── nginx_test.go │ ├── plugin │ │ ├── client │ │ │ └── client.go │ │ ├── plugin.go │ │ └── rpc │ │ │ ├── rpc.go │ │ │ ├── rpc_test.go │ │ │ └── rpc_test_implementation.go │ ├── service_helper.go │ ├── service_helper_test.go │ ├── smi │ │ ├── smi.go │ │ └── smi_test.go │ ├── traefik │ │ ├── mocks │ │ │ └── traefik.go │ │ ├── traefik.go │ │ └── traefik_test.go │ └── trafficroutingutil.go └── trafficrouting_test.go ├── server ├── server.go ├── server_static.go ├── server_static_test.go ├── static │ └── .gitkeep └── static_test │ ├── index.html │ └── test-dir │ └── test.css ├── service ├── service.go └── service_test.go ├── sonar-project.properties ├── test ├── cmd │ ├── metrics-plugin-sample │ │ ├── internal │ │ │ └── plugin │ │ │ │ ├── plugin.go │ │ │ │ └── plugin_test.go │ │ └── main.go │ ├── step-plugin-e2e │ │ ├── internal │ │ │ └── plugin │ │ │ │ └── plugin.go │ │ └── main.go │ ├── step-plugin-sample │ │ ├── internal │ │ │ └── plugin │ │ │ │ ├── plugin.go │ │ │ │ └── plugin_test.go │ │ ├── main.go │ │ └── main_test.go │ └── trafficrouter-plugin-sample │ │ ├── internal │ │ └── plugin │ │ │ └── plugin.go │ │ └── main.go ├── e2e │ ├── alb │ │ ├── rollout-alb-experiment-no-setweight.yaml │ │ ├── rollout-alb-experiment.yaml │ │ ├── rollout-alb-multi-ingress-experiment-no-setweight.yaml │ │ └── rollout-alb-multi-ingress-experiment.yaml │ ├── analysis_test.go │ ├── apisix │ │ ├── rollout-apisix-canary-set-header.yaml │ │ └── rollout-apisix-canary.yaml │ ├── apisix_test.go │ ├── appmesh │ │ └── appmesh-canary-rollout.yaml │ ├── appmesh_test.go │ ├── aws_test.go │ ├── bluegreen_test.go │ ├── canary_test.go │ ├── crds │ │ ├── README.md │ │ ├── apisix.yaml │ │ ├── istio.yaml │ │ └── split.yaml │ ├── expectedfailures │ │ ├── analysis-run-failfast.yaml │ │ ├── analysis-run-prometheus-bad-server.yaml │ │ ├── experiment-degrade.yaml │ │ ├── experiment-error-template-missing.yaml │ │ ├── experiment-failed-analysis.yaml │ │ ├── experiment-invalid.yaml │ │ ├── malformed-analysisrun.yaml │ │ ├── malformed-analysistemplate.yaml │ │ ├── malformed-clusteranalysistemplate.yaml │ │ ├── malformed-experiment.yaml │ │ ├── malformed-rollout-ephemeral.yaml │ │ └── malformed-rollout.yaml │ ├── experiment_test.go │ ├── functional │ │ ├── alb-bluegreen-rollout.yaml │ │ ├── alb-canary-multi-ingress-rollout.yaml │ │ ├── alb-canary-rollout.yaml │ │ ├── alb-pingpong-multi-ingress-rollout.yaml │ │ ├── alb-pingpong-rollout.yaml │ │ ├── albmesh-pingpong-stablecanary-rollout.yaml │ │ ├── analysis-run-job.yaml │ │ ├── analysistemplate-fail-multiple-job.yaml │ │ ├── analysistemplate-multiple-job.yaml │ │ ├── analysistemplate-sleep-job.yaml │ │ ├── analysistemplate-web-background-inconclusive.yaml │ │ ├── analysistemplate-web-background.yaml │ │ ├── canary-dynamic-stable-scale.yaml │ │ ├── canary-scaledowndelay.yaml │ │ ├── canary-scaledownonabort.yaml │ │ ├── canary-scaledownonabortnotrafficrouting.yaml │ │ ├── canary-unscaledownonabort.yaml │ │ ├── ephemeral-labels.yaml │ │ ├── experiment-basic.yaml │ │ ├── experiment-dry-run-analysis.yaml │ │ ├── experiment-measurement-retention-analysis.yaml │ │ ├── experiment-same-template.yaml │ │ ├── experiment-with-multiport-service.yaml │ │ ├── experiment-with-service-name.yaml │ │ ├── experiment-with-service.yaml │ │ ├── nginx-template.yaml │ │ ├── rollout-background-analysis-inconclusive.yaml │ │ ├── rollout-background-analysis.yaml │ │ ├── rollout-basic.yaml │ │ ├── rollout-bg-analysis-withArgs.yaml │ │ ├── rollout-bluegreen.yaml │ │ ├── rollout-canary-with-pause.yaml │ │ ├── rollout-degraded-inline-multiple-analysis.yaml │ │ ├── rollout-experiment-analysis.yaml │ │ ├── rollout-inline-analysis.yaml │ │ ├── rollout-inline-multiple-analysis.yaml │ │ ├── rollout-rollback-window.yaml │ │ ├── rollout-secret-withArgs.yaml │ │ └── rollout-secret.yaml │ ├── functional_test.go │ ├── header-routing │ │ ├── alb-header-route-multi-ingress.yaml │ │ ├── alb-header-route.yaml │ │ └── istio-hr-host.yaml │ ├── header_route_test.go │ ├── istio │ │ ├── istio-host-http-tls-split.yaml │ │ ├── istio-host-only-tls-split.yaml │ │ ├── istio-host-split-experiment-step.yaml │ │ ├── istio-host-split-ping-pong.yaml │ │ ├── istio-host-split-update-in-middle.yaml │ │ ├── istio-host-split.yaml │ │ ├── istio-rollout-abort-delete-all-canary-pods.yaml │ │ ├── istio-subset-split-experiment-step.yaml │ │ ├── istio-subset-split-in-stable-downscale-after-canary-abort.yaml │ │ ├── istio-subset-split-single-route.yaml │ │ ├── istio-subset-split.yaml │ │ └── load-test-job.yaml │ ├── istio_test.go │ ├── mirror-route │ │ └── istio-mirror-host.yaml │ ├── mirror_route_test.go │ ├── rollback_window_test.go │ ├── smi │ │ └── rollout-smi-experiment.yaml │ ├── smi_ingress │ │ └── rollout-smi-ingress-canary.yaml │ ├── smi_ingress_test.go │ ├── smi_test.go │ ├── step-plugin │ │ ├── argo-rollouts-config.yaml │ │ ├── disabled-rollout.yaml │ │ ├── invalid-rollout.yaml │ │ └── template-rollout.yaml │ └── step_plugin_test.go ├── fixtures │ ├── common.go │ ├── e2e_suite.go │ ├── given.go │ ├── then.go │ └── when.go ├── kustomize │ ├── rollout │ │ ├── expected.yaml │ │ ├── kustomization.yaml │ │ └── rollout.yaml │ └── test.sh └── util │ └── util.go ├── ui ├── .gitignore ├── .prettierrc ├── jest.config.js ├── package.json ├── src │ ├── app │ │ ├── App.scss │ │ ├── App.tsx │ │ ├── components │ │ │ ├── analysis-modal │ │ │ │ ├── analysis-modal.tsx │ │ │ │ ├── constants.ts │ │ │ │ ├── criteria-list │ │ │ │ │ ├── criteria-list.scss │ │ │ │ │ └── criteria-list.tsx │ │ │ │ ├── header │ │ │ │ │ ├── header.scss │ │ │ │ │ └── header.tsx │ │ │ │ ├── legend │ │ │ │ │ └── legend.tsx │ │ │ │ ├── metric-chart │ │ │ │ │ ├── metric-chart.scss │ │ │ │ │ └── metric-chart.tsx │ │ │ │ ├── metric-label │ │ │ │ │ ├── metric-label.scss │ │ │ │ │ └── metric-label.tsx │ │ │ │ ├── metric-table │ │ │ │ │ ├── metric-table.scss │ │ │ │ │ └── metric-table.tsx │ │ │ │ ├── panels │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── metric-panel.tsx │ │ │ │ │ ├── styles.scss │ │ │ │ │ └── summary-panel.tsx │ │ │ │ ├── query-box │ │ │ │ │ ├── query-box.scss │ │ │ │ │ └── query-box.tsx │ │ │ │ ├── status-indicator │ │ │ │ │ ├── status-indicator.scss │ │ │ │ │ └── status-indicator.tsx │ │ │ │ ├── styles.scss │ │ │ │ ├── theme │ │ │ │ │ └── theme.scss │ │ │ │ ├── transforms.test.ts │ │ │ │ ├── transforms.ts │ │ │ │ └── types.ts │ │ │ ├── confirm-button │ │ │ │ └── confirm-button.tsx │ │ │ ├── ellipsis-middle │ │ │ │ └── ellipsis-middle.tsx │ │ │ ├── header │ │ │ │ ├── header.scss │ │ │ │ └── header.tsx │ │ │ ├── info-item │ │ │ │ ├── info-item.scss │ │ │ │ └── info-item.tsx │ │ │ ├── modal │ │ │ │ ├── modal.scss │ │ │ │ └── modal.tsx │ │ │ ├── pods │ │ │ │ ├── pods.scss │ │ │ │ └── pods.tsx │ │ │ ├── rollout-actions │ │ │ │ └── rollout-actions.tsx │ │ │ ├── rollout-grid-widget │ │ │ │ ├── rollout-grid-widget.scss │ │ │ │ └── rollout-grid-widget.tsx │ │ │ ├── rollout │ │ │ │ ├── containers.tsx │ │ │ │ ├── revision.tsx │ │ │ │ ├── rollout.scss │ │ │ │ └── rollout.tsx │ │ │ ├── rollouts-grid │ │ │ │ ├── rollouts-grid.scss │ │ │ │ └── rollouts-grid.tsx │ │ │ ├── rollouts-home │ │ │ │ ├── rollouts-home.scss │ │ │ │ └── rollouts-home.tsx │ │ │ ├── rollouts-table │ │ │ │ ├── rollouts-table.scss │ │ │ │ └── rollouts-table.tsx │ │ │ ├── rollouts-toolbar │ │ │ │ ├── rollouts-toolbar.scss │ │ │ │ └── rollouts-toolbar.tsx │ │ │ ├── shortcuts │ │ │ │ ├── shortcuts.scss │ │ │ │ └── shortcuts.tsx │ │ │ ├── status-count │ │ │ │ ├── status-count.scss │ │ │ │ └── status-count.tsx │ │ │ ├── status-icon │ │ │ │ ├── status-icon.scss │ │ │ │ └── status-icon.tsx │ │ │ └── ticker │ │ │ │ └── ticker.tsx │ │ ├── index.html │ │ ├── index.tsx │ │ ├── shared │ │ │ ├── context │ │ │ │ └── api.tsx │ │ │ ├── services │ │ │ │ └── rollout.ts │ │ │ └── utils │ │ │ │ ├── utils.tsx │ │ │ │ └── watch.ts │ │ ├── webpack.common.js │ │ ├── webpack.dev.js │ │ └── webpack.prod.js │ ├── assets │ │ └── images │ │ │ ├── argo-icon-color-square.png │ │ │ ├── argo-icon-white-square.svg │ │ │ ├── argo-icon-white.svg │ │ │ └── argologo.svg │ ├── config │ │ └── theme.ts │ └── models │ │ ├── kubernetes.ts │ │ └── rollout │ │ ├── generated │ │ ├── .gitignore │ │ ├── .swagger-codegen-ignore │ │ ├── .swagger-codegen │ │ │ └── VERSION │ │ ├── api.ts │ │ ├── api_test.spec.ts │ │ ├── configuration.ts │ │ ├── custom.d.ts │ │ ├── git_push.sh │ │ └── index.ts │ │ └── rollout.tsx ├── tsconfig.json └── yarn.lock └── utils ├── analysis ├── factory.go ├── factory_test.go ├── filter.go ├── filter_test.go ├── helpers.go └── helpers_test.go ├── annotations ├── annotations.go └── annotations_test.go ├── apisix ├── apisix.go └── apisix_test.go ├── appmesh └── appmesh.go ├── aws ├── aws.go ├── aws_test.go └── mocks │ └── ELBv2APIClient.go ├── conditions ├── conditions.go ├── experiments.go ├── experiments_test.go └── rollouts_test.go ├── config └── config.go ├── controller ├── controller.go └── controller_test.go ├── defaults ├── defaults.go └── defaults_test.go ├── diff ├── diff.go └── diff_test.go ├── errors └── errors.go ├── evaluate ├── evaluate.go └── evaluate_test.go ├── experiment ├── experiment.go ├── experiment_test.go ├── filter.go └── filter_test.go ├── hash ├── hash.go └── hash_test.go ├── ingress ├── ingress.go ├── ingress_test.go ├── wrapper.go └── wrapper_test.go ├── istio ├── istio.go ├── istio_test.go ├── multicluster.go └── multicluster_test.go ├── json ├── json.go └── json_test.go ├── log ├── log.go ├── log_test.go └── redactor_formatter.go ├── metric ├── metric.go └── metric_test.go ├── plugin ├── downloader.go ├── downloader_test.go ├── plugin.go ├── plugin_test.go └── types │ ├── steps.go │ └── types.go ├── queue └── queue.go ├── record ├── record.go └── record_test.go ├── replicaset ├── bluegreen.go ├── bluegreen_test.go ├── canary.go ├── canary_test.go ├── replicaset.go └── replicaset_test.go ├── rollout ├── rolloututil.go └── rolloututil_test.go ├── service ├── service.go └── service_test.go ├── smi └── smi.go ├── template ├── template.go └── template_test.go ├── time └── now.go ├── tolerantinformer ├── analysisrun.go ├── analysistemplate.go ├── clusteranalysistemplate.go ├── experiment.go ├── rollout.go ├── tolerantinformer_test.go └── tollerantinformer.go ├── unstructured ├── unstructured.go └── unstructured_test.go ├── version └── version.go └── weightutil └── weight.go /.chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | 3 | ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} ({{ datetime "2006-01-02" .Tag.Date }}) 4 | 5 | {{ range .CommitGroups -}} 6 | ### {{ .Title }} 7 | 8 | {{ range .Commits -}} 9 | * {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} 10 | {{ end }} 11 | {{ end -}} 12 | 13 | {{- if .RevertCommits -}} 14 | ### Reverts 15 | 16 | {{ range .RevertCommits -}} 17 | * {{ .Revert.Header }} 18 | {{ end }} 19 | {{ end -}} 20 | 21 | {{- if .NoteGroups -}} 22 | {{ range .NoteGroups -}} 23 | ### {{ .Title }} 24 | 25 | {{ range .Notes }} 26 | {{ .Body }} 27 | {{ end }} 28 | {{ end -}} 29 | {{ end -}} 30 | {{ end -}} -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/argoproj/argo-rollouts 6 | options: 7 | commits: 8 | # filters: 9 | # Type: 10 | # - feat 11 | # - fix 12 | # - perf 13 | # - refactor 14 | commit_groups: 15 | # title_maps: 16 | # feat: Features 17 | # fix: Bug Fixes 18 | # perf: Performance Improvements 19 | # refactor: Code Refactoring 20 | header: 21 | pattern: "^(\\w*)(?:\\(([\\w\\$\\.\\-\\*\\s]*)\\))?\\:\\s(.*)$" 22 | pattern_maps: 23 | - Type 24 | - Scope 25 | - Subject 26 | notes: 27 | keywords: 28 | - BREAKING CHANGE -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | # allow test coverage to drop by 0.1%, assume that it's typically due to CI problems 4 | patch: 5 | default: 6 | threshold: 0.1 7 | project: 8 | default: 9 | threshold: 0.1 10 | ignore: 11 | - 'pkg/apis/rollouts/v1alpha1' 12 | - 'test' 13 | - '**/*.pb.go' 14 | - '**/*.pb.gw.go' 15 | - '**/*generated.go' 16 | - '**/*generated.deepcopy.go' 17 | - '**/*_test.go' 18 | - 'pkg/apis/client/.*' 19 | - 'pkg/client/.*' 20 | - 'vendor/.*' 21 | - '**/mocks/*' 22 | - 'hack/gen-crd-spec/main.go' 23 | - 'hack/gen-docs/main.go' 24 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .idea/ 3 | .DS_Store 4 | dist/ 5 | ui/dist/ 6 | *.iml 7 | # delve debug binaries 8 | cmd/**/debug 9 | debug.test 10 | coverage.* 11 | ui/node_modules 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | 3 | contact_links: 4 | - name: Have you read the docs? 5 | url: https://argo-rollouts.readthedocs.io/ 6 | about: Much help can be found in the docs 7 | - name: Ask a question 8 | url: https://github.com/argoproj/argo-rollouts/discussions/new 9 | about: Ask a question or start a discussion about Argo Rollouts 10 | - name: Chat on Slack 11 | url: https://argoproj.github.io/community/join-slack 12 | about: Maybe chatting with the community can help 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement_proposal.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Enhancement proposal 3 | about: Propose an enhancement for this project 4 | labels: 'enhancement' 5 | --- 6 | # Summary 7 | 8 | What change needs making? 9 | 10 | # Use Cases 11 | 12 | When would you use this? 13 | 14 | --- 15 | 16 | **Message from the maintainers**: 17 | 18 | Impacted by this bug? Give it a 👍. We prioritize the issues with the most 👍. -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | ignore: 8 | - dependency-name: k8s.io/* 9 | 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 14 5 | # Label requiring a response 6 | responseRequiredLabel: more-information-needed 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Checklist: 2 | 3 | * [ ] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo-rollouts/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this is a chore. 4 | * [ ] The title of the PR is (a) [conventional](https://www.conventionalcommits.org/en/v1.0.0/) with a list of types and scopes found [here](https://github.com/argoproj/argo-rollouts/blob/master/.github/workflows/pr-title-check.yml), (b) states what changed, and (c) suffixes the related issues number. E.g. `"fix(controller): Updates such and such. Fixes #1234"`. 5 | * [ ] I've signed my commits with [DCO](https://github.com/argoproj/argoproj/blob/main/community/CONTRIBUTING.md#legal) 6 | * [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged. 7 | * [ ] My builds are green. Try syncing with master if they are not. 8 | * [ ] My organization is added to [USERS.md](https://github.com/argoproj/argo-rollouts/blob/master/USERS.md). 9 | -------------------------------------------------------------------------------- /.github/workflows/changelog.yml: -------------------------------------------------------------------------------- 1 | name: Update Changelog 2 | on: 3 | release: 4 | types: [published] 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | updateChangelog: 10 | permissions: 11 | contents: write # for peter-evans/create-pull-request to create branch 12 | pull-requests: write # for peter-evans/create-pull-request to create a PR 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Update Changelog 19 | run: | 20 | curl -o git-chglog.tar.gz -L https://github.com/git-chglog/git-chglog/releases/download/v0.15.1/git-chglog_0.15.1_linux_amd64.tar.gz 21 | tar -zxvf git-chglog.tar.gz 22 | chmod u+x git-chglog 23 | ./git-chglog --sort semver -o CHANGELOG.md v1.3.1.. 24 | rm git-chglog 25 | - name: Create Pull Request 26 | uses: peter-evans/create-pull-request@v7 27 | with: 28 | commit-message: update changelog 29 | title: "docs: Update Changelog" 30 | body: Update changelog to reflect release changes 31 | branch: update-changelog 32 | base: master 33 | add-paths: | 34 | CHANGELOG.md -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy-Docs 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | deploy: 17 | permissions: 18 | contents: write # for peaceiris/actions-gh-pages to push pages branch 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Setup Python 23 | uses: actions/setup-python@v5 24 | with: 25 | python-version: 3.x 26 | - name: Set up Go 27 | uses: actions/setup-go@v5.4.0 28 | with: 29 | go-version: '1.23' 30 | - name: build 31 | run: | 32 | pip install mkdocs mkdocs_material 33 | make docs 34 | mkdocs build 35 | - name: Deploy 36 | uses: peaceiris/actions-gh-pages@v4 37 | with: 38 | github_token: ${{ secrets.GITHUB_TOKEN }} 39 | publish_dir: ./site 40 | -------------------------------------------------------------------------------- /.github/workflows/stale-issues-pr.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | stale: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | issues: write 14 | pull-requests: write 15 | steps: 16 | - uses: actions/stale@v9 17 | with: 18 | operations-per-run: 250 19 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity.' 20 | stale-pr-message: 'This PR is stale because it has been open 90 days with no activity.' 21 | close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' 22 | close-pr-message: 'This PR was closed because it has been stalled for 14 days with no activity.' 23 | days-before-issue-stale: 60 24 | days-before-pr-stale: 90 25 | days-before-issue-close: -1 26 | days-before-pr-close: -1 27 | stale-issue-label: 'no-issue-activity' 28 | exempt-issue-labels: 'awaiting-approval,work-in-progress' 29 | stale-pr-label: 'no-pr-activity' 30 | exempt-pr-labels: 'awaiting-approval,work-in-progress' 31 | exempt-all-milestones: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .idea/ 3 | .DS_Store 4 | dist/ 5 | *.iml 6 | # delve debug binaries 7 | __debug_bin* 8 | cmd/**/debug 9 | debug.test 10 | coverage.out 11 | coverage.html 12 | junit.xml 13 | rerunreport.txt 14 | site/ 15 | vendor/ 16 | plugin-bin/ 17 | # static 18 | server/static/* 19 | !server/static/.gitkeep 20 | coverage-output-e2e/ 21 | coverage-output-unit/ 22 | junit-unit-test.xml 23 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | modules-download-mode: readonly 4 | timeout: 10m 5 | linters: 6 | default: none 7 | enable: 8 | - govet 9 | - ineffassign 10 | - misspell 11 | - unconvert 12 | - unused 13 | exclusions: 14 | generated: lax 15 | presets: 16 | - comments 17 | - common-false-positives 18 | - legacy 19 | - std-error-handling 20 | paths: 21 | - .*\.pb\.go 22 | - pkg/client 23 | - third_party$ 24 | - builtin$ 25 | - examples$ 26 | formatters: 27 | enable: 28 | - gofmt 29 | - goimports 30 | settings: 31 | goimports: 32 | local-prefixes: 33 | - github.com/argoproj/argo-rollouts 34 | exclusions: 35 | generated: lax 36 | paths: 37 | - .*\.pb\.go 38 | - pkg/client 39 | - third_party$ 40 | - builtin$ 41 | - examples$ 42 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | formats: all 3 | mkdocs: 4 | configuration: mkdocs.yml 5 | fail_on_warning: false 6 | python: 7 | install: 8 | - requirements: docs/requirements.txt 9 | build: 10 | os: "ubuntu-22.04" 11 | tools: 12 | python: "3.7" -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). 2 | -------------------------------------------------------------------------------- /Dockerfile.dev: -------------------------------------------------------------------------------- 1 | #################################################################################################### 2 | # argo-rollouts-dev 3 | #################################################################################################### 4 | FROM golang:1.20 AS builder 5 | 6 | RUN apt-get update && apt-get install -y \ 7 | ca-certificates && \ 8 | apt-get clean && \ 9 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 10 | 11 | FROM gcr.io/distroless/static-debian11 12 | 13 | COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ 14 | COPY rollouts-controller-linux-amd64 /bin/rollouts-controller 15 | COPY step-plugin-e2e-linux-amd64 /bin/plugin-files/step-plugin-e2e 16 | 17 | # Use numeric user, allows kubernetes to identify this user as being 18 | # non-root when we use a security context with runAsNonRoot: true 19 | USER 999 20 | 21 | WORKDIR /home/argo-rollouts 22 | 23 | ENTRYPOINT [ "/bin/rollouts-controller" ] 24 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | owners: 2 | - alexmt 3 | - jessesuen 4 | - zachaller 5 | 6 | approvers: 7 | - alexmt 8 | - huikang 9 | - jessesuen 10 | - khhirani 11 | - leoluz 12 | 13 | reviewers: 14 | - agrawroh 15 | - dthomson25 16 | - harikrongali 17 | - kostis-codefresh 18 | - perenesenko 19 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.2.0 2 | -------------------------------------------------------------------------------- /cmd/kubectl-argo-rollouts/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | log "github.com/sirupsen/logrus" 7 | "k8s.io/cli-runtime/pkg/genericclioptions" 8 | _ "k8s.io/client-go/plugin/pkg/client/auth/azure" 9 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 10 | _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" 11 | "k8s.io/klog/v2" 12 | 13 | logutil "github.com/argoproj/argo-rollouts/utils/log" 14 | 15 | "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/cmd" 16 | "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" 17 | ) 18 | 19 | func main() { 20 | klog.InitFlags(nil) 21 | logutil.SetKLogLogger(log.New()) 22 | streams := genericclioptions.IOStreams{In: os.Stdin, Out: os.Stdout, ErrOut: os.Stderr} 23 | o := options.NewArgoRolloutsOptions(streams) 24 | root := cmd.NewCmdArgoRollouts(o) 25 | if err := root.Execute(); err != nil { 26 | os.Exit(1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /controller/healthz.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | const ( 9 | // HealthzPath is the endpoint to probe if controller is running 10 | HealthzPath = "/healthz" 11 | ) 12 | 13 | type healthzHandler struct{} 14 | 15 | func (h *healthzHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { 16 | w.Header().Set("Content-Type", "text/plain; charset=utf-8") 17 | w.Header().Set("X-Content-Type-Options", "nosniff") 18 | w.WriteHeader(http.StatusOK) 19 | 20 | fmt.Fprintf(w, "ok") 21 | } 22 | 23 | func NewHealthzServer(addr string) *http.Server { 24 | mux := http.NewServeMux() 25 | mux.Handle(HealthzPath, &healthzHandler{}) 26 | 27 | return &http.Server{ 28 | Addr: addr, 29 | Handler: mux, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /controller/healthz_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "net/http/httptest" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestHealthzServer(t *testing.T) { 15 | expectedResponse := `ok` 16 | 17 | addr := fmt.Sprintf("0.0.0.0:%d", DefaultHealthzPort) 18 | healthzServ := NewHealthzServer(addr) 19 | 20 | t.Helper() 21 | req, err := http.NewRequest("GET", "/healthz", nil) 22 | assert.NoError(t, err) 23 | rr := httptest.NewRecorder() 24 | healthzServ.Handler.ServeHTTP(rr, req) 25 | assert.Equal(t, rr.Code, http.StatusOK) 26 | body := rr.Body.String() 27 | log.Println(body) 28 | for _, line := range strings.Split(expectedResponse, "\n") { 29 | assert.Contains(t, body, line) 30 | } 31 | 32 | req, err = http.NewRequest("GET", "/extraneousPath", nil) 33 | assert.NoError(t, err) 34 | rr = httptest.NewRecorder() 35 | healthzServ.Handler.ServeHTTP(rr, req) 36 | assert.Equal(t, rr.Code, http.StatusNotFound) 37 | } 38 | -------------------------------------------------------------------------------- /controller/metrics/client_test.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | 8 | "github.com/argoproj/pkg/kubeclientmetrics" 9 | ) 10 | 11 | const expectedKubernetesRequest = `# TYPE controller_clientset_k8s_request_total counter 12 | controller_clientset_k8s_request_total{kind="Unknown",name="Unknown",namespace="Unknown",status_code="200",verb="Unknown"} 1 13 | controller_clientset_k8s_request_total{kind="replicasets",name="N/A",namespace="default",status_code="200",verb="List"} 1` 14 | 15 | func TestIncKubernetesRequest(t *testing.T) { 16 | config := newFakeServerConfig() 17 | metricsServ := NewMetricsServer(config) 18 | config.K8SRequestProvider.IncKubernetesRequest(kubeclientmetrics.ResourceInfo{ 19 | Kind: "replicasets", 20 | Namespace: "default", 21 | Name: "test", 22 | Verb: kubeclientmetrics.List, 23 | StatusCode: 200, 24 | }) 25 | config.K8SRequestProvider.IncKubernetesRequest(kubeclientmetrics.ResourceInfo{ 26 | Verb: kubeclientmetrics.Unknown, 27 | StatusCode: 200, 28 | }) 29 | testHttpResponse(t, metricsServ.Handler, expectedKubernetesRequest, assert.Contains) 30 | } 31 | -------------------------------------------------------------------------------- /controller/profiling.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "net/http" 5 | "net/http/pprof" 6 | ) 7 | 8 | // NewPProfServer returns a new pprof server to gather runtime profiling data 9 | func NewPProfServer() *http.ServeMux { 10 | mux := http.NewServeMux() 11 | 12 | // TODO: Remove enumerating all pprof endpoints if/when a more ergonomic 13 | // attachment solution is introduced. See: https://github.com/golang/go/issues/71213. 14 | mux.HandleFunc("/debug/pprof/", pprof.Index) 15 | mux.HandleFunc("/debug/pprof/cmdline/", pprof.Cmdline) 16 | mux.HandleFunc("/debug/pprof/profile/", pprof.Profile) 17 | mux.HandleFunc("/debug/pprof/symbol/", pprof.Symbol) 18 | mux.HandleFunc("/debug/pprof/trace/", pprof.Trace) 19 | 20 | return mux 21 | } 22 | -------------------------------------------------------------------------------- /docs/analysis/graphite.md: -------------------------------------------------------------------------------- 1 | # Graphite Metrics 2 | 3 | A [Graphite](https://graphiteapp.org/) query can be used to obtain measurements for analysis. 4 | 5 | ```yaml 6 | apiVersion: argoproj.io/v1alpha1 7 | kind: AnalysisTemplate 8 | metadata: 9 | name: success-rate 10 | spec: 11 | args: 12 | - name: service-name 13 | metrics: 14 | - name: success-rate 15 | interval: 5m 16 | # Note that the Argo Rollouts Graphite metrics provider returns results as an array of float64s with 6 decimal places. 17 | successCondition: results[0] >= 90.000000 18 | failureLimit: 3 19 | provider: 20 | graphite: 21 | address: http://graphite.example.com:9090 22 | query: | 23 | target=summarize( 24 | asPercent( 25 | sumSeries( 26 | stats.timers.httpServerRequests.app.{{args.service-name}}.exception.*.method.*.outcome.{CLIENT_ERROR,INFORMATIONAL,REDIRECTION,SUCCESS}.status.*.uri.*.count 27 | ), 28 | sumSeries( 29 | stats.timers.httpServerRequests.app.{{args.service-name}}.exception.*.method.*.outcome.*.status.*.uri.*.count 30 | ) 31 | ), 32 | '5min', 33 | 'avg' 34 | ) 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/analysis/skywalking.md: -------------------------------------------------------------------------------- 1 | # Apache SkyWalking Metrics 2 | 3 | !!! important 4 | Available since v1.5.0 5 | 6 | A [SkyWalking](https://skywalking.apache.org/) query using GraphQL can be used to obtain measurements for analysis. 7 | 8 | ```yaml 9 | apiVersion: argoproj.io/v1alpha1 10 | kind: AnalysisTemplate 11 | metadata: 12 | name: apdex 13 | spec: 14 | args: 15 | - name: service-name 16 | metrics: 17 | - name: apdex 18 | interval: 5m 19 | successCondition: "all(result.service_apdex.values.values, {asFloat(.value) >= 9900})" 20 | failureLimit: 3 21 | provider: 22 | skywalking: 23 | interval: 3m 24 | address: http://skywalking-oap.istio-system:12800 25 | query: | 26 | query queryData($duration: Duration!) { 27 | service_apdex: readMetricsValues( 28 | condition: { name: "service_apdex", entity: { scope: Service, serviceName: "{{ args.service-name }}", normal: true } }, 29 | duration: $duration) { 30 | label values { values { value } } 31 | } 32 | } 33 | ``` 34 | 35 | The `result` evaluated for the query depends on the specific GraphQL you give, you can try to run the GraphQL query first and inspect the output format, then compose the condition. 36 | -------------------------------------------------------------------------------- /docs/analysis/wavefront.md: -------------------------------------------------------------------------------- 1 | # Wavefront Metrics 2 | 3 | A [Wavefront](https://www.wavefront.com/) query can be used to obtain measurements for analysis. 4 | 5 | ```yaml 6 | apiVersion: argoproj.io/v1alpha1 7 | kind: AnalysisTemplate 8 | metadata: 9 | name: success-rate 10 | spec: 11 | args: 12 | - name: service-name 13 | metrics: 14 | - name: success-rate 15 | interval: 5m 16 | successCondition: result >= 0.95 17 | failureLimit: 3 18 | provider: 19 | wavefront: 20 | address: example.wavefront.com 21 | query: | 22 | sum(rate( 23 | 5m, ts("istio.requestcount.count", response_code!=500 and destination_service="{{args.service-name}}" 24 | ))) / 25 | sum(rate( 26 | 5m, ts("istio.requestcount.count", reporter=client and destination_service="{{args.service-name}}" 27 | ))) 28 | ``` 29 | 30 | Wavefront api tokens can be configured in a kubernetes secret in argo-rollouts namespace. 31 | 32 | ```yaml 33 | apiVersion: v1 34 | kind: Secret 35 | metadata: 36 | name: wavefront-api-tokens 37 | type: Opaque 38 | stringData: 39 | example1.wavefront.com: 40 | example2.wavefront.com: 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/architecture-assets/argo-rollout-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/architecture-assets/argo-rollout-architecture.png -------------------------------------------------------------------------------- /docs/architecture-assets/internal-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/architecture-assets/internal-architecture.png -------------------------------------------------------------------------------- /docs/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/assets/logo.png -------------------------------------------------------------------------------- /docs/concepts-assets/blue-green-deployments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/concepts-assets/blue-green-deployments.png -------------------------------------------------------------------------------- /docs/concepts-assets/canary-deployments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/concepts-assets/canary-deployments.png -------------------------------------------------------------------------------- /docs/contributing-assets/plugin-loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/contributing-assets/plugin-loading.png -------------------------------------------------------------------------------- /docs/dashboard.md: -------------------------------------------------------------------------------- 1 | # UI Dashboard 2 | 3 | The Argo Rollouts Kubectl plugin can serve a local UI Dashboard to visualize your Rollouts. 4 | 5 | To start it, run `kubectl argo rollouts dashboard` in the namespace that contains your Rollouts. 6 | Then visit `localhost:3100` to view the user interface. 7 | 8 | ## List view 9 | 10 | ![Rollouts List](dashboard/rollouts-list.png) 11 | 12 | ## Individual Rollout view 13 | 14 | ![Rollouts List](dashboard/rollout-ui.png) 15 | -------------------------------------------------------------------------------- /docs/dashboard/rollout-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/dashboard/rollout-ui.png -------------------------------------------------------------------------------- /docs/dashboard/rollouts-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/dashboard/rollouts-list.png -------------------------------------------------------------------------------- /docs/features/anti-affinity/images/solution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/anti-affinity/images/solution.png -------------------------------------------------------------------------------- /docs/features/anti-affinity/images/step-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/anti-affinity/images/step-0.png -------------------------------------------------------------------------------- /docs/features/anti-affinity/images/step-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/anti-affinity/images/step-1.png -------------------------------------------------------------------------------- /docs/features/anti-affinity/images/step-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/anti-affinity/images/step-2.png -------------------------------------------------------------------------------- /docs/features/anti-affinity/images/step-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/anti-affinity/images/step-3.png -------------------------------------------------------------------------------- /docs/features/controller-metrics-assets/argo-rollouts-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/controller-metrics-assets/argo-rollouts-metrics.png -------------------------------------------------------------------------------- /docs/features/controller-metrics-assets/prometheus-target.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/controller-metrics-assets/prometheus-target.png -------------------------------------------------------------------------------- /docs/features/kubectl-plugin/kubectl-get-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/kubectl-plugin/kubectl-get-rollout.png -------------------------------------------------------------------------------- /docs/features/kustomize/example/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | configurations: 5 | - ../rollout-transform.yaml 6 | 7 | namePrefix: my- 8 | 9 | resources: 10 | - rollouts-demo.yaml 11 | 12 | configMapGenerator: 13 | - name: rollouts-demo-cm 14 | literals: 15 | - FOO=BAR 16 | 17 | secretGenerator: 18 | - name: rollouts-demo-secret 19 | literals: 20 | - password=Pa55w0rd 21 | 22 | commonLabels: 23 | foo: bar 24 | 25 | commonAnnotations: 26 | foo: bar 27 | 28 | vars: 29 | - name: SERVICE_NAME 30 | objref: 31 | kind: Service 32 | name: rollouts-demo-svc 33 | apiVersion: v1 34 | fieldref: 35 | fieldpath: metadata.name 36 | 37 | images: 38 | - name: argoproj/rollouts-demo 39 | newTag: green 40 | -------------------------------------------------------------------------------- /docs/features/rollback.md: -------------------------------------------------------------------------------- 1 | # Rollback Windows 2 | 3 | !!! important 4 | 5 | Available for blue-green and canary rollouts since v1.4 6 | 7 | By default, when an older Rollout manifest is re-applied, the controller treats it the same as a spec change, and will execute the full list of steps, and perform analysis too. There are two exceptions to this rule: 8 | 1. the controller detects if it is moving back to a blue-green ReplicaSet which exists and is still scaled up (within its `scaleDownDelay`) 9 | 2. the controller detects it is moving back to the canary's "stable" ReplicaSet, and the upgrade had not yet completed. 10 | 11 | It is often undesirable to re-run analysis and steps for a rollout, when the desired behavior is to rollback as soon as possible. To help with this, a rollback window feature allows users to indicate that the promotion to the ReplicaSet within the window will skip all steps. 12 | 13 | Example: 14 | 15 | ```yaml 16 | spec: 17 | rollbackWindow: 18 | revisions: 3 19 | 20 | revisionHistoryLimit: 5 21 | ``` 22 | 23 | Assume a linear revision history: `1`, `2`, `3`, `4`, `5 (current)`. A rollback from revision 5 back to 4 or 3 will fall within the window, so it will be fast tracked. 24 | -------------------------------------------------------------------------------- /docs/features/traffic-management/google-cloud.md: -------------------------------------------------------------------------------- 1 | # Google Cloud 2 | 3 | With the introduction of the Kubernetes Gateway API it is now possible to use Argo Rollouts with all compliant implementations that support it. The integration is available with the [Argo Rollouts Gateway API plugin](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/) currently hosted in Argo Labs. 4 | 5 | Useful resources: 6 | 7 | * [The Gateway API specification](https://gateway-api.sigs.k8s.io/) 8 | * [Support of the Gateway API in Google Cloud](https://cloud.google.com/kubernetes-engine/docs/concepts/gateway-api) 9 | * [Argo Rollouts Plugin capabilities](../plugins/) 10 | * [Plugin for the Gateway API](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi) 11 | 12 | The process involves the following steps: 13 | 14 | 1. Creating a Kubernetes cluster with support for the Gateway API in Google Cloud 15 | 1. Creating a Load balancer that is managed by the Gateway API in Google Cloud 16 | 1. Installing Argo Rollouts + gateway API plugin in the cluster 17 | 1. Defining a Rollout that takes advantage of the plugin 18 | 19 | For a full application that includes all manifests see the [plugin example](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/tree/main/examples/google-cloud). 20 | 21 | 22 | -------------------------------------------------------------------------------- /docs/features/traffic-management/istio-service-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/traffic-management/istio-service-metrics.png -------------------------------------------------------------------------------- /docs/features/traffic-management/istio-workload-metrics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/traffic-management/istio-workload-metrics.png -------------------------------------------------------------------------------- /docs/features/traffic-management/kong.md: -------------------------------------------------------------------------------- 1 | # Kong Ingress 2 | 3 | With the introduction of the Kubernetes Gateway API it is now possible to use Argo Rollouts with all compliant implementations that support it. The integration is available with the [Argo Rollouts Gateway API plugin](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/) currently hosted in Argo Labs. 4 | 5 | Useful resources: 6 | 7 | * [The Gateway API specification](https://gateway-api.sigs.k8s.io/) 8 | * [Support of the Gateway API in Kong](https://docs.konghq.com/kubernetes-ingress-controller/latest/concepts/gateway-api/) 9 | * [Argo Rollouts Plugin capabilities](../plugins/) 10 | * [Plugin for the Gateway API](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi) 11 | 12 | The process involves the following steps: 13 | 14 | 1. Installing the Gateway API CRDs in your cluster 15 | 1. Installing Kong and enabling the Gateway API support feature 16 | 1. Creating a GatewayClass and Gateway resources 17 | 1. Installing Argo Rollouts + gateway API plugin in the cluster 18 | 1. Defining a Rollout that takes advantage of the plugin 19 | 20 | For a full application that includes all manifests see the [plugin example](https://github.com/argoproj-labs/rollouts-plugin-trafficrouter-gatewayapi/tree/main/examples/kong). 21 | 22 | 23 | -------------------------------------------------------------------------------- /docs/features/traffic-management/smi-diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/features/traffic-management/smi-diagram.jpg -------------------------------------------------------------------------------- /docs/generated/notification-services/pushover.md: -------------------------------------------------------------------------------- 1 | # Pushover 2 | 3 | 1. Create an app at [pushover.net](https://pushover.net/apps/build). 4 | 2. Store the API key in `` Secret and define the secret name in `argo-rollouts-notification-configmap` ConfigMap: 5 | 6 | ```yaml 7 | apiVersion: v1 8 | kind: ConfigMap 9 | metadata: 10 | name: argo-rollouts-notification-configmap 11 | data: 12 | service.pushover: | 13 | token: $pushover-token 14 | ``` 15 | 16 | ```yaml 17 | apiVersion: v1 18 | kind: Secret 19 | metadata: 20 | name: 21 | stringData: 22 | pushover-token: avtc41pn13asmra6zaiyf7dh6cgx97 23 | ``` 24 | 25 | 3. Add your user key to your Application resource: 26 | 27 | ```yaml 28 | apiVersion: argoproj.io/v1alpha1 29 | kind: Application 30 | metadata: 31 | annotations: 32 | notifications.argoproj.io/subscribe.on-sync-succeeded.pushover: uumy8u4owy7bgkapp6mc5mvhfsvpcd 33 | ``` -------------------------------------------------------------------------------- /docs/generated/notification-services/webex.md: -------------------------------------------------------------------------------- 1 | # Webex Teams 2 | 3 | ## Parameters 4 | 5 | The Webex Teams notification service configuration includes following settings: 6 | 7 | * `token` - the app token 8 | 9 | ## Configuration 10 | 11 | 1. Create a Webex [Bot](https://developer.webex.com/docs/bots) 12 | 1. Copy the bot access [token](https://developer.webex.com/my-apps) and store it in the `argo-rollouts-notification-secret` Secret and configure Webex Teams integration in `argo-rollouts-notification-configmap` ConfigMap 13 | 14 | ``` yaml 15 | apiVersion: v1 16 | kind: Secret 17 | metadata: 18 | name: 19 | stringData: 20 | webex-token: 21 | ``` 22 | 23 | ``` yaml 24 | apiVersion: v1 25 | kind: ConfigMap 26 | metadata: 27 | name: argo-rollouts-notification-configmap 28 | data: 29 | service.webex: | 30 | token: $webex-token 31 | ``` 32 | 33 | 1. Create subscription for your Webex Teams integration 34 | 35 | ``` yaml 36 | apiVersion: argoproj.io/v1alpha1 37 | kind: Application 38 | metadata: 39 | annotations: 40 | notifications.argoproj.io/subscribe..webex: 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/getting-started/alb/alb-listener-rules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/alb/alb-listener-rules.png -------------------------------------------------------------------------------- /docs/getting-started/alb/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: rollouts-demo-ingress 5 | annotations: 6 | kubernetes.io/ingress.class: alb 7 | spec: 8 | rules: 9 | - http: 10 | paths: 11 | - path: / 12 | pathType: Prefix 13 | backend: 14 | service: 15 | name: rollouts-demo-root 16 | port: 17 | name: use-annotation 18 | -------------------------------------------------------------------------------- /docs/getting-started/alb/paused-rollout-alb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/alb/paused-rollout-alb.png -------------------------------------------------------------------------------- /docs/getting-started/alb/rollout-alb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/alb/rollout-alb.png -------------------------------------------------------------------------------- /docs/getting-started/alb/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 1 7 | strategy: 8 | canary: 9 | canaryService: rollouts-demo-canary 10 | stableService: rollouts-demo-stable 11 | trafficRouting: 12 | alb: 13 | ingress: rollouts-demo-ingress 14 | servicePort: 80 15 | rootService: rollouts-demo-root 16 | steps: 17 | - setWeight: 5 18 | - pause: {} 19 | revisionHistoryLimit: 2 20 | selector: 21 | matchLabels: 22 | app: rollouts-demo 23 | template: 24 | metadata: 25 | labels: 26 | app: rollouts-demo 27 | spec: 28 | containers: 29 | - name: rollouts-demo 30 | image: argoproj/rollouts-demo:blue 31 | ports: 32 | - name: http 33 | containerPort: 8080 34 | protocol: TCP 35 | resources: 36 | requests: 37 | memory: 32Mi 38 | cpu: 5m 39 | -------------------------------------------------------------------------------- /docs/getting-started/alb/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo-root 5 | spec: 6 | type: NodePort 7 | ports: 8 | - port: 80 9 | targetPort: http 10 | protocol: TCP 11 | name: http 12 | selector: 13 | app: rollouts-demo 14 | 15 | --- 16 | apiVersion: v1 17 | kind: Service 18 | metadata: 19 | name: rollouts-demo-canary 20 | spec: 21 | type: NodePort 22 | ports: 23 | - port: 80 24 | targetPort: http 25 | protocol: TCP 26 | name: http 27 | selector: 28 | app: rollouts-demo 29 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 30 | # rollouts-pod-template-hash: 7bf84f9696 31 | 32 | --- 33 | apiVersion: v1 34 | kind: Service 35 | metadata: 36 | name: rollouts-demo-stable 37 | spec: 38 | type: NodePort 39 | ports: 40 | - port: 80 41 | targetPort: http 42 | protocol: TCP 43 | name: http 44 | selector: 45 | app: rollouts-demo 46 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 47 | # rollouts-pod-template-hash: 789746c88d 48 | -------------------------------------------------------------------------------- /docs/getting-started/basic/aborted-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/aborted-rollout.png -------------------------------------------------------------------------------- /docs/getting-started/basic/healthy-rollout-rev4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/healthy-rollout-rev4.png -------------------------------------------------------------------------------- /docs/getting-started/basic/initial-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/initial-rollout.png -------------------------------------------------------------------------------- /docs/getting-started/basic/paused-rollout-rev3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/paused-rollout-rev3.png -------------------------------------------------------------------------------- /docs/getting-started/basic/paused-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/paused-rollout.png -------------------------------------------------------------------------------- /docs/getting-started/basic/promoted-rollout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/basic/promoted-rollout.png -------------------------------------------------------------------------------- /docs/getting-started/basic/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 5 7 | strategy: 8 | canary: 9 | steps: 10 | - setWeight: 20 11 | - pause: {} 12 | - setWeight: 40 13 | - pause: {duration: 10} 14 | - setWeight: 60 15 | - pause: {duration: 10} 16 | - setWeight: 80 17 | - pause: {duration: 10} 18 | revisionHistoryLimit: 2 19 | selector: 20 | matchLabels: 21 | app: rollouts-demo 22 | template: 23 | metadata: 24 | labels: 25 | app: rollouts-demo 26 | spec: 27 | containers: 28 | - name: rollouts-demo 29 | image: argoproj/rollouts-demo:blue 30 | ports: 31 | - name: http 32 | containerPort: 8080 33 | protocol: TCP 34 | resources: 35 | requests: 36 | memory: 32Mi 37 | cpu: 5m 38 | -------------------------------------------------------------------------------- /docs/getting-started/basic/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollouts-demo 13 | -------------------------------------------------------------------------------- /docs/getting-started/istio/gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: rollouts-demo-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway # use istio default controller 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" 15 | -------------------------------------------------------------------------------- /docs/getting-started/istio/paused-rollout-istio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/istio/paused-rollout-istio.png -------------------------------------------------------------------------------- /docs/getting-started/istio/rollout-istio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/istio/rollout-istio.png -------------------------------------------------------------------------------- /docs/getting-started/istio/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 1 7 | strategy: 8 | canary: 9 | canaryService: rollouts-demo-canary 10 | stableService: rollouts-demo-stable 11 | trafficRouting: 12 | istio: 13 | virtualServices: 14 | - name: rollouts-demo-vsvc1 # At least one virtualService is required 15 | routes: 16 | - primary # At least one route is required 17 | - name: rollouts-demo-vsvc2 18 | routes: 19 | - secondary # At least one route is required 20 | steps: 21 | - setWeight: 5 22 | - pause: {} 23 | revisionHistoryLimit: 2 24 | selector: 25 | matchLabels: 26 | app: rollouts-demo 27 | template: 28 | metadata: 29 | labels: 30 | app: rollouts-demo 31 | istio-injection: enabled 32 | spec: 33 | containers: 34 | - name: rollouts-demo 35 | image: argoproj/rollouts-demo:blue 36 | ports: 37 | - name: http 38 | containerPort: 8080 39 | protocol: TCP 40 | resources: 41 | requests: 42 | memory: 32Mi 43 | cpu: 5m 44 | -------------------------------------------------------------------------------- /docs/getting-started/istio/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo-canary 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollouts-demo 13 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 14 | # rollouts-pod-template-hash: 7bf84f9696 15 | 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: rollouts-demo-stable 21 | spec: 22 | ports: 23 | - port: 80 24 | targetPort: http 25 | protocol: TCP 26 | name: http 27 | selector: 28 | app: rollouts-demo 29 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 30 | # rollouts-pod-template-hash: 789746c88d 31 | -------------------------------------------------------------------------------- /docs/getting-started/istio/virtualsvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: rollouts-demo-vsvc 5 | spec: 6 | gateways: 7 | - rollouts-demo-gateway 8 | hosts: 9 | - rollouts-demo.local 10 | http: 11 | - name: primary 12 | route: 13 | - destination: 14 | host: rollouts-demo-stable 15 | weight: 100 16 | - destination: 17 | host: rollouts-demo-canary 18 | weight: 0 19 | -------------------------------------------------------------------------------- /docs/getting-started/mixed/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: rollouts-demo-stable 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | spec: 8 | rules: 9 | - host: rollouts-demo.local 10 | http: 11 | paths: 12 | - path: / 13 | pathType: Prefix 14 | backend: 15 | service: 16 | # Reference to a Service name, also specified in the Rollout spec.strategy.canary.stableService field 17 | name: rollouts-demo-stable 18 | port: 19 | number: 80 20 | -------------------------------------------------------------------------------- /docs/getting-started/mixed/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 1 7 | strategy: 8 | canary: 9 | canaryService: rollouts-demo-canary 10 | stableService: rollouts-demo-stable 11 | trafficRouting: 12 | nginx: 13 | # Reference to an Ingress which has a rule pointing to the stable service (e.g. rollouts-demo-stable) 14 | # This ingress will be cloned with a new name, in order to achieve NGINX traffic splitting. 15 | stableIngress: rollouts-demo-stable 16 | smi: {} 17 | steps: 18 | - setWeight: 5 19 | - pause: {} 20 | revisionHistoryLimit: 2 21 | selector: 22 | matchLabels: 23 | app: rollouts-demo 24 | template: 25 | metadata: 26 | annotations: 27 | linkerd.io/inject: enabled 28 | labels: 29 | app: rollouts-demo 30 | spec: 31 | containers: 32 | - name: rollouts-demo 33 | image: argoproj/rollouts-demo:blue 34 | ports: 35 | - name: http 36 | containerPort: 8080 37 | protocol: TCP 38 | resources: 39 | requests: 40 | memory: 32Mi 41 | cpu: 5m 42 | -------------------------------------------------------------------------------- /docs/getting-started/mixed/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo-canary 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollouts-demo 13 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 14 | # rollouts-pod-template-hash: 7bf84f9696 15 | 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: rollouts-demo-stable 21 | spec: 22 | ports: 23 | - port: 80 24 | targetPort: http 25 | protocol: TCP 26 | name: http 27 | selector: 28 | app: rollouts-demo 29 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 30 | # rollouts-pod-template-hash: 789746c88d 31 | -------------------------------------------------------------------------------- /docs/getting-started/nginx/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: rollouts-demo-stable 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | spec: 8 | rules: 9 | - host: rollouts-demo.local 10 | http: 11 | paths: 12 | - path: / 13 | pathType: Prefix 14 | backend: 15 | service: 16 | # Reference to a Service name, also specified in the Rollout spec.strategy.canary.stableService field 17 | name: rollouts-demo-stable 18 | port: 19 | number: 80 20 | -------------------------------------------------------------------------------- /docs/getting-started/nginx/paused-rollout-nginx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/nginx/paused-rollout-nginx.png -------------------------------------------------------------------------------- /docs/getting-started/nginx/rollout-nginx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/nginx/rollout-nginx.png -------------------------------------------------------------------------------- /docs/getting-started/nginx/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 1 7 | strategy: 8 | canary: 9 | canaryService: rollouts-demo-canary 10 | stableService: rollouts-demo-stable 11 | trafficRouting: 12 | nginx: 13 | stableIngress: rollouts-demo-stable 14 | steps: 15 | - setWeight: 5 16 | - pause: {} 17 | revisionHistoryLimit: 2 18 | selector: 19 | matchLabels: 20 | app: rollouts-demo 21 | template: 22 | metadata: 23 | labels: 24 | app: rollouts-demo 25 | spec: 26 | containers: 27 | - name: rollouts-demo 28 | image: argoproj/rollouts-demo:blue 29 | ports: 30 | - name: http 31 | containerPort: 8080 32 | protocol: TCP 33 | resources: 34 | requests: 35 | memory: 32Mi 36 | cpu: 5m 37 | -------------------------------------------------------------------------------- /docs/getting-started/nginx/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo-canary 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollouts-demo 13 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 14 | # rollouts-pod-template-hash: 7bf84f9696 15 | 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: rollouts-demo-stable 21 | spec: 22 | ports: 23 | - port: 80 24 | targetPort: http 25 | protocol: TCP 26 | name: http 27 | selector: 28 | app: rollouts-demo 29 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 30 | # rollouts-pod-template-hash: 789746c88d 31 | -------------------------------------------------------------------------------- /docs/getting-started/setup/ingress-nginx-controller-metrics-scrape.yaml: -------------------------------------------------------------------------------- 1 | # This patch is intended to patch the ingress-nginx-controller deployment 2 | # so that Prometheus will scrape its metric endpoints 3 | spec: 4 | template: 5 | metadata: 6 | annotations: 7 | prometheus.io/scrape: "true" 8 | prometheus.io/port: "10254" 9 | -------------------------------------------------------------------------------- /docs/getting-started/setup/rollout-istio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/docs/getting-started/setup/rollout-istio.png -------------------------------------------------------------------------------- /docs/getting-started/setup/values-prometheus.yaml: -------------------------------------------------------------------------------- 1 | # A minimal prometheus installation for dev & demo purposes and works well in minikube. 2 | # Sets a more aggressive global scrape interval to allow for faster, realtime data. 3 | # Tested with: 4 | # * Prometheus helm chart: stable/prometheus-11.4.0 (prometheus 2.18.1) 5 | server: 6 | service: 7 | type: NodePort 8 | global: 9 | scrape_interval: 30s 10 | alertmanager: 11 | enabled: false 12 | pushgateway: 13 | enabled: false 14 | kubeStateMetrics: 15 | enabled: false 16 | nodeExporter: 17 | enabled: false 18 | -------------------------------------------------------------------------------- /docs/getting-started/smi/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.k8s.io/v1 2 | kind: Ingress 3 | metadata: 4 | name: rollouts-demo-stable 5 | annotations: 6 | kubernetes.io/ingress.class: nginx 7 | # The following annotation is needed with Linkerd in conjunction with NGINX ingress for 8 | # proxying HTTP traffic. The annotations rewrites the incoming header (example.com) to the 9 | # internal service name, preventing an infinite routing loop. 10 | # See: https://linkerd.io/2/tasks/using-ingress/#nginx 11 | nginx.ingress.kubernetes.io/configuration-snippet: | 12 | proxy_set_header l5d-dst-override $service_name.$namespace.svc.cluster.local:$service_port; 13 | spec: 14 | rules: 15 | - host: rollouts-demo.local 16 | http: 17 | paths: 18 | - path: / 19 | pathType: Prefix 20 | backend: 21 | service: 22 | # Reference to a Service name, also specified in the Rollout spec.strategy.canary.stableService field 23 | name: rollouts-demo-stable 24 | port: 25 | number: 80 26 | -------------------------------------------------------------------------------- /docs/getting-started/smi/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollouts-demo 5 | spec: 6 | replicas: 1 7 | strategy: 8 | canary: 9 | canaryService: rollouts-demo-canary 10 | stableService: rollouts-demo-stable 11 | trafficRouting: 12 | smi: {} 13 | steps: 14 | - setWeight: 5 15 | - pause: {} 16 | revisionHistoryLimit: 2 17 | selector: 18 | matchLabels: 19 | app: rollouts-demo 20 | template: 21 | metadata: 22 | annotations: 23 | linkerd.io/inject: enabled 24 | labels: 25 | app: rollouts-demo 26 | spec: 27 | containers: 28 | - name: rollouts-demo 29 | image: argoproj/rollouts-demo:blue 30 | ports: 31 | - name: http 32 | containerPort: 8080 33 | protocol: TCP 34 | resources: 35 | requests: 36 | memory: 32Mi 37 | cpu: 5m 38 | -------------------------------------------------------------------------------- /docs/getting-started/smi/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollouts-demo-canary 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollouts-demo 13 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 14 | # rollouts-pod-template-hash: 7bf84f9696 15 | 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | name: rollouts-demo-stable 21 | spec: 22 | ports: 23 | - port: 80 24 | targetPort: http 25 | protocol: TCP 26 | name: http 27 | selector: 28 | app: rollouts-demo 29 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 30 | # rollouts-pod-template-hash: 789746c88d 31 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.4.2 2 | mkdocs-material==8.5.11 3 | markdown_include==0.8.0 4 | pygments==2.13.0 5 | jinja2==3.1.2 6 | markdown==3.3.7 7 | -------------------------------------------------------------------------------- /examples/ambassador/echo-mapping.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: getambassador.io/v2 2 | kind: Mapping 3 | metadata: 4 | name: echo 5 | spec: 6 | resolver: endpoint 7 | prefix: /echo 8 | rewrite: /echo 9 | service: echo-stable:80 10 | -------------------------------------------------------------------------------- /examples/ambassador/echo-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: echo 6 | name: echo-stable 7 | spec: 8 | type: ClusterIP 9 | ports: 10 | - name: http 11 | port: 80 12 | protocol: TCP 13 | targetPort: 8080 14 | selector: 15 | app: echo 16 | --- 17 | apiVersion: v1 18 | kind: Service 19 | metadata: 20 | labels: 21 | app: echo 22 | name: echo-canary 23 | spec: 24 | type: ClusterIP 25 | ports: 26 | - name: http 27 | port: 80 28 | protocol: TCP 29 | targetPort: 8080 30 | selector: 31 | app: echo 32 | -------------------------------------------------------------------------------- /examples/ambassador/resolver.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: getambassador.io/v2 2 | kind: KubernetesEndpointResolver 3 | metadata: 4 | name: endpoint 5 | -------------------------------------------------------------------------------- /examples/ambassador/rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: echo-rollout 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: echo 9 | template: 10 | metadata: 11 | labels: 12 | app: echo 13 | spec: 14 | containers: 15 | - image: hashicorp/http-echo 16 | args: 17 | - "-text=VERSION 1" 18 | - -listen=:8080 19 | imagePullPolicy: Always 20 | name: echo-v1 21 | ports: 22 | - containerPort: 8080 23 | strategy: 24 | canary: 25 | stableService: echo-stable 26 | canaryService: echo-canary 27 | trafficRouting: 28 | ambassador: 29 | mappings: 30 | - echo 31 | steps: 32 | - setWeight: 30 33 | - pause: {duration: 30s} 34 | - setWeight: 60 35 | - pause: {duration: 30s} 36 | - setWeight: 100 37 | - pause: {duration: 10} 38 | -------------------------------------------------------------------------------- /examples/apisix/route.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apisix.apache.org/v2 2 | kind: ApisixRoute 3 | metadata: 4 | name: rollouts-apisix-route 5 | spec: 6 | http: 7 | - name: rollouts-apisix 8 | match: 9 | paths: 10 | - /* 11 | methods: 12 | - GET 13 | - POST 14 | - PUT 15 | - DELETE 16 | - PATCH 17 | hosts: 18 | - rollouts-demo.apisix.local 19 | backends: 20 | - serviceName: rollout-apisix-canary-stable 21 | servicePort: 80 22 | - serviceName: rollout-apisix-canary-canary 23 | servicePort: 80 24 | -------------------------------------------------------------------------------- /examples/apisix/services.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: rollout-apisix-canary-canary 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: rollout-apisix-canary 13 | # This selector will be updated with the pod-template-hash of the canary ReplicaSet. e.g.: 14 | # rollouts-pod-template-hash: 7bf84f9696 15 | --- 16 | apiVersion: v1 17 | kind: Service 18 | metadata: 19 | name: rollout-apisix-canary-stable 20 | spec: 21 | ports: 22 | - port: 80 23 | targetPort: http 24 | protocol: TCP 25 | name: http 26 | selector: 27 | app: rollout-apisix-canary 28 | # This selector will be updated with the pod-template-hash of the stable ReplicaSet. e.g.: 29 | # rollouts-pod-template-hash: 789746c88d 30 | -------------------------------------------------------------------------------- /examples/cluster-analysis-templates.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterAnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: always-pass 5 | spec: 6 | metrics: 7 | - name: pass 8 | count: 1 9 | interval: 5s 10 | failureLimit: 1 11 | provider: 12 | job: 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - name: sleep 18 | image: alpine:3.8 19 | command: [sh, -c] 20 | args: [exit 0] 21 | restartPolicy: Never 22 | backoffLimit: 0 23 | -------------------------------------------------------------------------------- /examples/experiment-with-analysis.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates an experiment which starts two ReplicaSets with different images, and 2 | # additionally starts an AnalysisRun in the background 3 | # 4 | # Prerequisites: 5 | # * kubectl apply -f analysis-templates.yaml 6 | # 7 | apiVersion: argoproj.io/v1alpha1 8 | kind: Experiment 9 | metadata: 10 | name: experiment-with-analysis 11 | spec: 12 | templates: 13 | - name: purple 14 | selector: 15 | matchLabels: 16 | app: rollouts-demo 17 | template: 18 | metadata: 19 | labels: 20 | app: rollouts-demo 21 | spec: 22 | containers: 23 | - name: rollouts-demo 24 | image: argoproj/rollouts-demo:purple 25 | imagePullPolicy: Always 26 | - name: orange 27 | selector: 28 | matchLabels: 29 | app: rollouts-demo 30 | template: 31 | metadata: 32 | labels: 33 | app: rollouts-demo 34 | spec: 35 | containers: 36 | - name: rollouts-demo 37 | image: argoproj/rollouts-demo:orange 38 | imagePullPolicy: Always 39 | analyses: 40 | - name: random-fail 41 | templateName: random-fail 42 | - name: pass 43 | templateName: pass 44 | -------------------------------------------------------------------------------- /examples/helm-blue-green/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /examples/helm-blue-green/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: helm-guestbook 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | appVersion: "1.0" 24 | -------------------------------------------------------------------------------- /examples/helm-blue-green/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "helm-guestbook.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "helm-guestbook.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "helm-guestbook.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /examples/helm-blue-green/templates/services.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: {{ template "helm-guestbook.fullname" . }} 6 | labels: 7 | app: {{ template "helm-guestbook.name" . }} 8 | chart: {{ template "helm-guestbook.chart" . }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | spec: 12 | type: {{ .Values.service.type }} 13 | ports: 14 | - port: {{ .Values.service.port }} 15 | targetPort: http 16 | protocol: TCP 17 | name: http 18 | selector: 19 | app: {{ template "helm-guestbook.name" . }} 20 | release: {{ .Release.Name }} 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: {{ template "helm-guestbook.fullname" . }}-preview 26 | labels: 27 | app: {{ template "helm-guestbook.name" . }} 28 | chart: {{ template "helm-guestbook.chart" . }} 29 | release: {{ .Release.Name }} 30 | heritage: {{ .Release.Service }} 31 | spec: 32 | type: {{ .Values.service.type }} 33 | ports: 34 | - port: {{ .Values.service.port }} 35 | targetPort: http 36 | protocol: TCP 37 | name: http 38 | selector: 39 | app: {{ template "helm-guestbook.name" . }} 40 | release: {{ .Release.Name }} 41 | -------------------------------------------------------------------------------- /examples/helm-blue-green/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for helm-guestbook. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: gcr.io/heptio-images/ks-guestbook-demo 9 | tag: 0.1 10 | pullPolicy: IfNotPresent 11 | 12 | service: 13 | type: ClusterIP 14 | port: 80 15 | 16 | ingress: 17 | enabled: false 18 | annotations: {} 19 | # kubernetes.io/ingress.class: nginx 20 | # kubernetes.io/tls-acme: "true" 21 | path: / 22 | hosts: 23 | - chart-example.local 24 | tls: [] 25 | # - secretName: chart-example-tls 26 | # hosts: 27 | # - chart-example.local 28 | 29 | resources: {} 30 | # We usually recommend not to specify default resources and to leave this as a conscious 31 | # choice for the user. This also increases chances charts run on environments with little 32 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 33 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 34 | # limits: 35 | # cpu: 100m 36 | # memory: 128Mi 37 | # requests: 38 | # cpu: 100m 39 | # memory: 128Mi 40 | 41 | nodeSelector: {} 42 | 43 | tolerations: [] 44 | 45 | affinity: {} 46 | -------------------------------------------------------------------------------- /examples/notifications/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | service.slack: | 7 | token: $slack-token 8 | ## Custom Trigger 9 | trigger.on-purple: | 10 | - send: [my-purple-template] 11 | when: rollout.spec.template.spec.containers[0].image == 'argoproj/rollouts-demo:purple' 12 | template.my-purple-template: | 13 | message: | 14 | Rollout {{.rollout.metadata.name}} has purple image 15 | slack: 16 | attachments: | 17 | [{ 18 | "title": "{{ .rollout.metadata.name}}", 19 | "color": "#800080" 20 | }] -------------------------------------------------------------------------------- /examples/notifications/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ../../manifests/notifications 6 | 7 | patches: 8 | - path: configmap.yaml 9 | -------------------------------------------------------------------------------- /examples/rollout-analysis-step.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates a Rollout which starts and finishes analysis at a specific canary step 2 | # 3 | # Prerequisites: 4 | # kubectl apply -f analysis-templates.yaml 5 | # 6 | apiVersion: argoproj.io/v1alpha1 7 | kind: Rollout 8 | metadata: 9 | name: rollout-analysis-step 10 | spec: 11 | replicas: 4 12 | revisionHistoryLimit: 2 13 | selector: 14 | matchLabels: 15 | app: rollout-analysis-step 16 | template: 17 | metadata: 18 | labels: 19 | app: rollout-analysis-step 20 | spec: 21 | containers: 22 | - name: rollouts-demo 23 | image: argoproj/rollouts-demo:blue 24 | imagePullPolicy: Always 25 | ports: 26 | - containerPort: 8080 27 | strategy: 28 | canary: 29 | steps: 30 | - setWeight: 25 31 | # An AnalysisTemplate is referenced at the second step, which starts an AnalysisRun after 32 | # the setWeight step. The rollout will not progress to the following step until the 33 | # AnalysisRun is complete. A failure/error of the analysis will cause the rollout's update to 34 | # abort, and set the canary weight to zero. 35 | - analysis: 36 | templates: 37 | - templateName: random-fail 38 | - templateName: always-pass 39 | clusterScope: true 40 | -------------------------------------------------------------------------------- /examples/rollout-background-analysis.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates a Rollout which performs background analysis while the Rollout is updating. 2 | # 3 | # Prerequisites: 4 | # * kubectl apply -f analysis-templates.yaml 5 | # 6 | apiVersion: argoproj.io/v1alpha1 7 | kind: Rollout 8 | metadata: 9 | name: rollout-background-analysis 10 | spec: 11 | replicas: 4 12 | revisionHistoryLimit: 2 13 | selector: 14 | matchLabels: 15 | app: rollout-background-analysis 16 | template: 17 | metadata: 18 | labels: 19 | app: rollout-background-analysis 20 | spec: 21 | containers: 22 | - name: rollouts-demo 23 | image: argoproj/rollouts-demo:blue 24 | imagePullPolicy: Always 25 | ports: 26 | - containerPort: 8080 27 | strategy: 28 | canary: 29 | # An AnalysisTemplate is referenced here, which starts an AnalysisRun as soon as the update 30 | # begins. The run is terminated when the update completes. A failure/error of the analysis 31 | # will cause the rollout's update to abort, and set the canary weight to zero. 32 | analysis: 33 | templates: 34 | - templateName: random-fail 35 | steps: 36 | - setWeight: 25 37 | - pause: {} 38 | -------------------------------------------------------------------------------- /examples/rollout-baseline-vs-canary.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates how start a new baseline and canary stack as part of the Rollout update. 2 | # Comparing a canary against a baseline (as opposed to existing production instances) is considered 3 | # a common best practice when performing canary analysis, since it eliminates many differences that 4 | # may skew the results of an analysis (cache warmup time, heap size, etc). 5 | apiVersion: argoproj.io/v1alpha1 6 | kind: Rollout 7 | metadata: 8 | name: rollout-baseline-vs-canary 9 | spec: 10 | replicas: 3 11 | revisionHistoryLimit: 2 12 | selector: 13 | matchLabels: 14 | app: rollout-baseline-vs-canary 15 | template: 16 | metadata: 17 | labels: 18 | app: rollout-baseline-vs-canary 19 | spec: 20 | containers: 21 | - name: rollouts-demo 22 | image: argoproj/rollouts-demo:blue 23 | imagePullPolicy: Always 24 | ports: 25 | - containerPort: 8080 26 | strategy: 27 | canary: 28 | steps: 29 | - experiment: 30 | templates: 31 | - name: baseline 32 | specRef: stable 33 | - name: canary 34 | specRef: canary 35 | -------------------------------------------------------------------------------- /examples/rollout-canary.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates a Rollout using the canary update strategy with a customized rollout 2 | # plan. The prescribed steps initially sets a canary weight of 20%, then pauses indefinitely. Once 3 | # resumed, the rollout performs a gradual, automated 20% weight increase until it reaches 100%. 4 | apiVersion: argoproj.io/v1alpha1 5 | kind: Rollout 6 | metadata: 7 | name: rollout-canary 8 | spec: 9 | replicas: 5 10 | revisionHistoryLimit: 2 11 | selector: 12 | matchLabels: 13 | app: rollout-canary 14 | template: 15 | metadata: 16 | labels: 17 | app: rollout-canary 18 | spec: 19 | containers: 20 | - name: rollouts-demo 21 | image: argoproj/rollouts-demo:blue 22 | imagePullPolicy: Always 23 | ports: 24 | - containerPort: 8080 25 | strategy: 26 | canary: 27 | steps: 28 | - setWeight: 20 29 | # The following pause step will pause the rollout indefinitely until manually resumed. 30 | # Rollouts can be manually resumed by running `kubectl argo rollouts promote ROLLOUT` 31 | - pause: {} 32 | - setWeight: 40 33 | - pause: {duration: 40s} 34 | - setWeight: 60 35 | - pause: {duration: 20s} 36 | - setWeight: 80 37 | - pause: {duration: 20s} 38 | -------------------------------------------------------------------------------- /examples/rollout-rolling-update.yaml: -------------------------------------------------------------------------------- 1 | # This example demonstrates how to use normal rolling update for a Rollout update strategy. 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: Rollout 4 | metadata: 5 | name: rollout-rollingupdate 6 | spec: 7 | replicas: 5 8 | revisionHistoryLimit: 2 9 | selector: 10 | matchLabels: 11 | app: rollout-rollingupdate 12 | template: 13 | metadata: 14 | labels: 15 | app: rollout-rollingupdate 16 | spec: 17 | containers: 18 | - name: rollouts-demo 19 | image: argoproj/rollouts-demo:blue 20 | imagePullPolicy: Always 21 | ports: 22 | - containerPort: 8080 23 | strategy: 24 | # For a normal rolling update, simply specify the canary strategy without steps defined. 25 | # The maxSurge and maxUnavailable fields can be specified. If omitted, defaults to 25% and 0 26 | # respectively. 27 | canary: 28 | maxSurge: 1 29 | maxUnavailable: 1 30 | -------------------------------------------------------------------------------- /examples/rollout-template-ref.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/instance: rollout-canary 6 | name: rollout-ref-deployment 7 | spec: 8 | replicas: 0 9 | selector: 10 | matchLabels: 11 | app: rollout-ref-deployment 12 | template: 13 | metadata: 14 | labels: 15 | app: rollout-ref-deployment 16 | spec: 17 | containers: 18 | - name: rollouts-demo 19 | image: argoproj/rollouts-demo:blue 20 | imagePullPolicy: Always 21 | ports: 22 | - containerPort: 8080 23 | --- 24 | apiVersion: argoproj.io/v1alpha1 25 | kind: Rollout 26 | metadata: 27 | name: rollout-ref-deployment 28 | spec: 29 | replicas: 5 30 | revisionHistoryLimit: 2 31 | workloadRef: 32 | apiVersion: apps/v1 33 | kind: Deployment 34 | name: rollout-ref-deployment 35 | strategy: 36 | canary: 37 | steps: 38 | - setWeight: 20 39 | - pause: {duration: 10s} 40 | -------------------------------------------------------------------------------- /examples/traffic-management/job.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: load-test 5 | spec: 6 | args: 7 | - name: namespace 8 | metrics: 9 | - name: pass 10 | interval: 5s 11 | failureLimit: 1 12 | provider: 13 | job: 14 | spec: 15 | template: 16 | metadata: 17 | annotations: 18 | linkerd.io/inject: enabled 19 | spec: 20 | restartPolicy: Never 21 | containers: 22 | - name: load-tester 23 | image: argoproj/load-tester:latest 24 | command: [sh, -c, -x, -e] 25 | args: 26 | - | 27 | wrk -t10 -c40 -d45s -s report.lua http://rollout-linkerd.{{ args.namespace }}.svc/color 28 | jq -e '.errors_ratio <= 0.35 and .latency_avg_ms < 100' report.json 29 | --- -------------------------------------------------------------------------------- /examples/workload-ref/workload-ref.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: rollout-ref-deployment 5 | spec: 6 | replicas: 0 7 | selector: 8 | matchLabels: 9 | app: rollout-ref-deployment 10 | template: 11 | metadata: 12 | labels: 13 | app: rollout-ref-deployment 14 | spec: 15 | containers: 16 | - name: rollouts-demo 17 | image: argoproj/rollouts-demo:blue 18 | imagePullPolicy: Always 19 | ports: 20 | - containerPort: 8080 21 | 22 | --- 23 | apiVersion: argoproj.io/v1alpha1 24 | kind: Rollout 25 | metadata: 26 | name: rollout-ref-deployment 27 | spec: 28 | replicas: 5 29 | workloadRef: 30 | apiVersion: apps/v1 31 | kind: Deployment 32 | name: rollout-ref-deployment 33 | strategy: 34 | canary: 35 | steps: 36 | - setWeight: 20 37 | - pause: {duration: 10s} -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | -------------------------------------------------------------------------------- /hack/build-release-plugins.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -xe 4 | 5 | SRCROOT="$( CDPATH='' cd -- "$(dirname "$0")/.." && pwd -P )" 6 | mkdir -p ${SRCROOT}/dist 7 | 8 | rollout_iid_file=$(mktemp -d "${SRCROOT}/dist/rollout_iid.XXXXXXXXX") 9 | DOCKER_BUILDKIT=1 docker build --iidfile ${rollout_iid_file} --build-arg MAKE_TARGET="plugin-linux plugin-darwin plugin-windows" \ 10 | --target argo-rollouts-build . 11 | rollout_iid=$(cat ${rollout_iid_file}) 12 | container_id=$(docker create ${rollout_iid}) 13 | 14 | for plat in linux-amd64 linux-arm64 darwin-amd64 darwin-arm64 windows-amd64; do 15 | docker cp ${container_id}:/go/src/github.com/argoproj/argo-rollouts/dist/kubectl-argo-rollouts-${plat} ${SRCROOT}/dist 16 | done 17 | 18 | docker rm -v ${container_id} 19 | rm -f ${rollout_iid_file} 20 | -------------------------------------------------------------------------------- /hack/custom-boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | -------------------------------------------------------------------------------- /hack/installers/install-dev-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux -o pipefail 3 | 4 | PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/../..; pwd) 5 | DIST_PATH="${PROJECT_ROOT}/dist" 6 | PATH="${DIST_PATH}:${PATH}" 7 | 8 | mkdir -p ${DIST_PATH} 9 | 10 | gotestsum_version=1.11.0 11 | 12 | OS=$(go env GOOS) 13 | ARCH=$(go env GOARCH) 14 | 15 | export TARGET_FILE=gotestsum_${gotestsum_version}_${OS}_${ARCH}.tar.gz 16 | temp_path="/tmp/${TARGET_FILE}" 17 | url=https://github.com/gotestyourself/gotestsum/releases/download/v${gotestsum_version}/gotestsum_${gotestsum_version}_${OS}_${ARCH}.tar.gz 18 | [ -e ${temp_path} ] || curl -sLf --retry 3 -o ${temp_path} ${url} 19 | 20 | mkdir -p /tmp/gotestsum-${gotestsum_version} 21 | tar -xvzf ${temp_path} -C /tmp/gotestsum-${gotestsum_version} 22 | cp /tmp/gotestsum-${gotestsum_version}/gotestsum ${DIST_PATH}/gotestsum 23 | chmod +x ${DIST_PATH}/gotestsum 24 | gotestsum --version 25 | -------------------------------------------------------------------------------- /hack/swagger-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SWAGGER_CODEGEN_VERSION=3.0.54 4 | PROJECT_ROOT=$(cd $(dirname ${BASH_SOURCE})/..; pwd) 5 | 6 | test -f "$PROJECT_ROOT/dist/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar" || \ 7 | curl https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/${SWAGGER_CODEGEN_VERSION}/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar -o "$PROJECT_ROOT/dist/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar" 8 | 9 | docker run --rm -v "$PROJECT_ROOT:/src" -w /src/ui -t maven:3-jdk-11-slim java -jar /src/dist/swagger-codegen-cli-${SWAGGER_CODEGEN_VERSION}.jar "$@" 10 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package tools 5 | 6 | import ( 7 | // used in `update-codegen.sh` 8 | _ "k8s.io/code-generator/cmd/client-gen" 9 | _ "k8s.io/code-generator/cmd/deepcopy-gen" 10 | _ "k8s.io/code-generator/cmd/defaulter-gen" 11 | _ "k8s.io/code-generator/cmd/go-to-protobuf" 12 | _ "k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo" 13 | _ "k8s.io/code-generator/cmd/informer-gen" 14 | _ "k8s.io/code-generator/cmd/lister-gen" 15 | _ "k8s.io/code-generator/pkg/util" 16 | 17 | // used in `make api-proto` 18 | _ "github.com/gogo/protobuf/protoc-gen-gogo" 19 | _ "github.com/gogo/protobuf/protoc-gen-gogofast" 20 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" 21 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" 22 | 23 | // used in `make gen-openapi` 24 | _ "k8s.io/kube-openapi/cmd/openapi-gen" 25 | ) 26 | -------------------------------------------------------------------------------- /hack/update-k8s-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -euo pipefail 3 | 4 | # Grabbed from https://github.com/kubernetes/kubernetes/issues/79384 5 | VERSION=${1#"v"} 6 | if [ -z "$VERSION" ]; then 7 | echo "Must specify version!" 8 | exit 1 9 | fi 10 | MODS=($( 11 | curl -sS https://raw.githubusercontent.com/kubernetes/kubernetes/v${VERSION}/go.mod | 12 | sed -n 's|.*k8s.io/\(.*\) => ./staging/src/k8s.io/.*|k8s.io/\1|p' 13 | )) 14 | for MOD in "${MODS[@]}"; do 15 | V=$( 16 | go mod download -json "${MOD}@kubernetes-${VERSION}" | 17 | sed -n 's|.*"Version": "\(.*\)".*|\1|p' 18 | ) 19 | go mod edit "-replace=${MOD}=${MOD}@${V}" 20 | done 21 | go get "k8s.io/kubernetes@v${VERSION}" 22 | 23 | go mod vendor 24 | go mod tidy 25 | -------------------------------------------------------------------------------- /hack/update-mocks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | set -o errexit 5 | set -o nounset 6 | set -o pipefail 7 | 8 | PROJECT_ROOT=$(cd $(dirname "$0")/.. ; pwd) 9 | 10 | mockery \ 11 | --dir "${PROJECT_ROOT}"/metric \ 12 | --name Provider \ 13 | --output "${PROJECT_ROOT}"/metricproviders/mocks 14 | 15 | mockery \ 16 | --dir "${PROJECT_ROOT}"/utils/aws \ 17 | --name ELBv2APIClient \ 18 | --output "${PROJECT_ROOT}"/utils/aws/mocks 19 | 20 | mockery \ 21 | --dir "${PROJECT_ROOT}"/rollout/trafficrouting \ 22 | --name TrafficRoutingReconciler \ 23 | --output "${PROJECT_ROOT}"/rollout/mocks 24 | 25 | mockery \ 26 | --dir "${PROJECT_ROOT}"/rollout/steps/plugin \ 27 | --name "Resolver|StepPlugin" \ 28 | --output "${PROJECT_ROOT}"/rollout/steps/plugin/mocks 29 | 30 | mockery \ 31 | --dir "${PROJECT_ROOT}"/rollout/steps/plugin/rpc \ 32 | --name "StepPlugin" \ 33 | --output "${PROJECT_ROOT}"/rollout/steps/plugin/rpc/mocks 34 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | set -o errexit 2 | set -o nounset 3 | set -o pipefail 4 | 5 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. 6 | 7 | DIFFROOT="${SCRIPT_ROOT}/pkg" 8 | TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" 9 | _tmp="${SCRIPT_ROOT}/_tmp" 10 | 11 | cleanup() { 12 | rm -rf "${_tmp}" 13 | } 14 | trap "cleanup" EXIT SIGINT 15 | 16 | cleanup 17 | 18 | mkdir -p "${TMP_DIFFROOT}" 19 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 20 | 21 | "${SCRIPT_ROOT}/hack/update-codegen.sh" 22 | echo "diffing ${DIFFROOT} against freshly generated codegen" 23 | ret=0 24 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 25 | cp -a "${TMP_DIFFROOT}"/* "${DIFFROOT}" 26 | if [[ $ret -eq 0 ]] 27 | then 28 | echo "${DIFFROOT} up to date." 29 | else 30 | echo "${DIFFROOT} is out of date. Please run hack/update-codegen.sh" 31 | exit 1 32 | fi 33 | -------------------------------------------------------------------------------- /manifests/README.md: -------------------------------------------------------------------------------- 1 | # Argo Rollouts Installation Manifests 2 | 3 | * [install.yaml](install.yaml) - Standard installation method. This will create a new namespace, `argo-rollouts`, 4 | where Argo Rollouts controller will run. 5 | 6 | * [namespace-install.yaml](namespace-install.yaml) - Installation which requires only namespace level privileges. 7 | 8 | > Note: Argo Rollouts CRDs are not included into [namespace-install.yaml](namespace-install.yaml). 9 | > and have to be installed separately. The CRD manifests are located in [manifests/crds](./crds) directory. 10 | > Use the following command to install them: 11 | > ```bash 12 | > kubectl apply -k https://github.com/argoproj/argo-rollouts/manifests/crds\?ref\=stable 13 | > ``` 14 | -------------------------------------------------------------------------------- /manifests/base/argo-rollouts-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-config 5 | labels: 6 | app.kubernetes.io/component: rollouts-controller 7 | app.kubernetes.io/name: argo-rollouts 8 | app.kubernetes.io/part-of: argo-rollouts 9 | -------------------------------------------------------------------------------- /manifests/base/argo-rollouts-metrics-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: argo-rollouts-metrics 5 | labels: 6 | app.kubernetes.io/component: server 7 | app.kubernetes.io/name: argo-rollouts-metrics 8 | app.kubernetes.io/part-of: argo-rollouts 9 | spec: 10 | ports: 11 | - name: metrics 12 | protocol: TCP 13 | port: 8090 14 | targetPort: 8090 15 | selector: 16 | app.kubernetes.io/name: argo-rollouts -------------------------------------------------------------------------------- /manifests/base/argo-rollouts-notification-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: argo-rollouts-notification-secret 5 | -------------------------------------------------------------------------------- /manifests/base/argo-rollouts-sa.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: argo-rollouts 5 | labels: 6 | app.kubernetes.io/component: rollouts-controller 7 | app.kubernetes.io/name: argo-rollouts 8 | app.kubernetes.io/part-of: argo-rollouts 9 | 10 | -------------------------------------------------------------------------------- /manifests/base/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - argo-rollouts-sa.yaml 6 | - argo-rollouts-deployment.yaml 7 | - argo-rollouts-aggregate-roles.yaml 8 | - argo-rollouts-metrics-service.yaml 9 | - argo-rollouts-notification-secret.yaml 10 | - argo-rollouts-config.yaml 11 | images: 12 | - name: quay.io/argoproj/argo-rollouts 13 | newTag: latest 14 | -------------------------------------------------------------------------------- /manifests/cluster-install/argo-rollouts-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: argo-rollouts 5 | labels: 6 | app.kubernetes.io/component: rollouts-controller 7 | app.kubernetes.io/name: argo-rollouts 8 | app.kubernetes.io/part-of: argo-rollouts 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: argo-rollouts 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argo-rollouts 16 | namespace: argo-rollouts 17 | -------------------------------------------------------------------------------- /manifests/cluster-install/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - ../crds 6 | - ../base 7 | - ../role 8 | - argo-rollouts-clusterrolebinding.yaml 9 | -------------------------------------------------------------------------------- /manifests/crds/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - rollout-crd.yaml 6 | - experiment-crd.yaml 7 | - analysis-run-crd.yaml 8 | - analysis-template-crd.yaml 9 | - cluster-analysis-template-crd.yaml 10 | -------------------------------------------------------------------------------- /manifests/dashboard-install/dashboard-clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: argo-rollouts-dashboard 6 | app.kubernetes.io/name: argo-rollouts-dashboard 7 | app.kubernetes.io/part-of: argo-rollouts 8 | name: argo-rollouts-dashboard 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: ClusterRole 12 | name: argo-rollouts-dashboard 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argo-rollouts-dashboard 16 | namespace: argo-rollouts 17 | -------------------------------------------------------------------------------- /manifests/dashboard-install/dashboard-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: argo-rollouts-dashboard 6 | app.kubernetes.io/name: argo-rollouts-dashboard 7 | app.kubernetes.io/part-of: argo-rollouts 8 | name: argo-rollouts-dashboard 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | app.kubernetes.io/name: argo-rollouts-dashboard 14 | template: 15 | metadata: 16 | labels: 17 | app.kubernetes.io/name: argo-rollouts-dashboard 18 | spec: 19 | serviceAccountName: argo-rollouts-dashboard 20 | containers: 21 | - name: argo-rollouts-dashboard 22 | image: quay.io/argoproj/kubectl-argo-rollouts 23 | ports: 24 | - containerPort: 3100 25 | -------------------------------------------------------------------------------- /manifests/dashboard-install/dashboard-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: argo-rollouts-dashboard 6 | app.kubernetes.io/name: argo-rollouts-dashboard 7 | app.kubernetes.io/part-of: argo-rollouts 8 | name: argo-rollouts-dashboard 9 | spec: 10 | selector: 11 | app.kubernetes.io/name: argo-rollouts-dashboard 12 | ports: 13 | - port: 3100 14 | protocol: TCP 15 | targetPort: 3100 16 | -------------------------------------------------------------------------------- /manifests/dashboard-install/dashboard-serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: argo-rollouts-dashboard 6 | app.kubernetes.io/name: argo-rollouts-dashboard 7 | app.kubernetes.io/part-of: argo-rollouts 8 | name: argo-rollouts-dashboard 9 | -------------------------------------------------------------------------------- /manifests/dashboard-install/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - dashboard-clusterrolebinding.yaml 6 | - dashboard-clusterrole.yaml 7 | - dashboard-deployment.yaml 8 | - dashboard-service.yaml 9 | - dashboard-serviceaccount.yaml 10 | images: 11 | - name: quay.io/argoproj/kubectl-argo-rollouts 12 | newTag: latest 13 | -------------------------------------------------------------------------------- /manifests/namespace-install/add-namespaced-flag.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: argo-rollouts 5 | spec: 6 | template: 7 | spec: 8 | containers: 9 | - name: argo-rollouts 10 | args: 11 | - --namespaced 12 | -------------------------------------------------------------------------------- /manifests/namespace-install/argo-rollouts-rolebinding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: argo-rollouts 5 | labels: 6 | app.kubernetes.io/component: rollouts-controller 7 | app.kubernetes.io/name: argo-rollouts 8 | app.kubernetes.io/part-of: argo-rollouts 9 | roleRef: 10 | apiGroup: rbac.authorization.k8s.io 11 | kind: Role 12 | name: argo-rollouts 13 | subjects: 14 | - kind: ServiceAccount 15 | name: argo-rollouts 16 | -------------------------------------------------------------------------------- /manifests/namespace-install/clusterrole-to-role.yaml: -------------------------------------------------------------------------------- 1 | - op: replace 2 | path: /kind 3 | value: Role 4 | -------------------------------------------------------------------------------- /manifests/namespace-install/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | bases: 5 | - ../base 6 | - ../role 7 | 8 | resources: 9 | - argo-rollouts-rolebinding.yaml 10 | 11 | patches: 12 | - path: add-namespaced-flag.yaml 13 | 14 | patchesJson6902: 15 | - path: clusterrole-to-role.yaml 16 | target: 17 | group: rbac.authorization.k8s.io 18 | kind: ClusterRole 19 | name: argo-rollouts 20 | version: v1 21 | -------------------------------------------------------------------------------- /manifests/notifications/argo-rollouts-notification-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap -------------------------------------------------------------------------------- /manifests/notifications/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - argo-rollouts-notification-configmap.yaml 6 | 7 | patches: 8 | - path: on-rollout-completed.yaml 9 | - path: on-scaling-replica-set.yaml 10 | - path: on-rollout-step-completed.yaml 11 | - path: on-rollout-updated.yaml 12 | - path: on-rollout-aborted.yaml 13 | - path: on-rollout-paused.yaml 14 | - path: on-analysis-run-running.yaml 15 | - path: on-analysis-run-error.yaml 16 | - path: on-analysis-run-failed.yaml 17 | -------------------------------------------------------------------------------- /manifests/notifications/on-analysis-run-error.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-analysis-run-error: | 7 | - send: [analysis-run-error] 8 | template.analysis-run-error: | 9 | message: Rollout {{.rollout.metadata.name}}'s analysis run is in error state. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}}'s analysis run is in error state. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#ECB22E", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-analysis-run-failed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-analysis-run-failed: | 7 | - send: [analysis-run-failed] 8 | template.analysis-run-failed: | 9 | message: Rollout {{.rollout.metadata.name}}'s analysis run failed. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}}'s analysis run failed. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#E01E5A", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-analysis-run-running.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-analysis-run-running: | 7 | - send: [analysis-run-running] 8 | template.analysis-run-running: | 9 | message: Rollout {{.rollout.metadata.name}}'s analysis run is running. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}}'s analysis run is running. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#18be52", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-rollout-aborted.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-rollout-aborted: | 7 | - send: [rollout-aborted] 8 | template.rollout-aborted: | 9 | message: Rollout {{.rollout.metadata.name}} has been aborted. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}} has been aborted. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#E01E5A", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-rollout-completed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-rollout-completed: | 7 | - send: [rollout-completed] 8 | template.rollout-completed: | 9 | message: Rollout {{.rollout.metadata.name}} has been completed. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}} has been completed. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#18be52", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-rollout-paused.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-rollout-paused: | 7 | - send: [rollout-paused] 8 | template.rollout-paused: | 9 | message: Rollout {{.rollout.metadata.name}} has been paused. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}} has been paused. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#18be52", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/notifications/on-rollout-updated.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: argo-rollouts-notification-configmap 5 | data: 6 | trigger.on-rollout-updated: | 7 | - send: [rollout-updated] 8 | template.rollout-updated: | 9 | message: Rollout {{.rollout.metadata.name}} has been updated. 10 | email: 11 | subject: Rollout {{.rollout.metadata.name}} has been updated. 12 | slack: 13 | attachments: | 14 | [{ 15 | "title": "{{ .rollout.metadata.name}}", 16 | "color": "#18be52", 17 | "fields": [ 18 | { 19 | "title": "Strategy", 20 | "value": "{{if .rollout.spec.strategy.blueGreen}}BlueGreen{{end}}{{if .rollout.spec.strategy.canary}}Canary{{end}}", 21 | "short": true 22 | } 23 | {{range $index, $c := .rollout.spec.template.spec.containers}} 24 | {{if not $index}},{{end}} 25 | {{if $index}},{{end}} 26 | { 27 | "title": "{{$c.name}}", 28 | "value": "{{$c.image}}", 29 | "short": true 30 | } 31 | {{end}} 32 | ] 33 | }] 34 | -------------------------------------------------------------------------------- /manifests/role/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | resources: 5 | - argo-rollouts-clusterrole.yaml 6 | -------------------------------------------------------------------------------- /metric/provider.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 4 | 5 | // Provider methods to query an external systems and generate a measurement 6 | type Provider interface { 7 | // Run start a new external system call for a measurement 8 | // Should be idempotent and do nothing if a call has already been started 9 | Run(*v1alpha1.AnalysisRun, v1alpha1.Metric) v1alpha1.Measurement 10 | // Resume Checks if the external system call is finished and returns the current measurement 11 | Resume(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement 12 | // Terminate will terminate an in-progress measurement 13 | Terminate(*v1alpha1.AnalysisRun, v1alpha1.Metric, v1alpha1.Measurement) v1alpha1.Measurement 14 | // GarbageCollect is used to garbage collect completed measurements to the specified limit 15 | GarbageCollect(*v1alpha1.AnalysisRun, v1alpha1.Metric, int) error 16 | // Type gets the provider type 17 | Type() string 18 | // GetMetadata returns any additional metadata which providers need to store/display as part 19 | // of the metric result. For example, Prometheus uses is to store the final resolved queries. 20 | GetMetadata(metric v1alpha1.Metric) map[string]string 21 | } 22 | -------------------------------------------------------------------------------- /metricproviders/cloudwatch/mock_test.go: -------------------------------------------------------------------------------- 1 | package cloudwatch 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/aws/aws-sdk-go-v2/service/cloudwatch" 7 | "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types" 8 | ) 9 | 10 | type mockAPI struct { 11 | response *cloudwatch.GetMetricDataOutput 12 | err error 13 | } 14 | 15 | func (m *mockAPI) Query(interval time.Duration, query []types.MetricDataQuery) (*cloudwatch.GetMetricDataOutput, error) { 16 | if m.err != nil { 17 | return nil, m.err 18 | } 19 | return m.response, nil 20 | } 21 | -------------------------------------------------------------------------------- /metricproviders/graphite/mock_test.go: -------------------------------------------------------------------------------- 1 | package graphite 2 | 3 | type mockAPI struct { 4 | response []dataPoint 5 | err error 6 | } 7 | 8 | func (m mockAPI) Query(query string) ([]dataPoint, error) { 9 | if m.err != nil { 10 | return nil, m.err 11 | } 12 | return m.response, nil 13 | } 14 | -------------------------------------------------------------------------------- /metricproviders/influxdb/mock_test.go: -------------------------------------------------------------------------------- 1 | package influxdb 2 | 3 | import ( 4 | "context" 5 | 6 | influxapi "github.com/influxdata/influxdb-client-go/v2/api" 7 | "github.com/influxdata/influxdb-client-go/v2/domain" 8 | ) 9 | 10 | type mockAPI struct { 11 | response *influxapi.QueryTableResult 12 | err error 13 | } 14 | 15 | func (m mockAPI) Query(ctx context.Context, query string) (*influxapi.QueryTableResult, error) { 16 | if m.err != nil { 17 | return nil, m.err 18 | } 19 | return m.response, nil 20 | } 21 | 22 | func (m mockAPI) QueryRaw(context.Context, string, *domain.Dialect) (string, error) { 23 | panic("Not used") 24 | } 25 | 26 | func (m mockAPI) QueryRawWithParams(ctx context.Context, query string, dialect *domain.Dialect, params any) (string, error) { 27 | panic("Not used") 28 | } 29 | 30 | func (m mockAPI) QueryWithParams(ctx context.Context, query string, params any) (*influxapi.QueryTableResult, error) { 31 | panic("Not used") 32 | } 33 | -------------------------------------------------------------------------------- /metricproviders/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/argoproj/argo-rollouts/metric" 7 | "github.com/argoproj/argo-rollouts/metricproviders/plugin/client" 8 | "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" 9 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 10 | ) 11 | 12 | const ProviderType = "RPCPlugin" 13 | 14 | type MetricPlugin struct { 15 | rpc.MetricProviderPlugin 16 | } 17 | 18 | // NewRpcPlugin returns a new RPC plugin with a singleton client 19 | func NewRpcPlugin(metric v1alpha1.Metric) (metric.Provider, error) { 20 | pluginClient, err := client.GetMetricPlugin(metric) 21 | if err != nil { 22 | return nil, fmt.Errorf("unable to get metric plugin: %w", err) 23 | } 24 | 25 | return MetricPlugin{ 26 | MetricProviderPlugin: pluginClient, 27 | }, nil 28 | } 29 | 30 | // GarbageCollect calls the plugins garbage collect method but cast the error back to an "error" type for the internal interface 31 | func (m MetricPlugin) GarbageCollect(run *v1alpha1.AnalysisRun, metric v1alpha1.Metric, limit int) error { 32 | resp := m.MetricProviderPlugin.GarbageCollect(run, metric, limit) 33 | if resp.HasError() { 34 | return fmt.Errorf("failed to garbage collect via plugin: %w", resp) 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /metricproviders/prometheus/headers_round_tripper.go: -------------------------------------------------------------------------------- 1 | package prometheus 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 7 | ) 8 | 9 | type httpHeadersRoundTripper struct { 10 | headers []v1alpha1.WebMetricHeader 11 | roundTripper http.RoundTripper 12 | } 13 | 14 | // RoundTrip implements the http.RoundTripper interface. 15 | func (h httpHeadersRoundTripper) RoundTrip(r *http.Request) (*http.Response, error) { 16 | for _, header := range h.headers { 17 | r.Header.Set(header.Key, header.Value) 18 | } 19 | 20 | return h.roundTripper.RoundTrip(r) 21 | } 22 | -------------------------------------------------------------------------------- /metricproviders/skywalking/mock_test.go: -------------------------------------------------------------------------------- 1 | package skywalking 2 | 3 | type mockAPI struct { 4 | err error 5 | results any 6 | } 7 | 8 | func (m mockAPI) Query(query string) (any, error) { 9 | if m.err != nil { 10 | return m.results, m.err 11 | } 12 | return m.results, nil 13 | } 14 | -------------------------------------------------------------------------------- /metricproviders/wavefront/mock_test.go: -------------------------------------------------------------------------------- 1 | package wavefront 2 | 3 | import ( 4 | wavefront_api "github.com/spaceapegames/go-wavefront" 5 | ) 6 | 7 | type mockAPI struct { 8 | response *wavefront_api.QueryResponse 9 | err error 10 | } 11 | 12 | type mockQuery struct { 13 | response *wavefront_api.QueryResponse 14 | err error 15 | } 16 | 17 | func (m mockAPI) NewQuery(queryParams *wavefront_api.QueryParams) WavefrontQueryAPI { 18 | return mockQuery{ 19 | response: m.response, 20 | err: m.err, 21 | } 22 | } 23 | 24 | func (q mockQuery) Execute() (*wavefront_api.QueryResponse, error) { 25 | if q.err != nil { 26 | return nil, q.err 27 | } 28 | return q.response, nil 29 | } 30 | -------------------------------------------------------------------------------- /pkg/apiclient/rollout/forwarder_overwrite.go: -------------------------------------------------------------------------------- 1 | package rollout 2 | 3 | import ( 4 | "github.com/argoproj/pkg/grpc/http" 5 | ) 6 | 7 | func init() { 8 | forward_RolloutService_WatchRolloutInfos_0 = http.StreamForwarder 9 | forward_RolloutService_WatchRolloutInfo_0 = http.StreamForwarder 10 | } 11 | -------------------------------------------------------------------------------- /pkg/apis/rollouts/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package 2 | // +groupName=argoproj.io 3 | // +k8s:openapi-gen=true 4 | 5 | // Package v1alpha1 is the v1alpha1 version of the API. 6 | package v1alpha1 7 | -------------------------------------------------------------------------------- /pkg/apis/rollouts/v1alpha1/types_test.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestRolloutPauseDuration(t *testing.T) { 10 | rp := RolloutPause{} 11 | assert.Equal(t, int32(0), rp.DurationSeconds()) 12 | rp.Duration = DurationFromInt(10) 13 | assert.Equal(t, int32(10), rp.DurationSeconds()) 14 | rp.Duration = DurationFromString("10") 15 | assert.Equal(t, int32(10), rp.DurationSeconds()) 16 | rp.Duration = DurationFromString("10s") 17 | assert.Equal(t, int32(10), rp.DurationSeconds()) 18 | rp.Duration = DurationFromString("1h") 19 | assert.Equal(t, int32(3600), rp.DurationSeconds()) 20 | rp.Duration = DurationFromString("1ms") 21 | assert.Equal(t, int32(0), rp.DurationSeconds()) 22 | rp.Duration = DurationFromString("1z") 23 | assert.Equal(t, int32(-1), rp.DurationSeconds()) 24 | rp.Duration = DurationFromString("20000000000") // out of int32 25 | assert.Equal(t, int32(-1), rp.DurationSeconds()) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated fake clientset. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package contains the scheme of the automatically generated clientset. 20 | package scheme 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/rollouts/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // This package has the automatically generated typed clients. 20 | package v1alpha1 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/rollouts/v1alpha1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | // Package fake has the automatically generated clients. 20 | package fake 21 | -------------------------------------------------------------------------------- /pkg/client/clientset/versioned/typed/rollouts/v1alpha1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Code generated by client-gen. DO NOT EDIT. 18 | 19 | package v1alpha1 20 | 21 | type AnalysisRunExpansion interface{} 22 | 23 | type AnalysisTemplateExpansion interface{} 24 | 25 | type ClusterAnalysisTemplateExpansion interface{} 26 | 27 | type ExperimentExpansion interface{} 28 | 29 | type RolloutExpansion interface{} 30 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/cmd_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake" 10 | ) 11 | 12 | func TestCmdArgoRolloutsCmdUsage(t *testing.T) { 13 | tf, o := options.NewFakeArgoRolloutsOptions() 14 | defer tf.Cleanup() 15 | cmd := NewCmdArgoRollouts(o) 16 | cmd.PersistentPreRunE = o.PersistentPreRunE 17 | err := cmd.Execute() 18 | assert.Error(t, err) 19 | stdout := o.Out.(*bytes.Buffer).String() 20 | stderr := o.ErrOut.(*bytes.Buffer).String() 21 | assert.Empty(t, stdout) 22 | assert.Contains(t, stderr, "Usage:") 23 | assert.Contains(t, stderr, "kubectl-argo-rollouts COMMAND") 24 | } 25 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/create/testdata/analysis-template-no-name.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | spec: 5 | args: 6 | - name: foo 7 | metrics: 8 | - name: pass 9 | interval: 5s 10 | failureLimit: 1 11 | provider: 12 | job: 13 | spec: 14 | template: 15 | spec: 16 | containers: 17 | - name: sleep 18 | image: alpine:3.8 19 | command: [sh, -c] 20 | args: [exit 0] 21 | restartPolicy: Never 22 | backoffLimit: 0 -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/create/testdata/analysis-template.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "kind": "AnalysisTemplate", 4 | "apiVersion": "argoproj.io/v1alpha1", 5 | "metadata": { 6 | "name": "pass" 7 | }, 8 | "spec": { 9 | "args": [ 10 | { 11 | "name": "foo" 12 | } 13 | ], 14 | "metrics": [ 15 | { 16 | "name": "pass", 17 | "interval": "5s", 18 | "failureLimit": 1, 19 | "provider": { 20 | "job": { 21 | "spec": { 22 | "template": { 23 | "spec": { 24 | "containers": [ 25 | { 26 | "name": "sleep", 27 | "image": "alpine:3.8", 28 | "command": [ 29 | "sh", 30 | "-c" 31 | ], 32 | "args": [ 33 | "exit 0" 34 | ] 35 | } 36 | ], 37 | "restartPolicy": "Never" 38 | } 39 | }, 40 | "backoffLimit": 0 41 | } 42 | } 43 | } 44 | } 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/create/testdata/analysis-template.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: pass 5 | spec: 6 | args: 7 | - name: foo 8 | metrics: 9 | - name: pass 10 | interval: 5s 11 | failureLimit: 1 12 | provider: 13 | job: 14 | spec: 15 | template: 16 | spec: 17 | containers: 18 | - name: sleep 19 | image: alpine:3.8 20 | command: [sh, -c] 21 | args: [exit 0] 22 | restartPolicy: Never 23 | backoffLimit: 0 -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/create/testdata/cluster-analysis-template.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterAnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: pass 5 | spec: 6 | args: 7 | - name: foo 8 | metrics: 9 | - name: pass 10 | interval: 5s 11 | failureLimit: 1 12 | provider: 13 | job: 14 | spec: 15 | template: 16 | spec: 17 | containers: 18 | - name: sleep 19 | image: alpine:3.8 20 | command: [sh, -c] 21 | args: [exit 0] 22 | restartPolicy: Never 23 | backoffLimit: 0 -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/invalid-empty-rollout-vsvc.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | --- 4 | apiVersion: networking.istio.io/v1alpha3 5 | kind: VirtualService -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/invalid-multiple-docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: argo-rollouts-metrics 6 | labels: 7 | region: us-west 8 | spec: 9 | ports: 10 | - name: metrics 11 | protocol: TCP 12 | port: 8090 13 | targetPort: 8090 14 | --- 15 | apiVersion: argoproj.io/v1alpha1 16 | kind: Rollout 17 | metadata: 18 | name: invalid-rollout 19 | spec: 20 | revisionHistoryLimit: 1 21 | replicas: 1 22 | strategy: 23 | canary: 24 | maxUnavailable: 0 25 | maxSurge: 0 26 | analysis: 27 | templates: 28 | - templateName: integrationtests 29 | steps: 30 | - setWeight: 10 31 | - setWeight: 20 32 | - setWeight: 40 33 | - setWeight: 80 34 | selector: 35 | matchLabels: 36 | app: invalid-rollout 37 | template: 38 | metadata: 39 | labels: 40 | app: invalid-rollout 41 | spec: 42 | containers: 43 | - name: invalid-rollout 44 | image: invalid-rollout:0.0.0 45 | ports: 46 | - name: http 47 | containerPort: 8080 48 | protocol: TCP 49 | readinessProbe: 50 | httpGet: 51 | path: /ping 52 | port: 8080 53 | periodSeconds: 5 54 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/invalid-unknown-field.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: Rollout 4 | metadata: 5 | name: invalid-rollout 6 | spec: 7 | replicas: 10 8 | strategy: 9 | unknown-strategy: 10 | analysis: 11 | templates: 12 | - templateName: integrationtests 13 | steps: 14 | - setWeight: 10 15 | selector: 16 | matchLabels: 17 | app: invalid-rollout 18 | template: 19 | metadata: 20 | labels: 21 | app: invalid-rollout 22 | spec: 23 | containers: 24 | - name: invalid-rollout 25 | image: valid-rollout:0.0.0 26 | ports: 27 | - name: http 28 | containerPort: 8080 29 | protocol: TCP 30 | readinessProbe: 31 | httpGet: 32 | path: /ping 33 | port: 8080 34 | periodSeconds: 5 35 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/invalid.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: Rollout 4 | metadata: 5 | name: invalid-rollout 6 | spec: 7 | revisionHistoryLimit: 1 8 | replicas: 1 9 | strategy: 10 | canary: 11 | maxUnavailable: 0 12 | maxSurge: 0 13 | analysis: 14 | templates: 15 | - templateName: integrationtests 16 | steps: 17 | - setWeight: 10 18 | - setWeight: 20 19 | - setWeight: 40 20 | - setWeight: 80 21 | selector: 22 | matchLabels: 23 | app: invalid-rollout 24 | template: 25 | metadata: 26 | labels: 27 | app: invalid-rollout 28 | spec: 29 | containers: 30 | - name: invalid-rollout 31 | image: invalid-rollout:0.0.0 32 | ports: 33 | - name: http 34 | containerPort: 8080 35 | protocol: TCP 36 | readinessProbe: 37 | httpGet: 38 | path: /ping 39 | port: 8080 40 | periodSeconds: 5 41 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/valid-blue-green.yml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-bluegreen 5 | spec: 6 | replicas: 2 7 | revisionHistoryLimit: 2 8 | selector: 9 | matchLabels: 10 | app: rollout-bluegreen 11 | template: 12 | metadata: 13 | labels: 14 | app: rollout-bluegreen 15 | spec: 16 | containers: 17 | - name: rollouts-demo 18 | image: argoproj/rollouts-demo:blue 19 | imagePullPolicy: Always 20 | ports: 21 | - containerPort: 8080 22 | strategy: 23 | blueGreen: 24 | activeService: rollout-bluegreen-active 25 | previewService: rollout-bluegreen-preview 26 | autoPromotionEnabled: false 27 | --- 28 | kind: Service 29 | apiVersion: v1 30 | metadata: 31 | name: rollout-bluegreen-active 32 | spec: 33 | selector: 34 | app: rollout-bluegreen 35 | ports: 36 | - protocol: TCP 37 | port: 80 38 | targetPort: 8080 39 | 40 | --- 41 | kind: Service 42 | apiVersion: v1 43 | metadata: 44 | name: rollout-bluegreen-preview 45 | spec: 46 | selector: 47 | app: rollout-bluegreen 48 | ports: 49 | - protocol: TCP 50 | port: 80 51 | targetPort: 8080 52 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/valid-with-another-empty-object.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: Rollout 4 | metadata: 5 | name: valid-rollout 6 | spec: 7 | revisionHistoryLimit: 1 8 | replicas: 10 9 | strategy: 10 | canary: 11 | steps: 12 | - setWeight: 10 13 | - setWeight: 20 14 | selector: 15 | matchLabels: 16 | app: valid-rollout 17 | template: 18 | metadata: 19 | labels: 20 | app: valid-rollout 21 | spec: 22 | containers: 23 | - name: valid-rollout 24 | image: valid-rollout:0.0.0 25 | --- 26 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/valid-workload-ref.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 # Create a rollout resource 2 | kind: Rollout 3 | metadata: 4 | name: rollout-ref-deployment 5 | spec: 6 | replicas: 5 7 | workloadRef: # Reference an existing Deployment using workloadRef field 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | name: rollout-ref-deployment 11 | strategy: 12 | canary: 13 | steps: 14 | - setWeight: 20 15 | - pause: {duration: 10s} 16 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/lint/testdata/valid.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: Rollout 4 | metadata: 5 | name: valid-rollout 6 | spec: 7 | revisionHistoryLimit: 1 8 | replicas: 10 9 | strategy: 10 | canary: 11 | maxUnavailable: 0 12 | maxSurge: 1 13 | analysis: 14 | templates: 15 | - templateName: integrationtests 16 | steps: 17 | - setWeight: 10 18 | - setWeight: 20 19 | - setWeight: 40 20 | - setWeight: 80 21 | selector: 22 | matchLabels: 23 | app: valid-rollout 24 | template: 25 | metadata: 26 | labels: 27 | app: valid-rollout 28 | spec: 29 | containers: 30 | - name: valid-rollout 31 | image: valid-rollout:0.0.0 32 | ports: 33 | - name: http 34 | containerPort: 8080 35 | protocol: TCP 36 | readinessProbe: 37 | httpGet: 38 | path: /ping 39 | port: 8080 40 | periodSeconds: 5 41 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/set/set.go: -------------------------------------------------------------------------------- 1 | package set 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options" 7 | ) 8 | 9 | const ( 10 | setExample = ` 11 | # Set rollout image 12 | %[1]s set image my-rollout demo=argoproj/rollouts-demo:yellow` 13 | ) 14 | 15 | // NewCmdSet returns a new instance of an `rollouts set` command 16 | func NewCmdSet(o *options.ArgoRolloutsOptions) *cobra.Command { 17 | var cmd = &cobra.Command{ 18 | Use: "set COMMAND", 19 | Short: "Update various values on resources", 20 | Long: "This command consists of multiple subcommands which can be used to update rollout resources.", 21 | Example: o.Example(setExample), 22 | SilenceUsage: true, 23 | RunE: func(c *cobra.Command, args []string) error { 24 | return o.UsageErr(c) 25 | }, 26 | } 27 | cmd.AddCommand(NewCmdSetImage(o)) 28 | return cmd 29 | } 30 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/signals/signal.go: -------------------------------------------------------------------------------- 1 | package signals 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | "syscall" 8 | ) 9 | 10 | func SetupSignalHandler(cancel context.CancelFunc) { 11 | c := make(chan os.Signal, 2) 12 | signal.Notify(c, os.Interrupt, syscall.SIGTERM) 13 | go func() { 14 | <-c 15 | cancel() 16 | <-c 17 | os.Exit(1) // second signal. Exit directly. 18 | }() 19 | } 20 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/cmd/version/version_test.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | 9 | options "github.com/argoproj/argo-rollouts/pkg/kubectl-argo-rollouts/options/fake" 10 | ) 11 | 12 | func TestVersionCmd(t *testing.T) { 13 | tf, o := options.NewFakeArgoRolloutsOptions() 14 | defer tf.Cleanup() 15 | cmd := NewCmdVersion(o) 16 | cmd.PersistentPreRunE = o.PersistentPreRunE 17 | cmd.SetArgs([]string{}) 18 | err := cmd.Execute() 19 | assert.NoError(t, err) 20 | stdout := o.Out.(*bytes.Buffer).String() 21 | assert.Contains(t, stdout, "kubectl-argo-rollouts: v99.99.99+unknown\n") 22 | assert.Contains(t, stdout, "BuildDate: 1970-01-01T00:00:00Z\n") 23 | } 24 | 25 | func TestVersionCmdShort(t *testing.T) { 26 | tf, o := options.NewFakeArgoRolloutsOptions() 27 | defer tf.Cleanup() 28 | cmd := NewCmdVersion(o) 29 | cmd.PersistentPreRunE = o.PersistentPreRunE 30 | cmd.SetArgs([]string{"--short"}) 31 | err := cmd.Execute() 32 | assert.NoError(t, err) 33 | stdout := o.Out.(*bytes.Buffer).String() 34 | assert.Equal(t, "kubectl-argo-rollouts: v99.99.99+unknown\n", stdout) 35 | } 36 | -------------------------------------------------------------------------------- /pkg/kubectl-argo-rollouts/info/testdata/canary/canary-ref-deploy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: canary-demo-deploy 5 | namespace: jesse-test 6 | spec: 7 | selector: 8 | matchLabels: 9 | app: canary-demo-deploy 10 | replicas: 0 11 | template: 12 | metadata: 13 | labels: 14 | app: canary-demo-deploy 15 | spec: 16 | containers: 17 | - name: canary-demo-deploy 18 | image: argoproj/rollouts-demo:red 19 | ports: 20 | - name: http 21 | containerPort: 8080 22 | protocol: TCP 23 | resources: 24 | requests: 25 | memory: 32Mi 26 | cpu: 5m 27 | -------------------------------------------------------------------------------- /pkg/signals/signal.go: -------------------------------------------------------------------------------- 1 | package signals 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | ) 8 | 9 | var onlyOneSignalHandler = make(chan struct{}) 10 | 11 | // SetupSignalHandlerContext registered for SIGTERM and SIGINT. A context is returned 12 | // which is canceled on one of these signals. If a second signal is caught, the program 13 | // is terminated with exit code 1. 14 | func SetupSignalHandlerContext() (ctx context.Context) { 15 | close(onlyOneSignalHandler) // panics when called twice 16 | 17 | ctx, cancel := context.WithCancel(context.Background()) 18 | c := make(chan os.Signal, 2) 19 | signal.Notify(c, shutdownSignals...) 20 | go func() { 21 | <-c 22 | cancel() 23 | <-c 24 | os.Exit(1) // second signal. Exit directly. 25 | }() 26 | 27 | return ctx 28 | } 29 | -------------------------------------------------------------------------------- /pkg/signals/signal_posix.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | package signals 5 | 6 | import ( 7 | "os" 8 | "syscall" 9 | ) 10 | 11 | var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM} 12 | -------------------------------------------------------------------------------- /pkg/signals/signal_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2017 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package signals 18 | 19 | import ( 20 | "os" 21 | ) 22 | 23 | var shutdownSignals = []os.Signal{os.Interrupt} 24 | -------------------------------------------------------------------------------- /rollout/scale_utils.go: -------------------------------------------------------------------------------- 1 | package rollout 2 | 3 | import ( 4 | "context" 5 | 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | ) 8 | 9 | func (c *rolloutContext) scaleDeployment(targetScale *int32) error { 10 | deploymentName := c.rollout.Spec.WorkloadRef.Name 11 | namespace := c.rollout.Namespace 12 | deployment, err := c.kubeclientset.AppsV1().Deployments(namespace).Get(context.TODO(), deploymentName, metav1.GetOptions{}) 13 | if err != nil { 14 | c.log.Warnf("Failed to fetch deployment %s: %s", deploymentName, err.Error()) 15 | return err 16 | } 17 | 18 | var newReplicasCount int32 19 | if *targetScale < 0 { 20 | newReplicasCount = 0 21 | } else { 22 | newReplicasCount = *targetScale 23 | } 24 | if newReplicasCount == *deployment.Spec.Replicas { 25 | return nil 26 | } 27 | c.log.Infof("Scaling deployment %s to %d replicas", deploymentName, newReplicasCount) 28 | *deployment.Spec.Replicas = newReplicasCount 29 | 30 | _, err = c.kubeclientset.AppsV1().Deployments(namespace).Update(context.TODO(), deployment, metav1.UpdateOptions{}) 31 | if err != nil { 32 | c.log.Warnf("Failed to update deployment %s: %s", deploymentName, err.Error()) 33 | return err 34 | } 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /rollout/steps/plugin/rpc/rpc_test_implementation.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 5 | "github.com/argoproj/argo-rollouts/utils/plugin/types" 6 | ) 7 | 8 | type testRpcPlugin struct{} 9 | 10 | func (p *testRpcPlugin) InitPlugin() types.RpcError { 11 | return types.RpcError{} 12 | } 13 | 14 | func (p *testRpcPlugin) Run(_ *v1alpha1.Rollout, _ *types.RpcStepContext) (types.RpcStepResult, types.RpcError) { 15 | return types.RpcStepResult{}, types.RpcError{} 16 | } 17 | 18 | func (p *testRpcPlugin) Terminate(_ *v1alpha1.Rollout, _ *types.RpcStepContext) (types.RpcStepResult, types.RpcError) { 19 | return types.RpcStepResult{}, types.RpcError{} 20 | } 21 | 22 | func (p *testRpcPlugin) Abort(_ *v1alpha1.Rollout, _ *types.RpcStepContext) (types.RpcStepResult, types.RpcError) { 23 | return types.RpcStepResult{}, types.RpcError{} 24 | } 25 | 26 | // Type returns the type of the step plugin 27 | func (p *testRpcPlugin) Type() string { 28 | return "StepPlugin Test" 29 | } 30 | -------------------------------------------------------------------------------- /server/static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/server/static/.gitkeep -------------------------------------------------------------------------------- /server/static_test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | index-title 5 | 6 | 7 | index-body 8 | 9 | 10 | -------------------------------------------------------------------------------- /server/static_test/test-dir/test.css: -------------------------------------------------------------------------------- 1 | /* empty by intent */ 2 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=argoproj_argo-rollouts 2 | sonar.organization=argoproj 3 | 4 | sonar.sources=. 5 | sonar.sourceEncoding=UTF-8 6 | sonar.host.url=https://sonarcloud.io 7 | 8 | # Exclude following set of patterns from coverage analysis 9 | sonar.coverage.exclusions=**/*.pb.go,**/*.pb.gw.go,**/mocks/**,**/*.ts*,**/vendor/**,**/openapi_generated.go,**/*_test.go,**/*_generated*,test/**,pkg/client/**,pkg/apiclient/** 10 | 11 | # Exclude following set of patterns from code analysis 12 | sonar.go.exclusions=**/vendor/**,*/*.pb.go,**/*_test.go,**/*.pb.gw.go,**/mocks/**,**/openapi_generated.go,**/*_generated*.go 13 | 14 | # Exclude following set of patterns from duplication detection 15 | sonar.cpd.exclusions=**/*.pb.go,**/*.g.cs,**/*.gw.go,**/mocks/* -------------------------------------------------------------------------------- /test/cmd/metrics-plugin-sample/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | goPlugin "github.com/hashicorp/go-plugin" 5 | log "github.com/sirupsen/logrus" 6 | 7 | "github.com/argoproj/argo-rollouts/metricproviders/plugin/rpc" 8 | "github.com/argoproj/argo-rollouts/test/cmd/metrics-plugin-sample/internal/plugin" 9 | ) 10 | 11 | // handshakeConfigs are used to just do a basic handshake between 12 | // a plugin and host. If the handshake fails, a user friendly error is shown. 13 | // This prevents users from executing bad plugins or executing a plugin 14 | // directory. It is a UX feature, not a security feature. 15 | var handshakeConfig = goPlugin.HandshakeConfig{ 16 | ProtocolVersion: 1, 17 | MagicCookieKey: "ARGO_ROLLOUTS_RPC_PLUGIN", 18 | MagicCookieValue: "metricprovider", 19 | } 20 | 21 | func main() { 22 | logCtx := *log.WithFields(log.Fields{"plugin": "prometheus"}) 23 | 24 | rpcPluginImp := &plugin.RpcPlugin{ 25 | LogCtx: logCtx, 26 | } 27 | // pluginMap is the map of plugins we can dispense. 28 | var pluginMap = map[string]goPlugin.Plugin{ 29 | "RpcMetricProviderPlugin": &rpc.RpcMetricProviderPlugin{Impl: rpcPluginImp}, 30 | } 31 | 32 | logCtx.Debug("message from plugin", "foo", "bar") 33 | 34 | goPlugin.Serve(&goPlugin.ServeConfig{ 35 | HandshakeConfig: handshakeConfig, 36 | Plugins: pluginMap, 37 | }) 38 | } 39 | -------------------------------------------------------------------------------- /test/e2e/crds/README.md: -------------------------------------------------------------------------------- 1 | # E2E CRDs 2 | 3 | This directory contains CRDs to install for e2e testing -------------------------------------------------------------------------------- /test/e2e/expectedfailures/analysis-run-failfast.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisRun 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | generateName: analysis-run-failfast- 5 | spec: 6 | metrics: 7 | - name: sleep-infinity 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | command: [sleep, 365d] 17 | restartPolicy: Never 18 | backoffLimit: 0 19 | - name: fail-after-30 20 | provider: 21 | job: 22 | spec: 23 | template: 24 | spec: 25 | containers: 26 | - name: sleep 27 | image: alpine:3.8 28 | command: [sh, -c, "sleep 30; exit 1"] 29 | restartPolicy: Never 30 | backoffLimit: 0 31 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/analysis-run-prometheus-bad-server.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisRun 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | generateName: analysis-run-prometheus-bad-server- 5 | spec: 6 | metrics: 7 | - name: success-rate 8 | provider: 9 | prometheus: 10 | address: http://prometheus-operator-prometheus.prometheus-operator:9090 11 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/experiment-degrade.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | generateName: experiment-degrade- 5 | spec: 6 | progressDeadlineSeconds: 30 7 | templates: 8 | - name: image-does-not-exist 9 | selector: 10 | matchLabels: 11 | app: rollouts-demo 12 | template: 13 | metadata: 14 | labels: 15 | app: rollouts-demo 16 | spec: 17 | containers: 18 | - name: rollouts-demo 19 | image: argoproj/rollouts-demo:does-not-exist 20 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/experiment-error-template-missing.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | name: experiment-error-template-missing 5 | spec: 6 | templates: 7 | - name: baseline 8 | selector: 9 | matchLabels: 10 | app: rollouts-demo 11 | template: 12 | metadata: 13 | labels: 14 | app: rollouts-demo 15 | spec: 16 | containers: 17 | - name: rollouts-demo 18 | image: argoproj/rollouts-demo:blue 19 | analyses: 20 | - name: does-not-exist 21 | templateName: does-not-exist 22 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/experiment-failed-analysis.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: bad-job 5 | spec: 6 | metrics: 7 | - name: test 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | command: [exit, "1"] 17 | restartPolicy: Never 18 | backoffLimit: 0 19 | 20 | --- 21 | apiVersion: argoproj.io/v1alpha1 22 | kind: Experiment 23 | metadata: 24 | name: experiment-failed-analysis 25 | spec: 26 | templates: 27 | - name: baseline 28 | selector: 29 | matchLabels: 30 | app: rollouts-demo 31 | template: 32 | metadata: 33 | labels: 34 | app: rollouts-demo 35 | spec: 36 | containers: 37 | - name: rollouts-demo 38 | image: argoproj/rollouts-demo:blue 39 | analyses: 40 | - name: bad-job 41 | templateName: bad-job 42 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/experiment-invalid.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | generateName: experiment-invalid-spec- 5 | spec: 6 | duration: 30s 7 | progressDeadlineSeconds: 30 8 | templates: 9 | - name: baseline 10 | selector: 11 | matchLabels: 12 | app: rollouts-demo 13 | template: 14 | metadata: 15 | labels: 16 | app: rollouts-demo 17 | spec: 18 | containers: 19 | - name: rollouts-demo 20 | image: argoproj/rollouts-demo:blue 21 | - name: invalid-spec 22 | selector: 23 | matchLabels: 24 | app: rollouts-demo 25 | template: 26 | metadata: 27 | labels: 28 | app: rollouts-demo 29 | # missing spec here -------------------------------------------------------------------------------- /test/e2e/expectedfailures/malformed-analysisrun.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisRun 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: malformed-analysis 5 | spec: 6 | metrics: 7 | - name: test 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | resources: 17 | requests: 18 | memory: invalid # invalid 19 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/malformed-analysistemplate.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: malformed-analysis 5 | spec: 6 | metrics: 7 | - name: test 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | resources: 17 | requests: 18 | memory: invalid # invalid 19 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/malformed-clusteranalysistemplate.yaml: -------------------------------------------------------------------------------- 1 | kind: ClusterAnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: malformed-analysis 5 | spec: 6 | metrics: 7 | - name: test 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | resources: 17 | requests: 18 | memory: invalid # invalid 19 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/malformed-experiment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | name: malformed-experiment 5 | spec: 6 | duration: 30s 7 | templates: 8 | - name: canary 9 | selector: 10 | matchLabels: 11 | app: rollouts-demo 12 | template: 13 | metadata: 14 | labels: 15 | app: rollouts-demo 16 | spec: 17 | containers: 18 | - name: rollouts-demo 19 | image: argoproj/rollouts-demo:yellow 20 | resources: 21 | requests: 22 | memory: invalid # invalid 23 | -------------------------------------------------------------------------------- /test/e2e/expectedfailures/malformed-rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: malformed-rollout 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: malformed-rollout 9 | template: 10 | metadata: 11 | labels: 12 | app: malformed-rollout 13 | spec: 14 | containers: 15 | - name: malformed-rollout 16 | image: argoproj/rollouts-demo:blue 17 | resources: 18 | requests: 19 | memory: invalid # invalid 20 | strategy: 21 | canary: {} 22 | -------------------------------------------------------------------------------- /test/e2e/functional/analysis-run-job.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisRun 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | generateName: analysis-run-job- 5 | spec: 6 | metrics: 7 | - name: test 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: nginx:1.19-alpine 16 | command: [sleep, "30"] 17 | restartPolicy: Never 18 | backoffLimit: 0 19 | -------------------------------------------------------------------------------- /test/e2e/functional/analysistemplate-multiple-job.yaml: -------------------------------------------------------------------------------- 1 | # AnalysisTemplate which sleeps for a specified duration and exits with a specified exit-code 2 | kind: AnalysisTemplate 3 | apiVersion: argoproj.io/v1alpha1 4 | metadata: 5 | name: multiple-job 6 | spec: 7 | args: 8 | - name: duration 9 | value: 0s 10 | - name: exit-code 11 | value: "0" 12 | - name: count 13 | value: "1" 14 | metrics: 15 | - name: sleep-job 16 | initialDelay: 10s 17 | count: 1 18 | provider: 19 | job: 20 | spec: 21 | template: 22 | spec: 23 | containers: 24 | - name: sleep-job 25 | image: nginx:1.19-alpine 26 | command: [sh, -c, -x] 27 | args: ["sleep {{args.duration}} && exit {{args.exit-code}}"] 28 | restartPolicy: Never 29 | backoffLimit: 0 30 | - name: sleep-job-rep 31 | count: 1 32 | provider: 33 | job: 34 | spec: 35 | template: 36 | spec: 37 | containers: 38 | - name: sleep-job 39 | image: nginx:1.19-alpine 40 | command: [sh, -c, -x] 41 | args: ["sleep {{args.duration}} && exit {{args.exit-code}}"] 42 | restartPolicy: Never 43 | backoffLimit: 0 -------------------------------------------------------------------------------- /test/e2e/functional/analysistemplate-sleep-job.yaml: -------------------------------------------------------------------------------- 1 | # AnalysisTemplate which sleeps for a specified duration and exits with a specified exit-code 2 | kind: AnalysisTemplate 3 | apiVersion: argoproj.io/v1alpha1 4 | metadata: 5 | name: sleep-job 6 | spec: 7 | args: 8 | - name: duration 9 | value: "0" 10 | - name: exit-code 11 | value: "0" 12 | - name: count 13 | value: "1" 14 | - name: rev 15 | value: "notset" 16 | metrics: 17 | - name: sleep-job 18 | count: "{{args.count}}" 19 | provider: 20 | job: 21 | spec: 22 | template: 23 | metadata: 24 | labels: 25 | rev: "{{ args.rev }}" 26 | spec: 27 | containers: 28 | - name: sleep-job 29 | image: nginx:1.19-alpine 30 | command: [sh, -c, -x] 31 | args: ["sleep {{args.duration}} && exit {{args.exit-code}}"] 32 | restartPolicy: Never 33 | backoffLimit: 0 -------------------------------------------------------------------------------- /test/e2e/functional/analysistemplate-web-background-inconclusive.yaml: -------------------------------------------------------------------------------- 1 | # A web metric which uses the httpbin service as a metric provider 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: AnalysisTemplate 4 | metadata: 5 | name: web-background-inconclusive 6 | spec: 7 | args: 8 | - name: url-val 9 | value: "https://httpbin.org/anything" 10 | metrics: 11 | - name: web 12 | interval: 7s 13 | inconclusiveLimit: 3 14 | successCondition: result.status == "success" 15 | failureCondition: result.status == "failure" 16 | provider: 17 | web: 18 | url: "{{args.url-val}}" 19 | method: POST 20 | jsonBody: 21 | status: "inconclusive" 22 | jsonPath: "{$.json}" 23 | -------------------------------------------------------------------------------- /test/e2e/functional/analysistemplate-web-background.yaml: -------------------------------------------------------------------------------- 1 | # A dummy web metric which uses the kubernetes version endpoint as a metric provider 2 | apiVersion: argoproj.io/v1alpha1 3 | kind: AnalysisTemplate 4 | metadata: 5 | name: web-background 6 | spec: 7 | args: 8 | - name: url-val 9 | value: "https://kubernetes.default.svc/version" 10 | metrics: 11 | - name: web 12 | interval: 5s 13 | successCondition: result.major == '1' 14 | provider: 15 | web: 16 | url: "{{args.url-val}}" 17 | insecure: true 18 | -------------------------------------------------------------------------------- /test/e2e/functional/ephemeral-labels.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: ephemeral-label 5 | spec: 6 | replicas: 2 7 | strategy: 8 | canary: 9 | stableMetadata: 10 | labels: 11 | role: stable 12 | canaryMetadata: 13 | labels: 14 | role: canary 15 | steps: 16 | - setWeight: 50 17 | - pause: {} 18 | revisionHistoryLimit: 2 19 | selector: 20 | matchLabels: 21 | app: ephemeral-label 22 | template: 23 | metadata: 24 | labels: 25 | app: ephemeral-label 26 | spec: 27 | containers: 28 | - name: ephemeral-label 29 | image: argoproj/rollouts-demo:blue 30 | ports: 31 | - name: http 32 | containerPort: 8080 33 | protocol: TCP 34 | resources: 35 | requests: 36 | memory: 32Mi 37 | cpu: 5m 38 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-basic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | generateName: experiment-basic- 5 | spec: 6 | duration: 30s 7 | templates: 8 | - name: baseline 9 | selector: 10 | matchLabels: 11 | app: rollouts-demo 12 | template: 13 | metadata: 14 | labels: 15 | app: rollouts-demo 16 | spec: 17 | containers: 18 | - name: rollouts-demo 19 | image: argoproj/rollouts-demo:blue 20 | - name: canary 21 | selector: 22 | matchLabels: 23 | app: rollouts-demo 24 | template: 25 | metadata: 26 | labels: 27 | app: rollouts-demo 28 | spec: 29 | containers: 30 | - name: rollouts-demo 31 | image: argoproj/rollouts-demo:yellow 32 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-dry-run-analysis.yaml: -------------------------------------------------------------------------------- 1 | kind: AnalysisTemplate 2 | apiVersion: argoproj.io/v1alpha1 3 | metadata: 4 | name: dry-run-job 5 | spec: 6 | metrics: 7 | - name: test-1 8 | provider: 9 | job: 10 | spec: 11 | template: 12 | spec: 13 | containers: 14 | - name: sleep 15 | image: alpine:3.8 16 | command: [exit, "1"] 17 | restartPolicy: Never 18 | backoffLimit: 0 19 | 20 | --- 21 | apiVersion: argoproj.io/v1alpha1 22 | kind: Experiment 23 | metadata: 24 | name: experiment-with-dry-run 25 | spec: 26 | duration: 30s 27 | progressDeadlineSeconds: 30 28 | templates: 29 | - name: baseline 30 | replicas: 1 31 | service: {} 32 | selector: 33 | matchLabels: 34 | app: experiment-with-dry-run 35 | template: 36 | metadata: 37 | labels: 38 | app: experiment-with-dry-run 39 | spec: 40 | containers: 41 | - name: experiment-with-dry-run 42 | image: nginx:1.19-alpine 43 | resources: 44 | requests: 45 | memory: 16Mi 46 | cpu: 1m 47 | analyses: 48 | - name: dry-run-job 49 | templateName: dry-run-job 50 | dryRun: 51 | - metricName: test.* 52 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-same-template.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | generateName: experiment-same-template- 5 | spec: 6 | duration: 30s 7 | progressDeadlineSeconds: 30 8 | templates: 9 | - name: first 10 | selector: 11 | matchLabels: 12 | app: rollouts-demo 13 | template: 14 | metadata: 15 | labels: 16 | app: rollouts-demo 17 | spec: 18 | containers: 19 | - name: rollouts-demo 20 | image: argoproj/rollouts-demo:blue 21 | - name: second 22 | selector: 23 | matchLabels: 24 | app: rollouts-demo 25 | template: 26 | metadata: 27 | labels: 28 | app: rollouts-demo 29 | spec: 30 | containers: 31 | - name: rollouts-demo 32 | image: argoproj/rollouts-demo:blue 33 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-with-multiport-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | name: experiment-with-multiport-service 5 | spec: 6 | duration: 10s 7 | scaleDownDelaySeconds: 5 8 | # List of pod template specs to run in the experiment as ReplicaSets 9 | templates: 10 | - name: test 11 | replicas: 1 12 | service: {} 13 | selector: 14 | matchLabels: 15 | app: experiment-with-multiport-service 16 | template: 17 | metadata: 18 | labels: 19 | app: experiment-with-multiport-service 20 | spec: 21 | containers: 22 | - name: experiment-with-multiport-service 23 | image: nginx:1.19-alpine 24 | resources: 25 | requests: 26 | memory: 16Mi 27 | cpu: 1m 28 | ports: 29 | - name: testport1 30 | protocol: TCP 31 | containerPort: 8080 32 | - name: testport2 33 | protocol: TCP 34 | containerPort: 8081 35 | - name: testport3 36 | protocol: TCP 37 | containerPort: 8082 38 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-with-service-name.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | name: experiment-with-service-name 5 | spec: 6 | duration: 10s 7 | scaleDownDelaySeconds: 5 8 | # List of pod template specs to run in the experiment as ReplicaSets 9 | templates: 10 | - name: test 11 | replicas: 1 12 | service: 13 | name: test-service-name 14 | selector: 15 | matchLabels: 16 | app: experiment-with-service-name 17 | template: 18 | metadata: 19 | labels: 20 | app: experiment-with-service-name 21 | spec: 22 | containers: 23 | - name: experiment-with-service-name 24 | image: nginx:1.19-alpine 25 | resources: 26 | requests: 27 | memory: 16Mi 28 | cpu: 1m 29 | ports: 30 | - protocol: TCP 31 | containerPort: 8080 32 | -------------------------------------------------------------------------------- /test/e2e/functional/experiment-with-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Experiment 3 | metadata: 4 | name: experiment-with-service 5 | spec: 6 | duration: 10s 7 | scaleDownDelaySeconds: 5 8 | # List of pod template specs to run in the experiment as ReplicaSets 9 | templates: 10 | - name: test 11 | replicas: 1 12 | service: {} 13 | selector: 14 | matchLabels: 15 | app: experiment-with-service 16 | template: 17 | metadata: 18 | labels: 19 | app: experiment-with-service 20 | spec: 21 | containers: 22 | - name: experiment-with-service 23 | image: nginx:1.19-alpine 24 | resources: 25 | requests: 26 | memory: 16Mi 27 | cpu: 1m 28 | ports: 29 | - protocol: TCP 30 | containerPort: 8080 31 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-background-analysis-inconclusive.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-background-analysis-inconclusive 5 | spec: 6 | replicas: 2 7 | strategy: 8 | canary: 9 | steps: 10 | - setWeight: 10 11 | - pause: { duration: 30s } 12 | analysis: 13 | templates: 14 | - templateName: web-background-inconclusive 15 | startingStep: 1 16 | selector: 17 | matchLabels: 18 | app: rollout-background-analysis-inconclusive 19 | template: 20 | metadata: 21 | labels: 22 | app: rollout-background-analysis-inconclusive 23 | spec: 24 | containers: 25 | - name: rollouts-demo 26 | image: nginx:1.19-alpine 27 | resources: 28 | requests: 29 | memory: 16Mi 30 | cpu: 5m 31 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-background-analysis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-background-analysis 5 | spec: 6 | strategy: 7 | canary: 8 | analysis: 9 | templates: 10 | - templateName: web-background 11 | steps: 12 | - setWeight: 10 13 | - pause: {} 14 | selector: 15 | matchLabels: 16 | app: rollout-background-analysis 17 | template: 18 | metadata: 19 | labels: 20 | app: rollout-background-analysis 21 | spec: 22 | containers: 23 | - name: rollouts-demo 24 | image: nginx:1.19-alpine 25 | resources: 26 | requests: 27 | memory: 16Mi 28 | cpu: 5m 29 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-basic.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: basic 5 | spec: 6 | strategy: 7 | canary: 8 | steps: 9 | - setWeight: 50 10 | - pause: {} 11 | selector: 12 | matchLabels: 13 | app: basic 14 | template: 15 | metadata: 16 | labels: 17 | app: basic 18 | spec: 19 | containers: 20 | - name: basic 21 | image: nginx:1.19-alpine 22 | resources: 23 | requests: 24 | memory: 16Mi 25 | cpu: 1m 26 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-bluegreen.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: bluegreen 5 | spec: 6 | ports: 7 | - port: 80 8 | targetPort: http 9 | protocol: TCP 10 | name: http 11 | selector: 12 | app: bluegreen 13 | --- 14 | apiVersion: argoproj.io/v1alpha1 15 | kind: Rollout 16 | metadata: 17 | name: bluegreen 18 | spec: 19 | replicas: 3 20 | strategy: 21 | blueGreen: 22 | activeService: bluegreen 23 | scaleDownDelaySeconds: 10 24 | selector: 25 | matchLabels: 26 | app: bluegreen 27 | template: 28 | metadata: 29 | labels: 30 | app: bluegreen 31 | spec: 32 | containers: 33 | - name: bluegreen 34 | image: nginx:1.19-alpine 35 | resources: 36 | requests: 37 | memory: 16Mi 38 | cpu: 1m 39 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-degraded-inline-multiple-analysis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-inline-analysis 5 | spec: 6 | strategy: 7 | canary: 8 | steps: 9 | - setWeight: 10 10 | - pause: {} 11 | - analysis: 12 | templates: 13 | - templateName: multiple-job-fail 14 | selector: 15 | matchLabels: 16 | app: rollout-inline-analysis 17 | template: 18 | metadata: 19 | labels: 20 | app: rollout-inline-analysis 21 | spec: 22 | containers: 23 | - name: rollouts-demo 24 | image: nginx:1.19-alpine 25 | resources: 26 | requests: 27 | memory: 16Mi 28 | cpu: 5m 29 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-inline-analysis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-inline-analysis 5 | spec: 6 | strategy: 7 | canary: 8 | steps: 9 | - setWeight: 10 10 | - analysis: 11 | templates: 12 | - templateName: sleep-job 13 | - pause: {} 14 | - analysis: 15 | templates: 16 | - templateName: sleep-job 17 | - analysis: 18 | templates: 19 | - templateName: sleep-job 20 | selector: 21 | matchLabels: 22 | app: rollout-inline-analysis 23 | template: 24 | metadata: 25 | labels: 26 | app: rollout-inline-analysis 27 | spec: 28 | containers: 29 | - name: rollouts-demo 30 | image: nginx:1.19-alpine 31 | resources: 32 | requests: 33 | memory: 16Mi 34 | cpu: 5m 35 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-inline-multiple-analysis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: rollout-inline-analysis 5 | spec: 6 | strategy: 7 | canary: 8 | steps: 9 | - setWeight: 10 10 | - pause: {} 11 | - analysis: 12 | templates: 13 | - templateName: multiple-job 14 | selector: 15 | matchLabels: 16 | app: rollout-inline-analysis 17 | template: 18 | metadata: 19 | labels: 20 | app: rollout-inline-analysis 21 | spec: 22 | containers: 23 | - name: rollouts-demo 24 | image: nginx:1.19-alpine 25 | resources: 26 | requests: 27 | memory: 16Mi 28 | cpu: 5m 29 | -------------------------------------------------------------------------------- /test/e2e/functional/rollout-rollback-window.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: window 5 | spec: 6 | replicas: 2 7 | rollbackWindow: 8 | revisions: 2 9 | strategy: 10 | canary: 11 | steps: 12 | - setWeight: 50 13 | - analysis: 14 | templates: 15 | - templateName: sleep-job 16 | args: 17 | - name: rev 18 | valueFrom: 19 | fieldRef: 20 | fieldPath: metadata.labels['rev'] 21 | selector: 22 | matchLabels: 23 | app: window 24 | template: 25 | metadata: 26 | labels: 27 | app: window 28 | rev: initial 29 | spec: 30 | containers: 31 | - name: basic 32 | image: nginx:1.19-alpine 33 | resources: 34 | requests: 35 | memory: 16Mi 36 | cpu: 1m -------------------------------------------------------------------------------- /test/e2e/istio/load-test-job.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: batch/v1 2 | kind: Job 3 | metadata: 4 | name: load-test 5 | spec: 6 | backoffLimit: 0 7 | template: 8 | metadata: 9 | annotations: 10 | sidecar.istio.io/inject: "false" 11 | spec: 12 | restartPolicy: Never 13 | containers: 14 | - name: load-tester 15 | image: argoproj/load-tester:latest 16 | command: [sh, -c, -x, -e] 17 | args: 18 | - | 19 | wrk -t11 -c40 -d300s -s report.lua -H "Host: istio-rollout.local" http://istio-ingressgateway.istio-system/color 20 | jq -e '.errors_ratio == 0' report.json 21 | 22 | -------------------------------------------------------------------------------- /test/e2e/step-plugin/argo-rollouts-config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | labels: 5 | app.kubernetes.io/component: rollouts-controller 6 | app.kubernetes.io/name: argo-rollouts 7 | app.kubernetes.io/part-of: argo-rollouts 8 | name: argo-rollouts-config 9 | namespace: argo-rollouts 10 | data: 11 | stepPlugins: |- 12 | - name: "step/e2e-test" # name of the plugin, it must match the name required by the plugin so it can find it's configuration 13 | location: "file://plugin-bin/e2e-step-plugin" # supports http(s):// urls and file:// 14 | disabled: false 15 | - name: "step/e2e-test-disabled" # name of the plugin, it must match the name required by the plugin so it can find it's configuration 16 | location: "file://plugin-bin/e2e-step-plugin" # supports http(s):// urls and file:// 17 | disabled: true 18 | -------------------------------------------------------------------------------- /test/e2e/step-plugin/disabled-rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: disabled-step-plugin 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: disabled-step-plugin 9 | strategy: 10 | canary: 11 | steps: 12 | - plugin: 13 | name: $$disabled_plugin$$ 14 | config: 15 | return: Successful 16 | - plugin: 17 | name: step/e2e-test 18 | config: 19 | return: Successful 20 | template: 21 | metadata: 22 | labels: 23 | app: disabled-step-plugin 24 | spec: 25 | containers: 26 | - name: basic 27 | image: nginx:1.19-alpine 28 | resources: 29 | requests: 30 | memory: 16Mi 31 | cpu: 1m 32 | -------------------------------------------------------------------------------- /test/e2e/step-plugin/invalid-rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: invalid-step-plugin 5 | spec: 6 | progressDeadlineSeconds: 15 7 | selector: 8 | matchLabels: 9 | app: invalid-step-plugin 10 | strategy: 11 | canary: 12 | steps: 13 | - plugin: 14 | name: step/e2e-test-invalid-name 15 | template: 16 | metadata: 17 | labels: 18 | app: invalid-step-plugin 19 | spec: 20 | containers: 21 | - name: basic 22 | image: nginx:1.19-alpine 23 | resources: 24 | requests: 25 | memory: 16Mi 26 | cpu: 1m 27 | -------------------------------------------------------------------------------- /test/e2e/step-plugin/template-rollout.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: argoproj.io/v1alpha1 2 | kind: Rollout 3 | metadata: 4 | name: step-plugin-e2e-$$name$$ 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: step-plugin-e2e-$$name$$ 9 | strategy: 10 | canary: 11 | steps: 12 | - plugin: 13 | name: step/e2e-test 14 | config: 15 | return: Successful 16 | - plugin: 17 | name: step/e2e-test 18 | config: 19 | return: $$phase$$ 20 | requeue: 17s 21 | template: 22 | metadata: 23 | labels: 24 | app: step-plugin-e2e-$$name$$ 25 | spec: 26 | containers: 27 | - name: basic 28 | image: nginx:1.19-alpine 29 | resources: 30 | requests: 31 | memory: 16Mi 32 | cpu: 1m 33 | -------------------------------------------------------------------------------- /test/kustomize/rollout/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | 4 | configurations: 5 | - ../../../docs/features/kustomize/rollout-transform.yaml 6 | 7 | namePrefix: my- 8 | 9 | resources: 10 | - rollout.yaml 11 | 12 | configMapGenerator: 13 | - name: guestbook-cm 14 | literals: 15 | - FOO=BAR 16 | 17 | secretGenerator: 18 | - name: guestbook-secret 19 | literals: 20 | - password=Pa55w0rd 21 | 22 | commonLabels: 23 | foo: bar 24 | 25 | commonAnnotations: 26 | foo: bar 27 | 28 | vars: 29 | - name: SERVICE_NAME 30 | objref: 31 | kind: Service 32 | name: guestbook-stable-svc 33 | apiVersion: v1 34 | fieldref: 35 | fieldpath: metadata.name 36 | 37 | replicas: 38 | - name: guestbook 39 | count: 3 40 | 41 | images: 42 | - name: guestbook 43 | newTag: v2 44 | 45 | openapi: 46 | path: https://raw.githubusercontent.com/argoproj/argo-schema-generator/main/schema/argo_all_k8s_kustomize_schema.json 47 | 48 | patches: 49 | - patch: |- 50 | apiVersion: argoproj.io/v1alpha1 51 | kind: Rollout 52 | metadata: 53 | name: guestbook 54 | spec: 55 | template: 56 | spec: 57 | containers: 58 | - name: guestbook 59 | image: guestbook-patched:v1 60 | -------------------------------------------------------------------------------- /test/kustomize/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 4 | 5 | for i in `ls ${DIR}/*/kustomization.yaml`; do 6 | dir_name=$(dirname $i) 7 | diff_out=$(diff ${dir_name}/expected.yaml <(kustomize build ${dir_name} --load-restrictor=LoadRestrictionsNone)) 8 | if [[ $? -ne 0 ]]; then 9 | echo "${i} had unexpected diff:" 10 | echo "${diff_out}" 11 | exit 1 12 | fi 13 | done -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /ui/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "bracketSpacing": false, 3 | "jsxSingleQuote": true, 4 | "printWidth": 180, 5 | "singleQuote": true, 6 | "tabWidth": 4, 7 | "jsxBracketSameLine": true, 8 | "quoteProps": "consistent" 9 | } 10 | -------------------------------------------------------------------------------- /ui/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/src'], 3 | testMatch: ['**/?(*.)+(spec|test).+(ts|tsx|js)'], 4 | transform: { 5 | '^.+\\.(ts|tsx)$': 'ts-jest', 6 | }, 7 | modulePathIgnorePatterns: ['generated'], 8 | }; 9 | -------------------------------------------------------------------------------- /ui/src/app/App.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | $argo-icon-fonts-root: 'assets/fonts/'; 4 | $fa-font-path: 'assets/fonts'; 5 | 6 | @import 'node_modules/argo-ui/src/styles/argo-icon'; 7 | 8 | @import 'node_modules/@fortawesome/fontawesome-free/scss/fontawesome'; 9 | @import 'node_modules/@fortawesome/fontawesome-free/scss/solid'; 10 | @import 'node_modules/@fortawesome/fontawesome-free/scss/brands'; 11 | 12 | body, 13 | html { 14 | padding: 0; 15 | margin: 0; 16 | height: 100%; 17 | button, 18 | form, 19 | input, 20 | textarea { 21 | appearance: none; 22 | -webkit-appearance: none; 23 | color: inherit; 24 | font: inherit; 25 | margin: 0; 26 | } 27 | } 28 | 29 | a { 30 | color: inherit !important; 31 | text-decoration: none; 32 | } 33 | 34 | #root { 35 | height: 100%; 36 | } 37 | 38 | .rollouts { 39 | height: 100%; 40 | overflow-y: auto; 41 | font-family: system-ui, sans-serif; 42 | background-color: $argo-color-gray-3; 43 | 44 | &--dark { 45 | background-color: $midnight-sky; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/constants.ts: -------------------------------------------------------------------------------- 1 | import {AnalysisStatus, FunctionalStatus} from './types'; 2 | 3 | export const METRIC_FAILURE_LIMIT_DEFAULT = 0; 4 | export const METRIC_INCONCLUSIVE_LIMIT_DEFAULT = 0; 5 | export const METRIC_CONSECUTIVE_ERROR_LIMIT_DEFAULT = 4; 6 | export const METRIC_CONSECUTIVE_SUCCESS_LIMIT_DEFAULT = 0; 7 | 8 | export const ANALYSIS_STATUS_THEME_MAP: {[key in AnalysisStatus]: string} = { 9 | Successful: FunctionalStatus.SUCCESS, 10 | Error: FunctionalStatus.WARNING, 11 | Failed: FunctionalStatus.ERROR, 12 | Running: FunctionalStatus.IN_PROGRESS, 13 | Pending: FunctionalStatus.INACTIVE, 14 | Inconclusive: FunctionalStatus.WARNING, 15 | Unknown: FunctionalStatus.INACTIVE, // added by frontend 16 | }; 17 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/criteria-list/criteria-list.scss: -------------------------------------------------------------------------------- 1 | @import '../theme/theme.scss'; 2 | 3 | .criteria-list { 4 | margin: 0; 5 | padding-left: 0; 6 | list-style-type: none; 7 | } 8 | 9 | .icon-pass { 10 | color: $success-foreground; 11 | } 12 | 13 | .icon-fail { 14 | color: $error-foreground; 15 | } 16 | 17 | .icon-pending { 18 | color: $in-progress-foreground; 19 | } 20 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/header/header.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | font-size: 14px; 3 | } 4 | 5 | h4.title { 6 | margin: 0; // antd override 7 | } 8 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/metric-label/metric-label.scss: -------------------------------------------------------------------------------- 1 | .metric-label { 2 | display: block; 3 | width: 140px; 4 | min-width: 140px; 5 | text-align: left; 6 | white-space: nowrap; 7 | overflow: hidden; 8 | text-overflow: ellipsis; 9 | } 10 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/metric-label/metric-label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import {Space} from 'antd'; 4 | 5 | import {AnalysisStatus, FunctionalStatus} from '../types'; 6 | import StatusIndicator from '../status-indicator/status-indicator'; 7 | 8 | import classNames from 'classnames/bind'; 9 | import './metric-label.scss'; 10 | 11 | const cx = classNames; 12 | 13 | interface AnalysisModalProps { 14 | label: string; 15 | status: AnalysisStatus; 16 | substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; 17 | } 18 | 19 | const MetricLabel = ({label, status, substatus}: AnalysisModalProps) => ( 20 | 21 | 22 | 23 | {label} 24 | 25 | 26 | ); 27 | 28 | export default MetricLabel; 29 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/metric-table/metric-table.scss: -------------------------------------------------------------------------------- 1 | @import '../theme/theme.scss'; 2 | 3 | .metric-table { 4 | border: 1px solid $gray-4; 5 | } 6 | 7 | .error-message { 8 | font-style: italic; 9 | } 10 | 11 | .condition { 12 | display: flex; 13 | align-items: center; 14 | justify-content: flex-end; 15 | margin-top: $space-unit; 16 | font-size: 12px; 17 | 18 | &::before { 19 | content: ''; 20 | display: block; 21 | height: 2px; 22 | width: 12px; 23 | margin-right: $space-unit; 24 | } 25 | 26 | &.is-ERROR::before { 27 | background: $error-foreground; 28 | } 29 | &.is-SUCCESS::before { 30 | background: $success-foreground; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/panels/index.tsx: -------------------------------------------------------------------------------- 1 | export {default as MetricPanel} from './metric-panel'; 2 | export {default as SummaryPanel} from './summary-panel'; 3 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/panels/styles.scss: -------------------------------------------------------------------------------- 1 | @import '../theme/theme.scss'; 2 | 3 | // Analysis Panel 4 | 5 | .analysis-header { 6 | margin: $space-unit 0 (3 * $space-unit); 7 | } 8 | 9 | .label { 10 | display: block; 11 | } 12 | 13 | // Metric Panel 14 | 15 | .metric-header { 16 | display: flex; 17 | align-items: center; 18 | justify-content: space-between; 19 | margin: $space-unit 0; 20 | } 21 | 22 | .legend { 23 | display: flex; 24 | justify-content: flex-end; 25 | } 26 | 27 | h5.section-title { 28 | margin-bottom: 0; // antd override 29 | } 30 | 31 | .query-header { 32 | display: flex; 33 | align-items: center; 34 | justify-content: space-between; 35 | margin-bottom: $space-unit; 36 | } 37 | 38 | .query-box { 39 | :not(:last-child) { 40 | margin-bottom: $space-small; 41 | } 42 | 43 | :last-child { 44 | margin-bottom: $space-large; 45 | } 46 | } 47 | 48 | // Common 49 | 50 | .summary-section, 51 | .metric-section { 52 | margin-bottom: 3 * $space-unit; 53 | 54 | &.medium-space { 55 | margin-bottom: $space-medium; 56 | } 57 | 58 | &.top-content { 59 | margin-top: $space-unit; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/query-box/query-box.scss: -------------------------------------------------------------------------------- 1 | @import '../theme/theme.scss'; 2 | 3 | .query-box { 4 | position: relative; 5 | padding: $space-small 48px $space-small $space-small; 6 | background-color: $gray-2; 7 | border: 1px solid $gray-4; 8 | max-height: 70px; 9 | overflow: hidden; 10 | transition: max-height 0.3s ease-in-out; 11 | 12 | &.can-expand { 13 | cursor: pointer; 14 | } 15 | 16 | .query { 17 | display: -webkit-box; 18 | -webkit-box-orient: vertical; 19 | overflow: auto hidden; 20 | white-space: pre-wrap; 21 | word-wrap: break-word; 22 | text-overflow: ellipsis; 23 | line-clamp: 3; 24 | -webkit-line-clamp: 3; 25 | font-family: $font-family-mono; 26 | font-size: 12px; 27 | margin-bottom: 0; 28 | } 29 | 30 | &.is-expanded { 31 | max-height: 500px; 32 | 33 | .query { 34 | line-clamp: initial; 35 | -webkit-line-clamp: initial; 36 | } 37 | } 38 | } 39 | 40 | .query-copy-button { 41 | position: absolute; 42 | font-size: 18px; 43 | line-height: 1; 44 | top: 8px; 45 | right: 6px; 46 | 47 | svg { 48 | color: $ant-primary; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/status-indicator/status-indicator.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import {AnalysisStatus, FunctionalStatus} from '../types'; 4 | import {ANALYSIS_STATUS_THEME_MAP} from '../constants'; 5 | 6 | import classNames from 'classnames'; 7 | import './status-indicator.scss'; 8 | 9 | const cx = classNames; 10 | 11 | interface StatusIndicatorProps { 12 | children?: React.ReactNode; 13 | className?: string[] | string; 14 | size?: 'small' | 'large'; 15 | status: AnalysisStatus; 16 | substatus?: FunctionalStatus.ERROR | FunctionalStatus.WARNING; 17 | } 18 | 19 | const StatusIndicator = ({children, className, size = 'large', status, substatus}: StatusIndicatorProps) => ( 20 |
21 |
{children}
22 | {substatus !== undefined &&
} 23 |
24 | ); 25 | 26 | export default StatusIndicator; 27 | -------------------------------------------------------------------------------- /ui/src/app/components/analysis-modal/styles.scss: -------------------------------------------------------------------------------- 1 | .tabs { 2 | min-height: 550px; 3 | margin-top: 16px !important; // antd override 4 | 5 | .ant-tabs-tab { 6 | padding-left: 0 !important; // antd override 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ui/src/app/components/ellipsis-middle/ellipsis-middle.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Typography} from 'antd'; 3 | 4 | const {Text} = Typography; 5 | export const EllipsisMiddle: React.FC<{suffixCount: number; children: string; style: React.CSSProperties}> = ({suffixCount, children, style}) => { 6 | const start = children.slice(0, children.length - suffixCount).trim(); 7 | const suffix = children.slice(-suffixCount).trim(); 8 | return ( 9 | 10 | {start} 11 | 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /ui/src/app/components/header/header.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .rollouts-header { 4 | display: flex; 5 | background: #0f2733; 6 | color: white; 7 | align-items: center; 8 | padding: 10px 0; 9 | 10 | &__brand { 11 | display: flex; 12 | align-items: center; 13 | text-decoration: none; 14 | -webkit-user-select: none; 15 | -moz-user-select: none; 16 | -ms-user-select: none; 17 | user-select: none; 18 | margin-left: 10px; 19 | } 20 | 21 | &__title { 22 | display: flex; 23 | align-items: center; 24 | color: white !important; 25 | font-weight: 600; 26 | font-size: 22px; 27 | width: 200px; 28 | } 29 | 30 | &__info { 31 | margin-left: auto; 32 | display: flex; 33 | align-items: center; 34 | } 35 | &__label { 36 | color: $shine; 37 | margin: auto; 38 | font-size: 10px; 39 | font-weight: 600; 40 | } 41 | &__namespace { 42 | margin: 0 20px; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ui/src/app/components/modal/modal.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .modal-container { 4 | width: 100%; 5 | height: 100%; 6 | z-index: 10; 7 | position: fixed; 8 | background-color: rgba(0, 0, 0, 0.4); 9 | } 10 | 11 | .modal { 12 | width: 420px; 13 | margin: 0 auto; 14 | background-color: $argo-color-gray-2; 15 | border-radius: 5px; 16 | margin-top: 10%; 17 | overflow: hidden; 18 | 19 | &__bar { 20 | background-color: white; 21 | padding: 10px; 22 | &__close { 23 | cursor: pointer; 24 | &:hover { 25 | color: $argo-color-gray-5; 26 | } 27 | } 28 | } 29 | 30 | &__content { 31 | padding: 20px; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ui/src/app/components/modal/modal.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {Key, KeybindingContext} from 'react-keyhooks'; 3 | 4 | import './modal.scss'; 5 | 6 | export const Modal = (props: {children: React.ReactNode; hide?: () => void}) => { 7 | const {useKeybinding} = React.useContext(KeybindingContext); 8 | useKeybinding(Key.ESCAPE, () => { 9 | if (props.hide) { 10 | props.hide(); 11 | return true; 12 | } 13 | return false; 14 | }); 15 | 16 | return ( 17 |
18 |
19 |
20 | 21 |
22 |
{props.children}
23 |
24 |
25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /ui/src/app/components/rollouts-grid/rollouts-grid.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .rollouts-grid { 4 | display: flex; 5 | box-sizing: border-box; 6 | flex-wrap: wrap; 7 | padding-top: 20px; 8 | } 9 | -------------------------------------------------------------------------------- /ui/src/app/components/rollouts-table/rollouts-table.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .rollouts-table { 4 | width: 100%; 5 | } 6 | 7 | .rollouts-table__row__selected { 8 | background-color: $argo-color-gray-2; 9 | } 10 | 11 | .rollouts-table_widget_actions { 12 | display: flex; 13 | flex-wrap: wrap; 14 | } 15 | 16 | .rollouts-table_widget_actions_button { 17 | margin-top: 10px; 18 | } 19 | -------------------------------------------------------------------------------- /ui/src/app/components/shortcuts/shortcuts.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .shortcuts { 4 | position: absolute; 5 | top: 50%; 6 | &__shortcut { 7 | display: flex; 8 | align-items: center; 9 | margin-bottom: 1em; 10 | } 11 | 12 | &__shortcut:last-child { 13 | margin-bottom: 0; 14 | } 15 | 16 | &__key { 17 | font-weight: 600; 18 | margin-right: 5px; 19 | $padding: 3px; 20 | padding: 0 $padding; 21 | $size: 30px; 22 | min-width: $size - (2 * $padding); 23 | height: $size; 24 | line-height: $size; 25 | background-color: $argo-color-gray-4; 26 | border-radius: 3px; 27 | text-align: center; 28 | } 29 | &__description { 30 | margin-left: auto; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ui/src/app/components/status-count/status-count.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .status-count { 4 | display: flex; 5 | align-items: center; 6 | border: 1px solid $argo-color-gray-4; 7 | border-radius: 5px; 8 | padding: 2px; 9 | margin: 1px; 10 | 11 | &__icon { 12 | font-size: 15px; 13 | color: $argo-color-gray-8; 14 | margin: 5px; 15 | 16 | text-align: center; 17 | flex: 0 0 auto; 18 | } 19 | 20 | &__count { 21 | font-size: 15px; 22 | font-weight: 500; 23 | color: $argo-color-gray-8; 24 | margin-right: 5px; 25 | text-align: right; 26 | flex: 1; 27 | } 28 | } 29 | .status-count.active { 30 | background-color: $argo-color-teal-2; 31 | } 32 | -------------------------------------------------------------------------------- /ui/src/app/components/status-count/status-count.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | import {RolloutStatus, StatusIcon} from '../status-icon/status-icon'; 4 | 5 | import './status-count.scss'; 6 | 7 | export const StatusCount = ({status, count, defaultIcon = 'fa-exclamation-circle', active = false}: {status: String; count: Number; defaultIcon?: String; active?: boolean}) => { 8 | return ( 9 |
10 |
11 | 12 |
13 |
{count}
14 |
15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /ui/src/app/components/status-icon/status-icon.scss: -------------------------------------------------------------------------------- 1 | @import 'node_modules/argo-ui/v2/styles/colors'; 2 | 3 | .status-icon, 4 | .condition-icon { 5 | &--progressing { 6 | color: $sky; 7 | } 8 | &--healthy { 9 | color: $argo-success-color; 10 | } 11 | &--degraded { 12 | color: $coral; 13 | } 14 | &--paused { 15 | color: $peach; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ui/src/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Argo Rollouts 8 | 9 | 10 | 11 |
12 | 13 | 14 | -------------------------------------------------------------------------------- /ui/src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root'), 10 | ); 11 | -------------------------------------------------------------------------------- /ui/src/app/shared/context/api.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import {RolloutNamespaceInfo, RolloutServiceApi} from '../../../models/rollout/generated'; 3 | 4 | export const RolloutAPI = new RolloutServiceApi(); 5 | export const RolloutAPIContext = React.createContext(RolloutAPI); 6 | 7 | export const APIProvider = (props: {children: React.ReactNode}) => { 8 | return {props.children}; 9 | }; 10 | 11 | export const NamespaceContext = React.createContext({namespace: '', availableNamespaces: []}); 12 | -------------------------------------------------------------------------------- /ui/src/app/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const {merge} = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 4 | const webpack = require('webpack'); 5 | 6 | module.exports = merge(common, { 7 | mode: 'development', 8 | plugins: [ 9 | new BundleAnalyzerPlugin(), 10 | new webpack.DefinePlugin({ 11 | 'process.env.NODE_ENV': JSON.stringify('development'), 12 | }), 13 | ], 14 | devServer: { 15 | historyApiFallback: { 16 | disableDotRule: true, 17 | }, 18 | watchOptions: { 19 | ignored: [/dist/, /node_modules/], 20 | }, 21 | headers: { 22 | 'X-Frame-Options': 'SAMEORIGIN', 23 | }, 24 | host: 'localhost', 25 | port: 3101, 26 | proxy: { 27 | '/api/v1': { 28 | target: 'http://localhost:3100', 29 | secure: false, 30 | }, 31 | }, 32 | }, 33 | }); 34 | -------------------------------------------------------------------------------- /ui/src/app/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const {merge} = require('webpack-merge'); 2 | const common = require('./webpack.common.js'); 3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 4 | const webpack = require('webpack'); 5 | 6 | module.exports = merge(common, { 7 | mode: 'production', 8 | devtool: 'source-map', 9 | plugins: [ 10 | new MiniCssExtractPlugin(), 11 | new webpack.DefinePlugin({ 12 | 'process.env.NODE_ENV': JSON.stringify('production'), 13 | }), 14 | ], 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.css$/i, 19 | use: [MiniCssExtractPlugin.loader, 'css-loader'], 20 | }, 21 | ], 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /ui/src/assets/images/argo-icon-color-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/argoproj/argo-rollouts/8668348cd42ef5dda0bfcb6f878da34ae816f242/ui/src/assets/images/argo-icon-color-square.png -------------------------------------------------------------------------------- /ui/src/assets/images/argologo.svg: -------------------------------------------------------------------------------- 1 | Asset 3 -------------------------------------------------------------------------------- /ui/src/config/theme.ts: -------------------------------------------------------------------------------- 1 | import {ThemeConfig} from 'antd/es/config-provider'; 2 | 3 | export const theme: ThemeConfig = { 4 | components: { 5 | Button: { 6 | colorPrimary: '#44505f', 7 | colorPrimaryBgHover: '#626f7e', 8 | colorPrimaryHover: '#626f7e', 9 | colorPrimaryActive: '#626f7e', 10 | borderRadius: 100, 11 | borderRadiusSM: 100, 12 | borderRadiusLG: 100, 13 | }, 14 | }, 15 | token: { 16 | colorPrimary: '#44505f', 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /ui/src/models/kubernetes.ts: -------------------------------------------------------------------------------- 1 | export type Time = string; 2 | 3 | export interface ObjectMeta { 4 | name?: string; 5 | generateName?: string; 6 | namespace?: string; 7 | selfLink?: string; 8 | uid?: string; 9 | resourceVersion?: string; 10 | generation?: number; 11 | creationTimestamp?: Time; 12 | deletionTimestamp?: Time; 13 | deletionGracePeriodSeconds?: number; 14 | labels?: {[name: string]: string}; 15 | annotations?: {[name: string]: string}; 16 | ownerReferences?: any[]; 17 | initializers?: any; 18 | finalizers?: string[]; 19 | clusterName?: string; 20 | } 21 | 22 | export interface TypeMeta { 23 | kind?: string; 24 | apiVersion?: string; 25 | } -------------------------------------------------------------------------------- /ui/src/models/rollout/generated/.gitignore: -------------------------------------------------------------------------------- 1 | wwwroot/*.js 2 | node_modules 3 | typings 4 | -------------------------------------------------------------------------------- /ui/src/models/rollout/generated/.swagger-codegen-ignore: -------------------------------------------------------------------------------- 1 | # Swagger Codegen Ignore 2 | # Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen 3 | 4 | # Use this file to prevent files from being overwritten by the generator. 5 | # The patterns follow closely to .gitignore or .dockerignore. 6 | 7 | # As an example, the C# client generator defines ApiClient.cs. 8 | # You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line: 9 | #ApiClient.cs 10 | 11 | # You can match any string of characters against a directory, file or extension with a single asterisk (*): 12 | #foo/*/qux 13 | # The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux 14 | 15 | # You can recursively match patterns against a directory, file or extension with a double asterisk (**): 16 | #foo/**/qux 17 | # This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux 18 | 19 | # You can also negate patterns with an exclamation (!). 20 | # For example, you can ignore all files in a docs folder with the file extension .md: 21 | #docs/*.md 22 | # Then explicitly reverse the ignore rule for a single file: 23 | #!docs/README.md 24 | -------------------------------------------------------------------------------- /ui/src/models/rollout/generated/.swagger-codegen/VERSION: -------------------------------------------------------------------------------- 1 | 3.0.54 -------------------------------------------------------------------------------- /ui/src/models/rollout/generated/custom.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'isomorphic-fetch'; 2 | declare module 'url'; -------------------------------------------------------------------------------- /ui/src/models/rollout/generated/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable 2 | /** 3 | * pkg/apiclient/rollout/rollout.proto 4 | * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) 5 | * 6 | * OpenAPI spec version: version not set 7 | * 8 | * 9 | * NOTE: This file is auto generated by the swagger code generator program. 10 | * https://github.com/swagger-api/swagger-codegen.git 11 | * Do not edit the file manually. 12 | */ 13 | 14 | export * from "./api"; 15 | export * from "./configuration"; 16 | -------------------------------------------------------------------------------- /ui/src/models/rollout/rollout.tsx: -------------------------------------------------------------------------------- 1 | import * as Generated from './generated'; 2 | 3 | export type RolloutInfo = Generated.RolloutRolloutInfo; 4 | export type Pod = Generated.RolloutPodInfo; 5 | -------------------------------------------------------------------------------- /ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "noImplicitAny": true, 5 | "module": "CommonJS", 6 | "target": "es6", 7 | "jsx": "react", 8 | "experimentalDecorators": true, 9 | "noUnusedLocals": true, 10 | "declaration": false, 11 | "lib": ["es2017", "dom"], 12 | "allowSyntheticDefaultImports": true 13 | }, 14 | "include": ["./**/*"], 15 | "exclude": ["node_modules", "./**/*.test.tsx"], 16 | "types": ["node", "jest"] 17 | } 18 | -------------------------------------------------------------------------------- /utils/apisix/apisix.go: -------------------------------------------------------------------------------- 1 | package apisix 2 | 3 | import ( 4 | "context" 5 | "strings" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/runtime/schema" 9 | "k8s.io/client-go/dynamic" 10 | 11 | "github.com/argoproj/argo-rollouts/utils/defaults" 12 | ) 13 | 14 | const apisixRoutes = "apisixroutes" 15 | 16 | var ( 17 | apiGroupToResource = map[string]string{ 18 | defaults.DefaultApisixAPIGroup: apisixRoutes, 19 | } 20 | ) 21 | 22 | func DoesApisixExist(dynamicClient dynamic.Interface, namespace string) bool { 23 | _, err := dynamicClient.Resource(GetMappingGVR()).Namespace(namespace).List(context.TODO(), metav1.ListOptions{Limit: 1}) 24 | if err != nil { 25 | return false 26 | } 27 | return true 28 | } 29 | 30 | func NewDynamicClient(di dynamic.Interface, namespace string) dynamic.ResourceInterface { 31 | return di.Resource(GetMappingGVR()).Namespace(namespace) 32 | } 33 | 34 | func GetMappingGVR() schema.GroupVersionResource { 35 | group := defaults.DefaultApisixAPIGroup 36 | parts := strings.Split(defaults.DefaultApisixVersion, "/") 37 | version := parts[len(parts)-1] 38 | resourceName := apiGroupToResource[group] 39 | return schema.GroupVersionResource{ 40 | Group: group, 41 | Version: version, 42 | Resource: resourceName, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /utils/apisix/apisix_test.go: -------------------------------------------------------------------------------- 1 | package apisix 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/tj/assert" 7 | 8 | "github.com/argoproj/argo-rollouts/rollout/trafficrouting/apisix/mocks" 9 | ) 10 | 11 | func TestNewDynamicClient(t *testing.T) { 12 | t.Run("NewDynamicClient", func(t *testing.T) { 13 | // Given 14 | t.Parallel() 15 | fakeDynamicClient := &mocks.FakeDynamicClient{} 16 | 17 | // When 18 | NewDynamicClient(fakeDynamicClient, "default") 19 | }) 20 | } 21 | 22 | func TestDoesApisixExist(t *testing.T) { 23 | t.Run("exist", func(t *testing.T) { 24 | fakeDynamicClient := &mocks.FakeDynamicClient{} 25 | assert.True(t, DoesApisixExist(fakeDynamicClient, "")) 26 | }) 27 | t.Run("not exist", func(t *testing.T) { 28 | fakeDynamicClient := &mocks.FakeDynamicClient{ 29 | IsListError: true, 30 | } 31 | assert.False(t, DoesApisixExist(fakeDynamicClient, "")) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /utils/diff/diff.go: -------------------------------------------------------------------------------- 1 | package diff 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "k8s.io/apimachinery/pkg/util/strategicpatch" 7 | ) 8 | 9 | // CreateTwoWayMergePatch is a helper to construct a two-way merge patch from objects (instead of bytes) 10 | func CreateTwoWayMergePatch(orig, new, dataStruct any) ([]byte, bool, error) { 11 | origBytes, err := json.Marshal(orig) 12 | if err != nil { 13 | return nil, false, err 14 | } 15 | newBytes, err := json.Marshal(new) 16 | if err != nil { 17 | return nil, false, err 18 | } 19 | patch, err := strategicpatch.CreateTwoWayMergePatch(origBytes, newBytes, dataStruct) 20 | if err != nil { 21 | return nil, false, err 22 | } 23 | return patch, string(patch) != "{}", nil 24 | } 25 | -------------------------------------------------------------------------------- /utils/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import log "github.com/sirupsen/logrus" 4 | 5 | // CheckError is a convenience function to fatally log an exit if the supplied error is non-nil 6 | func CheckError(err error) { 7 | if err != nil { 8 | log.Fatal(err) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /utils/hash/hash.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/json" 6 | "fmt" 7 | "hash/fnv" 8 | 9 | corev1 "k8s.io/api/core/v1" 10 | "k8s.io/apimachinery/pkg/util/rand" 11 | ) 12 | 13 | // ComputePodTemplateHash returns a hash value calculated from pod template. 14 | // The hash will be safe encoded to avoid bad words. 15 | func ComputePodTemplateHash(template *corev1.PodTemplateSpec, collisionCount *int32) string { 16 | podTemplateSpecHasher := fnv.New32a() 17 | stepsBytes, err := json.Marshal(template) 18 | if err != nil { 19 | panic(err) 20 | } 21 | _, err = podTemplateSpecHasher.Write(stepsBytes) 22 | if err != nil { 23 | panic(err) 24 | } 25 | if collisionCount != nil { 26 | collisionCountBytes := make([]byte, 8) 27 | binary.LittleEndian.PutUint32(collisionCountBytes, uint32(*collisionCount)) 28 | _, err = podTemplateSpecHasher.Write(collisionCountBytes) 29 | if err != nil { 30 | panic(err) 31 | } 32 | } 33 | return rand.SafeEncodeString(fmt.Sprint(podTemplateSpecHasher.Sum32())) 34 | } 35 | -------------------------------------------------------------------------------- /utils/json/json_test.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | type TestStruct struct { 10 | Foo string `json:"foo"` 11 | } 12 | 13 | func TestMustMarshalSuccess(t *testing.T) { 14 | test := TestStruct{ 15 | Foo: "hi", 16 | } 17 | bytes := MustMarshal(test) 18 | assert.Equal(t, `{"foo":"hi"}`, string(bytes)) 19 | } 20 | 21 | type I interface { 22 | Foo() string 23 | } 24 | 25 | func TestMustMarshalPanics(t *testing.T) { 26 | test := map[string]any{ 27 | "foo": make(chan int), 28 | } 29 | assert.Panics(t, func() { MustMarshal(test) }) 30 | } 31 | -------------------------------------------------------------------------------- /utils/log/redactor_formatter.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | 6 | log "github.com/sirupsen/logrus" 7 | ) 8 | 9 | type RedactorFormatter struct { 10 | formatter log.Formatter 11 | secrets []string 12 | } 13 | 14 | func (f *RedactorFormatter) Format(e *log.Entry) ([]byte, error) { 15 | data, err := f.formatter.Format(e) 16 | if err != nil { 17 | return nil, err 18 | } 19 | for _, secret := range f.secrets { 20 | // Only replace non-empty strings to prevent injection at every character in logger 21 | if secret != "" { 22 | data = bytes.ReplaceAll(data, []byte(secret), []byte("*****")) 23 | } 24 | } 25 | return data, nil 26 | } 27 | -------------------------------------------------------------------------------- /utils/metric/metric.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 5 | timeutil "github.com/argoproj/argo-rollouts/utils/time" 6 | ) 7 | 8 | // MarkMeasurementError sets an error message on a measurement along with finish time 9 | func MarkMeasurementError(m v1alpha1.Measurement, err error) v1alpha1.Measurement { 10 | m.Phase = v1alpha1.AnalysisPhaseError 11 | m.Message = err.Error() 12 | if m.FinishedAt == nil { 13 | finishedTime := timeutil.MetaNow() 14 | m.FinishedAt = &finishedTime 15 | } 16 | return m 17 | } 18 | -------------------------------------------------------------------------------- /utils/metric/metric_test.go: -------------------------------------------------------------------------------- 1 | package metric 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | 10 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 11 | ) 12 | 13 | func TestMarkMeasurementError(t *testing.T) { 14 | now := metav1.Now() 15 | m := v1alpha1.Measurement{ 16 | StartedAt: &now, 17 | } 18 | err := errors.New("my error message") 19 | m = MarkMeasurementError(m, err) 20 | assert.Equal(t, err.Error(), m.Message) 21 | assert.NotNil(t, m.FinishedAt) 22 | } 23 | -------------------------------------------------------------------------------- /utils/plugin/plugin.go: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | 7 | "github.com/argoproj/argo-rollouts/utils/defaults" 8 | "github.com/argoproj/argo-rollouts/utils/plugin/types" 9 | 10 | "github.com/argoproj/argo-rollouts/utils/config" 11 | ) 12 | 13 | // GetPluginInfo returns the location & command arguments of the plugin on the filesystem via plugin name. If the plugin is not 14 | // configured in the configmap, an error is returned. 15 | func GetPluginInfo(pluginName string, pluginType types.PluginType) (string, []string, error) { 16 | configMap, err := config.GetConfig() 17 | if err != nil { 18 | return "", nil, fmt.Errorf("failed to get config: %w", err) 19 | } 20 | 21 | plugin := configMap.GetPlugin(pluginName, pluginType) 22 | if plugin == nil { 23 | return "", nil, fmt.Errorf("plugin %s not configured in configmap", pluginName) 24 | } 25 | 26 | dir, filename, err := config.GetPluginDirectoryAndFilename(plugin.Name) 27 | if err != nil { 28 | return "", nil, err 29 | } 30 | absFilePath, err := filepath.Abs(filepath.Join(defaults.DefaultRolloutPluginFolder, dir, filename)) 31 | if err != nil { 32 | return "", nil, fmt.Errorf("failed to get absolute path of plugin folder: %w", err) 33 | } 34 | return absFilePath, plugin.Args, nil 35 | 36 | } 37 | -------------------------------------------------------------------------------- /utils/queue/queue.go: -------------------------------------------------------------------------------- 1 | package queue 2 | 3 | import ( 4 | "time" 5 | 6 | "k8s.io/client-go/util/workqueue" 7 | ) 8 | 9 | // DefaultArgoRolloutsRateLimiter is the default queue rate limiter. 10 | // Similar to workqueue.DefaultControllerRateLimiter() but the max limit is 10 seconds instead of 16 minutes 11 | func DefaultArgoRolloutsRateLimiter() workqueue.RateLimiter { 12 | return workqueue.NewItemExponentialFailureRateLimiter(time.Millisecond, 10*time.Second) 13 | } 14 | -------------------------------------------------------------------------------- /utils/replicaset/bluegreen.go: -------------------------------------------------------------------------------- 1 | package replicaset 2 | 3 | import ( 4 | appsv1 "k8s.io/api/apps/v1" 5 | 6 | v1alpha1 "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 7 | ) 8 | 9 | // GetReplicaSetByTemplateHash find the replicaset that matches the podTemplateHash 10 | func GetReplicaSetByTemplateHash(allRS []*appsv1.ReplicaSet, podTemplateHash string) (*appsv1.ReplicaSet, []*appsv1.ReplicaSet) { 11 | if podTemplateHash == "" { 12 | return nil, allRS 13 | } 14 | 15 | otherRSs := []*appsv1.ReplicaSet{} 16 | var filterRS *appsv1.ReplicaSet 17 | for i := range allRS { 18 | rs := allRS[i] 19 | if rs == nil { 20 | continue 21 | } 22 | if rsPodHash, ok := rs.Labels[v1alpha1.DefaultRolloutUniqueLabelKey]; ok { 23 | if rsPodHash == podTemplateHash { 24 | filterRS = rs.DeepCopy() 25 | continue 26 | } 27 | otherRSs = append(otherRSs, rs) 28 | } 29 | } 30 | return filterRS, otherRSs 31 | } 32 | 33 | func ReadyForPause(rollout *v1alpha1.Rollout, newRS *appsv1.ReplicaSet, allRSs []*appsv1.ReplicaSet) bool { 34 | newRSReplicaCount, err := NewRSNewReplicas(rollout, allRSs, newRS, nil) 35 | if err != nil { 36 | return false 37 | } 38 | return *(newRS.Spec.Replicas) == newRSReplicaCount && 39 | newRS.Status.AvailableReplicas == newRSReplicaCount 40 | } 41 | -------------------------------------------------------------------------------- /utils/smi/smi.go: -------------------------------------------------------------------------------- 1 | package smi 2 | 3 | import ( 4 | "context" 5 | 6 | smiclientset "github.com/servicemeshinterface/smi-sdk-go/pkg/gen/client/split/clientset/versioned" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | func DoesSMIExist(smiClient smiclientset.Interface, namespace string) bool { 11 | _, err := smiClient.SplitV1alpha1().TrafficSplits(namespace).List(context.TODO(), metav1.ListOptions{Limit: 1}) 12 | if err != nil { 13 | return false 14 | } 15 | return true 16 | } 17 | -------------------------------------------------------------------------------- /utils/time/now.go: -------------------------------------------------------------------------------- 1 | package time 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var ( 11 | timeNowFunc = time.Now 12 | 13 | // Acquire this mutex when accessing now function 14 | nowLock sync.RWMutex 15 | ) 16 | 17 | // Now is a wrapper around time.Now() and used to override behavior in tests. 18 | // Now invokes time.Now(), or its replacement function 19 | func Now() time.Time { 20 | nowLock.RLock() 21 | defer nowLock.RUnlock() 22 | 23 | return timeNowFunc() 24 | } 25 | 26 | // Replace the function used to return the current time (defaults to time.Now() ) 27 | func SetNowTimeFunc(f func() time.Time) { 28 | nowLock.Lock() 29 | defer nowLock.Unlock() 30 | 31 | timeNowFunc = f 32 | 33 | } 34 | 35 | // MetaNow is a wrapper around metav1.Now() and used to override behavior in tests. 36 | var MetaNow = func() metav1.Time { 37 | return metav1.Time{Time: Now()} 38 | } 39 | 40 | // MetaTime is a wrapper around metav1.Time and used to override behavior in tests. 41 | var MetaTime = func(time time.Time) metav1.Time { 42 | return metav1.Time{Time: time} 43 | } 44 | -------------------------------------------------------------------------------- /utils/weightutil/weight.go: -------------------------------------------------------------------------------- 1 | package weightutil 2 | 3 | import ( 4 | "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1" 5 | ) 6 | 7 | func MaxTrafficWeight(ro *v1alpha1.Rollout) int32 { 8 | maxWeight := int32(100) 9 | if ro.Spec.Strategy.Canary != nil && ro.Spec.Strategy.Canary.TrafficRouting != nil && ro.Spec.Strategy.Canary.TrafficRouting.MaxTrafficWeight != nil { 10 | maxWeight = *ro.Spec.Strategy.Canary.TrafficRouting.MaxTrafficWeight 11 | } 12 | return maxWeight 13 | } 14 | --------------------------------------------------------------------------------