github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/apiserver/common/modeldestroy.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 12 "github.com/juju/juju/apiserver/facades/agent/metricsender" 13 "github.com/juju/juju/core/status" 14 "github.com/juju/juju/state" 15 stateerrors "github.com/juju/juju/state/errors" 16 ) 17 18 var sendMetrics = func(st metricsender.ModelBackend) error { 19 ccfg, err := st.ControllerConfig() 20 if err != nil { 21 return errors.Annotate(err, "failed to get controller config") 22 } 23 meteringURL := ccfg.MeteringURL() 24 cfg, err := st.ModelConfig() 25 if err != nil { 26 return errors.Annotatef(err, "failed to get model config for %s", st.ModelTag()) 27 } 28 29 err = metricsender.SendMetrics( 30 st, 31 metricsender.DefaultSenderFactory()(meteringURL), 32 clock.WallClock, 33 metricsender.DefaultMaxBatchesPerSend(), 34 cfg.TransmitVendorMetrics(), 35 ) 36 return errors.Trace(err) 37 } 38 39 // DestroyController sets the controller model to Dying and, if requested, 40 // schedules cleanups so that all of the hosted models are destroyed, or 41 // otherwise returns an error indicating that there are hosted models 42 // remaining. 43 func DestroyController( 44 st ModelManagerBackend, 45 destroyHostedModels bool, 46 destroyStorage *bool, 47 force *bool, 48 maxWait *time.Duration, 49 modelTimeout *time.Duration, 50 ) error { 51 modelTag := st.ModelTag() 52 controllerModelTag := st.ControllerModelTag() 53 if modelTag != controllerModelTag { 54 return errors.Errorf( 55 "expected state for controller model UUID %v, got %v", 56 controllerModelTag.Id(), 57 modelTag.Id(), 58 ) 59 } 60 if destroyHostedModels { 61 uuids, err := st.AllModelUUIDs() 62 if err != nil { 63 return errors.Trace(err) 64 } 65 for _, uuid := range uuids { 66 modelSt, release, err := st.GetBackend(uuid) 67 if err != nil { 68 if errors.IsNotFound(err) { 69 // Model is already in the process of being destroyed. 70 continue 71 } 72 return errors.Trace(err) 73 } 74 defer release() 75 76 check := NewBlockChecker(modelSt) 77 if err = check.DestroyAllowed(); err != nil { 78 return errors.Trace(err) 79 } 80 err = sendMetrics(modelSt) 81 if err != nil { 82 logger.Errorf("failed to send leftover metrics: %v", err) 83 } 84 } 85 } 86 return destroyModel(st, state.DestroyModelParams{ 87 DestroyHostedModels: destroyHostedModels, 88 DestroyStorage: destroyStorage, 89 Force: force, 90 MaxWait: MaxWait(maxWait), 91 Timeout: modelTimeout, 92 }) 93 } 94 95 // DestroyModel sets the model to Dying, such that the model's resources will 96 // be destroyed and the model removed from the controller. 97 func DestroyModel( 98 st ModelManagerBackend, 99 destroyStorage *bool, 100 force *bool, 101 maxWait *time.Duration, 102 timeout *time.Duration, 103 ) error { 104 return destroyModel(st, state.DestroyModelParams{ 105 DestroyStorage: destroyStorage, 106 Force: force, 107 MaxWait: MaxWait(maxWait), 108 Timeout: timeout, 109 }) 110 } 111 112 func destroyModel(st ModelManagerBackend, args state.DestroyModelParams) error { 113 check := NewBlockChecker(st) 114 if err := check.DestroyAllowed(); err != nil { 115 return errors.Trace(err) 116 } 117 118 model, err := st.Model() 119 if err != nil { 120 return errors.Trace(err) 121 } 122 notForcing := args.Force == nil || !*args.Force 123 if notForcing { 124 // If model status is suspended, then model's cloud credential is invalid. 125 modelStatus, err := model.Status() 126 if err != nil { 127 return errors.Trace(err) 128 } 129 if modelStatus.Status == status.Suspended { 130 return errors.Errorf("invalid cloud credential, use --force") 131 } 132 } 133 if err := model.Destroy(args); err != nil { 134 if notForcing { 135 return errors.Trace(err) 136 } 137 logger.Warningf("failed destroying model %v: %v", model.UUID(), err) 138 if err := filterNonCriticalErrorForForce(err); err != nil { 139 return errors.Trace(err) 140 } 141 } 142 143 err = sendMetrics(st) 144 if err != nil { 145 logger.Errorf("failed to send leftover metrics: %v", err) 146 } 147 148 // Return to the caller. If it's the CLI, it will finish up by calling the 149 // provider's Destroy method, which will destroy the controllers, any 150 // straggler instances, and other provider-specific resources. Once all 151 // resources are torn down, the Undertaker worker handles the removal of 152 // the model. 153 return nil 154 } 155 156 func filterNonCriticalErrorForForce(err error) error { 157 if errors.Is(err, stateerrors.PersistentStorageError) { 158 return err 159 } 160 return nil 161 }