Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0322146

Browse files
committed
chore: adding tests
Signed-off-by: Danny Kopping <[email protected]>
1 parent 634082b commit 0322146

File tree

10 files changed

+299
-79
lines changed

10 files changed

+299
-79
lines changed

coderd/coderd.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -1763,11 +1763,6 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
17631763
return nil, xerrors.Errorf("failed to create in-memory provisioner daemon: %w", err)
17641764
}
17651765

1766-
var prebuildsOrchestrator prebuilds.ReconciliationOrchestrator
1767-
if val := api.PrebuildsReconciler.Load(); val != nil {
1768-
prebuildsOrchestrator = *val
1769-
}
1770-
17711766
mux := drpcmux.New()
17721767
api.Logger.Debug(dialCtx, "starting in-memory provisioner daemon", slog.F("name", name))
17731768
logger := api.Logger.Named(fmt.Sprintf("inmem-provisionerd-%s", name))
@@ -1795,7 +1790,7 @@ func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, n
17951790
Clock: api.Clock,
17961791
},
17971792
api.NotificationsEnqueuer,
1798-
prebuildsOrchestrator,
1793+
&api.PrebuildsReconciler,
17991794
)
18001795
if err != nil {
18011796
return nil, err

coderd/notifications/notificationstest/fake_enqueuer.go

+7
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/prometheus/client_golang/prometheus"
1010

1111
"github.com/coder/coder/v2/coderd/database/dbauthz"
12+
"github.com/coder/coder/v2/coderd/notifications"
1213
"github.com/coder/coder/v2/coderd/rbac"
1314
"github.com/coder/coder/v2/coderd/rbac/policy"
1415
)
@@ -19,6 +20,12 @@ type FakeEnqueuer struct {
1920
sent []*FakeNotification
2021
}
2122

23+
var _ notifications.Enqueuer = &FakeEnqueuer{}
24+
25+
func NewFakeEnqueuer() notifications.Enqueuer {
26+
return &FakeEnqueuer{}
27+
}
28+
2229
type FakeNotification struct {
2330
UserID, TemplateID uuid.UUID
2431
Labels map[string]string

coderd/prebuilds/api.go

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type ReconciliationOrchestrator interface {
3131

3232
// TrackResourceReplacement handles a pathological situation whereby a terraform resource is replaced due to drift,
3333
// which can obviate the whole point of pre-provisioning a prebuilt workspace.
34+
// See more detail at https://coder.com/docs/admin/templates/extending-templates/prebuilt-workspaces.md#preventing-resource-replacement.
3435
TrackResourceReplacement(ctx context.Context, workspaceID, buildID, claimantID uuid.UUID, replacements []*sdkproto.ResourceReplacement)
3536
}
3637

coderd/provisionerdserver/provisionerdserver.go

+9-6
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ type server struct {
109109
UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore]
110110
DeploymentValues *codersdk.DeploymentValues
111111
NotificationsEnqueuer notifications.Enqueuer
112-
PrebuildsOrchestrator prebuilds.ReconciliationOrchestrator
112+
PrebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator]
113113

114114
OIDCConfig promoauth.OAuth2Config
115115

@@ -164,7 +164,7 @@ func NewServer(lifecycleCtx context.Context,
164164
deploymentValues *codersdk.DeploymentValues,
165165
options Options,
166166
enqueuer notifications.Enqueuer,
167-
prebuildsOrchestrator prebuilds.ReconciliationOrchestrator,
167+
prebuildsOrchestrator *atomic.Pointer[prebuilds.ReconciliationOrchestrator],
168168
) (proto.DRPCProvisionerDaemonServer, error) {
169169
// Fail-fast if pointers are nil
170170
if lifecycleCtx == nil {
@@ -1731,10 +1731,13 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
17311731
})
17321732
}
17331733

