github.com/m3db/m3@v1.5.0/src/metrics/rules/namespace.go (about) 1 // Copyright (c) 2017 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 rules 22 23 import ( 24 "errors" 25 "fmt" 26 27 "github.com/m3db/m3/src/metrics/generated/proto/rulepb" 28 "github.com/m3db/m3/src/metrics/rules/view" 29 xerrors "github.com/m3db/m3/src/x/errors" 30 ) 31 32 var ( 33 emptyNamespaceSnapshot NamespaceSnapshot 34 emptyNamespace Namespace 35 emptyNamespaces Namespaces 36 37 errNamespaceSnapshotIndexOutOfRange = errors.New("namespace snapshot idx out of range") 38 errNilNamespaceSnapshotProto = errors.New("nil namespace snapshot proto") 39 errNilNamespaceProto = errors.New("nil namespace proto") 40 errNilNamespacesProto = errors.New("nil namespaces proto") 41 errNilNamespaceSnapshot = errors.New("nil namespace snapshot") 42 errMultipleNamespaceMatches = errors.New("more than one namespace match found") 43 errNamespaceNotFound = errors.New("namespace not found") 44 errNamespaceNotTombstoned = errors.New("namespace is not tombstoned") 45 errNamespaceAlreadyTombstoned = errors.New("namespace is already tombstoned") 46 errNoNamespaceSnapshots = errors.New("namespace has no snapshots") 47 48 namespaceActionErrorFmt = "cannot %s namespace %s" 49 ) 50 51 // NamespaceSnapshot defines a namespace snapshot for which rules are defined. 52 type NamespaceSnapshot struct { 53 forRuleSetVersion int 54 tombstoned bool 55 lastUpdatedAtNanos int64 56 lastUpdatedBy string 57 } 58 59 func newNamespaceSnapshot(snapshot *rulepb.NamespaceSnapshot) (NamespaceSnapshot, error) { 60 if snapshot == nil { 61 return emptyNamespaceSnapshot, errNilNamespaceSnapshotProto 62 } 63 return NamespaceSnapshot{ 64 forRuleSetVersion: int(snapshot.ForRulesetVersion), 65 tombstoned: snapshot.Tombstoned, 66 lastUpdatedAtNanos: snapshot.LastUpdatedAtNanos, 67 lastUpdatedBy: snapshot.LastUpdatedBy, 68 }, nil 69 } 70 71 // ForRuleSetVersion is the ruleset version this namespace change is related to. 72 func (s NamespaceSnapshot) ForRuleSetVersion() int { return s.forRuleSetVersion } 73 74 // Tombstoned determines whether the namespace has been tombstoned. 75 func (s NamespaceSnapshot) Tombstoned() bool { return s.tombstoned } 76 77 // LastUpdatedAtNanos returns the time when the namespace is last updated in nanoseconds. 78 func (s NamespaceSnapshot) LastUpdatedAtNanos() int64 { return s.lastUpdatedAtNanos } 79 80 // LastUpdatedBy returns the user who last updated the namespace. 81 func (s NamespaceSnapshot) LastUpdatedBy() string { return s.lastUpdatedBy } 82 83 // Proto returns the given Namespace in protobuf form 84 func (s NamespaceSnapshot) Proto() *rulepb.NamespaceSnapshot { 85 return &rulepb.NamespaceSnapshot{ 86 ForRulesetVersion: int32(s.forRuleSetVersion), 87 Tombstoned: s.tombstoned, 88 LastUpdatedAtNanos: s.lastUpdatedAtNanos, 89 LastUpdatedBy: s.lastUpdatedBy, 90 } 91 } 92 93 // Namespace stores namespace snapshots. 94 type Namespace struct { 95 name []byte 96 snapshots []NamespaceSnapshot 97 } 98 99 // newNamespace creates a new namespace. 100 func newNamespace(namespace *rulepb.Namespace) (Namespace, error) { 101 if namespace == nil { 102 return emptyNamespace, errNilNamespaceProto 103 } 104 snapshots := make([]NamespaceSnapshot, 0, len(namespace.Snapshots)) 105 for _, snapshot := range namespace.Snapshots { 106 s, err := newNamespaceSnapshot(snapshot) 107 if err != nil { 108 return emptyNamespace, err 109 } 110 snapshots = append(snapshots, s) 111 } 112 return Namespace{ 113 name: []byte(namespace.Name), 114 snapshots: snapshots, 115 }, nil 116 } 117 118 // NamespaceView returns the view representation of a namespace object. 119 func (n Namespace) NamespaceView(snapshotIdx int) (view.Namespace, error) { 120 if snapshotIdx < 0 || snapshotIdx >= len(n.snapshots) { 121 return view.Namespace{}, errNamespaceSnapshotIndexOutOfRange 122 } 123 s := n.snapshots[snapshotIdx] 124 return view.Namespace{ 125 ID: string(n.name), 126 ForRuleSetVersion: s.forRuleSetVersion, 127 Tombstoned: s.tombstoned, 128 LastUpdatedBy: s.lastUpdatedBy, 129 LastUpdatedAtMillis: s.lastUpdatedAtNanos / nanosPerMilli, 130 }, nil 131 } 132 133 func (n Namespace) clone() Namespace { 134 name := make([]byte, len(n.name)) 135 copy(name, n.name) 136 snapshots := make([]NamespaceSnapshot, len(n.snapshots)) 137 copy(snapshots, n.snapshots) 138 return Namespace{ 139 name: name, 140 snapshots: snapshots, 141 } 142 } 143 144 // Name is the name of the namespace. 145 func (n Namespace) Name() []byte { return n.name } 146 147 // Snapshots return the namespace snapshots. 148 func (n Namespace) Snapshots() []NamespaceSnapshot { return n.snapshots } 149 150 // Proto returns the given Namespace in protobuf form 151 func (n Namespace) Proto() (*rulepb.Namespace, error) { 152 if n.snapshots == nil { 153 return nil, errNilNamespaceSnapshot 154 } 155 156 res := &rulepb.Namespace{ 157 Name: string(n.name), 158 } 159 160 snapshots := make([]*rulepb.NamespaceSnapshot, len(n.snapshots)) 161 for i, s := range n.snapshots { 162 snapshots[i] = s.Proto() 163 } 164 res.Snapshots = snapshots 165 166 return res, nil 167 } 168 169 func (n *Namespace) markTombstoned(tombstonedRSVersion int, meta UpdateMetadata) error { 170 if n.Tombstoned() { 171 return errNamespaceAlreadyTombstoned 172 } 173 snapshot := NamespaceSnapshot{ 174 forRuleSetVersion: tombstonedRSVersion, 175 tombstoned: true, 176 lastUpdatedAtNanos: meta.updatedAtNanos, 177 lastUpdatedBy: meta.updatedBy, 178 } 179 n.snapshots = append(n.snapshots, snapshot) 180 return nil 181 } 182 183 func (n *Namespace) revive(meta UpdateMetadata) error { 184 if !n.Tombstoned() { 185 return errNamespaceNotTombstoned 186 } 187 if len(n.snapshots) == 0 { 188 return errNoNamespaceSnapshots 189 } 190 191 tombstonedRuleSetVersion := n.snapshots[len(n.snapshots)-1].forRuleSetVersion 192 // NB: The revived ruleset version is one after the tombstoned ruleset version. 193 snapshot := NamespaceSnapshot{ 194 forRuleSetVersion: tombstonedRuleSetVersion + 1, 195 tombstoned: false, 196 lastUpdatedAtNanos: meta.updatedAtNanos, 197 lastUpdatedBy: meta.updatedBy, 198 } 199 n.snapshots = append(n.snapshots, snapshot) 200 return nil 201 } 202 203 // Tombstoned returns the tombstoned state for a given namespace. 204 func (n Namespace) Tombstoned() bool { 205 if len(n.snapshots) == 0 { 206 return true 207 } 208 return n.snapshots[len(n.snapshots)-1].tombstoned 209 } 210 211 // Namespaces store the list of namespaces for which rules are defined. 212 type Namespaces struct { 213 version int 214 namespaces []Namespace 215 } 216 217 // NewNamespaces creates new namespaces. 218 func NewNamespaces(version int, namespaces *rulepb.Namespaces) (Namespaces, error) { 219 if namespaces == nil { 220 return emptyNamespaces, errNilNamespacesProto 221 } 222 nss := make([]Namespace, 0, len(namespaces.Namespaces)) 223 for _, namespace := range namespaces.Namespaces { 224 ns, err := newNamespace(namespace) 225 if err != nil { 226 return emptyNamespaces, err 227 } 228 nss = append(nss, ns) 229 } 230 return Namespaces{ 231 version: version, 232 namespaces: nss, 233 }, nil 234 } 235 236 // NamespacesView returns a view representation of a given Namespaces object. 237 func (nss Namespaces) NamespacesView() (view.Namespaces, error) { 238 namespaces := make([]view.Namespace, len(nss.namespaces)) 239 for i, n := range nss.namespaces { 240 ns, err := n.NamespaceView(len(n.snapshots) - 1) 241 if err != nil { 242 return view.Namespaces{}, err 243 } 244 namespaces[i] = ns 245 } 246 return view.Namespaces{ 247 Version: nss.version, 248 Namespaces: namespaces, 249 }, nil 250 } 251 252 // Clone creates a deep copy of this Namespaces object. 253 func (nss Namespaces) Clone() Namespaces { 254 namespaces := make([]Namespace, len(nss.namespaces)) 255 for i, n := range nss.namespaces { 256 namespaces[i] = n.clone() 257 } 258 return Namespaces{ 259 version: nss.version, 260 namespaces: namespaces, 261 } 262 } 263 264 // Version returns the namespaces version. 265 func (nss Namespaces) Version() int { return nss.version } 266 267 // Namespaces returns the list of namespaces. 268 func (nss Namespaces) Namespaces() []Namespace { return nss.namespaces } 269 270 // Proto returns the given Namespaces slice in protobuf form. 271 func (nss Namespaces) Proto() (*rulepb.Namespaces, error) { 272 res := &rulepb.Namespaces{} 273 274 namespaces := make([]*rulepb.Namespace, len(nss.namespaces)) 275 for i, n := range nss.namespaces { 276 namespace, err := n.Proto() 277 if err != nil { 278 return nil, err 279 } 280 namespaces[i] = namespace 281 } 282 res.Namespaces = namespaces 283 284 return res, nil 285 } 286 287 // Namespace returns a namespace with a given name. 288 func (nss *Namespaces) Namespace(name string) (*Namespace, error) { 289 var res *Namespace 290 291 for i, ns := range nss.namespaces { 292 if string(ns.name) != name { 293 continue 294 } 295 296 if res == nil { 297 res = &nss.namespaces[i] 298 } else { 299 return nil, errMultipleNamespaceMatches 300 } 301 } 302 303 if res == nil { 304 return nil, errNamespaceNotFound 305 } 306 307 return res, nil 308 } 309 310 // AddNamespace adds a new namespace to the namespaces structure and persists it. 311 // This function returns a boolean indicating whether or not the namespace was revived. 312 // The revived flag should be used to decided if the corresponding" ruleset should also 313 // be revived. 314 func (nss *Namespaces) AddNamespace(nsName string, meta UpdateMetadata) (bool, error) { 315 existing, err := nss.Namespace(nsName) 316 if err != nil && err != errNamespaceNotFound { 317 return false, xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "add", nsName)) 318 } 319 320 // Brand new namespace. 321 if err == errNamespaceNotFound { 322 ns := Namespace{ 323 name: []byte(nsName), 324 snapshots: []NamespaceSnapshot{ 325 NamespaceSnapshot{ 326 forRuleSetVersion: 1, 327 tombstoned: false, 328 lastUpdatedAtNanos: meta.updatedAtNanos, 329 lastUpdatedBy: meta.updatedBy, 330 }, 331 }, 332 } 333 334 nss.namespaces = append(nss.namespaces, ns) 335 return false, nil 336 } 337 338 // Revive the namespace. 339 if err = existing.revive(meta); err != nil { 340 return false, xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "revive", nsName)) 341 } 342 343 return true, nil 344 } 345 346 // DeleteNamespace tombstones the given namespace mapping it to the next ruleset version. 347 func (nss *Namespaces) DeleteNamespace(nsName string, currRuleSetVersion int, meta UpdateMetadata) error { 348 existing, err := nss.Namespace(nsName) 349 if err != nil { 350 return xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "delete", nsName)) 351 } 352 353 if err := existing.markTombstoned(currRuleSetVersion+1, meta); err != nil { 354 return xerrors.Wrap(err, fmt.Sprintf(namespaceActionErrorFmt, "delete", nsName)) 355 } 356 357 return nil 358 }