5
5
package upgrade
6
6
7
7
import (
8
+ "bufio"
9
+ "context"
8
10
"fmt"
9
11
"os"
12
+ "os/exec"
13
+ "path/filepath"
14
+ "runtime"
15
+ "strings"
10
16
"testing"
17
+ "time"
11
18
12
19
"github.com/stretchr/testify/assert"
13
20
"github.com/stretchr/testify/require"
@@ -52,11 +59,11 @@ func Test_markUpgradeLocking(t *testing.T) {
52
59
}{
53
60
{
54
61
name : "Lock file is created when writing update marker" , args : args {
55
- agent : newAgent456 ,
56
- previousAgent : prevAgent123 ,
57
- action : nil ,
58
- upgradeDetails : nil ,
59
- },
62
+ agent : newAgent456 ,
63
+ previousAgent : prevAgent123 ,
64
+ action : nil ,
65
+ upgradeDetails : nil ,
66
+ },
60
67
afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
61
68
assert .FileExists (t , markerFilePath (dataDir ), "Update marker file must exist" )
62
69
assert .FileExists (t , markerFilePath (dataDir )+ ".lock" , "Update marker lock file must exist" )
@@ -69,11 +76,11 @@ func Test_markUpgradeLocking(t *testing.T) {
69
76
},
70
77
{
71
78
name : "Update marker is re-lockable after writing" , args : args {
72
- agent : newAgent456 ,
73
- previousAgent : prevAgent123 ,
74
- action : nil ,
75
- upgradeDetails : nil ,
76
- },
79
+ agent : newAgent456 ,
80
+ previousAgent : prevAgent123 ,
81
+ action : nil ,
82
+ upgradeDetails : nil ,
83
+ },
77
84
afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
78
85
assert .FileExists (t , markerFilePath (dataDir ), "Update marker file must exist" )
79
86
assert .FileExists (t , markerFilePath (dataDir )+ ".lock" , "Update marker lock file must exist" )
@@ -88,11 +95,11 @@ func Test_markUpgradeLocking(t *testing.T) {
88
95
},
89
96
{
90
97
name : "Update marker creation should not fail if marker is already locked by the same process" , args : args {
91
- agent : newAgent456 ,
92
- previousAgent : prevAgent123 ,
93
- action : nil ,
94
- upgradeDetails : nil ,
95
- },
98
+ agent : newAgent456 ,
99
+ previousAgent : prevAgent123 ,
100
+ action : nil ,
101
+ upgradeDetails : nil ,
102
+ },
96
103
beforeUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
97
104
// write some fake data in update marker file
98
105
updateMarkerFilePath := markerFilePath (dataDir )
@@ -115,6 +122,62 @@ func Test_markUpgradeLocking(t *testing.T) {
115
122
},
116
123
wantErr : assert .NoError ,
117
124
},
125
+ {
126
+ name : "Update marker creation should fail if marker is already locked by another process" , args : args {
127
+ agent : newAgent456 ,
128
+ previousAgent : prevAgent123 ,
129
+ action : nil ,
130
+ upgradeDetails : nil ,
131
+ },
132
+ beforeUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
133
+ // write some fake data in update marker file
134
+ updateMarkerFilePath := markerFilePath (dataDir )
135
+ err := os .WriteFile (updateMarkerFilePath , []byte ("this: is not a real update marker" ), 0o664 )
136
+ require .NoError (t , err , "error creating fake update marker" )
137
+
138
+ // lock the fake update marker using an external process
139
+ lockFilePath := updateMarkerFilePath + ".lock"
140
+ cmdCancel , lockFileCmd := createFileLockerCmd (t , lockFilePath )
141
+
142
+ fileLockerStdErr , err := lockFileCmd .StderrPipe ()
143
+ require .NoError (t , err , "Error getting stderr pipe from filelocker" )
144
+
145
+ fileLockedCh := make (chan struct {})
146
+
147
+ // consume stderr to check for locking
148
+ go func () {
149
+ scanner := bufio .NewScanner (fileLockerStdErr )
150
+ for scanner .Scan () {
151
+ line := scanner .Text ()
152
+ if strings .Contains (line , "Acquired lock on file" ) {
153
+ fileLockedCh <- struct {}{}
154
+ }
155
+ }
156
+ }()
157
+
158
+ err = lockFileCmd .Start ()
159
+ require .NoError (t , err , "running filelocker should not fail" )
160
+
161
+ t .Cleanup (func () {
162
+ cmdCancel ()
163
+ _ = lockFileCmd .Wait ()
164
+ })
165
+
166
+ select {
167
+ case <- fileLockedCh :
168
+ // file was locked from the external process: all good
169
+ t .Log ("external filelocker acquired the lock!" )
170
+ case <- time .After (30 * time .Second ):
171
+ t .Fatalf ("timed out waiting for file locker to lock the file" )
172
+ }
173
+ },
174
+ afterUpdateMarkerCreation : func (t * testing.T , dataDir string ) {
175
+ // verify we can't read the actual update marker since it's still locked
176
+ _ , err := LoadMarker (dataDir )
177
+ require .Error (t , err , "loading update marker should fail" )
178
+ },
179
+ wantErr : assert .Error ,
180
+ },
118
181
}
119
182
for _ , tt := range tests {
120
183
t .Run (tt .name , func (t * testing.T ) {
@@ -131,6 +194,24 @@ func Test_markUpgradeLocking(t *testing.T) {
131
194
}
132
195
}
133
196
197
+ func createFileLockerCmd (t * testing.T , lockFilePath string ) (context.CancelFunc , * exec.Cmd ) {
198
+ executableName := "filelocker"
199
+ if runtime .GOOS == "windows" {
200
+ executableName += ".exe"
201
+ }
202
+ filelockerExecutablePath := filepath .Join ("test" , "filelocker" , executableName )
203
+ require .FileExistsf (
204
+ t ,
205
+ filelockerExecutablePath ,
206
+ "filelocker executable %s should exist. Please ensure that mage build:testbinaries has been executed." ,
207
+ filelockerExecutablePath ,
208
+ )
209
+
210
+ cmdCtx , cmdCancel := context .WithCancel (t .Context ())
211
+ lockFileCmd := exec .CommandContext (cmdCtx , filelockerExecutablePath , "-lockfile" , lockFilePath )
212
+ return cmdCancel , lockFileCmd
213
+ }
214
+
134
215
func checkUpgradeMarker (t * testing.T , updateMarker * UpdateMarker , prevAgent agentInstall , newAgent agentInstall ) {
135
216
t .Helper ()
136
217
require .NotNil (t , updateMarker , "update marker should not be nil" )
0 commit comments