1734-
// Track resource replacements, if there are any.
1735-
if resourceReplacements := completed.GetWorkspaceBuild().GetResourceReplacements(); len(resourceReplacements) > 0 {
1736-
// Fire and forget.
1737-
go s.PrebuildsOrchestrator.TrackResourceReplacement(context.Background(), workspace.ID, workspaceBuild.ID, input.PrebuildClaimedByUser, resourceReplacements)
1734+
if s.PrebuildsOrchestrator != nil {
1735+
// Track resource replacements, if there are any.
1736+
orchestrator := s.PrebuildsOrchestrator.Load()
1737+
if resourceReplacements := completed.GetWorkspaceBuild().GetResourceReplacements(); orchestrator != nil && len(resourceReplacements) > 0 {
1738+
// Fire and forget.
1739+
go (*orchestrator).TrackResourceReplacement(context.Background(), workspace.ID, workspaceBuild.ID, input.PrebuildClaimedByUser, resourceReplacements)
1740+
}
17381741
}
17391742

17401743
msg, err := json.Marshal(wspubsub.WorkspaceEvent{

coderd/provisionerdserver/provisionerdserver_test.go

+106-1
Original file line numberDiff line numberDiff line change
@@ -1747,6 +1747,103 @@ func TestCompleteJob(t *testing.T) {
17471747
})
17481748
}
17491749
})
1750+
1751+
t.Run("PrebuiltWorkspaceClaimWithResourceReplacements", func(t *testing.T) {
1752+
t.Parallel()
1753+
1754+
ctx := testutil.Context(t, testutil.WaitLong)
1755+
1756+
done := make(chan struct{})
1757+
orchestrator := &mockPrebuildsOrchestrator{
1758+
ReconciliationOrchestrator: agplprebuilds.DefaultReconciler,
1759+
done: done,
1760+
}
1761+
srv, db, ps, pd := setup(t, false, &overrides{
1762+
prebuildsOrchestrator: orchestrator,
1763+
})
1764+
1765+
user := dbgen.User(t, db, database.User{})
1766+
template := dbgen.Template(t, db, database.Template{
1767+
Name: "template",
1768+
Provisioner: database.ProvisionerTypeEcho,
1769+
OrganizationID: pd.OrganizationID,
1770+
})
1771+
file := dbgen.File(t, db, database.File{CreatedBy: user.ID})
1772+
workspaceTable := dbgen.Workspace(t, db, database.WorkspaceTable{
1773+
TemplateID: template.ID,
1774+
OwnerID: user.ID,
1775+
OrganizationID: pd.OrganizationID,
1776+
})
1777+
version := dbgen.TemplateVersion(t, db, database.TemplateVersion{
1778+
OrganizationID: pd.OrganizationID,
1779+
TemplateID: uuid.NullUUID{
1780+
UUID: template.ID,
1781+
Valid: true,
1782+
},
1783+
JobID: uuid.New(),
1784+
})
1785+
build := dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
1786+
WorkspaceID: workspaceTable.ID,
1787+
InitiatorID: user.ID,
1788+
TemplateVersionID: version.ID,
1789+
Transition: database.WorkspaceTransitionStart,
1790+
Reason: database.BuildReasonInitiator,
1791+
})
1792+
job := dbgen.ProvisionerJob(t, db, ps, database.ProvisionerJob{
1793+
FileID: file.ID,
1794+
InitiatorID: user.ID,
1795+
Type: database.ProvisionerJobTypeWorkspaceBuild,
1796+
Input: must(json.Marshal(provisionerdserver.WorkspaceProvisionJob{
1797+
WorkspaceBuildID: build.ID,
1798+
1799+
// Mark the job as a prebuilt workspace claim.
1800+
PrebuildClaimedByUser: uuid.New(),
1801+
IsPrebuild: false,
1802+
})),
1803+
OrganizationID: pd.OrganizationID,
1804+
})
1805+
_, err := db.AcquireProvisionerJob(ctx, database.AcquireProvisionerJobParams{
1806+
OrganizationID: pd.OrganizationID,
1807+
WorkerID: uuid.NullUUID{
1808+
UUID: pd.ID,
1809+
Valid: true,
1810+
},
1811+
Types: []database.ProvisionerType{database.ProvisionerTypeEcho},
1812+
})
1813+
require.NoError(t, err)
1814+
1815+
replacements := []*sdkproto.ResourceReplacement{
1816+
{
1817+
Resource: "docker_container[0]",
1818+
Paths: []string{"env"},
1819+
},
1820+
}
1821+
_, err = srv.CompleteJob(ctx, &proto.CompletedJob{
1822+
JobId: job.ID.String(),
1823+
Type: &proto.CompletedJob_WorkspaceBuild_{
1824+
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
1825+
State: []byte{},
1826+
ResourceReplacements: replacements,
1827+
},
1828+
},
1829+
})
1830+
require.NoError(t, err)
1831+
1832+
testutil.RequireReceive(ctx, t, done)
1833+
require.Equal(t, replacements, orchestrator.replacements)
1834+
})
1835+
}
1836+
1837+
type mockPrebuildsOrchestrator struct {
1838+
agplprebuilds.ReconciliationOrchestrator
1839+
1840+
replacements []*sdkproto.ResourceReplacement
1841+
done chan struct{}
1842+
}
1843+
1844+
func (m *mockPrebuildsOrchestrator) TrackResourceReplacement(_ context.Context, _, _, _ uuid.UUID, replacements []*sdkproto.ResourceReplacement) {
1845+
m.replacements = replacements
1846+
m.done <- struct{}{}
17501847
}
17511848

17521849
func TestInsertWorkspacePresetsAndParameters(t *testing.T) {
@@ -2632,6 +2729,7 @@ type overrides struct {
26322729
heartbeatInterval time.Duration
26332730
auditor audit.Auditor
26342731
notificationEnqueuer notifications.Enqueuer
2732+
prebuildsOrchestrator agplprebuilds.ReconciliationOrchestrator
26352733
}
26362734

26372735
func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisionerDaemonServer, database.Store, pubsub.Pubsub, database.ProvisionerDaemon) {
@@ -2713,6 +2811,13 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
27132811
})
27142812
require.NoError(t, err)
27152813

