7
7
"encoding/json"
8
8
"fmt"
9
9
"os"
10
+ "path/filepath"
10
11
"strings"
11
12
12
13
"github.com/opencontainers/go-digest"
@@ -49,7 +50,7 @@ func newCredHelperBinary(fp path.Finch, fs afero.Fs, cmdCreator command.Creator,
49
50
50
51
// updateConfigFile updates the config.json file to configure the credential helper.
51
52
func updateConfigFile (bin * credhelperbin ) error {
52
- cfgPath := fmt . Sprintf ( "%s%s" , bin .hcfg .finchPath , "config.json" )
53
+ cfgPath := filepath . Join ( bin .hcfg .finchPath , "config.json" )
53
54
binCfgName := bin .credHelperConfigName ()
54
55
fileExists , err := afero .Exists (bin .fs , cfgPath )
55
56
if err != nil {
@@ -71,6 +72,7 @@ func updateConfigFile(bin *credhelperbin) error {
71
72
if err != nil {
72
73
return err
73
74
}
75
+ defer fileRead .Close () //nolint:errcheck // closing the file
74
76
bytes , err := afero .ReadAll (fileRead )
75
77
if err != nil {
76
78
return err
@@ -81,8 +83,7 @@ func updateConfigFile(bin *credhelperbin) error {
81
83
return err
82
84
}
83
85
credsStore := cfg .CredentialsStore
84
- defer fileRead .Close () //nolint:errcheck // closing the file
85
- if strings .Compare (credsStore , binCfgName ) != 0 {
86
+ if credsStore != binCfgName {
86
87
file , err := bin .fs .OpenFile (cfgPath , os .O_RDWR | os .O_CREATE | os .O_TRUNC , 0o755 )
87
88
if err != nil {
88
89
return err
@@ -103,6 +104,38 @@ func updateConfigFile(bin *credhelperbin) error {
103
104
return nil
104
105
}
105
106
107
+ func (bin * credhelperbin ) binaryInstalled () (bool , error ) {
108
+ return binaryInstalled (bin )
109
+ }
110
+
111
+ func (bin * credhelperbin ) configFileInstalled () (bool , error ) {
112
+ cfgPath := filepath .Join (bin .hcfg .finchPath , "config.json" )
113
+ binCfgName := bin .credHelperConfigName ()
114
+
115
+ if fileExists , err := afero .Exists (bin .fs , cfgPath ); err != nil {
116
+ return false , err
117
+ } else if ! fileExists {
118
+ return false , nil
119
+ }
120
+
121
+ fileRead , err := bin .fs .Open (cfgPath )
122
+ if err != nil {
123
+ return false , err
124
+ }
125
+ bytes , err := afero .ReadAll (fileRead )
126
+ if err != nil {
127
+ return false , err
128
+ }
129
+ var cfg configfile.ConfigFile
130
+ err = json .Unmarshal (bytes , & cfg )
131
+ if err != nil {
132
+ return false , err
133
+ }
134
+ credsStore := cfg .CredentialsStore
135
+ defer fileRead .Close () //nolint:errcheck // closing the file
136
+ return credsStore == binCfgName , nil
137
+ }
138
+
106
139
// credHelperConfigName returns the name of the credential helper binary that will be used
107
140
// inside the config.json.
108
141
func (bin * credhelperbin ) credHelperConfigName () string {
@@ -111,85 +144,126 @@ func (bin *credhelperbin) credHelperConfigName() string {
111
144
112
145
// fullInstallPath returns the full installation path of the credential helper binary.
113
146
func (bin * credhelperbin ) fullInstallPath () string {
114
- return fmt . Sprintf ( "%s%s" , bin .hcfg .installFolder , bin .hcfg .binaryName )
147
+ return filepath . Join ( bin .hcfg .installFolder , bin .hcfg .binaryName )
115
148
}
116
149
117
150
// Installed checks if the credential helper already exists in the specified
118
151
// folder and checks if the hash of the installed binary is correct.
119
152
func (bin * credhelperbin ) Installed () bool {
120
- dirExists , err := afero .DirExists (bin .fs , bin .hcfg .installFolder )
121
- if err != nil {
122
- bin .l .Errorf ("failed to get status of credential helper directory: %v" , err )
123
- return false
124
- }
125
- if ! dirExists {
126
- return false
127
- }
128
- fileExists , err := afero .Exists (bin .fs , bin .fullInstallPath ())
129
- if err != nil {
130
- bin .l .Errorf ("failed to get status of credential helper binary: %v" , err )
131
- return false
132
- }
133
- if ! fileExists {
134
- return false
135
- }
136
- file , err := bin .fs .Open (bin .fullInstallPath ())
137
- if err != nil {
153
+ if binInstalled , err := bin .binaryInstalled (); err != nil {
138
154
bin .l .Error (err )
139
155
return false
156
+ } else if ! binInstalled {
157
+ return false
140
158
}
141
- defer file .Close () //nolint:errcheck // closing the file
142
- hash , err := digest .FromReader (file )
143
- if err != nil {
159
+
160
+ if cfgInstalled , err := bin .configFileInstalled (); err != nil {
144
161
bin .l .Error (err )
145
162
return false
146
- }
147
- if strings .Compare (hash .String (), bin .hcfg .hash ) != 0 {
148
- bin .l .Info ("Hash of the installed credential helper binary does not match" )
149
- err := bin .fs .Remove (bin .fullInstallPath ())
150
- if err != nil {
151
- bin .l .Error (err )
152
- }
163
+ } else if ! cfgInstalled {
153
164
return false
154
165
}
166
+
155
167
return true
156
168
}
157
169
158
170
// Install installs and configures the specified credential helper.
159
171
func (bin * credhelperbin ) Install () error {
160
- if bin .helper == "" {
161
- return nil
162
- }
163
- if strings .Compare (bin .helper , bin .credHelperConfigName ()) != 0 {
164
- return nil
165
- }
166
- credHelperName := strings .ReplaceAll (bin .credHelperConfigName (), "-login" , "" )
167
- bin .l .Infof ("Installing %s credential helper" , credHelperName )
168
- mkdirCmd := bin .cmdCreator .Create ("mkdir" , "-p" , bin .hcfg .installFolder )
169
- _ , err := mkdirCmd .Output ()
172
+ binInstalled , err := bin .binaryInstalled ()
170
173
if err != nil {
171
- return fmt . Errorf ( "error creating installation directory %s, err: %w" , bin . hcfg . installFolder , err )
174
+ return err
172
175
}
173
176
174
- curlCmd := bin .cmdCreator .Create ("curl" , "--retry" , "5" , "--retry-max-time" , "30" , "--url" ,
175
- bin .hcfg .credHelperURL , "--output" , bin .fullInstallPath ())
177
+ if ! binInstalled {
178
+ if bin .helper == "" {
179
+ return nil
180
+ }
181
+ if bin .helper != bin .credHelperConfigName () {
182
+ return nil
183
+ }
184
+ credHelperName := strings .ReplaceAll (bin .credHelperConfigName (), "-login" , "" )
185
+ bin .l .Infof ("Installing %s credential helper" , credHelperName )
186
+ if err := bin .fs .MkdirAll (bin .hcfg .installFolder , 0o700 ); err != nil {
187
+ return fmt .Errorf ("error creating installation directory %s, err: %w" , bin .hcfg .installFolder , err )
188
+ }
176
189
177
- _ , err = curlCmd .Output ()
178
- if err != nil {
179
- return fmt .Errorf ("error installing binary %s, err: %w" , bin .hcfg .binaryName , err )
190
+ curlCmd := bin .cmdCreator .Create (
191
+ "curl" ,
192
+ "--retry" ,
193
+ "5" ,
194
+ "--retry-max-time" ,
195
+ "30" ,
196
+ "--url" ,
197
+ bin .hcfg .credHelperURL ,
198
+ "--output" ,
199
+ bin .fullInstallPath (),
200
+ )
201
+
202
+ if _ , err = curlCmd .Output (); err != nil {
203
+ return fmt .Errorf ("error installing binary %s, err: %w" , bin .hcfg .binaryName , err )
204
+ }
205
+ if err := bin .fs .Chmod (bin .fullInstallPath (), 0o700 ); err != nil {
206
+ return err
207
+ }
180
208
}
181
- err = bin .fs .Chmod (bin .fullInstallPath (), 0o755 )
209
+
210
+ cfgInstalled , err := bin .configFileInstalled ()
182
211
if err != nil {
183
212
return err
184
213
}
185
- err = updateConfigFile (bin )
186
- if err != nil {
187
- return err
214
+
215
+ if ! cfgInstalled {
216
+ if err := updateConfigFile (bin ); err != nil {
217
+ return err
218
+ }
188
219
}
220
+
189
221
return nil
190
222
}
191
223
192
224
// RequiresRoot returns whether the installation of the binary needs root permissions.
193
225
func (bin * credhelperbin ) RequiresRoot () bool {
194
226
return false
195
227
}
228
+
229
+ // Using a var function allows overriding during testing.
230
+ // This is needed because the curl command directly outputs to a file, but binaryInstalled deletes
231
+ // any incorrect file that might exist at the fullInstallPath.
232
+ // This means that the mocking method that is typically used to mock filesystem will get the file it
233
+ // creates deleted, and then, since cmd.Output() is what is writing the new/correct file, and there's
234
+ // no opportunity to mock it or insert the file after it runs, the code that expects the file to exist
235
+ // then errors out because it was deleted by binaryInstalled.
236
+ var binaryInstalled = func (bin * credhelperbin ) (bool , error ) {
237
+ dirExists , err := afero .DirExists (bin .fs , bin .hcfg .installFolder )
238
+ if err != nil {
239
+ return false , fmt .Errorf ("failed to get status of credential helper directory: %w" , err )
240
+ }
241
+ if ! dirExists {
242
+ return false , nil
243
+ }
244
+ fileExists , err := afero .Exists (bin .fs , bin .fullInstallPath ())
245
+ if err != nil {
246
+ return false , fmt .Errorf ("failed to get status of credential helper binary: %w" , err )
247
+ }
248
+ if ! fileExists {
249
+ return false , nil
250
+ }
251
+ file , err := bin .fs .Open (bin .fullInstallPath ())
252
+ if err != nil {
253
+ return false , fmt .Errorf ("failed to open cred helper binary: %w" , err )
254
+ }
255
+ defer file .Close () //nolint:errcheck // closing the file
256
+ hash , err := digest .FromReader (file )
257
+ if err != nil {
258
+ return false , fmt .Errorf ("failed to get cred helper binary hash: %w" , err )
259
+ }
260
+ if hash .String () != bin .hcfg .hash {
261
+ bin .l .Info ("Hash of the installed credential helper binary does not match" )
262
+ if err := bin .fs .Remove (bin .fullInstallPath ()); err != nil {
263
+ return false , fmt .Errorf ("failed to remove mismatched cred helper binary: %w" , err )
264
+ }
265
+ return false , nil
266
+ }
267
+
268
+ return true , nil
269
+ }
0 commit comments