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  }