github.com/whtcorpsinc/MilevaDB-Prod@v0.0.0-20211104133533-f57f4be3b597/interlock/set_config.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package interlock
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"net"
    22  	"net/http"
    23  	"strings"
    24  
    25  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    26  	"github.com/whtcorpsinc/errors"
    27  	"github.com/whtcorpsinc/milevadb/causet/embedded"
    28  	"github.com/whtcorpsinc/milevadb/memex"
    29  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    30  	"github.com/whtcorpsinc/milevadb/soliton"
    31  	"github.com/whtcorpsinc/milevadb/soliton/FIDelapi"
    32  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    33  	"github.com/whtcorpsinc/milevadb/soliton/set"
    34  	"github.com/whtcorpsinc/milevadb/soliton/stringutil"
    35  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    36  	"github.com/whtcorpsinc/milevadb/types"
    37  )
    38  
    39  // SetConfigInterDirc executes 'SET CONFIG' memex.
    40  type SetConfigInterDirc struct {
    41  	baseInterlockingDirectorate
    42  	p        *embedded.SetConfig
    43  	jsonBody string
    44  }
    45  
    46  // Open implements the InterlockingDirectorate Open interface.
    47  func (s *SetConfigInterDirc) Open(ctx context.Context) error {
    48  	if s.p.Type != "" {
    49  		s.p.Type = strings.ToLower(s.p.Type)
    50  		if s.p.Type != "einsteindb" && s.p.Type != "milevadb" && s.p.Type != "fidel" {
    51  			return errors.Errorf("unknown type %v", s.p.Type)
    52  		}
    53  		if s.p.Type == "milevadb" {
    54  			return errors.Errorf("MilevaDB doesn't support to change configs online, please use ALLEGROALLEGROSQL variables")
    55  		}
    56  	}
    57  	if s.p.Instance != "" {
    58  		s.p.Instance = strings.ToLower(s.p.Instance)
    59  		if !isValidInstance(s.p.Instance) {
    60  			return errors.Errorf("invalid instance %v", s.p.Instance)
    61  		}
    62  	}
    63  	s.p.Name = strings.ToLower(s.p.Name)
    64  
    65  	body, err := ConvertConfigItem2JSON(s.ctx, s.p.Name, s.p.Value)
    66  	s.jsonBody = body
    67  	return err
    68  }
    69  
    70  // TestSetConfigServerInfoKey is used as the key to causetstore 'TestSetConfigServerInfoFunc' in the context.
    71  var TestSetConfigServerInfoKey stringutil.StringerStr = "TestSetConfigServerInfoKey"
    72  
    73  // TestSetConfigHTTPHandlerKey is used as the key to causetstore 'TestSetConfigDoRequestFunc' in the context.
    74  var TestSetConfigHTTPHandlerKey stringutil.StringerStr = "TestSetConfigHTTPHandlerKey"
    75  
    76  // Next implements the InterlockingDirectorate Next interface.
    77  func (s *SetConfigInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
    78  	req.Reset()
    79  	getServerFunc := schemareplicant.GetClusterServerInfo
    80  	if v := s.ctx.Value(TestSetConfigServerInfoKey); v != nil {
    81  		getServerFunc = v.(func(stochastikctx.Context) ([]schemareplicant.ServerInfo, error))
    82  	}
    83  
    84  	serversInfo, err := getServerFunc(s.ctx)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	nodeTypes := set.NewStringSet()
    89  	nodeAddrs := set.NewStringSet()
    90  	if s.p.Type != "" {
    91  		nodeTypes.Insert(s.p.Type)
    92  	}
    93  	if s.p.Instance != "" {
    94  		nodeAddrs.Insert(s.p.Instance)
    95  	}
    96  	serversInfo = filterClusterServerInfo(serversInfo, nodeTypes, nodeAddrs)
    97  	if s.p.Instance != "" && len(serversInfo) == 0 {
    98  		return errors.Errorf("instance %v is not found in this cluster", s.p.Instance)
    99  	}
   100  
   101  	for _, serverInfo := range serversInfo {
   102  		var url string
   103  		switch serverInfo.ServerType {
   104  		case "fidel":
   105  			url = fmt.Sprintf("%s://%s%s", soliton.InternalHTTPSchema(), serverInfo.StatusAddr, FIDelapi.Config)
   106  		case "einsteindb":
   107  			url = fmt.Sprintf("%s://%s/config", soliton.InternalHTTPSchema(), serverInfo.StatusAddr)
   108  		case "milevadb":
   109  			return errors.Errorf("MilevaDB doesn't support to change configs online, please use ALLEGROALLEGROSQL variables")
   110  		default:
   111  			return errors.Errorf("Unknown server type %s", serverInfo.ServerType)
   112  		}
   113  		if err := s.doRequest(url); err != nil {
   114  			s.ctx.GetStochastikVars().StmtCtx.AppendWarning(err)
   115  		}
   116  	}
   117  	return nil
   118  }
   119  
   120  func (s *SetConfigInterDirc) doRequest(url string) (retErr error) {
   121  	body := bytes.NewBufferString(s.jsonBody)
   122  	req, err := http.NewRequest(http.MethodPost, url, body)
   123  	if err != nil {
   124  		return err
   125  	}
   126  	var httpHandler func(req *http.Request) (*http.Response, error)
   127  	if v := s.ctx.Value(TestSetConfigHTTPHandlerKey); v != nil {
   128  		httpHandler = v.(func(*http.Request) (*http.Response, error))
   129  	} else {
   130  		httpHandler = soliton.InternalHTTPClient().Do
   131  	}
   132  	resp, err := httpHandler(req)
   133  	if err != nil {
   134  		return err
   135  	}
   136  	defer func() {
   137  		if err := resp.Body.Close(); err != nil {
   138  			if retErr == nil {
   139  				retErr = err
   140  			}
   141  		}
   142  	}()
   143  	if resp.StatusCode == http.StatusOK {
   144  		return nil
   145  	} else if resp.StatusCode >= 400 && resp.StatusCode < 600 {
   146  		message, err := ioutil.ReadAll(resp.Body)
   147  		if err != nil {
   148  			return err
   149  		}
   150  		return errors.Errorf("bad request to %s: %s", url, message)
   151  	}
   152  	return errors.Errorf("request %s failed: %s", url, resp.Status)
   153  }
   154  
   155  func isValidInstance(instance string) bool {
   156  	ip, port, err := net.SplitHostPort(instance)
   157  	if err != nil {
   158  		return false
   159  	}
   160  	if port == "" {
   161  		return false
   162  	}
   163  	v := net.ParseIP(ip)
   164  	return v != nil
   165  }
   166  
   167  // ConvertConfigItem2JSON converts the config item specified by key and val to json.
   168  // For example:
   169  // 	set config x key="val" ==> {"key":"val"}
   170  // 	set config x key=233 ==> {"key":233}
   171  func ConvertConfigItem2JSON(ctx stochastikctx.Context, key string, val memex.Expression) (body string, err error) {
   172  	if val == nil {
   173  		return "", errors.Errorf("cannot set config to null")
   174  	}
   175  	isNull := false
   176  	str := ""
   177  	switch val.GetType().EvalType() {
   178  	case types.ETString:
   179  		var s string
   180  		s, isNull, err = val.EvalString(ctx, chunk.Event{})
   181  		if err == nil && !isNull {
   182  			str = fmt.Sprintf(`"%s"`, s)
   183  		}
   184  	case types.ETInt:
   185  		var i int64
   186  		i, isNull, err = val.EvalInt(ctx, chunk.Event{})
   187  		if err == nil && !isNull {
   188  			if allegrosql.HasIsBooleanFlag(val.GetType().Flag) {
   189  				str = "true"
   190  				if i == 0 {
   191  					str = "false"
   192  				}
   193  			} else {
   194  				str = fmt.Sprintf("%v", i)
   195  			}
   196  		}
   197  	case types.ETReal:
   198  		var f float64
   199  		f, isNull, err = val.EvalReal(ctx, chunk.Event{})
   200  		if err == nil && !isNull {
   201  			str = fmt.Sprintf("%v", f)
   202  		}
   203  	case types.ETDecimal:
   204  		var d *types.MyDecimal
   205  		d, isNull, err = val.EvalDecimal(ctx, chunk.Event{})
   206  		if err == nil && !isNull {
   207  			str = string(d.ToString())
   208  		}
   209  	default:
   210  		return "", errors.Errorf("unsupported config value type")
   211  	}
   212  	if err != nil {
   213  		return
   214  	}
   215  	if isNull {
   216  		return "", errors.Errorf("can't set config to null")
   217  	}
   218  	body = fmt.Sprintf(`{"%s":%s}`, key, str)
   219  	return body, nil
   220  }