@@ -2,6 +2,7 @@ package certsyncpod
22
33import (
44 "context"
5+ "fmt"
56 "os"
67 "path/filepath"
78 "reflect"
@@ -17,17 +18,20 @@ import (
1718
1819 "github.com/openshift/library-go/pkg/controller/factory"
1920 "github.com/openshift/library-go/pkg/operator/events"
20- "github.com/openshift/library-go/pkg/operator/staticpod"
2121 "github.com/openshift/library-go/pkg/operator/staticpod/controller/installer"
22+ "github.com/openshift/library-go/pkg/operator/staticpod/internal/atomicdir"
23+ "github.com/openshift/library-go/pkg/operator/staticpod/internal/dirutils"
2224)
2325
26+ const stagingDirUID = "cert-sync"
27+
2428type CertSyncController struct {
2529 destinationDir string
2630 namespace string
2731 configMaps []installer.UnrevisionedResource
2832 secrets []installer.UnrevisionedResource
2933
30- configmapGetter corev1interface.ConfigMapInterface
34+ configMapGetter corev1interface.ConfigMapInterface
3135 configMapLister v1.ConfigMapLister
3236 secretGetter corev1interface.SecretInterface
3337 secretLister v1.SecretLister
@@ -42,10 +46,10 @@ func NewCertSyncController(targetDir, targetNamespace string, configmaps, secret
4246 secrets : secrets ,
4347 eventRecorder : eventRecorder .WithComponentSuffix ("cert-sync-controller" ),
4448
45- configmapGetter : kubeClient .CoreV1 ().ConfigMaps (targetNamespace ),
49+ configMapGetter : kubeClient .CoreV1 ().ConfigMaps (targetNamespace ),
4650 configMapLister : informers .Core ().V1 ().ConfigMaps ().Lister (),
47- secretLister : informers .Core ().V1 ().Secrets ().Lister (),
4851 secretGetter : kubeClient .CoreV1 ().Secrets (targetNamespace ),
52+ secretLister : informers .Core ().V1 ().Secrets ().Lister (),
4953 }
5054
5155 return factory .New ().
@@ -60,15 +64,32 @@ func NewCertSyncController(targetDir, targetNamespace string, configmaps, secret
6064 )
6165}
6266
67+ func getStagingDir (targetDir string ) string {
68+ return filepath .Join (targetDir , "staging" , stagingDirUID )
69+ }
70+
6371func getConfigMapDir (targetDir , configMapName string ) string {
6472 return filepath .Join (targetDir , "configmaps" , configMapName )
6573}
6674
75+ func getConfigMapStagingDir (targetDir , secretName string ) string {
76+ return filepath .Join (getStagingDir (targetDir ), "configmaps" , secretName )
77+ }
78+
6779func getSecretDir (targetDir , secretName string ) string {
6880 return filepath .Join (targetDir , "secrets" , secretName )
6981}
7082
83+ func getSecretStagingDir (targetDir , secretName string ) string {
84+ return filepath .Join (getStagingDir (targetDir ), "secrets" , secretName )
85+ }
86+
7187func (c * CertSyncController ) sync (ctx context.Context , syncCtx factory.SyncContext ) error {
88+ if err := dirutils .RemoveContent (getStagingDir (c .destinationDir )); err != nil {
89+ c .eventRecorder .Warningf ("CertificateUpdateFailed" , fmt .Sprintf ("Failed to prune staging directory: %v" , err ))
90+ return err
91+ }
92+
7293 errors := []error {}
7394
7495 klog .Infof ("Syncing configmaps: %v" , c .configMaps )
@@ -88,7 +109,7 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
88109 }
89110
90111 // Check with the live call it is really missing
91- configMap , err = c .configmapGetter .Get (ctx , cm .Name , metav1.GetOptions {})
112+ configMap , err = c .configMapGetter .Get (ctx , cm .Name , metav1.GetOptions {})
92113 if err == nil {
93114 klog .Infof ("Caches are stale. They don't see configmap '%s/%s', yet it is present" , configMap .Namespace , configMap .Name )
94115 // We will get re-queued when we observe the change
@@ -114,8 +135,9 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
114135 }
115136
116137 contentDir := getConfigMapDir (c .destinationDir , cm .Name )
138+ stagingDir := getConfigMapStagingDir (c .destinationDir , cm .Name )
117139
118- data := map [string ]string {}
140+ data := make ( map [string ]string , len ( configMap . Data ))
119141 for filename := range configMap .Data {
120142 fullFilename := filepath .Join (contentDir , filename )
121143
@@ -138,7 +160,7 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
138160 klog .V (2 ).Infof ("Syncing updated configmap '%s/%s'." , configMap .Namespace , configMap .Name )
139161
140162 // We need to do a live get here so we don't overwrite a newer file with one from a stale cache
141- configMap , err = c .configmapGetter .Get (ctx , configMap .Name , metav1.GetOptions {})
163+ configMap , err = c .configMapGetter .Get (ctx , configMap .Name , metav1.GetOptions {})
142164 if err != nil {
143165 // Even if the error is not exists we will act on it when caches catch up
144166 c .eventRecorder .Warningf ("CertificateUpdateFailed" , "Failed getting configmap: %s/%s: %v" , c .namespace , cm .Name , err )
@@ -152,27 +174,12 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
152174 continue
153175 }
154176
155- klog .Infof ("Creating directory %q ..." , contentDir )
156- if err := os .MkdirAll (contentDir , 0755 ); err != nil && ! os .IsExist (err ) {
157- c .eventRecorder .Warningf ("CertificateUpdateFailed" , "Failed creating directory for configmap: %s/%s: %v" , configMap .Namespace , configMap .Name , err )
158- errors = append (errors , err )
159- continue
160- }
161- for filename , content := range configMap .Data {
162- fullFilename := filepath .Join (contentDir , filename )
163- // if the existing is the same, do nothing
164- if reflect .DeepEqual (data [fullFilename ], content ) {
165- continue
166- }
167-
168- klog .Infof ("Writing configmap manifest %q ..." , fullFilename )
169- if err := staticpod .WriteFileAtomic ([]byte (content ), 0644 , fullFilename ); err != nil {
170- c .eventRecorder .Warningf ("CertificateUpdateFailed" , "Failed writing file for configmap: %s/%s: %v" , configMap .Namespace , configMap .Name , err )
171- errors = append (errors , err )
172- continue
173- }
177+ files := make (map [string ][]byte , len (configMap .Data ))
178+ for k , v := range configMap .Data {
179+ files [k ] = []byte (v )
174180 }
175- c .eventRecorder .Eventf ("CertificateUpdated" , "Wrote updated configmap: %s/%s" , configMap .Namespace , configMap .Name )
181+ // XXX: Are these permissions correct?
182+ errors = append (errors , syncDirectory (c .eventRecorder , "configmap" , configMap .ObjectMeta , contentDir , 0755 , stagingDir , files , 0644 ))
176183 }
177184
178185 klog .Infof ("Syncing secrets: %v" , c .secrets )
@@ -219,8 +226,9 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
219226 }
220227
221228 contentDir := getSecretDir (c .destinationDir , s .Name )
229+ stagingDir := getSecretStagingDir (c .destinationDir , s .Name )
222230
223- data := map [string ][]byte {}
231+ data := make ( map [string ][]byte , len ( secret . Data ))
224232 for filename := range secret .Data {
225233 fullFilename := filepath .Join (contentDir , filename )
226234
@@ -257,29 +265,22 @@ func (c *CertSyncController) sync(ctx context.Context, syncCtx factory.SyncConte
257265 continue
258266 }
259267
260- klog .Infof ("Creating directory %q ..." , contentDir )
261- if err := os .MkdirAll (contentDir , 0755 ); err != nil && ! os .IsExist (err ) {
262- c .eventRecorder .Warningf ("CertificateUpdateFailed" , "Failed creating directory for secret: %s/%s: %v" , secret .Namespace , secret .Name , err )
263- errors = append (errors , err )
264- continue
265- }
266- for filename , content := range secret .Data {
267- // TODO fix permissions
268- fullFilename := filepath .Join (contentDir , filename )
269- // if the existing is the same, do nothing
270- if reflect .DeepEqual (data [fullFilename ], content ) {
271- continue
272- }
273-
274- klog .Infof ("Writing secret manifest %q ..." , fullFilename )
275- if err := staticpod .WriteFileAtomic (content , 0600 , fullFilename ); err != nil {
276- c .eventRecorder .Warningf ("CertificateUpdateFailed" , "Failed writing file for secret: %s/%s: %v" , secret .Namespace , secret .Name , err )
277- errors = append (errors , err )
278- continue
279- }
280- }
281- c .eventRecorder .Eventf ("CertificateUpdated" , "Wrote updated secret: %s/%s" , secret .Namespace , secret .Name )
268+ errors = append (errors , syncDirectory (c .eventRecorder , "secret" , secret .ObjectMeta , contentDir , 0700 , stagingDir , secret .Data , 0600 ))
282269 }
283-
284270 return utilerrors .NewAggregate (errors )
285271}
272+
273+ func syncDirectory (
274+ eventRecorder events.Recorder ,
275+ typeName string , o metav1.ObjectMeta ,
276+ targetDir string , targetDirPerm os.FileMode , stagingDir string ,
277+ files map [string ][]byte , filePerm os.FileMode ,
278+ ) error {
279+ if err := atomicdir .Sync (targetDir , targetDirPerm , stagingDir , files , filePerm ); err != nil {
280+ err = fmt .Errorf ("failed to sync %s %s/%s (directory %q): %w" , typeName , o .Name , o .Namespace , targetDir , err )
281+ eventRecorder .Warning ("CertificateUpdateFailed" , err .Error ())
282+ return err
283+ }
284+ eventRecorder .Eventf ("CertificateUpdated" , "Wrote updated %s: %s/%s" , typeName , o .Namespace , o .Name )
285+ return nil
286+ }
0 commit comments