From 54268c4808cb17d2fa4c1fb5bc8bcc201a58bb64 Mon Sep 17 00:00:00 2001 From: Taran Pelkey Date: Thu, 5 Sep 2024 07:03:52 -0400 Subject: [PATCH 1/3] Add `edit`, `enable`, and `disable` to `mc idp ldap accesskey` (#5033) --- cmd/auto-complete.go | 3 + cmd/idp-ldap-accesskey-disable.go | 48 +++++++++ cmd/idp-ldap-accesskey-edit.go | 167 ++++++++++++++++++++++++++++++ cmd/idp-ldap-accesskey-enable.go | 84 +++++++++++++++ cmd/idp-ldap-accesskey-info.go | 35 +++---- cmd/idp-ldap-accesskey-remove.go | 4 - cmd/idp-ldap-accesskey.go | 3 + 7 files changed, 322 insertions(+), 22 deletions(-) create mode 100644 cmd/idp-ldap-accesskey-disable.go create mode 100644 cmd/idp-ldap-accesskey-edit.go create mode 100644 cmd/idp-ldap-accesskey-enable.go diff --git a/cmd/auto-complete.go b/cmd/auto-complete.go index b05a6c585e..3d54a01f73 100644 --- a/cmd/auto-complete.go +++ b/cmd/auto-complete.go @@ -388,6 +388,9 @@ var completeCmds = map[string]complete.Predictor{ "/idp/ldap/accesskey/remove": aliasCompleter, "/idp/ldap/accesskey/rm": aliasCompleter, "/idp/ldap/accesskey/info": aliasCompleter, + "/idp/ldap/accesskey/edit": aliasCompleter, + "/idp/ldap/accesskey/enable": aliasCompleter, + "/idp/ldap/accesskey/disable": aliasCompleter, "/admin/policy/info": aliasCompleter, "/admin/policy/update": aliasCompleter, diff --git a/cmd/idp-ldap-accesskey-disable.go b/cmd/idp-ldap-accesskey-disable.go new file mode 100644 index 0000000000..511da3afaf --- /dev/null +++ b/cmd/idp-ldap-accesskey-disable.go @@ -0,0 +1,48 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/minio/cli" +) + +var idpLdapAccesskeyDisableCmd = cli.Command{ + Name: "disable", + Usage: "disable an access key", + Action: mainIDPLdapAccesskeyDisable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Disable LDAP access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainIDPLdapAccesskeyDisable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, false) +} diff --git a/cmd/idp-ldap-accesskey-edit.go b/cmd/idp-ldap-accesskey-edit.go new file mode 100644 index 0000000000..2a8680a3e5 --- /dev/null +++ b/cmd/idp-ldap-accesskey-edit.go @@ -0,0 +1,167 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "bytes" + "errors" + "fmt" + "os" + "time" + + "github.com/minio/cli" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" + "github.com/minio/pkg/v3/policy" +) + +var idpLdapAccesskeyEditFlags = []cli.Flag{ + cli.StringFlag{ + Name: "secret-key", + Usage: "set a secret key for the account", + }, + cli.StringFlag{ + Name: "policy", + Usage: "path to a JSON policy file", + }, + cli.StringFlag{ + Name: "name", + Usage: "friendly name for the account", + }, + cli.StringFlag{ + Name: "description", + Usage: "description for the account", + }, + cli.StringFlag{ + Name: "expiry-duration", + Usage: "duration before the access key expires", + }, + cli.StringFlag{ + Name: "expiry", + Usage: "expiry date for the access key", + }, +} + +var idpLdapAccesskeyEditCmd = cli.Command{ + Name: "edit", + Usage: "edit existing access keys for LDAP", + Action: mainIDPLdapAccesskeyEdit, + Before: setGlobalsFromContext, + Flags: append(idpLdapAccesskeyEditFlags, globalFlags...), + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Change the secret key for the access key "testkey" + {{.Prompt}} {{.HelpName}} myminio/ testkey --secret-key 'xxxxxxx' + 2. Change the expiry duration for the access key "testkey" + {{.Prompt}} {{.HelpName}} myminio/ testkey ---expiry-duration 24h +`, +} + +func mainIDPLdapAccesskeyEdit(ctx *cli.Context) error { + if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { + showCommandHelpAndExit(ctx, 1) // last argument is exit code + } + + args := ctx.Args() + aliasedURL := args.Get(0) + accessKey := args.Get(1) + + opts := accessKeyEditOpts(ctx) + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + + e := client.UpdateServiceAccount(globalContext, accessKey, opts) + fatalIf(probe.NewError(e), "Unable to edit service account.") + + m := ldapAccesskeyMessage{ + op: "edit", + Status: "success", + AccessKey: accessKey, + } + printMsg(m) + + return nil +} + +func accessKeyEditOpts(ctx *cli.Context) madmin.UpdateServiceAccountReq { + name := ctx.String("name") + expVal := ctx.String("expiry") + policyPath := ctx.String("policy") + secretKey := ctx.String("secret-key") + description := ctx.String("description") + expDurVal := ctx.Duration("expiry-duration") + + if expVal != "" && expDurVal != 0 { + fatalIf(probe.NewError(errors.New("Only one of --expiry or --expiry-duration can be specified")), "invalid flags") + } + + opts := madmin.UpdateServiceAccountReq{ + NewName: name, + NewSecretKey: secretKey, + NewDescription: description, + } + + if policyPath != "" { + // Validate the policy document and ensure it has at least one statement + policyBytes, e := os.ReadFile(policyPath) + fatalIf(probe.NewError(e), "unable to read the policy document") + + p, e := policy.ParseConfig(bytes.NewReader(policyBytes)) + fatalIf(probe.NewError(e), "unable to parse the policy document") + + if p.IsEmpty() { + fatalIf(errInvalidArgument(), "empty policies are not allowed") + } + + opts.NewPolicy = policyBytes + } + + switch { + case expVal != "": + location, e := time.LoadLocation("Local") + fatalIf(probe.NewError(e), "unable to load local location. verify your local TZ= settings") + + var found bool + for _, format := range supportedTimeFormats { + t, e := time.ParseInLocation(format, expVal, location) + if e == nil { + found = true + opts.NewExpiration = &t + break + } + } + + if !found { + fatalIf(probe.NewError(fmt.Errorf("invalid expiry date format '%s'", expVal)), "unable to parse the expiry argument") + } + case expDurVal != 0: + t := time.Now().Add(expDurVal) + opts.NewExpiration = &t + } + + return opts +} diff --git a/cmd/idp-ldap-accesskey-enable.go b/cmd/idp-ldap-accesskey-enable.go new file mode 100644 index 0000000000..b32016f639 --- /dev/null +++ b/cmd/idp-ldap-accesskey-enable.go @@ -0,0 +1,84 @@ +// Copyright (c) 2015-2024 MinIO, Inc. +// +// This file is part of MinIO Object Storage stack +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package cmd + +import ( + "github.com/minio/cli" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" +) + +var idpLdapAccesskeyEnableCmd = cli.Command{ + Name: "enable", + Usage: "enable an access key", + Action: mainIDPLdapAccesskeyEnable, + Before: setGlobalsFromContext, + Flags: globalFlags, + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Enable LDAP access key + {{.Prompt}} {{.HelpName}} myminio myaccesskey +`, +} + +func mainIDPLdapAccesskeyEnable(ctx *cli.Context) error { + return enableDisableAccesskey(ctx, true) +} + +func enableDisableAccesskey(ctx *cli.Context, enable bool) error { + if len(ctx.Args()) == 0 || len(ctx.Args()) > 2 { + showCommandHelpAndExit(ctx, 1) // last argument is exit code + } + + args := ctx.Args() + aliasedURL := args.Get(0) + accessKey := args.Get(1) + + client, err := newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + + op := "disable" + status := "off" + if enable { + op = "enable" + status = "on" + } + + e := client.UpdateServiceAccount(globalContext, accessKey, madmin.UpdateServiceAccountReq{ + NewStatus: status, + }) + fatalIf(probe.NewError(e), "Unable to add service account.") + + m := ldapAccesskeyMessage{ + op: op, + Status: "success", + AccessKey: accessKey, + } + printMsg(m) + + return nil +} diff --git a/cmd/idp-ldap-accesskey-info.go b/cmd/idp-ldap-accesskey-info.go index ae0b3146fe..67f42e873a 100644 --- a/cmd/idp-ldap-accesskey-info.go +++ b/cmd/idp-ldap-accesskey-info.go @@ -26,7 +26,6 @@ import ( "github.com/minio/cli" json "github.com/minio/colorjson" "github.com/minio/mc/pkg/probe" - "github.com/minio/pkg/v3/console" ) var idpLdapAccesskeyInfoCmd = cli.Command{ @@ -68,6 +67,8 @@ type ldapAccesskeyMessage struct { } func (m ldapAccesskeyMessage) String() string { + labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green + o := strings.Builder{} switch m.op { case "info": expirationStr := "NONE" @@ -78,39 +79,37 @@ func (m ldapAccesskeyMessage) String() string { if m.ImpliedPolicy { policyStr = "implied" } - - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green - o := strings.Builder{} - + statusStr := "enabled" + if m.AccountStatus == "off" { + statusStr = "disabled" + } o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Access Key:"), m.AccessKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Parent User:"), m.ParentUser)) + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Status:"), statusStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Policy:"), policyStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description)) - o.WriteString(iFmt(0, "%s %s\n\n", labelStyle.Render("Expiration:"), expirationStr)) - - return o.String() - + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Expiration:"), expirationStr)) case "create": expirationStr := "NONE" if m.Expiration != nil && !m.Expiration.IsZero() && !m.Expiration.Equal(timeSentinel) { expirationStr = m.Expiration.String() } - - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green - o := strings.Builder{} - o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Access Key:"), m.AccessKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Secret Key:"), m.SecretKey)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Expiration:"), expirationStr)) o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name)) - o.WriteString(iFmt(0, "%s %s\n\n", labelStyle.Render("Description:"), m.Description)) - - return o.String() + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description)) case "remove": - return console.Colorize("RemoveAccessKey", "Successfully removed access key `"+m.AccessKey+"`.") + o.WriteString(labelStyle.Render(iFmt(0, "Successfully removed access key `%s`.", m.AccessKey))) + case "edit": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully edited access key `%s`.", m.AccessKey))) + case "enable": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully enabled access key `%s`.", m.AccessKey))) + case "disable": + o.WriteString(labelStyle.Render(iFmt(0, "Successfully disabled access key `%s`.", m.AccessKey))) } - return "" + return o.String() } func (m ldapAccesskeyMessage) JSON() string { diff --git a/cmd/idp-ldap-accesskey-remove.go b/cmd/idp-ldap-accesskey-remove.go index d2069625a1..8f4d57b667 100644 --- a/cmd/idp-ldap-accesskey-remove.go +++ b/cmd/idp-ldap-accesskey-remove.go @@ -18,10 +18,8 @@ package cmd import ( - "github.com/fatih/color" "github.com/minio/cli" "github.com/minio/mc/pkg/probe" - "github.com/minio/pkg/v3/console" ) var idpLdapAccesskeyRemoveCmd = cli.Command{ @@ -52,8 +50,6 @@ func mainIDPLdapAccesskeyRemove(ctx *cli.Context) error { showCommandHelpAndExit(ctx, 1) // last argument is exit code } - console.SetColor("RemoveAccessKey", color.New(color.FgGreen)) - args := ctx.Args() aliasedURL := args.Get(0) accessKey := args.Get(1) diff --git a/cmd/idp-ldap-accesskey.go b/cmd/idp-ldap-accesskey.go index 392be7c731..a0f1751670 100644 --- a/cmd/idp-ldap-accesskey.go +++ b/cmd/idp-ldap-accesskey.go @@ -25,6 +25,9 @@ var idpLdapAccesskeySubcommands = []cli.Command{ idpLdapAccesskeyInfoCmd, idpLdapAccesskeyCreateCmd, idpLdapAccesskeyCreateWithLoginCmd, + idpLdapAccesskeyEditCmd, + idpLdapAccesskeyEnableCmd, + idpLdapAccesskeyDisableCmd, } var idpLdapAccesskeyCmd = cli.Command{ From 25ea06308f4061b3c9e6fff312d8f1fc802488a1 Mon Sep 17 00:00:00 2001 From: Shubhendu Date: Thu, 5 Sep 2024 21:01:14 +0530 Subject: [PATCH 2/3] Update to print skipped entities during IAM import (#5032) Signed-off-by: Shubhendu Ram Tripathi --- cmd/admin-cluster-iam-import.go | 133 ++++++++++++++++++++++++++++++-- go.mod | 2 +- go.sum | 4 +- 3 files changed, 130 insertions(+), 9 deletions(-) diff --git a/cmd/admin-cluster-iam-import.go b/cmd/admin-cluster-iam-import.go index 0289913969..d7332ae4c2 100644 --- a/cmd/admin-cluster-iam-import.go +++ b/cmd/admin-cluster-iam-import.go @@ -23,11 +23,13 @@ import ( "io" "os" "path/filepath" + "strings" "github.com/klauspost/compress/zip" "github.com/minio/cli" + json "github.com/minio/colorjson" + "github.com/minio/madmin-go/v3" "github.com/minio/mc/pkg/probe" - "github.com/minio/pkg/v3/console" ) var adminClusterIAMImportCmd = cli.Command{ @@ -54,6 +56,123 @@ EXAMPLES: `, } +type iamImportInfo madmin.ImportIAMResult + +func (i iamImportInfo) JSON() string { + bs, e := json.MarshalIndent(madmin.ImportIAMResult(i), "", " ") + fatalIf(probe.NewError(e), "Unable to marshal into JSON.") + return string(bs) +} + +func (i iamImportInfo) String() string { + var messages []string + info := madmin.ImportIAMResult(i) + messages = append(messages, processIAMEntities(info.Skipped, "Skipped")...) + messages = append(messages, processIAMEntities(info.Removed, "Removed")...) + messages = append(messages, processIAMEntities(info.Added, "Added")...) + messages = append(messages, processErrIAMEntities(info.Failed)...) + return strings.Join(messages, "\n") +} + +func processIAMEntities(entities madmin.IAMEntities, action string) []string { + var messages []string + if len(entities.Policies) > 0 { + messages = append(messages, fmt.Sprintf("%s policies: %v", action, strings.Join(entities.Policies, ", "))) + } + if len(entities.Users) > 0 { + messages = append(messages, fmt.Sprintf("%s users: %v", action, strings.Join(entities.Users, ", "))) + } + if len(entities.Groups) > 0 { + messages = append(messages, fmt.Sprintf("%s groups: %v", action, strings.Join(entities.Groups, ", "))) + } + if len(entities.ServiceAccounts) > 0 { + messages = append(messages, fmt.Sprintf("%s service accounts: %v", action, strings.Join(entities.ServiceAccounts, ", "))) + } + var users []string + for _, pol := range entities.UserPolicies { + for name := range pol { + users = append(users, name) + } + } + if len(users) > 0 { + messages = append(messages, fmt.Sprintf("%s policies for users: %v", action, strings.Join(users, ", "))) + } + var groups []string + for _, pol := range entities.GroupPolicies { + for name := range pol { + groups = append(groups, name) + } + } + if len(groups) > 0 { + messages = append(messages, fmt.Sprintf("%s policies for groups: %v", action, strings.Join(groups, ", "))) + } + var stsarr []string + for _, pol := range entities.STSPolicies { + for name := range pol { + stsarr = append(stsarr, name) + } + } + if len(stsarr) > 0 { + messages = append(messages, fmt.Sprintf("%s policies for sts: %v", action, strings.Join(stsarr, ", "))) + } + return messages +} + +func processErrIAMEntities(entities madmin.IAMErrEntities) []string { + var messages []string + + var policies []string + for _, entry := range entities.Policies { + policies = append(policies, entry.Name) + } + if len(policies) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add policies: %v", strings.Join(policies, ", "))) + } + var users []string + for _, entry := range entities.Users { + users = append(users, entry.Name) + } + if len(users) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add users: %v", strings.Join(users, ", "))) + } + var groups []string + for _, entry := range entities.Groups { + groups = append(groups, entry.Name) + } + if len(groups) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add groups: %v", strings.Join(groups, ", "))) + } + var sas []string + for _, entry := range entities.ServiceAccounts { + sas = append(sas, entry.Name) + } + if len(sas) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add service accounts: %v", strings.Join(sas, ", "))) + } + var polusers []string + for _, pol := range entities.UserPolicies { + polusers = append(polusers, pol.Name) + } + if len(polusers) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add policies for users: %v", strings.Join(polusers, ", "))) + } + var polgroups []string + for _, pol := range entities.GroupPolicies { + polgroups = append(polgroups, pol.Name) + } + if len(polgroups) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add policies for groups: %v", strings.Join(polgroups, ", "))) + } + var polsts []string + for _, pol := range entities.STSPolicies { + polsts = append(polsts, pol.Name) + } + if len(polsts) > 0 { + messages = append(messages, fmt.Sprintf("Failed to add policies for sts: %v", strings.Join(polsts, ", "))) + } + return messages +} + func checkIAMImportSyntax(ctx *cli.Context) { if len(ctx.Args()) != 2 { showCommandHelpAndExit(ctx, 1) // last argument is exit code @@ -95,11 +214,13 @@ func mainClusterIAMImport(ctx *cli.Context) error { return nil } - e = client.ImportIAM(context.Background(), f) - fatalIf(probe.NewError(e).Trace(aliasedURL), "Unable to import IAM info.") - - if !globalJSON { - console.Infof("IAM info imported to %s from %s\n", aliasedURL, args.Get(1)) + iamr, e := client.ImportIAMV2(context.Background(), f) + if e != nil { + e = client.ImportIAM(context.Background(), f) + fatalIf(probe.NewError(e).Trace(aliasedURL), "Unable to import IAM info.") + } else { + printMsg(iamImportInfo(iamr)) } + return nil } diff --git a/go.mod b/go.mod index dd4998e890..0c7fc242c9 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/minio/cli v1.24.2 github.com/minio/colorjson v1.0.8 github.com/minio/filepath v1.0.0 - github.com/minio/madmin-go/v3 v3.0.64 + github.com/minio/madmin-go/v3 v3.0.66 github.com/minio/minio-go/v7 v7.0.76 github.com/minio/pkg/v3 v3.0.13 github.com/minio/selfupdate v0.6.0 diff --git a/go.sum b/go.sum index 1ba88a0999..4d42f8f7b7 100644 --- a/go.sum +++ b/go.sum @@ -138,8 +138,8 @@ github.com/minio/colorjson v1.0.8 h1:AS6gEQ1dTRYHmC4xuoodPDRILHP/9Wz5wYUGDQfPLpg github.com/minio/colorjson v1.0.8/go.mod h1:wrs39G/4kqNlGjwqHvPlAnXuc2tlPszo6JKdSBCLN8w= github.com/minio/filepath v1.0.0 h1:fvkJu1+6X+ECRA6G3+JJETj4QeAYO9sV43I79H8ubDY= github.com/minio/filepath v1.0.0/go.mod h1:/nRZA2ldl5z6jT9/KQuvZcQlxZIMQoFFQPvEXx9T/Bw= -github.com/minio/madmin-go/v3 v3.0.64 h1:Btwgs3CrgSciVaCWv/3clOxuDdUzylo/oTQp0M8GkwE= -github.com/minio/madmin-go/v3 v3.0.64/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw= +github.com/minio/madmin-go/v3 v3.0.66 h1:O4w7L3vTxhORqTeyegFdbuO4kKVbAUarJfcmsDXQMTs= +github.com/minio/madmin-go/v3 v3.0.66/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.76 h1:9nxHH2XDai61cT/EFhyIw/wW4vJfpPNvl7lSFpRt+Ng= From 3092b6bf1487e1a6ce8f37808134fb9cef43cc1a Mon Sep 17 00:00:00 2001 From: Anis Eleuch Date: Fri, 6 Sep 2024 19:28:18 +0100 Subject: [PATCH 3/3] tier: Add hidden --force to remove tiering command (#5031) To ensure that the server can remove an internal tier configuration when the tiering is unreachable or other corner cases where it is not possible for the server to determine if the tier contains some data or fully empty. --- cmd/ilm-tier-remove.go | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/cmd/ilm-tier-remove.go b/cmd/ilm-tier-remove.go index e645734d89..5a26dae8f4 100644 --- a/cmd/ilm-tier-remove.go +++ b/cmd/ilm-tier-remove.go @@ -19,9 +19,23 @@ package cmd import ( "github.com/minio/cli" + "github.com/minio/madmin-go/v3" "github.com/minio/mc/pkg/probe" ) +var adminTierRmFlags = []cli.Flag{ + cli.BoolFlag{ + Name: "force", + Usage: "forcefully remove the specified tier", + Hidden: true, + }, + cli.BoolFlag{ + Name: "dangerous", + Usage: "additional flag to be required in addition to force flag", + Hidden: true, + }, +} + var adminTierRmCmd = cli.Command{ Name: "remove", ShortName: "rm", @@ -29,7 +43,7 @@ var adminTierRmCmd = cli.Command{ Action: mainAdminTierRm, OnUsageError: onUsageError, Before: setGlobalsFromContext, - Flags: globalFlags, + Flags: append(globalFlags, adminTierRmFlags...), CustomHelpTemplate: `NAME: {{.HelpName}} - {{.Usage}} @@ -65,11 +79,15 @@ func mainAdminTierRm(ctx *cli.Context) error { fatalIf(errInvalidArgument(), "Tier name can't be empty") } + if ctx.Bool("force") && !ctx.Bool("dangerous") { + fatalIf(errInvalidArgument(), "This operation results in an irreversible disconnection from the specified remote tier. If you are really sure, retry this command with ‘--force’ and ‘--dangerous’ flags.") + } + // Create a new MinIO Admin Client client, cerr := newAdminClient(aliasedURL) fatalIf(cerr, "Unable to initialize admin connection.") - e := client.RemoveTier(globalContext, tierName) + e := client.RemoveTierV2(globalContext, tierName, madmin.RemoveTierOpts{Force: ctx.Bool("force")}) fatalIf(probe.NewError(e).Trace(args...), "Unable to remove remote tier target") printMsg(&tierMessage{