github.com/minio/mc@v0.0.0-20240503112107-b471de8d1882/cmd/legalhold-main.go (about)

     1  // Copyright (c) 2015-2022 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  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"errors"
    23  	"fmt"
    24  	"net/http"
    25  	"strings"
    26  
    27  	"github.com/minio/cli"
    28  	json "github.com/minio/colorjson"
    29  	"github.com/minio/mc/pkg/probe"
    30  	minio "github.com/minio/minio-go/v7"
    31  	"github.com/minio/pkg/v2/console"
    32  )
    33  
    34  var legalHoldSubcommands = []cli.Command{
    35  	legalHoldSetCmd,
    36  	legalHoldClearCmd,
    37  	legalHoldInfoCmd,
    38  }
    39  
    40  var legalHoldCmd = cli.Command{
    41  	Name:        "legalhold",
    42  	Usage:       "manage legal hold for object(s)",
    43  	Action:      mainLegalHold,
    44  	Before:      setGlobalsFromContext,
    45  	Flags:       globalFlags,
    46  	Subcommands: legalHoldSubcommands,
    47  }
    48  
    49  // Structured message depending on the type of console.
    50  type legalHoldCmdMessage struct {
    51  	LegalHold minio.LegalHoldStatus `json:"legalhold"`
    52  	URLPath   string                `json:"urlpath"`
    53  	Key       string                `json:"key"`
    54  	VersionID string                `json:"versionID"`
    55  	Status    string                `json:"status"`
    56  	Err       error                 `json:"error,omitempty"`
    57  }
    58  
    59  // Colorized message for console printing.
    60  func (l legalHoldCmdMessage) String() string {
    61  	if l.Err != nil {
    62  		return console.Colorize("LegalHoldMessageFailure", "Unable to set object legal hold status `"+l.Key+"`. "+l.Err.Error())
    63  	}
    64  	op := "set"
    65  	if l.LegalHold == minio.LegalHoldDisabled {
    66  		op = "cleared"
    67  	}
    68  
    69  	msg := fmt.Sprintf("Object legal hold successfully %s for `%s`", op, l.Key)
    70  	if l.VersionID != "" {
    71  		msg += fmt.Sprintf(" (version-id=%s)", l.VersionID)
    72  	}
    73  	msg += "."
    74  	return console.Colorize("LegalHoldSuccess", msg)
    75  }
    76  
    77  // JSON'ified message for scripting.
    78  func (l legalHoldCmdMessage) JSON() string {
    79  	msgBytes, e := json.MarshalIndent(l, "", " ")
    80  	fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
    81  	return string(msgBytes)
    82  }
    83  
    84  var (
    85  	errObjectLockConfigNotFound = errors.New("object locking is not configured")
    86  	errObjectLockNotSupported   = errors.New("object locking is not supported")
    87  )
    88  
    89  // Return true if this an S3 bucket with locking enabled
    90  // Return false if this an S3 bucket with no locking enabled
    91  // Return false if this is a filesystem URL
    92  // Otherwise return unexpected errors
    93  func isBucketLockEnabled(ctx context.Context, aliasedURL string) (bool, *probe.Error) {
    94  	st, err := getBucketLockStatus(ctx, aliasedURL)
    95  	if err != nil {
    96  		switch err.ToGoError() {
    97  		case errObjectLockConfigNotFound, errObjectLockNotSupported:
    98  			return false, nil
    99  		}
   100  		return false, err
   101  	}
   102  	return st == "Enabled", nil
   103  }
   104  
   105  // Check if the bucket corresponding to the target url has object locking enabled
   106  func getBucketLockStatus(ctx context.Context, aliasedURL string) (status string, err *probe.Error) {
   107  	clnt, err := newClient(aliasedURL)
   108  	if err != nil {
   109  		return "", err
   110  	}
   111  
   112  	// Remove the prefix/object from the aliased url and reconstruct the client
   113  	switch c := clnt.(type) {
   114  	case *S3Client:
   115  		_, object := c.url2BucketAndObject()
   116  		if object != "" {
   117  			clnt, _ = newClient(strings.TrimSuffix(aliasedURL, object))
   118  		}
   119  	default:
   120  		return "", probe.NewError(errObjectLockNotSupported)
   121  	}
   122  
   123  	status, _, _, _, err = clnt.GetObjectLockConfig(ctx)
   124  	if err != nil {
   125  		errResp := minio.ToErrorResponse(err.ToGoError())
   126  		switch {
   127  		case errResp.Code == "ObjectLockConfigurationNotFoundError":
   128  			return "", probe.NewError(errObjectLockConfigNotFound)
   129  		case errResp.StatusCode == http.StatusNotImplemented:
   130  			return "", probe.NewError(errObjectLockNotSupported)
   131  		}
   132  		return "", err
   133  	}
   134  
   135  	return status, nil
   136  }
   137  
   138  // main for retention command.
   139  func mainLegalHold(ctx *cli.Context) error {
   140  	commandNotFound(ctx, legalHoldSubcommands)
   141  	return nil
   142  }