github.com/pingcap/tiup@v1.15.1/components/playground/instance/instance.go (about)

     1  // Copyright 2020 PingCAP, 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 instance
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/BurntSushi/toml"
    24  	"github.com/pingcap/errors"
    25  	"github.com/pingcap/tiup/pkg/cluster/spec"
    26  	"github.com/pingcap/tiup/pkg/utils"
    27  )
    28  
    29  // Config of the instance.
    30  type Config struct {
    31  	ConfigPath string `yaml:"config_path"`
    32  	BinPath    string `yaml:"bin_path"`
    33  	Num        int    `yaml:"num"`
    34  	Host       string `yaml:"host"`
    35  	Port       int    `yaml:"port"`
    36  	UpTimeout  int    `yaml:"up_timeout"`
    37  	Version    string `yaml:"version"`
    38  }
    39  
    40  // CSEOptions contains configs to run TiDB cluster in CSE mode.
    41  type CSEOptions struct {
    42  	S3Endpoint string `yaml:"s3_endpoint"`
    43  	Bucket     string `yaml:"bucket"`
    44  	AccessKey  string `yaml:"access_key"`
    45  	SecretKey  string `yaml:"secret_key"`
    46  }
    47  
    48  type instance struct {
    49  	ID         int
    50  	Dir        string
    51  	Host       string
    52  	Port       int
    53  	StatusPort int // client port for PD
    54  	ConfigPath string
    55  	BinPath    string
    56  }
    57  
    58  // MetricAddr will be used by prometheus scrape_configs.
    59  type MetricAddr struct {
    60  	Targets []string          `json:"targets"`
    61  	Labels  map[string]string `json:"labels"`
    62  }
    63  
    64  // Instance represent running component
    65  type Instance interface {
    66  	Pid() int
    67  	// Start the instance process.
    68  	// Will kill the process once the context is done.
    69  	Start(ctx context.Context, version utils.Version) error
    70  	// Component Return the component name.
    71  	Component() string
    72  	// LogFile return the log file name
    73  	LogFile() string
    74  	// Uptime show uptime.
    75  	Uptime() string
    76  	// MetricAddr return the address to pull metrics.
    77  	MetricAddr() MetricAddr
    78  	// Wait Should only call this if the instance is started successfully.
    79  	// The implementation should be safe to call Wait multi times.
    80  	Wait() error
    81  }
    82  
    83  func (inst *instance) MetricAddr() (r MetricAddr) {
    84  	if inst.Host != "" && inst.StatusPort != 0 {
    85  		r.Targets = append(r.Targets, utils.JoinHostPort(inst.Host, inst.StatusPort))
    86  	}
    87  	return
    88  }
    89  
    90  // CompVersion return the format to run specified version of a component.
    91  func CompVersion(comp string, version utils.Version) string {
    92  	if version.IsEmpty() {
    93  		return comp
    94  	}
    95  	return fmt.Sprintf("%v:%v", comp, version)
    96  }
    97  
    98  // AdvertiseHost returns the interface's ip addr if listen host is 0.0.0.0
    99  func AdvertiseHost(listen string) string {
   100  	if listen == "0.0.0.0" {
   101  		addrs, err := net.InterfaceAddrs()
   102  		if err != nil || len(addrs) == 0 {
   103  			return "localhost"
   104  		}
   105  
   106  		for _, addr := range addrs {
   107  			if ip, ok := addr.(*net.IPNet); ok && !ip.IP.IsLoopback() && ip.IP.To4() != nil {
   108  				return ip.IP.To4().String()
   109  			}
   110  		}
   111  		return "localhost"
   112  	}
   113  
   114  	return listen
   115  }
   116  
   117  func logIfErr(err error) {
   118  	if err != nil {
   119  		fmt.Println(err)
   120  	}
   121  }
   122  
   123  func pdEndpoints(pds []*PDInstance, isHTTP bool) []string {
   124  	var endpoints []string
   125  	for _, pd := range pds {
   126  		if pd.Role == PDRoleTSO || pd.Role == PDRoleScheduling {
   127  			continue
   128  		}
   129  		if isHTTP {
   130  			endpoints = append(endpoints, "http://"+utils.JoinHostPort(AdvertiseHost(pd.Host), pd.StatusPort))
   131  		} else {
   132  			endpoints = append(endpoints, utils.JoinHostPort(AdvertiseHost(pd.Host), pd.StatusPort))
   133  		}
   134  	}
   135  	return endpoints
   136  }
   137  
   138  // prepareConfig accepts a user specified config and merge user config with a
   139  // pre-defined one.
   140  func prepareConfig(outputConfigPath string, userConfigPath string, preDefinedConfig map[string]any) error {
   141  	dir := filepath.Dir(outputConfigPath)
   142  	if err := utils.MkdirAll(dir, 0755); err != nil {
   143  		return err
   144  	}
   145  
   146  	userConfig, err := unmarshalConfig(userConfigPath)
   147  	if err != nil {
   148  		return errors.Trace(err)
   149  	}
   150  	if userConfig == nil {
   151  		userConfig = make(map[string]any)
   152  	}
   153  
   154  	cf, err := os.Create(outputConfigPath)
   155  	if err != nil {
   156  		return errors.Trace(err)
   157  	}
   158  
   159  	enc := toml.NewEncoder(cf)
   160  	enc.Indent = ""
   161  	return enc.Encode(spec.MergeConfig(preDefinedConfig, userConfig))
   162  }
   163  
   164  func unmarshalConfig(path string) (map[string]any, error) {
   165  	if path == "" {
   166  		return nil, nil
   167  	}
   168  	data, err := os.ReadFile(path)
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  	c := make(map[string]any)
   173  	err = toml.Unmarshal(data, &c)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	return c, nil
   178  }