11package  git
22
33import  (
4+ 	"context" 
45	"fmt" 
56	"os" 
67	"os/exec" 
@@ -17,6 +18,7 @@ import (
1718	"k8s.io/apimachinery/pkg/runtime" 
1819	"k8s.io/apimachinery/pkg/runtime/schema" 
1920	utilruntime "k8s.io/apimachinery/pkg/util/runtime" 
21+ 	"k8s.io/apimachinery/pkg/util/wait" 
2022	"k8s.io/client-go/tools/cache" 
2123	"k8s.io/klog/v2" 
2224	"sigs.k8s.io/yaml" 
@@ -56,12 +58,12 @@ func NewGitStorage(path string) (*GitStorage, error) {
5658	return  storage , nil 
5759}
5860
59- func  (s  * GitStorage ) GC () error  {
60- 	return  s .execGit ("gc" )
61+ func  (s  * GitStorage ) GC (ctx  context. Context ) error  {
62+ 	return  s .execGit (ctx ,  "gc" )
6163}
6264
6365// handle handles different operations on git 
64- func  (s  * GitStorage ) handle (timestamp  time.Time , gvr  schema.GroupVersionResource , oldObj , obj  * unstructured.Unstructured , delete  bool ) {
66+ func  (s  * GitStorage ) handle (ctx  context. Context ,  timestamp  time.Time , gvr  schema.GroupVersionResource , oldObj , obj  * unstructured.Unstructured , delete  bool ) {
6567	filePath , content , err  :=  decodeUnstructuredObject (gvr , obj )
6668	if  err  !=  nil  {
6769		klog .Warningf ("Decoding %q failed: %v" , filePath , err )
@@ -89,14 +91,14 @@ func (s *GitStorage) handle(timestamp time.Time, gvr schema.GroupVersionResource
8991			// Add it first before removing it. 
9092			if  os .IsNotExist (err ) {
9193				klog .Info ("Observed delete of file we haven't previously observed. Adding it first." )
92- 				s .handle (timestamp , gvr , nil , obj , false )
93- 				s .handle (timestamp , gvr , nil , obj , true )
94+ 				s .handle (ctx ,  timestamp , gvr , nil , obj , false )
95+ 				s .handle (ctx ,  timestamp , gvr , nil , obj , true )
9496				return 
9597			} else  {
9698				klog .Errorf ("Error removing %q: %v" , filePath , err )
9799			}
98100		}
99- 		if  err  :=  s .commitRemove (timestamp , filePath , "unknown" , ocCommand ); err  !=  nil  {
101+ 		if  err  :=  s .commitRemove (ctx ,  timestamp , filePath , "unknown" , ocCommand ); err  !=  nil  {
100102			klog .Error (err )
101103		}
102104
@@ -119,33 +121,33 @@ func (s *GitStorage) handle(timestamp time.Time, gvr schema.GroupVersionResource
119121	switch  operation  {
120122	case  gitOpAdded :
121123		klog .Infof ("Calling commitAdd for %s" , filePath )
122- 		if  err  :=  s .commitAdd (timestamp , filePath , modifyingUser , ocCommand ); err  !=  nil  {
124+ 		if  err  :=  s .commitAdd (ctx ,  timestamp , filePath , modifyingUser , ocCommand ); err  !=  nil  {
123125			klog .Error (err )
124126		}
125127	case  gitOpModified :
126128		klog .Infof ("Calling commitModify for %s" , filePath )
127- 		if  err  :=  s .commitModify (timestamp , filePath , modifyingUser , ocCommand ); err  !=  nil  {
129+ 		if  err  :=  s .commitModify (ctx ,  timestamp , filePath , modifyingUser , ocCommand ); err  !=  nil  {
128130			klog .Error (err )
129131		}
130132	default :
131133		klog .Errorf ("unhandled case for %s: %d" , filePath , operation )
132134	}
133135}
134136
135- func  (s  * GitStorage ) OnAdd (timestamp  time.Time , gvr  schema.GroupVersionResource , obj  interface {}) {
137+ func  (s  * GitStorage ) OnAdd (ctx  context. Context ,  timestamp  time.Time , gvr  schema.GroupVersionResource , obj  interface {}) {
136138	objUnstructured  :=  obj .(* unstructured.Unstructured )
137139
138- 	s .handle (timestamp , gvr , nil , objUnstructured , false )
140+ 	s .handle (ctx ,  timestamp , gvr , nil , objUnstructured , false )
139141}
140142
141- func  (s  * GitStorage ) OnUpdate (timestamp  time.Time , gvr  schema.GroupVersionResource , oldObj , obj  interface {}) {
143+ func  (s  * GitStorage ) OnUpdate (ctx  context. Context ,  timestamp  time.Time , gvr  schema.GroupVersionResource , oldObj , obj  interface {}) {
142144	objUnstructured  :=  obj .(* unstructured.Unstructured )
143145	oldObjUnstructured  :=  oldObj .(* unstructured.Unstructured )
144146
145- 	s .handle (timestamp , gvr , oldObjUnstructured , objUnstructured , false )
147+ 	s .handle (ctx ,  timestamp , gvr , oldObjUnstructured , objUnstructured , false )
146148}
147149
148- func  (s  * GitStorage ) OnDelete (timestamp  time.Time , gvr  schema.GroupVersionResource , obj  interface {}) {
150+ func  (s  * GitStorage ) OnDelete (ctx  context. Context ,  timestamp  time.Time , gvr  schema.GroupVersionResource , obj  interface {}) {
149151	objUnstructured , ok  :=  obj .(* unstructured.Unstructured )
150152	if  ! ok  {
151153		tombstone , ok  :=  obj .(cache.DeletedFinalStateUnknown )
@@ -160,7 +162,7 @@ func (s *GitStorage) OnDelete(timestamp time.Time, gvr schema.GroupVersionResour
160162		}
161163	}
162164
163- 	s .handle (timestamp , gvr , nil , objUnstructured , true )
165+ 	s .handle (ctx ,  timestamp , gvr , nil , objUnstructured , true )
164166}
165167
166168// guessAtModifyingUsers tries to figure out who modified the resource 
@@ -223,62 +225,69 @@ func resourceFilename(gvr schema.GroupVersionResource, namespace, name string) s
223225	return  filepath .Join ("namespaces" , namespace , groupStr , gvr .Resource , name + ".yaml" )
224226}
225227
226- func  (s  * GitStorage ) execGit (args  ... string ) error  {
228+ func  (s  * GitStorage ) execGit (ctx  context. Context ,  args  ... string ) error  {
227229	// Disable automatic garbage collection to avoid racing with other processes. 
228230	args  =  append ([]string {"-c" , "gc.auto=0" }, args ... )
229231
230- 	osCommand  :=  exec .Command ("git" , args ... )
231- 	osCommand .Dir  =  s .path 
232- 	output , err  :=  osCommand .CombinedOutput ()
233- 	if  err  !=  nil  {
234- 		klog .Errorf ("Ran git %v\n %v\n \n " , args , string (output ))
235- 		return  err 
236- 	}
237- 	return  nil 
232+ 	// The git store should no longer race with itself since we 
233+ 	// disabled auto gc so this should never happen in a CI run. However, 
234+ 	// manually executed git commands may still cause errors, so we have a retry 
235+ 	// loop for robustness. 
236+ 	return  wait .PollUntilContextCancel (ctx , 1 * time .Second , true , func (ctx  context.Context ) (bool , error ) {
237+ 		osCommand  :=  exec .Command ("git" , args ... )
238+ 		osCommand .Dir  =  s .path 
239+ 		output , err  :=  osCommand .CombinedOutput ()
240+ 		if  err  !=  nil  {
241+ 			klog .Errorf ("Ran git %v\n %v\n \n " , args , string (output ))
242+ 			// Don't return the error or we'll stop polling. 
243+ 			return  false , nil 
244+ 		}
245+ 		return  true , nil 
246+ 	})
238247}
239248
240- func  (s  * GitStorage ) commit (timestamp  time.Time , path , author , commitMessage  string ) error  {
249+ func  (s  * GitStorage ) commit (ctx  context. Context ,  timestamp  time.Time , path , author , commitMessage  string ) error  {
241250	authorString  :=  fmt .
Sprintf (
"%s <[email protected] >" , 
author )
 242251	dateString  :=  timestamp .Format (time .RFC3339 )
243252
244- 	return  s .execGit ("commit" , "--author" , authorString , "--date" , dateString , "-m" , commitMessage )
253+ 	return  s .execGit (ctx ,  "commit" , "--author" , authorString , "--date" , dateString , "-m" , commitMessage )
245254}
246255
247- func  (s  * GitStorage ) commitAdd (timestamp  time.Time , path , author , ocCommand  string ) error  {
248- 	if  err  :=  s .execGit ("add" , path ); err  !=  nil  {
256+ func  (s  * GitStorage ) commitAdd (ctx  context. Context ,  timestamp  time.Time , path , author , ocCommand  string ) error  {
257+ 	if  err  :=  s .execGit (ctx ,  "add" , path ); err  !=  nil  {
249258		return  err 
250259	}
251260
252261	commitMessage  :=  fmt .Sprintf ("added %s" , ocCommand )
253- 	if  err  :=  s .commit (timestamp , path , author , commitMessage ); err  !=  nil  {
262+ 	if  err  :=  s .commit (ctx ,  timestamp , path , author , commitMessage ); err  !=  nil  {
254263		return  err 
255264	}
256265
257266	klog .Infof ("Add: %v -- %v added %v" , path , author , ocCommand )
258267	return  nil 
259268}
260269
261- func  (s  * GitStorage ) commitModify (timestamp  time.Time , path , author , ocCommand  string ) error  {
262- 	if  err  :=  s .execGit ("add" , path ); err  !=  nil  {
270+ func  (s  * GitStorage ) commitModify (ctx  context. Context ,  timestamp  time.Time , path , author , ocCommand  string ) error  {
271+ 	if  err  :=  s .execGit (ctx ,  "add" , path ); err  !=  nil  {
263272		return  err 
264273	}
265274
266275	commitMessage  :=  fmt .Sprintf ("modifed %s" , ocCommand )
267- 	if  err  :=  s .commit (timestamp , path , author , commitMessage ); err  !=  nil  {
276+ 	if  err  :=  s .commit (ctx ,  timestamp , path , author , commitMessage ); err  !=  nil  {
268277		return  err 
269278	}
270279
271280	klog .Infof ("Modified: %v -- %v updated %v" , path , author , ocCommand )
272281	return  nil 
273282}
274283
275- func  (s  * GitStorage ) commitRemove (timestamp  time.Time , path , author , ocCommand  string ) error  {
276- 	if  err  :=  s .execGit ("rm" , path ); err  !=  nil  {
284+ func  (s  * GitStorage ) commitRemove (ctx  context. Context ,  timestamp  time.Time , path , author , ocCommand  string ) error  {
285+ 	if  err  :=  s .execGit (ctx ,  "rm" , path ); err  !=  nil  {
277286		return  err 
278287	}
279288
280289	commitMessage  :=  fmt .Sprintf ("removed %s" , ocCommand )
281- 	if  err  :=  s .commit (timestamp , path , author , commitMessage ); err  !=  nil  {
290+ 	if  err  :=  s .commit (ctx ,  timestamp , path , author , commitMessage ); err  !=  nil  {
282291		return  err 
283292	}
284293
0 commit comments