github.com/cilium/cilium@v1.16.2/pkg/endpoint/restore_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package endpoint 5 6 import ( 7 "context" 8 "encoding/binary" 9 "fmt" 10 "net/netip" 11 "os" 12 "path/filepath" 13 "sort" 14 "testing" 15 16 "github.com/stretchr/testify/require" 17 18 fake "github.com/cilium/cilium/pkg/datapath/fake/types" 19 "github.com/cilium/cilium/pkg/identity" 20 "github.com/cilium/cilium/pkg/labels" 21 "github.com/cilium/cilium/pkg/mac" 22 testidentity "github.com/cilium/cilium/pkg/testutils/identity" 23 testipcache "github.com/cilium/cilium/pkg/testutils/ipcache" 24 ) 25 26 func (s *EndpointSuite) createEndpoints() ([]*Endpoint, map[uint16]*Endpoint) { 27 epsWanted := []*Endpoint{ 28 s.endpointCreator(256, identity.NumericIdentity(1256)), 29 s.endpointCreator(257, identity.NumericIdentity(1257)), 30 s.endpointCreator(258, identity.NumericIdentity(1258)), 31 s.endpointCreator(259, identity.NumericIdentity(1259)), 32 } 33 epsMap := map[uint16]*Endpoint{ 34 epsWanted[0].ID: epsWanted[0], 35 epsWanted[1].ID: epsWanted[1], 36 epsWanted[2].ID: epsWanted[2], 37 epsWanted[3].ID: epsWanted[3], 38 } 39 return epsWanted, epsMap 40 } 41 42 func getStrID(id uint16) string { 43 return fmt.Sprintf("%05d", id) 44 } 45 46 func (s *EndpointSuite) endpointCreator(id uint16, secID identity.NumericIdentity) *Endpoint { 47 strID := getStrID(id) 48 b := make([]byte, 2) 49 binary.LittleEndian.PutUint16(b, id) 50 51 identity := &identity.Identity{ 52 ID: secID, 53 Labels: labels.Labels{ 54 "foo" + strID: labels.NewLabel("foo"+strID, "", ""), 55 }, 56 } 57 identity.Sanitize() 58 59 repo := s.GetPolicyRepository() 60 repo.GetPolicyCache().LocalEndpointIdentityAdded(identity) 61 62 ep := NewTestEndpointWithState(nil, s, s, testipcache.NewMockIPCache(), &FakeEndpointProxy{}, testidentity.NewMockIdentityAllocator(nil), id, StateReady) 63 // Random network ID and docker endpoint ID with 59 hex chars + 5 strID = 64 hex chars 64 ep.dockerNetworkID = "603e047d2268a57f5a5f93f7f9e1263e9207e348a06654bf64948def001" + strID 65 ep.dockerEndpointID = "93529fda8c401a071d21d6bd46fdf5499b9014dcb5a35f2e3efaa8d8002" + strID 66 ep.ifName = "lxc" + strID 67 ep.mac = mac.MAC([]byte{0x01, 0xff, 0xf2, 0x12, b[0], b[1]}) 68 ep.IPv4 = netip.AddrFrom4([4]byte{0xc0, 0xa8, b[0], b[1]}) 69 ep.IPv6 = netip.AddrFrom16([16]byte{0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, b[0], b[1]}) 70 ep.ifIndex = 1 71 ep.nodeMAC = []byte{0x02, 0xff, 0xf2, 0x12, 0x0, 0x0} 72 ep.SecurityIdentity = identity 73 ep.OpLabels = labels.NewOpLabels() 74 ep.NetNsCookie = 1234 75 return ep 76 } 77 78 func TestReadEPsFromDirNames(t *testing.T) { 79 s := setupEndpointSuite(t) 80 oldDatapath := s.datapath 81 defer func() { 82 s.datapath = oldDatapath 83 }() 84 85 s.datapath = fake.NewDatapath() 86 epsWanted, _ := s.createEndpoints() 87 tmpDir, err := os.MkdirTemp("", "cilium-tests") 88 defer func() { 89 os.RemoveAll(tmpDir) 90 }() 91 92 os.Chdir(tmpDir) 93 require.Nil(t, err) 94 epsNames := []string{} 95 for _, ep := range epsWanted { 96 require.NotNil(t, ep) 97 98 fullDirName := filepath.Join(tmpDir, ep.DirectoryPath()) 99 err := os.MkdirAll(fullDirName, 0777) 100 require.Nil(t, err) 101 102 err = ep.writeHeaderfile(fullDirName) 103 require.Nil(t, err) 104 105 switch ep.ID { 106 case 256, 257: 107 failedDir := filepath.Join(tmpDir, ep.FailedDirectoryPath()) 108 err := os.Rename(fullDirName, failedDir) 109 require.Nil(t, err) 110 epsNames = append(epsNames, ep.FailedDirectoryPath()) 111 112 // create one failed and the other non failed directory for ep 256. 113 if ep.ID == 256 { 114 // Change endpoint a little bit so we know which endpoint is in 115 // "256_next_fail" and with one is in the "256" directory. 116 ep.nodeMAC = []byte{0x02, 0xff, 0xf2, 0x12, 0xc1, 0xc1} 117 err = ep.writeHeaderfile(failedDir) 118 require.Nil(t, err) 119 } 120 default: 121 epsNames = append(epsNames, ep.DirectoryPath()) 122 } 123 } 124 eps := ReadEPsFromDirNames(context.TODO(), s, s, s, tmpDir, epsNames) 125 require.Equal(t, len(epsWanted), len(eps)) 126 127 sort.Slice(epsWanted, func(i, j int) bool { return epsWanted[i].ID < epsWanted[j].ID }) 128 restoredEPs := make([]*Endpoint, 0, len(eps)) 129 for _, ep := range eps { 130 restoredEPs = append(restoredEPs, ep) 131 } 132 sort.Slice(restoredEPs, func(i, j int) bool { return restoredEPs[i].ID < restoredEPs[j].ID }) 133 134 require.Equal(t, len(epsWanted), len(restoredEPs)) 135 for i, restoredEP := range restoredEPs { 136 // We probably shouldn't modify these, but the status will 137 // naturally differ between the wanted endpoint and the version 138 // that's restored, because the restored version has log 139 // messages relating to the restore. 140 restoredEP.status = nil 141 wanted := epsWanted[i] 142 wanted.status = nil 143 require.EqualValues(t, wanted.String(), restoredEP.String()) 144 } 145 } 146 147 func TestReadEPsFromDirNamesWithRestoreFailure(t *testing.T) { 148 s := setupEndpointSuite(t) 149 150 oldDatapath := s.datapath 151 defer func() { 152 s.datapath = oldDatapath 153 }() 154 155 s.datapath = fake.NewDatapath() 156 157 eps, _ := s.createEndpoints() 158 ep := eps[0] 159 require.NotNil(t, ep) 160 tmpDir, err := os.MkdirTemp("", "cilium-tests") 161 defer func() { 162 os.RemoveAll(tmpDir) 163 }() 164 165 os.Chdir(tmpDir) 166 require.Nil(t, err) 167 168 fullDirName := filepath.Join(tmpDir, ep.DirectoryPath()) 169 err = os.MkdirAll(fullDirName, 0777) 170 require.Nil(t, err) 171 172 err = ep.writeHeaderfile(fullDirName) 173 require.Nil(t, err) 174 175 nextDir := filepath.Join(tmpDir, ep.NextDirectoryPath()) 176 err = os.MkdirAll(nextDir, 0777) 177 require.Nil(t, err) 178 179 // Change endpoint a little bit so we know which endpoint is in 180 // "${EPID}_next" and with one is in the "${EPID}" directory. 181 tmpNodeMAC := ep.nodeMAC 182 ep.nodeMAC = []byte{0x02, 0xff, 0xf2, 0x12, 0xc1, 0xc1} 183 err = ep.writeHeaderfile(nextDir) 184 require.Nil(t, err) 185 ep.nodeMAC = tmpNodeMAC 186 187 epNames := []string{ 188 ep.DirectoryPath(), ep.NextDirectoryPath(), 189 } 190 191 epResult := ReadEPsFromDirNames(context.TODO(), s, s, s, tmpDir, epNames) 192 require.Equal(t, 1, len(epResult)) 193 194 restoredEP := epResult[ep.ID] 195 require.EqualValues(t, ep.String(), restoredEP.String()) 196 197 // Check that the directory for failed restore was removed. 198 fileExists := func(fileName string) bool { 199 _, err := os.Stat(fileName) 200 if err == nil { 201 return true 202 } 203 if !os.IsNotExist(err) { 204 require.Error(t, err) 205 } 206 return false 207 } 208 require.Equal(t, false, fileExists(nextDir)) 209 require.Equal(t, true, fileExists(fullDirName)) 210 } 211 212 func BenchmarkReadEPsFromDirNames(b *testing.B) { 213 s := setupEndpointSuite(b) 214 215 b.StopTimer() 216 217 // For this benchmark, the real linux datapath is necessary to properly 218 // serialize config files to disk and benchmark the restore. 219 oldDatapath := s.datapath 220 defer func() { 221 s.datapath = oldDatapath 222 }() 223 224 s.datapath = fake.NewDatapath() 225 226 epsWanted, _ := s.createEndpoints() 227 tmpDir, err := os.MkdirTemp("", "cilium-tests") 228 defer func() { 229 os.RemoveAll(tmpDir) 230 }() 231 232 os.Chdir(tmpDir) 233 require.Nil(b, err) 234 epsNames := []string{} 235 for _, ep := range epsWanted { 236 require.NotNil(b, ep) 237 238 fullDirName := filepath.Join(tmpDir, ep.DirectoryPath()) 239 err := os.MkdirAll(fullDirName, 0777) 240 require.Nil(b, err) 241 242 err = ep.writeHeaderfile(fullDirName) 243 require.Nil(b, err) 244 245 epsNames = append(epsNames, ep.DirectoryPath()) 246 } 247 b.StartTimer() 248 249 for i := 0; i < b.N; i++ { 250 eps := ReadEPsFromDirNames(context.TODO(), s, s, s, tmpDir, epsNames) 251 require.Equal(b, len(epsWanted), len(eps)) 252 } 253 } 254 255 func TestPartitionEPDirNamesByRestoreStatus(t *testing.T) { 256 setupEndpointSuite(t) 257 258 eptsDirNames := []string{ 259 "4", "12", "12_next", "3_next", "5_next_fail", "5", 260 } 261 completeWanted := []string{ 262 "12", "3_next", "4", "5", 263 } 264 incompleteWanted := []string{ 265 "12_next", "5_next_fail", 266 } 267 268 complete, incomplete := partitionEPDirNamesByRestoreStatus(eptsDirNames) 269 270 sort.Strings(complete) 271 sort.Strings(completeWanted) 272 sort.Strings(incomplete) 273 sort.Strings(incompleteWanted) 274 require.EqualValues(t, completeWanted, complete) 275 require.EqualValues(t, incompleteWanted, incomplete) 276 }