github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/cleanup_storage.go (about)

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/mgo/v3/bson"
     9  	"github.com/juju/names/v5"
    10  
    11  	"github.com/juju/juju/core/status"
    12  )
    13  
    14  type dyingEntityStorageCleaner struct {
    15  	sb      *storageBackend
    16  	hostTag names.Tag
    17  	manual  bool
    18  	force   bool
    19  }
    20  
    21  func newDyingEntityStorageCleaner(sb *storageBackend, hostTag names.Tag, manual, force bool) *dyingEntityStorageCleaner {
    22  	return &dyingEntityStorageCleaner{
    23  		sb:      sb,
    24  		hostTag: hostTag,
    25  		manual:  manual,
    26  		force:   force,
    27  	}
    28  }
    29  
    30  func (c *dyingEntityStorageCleaner) cleanupStorage(
    31  	filesystemAttachments []FilesystemAttachment,
    32  	volumeAttachments []VolumeAttachment,
    33  ) error {
    34  	filesystems, err := c.destroyNonDetachableFileSystems()
    35  	if err != nil {
    36  		return errors.Trace(err)
    37  	}
    38  
    39  	// Detach all filesystems from the machine/unit.
    40  	for _, fsa := range filesystemAttachments {
    41  		if err := c.detachFileSystem(fsa); err != nil {
    42  			return errors.Trace(err)
    43  		}
    44  	}
    45  
    46  	// For non-manual machines we immediately remove the non-detachable
    47  	// filesystems, which should have been detached above. Short-circuiting
    48  	// the removal of machine filesystems means we can avoid stuck
    49  	// filesystems preventing any model-scoped backing volumes from being
    50  	// detached and destroyed. For non-manual machines this is safe, because
    51  	// the machine is about to be terminated. For manual machines, stuck
    52  	// filesystems will have to be fixed manually.
    53  	if !c.manual {
    54  		for _, f := range filesystems {
    55  			if err := c.sb.RemoveFilesystem(f.FilesystemTag()); err != nil && !errors.IsNotFound(err) {
    56  				if !c.force {
    57  					return errors.Trace(err)
    58  				}
    59  				logger.Warningf("could not remove filesystem %v for dying %v: %v", f.FilesystemTag().Id(), c.hostTag, err)
    60  			}
    61  		}
    62  	}
    63  
    64  	// Detach all remaining volumes from the machine.
    65  	for _, va := range volumeAttachments {
    66  		if detachable, err := isDetachableVolumeTag(c.sb.mb.db(), va.Volume()); err != nil && !errors.IsNotFound(err) {
    67  			if !c.force {
    68  				return errors.Trace(err)
    69  			}
    70  			logger.Warningf("could not determine if volume %v for dying %v is detachable: %v", va.Volume().Id(), c.hostTag, err)
    71  		} else if !detachable {
    72  			// Non-detachable volumes will be removed along with the machine.
    73  			continue
    74  		}
    75  		if err := c.sb.DetachVolume(va.Host(), va.Volume(), c.force); err != nil && !errors.IsNotFound(err) {
    76  			if IsContainsFilesystem(err) {
    77  				// The volume will be destroyed when the
    78  				// contained filesystem is removed, whose
    79  				// destruction is initiated above.
    80  				continue
    81  			}
    82  			if !c.force {
    83  				return errors.Trace(err)
    84  			}
    85  			logger.Warningf("could not detach volume %v for dying %v: %v", va.Volume().Id(), c.hostTag, err)
    86  		}
    87  	}
    88  	return nil
    89  }
    90  
    91  // destroyNonDetachableFileSystems marks file-systems and their attachments for
    92  // future destruction. Such file-systems are indicated as non-detachable by the
    93  // presence of a host ID; see `filesystemDoc`.
    94  func (c *dyingEntityStorageCleaner) destroyNonDetachableFileSystems() ([]*filesystem, error) {
    95  	filesystems, err := c.sb.filesystems(bson.D{{"hostid", c.hostTag.Id()}})
    96  	if err != nil && !errors.IsNotFound(err) {
    97  		err = errors.Annotate(err, "getting host filesystems")
    98  		if !c.force {
    99  			return nil, err
   100  		}
   101  		logger.Warningf("%v", err)
   102  	}
   103  
   104  	for _, f := range filesystems {
   105  		if err := c.sb.DestroyFilesystem(f.FilesystemTag(), c.force); err != nil && !errors.IsNotFound(err) {
   106  			if !c.force {
   107  				return nil, errors.Trace(err)
   108  			}
   109  			logger.Warningf("could not destroy filesystem %v for %v: %v", f.FilesystemTag().Id(), c.hostTag, err)
   110  		}
   111  	}
   112  
   113  	return filesystems, nil
   114  }
   115  
   116  func (c *dyingEntityStorageCleaner) detachFileSystem(fsa FilesystemAttachment) error {
   117  	filesystem := fsa.Filesystem()
   118  
   119  	detachable, err := isDetachableFilesystemTag(c.sb.mb.db(), filesystem)
   120  	if err != nil && !errors.IsNotFound(err) {
   121  		if !c.force {
   122  			return errors.Trace(err)
   123  		}
   124  		logger.Warningf("could not determine if filesystem %v for %v is detachable: %v", filesystem.Id(), c.hostTag, err)
   125  	}
   126  	if detachable {
   127  		if err := c.sb.DetachFilesystem(fsa.Host(), filesystem); err != nil && !errors.IsNotFound(err) {
   128  			if !c.force {
   129  				return errors.Trace(err)
   130  			}
   131  			logger.Warningf("could not detach filesystem %v for %v: %v", filesystem.Id(), c.hostTag, err)
   132  		}
   133  	}
   134  
   135  	if c.manual {
   136  		return nil
   137  	}
   138  
   139  	// For non-manual machines we immediately remove the attachments for
   140  	// non-detachable or volume-backed filesystems, which should have been set
   141  	// to Dying by the destruction of the machine filesystems,
   142  	// or filesystem detachment, above.
   143  	machineTag := fsa.Host()
   144  	var volumeTag names.VolumeTag
   145  	var updateStatus func() error
   146  	if !detachable {
   147  		updateStatus = func() error { return nil }
   148  	} else {
   149  		f, err := c.sb.Filesystem(filesystem)
   150  		if err != nil {
   151  			if !errors.IsNotFound(err) {
   152  				if !c.force {
   153  					return errors.Trace(err)
   154  				}
   155  				logger.Warningf("could not get filesystem %v for %v: %v", filesystem.Id(), c.hostTag, err)
   156  			}
   157  			return nil
   158  		}
   159  
   160  		if v, err := f.Volume(); err == nil && !errors.IsNotFound(err) {
   161  			// Filesystem is volume-backed.
   162  			volumeTag = v
   163  		}
   164  		updateStatus = func() error {
   165  			return f.SetStatus(status.StatusInfo{
   166  				Status: status.Detached,
   167  			})
   168  		}
   169  	}
   170  
   171  	if err := c.sb.RemoveFilesystemAttachment(fsa.Host(), filesystem, c.force); err != nil && !errors.IsNotFound(err) {
   172  		if !c.force {
   173  			return errors.Trace(err)
   174  		}
   175  		logger.Warningf("could not remove attachment for filesystem %v for %v: %v", filesystem.Id(), c.hostTag, err)
   176  	}
   177  	if volumeTag != (names.VolumeTag{}) {
   178  		if err := c.sb.RemoveVolumeAttachmentPlan(machineTag, volumeTag, c.force); err != nil && !errors.IsNotFound(err) {
   179  			if !c.force {
   180  				return errors.Trace(err)
   181  			}
   182  			logger.Warningf("could not remove attachment plan for volume %v for %v: %v", volumeTag.Id(), c.hostTag, err)
   183  		}
   184  	}
   185  	if err := updateStatus(); err != nil && !errors.IsNotFound(err) {
   186  		if !c.force {
   187  			return errors.Trace(err)
   188  		}
   189  		logger.Warningf("could not update status while cleaning up storage for dying %v: %v", c.hostTag, err)
   190  	}
   191  
   192  	return nil
   193  }