@@ -23,35 +23,54 @@ type FS interface {
2323	EnsureDir (dirname  string ) error 
2424	Write (encoder  runtime.Encoder , filepath  string , obj  runtime.Object , storageVersion  uint64 ) error 
2525	Read (decoder  runtime.Decoder , path  string , newFunc  func () runtime.Object ) (runtime.Object , error )
26- 	VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) error 
26+ 	VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) ( uint64 ,  error ) 
2727}
2828
2929type  RealFS  struct  {
30+ 	mu   sync.Mutex 
31+ 	rev  uint64 
3032}
3133
32- var  _  FS  =  RealFS {}
34+ func  NewRealFS () * RealFS  {
35+ 	return  & RealFS {rev : 1 }
36+ }
3337
34- func  (fs  RealFS ) Remove (filepath  string ) error  {
38+ var  _  FS  =  & RealFS {}
39+ 
40+ func  (fs  * RealFS ) Remove (filepath  string ) error  {
41+ 	fs .mu .Lock ()
42+ 	defer  fs .mu .Unlock ()
43+ 	_  =  fs .incrementRev ()
3544	return  os .Remove (filepath )
3645}
3746
38- func  (fs  RealFS ) Exists (filepath  string ) bool  {
47+ func  (fs  * RealFS ) Exists (filepath  string ) bool  {
48+ 	fs .mu .Lock ()
49+ 	defer  fs .mu .Unlock ()
3950	_ , err  :=  os .Stat (filepath )
4051	return  err  ==  nil 
4152}
4253
43- func  (fs  RealFS ) EnsureDir (dirname  string ) error  {
44- 	if  ! fs .Exists (dirname ) {
54+ func  (fs  * RealFS ) EnsureDir (dirname  string ) error  {
55+ 	fs .mu .Lock ()
56+ 	defer  fs .mu .Unlock ()
57+ 	_ , err  :=  os .Stat (dirname )
58+ 	if  err  !=  nil  {
4559		return  os .MkdirAll (dirname , 0700 )
4660	}
4761	return  nil 
4862}
4963
50- func  (fs  RealFS ) Write (encoder  runtime.Encoder , filepath  string , obj  runtime.Object , storageVersion  uint64 ) error  {
51- 	// TODO(milas): use storageVersion to ensure we don't perform stale writes 
64+ func  (fs  * RealFS ) Write (encoder  runtime.Encoder , filepath  string , obj  runtime.Object , storageVersion  uint64 ) error  {
65+ 	fs .mu .Lock ()
66+ 	defer  fs .mu .Unlock ()
67+ 	rev  :=  fs .incrementRev ()
68+ 
69+ 	// TODO(milas): Currently we don't have optimistic concurrency at all. 
70+ 	// Each write has last-one-wins semantics. 
5271	// 	(currently, this isn't a critical priority as our use cases that rely 
5372	// 	on RealFS do not have simultaneous writers) 
54- 	if  err  :=  setResourceVersion (obj , storageVersion + 1 ); err  !=  nil  {
73+ 	if  err  :=  setResourceVersion (obj , rev ); err  !=  nil  {
5574		return  err 
5675	}
5776
@@ -62,11 +81,17 @@ func (fs RealFS) Write(encoder runtime.Encoder, filepath string, obj runtime.Obj
6281	return  ioutil .WriteFile (filepath , buf .Bytes (), 0600 )
6382}
6483
65- func  (fs  RealFS ) Read (decoder  runtime.Decoder , path  string , newFunc  func () runtime.Object ) (runtime.Object , error ) {
84+ func  (fs  * RealFS ) Read (decoder  runtime.Decoder , path  string , newFunc  func () runtime.Object ) (runtime.Object , error ) {
85+ 	fs .mu .Lock ()
6686	content , err  :=  ioutil .ReadFile (filepath .Clean (path ))
87+ 	fs .mu .Unlock ()
6788	if  err  !=  nil  {
6889		return  nil , err 
6990	}
91+ 	return  fs .decode (decoder , newFunc , content )
92+ }
93+ 
94+ func  (fs  * RealFS ) decode (decoder  runtime.Decoder , newFunc  func () runtime.Object , content  []byte ) (runtime.Object , error ) {
7095	newObj  :=  newFunc ()
7196	decodedObj , _ , err  :=  decoder .Decode (content , nil , newObj )
7297	if  err  !=  nil  {
@@ -75,8 +100,10 @@ func (fs RealFS) Read(decoder runtime.Decoder, path string, newFunc func() runti
75100	return  decodedObj , nil 
76101}
77102
78- func  (fs  RealFS ) VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) error  {
79- 	return  filepath .Walk (dirname , func (path  string , info  os.FileInfo , err  error ) error  {
103+ func  (fs  * RealFS ) VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) (uint64 , error ) {
104+ 	fs .mu .Lock ()
105+ 	defer  fs .mu .Unlock ()
106+ 	err  :=  filepath .Walk (dirname , func (path  string , info  os.FileInfo , err  error ) error  {
80107		if  err  !=  nil  {
81108			return  err 
82109		}
@@ -86,12 +113,28 @@ func (fs RealFS) VisitDir(dirname string, newFunc func() runtime.Object, codec r
86113		if  ! strings .HasSuffix (info .Name (), ".json" ) {
87114			return  nil 
88115		}
89- 		newObj , err  :=  fs .Read (codec , path , newFunc )
116+ 		content , err  :=  ioutil .ReadFile (filepath .Clean (path ))
117+ 		if  err  !=  nil  {
118+ 			return  err 
119+ 		}
120+ 		newObj , err  :=  fs .decode (codec , newFunc , content )
90121		if  err  !=  nil  {
91122			return  err 
92123		}
93124		return  visitFunc (path , newObj )
94125	})
126+ 	if  err  !=  nil  {
127+ 		return  0 , err 
128+ 	}
129+ 	return  fs .rev , nil 
130+ }
131+ 
132+ // incrementRev increases the revision counter and returns the new value. 
133+ // 
134+ // mu must be held. 
135+ func  (fs  * RealFS ) incrementRev () uint64  {
136+ 	fs .rev ++ 
137+ 	return  fs .rev 
95138}
96139
97140// An in-memory structure that pretends to be a filesystem, 
@@ -105,6 +148,7 @@ type MemoryFS struct {
105148func  NewMemoryFS () * MemoryFS  {
106149	return  & MemoryFS {
107150		dir : make (map [string ]interface {}),
151+ 		rev : 1 ,
108152	}
109153}
110154
@@ -289,27 +333,29 @@ func (fs *MemoryFS) decodeBuffer(decoder runtime.Decoder, rawObj versionedData,
289333}
290334
291335// Walk the directory, reading all objects in it. 
292- func  (fs  * MemoryFS ) VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) error  {
336+ // Return the ResourceVersion of when we did the read. 
337+ func  (fs  * MemoryFS ) VisitDir (dirname  string , newFunc  func () runtime.Object , codec  runtime.Decoder , visitFunc  func (string , runtime.Object ) error ) (uint64 , error ) {
293338	fs .mu .Lock ()
294339	keyPaths , buffers , err  :=  fs .readDir (dirname )
340+ 	version  :=  fs .rev 
295341	fs .mu .Unlock ()
296342	if  err  !=  nil  {
297- 		return  err 
343+ 		return  0 ,  err 
298344	}
299345
300346	// Do decoding and visitation outside the lock. 
301347	for  i , keyPath  :=  range  keyPaths  {
302348		buf  :=  buffers [i ]
303349		obj , err  :=  fs .decodeBuffer (codec , buf , newFunc )
304350		if  err  !=  nil  {
305- 			return  err 
351+ 			return  0 ,  err 
306352		}
307353		err  =  visitFunc (keyPath , obj )
308354		if  err  !=  nil  {
309- 			return  err 
355+ 			return  0 ,  err 
310356		}
311357	}
312- 	return  nil 
358+ 	return  version ,  nil 
313359}
314360
315361// Internal helper for reading the directory. Must hold the mutex. 
0 commit comments