Skip to content

Commit 563d6d2

Browse files
committed
Schedule module validation jobs after within Defer
Moving the scheduling of both validation jobs into the Defer function ensures that they run after we loaded the schema, reference targets, and reference origins. Before validation would run right after loading the metadata for a module without waiting on any of the work that we do in Defer.
1 parent 22316bb commit 563d6d2

File tree

3 files changed

+48
-34
lines changed

3 files changed

+48
-34
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: BUG FIXES
2+
body: Ensure validation runs after decoding the whole module to avoid stale diagnostics
3+
time: 2024-07-25T10:52:38.411762+02:00
4+
custom:
5+
Issue: "1777"
6+
Repository: terraform-ls

internal/features/modules/events.go

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,15 @@ func (f *ModulesFeature) decodeModule(ctx context.Context, dir document.DirHandl
253253
}
254254
ids = append(ids, parseId)
255255

256+
// Changes to a setting currently requires a LS restart, so the LS
257+
// setting context cannot change during the execution of a job. That's
258+
// why we can extract it here and use it in Defer.
259+
// See https://github.com/hashicorp/terraform-ls/issues/1008
260+
// We can safely ignore the error here. If we can't get the options from
261+
// the context, validationOptions.EnableEnhancedValidation will be false
262+
// by default. So we don't run the validation jobs.
263+
validationOptions, _ := lsctx.ValidationOptions(ctx)
264+
256265
metaId, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
257266
Dir: dir,
258267
Func: func(ctx context.Context) error {
@@ -322,6 +331,35 @@ func (f *ModulesFeature) decodeModule(ctx context.Context, dir document.DirHandl
322331
}
323332
deferIds = append(deferIds, refOriginsId)
324333

334+
// We don't want to validate nested modules
335+
if isFirstLevel && validationOptions.EnableEnhancedValidation {
336+
_, err = f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
337+
Dir: dir,
338+
Func: func(ctx context.Context) error {
339+
return jobs.SchemaModuleValidation(ctx, f.Store, f.rootFeature, dir.Path())
340+
},
341+
Type: op.OpTypeSchemaModuleValidation.String(),
342+
DependsOn: append(modCalls, eSchemaId),
343+
IgnoreState: ignoreState,
344+
})
345+
if err != nil {
346+
return deferIds, err
347+
}
348+
349+
_, err = f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
350+
Dir: dir,
351+
Func: func(ctx context.Context) error {
352+
return jobs.ReferenceValidation(ctx, f.Store, f.rootFeature, dir.Path())
353+
},
354+
Type: op.OpTypeReferenceValidation.String(),
355+
DependsOn: job.IDs{refOriginsId, refTargetsId},
356+
IgnoreState: ignoreState,
357+
})
358+
if err != nil {
359+
return deferIds, err
360+
}
361+
}
362+
325363
return deferIds, nil
326364
},
327365
})
@@ -330,44 +368,12 @@ func (f *ModulesFeature) decodeModule(ctx context.Context, dir document.DirHandl
330368
}
331369
ids = append(ids, metaId)
332370

333-
// We don't want to run validation or fetch module data from the registry
334-
// for nested modules, so we return early.
371+
// We don't want to fetch module data from the registry for nested modules,
372+
// so we return early.
335373
if !isFirstLevel {
336374
return ids, nil
337375
}
338376

339-
validationOptions, err := lsctx.ValidationOptions(ctx)
340-
if err != nil {
341-
return ids, err
342-
}
343-
if validationOptions.EnableEnhancedValidation {
344-
_, err = f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
345-
Dir: dir,
346-
Func: func(ctx context.Context) error {
347-
return jobs.SchemaModuleValidation(ctx, f.Store, f.rootFeature, dir.Path())
348-
},
349-
Type: op.OpTypeSchemaModuleValidation.String(),
350-
DependsOn: ids,
351-
IgnoreState: ignoreState,
352-
})
353-
if err != nil {
354-
return ids, err
355-
}
356-
357-
_, err = f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
358-
Dir: dir,
359-
Func: func(ctx context.Context) error {
360-
return jobs.ReferenceValidation(ctx, f.Store, f.rootFeature, dir.Path())
361-
},
362-
Type: op.OpTypeReferenceValidation.String(),
363-
DependsOn: ids,
364-
IgnoreState: ignoreState,
365-
})
366-
if err != nil {
367-
return ids, err
368-
}
369-
}
370-
371377
// This job may make an HTTP request, and we schedule it in
372378
// the low-priority queue, so we don't want to wait for it.
373379
_, err = f.stateStore.JobStore.EnqueueJob(ctx, job.Job{

internal/state/jobs.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ func isDirOpen(txn *memdb.Txn, dirHandle document.DirHandle) bool {
381381
return docObj != nil
382382
}
383383

384+
// WaitForJobs waits for all jobs to be done. If a job has a defer function
385+
// that returns new job IDs, it waits for those jobs to be done as well.
384386
func (js *JobStore) WaitForJobs(ctx context.Context, ids ...job.ID) error {
385387
if len(ids) == 0 {
386388
return nil

0 commit comments

Comments
 (0)