Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package minio-client for openSUSE:Factory checked in at 2024-01-07 21:40:24 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/minio-client (Old) and /work/SRC/openSUSE:Factory/.minio-client.new.28375 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "minio-client" Sun Jan 7 21:40:24 2024 rev:63 rq:1137376 version:20240105T050432Z Changes: -------- --- /work/SRC/openSUSE:Factory/minio-client/minio-client.changes 2023-12-21 23:39:59.657562694 +0100 +++ /work/SRC/openSUSE:Factory/.minio-client.new.28375/minio-client.changes 2024-01-07 21:40:41.768878678 +0100 @@ -1,0 +2,26 @@ +Sun Jan 07 15:59:45 UTC 2024 - opensuse_buildserv...@ojkastl.de + +- Update to version 20240105T050432Z: + * ILM rule type filter flags should work with --json (#4814) + * Add missing space between 'at' and filename (#4813) + * honor correct content-type for any files when passed via SQL + query (#4809) + * feat: support summary for mc mirror (#4811) + +------------------------------------------------------------------- +Sun Jan 07 15:58:28 UTC 2024 - opensuse_buildserv...@ojkastl.de + +- Update to version 20231229T201529Z: + * Use correct target path during mirroring objects (#4807) + * fix: tree traversal issue with empty folders (#4810) + * properly ignore permission denied error with mirror (#4803) + +------------------------------------------------------------------- +Sun Jan 07 15:56:55 UTC 2024 - opensuse_buildserv...@ojkastl.de + +- Update to version 20231223T084721Z: + * Add `ldap accesskey create`, update `accesskey list` with new + endpoint (#4760) + * Accept --license flag in `mc license register` (#4795) + +------------------------------------------------------------------- Old: ---- mc-20231220T071422Z.obscpio New: ---- mc-20240105T050432Z.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ minio-client.spec ++++++ --- /var/tmp/diff_new_pack.VhObcT/_old 2024-01-07 21:40:42.860918402 +0100 +++ /var/tmp/diff_new_pack.VhObcT/_new 2024-01-07 21:40:42.860918402 +0100 @@ -1,7 +1,7 @@ # # spec file for package minio-client # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -22,7 +22,7 @@ %define binary_name minio-client Name: minio-client -Version: 20231220T071422Z +Version: 20240105T050432Z Release: 0 Summary: Client for MinIO License: AGPL-3.0-only ++++++ _service ++++++ --- /var/tmp/diff_new_pack.VhObcT/_old 2024-01-07 21:40:42.892919566 +0100 +++ /var/tmp/diff_new_pack.VhObcT/_new 2024-01-07 21:40:42.892919566 +0100 @@ -5,7 +5,7 @@ <param name="exclude">.git</param> <param name="changesgenerate">enable</param> <param name="versionformat">@PARENT_TAG@</param> - <param name="revision">RELEASE.2023-12-20T07-14-22Z</param> + <param name="revision">RELEASE.2024-01-05T05-04-32Z</param> <param name="match-tag">RELEASE.*</param> <param name="versionrewrite-pattern">RELEASE\.(.*)-(.*)-(.*)-(.*)-(.*)</param> <param name="versionrewrite-replacement">\1\2\3\4\5</param> @@ -19,7 +19,7 @@ <param name="compression">gz</param> </service> <service name="go_modules" mode="manual"> - <param name="archive">mc-20231220T071422Z.obscpio</param> + <param name="archive">mc-20240105T050432Z.obscpio</param> </service> </services> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.VhObcT/_old 2024-01-07 21:40:42.912920294 +0100 +++ /var/tmp/diff_new_pack.VhObcT/_new 2024-01-07 21:40:42.916920439 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/minio/mc</param> - <param name="changesrevision">8e1573ec1b9c174e9f8d82ee9996d002c1d9caaa</param></service></servicedata> + <param name="changesrevision">59eca9fea8984adec1e8e7a1c95d0ea23107ceff</param></service></servicedata> (No newline at EOF) ++++++ mc-20231220T071422Z.obscpio -> mc-20240105T050432Z.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/auto-complete.go new/mc-20240105T050432Z/cmd/auto-complete.go --- old/mc-20231220T071422Z/cmd/auto-complete.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/auto-complete.go 2024-01-05 06:04:32.000000000 +0100 @@ -383,7 +383,9 @@ "/idp/ldap/accesskey/create": aliasCompleter, "/idp/ldap/accesskey/list": aliasCompleter, + "/idp/ldap/accesskey/ls": aliasCompleter, "/idp/ldap/accesskey/remove": aliasCompleter, + "/idp/ldap/accesskey/rm": aliasCompleter, "/idp/ldap/accesskey/info": aliasCompleter, "/admin/policy/info": aliasCompleter, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-create.go new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-create.go --- old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-create.go 1970-01-01 01:00:00.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-create.go 2024-01-05 06:04:32.000000000 +0100 @@ -0,0 +1,251 @@ +// Copyright (c) 2015-2023 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 <http://www.gnu.org/licenses/>. + +package cmd + +import ( + "bufio" + "bytes" + "fmt" + "net/url" + "os" + "time" + + "github.com/fatih/color" + "github.com/minio/cli" + "github.com/minio/madmin-go/v3" + "github.com/minio/mc/pkg/probe" + "github.com/minio/minio-go/v7/pkg/credentials" + "github.com/minio/pkg/v2/console" + "github.com/minio/pkg/v2/policy" + "golang.org/x/term" +) + +var idpLdapAccesskeyCreateFlags = []cli.Flag{ + cli.StringFlag{ + Name: "access-key", + Usage: "set an access key for the account", + }, + 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", + }, + cli.BoolFlag{ + Name: "login", + Usage: "log in using ldap credentials to generate access key par for future use", + }, +} + +var idpLdapAccesskeyCreateCmd = cli.Command{ + Name: "create", + Usage: "create access key pairs for LDAP", + Action: mainIDPLdapAccesskeyCreate, + Before: setGlobalsFromContext, + Flags: append(idpLdapAccesskeyCreateFlags, globalFlags...), + OnUsageError: onUsageError, + CustomHelpTemplate: `NAME: + {{.HelpName}} - {{.Usage}} + +USAGE: + {{.HelpName}} [FLAGS] [TARGET] + +FLAGS: + {{range .VisibleFlags}}{{.}} + {{end}} +EXAMPLES: + 1. Create a new access key pair with the same policy as the authenticated user + {{.Prompt}} {{.HelpName}} local/ + 2. Create a new access key pair with custom access key and secret key + {{.Prompt}} {{.HelpName}} local/ --access-key myaccesskey --secret-key mysecretkey + 4. Create a new access key pair for user with username "james" that expires in 1 day + {{.Prompt}} {{.HelpName}} james --expiry-duration 24h + 5. Create a new access key pair for authenticated user that expires on 2021-01-01 + {{.Prompt}} {{.HelpName}} --expiry 2021-01-01 + 6. Create a new access key pair for minio.example.com by logging in with LDAP credentials + {{.Prompt}} {{.HelpName}} --login minio.example.com + `, +} + +func mainIDPLdapAccesskeyCreate(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) + targetUser := args.Get(1) + + login := ctx.Bool("login") + accessVal := ctx.String("access-key") + secretVal := ctx.String("secret-key") + name := ctx.String("name") + description := ctx.String("description") + policyPath := ctx.String("policy") + + expDurVal := ctx.Duration("expiry-duration") + expVal := ctx.String("expiry") + if expVal != "" && expDurVal != 0 { + e := fmt.Errorf("Only one of --expiry or --expiry-duration can be specified") + fatalIf(probe.NewError(e), "Invalid flags.") + } + + var exp time.Time + if expVal != "" { + location, e := time.LoadLocation("Local") + if e != nil { + fatalIf(probe.NewError(e), "Unable to parse the expiry argument.") + } + + patternMatched := false + for _, format := range supportedTimeFormats { + t, e := time.ParseInLocation(format, expVal, location) + if e == nil { + patternMatched = true + exp = t + break + } + } + + if !patternMatched { + e := fmt.Errorf("invalid expiry date format '%s'", expVal) + fatalIf(probe.NewError(e), "unable to parse the expiry argument.") + } + } else if expDurVal != 0 { + exp = time.Now().Add(expDurVal) + } else { + exp = time.Unix(0, 0) + } + + var policyBytes []byte + if policyPath != "" { + // Validate the policy document and ensure it has at least when statement + var e error + policyBytes, e = os.ReadFile(policyPath) + fatalIf(probe.NewError(e), "Unable to open 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 policy documents are not allowed.") + } + } + + var client *madmin.AdminClient + + // If login flag is set, use LDAP credentials to generate access key pair + if login { + if targetUser != "" { + fatalIf(errInvalidArgument().Trace(targetUser), "login flag cannot be used with a target user") + } + isTerminal := term.IsTerminal(int(os.Stdin.Fd())) + if !isTerminal { + e := fmt.Errorf("login flag cannot be used with non-interactive terminal") + fatalIf(probe.NewError(e), "Invalid flags.") + } + + // For login, aliasedURL is not aliased, but the actual server URL + client = loginLDAPAccesskey(aliasedURL) + } else { + var err *probe.Error + // If login flag is not set, continue normally + client, err = newAdminClient(aliasedURL) + fatalIf(err, "Unable to initialize admin connection.") + } + + accessKey, secretKey, e := generateCredentials() + fatalIf(probe.NewError(e), "Unable to generate credentials.") + + // If access key and secret key are provided, use them instead + if accessVal != "" { + accessKey = accessVal + } + if secretVal != "" { + secretKey = secretVal + } + + res, e := client.AddServiceAccountLDAP(globalContext, + madmin.AddServiceAccountReq{ + Policy: policyBytes, + TargetUser: targetUser, + AccessKey: accessKey, + SecretKey: secretKey, + Name: name, + Description: description, + Expiration: &exp, + }) + fatalIf(probe.NewError(e), "Unable to add service account.") + + m := ldapAccesskeyMessage{ + op: "create", + Status: "success", + AccessKey: res.AccessKey, + SecretKey: res.SecretKey, + Expiration: &res.Expiration, + } + printMsg(m) + + return nil +} + +func loginLDAPAccesskey(URL string) *madmin.AdminClient { + console.SetColor(cred, color.New(color.FgYellow, color.Italic)) + reader := bufio.NewReader(os.Stdin) + + fmt.Printf("%s", console.Colorize(cred, "Enter LDAP Username: ")) + value, _, e := reader.ReadLine() + fatalIf(probe.NewError(e), "Unable to read username") + username := string(value) + + fmt.Printf("%s", console.Colorize(cred, "Enter Password: ")) + bytePassword, e := term.ReadPassword(int(os.Stdin.Fd())) + fatalIf(probe.NewError(e), "Unable to read password") + fmt.Printf("\n") + password := string(bytePassword) + + ldapID, e := credentials.NewLDAPIdentity(URL, username, password) + fatalIf(probe.NewError(e), "Unable to initialize LDAP identity.") + + u, e := url.Parse(URL) + fatalIf(probe.NewError(e), "Unable to parse server URL.") + + client, e := madmin.NewWithOptions(u.Host, &madmin.Options{ + Creds: ldapID, + Secure: u.Scheme == "https", + }) + fatalIf(probe.NewError(e), "Unable to initialize admin connection.") + + return client +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-info.go new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-info.go --- old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-info.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-info.go 2024-01-05 06:04:32.000000000 +0100 @@ -104,10 +104,15 @@ labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) // green o := strings.Builder{} - o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("User DN: "), m.ParentUser)) 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\n", labelStyle.Render("Expiration:"), expirationStr)) + if m.Name != "" { + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Name:"), m.Name)) + } + if m.Description != "" { + o.WriteString(iFmt(0, "%s %s\n", labelStyle.Render("Description:"), m.Description)) + } return o.String() case "remove": @@ -137,9 +142,29 @@ fatalIf(err, "Unable to initialize admin connection.") for _, accessKey := range accessKeys { + // Assume service account by default res, e := client.InfoServiceAccount(globalContext, accessKey) if e != nil { - errorIf(probe.NewError(e), "Unable to retrieve access key "+accessKey+" info.") + // If not a service account must be sts + tempRes, e := client.TemporaryAccountInfo(globalContext, accessKey) + if e != nil { + errorIf(probe.NewError(e), "Unable to retrieve access key "+accessKey+" info.") + } else { + m := ldapAccesskeyMessage{ + op: "info", + AccessKey: accessKey, + Status: "success", + ParentUser: tempRes.ParentUser, + AccountStatus: tempRes.AccountStatus, + ImpliedPolicy: tempRes.ImpliedPolicy, + Policy: json.RawMessage(tempRes.Policy), + Name: tempRes.Name, + Description: tempRes.Description, + Expiration: tempRes.Expiration, + } + + printMsg(m) + } } else { m := ldapAccesskeyMessage{ op: "info", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-list.go new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-list.go --- old/mc-20231220T071422Z/cmd/idp-ldap-accesskey-list.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/idp-ldap-accesskey-list.go 2024-01-05 06:04:32.000000000 +0100 @@ -38,8 +38,8 @@ Usage: "only list temporary access keys", }, cli.BoolFlag{ - Name: "permanent-only", - Usage: "only list permanent access keys/service accounts", + Name: "svcacc-only", + Usage: "only list service account access keys", }, } @@ -63,45 +63,48 @@ EXAMPLES: 1. Get list of all users and associated access keys in local server (if admin) {{.Prompt}} {{.HelpName}} local/ + 2. Get list of users in local server (if admin) {{.Prompt}} {{.HelpName}} local/ --users-only + 3. Get list of all users and associated temporary access keys in play server (if admin) {{.Prompt}} {{.HelpName}} play/ --temp-only + 4. Get list of access keys associated with user 'bobfisher' {{.Prompt}} {{.HelpName}} play/ uid=bobfisher,dc=min,dc=io - 5. Get list of access keys associated with users 'bobfisher' and 'cody3' + + 5. Get list of access keys associated with user 'bobfisher' (alt) + {{.Prompt}} {{.HelpName}} play/ bobfisher + + 6. Get list of access keys associated with users 'bobfisher' and 'cody3' {{.Prompt}} {{.HelpName}} play/ uid=bobfisher,dc=min,dc=io uid=cody3,dc=min,dc=io - 6. Get authenticated user and associated access keys in local server (if not admin) + + 7. Get authenticated user and associated access keys in local server (if not admin) {{.Prompt}} {{.HelpName}} local/ - `, +`, } type ldapUsersList struct { - Status string `json:"status"` - Result ldapUserAccessKeys `json:"result"` -} - -type ldapUserAccessKeys struct { - DN string `json:"dn"` - TempAccessKeys []madmin.ServiceAccountInfo `json:"tempAccessKeys,omitempty"` - PermanentAccessKeys []madmin.ServiceAccountInfo `json:"permanentAccessKeys,omitempty"` + Status string `json:"status"` + DN string `json:"dn"` + STSKeys []madmin.ServiceAccountInfo `json:"stsKeys"` + ServiceAccounts []madmin.ServiceAccountInfo `json:"svcaccs"` } func (m ldapUsersList) String() string { labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")) o := strings.Builder{} - u := m.Result - o.WriteString(iFmt(0, "%s\n", labelStyle.Render("DN "+u.DN))) - if len(u.TempAccessKeys) > 0 { - o.WriteString(iFmt(2, "%s\n", labelStyle.Render("Temporary Access Keys:"))) - for _, k := range u.TempAccessKeys { + o.WriteString(iFmt(0, "%s\n", labelStyle.Render("DN "+m.DN))) + if len(m.STSKeys) > 0 { + o.WriteString(iFmt(2, "%s\n", labelStyle.Render("STS Access Keys:"))) + for _, k := range m.STSKeys { o.WriteString(iFmt(4, "%s\n", k.AccessKey)) } } - if len(u.PermanentAccessKeys) > 0 { - o.WriteString(iFmt(2, "%s\n", labelStyle.Render("Permanent Access Keys:"))) - for _, k := range u.PermanentAccessKeys { + if len(m.ServiceAccounts) > 0 { + o.WriteString(iFmt(2, "%s\n", labelStyle.Render("Service Account Access Keys:"))) + for _, k := range m.ServiceAccounts { o.WriteString(iFmt(4, "%s\n", k.AccessKey)) } } @@ -123,13 +126,19 @@ } usersOnly := ctx.Bool("users-only") - tempOnly := ctx.Bool("temp-only") - permanentOnly := ctx.Bool("permanent-only") + tempOnly := ctx.Bool("sts-only") + permanentOnly := ctx.Bool("svcacc-only") + listType := "" if (usersOnly && permanentOnly) || (usersOnly && tempOnly) || (permanentOnly && tempOnly) { e := errors.New("only one of --users-only, --temp-only, or --permanent-only can be specified") fatalIf(probe.NewError(e), "Invalid flags.") } + if tempOnly { + listType = "sts-only" + } else if permanentOnly { + listType = "svcacc-only" + } args := ctx.Args() aliasedURL := args.Get(0) @@ -148,12 +157,7 @@ } else { users = make(map[string]madmin.UserInfo) for _, user := range userArg { - // Check existence of each user - if _, e = client.GetUserInfo(globalContext, user); e != nil { - errorIf(probe.NewError(e), "User '"+user+"' invalid") - } else { - users[user] = madmin.UserInfo{} - } + users[user] = madmin.UserInfo{} } } if e != nil { @@ -167,56 +171,30 @@ } for dn := range users { - if !usersOnly { - accessKeys, _ := client.ListServiceAccounts(globalContext, dn) - - var tempAccessKeys []madmin.ServiceAccountInfo - var permanentAccessKeys []madmin.ServiceAccountInfo - - for _, accessKey := range accessKeys.Accounts { - if accessKey.Expiration.Unix() == 0 { - permanentAccessKeys = append(permanentAccessKeys, accessKey) - } else { - tempAccessKeys = append(tempAccessKeys, accessKey) - } - } - - // if dn is blank, it means we are listing the current user's access keys - if dn == "" { - name, e := client.AccountInfo(globalContext, madmin.AccountOpts{}) - fatalIf(probe.NewError(e), "Unable to retrieve account name.") - dn = name.AccountName - } - - userAccessKeys := ldapUserAccessKeys{DN: dn} - if !tempOnly { - userAccessKeys.PermanentAccessKeys = permanentAccessKeys - } - if !permanentOnly { - userAccessKeys.TempAccessKeys = tempAccessKeys - } + // if dn is blank, it means we are listing the current user's access keys + if dn == "" { + name, e := client.AccountInfo(globalContext, madmin.AccountOpts{}) + fatalIf(probe.NewError(e), "Unable to retrieve account name.") + dn = name.AccountName + } - m := ldapUsersList{ - Status: "success", - Result: userAccessKeys, - } - printMsg(m) + m := ldapUsersList{ + Status: "success", + DN: dn, + } - } else { - // If dn is blank, it means we are listing the current user's access keys - if dn == "" { - name, e := client.AccountInfo(globalContext, madmin.AccountOpts{}) - fatalIf(probe.NewError(e), "Unable to retrieve account name.") - dn = name.AccountName + // Get access keys if not listing users only + if !usersOnly { + accessKeys, e := client.ListAccessKeysLDAP(globalContext, dn, listType) + if e != nil { + errorIf(probe.NewError(e), "Unable to retrieve access keys for user '"+dn+"'.") + continue } - m := ldapUsersList{ - Status: "success", - Result: ldapUserAccessKeys{DN: dn}, - } - printMsg(m) + m.STSKeys = accessKeys.STSKeys + m.ServiceAccounts = accessKeys.ServiceAccounts } + printMsg(m) } - return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/idp-ldap-accesskey.go new/mc-20240105T050432Z/cmd/idp-ldap-accesskey.go --- old/mc-20231220T071422Z/cmd/idp-ldap-accesskey.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/idp-ldap-accesskey.go 2024-01-05 06:04:32.000000000 +0100 @@ -23,6 +23,7 @@ idpLdapAccesskeyListCmd, idpLdapAccesskeyRemoveCmd, idpLdapAccesskeyInfoCmd, + idpLdapAccesskeyCreateCmd, } var idpLdapAccesskeyCmd = cli.Command{ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/ilm/table.go new/mc-20240105T050432Z/cmd/ilm/table.go --- old/mc-20231220T071422Z/cmd/ilm/table.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/ilm/table.go 2024-01-05 06:04:32.000000000 +0100 @@ -17,11 +17,15 @@ package ilm -import "github.com/jedib0t/go-pretty/v6/table" +import ( + "github.com/jedib0t/go-pretty/v6/table" + "github.com/minio/minio-go/v7/pkg/lifecycle" +) // Table interface provides methods when implemented allows a []T to be rendered // as a table. type Table interface { + Len() int Title() string Rows() []table.Row ColumnHeaders() table.Row @@ -39,6 +43,30 @@ TransitionOnly ) +// Apply applies f on rules and filters lifecycle rules matching it +func (f LsFilter) Apply(rules []lifecycle.Rule) []lifecycle.Rule { + check := func(rule lifecycle.Rule) bool { + switch f { + case ExpiryOnly: + return !rule.Expiration.IsNull() || !rule.NoncurrentVersionExpiration.IsDaysNull() || + rule.NoncurrentVersionExpiration.NewerNoncurrentVersions > 0 + case TransitionOnly: + return !rule.Transition.IsNull() || !rule.NoncurrentVersionTransition.IsStorageClassEmpty() + } + return true + } + + var n int + for _, rule := range rules { + if check(rule) { + rules[n] = rule + n++ + } + } + rules = rules[:n] + return rules +} + type expirationCurrentRow struct { ID string Status string @@ -50,6 +78,10 @@ type expirationCurrentTable []expirationCurrentRow +func (e expirationCurrentTable) Len() int { + return len(e) +} + func (e expirationCurrentTable) Title() string { return "Expiration for latest version (Expiration)" } @@ -82,6 +114,10 @@ KeepVersions int } +func (e expirationNoncurrentTable) Len() int { + return len(e) +} + func (e expirationNoncurrentTable) Title() string { return "Expiration for older versions (NoncurrentVersionExpiration)" } @@ -114,6 +150,10 @@ Tier string } +func (t tierCurrentTable) Len() int { + return len(t) +} + func (t tierCurrentTable) Title() string { return "Transition for latest version (Transition)" } @@ -140,6 +180,10 @@ tierNoncurrentRow tierCurrentRow ) +func (t tierNoncurrentTable) Len() int { + return len(t) +} + func (t tierNoncurrentTable) Title() string { return "Transition for older versions (NoncurrentVersionTransition)" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/ilm/utils.go new/mc-20240105T050432Z/cmd/ilm/utils.go --- old/mc-20231220T071422Z/cmd/ilm/utils.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/ilm/utils.go 2024-01-05 06:04:32.000000000 +0100 @@ -82,7 +82,7 @@ } // ToTables converts a lifecycle.Configuration into its tabular representation. -func ToTables(cfg *lifecycle.Configuration, filter LsFilter) []Table { +func ToTables(cfg *lifecycle.Configuration) []Table { var tierCur tierCurrentTable var tierNoncur tierNoncurrentTable var expCur expirationCurrentTable @@ -130,12 +130,15 @@ } } - switch filter { - case ExpiryOnly: - return []Table{expCur, expNoncur} - case TransitionOnly: - return []Table{tierCur, tierNoncur} - default: - return []Table{tierCur, tierNoncur, expCur, expNoncur} + var table []Table + inclTbl := func(tbl Table) { + if len(tbl.Rows()) > 0 { + table = append(table, tbl) + } } + inclTbl(expCur) + inclTbl(expNoncur) + inclTbl(tierCur) + inclTbl(tierNoncur) + return table } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/ilm-rule-list.go new/mc-20240105T050432Z/cmd/ilm-rule-list.go --- old/mc-20231220T071422Z/cmd/ilm-rule-list.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/ilm-rule-list.go 2024-01-05 06:04:32.000000000 +0100 @@ -98,16 +98,13 @@ return string(msgBytes) } -// validateILMListFlagSet - Only one of these flags needs to be set for display: --json, --expiry, --transition +// validateILMListFlagSet - validates ilm list flags func validateILMListFlagSet(ctx *cli.Context) bool { - flags := [...]bool{ctx.Bool("expiry"), ctx.Bool("transition"), ctx.Bool("json")} - found := false - for _, flag := range flags { - if found && flag { - return false - } else if flag { - found = true - } + expiryOnly := ctx.Bool("expiry") + transitionOnly := ctx.Bool("transition") + // Only one of expiry or transition rules can be filtered + if expiryOnly && transitionOnly { + return false } return true } @@ -153,6 +150,9 @@ "Unable to ls lifecycle configuration") } + // applies listing filter on ILM rules + ilmCfg.Rules = filter.Apply(ilmCfg.Rules) + if globalJSON { printMsg(ilmListMessage{ Status: "success", @@ -164,7 +164,7 @@ return nil } - for _, tbl := range ilm.ToTables(ilmCfg, filter) { + for _, tbl := range ilm.ToTables(ilmCfg) { rows := tbl.Rows() if len(rows) == 0 { continue diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/license-info.go new/mc-20240105T050432Z/cmd/license-info.go --- old/mc-20231220T071422Z/cmd/license-info.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/license-info.go 2024-01-05 06:04:32.000000000 +0100 @@ -179,7 +179,7 @@ initLicInfoColors() aliasedURL := ctx.Args().Get(0) - alias, _ := initSubnetConnectivity(ctx, aliasedURL, false) + alias, _ := initSubnetConnectivity(ctx, aliasedURL, false, true) apiKey, lic, e := getSubnetCreds(alias) fatalIf(probe.NewError(e), "Error in checking cluster registration status") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/license-register.go new/mc-20240105T050432Z/cmd/license-register.go --- old/mc-20231220T071422Z/cmd/license-register.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/license-register.go 2024-01-05 06:04:32.000000000 +0100 @@ -21,6 +21,7 @@ "fmt" "net" "net/url" + "os" "github.com/fatih/color" "github.com/minio/cli" @@ -41,6 +42,10 @@ Name: "name", Usage: "Specify the name to associate to this MinIO cluster in SUBNET", }, + cli.StringFlag{ + Name: "license", + Usage: "license of the account on SUBNET", + }, }, subnetCommonFlags...) var licenseRegisterCmd = cli.Command{ @@ -63,14 +68,17 @@ 1. Register MinIO cluster at alias 'play' on SUBNET, using api key for auth {{.Prompt}} {{.HelpName}} play --api-key 08efc836-4289-dbd4-ad82-b5e8b6d25577 - 2. Register MinIO cluster at alias 'play' on SUBNET, using api key for auth, + 2. Register MinIO cluster at alias 'play' on SUBNET, using license file ./minio.license + {{.Prompt}} {{.HelpName}} play --license ./minio.license + + 3. Register MinIO cluster at alias 'play' on SUBNET, using api key for auth, and "play-cluster" as the preferred name for the cluster on SUBNET. {{.Prompt}} {{.HelpName}} play --api-key 08efc836-4289-dbd4-ad82-b5e8b6d25577 --name play-cluster - 3. Register MinIO cluster at alias 'play' on SUBNET in an airgapped environment + 4. Register MinIO cluster at alias 'play' on SUBNET in an airgapped environment {{.Prompt}} {{.HelpName}} play --airgap - 4. Register MinIO cluster at alias 'play' on SUBNET, using alias as the cluster name. + 5. Register MinIO cluster at alias 'play' on SUBNET, using alias as the cluster name. This asks for SUBNET credentials if the cluster is not already registered. {{.Prompt}} {{.HelpName}} play `, @@ -203,7 +211,17 @@ aliasedURL := ctx.Args().Get(0) validateNotPlay(aliasedURL) - alias, accAPIKey := initSubnetConnectivity(ctx, aliasedURL, true) + licFile := ctx.String("license") + + var alias, accAPIKey string + if len(licFile) > 0 { + licBytes, e := os.ReadFile(licFile) + fatalIf(probe.NewError(e), fmt.Sprintf("Unable to read license file %s", licFile)) + alias, _ = url2Alias(aliasedURL) + accAPIKey = validateAndSaveLic(string(licBytes), alias, true) + } else { + alias, accAPIKey = initSubnetConnectivity(ctx, aliasedURL, true, false) + } clusterName := ctx.String("name") if len(clusterName) == 0 { @@ -217,14 +235,7 @@ regInfo := getClusterRegInfo(getAdminInfo(aliasedURL), clusterName) lrm := licRegisterMessage{Status: "success", Alias: alias} - if globalAirgapped { - lrm.Type = "offline" - - regToken, e := generateRegToken(regInfo) - fatalIf(probe.NewError(e), "Unable to generate registration token") - - lrm.URL = subnetOfflineRegisterURL(regToken) - } else { + if !globalAirgapped { alreadyRegistered := false if len(accAPIKey) == 0 { apiKey, _, e := getSubnetCreds(alias) @@ -242,14 +253,25 @@ lrm.Type = "online" _, _, e := registerClusterOnSubnet(regInfo, alias, accAPIKey) - fatalIf(probe.NewError(e), "Could not register cluster with SUBNET:") - - lrm.Action = "registered" - if alreadyRegistered { - lrm.Action = "updated" + if e == nil { + lrm.Action = "registered" + if alreadyRegistered { + lrm.Action = "updated" + } + printMsg(lrm) + return nil } + + console.Println("Could not register cluster with SUBNET: ", e.Error()) } + // Airgapped mode OR online mode with registration failure + lrm.Type = "offline" + + regToken, e := generateRegToken(regInfo) + fatalIf(probe.NewError(e), "Unable to generate registration token") + + lrm.URL = subnetOfflineRegisterURL(regToken) printMsg(lrm) return nil } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/license-unregister.go new/mc-20240105T050432Z/cmd/license-unregister.go --- old/mc-20231220T071422Z/cmd/license-unregister.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/license-unregister.go 2024-01-05 06:04:32.000000000 +0100 @@ -83,7 +83,7 @@ checkLicenseUnregisterSyntax(ctx) aliasedURL := ctx.Args().Get(0) - alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, false) + alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, false, true) if len(apiKey) == 0 { // api key not passed as flag. Check that the cluster is registered. apiKey = validateClusterRegistered(alias, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/mirror-main.go new/mc-20240105T050432Z/cmd/mirror-main.go --- old/mc-20231220T071422Z/cmd/mirror-main.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/mirror-main.go 2024-01-05 06:04:32.000000000 +0100 @@ -129,6 +129,10 @@ Name: "retry", Usage: "if specified, will enable retrying on a per object basis if errors occur", }, + cli.BoolFlag{ + Name: "summary", + Usage: "print a summary of the mirror session", + }, } ) @@ -461,13 +465,15 @@ sourcePath := filepath.ToSlash(filepath.Join(sourceAlias, sourceURL.Path)) targetPath := filepath.ToSlash(filepath.Join(targetAlias, targetURL.Path)) - mj.status.PrintMsg(mirrorMessage{ - Source: sourcePath, - Target: targetPath, - Size: length, - TotalCount: sURLs.TotalCount, - TotalSize: sURLs.TotalSize, - }) + if !mj.opts.isSummary { + mj.status.PrintMsg(mirrorMessage{ + Source: sourcePath, + Target: targetPath, + Size: length, + TotalCount: sURLs.TotalCount, + TotalSize: sURLs.TotalSize, + }) + } sURLs.MD5 = mj.opts.md5 sURLs.DisableMultipart = mj.opts.disableMultipart @@ -533,8 +539,15 @@ if isErrIgnored(sURLs.Error) { ignoreErr = true } else { - errorIf(sURLs.Error.Trace(sURLs.SourceContent.URL.String()), - fmt.Sprintf("Failed to copy `%s`.", sURLs.SourceContent.URL.String())) + switch sURLs.Error.ToGoError().(type) { + case PathInsufficientPermission: + // Ignore Permission error. + ignoreErr = true + } + if !ignoreErr { + errorIf(sURLs.Error.Trace(sURLs.SourceContent.URL.String()), + fmt.Sprintf("Failed to copy `%s`.", sURLs.SourceContent.URL.String())) + } } case sURLs.TargetContent != nil: // When sURLs.SourceContent is nil, we know that we have an error related to removing @@ -939,6 +952,7 @@ isOverwrite: isOverwrite, isWatch: isWatch, isMetadata: isMetadata, + isSummary: cli.Bool("summary"), isRetriable: cli.Bool("retry"), md5: cli.Bool("md5"), disableMultipart: cli.Bool("disable-multipart"), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/mirror-url.go new/mc-20240105T050432Z/cmd/mirror-url.go --- old/mc-20231220T071422Z/cmd/mirror-url.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/mirror-url.go 2024-01-05 06:04:32.000000000 +0100 @@ -127,6 +127,15 @@ return } + // If the passed source URL points to fs, fetch the absolute src path + // to correctly calculate targetPath + if sourceAlias == "" { + tmpSrcURL, e := filepath.Abs(sourceURL) + if e == nil { + sourceURL = tmpSrcURL + } + } + // List both source and target, compare and return values through channel. for diffMsg := range objectDifference(ctx, sourceClnt, targetClnt, opts.isMetadata) { if diffMsg.Error != nil { @@ -219,6 +228,7 @@ isFake, isOverwrite, activeActive bool isWatch, isRemove, isMetadata bool isRetriable bool + isSummary bool excludeOptions, excludeStorageClasses []string encKeyDB map[string][]prefixSSEPair md5, disableMultipart bool diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/sql-main.go new/mc-20240105T050432Z/cmd/sql-main.go --- old/mc-20231220T071422Z/cmd/sql-main.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/sql-main.go 2024-01-05 06:04:32.000000000 +0100 @@ -471,7 +471,7 @@ continue } - for content := range clnt.List(ctx, ListOptions{Recursive: cliCtx.Bool("recursive"), ShowDir: DirNone}) { + for content := range clnt.List(ctx, ListOptions{Recursive: cliCtx.Bool("recursive"), WithMetadata: true, ShowDir: DirNone}) { if content.Err != nil { errorIf(content.Err.Trace(url), "Unable to list on target `"+url+"`.") continue @@ -480,6 +480,9 @@ query, csvHdrs, selOpts = getAndValidateArgs(cliCtx, encKeyDB, targetAlias+content.URL.Path) } contentType := mimedb.TypeByExtension(filepath.Ext(content.URL.Path)) + if len(content.UserMetadata) != 0 && content.UserMetadata["content-type"] != "" { + contentType = content.UserMetadata["content-type"] + } for _, cTypeSuffix := range supportedContentTypes { if strings.Contains(contentType, cTypeSuffix) { errorIf(sqlSelect(targetAlias+content.URL.Path, query, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/subnet-utils.go new/mc-20240105T050432Z/cmd/subnet-utils.go --- old/mc-20231220T071422Z/cmd/subnet-utils.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/subnet-utils.go 2024-01-05 06:04:32.000000000 +0100 @@ -782,7 +782,7 @@ return apiKey, nil } -func initSubnetConnectivity(ctx *cli.Context, aliasedURL string, forUpload bool) (string, string) { +func initSubnetConnectivity(ctx *cli.Context, aliasedURL string, forUpload bool, failOnConnErr bool) (string, string) { e := validateSubnetFlags(ctx, forUpload) fatalIf(probe.NewError(e), "Invalid flags:") @@ -797,7 +797,10 @@ fatalIf(probe.NewError(e), "Error in setting SUBNET proxy:") sbu := subnetBaseURL() - fatalIf(checkURLReachable(sbu).Trace(aliasedURL), "Unable to reach %s, please use --airgap if there is no connectivity to SUBNET", sbu) + err := checkURLReachable(sbu) + if err != nil && failOnConnErr { + fatal(err.Trace(aliasedURL), "Unable to reach %s, please use --airgap if there is no connectivity to SUBNET", sbu) + } } return alias, apiKey diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/support-diag.go new/mc-20240105T050432Z/cmd/support-diag.go --- old/mc-20231220T071422Z/cmd/support-diag.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/support-diag.go 2024-01-05 06:04:32.000000000 +0100 @@ -142,7 +142,7 @@ warningMsgHeader := infoText(warningMsgBoundary) warningMsgTrailer := infoText(warningMsgBoundary) console.Printf("%s\n%s\n%s\n%s\n", warningMsgHeader, warning, warningContents, warningMsgTrailer) - console.Infoln("MinIO diagnostics report saved at", filename) + console.Infoln("MinIO diagnostics report saved at ", filename) } return nil @@ -168,7 +168,7 @@ // Get the alias parameter from cli aliasedURL := ctx.Args().Get(0) - alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true) + alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true, true) if len(apiKey) == 0 { // api key not passed as flag. Check that the cluster is registered. apiKey = validateClusterRegistered(alias, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/support-inspect.go new/mc-20240105T050432Z/cmd/support-inspect.go --- old/mc-20231220T071422Z/cmd/support-inspect.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/support-inspect.go 2024-01-05 06:04:32.000000000 +0100 @@ -122,7 +122,7 @@ args := ctx.Args() aliasedURL := args.Get(0) - alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true) + alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true, true) if len(apiKey) == 0 { // api key not passed as flag. Check that the cluster is registered. apiKey = validateClusterRegistered(alias, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/support-perf.go new/mc-20240105T050432Z/cmd/support-perf.go --- old/mc-20231220T071422Z/cmd/support-perf.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/support-perf.go 2024-01-05 06:04:32.000000000 +0100 @@ -461,7 +461,7 @@ } func execSupportPerf(ctx *cli.Context, aliasedURL, perfType string) { - alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true) + alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true, true) if len(apiKey) == 0 { // api key not passed as flag. Check that the cluster is registered. apiKey = validateClusterRegistered(alias, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/support-profile.go new/mc-20240105T050432Z/cmd/support-profile.go --- old/mc-20231220T071422Z/cmd/support-profile.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/support-profile.go 2024-01-05 06:04:32.000000000 +0100 @@ -169,7 +169,7 @@ // Get the alias parameter from cli aliasedURL := ctx.Args().Get(0) - alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true) + alias, apiKey := initSubnetConnectivity(ctx, aliasedURL, true, true) if len(apiKey) == 0 { // api key not passed as flag. Check that the cluster is registered. apiKey = validateClusterRegistered(alias, true) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/mc-20231220T071422Z/cmd/tree-main.go new/mc-20240105T050432Z/cmd/tree-main.go --- old/mc-20231220T071422Z/cmd/tree-main.go 2023-12-20 08:14:22.000000000 +0100 +++ new/mc-20240105T050432Z/cmd/tree-main.go 2024-01-05 06:04:32.000000000 +0100 @@ -195,6 +195,16 @@ prefixPath = strings.TrimPrefix(prefixPath, "."+separator) if prev.Type.IsDir() { + nextURL := "" + if targetAlias != "" { + nextURL = targetAlias + "/" + contentURL + } else { + nextURL = contentURL + } + + if nextURL == url { + return nil + } printMsg(treeMessage{ Entry: strings.TrimSuffix(strings.TrimPrefix(contentURL, prefixPath), "/"), IsDir: true, ++++++ mc.obsinfo ++++++ --- /var/tmp/diff_new_pack.VhObcT/_old 2024-01-07 21:40:43.180930042 +0100 +++ /var/tmp/diff_new_pack.VhObcT/_new 2024-01-07 21:40:43.184930188 +0100 @@ -1,5 +1,5 @@ name: mc -version: 20231220T071422Z -mtime: 1703056462 -commit: 8e1573ec1b9c174e9f8d82ee9996d002c1d9caaa +version: 20240105T050432Z +mtime: 1704431072 +commit: 59eca9fea8984adec1e8e7a1c95d0ea23107ceff ++++++ vendor.tar.gz ++++++ /work/SRC/openSUSE:Factory/minio-client/vendor.tar.gz /work/SRC/openSUSE:Factory/.minio-client.new.28375/vendor.tar.gz differ: char 5, line 1