mirror of
https://github.com/pgsty/minio.git
synced 2026-11-04 06:42:04 +03:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 499531f0b5 | |||
| 3c2141513f | |||
| 602f6a9ad0 | |||
| 22c5a5b91b | |||
| 41f508765d | |||
| 7dccd1f589 | |||
| 55ff598b23 | |||
| a22ce4550c | |||
| 168ae81b1f | |||
| 5f6a25cdd0 |
@@ -479,3 +479,179 @@ func (a adminAPIHandlers) ListAccessKeysLDAP(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
}
|
||||
|
||||
// ListAccessKeysLDAPBulk - GET /minio/admin/v3/idp/ldap/list-access-keys-bulk
|
||||
func (a adminAPIHandlers) ListAccessKeysLDAPBulk(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Get current object layer instance.
|
||||
objectAPI := newObjectLayerFn()
|
||||
if objectAPI == nil || globalNotificationSys == nil {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
cred, owner, s3Err := validateAdminSignature(ctx, r, "")
|
||||
if s3Err != ErrNone {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
dnList := r.Form["userDNs"]
|
||||
isAll := r.Form.Get("all") == "true"
|
||||
onlySelf := !isAll && len(dnList) == 0
|
||||
|
||||
if isAll && len(dnList) > 0 {
|
||||
// This should be checked on client side, so return generic error
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Empty DN list and not self, list access keys for all users
|
||||
if isAll {
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListUsersAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
} else if len(dnList) == 1 {
|
||||
var dn string
|
||||
foundResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(dnList[0])
|
||||
if err == nil {
|
||||
dn = foundResult.NormDN
|
||||
}
|
||||
if dn == cred.ParentUser || dnList[0] == cred.ParentUser {
|
||||
onlySelf = true
|
||||
}
|
||||
}
|
||||
|
||||
if !globalIAMSys.IsAllowed(policy.Args{
|
||||
AccountName: cred.AccessKey,
|
||||
Groups: cred.Groups,
|
||||
Action: policy.ListServiceAccountsAdminAction,
|
||||
ConditionValues: getConditionValues(r, "", cred),
|
||||
IsOwner: owner,
|
||||
Claims: cred.Claims,
|
||||
DenyOnly: onlySelf,
|
||||
}) {
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAccessDenied), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
if onlySelf && len(dnList) == 0 {
|
||||
selfDN := cred.AccessKey
|
||||
if cred.ParentUser != "" {
|
||||
selfDN = cred.ParentUser
|
||||
}
|
||||
dnList = append(dnList, selfDN)
|
||||
}
|
||||
|
||||
accessKeyMap := make(map[string]madmin.ListAccessKeysLDAPResp)
|
||||
if isAll {
|
||||
ldapUsers, err := globalIAMSys.ListLDAPUsers(ctx)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for user := range ldapUsers {
|
||||
accessKeyMap[user] = madmin.ListAccessKeysLDAPResp{}
|
||||
}
|
||||
} else {
|
||||
for _, userDN := range dnList {
|
||||
// Validate the userDN
|
||||
foundResult, err := globalIAMSys.LDAPConfig.GetValidatedDNForUsername(userDN)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
if foundResult == nil {
|
||||
continue
|
||||
}
|
||||
accessKeyMap[foundResult.NormDN] = madmin.ListAccessKeysLDAPResp{}
|
||||
}
|
||||
}
|
||||
|
||||
listType := r.Form.Get("listType")
|
||||
var listSTSKeys, listServiceAccounts bool
|
||||
switch listType {
|
||||
case madmin.AccessKeyListUsersOnly:
|
||||
listSTSKeys = false
|
||||
listServiceAccounts = false
|
||||
case madmin.AccessKeyListSTSOnly:
|
||||
listSTSKeys = true
|
||||
listServiceAccounts = false
|
||||
case madmin.AccessKeyListSvcaccOnly:
|
||||
listSTSKeys = false
|
||||
listServiceAccounts = true
|
||||
case madmin.AccessKeyListAll:
|
||||
listSTSKeys = true
|
||||
listServiceAccounts = true
|
||||
default:
|
||||
err := errors.New("invalid list type")
|
||||
writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrInvalidRequest, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
for dn, accessKeys := range accessKeyMap {
|
||||
if listSTSKeys {
|
||||
stsKeys, err := globalIAMSys.ListSTSAccounts(ctx, dn)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for _, sts := range stsKeys {
|
||||
expiryTime := sts.Expiration
|
||||
accessKeys.STSKeys = append(accessKeys.STSKeys, madmin.ServiceAccountInfo{
|
||||
AccessKey: sts.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
// if only STS keys, skip if user has no STS keys
|
||||
if !listServiceAccounts && len(stsKeys) == 0 {
|
||||
delete(accessKeyMap, dn)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if listServiceAccounts {
|
||||
serviceAccounts, err := globalIAMSys.ListServiceAccounts(ctx, dn)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
for _, svc := range serviceAccounts {
|
||||
expiryTime := svc.Expiration
|
||||
accessKeys.ServiceAccounts = append(accessKeys.ServiceAccounts, madmin.ServiceAccountInfo{
|
||||
AccessKey: svc.AccessKey,
|
||||
Expiration: &expiryTime,
|
||||
})
|
||||
}
|
||||
// if only service accounts, skip if user has no service accounts
|
||||
if !listSTSKeys && len(serviceAccounts) == 0 {
|
||||
delete(accessKeyMap, dn)
|
||||
continue
|
||||
}
|
||||
}
|
||||
accessKeyMap[dn] = accessKeys
|
||||
}
|
||||
|
||||
data, err := json.Marshal(accessKeyMap)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
encryptedData, err := madmin.EncryptData(cred.SecretKey, data)
|
||||
if err != nil {
|
||||
writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
writeSuccessResponseJSON(w, encryptedData)
|
||||
}
|
||||
|
||||
+3
-2
@@ -301,8 +301,9 @@ func registerAdminRouter(router *mux.Router, enableConfigOps bool) {
|
||||
// LDAP specific service accounts ops
|
||||
adminRouter.Methods(http.MethodPut).Path(adminVersion + "/idp/ldap/add-service-account").HandlerFunc(adminMiddleware(adminAPI.AddServiceAccountLDAP))
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/idp/ldap/list-access-keys").
|
||||
HandlerFunc(adminMiddleware(adminAPI.ListAccessKeysLDAP)).
|
||||
Queries("userDN", "{userDN:.*}", "listType", "{listType:.*}")
|
||||
HandlerFunc(adminMiddleware(adminAPI.ListAccessKeysLDAP)).Queries("userDN", "{userDN:.*}", "listType", "{listType:.*}")
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion+"/idp/ldap/list-access-keys-bulk").
|
||||
HandlerFunc(adminMiddleware(adminAPI.ListAccessKeysLDAPBulk)).Queries("listType", "{listType:.*}")
|
||||
|
||||
// LDAP IAM operations
|
||||
adminRouter.Methods(http.MethodGet).Path(adminVersion + "/idp/ldap/policy-entities").HandlerFunc(adminMiddleware(adminAPI.ListLDAPPolicyMappingEntities))
|
||||
|
||||
@@ -88,6 +88,8 @@ type healingTracker struct {
|
||||
|
||||
ItemsSkipped uint64
|
||||
BytesSkipped uint64
|
||||
|
||||
RetryAttempts uint64
|
||||
// Add future tracking capabilities
|
||||
// Be sure that they are included in toHealingDisk
|
||||
}
|
||||
@@ -382,6 +384,8 @@ func getLocalDisksToHeal() (disksToHeal Endpoints) {
|
||||
|
||||
var newDiskHealingTimeout = newDynamicTimeout(30*time.Second, 10*time.Second)
|
||||
|
||||
var errRetryHealing = errors.New("some items failed to heal, we will retry healing this drive again")
|
||||
|
||||
func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint) error {
|
||||
poolIdx, setIdx := endpoint.PoolIdx, endpoint.SetIdx
|
||||
disk := getStorageViaEndpoint(endpoint)
|
||||
@@ -451,8 +455,27 @@ func healFreshDisk(ctx context.Context, z *erasureServerPools, endpoint Endpoint
|
||||
return err
|
||||
}
|
||||
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is finished (healed: %d, skipped: %d, failed: %d).", disk, tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
// if objects have failed healing, we attempt a retry to heal the drive upto 3 times before giving up.
|
||||
if tracker.ItemsFailed > 0 && tracker.RetryAttempts < 4 {
|
||||
tracker.RetryAttempts++
|
||||
bugLogIf(ctx, tracker.update(ctx))
|
||||
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is incomplete, retrying %s time (healed: %d, skipped: %d, failed: %d).", disk,
|
||||
humanize.Ordinal(int(tracker.RetryAttempts)), tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
return errRetryHealing
|
||||
}
|
||||
|
||||
if tracker.ItemsFailed > 0 {
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is incomplete, retried %d times (healed: %d, skipped: %d, failed: %d).", disk,
|
||||
tracker.RetryAttempts-1, tracker.ItemsHealed, tracker.ItemsSkipped, tracker.ItemsFailed)
|
||||
} else {
|
||||
if tracker.RetryAttempts > 0 {
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is complete, retried %d times (healed: %d, skipped: %d).", disk,
|
||||
tracker.RetryAttempts-1, tracker.ItemsHealed, tracker.ItemsSkipped)
|
||||
} else {
|
||||
healingLogEvent(ctx, "Healing of drive '%s' is finished (healed: %d, skipped: %d).", disk, tracker.ItemsHealed, tracker.ItemsSkipped)
|
||||
}
|
||||
}
|
||||
if serverDebugLog {
|
||||
tracker.printTo(os.Stdout)
|
||||
fmt.Printf("\n")
|
||||
@@ -524,7 +547,7 @@ func monitorLocalDisksAndHeal(ctx context.Context, z *erasureServerPools) {
|
||||
if err := healFreshDisk(ctx, z, disk); err != nil {
|
||||
globalBackgroundHealState.setDiskHealingStatus(disk, false)
|
||||
timedout := OperationTimedOut{}
|
||||
if !errors.Is(err, context.Canceled) && !errors.As(err, &timedout) {
|
||||
if !errors.Is(err, context.Canceled) && !errors.As(err, &timedout) && !errors.Is(err, errRetryHealing) {
|
||||
printEndpointError(disk, err, false)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -200,6 +200,12 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
return
|
||||
}
|
||||
case "RetryAttempts":
|
||||
z.RetryAttempts, err = dc.ReadUint64()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
default:
|
||||
err = dc.Skip()
|
||||
if err != nil {
|
||||
@@ -213,9 +219,9 @@ func (z *healingTracker) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
// map header, size 25
|
||||
// map header, size 26
|
||||
// write "ID"
|
||||
err = en.Append(0xde, 0x0, 0x19, 0xa2, 0x49, 0x44)
|
||||
err = en.Append(0xde, 0x0, 0x1a, 0xa2, 0x49, 0x44)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -478,15 +484,25 @@ func (z *healingTracker) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
return
|
||||
}
|
||||
// write "RetryAttempts"
|
||||
err = en.Append(0xad, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = en.WriteUint64(z.RetryAttempts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
// map header, size 25
|
||||
// map header, size 26
|
||||
// string "ID"
|
||||
o = append(o, 0xde, 0x0, 0x19, 0xa2, 0x49, 0x44)
|
||||
o = append(o, 0xde, 0x0, 0x1a, 0xa2, 0x49, 0x44)
|
||||
o = msgp.AppendString(o, z.ID)
|
||||
// string "PoolIndex"
|
||||
o = append(o, 0xa9, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x6e, 0x64, 0x65, 0x78)
|
||||
@@ -566,6 +582,9 @@ func (z *healingTracker) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
// string "BytesSkipped"
|
||||
o = append(o, 0xac, 0x42, 0x79, 0x74, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64)
|
||||
o = msgp.AppendUint64(o, z.BytesSkipped)
|
||||
// string "RetryAttempts"
|
||||
o = append(o, 0xad, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73)
|
||||
o = msgp.AppendUint64(o, z.RetryAttempts)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -763,6 +782,12 @@ func (z *healingTracker) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
err = msgp.WrapError(err, "BytesSkipped")
|
||||
return
|
||||
}
|
||||
case "RetryAttempts":
|
||||
z.RetryAttempts, bts, err = msgp.ReadUint64Bytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err, "RetryAttempts")
|
||||
return
|
||||
}
|
||||
default:
|
||||
bts, err = msgp.Skip(bts)
|
||||
if err != nil {
|
||||
@@ -785,6 +810,6 @@ func (z *healingTracker) Msgsize() (s int) {
|
||||
for za0002 := range z.HealedBuckets {
|
||||
s += msgp.StringPrefixSize + len(z.HealedBuckets[za0002])
|
||||
}
|
||||
s += 7 + msgp.StringPrefixSize + len(z.HealID) + 13 + msgp.Uint64Size + 13 + msgp.Uint64Size
|
||||
s += 7 + msgp.StringPrefixSize + len(z.HealID) + 13 + msgp.Uint64Size + 13 + msgp.Uint64Size + 14 + msgp.Uint64Size
|
||||
return
|
||||
}
|
||||
|
||||
+6
-2
@@ -227,7 +227,9 @@ func runDataScanner(ctx context.Context, objAPI ObjectLayer) {
|
||||
binary.LittleEndian.PutUint64(tmp, cycleInfo.next)
|
||||
tmp, _ = cycleInfo.MarshalMsg(tmp)
|
||||
err = saveConfig(ctx, objAPI, dataUsageBloomNamePath, tmp)
|
||||
scannerLogIf(ctx, err, dataUsageBloomNamePath)
|
||||
if err != nil {
|
||||
scannerLogIf(ctx, fmt.Errorf("%w, Object %s", err, dataUsageBloomNamePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -797,7 +799,9 @@ func (f *folderScanner) scanFolder(ctx context.Context, folder cachedFolder, int
|
||||
}, madmin.HealItemObject)
|
||||
stopFn(int(ver.Size))
|
||||
if !isErrObjectNotFound(err) && !isErrVersionNotFound(err) {
|
||||
scannerLogIf(ctx, err, fiv.Name)
|
||||
if err != nil {
|
||||
scannerLogIf(ctx, fmt.Errorf("%w, Object %s/%s/%s", err, bucket, fiv.Name, ver.VersionID))
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
successVersions++
|
||||
|
||||
+62
-8
@@ -439,23 +439,42 @@ func (iamOS *IAMObjectStore) listAllIAMConfigItems(ctx context.Context) (res map
|
||||
return res, nil
|
||||
}
|
||||
|
||||
const (
|
||||
maxIAMLoadOpTime = 5 * time.Second
|
||||
)
|
||||
|
||||
// Assumes cache is locked by caller.
|
||||
func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iamCache) error {
|
||||
func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iamCache, firstTime bool) error {
|
||||
bootstrapTraceMsgFirstTime := func(s string) {
|
||||
if firstTime {
|
||||
bootstrapTraceMsg(s)
|
||||
}
|
||||
}
|
||||
|
||||
if iamOS.objAPI == nil {
|
||||
return errServerNotInitialized
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading all IAM items")
|
||||
bootstrapTraceMsgFirstTime("loading all IAM items")
|
||||
|
||||
listStartTime := UTCNow()
|
||||
listedConfigItems, err := iamOS.listAllIAMConfigItems(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to list IAM data: %w", err)
|
||||
}
|
||||
if took := time.Since(listStartTime); took > maxIAMLoadOpTime {
|
||||
var s strings.Builder
|
||||
for k, v := range listedConfigItems {
|
||||
s.WriteString(fmt.Sprintf(" %s: %d items\n", k, len(v)))
|
||||
}
|
||||
logger.Info("listAllIAMConfigItems took %.2fs with contents:\n%s", took.Seconds(), s.String())
|
||||
}
|
||||
|
||||
// Loads things in the same order as `LoadIAMCache()`
|
||||
|
||||
bootstrapTraceMsg("loading policy documents")
|
||||
bootstrapTraceMsgFirstTime("loading policy documents")
|
||||
|
||||
policyLoadStartTime := UTCNow()
|
||||
policiesList := listedConfigItems[policiesListKey]
|
||||
for _, item := range policiesList {
|
||||
policyName := path.Dir(item)
|
||||
@@ -463,10 +482,14 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the policy doc `%s`: %w", policyName, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(policyLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("Policy docs load took %.2fs (for %d items)", took.Seconds(), len(policiesList))
|
||||
}
|
||||
setDefaultCannedPolicies(cache.iamPolicyDocsMap)
|
||||
|
||||
if iamOS.usersSysType == MinIOUsersSysType {
|
||||
bootstrapTraceMsg("loading regular IAM users")
|
||||
bootstrapTraceMsgFirstTime("loading regular IAM users")
|
||||
regUsersLoadStartTime := UTCNow()
|
||||
regUsersList := listedConfigItems[usersListKey]
|
||||
for _, item := range regUsersList {
|
||||
userName := path.Dir(item)
|
||||
@@ -474,8 +497,14 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the user `%s`: %w", userName, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(regUsersLoadStartTime); took > maxIAMLoadOpTime {
|
||||
actualLoaded := len(cache.iamUsersMap)
|
||||
logger.Info("Reg. users load took %.2fs (for %d items with %d expired items)", took.Seconds(),
|
||||
len(regUsersList), len(regUsersList)-actualLoaded)
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading regular IAM groups")
|
||||
bootstrapTraceMsgFirstTime("loading regular IAM groups")
|
||||
groupsLoadStartTime := UTCNow()
|
||||
groupsList := listedConfigItems[groupsListKey]
|
||||
for _, item := range groupsList {
|
||||
group := path.Dir(item)
|
||||
@@ -483,9 +512,13 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the group `%s`: %w", group, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(groupsLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("Groups load took %.2fs (for %d items)", took.Seconds(), len(groupsList))
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading user policy mapping")
|
||||
bootstrapTraceMsgFirstTime("loading user policy mapping")
|
||||
userPolicyMappingLoadStartTime := UTCNow()
|
||||
userPolicyMappingsList := listedConfigItems[policyDBUsersListKey]
|
||||
for _, item := range userPolicyMappingsList {
|
||||
userName := strings.TrimSuffix(item, ".json")
|
||||
@@ -493,8 +526,12 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the policy mapping for the user `%s`: %w", userName, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(userPolicyMappingLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("User policy mappings load took %.2fs (for %d items)", took.Seconds(), len(userPolicyMappingsList))
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading group policy mapping")
|
||||
bootstrapTraceMsgFirstTime("loading group policy mapping")
|
||||
groupPolicyMappingLoadStartTime := UTCNow()
|
||||
groupPolicyMappingsList := listedConfigItems[policyDBGroupsListKey]
|
||||
for _, item := range groupPolicyMappingsList {
|
||||
groupName := strings.TrimSuffix(item, ".json")
|
||||
@@ -502,8 +539,12 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the policy mapping for the group `%s`: %w", groupName, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(groupPolicyMappingLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("Group policy mappings load took %.2fs (for %d items)", took.Seconds(), len(groupPolicyMappingsList))
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading service accounts")
|
||||
bootstrapTraceMsgFirstTime("loading service accounts")
|
||||
svcAccLoadStartTime := UTCNow()
|
||||
svcAccList := listedConfigItems[svcAccListKey]
|
||||
svcUsersMap := make(map[string]UserIdentity, len(svcAccList))
|
||||
for _, item := range svcAccList {
|
||||
@@ -512,9 +553,18 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
return fmt.Errorf("unable to load the service account `%s`: %w", userName, err)
|
||||
}
|
||||
}
|
||||
if took := time.Since(svcAccLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("Service accounts load took %.2fs (for %d items with %d expired items)", took.Seconds(),
|
||||
len(svcAccList), len(svcAccList)-len(svcUsersMap))
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading STS account policy mapping")
|
||||
stsPolicyMappingLoadStartTime := UTCNow()
|
||||
var stsPolicyMappingsCount int
|
||||
for _, svcAcc := range svcUsersMap {
|
||||
svcParent := svcAcc.Credentials.ParentUser
|
||||
if _, ok := cache.iamUsersMap[svcParent]; !ok {
|
||||
stsPolicyMappingsCount++
|
||||
// If a service account's parent user is not in iamUsersMap, the
|
||||
// parent is an STS account. Such accounts may have a policy mapped
|
||||
// on the parent user, so we load them. This is not needed for the
|
||||
@@ -533,6 +583,10 @@ func (iamOS *IAMObjectStore) loadAllFromObjStore(ctx context.Context, cache *iam
|
||||
}
|
||||
}
|
||||
}
|
||||
if took := time.Since(stsPolicyMappingLoadStartTime); took > maxIAMLoadOpTime {
|
||||
logger.Info("STS policy mappings load took %.2fs (for %d items)", took.Seconds(), stsPolicyMappingsCount)
|
||||
}
|
||||
|
||||
// Copy svcUsersMap to cache.iamUsersMap
|
||||
for k, v := range svcUsersMap {
|
||||
cache.iamUsersMap[k] = v
|
||||
|
||||
+47
-10
@@ -537,25 +537,25 @@ func setDefaultCannedPolicies(policies map[string]PolicyDoc) {
|
||||
// LoadIAMCache reads all IAM items and populates a new iamCache object and
|
||||
// replaces the in-memory cache object.
|
||||
func (store *IAMStoreSys) LoadIAMCache(ctx context.Context, firstTime bool) error {
|
||||
bootstrapTraceMsg := func(s string) {
|
||||
bootstrapTraceMsgFirstTime := func(s string) {
|
||||
if firstTime {
|
||||
bootstrapTraceMsg(s)
|
||||
}
|
||||
}
|
||||
bootstrapTraceMsg("loading IAM data")
|
||||
bootstrapTraceMsgFirstTime("loading IAM data")
|
||||
|
||||
newCache := newIamCache()
|
||||
|
||||
loadedAt := time.Now()
|
||||
|
||||
if iamOS, ok := store.IAMStorageAPI.(*IAMObjectStore); ok {
|
||||
err := iamOS.loadAllFromObjStore(ctx, newCache)
|
||||
err := iamOS.loadAllFromObjStore(ctx, newCache, firstTime)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
||||
bootstrapTraceMsg("loading policy documents")
|
||||
// Only non-object IAM store (i.e. only etcd backend).
|
||||
bootstrapTraceMsgFirstTime("loading policy documents")
|
||||
if err := store.loadPolicyDocs(ctx, newCache.iamPolicyDocsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -564,29 +564,29 @@ func (store *IAMStoreSys) LoadIAMCache(ctx context.Context, firstTime bool) erro
|
||||
setDefaultCannedPolicies(newCache.iamPolicyDocsMap)
|
||||
|
||||
if store.getUsersSysType() == MinIOUsersSysType {
|
||||
bootstrapTraceMsg("loading regular users")
|
||||
bootstrapTraceMsgFirstTime("loading regular users")
|
||||
if err := store.loadUsers(ctx, regUser, newCache.iamUsersMap); err != nil {
|
||||
return err
|
||||
}
|
||||
bootstrapTraceMsg("loading regular groups")
|
||||
bootstrapTraceMsgFirstTime("loading regular groups")
|
||||
if err := store.loadGroups(ctx, newCache.iamGroupsMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading user policy mapping")
|
||||
bootstrapTraceMsgFirstTime("loading user policy mapping")
|
||||
// load polices mapped to users
|
||||
if err := store.loadMappedPolicies(ctx, regUser, false, newCache.iamUserPolicyMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading group policy mapping")
|
||||
bootstrapTraceMsgFirstTime("loading group policy mapping")
|
||||
// load policies mapped to groups
|
||||
if err := store.loadMappedPolicies(ctx, regUser, true, newCache.iamGroupPolicyMap); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bootstrapTraceMsg("loading service accounts")
|
||||
bootstrapTraceMsgFirstTime("loading service accounts")
|
||||
// load service accounts
|
||||
if err := store.loadUsers(ctx, svcUser, newCache.iamUsersMap); err != nil {
|
||||
return err
|
||||
@@ -1907,6 +1907,11 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo {
|
||||
cache := store.rlock()
|
||||
defer store.runlock()
|
||||
|
||||
return store.getParentUsers(cache)
|
||||
}
|
||||
|
||||
// assumes store is locked by caller.
|
||||
func (store *IAMStoreSys) getParentUsers(cache *iamCache) map[string]ParentUserInfo {
|
||||
res := map[string]ParentUserInfo{}
|
||||
for _, ui := range cache.iamUsersMap {
|
||||
cred := ui.Credentials
|
||||
@@ -1977,6 +1982,38 @@ func (store *IAMStoreSys) GetAllParentUsers() map[string]ParentUserInfo {
|
||||
return res
|
||||
}
|
||||
|
||||
// GetAllSTSUserMappings - Loads all STS user policy mappings from storage and
|
||||
// returns them. Also gets any STS users that do not have policy mappings but have
|
||||
// Service Accounts or STS keys (This is useful if the user is part of a group)
|
||||
func (store *IAMStoreSys) GetAllSTSUserMappings(userPredicate func(string) bool) (map[string]string, error) {
|
||||
cache := store.rlock()
|
||||
defer store.runlock()
|
||||
|
||||
stsMap := make(map[string]string)
|
||||
m := xsync.NewMapOf[string, MappedPolicy]()
|
||||
if err := store.loadMappedPolicies(context.Background(), stsUser, false, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.Range(func(user string, mappedPolicy MappedPolicy) bool {
|
||||
if userPredicate != nil && !userPredicate(user) {
|
||||
return true
|
||||
}
|
||||
stsMap[user] = mappedPolicy.Policies
|
||||
return true
|
||||
})
|
||||
|
||||
for user := range store.getParentUsers(cache) {
|
||||
if _, ok := stsMap[user]; !ok {
|
||||
if userPredicate != nil && !userPredicate(user) {
|
||||
continue
|
||||
}
|
||||
stsMap[user] = ""
|
||||
}
|
||||
}
|
||||
return stsMap, nil
|
||||
}
|
||||
|
||||
// Assumes store is locked by caller. If users is empty, returns all user mappings.
|
||||
func (store *IAMStoreSys) listUserPolicyMappings(cache *iamCache, users []string,
|
||||
userPredicate func(string) bool,
|
||||
|
||||
+7
-3
@@ -786,11 +786,15 @@ func (sys *IAMSys) ListLDAPUsers(ctx context.Context) (map[string]madmin.UserInf
|
||||
|
||||
select {
|
||||
case <-sys.configLoaded:
|
||||
ldapUsers := make(map[string]madmin.UserInfo)
|
||||
for user, policy := range sys.store.GetUsersWithMappedPolicies() {
|
||||
stsMap, err := sys.store.GetAllSTSUserMappings(sys.LDAPConfig.IsLDAPUserDN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ldapUsers := make(map[string]madmin.UserInfo, len(stsMap))
|
||||
for user, policy := range stsMap {
|
||||
ldapUsers[user] = madmin.UserInfo{
|
||||
PolicyName: policy,
|
||||
Status: madmin.AccountEnabled,
|
||||
Status: statusEnabled,
|
||||
}
|
||||
}
|
||||
return ldapUsers, nil
|
||||
|
||||
@@ -43,8 +43,8 @@ unset MINIO_KMS_KES_KEY_FILE
|
||||
unset MINIO_KMS_KES_ENDPOINT
|
||||
unset MINIO_KMS_KES_KEY_NAME
|
||||
|
||||
wget -q -O mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x mc
|
||||
go install -v github.com/minio/mc@master
|
||||
cp -a $(go env GOPATH)/bin/mc ./mc
|
||||
|
||||
if [ ! -f mc.RELEASE.2021-03-12T03-36-59Z ]; then
|
||||
wget -q -O mc.RELEASE.2021-03-12T03-36-59Z https://dl.minio.io/client/mc/release/linux-amd64/archive/mc.RELEASE.2021-03-12T03-36-59Z &&
|
||||
|
||||
@@ -74,7 +74,7 @@ pools:
|
||||
- Each pool expects a minimum of 2 nodes per pool, and unique non-repeating hosts for each argument.
|
||||
- Each pool expects each host in this pool has the same number of drives specified as any other host.
|
||||
- Mixing `local-path` and `distributed-path` is not allowed, doing so would cause MinIO to refuse starting the server.
|
||||
- Ellipses notation (e.g. `{1...10}`) or bracket notations are fully allowed (e.g. `{a,c,f}`) to have multiple entries in one line.
|
||||
- Ellipses and bracket notation (e.g. `{1...10}`) are allowed.
|
||||
|
||||
> NOTE: MinIO environmental variables still take precedence over the `config.yaml` file, however `config.yaml` is preferred over MinIO internal config KV settings via `mc admin config set alias/ <sub-system>`.
|
||||
|
||||
@@ -88,3 +88,4 @@ In subsequent releases we are planning to extend this to provide things like
|
||||
and decommissioning to provide a functionality that smaller deployments
|
||||
care about.
|
||||
|
||||
- Fully allow bracket notation (e.g. `{a,c,f}`) to have multiple entries on one line.
|
||||
@@ -8,10 +8,8 @@ pkill minio
|
||||
pkill kes
|
||||
rm -rf /tmp/xl
|
||||
|
||||
if [ ! -f ./mc ]; then
|
||||
wget --quiet -O mc https://dl.minio.io/client/mc/release/linux-amd64/mc &&
|
||||
chmod +x mc
|
||||
fi
|
||||
go install -v github.com/minio/mc@master
|
||||
cp -a $(go env GOPATH)/bin/mc ./mc
|
||||
|
||||
if [ ! -f ./kes ]; then
|
||||
wget --quiet -O kes https://github.com/minio/kes/releases/latest/download/kes-linux-amd64 &&
|
||||
@@ -39,37 +37,37 @@ export MC_HOST_myminio="http://minioadmin:minioadmin@localhost:9000/"
|
||||
(minio server http://localhost:9000/tmp/xl/{1...10}/disk{0...1} 2>&1 >/dev/null) &
|
||||
pid=$!
|
||||
|
||||
./mc ready myminio
|
||||
mc ready myminio
|
||||
|
||||
./mc admin user add myminio/ minio123 minio123
|
||||
mc admin user add myminio/ minio123 minio123
|
||||
|
||||
./mc admin policy create myminio/ deny-non-sse-kms-pol ./docs/iam/policies/deny-non-sse-kms-objects.json
|
||||
./mc admin policy create myminio/ deny-invalid-sse-kms-pol ./docs/iam/policies/deny-objects-with-invalid-sse-kms-key-id.json
|
||||
mc admin policy create myminio/ deny-non-sse-kms-pol ./docs/iam/policies/deny-non-sse-kms-objects.json
|
||||
mc admin policy create myminio/ deny-invalid-sse-kms-pol ./docs/iam/policies/deny-objects-with-invalid-sse-kms-key-id.json
|
||||
|
||||
./mc admin policy attach myminio deny-non-sse-kms-pol --user minio123
|
||||
./mc admin policy attach myminio deny-invalid-sse-kms-pol --user minio123
|
||||
./mc admin policy attach myminio consoleAdmin --user minio123
|
||||
mc admin policy attach myminio deny-non-sse-kms-pol --user minio123
|
||||
mc admin policy attach myminio deny-invalid-sse-kms-pol --user minio123
|
||||
mc admin policy attach myminio consoleAdmin --user minio123
|
||||
|
||||
./mc mb -l myminio/test-bucket
|
||||
./mc mb -l myminio/multi-key-poc
|
||||
mc mb -l myminio/test-bucket
|
||||
mc mb -l myminio/multi-key-poc
|
||||
|
||||
export MC_HOST_myminio1="http://minio123:minio123@localhost:9000/"
|
||||
|
||||
./mc cp /etc/issue myminio1/test-bucket
|
||||
mc cp /etc/issue myminio1/test-bucket
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "BUG: PutObject to bucket: test-bucket should succeed. Failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./mc cp /etc/issue myminio1/multi-key-poc | grep -q "Insufficient permissions to access this path"
|
||||
mc cp /etc/issue myminio1/multi-key-poc | grep -q "Insufficient permissions to access this path"
|
||||
ret=$?
|
||||
if [ $ret -eq 0 ]; then
|
||||
echo "BUG: PutObject to bucket: multi-key-poc without sse-kms should fail. Succedded"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./mc cp /etc/hosts myminio1/multi-key-poc/hosts --enc-kms "myminio1/multi-key-poc/hosts=minio-default-key"
|
||||
mc cp /etc/hosts myminio1/multi-key-poc/hosts --enc-kms "myminio1/multi-key-poc/hosts=minio-default-key"
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "BUG: PutObject to bucket: multi-key-poc with valid sse-kms should succeed. Failed"
|
||||
|
||||
@@ -2,7 +2,7 @@ version: '3.7'
|
||||
|
||||
# Settings and configurations that are common for all containers
|
||||
x-minio-common: &minio-common
|
||||
image: quay.io/minio/minio:RELEASE.2024-06-13T22-53-53Z
|
||||
image: quay.io/minio/minio:RELEASE.2024-06-22T05-26-45Z
|
||||
command: server --console-address ":9001" http://minio{1...4}/data{1...2}
|
||||
expose:
|
||||
- "9000"
|
||||
|
||||
@@ -45,14 +45,14 @@ require (
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/minio/cli v1.24.2
|
||||
github.com/minio/console v1.6.0
|
||||
github.com/minio/console v1.6.1
|
||||
github.com/minio/csvparser v1.0.0
|
||||
github.com/minio/dnscache v0.1.1
|
||||
github.com/minio/dperf v0.5.3
|
||||
github.com/minio/highwayhash v1.0.2
|
||||
github.com/minio/kms-go/kes v0.3.0
|
||||
github.com/minio/kms-go/kms v0.4.0
|
||||
github.com/minio/madmin-go/v3 v3.0.55
|
||||
github.com/minio/madmin-go/v3 v3.0.57
|
||||
github.com/minio/minio-go/v7 v7.0.72-0.20240610154810-fa174cbf14b0
|
||||
github.com/minio/mux v1.9.0
|
||||
github.com/minio/pkg/v3 v3.0.2
|
||||
|
||||
@@ -440,8 +440,8 @@ github.com/minio/cli v1.24.2 h1:J+fCUh9mhPLjN3Lj/YhklXvxj8mnyE/D6FpFduXJ2jg=
|
||||
github.com/minio/cli v1.24.2/go.mod h1:bYxnK0uS629N3Bq+AOZZ+6lwF77Sodk4+UL9vNuXhOY=
|
||||
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/console v1.6.0 h1:G3mjhGV2Pox1Sqjwp/jRbRY7WiKsVyCLaZkxoIOaMCU=
|
||||
github.com/minio/console v1.6.0/go.mod h1:XJ3HKHmigs1MgjaNjUwpyuOAJnwqlSMB+QnZCZ+BROY=
|
||||
github.com/minio/console v1.6.1 h1:/rlXITBdZeDcX33PCjhEdA2vufeMMFMufj1XVukbu+0=
|
||||
github.com/minio/console v1.6.1/go.mod h1:XJ3HKHmigs1MgjaNjUwpyuOAJnwqlSMB+QnZCZ+BROY=
|
||||
github.com/minio/csvparser v1.0.0 h1:xJEHcYK8ZAjeW4hNV9Zu30u+/2o4UyPnYgyjWp8b7ZU=
|
||||
github.com/minio/csvparser v1.0.0/go.mod h1:lKXskSLzPgC5WQyzP7maKH7Sl1cqvANXo9YCto8zbtM=
|
||||
github.com/minio/dnscache v0.1.1 h1:AMYLqomzskpORiUA1ciN9k7bZT1oB3YZN4cEIi88W5o=
|
||||
@@ -456,8 +456,8 @@ github.com/minio/kms-go/kes v0.3.0 h1:SU8VGVM/Hk9w1OiSby3OatkcojooUqIdDHl6dtM6Nk
|
||||
github.com/minio/kms-go/kes v0.3.0/go.mod h1:w6DeVT878qEOU3nUrYVy1WOT5H1Ig9hbDIh698NYJKY=
|
||||
github.com/minio/kms-go/kms v0.4.0 h1:cLPZceEp+05xHotVBaeFJrgL7JcXM4lBy6PU0idkE7I=
|
||||
github.com/minio/kms-go/kms v0.4.0/go.mod h1:q12CehiIy2qgBnDKq6Q7wmPi2PHSyRVug5DKp0HAVeE=
|
||||
github.com/minio/madmin-go/v3 v3.0.55 h1:Vm5AWS0kFoWwoJX4epskjVwmmS64xMNORMZaGR3cbK8=
|
||||
github.com/minio/madmin-go/v3 v3.0.55/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
|
||||
github.com/minio/madmin-go/v3 v3.0.57 h1:fXoOnYP8/k9x0MWWowXkAQWYu59hongieCcT3urUaAQ=
|
||||
github.com/minio/madmin-go/v3 v3.0.57/go.mod h1:IFAwr0XMrdsLovxAdCcuq/eoL4nRuMVQQv0iubJANQw=
|
||||
github.com/minio/mc v0.0.0-20240612143403-e7c9a733c680 h1:Ns5mhSm86qJx6a9GJ1kzHkZMjRMZrQGsptakVRmq4QA=
|
||||
github.com/minio/mc v0.0.0-20240612143403-e7c9a733c680/go.mod h1:21/cb+wUd+lLRsdX7ACqyO8DzPNSpXftp1bOkQlIbh8=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
|
||||
@@ -98,7 +98,8 @@ func (l *Config) GetValidatedDNForUsername(username string) (*xldap.DNSearchResu
|
||||
// under a configured base DN in the LDAP directory.
|
||||
validDN, isUnderBaseDN, err := l.GetValidatedUserDN(conn, username)
|
||||
if err == nil && !isUnderBaseDN {
|
||||
return nil, fmt.Errorf("Unable to find user DN: %w", err)
|
||||
// Not under any configured base DN, so treat as not found.
|
||||
return nil, nil
|
||||
}
|
||||
return validDN, err
|
||||
}
|
||||
|
||||
@@ -90,13 +90,14 @@ type Config struct {
|
||||
// buffer is full, new logs are just ignored and an error
|
||||
// is returned to the caller.
|
||||
type Target struct {
|
||||
totalMessages int64
|
||||
failedMessages int64
|
||||
status int32
|
||||
totalMessages atomic.Int64
|
||||
failedMessages atomic.Int64
|
||||
status atomic.Int32
|
||||
|
||||
// Worker control
|
||||
workers int64
|
||||
workers atomic.Int64
|
||||
maxWorkers int64
|
||||
|
||||
// workerStartMu sync.Mutex
|
||||
lastStarted time.Time
|
||||
|
||||
@@ -157,7 +158,7 @@ func (h *Target) String() string {
|
||||
|
||||
// IsOnline returns true if the target is reachable using a cached value
|
||||
func (h *Target) IsOnline(ctx context.Context) bool {
|
||||
return atomic.LoadInt32(&h.status) == statusOnline
|
||||
return h.status.Load() == statusOnline
|
||||
}
|
||||
|
||||
// Stats returns the target statistics.
|
||||
@@ -166,8 +167,8 @@ func (h *Target) Stats() types.TargetStats {
|
||||
queueLength := len(h.logCh)
|
||||
h.logChMu.RUnlock()
|
||||
stats := types.TargetStats{
|
||||
TotalMessages: atomic.LoadInt64(&h.totalMessages),
|
||||
FailedMessages: atomic.LoadInt64(&h.failedMessages),
|
||||
TotalMessages: h.totalMessages.Load(),
|
||||
FailedMessages: h.failedMessages.Load(),
|
||||
QueueLength: queueLength,
|
||||
}
|
||||
|
||||
@@ -221,9 +222,9 @@ func (h *Target) initMemoryStore(ctx context.Context) (err error) {
|
||||
func (h *Target) send(ctx context.Context, payload []byte, payloadType string, timeout time.Duration) (err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
atomic.StoreInt32(&h.status, statusOffline)
|
||||
h.status.Store(statusOffline)
|
||||
} else {
|
||||
atomic.StoreInt32(&h.status, statusOnline)
|
||||
h.status.Store(statusOnline)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -275,8 +276,8 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
}
|
||||
h.logChMu.RUnlock()
|
||||
|
||||
atomic.AddInt64(&h.workers, 1)
|
||||
defer atomic.AddInt64(&h.workers, -1)
|
||||
h.workers.Add(1)
|
||||
defer h.workers.Add(-1)
|
||||
|
||||
h.wg.Add(1)
|
||||
defer h.wg.Done()
|
||||
@@ -353,7 +354,7 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
}
|
||||
|
||||
if !isTick {
|
||||
atomic.AddInt64(&h.totalMessages, 1)
|
||||
h.totalMessages.Add(1)
|
||||
|
||||
if !isDirQueue {
|
||||
if err := enc.Encode(&entry); err != nil {
|
||||
@@ -362,7 +363,7 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
fmt.Errorf("unable to encode webhook log entry, err '%w' entry: %v\n", err, entry),
|
||||
h.Name(),
|
||||
)
|
||||
atomic.AddInt64(&h.failedMessages, 1)
|
||||
h.failedMessages.Add(1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -395,7 +396,7 @@ func (h *Target) startQueueProcessor(ctx context.Context, mainWorker bool) {
|
||||
// and when it's been at least 30 seconds since
|
||||
// we launched a new worker.
|
||||
if mainWorker && len(h.logCh) > cap(h.logCh)/2 {
|
||||
nWorkers := atomic.LoadInt64(&h.workers)
|
||||
nWorkers := h.workers.Load()
|
||||
if nWorkers < h.maxWorkers {
|
||||
if time.Since(h.lastStarted).Milliseconds() > 10 {
|
||||
h.lastStarted = time.Now()
|
||||
@@ -493,10 +494,10 @@ func New(config Config) (*Target, error) {
|
||||
h := &Target{
|
||||
logCh: make(chan interface{}, config.QueueSize),
|
||||
config: config,
|
||||
status: statusOffline,
|
||||
batchSize: config.BatchSize,
|
||||
maxWorkers: int64(maxWorkers),
|
||||
}
|
||||
h.status.Store(statusOffline)
|
||||
|
||||
if config.BatchSize > 1 {
|
||||
h.payloadType = ""
|
||||
@@ -528,10 +529,17 @@ func (h *Target) SendFromStore(key store.Key) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
h.failedMessages.Add(1)
|
||||
defer func() {
|
||||
if err == nil {
|
||||
h.failedMessages.Add(-1)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := h.send(context.Background(), eventData, h.payloadType, webhookCallTimeout); err != nil {
|
||||
atomic.AddInt64(&h.failedMessages, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete the event from store.
|
||||
return h.store.Del(key.Name)
|
||||
}
|
||||
@@ -540,7 +548,7 @@ func (h *Target) SendFromStore(key store.Key) (err error) {
|
||||
// Messages are queued in the disk if the store is enabled
|
||||
// If Cancel has been called the message is ignored.
|
||||
func (h *Target) Send(ctx context.Context, entry interface{}) error {
|
||||
if atomic.LoadInt32(&h.status) == statusClosed {
|
||||
if h.status.Load() == statusClosed {
|
||||
if h.migrateTarget != nil {
|
||||
return h.migrateTarget.Send(ctx, entry)
|
||||
}
|
||||
@@ -557,7 +565,7 @@ func (h *Target) Send(ctx context.Context, entry interface{}) error {
|
||||
retry:
|
||||
select {
|
||||
case h.logCh <- entry:
|
||||
atomic.AddInt64(&h.totalMessages, 1)
|
||||
h.totalMessages.Add(1)
|
||||
case <-ctx.Done():
|
||||
// return error only for context timedout.
|
||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
||||
@@ -565,11 +573,14 @@ retry:
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
if h.workers < h.maxWorkers {
|
||||
nWorkers := h.workers.Load()
|
||||
if nWorkers < h.maxWorkers {
|
||||
// Just sleep to avoid any possible hot-loops.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
goto retry
|
||||
}
|
||||
atomic.AddInt64(&h.totalMessages, 1)
|
||||
atomic.AddInt64(&h.failedMessages, 1)
|
||||
h.totalMessages.Add(1)
|
||||
h.failedMessages.Add(1)
|
||||
return errors.New("log buffer full")
|
||||
}
|
||||
|
||||
@@ -580,7 +591,7 @@ retry:
|
||||
// All queued messages are flushed and the function returns afterwards.
|
||||
// All messages sent to the target after this function has been called will be dropped.
|
||||
func (h *Target) Cancel() {
|
||||
atomic.StoreInt32(&h.status, statusClosed)
|
||||
h.status.Store(statusClosed)
|
||||
h.storeCtxCancel()
|
||||
|
||||
// Wait for messages to be sent...
|
||||
|
||||
Reference in New Issue
Block a user