github.com/minio/madmin-go@v1.7.5/batch-job.go (about) 1 // 2 // MinIO Object Storage (c) 2022 MinIO, Inc. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 // 16 17 package madmin 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 "net/http" 26 "net/url" 27 "time" 28 ) 29 30 // BatchJobType type to describe batch job types 31 type BatchJobType string 32 33 const ( 34 BatchJobReplicate BatchJobType = "replicate" 35 ) 36 37 // SupportedJobTypes supported job types 38 var SupportedJobTypes = []BatchJobType{ 39 BatchJobReplicate, 40 // add new job types 41 } 42 43 // BatchJobReplicateTemplate provides a sample template 44 // for batch replication 45 const BatchJobReplicateTemplate = `replicate: 46 apiVersion: v1 47 # source of the objects to be replicated 48 source: 49 type: TYPE # valid values are "minio" 50 bucket: BUCKET 51 prefix: PREFIX # 'PREFIX' is optional 52 # NOTE: if source is remote then target must be "local" 53 # endpoint: ENDPOINT 54 # credentials: 55 # accessKey: ACCESS-KEY 56 # secretKey: SECRET-KEY 57 # sessionToken: SESSION-TOKEN # Optional only available when rotating credentials are used 58 59 # target where the objects must be replicated 60 target: 61 type: TYPE # valid values are "minio" 62 bucket: BUCKET 63 prefix: PREFIX # 'PREFIX' is optional 64 # NOTE: if target is remote then source must be "local" 65 # endpoint: ENDPOINT 66 # credentials: 67 # accessKey: ACCESS-KEY 68 # secretKey: SECRET-KEY 69 # sessionToken: SESSION-TOKEN # Optional only available when rotating credentials are used 70 71 # NOTE: All flags are optional 72 # - filtering criteria only applies for all source objects match the criteria 73 # - configurable notification endpoints 74 # - configurable retries for the job (each retry skips successfully previously replaced objects) 75 flags: 76 filter: 77 newerThan: "7d" # match objects newer than this value (e.g. 7d10h31s) 78 olderThan: "7d" # match objects older than this value (e.g. 7d10h31s) 79 createdAfter: "date" # match objects created after "date" 80 createdBefore: "date" # match objects created before "date" 81 82 ## NOTE: tags are not supported when "source" is remote. 83 # tags: 84 # - key: "name" 85 # value: "pick*" # match objects with tag 'name', with all values starting with 'pick' 86 87 ## NOTE: metadata filter not supported when "source" is non MinIO. 88 # metadata: 89 # - key: "content-type" 90 # value: "image/*" # match objects with 'content-type', with all values starting with 'image/' 91 92 notify: 93 endpoint: "https://notify.endpoint" # notification endpoint to receive job status events 94 token: "Bearer xxxxx" # optional authentication token for the notification endpoint 95 96 retry: 97 attempts: 10 # number of retries for the job before giving up 98 delay: "500ms" # least amount of delay between each retry 99 ` 100 101 // BatchJobResult returned by StartBatchJob 102 type BatchJobResult struct { 103 ID string `json:"id"` 104 Type BatchJobType `json:"type"` 105 User string `json:"user,omitempty"` 106 Started time.Time `json:"started"` 107 Elapsed time.Duration `json:"elapsed,omitempty"` 108 } 109 110 // StartBatchJob start a new batch job, input job description is in YAML. 111 func (adm *AdminClient) StartBatchJob(ctx context.Context, job string) (BatchJobResult, error) { 112 resp, err := adm.executeMethod(ctx, http.MethodPost, 113 requestData{ 114 relPath: adminAPIPrefix + "/start-job", 115 content: []byte(job), 116 }, 117 ) 118 if err != nil { 119 return BatchJobResult{}, err 120 } 121 defer closeResponse(resp) 122 if resp.StatusCode != http.StatusOK { 123 return BatchJobResult{}, httpRespToErrorResponse(resp) 124 } 125 126 res := BatchJobResult{} 127 dec := json.NewDecoder(resp.Body) 128 if err = dec.Decode(&res); err != nil { 129 return res, err 130 } 131 132 return res, nil 133 } 134 135 // DescribeBatchJob - describes a currently running Job. 136 func (adm *AdminClient) DescribeBatchJob(ctx context.Context, jobID string) (string, error) { 137 values := make(url.Values) 138 values.Set("jobId", jobID) 139 140 resp, err := adm.executeMethod(ctx, http.MethodGet, 141 requestData{ 142 relPath: adminAPIPrefix + "/describe-job", 143 queryValues: values, 144 }, 145 ) 146 if err != nil { 147 return "", err 148 } 149 defer closeResponse(resp) 150 if resp.StatusCode != http.StatusOK { 151 return "", httpRespToErrorResponse(resp) 152 } 153 154 buf, err := io.ReadAll(resp.Body) 155 if err != nil { 156 return "", err 157 } 158 159 return string(buf), nil 160 } 161 162 // GenerateBatchJobOpts is to be implemented in future. 163 type GenerateBatchJobOpts struct { 164 Type BatchJobType 165 } 166 167 // GenerateBatchJob creates a new job template from standard template 168 // TODO: allow configuring yaml values 169 func (adm *AdminClient) GenerateBatchJob(ctx context.Context, opts GenerateBatchJobOpts) (string, error) { 170 if opts.Type == BatchJobReplicate { 171 // TODO: allow configuring the template to fill values from GenerateBatchJobOpts 172 return BatchJobReplicateTemplate, nil 173 } 174 return "", fmt.Errorf("unsupported batch type requested: %s", opts.Type) 175 } 176 177 // ListBatchJobsResult contains entries for all current jobs. 178 type ListBatchJobsResult struct { 179 Jobs []BatchJobResult `json:"jobs"` 180 } 181 182 // ListBatchJobsFilter returns list based on following 183 // filtering params. 184 type ListBatchJobsFilter struct { 185 ByJobType string 186 } 187 188 // ListBatchJobs list all the currently active batch jobs 189 func (adm *AdminClient) ListBatchJobs(ctx context.Context, fl *ListBatchJobsFilter) (ListBatchJobsResult, error) { 190 if fl == nil { 191 return ListBatchJobsResult{}, errors.New("ListBatchJobsFilter cannot be nil") 192 } 193 194 values := make(url.Values) 195 values.Set("jobType", fl.ByJobType) 196 197 resp, err := adm.executeMethod(ctx, http.MethodGet, 198 requestData{ 199 relPath: adminAPIPrefix + "/list-jobs", 200 queryValues: values, 201 }, 202 ) 203 if err != nil { 204 return ListBatchJobsResult{}, err 205 } 206 defer closeResponse(resp) 207 208 if resp.StatusCode != http.StatusOK { 209 return ListBatchJobsResult{}, httpRespToErrorResponse(resp) 210 } 211 212 d := json.NewDecoder(resp.Body) 213 result := ListBatchJobsResult{} 214 if err = d.Decode(&result); err != nil { 215 return result, err 216 } 217 218 return result, nil 219 }