github.com/chenbh/concourse/v6@v6.4.2/atc/lidar/scanner_test.go (about)

     1  package lidar_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/lager/lagertest"
     9  	"github.com/chenbh/concourse/v6/atc"
    10  	"github.com/chenbh/concourse/v6/atc/creds/credsfakes"
    11  	"github.com/chenbh/concourse/v6/atc/db"
    12  	"github.com/chenbh/concourse/v6/atc/db/dbfakes"
    13  	"github.com/chenbh/concourse/v6/atc/db/lock/lockfakes"
    14  	"github.com/chenbh/concourse/v6/atc/lidar"
    15  	. "github.com/onsi/ginkgo"
    16  	. "github.com/onsi/gomega"
    17  )
    18  
    19  type Scanner interface {
    20  	Run(ctx context.Context) error
    21  }
    22  
    23  var _ = Describe("Scanner", func() {
    24  	var (
    25  		err error
    26  
    27  		fakeCheckFactory *dbfakes.FakeCheckFactory
    28  		fakeSecrets      *credsfakes.FakeSecrets
    29  
    30  		logger  *lagertest.TestLogger
    31  		scanner Scanner
    32  	)
    33  
    34  	BeforeEach(func() {
    35  		fakeCheckFactory = new(dbfakes.FakeCheckFactory)
    36  		fakeSecrets = new(credsfakes.FakeSecrets)
    37  
    38  		logger = lagertest.NewTestLogger("test")
    39  		scanner = lidar.NewScanner(
    40  			logger,
    41  			fakeCheckFactory,
    42  			fakeSecrets,
    43  			time.Minute*1,
    44  			time.Minute*1,
    45  			time.Minute*10,
    46  		)
    47  	})
    48  
    49  	JustBeforeEach(func() {
    50  		err = scanner.Run(context.TODO())
    51  	})
    52  
    53  	Describe("Run", func() {
    54  		var fakeLock *lockfakes.FakeLock
    55  
    56  		BeforeEach(func() {
    57  			fakeLock = new(lockfakes.FakeLock)
    58  			fakeCheckFactory.AcquireScanningLockReturns(fakeLock, true, nil)
    59  		})
    60  
    61  		Context("when fetching resources fails", func() {
    62  			BeforeEach(func() {
    63  				fakeCheckFactory.ResourcesReturns(nil, errors.New("nope"))
    64  			})
    65  
    66  			It("errors", func() {
    67  				Expect(err).To(HaveOccurred())
    68  			})
    69  		})
    70  
    71  		Context("when fetching resources succeeds", func() {
    72  			var fakeResource *dbfakes.FakeResource
    73  
    74  			BeforeEach(func() {
    75  				fakeResource = new(dbfakes.FakeResource)
    76  				fakeResource.NameReturns("some-name")
    77  				fakeResource.TagsReturns([]string{"tag-a", "tag-b"})
    78  				fakeResource.SourceReturns(atc.Source{"some": "source"})
    79  
    80  				fakeCheckFactory.ResourcesReturns([]db.Resource{fakeResource}, nil)
    81  			})
    82  
    83  			Context("when fetching resource types fails", func() {
    84  				BeforeEach(func() {
    85  					fakeCheckFactory.ResourceTypesReturns(nil, errors.New("nope"))
    86  				})
    87  
    88  				It("errors", func() {
    89  					Expect(err).To(HaveOccurred())
    90  				})
    91  			})
    92  
    93  			Context("when fetching resources types succeeds", func() {
    94  				var fakeResourceType *dbfakes.FakeResourceType
    95  
    96  				BeforeEach(func() {
    97  					fakeResourceType = new(dbfakes.FakeResourceType)
    98  					fakeResourceType.NameReturns("some-type")
    99  					fakeResourceType.TypeReturns("some-base-type")
   100  					fakeResourceType.TagsReturns([]string{"some-tag"})
   101  					fakeResourceType.SourceReturns(atc.Source{"some": "type-source"})
   102  
   103  					fakeCheckFactory.ResourceTypesReturns([]db.ResourceType{fakeResourceType}, nil)
   104  				})
   105  
   106  				Context("when the resource parent type is a base type", func() {
   107  					BeforeEach(func() {
   108  						fakeResource.TypeReturns("base-type")
   109  					})
   110  
   111  					Context("when the check interval is parseable", func() {
   112  						BeforeEach(func() {
   113  							fakeResource.CheckEveryReturns("10s")
   114  						})
   115  
   116  						Context("when the last check end time is within our interval", func() {
   117  							BeforeEach(func() {
   118  								fakeResource.LastCheckEndTimeReturns(time.Now())
   119  							})
   120  
   121  							It("does not check", func() {
   122  								Expect(fakeCheckFactory.CreateCheckCallCount()).To(Equal(0))
   123  							})
   124  
   125  							It("clears the check error", func() {
   126  								Expect(fakeResource.SetCheckSetupErrorCallCount()).To(Equal(1))
   127  								Expect(fakeResource.SetCheckSetupErrorArgsForCall(0)).To(BeNil())
   128  							})
   129  						})
   130  
   131  						Context("when the last check end time is past our interval", func() {
   132  							BeforeEach(func() {
   133  								fakeResource.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   134  							})
   135  
   136  							It("creates a check", func() {
   137  								Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   138  							})
   139  
   140  							It("clears the check error", func() {
   141  								Expect(fakeResource.SetCheckSetupErrorCallCount()).To(Equal(1))
   142  								Expect(fakeResource.SetCheckSetupErrorArgsForCall(0)).To(BeNil())
   143  							})
   144  
   145  							It("sends a notification for the checker to run", func() {
   146  								Expect(fakeCheckFactory.NotifyCheckerCallCount()).To(Equal(1))
   147  							})
   148  
   149  							Context("when try creating a check panic", func() {
   150  								BeforeEach(func() {
   151  									fakeCheckFactory.TryCreateCheckStub = func(context.Context, db.Checkable, db.ResourceTypes, atc.Version, bool) (db.Check, bool, error) {
   152  										panic("something went wrong")
   153  									}
   154  								})
   155  
   156  								It("recover from the panic", func() {
   157  									Expect(err).ToNot(HaveOccurred())
   158  									Eventually(fakeResource.SetCheckSetupErrorCallCount).Should(Equal(1))
   159  									Eventually(fakeResource.SetCheckSetupErrorArgsForCall(0).Error).Should(ContainSubstring("something went wrong"))
   160  								})
   161  							})
   162  						})
   163  
   164  						Context("when the checkable has a pinned version", func() {
   165  							BeforeEach(func() {
   166  								fakeResource.CurrentPinnedVersionReturns(atc.Version{"some": "version"})
   167  							})
   168  
   169  							It("creates a check", func() {
   170  								Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   171  								_, _, _, fromVersion, _ := fakeCheckFactory.TryCreateCheckArgsForCall(0)
   172  								Expect(fromVersion).To(Equal(atc.Version{"some": "version"}))
   173  							})
   174  
   175  							It("clears the check error", func() {
   176  								Expect(fakeResource.SetCheckSetupErrorCallCount()).To(Equal(1))
   177  								Expect(fakeResource.SetCheckSetupErrorArgsForCall(0)).To(BeNil())
   178  							})
   179  
   180  							It("sends a notification for the checker to run", func() {
   181  								Expect(fakeCheckFactory.NotifyCheckerCallCount()).To(Equal(1))
   182  							})
   183  						})
   184  
   185  						Context("when the checkable does not have a pinned version", func() {
   186  							BeforeEach(func() {
   187  								fakeResource.CurrentPinnedVersionReturns(nil)
   188  							})
   189  
   190  							It("creates a check", func() {
   191  								Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   192  								_, _, _, fromVersion, _ := fakeCheckFactory.TryCreateCheckArgsForCall(0)
   193  								Expect(fromVersion).To(BeNil())
   194  							})
   195  
   196  							It("clears the check error", func() {
   197  								Expect(fakeResource.SetCheckSetupErrorCallCount()).To(Equal(1))
   198  								Expect(fakeResource.SetCheckSetupErrorArgsForCall(0)).To(BeNil())
   199  							})
   200  
   201  							It("sends a notification for the checker to run", func() {
   202  								Expect(fakeCheckFactory.NotifyCheckerCallCount()).To(Equal(1))
   203  							})
   204  						})
   205  					})
   206  				})
   207  
   208  				Context("when the resource has a parent type", func() {
   209  					BeforeEach(func() {
   210  						fakeResource.TypeReturns("custom-type")
   211  						fakeResource.PipelineIDReturns(1)
   212  						fakeResourceType.NameReturns("custom-type")
   213  						fakeResourceType.PipelineIDReturns(1)
   214  					})
   215  
   216  					Context("when it fails to create a check for parent resource", func() {
   217  						BeforeEach(func() {
   218  							fakeResourceType.CheckEveryReturns("not-a-duration")
   219  						})
   220  
   221  						It("sets the check error", func() {
   222  							Expect(fakeResourceType.SetCheckSetupErrorCallCount()).To(Equal(1))
   223  							Expect(fakeResource.SetCheckSetupErrorCallCount()).To(Equal(1))
   224  							err := fakeResource.SetCheckSetupErrorArgsForCall(0)
   225  							Expect(err.Error()).To(ContainSubstring("parent type 'custom-type' error:"))
   226  						})
   227  
   228  						It("does not create a check", func() {
   229  							Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(0))
   230  						})
   231  					})
   232  
   233  					Context("when the parent type requires a check", func() {
   234  						BeforeEach(func() {
   235  							fakeResourceType.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   236  							fakeResource.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   237  						})
   238  
   239  						Context("when the parent type has a version", func() {
   240  							BeforeEach(func() {
   241  								fakeResourceType.VersionReturns(atc.Version{"some": "version"})
   242  							})
   243  
   244  							It("creates a check for both the parent and the resource", func() {
   245  								Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(2))
   246  
   247  								_, checkable, _, _, manuallyTriggered := fakeCheckFactory.TryCreateCheckArgsForCall(0)
   248  								Expect(checkable).To(Equal(fakeResourceType))
   249  								Expect(manuallyTriggered).To(BeFalse())
   250  
   251  								_, checkable, _, _, manuallyTriggered = fakeCheckFactory.TryCreateCheckArgsForCall(1)
   252  								Expect(checkable).To(Equal(fakeResource))
   253  								Expect(manuallyTriggered).To(BeFalse())
   254  							})
   255  
   256  							It("sends a notification for the checker to run", func() {
   257  								Expect(fakeCheckFactory.NotifyCheckerCallCount()).To(Equal(1))
   258  							})
   259  						})
   260  					})
   261  				})
   262  			})
   263  		})
   264  
   265  		Context("when there are multiple resources that use the same resource type", func() {
   266  			var fakeResource1, fakeResource2 *dbfakes.FakeResource
   267  			var fakeResourceType *dbfakes.FakeResourceType
   268  
   269  			BeforeEach(func() {
   270  				fakeResource1 = new(dbfakes.FakeResource)
   271  				fakeResource1.NameReturns("some-name")
   272  				fakeResource1.SourceReturns(atc.Source{"some": "source"})
   273  				fakeResource1.TypeReturns("custom-type")
   274  				fakeResource1.PipelineIDReturns(1)
   275  				fakeResource1.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   276  
   277  				fakeResource2 = new(dbfakes.FakeResource)
   278  				fakeResource2.NameReturns("some-name")
   279  				fakeResource2.SourceReturns(atc.Source{"some": "source"})
   280  				fakeResource2.TypeReturns("custom-type")
   281  				fakeResource2.PipelineIDReturns(1)
   282  				fakeResource2.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   283  
   284  				fakeCheckFactory.ResourcesReturns([]db.Resource{fakeResource1, fakeResource2}, nil)
   285  
   286  				fakeResourceType = new(dbfakes.FakeResourceType)
   287  				fakeResourceType.NameReturns("custom-type")
   288  				fakeResourceType.PipelineIDReturns(1)
   289  				fakeResourceType.TypeReturns("some-base-type")
   290  				fakeResourceType.SourceReturns(atc.Source{"some": "type-source"})
   291  				fakeResourceType.LastCheckEndTimeReturns(time.Now().Add(-time.Hour))
   292  
   293  				fakeCheckFactory.ResourceTypesReturns([]db.ResourceType{fakeResourceType}, nil)
   294  			})
   295  
   296  			It("only tries to create a check for the resource type once", func() {
   297  				Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(3))
   298  
   299  				var checked []string
   300  				_, checkable, _, _, _ := fakeCheckFactory.TryCreateCheckArgsForCall(0)
   301  				checked = append(checked, checkable.Name())
   302  
   303  				_, checkable, _, _, _ = fakeCheckFactory.TryCreateCheckArgsForCall(1)
   304  				checked = append(checked, checkable.Name())
   305  
   306  				_, checkable, _, _, _ = fakeCheckFactory.TryCreateCheckArgsForCall(2)
   307  				checked = append(checked, checkable.Name())
   308  
   309  				Expect(checked).To(ConsistOf([]string{fakeResourceType.Name(), fakeResource1.Name(), fakeResource2.Name()}))
   310  			})
   311  		})
   312  
   313  		Context("Default with webhook check interval", func() {
   314  			var fakeResource *dbfakes.FakeResource
   315  			BeforeEach(func() {
   316  				fakeResource = new(dbfakes.FakeResource)
   317  				fakeResource.NameReturns("some-name")
   318  				fakeResource.TagsReturns([]string{"tag-a", "tag-b"})
   319  				fakeResource.SourceReturns(atc.Source{"some": "source"})
   320  				fakeResource.TypeReturns("base-type")
   321  				fakeResource.CheckEveryReturns("")
   322  				fakeCheckFactory.ResourcesReturns([]db.Resource{fakeResource}, nil)
   323  
   324  			})
   325  
   326  			Context("resource has webhook", func() {
   327  				BeforeEach(func() {
   328  					fakeResource.HasWebhookReturns(true)
   329  				})
   330  
   331  				Context("last check is 9 minutes ago", func() {
   332  					BeforeEach(func() {
   333  						fakeResource.LastCheckEndTimeReturns(time.Now().Add(-time.Minute * 9))
   334  					})
   335  
   336  					It("does not create a check", func() {
   337  						Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(0))
   338  					})
   339  				})
   340  
   341  				Context("last check is 11 minutes ago", func() {
   342  					BeforeEach(func() {
   343  						fakeResource.LastCheckEndTimeReturns(time.Now().Add(-time.Minute * 11))
   344  					})
   345  
   346  					It("does not create a check", func() {
   347  						Expect(fakeCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   348  					})
   349  				})
   350  			})
   351  		})
   352  	})
   353  })