github.com/minio/madmin-go/v3@v3.0.51/batch-job.go (about) 1 // 2 // Copyright (c) 2015-2022 MinIO, Inc. 3 // 4 // This file is part of MinIO Object Storage stack 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Affero General Public License as 8 // published by the Free Software Foundation, either version 3 of the 9 // License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Affero General Public License for more details. 15 // 16 // You should have received a copy of the GNU Affero General Public License 17 // along with this program. If not, see <http://www.gnu.org/licenses/>. 18 // 19 20 package madmin 21 22 import ( 23 "context" 24 "encoding/json" 25 "errors" 26 "fmt" 27 "io" 28 "net/http" 29 "net/url" 30 "time" 31 ) 32 33 // BatchJobType type to describe batch job types 34 type BatchJobType string 35 36 const ( 37 BatchJobReplicate BatchJobType = "replicate" 38 BatchJobKeyRotate BatchJobType = "keyrotate" 39 BatchJobExpire BatchJobType = "expire" 40 ) 41 42 // SupportedJobTypes supported job types 43 var SupportedJobTypes = []BatchJobType{ 44 BatchJobReplicate, 45 BatchJobKeyRotate, 46 BatchJobExpire, 47 // add new job types 48 } 49 50 // BatchJobReplicateTemplate provides a sample template 51 // for batch replication 52 const BatchJobReplicateTemplate = `replicate: 53 apiVersion: v1 54 # source of the objects to be replicated 55 source: 56 type: TYPE # valid values are "s3" or "minio" 57 bucket: BUCKET 58 prefix: PREFIX # 'PREFIX' is optional 59 # If your source is the 'local' alias specified to 'mc batch start', then the 'endpoint' and 'credentials' fields are optional and can be omitted 60 # Either the 'source' or 'remote' *must* be the "local" deployment 61 endpoint: "http[s]://HOSTNAME:PORT" 62 # path: "on|off|auto" # "on" enables path-style bucket lookup. "off" enables virtual host (DNS)-style bucket lookup. Defaults to "auto" 63 credentials: 64 accessKey: ACCESS-KEY # Required 65 secretKey: SECRET-KEY # Required 66 # sessionToken: SESSION-TOKEN # Optional only available when rotating credentials are used 67 snowball: # automatically activated if the source is local 68 disable: false # optionally turn-off snowball archive transfer 69 batch: 100 # upto this many objects per archive 70 inmemory: true # indicates if the archive must be staged locally or in-memory 71 compress: false # S2/Snappy compressed archive 72 smallerThan: 5MiB # create archive for all objects smaller than 5MiB 73 skipErrs: false # skips any source side read() errors 74 75 # target where the objects must be replicated 76 target: 77 type: TYPE # valid values are "s3" or "minio" 78 bucket: BUCKET 79 prefix: PREFIX # 'PREFIX' is optional 80 # If your source is the 'local' alias specified to 'mc batch start', then the 'endpoint' and 'credentials' fields are optional and can be omitted 81 82 # Either the 'source' or 'remote' *must* be the "local" deployment 83 endpoint: "http[s]://HOSTNAME:PORT" 84 # path: "on|off|auto" # "on" enables path-style bucket lookup. "off" enables virtual host (DNS)-style bucket lookup. Defaults to "auto" 85 credentials: 86 accessKey: ACCESS-KEY 87 secretKey: SECRET-KEY 88 # sessionToken: SESSION-TOKEN # Optional only available when rotating credentials are used 89 90 # NOTE: All flags are optional 91 # - filtering criteria only applies for all source objects match the criteria 92 # - configurable notification endpoints 93 # - configurable retries for the job (each retry skips successfully previously replaced objects) 94 flags: 95 filter: 96 newerThan: "7d" # match objects newer than this value (e.g. 7d10h31s) 97 olderThan: "7d" # match objects older than this value (e.g. 7d10h31s) 98 createdAfter: "date" # match objects created after "date" 99 createdBefore: "date" # match objects created before "date" 100 101 ## NOTE: tags are not supported when "source" is remote. 102 # tags: 103 # - key: "name" 104 # value: "pick*" # match objects with tag 'name', with all values starting with 'pick' 105 106 # metadata: 107 # - key: "content-type" 108 # value: "image/*" # match objects with 'content-type', with all values starting with 'image/' 109 110 notify: 111 endpoint: "https://notify.endpoint" # notification endpoint to receive job status events 112 token: "Bearer xxxxx" # optional authentication token for the notification endpoint 113 114 retry: 115 attempts: 10 # number of retries for the job before giving up 116 delay: "500ms" # least amount of delay between each retry 117 ` 118 119 // BatchJobKeyRotateTemplate provides a sample template 120 // for batch key rotation 121 const BatchJobKeyRotateTemplate = `keyrotate: 122 apiVersion: v1 123 bucket: BUCKET 124 prefix: PREFIX 125 encryption: 126 type: sse-s3 # valid values are sse-s3 and sse-kms 127 key: <new-kms-key> # valid only for sse-kms 128 context: <new-kms-key-context> # valid only for sse-kms 129 130 # optional flags based filtering criteria 131 # for all objects 132 flags: 133 filter: 134 newerThan: "7d" # match objects newer than this value (e.g. 7d10h31s) 135 olderThan: "7d" # match objects older than this value (e.g. 7d10h31s) 136 createdAfter: "date" # match objects created after "date" 137 createdBefore: "date" # match objects created before "date" 138 tags: 139 - key: "name" 140 value: "pick*" # match objects with tag 'name', with all values starting with 'pick' 141 metadata: 142 - key: "content-type" 143 value: "image/*" # match objects with 'content-type', with all values starting with 'image/' 144 kmskey: "key-id" # match objects with KMS key-id (applicable only for sse-kms) 145 notify: 146 endpoint: "https://notify.endpoint" # notification endpoint to receive job status events 147 token: "Bearer xxxxx" # optional authentication token for the notification endpoint 148 retry: 149 attempts: 10 # number of retries for the job before giving up 150 delay: "500ms" # least amount of delay between each retry 151 ` 152 153 // BatchJobExpireTemplate provides a sample template 154 // for batch expiring objects 155 const BatchJobExpireTemplate = `expire: 156 apiVersion: v1 157 bucket: mybucket # Bucket where this job will expire matching objects from 158 prefix: myprefix # (Optional) Prefix under which this job will expire objects matching the rules below. 159 rules: 160 - type: object # objects with zero ore more older versions 161 name: NAME # match object names that satisfy the wildcard expression. 162 olderThan: 70h # match objects older than this value 163 createdBefore: "2006-01-02T15:04:05.00Z" # match objects created before "date" 164 tags: 165 - key: name 166 value: pick* # match objects with tag 'name', all values starting with 'pick' 167 metadata: 168 - key: content-type 169 value: image/* # match objects with 'content-type', all values starting with 'image/' 170 size: 171 lessThan: 10MiB # match objects with size less than this value (e.g. 10MiB) 172 greaterThan: 1MiB # match objects with size greater than this value (e.g. 1MiB) 173 purge: 174 # retainVersions: 0 # (default) delete all versions of the object. This option is the fastest. 175 # retainVersions: 5 # keep the latest 5 versions of the object. 176 177 - type: deleted # objects with delete marker as their latest version 178 name: NAME # match object names that satisfy the wildcard expression. 179 olderThan: 10h # match objects older than this value (e.g. 7d10h31s) 180 createdBefore: "2006-01-02T15:04:05.00Z" # match objects created before "date" 181 purge: 182 # retainVersions: 0 # (default) delete all versions of the object. This option is the fastest. 183 # retainVersions: 5 # keep the latest 5 versions of the object including delete markers. 184 185 notify: 186 endpoint: https://notify.endpoint # notification endpoint to receive job completion status 187 token: Bearer xxxxx # optional authentication token for the notification endpoint 188 189 retry: 190 attempts: 10 # number of retries for the job before giving up 191 delay: 500ms # least amount of delay between each retry 192 ` 193 194 // BatchJobResult returned by StartBatchJob 195 type BatchJobResult struct { 196 ID string `json:"id"` 197 Type BatchJobType `json:"type"` 198 User string `json:"user,omitempty"` 199 Started time.Time `json:"started"` 200 Elapsed time.Duration `json:"elapsed,omitempty"` 201 } 202 203 // StartBatchJob start a new batch job, input job description is in YAML. 204 func (adm *AdminClient) StartBatchJob(ctx context.Context, job string) (BatchJobResult, error) { 205 resp, err := adm.executeMethod(ctx, http.MethodPost, 206 requestData{ 207 relPath: adminAPIPrefix + "/start-job", 208 content: []byte(job), 209 }, 210 ) 211 if err != nil { 212 return BatchJobResult{}, err 213 } 214 defer closeResponse(resp) 215 if resp.StatusCode != http.StatusOK { 216 return BatchJobResult{}, httpRespToErrorResponse(resp) 217 } 218 219 res := BatchJobResult{} 220 dec := json.NewDecoder(resp.Body) 221 if err = dec.Decode(&res); err != nil { 222 return res, err 223 } 224 225 return res, nil 226 } 227 228 // DescribeBatchJob - describes a currently running Job. 229 func (adm *AdminClient) DescribeBatchJob(ctx context.Context, jobID string) (string, error) { 230 values := make(url.Values) 231 values.Set("jobId", jobID) 232 233 resp, err := adm.executeMethod(ctx, http.MethodGet, 234 requestData{ 235 relPath: adminAPIPrefix + "/describe-job", 236 queryValues: values, 237 }, 238 ) 239 if err != nil { 240 return "", err 241 } 242 defer closeResponse(resp) 243 if resp.StatusCode != http.StatusOK { 244 return "", httpRespToErrorResponse(resp) 245 } 246 247 buf, err := io.ReadAll(resp.Body) 248 if err != nil { 249 return "", err 250 } 251 252 return string(buf), nil 253 } 254 255 // GenerateBatchJobOpts is to be implemented in future. 256 type GenerateBatchJobOpts struct { 257 Type BatchJobType 258 } 259 260 // GenerateBatchJob creates a new job template from standard template 261 // TODO: allow configuring yaml values 262 func (adm *AdminClient) GenerateBatchJob(_ context.Context, opts GenerateBatchJobOpts) (string, error) { 263 // TODO: allow configuring the template to fill values from GenerateBatchJobOpts 264 switch opts.Type { 265 case BatchJobReplicate: 266 return BatchJobReplicateTemplate, nil 267 case BatchJobKeyRotate: 268 return BatchJobKeyRotateTemplate, nil 269 case BatchJobExpire: 270 return BatchJobExpireTemplate, nil 271 } 272 return "", fmt.Errorf("unknown batch job requested: %s", opts.Type) 273 } 274 275 // ListBatchJobsResult contains entries for all current jobs. 276 type ListBatchJobsResult struct { 277 Jobs []BatchJobResult `json:"jobs"` 278 } 279 280 // ListBatchJobsFilter returns list based on following 281 // filtering params. 282 type ListBatchJobsFilter struct { 283 ByJobType string 284 } 285 286 // ListBatchJobs list all the currently active batch jobs 287 func (adm *AdminClient) ListBatchJobs(ctx context.Context, fl *ListBatchJobsFilter) (ListBatchJobsResult, error) { 288 if fl == nil { 289 return ListBatchJobsResult{}, errors.New("ListBatchJobsFilter cannot be nil") 290 } 291 292 values := make(url.Values) 293 values.Set("jobType", fl.ByJobType) 294 295 resp, err := adm.executeMethod(ctx, http.MethodGet, 296 requestData{ 297 relPath: adminAPIPrefix + "/list-jobs", 298 queryValues: values, 299 }, 300 ) 301 if err != nil { 302 return ListBatchJobsResult{}, err 303 } 304 defer closeResponse(resp) 305 306 if resp.StatusCode != http.StatusOK { 307 return ListBatchJobsResult{}, httpRespToErrorResponse(resp) 308 } 309 310 d := json.NewDecoder(resp.Body) 311 result := ListBatchJobsResult{} 312 if err = d.Decode(&result); err != nil { 313 return result, err 314 } 315 316 return result, nil 317 } 318 319 // CancelBatchJob cancels ongoing batch job. 320 func (adm *AdminClient) CancelBatchJob(ctx context.Context, jobID string) error { 321 values := make(url.Values) 322 values.Set("id", jobID) 323 324 resp, err := adm.executeMethod(ctx, http.MethodDelete, 325 requestData{ 326 relPath: adminAPIPrefix + "/cancel-job", 327 queryValues: values, 328 }, 329 ) 330 if err != nil { 331 return err 332 } 333 defer closeResponse(resp) 334 if resp.StatusCode != http.StatusNoContent { 335 return httpRespToErrorResponse(resp) 336 } 337 return nil 338 }