1// Copyright 2016 The Prometheus Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14package triton
15
16import (
17	"context"
18	"fmt"
19	"net"
20	"net/http"
21	"net/http/httptest"
22	"net/url"
23	"strconv"
24	"strings"
25	"testing"
26
27	"github.com/prometheus/common/config"
28	"github.com/prometheus/common/model"
29	"github.com/stretchr/testify/require"
30)
31
32var (
33	conf = SDConfig{
34		Account:         "testAccount",
35		Role:            "container",
36		DNSSuffix:       "triton.example.com",
37		Endpoint:        "127.0.0.1",
38		Port:            443,
39		Version:         1,
40		RefreshInterval: 1,
41		TLSConfig:       config.TLSConfig{InsecureSkipVerify: true},
42	}
43	badconf = SDConfig{
44		Account:         "badTestAccount",
45		Role:            "container",
46		DNSSuffix:       "bad.triton.example.com",
47		Endpoint:        "127.0.0.1",
48		Port:            443,
49		Version:         1,
50		RefreshInterval: 1,
51		TLSConfig: config.TLSConfig{
52			InsecureSkipVerify: false,
53			KeyFile:            "shouldnotexist.key",
54			CAFile:             "shouldnotexist.ca",
55			CertFile:           "shouldnotexist.cert",
56		},
57	}
58	groupsconf = SDConfig{
59		Account:         "testAccount",
60		Role:            "container",
61		DNSSuffix:       "triton.example.com",
62		Endpoint:        "127.0.0.1",
63		Groups:          []string{"foo", "bar"},
64		Port:            443,
65		Version:         1,
66		RefreshInterval: 1,
67		TLSConfig:       config.TLSConfig{InsecureSkipVerify: true},
68	}
69	cnconf = SDConfig{
70		Account:         "testAccount",
71		Role:            "cn",
72		DNSSuffix:       "triton.example.com",
73		Endpoint:        "127.0.0.1",
74		Port:            443,
75		Version:         1,
76		RefreshInterval: 1,
77		TLSConfig:       config.TLSConfig{InsecureSkipVerify: true},
78	}
79)
80
81func newTritonDiscovery(c SDConfig) (*Discovery, error) {
82	return New(nil, &c)
83}
84
85func TestTritonSDNew(t *testing.T) {
86	td, err := newTritonDiscovery(conf)
87	require.NoError(t, err)
88	require.NotNil(t, td)
89	require.NotNil(t, td.client)
90	require.NotZero(t, td.interval)
91	require.NotNil(t, td.sdConfig)
92	require.Equal(t, conf.Account, td.sdConfig.Account)
93	require.Equal(t, conf.DNSSuffix, td.sdConfig.DNSSuffix)
94	require.Equal(t, conf.Endpoint, td.sdConfig.Endpoint)
95	require.Equal(t, conf.Port, td.sdConfig.Port)
96}
97
98func TestTritonSDNewBadConfig(t *testing.T) {
99	td, err := newTritonDiscovery(badconf)
100	require.Error(t, err)
101	require.Nil(t, td)
102}
103
104func TestTritonSDNewGroupsConfig(t *testing.T) {
105	td, err := newTritonDiscovery(groupsconf)
106	require.NoError(t, err)
107	require.NotNil(t, td)
108	require.NotNil(t, td.client)
109	require.NotZero(t, td.interval)
110	require.NotNil(t, td.sdConfig)
111	require.Equal(t, groupsconf.Account, td.sdConfig.Account)
112	require.Equal(t, groupsconf.DNSSuffix, td.sdConfig.DNSSuffix)
113	require.Equal(t, groupsconf.Endpoint, td.sdConfig.Endpoint)
114	require.Equal(t, groupsconf.Groups, td.sdConfig.Groups)
115	require.Equal(t, groupsconf.Port, td.sdConfig.Port)
116}
117
118func TestTritonSDNewCNConfig(t *testing.T) {
119	td, err := newTritonDiscovery(cnconf)
120	require.NoError(t, err)
121	require.NotNil(t, td)
122	require.NotNil(t, td.client)
123	require.NotZero(t, td.interval)
124	require.NotZero(t, td.sdConfig)
125	require.Equal(t, cnconf.Role, td.sdConfig.Role)
126	require.Equal(t, cnconf.Account, td.sdConfig.Account)
127	require.Equal(t, cnconf.DNSSuffix, td.sdConfig.DNSSuffix)
128	require.Equal(t, cnconf.Endpoint, td.sdConfig.Endpoint)
129	require.Equal(t, cnconf.Port, td.sdConfig.Port)
130}
131
132func TestTritonSDRefreshNoTargets(t *testing.T) {
133	tgts := testTritonSDRefresh(t, conf, "{\"containers\":[]}")
134	require.Nil(t, tgts)
135}
136
137func TestTritonSDRefreshMultipleTargets(t *testing.T) {
138	var (
139		dstr = `{"containers":[
140		 	{
141                                "groups":["foo","bar","baz"],
142				"server_uuid":"44454c4c-5000-104d-8037-b7c04f5a5131",
143				"vm_alias":"server01",
144				"vm_brand":"lx",
145				"vm_image_uuid":"7b27a514-89d7-11e6-bee6-3f96f367bee7",
146				"vm_uuid":"ad466fbf-46a2-4027-9b64-8d3cdb7e9072"
147			},
148			{
149				"server_uuid":"a5894692-bd32-4ca1-908a-e2dda3c3a5e6",
150				"vm_alias":"server02",
151				"vm_brand":"kvm",
152				"vm_image_uuid":"a5894692-bd32-4ca1-908a-e2dda3c3a5e6",
153				"vm_uuid":"7b27a514-89d7-11e6-bee6-3f96f367bee7"
154			}]
155		}`
156	)
157
158	tgts := testTritonSDRefresh(t, conf, dstr)
159	require.NotNil(t, tgts)
160	require.Equal(t, 2, len(tgts))
161}
162
163func TestTritonSDRefreshNoServer(t *testing.T) {
164	var (
165		td, _ = newTritonDiscovery(conf)
166	)
167
168	_, err := td.refresh(context.Background())
169	require.Error(t, err)
170	require.Equal(t, strings.Contains(err.Error(), "an error occurred when requesting targets from the discovery endpoint"), true)
171}
172
173func TestTritonSDRefreshCancelled(t *testing.T) {
174	var (
175		td, _ = newTritonDiscovery(conf)
176	)
177
178	ctx, cancel := context.WithCancel(context.Background())
179	cancel()
180	_, err := td.refresh(ctx)
181	require.Error(t, err)
182	require.Equal(t, strings.Contains(err.Error(), context.Canceled.Error()), true)
183}
184
185func TestTritonSDRefreshCNsUUIDOnly(t *testing.T) {
186	var (
187		dstr = `{"cns":[
188		 	{
189				"server_uuid":"44454c4c-5000-104d-8037-b7c04f5a5131"
190			},
191			{
192				"server_uuid":"a5894692-bd32-4ca1-908a-e2dda3c3a5e6"
193			}]
194		}`
195	)
196
197	tgts := testTritonSDRefresh(t, cnconf, dstr)
198	require.NotNil(t, tgts)
199	require.Equal(t, 2, len(tgts))
200}
201
202func TestTritonSDRefreshCNsWithHostname(t *testing.T) {
203	var (
204		dstr = `{"cns":[
205		 	{
206				"server_uuid":"44454c4c-5000-104d-8037-b7c04f5a5131",
207				"server_hostname": "server01"
208			},
209			{
210				"server_uuid":"a5894692-bd32-4ca1-908a-e2dda3c3a5e6",
211				"server_hostname": "server02"
212			}]
213		}`
214	)
215
216	tgts := testTritonSDRefresh(t, cnconf, dstr)
217	require.NotNil(t, tgts)
218	require.Equal(t, 2, len(tgts))
219}
220
221func testTritonSDRefresh(t *testing.T, c SDConfig, dstr string) []model.LabelSet {
222	var (
223		td, _ = newTritonDiscovery(c)
224		s     = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
225			fmt.Fprintln(w, dstr)
226		}))
227	)
228
229	defer s.Close()
230
231	u, err := url.Parse(s.URL)
232	require.NoError(t, err)
233	require.NotNil(t, u)
234
235	host, strport, err := net.SplitHostPort(u.Host)
236	require.NoError(t, err)
237	require.NotEmpty(t, host)
238	require.NotEmpty(t, strport)
239
240	port, err := strconv.Atoi(strport)
241	require.NoError(t, err)
242	require.NotZero(t, port)
243
244	td.sdConfig.Port = port
245
246	tgs, err := td.refresh(context.Background())
247	require.NoError(t, err)
248	require.Equal(t, 1, len(tgs))
249	tg := tgs[0]
250	require.NotNil(t, tg)
251
252	return tg.Targets
253}
254