github.com/minio/madmin-go/v2@v2.2.1/service-commands.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 "strconv" 28 "strings" 29 "time" 30 ) 31 32 // ServiceRestart - restarts the MinIO cluster 33 func (adm *AdminClient) ServiceRestart(ctx context.Context) error { 34 return adm.serviceCallAction(ctx, ServiceActionRestart) 35 } 36 37 // ServiceStop - stops the MinIO cluster 38 func (adm *AdminClient) ServiceStop(ctx context.Context) error { 39 return adm.serviceCallAction(ctx, ServiceActionStop) 40 } 41 42 // ServiceFreeze - freezes all incoming S3 API calls on MinIO cluster 43 func (adm *AdminClient) ServiceFreeze(ctx context.Context) error { 44 return adm.serviceCallAction(ctx, ServiceActionFreeze) 45 } 46 47 // ServiceUnfreeze - un-freezes all incoming S3 API calls on MinIO cluster 48 func (adm *AdminClient) ServiceUnfreeze(ctx context.Context) error { 49 return adm.serviceCallAction(ctx, ServiceActionUnfreeze) 50 } 51 52 // ServiceAction - type to restrict service-action values 53 type ServiceAction string 54 55 const ( 56 // ServiceActionRestart represents restart action 57 ServiceActionRestart ServiceAction = "restart" 58 // ServiceActionStop represents stop action 59 ServiceActionStop = "stop" 60 // ServiceActionFreeze represents freeze action 61 ServiceActionFreeze = "freeze" 62 // ServiceActionUnfreeze represents unfreeze a previous freeze action 63 ServiceActionUnfreeze = "unfreeze" 64 ) 65 66 // serviceCallAction - call service restart/update/stop API. 67 func (adm *AdminClient) serviceCallAction(ctx context.Context, action ServiceAction) error { 68 queryValues := url.Values{} 69 queryValues.Set("action", string(action)) 70 71 // Request API to Restart server 72 resp, err := adm.executeMethod(ctx, 73 http.MethodPost, requestData{ 74 relPath: adminAPIPrefix + "/service", 75 queryValues: queryValues, 76 }, 77 ) 78 defer closeResponse(resp) 79 if err != nil { 80 return err 81 } 82 83 if resp.StatusCode != http.StatusOK { 84 return httpRespToErrorResponse(resp) 85 } 86 87 return nil 88 } 89 90 // ServiceTraceInfo holds http trace 91 type ServiceTraceInfo struct { 92 Trace TraceInfo 93 Err error `json:"-"` 94 } 95 96 // ServiceTraceOpts holds tracing options 97 type ServiceTraceOpts struct { 98 // Trace types: 99 S3 bool 100 Internal bool 101 Storage bool 102 OS bool 103 Scanner bool 104 Decommission bool 105 Healing bool 106 BatchReplication bool 107 BatchKeyRotation bool 108 Rebalance bool 109 ReplicationResync bool 110 Bootstrap bool 111 FTP bool 112 ILM bool 113 OnlyErrors bool 114 Threshold time.Duration 115 } 116 117 // TraceTypes returns the enabled traces as a bitfield value. 118 func (t ServiceTraceOpts) TraceTypes() TraceType { 119 var tt TraceType 120 tt.SetIf(t.S3, TraceS3) 121 tt.SetIf(t.Internal, TraceInternal) 122 tt.SetIf(t.Storage, TraceStorage) 123 tt.SetIf(t.OS, TraceOS) 124 tt.SetIf(t.Scanner, TraceScanner) 125 tt.SetIf(t.Decommission, TraceDecommission) 126 tt.SetIf(t.Healing, TraceHealing) 127 tt.SetIf(t.BatchReplication, TraceBatchReplication) 128 tt.SetIf(t.BatchKeyRotation, TraceBatchKeyRotation) 129 tt.SetIf(t.Rebalance, TraceRebalance) 130 tt.SetIf(t.ReplicationResync, TraceReplicationResync) 131 tt.SetIf(t.Bootstrap, TraceBootstrap) 132 tt.SetIf(t.FTP, TraceFTP) 133 tt.SetIf(t.ILM, TraceILM) 134 135 return tt 136 } 137 138 // AddParams will add parameter to url values. 139 func (t ServiceTraceOpts) AddParams(u url.Values) { 140 u.Set("err", strconv.FormatBool(t.OnlyErrors)) 141 u.Set("threshold", t.Threshold.String()) 142 143 u.Set("s3", strconv.FormatBool(t.S3)) 144 u.Set("internal", strconv.FormatBool(t.Internal)) 145 u.Set("storage", strconv.FormatBool(t.Storage)) 146 u.Set("os", strconv.FormatBool(t.OS)) 147 u.Set("scanner", strconv.FormatBool(t.Scanner)) 148 u.Set("decommission", strconv.FormatBool(t.Decommission)) 149 u.Set("healing", strconv.FormatBool(t.Healing)) 150 u.Set("batch-replication", strconv.FormatBool(t.BatchReplication)) 151 u.Set("batch-keyrotation", strconv.FormatBool(t.BatchKeyRotation)) 152 u.Set("rebalance", strconv.FormatBool(t.Rebalance)) 153 u.Set("replication-resync", strconv.FormatBool(t.ReplicationResync)) 154 u.Set("bootstrap", strconv.FormatBool(t.Bootstrap)) 155 u.Set("ftp", strconv.FormatBool(t.FTP)) 156 u.Set("ilm", strconv.FormatBool(t.ILM)) 157 } 158 159 // ParseParams will parse parameters and set them to t. 160 func (t *ServiceTraceOpts) ParseParams(r *http.Request) (err error) { 161 t.S3 = r.Form.Get("s3") == "true" 162 t.OS = r.Form.Get("os") == "true" 163 t.Scanner = r.Form.Get("scanner") == "true" 164 t.Decommission = r.Form.Get("decommission") == "true" 165 t.Healing = r.Form.Get("healing") == "true" 166 t.BatchReplication = r.Form.Get("batch-replication") == "true" 167 t.BatchKeyRotation = r.Form.Get("batch-keyrotation") == "true" 168 t.Rebalance = r.Form.Get("rebalance") == "true" 169 t.Storage = r.Form.Get("storage") == "true" 170 t.Internal = r.Form.Get("internal") == "true" 171 t.OnlyErrors = r.Form.Get("err") == "true" 172 t.ReplicationResync = r.Form.Get("replication-resync") == "true" 173 t.Bootstrap = r.Form.Get("bootstrap") == "true" 174 t.FTP = r.Form.Get("ftp") == "true" 175 t.ILM = r.Form.Get("ilm") == "true" 176 177 if th := r.Form.Get("threshold"); th != "" { 178 d, err := time.ParseDuration(th) 179 if err != nil { 180 return err 181 } 182 t.Threshold = d 183 } 184 return nil 185 } 186 187 // ServiceTrace - listen on http trace notifications. 188 func (adm AdminClient) ServiceTrace(ctx context.Context, opts ServiceTraceOpts) <-chan ServiceTraceInfo { 189 traceInfoCh := make(chan ServiceTraceInfo) 190 // Only success, start a routine to start reading line by line. 191 go func(traceInfoCh chan<- ServiceTraceInfo) { 192 defer close(traceInfoCh) 193 for { 194 urlValues := make(url.Values) 195 opts.AddParams(urlValues) 196 197 reqData := requestData{ 198 relPath: adminAPIPrefix + "/trace", 199 queryValues: urlValues, 200 } 201 // Execute GET to call trace handler 202 resp, err := adm.executeMethod(ctx, http.MethodGet, reqData) 203 if err != nil { 204 traceInfoCh <- ServiceTraceInfo{Err: err} 205 return 206 } 207 208 if resp.StatusCode != http.StatusOK { 209 closeResponse(resp) 210 traceInfoCh <- ServiceTraceInfo{Err: httpRespToErrorResponse(resp)} 211 return 212 } 213 214 dec := json.NewDecoder(resp.Body) 215 for { 216 var info traceInfoLegacy 217 if err = dec.Decode(&info); err != nil { 218 closeResponse(resp) 219 traceInfoCh <- ServiceTraceInfo{Err: err} 220 break 221 } 222 // Convert if legacy... 223 if info.TraceType == TraceType(0) { 224 if strings.HasPrefix(info.FuncName, "s3.") { 225 info.TraceType = TraceS3 226 } else { 227 info.TraceType = TraceInternal 228 } 229 info.HTTP = &TraceHTTPStats{} 230 if info.ReqInfo != nil { 231 info.Path = info.ReqInfo.Path 232 info.HTTP.ReqInfo = *info.ReqInfo 233 } 234 if info.RespInfo != nil { 235 info.HTTP.RespInfo = *info.RespInfo 236 } 237 if info.CallStats != nil { 238 info.Duration = info.CallStats.Latency 239 info.HTTP.CallStats = *info.CallStats 240 } 241 } 242 if info.TraceType == TraceOS && info.OSStats != nil { 243 info.Path = info.OSStats.Path 244 info.Duration = info.OSStats.Duration 245 } 246 if info.TraceType == TraceStorage && info.StorageStats != nil { 247 info.Path = info.StorageStats.Path 248 info.Duration = info.StorageStats.Duration 249 } 250 select { 251 case <-ctx.Done(): 252 closeResponse(resp) 253 return 254 case traceInfoCh <- ServiceTraceInfo{Trace: info.TraceInfo}: 255 } 256 } 257 } 258 }(traceInfoCh) 259 260 // Returns the trace info channel, for caller to start reading from. 261 return traceInfoCh 262 }