github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/resource_config_factory_test.go (about)

     1  package db_test
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/pf-qiu/concourse/v6/atc"
     8  	"github.com/pf-qiu/concourse/v6/atc/db"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  )
    12  
    13  var _ = Describe("ResourceConfigFactory", func() {
    14  	var build db.Build
    15  
    16  	BeforeEach(func() {
    17  		var err error
    18  		job, found, err := defaultPipeline.Job("some-job")
    19  		Expect(err).NotTo(HaveOccurred())
    20  		Expect(found).To(BeTrue())
    21  
    22  		build, err = job.CreateBuild()
    23  		Expect(err).NotTo(HaveOccurred())
    24  	})
    25  
    26  	Context("when a config is created", func() {
    27  		var resourceConfig db.ResourceConfig
    28  
    29  		BeforeEach(func() {
    30  			var err error
    31  			resourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig(
    32  				"some-base-resource-type",
    33  				atc.Source{"some": "unique-source"},
    34  				atc.VersionedResourceTypes{},
    35  			)
    36  			Expect(err).ToNot(HaveOccurred())
    37  		})
    38  
    39  		It("has a recent 'last referenced' value", func() {
    40  			Expect(resourceConfig.LastReferenced()).To(BeTemporally("~", time.Now(), time.Minute))
    41  		})
    42  
    43  		Context("and created again", func() {
    44  			var sameConfig db.ResourceConfig
    45  
    46  			BeforeEach(func() {
    47  				var err error
    48  				sameConfig, err = resourceConfigFactory.FindOrCreateResourceConfig(
    49  					"some-base-resource-type",
    50  					atc.Source{"some": "unique-source"},
    51  					atc.VersionedResourceTypes{},
    52  				)
    53  				Expect(err).ToNot(HaveOccurred())
    54  			})
    55  
    56  			It("returns the same config, but with a newer 'last referenced' time", func() {
    57  				Expect(sameConfig.ID()).To(Equal(resourceConfig.ID()))
    58  				Expect(sameConfig.LastReferenced()).To(BeTemporally(">", resourceConfig.LastReferenced()))
    59  			})
    60  		})
    61  
    62  		Context("when cleaning up with no grace period", func() {
    63  			It("removes the config immediately", func() {
    64  				Expect(resourceConfigFactory.CleanUnreferencedConfigs(0)).To(Succeed())
    65  
    66  				recreated, err := resourceConfigFactory.FindOrCreateResourceConfig(
    67  					"some-base-resource-type",
    68  					atc.Source{"some": "unique-source"},
    69  					atc.VersionedResourceTypes{},
    70  				)
    71  				Expect(err).ToNot(HaveOccurred())
    72  				Expect(recreated.ID()).ToNot(Equal(resourceConfig.ID()))
    73  			})
    74  		})
    75  
    76  		Context("when cleaning up with a grace period", func() {
    77  			It("spares the config", func() {
    78  				Expect(resourceConfigFactory.CleanUnreferencedConfigs(time.Hour)).To(Succeed())
    79  
    80  				recreated, err := resourceConfigFactory.FindOrCreateResourceConfig(
    81  					"some-base-resource-type",
    82  					atc.Source{"some": "unique-source"},
    83  					atc.VersionedResourceTypes{},
    84  				)
    85  				Expect(err).ToNot(HaveOccurred())
    86  				Expect(recreated.ID()).To(Equal(resourceConfig.ID()))
    87  			})
    88  		})
    89  	})
    90  
    91  	Context("when the resource config is concurrently created", func() {
    92  		BeforeEach(func() {
    93  			Expect(build.Finish(db.BuildStatusSucceeded)).To(Succeed())
    94  			Expect(build.SetInterceptible(false)).To(Succeed())
    95  		})
    96  
    97  		It("consistently is able to be created", func() {
    98  			// enable concurrent use of database. this is set to 1 by default to
    99  			// ensure methods don't require more than one in a single connection,
   100  			// which can cause deadlocking as the pool is limited.
   101  			dbConn.SetMaxOpenConns(2)
   102  
   103  			done := make(chan struct{})
   104  
   105  			wg := new(sync.WaitGroup)
   106  			wg.Add(1)
   107  			go func() {
   108  				defer GinkgoRecover()
   109  				defer wg.Done()
   110  
   111  				for {
   112  					select {
   113  					case <-done:
   114  						return
   115  					default:
   116  						_, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{})
   117  						Expect(err).ToNot(HaveOccurred())
   118  					}
   119  				}
   120  			}()
   121  
   122  			wg.Add(1)
   123  			go func() {
   124  				defer GinkgoRecover()
   125  				defer close(done)
   126  				defer wg.Done()
   127  
   128  				for i := 0; i < 100; i++ {
   129  					_, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{})
   130  					Expect(err).ToNot(HaveOccurred())
   131  				}
   132  			}()
   133  
   134  			wg.Wait()
   135  		})
   136  	})
   137  	Context("when the resource config is concurrently deleted and created", func() {
   138  		BeforeEach(func() {
   139  			Expect(build.Finish(db.BuildStatusSucceeded)).To(Succeed())
   140  			Expect(build.SetInterceptible(false)).To(Succeed())
   141  		})
   142  
   143  		It("consistently is able to be used", func() {
   144  			// enable concurrent use of database. this is set to 1 by default to
   145  			// ensure methods don't require more than one in a single connection,
   146  			// which can cause deadlocking as the pool is limited.
   147  			dbConn.SetMaxOpenConns(2)
   148  
   149  			done := make(chan struct{})
   150  
   151  			wg := new(sync.WaitGroup)
   152  			wg.Add(1)
   153  			go func() {
   154  				defer GinkgoRecover()
   155  				defer wg.Done()
   156  
   157  				for {
   158  					select {
   159  					case <-done:
   160  						return
   161  					default:
   162  						Expect(resourceConfigFactory.CleanUnreferencedConfigs(0)).To(Succeed())
   163  					}
   164  				}
   165  			}()
   166  
   167  			wg.Add(1)
   168  			go func() {
   169  				defer GinkgoRecover()
   170  				defer close(done)
   171  				defer wg.Done()
   172  
   173  				for i := 0; i < 100; i++ {
   174  					_, err := resourceConfigFactory.FindOrCreateResourceConfig("some-base-resource-type", atc.Source{"some": "unique-source"}, atc.VersionedResourceTypes{})
   175  					Expect(err).ToNot(HaveOccurred())
   176  				}
   177  			}()
   178  
   179  			wg.Wait()
   180  		})
   181  	})
   182  
   183  	Describe("FindResourceConfigByID", func() {
   184  		var (
   185  			resourceConfigID      int
   186  			resourceConfig        db.ResourceConfig
   187  			createdResourceConfig db.ResourceConfig
   188  			found                 bool
   189  			err                   error
   190  		)
   191  
   192  		JustBeforeEach(func() {
   193  			resourceConfig, found, err = resourceConfigFactory.FindResourceConfigByID(resourceConfigID)
   194  			Expect(err).ToNot(HaveOccurred())
   195  		})
   196  
   197  		Context("when the resource config does exist", func() {
   198  			Context("when the resource config uses a base resource type", func() {
   199  				BeforeEach(func() {
   200  					setupTx, err := dbConn.Begin()
   201  					Expect(err).ToNot(HaveOccurred())
   202  
   203  					brt := db.BaseResourceType{
   204  						Name: "base-resource-type-name",
   205  					}
   206  
   207  					_, err = brt.FindOrCreate(setupTx, false)
   208  					Expect(err).NotTo(HaveOccurred())
   209  					Expect(setupTx.Commit()).To(Succeed())
   210  
   211  					createdResourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig("base-resource-type-name", atc.Source{}, atc.VersionedResourceTypes{})
   212  					Expect(err).ToNot(HaveOccurred())
   213  					Expect(createdResourceConfig).ToNot(BeNil())
   214  
   215  					resourceConfigID = createdResourceConfig.ID()
   216  				})
   217  
   218  				It("should find the resource config using the resource's config id", func() {
   219  					Expect(found).To(BeTrue())
   220  					Expect(resourceConfig).ToNot(BeNil())
   221  					Expect(resourceConfig.ID()).To(Equal(resourceConfigID))
   222  					Expect(resourceConfig.CreatedByBaseResourceType()).To(Equal(createdResourceConfig.CreatedByBaseResourceType()))
   223  				})
   224  			})
   225  
   226  			Context("when the resource config uses a custom resource type", func() {
   227  				BeforeEach(func() {
   228  					pipelineResourceTypes, err := defaultPipeline.ResourceTypes()
   229  					Expect(err).ToNot(HaveOccurred())
   230  
   231  					createdResourceConfig, err = resourceConfigFactory.FindOrCreateResourceConfig("some-type", atc.Source{}, pipelineResourceTypes.Deserialize())
   232  					Expect(err).ToNot(HaveOccurred())
   233  					Expect(createdResourceConfig).ToNot(BeNil())
   234  
   235  					resourceConfigID = createdResourceConfig.ID()
   236  				})
   237  
   238  				It("should find the resource config using the resource's config id", func() {
   239  					Expect(found).To(BeTrue())
   240  					Expect(resourceConfig).ToNot(BeNil())
   241  					Expect(resourceConfig.ID()).To(Equal(resourceConfigID))
   242  					Expect(resourceConfig.CreatedByResourceCache().ID()).To(Equal(createdResourceConfig.CreatedByResourceCache().ID()))
   243  					Expect(resourceConfig.CreatedByResourceCache().ResourceConfig().ID()).To(Equal(createdResourceConfig.CreatedByResourceCache().ResourceConfig().ID()))
   244  				})
   245  			})
   246  		})
   247  
   248  		Context("when the resource config id does not exist", func() {
   249  			BeforeEach(func() {
   250  				resourceConfigID = 123
   251  			})
   252  
   253  			It("should not find the resource config", func() {
   254  				Expect(found).To(BeFalse())
   255  			})
   256  		})
   257  	})
   258  })