go.etcd.io/etcd@v3.3.27+incompatible/etcdmain/gateway.go (about)

     1  // Copyright 2016 The etcd Authors
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package etcdmain
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"net/url"
    21  	"os"
    22  	"time"
    23  
    24  	"github.com/coreos/etcd/proxy/tcpproxy"
    25  
    26  	"github.com/spf13/cobra"
    27  )
    28  
    29  var (
    30  	gatewayListenAddr        string
    31  	gatewayEndpoints         []string
    32  	gatewayDNSCluster        string
    33  	gatewayInsecureDiscovery bool
    34  	getewayRetryDelay        time.Duration
    35  	gatewayCA                string
    36  )
    37  
    38  var (
    39  	rootCmd = &cobra.Command{
    40  		Use:        "etcd",
    41  		Short:      "etcd server",
    42  		SuggestFor: []string{"etcd"},
    43  	}
    44  )
    45  
    46  func init() {
    47  	rootCmd.AddCommand(newGatewayCommand())
    48  }
    49  
    50  // newGatewayCommand returns the cobra command for "gateway".
    51  func newGatewayCommand() *cobra.Command {
    52  	lpc := &cobra.Command{
    53  		Use:   "gateway <subcommand>",
    54  		Short: "gateway related command",
    55  	}
    56  	lpc.AddCommand(newGatewayStartCommand())
    57  
    58  	return lpc
    59  }
    60  
    61  func newGatewayStartCommand() *cobra.Command {
    62  	cmd := cobra.Command{
    63  		Use:   "start",
    64  		Short: "start the gateway",
    65  		Run:   startGateway,
    66  	}
    67  
    68  	cmd.Flags().StringVar(&gatewayListenAddr, "listen-addr", "127.0.0.1:23790", "listen address")
    69  	cmd.Flags().StringVar(&gatewayDNSCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster")
    70  	cmd.Flags().BoolVar(&gatewayInsecureDiscovery, "insecure-discovery", false, "accept insecure SRV records")
    71  	cmd.Flags().StringVar(&gatewayCA, "trusted-ca-file", "", "path to the client server TLS CA file for verifying the discovered endpoints when discovery-srv is provided.")
    72  
    73  	cmd.Flags().StringSliceVar(&gatewayEndpoints, "endpoints", []string{"127.0.0.1:2379"}, "comma separated etcd cluster endpoints")
    74  
    75  	cmd.Flags().DurationVar(&getewayRetryDelay, "retry-delay", time.Minute, "duration of delay before retrying failed endpoints")
    76  
    77  	return &cmd
    78  }
    79  
    80  func stripSchema(eps []string) []string {
    81  	var endpoints []string
    82  
    83  	for _, ep := range eps {
    84  
    85  		if u, err := url.Parse(ep); err == nil && u.Host != "" {
    86  			ep = u.Host
    87  		}
    88  
    89  		endpoints = append(endpoints, ep)
    90  	}
    91  
    92  	return endpoints
    93  }
    94  
    95  func startGateway(cmd *cobra.Command, args []string) {
    96  	srvs := discoverEndpoints(gatewayDNSCluster, gatewayCA, gatewayInsecureDiscovery)
    97  	if len(srvs.Endpoints) == 0 {
    98  		// no endpoints discovered, fall back to provided endpoints
    99  		srvs.Endpoints = gatewayEndpoints
   100  	}
   101  	// Strip the schema from the endpoints because we start just a TCP proxy
   102  	srvs.Endpoints = stripSchema(srvs.Endpoints)
   103  	if len(srvs.SRVs) == 0 {
   104  		for _, ep := range srvs.Endpoints {
   105  			h, p, err := net.SplitHostPort(ep)
   106  			if err != nil {
   107  				plog.Fatalf("error parsing endpoint %q", ep)
   108  			}
   109  			var port uint16
   110  			fmt.Sscanf(p, "%d", &port)
   111  			srvs.SRVs = append(srvs.SRVs, &net.SRV{Target: h, Port: port})
   112  		}
   113  	}
   114  
   115  	lhost, lport, err := net.SplitHostPort(gatewayListenAddr)
   116  	if err != nil {
   117  		fmt.Println("failed to validate listen address:", gatewayListenAddr)
   118  		os.Exit(1)
   119  	}
   120  
   121  	laddrs, err := net.LookupHost(lhost)
   122  	if err != nil {
   123  		fmt.Println("failed to resolve listen host:", lhost)
   124  		os.Exit(1)
   125  	}
   126  	laddrsMap := make(map[string]bool)
   127  	for _, addr := range laddrs {
   128  		laddrsMap[addr] = true
   129  	}
   130  
   131  	for _, srv := range srvs.SRVs {
   132  		eaddrs, err := net.LookupHost(srv.Target)
   133  		if err != nil {
   134  			fmt.Println("failed to resolve endpoint host:", srv.Target)
   135  			os.Exit(1)
   136  		}
   137  		if fmt.Sprintf("%d", srv.Port) != lport {
   138  			continue
   139  		}
   140  
   141  		for _, ea := range eaddrs {
   142  			if laddrsMap[ea] {
   143  				fmt.Printf("SRV or endpoint (%s:%d->%s:%d) should not resolve to the gateway listen addr (%s)\n", srv.Target, srv.Port, ea, srv.Port, gatewayListenAddr)
   144  				os.Exit(1)
   145  			}
   146  		}
   147  	}
   148  
   149  	if len(srvs.Endpoints) == 0 {
   150  		plog.Fatalf("no endpoints found")
   151  	}
   152  
   153  	l, err := net.Listen("tcp", gatewayListenAddr)
   154  	if err != nil {
   155  		fmt.Fprintln(os.Stderr, err)
   156  		os.Exit(1)
   157  	}
   158  
   159  	tp := tcpproxy.TCPProxy{
   160  		Listener:        l,
   161  		Endpoints:       srvs.SRVs,
   162  		MonitorInterval: getewayRetryDelay,
   163  	}
   164  
   165  	// At this point, etcd gateway listener is initialized
   166  	notifySystemd()
   167  
   168  	tp.Run()
   169  }