github.com/coreos/mantle@v0.13.0/platform/local/etcd.go (about)

     1  // Copyright 2015 CoreOS, 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  // 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 local
    16  
    17  import (
    18  	"fmt"
    19  	"io/ioutil"
    20  	"net"
    21  	"net/http"
    22  	"net/url"
    23  	"os"
    24  
    25  	"github.com/coreos/etcd/etcdserver"
    26  	"github.com/coreos/etcd/etcdserver/api/v2http"
    27  	"github.com/coreos/etcd/pkg/types"
    28  )
    29  
    30  const (
    31  	memberName  = "simple"
    32  	clusterName = "simple-cluster"
    33  	tempPrefix  = "simple-etcd-"
    34  
    35  	// No peer URL exists but etcd doesn't allow the value to be empty.
    36  	peerURL    = "http://localhost:0"
    37  	clusterCfg = memberName + "=" + peerURL
    38  )
    39  
    40  // SimpleEtcd provides a single node etcd server.
    41  type SimpleEtcd struct {
    42  	Port     int
    43  	listener net.Listener
    44  	server   *etcdserver.EtcdServer
    45  	dataDir  string
    46  }
    47  
    48  func NewSimpleEtcd() (*SimpleEtcd, error) {
    49  	var err error
    50  	se := &SimpleEtcd{}
    51  	se.listener, err = net.Listen("tcp", ":0")
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	se.Port = se.listener.Addr().(*net.TCPAddr).Port
    57  	clientURLs, err := interfaceURLs(se.Port)
    58  	if err != nil {
    59  		se.Destroy()
    60  		return nil, err
    61  	}
    62  
    63  	se.dataDir, err = ioutil.TempDir("", tempPrefix)
    64  	if err != nil {
    65  		se.Destroy()
    66  		return nil, err
    67  	}
    68  
    69  	peerURLs, err := types.NewURLs([]string{peerURL})
    70  	if err != nil {
    71  		se.Destroy()
    72  		return nil, err
    73  	}
    74  
    75  	cfg := etcdserver.ServerConfig{
    76  		Name:       memberName,
    77  		ClientURLs: clientURLs,
    78  		PeerURLs:   peerURLs,
    79  		DataDir:    se.dataDir,
    80  		InitialPeerURLsMap: types.URLsMap{
    81  			memberName: peerURLs,
    82  		},
    83  		NewCluster:    true,
    84  		TickMs:        100,
    85  		ElectionTicks: 10,
    86  	}
    87  
    88  	se.server, err = etcdserver.NewServer(cfg)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	se.server.Start()
    94  	go http.Serve(se.listener,
    95  		v2http.NewClientHandler(se.server, cfg.ReqTimeout()))
    96  
    97  	return se, nil
    98  }
    99  
   100  func (se *SimpleEtcd) Destroy() {
   101  	if se.listener != nil {
   102  		if err := se.listener.Close(); err != nil {
   103  			plog.Errorf("Error closing etcd listener: %v", err)
   104  		}
   105  	}
   106  
   107  	if se.server != nil {
   108  		se.server.Stop()
   109  	}
   110  
   111  	if se.dataDir != "" {
   112  		if err := os.RemoveAll(se.dataDir); err != nil {
   113  			plog.Errorf("Error removing etcd data dir: %v", err)
   114  		}
   115  	}
   116  }
   117  
   118  // Generate all publishable URLs for a given HTTP port.
   119  func interfaceURLs(port int) (types.URLs, error) {
   120  	allAddrs, err := net.InterfaceAddrs()
   121  	if err != nil {
   122  		return []url.URL{}, err
   123  	}
   124  
   125  	var allURLs types.URLs
   126  	for _, a := range allAddrs {
   127  		ip, ok := a.(*net.IPNet)
   128  		if !ok || !ip.IP.IsGlobalUnicast() {
   129  			continue
   130  		}
   131  
   132  		tcp := net.TCPAddr{
   133  			IP:   ip.IP,
   134  			Port: port,
   135  		}
   136  
   137  		u := url.URL{
   138  			Scheme: "http",
   139  			Host:   tcp.String(),
   140  		}
   141  		allURLs = append(allURLs, u)
   142  	}
   143  
   144  	if len(allAddrs) == 0 {
   145  		return []url.URL{}, fmt.Errorf("no publishable addresses")
   146  	}
   147  
   148  	return allURLs, nil
   149  }