github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/cmn/err.go (about) 1 // Package cmn provides common constants, types, and utilities for AIS clients 2 // and AIStore. 3 /* 4 * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. 5 */ 6 package cmn 7 8 import ( 9 "bytes" 10 "errors" 11 "fmt" 12 "io/fs" 13 "net/http" 14 "path/filepath" 15 "runtime" 16 "strconv" 17 "strings" 18 "sync" 19 20 "github.com/NVIDIA/aistore/api/apc" 21 "github.com/NVIDIA/aistore/cmn/cos" 22 "github.com/NVIDIA/aistore/cmn/debug" 23 "github.com/NVIDIA/aistore/cmn/nlog" 24 jsoniter "github.com/json-iterator/go" 25 ) 26 27 // This source contains common AIS node inter-module errors - 28 // the errors that some AIS packages (within a given running AIS node) return 29 // and other AIS packages handle. 30 31 const ( 32 stackTracePrefix = "stack: [" 33 34 fmtErrBckName = "bucket name %q is invalid: " + cos.OnlyPlus 35 fmtErrNamespace = "bucket namespace (uuid: %q, name: %q) " + cos.OnlyNice 36 37 FmtErrIntegrity = "[%s%d, for troubleshooting see %s/blob/main/docs/troubleshooting.md]" 38 FmtErrUnmarshal = "%s: failed to unmarshal %s (%s), err: %w" 39 FmtErrMorphUnmarshal = "%s: failed to unmarshal %s (%T), err: %w" 40 FmtErrUnknown = "%s: unknown %s %q" 41 FmtErrBackwardCompat = "%v (backward compatibility is supported only one version back, e.g. 3.9 => 3.10)" 42 43 fmtErrFailedTo = "%s: failed to %s %s, err: %v" // (ErrFailedTo) 44 45 BadSmapPrefix = "[bad cluster map]" 46 47 StartupMayTimeout = "cluster startup is taking unusually long time..." // related ErrStartupTimeout 48 ) 49 50 // API error structure 51 // is returned to aistore client and carries one of the specific errors enumerated below 52 type ( 53 ErrHTTP struct { 54 TypeCode string `json:"tcode,omitempty"` 55 Message string `json:"message"` 56 Method string `json:"method"` 57 URLPath string `json:"url_path"` 58 RemoteAddr string `json:"remote_addr"` 59 Caller string `json:"caller"` 60 Node string `json:"node"` 61 trace []byte 62 Status int `json:"status"` 63 } 64 ) 65 66 // assorted aistore errors 67 type ( 68 ErrBucketAlreadyExists struct{ bck Bck } 69 ErrRemoteBckNotFound struct{ bck Bck } 70 ErrRemoteBucketOffline struct{ bck Bck } 71 ErrBckNotFound struct{ bck Bck } 72 73 ErrBusy struct { 74 whereOrType string 75 what string 76 detail []string 77 } 78 79 ErrFailedTo struct { 80 actor string // most of the time it's this (target|proxy) node but may also be some other "actor" 81 what any // not necessarily LOM 82 err error // original error that can be Unwrap-ed 83 action string // not necessarily msg.Action 84 status int // http status, if available 85 } 86 ErrUnsupp struct { 87 action, what string 88 } 89 ErrNotImpl struct { 90 action, what string 91 } 92 93 ErrInvalidBackendProvider struct { 94 bck Bck 95 } 96 ErrCapExceeded struct { 97 totalBytes uint64 98 totalBytesUsed uint64 99 highWM int64 100 cleanupWM int64 101 usedPct int32 102 oos bool 103 } 104 ErrBucketAccessDenied struct{ errAccessDenied } 105 ErrObjectAccessDenied struct{ errAccessDenied } 106 errAccessDenied struct { 107 entity string 108 operation string 109 accessAttrs apc.AccessAttrs 110 } 111 112 ErrInvalidCksum struct { 113 expectedHash string 114 actualHash string 115 } 116 117 ErrMountpathNotFound struct { 118 mpath string 119 fqn string 120 disabled bool 121 } 122 ErrInvalidMountpath struct { 123 mpath string 124 cause string 125 } 126 ErrInvalidFSPathsConf struct { 127 err error 128 } 129 130 ErrNoNodes struct { 131 role string 132 mmcount int // maintenance mode 133 } 134 ErrXactNotFound struct { 135 cause string 136 } 137 ErrObjDefunct struct { 138 name string // object's name 139 d1, d2 uint64 // lom.md.(bucket-ID) and lom.bck.(bucket-ID), respectively 140 } 141 ErrAborted struct { 142 err error 143 what string 144 ctx string 145 } 146 ErrInitBackend struct { 147 Provider string 148 } 149 ErrMissingBackend struct { 150 Provider string 151 Msg string 152 } 153 ErrETL struct { 154 Reason string 155 ETLErrCtx 156 } 157 ETLErrCtx struct { 158 TID string 159 ETLName string 160 PodName string 161 SvcName string 162 } 163 ErrSoft struct { 164 what string 165 } 166 167 ErrLmetaCorrupted struct { 168 err error 169 } 170 ErrLmetaNotFound struct { 171 err error 172 } 173 174 ErrLimitedCoexistence struct { 175 node string // this (local) node 176 xaction string 177 action string 178 detail string 179 } 180 ErrXactUsePrev struct { // equivalent to xreg.WprUse 181 xaction string 182 } 183 ErrXactTgtInMaint struct { 184 xaction string 185 tname string 186 } 187 ErrStreamTerminated struct { 188 err error 189 stream string 190 reason string 191 detail string 192 } 193 ErrInvalidObjName struct { 194 name string 195 } 196 ErrNotRemoteBck struct { 197 act string 198 bck *Bck 199 } 200 ErrRangeNotSatisfiable struct { 201 err error // original (backend reported) error 202 ranges []string // RFC 7233 203 size int64 // [0, size) 204 } 205 ) 206 207 var ( 208 thisNodeName string 209 cleanPathErr func(error) 210 ) 211 212 func InitErrs(a string, b func(error)) { thisNodeName, cleanPathErr = a, b } 213 214 var ( 215 ErrSkip = errors.New("skip") 216 ErrStartupTimeout = errors.New("startup timeout") // related StartupMayTimeout 217 ErrQuiesceTimeout = errors.New("timed out waiting for quiescence") 218 ErrNotEnoughTargets = errors.New("not enough target nodes") 219 ErrNoMountpaths = errors.New("no mountpaths") 220 221 // aborts 222 ErrXactRenewAbort = errors.New("renewal abort") 223 ErrXactUserAbort = errors.New("user abort") // via apc.ActXactStop 224 ErrXactICNotifAbort = errors.New("IC(notifications) abort") // ditto 225 ) 226 227 // ErrFailedTo 228 229 func NewErrFailedTo(actor fmt.Stringer, action string, what any, err error, ecode ...int) *ErrFailedTo { 230 if e, ok := err.(*ErrFailedTo); ok { 231 return e 232 } 233 _clean(err) 234 235 e := &ErrFailedTo{action: action, what: what, err: err} 236 e.actor = thisNodeName 237 if actor != nil { 238 e.actor = actor.String() 239 } 240 if len(ecode) > 0 { 241 e.status = ecode[0] 242 if err == nil && e.status > 0 { 243 e.err = errors.New("error code: " + strconv.Itoa(e.status) + "(\"" + http.StatusText(e.status) + "\")") 244 } 245 } 246 return e 247 } 248 249 func (e *ErrFailedTo) Error() string { 250 return fmt.Sprintf(fmtErrFailedTo, e.actor, e.action, e.what, e.err) 251 } 252 253 func (e *ErrFailedTo) Unwrap() (err error) { return e.err } 254 255 // ErrStreamTerminated 256 257 func NewErrStreamTerminated(stream string, err error, reason, detail string) *ErrStreamTerminated { 258 return &ErrStreamTerminated{stream: stream, err: err, reason: reason, detail: detail} 259 } 260 261 func (e *ErrStreamTerminated) Error() string { 262 return fmt.Sprintf("%s terminated(%q, %v): %s", e.stream, e.reason, e.err, e.detail) 263 } 264 265 func (e *ErrStreamTerminated) Unwrap() (err error) { return e.err } 266 267 func IsErrStreamTerminated(err error) bool { 268 _, ok := err.(*ErrStreamTerminated) 269 return ok 270 } 271 272 // ErrUnsupp & ErrNotImpl 273 274 func NewErrUnsupp(action, what string) *ErrUnsupp { return &ErrUnsupp{action, what} } 275 276 func (e *ErrUnsupp) Error() string { 277 return fmt.Sprintf("cannot %s %s - operation not supported", e.action, e.what) 278 } 279 280 func isErrUnsupp(err error) bool { 281 _, ok := err.(*ErrUnsupp) 282 return ok 283 } 284 285 func NewErrNotImpl(action, what string) *ErrNotImpl { return &ErrNotImpl{action, what} } 286 287 func (e *ErrNotImpl) Error() string { 288 return fmt.Sprintf("cannot %s %s - not impemented yet", e.action, e.what) 289 } 290 291 func isErrNotImpl(err error) bool { 292 _, ok := err.(*ErrNotImpl) 293 return ok 294 } 295 296 // (ais) ErrBucketAlreadyExists 297 298 func NewErrBckAlreadyExists(bck *Bck) *ErrBucketAlreadyExists { 299 return &ErrBucketAlreadyExists{bck: *bck} 300 } 301 302 func (e *ErrBucketAlreadyExists) Error() string { 303 return fmt.Sprintf("bucket %q already exists", e.bck) 304 } 305 306 func IsErrBucketAlreadyExists(err error) bool { 307 _, ok := err.(*ErrBucketAlreadyExists) 308 return ok 309 } 310 311 // remote ErrRemoteBckNotFound (compare with ErrBckNotFound) 312 313 func NewErrRemoteBckNotFound(bck *Bck) *ErrRemoteBckNotFound { 314 return &ErrRemoteBckNotFound{bck: *bck} 315 } 316 317 func (e *ErrRemoteBckNotFound) Error() string { 318 if e.bck.IsCloud() { 319 return fmt.Sprintf("%s bucket %q does not exist", apc.NormalizeProvider(e.bck.Provider), e.bck.Cname("")) 320 } 321 return fmt.Sprintf("remote bucket %q does not exist", e.bck) 322 } 323 324 func IsErrRemoteBckNotFound(err error) bool { 325 _, ok := err.(*ErrRemoteBckNotFound) 326 return ok 327 } 328 329 // ErrBckNotFound - applies to ais buckets exclusively 330 // (compare with ErrRemoteBckNotFound) 331 332 func NewErrBckNotFound(bck *Bck) *ErrBckNotFound { 333 return &ErrBckNotFound{bck: *bck} 334 } 335 336 func (e *ErrBckNotFound) Error() string { 337 return fmt.Sprintf("bucket %q does not exist", e.bck) 338 } 339 340 func IsErrBckNotFound(err error) bool { 341 _, ok := err.(*ErrBckNotFound) 342 return ok 343 } 344 345 // ErrRemoteBucketOffline 346 347 func NewErrRemoteBckOffline(bck *Bck) *ErrRemoteBucketOffline { 348 return &ErrRemoteBucketOffline{bck: *bck} 349 } 350 351 func (e *ErrRemoteBucketOffline) Error() string { 352 return fmt.Sprintf("bucket %q is currently unreachable", e.bck) 353 } 354 355 func isErrRemoteBucketOffline(err error) bool { 356 _, ok := err.(*ErrRemoteBucketOffline) 357 return ok 358 } 359 360 // ErrInvalidBackendProvider 361 362 func (e *ErrInvalidBackendProvider) Error() string { 363 if e.bck.Name != "" { 364 return fmt.Sprintf("invalid backend provider %q for bucket %s: must be one of [%s]", 365 e.bck.Provider, e.bck, apc.AllProviders) 366 } 367 return fmt.Sprintf("invalid backend provider %q: must be one of [%s]", e.bck.Provider, apc.AllProviders) 368 } 369 370 func (*ErrInvalidBackendProvider) Is(target error) bool { 371 _, ok := target.(*ErrInvalidBackendProvider) 372 return ok 373 } 374 375 // ErrBusy 376 377 func NewErrBusy(whereOrType, what string, detail ...string) *ErrBusy { 378 return &ErrBusy{whereOrType, what, detail} 379 } 380 381 func (e *ErrBusy) Error() string { 382 var s string 383 if len(e.detail) > 0 { 384 s = " (" + e.detail[0] + ")" 385 } 386 return fmt.Sprintf("%s %q is currently busy%s, please try again", e.whereOrType, e.what, s) 387 } 388 389 // errAccessDenied & ErrBucketAccessDenied 390 391 func (e *errAccessDenied) String() string { 392 return fmt.Sprintf("%s: %s access denied (allowed: [%s])", 393 e.entity, e.operation, e.accessAttrs.Describe(false /*include all*/)) 394 } 395 396 func (e *ErrBucketAccessDenied) Error() string { 397 return "bucket " + e.String() 398 } 399 400 func NewBucketAccessDenied(bucket, oper string, aattrs apc.AccessAttrs) *ErrBucketAccessDenied { 401 return &ErrBucketAccessDenied{errAccessDenied{bucket, oper, aattrs}} 402 } 403 404 func (e *ErrObjectAccessDenied) Error() string { 405 return "object " + e.String() 406 } 407 408 func NewObjectAccessDenied(object, oper string, aattrs apc.AccessAttrs) *ErrObjectAccessDenied { 409 return &ErrObjectAccessDenied{errAccessDenied{object, oper, aattrs}} 410 } 411 412 // ErrCapExceeded 413 414 func NewErrCapExceeded(totalBytesUsed, totalBytes uint64, highWM, cleanupWM int64, usedPct int32, oos bool) *ErrCapExceeded { 415 return &ErrCapExceeded{ 416 totalBytes: totalBytes, // avail + used 417 totalBytesUsed: totalBytesUsed, 418 highWM: highWM, 419 cleanupWM: cleanupWM, 420 usedPct: usedPct, 421 oos: oos, 422 } 423 } 424 425 func (e *ErrCapExceeded) Error() string { 426 suffix := fmt.Sprintf("total used %s out of %s", cos.ToSizeIEC(int64(e.totalBytesUsed), 2), 427 cos.ToSizeIEC(int64(e.totalBytes), 2)) 428 if e.oos { 429 return fmt.Sprintf("out of space: used %d%% of total capacity on at least one of the mountpaths (%s)", 430 e.usedPct, suffix) 431 } 432 if e.highWM == 0 { 433 debug.Assert(e.cleanupWM > 0) 434 return fmt.Sprintf("low on free space: used capacity %d%% exceeded cleanup watermark(%d%%) (%s)", 435 e.usedPct, e.cleanupWM, suffix) 436 } 437 debug.Assert(e.highWM > 0) 438 return fmt.Sprintf("low on free space: used capacity %d%% exceeded high watermark(%d%%) (%s)", 439 e.usedPct, e.highWM, suffix) 440 } 441 442 func IsErrCapExceeded(err error) bool { 443 _, ok := err.(*ErrCapExceeded) 444 return ok 445 } 446 447 // ErrInvalidCksum 448 449 func (e *ErrInvalidCksum) Error() string { 450 return fmt.Sprintf("checksum: expected [%s], actual [%s]", e.expectedHash, e.actualHash) 451 } 452 453 func NewErrInvalidCksum(eHash, aHash string) *ErrInvalidCksum { 454 return &ErrInvalidCksum{actualHash: aHash, expectedHash: eHash} 455 } 456 457 func (e *ErrInvalidCksum) Expected() string { return e.expectedHash } 458 459 // ErrMountpathNotFound 460 461 func (e *ErrMountpathNotFound) Error() string { 462 if e.mpath != "" { 463 if e.disabled { 464 return "mountpath " + e.mpath + " is disabled" 465 } 466 return "mountpath " + e.mpath + " does not exist" 467 } 468 debug.Assert(e.fqn != "") 469 if e.disabled { 470 return "mountpath for fqn " + e.fqn + " is disabled" 471 } 472 return "mountpath for fqn " + e.fqn + " does not exist" 473 } 474 475 func NewErrMountpathNotFound(mpath, fqn string, disabled bool) *ErrMountpathNotFound { 476 return &ErrMountpathNotFound{mpath: mpath, fqn: fqn, disabled: disabled} 477 } 478 479 func IsErrMountpathNotFound(err error) bool { 480 _, ok := err.(*ErrMountpathNotFound) 481 return ok 482 } 483 484 // ErrInvalidMountpath 485 486 func (e *ErrInvalidMountpath) Error() string { 487 return "invalid mountpath [" + e.mpath + "]; " + e.cause 488 } 489 490 func NewErrInvalidaMountpath(mpath, cause string) *ErrInvalidMountpath { 491 return &ErrInvalidMountpath{mpath: mpath, cause: cause} 492 } 493 494 // ErrInvalidFSPathsConf 495 496 func NewErrInvalidFSPathsConf(err error) *ErrInvalidFSPathsConf { 497 return &ErrInvalidFSPathsConf{err} 498 } 499 500 func (e *ErrInvalidFSPathsConf) Unwrap() (err error) { return e.err } 501 502 func (e *ErrInvalidFSPathsConf) Error() string { 503 return fmt.Sprintf("invalid \"fspaths\" configuration: %v", e.err) 504 } 505 506 // ErrNoNodes 507 508 func NewErrNoNodes(role string, mmcount int) *ErrNoNodes { 509 return &ErrNoNodes{role: role, mmcount: mmcount} 510 } 511 512 func (e *ErrNoNodes) Error() (s string) { 513 var what string 514 if e.role == apc.Proxy { 515 what = "gateway" 516 s = "no proxies (gateways) in the cluster" 517 } else { 518 debug.Assert(e.role == apc.Target) 519 what = "target" 520 s = "no storage targets in the cluster" 521 } 522 if e.mmcount > 0 { 523 s += fmt.Sprintf(" (%d %s%s in maintenance mode or being decommissioned)", 524 e.mmcount, what, cos.Plural(e.mmcount)) 525 } 526 return 527 } 528 529 // ErrXactNotFound 530 531 func (e *ErrXactNotFound) Error() string { 532 return "xaction " + e.cause + " not found" 533 } 534 535 func NewErrXactNotFoundError(cause string) *ErrXactNotFound { 536 return &ErrXactNotFound{cause: cause} 537 } 538 539 func IsErrXactNotFound(err error) bool { 540 _, ok := err.(*ErrXactNotFound) 541 return ok 542 } 543 544 // ErrObjDefunct 545 546 func (e *ErrObjDefunct) Error() string { 547 return fmt.Sprintf("%s is defunct (%d != %d)", e.name, e.d1, e.d2) 548 } 549 550 func NewErrObjDefunct(name string, d1, d2 uint64) *ErrObjDefunct { 551 return &ErrObjDefunct{name, d1, d2} 552 } 553 554 func isErrObjDefunct(err error) bool { 555 _, ok := err.(*ErrObjDefunct) 556 return ok 557 } 558 559 // ErrAborted 560 561 func NewErrAborted(what, ctx string, err error) *ErrAborted { 562 if e, ok := err.(*ErrAborted); ok { 563 return e 564 } 565 _clean(err) 566 return &ErrAborted{what: what, ctx: ctx, err: err} 567 } 568 569 func (e *ErrAborted) Error() (s string) { 570 s = e.what + " aborted" 571 if e.err != nil { 572 s = fmt.Sprintf("%s, err: %v", s, e.err) 573 } 574 if e.ctx != "" { 575 s += " (" + e.ctx + ")" 576 } 577 return 578 } 579 580 func (e *ErrAborted) Unwrap() (err error) { return e.err } 581 582 func IsErrAborted(err error) bool { return AsErrAborted(err) != nil } 583 584 func AsErrAborted(err error) (errAborted *ErrAborted) { 585 var ok bool 586 if errAborted, ok = err.(*ErrAborted); ok { 587 return 588 } 589 target := &ErrAborted{} 590 if errors.As(err, &target) { 591 errAborted = target 592 } 593 return 594 } 595 596 // ErrInitBackend & ErrMissingBackend 597 598 func (e *ErrInitBackend) Error() string { 599 return fmt.Sprintf( 600 "cannot initialize %q backend (present in the cluster configuration): missing %s-supporting libraries in the build", 601 e.Provider, e.Provider, 602 ) 603 } 604 605 func (e *ErrMissingBackend) Error() string { 606 if e.Msg != "" { 607 return e.Msg 608 } 609 return fmt.Sprintf("%q backend is missing in the cluster configuration", e.Provider) 610 } 611 612 // ErrETL 613 614 func NewErrETL(ctx *ETLErrCtx, format string, a ...any) *ErrETL { 615 e := &ErrETL{ 616 Reason: fmt.Sprintf(format, a...), 617 } 618 return e.WithContext(ctx) 619 } 620 621 func (e *ErrETL) Error() string { 622 s := make([]string, 0, 3) 623 if e.TID != "" { 624 s = append(s, fmt.Sprintf("t[%s]", e.TID)) 625 } 626 if e.ETLName != "" { 627 s = append(s, fmt.Sprintf("etl=%q", e.ETLName)) 628 } 629 if e.PodName != "" { 630 s = append(s, fmt.Sprintf("pod=%q", e.PodName)) 631 } 632 if e.SvcName != "" { 633 s = append(s, fmt.Sprintf("service=%q", e.SvcName)) 634 } 635 return fmt.Sprintf("[%s] %s", strings.Join(s, ","), e.Reason) 636 } 637 638 func (e *ErrETL) withTarget(tid string) *ErrETL { 639 if tid != "" { 640 e.TID = tid 641 } 642 return e 643 } 644 645 func (e *ErrETL) withETLName(name string) *ErrETL { 646 if name != "" { 647 e.ETLName = name 648 } 649 return e 650 } 651 652 func (e *ErrETL) withSvcName(name string) *ErrETL { 653 if name != "" { 654 e.SvcName = name 655 } 656 return e 657 } 658 659 func (e *ErrETL) WithPodName(name string) *ErrETL { 660 if name != "" { 661 e.PodName = name 662 } 663 return e 664 } 665 666 func (e *ErrETL) WithContext(ctx *ETLErrCtx) *ErrETL { 667 if ctx == nil { 668 return e 669 } 670 return e. 671 withTarget(ctx.TID). 672 WithPodName(ctx.PodName). 673 withETLName(ctx.ETLName). 674 withSvcName(ctx.SvcName) 675 } 676 677 // ErrSoft 678 // non-critical and can be ignored in certain cases (e.g, when `--force` is set) 679 680 func NewErrSoft(what string) *ErrSoft { 681 return &ErrSoft{what} 682 } 683 684 func (e *ErrSoft) Error() string { 685 return e.what 686 } 687 688 func IsErrSoft(err error) bool { 689 if _, ok := err.(*ErrSoft); ok { 690 return true 691 } 692 target := &ErrSoft{} 693 return errors.As(err, &target) 694 } 695 696 // ErrLmetaCorrupted & ErrLmetaNotFound 697 698 func NewErrLmetaCorrupted(err error) *ErrLmetaCorrupted { return &ErrLmetaCorrupted{err} } 699 func (e *ErrLmetaCorrupted) Error() string { return e.err.Error() } 700 func (e *ErrLmetaCorrupted) Unwrap() (err error) { return e.err } 701 702 func IsErrLmetaCorrupted(err error) bool { 703 _, ok := err.(*ErrLmetaCorrupted) 704 return ok 705 } 706 707 func NewErrLmetaNotFound(err error) *ErrLmetaNotFound { return &ErrLmetaNotFound{err} } 708 func (e *ErrLmetaNotFound) Error() string { return e.err.Error() } 709 func (e *ErrLmetaNotFound) Unwrap() (err error) { return e.err } 710 711 func IsErrLmetaNotFound(err error) bool { 712 _, ok := err.(*ErrLmetaNotFound) 713 return ok 714 } 715 716 // ErrLimitedCoexistence 717 718 func NewErrLimitedCoexistence(node, xaction, action, detail string) *ErrLimitedCoexistence { 719 return &ErrLimitedCoexistence{node, xaction, action, detail} 720 } 721 722 func (e *ErrLimitedCoexistence) Error() string { 723 return fmt.Sprintf("%s: %s is currently running, cannot run %q(%s) concurrently", 724 e.node, e.xaction, e.action, e.detail) 725 } 726 727 // ErrXactUsePrev 728 729 func NewErrXactUsePrev(xaction string) *ErrXactUsePrev { 730 return &ErrXactUsePrev{xaction} 731 } 732 733 func (e *ErrXactUsePrev) Error() string { 734 return e.xaction + "is already running - not starting" 735 } 736 737 func IsErrXactUsePrev(err error) bool { 738 _, ok := err.(*ErrXactUsePrev) 739 return ok 740 } 741 742 // ErrInvalidObjName 743 744 func ValidateObjName(name string) (err *ErrInvalidObjName) { 745 if cos.IsLastB(name, filepath.Separator) || strings.Contains(name, "../") { 746 err = &ErrInvalidObjName{name} 747 } 748 return err 749 } 750 751 func (e *ErrInvalidObjName) Error() string { 752 return fmt.Sprintf("invalid object name %q", e.name) 753 } 754 755 // ErrNotRemoteBck 756 757 func ValidateRemoteBck(act string, bck *Bck) (err *ErrNotRemoteBck) { 758 if !bck.IsRemote() { 759 err = &ErrNotRemoteBck{act, bck} 760 } 761 return err 762 } 763 764 func (e *ErrNotRemoteBck) Error() string { 765 return fmt.Sprintf("%s: expecting remote bucket (have %s)", e.act, e.bck) 766 } 767 768 // ErrXactTgtInMaint 769 770 func NewErrXactTgtInMaint(xaction, tname string) *ErrXactTgtInMaint { 771 return &ErrXactTgtInMaint{xaction, tname} 772 } 773 774 func (e *ErrXactTgtInMaint) Error() string { 775 return fmt.Sprintf("%s is in maintenance or being decommissioned - cannot run %s", 776 e.tname, e.xaction) 777 } 778 779 // ErrRangeNotSatisfiable 780 // http.StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 781 782 func NewErrRangeNotSatisfiable(err error, ranges []string, size int64) *ErrRangeNotSatisfiable { 783 return &ErrRangeNotSatisfiable{err, ranges, size} 784 } 785 786 func (e *ErrRangeNotSatisfiable) Error() string { 787 if e.err == nil { 788 s := "object size = " + strconv.FormatInt(e.size, 10) 789 return fmt.Sprintf("%s, range%s %v not satisfiable", s, cos.Plural(len(e.ranges)), e.ranges) 790 } 791 return e.err.Error() 792 } 793 794 func IsErrRangeNotSatisfiable(err error) bool { 795 _, ok := err.(*ErrRangeNotSatisfiable) 796 return ok 797 } 798 799 // 800 // more is-error helpers 801 // 802 803 // nought: not a thing 804 func IsErrBucketNought(err error) bool { 805 return IsErrBckNotFound(err) || IsErrRemoteBckNotFound(err) || isErrRemoteBucketOffline(err) 806 } 807 808 // lom.Load 809 func IsErrObjNought(err error) bool { 810 return cos.IsNotExist(err, 0) || IsStatusNotFound(err) || isErrObjDefunct(err) || IsErrLmetaNotFound(err) 811 } 812 813 // used internally to report http.StatusNotFound _iff_ status is not set (is zero) 814 func isErrNotFoundExtended(err error, status int) bool { 815 return IsErrBckNotFound(err) || IsErrRemoteBckNotFound(err) || 816 IsErrMountpathNotFound(err) || IsErrXactNotFound(err) || 817 cos.IsNotExist(err, status) 818 } 819 820 func IsFileAlreadyClosed(err error) bool { 821 return errors.Is(err, fs.ErrClosed) 822 } 823 824 func IsErrBucketLevel(err error) bool { return IsErrBucketNought(err) } 825 func IsErrObjLevel(err error) bool { return IsErrObjNought(err) } 826 827 ///////////// 828 // ErrHTTP // 829 ///////////// 830 831 func Str2HTTPErr(msg string) *ErrHTTP { 832 var herr ErrHTTP 833 if err := jsoniter.UnmarshalFromString(msg, &herr); err == nil { 834 return &herr 835 } 836 return nil 837 } 838 839 func Err2HTTPErr(err error) *ErrHTTP { 840 e, ok := err.(*ErrHTTP) 841 if !ok { 842 e = &ErrHTTP{} 843 if !errors.As(err, &e) { 844 return nil 845 } 846 } 847 return e 848 } 849 850 const maxTypeCodeLen = 30 851 852 func TypeCodeHTTPErr(s string) (tcode string) { 853 if !strings.HasPrefix(s, "Err") { 854 return 855 } 856 for i := 3; i < min(maxTypeCodeLen, len(s)); i++ { 857 c := s[i] 858 if (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') { 859 continue 860 } 861 if c == ':' && i+8 < len(s) && s[i+1] == ' ' { 862 tcode = s[:i] 863 } 864 break 865 } 866 return 867 } 868 869 func NewErrHTTP(r *http.Request, err error, ecode int) (e *ErrHTTP) { 870 e = &ErrHTTP{} 871 e.init(r, err, ecode) 872 return e 873 } 874 875 // uses `allocHterr` to allocate - caller must free via `FreeHterr` 876 func InitErrHTTP(r *http.Request, err error, ecode int) (e *ErrHTTP) { 877 e = allocHterr() 878 e.init(r, err, ecode) 879 return e 880 } 881 882 func (e *ErrHTTP) init(r *http.Request, err error, ecode int) { 883 e.Status = http.StatusBadRequest 884 if ecode != 0 { 885 e.Status = ecode 886 } 887 tcode := fmt.Sprintf("%T", err) 888 if i := strings.Index(tcode, "."); i > 0 { 889 if pkg := tcode[:i]; pkg != "*errors" && pkg != "errors" { 890 e.TypeCode = tcode[i+1:] 891 } 892 } 893 _clean(err) 894 e.Message = err.Error() 895 if r != nil { 896 e.Method, e.URLPath = r.Method, r.URL.Path 897 e.RemoteAddr = r.RemoteAddr 898 e.Caller = r.Header.Get(apc.HdrCallerName) 899 } 900 e.Node = thisNodeName 901 } 902 903 func (e *ErrHTTP) Error() (s string) { 904 if e.TypeCode != "" && e.TypeCode != "ErrFailedTo" { 905 if !strings.Contains(e.Message, e.TypeCode+":") { 906 return e.TypeCode + ": " + e.Message 907 } 908 } 909 return e.Message 910 } 911 912 func _clean(err error) { 913 if cleanPathErr != nil { 914 cleanPathErr(err) 915 } 916 } 917 918 // Example: 919 // ErrBckNotFound: bucket "ais://abc" does not exist: HEAD /v1/buckets/abc (p[kWQp8080]: htrun.go:1035 <- prxtrybck.go:180 <- ... 920 func (e *ErrHTTP) StringEx() (s string) { 921 s = e.Error() 922 if e.Method != "" || e.URLPath != "" { 923 if !cos.IsLastB(s, '.') { 924 s += ":" 925 } 926 if e.Method != "" { 927 s += " " + e.Method 928 } 929 if e.URLPath != "" { 930 s += " " + e.URLPath 931 } 932 } 933 if thisNodeName != "" && !strings.Contains(e.Message, thisNodeName) { 934 s += " (failed at " + thisNodeName + ")" 935 } 936 if e.Caller != "" { 937 s += " (called by " + e.Caller + ")" 938 } 939 if len(e.trace) == 0 { 940 e._trace() 941 } 942 return s + " (" + string(e.trace) + ")" 943 } 944 945 func (e *ErrHTTP) _jsonError(buf *bytes.Buffer) { 946 enc := jsoniter.NewEncoder(buf) 947 enc.SetEscapeHTML(false) // stop from escaping `<`, `>` and `&`. 948 if err := enc.Encode(e); err != nil { 949 buf.Reset() 950 buf.WriteString(err.Error()) 951 } 952 } 953 954 func (e *ErrHTTP) write(w http.ResponseWriter, r *http.Request, silent bool) { 955 if !silent { 956 s := e.StringEx() 957 if thisNodeName != "" && !strings.Contains(e.Message, thisNodeName) { 958 // node name instead of generic stack: 959 replaced1 := strings.Replace(s, stackTracePrefix, thisNodeName+": ", 1) 960 if replaced1 != s { 961 replaced2 := strings.Replace(replaced1, " (failed at "+thisNodeName+")", "", 1) 962 if replaced2 != replaced1 { 963 s = replaced2 964 } 965 } 966 } 967 nlog.Errorln(s) 968 } 969 hdr := w.Header() 970 hdr.Set(cos.HdrContentType, cos.ContentJSON) 971 hdr.Set(cos.HdrContentTypeOptions, "nosniff") 972 973 berr := NewBuffer() 974 e._jsonError(berr) 975 if r.Method == http.MethodHead { 976 hdr.Set(apc.HdrError, berr.String()) 977 w.WriteHeader(e.Status) 978 } else { 979 w.WriteHeader(e.Status) 980 w.Write(berr.Bytes()) // no newline 981 } 982 FreeBuffer(berr) 983 } 984 985 func (e *ErrHTTP) _trace() { 986 buffer := bytes.NewBuffer(e.trace) 987 fmt.Fprint(buffer, stackTracePrefix) 988 for i := 1; i < 9; i++ { 989 _, file, line, ok := runtime.Caller(i) 990 if !ok { 991 break 992 } 993 if !strings.Contains(file, "aistore") { 994 break 995 } 996 f := filepath.Base(file) 997 if f == "err.go" { 998 continue 999 } 1000 if buffer.Len() > len(stackTracePrefix) { 1001 buffer.WriteString(" <- ") 1002 } 1003 fmt.Fprintf(buffer, "%s:%d", f, line) 1004 } 1005 fmt.Fprint(buffer, "]") 1006 e.trace = buffer.Bytes() 1007 } 1008 1009 func IsStatusServiceUnavailable(err error) (yes bool) { 1010 herr, ok := err.(*ErrHTTP) 1011 return ok && herr.Status == http.StatusServiceUnavailable 1012 } 1013 1014 func IsStatusNotFound(err error) (yes bool) { 1015 herr, ok := err.(*ErrHTTP) 1016 return ok && herr.Status == http.StatusNotFound 1017 } 1018 1019 func IsStatusBadGateway(err error) (yes bool) { 1020 herr, ok := err.(*ErrHTTP) 1021 return ok && herr.Status == http.StatusBadGateway 1022 } 1023 1024 func IsStatusGone(err error) (yes bool) { 1025 herr, ok := err.(*ErrHTTP) 1026 return ok && herr.Status == http.StatusGone 1027 } 1028 1029 // 1030 // WriteErr and friends 1031 // 1032 1033 // sends HTTP response header with the provided status (alloc/free via mem-pool) 1034 func WriteErr(w http.ResponseWriter, r *http.Request, err error, opts ...int /*[status[, silent]]*/) { 1035 if herr, allocated := err2HTTP(err); herr != nil { 1036 herr.Status = http.StatusBadRequest 1037 if len(opts) > 0 && opts[0] > http.StatusBadRequest { 1038 herr.Status = opts[0] 1039 } 1040 herr.write(w, r, len(opts) > 1 /*silent*/) 1041 if allocated { 1042 FreeHterr(herr) 1043 } 1044 return 1045 } 1046 var ( 1047 herr = allocHterr() 1048 l = len(opts) 1049 status = http.StatusBadRequest 1050 ) 1051 1052 // assign status (in order of priority) 1053 if cos.IsErrNotFound(err) { 1054 // NOTE: override opts[0] status, e.g.: "remote cluster "uuid" does not exist, status=500" 1055 status = http.StatusNotFound 1056 } else if l > 0 { 1057 status = opts[0] 1058 } else if errf, ok := err.(*ErrFailedTo); ok { 1059 status = errf.status 1060 } else { 1061 switch { 1062 case isErrNotFoundExtended(err, status): 1063 status = http.StatusNotFound 1064 case IsErrCapExceeded(err): 1065 status = http.StatusInsufficientStorage 1066 case IsErrRangeNotSatisfiable(err): 1067 status = http.StatusRequestedRangeNotSatisfiable 1068 case isErrUnsupp(err), isErrNotImpl(err): 1069 status = http.StatusNotImplemented 1070 } 1071 } 1072 1073 herr.init(r, err, status) 1074 herr.write(w, r, l > 1) 1075 FreeHterr(herr) 1076 } 1077 1078 // NOTE: internal use w/ duplication/simplicity traded off 1079 func err2HTTP(err error) (*ErrHTTP, bool) { 1080 if e, ok := err.(*ErrHTTP); ok { 1081 return e, false 1082 } 1083 e := allocHterr() 1084 if !errors.As(err, &e) { 1085 FreeHterr(e) 1086 return nil, false 1087 } 1088 return e, true 1089 } 1090 1091 // Create ErrHTTP (based on `msg` and `opts`) and write it into HTTP response. 1092 func WriteErrMsg(w http.ResponseWriter, r *http.Request, msg string, opts ...int) { 1093 var ecode int 1094 if len(opts) > 0 { 1095 ecode = opts[0] 1096 } 1097 herr := InitErrHTTP(r, errors.New(msg), ecode) 1098 herr.write(w, r, len(opts) > 1 /*silent*/) 1099 FreeHterr(herr) 1100 } 1101 1102 // 405 Method Not Allowed, see: 1103 // * https://www.rfc-editor.org/rfc/rfc7231#section-6.5.5 1104 func WriteErr405(w http.ResponseWriter, r *http.Request, methods ...string) { 1105 w.Header().Set("Allow", strings.Join(methods, ", ")) 1106 if r.Method == http.MethodOptions { 1107 w.WriteHeader(http.StatusOK) 1108 } else { 1109 http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) 1110 } 1111 } 1112 1113 // 1114 // 1) ErrHTTP struct pool 1115 // 2) bytes.Buffer pool 1116 // 1117 1118 const maxBuffer = 4 * cos.KiB 1119 1120 var ( 1121 errPool sync.Pool 1122 bufPool sync.Pool 1123 1124 err0 ErrHTTP 1125 ) 1126 1127 func allocHterr() (a *ErrHTTP) { 1128 if v := errPool.Get(); v != nil { 1129 a = v.(*ErrHTTP) 1130 return 1131 } 1132 return &ErrHTTP{} 1133 } 1134 1135 func FreeHterr(a *ErrHTTP) { 1136 trace := a.trace 1137 *a = err0 1138 if trace != nil { 1139 a.trace = trace[:0] 1140 } 1141 errPool.Put(a) 1142 } 1143 1144 func NewBuffer() (buf *bytes.Buffer) { 1145 if v := bufPool.Get(); v != nil { 1146 buf = v.(*bytes.Buffer) 1147 } else { 1148 buf = bytes.NewBuffer(nil) 1149 } 1150 return 1151 } 1152 1153 func FreeBuffer(buf *bytes.Buffer) { 1154 if buf.Cap() > maxBuffer { 1155 return 1156 } 1157 buf.Reset() 1158 bufPool.Put(buf) 1159 }