9
9
10
10
"github.com/coder/coder/v2/coderd/database/dbgen"
11
11
"github.com/coder/coder/v2/coderd/database/pubsub"
12
+ "github.com/coder/coder/v2/coderd/provisionerdserver"
12
13
"github.com/coder/coder/v2/coderd/rbac"
13
14
"github.com/coder/quartz"
14
15
@@ -1669,7 +1670,6 @@ func TestMain(m *testing.M) {
1669
1670
func TestExecutorAutostartSkipsWhenNoProvisionersAvailable (t * testing.T ) {
1670
1671
t .Parallel ()
1671
1672
1672
- db , ps := dbtestutil .NewDB (t )
1673
1673
var (
1674
1674
sched = mustSchedule (t , "CRON_TZ=UTC 0 * * * *" )
1675
1675
tickCh = make (chan time.Time )
@@ -1679,12 +1679,10 @@ func TestExecutorAutostartSkipsWhenNoProvisionersAvailable(t *testing.T) {
1679
1679
// Create client with provisioner closer
1680
1680
provisionerDaemonTags := map [string ]string {"owner" : "testowner" , "scope" : "organization" }
1681
1681
t .Logf ("Setting provisioner daemon tags: %v" , provisionerDaemonTags )
1682
- client , provisionerCloser := coderdtest .NewWithProvisionerCloser (t , & coderdtest.Options {
1682
+ client , db := coderdtest .NewWithDatabase (t , & coderdtest.Options {
1683
1683
AutobuildTicker : tickCh ,
1684
1684
IncludeProvisionerDaemon : true ,
1685
1685
AutobuildStats : statsCh ,
1686
- Database : db ,
1687
- Pubsub : ps ,
1688
1686
ProvisionerDaemonTags : provisionerDaemonTags ,
1689
1687
})
1690
1688
@@ -1695,147 +1693,34 @@ func TestExecutorAutostartSkipsWhenNoProvisionersAvailable(t *testing.T) {
1695
1693
})
1696
1694
1697
1695
// Stop the workspace while provisioner is available
1698
- workspace = coderdtest .MustTransitionWorkspace (t , client , workspace .ID ,
1699
- codersdk .WorkspaceTransitionStart , codersdk .WorkspaceTransitionStop )
1700
-
1701
- // Wait for provisioner to be registered
1702
- coderdtest .MustWaitForProvisioners (t , db )
1696
+ workspace = coderdtest .MustTransitionWorkspace (t , client , workspace .ID , codersdk .WorkspaceTransitionStart , codersdk .WorkspaceTransitionStop )
1703
1697
1704
1698
// Wait for provisioner to be available for this specific workspace
1705
- coderdtest .MustWaitForProvisionersAvailable (t , db , workspace , autobuild .TestingStaleInterval )
1706
-
1707
- // Now shut down the provisioner daemon
1708
- ctx := testutil .Context (t , testutil .WaitShort )
1709
- err := provisionerCloser .Close ()
1710
- require .NoError (t , err )
1711
-
1712
- // Wait for the provisioner to become stale (heartbeat stops)
1713
- // The stale interval is 5 seconds, so we need to wait a bit longer
1714
- time .Sleep (6 * time .Second )
1715
-
1716
- // Debug: check what's in the database
1717
- daemons , err := db .GetProvisionerDaemons (ctx )
1718
- require .NoError (t , err )
1719
- t .Logf ("After close: found %d daemons" , len (daemons ))
1720
- for i , daemon := range daemons {
1721
- t .Logf ("Daemon %d: ID=%s, Name=%s, LastSeen=%v" , i , daemon .ID , daemon .Name , daemon .LastSeenAt )
1722
- }
1699
+ id := coderdtest .MustWaitForProvisionersAvailable (t , db , workspace , provisionerdserver .StaleInterval )
1723
1700
1724
- // Since we commented out the close call, the provisioner should NOT become stale
1725
- // Let's wait a bit but NOT longer than the stale interval
1726
- time .Sleep (2 * time .Second ) // Wait less than the 5-second stale interval
1727
-
1728
- daemons , err = db .GetProvisionerDaemons (ctx )
1729
- require .NoError (t , err )
1730
- require .NotEmpty (t , daemons , "should have provisioner daemons" )
1731
-
1732
- now := time .Now ()
1733
- for _ , daemon := range daemons {
1734
- if daemon .LastSeenAt .Valid {
1735
- age := now .Sub (daemon .LastSeenAt .Time )
1736
- t .Logf ("Daemon %s: age=%v, staleInterval=%v, isStale=%v" , daemon .Name , age , autobuild .TestingStaleInterval , age > autobuild .TestingStaleInterval )
1737
- // Since we closed the provisioner, it should be stale
1738
- require .True (t , age > autobuild .TestingStaleInterval , "provisioner should be stale since we closed it" )
1739
- }
1740
- }
1741
-
1742
- // Debug: Check the template version job tags and organization
1743
- templateVersion , err := client .TemplateVersion (context .Background (), workspace .LatestBuild .TemplateVersionID )
1744
- require .NoError (t , err )
1745
- t .Logf ("Template version job ID: %s" , templateVersion .Job .ID )
1746
- t .Logf ("Template version job tags: %v" , templateVersion .Job .Tags )
1747
- t .Logf ("Workspace organization ID: %s" , workspace .OrganizationID )
1748
- t .Logf ("Template version organization ID: %s" , templateVersion .OrganizationID )
1749
- t .Logf ("Workspace LatestBuild.TemplateVersionID: %s" , workspace .LatestBuild .TemplateVersionID )
1750
-
1751
- // Debug: Get the template version job directly from database to compare
1752
- templateVersionJobFromDB , err := db .GetProvisionerJobByID (context .Background (), templateVersion .Job .ID )
1753
- require .NoError (t , err )
1754
- t .Logf ("Template version job from DB - ID: %s, Tags: %v" , templateVersionJobFromDB .ID , templateVersionJobFromDB .Tags )
1755
-
1756
- // Debug: Query database directly to see what provisioner daemons exist
1757
- allDaemons , err := db .GetProvisionerDaemons (context .Background ())
1758
- require .NoError (t , err )
1759
- t .Logf ("Total provisioner daemons in database: %d" , len (allDaemons ))
1760
- for i , daemon := range allDaemons {
1761
- t .Logf ("Daemon %d: ID=%s, Name=%s, OrgID=%s, Tags=%v" , i , daemon .ID , daemon .Name , daemon .OrganizationID , daemon .Tags )
1762
- }
1701
+ // Ensure the provisioner is stale
1702
+ staleTime := sched .Next (workspace .LatestBuild .CreatedAt ).Add ((- 1 * provisionerdserver .StaleInterval ) + - 10 * time .Second )
1703
+ coderdtest .UpdateProvisionerLastSeenAt (t , db , id , time .Now (), staleTime )
1763
1704
1764
- // Debug: Test if we can query using the ACTUAL provisioner daemon tags
1765
- if len (allDaemons ) > 0 {
1766
- actualDaemonTags := allDaemons [0 ].Tags
1767
- t .Logf ("Testing query with ACTUAL daemon tags: %v" , actualDaemonTags )
1768
- queryWithActualTags := database.GetProvisionerDaemonsByOrganizationParams {
1769
- OrganizationID : workspace .OrganizationID ,
1770
- WantTags : actualDaemonTags ,
1771
- }
1772
- matchingWithActualTags , err := db .GetProvisionerDaemonsByOrganization (context .Background (), queryWithActualTags )
1773
- require .NoError (t , err )
1774
- t .Logf ("Query with actual daemon tags returns: %d daemons" , len (matchingWithActualTags ))
1775
- }
1776
-
1777
- // Debug: Test the exact query that hasAvailableProvisioners uses
1778
- queryParams := database.GetProvisionerDaemonsByOrganizationParams {
1779
- OrganizationID : workspace .OrganizationID ,
1780
- WantTags : templateVersion .Job .Tags ,
1781
- }
1782
- t .Logf ("Query params: OrgID=%s, WantTags=%v" , queryParams .OrganizationID , queryParams .WantTags )
1783
- t .Logf ("Test query detailed params: org_id_type=%T, org_id_value=%s, want_tags_type=%T, want_tags_value=%v" ,
1784
- queryParams .OrganizationID , queryParams .OrganizationID , queryParams .WantTags , queryParams .WantTags )
1785
-
1786
- // Test 1: Transaction isolation - try with different transaction contexts
1787
- t .Logf ("=== Testing transaction isolation ===" )
1788
-
1789
- // Query with context.Background() (what we used above)
1790
- matchingDaemons1 , err := db .GetProvisionerDaemonsByOrganization (context .Background (), queryParams )
1791
- require .NoError (t , err )
1792
- t .Logf ("With context.Background(): %d daemons" , len (matchingDaemons1 ))
1793
-
1794
- // Query with a fresh context
1795
- ctx2 := context .Background ()
1796
- matchingDaemons2 , err2 := db .GetProvisionerDaemonsByOrganization (ctx2 , queryParams )
1797
- require .NoError (t , err2 )
1798
- t .Logf ("With fresh context: %d daemons" , len (matchingDaemons2 ))
1799
-
1800
- // Query within a transaction (like hasAvailableProvisioners might use)
1801
- err = db .InTx (func (tx database.Store ) error {
1802
- matchingDaemons3 , err := tx .GetProvisionerDaemonsByOrganization (ctx2 , queryParams )
1803
- if err != nil {
1804
- return err
1805
- }
1806
- t .Logf ("Within transaction: %d daemons" , len (matchingDaemons3 ))
1807
- return nil
1808
- }, nil )
1809
- require .NoError (t , err )
1705
+ // Trigger autobuild
1706
+ tickCh <- sched .Next (workspace .LatestBuild .CreatedAt )
1810
1707
1811
- // Test 2: Timing issue - query right before and after hasAvailableProvisioners
1812
- t .Logf ("=== Testing timing issue ===" )
1708
+ stats := <- statsCh
1813
1709
1814
- // Query right before the autostart trigger
1815
- beforeDaemons , err := db .GetProvisionerDaemonsByOrganization (context .Background (), queryParams )
1816
- require .NoError (t , err )
1817
- t .Logf ("Right before autostart: %d daemons" , len (beforeDaemons ))
1710
+ // This assertion should FAIL when provisioner is available (not stale), can confirm by commenting out the
1711
+ // UpdateProvisionerLastSeenAt call above.
1712
+ assert .Len (t , stats .Transitions , 0 , "should not create builds when no provisioners available" )
1818
1713
1819
- // Since we commented out the close call, the provisioner is still active
1820
- // This means autobuild should proceed (not be skipped) and create transitions
1821
- // The test expects 0 transitions (skipped autostart), but with an active provisioner,
1822
- // it should get >0 transitions, causing the test to FAIL as intended
1714
+ // Ensure the provisioner is NOT stale, and see if we get a successful state transition.
1715
+ notStaleTime := sched .Next (workspace .LatestBuild .CreatedAt ).Add ((- 1 * provisionerdserver .StaleInterval ) + 10 * time .Second )
1716
+ coderdtest .UpdateProvisionerLastSeenAt (t , db , id , time .Now (), notStaleTime )
1823
1717
1824
1718
// Trigger autobuild
1825
1719
go func () {
1826
1720
tickCh <- sched .Next (workspace .LatestBuild .CreatedAt )
1827
1721
close (tickCh )
1828
1722
}()
1723
+ stats = <- statsCh
1829
1724
1830
- stats := <- statsCh
1831
-
1832
- // Query right after hasAvailableProvisioners was called
1833
- afterDaemons , err := db .GetProvisionerDaemonsByOrganization (context .Background (), queryParams )
1834
- require .NoError (t , err )
1835
- t .Logf ("Right after autostart: %d daemons" , len (afterDaemons ))
1836
-
1837
- // This assertion should FAIL when provisioner is available (demonstrating the fix works)
1838
- // When provisioner close is commented out: provisioner available → autostart proceeds → transitions > 0 → test fails ✓
1839
- // When provisioner close is active: provisioner unavailable → autostart skipped → transitions = 0 → test passes ✓
1840
- assert .Len (t , stats .Transitions , 0 , "should not create builds when no provisioners available" )
1725
+ assert .Len (t , stats .Transitions , 1 , "should not create builds when no provisioners available" )
1841
1726
}
0 commit comments