99 "regexp"
1010 "strings"
1111 "sync"
12+ "syscall"
1213 "text/template"
14+ "time"
1315
1416 "github.com/aws/aws-sdk-go-v2/aws"
1517 "github.com/hashicorp/terraform-exec/tfexec"
@@ -28,6 +30,51 @@ var templates embed.FS
2830
2931var tfPluginMux sync.Mutex
3032
33+ func lockPluginCache (pluginCacheDir string ) (* os.File , error ) {
34+ if err := os .MkdirAll (pluginCacheDir , 0755 ); err != nil {
35+ return nil , err
36+ }
37+
38+ lockPath := filepath .Join (pluginCacheDir , ".lock" )
39+ lockFile , err := os .OpenFile (lockPath , os .O_CREATE | os .O_RDWR , 0644 )
40+ if err != nil {
41+ return nil , err
42+ }
43+
44+ // Wait up to 5 minutes for the lock
45+ timeout := time .After (5 * time .Minute )
46+ ticker := time .NewTicker (5 * time .Second )
47+ defer ticker .Stop ()
48+
49+ for {
50+ err = syscall .Flock (int (lockFile .Fd ()), syscall .LOCK_EX | syscall .LOCK_NB )
51+ if err == nil {
52+ return lockFile , nil
53+ }
54+
55+ if err != syscall .EWOULDBLOCK {
56+ lockFile .Close ()
57+ return nil , err
58+ }
59+
60+ select {
61+ case <- timeout :
62+ lockFile .Close ()
63+ return nil , fmt .Errorf ("timeout waiting for plugin cache lock" )
64+ case <- ticker .C :
65+ // Retry
66+ }
67+ }
68+ }
69+
70+ func unlockPluginCache (lockFile * os.File ) error {
71+ if lockFile == nil {
72+ return nil
73+ }
74+ syscall .Flock (int (lockFile .Fd ()), syscall .LOCK_UN )
75+ return lockFile .Close ()
76+ }
77+
3178// CreateTerraformFileFromTemplate populates a Terraform template and create files in the state
3279func CreateTerraformFilesFromTemplate (terraformTemplateFilePath string , TerraformOutputFileName string , terraformOutputDir string , templateData any ) error {
3380 template := Template {
@@ -99,24 +146,31 @@ func initTerraform(ctx context.Context, workingDir, terraformExecPath string, cr
99146 return nil , err
100147 }
101148
149+ pluginCacheDir := fmt .Sprintf ("%s/plugin-cache" , filepath .Dir (terraformExecPath ))
150+
102151 env := map [string ]string {
103152 "AWS_ACCESS_KEY_ID" : credentials .AccessKeyID ,
104153 "AWS_SECRET_ACCESS_KEY" : credentials .SecretAccessKey ,
105154 "SPOTINST_TOKEN" : os .Getenv ("SPOTINST_TOKEN" ),
106155 "SPOTINST_ACCOUNT" : os .Getenv ("SPOTINST_ACCOUNT" ),
107- "TF_PLUGIN_CACHE_DIR" : fmt . Sprintf ( "%s/plugin-cache" , filepath . Dir ( terraformExecPath )) ,
156+ "TF_PLUGIN_CACHE_DIR" : pluginCacheDir ,
108157 }
109158
110- // this overrides all ENVVARs that are passed to Terraform
111159 err = tf .SetEnv (env )
112160 if err != nil {
113161 return nil , err
114162 }
115163
116164 tfPluginMux .Lock ()
117- err = tf .Init (ctx , tfexec .Upgrade (true ))
118- tfPluginMux .Unlock ()
165+ defer tfPluginMux .Unlock ()
166+
167+ lockFile , err := lockPluginCache (pluginCacheDir )
168+ if err != nil {
169+ return nil , fmt .Errorf ("failed to acquire plugin cache lock: %w" , err )
170+ }
171+ defer unlockPluginCache (lockFile )
119172
173+ err = tf .Init (ctx , tfexec .Upgrade (true ))
120174 if err != nil {
121175 return nil , err
122176 }
0 commit comments