@@ -221,6 +221,14 @@ func resourceRegistryEcrCreate(ctx context.Context, d *schema.ResourceData, m in
221221 return diag .FromErr (err )
222222 }
223223 d .SetId (uid )
224+
225+ // Wait for sync if requested and provider_type is helm (ECR supports helm only for wait_for_sync)
226+ if providerType == "helm" && d .Get ("wait_for_sync" ) != nil && d .Get ("wait_for_sync" ).(bool ) {
227+ diags , isError := waitForOCIRegistrySyncAndSetStatus (ctx , d , uid , diags , c , schema .TimeoutCreate , "ecr" )
228+ if isError {
229+ return diags
230+ }
231+ }
224232 case "basic" :
225233 registry := toRegistryBasic (d )
226234 if err := validateRegistryCred (c , registryType , providerType , isSync , registry .Spec , nil ); err != nil {
@@ -235,29 +243,12 @@ func resourceRegistryEcrCreate(ctx context.Context, d *schema.ResourceData, m in
235243
236244 // Wait for sync if requested and provider_type is zarf or helm
237245 if (providerType == "zarf" || providerType == "helm" ) && d .Get ("wait_for_sync" ) != nil && d .Get ("wait_for_sync" ).(bool ) {
238- diagnostics , isError := waitForOciRegistrySync (ctx , d , uid , diags , c , schema .TimeoutCreate )
239- if len (diagnostics ) > 0 {
240- diags = append (diags , diagnostics ... )
241- }
242- // Fetch final sync status and set wait_for_status_message
243- syncStatus , statusErr := c .GetOciBasicRegistrySyncStatus (uid )
244- if statusErr == nil && syncStatus != nil {
245- statusMessage := ""
246- if syncStatus .Message != "" {
247- statusMessage = syncStatus .Message
248- } else if syncStatus .Status != "" {
249- statusMessage = fmt .Sprintf ("Status: %s" , syncStatus .Status )
250- }
251- if err := d .Set ("wait_for_status_message" , statusMessage ); err != nil {
252- diags = append (diags , diag .FromErr (err )... )
253- }
254- }
246+ diags , isError := waitForOCIRegistrySyncAndSetStatus (ctx , d , uid , diags , c , schema .TimeoutCreate , "basic" )
255247 if isError {
256- return diagnostics
248+ return diags
257249 }
258250 }
259251 }
260-
261252 return diags
262253}
263254
@@ -298,9 +289,6 @@ func resourceRegistryEcrRead(ctx context.Context, d *schema.ResourceData, m inte
298289 return diag .FromErr (err )
299290 }
300291
301- if err := d .Set ("wait_for_sync" , false ); err != nil {
302- return diag .FromErr (err )
303- }
304292 credentials := make ([]interface {}, 0 , 1 )
305293 acc := make (map [string ]interface {})
306294 switch * registry .Spec .Credentials .CredentialType {
@@ -311,17 +299,29 @@ func resourceRegistryEcrRead(ctx context.Context, d *schema.ResourceData, m inte
311299 case models .V1AwsCloudAccountCredentialTypeSecret :
312300 acc ["access_key" ] = registry .Spec .Credentials .AccessKey
313301 acc ["credential_type" ] = models .V1AwsCloudAccountCredentialTypeSecret
302+ // Preserve secret_key from state to avoid drift when API does not return it
303+ if currentCredsRaw := d .Get ("credentials" ); currentCredsRaw != nil {
304+ if currentCredsList , ok := currentCredsRaw .([]interface {}); ok && len (currentCredsList ) > 0 {
305+ if currentCredMap , ok := currentCredsList [0 ].(map [string ]interface {}); ok {
306+ if secretKey , exists := currentCredMap ["secret_key" ]; exists && secretKey != nil {
307+ acc ["secret_key" ] = secretKey
308+ }
309+ }
310+ }
311+ }
314312 default :
315313 errMsg := fmt .Sprintf ("Registry type %s not implemented." , * registry .Spec .Credentials .CredentialType )
316314 err = errors .New (errMsg )
317315 return diag .FromErr (err )
318316 }
319317 // tls configuration handling
320318 tlsConfig := make ([]interface {}, 0 , 1 )
321- tls := make (map [string ]interface {})
322- tls ["certificate" ] = registry .Spec .TLS .Certificate
323- tls ["insecure_skip_verify" ] = registry .Spec .TLS .InsecureSkipVerify
324- tlsConfig = append (tlsConfig , tls )
319+ if registry .Spec .TLS != nil {
320+ tls := make (map [string ]interface {})
321+ tls ["certificate" ] = registry .Spec .TLS .Certificate
322+ tls ["insecure_skip_verify" ] = registry .Spec .TLS .InsecureSkipVerify
323+ tlsConfig = append (tlsConfig , tls )
324+ }
325325 acc ["tls_config" ] = tlsConfig
326326 credentials = append (credentials , acc )
327327
@@ -367,9 +367,6 @@ func resourceRegistryEcrRead(ctx context.Context, d *schema.ResourceData, m inte
367367 return diag .FromErr (err )
368368 }
369369
370- if err := d .Set ("wait_for_sync" , false ); err != nil {
371- return diag .FromErr (err )
372- }
373370 credentials := make ([]interface {}, 0 , 1 )
374371 acc := make (map [string ]interface {})
375372 // Read the actual auth type from the API response
@@ -445,6 +442,14 @@ func resourceRegistryEcrUpdate(ctx context.Context, d *schema.ResourceData, m in
445442 if err != nil {
446443 return diag .FromErr (err )
447444 }
445+
446+ // Wait for sync if requested and provider_type is helm
447+ if providerType == "helm" && d .Get ("wait_for_sync" ) != nil && d .Get ("wait_for_sync" ).(bool ) {
448+ diags , isError := waitForOCIRegistrySyncAndSetStatus (ctx , d , d .Id (), diags , c , schema .TimeoutUpdate , "ecr" )
449+ if isError {
450+ return diags
451+ }
452+ }
448453 case "basic" :
449454 registry := toRegistryBasic (d )
450455 if err := validateRegistryCred (c , registryType , providerType , isSync , registry .Spec , nil ); err != nil {
@@ -457,25 +462,9 @@ func resourceRegistryEcrUpdate(ctx context.Context, d *schema.ResourceData, m in
457462
458463 // Wait for sync if requested and provider_type is zarf or helm
459464 if (providerType == "zarf" || providerType == "helm" ) && d .Get ("wait_for_sync" ) != nil && d .Get ("wait_for_sync" ).(bool ) {
460- diagnostics , isError := waitForOciRegistrySync (ctx , d , d .Id (), diags , c , schema .TimeoutUpdate )
461- if len (diagnostics ) > 0 {
462- diags = append (diags , diagnostics ... )
463- }
464- // Fetch final sync status and set wait_for_status_message
465- syncStatus , statusErr := c .GetOciBasicRegistrySyncStatus (d .Id ())
466- if statusErr == nil && syncStatus != nil {
467- statusMessage := ""
468- if syncStatus .Message != "" {
469- statusMessage = syncStatus .Message
470- } else if syncStatus .Status != "" {
471- statusMessage = fmt .Sprintf ("Status: %s" , syncStatus .Status )
472- }
473- if err := d .Set ("wait_for_status_message" , statusMessage ); err != nil {
474- diags = append (diags , diag .FromErr (err )... )
475- }
476- }
465+ diags , isError := waitForOCIRegistrySyncAndSetStatus (ctx , d , d .Id (), diags , c , schema .TimeoutUpdate , "basic" )
477466 if isError {
478- return diagnostics
467+ return diags
479468 }
480469 }
481470 }
@@ -607,6 +596,51 @@ func toRegistryAwsAccountCredential(regCred map[string]interface{}) *models.V1Aw
607596 return account
608597}
609598
599+ // waitForOCIRegistrySyncAndSetStatus runs the appropriate wait-for-sync for the given registry type,
600+ // then sets wait_for_status_message from the API. Returns (combined diagnostics, true if caller should return).
601+ func waitForOCIRegistrySyncAndSetStatus (ctx context.Context , d * schema.ResourceData , uid string , diags diag.Diagnostics , c * client.V1Client , timeoutType string , registryType string ) (diag.Diagnostics , bool ) {
602+ switch registryType {
603+ case "ecr" :
604+ diagnostics , isError := waitForOciEcrRegistrySync (ctx , d , uid , diags , c , timeoutType )
605+ if len (diagnostics ) > 0 {
606+ diags = append (diags , diagnostics ... )
607+ }
608+ registry , statusErr := c .GetOciEcrRegistry (uid )
609+ if statusErr == nil && registry != nil && registry .Status != nil && registry .Status .SyncStatus != nil {
610+ statusMessage := ""
611+ if registry .Status .SyncStatus .Message != "" {
612+ statusMessage = registry .Status .SyncStatus .Message
613+ } else if registry .Status .SyncStatus .Status != "" {
614+ statusMessage = fmt .Sprintf ("Status: %s" , registry .Status .SyncStatus .Status )
615+ }
616+ if err := d .Set ("wait_for_status_message" , statusMessage ); err != nil {
617+ diags = append (diags , diag .FromErr (err )... )
618+ }
619+ }
620+ return diags , isError
621+ case "basic" :
622+ diagnostics , isError := waitForOciRegistrySync (ctx , d , uid , diags , c , timeoutType )
623+ if len (diagnostics ) > 0 {
624+ diags = append (diags , diagnostics ... )
625+ }
626+ syncStatus , statusErr := c .GetOciBasicRegistrySyncStatus (uid )
627+ if statusErr == nil && syncStatus != nil {
628+ statusMessage := ""
629+ if syncStatus .Message != "" {
630+ statusMessage = syncStatus .Message
631+ } else if syncStatus .Status != "" {
632+ statusMessage = fmt .Sprintf ("Status: %s" , syncStatus .Status )
633+ }
634+ if err := d .Set ("wait_for_status_message" , statusMessage ); err != nil {
635+ diags = append (diags , diag .FromErr (err )... )
636+ }
637+ }
638+ return diags , isError
639+ default :
640+ return diags , false
641+ }
642+ }
643+
610644// waitForOciRegistrySync waits for an OCI registry to complete its synchronization
611645func waitForOciRegistrySync (ctx context.Context , d * schema.ResourceData , uid string , diags diag.Diagnostics , c * client.V1Client , timeoutType string ) (diag.Diagnostics , bool ) {
612646 stateConf := & retry.StateChangeConf {
@@ -727,3 +761,108 @@ func resourceOciRegistrySyncRefreshFunc(c *client.V1Client, uid string) retry.St
727761 }
728762 }
729763}
764+
765+ // waitForOciEcrRegistrySync waits for an OCI ECR registry to complete its synchronization by polling GetOciEcrRegistry.
766+ func waitForOciEcrRegistrySync (ctx context.Context , d * schema.ResourceData , uid string , diags diag.Diagnostics , c * client.V1Client , timeoutType string ) (diag.Diagnostics , bool ) {
767+ stateConf := & retry.StateChangeConf {
768+ Pending : []string {
769+ "InProgress" ,
770+ "Pending" ,
771+ "Unknown" ,
772+ "" ,
773+ },
774+ Target : []string {
775+ "Success" ,
776+ "Completed" ,
777+ },
778+ Refresh : resourceOciEcrRegistrySyncRefreshFunc (c , uid ),
779+ Timeout : d .Timeout (timeoutType ) - 1 * time .Minute ,
780+ MinTimeout : 10 * time .Second ,
781+ Delay : 30 * time .Second ,
782+ }
783+
784+ _ , err := stateConf .WaitForStateContext (ctx )
785+ if err != nil {
786+ var timeoutErr * retry.TimeoutError
787+ if errors .As (err , & timeoutErr ) {
788+ currentStatus := timeoutErr .LastState
789+ statusMessage := ""
790+ registry , statusErr := c .GetOciEcrRegistry (uid )
791+ if statusErr == nil && registry != nil && registry .Status != nil && registry .Status .SyncStatus != nil {
792+ if registry .Status .SyncStatus .Status != "" {
793+ currentStatus = registry .Status .SyncStatus .Status
794+ }
795+ if registry .Status .SyncStatus .Message != "" {
796+ statusMessage = fmt .Sprintf (" Message: %s" , registry .Status .SyncStatus .Message )
797+ }
798+ }
799+ if currentStatus == "" {
800+ currentStatus = "Unknown"
801+ }
802+ diags = append (diags , diag.Diagnostic {
803+ Severity : diag .Warning ,
804+ Summary : "OCI ECR registry sync timeout" ,
805+ Detail : fmt .Sprintf (
806+ "OCI ECR registry synchronization timed out after waiting for %v. Current sync status is '%s'.%s " +
807+ "The registry sync may still be in progress. You may need to increase the timeout or wait for the sync to complete manually." ,
808+ d .Timeout (timeoutType )- 1 * time .Minute , currentStatus , statusMessage ),
809+ })
810+ return diags , false
811+ }
812+
813+ registry , statusErr := c .GetOciEcrRegistry (uid )
814+ if statusErr == nil && registry != nil && registry .Status != nil && registry .Status .SyncStatus != nil {
815+ status := registry .Status .SyncStatus .Status
816+ if status == "Failed" || status == "Error" || status == "failed" || status == "error" {
817+ errorDetail := fmt .Sprintf ("OCI ECR registry synchronization failed with status '%s'." , status )
818+ if registry .Status .SyncStatus .Message != "" {
819+ errorDetail += fmt .Sprintf ("\n \n Error details: %s" , registry .Status .SyncStatus .Message )
820+ }
821+ errorDetail += "\n \n Please check the registry configuration (endpoint, credentials) and try again."
822+ diags = append (diags , diag.Diagnostic {
823+ Severity : diag .Warning ,
824+ Summary : "OCI ECR registry sync failed" ,
825+ Detail : errorDetail ,
826+ })
827+ return diags , false
828+ }
829+ }
830+
831+ return diag .FromErr (err ), true
832+ }
833+ return nil , false
834+ }
835+
836+ // resourceOciEcrRegistrySyncRefreshFunc returns a retry.StateRefreshFunc that checks the sync status of an OCI ECR registry via GetOciEcrRegistry.
837+ func resourceOciEcrRegistrySyncRefreshFunc (c * client.V1Client , uid string ) retry.StateRefreshFunc {
838+ return func () (interface {}, string , error ) {
839+ registry , err := c .GetOciEcrRegistry (uid )
840+ if err != nil {
841+ return nil , "" , err
842+ }
843+ if registry == nil || registry .Status == nil || registry .Status .SyncStatus == nil {
844+ return nil , "" , nil
845+ }
846+ syncStatus := registry .Status .SyncStatus
847+ if ! syncStatus .IsSyncSupported {
848+ return syncStatus , "Success" , nil
849+ }
850+ status := syncStatus .Status
851+ if status == "" {
852+ return syncStatus , "" , nil
853+ }
854+ switch status {
855+ case "Success" , "Completed" , "success" , "completed" :
856+ return syncStatus , "Success" , nil
857+ case "Failed" , "Error" , "failed" , "error" :
858+ if syncStatus .Message != "" {
859+ return syncStatus , status , fmt .Errorf ("registry sync failed: %s" , syncStatus .Message )
860+ }
861+ return syncStatus , status , fmt .Errorf ("registry sync failed" )
862+ case "InProgress" , "Running" , "Syncing" , "inprogress" , "running" , "syncing" :
863+ return syncStatus , "InProgress" , nil
864+ default :
865+ return syncStatus , status , nil
866+ }
867+ }
868+ }
0 commit comments