2814+
prebuildsOrchestrator := ov.prebuildsOrchestrator
2815+
if prebuildsOrchestrator == nil {
2816+
prebuildsOrchestrator = agplprebuilds.DefaultReconciler
2817+
}
2818+
var op atomic.Pointer[agplprebuilds.ReconciliationOrchestrator]
2819+
op.Store(&prebuildsOrchestrator)
2820+
27162821
srv, err := provisionerdserver.NewServer(
27172822
ov.ctx,
27182823
&url.URL{},
@@ -2740,7 +2845,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
27402845
HeartbeatFn: ov.heartbeatFn,
27412846
},
27422847
notifEnq,
2743-
agplprebuilds.DefaultReconciler,
2848+
&op,
27442849
)
27452850
require.NoError(t, err)
27462851
return srv, db, ps, daemon

enterprise/coderd/prebuilds/metricscollector.go

+24-7
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,42 @@ import (
1616
"github.com/coder/coder/v2/coderd/prebuilds"
1717
)
1818

19+
const (
20+
namespace = "coderd_prebuilt_workspaces_"
21+
22+
MetricCreatedCount = namespace + "created_total"
23+
MetricFailedCount = namespace + "failed_total"
24+
MetricClaimedCount = namespace + "claimed_total"
25+
MetricResourceReplacementsCount = namespace + "resource_replacements_total"
26+
MetricDesiredGauge = namespace + "desired"
27+
MetricRunningGauge = namespace + "running"
28+
MetricEligibleGauge = namespace + "eligible"
29+
)
30+
1931
var (
2032
labels = []string{"template_name", "preset_name", "organization_name"}
2133
createdPrebuildsDesc = prometheus.NewDesc(
22-
"coderd_prebuilt_workspaces_created_total",
34+
MetricCreatedCount,
2335
"Total number of prebuilt workspaces that have been created to meet the desired instance count of each "+
2436
"template preset.",
2537
labels,
2638
nil,
2739
)
2840
failedPrebuildsDesc = prometheus.NewDesc(
29-
"coderd_prebuilt_workspaces_failed_total",
41+
MetricFailedCount,
3042
"Total number of prebuilt workspaces that failed to build.",
3143
labels,
3244
nil,
3345
)
3446
claimedPrebuildsDesc = prometheus.NewDesc(
35-
"coderd_prebuilt_workspaces_claimed_total",
47+
MetricClaimedCount,
3648
"Total number of prebuilt workspaces which were claimed by users. Claiming refers to creating a workspace "+
3749
"with a preset selected for which eligible prebuilt workspaces are available and one is reassigned to a user.",
3850
labels,
3951
nil,
4052
)
4153
resourceReplacementsDesc = prometheus.NewDesc(
42-
"coderd_prebuilt_workspaces_resource_replacements_total",
54+
MetricResourceReplacementsCount,
4355
"Total number of prebuilt workspaces whose resource(s) got replaced upon being claimed. "+
4456
"In Terraform, drift on immutable attributes results in resource replacement. "+
4557
"This represents a worst-case scenario for prebuilt workspaces because the pre-provisioned resource "+
@@ -49,20 +61,20 @@ var (
4961
nil,
5062
)
5163
desiredPrebuildsDesc = prometheus.NewDesc(
52-
"coderd_prebuilt_workspaces_desired",
64+
MetricDesiredGauge,
5365
"Target number of prebuilt workspaces that should be available for each template preset.",
5466
labels,
5567
nil,
5668
)
5769
runningPrebuildsDesc = prometheus.NewDesc(
58-
"coderd_prebuilt_workspaces_running",
70+
MetricRunningGauge,
5971
"Current number of prebuilt workspaces that are in a running state. These workspaces have started "+
6072
"successfully but may not yet be claimable by users (see coderd_prebuilt_workspaces_eligible).",
6173
labels,
6274
nil,
6375
)
6476
eligiblePrebuildsDesc = prometheus.NewDesc(
65-
"coderd_prebuilt_workspaces_eligible",
77+
MetricEligibleGauge,
6678
"Current number of prebuilt workspaces that are eligible to be claimed by users. These are workspaces that "+
6779
"have completed their build process with their agent reporting 'ready' status.",
6880
labels,
@@ -162,5 +174,10 @@ func (mc *MetricsCollector) trackResourceReplacement(orgName, templateName, pres
162174
if _, ok := mc.replacementsCounter[key]; !ok {
163175
mc.replacementsCounter[key] = &atomic.Int64{}
164176
}
177+
178+
// We only track _that_ a resource replacement occurred, not how many.
179+
// Just one is enough to ruin a prebuild, but we can't know apriori which replacement would cause this.
180+
// For example, say we have 2 replacements: a docker_container and a null_resource; we don't know which one might
181+
// cause an issue (or indeed if either would), so we just track the replacement.
165182
mc.replacementsCounter[key].Add(1)
166183
}

0 commit comments

Comments
 (0)