@@ -2,11 +2,13 @@ package terraform
2
2
3
3
import (
4
4
"context"
5
+ "errors"
5
6
"path/filepath"
6
7
"sync"
7
8
"time"
8
9
9
10
"github.com/cli/safeexec"
11
+ "github.com/hashicorp/go-version"
10
12
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
11
13
"go.opentelemetry.io/otel/trace"
12
14
"golang.org/x/xerrors"
@@ -41,10 +43,15 @@ type ServeOptions struct {
41
43
ExitTimeout time.Duration
42
44
}
43
45
44
- func absoluteBinaryPath (ctx context.Context , logger slog.Logger ) (string , error ) {
46
+ type systemBinaryDetails struct {
47
+ absolutePath string
48
+ version * version.Version
49
+ }
50
+
51
+ func systemBinary (ctx context.Context ) (* systemBinaryDetails , error ) {
45
52
binaryPath , err := safeexec .LookPath ("terraform" )
46
53
if err != nil {
47
- return "" , xerrors .Errorf ("Terraform binary not found: %w" , err )
54
+ return nil , xerrors .Errorf ("Terraform binary not found: %w" , err )
48
55
}
49
56
50
57
// If the "coder" binary is in the same directory as
@@ -54,59 +61,68 @@ func absoluteBinaryPath(ctx context.Context, logger slog.Logger) (string, error)
54
61
// to execute this properly!
55
62
absoluteBinary , err := filepath .Abs (binaryPath )
56
63
if err != nil {
57
- return "" , xerrors .Errorf ("Terraform binary absolute path not found: %w" , err )
64
+ return nil , xerrors .Errorf ("Terraform binary absolute path not found: %w" , err )
58
65
}
59
66
60
67
// Checking the installed version of Terraform.
61
68
installedVersion , err := versionFromBinaryPath (ctx , absoluteBinary )
62
69
if err != nil {
63
- return "" , xerrors .Errorf ("Terraform binary get version failed: %w" , err )
70
+ return nil , xerrors .Errorf ("Terraform binary get version failed: %w" , err )
64
71
}
65
72
66
- logger .Info (ctx , "detected terraform version" ,
67
- slog .F ("installed_version" , installedVersion .String ()),
68
- slog .F ("min_version" , minTerraformVersion .String ()),
69
- slog .F ("max_version" , maxTerraformVersion .String ()))
70
-
71
- if installedVersion .LessThan (minTerraformVersion ) {
72
- logger .Warn (ctx , "installed terraform version too old, will download known good version to cache" )
73
- return "" , terraformMinorVersionMismatch
73
+ details := & systemBinaryDetails {
74
+ absolutePath : absoluteBinary ,
75
+ version : installedVersion ,
74
76
}
75
77
76
- // Warn if the installed version is newer than what we've decided is the max.
77
- // We used to ignore it and download our own version but this makes it easier
78
- // to test out newer versions of Terraform.
79
- if installedVersion .GreaterThanOrEqual (maxTerraformVersion ) {
80
- logger .Warn (ctx , "installed terraform version newer than expected, you may experience bugs" ,
81
- slog .F ("installed_version" , installedVersion .String ()),
82
- slog .F ("max_version" , maxTerraformVersion .String ()))
78
+ if installedVersion .LessThan (minTerraformVersion ) {
79
+ return details , terraformMinorVersionMismatch
83
80
}
84
81
85
- return absoluteBinary , nil
82
+ return details , nil
86
83
}
87
84
88
85
// Serve starts a dRPC server on the provided transport speaking Terraform provisioner.
89
86
func Serve (ctx context.Context , options * ServeOptions ) error {
90
87
if options .BinaryPath == "" {
91
- absoluteBinary , err := absoluteBinaryPath (ctx , options . Logger )
88
+ binaryDetails , err := systemBinary (ctx )
92
89
if err != nil {
93
90
// This is an early exit to prevent extra execution in case the context is canceled.
94
91
// It generally happens in unit tests since this method is asynchronous and
95
92
// the unit test kills the app before this is complete.
96
- if xerrors .Is (err , context .Canceled ) {
97
- return xerrors .Errorf ("absolute binary context canceled: %w" , err )
93
+ if errors .Is (err , context .Canceled ) {
94
+ return xerrors .Errorf ("system binary context canceled: %w" , err )
98
95
}
99
96
100
- options .Logger .Warn (ctx , "no usable terraform binary found, downloading to cache dir" ,
101
- slog .F ("terraform_version" , TerraformVersion .String ()),
102
- slog .F ("cache_dir" , options .CachePath ))
103
- binPath , err := Install (ctx , options .Logger , options .CachePath , TerraformVersion )
97
+ if errors .Is (err , terraformMinorVersionMismatch ) {
98
+ options .Logger .Warn (ctx , "installed terraform version too old, will download known good version to cache, or use a previously cached version" ,
99
+ slog .F ("installed_version" , binaryDetails .version .String ()),
100
+ slog .F ("min_version" , minTerraformVersion .String ()))
101
+ }
102
+
103
+ binPath , err := Install (ctx , options .Logger , options .ExternalProvisioner , options .CachePath , TerraformVersion )
104
104
if err != nil {
105
105
return xerrors .Errorf ("install terraform: %w" , err )
106
106
}
107
107
options .BinaryPath = binPath
108
108
} else {
109
- options .BinaryPath = absoluteBinary
109
+ logVersion := options .Logger .Debug
110
+ if options .ExternalProvisioner {
111
+ logVersion = options .Logger .Info
112
+ }
113
+ logVersion (ctx , "detected terraform version" ,
114
+ slog .F ("installed_version" , binaryDetails .version .String ()),
115
+ slog .F ("min_version" , minTerraformVersion .String ()),
116
+ slog .F ("max_version" , maxTerraformVersion .String ()))
117
+ // Warn if the installed version is newer than what we've decided is the max.
118
+ // We used to ignore it and download our own version but this makes it easier
119
+ // to test out newer versions of Terraform.
120
+ if binaryDetails .version .GreaterThanOrEqual (maxTerraformVersion ) {
121
+ options .Logger .Warn (ctx , "installed terraform version newer than expected, you may experience bugs" ,
122
+ slog .F ("installed_version" , binaryDetails .version .String ()),
123
+ slog .F ("max_version" , maxTerraformVersion .String ()))
124
+ }
125
+ options .BinaryPath = binaryDetails .absolutePath
110
126
}
111
127
}
112
128
if options .Tracer == nil {
0 commit comments