diff --git a/examples/example-active-service.yaml b/examples/example-active-service.yaml deleted file mode 100644 index d97bbd8ca2..0000000000 --- a/examples/example-active-service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: active-service -spec: - selector: - app: guestbook - ports: - - protocol: TCP - port: 80 - targetPort: 9011 diff --git a/examples/example-experiment.yaml b/examples/example-experiment.yaml deleted file mode 100644 index 00d9f2da5b..0000000000 --- a/examples/example-experiment.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Experiment -metadata: - name: example-experiment -spec: - duration: 60 - templates: - - name: baseline - selector: - matchLabels: - app: rollouts-demo - color: blue - template: - metadata: - labels: - app: rollouts-demo - color: blue - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:blue - - name: canary - selector: - matchLabels: - app: rollouts-demo - color: yellow - template: - metadata: - labels: - app: rollouts-demo - color: yellow - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:yellow diff --git a/examples/example-preview-service.yaml b/examples/example-preview-service.yaml deleted file mode 100644 index af878be572..0000000000 --- a/examples/example-preview-service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: preview-service -spec: - selector: - app: guestbook - ports: - - protocol: TCP - port: 80 - targetPort: 9010 diff --git a/examples/example-rollout-bluegreen.yaml b/examples/example-rollout-bluegreen.yaml deleted file mode 100644 index 782490cfb5..0000000000 --- a/examples/example-rollout-bluegreen.yaml +++ /dev/null @@ -1,26 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: example-rollout-bluegreen -spec: - replicas: 1 - selector: - matchLabels: - app: guestbook - template: - metadata: - labels: - app: guestbook - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:blue - ports: - - containerPort: 80 - minReadySeconds: 30 - revisionHistoryLimit: 3 - strategy: - blueGreen: - activeService: active-service - previewService: preview-service - autoPromotionEnabled: false diff --git a/examples/example-rollout-canary-run-tmp-canary.yaml b/examples/example-rollout-canary-run-tmp-canary.yaml deleted file mode 100644 index 99b0a4d546..0000000000 --- a/examples/example-rollout-canary-run-tmp-canary.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: example-rollout-canary -spec: - replicas: 5 - selector: - matchLabels: - app: guestbook - template: - metadata: - labels: - app: guestbook - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:blue - ports: - - containerPort: 80 - minReadySeconds: 30 - revisionHistoryLimit: 3 - strategy: - canary: - steps: - - setWeight: 20 - - pause: - duration: 3600 # One Hour - - setWeight: 0 - - pause: {} \ No newline at end of file diff --git a/examples/example-rollout-canary.yaml b/examples/example-rollout-canary.yaml deleted file mode 100644 index 78c2028c60..0000000000 --- a/examples/example-rollout-canary.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: example-rollout-canary -spec: - replicas: 5 - selector: - matchLabels: - app: rollout - template: - metadata: - labels: - app: guestbook - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:blue - ports: - - containerPort: 80 - minReadySeconds: 30 - revisionHistoryLimit: 3 - strategy: - canary: - steps: - - setWeight: 20 - - pause: - duration: 20 - - setWeight: 40 - - pause: {} diff --git a/examples/example-rollout-rolling-update.yaml b/examples/example-rollout-rolling-update.yaml deleted file mode 100644 index 3085d44544..0000000000 --- a/examples/example-rollout-rolling-update.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: example-rollout-canary -spec: - replicas: 5 - selector: - matchLabels: - app: guestbook - template: - metadata: - labels: - app: guestbook - spec: - containers: - - name: guestbook - image: argoproj/rollouts-demo:blue - ports: - - containerPort: 80 - minReadySeconds: 30 - revisionHistoryLimit: 3 - strategy: - canary: {} diff --git a/examples/rollout-baseline-vs-canary.yaml b/examples/rollout-baseline-vs-canary.yaml new file mode 100644 index 0000000000..a908d222ac --- /dev/null +++ b/examples/rollout-baseline-vs-canary.yaml @@ -0,0 +1,35 @@ +# This example demonstrates how start a new baseline and canary stack as part of the Rollout update. +# Comparing a canary against a baseline (as opposed to existing production instances) is considered +# a common best practice when performing canary analysis, since it eliminates many differences that +# may skew the results of an analysis (cache warmup time, heap size, etc). +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-baseline-vs-canary +spec: + replicas: 5 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-baseline-vs-canary + template: + metadata: + labels: + app: rollout-baseline-vs-canary + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + canary: + steps: + - experiment: + templates: + - name: baseline + specRef: stable + - name: canary + specRef: canary diff --git a/examples/rollout-bluegreen.yaml b/examples/rollout-bluegreen.yaml new file mode 100644 index 0000000000..65fbe449c9 --- /dev/null +++ b/examples/rollout-bluegreen.yaml @@ -0,0 +1,64 @@ +# This example demonstrates a Rollout using the blue-green update strategy, which contains a manual +# gate before promoting the new stack. +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-bluegreen +spec: + replicas: 1 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-bluegreen + template: + metadata: + labels: + app: rollout-bluegreen + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + blueGreen: + # activeService specifies the service to update with the new template hash at time of promotion. + # This field is mandatory for the blueGreen update strategy. + activeService: rollout-bluegreen-active + # previewService specifies the service to update with the new template hash before promotion. + # This allows the preview stack to be reachable without serving production traffic. + # This field is optional. + previewService: rollout-bluegreen-preview + # autoPromotionEnabled disables automated promotion of the new stack by pausing the rollout + # immediately before the promotion. If omitted, the default behavior is to promote the new + # stack as soon as the ReplicaSet are completely ready/available. + # Rollouts can be resumed using: `kubectl argo rollouts resume ROLLOUT` + autoPromotionEnabled: false + +--- +kind: Service +apiVersion: v1 +metadata: + name: rollout-bluegreen-active +spec: + selector: + app: rollout-bluegreen + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + +--- +kind: Service +apiVersion: v1 +metadata: + name: rollout-bluegreen-preview +spec: + selector: + app: rollout-bluegreen + ports: + - protocol: TCP + port: 80 + targetPort: 8080 diff --git a/examples/rollout-canary-preview.yaml b/examples/rollout-canary-preview.yaml new file mode 100644 index 0000000000..dc6e96615c --- /dev/null +++ b/examples/rollout-canary-preview.yaml @@ -0,0 +1,71 @@ +# This example demonstrates how to deploy a preview ReplicaSet using an Experiment. The preview +# ReplicaSet will run but receive no production traffic, since its selector labels are set +# differently than the rollout's selector labels. +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-canary-preview +spec: + replicas: 5 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-canary-production + template: + metadata: + labels: + app: rollout-canary-production + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + canary: + steps: + # The initial step starts an experiment that runs a single pod ReplicaSet using the new pod spec + # When the experiment is terminated, the rollout will progress through rest of the canary steps. + - experiment: + templates: + - name: canary-preview + specRef: canary + # Selector and metadata are overwritten to be something different from the rollout's + # spec.selector. This allows the preview stack to be reachable through a different + # service without receiving production traffic. + selector: + matchLabels: + app: rollout-canary-preview + metadata: + labels: + app: rollout-canary-preview + +--- +# The rollout-canary-preview service will point to the preview stack, making them reachable without +# exposing them to production traffic. +kind: Service +apiVersion: v1 +metadata: + name: rollout-canary-preview +spec: + selector: + app: rollout-canary-preview + ports: + - protocol: TCP + port: 80 + targetPort: 8080 + +--- +kind: Service +apiVersion: v1 +metadata: + name: rollout-canary-production +spec: + selector: + app: rollout-canary-production + ports: + - protocol: TCP + port: 80 + targetPort: 8080 diff --git a/examples/rollout-canary.yaml b/examples/rollout-canary.yaml new file mode 100644 index 0000000000..b06918949b --- /dev/null +++ b/examples/rollout-canary.yaml @@ -0,0 +1,38 @@ +# This example demonstrates a Rollout using the canary update strategy with a customized rollout +# plan. The prescribed steps initially sets a canary weight of 20%, then pauses indefinitely. Once +# resumed, the rollout performs a gradual, automated 20% weight increase until it reaches 100%. +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-canary +spec: + replicas: 5 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-canary + template: + metadata: + labels: + app: rollout-canary + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + canary: + steps: + - setWeight: 20 + # The following pause step will pause the rollout indefinitely until manually resumed. + # Rollouts can be manually resumed by running `kubectl argo rollouts resume ROLLOUT` + - pause: {} + - setWeight: 40 + - pause: {duration: 40} + - setWeight: 60 + - pause: {duration: 20} + - setWeight: 80 + - pause: {duration: 20} diff --git a/examples/rollout-rolling-update.yaml b/examples/rollout-rolling-update.yaml new file mode 100644 index 0000000000..38ee259437 --- /dev/null +++ b/examples/rollout-rolling-update.yaml @@ -0,0 +1,30 @@ +# This example demonstrates how to use normal rolling update for a Rollout update strategy. +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-rollingupdate +spec: + replicas: 5 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-rollingupdate + template: + metadata: + labels: + app: rollout-rollingupdate + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + # For a normal rolling update, simply specify the canary strategy without steps defined. + # The maxSurge and maxUnavailable fields can be specified. If omitted, defaults to 25% and 0 + # respectively. + canary: + maxSurge: 1 + maxUnavailable: 1 diff --git a/examples/rollout-with-analysis.yaml b/examples/rollout-with-analysis.yaml new file mode 100644 index 0000000000..9ff6df7b87 --- /dev/null +++ b/examples/rollout-with-analysis.yaml @@ -0,0 +1,59 @@ +# This example demonstrates a Rollout which performs background analysis while the Rollout is updating. +apiVersion: argoproj.io/v1alpha1 +kind: Rollout +metadata: + name: rollout-with-analysis +spec: + replicas: 4 + minReadySeconds: 10 + revisionHistoryLimit: 3 + selector: + matchLabels: + app: rollout-with-analysis + template: + metadata: + labels: + app: rollout-with-analysis + spec: + containers: + - name: rollouts-demo + image: argoproj/rollouts-demo:blue + imagePullPolicy: Always + ports: + - containerPort: 8080 + strategy: + canary: + # An AnalysisTemplate is referenced here, which starts an AnalysisRun as soon as the update + # begins, and terminates the run when the update completes. A failure/error of the analysis + # will cause the rollout's update to abort, and set the canary weight to zero. + analysis: + name: random-fail + templateName: random-fail + steps: + - setWeight: 50 + - pause: {} + +--- +# This AnalysisTemplate will run a Kubernetes Job every 10 seconds, with a 50% chance of failure. +# When the number of accumulated failures exceeds maxFailures, it will cause the rollout to abort. +kind: AnalysisTemplate +apiVersion: argoproj.io/v1alpha1 +metadata: + name: random-fail +spec: + metrics: + - name: random-fail + interval: 10 + maxFailures: 2 + provider: + job: + spec: + template: + spec: + containers: + - name: sleep + image: alpine:3.8 + command: [sh, -c] + args: [FLIP=$(($(($RANDOM%10))%2)) && exit $FLIP] + restartPolicy: Never + backoffLimit: 0 diff --git a/manifests/crds/rollout-crd.yaml b/manifests/crds/rollout-crd.yaml index 0f5317e063..f54fd61827 100644 --- a/manifests/crds/rollout-crd.yaml +++ b/manifests/crds/rollout-crd.yaml @@ -228,6 +228,29 @@ spec: replicas: format: int32 type: integer + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object specRef: type: string required: diff --git a/manifests/install.yaml b/manifests/install.yaml index b27236f9ca..e624c068e3 100644 --- a/manifests/install.yaml +++ b/manifests/install.yaml @@ -7955,6 +7955,29 @@ spec: replicas: format: int32 type: integer + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object specRef: type: string required: diff --git a/manifests/namespace-install.yaml b/manifests/namespace-install.yaml index 8b305b81a7..f76b44f91b 100644 --- a/manifests/namespace-install.yaml +++ b/manifests/namespace-install.yaml @@ -7955,6 +7955,29 @@ spec: replicas: format: int32 type: integer + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object specRef: type: string required: diff --git a/pkg/apis/rollouts/v1alpha1/openapi_generated.go b/pkg/apis/rollouts/v1alpha1/openapi_generated.go index 266c23d02b..441b70fcf6 100644 --- a/pkg/apis/rollouts/v1alpha1/openapi_generated.go +++ b/pkg/apis/rollouts/v1alpha1/openapi_generated.go @@ -1661,7 +1661,7 @@ func schema_pkg_apis_rollouts_v1alpha1_RolloutExperimentTemplate(ref common.Refe }, "specRef": { SchemaProps: spec.SchemaProps{ - Description: "Type indicates where the rollout should get the RS template from", + Description: "SpecRef indicates where the rollout should get the RS template from", Type: []string{"string"}, Format: "", }, @@ -1675,16 +1675,22 @@ func schema_pkg_apis_rollouts_v1alpha1_RolloutExperimentTemplate(ref common.Refe }, "metadata": { SchemaProps: spec.SchemaProps{ - Description: "AdditionalSelectors additional selectors to use for the RS created from the template", + Description: "Metadata sets labels and annotations to use for the RS created from the template", Ref: ref("github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata"), }, }, + "selector": { + SchemaProps: spec.SchemaProps{ + Description: "Selector overrides the selector to be used for the template's ReplicaSet. If omitted, will use the same selector as the Rollout", + Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"), + }, + }, }, Required: []string{"name", "specRef"}, }, }, Dependencies: []string{ - "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata"}, + "github.com/argoproj/argo-rollouts/pkg/apis/rollouts/v1alpha1.PodTemplateMetadata", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, } } diff --git a/pkg/apis/rollouts/v1alpha1/types.go b/pkg/apis/rollouts/v1alpha1/types.go index e27a4a98bc..91f50dafa9 100644 --- a/pkg/apis/rollouts/v1alpha1/types.go +++ b/pkg/apis/rollouts/v1alpha1/types.go @@ -153,7 +153,7 @@ type RolloutExperimentStep struct { // Duration is the duration in seconds that the experiment should run for // +optional Duration *int32 `json:"duration,omitempty"` - //Analyses what analyses to run with the experiment + // Analyses what analyses to run with the experiment Analyses []RolloutAnalysisStep `json:"analyses,omitempty"` } @@ -161,17 +161,21 @@ type RolloutExperimentStep struct { type RolloutExperimentTemplate struct { // Name description of template that passed to the template Name string `json:"name"` - // Type indicates where the rollout should get the RS template from + // SpecRef indicates where the rollout should get the RS template from SpecRef ReplicaSetSpecRef `json:"specRef"` // Replicas replica count for the template // +optional Replicas *int32 `json:"replicas,omitempty"` - // AdditionalSelectors additional selectors to use for the RS created from the template + // Metadata sets labels and annotations to use for the RS created from the template // +optional Metadata PodTemplateMetadata `json:"metadata,omitempty"` + // Selector overrides the selector to be used for the template's ReplicaSet. If omitted, will + // use the same selector as the Rollout + // +optional + Selector *metav1.LabelSelector `json:"selector,omitempty"` } -//PodTemplateMetadata extra labels to add to the template +// PodTemplateMetadata extra labels to add to the template type PodTemplateMetadata struct { // Labels Additional labels to add to the experiment // +optional diff --git a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go index 7229f44014..be08eeef68 100644 --- a/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/rollouts/v1alpha1/zz_generated.deepcopy.go @@ -929,6 +929,11 @@ func (in *RolloutExperimentTemplate) DeepCopyInto(out *RolloutExperimentTemplate **out = **in } in.Metadata.DeepCopyInto(&out.Metadata) + if in.Selector != nil { + in, out := &in.Selector, &out.Selector + *out = new(v1.LabelSelector) + (*in).DeepCopyInto(*out) + } return } diff --git a/rollout/experiment.go b/rollout/experiment.go index 93b6bd5d3c..15631fe83c 100644 --- a/rollout/experiment.go +++ b/rollout/experiment.go @@ -62,9 +62,14 @@ func GetExperimentFromTemplate(r *v1alpha1.Rollout, stableRS, newRS *appsv1.Repl template.Template = templateRS.Spec.Template template.MinReadySeconds = templateRS.Spec.MinReadySeconds - template.Selector = templateRS.Spec.Selector.DeepCopy() + if templateStep.Selector != nil { + template.Selector = templateStep.Selector.DeepCopy() + } else { + template.Selector = templateRS.Spec.Selector.DeepCopy() + } + if templateStep.Metadata.Labels != nil { - if templateStep.Metadata.Labels == nil { + if template.Template.ObjectMeta.Labels == nil { template.Template.ObjectMeta.Labels = make(map[string]string) } for key := range templateStep.Metadata.Labels { diff --git a/rollout/experiment_test.go b/rollout/experiment_test.go index 0c6f63d478..35b3c7ca00 100644 --- a/rollout/experiment_test.go +++ b/rollout/experiment_test.go @@ -342,6 +342,17 @@ func TestGetExperimentFromTemplate(t *testing.T) { stable, err := GetExperimentFromTemplate(r2, rs1, rs2) assert.Nil(t, err) assert.Equal(t, rs1.Spec.Template, stable.Spec.Templates[0].Template) + assert.Equal(t, rs1.Spec.Selector, stable.Spec.Templates[0].Selector) + + newSelector := metav1.LabelSelector{ + MatchLabels: map[string]string{ + "foo": "bar", + }, + } + r2.Spec.Strategy.Canary.Steps[0].Experiment.Templates[0].Selector = &newSelector + modifiedSelector, err := GetExperimentFromTemplate(r2, rs1, rs2) + assert.Nil(t, err) + assert.Equal(t, newSelector, *modifiedSelector.Spec.Templates[0].Selector) r2.Spec.Strategy.Canary.Steps[0].Experiment.Templates[0].SpecRef = v1alpha1.CanarySpecRef canary, err := GetExperimentFromTemplate(r2, rs1, rs2) diff --git a/test/e2e/container-resource-formats.yaml b/test/e2e/functional/container-resource-formats.yaml similarity index 100% rename from test/e2e/container-resource-formats.yaml rename to test/e2e/functional/container-resource-formats.yaml diff --git a/test/e2e/functional/rollout-experiment-analysis.yaml b/test/e2e/functional/rollout-experiment-analysis.yaml index 6e59adbd5c..d2089294d3 100644 --- a/test/e2e/functional/rollout-experiment-analysis.yaml +++ b/test/e2e/functional/rollout-experiment-analysis.yaml @@ -49,7 +49,7 @@ spec: templates: - name: baseline specRef: stable - replicas: 1 - name: canary specRef: canary - replicas: 1 + analyses: + - templateName: random-fail diff --git a/test/e2e/functional/rollout-with-experiment.yaml b/test/e2e/functional/rollout-with-experiment.yaml deleted file mode 100644 index cae9abf692..0000000000 --- a/test/e2e/functional/rollout-with-experiment.yaml +++ /dev/null @@ -1,30 +0,0 @@ -apiVersion: argoproj.io/v1alpha1 -kind: Rollout -metadata: - name: rollout-with-experiment - labels: - app: rollout-with-experiment -spec: - selector: - matchLabels: - app: rollouts-demo - template: - metadata: - labels: - app: rollouts-demo - spec: - containers: - - name: rollouts-demo - image: argoproj/rollouts-demo:blue - strategy: - canary: - steps: - - experiment: - duration: 30 - templates: - - name: baseline - specRef: stable - replicas: 1 - - name: canary - specRef: canary - replicas: 1