1
0
mirror of https://github.com/minio/mc.git synced 2025-11-12 01:02:26 +03:00
Files
mc/cmd/rm-main.go
Bala FA 6de2b4559e Refactor rm command. (#1855)
This patch makes rm command to use minio-go.RemoveObjects() to removeobjects recursively.
2016-10-17 20:24:09 -07:00

302 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Minio Client (C) 2014, 2015 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"bufio"
"encoding/json"
"fmt"
"os"
"time"
"github.com/fatih/color"
"github.com/minio/cli"
"github.com/minio/mc/pkg/console"
"github.com/minio/minio/pkg/probe"
)
// Day time.Duration for day.
const Day = 24 * time.Hour
// rm specific flags.
var (
rmFlags = []cli.Flag{
cli.BoolFlag{
Name: "help, h",
Usage: "Show this help.",
},
cli.BoolFlag{
Name: "recursive, r",
Usage: "Remove recursively.",
},
cli.BoolFlag{
Name: "force",
Usage: "Force a dangerous remove operation.",
},
cli.BoolFlag{
Name: "incomplete, I",
Usage: "Remove an incomplete objects.",
},
cli.BoolFlag{
Name: "fake",
Usage: "Perform a fake remove operation.",
},
cli.BoolFlag{
Name: "stdin",
Usage: "Read object names from STDIN.",
},
cli.IntFlag{
Name: "older-than",
Usage: "Remove objects older than N days.",
},
}
)
// remove a file or folder.
var rmCmd = cli.Command{
Name: "rm",
Usage: "Remove files and objects.",
Action: mainRm,
Flags: append(rmFlags, globalFlags...),
CustomHelpTemplate: `NAME:
mc {{.Name}} - {{.Usage}}
USAGE:
mc {{.Name}} [FLAGS] TARGET [TARGET ...]
FLAGS:
{{range .Flags}}{{.}}
{{end}}
EXAMPLES:
1. Remove a file.
$ mc {{.Name}} 1999/old-backup.tgz
2. Remove all objects recursively.
$ mc {{.Name}} --recursive s3/jazz-songs/louis/
3. Remove all objects older than '90' days.
$ mc {{.Name}} --recursive --older-than=90 s3/jazz-songs/louis/
4. Remove all objects read from STDIN.
$ mc {{.Name}} --force --stdin
5. Drop all incomplete uploads on 'jazz-songs' bucket.
$ mc {{.Name}} --incomplete --recursive s3/jazz-songs/
`,
}
// Structured message depending on the type of console.
type rmMessage struct {
Key string `json:"key"`
}
// Colorized message for console printing.
func (r rmMessage) String() string {
return console.Colorize("Remove", fmt.Sprintf("Removing %s.", r.Key))
}
// JSON'ified message for scripting.
func (r rmMessage) JSON() string {
msgBytes, e := json.Marshal(r)
fatalIf(probe.NewError(e), "Unable to marshal into JSON.")
return string(msgBytes)
}
// Validate command line arguments.
func checkRmSyntax(ctx *cli.Context) {
// Set command flags from context.
isForce := ctx.Bool("force")
isRecursive := ctx.Bool("recursive")
isStdin := ctx.Bool("stdin")
if !ctx.Args().Present() && !isStdin {
exitCode := 1
cli.ShowCommandHelpAndExit(ctx, "rm", exitCode)
}
// For all recursive operations make sure to check for 'force' flag.
if (isRecursive || isStdin) && !isForce {
fatalIf(errDummy().Trace(),
"Removal requires --force option. This operational is *IRREVERSIBLE*. Please review carefully before performing this *DANGEROUS* operation.")
}
}
func removeSingle(url string, isIncomplete bool, isFake bool, older int) {
targetAlias, targetURL, _ := mustExpandAlias(url)
clnt, pErr := newClientFromAlias(targetAlias, targetURL)
if pErr != nil {
errorIf(pErr.Trace(url), "Invalid argument "+url+".")
return // End of journey.
}
if older > 0 {
content, pErr := clnt.Stat(isIncomplete)
if pErr != nil {
errorIf(pErr.Trace(url), "Failed to remove "+url+".")
return
}
// Check whether object is created older than given time.
now := time.Now().UTC()
timeDiff := now.Sub(content.Time)
if timeDiff < (time.Duration(older) * Day) {
// time difference of info.Time with current time is less than older time.
return
}
}
printMsg(rmMessage{Key: url})
if !isFake {
contentCh := make(chan *clientContent, 1)
contentCh <- &clientContent{URL: *newClientURL(targetURL)}
close(contentCh)
errorCh := clnt.Remove(isIncomplete, contentCh)
for pErr := range errorCh {
if pErr != nil {
errorIf(pErr.Trace(url), "Failed to remove "+url+".")
switch pErr.ToGoError().(type) {
case PathInsufficientPermission:
// Ignore Permission error.
continue
}
return
}
}
}
}
func removeRecursive(url string, isIncomplete bool, isFake bool, older int) {
targetAlias, targetURL, _ := mustExpandAlias(url)
clnt, pErr := newClientFromAlias(targetAlias, targetURL)
if pErr != nil {
errorIf(pErr.Trace(url), "Failed to remove "+url+" recursively.")
return // End of journey.
}
contentCh := make(chan *clientContent)
errorCh := clnt.Remove(isIncomplete, contentCh)
isEmpty := true
isRecursive := true
for content := range clnt.List(isRecursive, isIncomplete, DirLast) {
isEmpty = false
pErr := content.Err
if pErr != nil {
errorIf(pErr.Trace(url), "Failed to remove "+url+" recursively.")
switch pErr.ToGoError().(type) {
case PathInsufficientPermission:
// Ignore Permission error.
continue
}
close(contentCh)
return
}
if older > 0 {
// Check whether object is created older than given time.
now := time.Now().UTC()
timeDiff := now.Sub(content.Time)
if timeDiff < (time.Duration(older) * Day) {
// time difference of info.Time with current time is less than older time.
continue
}
}
urlString := content.URL.Path
printMsg(rmMessage{Key: targetAlias + urlString})
if !isFake {
sent := false
for sent == false {
select {
case contentCh <- content:
sent = true
case pErr := <-errorCh:
errorIf(pErr.Trace(urlString), "Failed to remove "+urlString+".")
switch pErr.ToGoError().(type) {
case PathInsufficientPermission:
// Ignore Permission error.
continue
}
close(contentCh)
return
}
}
}
}
close(contentCh)
for pErr := range errorCh {
errorIf(pErr.Trace(url), "Failed to remove "+url+" recursively.")
switch pErr.ToGoError().(type) {
case PathInsufficientPermission:
// Ignore Permission error.
continue
}
return
}
// As clnt.List() returns empty, we just send dummy value to behave like non-recursive.
if isEmpty {
printMsg(rmMessage{Key: url})
}
}
// main for rm command.
func mainRm(ctx *cli.Context) {
// Set global flags from context.
setGlobalsFromContext(ctx)
// check 'rm' cli arguments.
checkRmSyntax(ctx)
// rm specific flags.
isIncomplete := ctx.Bool("incomplete")
isRecursive := ctx.Bool("recursive")
isFake := ctx.Bool("fake")
isStdin := ctx.Bool("stdin")
older := ctx.Int("older-than")
// Set color.
console.SetColor("Remove", color.New(color.FgGreen, color.Bold))
// Support multiple targets.
for _, url := range ctx.Args() {
if isRecursive {
removeRecursive(url, isIncomplete, isFake, older)
} else {
removeSingle(url, isIncomplete, isFake, older)
}
}
if !isStdin {
return
}
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
url := scanner.Text()
if isRecursive {
removeRecursive(url, isIncomplete, isFake, older)
} else {
removeSingle(url, isIncomplete, isFake, older)
}
}
}