1// Copyright (c) 2014 Couchbase, Inc.
2// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3// except in compliance with the License. You may obtain a copy of the License at
4//   http://www.apache.org/licenses/LICENSE-2.0
5// Unless required by applicable law or agreed to in writing, software distributed under the
6// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7// either express or implied. See the License for the specific language governing permissions
8// and limitations under the License.
9
10package manager
11
12import (
13	"bytes"
14	json "encoding/json"
15	"github.com/couchbase/indexing/secondary/logging"
16	"github.com/couchbase/gometa/common"
17	"net"
18	"os"
19	"strings"
20)
21
22type env struct {
23	hostUDPAddr     net.Addr
24	hostTCPAddr     net.Addr
25	hostRequestAddr net.Addr
26	peerUDPAddr     []string
27	peerTCPAddr     []string
28}
29
30type node struct {
31	ElectionAddr string
32	MessageAddr  string
33	RequestAddr  string
34}
35
36type config struct {
37	Host *node
38	Peer []*node
39}
40
41func newEnv(config string) (e *env, err error) {
42	e = new(env)
43
44	if config == "" {
45		err = e.initWithArgs()
46		if err != nil {
47			return nil, err
48		}
49	}
50
51	err = e.initWithConfig(config)
52	if err != nil {
53		return nil, err
54	}
55
56	return e, nil
57}
58
59func (e *env) getHostUDPAddr() string {
60	return e.hostUDPAddr.String()
61}
62
63func (e *env) getHostElectionPort() string {
64
65	_, port, err := net.SplitHostPort(e.hostUDPAddr.String())
66	if err != nil {
67		port = e.hostUDPAddr.String()
68	}
69
70	return port
71}
72
73func (e *env) getHostTCPAddr() string {
74	return e.hostTCPAddr.String()
75}
76
77func (e *env) getHostRequestAddr() string {
78	return e.hostRequestAddr.String()
79}
80
81func (e *env) getPeerUDPAddr() []string {
82	return e.peerUDPAddr
83}
84
85func (e *env) getPeerTCPAddr() []string {
86	return e.peerTCPAddr
87}
88
89func (e *env) getPeerHost() ([]string, error) {
90	var result []string = nil
91	for _, addr := range e.peerUDPAddr {
92		host, _, err := net.SplitHostPort(addr)
93		if err != nil {
94			return nil, err
95		}
96		result = append(result, host)
97	}
98	return result, nil
99}
100
101func (e *env) getLocalHost() (string, error) {
102	host, _, err := net.SplitHostPort(e.getHostUDPAddr())
103	if err != nil {
104		return "", err
105	}
106	return host, nil
107}
108
109func (e *env) findMatchingPeerTCPAddr(updAddr string) string {
110	for i := 0; i < len(e.peerUDPAddr); i++ {
111		if e.peerUDPAddr[i] == updAddr {
112			return e.peerTCPAddr[i]
113		}
114	}
115	return ""
116}
117
118func (e *env) findMatchingPeerUDPAddr(tcpAddr string) string {
119	for i := 0; i < len(e.peerTCPAddr); i++ {
120		if e.peerTCPAddr[i] == tcpAddr {
121			return e.peerUDPAddr[i]
122		}
123	}
124	return ""
125}
126
127func (e *env) initWithConfig(path string) error {
128
129	file, err := os.Open(path)
130	if err != nil {
131		return err
132	}
133
134	buffer := new(bytes.Buffer)
135	_, err = buffer.ReadFrom(file)
136	if err != nil {
137		return err
138	}
139
140	var c config
141	err = json.Unmarshal(buffer.Bytes(), &c)
142	if err != nil {
143		return err
144	}
145
146	if e.hostUDPAddr, err = resolveAddr(common.ELECTION_TRANSPORT_TYPE, c.Host.ElectionAddr); err != nil {
147		return err
148	}
149	logging.Debugf("Env.initWithConfig(): Host UDP Addr %s", e.hostUDPAddr.String())
150
151	if e.hostTCPAddr, err = resolveAddr(common.MESSAGE_TRANSPORT_TYPE, c.Host.MessageAddr); err != nil {
152		return err
153	}
154	logging.Debugf("Env.initWithConfig(): Host TCP Addr %s", e.hostTCPAddr.String())
155
156	if e.hostRequestAddr, err = resolveAddr(common.MESSAGE_TRANSPORT_TYPE, c.Host.RequestAddr); err != nil {
157		return err
158	}
159	logging.Debugf("Env.initWithConfig(): Host Request Addr %s", e.hostRequestAddr.String())
160
161	e.peerUDPAddr = make([]string, 0, len(c.Peer))
162	e.peerTCPAddr = make([]string, 0, len(c.Peer))
163
164	for _, peer := range c.Peer {
165		udpAddr, err := resolveAddr(common.ELECTION_TRANSPORT_TYPE, peer.ElectionAddr)
166		if err != nil {
167			return err
168		}
169		e.peerUDPAddr = append(e.peerUDPAddr, udpAddr.String())
170		logging.Debugf("Env.initWithConfig(): Peer UDP Addr %s", udpAddr.String())
171
172		tcpAddr, err := resolveAddr(common.MESSAGE_TRANSPORT_TYPE, peer.MessageAddr)
173		if err != nil {
174			return err
175		}
176		e.peerTCPAddr = append(e.peerTCPAddr, tcpAddr.String())
177		logging.Debugf("Env.initWithConfig(): Peer TCP Addr %s", tcpAddr.String())
178	}
179
180	return nil
181}
182
183func (e *env) initWithArgs() error {
184	if len(os.Args) < 3 {
185		return NewError(ERROR_ARGUMENTS, NORMAL, GENERIC, nil,
186			"Missing command line argument")
187	}
188
189	err := e.resolveHostAddr()
190	if err != nil {
191		return err
192	}
193
194	if len(os.Args) >= 6 {
195		e.resolvePeerAddr()
196		if err != nil {
197			return err
198		}
199	}
200	return nil
201}
202
203func (e *env) resolveHostAddr() (err error) {
204
205	e.hostUDPAddr, err = resolveAddr(common.ELECTION_TRANSPORT_TYPE, os.Args[1])
206	if err != nil {
207		return err
208	}
209	logging.Debugf("Env.resoleHostAddr(): Host UDP Addr %s", e.hostUDPAddr.String())
210
211	e.hostTCPAddr, err = resolveAddr(common.MESSAGE_TRANSPORT_TYPE, os.Args[2])
212	if err != nil {
213		return err
214	}
215	logging.Debugf("Env.resolveHostAddr(): Host TCP Addr %s", e.hostTCPAddr.String())
216
217	e.hostRequestAddr, err = resolveAddr(common.MESSAGE_TRANSPORT_TYPE, os.Args[3])
218	if err != nil {
219		return err
220	}
221	logging.Debugf("Env.resolveHostAddr(): Host Request Addr %s", e.hostRequestAddr.String())
222
223	return nil
224}
225
226func (e *env) resolvePeerAddr() error {
227	args := os.Args[4:]
228	e.peerUDPAddr = make([]string, 0, len(args))
229	e.peerTCPAddr = make([]string, 0, len(args))
230
231	for i := 0; i < len(args); {
232		peer, err := resolveAddr(common.ELECTION_TRANSPORT_TYPE, args[i])
233		if err != nil {
234			return err
235		}
236		e.peerUDPAddr = append(e.peerUDPAddr, peer.String())
237		i++
238		logging.Debugf("Env.resolvePeerAddr(): Peer UDP Addr %s", peer.String())
239
240		peer, err = resolveAddr(common.MESSAGE_TRANSPORT_TYPE, args[i])
241		if err != nil {
242			return err
243		}
244		e.peerTCPAddr = append(e.peerTCPAddr, peer.String())
245		i++
246		logging.Debugf("Env.resolvePeerAddr(): Peer TCP Addr %s", peer.String())
247	}
248
249	return nil
250}
251
252func resolveAddr(network string, addr string) (addrObj net.Addr, err error) {
253
254	if strings.Contains(network, common.MESSAGE_TRANSPORT_TYPE) {
255		addrObj, err = net.ResolveTCPAddr(network, addr)
256	} else {
257		addrObj, err = net.ResolveUDPAddr(network, addr)
258	}
259
260	if err != nil {
261		return nil, err
262	}
263
264	return addrObj, nil
265}
266