github.com/technosophos/deis@v1.7.1-0.20150915173815-f9005256004b/deisctl/config/etcd/etcd.go (about) 1 package etcd 2 3 import ( 4 "fmt" 5 "net" 6 "net/http" 7 "strings" 8 "time" 9 10 "github.com/coreos/fleet/pkg" 11 "github.com/coreos/fleet/ssh" 12 etcdlib "github.com/coreos/go-etcd/etcd" 13 "github.com/deis/deis/deisctl/backend/fleet" 14 "github.com/deis/deis/deisctl/config/model" 15 ) 16 17 // ConfigBackend is an etcd-based implementation of the config.Backend interface 18 type ConfigBackend struct { 19 etcdlib *etcdlib.Client 20 } 21 22 func getTunnelFlag() string { 23 tun := fleet.Flags.Tunnel 24 if tun != "" && !strings.Contains(tun, ":") { 25 tun += ":22" 26 } 27 return tun 28 } 29 30 func getChecker() *ssh.HostKeyChecker { 31 if !fleet.Flags.StrictHostKeyChecking { 32 return nil 33 } 34 keyFile := ssh.NewHostKeyFile(fleet.Flags.KnownHostsFile) 35 return ssh.NewHostKeyChecker(keyFile) 36 } 37 38 // Get a value by key from etcd 39 func (cb *ConfigBackend) Get(key string) (string, error) { 40 sort, recursive := true, false 41 resp, err := cb.etcdlib.Get(key, sort, recursive) 42 if err != nil { 43 return "", err 44 } 45 return resp.Node.Value, nil 46 } 47 48 // GetWithDefault gets a value by key from etcd and return a default value if 49 // not found 50 func (cb *ConfigBackend) GetWithDefault(key string, defaultValue string) (string, error) { 51 sort, recursive := true, false 52 resp, err := cb.etcdlib.Get(key, sort, recursive) 53 if err != nil { 54 etcdErr, ok := err.(*etcdlib.EtcdError) 55 if ok && etcdErr.ErrorCode == 100 { 56 return defaultValue, nil 57 } 58 return "", err 59 } 60 return resp.Node.Value, nil 61 } 62 63 func singleNodeToConfigNode(node *etcdlib.Node) *model.ConfigNode { 64 key := model.ConfigNode{ 65 Key: node.Key, 66 Expiration: node.Expiration, 67 } 68 69 if node.Dir != true && node.Key != "" { 70 key.Value = node.Value 71 } 72 73 return &key 74 } 75 76 func traverseNode(node *etcdlib.Node) []*model.ConfigNode { 77 var serviceKeys []*model.ConfigNode 78 79 if len(node.Nodes) > 0 { 80 for _, nodeChild := range node.Nodes { 81 serviceKeys = append(serviceKeys, traverseNode(nodeChild)...) 82 } 83 } else { 84 key := singleNodeToConfigNode(node) 85 if key.Key != "" { 86 serviceKeys = append(serviceKeys, key) 87 } 88 } 89 90 return serviceKeys 91 } 92 93 // GetRecursive returns a slice of all key/value pairs "under" a specified key 94 // in etcd 95 func (cb *ConfigBackend) GetRecursive(key string) ([]*model.ConfigNode, error) { 96 resp, err := cb.etcdlib.Get(key, true, true) 97 if err != nil { 98 return nil, err 99 } 100 101 nodes := traverseNode(resp.Node) 102 return nodes, nil 103 } 104 105 // Delete a key/value pair by key from etcd 106 func (cb *ConfigBackend) Delete(key string) error { 107 _, err := cb.etcdlib.Delete(key, false) 108 return err 109 } 110 111 // Set a value for the specified key in etcd 112 func (cb *ConfigBackend) Set(key string, value string) (string, error) { 113 resp, err := cb.etcdlib.Set(key, value, 0) // don't use TTLs 114 if err != nil { 115 return "", err 116 } 117 return resp.Node.Value, nil 118 } 119 120 // SetWithTTL sets a value for the specified key in etcd-- with a time to live 121 func (cb *ConfigBackend) SetWithTTL(key string, value string, ttl uint64) (string, error) { 122 resp, err := cb.etcdlib.Update(key, value, ttl) 123 if err != nil { 124 return "", err 125 } 126 return resp.Node.Value, nil 127 } 128 129 // NewConfigBackend returns this etcd-based implementation of the config.Backend 130 // interface 131 func NewConfigBackend() (*ConfigBackend, error) { 132 var dial func(string, string) (net.Conn, error) 133 sshTimeout := time.Duration(fleet.Flags.SSHTimeout*1000) * time.Millisecond 134 tun := getTunnelFlag() 135 if tun != "" { 136 sshClient, err := ssh.NewSSHClient("core", tun, getChecker(), false, sshTimeout) 137 if err != nil { 138 return nil, fmt.Errorf("failed initializing SSH client: %v", err) 139 } 140 141 dial = func(network, addr string) (net.Conn, error) { 142 tcpaddr, err := net.ResolveTCPAddr(network, addr) 143 if err != nil { 144 return nil, err 145 } 146 return sshClient.DialTCP(network, nil, tcpaddr) 147 } 148 } 149 150 tlsConfig, err := pkg.ReadTLSConfigFiles(fleet.Flags.EtcdCAFile, 151 fleet.Flags.EtcdCertFile, fleet.Flags.EtcdKeyFile) 152 if err != nil { 153 return nil, err 154 } 155 156 trans := http.Transport{ 157 Dial: dial, 158 TLSClientConfig: tlsConfig, 159 } 160 161 timeout := time.Duration(fleet.Flags.RequestTimeout*1000) * time.Millisecond 162 machines := []string{fleet.Flags.Endpoint} 163 164 c := etcdlib.NewClient(machines) 165 c.SetDialTimeout(timeout) 166 167 // use custom transport with SSH tunnel capability 168 c.SetTransport(&trans) 169 170 return &ConfigBackend{etcdlib: c}, nil 171 }