1// +build !consulent
2
3package consul
4
5import (
6	"sort"
7	"testing"
8	"time"
9
10	"github.com/hashicorp/consul/agent/structs"
11	"github.com/stretchr/testify/require"
12)
13
14func testLeader_LegacyIntentionMigrationHookEnterprise(_ *testing.T, _ *Server, _ bool) {
15}
16
17func appendLegacyIntentionsForMigrationTestEnterprise(_ *testing.T, _ *Server, ixns []*structs.Intention) []*structs.Intention {
18	return ixns
19}
20
21func TestMigrateIntentionsToConfigEntries(t *testing.T) {
22	compare := func(t *testing.T, got structs.Intentions, expect [][]string) {
23		t.Helper()
24
25		var actual [][]string
26		for _, ixn := range got {
27			actual = append(actual, []string{
28				ixn.SourceNS,
29				ixn.SourceName,
30				ixn.DestinationNS,
31				ixn.DestinationName,
32			})
33		}
34		require.ElementsMatch(t, expect, actual)
35	}
36
37	type testCase struct {
38		insert [][]string
39		expect [][]string
40	}
41
42	cases := map[string]testCase{
43		"no change": {
44			insert: [][]string{
45				{"default", "foo", "default", "bar"},
46				{"default", "*", "default", "*"},
47			},
48			expect: [][]string{
49				{"default", "foo", "default", "bar"},
50				{"default", "*", "default", "*"},
51			},
52		},
53		"non-wildcard deletions": {
54			insert: [][]string{
55				{"default", "foo", "default", "bar"},
56				{"alpha", "*", "default", "bar"},
57				{"default", "foo", "beta", "*"},
58				{"alpha", "zoo", "beta", "bar"},
59			},
60			expect: [][]string{
61				{"default", "foo", "default", "bar"},
62			},
63		},
64		"updates with no deletions and no collisions": {
65			insert: [][]string{
66				{"default", "foo", "default", "bar"},
67				{"default", "foo", "*", "*"},
68				{"*", "*", "default", "bar"},
69				{"*", "*", "*", "*"},
70			},
71			expect: [][]string{
72				{"default", "foo", "default", "bar"},
73				{"default", "foo", "default", "*"},
74				{"default", "*", "default", "bar"},
75				{"default", "*", "default", "*"},
76			},
77		},
78		"updates with only collision deletions": {
79			insert: [][]string{
80				{"default", "foo", "default", "bar"},
81				{"default", "foo", "default", "*"},
82				{"default", "foo", "*", "*"},
83				{"default", "*", "default", "bar"},
84				{"*", "*", "default", "bar"},
85				{"default", "*", "default", "*"},
86				{"*", "*", "*", "*"},
87			},
88			expect: [][]string{
89				{"default", "foo", "default", "bar"},
90				{"default", "foo", "default", "*"},
91				{"default", "*", "default", "bar"},
92				{"default", "*", "default", "*"},
93			},
94		},
95		"a bit of everything": {
96			insert: [][]string{
97				{"default", "foo", "default", "bar"}, // retained
98				{"default", "foo", "*", "*"},         // upgrade
99				{"default", "*", "default", "bar"},   // retained in collision
100				{"*", "*", "default", "bar"},         // deleted in collision
101				{"default", "*", "default", "*"},     // retained in collision
102				{"*", "*", "*", "*"},                 // deleted in collision
103				{"alpha", "*", "default", "bar"},     // deleted
104				{"default", "foo", "beta", "*"},      // deleted
105				{"alpha", "zoo", "beta", "bar"},      // deleted
106			},
107			expect: [][]string{
108				{"default", "foo", "default", "bar"},
109				{"default", "foo", "default", "*"},
110				{"default", "*", "default", "bar"},
111				{"default", "*", "default", "*"},
112			},
113		},
114	}
115	for name, tc := range cases {
116		tc := tc
117		t.Run(name, func(t *testing.T) {
118
119			// Do something super evil and directly reach into the FSM to seed it with "bad" data.
120			var ixns structs.Intentions
121			for _, elem := range tc.insert {
122				require.Len(t, elem, 4)
123				ixn := structs.TestIntention(t)
124				ixn.ID = generateUUID()
125				ixn.SourceNS = elem[0]
126				ixn.SourceName = elem[1]
127				ixn.DestinationNS = elem[2]
128				ixn.DestinationName = elem[3]
129				ixn.CreatedAt = time.Now().UTC()
130				ixn.UpdatedAt = ixn.CreatedAt
131
132				ixns = append(ixns, ixn)
133			}
134
135			// Sleep a bit so that the UpdatedAt field will definitely be different
136			time.Sleep(1 * time.Millisecond)
137
138			got := migrateIntentionsToConfigEntries(ixns)
139
140			// Convert them back to the line-item version.
141			var gotIxns structs.Intentions
142			for _, entry := range got {
143				gotIxns = append(gotIxns, entry.ToIntentions()...)
144			}
145			sort.Sort(structs.IntentionPrecedenceSorter(gotIxns))
146
147			compare(t, gotIxns, tc.expect)
148		})
149	}
150}
151