github.com/m3db/m3@v1.5.0/src/dbnode/namespace/kvadmin/ns_admin.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package kvadmin 22 23 import ( 24 "errors" 25 "fmt" 26 27 "github.com/pborman/uuid" 28 29 "github.com/m3db/m3/src/cluster/kv" 30 nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 31 "github.com/m3db/m3/src/dbnode/namespace" 32 xerrors "github.com/m3db/m3/src/x/errors" 33 ) 34 35 var ( 36 // ErrNamespaceNotFound is returned when namespace is not found in registry. 37 ErrNamespaceNotFound = errors.New("namespace is not found") 38 // ErrNamespaceAlreadyExist is returned for addition of a namespace which already exists. 39 ErrNamespaceAlreadyExist = errors.New("namespace already exists") 40 ) 41 42 type adminService struct { 43 store kv.Store 44 key string 45 idGen func() string 46 } 47 48 const ( 49 // M3DBNodeNamespacesKey is the KV key that holds namespaces. 50 M3DBNodeNamespacesKey = "m3db.node.namespaces" 51 ) 52 53 func NewAdminService(store kv.Store, key string, idGen func() string) NamespaceMetadataAdminService { 54 if idGen == nil { 55 idGen = func() string { 56 return uuid.New() 57 } 58 } 59 if len(key) == 0 { 60 key = M3DBNodeNamespacesKey 61 } 62 return &adminService{ 63 store: store, 64 key: key, 65 idGen: idGen, 66 } 67 } 68 69 func (as *adminService) GetAll() (*nsproto.Registry, error) { 70 currentRegistry, _, err := as.currentRegistry() 71 if err == kv.ErrNotFound { 72 return nil, ErrNamespaceNotFound 73 } 74 if err != nil { 75 return nil, xerrors.Wrapf(err, "failed to load current namespace metadatas for %s", as.key) 76 } 77 return currentRegistry, nil 78 } 79 80 func (as *adminService) Get(name string) (*nsproto.NamespaceOptions, error) { 81 nsReg, err := as.GetAll() 82 if err != nil { 83 return nil, err 84 } 85 if nsOpt, ok := nsReg.GetNamespaces()[name]; ok { 86 return nsOpt, nil 87 } 88 return nil, ErrNamespaceNotFound 89 } 90 91 func (as *adminService) Add(name string, options *nsproto.NamespaceOptions) error { 92 nsMeta, err := namespace.ToMetadata(name, options) 93 if err != nil { 94 return xerrors.Wrapf(err, "invalid namespace options for namespace: %v", name) 95 } 96 currentRegistry, currentVersion, err := as.currentRegistry() 97 if err == kv.ErrNotFound { 98 _, err = as.store.SetIfNotExists(as.key, &nsproto.Registry{ 99 Namespaces: map[string]*nsproto.NamespaceOptions{name: options}, 100 }) 101 if err != nil { 102 return xerrors.Wrapf(err, "failed to add namespace %v", name) 103 } 104 return nil 105 } 106 if err != nil { 107 return xerrors.Wrapf(err, "failed to load namespace registry at %s", as.key) 108 } 109 110 if _, ok := currentRegistry.GetNamespaces()[name]; ok { 111 return ErrNamespaceAlreadyExist 112 } 113 nsMap, err := namespace.FromProto(*currentRegistry) 114 if err != nil { 115 return xerrors.Wrap(err, "failed to unmarshall namespace registry") 116 } 117 118 newMap, err := namespace.NewMap(append(nsMap.Metadatas(), nsMeta)) 119 if err != nil { 120 return err 121 } 122 123 protoMap, err := namespace.ToProto(newMap) 124 if err != nil { 125 return err 126 } 127 128 _, err = as.store.CheckAndSet(as.key, currentVersion, protoMap) 129 if err != nil { 130 return xerrors.Wrapf(err, "failed to add namespace %v", name) 131 } 132 return nil 133 } 134 135 func (as *adminService) Set(name string, options *nsproto.NamespaceOptions) error { 136 _, err := namespace.ToMetadata(name, options) 137 if err != nil { 138 return xerrors.Wrapf(err, "invalid options for namespace: %v", name) 139 } 140 currentRegistry, currentVersion, err := as.currentRegistry() 141 if err != nil { 142 return xerrors.Wrapf(err, "failed to load namespace registry at %s", as.key) 143 } 144 if _, ok := currentRegistry.GetNamespaces()[name]; !ok { 145 return ErrNamespaceNotFound 146 } 147 148 currentRegistry.Namespaces[name] = options 149 150 _, err = as.store.CheckAndSet(as.key, currentVersion, currentRegistry) 151 if err != nil { 152 return xerrors.Wrapf(err, "failed to update namespace %v", name) 153 } 154 return nil 155 } 156 157 func (as *adminService) Delete(name string) error { 158 currentRegistry, currentVersion, err := as.currentRegistry() 159 if err != nil { 160 return xerrors.Wrapf(err, "failed to load current namespace metadatas for %s", as.key) 161 } 162 163 nsMap, err := namespace.FromProto(*currentRegistry) 164 if err != nil { 165 return xerrors.Wrap(err, "failed to unmarshal namespace registry") 166 } 167 168 metadatas := nsMap.Metadatas() 169 mdIdx := -1 170 for idx, md := range nsMap.Metadatas() { 171 if md.ID().String() == name { 172 mdIdx = idx 173 174 break 175 } 176 } 177 178 if mdIdx == -1 { 179 return ErrNamespaceNotFound 180 } 181 182 if len(metadatas) == 1 { 183 if _, err := as.store.Delete(as.key); err != nil { 184 return xerrors.Wrap(err, "failed to delete kv key") 185 } 186 187 return nil 188 } 189 190 // Replace the index where we found the metadata with the last element, then truncate 191 metadatas[mdIdx] = metadatas[len(metadatas)-1] 192 metadatas = metadatas[:len(metadatas)-1] 193 194 newMap, err := namespace.NewMap(metadatas) 195 if err != nil { 196 return xerrors.Wrap(err, "namespace map construction failed") 197 } 198 199 protoMap, err := namespace.ToProto(newMap) 200 if err != nil { 201 return xerrors.Wrap(err, "namespace registry proto conversion failed") 202 } 203 204 _, err = as.store.CheckAndSet(as.key, currentVersion, protoMap) 205 if err != nil { 206 return xerrors.Wrapf(err, "failed to delete namespace %v", name) 207 } 208 209 return nil 210 } 211 212 func (as *adminService) ResetSchema(name string) error { 213 currentRegistry, currentVersion, err := as.currentRegistry() 214 if err == kv.ErrNotFound { 215 return ErrNamespaceNotFound 216 } 217 if err != nil { 218 return xerrors.Wrapf(err, "failed to load current namespace metadatas for %s", as.key) 219 } 220 221 var targetMeta *nsproto.NamespaceOptions 222 for nsID, nsOpts := range currentRegistry.GetNamespaces() { 223 if nsID == name { 224 targetMeta = nsOpts 225 break 226 } 227 } 228 if targetMeta == nil { 229 return ErrNamespaceNotFound 230 } 231 232 // Clear schema options in place. 233 targetMeta.SchemaOptions = nil 234 235 _, err = as.store.CheckAndSet(as.key, currentVersion, currentRegistry) 236 if err != nil { 237 return xerrors.Wrapf(err, "failed to reset schema for namespace %s", name) 238 } 239 return nil 240 } 241 242 func (as *adminService) DeploySchema(name, protoFileName, msgName string, protos map[string]string) (string, error) { 243 currentRegistry, currentVersion, err := as.currentRegistry() 244 if err == kv.ErrNotFound { 245 return "", ErrNamespaceNotFound 246 } 247 if err != nil { 248 return "", xerrors.Wrapf(err, "failed to load current namespace metadatas for %s", as.key) 249 } 250 var targetMeta *nsproto.NamespaceOptions 251 for nsID, nsOpts := range currentRegistry.GetNamespaces() { 252 if nsID == name { 253 targetMeta = nsOpts 254 break 255 } 256 } 257 if targetMeta == nil { 258 return "", ErrNamespaceNotFound 259 } 260 261 deployID := as.idGen() 262 263 schemaOpt, err := namespace.AppendSchemaOptions(targetMeta.SchemaOptions, 264 protoFileName, msgName, protos, deployID) 265 if err != nil { 266 return "", xerrors.Wrapf(err, "failed to append schema history from %s for message %s", protoFileName, msgName) 267 } 268 269 // Update schema options in place. 270 targetMeta.SchemaOptions = schemaOpt 271 272 _, err = as.store.CheckAndSet(as.key, currentVersion, currentRegistry) 273 if err != nil { 274 return "", xerrors.Wrapf(err, "failed to deploy schema from %s with version %s to namespace %s", protoFileName, deployID, name) 275 } 276 return deployID, nil 277 } 278 279 func (as *adminService) currentRegistry() (*nsproto.Registry, int, error) { 280 value, err := as.store.Get(as.key) 281 if err != nil { 282 return nil, -1, err 283 } 284 285 var protoRegistry nsproto.Registry 286 if err := value.Unmarshal(&protoRegistry); err != nil { 287 return nil, -1, fmt.Errorf("unable to parse value, err: %v", err) 288 } 289 290 return &protoRegistry, value.Version(), nil 291 } 292 293 func LoadSchemaRegistryFromKVStore(schemaReg namespace.SchemaRegistry, kvStore kv.Store) error { 294 if kvStore == nil { 295 return errors.New("m3db metadata store is not configured properly") 296 } 297 as := NewAdminService(kvStore, "", nil) 298 nsReg, err := as.GetAll() 299 if err != nil { 300 return xerrors.Wrap(err, "could not get metadata from metadata store") 301 } 302 nsMap, err := namespace.FromProto(*nsReg) 303 if err != nil { 304 return xerrors.Wrap(err, "could not unmarshal metadata") 305 } 306 merr := xerrors.NewMultiError() 307 for _, metadata := range nsMap.Metadatas() { 308 err = schemaReg.SetSchemaHistory(metadata.ID(), metadata.Options().SchemaHistory()) 309 if err != nil { 310 merr = merr.Add(xerrors.Wrapf(err, "could not set schema history for namespace %s", metadata.ID().String())) 311 } 312 } 313 return merr.FinalError() 314 }