github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/store/resources.go (about) 1 package store 2 3 import ( 4 "strings" 5 6 "github.com/docker/swarmkit/api" 7 memdb "github.com/hashicorp/go-memdb" 8 "github.com/pkg/errors" 9 ) 10 11 const tableResource = "resource" 12 13 var ( 14 // ErrNoKind is returned by resource create operations if the provided Kind 15 // of the resource does not exist 16 ErrNoKind = errors.New("object kind is unregistered") 17 ) 18 19 func init() { 20 register(ObjectStoreConfig{ 21 Table: &memdb.TableSchema{ 22 Name: tableResource, 23 Indexes: map[string]*memdb.IndexSchema{ 24 indexID: { 25 Name: indexID, 26 Unique: true, 27 Indexer: resourceIndexerByID{}, 28 }, 29 indexName: { 30 Name: indexName, 31 Unique: true, 32 Indexer: resourceIndexerByName{}, 33 }, 34 indexKind: { 35 Name: indexKind, 36 Indexer: resourceIndexerByKind{}, 37 }, 38 indexCustom: { 39 Name: indexCustom, 40 Indexer: resourceCustomIndexer{}, 41 AllowMissing: true, 42 }, 43 }, 44 }, 45 Save: func(tx ReadTx, snapshot *api.StoreSnapshot) error { 46 var err error 47 snapshot.Resources, err = FindResources(tx, All) 48 return err 49 }, 50 Restore: func(tx Tx, snapshot *api.StoreSnapshot) error { 51 toStoreObj := make([]api.StoreObject, len(snapshot.Resources)) 52 for i, x := range snapshot.Resources { 53 toStoreObj[i] = resourceEntry{x} 54 } 55 return RestoreTable(tx, tableResource, toStoreObj) 56 }, 57 ApplyStoreAction: func(tx Tx, sa api.StoreAction) error { 58 switch v := sa.Target.(type) { 59 case *api.StoreAction_Resource: 60 obj := v.Resource 61 switch sa.Action { 62 case api.StoreActionKindCreate: 63 return CreateResource(tx, obj) 64 case api.StoreActionKindUpdate: 65 return UpdateResource(tx, obj) 66 case api.StoreActionKindRemove: 67 return DeleteResource(tx, obj.ID) 68 } 69 } 70 return errUnknownStoreAction 71 }, 72 }) 73 } 74 75 type resourceEntry struct { 76 *api.Resource 77 } 78 79 func (r resourceEntry) CopyStoreObject() api.StoreObject { 80 return resourceEntry{Resource: r.Resource.Copy()} 81 } 82 83 // ensure that when update events are emitted, we unwrap resourceEntry 84 func (r resourceEntry) EventUpdate(oldObject api.StoreObject) api.Event { 85 if oldObject != nil { 86 return api.EventUpdateResource{Resource: r.Resource, OldResource: oldObject.(resourceEntry).Resource} 87 } 88 return api.EventUpdateResource{Resource: r.Resource} 89 } 90 91 func confirmExtension(tx Tx, r *api.Resource) error { 92 // There must be an extension corresponding to the Kind field. 93 extensions, err := FindExtensions(tx, ByName(r.Kind)) 94 if err != nil { 95 return errors.Wrap(err, "failed to query extensions") 96 } 97 if len(extensions) == 0 { 98 return ErrNoKind 99 } 100 return nil 101 } 102 103 // CreateResource adds a new resource object to the store. 104 // Returns ErrExist if the ID is already taken. 105 // Returns ErrNameConflict if a Resource with this Name already exists 106 // Returns ErrNoKind if the specified Kind does not exist 107 func CreateResource(tx Tx, r *api.Resource) error { 108 if err := confirmExtension(tx, r); err != nil { 109 return err 110 } 111 // TODO(dperny): currently the "name" index is unique, which means only one 112 // Resource of _any_ Kind can exist with that name. This isn't a problem 113 // right now, but the ideal case would be for names to be namespaced to the 114 // kind. 115 if tx.lookup(tableResource, indexName, strings.ToLower(r.Annotations.Name)) != nil { 116 return ErrNameConflict 117 } 118 return tx.create(tableResource, resourceEntry{r}) 119 } 120 121 // UpdateResource updates an existing resource object in the store. 122 // Returns ErrNotExist if the object doesn't exist. 123 func UpdateResource(tx Tx, r *api.Resource) error { 124 if err := confirmExtension(tx, r); err != nil { 125 return err 126 } 127 return tx.update(tableResource, resourceEntry{r}) 128 } 129 130 // DeleteResource removes a resource object from the store. 131 // Returns ErrNotExist if the object doesn't exist. 132 func DeleteResource(tx Tx, id string) error { 133 return tx.delete(tableResource, id) 134 } 135 136 // GetResource looks up a resource object by ID. 137 // Returns nil if the object doesn't exist. 138 func GetResource(tx ReadTx, id string) *api.Resource { 139 r := tx.get(tableResource, id) 140 if r == nil { 141 return nil 142 } 143 return r.(resourceEntry).Resource 144 } 145 146 // FindResources selects a set of resource objects and returns them. 147 func FindResources(tx ReadTx, by By) ([]*api.Resource, error) { 148 checkType := func(by By) error { 149 switch by.(type) { 150 case byIDPrefix, byName, byNamePrefix, byKind, byCustom, byCustomPrefix: 151 return nil 152 default: 153 return ErrInvalidFindBy 154 } 155 } 156 157 resourceList := []*api.Resource{} 158 appendResult := func(o api.StoreObject) { 159 resourceList = append(resourceList, o.(resourceEntry).Resource) 160 } 161 162 err := tx.find(tableResource, by, checkType, appendResult) 163 return resourceList, err 164 } 165 166 type resourceIndexerByKind struct{} 167 168 func (ri resourceIndexerByKind) FromArgs(args ...interface{}) ([]byte, error) { 169 return fromArgs(args...) 170 } 171 172 func (ri resourceIndexerByKind) FromObject(obj interface{}) (bool, []byte, error) { 173 r := obj.(resourceEntry) 174 175 // Add the null character as a terminator 176 val := r.Resource.Kind + "\x00" 177 return true, []byte(val), nil 178 } 179 180 type resourceIndexerByID struct{} 181 182 func (indexer resourceIndexerByID) FromArgs(args ...interface{}) ([]byte, error) { 183 return api.ResourceIndexerByID{}.FromArgs(args...) 184 } 185 func (indexer resourceIndexerByID) PrefixFromArgs(args ...interface{}) ([]byte, error) { 186 return api.ResourceIndexerByID{}.PrefixFromArgs(args...) 187 } 188 func (indexer resourceIndexerByID) FromObject(obj interface{}) (bool, []byte, error) { 189 return api.ResourceIndexerByID{}.FromObject(obj.(resourceEntry).Resource) 190 } 191 192 type resourceIndexerByName struct{} 193 194 func (indexer resourceIndexerByName) FromArgs(args ...interface{}) ([]byte, error) { 195 return api.ResourceIndexerByName{}.FromArgs(args...) 196 } 197 func (indexer resourceIndexerByName) PrefixFromArgs(args ...interface{}) ([]byte, error) { 198 return api.ResourceIndexerByName{}.PrefixFromArgs(args...) 199 } 200 func (indexer resourceIndexerByName) FromObject(obj interface{}) (bool, []byte, error) { 201 return api.ResourceIndexerByName{}.FromObject(obj.(resourceEntry).Resource) 202 } 203 204 type resourceCustomIndexer struct{} 205 206 func (indexer resourceCustomIndexer) FromArgs(args ...interface{}) ([]byte, error) { 207 return api.ResourceCustomIndexer{}.FromArgs(args...) 208 } 209 func (indexer resourceCustomIndexer) PrefixFromArgs(args ...interface{}) ([]byte, error) { 210 return api.ResourceCustomIndexer{}.PrefixFromArgs(args...) 211 } 212 func (indexer resourceCustomIndexer) FromObject(obj interface{}) (bool, [][]byte, error) { 213 return api.ResourceCustomIndexer{}.FromObject(obj.(resourceEntry).Resource) 214 }