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
15package e2e
16
17import (
18	"fmt"
19	"io/ioutil"
20	"os"
21	"strings"
22	"testing"
23
24	"github.com/coreos/etcd/pkg/expect"
25)
26
27const exampleConfigFile = "../etcd.conf.yml.sample"
28
29func TestEtcdExampleConfig(t *testing.T) {
30	proc, err := spawnCmd([]string{binDir + "/etcd", "--config-file", exampleConfigFile})
31	if err != nil {
32		t.Fatal(err)
33	}
34	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
35		t.Fatal(err)
36	}
37	if err = proc.Stop(); err != nil {
38		t.Fatal(err)
39	}
40}
41
42func TestEtcdMultiPeer(t *testing.T) {
43	peers, tmpdirs := make([]string, 3), make([]string, 3)
44	for i := range peers {
45		peers[i] = fmt.Sprintf("e%d=http://127.0.0.1:%d", i, etcdProcessBasePort+i)
46		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
47		if err != nil {
48			t.Fatal(err)
49		}
50		tmpdirs[i] = d
51	}
52	ic := strings.Join(peers, ",")
53
54	procs := make([]*expect.ExpectProcess, len(peers))
55	defer func() {
56		for i := range procs {
57			if procs[i] != nil {
58				procs[i].Stop()
59			}
60			os.RemoveAll(tmpdirs[i])
61		}
62	}()
63	for i := range procs {
64		args := []string{
65			binDir + "/etcd",
66			"--name", fmt.Sprintf("e%d", i),
67			"--listen-client-urls", "http://0.0.0.0:0",
68			"--data-dir", tmpdirs[i],
69			"--advertise-client-urls", "http://0.0.0.0:0",
70			"--listen-peer-urls", fmt.Sprintf("http://127.0.0.1:%d,http://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
71			"--initial-advertise-peer-urls", fmt.Sprintf("http://127.0.0.1:%d", etcdProcessBasePort+i),
72			"--initial-cluster", ic,
73		}
74		p, err := spawnCmd(args)
75		if err != nil {
76			t.Fatal(err)
77		}
78		procs[i] = p
79	}
80
81	for _, p := range procs {
82		if err := waitReadyExpectProc(p, etcdServerReadyLines); err != nil {
83			t.Fatal(err)
84		}
85	}
86}
87
88// TestEtcdUnixPeers checks that etcd will boot with unix socket peers.
89func TestEtcdUnixPeers(t *testing.T) {
90	d, err := ioutil.TempDir("", "e1.etcd")
91	if err != nil {
92		t.Fatal(err)
93	}
94	defer os.RemoveAll(d)
95	proc, err := spawnCmd(
96		[]string{
97			binDir + "/etcd",
98			"--data-dir", d,
99			"--name", "e1",
100			"--listen-peer-urls", "unix://etcd.unix:1",
101			"--initial-advertise-peer-urls", "unix://etcd.unix:1",
102			"--initial-cluster", "e1=unix://etcd.unix:1",
103		},
104	)
105	defer os.Remove("etcd.unix:1")
106	if err != nil {
107		t.Fatal(err)
108	}
109	if err = waitReadyExpectProc(proc, etcdServerReadyLines); err != nil {
110		t.Fatal(err)
111	}
112	if err = proc.Stop(); err != nil {
113		t.Fatal(err)
114	}
115}
116
117// TestEtcdPeerCNAuth checks that the inter peer auth based on CN of cert is working correctly.
118func TestEtcdPeerCNAuth(t *testing.T) {
119	peers, tmpdirs := make([]string, 3), make([]string, 3)
120	for i := range peers {
121		peers[i] = fmt.Sprintf("e%d=https://127.0.0.1:%d", i, etcdProcessBasePort+i)
122		d, err := ioutil.TempDir("", fmt.Sprintf("e%d.etcd", i))
123		if err != nil {
124			t.Fatal(err)
125		}
126		tmpdirs[i] = d
127	}
128	ic := strings.Join(peers, ",")
129
130	procs := make([]*expect.ExpectProcess, len(peers))
131	defer func() {
132		for i := range procs {
133			if procs[i] != nil {
134				procs[i].Stop()
135			}
136			os.RemoveAll(tmpdirs[i])
137		}
138	}()
139
140	// node 0 and 1 have a cert with the correct CN, node 2 doesn't
141	for i := range procs {
142		commonArgs := []string{
143			binDir + "/etcd",
144			"--name", fmt.Sprintf("e%d", i),
145			"--listen-client-urls", "http://0.0.0.0:0",
146			"--data-dir", tmpdirs[i],
147			"--advertise-client-urls", "http://0.0.0.0:0",
148			"--listen-peer-urls", fmt.Sprintf("https://127.0.0.1:%d,https://127.0.0.1:%d", etcdProcessBasePort+i, etcdProcessBasePort+len(peers)+i),
149			"--initial-advertise-peer-urls", fmt.Sprintf("https://127.0.0.1:%d", etcdProcessBasePort+i),
150			"--initial-cluster", ic,
151		}
152
153		var args []string
154		if i <= 1 {
155			args = []string{
156				"--peer-cert-file", certPath,
157				"--peer-key-file", privateKeyPath,
158				"--peer-trusted-ca-file", caPath,
159				"--peer-client-cert-auth",
160				"--peer-cert-allowed-cn", "example.com",
161			}
162		} else {
163			args = []string{
164				"--peer-cert-file", certPath2,
165				"--peer-key-file", privateKeyPath2,
166				"--peer-trusted-ca-file", caPath,
167				"--peer-client-cert-auth",
168				"--peer-cert-allowed-cn", "example2.com",
169			}
170		}
171
172		commonArgs = append(commonArgs, args...)
173
174		p, err := spawnCmd(commonArgs)
175		if err != nil {
176			t.Fatal(err)
177		}
178		procs[i] = p
179	}
180
181	for i, p := range procs {
182		var expect []string
183		if i <= 1 {
184			expect = etcdServerReadyLines
185		} else {
186			expect = []string{"remote error: tls: bad certificate"}
187		}
188		if err := waitReadyExpectProc(p, expect); err != nil {
189			t.Fatal(err)
190		}
191	}
192}
193