Files
coder/provisioner/terraform/modules.go
Hugo Dutka aa0dc2daa1 chore: track terraform modules in telemetry (#15450)
Addresses https://github.com/coder/nexus/issues/35.

This PR:

- Adds a `workspace_modules` table to track modules used by the
Terraform provisioner in provisioner jobs.
- Adds a `module_path` column to the `workspace_resources` table,
allowing to identify which module a resource originates from.
- Starts pushing this new information into telemetry.

For the person reviewing this PR, do not fret about the 1,500 new lines
- ~1,000 of them are auto-generated.
2024-11-16 21:56:19 +01:00

65 lines
1.7 KiB
Go

package terraform
import (
"encoding/json"
"os"
"path/filepath"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/provisionersdk/proto"
)
type module struct {
Source string `json:"Source"`
Version string `json:"Version"`
Key string `json:"Key"`
}
type modulesFile struct {
Modules []*module `json:"Modules"`
}
func getModulesFilePath(workdir string) string {
return filepath.Join(workdir, ".terraform", "modules", "modules.json")
}
func parseModulesFile(filePath string) ([]*proto.Module, error) {
modules := &modulesFile{}
data, err := os.ReadFile(filePath)
if err != nil {
return nil, xerrors.Errorf("read modules file: %w", err)
}
if err := json.Unmarshal(data, modules); err != nil {
return nil, xerrors.Errorf("unmarshal modules file: %w", err)
}
protoModules := make([]*proto.Module, len(modules.Modules))
for i, m := range modules.Modules {
protoModules[i] = &proto.Module{Source: m.Source, Version: m.Version, Key: m.Key}
}
return protoModules, nil
}
// getModules returns the modules from the modules file if it exists.
// It returns nil if the file does not exist.
// Modules become available after terraform init.
func getModules(workdir string) ([]*proto.Module, error) {
filePath := getModulesFilePath(workdir)
if _, err := os.Stat(filePath); os.IsNotExist(err) {
return nil, nil
}
modules, err := parseModulesFile(filePath)
if err != nil {
return nil, xerrors.Errorf("parse modules file: %w", err)
}
filteredModules := []*proto.Module{}
for _, m := range modules {
// Empty string means root module. It's always present, so we skip it.
if m.Source == "" {
continue
}
filteredModules = append(filteredModules, m)
}
return filteredModules, nil
}