github.com/minio/madmin-go@v1.7.5/service-commands.go (about)

     1  //
     2  // MinIO Object Storage (c) 2021 MinIO, Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //      http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  package madmin
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"net/http"
    23  	"net/url"
    24  	"strconv"
    25  	"strings"
    26  	"time"
    27  )
    28  
    29  // ServiceRestart - restarts the MinIO cluster
    30  func (adm *AdminClient) ServiceRestart(ctx context.Context) error {
    31  	return adm.serviceCallAction(ctx, ServiceActionRestart)
    32  }
    33  
    34  // ServiceStop - stops the MinIO cluster
    35  func (adm *AdminClient) ServiceStop(ctx context.Context) error {
    36  	return adm.serviceCallAction(ctx, ServiceActionStop)
    37  }
    38  
    39  // ServiceFreeze - freezes all incoming S3 API calls on MinIO cluster
    40  func (adm *AdminClient) ServiceFreeze(ctx context.Context) error {
    41  	return adm.serviceCallAction(ctx, ServiceActionFreeze)
    42  }
    43  
    44  // ServiceUnfreeze - un-freezes all incoming S3 API calls on MinIO cluster
    45  func (adm *AdminClient) ServiceUnfreeze(ctx context.Context) error {
    46  	return adm.serviceCallAction(ctx, ServiceActionUnfreeze)
    47  }
    48  
    49  // ServiceAction - type to restrict service-action values
    50  type ServiceAction string
    51  
    52  const (
    53  	// ServiceActionRestart represents restart action
    54  	ServiceActionRestart ServiceAction = "restart"
    55  	// ServiceActionStop represents stop action
    56  	ServiceActionStop = "stop"
    57  	// ServiceActionFreeze represents freeze action
    58  	ServiceActionFreeze = "freeze"
    59  	// ServiceActionUnfreeze represents unfreeze a previous freeze action
    60  	ServiceActionUnfreeze = "unfreeze"
    61  )
    62  
    63  // serviceCallAction - call service restart/update/stop API.
    64  func (adm *AdminClient) serviceCallAction(ctx context.Context, action ServiceAction) error {
    65  	queryValues := url.Values{}
    66  	queryValues.Set("action", string(action))
    67  
    68  	// Request API to Restart server
    69  	resp, err := adm.executeMethod(ctx,
    70  		http.MethodPost, requestData{
    71  			relPath:     adminAPIPrefix + "/service",
    72  			queryValues: queryValues,
    73  		},
    74  	)
    75  	defer closeResponse(resp)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	if resp.StatusCode != http.StatusOK {
    81  		return httpRespToErrorResponse(resp)
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  // ServiceTraceInfo holds http trace
    88  type ServiceTraceInfo struct {
    89  	Trace TraceInfo
    90  	Err   error `json:"-"`
    91  }
    92  
    93  // ServiceTraceOpts holds tracing options
    94  type ServiceTraceOpts struct {
    95  	// Trace types:
    96  	S3                bool
    97  	Internal          bool
    98  	Storage           bool
    99  	OS                bool
   100  	Scanner           bool
   101  	Decommission      bool
   102  	Healing           bool
   103  	BatchReplication  bool
   104  	Rebalance         bool
   105  	ReplicationResync bool
   106  	OnlyErrors        bool
   107  	Threshold         time.Duration
   108  }
   109  
   110  // TraceTypes returns the enabled traces as a bitfield value.
   111  func (t ServiceTraceOpts) TraceTypes() TraceType {
   112  	var tt TraceType
   113  	tt.SetIf(t.S3, TraceS3)
   114  	tt.SetIf(t.Internal, TraceInternal)
   115  	tt.SetIf(t.Storage, TraceStorage)
   116  	tt.SetIf(t.OS, TraceOS)
   117  	tt.SetIf(t.Scanner, TraceScanner)
   118  	tt.SetIf(t.Decommission, TraceDecommission)
   119  	tt.SetIf(t.Healing, TraceHealing)
   120  	tt.SetIf(t.BatchReplication, TraceBatchReplication)
   121  	tt.SetIf(t.Rebalance, TraceRebalance)
   122  	tt.SetIf(t.ReplicationResync, TraceReplicationResync)
   123  
   124  	return tt
   125  }
   126  
   127  // AddParams will add parameter to url values.
   128  func (t ServiceTraceOpts) AddParams(u url.Values) {
   129  	u.Set("err", strconv.FormatBool(t.OnlyErrors))
   130  	u.Set("threshold", t.Threshold.String())
   131  
   132  	u.Set("s3", strconv.FormatBool(t.S3))
   133  	u.Set("internal", strconv.FormatBool(t.Internal))
   134  	u.Set("storage", strconv.FormatBool(t.Storage))
   135  	u.Set("os", strconv.FormatBool(t.OS))
   136  	u.Set("scanner", strconv.FormatBool(t.Scanner))
   137  	u.Set("decommission", strconv.FormatBool(t.Decommission))
   138  	u.Set("healing", strconv.FormatBool(t.Healing))
   139  	u.Set("batch-replication", strconv.FormatBool(t.BatchReplication))
   140  	u.Set("rebalance", strconv.FormatBool(t.Rebalance))
   141  	u.Set("replication-resync", strconv.FormatBool(t.ReplicationResync))
   142  }
   143  
   144  // ParseParams will parse parameters and set them to t.
   145  func (t *ServiceTraceOpts) ParseParams(r *http.Request) (err error) {
   146  	t.S3 = r.Form.Get("s3") == "true"
   147  	t.OS = r.Form.Get("os") == "true"
   148  	t.Scanner = r.Form.Get("scanner") == "true"
   149  	t.Decommission = r.Form.Get("decommission") == "true"
   150  	t.Healing = r.Form.Get("healing") == "true"
   151  	t.BatchReplication = r.Form.Get("batch-replication") == "true"
   152  	t.Rebalance = r.Form.Get("rebalance") == "true"
   153  	t.Storage = r.Form.Get("storage") == "true"
   154  	t.Internal = r.Form.Get("internal") == "true"
   155  	t.OnlyErrors = r.Form.Get("err") == "true"
   156  	t.ReplicationResync = r.Form.Get("replication-resync") == "true"
   157  
   158  	if th := r.Form.Get("threshold"); th != "" {
   159  		d, err := time.ParseDuration(th)
   160  		if err != nil {
   161  			return err
   162  		}
   163  		t.Threshold = d
   164  	}
   165  	return nil
   166  }
   167  
   168  // ServiceTrace - listen on http trace notifications.
   169  func (adm AdminClient) ServiceTrace(ctx context.Context, opts ServiceTraceOpts) <-chan ServiceTraceInfo {
   170  	traceInfoCh := make(chan ServiceTraceInfo)
   171  	// Only success, start a routine to start reading line by line.
   172  	go func(traceInfoCh chan<- ServiceTraceInfo) {
   173  		defer close(traceInfoCh)
   174  		for {
   175  			urlValues := make(url.Values)
   176  			opts.AddParams(urlValues)
   177  
   178  			reqData := requestData{
   179  				relPath:     adminAPIPrefix + "/trace",
   180  				queryValues: urlValues,
   181  			}
   182  			// Execute GET to call trace handler
   183  			resp, err := adm.executeMethod(ctx, http.MethodGet, reqData)
   184  			if err != nil {
   185  				traceInfoCh <- ServiceTraceInfo{Err: err}
   186  				return
   187  			}
   188  
   189  			if resp.StatusCode != http.StatusOK {
   190  				closeResponse(resp)
   191  				traceInfoCh <- ServiceTraceInfo{Err: httpRespToErrorResponse(resp)}
   192  				return
   193  			}
   194  
   195  			dec := json.NewDecoder(resp.Body)
   196  			for {
   197  				var info traceInfoLegacy
   198  				if err = dec.Decode(&info); err != nil {
   199  					closeResponse(resp)
   200  					traceInfoCh <- ServiceTraceInfo{Err: err}
   201  					break
   202  				}
   203  				// Convert if legacy...
   204  				if info.TraceType == TraceType(0) {
   205  					if strings.HasPrefix(info.FuncName, "s3.") {
   206  						info.TraceType = TraceS3
   207  					} else {
   208  						info.TraceType = TraceInternal
   209  					}
   210  					info.HTTP = &TraceHTTPStats{}
   211  					if info.ReqInfo != nil {
   212  						info.Path = info.ReqInfo.Path
   213  						info.HTTP.ReqInfo = *info.ReqInfo
   214  					}
   215  					if info.RespInfo != nil {
   216  						info.HTTP.RespInfo = *info.RespInfo
   217  					}
   218  					if info.CallStats != nil {
   219  						info.Duration = info.CallStats.Latency
   220  						info.HTTP.CallStats = *info.CallStats
   221  					}
   222  				}
   223  				if info.TraceType == TraceOS && info.OSStats != nil {
   224  					info.Path = info.OSStats.Path
   225  					info.Duration = info.OSStats.Duration
   226  				}
   227  				if info.TraceType == TraceStorage && info.StorageStats != nil {
   228  					info.Path = info.StorageStats.Path
   229  					info.Duration = info.StorageStats.Duration
   230  				}
   231  				select {
   232  				case <-ctx.Done():
   233  					closeResponse(resp)
   234  					return
   235  				case traceInfoCh <- ServiceTraceInfo{Trace: info.TraceInfo}:
   236  				}
   237  			}
   238  		}
   239  	}(traceInfoCh)
   240  
   241  	// Returns the trace info channel, for caller to start reading from.
   242  	return traceInfoCh
   243  }