github.com/minio/madmin-go/v3@v3.0.51/replication-api.go (about) 1 // 2 // Copyright (c) 2015-2022 MinIO, Inc. 3 // 4 // This file is part of MinIO Object Storage stack 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU Affero General Public License as 8 // published by the Free Software Foundation, either version 3 of the 9 // License, or (at your option) any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU Affero General Public License for more details. 15 // 16 // You should have received a copy of the GNU Affero General Public License 17 // along with this program. If not, see <http://www.gnu.org/licenses/>. 18 // 19 20 package madmin 21 22 import ( 23 "context" 24 "encoding/json" 25 "net/http" 26 "net/url" 27 "time" 28 ) 29 30 //go:generate msgp -file $GOFILE 31 32 // ReplDiffOpts holds options for `mc replicate diff` command 33 // 34 //msgp:ignore ReplDiffOpts 35 type ReplDiffOpts struct { 36 ARN string 37 Verbose bool 38 Prefix string 39 } 40 41 // TgtDiffInfo returns status of unreplicated objects 42 // for the target ARN 43 //msgp:ignore TgtDiffInfo 44 45 type TgtDiffInfo struct { 46 ReplicationStatus string `json:"rStatus,omitempty"` // target replication status 47 DeleteReplicationStatus string `json:"drStatus,omitempty"` // target delete replication status 48 } 49 50 // DiffInfo represents relevant replication status and last attempt to replicate 51 // for the replication targets configured for the bucket 52 //msgp:ignore DiffInfo 53 54 type DiffInfo struct { 55 Object string `json:"object"` 56 VersionID string `json:"versionId"` 57 Targets map[string]TgtDiffInfo `json:"targets,omitempty"` 58 Err error `json:"error,omitempty"` 59 ReplicationStatus string `json:"rStatus,omitempty"` // overall replication status 60 DeleteReplicationStatus string `json:"dStatus,omitempty"` // overall replication status of version delete 61 ReplicationTimestamp time.Time `json:"replTimestamp,omitempty"` 62 LastModified time.Time `json:"lastModified,omitempty"` 63 IsDeleteMarker bool `json:"deletemarker"` 64 } 65 66 // BucketReplicationDiff - gets diff for non-replicated entries. 67 func (adm *AdminClient) BucketReplicationDiff(ctx context.Context, bucketName string, opts ReplDiffOpts) <-chan DiffInfo { 68 diffCh := make(chan DiffInfo) 69 70 // start a routine to start reading line by line. 71 go func(diffCh chan<- DiffInfo) { 72 defer close(diffCh) 73 queryValues := url.Values{} 74 queryValues.Set("bucket", bucketName) 75 76 if opts.Verbose { 77 queryValues.Set("verbose", "true") 78 } 79 if opts.ARN != "" { 80 queryValues.Set("arn", opts.ARN) 81 } 82 if opts.Prefix != "" { 83 queryValues.Set("prefix", opts.Prefix) 84 } 85 86 reqData := requestData{ 87 relPath: adminAPIPrefix + "/replication/diff", 88 queryValues: queryValues, 89 } 90 91 // Execute PUT on /minio/admin/v3/diff to set quota for a bucket. 92 resp, err := adm.executeMethod(ctx, http.MethodPost, reqData) 93 if err != nil { 94 diffCh <- DiffInfo{Err: err} 95 return 96 } 97 defer closeResponse(resp) 98 99 if resp.StatusCode != http.StatusOK { 100 diffCh <- DiffInfo{Err: httpRespToErrorResponse(resp)} 101 return 102 } 103 104 dec := json.NewDecoder(resp.Body) 105 for { 106 var di DiffInfo 107 if err = dec.Decode(&di); err != nil { 108 break 109 } 110 select { 111 case <-ctx.Done(): 112 return 113 case diffCh <- di: 114 } 115 } 116 }(diffCh) 117 // Returns the diff channel, for caller to start reading from. 118 return diffCh 119 } 120 121 // ReplicationMRF represents MRF backlog for a bucket 122 type ReplicationMRF struct { 123 NodeName string `json:"nodeName" msg:"n"` 124 Bucket string `json:"bucket" msg:"b"` 125 Object string `json:"object" msg:"o"` 126 VersionID string `json:"versionId" msg:"v"` 127 RetryCount int `json:"retryCount" msg:"rc"` 128 Err string `json:"error,omitempty" msg:"err"` 129 } 130 131 // BucketReplicationMRF - gets MRF entries for bucket and node. Return MRF across buckets if bucket is empty, across nodes 132 // if node is `all` 133 func (adm *AdminClient) BucketReplicationMRF(ctx context.Context, bucketName string, node string) <-chan ReplicationMRF { 134 mrfCh := make(chan ReplicationMRF) 135 136 // start a routine to start reading line by line. 137 go func(mrfCh chan<- ReplicationMRF) { 138 defer close(mrfCh) 139 queryValues := url.Values{} 140 queryValues.Set("bucket", bucketName) 141 if node != "" { 142 queryValues.Set("node", node) 143 } 144 reqData := requestData{ 145 relPath: adminAPIPrefix + "/replication/mrf", 146 queryValues: queryValues, 147 } 148 149 // Execute GET on /minio/admin/v3/replication/mrf to get mrf backlog for a bucket. 150 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 151 if err != nil { 152 mrfCh <- ReplicationMRF{Err: err.Error()} 153 return 154 } 155 defer closeResponse(resp) 156 157 if resp.StatusCode != http.StatusOK { 158 mrfCh <- ReplicationMRF{Err: httpRespToErrorResponse(resp).Error()} 159 return 160 } 161 dec := json.NewDecoder(resp.Body) 162 for { 163 var bk ReplicationMRF 164 if err = dec.Decode(&bk); err != nil { 165 break 166 } 167 select { 168 case <-ctx.Done(): 169 return 170 case mrfCh <- bk: 171 } 172 } 173 }(mrfCh) 174 // Returns the mrf backlog channel, for caller to start reading from. 175 return mrfCh 176 } 177 178 // LatencyStat represents replication link latency statistics 179 type LatencyStat struct { 180 Curr time.Duration `json:"curr"` 181 Avg time.Duration `json:"avg"` 182 Max time.Duration `json:"max"` 183 } 184 185 // TimedErrStats has failed replication stats across time windows 186 type TimedErrStats struct { 187 LastMinute RStat `json:"lastMinute"` 188 LastHour RStat `json:"lastHour"` 189 Totals RStat `json:"totals"` 190 // ErrCounts is a map of error codes to count of errors since server start - tracks 191 // only AccessDenied errors for now. 192 ErrCounts map[string]int `json:"errCounts,omitempty"` 193 } 194 195 // Add - adds two TimedErrStats 196 func (te TimedErrStats) Add(o TimedErrStats) TimedErrStats { 197 m := make(map[string]int) 198 for k, v := range te.ErrCounts { 199 m[k] = v 200 } 201 for k, v := range o.ErrCounts { 202 m[k] += v 203 } 204 return TimedErrStats{ 205 LastMinute: te.LastMinute.Add(o.LastMinute), 206 LastHour: te.LastHour.Add(o.LastHour), 207 Totals: te.Totals.Add(o.Totals), 208 ErrCounts: m, 209 } 210 } 211 212 // RStat represents count and bytes replicated/failed 213 type RStat struct { 214 Count float64 `json:"count"` 215 Bytes int64 `json:"bytes"` 216 } 217 218 // Add - adds two RStats 219 func (r RStat) Add(r1 RStat) RStat { 220 return RStat{ 221 Count: r.Count + r1.Count, 222 Bytes: r.Bytes + r1.Bytes, 223 } 224 }