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  }