go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipd/appengine/impl/metadata/metadata.go (about) 1 // Copyright 2018 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package metadata 16 17 import ( 18 "context" 19 20 api "go.chromium.org/luci/cipd/api/cipd/v1" 21 ) 22 23 // Visitor is a callback passed to VisitMetadata. 24 // 25 // It decides whether to continue exploring this metadata subtree or not. 26 type Visitor func(prefix string, md []*api.PrefixMetadata) (cont bool, err error) 27 28 // Storage knows how to store, fetch and update prefix metadata, as well as 29 // how to calculate its fingerprint. 30 // 31 // The metadata is organized in a forest-like structure, where each node is 32 // associated with some package prefix (e.g. has a name "a/b/c"). The single 33 // root ("") may or may not be present, depending on the implementation. 34 // 35 // It doesn't try to understand what metadata means, just fingerprints, stores 36 // and enumerates it. 37 // 38 // This functionality is organized into an interface to simplify mocking. Use 39 // GetStorage to grab a real implementation. 40 type Storage interface { 41 // GetMetadata fetches metadata associated with the given prefix and all 42 // parent prefixes. 43 // 44 // The prefix may be an empty string, in which case the root metadata will be 45 // returned, if it is defined. 46 // 47 // Does not check permissions. 48 // 49 // The return value is sorted by the prefix length. Prefixes without metadata 50 // are skipped. For example, when requesting metadata for prefix "a/b/c/d" the 51 // return value may contain entries for "", "a", "a/b", "a/b/c/d" (in that 52 // order, where "" indicates the root and "a/b/c" is skipped in this example 53 // as not having any metadata attached). 54 // 55 // Note that the prefix of the last entry doesn't necessary match 'prefix'. 56 // This happens if metadata for that prefix doesn't exist. Similarly, the 57 // returns value may be completely empty slice in case there's no metadata 58 // for the requested prefix and all its parent prefixes. 59 // 60 // Returns a fatal error if the prefix is malformed, all other errors are 61 // transient. 62 GetMetadata(ctx context.Context, prefix string) ([]*api.PrefixMetadata, error) 63 64 // VisitMetadata enumerates the metadata in depth-first order. 65 // 66 // Can be used to fetch all metadata items at and under the given prefix. 67 // 68 // The callback is called for each visited node (always starting from 'prefix' 69 // itself, even if it has no metadata directly attached to it), receiving same 70 // metadata list as if GetMetadata was used to fetch it. The callback can 71 // decide whether to proceed with the enumeration of the corresponding subtree 72 // or skip it (by returning either true or false). 73 // 74 // Aborts the traversal on a first error from the callback. 75 // 76 // Returns either a transient error if fetching failed, or whatever error the 77 // callback returned. 78 VisitMetadata(ctx context.Context, prefix string, cb Visitor) error 79 80 // UpdateMetadata transactionally (with XG transaction) updates or creates 81 // metadata of some prefix and returns it. 82 // 83 // The prefix may be an empty string, in which case the root metadata will 84 // be updated, if allowed. 85 // 86 // Does not check permissions. Does not check the format of the updated 87 // metadata. 88 // 89 // It fetches the metadata object and calls the callback to modify it. The 90 // callback may be called multiple times when retrying the transaction. If the 91 // callback mutates the metadata and doesn't return an error, the metadata's 92 // fingerprint is updated, the metadata is saved to the storage and returned 93 // to the caller. 94 // 95 // If the metadata object doesn't exist yet, the callback will be called with 96 // an empty object that has only 'Prefix' field populated. The callback then 97 // can populate the rest of the fields. If it doesn't touch any fields 98 // (but still succeeds), UpdateMetadata will return nil PrefixMetadata, to 99 // indicate the metadata is still absent. 100 // 101 // If the callback returns an error, it will be returned as is. If the 102 // transaction itself fails, returns a transient error. 103 UpdateMetadata(ctx context.Context, prefix string, cb func(ctx context.Context, m *api.PrefixMetadata) error) (*api.PrefixMetadata, error) 104 } 105 106 // GetStorage returns production implementation of the metadata storage. 107 func GetStorage() Storage { 108 return &legacyStorageImpl{} 109 }