fix merge conflict -- build ok
Signed-off-by: veophi <vec.g.sun@gmail.com>
This commit is contained in:
parent
4ecbdf0d49
commit
83dad6848c
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2022 The Kruise Authors.
|
||||
Copyright 2022 Kruise Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
@ -253,24 +253,20 @@ type RolloutPhase string
|
|||
const (
|
||||
// RolloutPhaseInitial indicates a rollout is Initial
|
||||
RolloutPhaseInitial RolloutPhase = "Initial"
|
||||
// RolloutPhaseVerify indicates a rollout is verifying
|
||||
RolloutPhaseVerify RolloutPhase = "Verifying"
|
||||
// RolloutPhaseHealthy indicates a rollout is healthy
|
||||
RolloutPhaseHealthy RolloutPhase = "Healthy"
|
||||
// RolloutPhasePreparing indicates a rollout is preparing for next progress.
|
||||
RolloutPhasePreparing RolloutPhase = "Preparing"
|
||||
// RolloutPhaseProgressing indicates a rollout is not yet healthy but still making progress towards a healthy state
|
||||
RolloutPhaseProgressing RolloutPhase = "Progressing"
|
||||
// RolloutPhaseRollback indicates rollback
|
||||
RolloutPhaseRollback RolloutPhase = "Rollback"
|
||||
// RolloutPhasePaused indicates a rollout is not yet healthy and will not make progress until unpaused
|
||||
RolloutPhasePaused RolloutPhase = "Paused"
|
||||
// RolloutPhaseFinalizing indicates a rollout is finalizing
|
||||
RolloutPhaseFinalizing RolloutPhase = "Finalizing"
|
||||
// RolloutPhaseRollingBack indicates a rollout is rolling back
|
||||
RolloutPhaseRollingBack RolloutPhase = "RollingBack"
|
||||
// RolloutPhaseTerminating indicates a rollout is terminated
|
||||
RolloutPhaseTerminating RolloutPhase = "Terminating"
|
||||
// RolloutPhaseRollback indicates rollback
|
||||
RolloutPhaseRollback RolloutPhase = "Rollback"
|
||||
// RolloutPhaseCompleted indicates a rollout is completed
|
||||
RolloutPhaseCompleted RolloutPhase = "Completed"
|
||||
// RolloutPhaseCancelled indicates a rollout is cancelled
|
||||
|
|
|
|||
|
|
@ -104,11 +104,7 @@ func (in *BatchReleaseList) DeepCopyObject() runtime.Object {
|
|||
func (in *BatchReleaseSpec) DeepCopyInto(out *BatchReleaseSpec) {
|
||||
*out = *in
|
||||
out.Strategy = in.Strategy
|
||||
<<<<<<< HEAD
|
||||
in.TargetRef.DeepCopyInto(&out.TargetRef)
|
||||
=======
|
||||
out.TargetRef = in.TargetRef
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
in.ReleasePlan.DeepCopyInto(&out.ReleasePlan)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ spec:
|
|||
description: TargetRevisionName contains the name of the componentRevisionName
|
||||
that we need to upgrade to.
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
type:
|
||||
description: workloadRef, revisionRef default is workloadRef
|
||||
|
|
@ -142,12 +143,38 @@ spec:
|
|||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
=======
|
||||
type:
|
||||
description: workloadRef, revisionRef default is workloadRef
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
type: string
|
||||
workloadRef:
|
||||
description: WorkloadRef contains enough information to let you
|
||||
identify a workload for Rollout Batch release of the bypass
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API Version of the referent
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
type: object
|
||||
required:
|
||||
<<<<<<< HEAD
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
- type
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
type: object
|
||||
required:
|
||||
- releasePlan
|
||||
|
|
@ -161,11 +188,16 @@ spec:
|
|||
description: Canary describes the state of the canary rollout
|
||||
properties:
|
||||
batchState:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
description: ReleasingBatchState indicates the state of the current
|
||||
batch.
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
description: ReleasingBatchState indicates the state of the current
|
||||
batch.
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
type: string
|
||||
currentBatch:
|
||||
description: The current batch the rollout is working on/blocked,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ spec:
|
|||
description: RolloutSpec defines the desired state of Rollout
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
objectRef:
|
||||
properties:
|
||||
type:
|
||||
|
|
@ -63,12 +66,16 @@ spec:
|
|||
required:
|
||||
- type
|
||||
type: object
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
strategy:
|
||||
description: The deployment strategy to use to replace existing pods
|
||||
with new ones.
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
canaryPlan:
|
||||
description: CanaryStrategy defines parameters for a Replica Based
|
||||
|
|
@ -88,6 +95,12 @@ spec:
|
|||
canary version.
|
||||
type: string
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
canaryPlan:
|
||||
description: CanaryStrategy defines parameters for a Replica Based
|
||||
Canary
|
||||
properties:
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
steps:
|
||||
description: Steps define the order of phases to execute the
|
||||
canary deployment
|
||||
|
|
@ -95,6 +108,9 @@ spec:
|
|||
description: CanaryStep defines a step of a canary workload.
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
canaryReplicas:
|
||||
anyOf:
|
||||
- type: integer
|
||||
|
|
@ -104,8 +120,11 @@ spec:
|
|||
5) or a percentage of total pods. it is mutually exclusive
|
||||
with the PodList field'
|
||||
x-kubernetes-int-or-string: true
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
pause:
|
||||
description: Pause freezes the rollout by setting spec.Paused
|
||||
to true. A Rollout will resume when spec.Paused is
|
||||
|
|
@ -117,11 +136,15 @@ spec:
|
|||
format: int32
|
||||
type: integer
|
||||
type: object
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
weight:
|
||||
=======
|
||||
setWeight:
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
weight:
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
description: SetWeight sets what percentage of the canary
|
||||
pods should receive
|
||||
format: int32
|
||||
|
|
@ -133,6 +156,7 @@ spec:
|
|||
meshes supported to enable more fine-grained traffic routing
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
alb:
|
||||
description: AlbTrafficRouting configuration for Nginx
|
||||
|
|
@ -158,6 +182,8 @@ spec:
|
|||
- ingress
|
||||
type: object
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
nginx:
|
||||
description: Nginx holds Nginx Ingress specific configuration
|
||||
to route traffic
|
||||
|
|
@ -166,6 +192,7 @@ spec:
|
|||
description: Ingress refers to the name of an `Ingress`
|
||||
resource in the same namespace as the `Rollout`
|
||||
type: string
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
required:
|
||||
- ingress
|
||||
|
|
@ -208,46 +235,57 @@ spec:
|
|||
type: string
|
||||
type: object
|
||||
type: object
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
required:
|
||||
- ingress
|
||||
type: object
|
||||
type:
|
||||
service:
|
||||
description: Service holds the name of a service which
|
||||
selects pods with stable version and don't select any
|
||||
pods with canary version.
|
||||
type: string
|
||||
type:
|
||||
description: Nginx, Alb, Istio etc.
|
||||
type: string
|
||||
required:
|
||||
- type
|
||||
type: object
|
||||
type: object
|
||||
type: object
|
||||
targetRef:
|
||||
description: TargetRef contains enough information to let you identify
|
||||
a workload for Rollout
|
||||
properties:
|
||||
apiVersion:
|
||||
description: API Version of the referent
|
||||
type: string
|
||||
kind:
|
||||
description: Kind of the referent
|
||||
type: string
|
||||
name:
|
||||
description: Name of the referent
|
||||
paused:
|
||||
description: Paused indicates that the Rollout is paused. Default
|
||||
value is false
|
||||
type: boolean
|
||||
type:
|
||||
description: canaryPlan, BlueGreenPlan Default value is canaryPlan
|
||||
type: string
|
||||
required:
|
||||
- apiVersion
|
||||
- kind
|
||||
- name
|
||||
- type
|
||||
type: object
|
||||
required:
|
||||
- objectRef
|
||||
- strategy
|
||||
<<<<<<< HEAD
|
||||
- targetRef
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
type: object
|
||||
status:
|
||||
description: RolloutStatus defines the observed state of Rollout
|
||||
properties:
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
canaryRevision:
|
||||
description: CanaryRevision the hash of the canary pod template
|
||||
type: string
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
canaryRevision:
|
||||
description: CanaryRevision the hash of the canary pod template
|
||||
type: string
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
canaryStatus:
|
||||
description: Canary describes the state of the canary rollout
|
||||
properties:
|
||||
|
|
@ -340,11 +378,14 @@ spec:
|
|||
rolled out
|
||||
type: string
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
updateRevision:
|
||||
description: UpdateRevision the hash of the current pod template
|
||||
type: string
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
=======
|
||||
>>>>>>> 42b951e (completed-v1)
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -8,9 +8,6 @@ require (
|
|||
github.com/onsi/gomega v1.17.0
|
||||
github.com/openkruise/kruise-api v1.0.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
github.com/onsi/ginkgo v1.14.1
|
||||
github.com/onsi/gomega v1.10.2
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
k8s.io/api v0.20.10
|
||||
k8s.io/apiextensions-apiserver v0.20.1
|
||||
k8s.io/apimachinery v0.20.10
|
||||
|
|
|
|||
21
go.sum
21
go.sum
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< Updated upstream
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
|
|
@ -247,9 +246,11 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -300,11 +301,8 @@ github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
|||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
<<<<<<< HEAD
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
github.com/openkruise/kruise-api v1.0.0 h1:ScA0LxRRNBsgbcyLhTzR9B+KpGNWsIMptzzmjTqfYQo=
|
||||
github.com/openkruise/kruise-api v1.0.0/go.mod h1:kxV/UA/vrf/hz3z+kL21c0NOawC6K1ZjaKcJFgiOwsE=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
|
|
@ -314,6 +312,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
|
|
@ -372,6 +371,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
|
|
@ -392,10 +392,12 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.8.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
|
|
@ -432,6 +434,7 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
|||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
|
|
@ -440,6 +443,7 @@ golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -469,10 +473,6 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
|
|
@ -586,11 +586,8 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
|
|||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
<<<<<<< HEAD
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ=
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
|
@ -659,6 +656,7 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
|
|||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
|
@ -690,6 +688,7 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3 h1:sXmLre5bzIR6ypkjXCDI3jHPssRhc8KD/Ome589sc3U=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
|
||||
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
|
||||
|
|
|
|||
|
|
@ -70,10 +70,10 @@ func add(mgr manager.Manager, r reconcile.Reconciler) error {
|
|||
// Watch for changes to BatchRelease
|
||||
err = c.Watch(&source.Kind{Type: &v1alpha1.BatchRelease{}}, &handler.EnqueueRequestForObject{}, predicate.Funcs{
|
||||
UpdateFunc: func(e event.UpdateEvent) bool {
|
||||
old := e.ObjectOld.(*v1alpha1.BatchRelease)
|
||||
new := e.ObjectNew.(*v1alpha1.BatchRelease)
|
||||
if old.Generation != new.Generation {
|
||||
klog.V(3).Infof("Observed updated Spec for BatchRelease: %s/%s", new.Namespace, new.Name)
|
||||
oldObject := e.ObjectOld.(*v1alpha1.BatchRelease)
|
||||
newObject := e.ObjectNew.(*v1alpha1.BatchRelease)
|
||||
if oldObject.Generation != newObject.Generation || newObject.DeletionTimestamp != nil {
|
||||
klog.V(3).Infof("Observed updated Spec for BatchRelease: %s/%s", newObject.Namespace, newObject.Name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,998 @@
|
|||
package batchrelease
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
apimachineryruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
kruiseappsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/pkg/controller/batchrelease/workloads"
|
||||
)
|
||||
|
||||
const TIME_LAYOUT = "2006-01-02 15:04:05"
|
||||
|
||||
var (
|
||||
scheme *runtime.Scheme
|
||||
releaseDeploy = &v1alpha1.BatchRelease{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "BatchRelease",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "release",
|
||||
Namespace: "application",
|
||||
UID: uuid.NewUUID(),
|
||||
},
|
||||
Spec: v1alpha1.BatchReleaseSpec{
|
||||
TargetRef: v1alpha1.ObjectRef{
|
||||
WorkloadRef: &v1alpha1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "sample",
|
||||
},
|
||||
},
|
||||
ReleasePlan: v1alpha1.ReleasePlan{
|
||||
Batches: []v1alpha1.ReleaseBatch{
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("10%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("50%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("80%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stableDeploy = &apps.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: apps.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sample",
|
||||
Namespace: "application",
|
||||
UID: types.UID("87076677"),
|
||||
Generation: 2,
|
||||
Labels: map[string]string{
|
||||
"app": "busybox",
|
||||
apps.DefaultDeploymentUniqueLabelKey: "update-pod-hash",
|
||||
},
|
||||
},
|
||||
Spec: apps.DeploymentSpec{
|
||||
Replicas: pointer.Int32Ptr(100),
|
||||
Strategy: apps.DeploymentStrategy{
|
||||
Type: apps.RollingUpdateDeploymentStrategyType,
|
||||
RollingUpdate: &apps.RollingUpdateDeployment{
|
||||
MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
|
||||
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
},
|
||||
},
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: containers("v2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apps.DeploymentStatus{
|
||||
Replicas: 100,
|
||||
ReadyReplicas: 100,
|
||||
UpdatedReplicas: 0,
|
||||
AvailableReplicas: 100,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
releaseClone = &v1alpha1.BatchRelease{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "BatchRelease",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "release",
|
||||
Namespace: "application",
|
||||
UID: uuid.NewUUID(),
|
||||
},
|
||||
Spec: v1alpha1.BatchReleaseSpec{
|
||||
TargetRef: v1alpha1.ObjectRef{
|
||||
WorkloadRef: &v1alpha1.WorkloadRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "CloneSet",
|
||||
Name: "sample",
|
||||
},
|
||||
},
|
||||
ReleasePlan: v1alpha1.ReleasePlan{
|
||||
Batches: []v1alpha1.ReleaseBatch{
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("10%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("50%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("80%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stableClone = &kruiseappsv1alpha1.CloneSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: kruiseappsv1alpha1.SchemeGroupVersion.String(),
|
||||
Kind: "CloneSet",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sample",
|
||||
Namespace: "application",
|
||||
UID: types.UID("87076677"),
|
||||
Generation: 1,
|
||||
Labels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Spec: kruiseappsv1alpha1.CloneSetSpec{
|
||||
Replicas: pointer.Int32Ptr(100),
|
||||
UpdateStrategy: kruiseappsv1alpha1.CloneSetUpdateStrategy{
|
||||
Partition: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
|
||||
MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
},
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: containers("v2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: kruiseappsv1alpha1.CloneSetStatus{
|
||||
Replicas: 100,
|
||||
ReadyReplicas: 100,
|
||||
UpdatedReplicas: 0,
|
||||
UpdatedReadyReplicas: 0,
|
||||
ObservedGeneration: 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
scheme = runtime.NewScheme()
|
||||
apimachineryruntime.Must(apps.AddToScheme(scheme))
|
||||
apimachineryruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||
apimachineryruntime.Must(kruiseappsv1alpha1.AddToScheme(scheme))
|
||||
|
||||
controlInfo, _ := json.Marshal(metav1.NewControllerRef(releaseDeploy, releaseDeploy.GroupVersionKind()))
|
||||
stableDeploy.Annotations = map[string]string{
|
||||
workloads.BatchReleaseControlAnnotation: string(controlInfo),
|
||||
}
|
||||
stableClone.Annotations = map[string]string{
|
||||
workloads.BatchReleaseControlAnnotation: string(controlInfo),
|
||||
}
|
||||
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate := canaryTemplate.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
stableClone.Status.CurrentRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
stableClone.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
}
|
||||
|
||||
func TestReconcile_CloneSet(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
GetRelease func() client.Object
|
||||
GetCloneSet func() []client.Object
|
||||
ExpectedBatch int32
|
||||
ExpectedPhase v1alpha1.RolloutPhase
|
||||
ExpectedState v1alpha1.ReleasingBatchStateType
|
||||
}{
|
||||
// Following cases of Linear Transaction on State Machine
|
||||
{
|
||||
Name: "IfNeedProgress=false, Input-Phase=Initial, Output-Phase=Healthy",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseClone, v1alpha1.RolloutPhaseInitial)
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
return []client.Object{
|
||||
stableClone.DeepCopy(),
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseHealthy,
|
||||
},
|
||||
{
|
||||
Name: "IfNeedProgress=false, Input-Phase=Healthy, Output-Phase=Healthy",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseClone, v1alpha1.RolloutPhaseHealthy)
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
return []client.Object{
|
||||
stableClone.DeepCopy(),
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseHealthy,
|
||||
},
|
||||
{
|
||||
Name: "IfNeedProgress=true, Input-Phase=Healthy, Output-Phase=Preparing",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseClone, v1alpha1.RolloutPhaseHealthy)
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhasePreparing,
|
||||
},
|
||||
{
|
||||
Name: "Preparing, Input-Phase=Preparing, Output-Phase=Progressing",
|
||||
GetRelease: func() client.Object {
|
||||
release := setPhase(releaseClone, v1alpha1.RolloutPhasePreparing)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=Initialize, Output-State=Verify",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.InitializeBatchState)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.VerifyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=DoCanary, Output-State=Verify",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.DoCanaryBatchState)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.VerifyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=Verify, Output-State=BatchReady",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.VerifyBatchState)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0->1, Input-State=BatchReady, Output-State=Initialize",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = *getOldTime()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.InitializeBatchState,
|
||||
ExpectedBatch: 1,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0->1, Input-State=BatchReady, Output-State=BatchReady",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Special Case: Scaling, Input-State=BatchReady, Output-State=Initialize",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2").(*kruiseappsv1alpha1.CloneSet)
|
||||
stable.Spec.Replicas = pointer.Int32Ptr(200)
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.InitializeBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: RollBack, Input-Phase=Progressing, Output-Phase=Cancelled`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v1")
|
||||
canary := getCanaryWithStage(stable, "v1", 0, true).(*kruiseappsv1alpha1.CloneSet)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canary.Status.CurrentRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
canary.Status.UpdateRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseCancelled,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Deletion, Input-Phase=Progressing, Output-Phase=Terminating`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
release.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseTerminating,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Cancelled, Input-Phase=Progressing, Output-Phase=Cancelled`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
release.Spec.Cancelled = true
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseCancelled,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Continuous Release, Input-Phase=Progressing, Output-Phase=Initial`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseClone, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetCloneSet: func() []client.Object {
|
||||
stable := getStableWithReady(stableClone, "v3")
|
||||
canary := getCanaryWithStage(stable, "v3", 0, true).(*kruiseappsv1alpha1.CloneSet)
|
||||
stableTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v3")
|
||||
canary.Status.CurrentRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
canary.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return []client.Object{
|
||||
canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseInitial,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
release := cs.GetRelease()
|
||||
clonesets := cs.GetCloneSet()
|
||||
rec := record.NewFakeRecorder(100)
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(release).
|
||||
WithObjects(clonesets...).Build()
|
||||
reconciler := &BatchReleaseReconciler{
|
||||
Client: cli,
|
||||
recorder: rec,
|
||||
Scheme: scheme,
|
||||
executor: NewReleasePlanExecutor(cli, rec),
|
||||
}
|
||||
|
||||
key := client.ObjectKeyFromObject(release)
|
||||
request := reconcile.Request{NamespacedName: key}
|
||||
result, err := reconciler.Reconcile(context.TODO(), request)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.RequeueAfter).Should(BeNumerically(">=", int64(0)))
|
||||
|
||||
newRelease := v1alpha1.BatchRelease{}
|
||||
Expect(cli.Get(context.TODO(), key, &newRelease)).NotTo(HaveOccurred())
|
||||
Expect(newRelease.Status.Phase).Should(Equal(cs.ExpectedPhase))
|
||||
Expect(newRelease.Status.CanaryStatus.CurrentBatch).Should(Equal(cs.ExpectedBatch))
|
||||
Expect(newRelease.Status.CanaryStatus.ReleasingBatchState).Should(Equal(cs.ExpectedState))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReconcile_Deployment(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
GetRelease func() client.Object
|
||||
GetDeployments func() []client.Object
|
||||
ExpectedBatch int32
|
||||
ExpectedPhase v1alpha1.RolloutPhase
|
||||
ExpectedState v1alpha1.ReleasingBatchStateType
|
||||
}{
|
||||
// Following cases of Linear Transaction on State Machine
|
||||
{
|
||||
Name: "IfNeedProgress=false, Input-Phase=Initial, Output-Phase=Healthy",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseDeploy, v1alpha1.RolloutPhaseInitial)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
return []client.Object{
|
||||
stableDeploy.DeepCopy(),
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseHealthy,
|
||||
},
|
||||
{
|
||||
Name: "IfNeedProgress=false, Input-Phase=Healthy, Output-Phase=Healthy",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseDeploy, v1alpha1.RolloutPhaseHealthy)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
return []client.Object{
|
||||
stableDeploy.DeepCopy(),
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseHealthy,
|
||||
},
|
||||
{
|
||||
Name: "IfNeedProgress=true, Input-Phase=Healthy, Output-Phase=Preparing",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseDeploy, v1alpha1.RolloutPhaseHealthy)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhasePreparing,
|
||||
},
|
||||
{
|
||||
Name: "Preparing, Input-Phase=Preparing, Output-Phase=Progressing",
|
||||
GetRelease: func() client.Object {
|
||||
return setPhase(releaseDeploy, v1alpha1.RolloutPhasePreparing)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=Initialize, Output-State=Verify",
|
||||
GetRelease: func() client.Object {
|
||||
return setState(releaseDeploy, v1alpha1.InitializeBatchState)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.VerifyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=DoCanary, Output-State=Verify",
|
||||
GetRelease: func() client.Object {
|
||||
return setState(releaseDeploy, v1alpha1.DoCanaryBatchState)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", -1, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.VerifyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0, Input-State=Verify, Output-State=BatchReady",
|
||||
GetRelease: func() client.Object {
|
||||
return setState(releaseDeploy, v1alpha1.VerifyBatchState)
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0->1, Input-State=BatchReady, Output-State=Initialize",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = *getOldTime()
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.InitializeBatchState,
|
||||
ExpectedBatch: 1,
|
||||
},
|
||||
{
|
||||
Name: "Progressing, stage=0->1, Input-State=BatchReady, Output-State=BatchReady",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: "Special Case: Scaling, Input-State=BatchReady, Output-State=Initialize",
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
stable.Spec.Replicas = pointer.Int32Ptr(200)
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseProgressing,
|
||||
ExpectedState: v1alpha1.InitializeBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: RollBack, Input-Phase=Progressing, Output-Phase=Cancelled`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v1")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseCancelled,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Deletion, Input-Phase=Progressing, Output-Phase=Terminating`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
release.DeletionTimestamp = &metav1.Time{Time: time.Now()}
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseTerminating,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Cancelled, Input-Phase=Progressing, Output-Phase=Cancelled`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
release.Spec.Cancelled = true
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v2")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseCancelled,
|
||||
ExpectedState: v1alpha1.ReadyBatchState,
|
||||
},
|
||||
{
|
||||
Name: `Special Case: Continuous Release, Input-Phase=Progressing, Output-Phase=Initial`,
|
||||
GetRelease: func() client.Object {
|
||||
release := setState(releaseDeploy, v1alpha1.ReadyBatchState)
|
||||
release.Status.CanaryStatus.LastBatchReadyTime = metav1.Now()
|
||||
stableTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
canaryTemplate := stableDeploy.Spec.Template.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
canaryTemplate.Spec.Containers = containers("v2")
|
||||
release.Status.StableRevision = workloads.ComputeHash(stableTemplate, nil)
|
||||
release.Status.UpdateRevision = workloads.ComputeHash(canaryTemplate, nil)
|
||||
return release
|
||||
},
|
||||
GetDeployments: func() []client.Object {
|
||||
stable := getStableWithReady(stableDeploy, "v3")
|
||||
canary := getCanaryWithStage(stable, "v2", 0, true)
|
||||
return []client.Object{
|
||||
stable, canary,
|
||||
}
|
||||
},
|
||||
ExpectedPhase: v1alpha1.RolloutPhaseInitial,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
release := cs.GetRelease()
|
||||
deployments := cs.GetDeployments()
|
||||
rec := record.NewFakeRecorder(100)
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(release).
|
||||
WithObjects(deployments...).WithObjects(makeReplicaSets(deployments...)...).Build()
|
||||
reconciler := &BatchReleaseReconciler{
|
||||
Client: cli,
|
||||
recorder: rec,
|
||||
Scheme: scheme,
|
||||
executor: NewReleasePlanExecutor(cli, rec),
|
||||
}
|
||||
|
||||
key := client.ObjectKeyFromObject(release)
|
||||
request := reconcile.Request{NamespacedName: key}
|
||||
result, err := reconciler.Reconcile(context.TODO(), request)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.RequeueAfter).Should(BeNumerically(">=", int64(0)))
|
||||
|
||||
newRelease := v1alpha1.BatchRelease{}
|
||||
Expect(cli.Get(context.TODO(), key, &newRelease)).NotTo(HaveOccurred())
|
||||
Expect(newRelease.Status.Phase).Should(Equal(cs.ExpectedPhase))
|
||||
Expect(newRelease.Status.CanaryStatus.CurrentBatch).Should(Equal(cs.ExpectedBatch))
|
||||
Expect(newRelease.Status.CanaryStatus.ReleasingBatchState).Should(Equal(cs.ExpectedState))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func containers(version string) []corev1.Container {
|
||||
return []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: fmt.Sprintf("busybox:%v", version),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setPhase(release *v1alpha1.BatchRelease, phase v1alpha1.RolloutPhase) *v1alpha1.BatchRelease {
|
||||
r := release.DeepCopy()
|
||||
r.Status.Phase = phase
|
||||
switch phase {
|
||||
case v1alpha1.RolloutPhaseInitial, v1alpha1.RolloutPhaseHealthy:
|
||||
default:
|
||||
r.Status.ObservedWorkloadReplicas = 100
|
||||
r.Status.ObservedReleasePlanHash = hashReleasePlanBatches(&release.Spec.ReleasePlan)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func setState(release *v1alpha1.BatchRelease, state v1alpha1.ReleasingBatchStateType) *v1alpha1.BatchRelease {
|
||||
r := release.DeepCopy()
|
||||
r.Status.Phase = v1alpha1.RolloutPhaseProgressing
|
||||
r.Status.CanaryStatus.ReleasingBatchState = state
|
||||
r.Status.ObservedWorkloadReplicas = 100
|
||||
r.Status.ObservedReleasePlanHash = hashReleasePlanBatches(&release.Spec.ReleasePlan)
|
||||
return r
|
||||
}
|
||||
|
||||
func getStableWithReady(workload client.Object, version string) client.Object {
|
||||
switch workload.(type) {
|
||||
case *apps.Deployment:
|
||||
deploy := workload.(*apps.Deployment)
|
||||
d := deploy.DeepCopy()
|
||||
d.Spec.Paused = true
|
||||
d.Spec.Template.Spec.Containers = containers(version)
|
||||
d.Status.ObservedGeneration = deploy.Generation
|
||||
return d
|
||||
|
||||
case *kruiseappsv1alpha1.CloneSet:
|
||||
clone := workload.(*kruiseappsv1alpha1.CloneSet)
|
||||
c := clone.DeepCopy()
|
||||
c.Spec.UpdateStrategy.Paused = true
|
||||
c.Spec.UpdateStrategy.Partition = &intstr.IntOrString{Type: intstr.String, StrVal: "100%"}
|
||||
c.Spec.Template.Spec.Containers = containers(version)
|
||||
c.Status.ObservedGeneration = clone.Generation
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getCanaryWithStage(workload client.Object, version string, stage int, ready bool) client.Object {
|
||||
var err error
|
||||
var stageReplicas int
|
||||
|
||||
switch workload.(type) {
|
||||
case *apps.Deployment:
|
||||
deploy := workload.(*apps.Deployment)
|
||||
|
||||
if stage >= 0 {
|
||||
stageReplicas, err = intstr.GetScaledValueFromIntOrPercent(
|
||||
&releaseDeploy.Spec.ReleasePlan.Batches[stage].CanaryReplicas, int(*deploy.Spec.Replicas), true)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if !ready {
|
||||
stageReplicas -= 5
|
||||
}
|
||||
}
|
||||
|
||||
d := deploy.DeepCopy()
|
||||
d.Name += "-canary-324785678"
|
||||
d.UID = uuid.NewUUID()
|
||||
d.Spec.Paused = false
|
||||
d.Labels[workloads.CanaryDeploymentLabelKey] = string(deploy.UID)
|
||||
d.Spec.Replicas = pointer.Int32Ptr(int32(stageReplicas))
|
||||
d.Spec.Template.Spec.Containers = containers(version)
|
||||
d.Status.Replicas = int32(stageReplicas)
|
||||
d.Status.ReadyReplicas = int32(stageReplicas)
|
||||
d.Status.UpdatedReplicas = int32(stageReplicas)
|
||||
d.Status.AvailableReplicas = int32(stageReplicas)
|
||||
d.Status.ObservedGeneration = deploy.Generation
|
||||
d.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(releaseDeploy, releaseDeploy.GroupVersionKind())}
|
||||
controlInfo, _ := json.Marshal(metav1.NewControllerRef(releaseDeploy, releaseDeploy.GroupVersionKind()))
|
||||
d.Annotations = map[string]string{
|
||||
workloads.BatchReleaseControlAnnotation: string(controlInfo),
|
||||
}
|
||||
return d
|
||||
|
||||
case *kruiseappsv1alpha1.CloneSet:
|
||||
clone := workload.(*kruiseappsv1alpha1.CloneSet)
|
||||
|
||||
if stage >= 0 {
|
||||
stageReplicas, err = intstr.GetScaledValueFromIntOrPercent(
|
||||
&releaseClone.Spec.ReleasePlan.Batches[stage].CanaryReplicas, int(*clone.Spec.Replicas), true)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if !ready {
|
||||
stageReplicas -= 5
|
||||
}
|
||||
}
|
||||
|
||||
c := clone
|
||||
c.Spec.UpdateStrategy.Paused = false
|
||||
c.Spec.UpdateStrategy.Partition = &intstr.IntOrString{Type: intstr.Int, IntVal: *c.Spec.Replicas - int32(stageReplicas)}
|
||||
c.Spec.Template.Spec.Containers = containers(version)
|
||||
c.Status.Replicas = *c.Spec.Replicas
|
||||
c.Status.UpdatedReplicas = int32(stageReplicas)
|
||||
c.Status.UpdatedReadyReplicas = int32(stageReplicas)
|
||||
c.Status.ReadyReplicas = c.Status.Replicas
|
||||
c.Status.ObservedGeneration = clone.Generation
|
||||
controlInfo, _ := json.Marshal(metav1.NewControllerRef(releaseClone, releaseClone.GroupVersionKind()))
|
||||
c.Annotations = map[string]string{
|
||||
workloads.BatchReleaseControlAnnotation: string(controlInfo),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeReplicaSets(deploys ...client.Object) []client.Object {
|
||||
var rss []client.Object
|
||||
for _, d := range deploys {
|
||||
deploy := d.(*apps.Deployment)
|
||||
labels := deploy.Spec.Selector.DeepCopy().MatchLabels
|
||||
labels[apps.DefaultDeploymentUniqueLabelKey] = workloads.ComputeHash(&deploy.Spec.Template, nil)
|
||||
rss = append(rss, &apps.ReplicaSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: apps.SchemeGroupVersion.String(),
|
||||
Kind: "ReplicaSet",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: deploy.Name + rand.String(5),
|
||||
Namespace: deploy.Namespace,
|
||||
UID: uuid.NewUUID(),
|
||||
Labels: labels,
|
||||
OwnerReferences: []metav1.OwnerReference{
|
||||
*metav1.NewControllerRef(deploy, deploy.GroupVersionKind()),
|
||||
},
|
||||
},
|
||||
Spec: apps.ReplicaSetSpec{
|
||||
Replicas: deploy.Spec.Replicas,
|
||||
Selector: deploy.Spec.Selector.DeepCopy(),
|
||||
Template: *deploy.Spec.Template.DeepCopy(),
|
||||
},
|
||||
})
|
||||
}
|
||||
return rss
|
||||
}
|
||||
|
||||
func getOldTime() *metav1.Time {
|
||||
time, _ := time.Parse(TIME_LAYOUT, "2018-09-10 00:00:00")
|
||||
return &metav1.Time{Time: time}
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func (w workloadEventHandler) Create(evt event.CreateEvent, q workqueue.RateLimi
|
|||
}
|
||||
|
||||
func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
|
||||
var oldAccessor, newAccessor *workloads.Accessor
|
||||
var oldAccessor, newAccessor *workloads.WorkloadAccessor
|
||||
var gvk schema.GroupVersionKind
|
||||
|
||||
switch evt.ObjectNew.(type) {
|
||||
|
|
@ -76,7 +76,7 @@ func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimi
|
|||
newReplicas = *newClone.Spec.Replicas
|
||||
}
|
||||
|
||||
oldAccessor = &workloads.Accessor{
|
||||
oldAccessor = &workloads.WorkloadAccessor{
|
||||
Replicas: &oldReplicas,
|
||||
Paused: oldClone.Spec.UpdateStrategy.Paused,
|
||||
Status: &workloads.Status{
|
||||
|
|
@ -89,7 +89,7 @@ func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimi
|
|||
Metadata: &oldClone.ObjectMeta,
|
||||
}
|
||||
|
||||
newAccessor = &workloads.Accessor{
|
||||
newAccessor = &workloads.WorkloadAccessor{
|
||||
Replicas: &newReplicas,
|
||||
Paused: newClone.Spec.UpdateStrategy.Paused,
|
||||
Status: &workloads.Status{
|
||||
|
|
@ -115,7 +115,7 @@ func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimi
|
|||
newReplicas = *newDeploy.Spec.Replicas
|
||||
}
|
||||
|
||||
oldAccessor = &workloads.Accessor{
|
||||
oldAccessor = &workloads.WorkloadAccessor{
|
||||
Replicas: &oldReplicas,
|
||||
Paused: oldDeploy.Spec.Paused,
|
||||
Status: &workloads.Status{
|
||||
|
|
@ -127,7 +127,7 @@ func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimi
|
|||
Metadata: &oldDeploy.ObjectMeta,
|
||||
}
|
||||
|
||||
newAccessor = &workloads.Accessor{
|
||||
newAccessor = &workloads.WorkloadAccessor{
|
||||
Replicas: &newReplicas,
|
||||
Paused: newDeploy.Spec.Paused,
|
||||
Status: &workloads.Status{
|
||||
|
|
@ -153,29 +153,17 @@ func (w workloadEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimi
|
|||
Name: newAccessor.Metadata.Name,
|
||||
}
|
||||
|
||||
controllerInfo, controlled := newAccessor.Metadata.Annotations[workloads.BatchReleaseControlAnnotation]
|
||||
if controlled && len(controllerInfo) > 0 {
|
||||
br := &metav1.OwnerReference{}
|
||||
if err := json.Unmarshal([]byte(controllerInfo), br); err == nil {
|
||||
klog.V(3).Infof("%s (%v) is managed by BatchRelease (%s), append queue", gvk.Kind, workloadNsn, br.Name)
|
||||
nsn := types.NamespacedName{Namespace: workloadNsn.Namespace, Name: br.Name}
|
||||
q.Add(reconcile.Request{NamespacedName: nsn})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
br, err := w.getBatchRelease(workloadNsn, gvk)
|
||||
brNsn, err := w.getBatchRelease(workloadNsn, gvk, newAccessor.Metadata.Annotations[workloads.BatchReleaseControlAnnotation])
|
||||
if err != nil {
|
||||
klog.Errorf("unable to get BatchRelease related with %s (%s/%s), err: %v",
|
||||
gvk.Kind, workloadNsn.Namespace, workloadNsn.Name, err)
|
||||
return
|
||||
}
|
||||
|
||||
if br != nil {
|
||||
klog.V(3).Infof("%s (%s/%s) changed generation from %d to %d managed by BatchRelease (%s/%s)",
|
||||
gvk.Kind, workloadNsn.Namespace, workloadNsn.Name, oldAccessor.Metadata.Generation, newAccessor.Metadata.Generation, br.GetNamespace(), br.GetName())
|
||||
nsn := types.NamespacedName{Namespace: br.GetNamespace(), Name: br.GetName()}
|
||||
q.Add(reconcile.Request{NamespacedName: nsn})
|
||||
if len(brNsn.Name) != 0 {
|
||||
klog.V(3).Infof("%s (%s/%s) changed generation from %d to %d managed by BatchRelease (%v)",
|
||||
gvk.Kind, workloadNsn.Namespace, workloadNsn.Name, oldAccessor.Metadata.Generation, newAccessor.Metadata.Generation, brNsn)
|
||||
q.Add(reconcile.Request{NamespacedName: brNsn})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -189,12 +177,15 @@ func (w workloadEventHandler) Generic(evt event.GenericEvent, q workqueue.RateLi
|
|||
|
||||
func (w *workloadEventHandler) handleWorkload(q workqueue.RateLimitingInterface,
|
||||
obj client.Object, action EventAction) {
|
||||
var controlInfo string
|
||||
var gvk schema.GroupVersionKind
|
||||
switch obj.(type) {
|
||||
case *kruiseappsv1alpha1.CloneSet:
|
||||
gvk = controllerKruiseKindCS
|
||||
controlInfo = obj.(*kruiseappsv1alpha1.CloneSet).Annotations[workloads.BatchReleaseControlAnnotation]
|
||||
case *appsv1.Deployment:
|
||||
gvk = controllerKindDep
|
||||
controlInfo = obj.(*appsv1.Deployment).Annotations[workloads.BatchReleaseControlAnnotation]
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
|
@ -203,78 +194,89 @@ func (w *workloadEventHandler) handleWorkload(q workqueue.RateLimitingInterface,
|
|||
Namespace: obj.GetNamespace(),
|
||||
Name: obj.GetName(),
|
||||
}
|
||||
ws, err := w.getBatchRelease(workloadNsn, gvk)
|
||||
brNsn, err := w.getBatchRelease(workloadNsn, gvk, controlInfo)
|
||||
if err != nil {
|
||||
klog.Errorf("unable to get BatchRelease related with %s (%s/%s), err: %v",
|
||||
gvk.Kind, workloadNsn.Namespace, workloadNsn.Name, err)
|
||||
return
|
||||
}
|
||||
if ws != nil {
|
||||
klog.V(5).Infof("%s %s (%s/%s) and reconcile BatchRelease (%s/%s)",
|
||||
action, gvk.Kind, workloadNsn.Namespace, workloadNsn.Namespace, ws.Namespace, ws.Name)
|
||||
nsn := types.NamespacedName{Namespace: ws.GetNamespace(), Name: ws.GetName()}
|
||||
q.Add(reconcile.Request{NamespacedName: nsn})
|
||||
if len(brNsn.Name) != 0 {
|
||||
klog.V(5).Infof("%s %s (%s/%s) and reconcile BatchRelease (%v)",
|
||||
action, gvk.Kind, workloadNsn.Namespace, workloadNsn.Namespace, brNsn)
|
||||
q.Add(reconcile.Request{NamespacedName: brNsn})
|
||||
}
|
||||
}
|
||||
|
||||
func (w *workloadEventHandler) getBatchRelease(workloadNamespaceName types.NamespacedName, gvk schema.GroupVersionKind) (*v1alpha1.BatchRelease, error) {
|
||||
bsList := &v1alpha1.BatchReleaseList{}
|
||||
listOptions := &client.ListOptions{Namespace: workloadNamespaceName.Namespace}
|
||||
if err := w.List(context.TODO(), bsList, listOptions); err != nil {
|
||||
klog.Errorf("List BatchRelease failed: %s", err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, bs := range bsList.Items {
|
||||
if bs.DeletionTimestamp != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
targetRef := bs.Spec.TargetRef
|
||||
targetGV, err := schema.ParseGroupVersion(targetRef.APIVersion)
|
||||
func (w *workloadEventHandler) getBatchRelease(workloadNamespaceName types.NamespacedName, gvk schema.GroupVersionKind, controlInfo string) (nsn types.NamespacedName, err error) {
|
||||
if len(controlInfo) > 0 {
|
||||
br := &metav1.OwnerReference{}
|
||||
err = json.Unmarshal([]byte(controlInfo), br)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse targetRef's group version: %s", targetRef.APIVersion)
|
||||
continue
|
||||
klog.Errorf("Failed to unmarshal controller info annotations for %v(%v)", gvk, workloadNamespaceName)
|
||||
}
|
||||
|
||||
if targetRef.Kind == gvk.Kind && targetGV.Group == gvk.Group && targetRef.Name == workloadNamespaceName.Name {
|
||||
return &bs, nil
|
||||
if br.APIVersion == v1alpha1.GroupVersion.String() && br.Kind == "BatchRelease" {
|
||||
klog.V(3).Infof("%s (%v) is managed by BatchRelease (%s), append queue", gvk.Kind, workloadNamespaceName, br.Name)
|
||||
nsn = types.NamespacedName{Namespace: workloadNamespaceName.Namespace, Name: br.Name}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
brList := &v1alpha1.BatchReleaseList{}
|
||||
listOptions := &client.ListOptions{Namespace: workloadNamespaceName.Namespace}
|
||||
if err = w.List(context.TODO(), brList, listOptions); err != nil {
|
||||
klog.Errorf("List BatchRelease failed: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for i := range brList.Items {
|
||||
br := &brList.Items[i]
|
||||
targetRef := br.Spec.TargetRef
|
||||
targetGV, err := schema.ParseGroupVersion(targetRef.WorkloadRef.APIVersion)
|
||||
if err != nil {
|
||||
klog.Errorf("failed to parse targetRef's group version: %s", targetRef.WorkloadRef.APIVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
if targetRef.WorkloadRef.Kind == gvk.Kind && targetGV.Group == gvk.Group && targetRef.WorkloadRef.Name == workloadNamespaceName.Name {
|
||||
nsn = client.ObjectKeyFromObject(br)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func observeGenerationChanged(newOne, oldOne *workloads.Accessor) bool {
|
||||
func observeGenerationChanged(newOne, oldOne *workloads.WorkloadAccessor) bool {
|
||||
return newOne.Metadata.Generation != oldOne.Metadata.Generation
|
||||
}
|
||||
|
||||
func observeLatestGeneration(newOne, oldOne *workloads.Accessor) bool {
|
||||
func observeLatestGeneration(newOne, oldOne *workloads.WorkloadAccessor) bool {
|
||||
oldNot := oldOne.Metadata.Generation != oldOne.Status.ObservedGeneration
|
||||
newDid := newOne.Metadata.Generation == newOne.Status.ObservedGeneration
|
||||
return oldNot && newDid
|
||||
}
|
||||
|
||||
func observeScaleEventDone(newOne, oldOne *workloads.Accessor) bool {
|
||||
func observeScaleEventDone(newOne, oldOne *workloads.WorkloadAccessor) bool {
|
||||
_, controlled := newOne.Metadata.Annotations[workloads.BatchReleaseControlAnnotation]
|
||||
if !controlled {
|
||||
return false
|
||||
}
|
||||
|
||||
oldScaling := oldOne.Replicas != newOne.Replicas ||
|
||||
oldScaling := *oldOne.Replicas != *newOne.Replicas ||
|
||||
*oldOne.Replicas != oldOne.Status.Replicas
|
||||
newDone := newOne.Metadata.Generation == newOne.Status.ObservedGeneration &&
|
||||
*oldOne.Replicas == oldOne.Status.Replicas
|
||||
*newOne.Replicas == newOne.Status.Replicas
|
||||
return oldScaling && newDone
|
||||
}
|
||||
|
||||
func observeReplicasChanged(newOne, oldOne *workloads.Accessor) bool {
|
||||
func observeReplicasChanged(newOne, oldOne *workloads.WorkloadAccessor) bool {
|
||||
_, controlled := newOne.Metadata.Annotations[workloads.BatchReleaseControlAnnotation]
|
||||
if !controlled {
|
||||
return false
|
||||
}
|
||||
|
||||
return oldOne.Status.Replicas != newOne.Status.Replicas ||
|
||||
return *oldOne.Replicas != *newOne.Replicas ||
|
||||
oldOne.Status.Replicas != newOne.Status.Replicas ||
|
||||
oldOne.Status.ReadyReplicas != newOne.Status.ReadyReplicas ||
|
||||
oldOne.Status.UpdatedReplicas != newOne.Status.UpdatedReplicas ||
|
||||
oldOne.Status.UpdatedReadyReplicas != newOne.Status.UpdatedReadyReplicas
|
||||
|
|
|
|||
|
|
@ -0,0 +1,238 @@
|
|||
package batchrelease
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
"github.com/openkruise/rollouts/pkg/controller/batchrelease/workloads"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestEventHandler_Update(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
GetOldWorkload func() client.Object
|
||||
GetNewWorkload func() client.Object
|
||||
ExpectedQueueLen int
|
||||
}{
|
||||
{
|
||||
Name: "Deployment Batch NotReady -> Ready",
|
||||
GetOldWorkload: func() client.Object {
|
||||
return getCanaryWithStage(stableDeploy, "v2", 0, false)
|
||||
},
|
||||
GetNewWorkload: func() client.Object {
|
||||
return getCanaryWithStage(stableDeploy, "v2", 0, true)
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment Generation 1 -> 2",
|
||||
GetOldWorkload: func() client.Object {
|
||||
oldObject := getStableWithReady(stableDeploy, "v2")
|
||||
oldObject.SetGeneration(1)
|
||||
return oldObject
|
||||
},
|
||||
GetNewWorkload: func() client.Object {
|
||||
newObject := getStableWithReady(stableDeploy, "v2")
|
||||
newObject.SetGeneration(2)
|
||||
return newObject
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment watched the latest generation",
|
||||
GetOldWorkload: func() client.Object {
|
||||
oldObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
oldObject.SetGeneration(2)
|
||||
oldObject.Status.ObservedGeneration = 1
|
||||
return oldObject
|
||||
},
|
||||
GetNewWorkload: func() client.Object {
|
||||
newObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
newObject.SetGeneration(2)
|
||||
newObject.Status.ObservedGeneration = 2
|
||||
return newObject
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment scaling done",
|
||||
GetOldWorkload: func() client.Object {
|
||||
oldObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
oldObject.SetGeneration(2)
|
||||
oldObject.Status.ObservedGeneration = 2
|
||||
oldObject.Spec.Replicas = pointer.Int32Ptr(1000)
|
||||
return oldObject
|
||||
},
|
||||
GetNewWorkload: func() client.Object {
|
||||
newObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
newObject.SetGeneration(2)
|
||||
newObject.Status.ObservedGeneration = 2
|
||||
newObject.Spec.Replicas = pointer.Int32Ptr(1000)
|
||||
newObject.Status.Replicas = 1000
|
||||
return newObject
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment available pod changed",
|
||||
GetOldWorkload: func() client.Object {
|
||||
oldObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
oldObject.SetGeneration(2)
|
||||
oldObject.Status.ObservedGeneration = 2
|
||||
oldObject.Status.AvailableReplicas = 20
|
||||
return oldObject
|
||||
},
|
||||
GetNewWorkload: func() client.Object {
|
||||
newObject := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
newObject.SetGeneration(2)
|
||||
newObject.Status.ObservedGeneration = 2
|
||||
newObject.Status.AvailableReplicas = 50
|
||||
return newObject
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
oldObject := cs.GetOldWorkload()
|
||||
newObject := cs.GetNewWorkload()
|
||||
newSJk := scheme
|
||||
fmt.Println(newSJk)
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(releaseDeploy.DeepCopy()).Build()
|
||||
handler := workloadEventHandler{Reader: cli}
|
||||
updateQ := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
updateEvt := event.UpdateEvent{
|
||||
ObjectOld: oldObject,
|
||||
ObjectNew: newObject,
|
||||
}
|
||||
handler.Update(updateEvt, updateQ)
|
||||
Expect(updateQ.Len()).Should(Equal(cs.ExpectedQueueLen))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventHandler_Create(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
GetNewWorkload func() client.Object
|
||||
ExpectedQueueLen int
|
||||
}{
|
||||
{
|
||||
Name: "Deployment with controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
return getCanaryWithStage(stableDeploy, "v2", 0, true)
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment without controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
object := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
object.Annotations = nil
|
||||
return object
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment with wrong controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
object := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
controlInfo, _ := json.Marshal(&metav1.OwnerReference{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "Rollout",
|
||||
Name: "whatever",
|
||||
})
|
||||
object.SetName("another")
|
||||
object.Annotations[workloads.BatchReleaseControlAnnotation] = string(controlInfo)
|
||||
return object
|
||||
},
|
||||
ExpectedQueueLen: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
newObject := cs.GetNewWorkload()
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(releaseDeploy.DeepCopy()).Build()
|
||||
handler := workloadEventHandler{Reader: cli}
|
||||
createQ := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
createEvt := event.CreateEvent{
|
||||
Object: newObject,
|
||||
}
|
||||
handler.Create(createEvt, createQ)
|
||||
Expect(createQ.Len()).Should(Equal(cs.ExpectedQueueLen))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventHandler_Delete(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
GetNewWorkload func() client.Object
|
||||
ExpectedQueueLen int
|
||||
}{
|
||||
{
|
||||
Name: "Deployment with controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
return getCanaryWithStage(stableDeploy, "v2", 0, true)
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment without controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
object := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
object.Annotations = nil
|
||||
return object
|
||||
},
|
||||
ExpectedQueueLen: 1,
|
||||
},
|
||||
{
|
||||
Name: "Deployment with wrong controller info annotation",
|
||||
GetNewWorkload: func() client.Object {
|
||||
object := getStableWithReady(stableDeploy, "v2").(*apps.Deployment)
|
||||
controlInfo, _ := json.Marshal(&metav1.OwnerReference{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "Rollout",
|
||||
Name: "whatever",
|
||||
})
|
||||
object.SetName("another")
|
||||
object.Annotations[workloads.BatchReleaseControlAnnotation] = string(controlInfo)
|
||||
return object
|
||||
},
|
||||
ExpectedQueueLen: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
newObject := cs.GetNewWorkload()
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(releaseDeploy.DeepCopy()).Build()
|
||||
handler := workloadEventHandler{Reader: cli}
|
||||
deleteQ := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
|
||||
deleteEvt := event.DeleteEvent{
|
||||
Object: newObject,
|
||||
}
|
||||
handler.Delete(deleteEvt, deleteQ)
|
||||
Expect(deleteQ.Len()).Should(Equal(cs.ExpectedQueueLen))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ func (r *Executor) SetReleaseInfo(release *v1alpha1.BatchRelease) {
|
|||
// Do execute the release plan
|
||||
func (r *Executor) Do() (reconcile.Result, *v1alpha1.BatchReleaseStatus) {
|
||||
klog.V(3).InfoS("Reconcile the release plan",
|
||||
"target-workload", r.release.Spec.TargetRef.Name)
|
||||
"target-workload", r.release.Spec.TargetRef.WorkloadRef.Name)
|
||||
|
||||
klog.V(3).InfoS("release-status:",
|
||||
"release-phase", r.releaseStatus.Phase,
|
||||
|
|
@ -77,34 +77,29 @@ func (r *Executor) executeBatchReleasePlan(workloadController workloads.Workload
|
|||
retryDuration := reconcile.Result{}
|
||||
|
||||
switch status.Phase {
|
||||
case v1alpha1.RolloutPhaseHealthy:
|
||||
r.releaseStatus.Phase = v1alpha1.RolloutPhaseVerify
|
||||
fallthrough
|
||||
case v1alpha1.RolloutPhaseInitial:
|
||||
// if this batchRelease was created but workload doest not exist,
|
||||
// should keep this phase and do nothing util workload is created.
|
||||
|
||||
case v1alpha1.RolloutPhaseVerify:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseVerify)
|
||||
case v1alpha1.RolloutPhaseHealthy:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseHealthy)
|
||||
// verify whether the workload is ready to execute the release plan in this state.
|
||||
verified, err := workloadController.VerifySpec()
|
||||
needed, err := workloadController.IfNeedToProgress()
|
||||
switch {
|
||||
case err != nil:
|
||||
setCondition(r.releaseStatus, "VerifyWorkloadError", err.Error(), v1.ConditionFalse)
|
||||
case verified:
|
||||
case needed:
|
||||
setCondition(r.releaseStatus, "VerifyWorkloadSuccessfully", "", v1.ConditionTrue)
|
||||
status.Phase = v1alpha1.RolloutPhaseInitial
|
||||
status.Phase = v1alpha1.RolloutPhasePreparing
|
||||
fallthrough
|
||||
default:
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
}
|
||||
|
||||
case v1alpha1.RolloutPhaseInitial:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseInitial)
|
||||
r.releaseStatus.Phase = v1alpha1.RolloutPhasePreparing
|
||||
fallthrough
|
||||
|
||||
case v1alpha1.RolloutPhasePreparing:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhasePreparing)
|
||||
// prepare and initialize something before progressing in this state.
|
||||
initialized, err := workloadController.Initialize()
|
||||
initialized, err := workloadController.Prepare()
|
||||
switch {
|
||||
case err != nil:
|
||||
setCondition(r.releaseStatus, "InitializeError", err.Error(), v1.ConditionFalse)
|
||||
|
|
@ -120,8 +115,7 @@ func (r *Executor) executeBatchReleasePlan(workloadController workloads.Workload
|
|||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseProgressing)
|
||||
// progress the release plan in this state.
|
||||
var progressDone bool
|
||||
progressDone, retryDuration = r.progressBatches(workloadController)
|
||||
if progressDone {
|
||||
if progressDone, retryDuration = r.progressBatches(workloadController); progressDone {
|
||||
setCondition(r.releaseStatus, "ProgressSuccessfully", "", v1.ConditionTrue)
|
||||
status.Phase = v1alpha1.RolloutPhaseFinalizing
|
||||
}
|
||||
|
|
@ -132,33 +126,39 @@ func (r *Executor) executeBatchReleasePlan(workloadController workloads.Workload
|
|||
if succeed := workloadController.Finalize(false, false); succeed {
|
||||
cleanupConditions(status)
|
||||
status.Phase = v1alpha1.RolloutPhaseCompleted
|
||||
} else {
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
}
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
|
||||
case v1alpha1.RolloutPhaseRollingBack:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseRollingBack)
|
||||
case v1alpha1.RolloutPhaseRollback:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseRollback)
|
||||
// restore the workload in this state
|
||||
cleanup := metav1.GetControllerOf(r.release) == nil
|
||||
if succeed := workloadController.Finalize(false, cleanup); succeed {
|
||||
pause, clean := false, true
|
||||
if workloads.IsControlledByRollout(r.release) {
|
||||
pause, clean = true, false
|
||||
}
|
||||
if succeed := workloadController.Finalize(pause, clean); succeed {
|
||||
cleanupConditions(status)
|
||||
status.Phase = v1alpha1.RolloutPhaseCancelled
|
||||
} else {
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
}
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
|
||||
case v1alpha1.RolloutPhaseCompleted:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseCompleted)
|
||||
// this state indicates that the plan is executed successfully, should do nothing in this state.
|
||||
|
||||
case v1alpha1.RolloutPhaseTerminating:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseTerminating)
|
||||
if succeed := workloadController.Finalize(true, true); succeed {
|
||||
if succeed := workloadController.Finalize(false, true); succeed {
|
||||
if r.release.DeletionTimestamp != nil {
|
||||
setCondition(status, v1alpha1.TerminatingReasonInTerminating, "Release plan was cancelled or deleted", v1.ConditionTrue)
|
||||
} else {
|
||||
status.Phase = v1alpha1.RolloutPhaseCancelled
|
||||
}
|
||||
} else {
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
}
|
||||
retryDuration = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
|
||||
case v1alpha1.RolloutPhaseCompleted:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseCompleted)
|
||||
// this state indicates that the plan is executed successfully, should do nothing in this state.
|
||||
|
||||
case v1alpha1.RolloutPhaseCancelled:
|
||||
klog.V(3).Infof("ReleasePlan State Machine into %s state", v1alpha1.RolloutPhaseCancelled)
|
||||
|
|
@ -231,7 +231,7 @@ func (r *Executor) progressBatches(workloadController workloads.WorkloadControll
|
|||
|
||||
// GetWorkloadController pick the right workload controller to work on the workload
|
||||
func (r *Executor) GetWorkloadController() (workloads.WorkloadController, error) {
|
||||
targetRef := r.release.Spec.TargetRef
|
||||
targetRef := r.release.Spec.TargetRef.WorkloadRef
|
||||
targetKey := types.NamespacedName{
|
||||
Namespace: r.release.Namespace,
|
||||
Name: targetRef.Name,
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package batchrelease
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
const (
|
||||
Keep = "Keep"
|
||||
Start = "Start"
|
||||
Restart = "Restart"
|
||||
RollingBack = "RollingBack"
|
||||
Terminating = "Terminating"
|
||||
|
|
@ -32,8 +33,8 @@ func (r *Executor) handleSpecialCases(controller workloads.WorkloadController) (
|
|||
// Note: must keep the order of the following cases
|
||||
switch {
|
||||
case r.releasePlanTerminating():
|
||||
reason = "PlanCancelled"
|
||||
message = "release plan is cancelled, clean up and stop reconcile"
|
||||
reason = "PlanTerminating"
|
||||
message = "release plan is terminating, clean up and stop reconcile"
|
||||
needStopThisRound = false
|
||||
action = Terminating
|
||||
|
||||
|
|
@ -59,16 +60,20 @@ func (r *Executor) handleSpecialCases(controller workloads.WorkloadController) (
|
|||
case r.releasePlanUnhealthy():
|
||||
reason = "PlanStatusUnhealthy"
|
||||
message = "release plan status is unhealthy, try to restart release plan"
|
||||
needStopThisRound = false
|
||||
needStopThisRound = true
|
||||
action = Restart
|
||||
|
||||
case r.releasePlanChanged():
|
||||
reason = "PlanChanged"
|
||||
message = "release plan was changed, try to recalculate canary status"
|
||||
needStopThisRound = false
|
||||
needStopThisRound = true
|
||||
action = Recalculate
|
||||
|
||||
case workloadEvent == workloads.WorkloadStableOrRollback:
|
||||
case r.locatedWorkloadAndStart(err):
|
||||
needStopThisRound = false
|
||||
action = Start
|
||||
|
||||
case workloadEvent == workloads.WorkloadRollback:
|
||||
reason = "StableOrRollback"
|
||||
message = "workload is table or rolling back, stop the release plan"
|
||||
needStopThisRound = false
|
||||
|
|
@ -83,7 +88,7 @@ func (r *Executor) handleSpecialCases(controller workloads.WorkloadController) (
|
|||
case workloadEvent == workloads.WorkloadPodTemplateChanged:
|
||||
reason = "RevisionChanged"
|
||||
message = "workload revision was changed, try to restart release plan"
|
||||
needStopThisRound = false
|
||||
needStopThisRound = true
|
||||
action = Restart
|
||||
|
||||
case workloadEvent == workloads.WorkloadUnHealthy:
|
||||
|
|
@ -140,14 +145,18 @@ func (r *Executor) handleSpecialCases(controller workloads.WorkloadController) (
|
|||
switch action {
|
||||
case Keep:
|
||||
// keep current status, do nothing
|
||||
case Restart:
|
||||
signalRestart(r.releaseStatus)
|
||||
case Recalculate:
|
||||
signalRecalculate(r.releaseStatus)
|
||||
case Start:
|
||||
signalStart(r.releaseStatus)
|
||||
case RollingBack:
|
||||
signalRollingBack(r.releaseStatus)
|
||||
case Terminating:
|
||||
signalTerminating(r.releaseStatus)
|
||||
case Restart:
|
||||
signalRestart(r.releaseStatus)
|
||||
result = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
case Recalculate:
|
||||
signalRecalculate(r.releaseStatus)
|
||||
result = reconcile.Result{RequeueAfter: DefaultShortDuration}
|
||||
}
|
||||
|
||||
return needStopThisRound, result
|
||||
|
|
@ -165,8 +174,12 @@ func (r *Executor) releasePlanChanged() bool {
|
|||
return r.isProgressing() && r.releaseStatus.ObservedReleasePlanHash != hashReleasePlanBatches(r.releasePlan)
|
||||
}
|
||||
|
||||
func (r *Executor) locatedWorkloadAndStart(err error) bool {
|
||||
return err == nil && r.releaseStatus.Phase == v1alpha1.RolloutPhaseInitial
|
||||
}
|
||||
|
||||
func (r *Executor) workloadHasGone(err error) bool {
|
||||
return !r.isTerminating() && errors.IsNotFound(err)
|
||||
return !r.isTerminating() && r.releaseStatus.Phase != v1alpha1.RolloutPhaseInitial && errors.IsNotFound(err)
|
||||
}
|
||||
|
||||
func (r *Executor) releasePlanPaused() bool {
|
||||
|
|
@ -178,9 +191,10 @@ func (r *Executor) releasePlanPaused() bool {
|
|||
}
|
||||
|
||||
func (r *Executor) isTerminating() bool {
|
||||
return r.release.Spec.Cancelled ||
|
||||
r.release.DeletionTimestamp != nil ||
|
||||
r.release.Status.Phase == v1alpha1.RolloutPhaseTerminating
|
||||
return r.release.DeletionTimestamp != nil ||
|
||||
r.release.Status.Phase == v1alpha1.RolloutPhaseTerminating ||
|
||||
(r.release.Spec.Cancelled && r.releaseStatus.Phase != v1alpha1.RolloutPhaseCancelled)
|
||||
|
||||
}
|
||||
|
||||
func (r *Executor) isProgressing() bool {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ func initializeStatusIfNeeds(status *v1alpha1.BatchReleaseStatus) {
|
|||
}
|
||||
}
|
||||
|
||||
func signalStart(status *v1alpha1.BatchReleaseStatus) {
|
||||
status.Phase = v1alpha1.RolloutPhaseHealthy
|
||||
}
|
||||
|
||||
func signalRestart(status *v1alpha1.BatchReleaseStatus) {
|
||||
resetStatus(status)
|
||||
}
|
||||
|
|
@ -47,11 +51,11 @@ func signalTerminating(status *v1alpha1.BatchReleaseStatus) {
|
|||
}
|
||||
|
||||
func signalRollingBack(status *v1alpha1.BatchReleaseStatus) {
|
||||
status.Phase = v1alpha1.RolloutPhaseRollingBack
|
||||
status.Phase = v1alpha1.RolloutPhaseRollback
|
||||
}
|
||||
|
||||
func resetStatus(status *v1alpha1.BatchReleaseStatus) {
|
||||
status.Phase = v1alpha1.RolloutPhaseHealthy
|
||||
status.Phase = v1alpha1.RolloutPhaseInitial
|
||||
status.StableRevision = ""
|
||||
status.UpdateRevision = ""
|
||||
status.ObservedReleasePlanHash = ""
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ func NewCloneSetRolloutController(client client.Client, recorder record.EventRec
|
|||
}
|
||||
}
|
||||
|
||||
// VerifySpec verifies that the rollout resource is consistent with the rollout spec
|
||||
func (c *CloneSetRolloutController) VerifySpec() (bool, error) {
|
||||
// IfNeedToProgress verifies that the workload is ready to execute release plan
|
||||
func (c *CloneSetRolloutController) IfNeedToProgress() (bool, error) {
|
||||
var verifyErr error
|
||||
defer func() {
|
||||
if verifyErr != nil {
|
||||
|
|
@ -76,8 +76,8 @@ func (c *CloneSetRolloutController) VerifySpec() (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Initialize makes sure that the source and target CloneSet is under our control
|
||||
func (c *CloneSetRolloutController) Initialize() (bool, error) {
|
||||
// Prepare makes sure that the source and target CloneSet is under our control
|
||||
func (c *CloneSetRolloutController) Prepare() (bool, error) {
|
||||
if err := c.fetchCloneSet(); err != nil {
|
||||
//c.releaseStatus.RolloutRetry(err.Error())
|
||||
return false, nil
|
||||
|
|
@ -185,16 +185,16 @@ func (c *CloneSetRolloutController) Finalize(pause, cleanup bool) bool {
|
|||
}
|
||||
|
||||
// WatchWorkload return change type if workload was changed during release
|
||||
func (c *CloneSetRolloutController) WatchWorkload() (WorkloadChangeEventType, *Accessor, error) {
|
||||
func (c *CloneSetRolloutController) WatchWorkload() (WorkloadChangeEventType, *WorkloadAccessor, error) {
|
||||
if c.parentController.Spec.Cancelled ||
|
||||
c.parentController.DeletionTimestamp != nil ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseFinalizing ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseRollingBack ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseRollback ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseTerminating {
|
||||
return IgnoreWorkloadEvent, nil, nil
|
||||
}
|
||||
|
||||
workloadInfo := &Accessor{}
|
||||
workloadInfo := &WorkloadAccessor{}
|
||||
err := c.fetchCloneSet()
|
||||
if client.IgnoreNotFound(err) != nil {
|
||||
return "", nil, err
|
||||
|
|
@ -219,15 +219,12 @@ func (c *CloneSetRolloutController) WatchWorkload() (WorkloadChangeEventType, *A
|
|||
}
|
||||
|
||||
switch c.releaseStatus.Phase {
|
||||
case v1alpha1.RolloutPhaseHealthy, v1alpha1.RolloutPhaseVerify:
|
||||
return IgnoreWorkloadEvent, workloadInfo, nil
|
||||
|
||||
default:
|
||||
if c.clone.Status.CurrentRevision == c.clone.Status.UpdateRevision &&
|
||||
c.parentController.Status.UpdateRevision != c.clone.Status.UpdateRevision {
|
||||
workloadInfo.UpdateRevision = &c.clone.Status.UpdateRevision
|
||||
klog.Warning("CloneSet is stable or is rolling back, release plan should stop")
|
||||
return WorkloadStableOrRollback, workloadInfo, nil
|
||||
return WorkloadRollback, workloadInfo, nil
|
||||
}
|
||||
if *c.clone.Spec.Replicas != c.releaseStatus.ObservedWorkloadReplicas {
|
||||
workloadInfo.Replicas = c.clone.Spec.Replicas
|
||||
|
|
@ -244,6 +241,9 @@ func (c *CloneSetRolloutController) WatchWorkload() (WorkloadChangeEventType, *A
|
|||
c.releaseStatus.UpdateRevision, c.clone.Status.UpdateRevision)
|
||||
return WorkloadPodTemplateChanged, workloadInfo, nil
|
||||
}
|
||||
|
||||
case v1alpha1.RolloutPhaseHealthy, v1alpha1.RolloutPhaseInitial:
|
||||
return IgnoreWorkloadEvent, workloadInfo, nil
|
||||
}
|
||||
|
||||
return IgnoreWorkloadEvent, workloadInfo, nil
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ func (c *cloneSetController) claimCloneSet(clone *kruiseappsv1alpha1.CloneSet) (
|
|||
ref := &metav1.OwnerReference{}
|
||||
err := json.Unmarshal([]byte(controlInfo), ref)
|
||||
if err == nil && ref.UID == c.parentController.UID {
|
||||
klog.V(3).Info("CloneSet has been controlled by this BatchRelease, no need to claim again")
|
||||
controlled = true
|
||||
klog.V(3).Info("CloneSet has been controlled by this BatchRelease, no need to claim again")
|
||||
} else {
|
||||
klog.Error("Failed to parse controller info from cloneset annotation, error: %v, controller info: %+v", err, *ref)
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ func (c *cloneSetController) claimCloneSet(clone *kruiseappsv1alpha1.CloneSet) (
|
|||
}
|
||||
|
||||
klog.V(3).Info("Claim CloneSet Successfully")
|
||||
return false, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// remove the parent controller from the deployment's owner list
|
||||
|
|
@ -116,6 +116,15 @@ func (c *cloneSetController) releaseCloneSet(clone *kruiseappsv1alpha1.CloneSet,
|
|||
"paused": pause,
|
||||
},
|
||||
}
|
||||
|
||||
if len(clone.Annotations[StashCloneSetPartition]) > 0 {
|
||||
restoredPartition := &intstr.IntOrString{}
|
||||
if err := json.Unmarshal([]byte(clone.Annotations[StashCloneSetPartition]), restoredPartition); err == nil {
|
||||
updateStrategy := patchSpec["updateStrategy"].(map[string]interface{})
|
||||
updateStrategy["partition"] = restoredPartition
|
||||
}
|
||||
}
|
||||
|
||||
patchSpecByte, _ := json.Marshal(patchSpec)
|
||||
patchByte := fmt.Sprintf(`{"metadata":{"annotations":{"%s":null, "%s":null}},"spec":%s}`,
|
||||
BatchReleaseControlAnnotation, StashCloneSetPartition, string(patchSpecByte))
|
||||
|
|
@ -126,7 +135,7 @@ func (c *cloneSetController) releaseCloneSet(clone *kruiseappsv1alpha1.CloneSet,
|
|||
}
|
||||
|
||||
klog.V(3).Info("Release CloneSet Successfully")
|
||||
return false, nil
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// scale the deployment
|
||||
|
|
@ -141,8 +150,8 @@ func (c *cloneSetController) patchCloneSetPartition(clone *kruiseappsv1alpha1.Cl
|
|||
|
||||
patchByte, _ := json.Marshal(patch)
|
||||
if err := c.client.Patch(context.TODO(), clone, client.RawPatch(types.MergePatchType, patchByte)); err != nil {
|
||||
c.recorder.Eventf(c.parentController, v1.EventTypeWarning, "PatchPartitionFailed", "Failed to update the CloneSet to the correct target partition %d, error: %v", partition, err)
|
||||
//c.releaseStatus.RolloutRetry(err.Error())
|
||||
c.recorder.Eventf(c.parentController, v1.EventTypeWarning, "PatchPartitionFailed",
|
||||
"Failed to update the CloneSet to the correct target partition %d, error: %v", partition, err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,196 @@
|
|||
package workloads
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
apimachineryruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
kruiseappsv1alpha1 "github.com/openkruise/kruise-api/apps/v1alpha1"
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme *runtime.Scheme
|
||||
releaseClone = &v1alpha1.BatchRelease{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "BatchRelease",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "release",
|
||||
Namespace: "application",
|
||||
UID: uuid.NewUUID(),
|
||||
},
|
||||
Spec: v1alpha1.BatchReleaseSpec{
|
||||
TargetRef: v1alpha1.ObjectRef{
|
||||
WorkloadRef: &v1alpha1.WorkloadRef{
|
||||
APIVersion: "apps.kruise.io/v1alpha1",
|
||||
Kind: "CloneSet",
|
||||
Name: "sample",
|
||||
},
|
||||
},
|
||||
ReleasePlan: v1alpha1.ReleasePlan{
|
||||
Batches: []v1alpha1.ReleaseBatch{
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("10%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("50%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("80%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stableClone = &kruiseappsv1alpha1.CloneSet{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: kruiseappsv1alpha1.SchemeGroupVersion.String(),
|
||||
Kind: "CloneSet",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sample",
|
||||
Namespace: "application",
|
||||
UID: types.UID("87076677"),
|
||||
Generation: 1,
|
||||
Labels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Spec: kruiseappsv1alpha1.CloneSetSpec{
|
||||
Replicas: pointer.Int32Ptr(100),
|
||||
UpdateStrategy: kruiseappsv1alpha1.CloneSetUpdateStrategy{
|
||||
Partition: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
|
||||
MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
},
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: containers("v2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: kruiseappsv1alpha1.CloneSetStatus{
|
||||
Replicas: 100,
|
||||
ReadyReplicas: 100,
|
||||
UpdatedReplicas: 0,
|
||||
UpdatedReadyReplicas: 0,
|
||||
ObservedGeneration: 1,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
scheme = runtime.NewScheme()
|
||||
apimachineryruntime.Must(apps.AddToScheme(scheme))
|
||||
apimachineryruntime.Must(v1alpha1.AddToScheme(scheme))
|
||||
apimachineryruntime.Must(kruiseappsv1alpha1.AddToScheme(scheme))
|
||||
|
||||
canaryTemplate := stableClone.Spec.Template.DeepCopy()
|
||||
stableTemplate := canaryTemplate.DeepCopy()
|
||||
stableTemplate.Spec.Containers = containers("v1")
|
||||
stableClone.Status.CurrentRevision = ComputeHash(stableTemplate, nil)
|
||||
stableClone.Status.UpdateRevision = ComputeHash(canaryTemplate, nil)
|
||||
}
|
||||
|
||||
func TestCloneSetController(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
Paused bool
|
||||
Cleanup bool
|
||||
}{
|
||||
{
|
||||
Name: "paused=true, cleanup=true",
|
||||
Paused: true,
|
||||
Cleanup: true,
|
||||
},
|
||||
{
|
||||
Name: "paused=true, cleanup=false",
|
||||
Paused: true,
|
||||
Cleanup: false,
|
||||
},
|
||||
{
|
||||
Name: "paused=false cleanup=true",
|
||||
Paused: false,
|
||||
Cleanup: true,
|
||||
},
|
||||
{
|
||||
Name: "paused=false , cleanup=false",
|
||||
Paused: false,
|
||||
Cleanup: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(releaseClone.DeepCopy(), stableClone.DeepCopy()).Build()
|
||||
rec := record.NewFakeRecorder(100)
|
||||
c := cloneSetController{
|
||||
workloadController: workloadController{
|
||||
client: cli,
|
||||
recorder: rec,
|
||||
parentController: releaseClone,
|
||||
releasePlan: &releaseClone.Spec.ReleasePlan,
|
||||
releaseStatus: &releaseClone.Status,
|
||||
},
|
||||
targetNamespacedName: client.ObjectKeyFromObject(stableClone),
|
||||
}
|
||||
oldObject := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(cli.Get(context.TODO(), c.targetNamespacedName, oldObject)).NotTo(HaveOccurred())
|
||||
succeed, err := c.claimCloneSet(oldObject.DeepCopy())
|
||||
Expect(succeed).Should(BeTrue())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
newObject := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(cli.Get(context.TODO(), c.targetNamespacedName, newObject)).NotTo(HaveOccurred())
|
||||
succeed, err = c.releaseCloneSet(newObject.DeepCopy(), cs.Paused, cs.Cleanup)
|
||||
Expect(succeed).Should(BeTrue())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
newObject = &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(cli.Get(context.TODO(), c.targetNamespacedName, newObject)).NotTo(HaveOccurred())
|
||||
newObject.Spec.UpdateStrategy.Paused = oldObject.Spec.UpdateStrategy.Paused
|
||||
Expect(reflect.DeepEqual(oldObject.Spec, newObject.Spec)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Labels, newObject.Labels)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Finalizers, newObject.Finalizers)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Annotations, newObject.Annotations)).Should(BeTrue())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func containers(version string) []corev1.Container {
|
||||
return []corev1.Container{
|
||||
{
|
||||
Name: "busybox",
|
||||
Image: fmt.Sprintf("busybox:%v", version),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ type WorkloadChangeEventType string
|
|||
|
||||
const (
|
||||
IgnoreWorkloadEvent WorkloadChangeEventType = "workload-not-cared"
|
||||
WorkloadStableOrRollback WorkloadChangeEventType = "workload-rollback"
|
||||
WorkloadRollback WorkloadChangeEventType = "workload-rollback"
|
||||
WorkloadPodTemplateChanged WorkloadChangeEventType = "workload-pod-template-changed"
|
||||
WorkloadReplicasChanged WorkloadChangeEventType = "workload-replicas-changed"
|
||||
WorkloadStillReconciling WorkloadChangeEventType = "workload-is-reconciling"
|
||||
|
|
@ -21,14 +21,14 @@ const (
|
|||
|
||||
// WorkloadController is the interface that all type of cloneSet controller implements
|
||||
type WorkloadController interface {
|
||||
// VerifySpec makes sure that the resources can be upgraded according to the rollout plan
|
||||
// IfNeedToProgress makes sure that the resources can be upgraded according to the rollout plan
|
||||
// it returns if the verification succeeded/failed or should retry
|
||||
VerifySpec() (bool, error)
|
||||
IfNeedToProgress() (bool, error)
|
||||
|
||||
// Initialize make sure that the resource is ready to be upgraded
|
||||
// Prepare make sure that the resource is ready to be upgraded
|
||||
// this function is tasked to do any initialization work on the resources
|
||||
// it returns if the initialization succeeded/failed or should retry
|
||||
Initialize() (bool, error)
|
||||
Prepare() (bool, error)
|
||||
|
||||
// RolloutOneBatchPods tries to upgrade pods in the resources following the rollout plan
|
||||
// it will upgrade pods as the rollout plan allows at once
|
||||
|
|
@ -52,7 +52,7 @@ type WorkloadController interface {
|
|||
// WatchWorkload will observe and compare the status recorded in release.status and the real-time
|
||||
// workload status. If workload status is inconsistent with that recorded in release.status,
|
||||
// will return the corresponding WorkloadChangeEventType and info.
|
||||
WatchWorkload() (WorkloadChangeEventType, *Accessor, error)
|
||||
WatchWorkload() (WorkloadChangeEventType, *WorkloadAccessor, error)
|
||||
}
|
||||
|
||||
type Status struct {
|
||||
|
|
@ -63,7 +63,7 @@ type Status struct {
|
|||
ObservedGeneration int64
|
||||
}
|
||||
|
||||
type Accessor struct {
|
||||
type WorkloadAccessor struct {
|
||||
Replicas *int32
|
||||
Paused bool
|
||||
Status *Status
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ package workloads
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sort"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
|
@ -16,6 +13,8 @@ import (
|
|||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/klog/v2"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sort"
|
||||
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
)
|
||||
|
|
@ -50,8 +49,8 @@ func NewDeploymentRolloutController(client client.Client, recorder record.EventR
|
|||
}
|
||||
}
|
||||
|
||||
// VerifySpec verifies that the rollout resource is consistent with the rollout spec
|
||||
func (c *DeploymentsRolloutController) VerifySpec() (bool, error) {
|
||||
// IfNeedToProgress verifies that the workload is ready to execute release plan
|
||||
func (c *DeploymentsRolloutController) IfNeedToProgress() (bool, error) {
|
||||
var verifyErr error
|
||||
|
||||
defer func() {
|
||||
|
|
@ -90,8 +89,8 @@ func (c *DeploymentsRolloutController) VerifySpec() (bool, error) {
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Initialize makes sure that the source and target Deployment is under our control
|
||||
func (c *DeploymentsRolloutController) Initialize() (bool, error) {
|
||||
// Prepare makes sure that the source and target Deployment is under our control
|
||||
func (c *DeploymentsRolloutController) Prepare() (bool, error) {
|
||||
if err := c.fetchStableDeployment(); err != nil {
|
||||
//c.releaseStatus.RolloutRetry(err.Error())
|
||||
return false, nil
|
||||
|
|
@ -201,17 +200,17 @@ func (c *DeploymentsRolloutController) Finalize(pause, cleanup bool) bool {
|
|||
}
|
||||
|
||||
// WatchWorkload return change type if workload was changed during release
|
||||
func (c *DeploymentsRolloutController) WatchWorkload() (WorkloadChangeEventType, *Accessor, error) {
|
||||
func (c *DeploymentsRolloutController) WatchWorkload() (WorkloadChangeEventType, *WorkloadAccessor, error) {
|
||||
if c.parentController.Spec.Cancelled ||
|
||||
c.parentController.DeletionTimestamp != nil ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseFinalizing ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseRollingBack ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseRollback ||
|
||||
c.releaseStatus.Phase == v1alpha1.RolloutPhaseTerminating {
|
||||
return IgnoreWorkloadEvent, nil, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
workloadInfo := &Accessor{}
|
||||
workloadInfo := &WorkloadAccessor{}
|
||||
err = c.fetchStableDeployment()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
|
|
@ -247,7 +246,7 @@ func (c *DeploymentsRolloutController) WatchWorkload() (WorkloadChangeEventType,
|
|||
|
||||
var updateRevision string
|
||||
switch c.releaseStatus.Phase {
|
||||
case v1alpha1.RolloutPhaseHealthy, v1alpha1.RolloutPhaseVerify:
|
||||
case v1alpha1.RolloutPhaseInitial, v1alpha1.RolloutPhaseHealthy:
|
||||
return IgnoreWorkloadEvent, workloadInfo, nil
|
||||
|
||||
default:
|
||||
|
|
@ -255,7 +254,7 @@ func (c *DeploymentsRolloutController) WatchWorkload() (WorkloadChangeEventType,
|
|||
return "", workloadInfo, err
|
||||
} else if isRollingBack {
|
||||
workloadInfo.UpdateRevision = &updateRevision
|
||||
return WorkloadStableOrRollback, workloadInfo, nil
|
||||
return WorkloadRollback, workloadInfo, nil
|
||||
}
|
||||
if *c.stable.Spec.Replicas != c.releaseStatus.ObservedWorkloadReplicas {
|
||||
workloadInfo.Replicas = c.stable.Spec.Replicas
|
||||
|
|
@ -266,12 +265,8 @@ func (c *DeploymentsRolloutController) WatchWorkload() (WorkloadChangeEventType,
|
|||
fallthrough
|
||||
|
||||
case v1alpha1.RolloutPhaseCompleted, v1alpha1.RolloutPhaseCancelled:
|
||||
realStableRevision, err := c.GetPodTemplateHash(c.stable, "stable")
|
||||
if err != nil {
|
||||
return "", workloadInfo, err
|
||||
}
|
||||
if (c.canary == nil || !EqualIgnoreHash(&c.stable.Spec.Template, &c.canary.Spec.Template)) &&
|
||||
c.releaseStatus.UpdateRevision != realStableRevision {
|
||||
_, err = c.GetPodTemplateHash(c.stable, Latest)
|
||||
if (c.canary == nil || !EqualIgnoreHash(&c.stable.Spec.Template, &c.canary.Spec.Template)) && apierrors.IsNotFound(err) {
|
||||
workloadInfo.UpdateRevision = &updateRevision
|
||||
klog.Warning("Deployment updateRevision changed during releasing, should try to restart the release plan")
|
||||
return WorkloadPodTemplateChanged, workloadInfo, nil
|
||||
|
|
@ -374,11 +369,11 @@ func (c *DeploymentsRolloutController) recordDeploymentRevisionAndReplicas() err
|
|||
return claimErr
|
||||
}
|
||||
|
||||
c.releaseStatus.StableRevision, err = c.GetPodTemplateHash(c.stable, "stable")
|
||||
c.releaseStatus.StableRevision, err = c.GetPodTemplateHash(c.stable, Stable)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.releaseStatus.UpdateRevision, err = c.GetPodTemplateHash(c.canary, "canary")
|
||||
c.releaseStatus.UpdateRevision, err = c.GetPodTemplateHash(c.canary, Latest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -386,9 +381,16 @@ func (c *DeploymentsRolloutController) recordDeploymentRevisionAndReplicas() err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *DeploymentsRolloutController) GetPodTemplateHash(deploy *apps.Deployment, kind string) (string, error) {
|
||||
type PodTemplateHashType string
|
||||
|
||||
const (
|
||||
Latest PodTemplateHashType = "Latest"
|
||||
Stable PodTemplateHashType = "Stable"
|
||||
)
|
||||
|
||||
func (c *DeploymentsRolloutController) GetPodTemplateHash(deploy *apps.Deployment, kind PodTemplateHashType) (string, error) {
|
||||
switch kind {
|
||||
case "stable", "canary":
|
||||
case Latest, Stable:
|
||||
if deploy == nil {
|
||||
return "", fmt.Errorf("workload cannot be found, may be deleted or not be created yet")
|
||||
}
|
||||
|
|
@ -407,18 +409,23 @@ func (c *DeploymentsRolloutController) GetPodTemplateHash(deploy *apps.Deploymen
|
|||
|
||||
for _, rs := range rss {
|
||||
switch kind {
|
||||
case "stable":
|
||||
case Stable:
|
||||
if rs.Spec.Replicas != nil && *rs.Spec.Replicas > 0 {
|
||||
return rs.Labels[apps.DefaultDeploymentUniqueLabelKey], nil
|
||||
}
|
||||
case "canary":
|
||||
case Latest:
|
||||
if EqualIgnoreHash(&deploy.Spec.Template, &rs.Spec.Template) {
|
||||
return rs.Labels[apps.DefaultDeploymentUniqueLabelKey], nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("cannot find the suitable replicaset pod template hash, kind: %v", kind)
|
||||
notFoundErr := apierrors.NewNotFound(schema.GroupResource{
|
||||
Group: apps.SchemeGroupVersion.Group,
|
||||
Resource: fmt.Sprintf("%v-ReplicaSet", kind),
|
||||
}, c.canaryNamespacedName.Name)
|
||||
|
||||
return "", notFoundErr
|
||||
}
|
||||
|
||||
func (c *DeploymentsRolloutController) listReplicaSetsFor(deploy *apps.Deployment) ([]*apps.ReplicaSet, error) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
apps "k8s.io/api/apps/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
|
@ -18,11 +20,6 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const (
|
||||
CanaryDeploymentLabelKey = "rollouts.kruise.io/canary-deployment"
|
||||
CanaryDeploymentFinalizer = "finalizer.rollouts.kruise.io/canary-deployment"
|
||||
)
|
||||
|
||||
// deploymentController is the place to hold fields needed for handle Deployment type of workloads
|
||||
type deploymentController struct {
|
||||
workloadController
|
||||
|
|
@ -33,6 +30,34 @@ type deploymentController struct {
|
|||
// add the parent controller to the owner of the deployment, unpause it and initialize the size
|
||||
// before kicking start the update and start from every pod in the old version
|
||||
func (c *deploymentController) claimDeployment(stableDeploy, canaryDeploy *apps.Deployment) (*apps.Deployment, error) {
|
||||
var controlled bool
|
||||
if controlInfo, ok := stableDeploy.Annotations[BatchReleaseControlAnnotation]; ok && controlInfo != "" {
|
||||
ref := &metav1.OwnerReference{}
|
||||
err := json.Unmarshal([]byte(controlInfo), ref)
|
||||
if err == nil && ref.UID == c.parentController.UID {
|
||||
klog.V(3).Info("CloneSet has been controlled by this BatchRelease, no need to claim again")
|
||||
controlled = true
|
||||
} else {
|
||||
klog.Error("Failed to parse controller info from cloneset annotation, error: %v, controller info: %+v", err, *ref)
|
||||
}
|
||||
}
|
||||
|
||||
if !controlled {
|
||||
controlInfo, _ := json.Marshal(metav1.NewControllerRef(c.parentController, c.parentController.GetObjectKind().GroupVersionKind()))
|
||||
patchedInfo := map[string]interface{}{
|
||||
"metadata": map[string]interface{}{
|
||||
"annotations": map[string]string{
|
||||
BatchReleaseControlAnnotation: string(controlInfo),
|
||||
},
|
||||
},
|
||||
}
|
||||
patchedBody, _ := json.Marshal(patchedInfo)
|
||||
if err := c.client.Patch(context.TODO(), stableDeploy, client.RawPatch(types.StrategicMergePatchType, patchedBody)); err != nil {
|
||||
klog.Error("Failed to patch controller info annotations to stable deployment(%v), error: %v", client.ObjectKeyFromObject(canaryDeploy), err)
|
||||
return canaryDeploy, err
|
||||
}
|
||||
}
|
||||
|
||||
if canaryDeploy == nil || !EqualIgnoreHash(&stableDeploy.Spec.Template, &canaryDeploy.Spec.Template) {
|
||||
var err error
|
||||
var collisionCount int32
|
||||
|
|
@ -51,8 +76,11 @@ func (c *deploymentController) claimDeployment(stableDeploy, canaryDeploy *apps.
|
|||
break
|
||||
}
|
||||
|
||||
*c.releaseStatus.CollisionCount = collisionCount
|
||||
if collisionCount > 0 {
|
||||
c.releaseStatus.CollisionCount = pointer.Int32Ptr(collisionCount)
|
||||
}
|
||||
}
|
||||
|
||||
return canaryDeploy, nil
|
||||
}
|
||||
|
||||
|
|
@ -96,18 +124,22 @@ func (c *deploymentController) createCanaryDeployment(stableDeploy *apps.Deploym
|
|||
canaryDeploy.Spec.Replicas = pointer.Int32Ptr(0)
|
||||
canaryDeploy.Spec.Paused = false
|
||||
|
||||
canaryKey := client.ObjectKeyFromObject(canaryDeploy)
|
||||
// create canary Deployment
|
||||
err := c.client.Create(context.TODO(), canaryDeploy)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to create canary Deployment(%v), error: %v", c.canaryNamespacedName, err)
|
||||
klog.Errorf("Failed to create canary Deployment(%v), error: %v", canaryKey, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
canaryDeployInfo, _ := json.Marshal(canaryDeploy)
|
||||
klog.V(3).Infof("Create canary deployment successfully, details: %+v", canaryDeployInfo)
|
||||
|
||||
// fetch the canary Deployment
|
||||
var fetchedCanary *apps.Deployment
|
||||
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
fetchedCanary = &apps.Deployment{}
|
||||
return c.client.Get(context.TODO(), c.canaryNamespacedName, fetchedCanary)
|
||||
return c.client.Get(context.TODO(), canaryKey, fetchedCanary)
|
||||
})
|
||||
|
||||
return fetchedCanary, err
|
||||
|
|
@ -119,6 +151,15 @@ func (c *deploymentController) releaseDeployment(stableDeploy *apps.Deployment,
|
|||
}
|
||||
|
||||
var patchErr, deleteErr error
|
||||
{
|
||||
patchByte := []byte(fmt.Sprintf(`{"metadata":{"annotations":{"%v":null}},"spec":{"paused":%v}}`, BatchReleaseControlAnnotation, pause))
|
||||
patchErr = c.client.Patch(context.TODO(), stableDeploy, client.RawPatch(types.StrategicMergePatchType, patchByte))
|
||||
if patchErr != nil {
|
||||
klog.Error("Error occurred when patching Deployment, error: %v", patchErr)
|
||||
return false, patchErr
|
||||
}
|
||||
}
|
||||
|
||||
if cleanup {
|
||||
ds, err := c.listCanaryDeployment(client.InNamespace(stableDeploy.Namespace),
|
||||
client.MatchingLabels(map[string]string{CanaryDeploymentLabelKey: string(stableDeploy.UID)}))
|
||||
|
|
@ -126,33 +167,40 @@ func (c *deploymentController) releaseDeployment(stableDeploy *apps.Deployment,
|
|||
return false, err
|
||||
}
|
||||
|
||||
// must delete the older first
|
||||
// must make sure the older is deleted firstly
|
||||
sort.Slice(ds, func(i, j int) bool {
|
||||
return ds[i].CreationTimestamp.Before(&ds[j].CreationTimestamp)
|
||||
})
|
||||
|
||||
// delete all the canary deployments
|
||||
for _, d := range ds {
|
||||
finalizers := sets.NewString(d.Finalizers...).Delete(CanaryDeploymentFinalizer).List()
|
||||
patchErr = PatchFinalizer(c.client, d, finalizers)
|
||||
if patchErr != nil && !errors.IsNotFound(patchErr) {
|
||||
klog.Error("Error occurred when patching Deployment, error: %v", patchErr)
|
||||
return false, patchErr
|
||||
// clean up finalizers first
|
||||
if controllerutil.ContainsFinalizer(d, CanaryDeploymentFinalizer) {
|
||||
finalizers := sets.NewString(d.Finalizers...).Delete(CanaryDeploymentFinalizer).List()
|
||||
patchErr = PatchFinalizer(c.client, d, finalizers)
|
||||
if patchErr != nil && !errors.IsNotFound(patchErr) {
|
||||
klog.Error("Error occurred when patching Deployment, error: %v", patchErr)
|
||||
return false, patchErr
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
// delete the deployment
|
||||
deleteErr = c.client.Delete(context.TODO(), d)
|
||||
if deleteErr != nil && !errors.IsNotFound(deleteErr) {
|
||||
klog.Error("Error occurred when deleting Deployment, error: %v", deleteErr)
|
||||
return false, deleteErr
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
if stableDeploy.Spec.Paused != pause {
|
||||
patchByte := []byte(fmt.Sprintf(`{"spec":{"paused":%v}}`, pause))
|
||||
patchErr = c.client.Patch(context.TODO(), stableDeploy, client.RawPatch(types.StrategicMergePatchType, patchByte))
|
||||
if patchErr != nil {
|
||||
klog.Error("Error occurred when patching Deployment, error: %v", patchErr)
|
||||
return false, patchErr
|
||||
// make sure that all canary deployments has been deleted
|
||||
ds, err = c.listCanaryDeployment(client.InNamespace(stableDeploy.Namespace),
|
||||
client.MatchingLabels(map[string]string{CanaryDeploymentLabelKey: string(stableDeploy.UID)}))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(ds) == 0, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
|
|
|
|||
|
|
@ -0,0 +1,172 @@
|
|||
package workloads
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
apps "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/openkruise/rollouts/api/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
releaseDeploy = &v1alpha1.BatchRelease{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: v1alpha1.GroupVersion.String(),
|
||||
Kind: "BatchRelease",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "release",
|
||||
Namespace: "application",
|
||||
UID: uuid.NewUUID(),
|
||||
},
|
||||
Spec: v1alpha1.BatchReleaseSpec{
|
||||
TargetRef: v1alpha1.ObjectRef{
|
||||
WorkloadRef: &v1alpha1.WorkloadRef{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
Name: "sample",
|
||||
},
|
||||
},
|
||||
ReleasePlan: v1alpha1.ReleasePlan{
|
||||
Batches: []v1alpha1.ReleaseBatch{
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("10%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("50%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
{
|
||||
CanaryReplicas: intstr.FromString("80%"),
|
||||
PauseSeconds: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stableDeploy = &apps.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: apps.SchemeGroupVersion.String(),
|
||||
Kind: "Deployment",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "sample",
|
||||
Namespace: "application",
|
||||
UID: types.UID("87076677"),
|
||||
Generation: 2,
|
||||
Labels: map[string]string{
|
||||
"app": "busybox",
|
||||
apps.DefaultDeploymentUniqueLabelKey: "update-pod-hash",
|
||||
},
|
||||
},
|
||||
Spec: apps.DeploymentSpec{
|
||||
Replicas: pointer.Int32Ptr(100),
|
||||
Strategy: apps.DeploymentStrategy{
|
||||
Type: apps.RollingUpdateDeploymentStrategyType,
|
||||
RollingUpdate: &apps.RollingUpdateDeployment{
|
||||
MaxSurge: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(1)},
|
||||
MaxUnavailable: &intstr.IntOrString{Type: intstr.Int, IntVal: int32(2)},
|
||||
},
|
||||
},
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"app": "busybox",
|
||||
},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
Spec: corev1.PodSpec{
|
||||
Containers: containers("v2"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: apps.DeploymentStatus{
|
||||
Replicas: 100,
|
||||
ReadyReplicas: 100,
|
||||
UpdatedReplicas: 0,
|
||||
AvailableReplicas: 100,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func TestDeploymentController(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
cases := []struct {
|
||||
Name string
|
||||
Paused bool
|
||||
Cleanup bool
|
||||
}{
|
||||
{
|
||||
Name: "paused=true, cleanup=true",
|
||||
Paused: true,
|
||||
Cleanup: true,
|
||||
},
|
||||
{
|
||||
Name: "paused=true, cleanup=false",
|
||||
Paused: true,
|
||||
Cleanup: false,
|
||||
},
|
||||
{
|
||||
Name: "paused=false cleanup=true",
|
||||
Paused: false,
|
||||
Cleanup: true,
|
||||
},
|
||||
{
|
||||
Name: "paused=false , cleanup=false",
|
||||
Paused: false,
|
||||
Cleanup: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, cs := range cases {
|
||||
t.Run(cs.Name, func(t *testing.T) {
|
||||
cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(releaseDeploy.DeepCopy(), stableDeploy.DeepCopy()).Build()
|
||||
rec := record.NewFakeRecorder(100)
|
||||
c := deploymentController{
|
||||
workloadController: workloadController{
|
||||
client: cli,
|
||||
recorder: rec,
|
||||
parentController: releaseDeploy,
|
||||
releasePlan: &releaseDeploy.Spec.ReleasePlan,
|
||||
releaseStatus: &releaseDeploy.Status,
|
||||
},
|
||||
stableNamespacedName: client.ObjectKeyFromObject(stableDeploy),
|
||||
canaryNamespacedName: client.ObjectKeyFromObject(stableDeploy),
|
||||
}
|
||||
oldObject := &apps.Deployment{}
|
||||
Expect(cli.Get(context.TODO(), c.stableNamespacedName, oldObject)).NotTo(HaveOccurred())
|
||||
canary, err := c.claimDeployment(oldObject.DeepCopy(), nil)
|
||||
Expect(canary).ShouldNot(BeNil())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
newObject := &apps.Deployment{}
|
||||
Expect(cli.Get(context.TODO(), c.stableNamespacedName, newObject)).NotTo(HaveOccurred())
|
||||
succeed, err := c.releaseDeployment(newObject.DeepCopy(), cs.Paused, cs.Cleanup)
|
||||
Expect(succeed).Should(BeTrue())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
newObject = &apps.Deployment{}
|
||||
Expect(cli.Get(context.TODO(), c.stableNamespacedName, newObject)).NotTo(HaveOccurred())
|
||||
newObject.Spec.Paused = oldObject.Spec.Paused
|
||||
Expect(reflect.DeepEqual(oldObject.Spec, newObject.Spec)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Labels, newObject.Labels)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Finalizers, newObject.Finalizers)).Should(BeTrue())
|
||||
Expect(reflect.DeepEqual(oldObject.Annotations, newObject.Annotations)).Should(BeTrue())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
CanaryDeploymentLabelKey = "rollouts.kruise.io/canary-deployment"
|
||||
CanaryDeploymentFinalizer = "finalizer.rollouts.kruise.io/batch-release"
|
||||
|
||||
// We omit vowels from the set of available characters to reduce the chances
|
||||
// of "bad words" being formed.
|
||||
alphanums = "bcdfghjklmnpqrstvwxz2456789"
|
||||
|
|
@ -114,3 +117,11 @@ func PatchFinalizer(c client.Client, object client.Object, finalizers []string)
|
|||
})
|
||||
return c.Patch(context.TODO(), object, client.RawPatch(types.MergePatchType, patchByte))
|
||||
}
|
||||
|
||||
func IsControlledByRollout(release *v1alpha1.BatchRelease) bool {
|
||||
owner := metav1.GetControllerOf(release)
|
||||
if owner != nil && owner.APIVersion == v1alpha1.GroupVersion.String() && owner.Kind == "Rollout" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +190,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2: Percentage, 50%, Succeeded", func() {
|
||||
|
|
@ -227,7 +227,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -236,14 +236,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() int32 {
|
||||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 5*time.Minute, time.Second).Should(Equal(*cloneset.Spec.Replicas))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(*cloneset.Spec.Replicas))
|
||||
})
|
||||
|
||||
It("V1->V2(Completed)->V3: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -284,7 +284,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +293,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
/*************************************************************************************
|
||||
V1->V2 Succeeded, Start to release V2->V3
|
||||
|
|
@ -318,7 +318,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 20*time.Second, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -327,7 +327,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2(UnCompleted)->V3: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -368,7 +368,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -400,7 +400,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
}, 5*time.Minute, time.Second).Should(Equal(int32(expectedUpdatedReplicas)))
|
||||
time.Sleep(time.Duration(batch.PauseSeconds) * time.Second)
|
||||
}
|
||||
|
||||
|
|
@ -409,7 +409,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingUp, Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -463,14 +463,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingDown, Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -524,14 +524,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingUp, Number, 100%, Succeeded", func() {
|
||||
|
|
@ -585,14 +585,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingDown, Number, 100%, Succeeded", func() {
|
||||
|
|
@ -647,14 +647,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Rollback V1->V2->V1: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -700,14 +700,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &kruiseappsv1alpha1.CloneSet{}
|
||||
Expect(GetObject(cloneset.Namespace, cloneset.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
|
||||
By("Checking BatchRelease completed status phase...")
|
||||
Eventually(func() rolloutsv1alpha1.RolloutPhase {
|
||||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCancelled))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCancelled))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -755,7 +755,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2: Percentage, 50%, Succeeded", func() {
|
||||
|
|
@ -807,7 +807,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 10*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
})
|
||||
|
||||
It("V1->V2(Completed)->V3: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -856,7 +856,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
/*************************************************************************************
|
||||
V1->V2 Succeeded, Start to release V2->V3
|
||||
|
|
@ -889,7 +889,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2(UnCompleted)->V3: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -969,7 +969,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingUp, Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -1023,14 +1023,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() int32 {
|
||||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 10*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingDown, Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -1084,14 +1084,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingUp, Number, 100%, Succeeded", func() {
|
||||
|
|
@ -1144,14 +1144,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("V1->V2: ScalingDown, Number, 100%, Succeeded", func() {
|
||||
|
|
@ -1205,14 +1205,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 10*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCompleted))
|
||||
|
||||
By("Checking all pod were updated when release completed...")
|
||||
Eventually(func() bool {
|
||||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas == *clone.Spec.Replicas
|
||||
}, 10*time.Minute, time.Second).Should(BeTrue())
|
||||
}, 15*time.Minute, time.Second).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("Rollback V1->V2->V1: Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -1262,14 +1262,14 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 100*time.Second, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
|
||||
By("Checking BatchRelease completed status phase...")
|
||||
Eventually(func() rolloutsv1alpha1.RolloutPhase {
|
||||
clone := &rolloutsv1alpha1.BatchRelease{}
|
||||
Expect(GetObject(release.Namespace, release.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.Phase
|
||||
}, 100*time.Second, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCancelled))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(rolloutsv1alpha1.RolloutPhaseCancelled))
|
||||
})
|
||||
|
||||
It("Rollback V1->V2: Delete BatchRelease, Percentage, 100%, Succeeded", func() {
|
||||
|
|
@ -1318,7 +1318,6 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
By("Updating cloneset to V1...")
|
||||
deployment.Spec.Template.Spec.Containers[0].Image = images.GetE2EImage(images.BusyBoxV1)
|
||||
UpdateDeployment(deployment)
|
||||
// record canary revision --> v2
|
||||
canaryRevisionV3 := workloads.ComputeHash(&deployment.Spec.Template, deployment.Status.CollisionCount)
|
||||
Expect(canaryRevisionV3).Should(Equal(stableRevisionV1))
|
||||
|
||||
|
|
@ -1327,7 +1326,7 @@ var _ = SIGDescribe("Test BatchRelease Controller", func() {
|
|||
clone := &apps.Deployment{}
|
||||
Expect(GetObject(deployment.Namespace, deployment.Name, clone)).NotTo(HaveOccurred())
|
||||
return clone.Status.UpdatedReplicas
|
||||
}, 100*time.Second, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
}, 15*time.Minute, time.Second).Should(Equal(*deployment.Spec.Replicas))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ metadata:
|
|||
name: release-cloneset-100
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 1
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 2
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 3
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 5
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ metadata:
|
|||
name: release-cloneset-100
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 20%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 40%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 60%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 100%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 10
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@ metadata:
|
|||
name: release-cloneset-50
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps.kruise.io/v1alpha1
|
||||
kind: CloneSet
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 20%
|
||||
pauseSeconds: 30
|
||||
- canaryReplicas: 40%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 60
|
||||
- canaryReplicas: 50%
|
||||
pauseSeconds: 100
|
||||
pauseSeconds: 30
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ metadata:
|
|||
name: release-deployment-number-100
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 1
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 2
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 3
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 5
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 10
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -4,16 +4,18 @@ metadata:
|
|||
name: release-deployment-percentage-100
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 20%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 40%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 60%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 20
|
||||
- canaryReplicas: 100%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 10
|
||||
|
|
|
|||
|
|
@ -4,17 +4,19 @@ metadata:
|
|||
name: release-deployment-percentage-50
|
||||
spec:
|
||||
targetReference:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
type: workloadRef
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: sample
|
||||
releasePlan:
|
||||
batches:
|
||||
- canaryReplicas: 20%
|
||||
pauseSeconds: 30
|
||||
- canaryReplicas: 40%
|
||||
pauseSeconds: 30
|
||||
pauseSeconds: 60
|
||||
- canaryReplicas: 50%
|
||||
pauseSeconds: 100
|
||||
pauseSeconds: 30
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
.idea/
|
||||
<<<<<<< HEAD
|
||||
.test/
|
||||
examples/_*
|
||||
=======
|
||||
.test/
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
examples/_*
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
language: go
|
||||
|
||||
script:
|
||||
- go test -race -v ./...
|
||||
|
||||
go:
|
||||
- "1.9"
|
||||
- "1.10"
|
||||
- "1.11"
|
||||
- "1.12"
|
||||
- "1.13"
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
# Version v1.4.7-v1.4.8
|
||||
* Documentation updates.
|
||||
* Small linter cleanups.
|
||||
|
|
@ -10,9 +9,6 @@
|
|||
* Add example directories with example and tests for issues.
|
||||
|
||||
# Version v1.4.4-v1.4.5
|
||||
=======
|
||||
# Version v1.4.4
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
* Fix of checksum problem because of forced tag. No changes to the code.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
[](https://pkg.go.dev/github.com/nxadm/tail)
|
||||
|
||||
# tail functionality in Go
|
||||
|
|
@ -15,50 +14,22 @@ A simple example:
|
|||
// Create a tail
|
||||
t, err := tail.TailFile(
|
||||
"/var/log/nginx.log", tail.Config{Follow: true, ReOpen: true})
|
||||
=======
|
||||
[](https://travis-ci.org/nxadm/tail)
|
||||
|
||||
This is repo is forked from the dormant upstream repo at
|
||||
[hpcloud](https://github.com/hpcloud/tail). This fork adds support for go
|
||||
modules, updates the dependencies, adds features and fixes bugs. Go 1.9 is
|
||||
the oldest compiler release supported.
|
||||
|
||||
# Go package for tail-ing files
|
||||
|
||||
A Go package striving to emulate the features of the BSD `tail` program.
|
||||
|
||||
```Go
|
||||
t, err := tail.TailFile("/var/log/nginx.log", tail.Config{Follow: true})
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Print the text of each received line
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
for line := range t.Lines {
|
||||
fmt.Println(line.Text)
|
||||
}
|
||||
```
|
||||
|
||||
<<<<<<< HEAD
|
||||
See [API documentation](https://pkg.go.dev/github.com/nxadm/tail).
|
||||
=======
|
||||
See [API documentation](http://godoc.org/github.com/nxadm/tail).
|
||||
|
||||
## Log rotation
|
||||
|
||||
Tail comes with full support for truncation/move detection as it is
|
||||
designed to work with log rotation tools.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
## Installing
|
||||
|
||||
go get github.com/nxadm/tail/...
|
||||
|
||||
<<<<<<< HEAD
|
||||
## History
|
||||
|
||||
This project is an active, drop-in replacement for the
|
||||
|
|
@ -70,9 +41,4 @@ nxadm/tail continues the development by keeping up to date with the Go toolchain
|
|||
and fixing bugs.
|
||||
|
||||
## Examples
|
||||
Examples, e.g. used to debug an issue, are kept in the [examples directory](/examples).
|
||||
=======
|
||||
## Windows support
|
||||
|
||||
This package [needs assistance](https://github.com/nxadm/tail/labels/Windows) for full Windows support.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
Examples, e.g. used to debug an issue, are kept in the [examples directory](/examples).
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
version: 0.{build}
|
||||
skip_tags: true
|
||||
cache: C:\Users\appveyor\AppData\Local\NuGet\Cache
|
||||
build_script:
|
||||
- SET GOPATH=c:\workspace
|
||||
- go test -v -race ./...
|
||||
test: off
|
||||
clone_folder: c:\workspace\src\github.com\nxadm\tail
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
|
@ -3,11 +3,6 @@ module github.com/nxadm/tail
|
|||
go 1.13
|
||||
|
||||
require (
|
||||
<<<<<<< HEAD
|
||||
github.com/fsnotify/fsnotify v1.4.9
|
||||
=======
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd // indirect
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
<<<<<<< HEAD
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
=======
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd h1:DBH9mDw0zluJT/R+nGuV3jWFWLFaHyYZWD4tOT+cjn0=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
|
@ -8,11 +7,6 @@
|
|||
//it is designed to work with log rotation tools. The library works on all
|
||||
//operating systems supported by Go, including POSIX systems like Linux and
|
||||
//*BSD, and MS Windows. Go 1.9 is the oldest compiler release supported.
|
||||
=======
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
package tail
|
||||
|
||||
import (
|
||||
|
|
@ -34,15 +28,11 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
<<<<<<< HEAD
|
||||
// ErrStop is returned when the tail of a file has been marked to be stopped.
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
ErrStop = errors.New("tail should now stop")
|
||||
)
|
||||
|
||||
type Line struct {
|
||||
<<<<<<< HEAD
|
||||
Text string // The contents of the file
|
||||
Num int // The line number
|
||||
SeekInfo SeekInfo // SeekInfo
|
||||
|
|
@ -55,31 +45,14 @@ type Line struct {
|
|||
// release.
|
||||
//
|
||||
// NewLine returns a * pointer to a Line struct.
|
||||
=======
|
||||
Text string
|
||||
Num int
|
||||
SeekInfo SeekInfo
|
||||
Time time.Time
|
||||
Err error // Error from tail
|
||||
}
|
||||
|
||||
// NewLine returns a Line with present time.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func NewLine(text string, lineNum int) *Line {
|
||||
return &Line{text, lineNum, SeekInfo{}, time.Now(), nil}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// SeekInfo represents arguments to io.Seek. See: https://golang.org/pkg/io/#SectionReader.Seek
|
||||
type SeekInfo struct {
|
||||
Offset int64
|
||||
Whence int
|
||||
=======
|
||||
// SeekInfo represents arguments to `io.Seek`
|
||||
type SeekInfo struct {
|
||||
Offset int64
|
||||
Whence int // io.Seek*
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
type logger interface {
|
||||
|
|
@ -97,48 +70,28 @@ type logger interface {
|
|||
// Config is used to specify how a file must be tailed.
|
||||
type Config struct {
|
||||
// File-specifc
|
||||
<<<<<<< HEAD
|
||||
Location *SeekInfo // Tail from this location. If nil, start at the beginning of the file
|
||||
ReOpen bool // Reopen recreated files (tail -F)
|
||||
MustExist bool // Fail early if the file does not exist
|
||||
Poll bool // Poll for file changes instead of using the default inotify
|
||||
Pipe bool // The file is a named pipe (mkfifo)
|
||||
=======
|
||||
Location *SeekInfo // Seek to this location before tailing
|
||||
ReOpen bool // Reopen recreated files (tail -F)
|
||||
MustExist bool // Fail early if the file does not exist
|
||||
Poll bool // Poll for file changes instead of using inotify
|
||||
Pipe bool // Is a named pipe (mkfifo)
|
||||
RateLimiter *ratelimiter.LeakyBucket
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
// Generic IO
|
||||
Follow bool // Continue looking for new lines (tail -f)
|
||||
MaxLineSize int // If non-zero, split longer lines into multiple lines
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Optionally, use a ratelimiter (e.g. created by the ratelimiter/NewLeakyBucket function)
|
||||
RateLimiter *ratelimiter.LeakyBucket
|
||||
|
||||
// Optionally use a Logger. When nil, the Logger is set to tail.DefaultLogger.
|
||||
// To disable logging, set it to tail.DiscardingLogger
|
||||
=======
|
||||
// Logger, when nil, is set to tail.DefaultLogger
|
||||
// To disable logging: set field to tail.DiscardingLogger
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
Logger logger
|
||||
}
|
||||
|
||||
type Tail struct {
|
||||
<<<<<<< HEAD
|
||||
Filename string // The filename
|
||||
Lines chan *Line // A consumable channel of *Line
|
||||
Config // Tail.Configuration
|
||||
=======
|
||||
Filename string
|
||||
Lines chan *Line
|
||||
Config
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
file *os.File
|
||||
reader *bufio.Reader
|
||||
|
|
@ -153,28 +106,17 @@ type Tail struct {
|
|||
}
|
||||
|
||||
var (
|
||||
<<<<<<< HEAD
|
||||
// DefaultLogger logs to os.Stderr and it is used when Config.Logger == nil
|
||||
=======
|
||||
// DefaultLogger is used when Config.Logger == nil
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
DefaultLogger = log.New(os.Stderr, "", log.LstdFlags)
|
||||
// DiscardingLogger can be used to disable logging output
|
||||
DiscardingLogger = log.New(ioutil.Discard, "", 0)
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
// TailFile begins tailing the file. And returns a pointer to a Tail struct
|
||||
// and an error. An output stream is made available via the Tail.Lines
|
||||
// channel (e.g. to be looped and printed). To handle errors during tailing,
|
||||
// after finishing reading from the Lines channel, invoke the `Wait` or `Err`
|
||||
// method on the returned *Tail.
|
||||
=======
|
||||
// TailFile begins tailing the file. Output stream is made available
|
||||
// via the `Tail.Lines` channel. To handle errors during tailing,
|
||||
// invoke the `Wait` or `Err` method after finishing reading from the
|
||||
// `Lines` channel.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func TailFile(filename string, config Config) (*Tail, error) {
|
||||
if config.ReOpen && !config.Follow {
|
||||
util.Fatal("cannot set ReOpen without Follow.")
|
||||
|
|
@ -210,16 +152,9 @@ func TailFile(filename string, config Config) (*Tail, error) {
|
|||
return t, nil
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Tell returns the file's current position, like stdio's ftell() and an error.
|
||||
// Beware that this value may not be completely accurate because one line from
|
||||
// the chan(tail.Lines) may have been read already.
|
||||
=======
|
||||
// Tell returns the file's current position, like stdio's ftell().
|
||||
// But this value is not very accurate.
|
||||
// One line from the chan(tail.Lines) may have been read,
|
||||
// so it may have lost one line.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (tail *Tail) Tell() (offset int64, err error) {
|
||||
if tail.file == nil {
|
||||
return
|
||||
|
|
@ -245,12 +180,8 @@ func (tail *Tail) Stop() error {
|
|||
return tail.Wait()
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// StopAtEOF stops tailing as soon as the end of the file is reached. The function
|
||||
// returns an error,
|
||||
=======
|
||||
// StopAtEOF stops tailing as soon as the end of the file is reached.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (tail *Tail) StopAtEOF() error {
|
||||
tail.Kill(errStopAtEOF)
|
||||
return tail.Wait()
|
||||
|
|
@ -518,10 +449,7 @@ func (tail *Tail) sendLine(line string) bool {
|
|||
// Cleanup removes inotify watches added by the tail package. This function is
|
||||
// meant to be invoked from a process's exit handler. Linux kernel may not
|
||||
// automatically remove inotify watches after the process exits.
|
||||
<<<<<<< HEAD
|
||||
// If you plan to re-read a file, don't call Cleanup in between.
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (tail *Tail) Cleanup() {
|
||||
watch.Cleanup(tail.Filename)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// +build !windows
|
||||
|
||||
package tail
|
||||
|
|
@ -10,14 +7,11 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Deprecated: this function is only useful internally and, as such,
|
||||
// it will be removed from the API in a future major release.
|
||||
//
|
||||
// OpenFile proxies a os.Open call for a file so it can be correctly tailed
|
||||
// on POSIX and non-POSIX OSes like MS Windows.
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func OpenFile(name string) (file *os.File, err error) {
|
||||
return os.Open(name)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// +build windows
|
||||
|
||||
package tail
|
||||
|
||||
import (
|
||||
<<<<<<< HEAD
|
||||
"os"
|
||||
|
||||
"github.com/nxadm/tail/winfile"
|
||||
|
|
@ -18,12 +14,6 @@ import (
|
|||
//
|
||||
// OpenFile proxies a os.Open call for a file so it can be correctly tailed
|
||||
// on POSIX and non-POSIX OSes like MS Windows.
|
||||
=======
|
||||
"github.com/nxadm/tail/winfile"
|
||||
"os"
|
||||
)
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func OpenFile(name string) (file *os.File, err error) {
|
||||
return winfile.OpenFile(name, os.O_RDONLY, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
package watch
|
||||
|
||||
type FileChanges struct {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Copyright (c) 2015 HPE Software Inc. All rights reserved.
|
||||
// Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
<<<<<<< HEAD
|
||||
// Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// +build windows
|
||||
|
||||
package winfile
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
language: go
|
||||
go:
|
||||
<<<<<<< HEAD
|
||||
- tip
|
||||
- 1.16.x
|
||||
- 1.15.x
|
||||
=======
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- tip
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
|
@ -22,19 +16,9 @@ install:
|
|||
- GO111MODULE="off" go get golang.org/x/tools/cmd/cover
|
||||
- GO111MODULE="off" go get github.com/onsi/gomega
|
||||
- GO111MODULE="off" go install github.com/onsi/ginkgo/ginkgo
|
||||
<<<<<<< HEAD
|
||||
- export PATH=$GOPATH/bin:$PATH
|
||||
|
||||
script:
|
||||
- GO111MODULE="on" go mod tidy && git diff --exit-code go.mod go.sum
|
||||
- go vet
|
||||
- ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace
|
||||
=======
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
|
||||
script:
|
||||
- GO111MODULE="on" go mod tidy
|
||||
- diff -u <(echo -n) <(git diff go.mod)
|
||||
- diff -u <(echo -n) <(git diff go.sum)
|
||||
- $HOME/gopath/bin/ginkgo -r --randomizeAllSpecs --randomizeSuites --race --trace && go vet
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
## 1.16.5
|
||||
|
||||
Ginkgo 2.0 now has a Release Candidate. 1.16.5 advertises the existence of the RC.
|
||||
|
|
@ -66,8 +65,6 @@ You can silence the RC advertisement by setting an `ACK_GINKG_RC=true` environme
|
|||
- correct handling windows backslash in import path (#721) [97f3d51]
|
||||
- Add additional methods to GinkgoT() to improve compatibility with the testing.TB interface [b5fe44d]
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
## 1.14.1
|
||||
|
||||
### Fixes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||

|
||||
|
||||
<<<<<<< HEAD
|
||||
[](https://github.com/onsi/ginkgo/actions?query=workflow%3Atest+branch%3Amaster)
|
||||
|
||||
Jump to the [docs](https://onsi.github.io/ginkgo/) | [中文文档](https://ke-chain.github.io/ginkgodoc) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
|
||||
|
|
@ -15,14 +14,6 @@ As described in the [changelog](https://github.com/onsi/ginkgo/blob/ver2/docs/MI
|
|||
|
||||
Please start exploring and using the V2 release! To get started follow the [Using the Release Candidate](https://github.com/onsi/ginkgo/blob/ver2/docs/MIGRATING_TO_V2.md#using-the-beta) directions in the migration guide.
|
||||
|
||||
=======
|
||||
[](https://travis-ci.org/onsi/ginkgo)
|
||||
|
||||
Jump to the [docs](https://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
|
||||
|
||||
If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW).
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
## TLDR
|
||||
Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests.
|
||||
It is typically (and optionally) paired with the [Gomega](https://github.com/onsi/gomega) matcher library.
|
||||
|
|
@ -78,11 +69,8 @@ Describe("the strings package", func() {
|
|||
|
||||
- [Completions for VSCode](https://github.com/onsi/vscode-ginkgo): just use VSCode's extension installer to install `vscode-ginkgo`.
|
||||
|
||||
<<<<<<< HEAD
|
||||
- [Ginkgo tools for VSCode](https://marketplace.visualstudio.com/items?itemName=joselitofilho.ginkgotestexplorer): just use VSCode's extension installer to install `ginkgoTestExplorer`.
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
- Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](https://onsi.github.io/ginkgo/#third-party-integrations) for details.
|
||||
|
||||
- A modular architecture that lets you easily:
|
||||
|
|
|
|||
|
|
@ -8,17 +8,10 @@ A Ginkgo release is a tagged git sha and a GitHub release. To cut a release:
|
|||
- Fixes (fix version)
|
||||
- Maintenance (which in general should not be mentioned in `CHANGELOG.md` as they have no user impact)
|
||||
1. Update `VERSION` in `config/config.go`
|
||||
<<<<<<< HEAD
|
||||
1. Commit, push, and release:
|
||||
```
|
||||
git commit -m "vM.m.p"
|
||||
git push
|
||||
gh release create "vM.m.p"
|
||||
git fetch --tags origin master
|
||||
```
|
||||
=======
|
||||
1. Create a commit with the version number as the commit message (e.g. `v1.3.0`)
|
||||
1. Tag the commit with the version number as the tag name (e.g. `v1.3.0`)
|
||||
1. Push the commit and tag to GitHub
|
||||
1. Create a new [GitHub release](https://help.github.com/articles/creating-releases/) with the version number as the tag (e.g. `v1.3.0`). List the key changes in the release notes.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
```
|
||||
|
|
@ -20,23 +20,14 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
const VERSION = "1.16.5"
|
||||
=======
|
||||
const VERSION = "1.14.1"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
type GinkgoConfigType struct {
|
||||
RandomSeed int64
|
||||
RandomizeAllSpecs bool
|
||||
RegexScansFilePath bool
|
||||
<<<<<<< HEAD
|
||||
FocusStrings []string
|
||||
SkipStrings []string
|
||||
=======
|
||||
FocusString string
|
||||
SkipString string
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
SkipMeasurements bool
|
||||
FailOnPending bool
|
||||
FailFast bool
|
||||
|
|
@ -74,14 +65,11 @@ func processPrefix(prefix string) string {
|
|||
return prefix
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
type flagFunc func(string)
|
||||
|
||||
func (f flagFunc) String() string { return "" }
|
||||
func (f flagFunc) Set(s string) error { f(s); return nil }
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) {
|
||||
prefix = processPrefix(prefix)
|
||||
flagSet.Int64Var(&(GinkgoConfig.RandomSeed), prefix+"seed", time.Now().Unix(), "The seed used to randomize the spec suite.")
|
||||
|
|
@ -92,13 +80,8 @@ func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) {
|
|||
|
||||
flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.")
|
||||
|
||||
<<<<<<< HEAD
|
||||
flagSet.Var(flagFunc(flagFocus), prefix+"focus", "If set, ginkgo will only run specs that match this regular expression. Can be specified multiple times, values are ORed.")
|
||||
flagSet.Var(flagFunc(flagSkip), prefix+"skip", "If set, ginkgo will only run specs that do not match this regular expression. Can be specified multiple times, values are ORed.")
|
||||
=======
|
||||
flagSet.StringVar(&(GinkgoConfig.FocusString), prefix+"focus", "", "If set, ginkgo will only run specs that match this regular expression.")
|
||||
flagSet.StringVar(&(GinkgoConfig.SkipString), prefix+"skip", "", "If set, ginkgo will only run specs that do not match this regular expression.")
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
flagSet.BoolVar(&(GinkgoConfig.RegexScansFilePath), prefix+"regexScansFilePath", false, "If set, ginkgo regex matching also will look at the file path (code location).")
|
||||
|
||||
|
|
@ -155,21 +138,12 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
|
|||
result = append(result, fmt.Sprintf("--%sdryRun", prefix))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
for _, s := range ginkgo.FocusStrings {
|
||||
result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, s))
|
||||
}
|
||||
|
||||
for _, s := range ginkgo.SkipStrings {
|
||||
result = append(result, fmt.Sprintf("--%sskip=%s", prefix, s))
|
||||
=======
|
||||
if ginkgo.FocusString != "" {
|
||||
result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, ginkgo.FocusString))
|
||||
}
|
||||
|
||||
if ginkgo.SkipString != "" {
|
||||
result = append(result, fmt.Sprintf("--%sskip=%s", prefix, ginkgo.SkipString))
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
if ginkgo.FlakeAttempts > 1 {
|
||||
|
|
@ -242,7 +216,6 @@ func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultRepor
|
|||
|
||||
return result
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
// flagFocus implements the -focus flag.
|
||||
func flagFocus(arg string) {
|
||||
|
|
@ -257,5 +230,3 @@ func flagSkip(arg string) {
|
|||
GinkgoConfig.SkipStrings = append(GinkgoConfig.SkipStrings, arg)
|
||||
}
|
||||
}
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
<<<<<<< HEAD
|
||||
"reflect"
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -36,11 +33,8 @@ import (
|
|||
"github.com/onsi/ginkgo/types"
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
var deprecationTracker = types.NewDeprecationTracker()
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
const GINKGO_VERSION = config.VERSION
|
||||
const GINKGO_PANIC = `
|
||||
Your test failed.
|
||||
|
|
@ -79,7 +73,6 @@ func GinkgoRandomSeed() int64 {
|
|||
return config.GinkgoConfig.RandomSeed
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
//GinkgoParallelNode is deprecated, use GinkgoParallelProcess instead
|
||||
func GinkgoParallelNode() int {
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.ParallelNode(), codelocation.New(1))
|
||||
|
|
@ -89,11 +82,6 @@ func GinkgoParallelNode() int {
|
|||
//GinkgoParallelProcess returns the parallel process number for the current ginkgo process
|
||||
//The process number is 1-indexed
|
||||
func GinkgoParallelProcess() int {
|
||||
=======
|
||||
//GinkgoParallelNode returns the parallel node number for the current ginkgo process
|
||||
//The node number is 1-indexed
|
||||
func GinkgoParallelNode() int {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return config.GinkgoConfig.ParallelNode
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +102,6 @@ func GinkgoT(optionalOffset ...int) GinkgoTInterface {
|
|||
if len(optionalOffset) > 0 {
|
||||
offset = optionalOffset[0]
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
failedFunc := func() bool {
|
||||
return CurrentGinkgoTestDescription().Failed
|
||||
}
|
||||
|
|
@ -122,15 +109,11 @@ func GinkgoT(optionalOffset ...int) GinkgoTInterface {
|
|||
return CurrentGinkgoTestDescription().FullTestText
|
||||
}
|
||||
return testingtproxy.New(GinkgoWriter, Fail, Skip, failedFunc, nameFunc, offset)
|
||||
=======
|
||||
return testingtproxy.New(GinkgoWriter, Fail, offset)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
//The interface returned by GinkgoT(). This covers most of the methods
|
||||
//in the testing package's T.
|
||||
type GinkgoTInterface interface {
|
||||
<<<<<<< HEAD
|
||||
Cleanup(func())
|
||||
Setenv(key, value string)
|
||||
Error(args ...interface{})
|
||||
|
|
@ -150,22 +133,6 @@ type GinkgoTInterface interface {
|
|||
Skipf(format string, args ...interface{})
|
||||
Skipped() bool
|
||||
TempDir() string
|
||||
=======
|
||||
Fail()
|
||||
Error(args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
FailNow()
|
||||
Fatal(args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
Log(args ...interface{})
|
||||
Logf(format string, args ...interface{})
|
||||
Failed() bool
|
||||
Parallel()
|
||||
Skip(args ...interface{})
|
||||
Skipf(format string, args ...interface{})
|
||||
SkipNow()
|
||||
Skipped() bool
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
//Custom Ginkgo test reporters must implement the Reporter interface.
|
||||
|
|
@ -248,41 +215,27 @@ func RunSpecs(t GinkgoTestingT, description string) bool {
|
|||
if config.DefaultReporterConfig.ReportFile != "" {
|
||||
reportFile := config.DefaultReporterConfig.ReportFile
|
||||
specReporters[0] = reporters.NewJUnitReporter(reportFile)
|
||||
<<<<<<< HEAD
|
||||
specReporters = append(specReporters, buildDefaultReporter())
|
||||
}
|
||||
return runSpecsWithCustomReporters(t, description, specReporters)
|
||||
=======
|
||||
return RunSpecsWithDefaultAndCustomReporters(t, description, specReporters)
|
||||
}
|
||||
return RunSpecsWithCustomReporters(t, description, specReporters)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
//To run your tests with Ginkgo's default reporter and your custom reporter(s), replace
|
||||
//RunSpecs() with this method.
|
||||
func RunSpecsWithDefaultAndCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter())
|
||||
specReporters = append(specReporters, buildDefaultReporter())
|
||||
return runSpecsWithCustomReporters(t, description, specReporters)
|
||||
=======
|
||||
specReporters = append(specReporters, buildDefaultReporter())
|
||||
return RunSpecsWithCustomReporters(t, description, specReporters)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
//To run your tests with your custom reporter(s) (and *not* Ginkgo's default reporter), replace
|
||||
//RunSpecs() with this method. Note that parallel tests will not work correctly without the default reporter
|
||||
func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.CustomReporter())
|
||||
return runSpecsWithCustomReporters(t, description, specReporters)
|
||||
}
|
||||
|
||||
func runSpecsWithCustomReporters(t GinkgoTestingT, description string, specReporters []Reporter) bool {
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
writer := GinkgoWriter.(*writer.Writer)
|
||||
writer.SetStream(config.DefaultReporterConfig.Verbose)
|
||||
reporters := make([]reporters.Reporter, len(specReporters))
|
||||
|
|
@ -290,14 +243,11 @@ func runSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
|
|||
reporters[i] = reporter
|
||||
}
|
||||
passed, hasFocusedTests := global.Suite.Run(t, description, reporters, writer, config.GinkgoConfig)
|
||||
<<<<<<< HEAD
|
||||
|
||||
if deprecationTracker.DidTrackDeprecations() {
|
||||
fmt.Fprintln(colorable.NewColorableStderr(), deprecationTracker.DeprecationsReport())
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
|
||||
fmt.Println("PASS | FOCUSED")
|
||||
os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
|
||||
|
|
@ -451,20 +401,14 @@ func XWhen(text string, body func()) bool {
|
|||
//Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a
|
||||
//function that accepts a Done channel. When you do this, you can also provide an optional timeout.
|
||||
func It(text string, body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
||||
//You can focus individual Its using FIt
|
||||
func FIt(text string, body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -485,20 +429,14 @@ func XIt(text string, _ ...interface{}) bool {
|
|||
//which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks
|
||||
//which apply to It blocks.
|
||||
func Specify(text string, body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
||||
//You can focus individual Specifys using FSpecify
|
||||
func FSpecify(text string, body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -542,40 +480,28 @@ func By(text string, callbacks ...func()) {
|
|||
//The body function must have the signature:
|
||||
// func(b Benchmarker)
|
||||
func Measure(text string, body interface{}, samples int) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples)
|
||||
return true
|
||||
}
|
||||
|
||||
//You can focus individual Measures using FMeasure
|
||||
func FMeasure(text string, body interface{}, samples int) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples)
|
||||
return true
|
||||
}
|
||||
|
||||
//You can mark Measurements as pending using PMeasure
|
||||
func PMeasure(text string, _ ...interface{}) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
|
||||
return true
|
||||
}
|
||||
|
||||
//You can mark Measurements as pending using XMeasure
|
||||
func XMeasure(text string, _ ...interface{}) bool {
|
||||
<<<<<<< HEAD
|
||||
deprecationTracker.TrackDeprecation(types.Deprecations.Measure(), codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
|
||||
return true
|
||||
}
|
||||
|
|
@ -587,10 +513,7 @@ func XMeasure(text string, _ ...interface{}) bool {
|
|||
//
|
||||
//You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level.
|
||||
func BeforeSuite(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -604,10 +527,7 @@ func BeforeSuite(body interface{}, timeout ...float64) bool {
|
|||
//
|
||||
//You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level.
|
||||
func AfterSuite(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -695,10 +615,7 @@ func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, tim
|
|||
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
|
||||
//a Done channel
|
||||
func BeforeEach(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -709,10 +626,7 @@ func BeforeEach(body interface{}, timeout ...float64) bool {
|
|||
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
|
||||
//a Done channel
|
||||
func JustBeforeEach(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -723,10 +637,7 @@ func JustBeforeEach(body interface{}, timeout ...float64) bool {
|
|||
//Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts
|
||||
//a Done channel
|
||||
func JustAfterEach(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
|
@ -737,15 +648,11 @@ func JustAfterEach(body interface{}, timeout ...float64) bool {
|
|||
//Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts
|
||||
//a Done channel
|
||||
func AfterEach(body interface{}, timeout ...float64) bool {
|
||||
<<<<<<< HEAD
|
||||
validateBodyFunc(body, codelocation.New(1))
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
global.Suite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
|
||||
return true
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func validateBodyFunc(body interface{}, cl types.CodeLocation) {
|
||||
t := reflect.TypeOf(body)
|
||||
if t.Kind() != reflect.Func {
|
||||
|
|
@ -765,8 +672,6 @@ func validateBodyFunc(body interface{}, cl types.CodeLocation) {
|
|||
}
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func parseTimeout(timeout ...float64) time.Duration {
|
||||
if len(timeout) == 0 {
|
||||
return global.DefaultTimeout
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
module github.com/onsi/ginkgo
|
||||
|
||||
<<<<<<< HEAD
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
|
|
@ -12,14 +11,3 @@ require (
|
|||
)
|
||||
|
||||
retract v1.16.3 // git tag accidentally associated with incorrect git commit
|
||||
=======
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/nxadm/tail v1.4.4
|
||||
github.com/onsi/gomega v1.10.1
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
)
|
||||
|
||||
go 1.13
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -7,13 +6,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
=======
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
|
|
@ -26,7 +18,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
|||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
<<<<<<< HEAD
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
|
|
@ -77,41 +68,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
=======
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
|
|
@ -121,18 +77,10 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
<<<<<<< HEAD
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
=======
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
// +build darwin
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// +build dragonfly
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// +build freebsd
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
// +build linux
|
||||
// +build !mips64le
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
12
vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux_mips64le.go
generated
vendored
12
vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux_mips64le.go
generated
vendored
|
|
@ -1,12 +0,0 @@
|
|||
// +build linux
|
||||
// +build mips64le
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup3(oldfd, newfd, 0)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// +build netbsd
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// +build openbsd
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
// +build solaris
|
||||
|
||||
package remote
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func interceptorDupx(oldfd int, newfd int) {
|
||||
unix.Dup2(oldfd, newfd)
|
||||
}
|
||||
|
|
@ -8,10 +8,7 @@ import (
|
|||
"os"
|
||||
|
||||
"github.com/nxadm/tail"
|
||||
<<<<<<< HEAD
|
||||
"golang.org/x/sys/unix"
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
)
|
||||
|
||||
func NewOutputInterceptor() OutputInterceptor {
|
||||
|
|
@ -39,15 +36,10 @@ func (interceptor *outputInterceptor) StartInterceptingOutput() error {
|
|||
return err
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// This might call Dup3 if the dup2 syscall is not available, e.g. on
|
||||
// linux/arm64 or linux/riscv64
|
||||
unix.Dup2(int(interceptor.redirectFile.Fd()), 1)
|
||||
unix.Dup2(int(interceptor.redirectFile.Fd()), 2)
|
||||
=======
|
||||
interceptorDupx(int(interceptor.redirectFile.Fd()), 1)
|
||||
interceptorDupx(int(interceptor.redirectFile.Fd()), 2)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
if interceptor.streamTarget != nil {
|
||||
interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true})
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ import (
|
|||
"math/rand"
|
||||
"regexp"
|
||||
"sort"
|
||||
<<<<<<< HEAD
|
||||
"strings"
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
)
|
||||
|
||||
type Specs struct {
|
||||
|
|
@ -50,19 +47,11 @@ func (e *Specs) Shuffle(r *rand.Rand) {
|
|||
e.names = names
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (e *Specs) ApplyFocus(description string, focus, skip []string) {
|
||||
if len(focus)+len(skip) == 0 {
|
||||
e.applyProgrammaticFocus()
|
||||
} else {
|
||||
e.applyRegExpFocusAndSkip(description, focus, skip)
|
||||
=======
|
||||
func (e *Specs) ApplyFocus(description string, focusString string, skipString string) {
|
||||
if focusString == "" && skipString == "" {
|
||||
e.applyProgrammaticFocus()
|
||||
} else {
|
||||
e.applyRegExpFocusAndSkip(description, focusString, skipString)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +91,6 @@ func (e *Specs) toMatch(description string, i int) []byte {
|
|||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (e *Specs) applyRegExpFocusAndSkip(description string, focus, skip []string) {
|
||||
var focusFilter, skipFilter *regexp.Regexp
|
||||
if len(focus) > 0 {
|
||||
|
|
@ -110,16 +98,6 @@ func (e *Specs) applyRegExpFocusAndSkip(description string, focus, skip []string
|
|||
}
|
||||
if len(skip) > 0 {
|
||||
skipFilter = regexp.MustCompile(strings.Join(skip, "|"))
|
||||
=======
|
||||
func (e *Specs) applyRegExpFocusAndSkip(description string, focusString string, skipString string) {
|
||||
var focusFilter *regexp.Regexp
|
||||
if focusString != "" {
|
||||
focusFilter = regexp.MustCompile(focusString)
|
||||
}
|
||||
var skipFilter *regexp.Regexp
|
||||
if skipString != "" {
|
||||
skipFilter = regexp.MustCompile(skipString)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
for i, spec := range e.specs {
|
||||
|
|
|
|||
|
|
@ -97,11 +97,7 @@ func (suite *Suite) generateSpecsIterator(description string, config config.Gink
|
|||
specs.Shuffle(rand.New(rand.NewSource(config.RandomSeed)))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
specs.ApplyFocus(description, config.FocusStrings, config.SkipStrings)
|
||||
=======
|
||||
specs.ApplyFocus(description, config.FocusString, config.SkipString)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
if config.SkipMeasurements {
|
||||
specs.SkipMeasurements()
|
||||
|
|
|
|||
|
|
@ -6,42 +6,30 @@ import (
|
|||
)
|
||||
|
||||
type failFunc func(message string, callerSkip ...int)
|
||||
<<<<<<< HEAD
|
||||
type skipFunc func(message string, callerSkip ...int)
|
||||
type failedFunc func() bool
|
||||
type nameFunc func() string
|
||||
|
||||
func New(writer io.Writer, fail failFunc, skip skipFunc, failed failedFunc, name nameFunc, offset int) *ginkgoTestingTProxy {
|
||||
=======
|
||||
|
||||
func New(writer io.Writer, fail failFunc, offset int) *ginkgoTestingTProxy {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return &ginkgoTestingTProxy{
|
||||
fail: fail,
|
||||
offset: offset,
|
||||
writer: writer,
|
||||
<<<<<<< HEAD
|
||||
skip: skip,
|
||||
failed: failed,
|
||||
name: name,
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
|
||||
type ginkgoTestingTProxy struct {
|
||||
fail failFunc
|
||||
<<<<<<< HEAD
|
||||
skip skipFunc
|
||||
failed failedFunc
|
||||
name nameFunc
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
offset int
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (t *ginkgoTestingTProxy) Cleanup(func()) {
|
||||
// No-op
|
||||
}
|
||||
|
|
@ -51,8 +39,6 @@ func (t *ginkgoTestingTProxy) Setenv(kev, value string) {
|
|||
// No-op until Cleanup is implemented
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (t *ginkgoTestingTProxy) Error(args ...interface{}) {
|
||||
t.fail(fmt.Sprintln(args...), t.offset)
|
||||
}
|
||||
|
|
@ -69,13 +55,10 @@ func (t *ginkgoTestingTProxy) FailNow() {
|
|||
t.fail("failed", t.offset)
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (t *ginkgoTestingTProxy) Failed() bool {
|
||||
return t.failed()
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (t *ginkgoTestingTProxy) Fatal(args ...interface{}) {
|
||||
t.fail(fmt.Sprintln(args...), t.offset)
|
||||
}
|
||||
|
|
@ -84,13 +67,10 @@ func (t *ginkgoTestingTProxy) Fatalf(format string, args ...interface{}) {
|
|||
t.fail(fmt.Sprintf(format, args...), t.offset)
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (t *ginkgoTestingTProxy) Helper() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (t *ginkgoTestingTProxy) Log(args ...interface{}) {
|
||||
fmt.Fprintln(t.writer, args...)
|
||||
}
|
||||
|
|
@ -99,7 +79,6 @@ func (t *ginkgoTestingTProxy) Logf(format string, args ...interface{}) {
|
|||
t.Log(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func (t *ginkgoTestingTProxy) Name() string {
|
||||
return t.name()
|
||||
}
|
||||
|
|
@ -118,34 +97,13 @@ func (t *ginkgoTestingTProxy) SkipNow() {
|
|||
|
||||
func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) {
|
||||
t.skip(fmt.Sprintf(format, args...), t.offset)
|
||||
=======
|
||||
func (t *ginkgoTestingTProxy) Failed() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *ginkgoTestingTProxy) Parallel() {
|
||||
}
|
||||
|
||||
func (t *ginkgoTestingTProxy) Skip(args ...interface{}) {
|
||||
fmt.Println(args...)
|
||||
}
|
||||
|
||||
func (t *ginkgoTestingTProxy) Skipf(format string, args ...interface{}) {
|
||||
t.Skip(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (t *ginkgoTestingTProxy) SkipNow() {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
func (t *ginkgoTestingTProxy) Skipped() bool {
|
||||
return false
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
func (t *ginkgoTestingTProxy) TempDir() string {
|
||||
// No-op
|
||||
return ""
|
||||
}
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -33,23 +33,12 @@ type JUnitTestSuite struct {
|
|||
type JUnitTestCase struct {
|
||||
Name string `xml:"name,attr"`
|
||||
ClassName string `xml:"classname,attr"`
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
PassedMessage *JUnitPassedMessage `xml:"passed,omitempty"`
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
FailureMessage *JUnitFailureMessage `xml:"failure,omitempty"`
|
||||
Skipped *JUnitSkipped `xml:"skipped,omitempty"`
|
||||
Time float64 `xml:"time,attr"`
|
||||
SystemOut string `xml:"system-out,omitempty"`
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
type JUnitPassedMessage struct {
|
||||
Message string `xml:",chardata"`
|
||||
}
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
type JUnitFailureMessage struct {
|
||||
Type string `xml:"type,attr"`
|
||||
Message string `xml:",chardata"`
|
||||
|
|
@ -120,13 +109,7 @@ func (reporter *JUnitReporter) SpecDidComplete(specSummary *types.SpecSummary) {
|
|||
ClassName: reporter.testSuiteName,
|
||||
}
|
||||
if reporter.ReporterConfig.ReportPassed && specSummary.State == types.SpecStatePassed {
|
||||
<<<<<<< HEAD
|
||||
testCase.SystemOut = specSummary.CapturedOutput
|
||||
=======
|
||||
testCase.PassedMessage = &JUnitPassedMessage{
|
||||
Message: specSummary.CapturedOutput,
|
||||
}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
if specSummary.State == types.SpecStateFailed || specSummary.State == types.SpecStateTimedOut || specSummary.State == types.SpecStatePanicked {
|
||||
testCase.FailureMessage = &JUnitFailureMessage{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
language: go
|
||||
<<<<<<< HEAD
|
||||
arch:
|
||||
- amd64
|
||||
- ppc64le
|
||||
|
|
@ -8,29 +7,12 @@ go:
|
|||
- gotip
|
||||
- 1.16.x
|
||||
- 1.15.x
|
||||
=======
|
||||
|
||||
go:
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- gotip
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
|
||||
<<<<<<< HEAD
|
||||
install: skip
|
||||
|
||||
script:
|
||||
- go mod tidy && git diff --exit-code go.mod go.sum
|
||||
- make test
|
||||
=======
|
||||
install:
|
||||
- go get -v ./...
|
||||
- go build ./...
|
||||
- go get github.com/onsi/ginkgo
|
||||
- go install github.com/onsi/ginkgo/ginkgo
|
||||
|
||||
script: make test
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
## 1.17.0
|
||||
|
||||
### Features
|
||||
|
|
@ -97,8 +96,6 @@ In addition, 1.15.0 cleans up some of Gomega's internals. Most users shouldn't
|
|||
### Fixes
|
||||
- updates golang/x/net to fix vulnerability detected by snyk (#394) [c479356]
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
## 1.10.2
|
||||
|
||||
### Fixes
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
###### Help ###################################################################
|
||||
|
||||
.DEFAULT_GOAL = help
|
||||
|
|
@ -32,11 +31,3 @@ docker_test: ## Run tests in a container via docker-compose
|
|||
|
||||
version: ## Display the version of Go
|
||||
@@go version
|
||||
=======
|
||||
test:
|
||||
[ -z "`gofmt -s -w -l -e .`" ]
|
||||
go vet
|
||||
ginkgo -p -r --randomizeAllSpecs --failOnPending --randomizeSuites --race
|
||||
|
||||
.PHONY: test
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||

|
||||
|
||||
<<<<<<< HEAD
|
||||
[](https://github.com/onsi/gomega/actions/workflows/test.yml)
|
||||
=======
|
||||
[](https://travis-ci.org/onsi/gomega)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
Jump straight to the [docs](http://onsi.github.io/gomega/) to learn about Gomega, including a list of [all available matchers](http://onsi.github.io/gomega/#provided-matchers).
|
||||
|
||||
|
|
|
|||
|
|
@ -7,10 +7,7 @@ Gomega's format package pretty-prints objects. It explores input objects recurs
|
|||
package format
|
||||
|
||||
import (
|
||||
<<<<<<< HEAD
|
||||
"context"
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
|
@ -21,13 +18,10 @@ import (
|
|||
// Use MaxDepth to set the maximum recursion depth when printing deeply nested objects
|
||||
var MaxDepth = uint(10)
|
||||
|
||||
<<<<<<< HEAD
|
||||
// MaxLength of the string representation of an object.
|
||||
// If MaxLength is set to 0, the Object will not be truncated.
|
||||
var MaxLength = 4000
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
/*
|
||||
By default, all objects (even those that implement fmt.Stringer and fmt.GoStringer) are recursively inspected to generate output.
|
||||
|
||||
|
|
@ -55,20 +49,7 @@ var TruncateThreshold uint = 50
|
|||
// after the first diff location in a truncated string assertion error message.
|
||||
var CharactersAroundMismatchToInclude uint = 5
|
||||
|
||||
<<<<<<< HEAD
|
||||
var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||
=======
|
||||
// Ctx interface defined here to keep backwards compatibility with go < 1.7
|
||||
// It matches the context.Context interface
|
||||
type Ctx interface {
|
||||
Deadline() (deadline time.Time, ok bool)
|
||||
Done() <-chan struct{}
|
||||
Err() error
|
||||
Value(key interface{}) interface{}
|
||||
}
|
||||
|
||||
var contextType = reflect.TypeOf((*Ctx)(nil)).Elem()
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
var timeType = reflect.TypeOf(time.Time{})
|
||||
|
||||
//The default indentation string emitted by the format package
|
||||
|
|
@ -76,7 +57,6 @@ var Indent = " "
|
|||
|
||||
var longFormThreshold = 20
|
||||
|
||||
<<<<<<< HEAD
|
||||
// GomegaStringer allows for custom formating of objects for gomega.
|
||||
type GomegaStringer interface {
|
||||
// GomegaString will be used to custom format an object.
|
||||
|
|
@ -85,8 +65,6 @@ type GomegaStringer interface {
|
|||
GomegaString() string
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
/*
|
||||
Generates a formatted matcher success/failure message of the form:
|
||||
|
||||
|
|
@ -131,7 +109,6 @@ func MessageWithDiff(actual, message, expected string) string {
|
|||
|
||||
tabLength := 4
|
||||
spaceFromMessageToActual := tabLength + len("<string>: ") - len(message)
|
||||
<<<<<<< HEAD
|
||||
|
||||
paddingCount := spaceFromMessageToActual + spacesBeforeFormattedMismatch
|
||||
if paddingCount < 0 {
|
||||
|
|
@ -139,9 +116,6 @@ func MessageWithDiff(actual, message, expected string) string {
|
|||
}
|
||||
|
||||
padding := strings.Repeat(" ", paddingCount) + "|"
|
||||
=======
|
||||
padding := strings.Repeat(" ", spaceFromMessageToActual+spacesBeforeFormattedMismatch) + "|"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return Message(formattedActual, message+padding, formattedExpected)
|
||||
}
|
||||
|
||||
|
|
@ -197,7 +171,6 @@ func findFirstMismatch(a, b string) int {
|
|||
return 0
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
const truncateHelpText = `
|
||||
Gomega truncated this representation as it exceeds 'format.MaxLength'.
|
||||
Consider having the object provide a custom 'GomegaStringer' representation
|
||||
|
|
@ -225,8 +198,6 @@ func truncateLongStrings(s string) string {
|
|||
return s
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
/*
|
||||
Pretty prints the passed in object at the passed in indentation level.
|
||||
|
||||
|
|
@ -241,11 +212,7 @@ Set PrintContextObjects to true to print the content of objects implementing con
|
|||
func Object(object interface{}, indentation uint) string {
|
||||
indent := strings.Repeat(Indent, int(indentation))
|
||||
value := reflect.ValueOf(object)
|
||||
<<<<<<< HEAD
|
||||
return fmt.Sprintf("%s<%s>: %s", indent, formatType(value), formatValue(value, indentation))
|
||||
=======
|
||||
return fmt.Sprintf("%s<%s>: %s", indent, formatType(object), formatValue(value, indentation))
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -265,7 +232,6 @@ func IndentString(s string, indentation uint) string {
|
|||
return result
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func formatType(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
|
|
@ -280,27 +246,6 @@ func formatType(v reflect.Value) string {
|
|||
return fmt.Sprintf("%s | len:%d", v.Type(), v.Len())
|
||||
default:
|
||||
return fmt.Sprintf("%s", v.Type())
|
||||
=======
|
||||
func formatType(object interface{}) string {
|
||||
t := reflect.TypeOf(object)
|
||||
if t == nil {
|
||||
return "nil"
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Chan:
|
||||
v := reflect.ValueOf(object)
|
||||
return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
|
||||
case reflect.Ptr:
|
||||
return fmt.Sprintf("%T | %p", object, object)
|
||||
case reflect.Slice:
|
||||
v := reflect.ValueOf(object)
|
||||
return fmt.Sprintf("%T | len:%d, cap:%d", object, v.Len(), v.Cap())
|
||||
case reflect.Map:
|
||||
v := reflect.ValueOf(object)
|
||||
return fmt.Sprintf("%T | len:%d", object, v.Len())
|
||||
default:
|
||||
return fmt.Sprintf("%T", object)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +258,6 @@ func formatValue(value reflect.Value, indentation uint) string {
|
|||
return "nil"
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if value.CanInterface() {
|
||||
obj := value.Interface()
|
||||
|
||||
|
|
@ -329,16 +273,6 @@ func formatValue(value reflect.Value, indentation uint) string {
|
|||
return truncateLongStrings(x.GoString())
|
||||
case fmt.Stringer:
|
||||
return truncateLongStrings(x.String())
|
||||
=======
|
||||
if UseStringerRepresentation {
|
||||
if value.CanInterface() {
|
||||
obj := value.Interface()
|
||||
switch x := obj.(type) {
|
||||
case fmt.GoStringer:
|
||||
return x.GoString()
|
||||
case fmt.Stringer:
|
||||
return x.String()
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -369,7 +303,6 @@ func formatValue(value reflect.Value, indentation uint) string {
|
|||
case reflect.Ptr:
|
||||
return formatValue(value.Elem(), indentation)
|
||||
case reflect.Slice:
|
||||
<<<<<<< HEAD
|
||||
return truncateLongStrings(formatSlice(value, indentation))
|
||||
case reflect.String:
|
||||
return truncateLongStrings(formatString(value.String(), indentation))
|
||||
|
|
@ -377,21 +310,11 @@ func formatValue(value reflect.Value, indentation uint) string {
|
|||
return truncateLongStrings(formatSlice(value, indentation))
|
||||
case reflect.Map:
|
||||
return truncateLongStrings(formatMap(value, indentation))
|
||||
=======
|
||||
return formatSlice(value, indentation)
|
||||
case reflect.String:
|
||||
return formatString(value.String(), indentation)
|
||||
case reflect.Array:
|
||||
return formatSlice(value, indentation)
|
||||
case reflect.Map:
|
||||
return formatMap(value, indentation)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
case reflect.Struct:
|
||||
if value.Type() == timeType && value.CanInterface() {
|
||||
t, _ := value.Interface().(time.Time)
|
||||
return t.Format(time.RFC3339Nano)
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
return truncateLongStrings(formatStruct(value, indentation))
|
||||
case reflect.Interface:
|
||||
return formatInterface(value, indentation)
|
||||
|
|
@ -400,16 +323,6 @@ func formatValue(value reflect.Value, indentation uint) string {
|
|||
return truncateLongStrings(fmt.Sprintf("%#v", value.Interface()))
|
||||
}
|
||||
return truncateLongStrings(fmt.Sprintf("%#v", value))
|
||||
=======
|
||||
return formatStruct(value, indentation)
|
||||
case reflect.Interface:
|
||||
return formatValue(value.Elem(), indentation)
|
||||
default:
|
||||
if value.CanInterface() {
|
||||
return fmt.Sprintf("%#v", value.Interface())
|
||||
}
|
||||
return fmt.Sprintf("%#v", value)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -499,13 +412,10 @@ func formatStruct(v reflect.Value, indentation uint) string {
|
|||
return fmt.Sprintf("{%s}", strings.Join(result, ", "))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func formatInterface(v reflect.Value, indentation uint) string {
|
||||
return fmt.Sprintf("<%s>%s", formatType(v.Elem()), formatValue(v.Elem(), indentation))
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func isNilValue(a reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Invalid:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
module github.com/onsi/gomega
|
||||
|
||||
<<<<<<< HEAD
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
|
|
@ -8,12 +7,4 @@ require (
|
|||
github.com/onsi/ginkgo v1.16.4
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
=======
|
||||
require (
|
||||
github.com/golang/protobuf v1.4.2
|
||||
github.com/onsi/ginkgo v1.12.1
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/yaml.v2 v2.3.0
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
<<<<<<< HEAD
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -7,18 +6,12 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo
|
|||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
=======
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
<<<<<<< HEAD
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
|
|
@ -91,71 +84,23 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
=======
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
<<<<<<< HEAD
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
=======
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
<<<<<<< HEAD
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
=======
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ Gomega is MIT-Licensed
|
|||
package gomega
|
||||
|
||||
import (
|
||||
<<<<<<< HEAD
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
|
@ -26,27 +25,11 @@ import (
|
|||
const GOMEGA_VERSION = "1.17.0"
|
||||
|
||||
const nilGomegaPanic = `You are trying to make an assertion, but haven't registered Gomega's fail handler.
|
||||
=======
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/gomega/internal/assertion"
|
||||
"github.com/onsi/gomega/internal/asyncassertion"
|
||||
"github.com/onsi/gomega/internal/testingtsupport"
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
const GOMEGA_VERSION = "1.10.2"
|
||||
|
||||
const nilFailHandlerPanic = `You are trying to make an assertion, but Gomega's fail handler is nil.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
If you're using Ginkgo then you probably forgot to put your assertion in an It().
|
||||
Alternatively, you may have forgotten to register a fail handler with RegisterFailHandler() or RegisterTestingT().
|
||||
Depending on your vendoring solution you may be inadvertently importing gomega and subpackages (e.g. ghhtp, gexec,...) from different locations.
|
||||
`
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Gomega describes the essential Gomega DSL. This interface allows libraries
|
||||
// to abstract between the standard package-level function implementations
|
||||
// and alternatives like *WithT.
|
||||
|
|
@ -119,81 +102,16 @@ func RegisterFailHandlerWithT(_ types.GomegaTestingT, fail types.GomegaFailHandl
|
|||
// Testing.T tests. It is now deprecated and you should use NewWithT() instead to get a fresh instance of Gomega for each test.
|
||||
func RegisterTestingT(t types.GomegaTestingT) {
|
||||
Default.(*internal.Gomega).ConfigureWithT(t)
|
||||
=======
|
||||
var globalFailWrapper *types.GomegaFailWrapper
|
||||
|
||||
var defaultEventuallyTimeout = time.Second
|
||||
var defaultEventuallyPollingInterval = 10 * time.Millisecond
|
||||
var defaultConsistentlyDuration = 100 * time.Millisecond
|
||||
var defaultConsistentlyPollingInterval = 10 * time.Millisecond
|
||||
|
||||
// RegisterFailHandler connects Ginkgo to Gomega. When a matcher fails
|
||||
// the fail handler passed into RegisterFailHandler is called.
|
||||
func RegisterFailHandler(handler types.GomegaFailHandler) {
|
||||
RegisterFailHandlerWithT(testingtsupport.EmptyTWithHelper{}, handler)
|
||||
}
|
||||
|
||||
// RegisterFailHandlerWithT ensures that the given types.TWithHelper and fail handler
|
||||
// are used globally.
|
||||
func RegisterFailHandlerWithT(t types.TWithHelper, handler types.GomegaFailHandler) {
|
||||
if handler == nil {
|
||||
globalFailWrapper = nil
|
||||
return
|
||||
}
|
||||
|
||||
globalFailWrapper = &types.GomegaFailWrapper{
|
||||
Fail: handler,
|
||||
TWithHelper: t,
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTestingT connects Gomega to Golang's XUnit style
|
||||
// Testing.T tests. It is now deprecated and you should use NewWithT() instead.
|
||||
//
|
||||
// Legacy Documentation:
|
||||
//
|
||||
// You'll need to call this at the top of each XUnit style test:
|
||||
//
|
||||
// func TestFarmHasCow(t *testing.T) {
|
||||
// RegisterTestingT(t)
|
||||
//
|
||||
// f := farm.New([]string{"Cow", "Horse"})
|
||||
// Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
|
||||
// }
|
||||
//
|
||||
// Note that this *testing.T is registered *globally* by Gomega (this is why you don't have to
|
||||
// pass `t` down to the matcher itself). This means that you cannot run the XUnit style tests
|
||||
// in parallel as the global fail handler cannot point to more than one testing.T at a time.
|
||||
//
|
||||
// NewWithT() does not have this limitation
|
||||
//
|
||||
// (As an aside: Ginkgo gets around this limitation by running parallel tests in different *processes*).
|
||||
func RegisterTestingT(t types.GomegaTestingT) {
|
||||
tWithHelper, hasHelper := t.(types.TWithHelper)
|
||||
if !hasHelper {
|
||||
RegisterFailHandler(testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
|
||||
return
|
||||
}
|
||||
RegisterFailHandlerWithT(tWithHelper, testingtsupport.BuildTestingTGomegaFailWrapper(t).Fail)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// InterceptGomegaFailures runs a given callback and returns an array of
|
||||
// failure messages generated by any Gomega assertions within the callback.
|
||||
<<<<<<< HEAD
|
||||
// Exeuction continues after the first failure allowing users to collect all failures
|
||||
// in the callback.
|
||||
=======
|
||||
//
|
||||
// This is accomplished by temporarily replacing the *global* fail handler
|
||||
// with a fail handler that simply annotates failures. The original fail handler
|
||||
// is reset when InterceptGomegaFailures returns.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//
|
||||
// This is most useful when testing custom matchers, but can also be used to check
|
||||
// on a value using a Gomega assertion without causing a test failure.
|
||||
func InterceptGomegaFailures(f func()) []string {
|
||||
<<<<<<< HEAD
|
||||
originalHandler := Default.(*internal.Gomega).Fail
|
||||
failures := []string{}
|
||||
Default.(*internal.Gomega).Fail = func(message string, callerSkip ...int) {
|
||||
|
|
@ -238,18 +156,6 @@ func ensureDefaultGomegaIsConfigured() {
|
|||
}
|
||||
}
|
||||
|
||||
=======
|
||||
originalHandler := globalFailWrapper.Fail
|
||||
failures := []string{}
|
||||
RegisterFailHandler(func(message string, callerSkip ...int) {
|
||||
failures = append(failures, message)
|
||||
})
|
||||
f()
|
||||
RegisterFailHandler(originalHandler)
|
||||
return failures
|
||||
}
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Ω wraps an actual value allowing assertions to be made on it:
|
||||
// Ω("foo").Should(Equal("foo"))
|
||||
//
|
||||
|
|
@ -268,12 +174,8 @@ func ensureDefaultGomegaIsConfigured() {
|
|||
//
|
||||
// Ω and Expect are identical
|
||||
func Ω(actual interface{}, extra ...interface{}) Assertion {
|
||||
<<<<<<< HEAD
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.Ω(actual, extra...)
|
||||
=======
|
||||
return ExpectWithOffset(0, actual, extra...)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// Expect wraps an actual value allowing assertions to be made on it:
|
||||
|
|
@ -294,30 +196,21 @@ func Ω(actual interface{}, extra ...interface{}) Assertion {
|
|||
//
|
||||
// Expect and Ω are identical
|
||||
func Expect(actual interface{}, extra ...interface{}) Assertion {
|
||||
<<<<<<< HEAD
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.Expect(actual, extra...)
|
||||
=======
|
||||
return ExpectWithOffset(0, actual, extra...)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// ExpectWithOffset wraps an actual value allowing assertions to be made on it:
|
||||
// ExpectWithOffset(1, "foo").To(Equal("foo"))
|
||||
//
|
||||
// Unlike `Expect` and `Ω`, `ExpectWithOffset` takes an additional integer argument
|
||||
<<<<<<< HEAD
|
||||
// that is used to modify the call-stack offset when computing line numbers. It is
|
||||
// the same as `Expect(...).WithOffset`.
|
||||
=======
|
||||
// that is used to modify the call-stack offset when computing line numbers.
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//
|
||||
// This is most useful in helper functions that make assertions. If you want Gomega's
|
||||
// error message to refer to the calling line in the test (as opposed to the line in the helper function)
|
||||
// set the first argument of `ExpectWithOffset` appropriately.
|
||||
func ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
|
||||
<<<<<<< HEAD
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.ExpectWithOffset(offset, actual, extra...)
|
||||
}
|
||||
|
|
@ -415,58 +308,11 @@ the same as `Eventually(...).WithTimeout` or `Eventually(...).WithTimeout(...).W
|
|||
func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.Eventually(actual, intervals...)
|
||||
=======
|
||||
if globalFailWrapper == nil {
|
||||
panic(nilFailHandlerPanic)
|
||||
}
|
||||
return assertion.New(actual, globalFailWrapper, offset, extra...)
|
||||
}
|
||||
|
||||
// Eventually wraps an actual value allowing assertions to be made on it.
|
||||
// The assertion is tried periodically until it passes or a timeout occurs.
|
||||
//
|
||||
// Both the timeout and polling interval are configurable as optional arguments:
|
||||
// The first optional argument is the timeout
|
||||
// The second optional argument is the polling interval
|
||||
//
|
||||
// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the
|
||||
// last case they are interpreted as seconds.
|
||||
//
|
||||
// If Eventually is passed an actual that is a function taking no arguments and returning at least one value,
|
||||
// then Eventually will call the function periodically and try the matcher against the function's first return value.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// Eventually(func() int {
|
||||
// return thingImPolling.Count()
|
||||
// }).Should(BeNumerically(">=", 17))
|
||||
//
|
||||
// Note that this example could be rewritten:
|
||||
//
|
||||
// Eventually(thingImPolling.Count).Should(BeNumerically(">=", 17))
|
||||
//
|
||||
// If the function returns more than one value, then Eventually will pass the first value to the matcher and
|
||||
// assert that all other values are nil/zero.
|
||||
// This allows you to pass Eventually a function that returns a value and an error - a common pattern in Go.
|
||||
//
|
||||
// For example, consider a method that returns a value and an error:
|
||||
// func FetchFromDB() (string, error)
|
||||
//
|
||||
// Then
|
||||
// Eventually(FetchFromDB).Should(Equal("hasselhoff"))
|
||||
//
|
||||
// Will pass only if the the returned error is nil and the returned string passes the matcher.
|
||||
//
|
||||
// Eventually's default timeout is 1 second, and its default polling interval is 10ms
|
||||
func Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
return EventuallyWithOffset(0, actual, intervals...)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// EventuallyWithOffset operates like Eventually but takes an additional
|
||||
// initial argument to indicate an offset in the call stack. This is useful when building helper
|
||||
// functions that contain matchers. To learn more, read about `ExpectWithOffset`.
|
||||
<<<<<<< HEAD
|
||||
//
|
||||
// `EventuallyWithOffset` is the same as `Eventually(...).WithOffset`.
|
||||
//
|
||||
|
|
@ -496,111 +342,37 @@ This will block for 200 milliseconds and repeatedly check the channel and ensure
|
|||
func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.Consistently(actual, intervals...)
|
||||
=======
|
||||
func EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
if globalFailWrapper == nil {
|
||||
panic(nilFailHandlerPanic)
|
||||
}
|
||||
timeoutInterval := defaultEventuallyTimeout
|
||||
pollingInterval := defaultEventuallyPollingInterval
|
||||
if len(intervals) > 0 {
|
||||
timeoutInterval = toDuration(intervals[0])
|
||||
}
|
||||
if len(intervals) > 1 {
|
||||
pollingInterval = toDuration(intervals[1])
|
||||
}
|
||||
return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
|
||||
}
|
||||
|
||||
// Consistently wraps an actual value allowing assertions to be made on it.
|
||||
// The assertion is tried periodically and is required to pass for a period of time.
|
||||
//
|
||||
// Both the total time and polling interval are configurable as optional arguments:
|
||||
// The first optional argument is the duration that Consistently will run for
|
||||
// The second optional argument is the polling interval
|
||||
//
|
||||
// Both intervals can either be specified as time.Duration, parsable duration strings or as floats/integers. In the
|
||||
// last case they are interpreted as seconds.
|
||||
//
|
||||
// If Consistently is passed an actual that is a function taking no arguments and returning at least one value,
|
||||
// then Consistently will call the function periodically and try the matcher against the function's first return value.
|
||||
//
|
||||
// If the function returns more than one value, then Consistently will pass the first value to the matcher and
|
||||
// assert that all other values are nil/zero.
|
||||
// This allows you to pass Consistently a function that returns a value and an error - a common pattern in Go.
|
||||
//
|
||||
// Consistently is useful in cases where you want to assert that something *does not happen* over a period of time.
|
||||
// For example, you want to assert that a goroutine does *not* send data down a channel. In this case, you could:
|
||||
//
|
||||
// Consistently(channel).ShouldNot(Receive())
|
||||
//
|
||||
// Consistently's default duration is 100ms, and its default polling interval is 10ms
|
||||
func Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
return ConsistentlyWithOffset(0, actual, intervals...)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// ConsistentlyWithOffset operates like Consistently but takes an additional
|
||||
// initial argument to indicate an offset in the call stack. This is useful when building helper
|
||||
// functions that contain matchers. To learn more, read about `ExpectWithOffset`.
|
||||
<<<<<<< HEAD
|
||||
//
|
||||
// `ConsistentlyWithOffset` is the same as `Consistently(...).WithOffset` and
|
||||
// optional `WithTimeout` and `WithPolling`.
|
||||
func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
ensureDefaultGomegaIsConfigured()
|
||||
return Default.ConsistentlyWithOffset(offset, actual, intervals...)
|
||||
=======
|
||||
func ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
if globalFailWrapper == nil {
|
||||
panic(nilFailHandlerPanic)
|
||||
}
|
||||
timeoutInterval := defaultConsistentlyDuration
|
||||
pollingInterval := defaultConsistentlyPollingInterval
|
||||
if len(intervals) > 0 {
|
||||
timeoutInterval = toDuration(intervals[0])
|
||||
}
|
||||
if len(intervals) > 1 {
|
||||
pollingInterval = toDuration(intervals[1])
|
||||
}
|
||||
return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, globalFailWrapper, timeoutInterval, pollingInterval, offset)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// SetDefaultEventuallyTimeout sets the default timeout duration for Eventually. Eventually will repeatedly poll your condition until it succeeds, or until this timeout elapses.
|
||||
func SetDefaultEventuallyTimeout(t time.Duration) {
|
||||
<<<<<<< HEAD
|
||||
Default.SetDefaultEventuallyTimeout(t)
|
||||
=======
|
||||
defaultEventuallyTimeout = t
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// SetDefaultEventuallyPollingInterval sets the default polling interval for Eventually.
|
||||
func SetDefaultEventuallyPollingInterval(t time.Duration) {
|
||||
<<<<<<< HEAD
|
||||
Default.SetDefaultEventuallyPollingInterval(t)
|
||||
=======
|
||||
defaultEventuallyPollingInterval = t
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// SetDefaultConsistentlyDuration sets the default duration for Consistently. Consistently will verify that your condition is satisfied for this long.
|
||||
func SetDefaultConsistentlyDuration(t time.Duration) {
|
||||
<<<<<<< HEAD
|
||||
Default.SetDefaultConsistentlyDuration(t)
|
||||
=======
|
||||
defaultConsistentlyDuration = t
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// SetDefaultConsistentlyPollingInterval sets the default polling interval for Consistently.
|
||||
func SetDefaultConsistentlyPollingInterval(t time.Duration) {
|
||||
<<<<<<< HEAD
|
||||
Default.SetDefaultConsistentlyPollingInterval(t)
|
||||
=======
|
||||
defaultConsistentlyPollingInterval = t
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// AsyncAssertion is returned by Eventually and Consistently and polls the actual value passed into Eventually against
|
||||
|
|
@ -618,20 +390,10 @@ func SetDefaultConsistentlyPollingInterval(t time.Duration) {
|
|||
//
|
||||
// Eventually(myChannel).Should(Receive(), "Something should have come down the pipe.")
|
||||
// Consistently(myChannel).ShouldNot(Receive(), func() string { return "Nothing should have come down the pipe." })
|
||||
<<<<<<< HEAD
|
||||
type AsyncAssertion = types.AsyncAssertion
|
||||
|
||||
// GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter.
|
||||
type GomegaAsyncAssertion = types.AsyncAssertion
|
||||
=======
|
||||
type AsyncAssertion interface {
|
||||
Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
}
|
||||
|
||||
// GomegaAsyncAssertion is deprecated in favor of AsyncAssertion, which does not stutter.
|
||||
type GomegaAsyncAssertion = AsyncAssertion
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
// Assertion is returned by Ω and Expect and compares the actual value to the matcher
|
||||
// passed to the Should/ShouldNot and To/ToNot/NotTo methods.
|
||||
|
|
@ -650,7 +412,6 @@ type GomegaAsyncAssertion = AsyncAssertion
|
|||
// Example:
|
||||
//
|
||||
// Ω(farm.HasCow()).Should(BeTrue(), "Farm %v should have a cow", farm)
|
||||
<<<<<<< HEAD
|
||||
type Assertion = types.Assertion
|
||||
|
||||
// GomegaAssertion is deprecated in favor of Assertion, which does not stutter.
|
||||
|
|
@ -658,151 +419,3 @@ type GomegaAssertion = types.Assertion
|
|||
|
||||
// OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it
|
||||
type OmegaMatcher = types.GomegaMatcher
|
||||
=======
|
||||
type Assertion interface {
|
||||
Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
|
||||
To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool
|
||||
}
|
||||
|
||||
// GomegaAssertion is deprecated in favor of Assertion, which does not stutter.
|
||||
type GomegaAssertion = Assertion
|
||||
|
||||
// OmegaMatcher is deprecated in favor of the better-named and better-organized types.GomegaMatcher but sticks around to support existing code that uses it
|
||||
type OmegaMatcher types.GomegaMatcher
|
||||
|
||||
// WithT wraps a *testing.T and provides `Expect`, `Eventually`, and `Consistently` methods. This allows you to leverage
|
||||
// Gomega's rich ecosystem of matchers in standard `testing` test suites.
|
||||
//
|
||||
// Use `NewWithT` to instantiate a `WithT`
|
||||
type WithT struct {
|
||||
t types.GomegaTestingT
|
||||
}
|
||||
|
||||
// GomegaWithT is deprecated in favor of gomega.WithT, which does not stutter.
|
||||
type GomegaWithT = WithT
|
||||
|
||||
// NewWithT takes a *testing.T and returngs a `gomega.WithT` allowing you to use `Expect`, `Eventually`, and `Consistently` along with
|
||||
// Gomega's rich ecosystem of matchers in standard `testing` test suits.
|
||||
//
|
||||
// func TestFarmHasCow(t *testing.T) {
|
||||
// g := gomega.NewWithT(t)
|
||||
//
|
||||
// f := farm.New([]string{"Cow", "Horse"})
|
||||
// g.Expect(f.HasCow()).To(BeTrue(), "Farm should have cow")
|
||||
// }
|
||||
func NewWithT(t types.GomegaTestingT) *WithT {
|
||||
return &WithT{
|
||||
t: t,
|
||||
}
|
||||
}
|
||||
|
||||
// NewGomegaWithT is deprecated in favor of gomega.NewWithT, which does not stutter.
|
||||
func NewGomegaWithT(t types.GomegaTestingT) *GomegaWithT {
|
||||
return NewWithT(t)
|
||||
}
|
||||
|
||||
// ExpectWithOffset is used to make assertions. See documentation for ExpectWithOffset.
|
||||
func (g *WithT) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) Assertion {
|
||||
return assertion.New(actual, testingtsupport.BuildTestingTGomegaFailWrapper(g.t), offset, extra...)
|
||||
}
|
||||
|
||||
// EventuallyWithOffset is used to make asynchronous assertions. See documentation for EventuallyWithOffset.
|
||||
func (g *WithT) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
timeoutInterval := defaultEventuallyTimeout
|
||||
pollingInterval := defaultEventuallyPollingInterval
|
||||
if len(intervals) > 0 {
|
||||
timeoutInterval = toDuration(intervals[0])
|
||||
}
|
||||
if len(intervals) > 1 {
|
||||
pollingInterval = toDuration(intervals[1])
|
||||
}
|
||||
return asyncassertion.New(asyncassertion.AsyncAssertionTypeEventually, actual, testingtsupport.BuildTestingTGomegaFailWrapper(g.t), timeoutInterval, pollingInterval, offset)
|
||||
}
|
||||
|
||||
// ConsistentlyWithOffset is used to make asynchronous assertions. See documentation for ConsistentlyWithOffset.
|
||||
func (g *WithT) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
timeoutInterval := defaultConsistentlyDuration
|
||||
pollingInterval := defaultConsistentlyPollingInterval
|
||||
if len(intervals) > 0 {
|
||||
timeoutInterval = toDuration(intervals[0])
|
||||
}
|
||||
if len(intervals) > 1 {
|
||||
pollingInterval = toDuration(intervals[1])
|
||||
}
|
||||
return asyncassertion.New(asyncassertion.AsyncAssertionTypeConsistently, actual, testingtsupport.BuildTestingTGomegaFailWrapper(g.t), timeoutInterval, pollingInterval, offset)
|
||||
}
|
||||
|
||||
// Expect is used to make assertions. See documentation for Expect.
|
||||
func (g *WithT) Expect(actual interface{}, extra ...interface{}) Assertion {
|
||||
return g.ExpectWithOffset(0, actual, extra...)
|
||||
}
|
||||
|
||||
// Eventually is used to make asynchronous assertions. See documentation for Eventually.
|
||||
func (g *WithT) Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
return g.EventuallyWithOffset(0, actual, intervals...)
|
||||
}
|
||||
|
||||
// Consistently is used to make asynchronous assertions. See documentation for Consistently.
|
||||
func (g *WithT) Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion {
|
||||
return g.ConsistentlyWithOffset(0, actual, intervals...)
|
||||
}
|
||||
|
||||
func toDuration(input interface{}) time.Duration {
|
||||
duration, ok := input.(time.Duration)
|
||||
if ok {
|
||||
return duration
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(input)
|
||||
kind := reflect.TypeOf(input).Kind()
|
||||
|
||||
if reflect.Int <= kind && kind <= reflect.Int64 {
|
||||
return time.Duration(value.Int()) * time.Second
|
||||
} else if reflect.Uint <= kind && kind <= reflect.Uint64 {
|
||||
return time.Duration(value.Uint()) * time.Second
|
||||
} else if reflect.Float32 <= kind && kind <= reflect.Float64 {
|
||||
return time.Duration(value.Float() * float64(time.Second))
|
||||
} else if reflect.String == kind {
|
||||
duration, err := time.ParseDuration(value.String())
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
|
||||
}
|
||||
|
||||
// Gomega describes the essential Gomega DSL. This interface allows libraries
|
||||
// to abstract between the standard package-level function implementations
|
||||
// and alternatives like *WithT.
|
||||
type Gomega interface {
|
||||
Expect(actual interface{}, extra ...interface{}) Assertion
|
||||
Eventually(actual interface{}, intervals ...interface{}) AsyncAssertion
|
||||
Consistently(actual interface{}, intervals ...interface{}) AsyncAssertion
|
||||
}
|
||||
|
||||
type globalFailHandlerGomega struct{}
|
||||
|
||||
// DefaultGomega supplies the standard package-level implementation
|
||||
var Default Gomega = globalFailHandlerGomega{}
|
||||
|
||||
// Expect is used to make assertions. See documentation for Expect.
|
||||
func (globalFailHandlerGomega) Expect(actual interface{}, extra ...interface{}) Assertion {
|
||||
return Expect(actual, extra...)
|
||||
}
|
||||
|
||||
// Eventually is used to make asynchronous assertions. See documentation for Eventually.
|
||||
func (globalFailHandlerGomega) Eventually(actual interface{}, extra ...interface{}) AsyncAssertion {
|
||||
return Eventually(actual, extra...)
|
||||
}
|
||||
|
||||
// Consistently is used to make asynchronous assertions. See documentation for Consistently.
|
||||
func (globalFailHandlerGomega) Consistently(actual interface{}, extra ...interface{}) AsyncAssertion {
|
||||
return Consistently(actual, extra...)
|
||||
}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
package assertion
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
type Assertion struct {
|
||||
actualInput interface{}
|
||||
failWrapper *types.GomegaFailWrapper
|
||||
offset int
|
||||
extra []interface{}
|
||||
}
|
||||
|
||||
func New(actualInput interface{}, failWrapper *types.GomegaFailWrapper, offset int, extra ...interface{}) *Assertion {
|
||||
return &Assertion{
|
||||
actualInput: actualInput,
|
||||
failWrapper: failWrapper,
|
||||
offset: offset,
|
||||
extra: extra,
|
||||
}
|
||||
}
|
||||
|
||||
func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string {
|
||||
switch len(optionalDescription) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
if describe, ok := optionalDescription[0].(func() string); ok {
|
||||
return describe() + "\n"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
|
||||
}
|
||||
|
||||
func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
|
||||
matches, err := matcher.Match(assertion.actualInput)
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
if err != nil {
|
||||
description := assertion.buildDescription(optionalDescription...)
|
||||
assertion.failWrapper.Fail(description+err.Error(), 2+assertion.offset)
|
||||
return false
|
||||
}
|
||||
if matches != desiredMatch {
|
||||
var message string
|
||||
if desiredMatch {
|
||||
message = matcher.FailureMessage(assertion.actualInput)
|
||||
} else {
|
||||
message = matcher.NegatedFailureMessage(assertion.actualInput)
|
||||
}
|
||||
description := assertion.buildDescription(optionalDescription...)
|
||||
assertion.failWrapper.Fail(description+message, 2+assertion.offset)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
|
||||
success, message := vetExtras(assertion.extra)
|
||||
if success {
|
||||
return true
|
||||
}
|
||||
|
||||
description := assertion.buildDescription(optionalDescription...)
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
assertion.failWrapper.Fail(description+message, 2+assertion.offset)
|
||||
return false
|
||||
}
|
||||
|
||||
func vetExtras(extras []interface{}) (bool, string) {
|
||||
for i, extra := range extras {
|
||||
if extra != nil {
|
||||
zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
|
||||
if !reflect.DeepEqual(zeroValue, extra) {
|
||||
message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
|
||||
return false, message
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
// untested sections: 2
|
||||
|
||||
package asyncassertion
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/gomega/internal/oraclematcher"
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
type AsyncAssertionType uint
|
||||
|
||||
const (
|
||||
AsyncAssertionTypeEventually AsyncAssertionType = iota
|
||||
AsyncAssertionTypeConsistently
|
||||
)
|
||||
|
||||
type AsyncAssertion struct {
|
||||
asyncType AsyncAssertionType
|
||||
actualInput interface{}
|
||||
timeoutInterval time.Duration
|
||||
pollingInterval time.Duration
|
||||
failWrapper *types.GomegaFailWrapper
|
||||
offset int
|
||||
}
|
||||
|
||||
func New(asyncType AsyncAssertionType, actualInput interface{}, failWrapper *types.GomegaFailWrapper, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
|
||||
actualType := reflect.TypeOf(actualInput)
|
||||
if actualType.Kind() == reflect.Func {
|
||||
if actualType.NumIn() != 0 || actualType.NumOut() == 0 {
|
||||
panic("Expected a function with no arguments and one or more return values.")
|
||||
}
|
||||
}
|
||||
|
||||
return &AsyncAssertion{
|
||||
asyncType: asyncType,
|
||||
actualInput: actualInput,
|
||||
failWrapper: failWrapper,
|
||||
timeoutInterval: timeoutInterval,
|
||||
pollingInterval: pollingInterval,
|
||||
offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.match(matcher, true, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
return assertion.match(matcher, false, optionalDescription...)
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
|
||||
switch len(optionalDescription) {
|
||||
case 0:
|
||||
return ""
|
||||
case 1:
|
||||
if describe, ok := optionalDescription[0].(func() string); ok {
|
||||
return describe() + "\n"
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) actualInputIsAFunction() bool {
|
||||
actualType := reflect.TypeOf(assertion.actualInput)
|
||||
return actualType.Kind() == reflect.Func && actualType.NumIn() == 0 && actualType.NumOut() > 0
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
|
||||
if assertion.actualInputIsAFunction() {
|
||||
values := reflect.ValueOf(assertion.actualInput).Call([]reflect.Value{})
|
||||
|
||||
extras := []interface{}{}
|
||||
for _, value := range values[1:] {
|
||||
extras = append(extras, value.Interface())
|
||||
}
|
||||
|
||||
success, message := vetExtras(extras)
|
||||
|
||||
if !success {
|
||||
return nil, errors.New(message)
|
||||
}
|
||||
|
||||
return values[0].Interface(), nil
|
||||
}
|
||||
|
||||
return assertion.actualInput, nil
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
|
||||
if assertion.actualInputIsAFunction() {
|
||||
return true
|
||||
}
|
||||
|
||||
return oraclematcher.MatchMayChangeInTheFuture(matcher, value)
|
||||
}
|
||||
|
||||
func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
|
||||
timer := time.Now()
|
||||
timeout := time.After(assertion.timeoutInterval)
|
||||
|
||||
var matches bool
|
||||
var err error
|
||||
mayChange := true
|
||||
value, err := assertion.pollActual()
|
||||
if err == nil {
|
||||
mayChange = assertion.matcherMayChange(matcher, value)
|
||||
matches, err = matcher.Match(value)
|
||||
}
|
||||
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
|
||||
fail := func(preamble string) {
|
||||
errMsg := ""
|
||||
message := ""
|
||||
if err != nil {
|
||||
errMsg = "Error: " + err.Error()
|
||||
} else {
|
||||
if desiredMatch {
|
||||
message = matcher.FailureMessage(value)
|
||||
} else {
|
||||
message = matcher.NegatedFailureMessage(value)
|
||||
}
|
||||
}
|
||||
assertion.failWrapper.TWithHelper.Helper()
|
||||
description := assertion.buildDescription(optionalDescription...)
|
||||
assertion.failWrapper.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
|
||||
}
|
||||
|
||||
if assertion.asyncType == AsyncAssertionTypeEventually {
|
||||
for {
|
||||
if err == nil && matches == desiredMatch {
|
||||
return true
|
||||
}
|
||||
|
||||
if !mayChange {
|
||||
fail("No future change is possible. Bailing out early")
|
||||
return false
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(assertion.pollingInterval):
|
||||
value, err = assertion.pollActual()
|
||||
if err == nil {
|
||||
mayChange = assertion.matcherMayChange(matcher, value)
|
||||
matches, err = matcher.Match(value)
|
||||
}
|
||||
case <-timeout:
|
||||
fail("Timed out")
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else if assertion.asyncType == AsyncAssertionTypeConsistently {
|
||||
for {
|
||||
if !(err == nil && matches == desiredMatch) {
|
||||
fail("Failed")
|
||||
return false
|
||||
}
|
||||
|
||||
if !mayChange {
|
||||
return true
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(assertion.pollingInterval):
|
||||
value, err = assertion.pollActual()
|
||||
if err == nil {
|
||||
mayChange = assertion.matcherMayChange(matcher, value)
|
||||
matches, err = matcher.Match(value)
|
||||
}
|
||||
case <-timeout:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func vetExtras(extras []interface{}) (bool, string) {
|
||||
for i, extra := range extras {
|
||||
if extra != nil {
|
||||
zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
|
||||
if !reflect.DeepEqual(zeroValue, extra) {
|
||||
message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
|
||||
return false, message
|
||||
}
|
||||
}
|
||||
}
|
||||
return true, ""
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package oraclematcher
|
||||
|
||||
import "github.com/onsi/gomega/types"
|
||||
|
||||
/*
|
||||
GomegaMatchers that also match the OracleMatcher interface can convey information about
|
||||
whether or not their result will change upon future attempts.
|
||||
|
||||
This allows `Eventually` and `Consistently` to short circuit if success becomes impossible.
|
||||
|
||||
For example, a process' exit code can never change. So, gexec's Exit matcher returns `true`
|
||||
for `MatchMayChangeInTheFuture` until the process exits, at which point it returns `false` forevermore.
|
||||
*/
|
||||
type OracleMatcher interface {
|
||||
MatchMayChangeInTheFuture(actual interface{}) bool
|
||||
}
|
||||
|
||||
func MatchMayChangeInTheFuture(matcher types.GomegaMatcher, value interface{}) bool {
|
||||
oracleMatcher, ok := matcher.(OracleMatcher)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return oracleMatcher.MatchMayChangeInTheFuture(value)
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package testingtsupport
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
var StackTracePruneRE = regexp.MustCompile(`\/gomega\/|\/ginkgo\/|\/pkg\/testing\/|\/pkg\/runtime\/`)
|
||||
|
||||
type EmptyTWithHelper struct{}
|
||||
|
||||
func (e EmptyTWithHelper) Helper() {}
|
||||
|
||||
type gomegaTestingT interface {
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
func BuildTestingTGomegaFailWrapper(t gomegaTestingT) *types.GomegaFailWrapper {
|
||||
tWithHelper, hasHelper := t.(types.TWithHelper)
|
||||
if !hasHelper {
|
||||
tWithHelper = EmptyTWithHelper{}
|
||||
}
|
||||
|
||||
fail := func(message string, callerSkip ...int) {
|
||||
if hasHelper {
|
||||
tWithHelper.Helper()
|
||||
t.Fatalf("\n%s", message)
|
||||
} else {
|
||||
skip := 2
|
||||
if len(callerSkip) > 0 {
|
||||
skip += callerSkip[0]
|
||||
}
|
||||
stackTrace := pruneStack(string(debug.Stack()), skip)
|
||||
t.Fatalf("\n%s\n%s\n", stackTrace, message)
|
||||
}
|
||||
}
|
||||
|
||||
return &types.GomegaFailWrapper{
|
||||
Fail: fail,
|
||||
TWithHelper: tWithHelper,
|
||||
}
|
||||
}
|
||||
|
||||
func pruneStack(fullStackTrace string, skip int) string {
|
||||
stack := strings.Split(fullStackTrace, "\n")[1:]
|
||||
if len(stack) > 2*skip {
|
||||
stack = stack[2*skip:]
|
||||
}
|
||||
prunedStack := []string{}
|
||||
for i := 0; i < len(stack)/2; i++ {
|
||||
if !StackTracePruneRE.Match([]byte(stack[i*2])) {
|
||||
prunedStack = append(prunedStack, stack[i*2])
|
||||
prunedStack = append(prunedStack, stack[i*2+1])
|
||||
}
|
||||
}
|
||||
return strings.Join(prunedStack, "\n")
|
||||
}
|
||||
|
|
@ -342,7 +342,6 @@ func HaveKeyWithValue(key interface{}, value interface{}) types.GomegaMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
//HaveField succeeds if actual is a struct and the value at the passed in field
|
||||
//matches the passed in matcher. By default HaveField used Equal() to perform the match,
|
||||
//however a matcher can be passed in in stead.
|
||||
|
|
@ -371,8 +370,6 @@ func HaveField(field string, expected interface{}) types.GomegaMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//BeNumerically performs numerical assertions in a type-agnostic way.
|
||||
//Actual and expected should be numbers, though the specific type of
|
||||
//number is irrelevant (float32, float64, uint8, etc...).
|
||||
|
|
@ -454,7 +451,6 @@ func BeADirectory() types.GomegaMatcher {
|
|||
//Expected must be either an int or a string.
|
||||
// Expect(resp).Should(HaveHTTPStatus(http.StatusOK)) // asserts that resp.StatusCode == 200
|
||||
// Expect(resp).Should(HaveHTTPStatus("404 Not Found")) // asserts that resp.Status == "404 Not Found"
|
||||
<<<<<<< HEAD
|
||||
// Expect(resp).Should(HaveHTTPStatus(http.StatusOK, http.StatusNoContent)) // asserts that resp.StatusCode == 200 || resp.StatusCode == 204
|
||||
func HaveHTTPStatus(expected ...interface{}) types.GomegaMatcher {
|
||||
return &matchers.HaveHTTPStatusMatcher{Expected: expected}
|
||||
|
|
@ -478,12 +474,6 @@ func HaveHTTPBody(expected interface{}) types.GomegaMatcher {
|
|||
return &matchers.HaveHTTPBodyMatcher{Expected: expected}
|
||||
}
|
||||
|
||||
=======
|
||||
func HaveHTTPStatus(expected interface{}) types.GomegaMatcher {
|
||||
return &matchers.HaveHTTPStatusMatcher{Expected: expected}
|
||||
}
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//And succeeds only if all of the given matchers succeed.
|
||||
//The matchers are tried in order, and will fail-fast if one doesn't succeed.
|
||||
// Expect("hi").To(And(HaveLen(2), Equal("hi"))
|
||||
|
|
@ -523,7 +513,6 @@ func Not(matcher types.GomegaMatcher) types.GomegaMatcher {
|
|||
}
|
||||
|
||||
//WithTransform applies the `transform` to the actual value and matches it against `matcher`.
|
||||
<<<<<<< HEAD
|
||||
//The given transform must be either a function of one parameter that returns one value or a
|
||||
// function of one parameter that returns two values, where the second value must be of the
|
||||
// error type.
|
||||
|
|
@ -533,17 +522,10 @@ func Not(matcher types.GomegaMatcher) types.GomegaMatcher {
|
|||
// var failingplus1 = func(i int) (int, error) { return 42, "this does not compute" }
|
||||
// Expect(1).To(WithTransform(failingplus1, Equal(2)))
|
||||
//
|
||||
=======
|
||||
//The given transform must be a function of one parameter that returns one value.
|
||||
// var plus1 = func(i int) int { return i + 1 }
|
||||
// Expect(1).To(WithTransform(plus1, Equal(2))
|
||||
//
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//And(), Or(), Not() and WithTransform() allow matchers to be composed into complex expressions.
|
||||
func WithTransform(transform interface{}, matcher types.GomegaMatcher) types.GomegaMatcher {
|
||||
return matchers.NewWithTransformMatcher(transform, matcher)
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
//Satisfy matches the actual value against the `predicate` function.
|
||||
//The given predicate must be a function of one paramter that returns bool.
|
||||
|
|
@ -552,5 +534,3 @@ func WithTransform(transform interface{}, matcher types.GomegaMatcher) types.Gom
|
|||
func Satisfy(predicate interface{}) types.GomegaMatcher {
|
||||
return matchers.NewSatisfyMatcher(predicate)
|
||||
}
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"github.com/onsi/gomega/internal/oraclematcher"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
|
|
@ -55,20 +51,12 @@ func (m *AndMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
|
|||
if m.firstFailedMatcher == nil {
|
||||
// so all matchers succeeded.. Any one of them changing would change the result.
|
||||
for _, matcher := range m.Matchers {
|
||||
<<<<<<< HEAD
|
||||
if types.MatchMayChangeInTheFuture(matcher, actual) {
|
||||
=======
|
||||
if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false // none of were going to change
|
||||
}
|
||||
// one of the matchers failed.. it must be able to change in order to affect the result
|
||||
<<<<<<< HEAD
|
||||
return types.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
|
||||
=======
|
||||
return oraclematcher.MatchMayChangeInTheFuture(m.firstFailedMatcher, actual)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,29 +18,9 @@ func (matcher *BeElementOfMatcher) Match(actual interface{}) (success bool, err
|
|||
return false, fmt.Errorf("BeElement matcher expects actual to be typed")
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
var lastError error
|
||||
for _, m := range flatten(matcher.Elements) {
|
||||
matcher := &EqualMatcher{Expected: m}
|
||||
=======
|
||||
length := len(matcher.Elements)
|
||||
valueAt := func(i int) interface{} {
|
||||
return matcher.Elements[i]
|
||||
}
|
||||
// Special handling of a single element of type Array or Slice
|
||||
if length == 1 && isArrayOrSlice(valueAt(0)) {
|
||||
element := valueAt(0)
|
||||
value := reflect.ValueOf(element)
|
||||
length = value.Len()
|
||||
valueAt = func(i int) interface{} {
|
||||
return value.Index(i).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
var lastError error
|
||||
for i := 0; i < length; i++ {
|
||||
matcher := &EqualMatcher{Expected: valueAt(i)}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
success, err := matcher.Match(actual)
|
||||
if err != nil {
|
||||
lastError = err
|
||||
|
|
@ -55,17 +35,9 @@ func (matcher *BeElementOfMatcher) Match(actual interface{}) (success bool, err
|
|||
}
|
||||
|
||||
func (matcher *BeElementOfMatcher) FailureMessage(actual interface{}) (message string) {
|
||||
<<<<<<< HEAD
|
||||
return format.Message(actual, "to be an element of", presentable(matcher.Elements))
|
||||
}
|
||||
|
||||
func (matcher *BeElementOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "not to be an element of", presentable(matcher.Elements))
|
||||
=======
|
||||
return format.Message(actual, "to be an element of", matcher.Elements)
|
||||
}
|
||||
|
||||
func (matcher *BeElementOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "not to be an element of", matcher.Elements)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,7 @@ func (matcher *BeNumericallyMatcher) Match(actual interface{}) (success bool, er
|
|||
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1))
|
||||
}
|
||||
if len(matcher.CompareTo) == 2 && !isNumber(matcher.CompareTo[1]) {
|
||||
<<<<<<< HEAD
|
||||
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[1], 1))
|
||||
=======
|
||||
return false, fmt.Errorf("Expected a number. Got:\n%s", format.Object(matcher.CompareTo[0], 1))
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
switch matcher.Comparator {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ func equalMatchersToElements(matchers []interface{}) (elements []interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func flatten(elems []interface{}) []interface{} {
|
||||
if len(elems) != 1 || !isArrayOrSlice(elems[0]) {
|
||||
return elems
|
||||
|
|
@ -73,19 +72,6 @@ func flatten(elems []interface{}) []interface{} {
|
|||
|
||||
func matchers(expectedElems []interface{}) (matchers []interface{}) {
|
||||
for _, e := range flatten(expectedElems) {
|
||||
=======
|
||||
func matchers(expectedElems []interface{}) (matchers []interface{}) {
|
||||
elems := expectedElems
|
||||
if len(expectedElems) == 1 && isArrayOrSlice(expectedElems[0]) {
|
||||
elems = []interface{}{}
|
||||
value := reflect.ValueOf(expectedElems[0])
|
||||
for i := 0; i < value.Len(); i++ {
|
||||
elems = append(elems, value.Index(i).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
for _, e := range elems {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
matcher, isMatcher := e.(omegaMatcher)
|
||||
if !isMatcher {
|
||||
matcher = &EqualMatcher{Expected: e}
|
||||
|
|
@ -95,7 +81,6 @@ func matchers(expectedElems []interface{}) (matchers []interface{}) {
|
|||
return
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
func presentable(elems []interface{}) interface{} {
|
||||
elems = flatten(elems)
|
||||
|
||||
|
|
@ -119,8 +104,6 @@ func presentable(elems []interface{}) interface{} {
|
|||
return ss.Interface()
|
||||
}
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func valuesOf(actual interface{}) []interface{} {
|
||||
value := reflect.ValueOf(actual)
|
||||
values := []interface{}{}
|
||||
|
|
@ -139,19 +122,11 @@ func valuesOf(actual interface{}) []interface{} {
|
|||
}
|
||||
|
||||
func (matcher *ConsistOfMatcher) FailureMessage(actual interface{}) (message string) {
|
||||
<<<<<<< HEAD
|
||||
message = format.Message(actual, "to consist of", presentable(matcher.Elements))
|
||||
message = appendMissingElements(message, matcher.missingElements)
|
||||
if len(matcher.extraElements) > 0 {
|
||||
message = fmt.Sprintf("%s\nthe extra elements were\n%s", message,
|
||||
format.Object(presentable(matcher.extraElements), 1))
|
||||
=======
|
||||
message = format.Message(actual, "to consist of", matcher.Elements)
|
||||
message = appendMissingElements(message, matcher.missingElements)
|
||||
if len(matcher.extraElements) > 0 {
|
||||
message = fmt.Sprintf("%s\nthe extra elements were\n%s", message,
|
||||
format.Object(matcher.extraElements, 1))
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -161,17 +136,9 @@ func appendMissingElements(message string, missingElements []interface{}) string
|
|||
return message
|
||||
}
|
||||
return fmt.Sprintf("%s\nthe missing elements were\n%s", message,
|
||||
<<<<<<< HEAD
|
||||
format.Object(presentable(missingElements), 1))
|
||||
}
|
||||
|
||||
func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "not to consist of", presentable(matcher.Elements))
|
||||
=======
|
||||
format.Object(missingElements, 1))
|
||||
}
|
||||
|
||||
func (matcher *ConsistOfMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "not to consist of", matcher.Elements)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,18 +35,10 @@ func (matcher *ContainElementsMatcher) Match(actual interface{}) (success bool,
|
|||
}
|
||||
|
||||
func (matcher *ContainElementsMatcher) FailureMessage(actual interface{}) (message string) {
|
||||
<<<<<<< HEAD
|
||||
message = format.Message(actual, "to contain elements", presentable(matcher.Elements))
|
||||
=======
|
||||
message = format.Message(actual, "to contain elements", matcher.Elements)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return appendMissingElements(message, matcher.missingElements)
|
||||
}
|
||||
|
||||
func (matcher *ContainElementsMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
<<<<<<< HEAD
|
||||
return format.Message(actual, "not to contain elements", presentable(matcher.Elements))
|
||||
=======
|
||||
return format.Message(actual, "not to contain elements", matcher.Elements)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,26 +2,17 @@ package matchers
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
<<<<<<< HEAD
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
=======
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
)
|
||||
|
||||
type HaveHTTPStatusMatcher struct {
|
||||
<<<<<<< HEAD
|
||||
Expected []interface{}
|
||||
=======
|
||||
Expected interface{}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, err error) {
|
||||
|
|
@ -35,7 +26,6 @@ func (matcher *HaveHTTPStatusMatcher) Match(actual interface{}) (success bool, e
|
|||
return false, fmt.Errorf("HaveHTTPStatus matcher expects *http.Response or *httptest.ResponseRecorder. Got:\n%s", format.Object(actual, 1))
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
if len(matcher.Expected) == 0 {
|
||||
return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got nothing")
|
||||
}
|
||||
|
|
@ -103,22 +93,4 @@ func formatHttpResponse(input interface{}) string {
|
|||
s.WriteString(fmt.Sprintf("%s}", format.Indent))
|
||||
|
||||
return s.String()
|
||||
=======
|
||||
switch e := matcher.Expected.(type) {
|
||||
case int:
|
||||
return resp.StatusCode == e, nil
|
||||
case string:
|
||||
return resp.Status == e, nil
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("HaveHTTPStatus matcher must be passed an int or a string. Got:\n%s", format.Object(matcher.Expected, 1))
|
||||
}
|
||||
|
||||
func (matcher *HaveHTTPStatusMatcher) FailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "to have HTTP status", matcher.Expected)
|
||||
}
|
||||
|
||||
func (matcher *HaveHTTPStatusMatcher) NegatedFailureMessage(actual interface{}) (message string) {
|
||||
return format.Message(actual, "not to have HTTP status", matcher.Expected)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,11 @@
|
|||
package matchers
|
||||
|
||||
import (
|
||||
<<<<<<< HEAD
|
||||
"errors"
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"golang.org/x/xerrors"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
)
|
||||
|
||||
type MatchErrorMatcher struct {
|
||||
|
|
@ -32,11 +25,7 @@ func (matcher *MatchErrorMatcher) Match(actual interface{}) (success bool, err e
|
|||
expected := matcher.Expected
|
||||
|
||||
if isError(expected) {
|
||||
<<<<<<< HEAD
|
||||
return reflect.DeepEqual(actualErr, expected) || errors.Is(actualErr, expected.(error)), nil
|
||||
=======
|
||||
return reflect.DeepEqual(actualErr, expected) || xerrors.Is(actualErr, expected.(error)), nil
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
if isString(expected) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
package matchers
|
||||
|
||||
import (
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"github.com/onsi/gomega/internal/oraclematcher"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
|
|
@ -29,9 +25,5 @@ func (m *NotMatcher) NegatedFailureMessage(actual interface{}) (message string)
|
|||
}
|
||||
|
||||
func (m *NotMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
|
||||
<<<<<<< HEAD
|
||||
return types.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
|
||||
=======
|
||||
return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, actual) // just return m.Matcher's value
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/onsi/gomega/format"
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"github.com/onsi/gomega/internal/oraclematcher"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
|
|
@ -57,19 +53,11 @@ func (m *OrMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
|
|||
|
||||
if m.firstSuccessfulMatcher != nil {
|
||||
// one of the matchers succeeded.. it must be able to change in order to affect the result
|
||||
<<<<<<< HEAD
|
||||
return types.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
|
||||
} else {
|
||||
// so all matchers failed.. Any one of them changing would change the result.
|
||||
for _, matcher := range m.Matchers {
|
||||
if types.MatchMayChangeInTheFuture(matcher, actual) {
|
||||
=======
|
||||
return oraclematcher.MatchMayChangeInTheFuture(m.firstSuccessfulMatcher, actual)
|
||||
} else {
|
||||
// so all matchers failed.. Any one of them changing would change the result.
|
||||
for _, matcher := range m.Matchers {
|
||||
if oraclematcher.MatchMayChangeInTheFuture(matcher, actual) {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,20 +4,12 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
"github.com/onsi/gomega/internal/oraclematcher"
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
"github.com/onsi/gomega/types"
|
||||
)
|
||||
|
||||
type WithTransformMatcher struct {
|
||||
// input
|
||||
<<<<<<< HEAD
|
||||
Transform interface{} // must be a function of one parameter that returns one value and an optional error
|
||||
=======
|
||||
Transform interface{} // must be a function of one parameter that returns one value
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
Matcher types.GomegaMatcher
|
||||
|
||||
// cached value
|
||||
|
|
@ -27,12 +19,9 @@ type WithTransformMatcher struct {
|
|||
transformedValue interface{}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
// reflect.Type for error
|
||||
var errorT = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher) *WithTransformMatcher {
|
||||
if transform == nil {
|
||||
panic("transform function cannot be nil")
|
||||
|
|
@ -41,15 +30,10 @@ func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher)
|
|||
if txType.NumIn() != 1 {
|
||||
panic("transform function must have 1 argument")
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
if numout := txType.NumOut(); numout != 1 {
|
||||
if numout != 2 || !txType.Out(1).AssignableTo(errorT) {
|
||||
panic("transform function must either have 1 return value, or 1 return value plus 1 error value")
|
||||
}
|
||||
=======
|
||||
if txType.NumOut() != 1 {
|
||||
panic("transform function must have 1 return value")
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
return &WithTransformMatcher{
|
||||
|
|
@ -60,7 +44,6 @@ func NewWithTransformMatcher(transform interface{}, matcher types.GomegaMatcher)
|
|||
}
|
||||
|
||||
func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) {
|
||||
<<<<<<< HEAD
|
||||
// prepare a parameter to pass to the Transform function
|
||||
var param reflect.Value
|
||||
if actual != nil && reflect.TypeOf(actual).AssignableTo(m.transformArgType) {
|
||||
|
|
@ -74,26 +57,16 @@ func (m *WithTransformMatcher) Match(actual interface{}) (bool, error) {
|
|||
|
||||
} else {
|
||||
return false, fmt.Errorf("Transform function expects '%s' but we have '%T'", m.transformArgType, actual)
|
||||
=======
|
||||
// return error if actual's type is incompatible with Transform function's argument type
|
||||
actualType := reflect.TypeOf(actual)
|
||||
if !actualType.AssignableTo(m.transformArgType) {
|
||||
return false, fmt.Errorf("Transform function expects '%s' but we have '%s'", m.transformArgType, actualType)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
||||
// call the Transform function with `actual`
|
||||
fn := reflect.ValueOf(m.Transform)
|
||||
<<<<<<< HEAD
|
||||
result := fn.Call([]reflect.Value{param})
|
||||
if len(result) == 2 {
|
||||
if !result[1].IsNil() {
|
||||
return false, fmt.Errorf("Transform function failed: %s", result[1].Interface().(error).Error())
|
||||
}
|
||||
}
|
||||
=======
|
||||
result := fn.Call([]reflect.Value{reflect.ValueOf(actual)})
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
m.transformedValue = result[0].Interface() // expect exactly one value
|
||||
|
||||
return m.Matcher.Match(m.transformedValue)
|
||||
|
|
@ -113,9 +86,5 @@ func (m *WithTransformMatcher) MatchMayChangeInTheFuture(_ interface{}) bool {
|
|||
// Querying the next matcher is fine if the transformer always will return the same value.
|
||||
// But if the transformer is non-deterministic and returns a different value each time, then there
|
||||
// is no point in querying the next matcher, since it can only comment on the last transformed value.
|
||||
<<<<<<< HEAD
|
||||
return types.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
|
||||
=======
|
||||
return oraclematcher.MatchMayChangeInTheFuture(m.Matcher, m.transformedValue)
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package types
|
||||
|
||||
<<<<<<< HEAD
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
|
@ -31,24 +30,6 @@ type Gomega interface {
|
|||
SetDefaultConsistentlyPollingInterval(time.Duration)
|
||||
}
|
||||
|
||||
=======
|
||||
type TWithHelper interface {
|
||||
Helper()
|
||||
}
|
||||
|
||||
type GomegaFailHandler func(message string, callerSkip ...int)
|
||||
|
||||
type GomegaFailWrapper struct {
|
||||
Fail GomegaFailHandler
|
||||
TWithHelper TWithHelper
|
||||
}
|
||||
|
||||
//A simple *testing.T interface wrapper
|
||||
type GomegaTestingT interface {
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
//All Gomega matchers must implement the GomegaMatcher interface
|
||||
//
|
||||
//For details on writing custom matchers, check out: http://onsi.github.io/gomega/#adding-your-own-matchers
|
||||
|
|
@ -57,7 +38,6 @@ type GomegaMatcher interface {
|
|||
FailureMessage(actual interface{}) (message string)
|
||||
NegatedFailureMessage(actual interface{}) (message string)
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
|
||||
/*
|
||||
GomegaMatchers that also match the OracleMatcher interface can convey information about
|
||||
|
|
@ -105,5 +85,3 @@ type Assertion interface {
|
|||
|
||||
Error() Assertion
|
||||
}
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -57,11 +57,7 @@ loop:
|
|||
err = transform.ErrShortSrc
|
||||
break loop
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
r, size = utf8.RuneError, 1
|
||||
=======
|
||||
r = utf8.RuneError
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
goto write
|
||||
}
|
||||
size = 2
|
||||
|
|
|
|||
|
|
@ -303,7 +303,6 @@ func (t Tag) Extensions() []string {
|
|||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// TypeForKey will traverse the inheritance chain to get the correct value.
|
||||
<<<<<<< HEAD
|
||||
//
|
||||
// If there are multiple types associated with a key, only the first will be
|
||||
// returned. If there is no type associated with a key, it returns the empty
|
||||
|
|
@ -315,11 +314,6 @@ func (t Tag) TypeForKey(key string) string {
|
|||
s = s[:p]
|
||||
}
|
||||
return s
|
||||
=======
|
||||
func (t Tag) TypeForKey(key string) string {
|
||||
if start, end, _ := t.findTypeForKey(key); end != start {
|
||||
return t.str[start:end]
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
|
@ -343,7 +337,6 @@ func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
|||
|
||||
// Remove the setting if value is "".
|
||||
if value == "" {
|
||||
<<<<<<< HEAD
|
||||
start, sep, end, _ := t.findTypeForKey(key)
|
||||
if start != sep {
|
||||
// Remove a possible empty extension.
|
||||
|
|
@ -351,15 +344,6 @@ func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
|||
case t.str[start-2] != '-': // has previous elements.
|
||||
case end == len(t.str), // end of string
|
||||
end+2 < len(t.str) && t.str[end+2] == '-': // end of extension
|
||||
=======
|
||||
start, end, _ := t.findTypeForKey(key)
|
||||
if start != end {
|
||||
// Remove key tag and leading '-'.
|
||||
start -= 4
|
||||
|
||||
// Remove a possible empty extension.
|
||||
if (end == len(t.str) || t.str[end+2] == '-') && t.str[start-2] == '-' {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
start -= 2
|
||||
}
|
||||
if start == int(t.pVariant) && end == len(t.str) {
|
||||
|
|
@ -405,7 +389,6 @@ func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
|||
t.str = string(buf[:uStart+len(b)])
|
||||
} else {
|
||||
s := t.str
|
||||
<<<<<<< HEAD
|
||||
start, sep, end, hasExt := t.findTypeForKey(key)
|
||||
if start == sep {
|
||||
if hasExt {
|
||||
|
|
@ -414,16 +397,6 @@ func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
|||
t.str = fmt.Sprintf("%s-%s%s", s[:sep], b, s[end:])
|
||||
} else {
|
||||
t.str = fmt.Sprintf("%s-%s%s", s[:start+3], value, s[end:])
|
||||
=======
|
||||
start, end, hasExt := t.findTypeForKey(key)
|
||||
if start == end {
|
||||
if hasExt {
|
||||
b = b[2:]
|
||||
}
|
||||
t.str = fmt.Sprintf("%s-%s%s", s[:start], b, s[end:])
|
||||
} else {
|
||||
t.str = fmt.Sprintf("%s%s%s", s[:start], value, s[end:])
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
return t, nil
|
||||
|
|
@ -434,17 +407,10 @@ func (t Tag) SetTypeForKey(key, value string) (Tag, error) {
|
|||
// wasn't found. The hasExt return value reports whether an -u extension was present.
|
||||
// Note: the extensions are typically very small and are likely to contain
|
||||
// only one key-type pair.
|
||||
<<<<<<< HEAD
|
||||
func (t Tag) findTypeForKey(key string) (start, sep, end int, hasExt bool) {
|
||||
p := int(t.pExt)
|
||||
if len(key) != 2 || p == len(t.str) || p == 0 {
|
||||
return p, p, p, false
|
||||
=======
|
||||
func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
|
||||
p := int(t.pExt)
|
||||
if len(key) != 2 || p == len(t.str) || p == 0 {
|
||||
return p, p, false
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
s := t.str
|
||||
|
||||
|
|
@ -452,17 +418,10 @@ func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
|
|||
for p++; s[p] != 'u'; p++ {
|
||||
if s[p] > 'u' {
|
||||
p--
|
||||
<<<<<<< HEAD
|
||||
return p, p, p, false
|
||||
}
|
||||
if p = nextExtension(s, p); p == len(s) {
|
||||
return len(s), len(s), len(s), false
|
||||
=======
|
||||
return p, p, false
|
||||
}
|
||||
if p = nextExtension(s, p); p == len(s) {
|
||||
return len(s), len(s), false
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
// Proceed to the hyphen following the extension name.
|
||||
|
|
@ -473,7 +432,6 @@ func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
|
|||
|
||||
// Iterate over keys until we get the end of a section.
|
||||
for {
|
||||
<<<<<<< HEAD
|
||||
end = p
|
||||
for p++; p < len(s) && s[p] != '-'; p++ {
|
||||
}
|
||||
|
|
@ -496,42 +454,6 @@ func (t Tag) findTypeForKey(key string) (start, end int, hasExt bool) {
|
|||
}
|
||||
start = end
|
||||
sep = p
|
||||
=======
|
||||
// p points to the hyphen preceding the current token.
|
||||
if p3 := p + 3; s[p3] == '-' {
|
||||
// Found a key.
|
||||
// Check whether we just processed the key that was requested.
|
||||
if curKey == key {
|
||||
return start, p, true
|
||||
}
|
||||
// Set to the next key and continue scanning type tokens.
|
||||
curKey = s[p+1 : p3]
|
||||
if curKey > key {
|
||||
return p, p, true
|
||||
}
|
||||
// Start of the type token sequence.
|
||||
start = p + 4
|
||||
// A type is at least 3 characters long.
|
||||
p += 7 // 4 + 3
|
||||
} else {
|
||||
// Attribute or type, which is at least 3 characters long.
|
||||
p += 4
|
||||
}
|
||||
// p points past the third character of a type or attribute.
|
||||
max := p + 5 // maximum length of token plus hyphen.
|
||||
if len(s) < max {
|
||||
max = len(s)
|
||||
}
|
||||
for ; p < max && s[p] != '-'; p++ {
|
||||
}
|
||||
// Bail if we have exhausted all tokens or if the next token starts
|
||||
// a new extension.
|
||||
if p == len(s) || s[p+2] == '-' {
|
||||
if curKey == key {
|
||||
return start, p, true
|
||||
}
|
||||
return p, p, true
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ func (s *scanner) resizeRange(oldStart, oldEnd, newSize int) {
|
|||
s.start = oldStart
|
||||
if end := oldStart + newSize; end != oldEnd {
|
||||
diff := end - oldEnd
|
||||
<<<<<<< HEAD
|
||||
var b []byte
|
||||
if n := len(s.b) + diff; n > cap(s.b) {
|
||||
b = make([]byte, n)
|
||||
|
|
@ -143,16 +142,6 @@ func (s *scanner) resizeRange(oldStart, oldEnd, newSize int) {
|
|||
}
|
||||
copy(b[end:], s.b[oldEnd:])
|
||||
s.b = b
|
||||
=======
|
||||
if end < cap(s.b) {
|
||||
b := make([]byte, len(s.b)+diff)
|
||||
copy(b, s.b[:oldStart])
|
||||
copy(b[end:], s.b[oldEnd:])
|
||||
s.b = b
|
||||
} else {
|
||||
s.b = append(s.b[end:], s.b[oldEnd:]...)
|
||||
}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
s.next = end + (s.next - s.end)
|
||||
s.end = end
|
||||
}
|
||||
|
|
@ -494,11 +483,7 @@ func parseExtensions(scan *scanner) int {
|
|||
func parseExtension(scan *scanner) int {
|
||||
start, end := scan.start, scan.end
|
||||
switch scan.token[0] {
|
||||
<<<<<<< HEAD
|
||||
case 'u': // https://www.ietf.org/rfc/rfc6067.txt
|
||||
=======
|
||||
case 'u':
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
attrStart := end
|
||||
scan.scan()
|
||||
for last := []byte{}; len(scan.token) > 2; scan.scan() {
|
||||
|
|
@ -518,7 +503,6 @@ func parseExtension(scan *scanner) int {
|
|||
last = scan.token
|
||||
end = scan.end
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
// Scan key-type sequences. A key is of length 2 and may be followed
|
||||
// by 0 or more "type" subtags from 3 to the maximum of 8 letters.
|
||||
var last, key []byte
|
||||
|
|
@ -530,38 +514,18 @@ func parseExtension(scan *scanner) int {
|
|||
}
|
||||
// TODO: check key value validity
|
||||
if bytes.Compare(key, last) != 1 || scan.err != nil {
|
||||
=======
|
||||
var last, key []byte
|
||||
for attrEnd := end; len(scan.token) == 2; last = key {
|
||||
key = scan.token
|
||||
keyEnd := scan.end
|
||||
end = scan.acceptMinSize(3)
|
||||
// TODO: check key value validity
|
||||
if keyEnd == end || bytes.Compare(key, last) != 1 {
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// We have an invalid key or the keys are not sorted.
|
||||
// Start scanning keys from scratch and reorder.
|
||||
p := attrEnd + 1
|
||||
scan.next = p
|
||||
keys := [][]byte{}
|
||||
for scan.scan(); len(scan.token) == 2; {
|
||||
<<<<<<< HEAD
|
||||
keyStart := scan.start
|
||||
end = scan.end
|
||||
for scan.scan(); end < scan.end && len(scan.token) > 2; scan.scan() {
|
||||
end = scan.end
|
||||
}
|
||||
keys = append(keys, scan.b[keyStart:end])
|
||||
=======
|
||||
keyStart, keyEnd := scan.start, scan.end
|
||||
end = scan.acceptMinSize(3)
|
||||
if keyEnd != end {
|
||||
keys = append(keys, scan.b[keyStart:end])
|
||||
} else {
|
||||
scan.setError(ErrSyntax)
|
||||
end = keyStart
|
||||
}
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
}
|
||||
sort.Stable(bytesSort{keys, 2})
|
||||
if n := len(keys); n > 0 {
|
||||
|
|
@ -585,11 +549,7 @@ func parseExtension(scan *scanner) int {
|
|||
break
|
||||
}
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
case 't': // https://www.ietf.org/rfc/rfc6497.txt
|
||||
=======
|
||||
case 't':
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
scan.scan()
|
||||
if n := len(scan.token); n >= 2 && n <= 3 && isAlpha(scan.token[1]) {
|
||||
_, end = parseTag(scan)
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
<<<<<<< HEAD
|
||||
//go:build !go1.2
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// +build !go1.2
|
||||
|
||||
package language
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
<<<<<<< HEAD
|
||||
//go:build go1.2
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// +build go1.2
|
||||
|
||||
package language
|
||||
|
|
|
|||
|
|
@ -412,13 +412,10 @@ func (t Tag) Extensions() []Extension {
|
|||
// are of the allowed values defined for the Unicode locale extension ('u') in
|
||||
// https://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers.
|
||||
// TypeForKey will traverse the inheritance chain to get the correct value.
|
||||
<<<<<<< HEAD
|
||||
//
|
||||
// If there are multiple types associated with a key, only the first will be
|
||||
// returned. If there is no type associated with a key, it returns the empty
|
||||
// string.
|
||||
=======
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
func (t Tag) TypeForKey(key string) string {
|
||||
if !compact.Tag(t).MayHaveExtensions() {
|
||||
if key != "rg" && key != "va" {
|
||||
|
|
|
|||
|
|
@ -47,11 +47,7 @@ const (
|
|||
_Zzzz = 251
|
||||
)
|
||||
|
||||
<<<<<<< HEAD
|
||||
var regionToGroups = []uint8{ // 358 elements
|
||||
=======
|
||||
var regionToGroups = []uint8{ // 357 elements
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
// Entry 0 - 3F
|
||||
0x00, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x00,
|
||||
|
|
@ -102,13 +98,8 @@ var regionToGroups = []uint8{ // 357 elements
|
|||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
<<<<<<< HEAD
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
} // Size: 382 bytes
|
||||
=======
|
||||
0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
} // Size: 381 bytes
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
||||
var paradigmLocales = [][3]uint16{ // 3 elements
|
||||
0: [3]uint16{0x139, 0x0, 0x7b},
|
||||
|
|
@ -304,8 +295,4 @@ var matchRegion = []regionIntelligibility{ // 15 elements
|
|||
14: {lang: 0x529, script: 0x3c, group: 0x80, distance: 0x5},
|
||||
} // Size: 114 bytes
|
||||
|
||||
<<<<<<< HEAD
|
||||
// Total table size 1472 bytes (1KiB); checksum: F86C669
|
||||
=======
|
||||
// Total table size 1471 bytes (1KiB); checksum: 4CB1CD46
|
||||
>>>>>>> 33cbc1d (add batchrelease controller)
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
This repository holds the transition packages for the new Go 1.13 error values.
|
||||
See golang.org/design/29934-error-values.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue