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 }