You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
7.9 KiB

  1. // Copyright (c) 2015-2023 MinIO, Inc.
  2. //
  3. // This file is part of MinIO Object Storage stack
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU Affero General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU Affero General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU Affero General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. package cmd
  18. import (
  19. "fmt"
  20. "strings"
  21. "time"
  22. "github.com/dustin/go-humanize"
  23. "github.com/minio/pkg/v3/wildcard"
  24. "gopkg.in/yaml.v3"
  25. )
  26. //go:generate msgp -file $GOFILE
  27. //msgp:ignore BatchJobYamlErr
  28. // BatchJobYamlErr can be used to return yaml validation errors with line,
  29. // column information guiding user to fix syntax errors
  30. type BatchJobYamlErr struct {
  31. line, col int
  32. msg string
  33. }
  34. // message returns the error message excluding line, col information.
  35. // Intended to be used in unit tests.
  36. func (b BatchJobYamlErr) message() string {
  37. return b.msg
  38. }
  39. // Error implements Error interface
  40. func (b BatchJobYamlErr) Error() string {
  41. return fmt.Sprintf("%s\n Hint: error near line: %d, col: %d", b.msg, b.line, b.col)
  42. }
  43. // BatchJobKV is a key-value data type which supports wildcard matching
  44. type BatchJobKV struct {
  45. line, col int
  46. Key string `yaml:"key" json:"key"`
  47. Value string `yaml:"value" json:"value"`
  48. }
  49. var _ yaml.Unmarshaler = &BatchJobKV{}
  50. // UnmarshalYAML - BatchJobKV extends default unmarshal to extract line, col information.
  51. func (kv *BatchJobKV) UnmarshalYAML(val *yaml.Node) error {
  52. type jobKV BatchJobKV
  53. var tmp jobKV
  54. err := val.Decode(&tmp)
  55. if err != nil {
  56. return err
  57. }
  58. *kv = BatchJobKV(tmp)
  59. kv.line, kv.col = val.Line, val.Column
  60. return nil
  61. }
  62. // Validate returns an error if key is empty
  63. func (kv BatchJobKV) Validate() error {
  64. if kv.Key == "" {
  65. return BatchJobYamlErr{
  66. line: kv.line,
  67. col: kv.col,
  68. msg: "key can't be empty",
  69. }
  70. }
  71. return nil
  72. }
  73. // Empty indicates if kv is not set
  74. func (kv BatchJobKV) Empty() bool {
  75. return kv.Key == "" && kv.Value == ""
  76. }
  77. // Match matches input kv with kv, value will be wildcard matched depending on the user input
  78. func (kv BatchJobKV) Match(ikv BatchJobKV) bool {
  79. if kv.Empty() {
  80. return true
  81. }
  82. if strings.EqualFold(kv.Key, ikv.Key) {
  83. return wildcard.Match(kv.Value, ikv.Value)
  84. }
  85. return false
  86. }
  87. // BatchJobNotification stores notification endpoint and token information.
  88. // Used by batch jobs to notify of their status.
  89. type BatchJobNotification struct {
  90. line, col int
  91. Endpoint string `yaml:"endpoint" json:"endpoint"`
  92. Token string `yaml:"token" json:"token"`
  93. }
  94. var _ yaml.Unmarshaler = &BatchJobNotification{}
  95. // UnmarshalYAML - BatchJobNotification extends unmarshal to extract line, column information
  96. func (b *BatchJobNotification) UnmarshalYAML(val *yaml.Node) error {
  97. type notification BatchJobNotification
  98. var tmp notification
  99. err := val.Decode(&tmp)
  100. if err != nil {
  101. return err
  102. }
  103. *b = BatchJobNotification(tmp)
  104. b.line, b.col = val.Line, val.Column
  105. return nil
  106. }
  107. // BatchJobRetry stores retry configuration used in the event of failures.
  108. type BatchJobRetry struct {
  109. line, col int
  110. Attempts int `yaml:"attempts" json:"attempts"` // number of retry attempts
  111. Delay time.Duration `yaml:"delay" json:"delay"` // delay between each retries
  112. }
  113. var _ yaml.Unmarshaler = &BatchJobRetry{}
  114. // UnmarshalYAML - BatchJobRetry extends unmarshal to extract line, column information
  115. func (r *BatchJobRetry) UnmarshalYAML(val *yaml.Node) error {
  116. type retry BatchJobRetry
  117. var tmp retry
  118. err := val.Decode(&tmp)
  119. if err != nil {
  120. return err
  121. }
  122. *r = BatchJobRetry(tmp)
  123. r.line, r.col = val.Line, val.Column
  124. return nil
  125. }
  126. // Validate validates input replicate retries.
  127. func (r BatchJobRetry) Validate() error {
  128. if r.Attempts < 0 {
  129. return BatchJobYamlErr{
  130. line: r.line,
  131. col: r.col,
  132. msg: "Invalid arguments specified",
  133. }
  134. }
  135. if r.Delay < 0 {
  136. return BatchJobYamlErr{
  137. line: r.line,
  138. col: r.col,
  139. msg: "Invalid arguments specified",
  140. }
  141. }
  142. return nil
  143. }
  144. // # snowball based archive transfer is by default enabled when source
  145. // # is local and target is remote which is also minio.
  146. // snowball:
  147. // disable: false # optionally turn-off snowball archive transfer
  148. // batch: 100 # upto this many objects per archive
  149. // inmemory: true # indicates if the archive must be staged locally or in-memory
  150. // compress: true # S2/Snappy compressed archive
  151. // smallerThan: 5MiB # create archive for all objects smaller than 5MiB
  152. // skipErrs: false # skips any source side read() errors
  153. // BatchJobSnowball describes the snowball feature when replicating objects from a local source to a remote target
  154. type BatchJobSnowball struct {
  155. line, col int
  156. Disable *bool `yaml:"disable" json:"disable"`
  157. Batch *int `yaml:"batch" json:"batch"`
  158. InMemory *bool `yaml:"inmemory" json:"inmemory"`
  159. Compress *bool `yaml:"compress" json:"compress"`
  160. SmallerThan *string `yaml:"smallerThan" json:"smallerThan"`
  161. SkipErrs *bool `yaml:"skipErrs" json:"skipErrs"`
  162. }
  163. var _ yaml.Unmarshaler = &BatchJobSnowball{}
  164. // UnmarshalYAML - BatchJobSnowball extends unmarshal to extract line, column information
  165. func (b *BatchJobSnowball) UnmarshalYAML(val *yaml.Node) error {
  166. type snowball BatchJobSnowball
  167. var tmp snowball
  168. err := val.Decode(&tmp)
  169. if err != nil {
  170. return err
  171. }
  172. *b = BatchJobSnowball(tmp)
  173. b.line, b.col = val.Line, val.Column
  174. return nil
  175. }
  176. // Validate the snowball parameters in the job description
  177. func (b BatchJobSnowball) Validate() error {
  178. if *b.Batch <= 0 {
  179. return BatchJobYamlErr{
  180. line: b.line,
  181. col: b.col,
  182. msg: "batch number should be non positive zero",
  183. }
  184. }
  185. _, err := humanize.ParseBytes(*b.SmallerThan)
  186. if err != nil {
  187. return BatchJobYamlErr{
  188. line: b.line,
  189. col: b.col,
  190. msg: err.Error(),
  191. }
  192. }
  193. return nil
  194. }
  195. // BatchJobSizeFilter supports size based filters - LesserThan and GreaterThan
  196. type BatchJobSizeFilter struct {
  197. line, col int
  198. UpperBound BatchJobSize `yaml:"lessThan" json:"lessThan"`
  199. LowerBound BatchJobSize `yaml:"greaterThan" json:"greaterThan"`
  200. }
  201. // UnmarshalYAML - BatchJobSizeFilter extends unmarshal to extract line, column information
  202. func (sf *BatchJobSizeFilter) UnmarshalYAML(val *yaml.Node) error {
  203. type sizeFilter BatchJobSizeFilter
  204. var tmp sizeFilter
  205. err := val.Decode(&tmp)
  206. if err != nil {
  207. return err
  208. }
  209. *sf = BatchJobSizeFilter(tmp)
  210. sf.line, sf.col = val.Line, val.Column
  211. return nil
  212. }
  213. // InRange returns true in the following cases and false otherwise,
  214. // - sf.LowerBound < sz, when sf.LowerBound alone is specified
  215. // - sz < sf.UpperBound, when sf.UpperBound alone is specified
  216. // - sf.LowerBound < sz < sf.UpperBound when both are specified,
  217. func (sf BatchJobSizeFilter) InRange(sz int64) bool {
  218. if sf.UpperBound > 0 && sz > int64(sf.UpperBound) {
  219. return false
  220. }
  221. if sf.LowerBound > 0 && sz < int64(sf.LowerBound) {
  222. return false
  223. }
  224. return true
  225. }
  226. // Validate checks if sf is a valid batch-job size filter
  227. func (sf BatchJobSizeFilter) Validate() error {
  228. if sf.LowerBound > 0 && sf.UpperBound > 0 && sf.LowerBound >= sf.UpperBound {
  229. return BatchJobYamlErr{
  230. line: sf.line,
  231. col: sf.col,
  232. msg: "invalid batch-job size filter",
  233. }
  234. }
  235. return nil
  236. }
  237. // BatchJobSize supports humanized byte values in yaml files type BatchJobSize uint64
  238. type BatchJobSize int64
  239. // UnmarshalYAML to parse humanized byte values
  240. func (s *BatchJobSize) UnmarshalYAML(unmarshal func(interface{}) error) error {
  241. var batchExpireSz string
  242. err := unmarshal(&batchExpireSz)
  243. if err != nil {
  244. return err
  245. }
  246. sz, err := humanize.ParseBytes(batchExpireSz)
  247. if err != nil {
  248. return err
  249. }
  250. *s = BatchJobSize(sz)
  251. return nil
  252. }