1package k8s_test
2
3import (
4	"log"
5	"os"
6	"path/filepath"
7	"reflect"
8	"testing"
9
10	discover "github.com/hashicorp/go-discover"
11	"github.com/hashicorp/go-discover/provider/k8s"
12	corev1 "k8s.io/api/core/v1"
13	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14)
15
16var _ discover.Provider = (*k8s.Provider)(nil)
17
18// Acceptance test against a real cluster
19func TestAcc(t *testing.T) {
20	path := "../../test/tf/k8s/kubeconfig.yaml"
21	path, err := filepath.Abs(path)
22	if err != nil {
23		t.Fatalf("err: %s", err)
24	}
25
26	if _, err := os.Stat(path); err != nil {
27		t.Skipf("Skipping, can't find %q: %s", path, err)
28		return
29	}
30
31	args := discover.Config{
32		"provider":       "k8s",
33		"kubeconfig":     path,
34		"label_selector": "app = valid",
35	}
36
37	l := log.New(os.Stderr, "", log.LstdFlags)
38	p := &k8s.Provider{}
39	addrs, err := p.Addrs(args, l)
40	if err != nil {
41		t.Fatal(err)
42	}
43	t.Logf("Addrs: %v", addrs)
44	if len(addrs) != 3 {
45		t.Fatalf("bad: %v", addrs)
46	}
47}
48
49func TestPodAddrs(t *testing.T) {
50	cases := []struct {
51		Name     string
52		Args     map[string]string
53		Pods     []corev1.Pod
54		Expected []string
55	}{
56		{
57			"Simple pods (no ready, no annotations, etc.)",
58			nil,
59			[]corev1.Pod{
60				corev1.Pod{
61					Status: corev1.PodStatus{
62						Phase:  corev1.PodRunning,
63						PodIP:  "1.2.3.4",
64						HostIP: "2.3.4.5",
65					},
66				},
67			},
68			[]string{"1.2.3.4"},
69		},
70
71		{
72			"Simple pods host network",
73			map[string]string{"host_network": "true"},
74			[]corev1.Pod{
75				corev1.Pod{
76					Status: corev1.PodStatus{
77						Phase:  corev1.PodRunning,
78						PodIP:  "1.2.3.4",
79						HostIP: "2.3.4.5",
80					},
81				},
82			},
83			[]string{"2.3.4.5"},
84		},
85
86		{
87			"Only running pods",
88			nil,
89			[]corev1.Pod{
90				corev1.Pod{
91					Status: corev1.PodStatus{
92						Phase: corev1.PodPending,
93						PodIP: "2.3.4.5",
94					},
95				},
96
97				corev1.Pod{
98					Status: corev1.PodStatus{
99						Phase: corev1.PodRunning,
100						PodIP: "1.2.3.4",
101					},
102				},
103			},
104			[]string{"1.2.3.4"},
105		},
106
107		{
108			"Only pods that are ready",
109			nil,
110			[]corev1.Pod{
111				corev1.Pod{
112					Status: corev1.PodStatus{
113						Phase: corev1.PodPending,
114						PodIP: "2.3.4.5",
115					},
116				},
117
118				corev1.Pod{
119					Status: corev1.PodStatus{
120						Phase: corev1.PodRunning,
121						PodIP: "ready",
122						Conditions: []corev1.PodCondition{
123							corev1.PodCondition{
124								Type:   corev1.PodReady,
125								Status: corev1.ConditionTrue,
126							},
127						},
128					},
129				},
130
131				// Not true
132				corev1.Pod{
133					Status: corev1.PodStatus{
134						Phase: corev1.PodRunning,
135						PodIP: "not-ready",
136						Conditions: []corev1.PodCondition{
137							corev1.PodCondition{
138								Type:   corev1.PodReady,
139								Status: corev1.ConditionUnknown,
140							},
141						},
142					},
143				},
144
145				// Not ready type, ignored
146				corev1.Pod{
147					Status: corev1.PodStatus{
148						Phase: corev1.PodRunning,
149						PodIP: "scheduled",
150						Conditions: []corev1.PodCondition{
151							corev1.PodCondition{
152								Type:   corev1.PodScheduled,
153								Status: corev1.ConditionUnknown,
154							},
155						},
156					},
157				},
158			},
159			[]string{"ready", "scheduled"},
160		},
161
162		{
163			"Port annotation (named)",
164			nil,
165			[]corev1.Pod{
166				corev1.Pod{
167					Status: corev1.PodStatus{
168						Phase: corev1.PodRunning,
169						PodIP: "1.2.3.4",
170					},
171
172					Spec: corev1.PodSpec{
173						Containers: []corev1.Container{
174							corev1.Container{
175								Ports: []corev1.ContainerPort{
176									corev1.ContainerPort{
177										Name:          "my-port",
178										HostPort:      1234,
179										ContainerPort: 8500,
180									},
181
182									corev1.ContainerPort{
183										Name:          "http",
184										HostPort:      80,
185										ContainerPort: 8080,
186									},
187								},
188							},
189						},
190					},
191
192					ObjectMeta: metav1.ObjectMeta{
193						Annotations: map[string]string{
194							k8s.AnnotationKeyPort: "my-port",
195						},
196					},
197				},
198			},
199			[]string{"1.2.3.4:8500"},
200		},
201
202		{
203			"Port annotation (named with host network)",
204			map[string]string{"host_network": "true"},
205			[]corev1.Pod{
206				corev1.Pod{
207					Status: corev1.PodStatus{
208						Phase:  corev1.PodRunning,
209						PodIP:  "1.2.3.4",
210						HostIP: "2.3.4.5",
211					},
212
213					Spec: corev1.PodSpec{
214						Containers: []corev1.Container{
215							corev1.Container{
216								Ports: []corev1.ContainerPort{
217									corev1.ContainerPort{
218										Name:          "http",
219										HostPort:      80,
220										ContainerPort: 8080,
221									},
222								},
223							},
224						},
225					},
226
227					ObjectMeta: metav1.ObjectMeta{
228						Annotations: map[string]string{
229							k8s.AnnotationKeyPort: "http",
230						},
231					},
232				},
233			},
234			[]string{"2.3.4.5:80"},
235		},
236
237		{
238			"Port annotation (direct)",
239			nil,
240			[]corev1.Pod{
241				corev1.Pod{
242					Status: corev1.PodStatus{
243						Phase: corev1.PodRunning,
244						PodIP: "1.2.3.4",
245					},
246
247					Spec: corev1.PodSpec{
248						Containers: []corev1.Container{
249							corev1.Container{
250								Ports: []corev1.ContainerPort{
251									corev1.ContainerPort{
252										Name:          "http",
253										HostPort:      80,
254										ContainerPort: 8080,
255									},
256								},
257							},
258						},
259					},
260
261					ObjectMeta: metav1.ObjectMeta{
262						Annotations: map[string]string{
263							k8s.AnnotationKeyPort: "4600",
264						},
265					},
266				},
267			},
268			[]string{"1.2.3.4:4600"},
269		},
270
271		{
272			"Port annotation (direct with host network)",
273			map[string]string{"host_network": "true"},
274			[]corev1.Pod{
275				corev1.Pod{
276					Status: corev1.PodStatus{
277						Phase:  corev1.PodRunning,
278						PodIP:  "1.2.3.4",
279						HostIP: "2.3.4.5",
280					},
281
282					Spec: corev1.PodSpec{
283						Containers: []corev1.Container{
284							corev1.Container{
285								Ports: []corev1.ContainerPort{
286									corev1.ContainerPort{
287										Name:          "http",
288										HostPort:      80,
289										ContainerPort: 8080,
290									},
291								},
292							},
293						},
294					},
295
296					ObjectMeta: metav1.ObjectMeta{
297						Annotations: map[string]string{
298							k8s.AnnotationKeyPort: "4600",
299						},
300					},
301				},
302			},
303			[]string{"2.3.4.5:4600"},
304		},
305	}
306
307	for _, tt := range cases {
308		t.Run(tt.Name, func(t *testing.T) {
309			l := log.New(os.Stderr, "", log.LstdFlags)
310			addrs, err := k8s.PodAddrs(&corev1.PodList{Items: tt.Pods}, tt.Args, l)
311			if err != nil {
312				t.Fatalf("err: %s", err)
313			}
314
315			if !reflect.DeepEqual(addrs, tt.Expected) {
316				t.Fatalf("bad: %#v", addrs)
317			}
318		})
319	}
320}
321