github.com/pingcap/tiup@v1.15.1/components/playground/instance/tiproxy.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  	"encoding/pem"
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  	"strings"
    23  
    24  	"github.com/BurntSushi/toml"
    25  	"github.com/pingcap/tiup/pkg/cluster/spec"
    26  	"github.com/pingcap/tiup/pkg/crypto"
    27  	tiupexec "github.com/pingcap/tiup/pkg/exec"
    28  	"github.com/pingcap/tiup/pkg/utils"
    29  )
    30  
    31  // TiProxy represent a ticdc instance.
    32  type TiProxy struct {
    33  	instance
    34  	pds []*PDInstance
    35  	Process
    36  }
    37  
    38  var _ Instance = &TiProxy{}
    39  
    40  // GenTiProxySessionCerts will create a self-signed certs for TiProxy session migration. NOTE that this cert is directly used by TiDB.
    41  func GenTiProxySessionCerts(dir string) error {
    42  	if _, err := os.Stat(filepath.Join(dir, "tiproxy.crt")); err == nil {
    43  		return nil
    44  	}
    45  
    46  	ca, err := crypto.NewCA("tiproxy")
    47  	if err != nil {
    48  		return err
    49  	}
    50  	privKey, err := crypto.NewKeyPair(crypto.KeyTypeRSA, crypto.KeySchemeRSASSAPSSSHA256)
    51  	if err != nil {
    52  		return err
    53  	}
    54  	csr, err := privKey.CSR("tiproxy", "tiproxy", nil, nil)
    55  	if err != nil {
    56  		return err
    57  	}
    58  	cert, err := ca.Sign(csr)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	if err := utils.SaveFileWithBackup(filepath.Join(dir, "tiproxy.key"), privKey.Pem(), ""); err != nil {
    63  		return err
    64  	}
    65  	return utils.SaveFileWithBackup(filepath.Join(dir, "tiproxy.crt"), pem.EncodeToMemory(&pem.Block{
    66  		Type:  "CERTIFICATE",
    67  		Bytes: cert,
    68  	}), "")
    69  }
    70  
    71  // NewTiProxy create a TiProxy instance.
    72  func NewTiProxy(binPath string, dir, host, configPath string, id int, port int, pds []*PDInstance) *TiProxy {
    73  	if port <= 0 {
    74  		port = 6000
    75  	}
    76  	tiproxy := &TiProxy{
    77  		instance: instance{
    78  			BinPath:    binPath,
    79  			ID:         id,
    80  			Dir:        dir,
    81  			Host:       host,
    82  			Port:       utils.MustGetFreePort(host, port),
    83  			StatusPort: utils.MustGetFreePort(host, 3080),
    84  			ConfigPath: configPath,
    85  		},
    86  		pds: pds,
    87  	}
    88  	return tiproxy
    89  }
    90  
    91  // MetricAddr implements Instance interface.
    92  func (c *TiProxy) MetricAddr() (r MetricAddr) {
    93  	r.Targets = append(r.Targets, utils.JoinHostPort(c.Host, c.StatusPort))
    94  	r.Labels = map[string]string{
    95  		"__metrics_path__": "/api/metrics",
    96  	}
    97  	return
    98  }
    99  
   100  // Start implements Instance interface.
   101  func (c *TiProxy) Start(ctx context.Context, version utils.Version) error {
   102  	endpoints := pdEndpoints(c.pds, false)
   103  
   104  	configPath := filepath.Join(c.Dir, "config", "proxy.toml")
   105  	dir := filepath.Dir(configPath)
   106  	if err := utils.MkdirAll(dir, 0755); err != nil {
   107  		return err
   108  	}
   109  
   110  	userConfig, err := unmarshalConfig(c.ConfigPath)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if userConfig == nil {
   115  		userConfig = make(map[string]any)
   116  	}
   117  
   118  	cf, err := os.Create(configPath)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	enc := toml.NewEncoder(cf)
   124  	enc.Indent = ""
   125  	if err := enc.Encode(spec.MergeConfig(userConfig, map[string]any{
   126  		"proxy.pd-addrs":        strings.Join(endpoints, ","),
   127  		"proxy.addr":            utils.JoinHostPort(c.Host, c.Port),
   128  		"proxy.advertise-addr":  AdvertiseHost(c.Host),
   129  		"api.addr":              utils.JoinHostPort(c.Host, c.StatusPort),
   130  		"log.log-file.filename": c.LogFile(),
   131  	})); err != nil {
   132  		return err
   133  	}
   134  
   135  	args := []string{
   136  		fmt.Sprintf("--config=%s", configPath),
   137  	}
   138  
   139  	if c.BinPath, err = tiupexec.PrepareBinary("tiproxy", version, c.BinPath); err != nil {
   140  		return err
   141  	}
   142  
   143  	c.Process = &process{cmd: PrepareCommand(ctx, c.BinPath, args, nil, c.Dir)}
   144  
   145  	logIfErr(c.Process.SetOutputFile(c.LogFile()))
   146  	return c.Process.Start()
   147  }
   148  
   149  // Addr return addresses that can be connected by MySQL clients.
   150  func (c *TiProxy) Addr() string {
   151  	return utils.JoinHostPort(AdvertiseHost(c.Host), c.Port)
   152  }
   153  
   154  // Component return component name.
   155  func (c *TiProxy) Component() string {
   156  	return "tiproxy"
   157  }
   158  
   159  // LogFile return the log file.
   160  func (c *TiProxy) LogFile() string {
   161  	return filepath.Join(c.Dir, "tiproxy.log")
   162  }