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  }