@@ -4,15 +4,20 @@ import (
44 "context"
55 "path"
66 "testing"
7+ "time"
78
89 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
910 "github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
11+ "github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
1012 tektontest "github.com/openshift-pipelines/pipelines-as-code/pkg/test/tekton"
1113 pipelinev1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
14+ tektonv1lister "github.com/tektoncd/pipeline/pkg/client/listers/pipeline/v1"
1215 "go.uber.org/zap"
1316 zapobserver "go.uber.org/zap/zaptest/observer"
1417 "gotest.tools/v3/assert"
1518 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
19+ "k8s.io/client-go/tools/cache"
20+ duckv1 "knative.dev/pkg/apis/duck/v1"
1621 "knative.dev/pkg/controller"
1722)
1823
@@ -81,3 +86,125 @@ func TestCtrlOpts(t *testing.T) {
8186 // Assert that the promote filter function returns true.
8287 assert .Assert (t , promote )
8388}
89+
90+ func TestSelectLeaseQueueRecoveryKeys (t * testing.T ) {
91+ now := time .Unix (1_700_001_000 , 0 )
92+
93+ tests := []struct {
94+ name string
95+ pipelineRuns []* pipelinev1.PipelineRun
96+ want []string
97+ }{
98+ {
99+ name : "selects one oldest queued pending run per repository" ,
100+ pipelineRuns : []* pipelinev1.PipelineRun {
101+ newLeaseRecoveryPR ("later" , "test-ns" , "repo-a" , now .Add (2 * time .Second ), map [string ]string {}, false ),
102+ newLeaseRecoveryPR ("earlier" , "test-ns" , "repo-a" , now .Add (time .Second ), map [string ]string {}, false ),
103+ newLeaseRecoveryPR ("blocked" , "test-ns" , "repo-b" , now , map [string ]string {
104+ keys .QueuePromotionBlocked : "true" ,
105+ }, false ),
106+ newLeaseRecoveryPR ("valid" , "test-ns" , "repo-b" , now .Add (3 * time .Second ), map [string ]string {}, false ),
107+ newLeaseRecoveryPR ("other-namespace" , "other-ns" , "repo-a" , now .Add (4 * time .Second ), map [string ]string {}, false ),
108+ newLeaseRecoveryPR ("missing-repo" , "test-ns" , "" , now .Add (5 * time .Second ), map [string ]string {}, false ),
109+ newLeaseRecoveryStartedPR ("started" , "test-ns" , "repo-c" , now .Add (6 * time .Second )),
110+ newLeaseRecoveryPR ("done" , "test-ns" , "repo-d" , now .Add (7 * time .Second ), map [string ]string {}, true ),
111+ },
112+ want : []string {
113+ "test-ns/earlier" ,
114+ "test-ns/valid" ,
115+ "other-ns/other-namespace" ,
116+ },
117+ },
118+ }
119+
120+ for _ , tt := range tests {
121+ t .Run (tt .name , func (t * testing.T ) {
122+ keys := selectLeaseQueueRecoveryKeys (tt .pipelineRuns )
123+ got := make ([]string , 0 , len (keys ))
124+ for _ , key := range keys {
125+ got = append (got , key .String ())
126+ }
127+ assert .DeepEqual (t , got , tt .want )
128+ })
129+ }
130+ }
131+
132+ func TestRunLeaseQueueRecovery (t * testing.T ) {
133+ observer , catcher := zapobserver .New (zap .DebugLevel )
134+ logger := zap .New (observer ).Sugar ()
135+ wh := & fakeReconciler {}
136+ impl := controller .NewContext (context .TODO (), wh , controller.ControllerOptions {
137+ WorkQueueName : "LeaseRecovery" ,
138+ Logger : logger .Named ("LeaseRecovery" ),
139+ })
140+
141+ now := time .Unix (1_700_001_100 , 0 )
142+ indexer := cache .NewIndexer (cache .MetaNamespaceKeyFunc , cache.Indexers {})
143+ for _ , pipelineRun := range []* pipelinev1.PipelineRun {
144+ newLeaseRecoveryPR ("first" , "test-ns" , "repo-a" , now , map [string ]string {}, false ),
145+ newLeaseRecoveryPR ("second" , "test-ns" , "repo-a" , now .Add (time .Second ), map [string ]string {}, false ),
146+ newLeaseRecoveryPR ("third" , "test-ns" , "repo-b" , now .Add (2 * time .Second ), map [string ]string {}, false ),
147+ newLeaseRecoveryStartedPR ("started" , "test-ns" , "repo-c" , now .Add (3 * time .Second )),
148+ } {
149+ assert .NilError (t , indexer .Add (pipelineRun ))
150+ }
151+
152+ runLeaseQueueRecovery (logger , impl , tektonv1lister .NewPipelineRunLister (indexer ))
153+
154+ assert .Equal (t , catcher .FilterMessageSnippet ("Adding to queue test-ns/first" ).Len (), 1 )
155+ assert .Equal (t , catcher .FilterMessageSnippet ("Adding to queue test-ns/third" ).Len (), 1 )
156+ assert .Equal (t , catcher .FilterMessageSnippet ("Adding to queue" ).Len (), 2 )
157+ }
158+
159+ func newLeaseRecoveryPR (
160+ name , namespace , repo string ,
161+ createdAt time.Time ,
162+ extraAnnotations map [string ]string ,
163+ done bool ,
164+ ) * pipelinev1.PipelineRun {
165+ annotations := map [string ]string {
166+ keys .State : kubeinteraction .StateQueued ,
167+ }
168+ if repo != "" {
169+ annotations [keys .Repository ] = repo
170+ }
171+ for key , value := range extraAnnotations {
172+ annotations [key ] = value
173+ }
174+
175+ pipelineRun := & pipelinev1.PipelineRun {
176+ ObjectMeta : metav1.ObjectMeta {
177+ Name : name ,
178+ Namespace : namespace ,
179+ CreationTimestamp : metav1.Time {Time : createdAt },
180+ Annotations : annotations ,
181+ },
182+ Spec : pipelinev1.PipelineRunSpec {
183+ Status : pipelinev1 .PipelineRunSpecStatusPending ,
184+ },
185+ }
186+ if done {
187+ pipelineRun .Status .Conditions = duckv1.Conditions {{
188+ Type : "Succeeded" ,
189+ Status : "True" ,
190+ }}
191+ }
192+ return pipelineRun
193+ }
194+
195+ func newLeaseRecoveryStartedPR (name , namespace , repo string , createdAt time.Time ) * pipelinev1.PipelineRun {
196+ return & pipelinev1.PipelineRun {
197+ ObjectMeta : metav1.ObjectMeta {
198+ Name : name ,
199+ Namespace : namespace ,
200+ CreationTimestamp : metav1.Time {Time : createdAt },
201+ Annotations : map [string ]string {
202+ keys .Repository : repo ,
203+ keys .State : kubeinteraction .StateStarted ,
204+ },
205+ },
206+ Spec : pipelinev1.PipelineRunSpec {
207+ Status : pipelinev1 .PipelineRunSpecStatusPending ,
208+ },
209+ }
210+ }
0 commit comments