@@ -426,15 +426,99 @@ func isBroken(st *state.State, snapName string) (bool, error) {
426426 return false , nil
427427}
428428
429+ func cloneConnState (connState * schema.ConnState ) * schema.ConnState {
430+ clone := * connState
431+
432+ cloneAttrs := func (attrs map [string ]any ) map [string ]any {
433+ if attrs == nil {
434+ return nil
435+ }
436+ return utils .CopyAttributes (attrs )
437+ }
438+
439+ clone .StaticPlugAttrs = cloneAttrs (connState .StaticPlugAttrs )
440+ clone .DynamicPlugAttrs = cloneAttrs (connState .DynamicPlugAttrs )
441+ clone .StaticSlotAttrs = cloneAttrs (connState .StaticSlotAttrs )
442+ clone .DynamicSlotAttrs = cloneAttrs (connState .DynamicSlotAttrs )
443+
444+ return & clone
445+ }
446+
447+ // saveChangedConnectionsForSetupProfilesRestore records original connection
448+ // states for setup-profiles do tasks whose undo can restore them.
449+ func saveChangedConnectionsForSetupProfilesRestore (task * state.Task , instanceName string , changedConns map [string ]* schema.ConnState ) error {
450+ if len (changedConns ) == 0 {
451+ return nil
452+ }
453+
454+ // undo setup-profiles also calls this code while rebuilding old profiles,
455+ // but restoration data should only come from the original do path
456+ if task .Status () == state .UndoingStatus {
457+ return nil
458+ }
459+
460+ // if this isn't the setup-profiles task that is going to handle the undo,
461+ // then we don't need to keep track of these on the task
462+ if ! shouldUndoSetupProfiles (task , instanceName ) {
463+ return nil
464+ }
465+
466+ var originalConns map [string ]* schema.ConnState
467+ err := task .Get ("original-connection-states" , & originalConns )
468+ if err != nil && ! errors .Is (err , state .ErrNoState ) {
469+ return err
470+ }
471+ if originalConns == nil {
472+ originalConns = make (map [string ]* schema.ConnState )
473+ }
474+
475+ for connID , connState := range changedConns {
476+ if originalConns [connID ] != nil {
477+ // a setup-profiles task can be retried after saving original states
478+ // and unlocking for backend setup. keep the original snapshot.
479+ continue
480+ }
481+ originalConns [connID ] = connState
482+ }
483+
484+ task .Set ("original-connection-states" , originalConns )
485+
486+ return nil
487+ }
488+
489+ // restoreConnectionsForSetupProfiles restores connection states saved on a
490+ // setup-profiles task.
491+ func restoreConnectionsForSetupProfiles (task * state.Task ) error {
492+ var originalConns map [string ]* schema.ConnState
493+ err := task .Get ("original-connection-states" , & originalConns )
494+ if errors .Is (err , state .ErrNoState ) {
495+ return nil
496+ }
497+ if err != nil {
498+ return err
499+ }
500+
501+ conns , err := getConns (task .State ())
502+ if err != nil {
503+ return err
504+ }
505+ for connID , connState := range originalConns {
506+ conns [connID ] = cloneConnState (connState )
507+ }
508+ setConns (task .State (), conns )
509+ return nil
510+ }
511+
429512// reloadConnections reloads connections stored in the state in the repository.
430513// Using non-empty snapName the operation can be scoped to connections
431514// affecting a given snap.
432515//
433- // The return value is the list of affected snap names and their connection IDs.
434- func (m * InterfaceManager ) reloadConnections (snapName string ) (reloadedConnectionIDs []string , err error ) {
516+ // The return value is the list of affected snap names and their connection IDs,
517+ // plus the original connection states that were changed.
518+ func (m * InterfaceManager ) reloadConnections (snapName string ) (reloadedConnectionIDs []string , changedConns map [string ]* schema.ConnState , err error ) {
435519 conns , err := getConns (m .state )
436520 if err != nil {
437- return nil , err
521+ return nil , nil , err
438522 }
439523
440524 var policyChecker interfaces.PolicyFunc
@@ -445,21 +529,22 @@ func (m *InterfaceManager) reloadConnections(snapName string) (reloadedConnectio
445529 if errors .Is (err , state .ErrNoState ) {
446530 // everything else is a noop, as no model means no connections
447531 // to reload
448- return nil , nil
532+ return nil , nil , nil
449533 } else if err != nil {
450- return nil , err
534+ return nil , nil , err
451535 }
452536 autoChecker , err = newAutoConnectChecker (m .state , m .repo , deviceCtx )
453537 if err != nil {
454- return nil , err
538+ return nil , nil , err
455539 }
456540
457541 connChecker , err = newConnectChecker (m .state , deviceCtx )
458542 if err != nil {
459- return nil , err
543+ return nil , nil , err
460544 }
461545
462546 connStateChanged := false
547+ changedConns = make (map [string ]* schema.ConnState )
463548
464549 var reloadedConnections []string
465550ConnsLoop:
@@ -473,7 +558,7 @@ ConnsLoop:
473558 }
474559 connRef , err := interfaces .ParseConnRef (connId )
475560 if err != nil {
476- return nil , err
561+ return nil , nil , err
477562 }
478563 // Apply filtering, this allows us to reload only a subset of
479564 // connections (and similarly, refresh the static attributes of only a
@@ -497,13 +582,14 @@ ConnsLoop:
497582 for _ , snapName := range []string {connRef .PlugRef .Snap , connRef .SlotRef .Snap } {
498583 broken , err := isBroken (m .state , snapName )
499584 if err != nil {
500- return nil , err
585+ return nil , nil , err
501586 }
502587 if broken {
503588 logger .Noticef ("Snap %q is broken, ignored by reloadConnections" , snapName )
504589 continue ConnsLoop
505590 }
506591 }
592+ changedConns [connId ] = cloneConnState (connState )
507593 delete (conns , connId )
508594 connStateChanged = true
509595 }
@@ -540,11 +626,11 @@ ConnsLoop:
540626
541627 plugAppSet , err := interfaces .NewSnapAppSet (plugInfo .Snap , nil )
542628 if err != nil {
543- return nil , err
629+ return nil , nil , err
544630 }
545631 slotAppSet , err := interfaces .NewSnapAppSet (slotInfo .Snap , nil )
546632 if err != nil {
547- return nil , err
633+ return nil , nil , err
548634 }
549635
550636 cplug := interfaces .NewConnectedPlug (plugInfo , plugAppSet , newStaticPlugAttrs , connState .DynamicPlugAttrs )
@@ -568,6 +654,7 @@ ConnsLoop:
568654 reloadedConnections = append (reloadedConnections , connId )
569655
570656 if updateStaticAttrs {
657+ changedConns [connId ] = cloneConnState (connState )
571658 connState .StaticPlugAttrs = staticPlugAttrs
572659 connState .StaticSlotAttrs = staticSlotAttrs
573660 connStateChanged = true
@@ -578,7 +665,7 @@ ConnsLoop:
578665 setConns (m .state , conns )
579666 }
580667
581- return reloadedConnections , nil
668+ return reloadedConnections , changedConns , nil
582669}
583670
584671// removeConnections disconnects all connections of the snap in the repo. It should only be used if the snap
0 commit comments