github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/common/resource.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "fmt" 8 "strconv" 9 "sync" 10 11 "github.com/juju/juju/apiserver/facade" 12 ) 13 14 // Resources holds all the resources for a connection. 15 // It allows the registration of resources that will be cleaned 16 // up when a connection terminates. 17 type Resources struct { 18 mu sync.Mutex 19 maxId uint64 20 resources map[string]facade.Resource 21 22 // The stack is used to control the order of destruction. 23 // last registered, first stopped. 24 // XXX(fwereade): is this necessary only because we have 25 // Resource instead of Worker (which would let us kill them all, 26 // and wait for them all, without danger of races)? 27 stack []string 28 } 29 30 func NewResources() *Resources { 31 return &Resources{ 32 resources: make(map[string]facade.Resource), 33 } 34 } 35 36 // Get returns the resource for the given id, or 37 // nil if there is no such resource. 38 func (rs *Resources) Get(id string) facade.Resource { 39 rs.mu.Lock() 40 defer rs.mu.Unlock() 41 return rs.resources[id] 42 } 43 44 // Register registers the given resource. It returns a unique 45 // identifier for the resource which can then be used in 46 // subsequent API requests to refer to the resource. 47 func (rs *Resources) Register(r facade.Resource) string { 48 rs.mu.Lock() 49 defer rs.mu.Unlock() 50 rs.maxId++ 51 id := strconv.FormatUint(rs.maxId, 10) 52 rs.resources[id] = r 53 rs.stack = append(rs.stack, id) 54 logger.Tracef("registered unnamed resource: %s", id) 55 return id 56 } 57 58 // RegisterNamed registers the given resource. Callers must supply a unique 59 // name for the given resource. It is an error to try to register another 60 // resource with the same name as an already registered name. (This could be 61 // softened that you can overwrite an existing one and it will be Stopped and 62 // replaced, but we don't have a need for that yet.) 63 // It is also an error to supply a name that is an integer string, since that 64 // collides with the auto-naming from Register. 65 func (rs *Resources) RegisterNamed(name string, r facade.Resource) error { 66 rs.mu.Lock() 67 defer rs.mu.Unlock() 68 if _, err := strconv.Atoi(name); err == nil { 69 return fmt.Errorf("RegisterNamed does not allow integer names: %q", name) 70 } 71 if _, ok := rs.resources[name]; ok { 72 return fmt.Errorf("resource %q already registered", name) 73 } 74 rs.resources[name] = r 75 rs.stack = append(rs.stack, name) 76 logger.Tracef("registered named resource: %s", name) 77 return nil 78 } 79 80 // Stop stops the resource with the given id and unregisters it. 81 // It returns any error from the underlying Stop call. 82 // It does not return an error if the resource has already 83 // been unregistered. 84 func (rs *Resources) Stop(id string) error { 85 // We don't hold the mutex while calling Stop, because 86 // that might take a while and we don't want to 87 // stop all other resource manipulation while we do so. 88 // If resources.Stop is called concurrently, we'll get 89 // two concurrent calls to Stop, but that should fit 90 // well with the way we invariably implement Stop. 91 logger.Tracef("stopping resource: %s", id) 92 r := rs.Get(id) 93 if r == nil { 94 return nil 95 } 96 err := r.Stop() 97 rs.mu.Lock() 98 defer rs.mu.Unlock() 99 delete(rs.resources, id) 100 for pos := 0; pos < len(rs.stack); pos++ { 101 if rs.stack[pos] == id { 102 rs.stack = append(rs.stack[0:pos], rs.stack[pos+1:]...) 103 break 104 } 105 } 106 return err 107 } 108 109 // StopAll stops all the resources. 110 func (rs *Resources) StopAll() { 111 rs.mu.Lock() 112 defer rs.mu.Unlock() 113 for i := len(rs.stack); i > 0; i-- { 114 id := rs.stack[i-1] 115 r := rs.resources[id] 116 logger.Tracef("stopping resource: %s", id) 117 if err := r.Stop(); err != nil { 118 logger.Errorf("error stopping %T resource: %v", r, err) 119 } 120 } 121 rs.resources = make(map[string]facade.Resource) 122 rs.stack = nil 123 } 124 125 // Count returns the number of resources currently held. 126 func (rs *Resources) Count() int { 127 rs.mu.Lock() 128 defer rs.mu.Unlock() 129 return len(rs.resources) 130 } 131 132 // StringResource is just a regular 'string' that matches the Resource 133 // interface. 134 type StringResource string 135 136 func (StringResource) Stop() error { 137 return nil 138 } 139 140 func (s StringResource) String() string { 141 return string(s) 142 } 143 144 // ValueResource is a Resource with a no-op Stop method, containing an 145 // interface{} value. 146 type ValueResource struct { 147 Value interface{} 148 } 149 150 func (r ValueResource) Stop() error { 151 return nil 152 }