errors.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. package distribution
  2. import (
  3. "net/url"
  4. "strings"
  5. "syscall"
  6. "github.com/docker/distribution/registry/api/errcode"
  7. "github.com/docker/distribution/registry/api/v2"
  8. "github.com/docker/distribution/registry/client"
  9. "github.com/docker/docker/distribution/xfer"
  10. )
  11. // ErrNoSupport is an error type used for errors indicating that an operation
  12. // is not supported. It encapsulates a more specific error.
  13. type ErrNoSupport struct{ Err error }
  14. func (e ErrNoSupport) Error() string {
  15. if e.Err == nil {
  16. return "not supported"
  17. }
  18. return e.Err.Error()
  19. }
  20. // fallbackError wraps an error that can possibly allow fallback to a different
  21. // endpoint.
  22. type fallbackError struct {
  23. // err is the error being wrapped.
  24. err error
  25. // confirmedV2 is set to true if it was confirmed that the registry
  26. // supports the v2 protocol. This is used to limit fallbacks to the v1
  27. // protocol.
  28. confirmedV2 bool
  29. }
  30. // Error renders the FallbackError as a string.
  31. func (f fallbackError) Error() string {
  32. return f.err.Error()
  33. }
  34. // shouldV2Fallback returns true if this error is a reason to fall back to v1.
  35. func shouldV2Fallback(err errcode.Error) bool {
  36. switch err.Code {
  37. case errcode.ErrorCodeUnauthorized, v2.ErrorCodeManifestUnknown, v2.ErrorCodeNameUnknown:
  38. return true
  39. }
  40. return false
  41. }
  42. // continueOnError returns true if we should fallback to the next endpoint
  43. // as a result of this error.
  44. func continueOnError(err error) bool {
  45. switch v := err.(type) {
  46. case errcode.Errors:
  47. if len(v) == 0 {
  48. return true
  49. }
  50. return continueOnError(v[0])
  51. case ErrNoSupport:
  52. return continueOnError(v.Err)
  53. case errcode.Error:
  54. return shouldV2Fallback(v)
  55. case *client.UnexpectedHTTPResponseError:
  56. return true
  57. case ImageConfigPullError:
  58. return false
  59. case error:
  60. return !strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error()))
  61. }
  62. // let's be nice and fallback if the error is a completely
  63. // unexpected one.
  64. // If new errors have to be handled in some way, please
  65. // add them to the switch above.
  66. return true
  67. }
  68. // retryOnError wraps the error in xfer.DoNotRetry if we should not retry the
  69. // operation after this error.
  70. func retryOnError(err error) error {
  71. switch v := err.(type) {
  72. case errcode.Errors:
  73. return retryOnError(v[0])
  74. case errcode.Error:
  75. switch v.Code {
  76. case errcode.ErrorCodeUnauthorized, errcode.ErrorCodeUnsupported, errcode.ErrorCodeDenied:
  77. return xfer.DoNotRetry{Err: err}
  78. }
  79. case *url.Error:
  80. return retryOnError(v.Err)
  81. case *client.UnexpectedHTTPResponseError:
  82. return xfer.DoNotRetry{Err: err}
  83. case error:
  84. if strings.Contains(err.Error(), strings.ToLower(syscall.ENOSPC.Error())) {
  85. return xfer.DoNotRetry{Err: err}
  86. }
  87. }
  88. // let's be nice and fallback if the error is a completely
  89. // unexpected one.
  90. // If new errors have to be handled in some way, please
  91. // add them to the switch above.
  92. return err
  93. }