github.com/cilium/cilium@v1.16.2/pkg/fqdn/name_manager_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package fqdn
     5  
     6  import (
     7  	"context"
     8  	"net"
     9  	"net/netip"
    10  	"regexp"
    11  	"testing"
    12  
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/cilium/cilium/pkg/fqdn/dns"
    16  	"github.com/cilium/cilium/pkg/ipcache"
    17  	ipcacheTypes "github.com/cilium/cilium/pkg/ipcache/types"
    18  	"github.com/cilium/cilium/pkg/labels"
    19  	"github.com/cilium/cilium/pkg/policy/api"
    20  	"github.com/cilium/cilium/pkg/time"
    21  )
    22  
    23  type mockIPCache struct {
    24  	metadata map[netip.Prefix]map[ipcacheTypes.ResourceID]labels.Labels
    25  }
    26  
    27  func newMockIPCache() *mockIPCache {
    28  	return &mockIPCache{
    29  		metadata: make(map[netip.Prefix]map[ipcacheTypes.ResourceID]labels.Labels),
    30  	}
    31  }
    32  
    33  func (m *mockIPCache) labelsForPrefix(prefix netip.Prefix) labels.Labels {
    34  	lbls := labels.Labels{}
    35  	for _, l := range m.metadata[prefix] {
    36  		lbls.MergeLabels(l)
    37  	}
    38  	return lbls
    39  }
    40  
    41  func (m *mockIPCache) UpsertMetadataBatch(updates ...ipcache.MU) (revision uint64) {
    42  	for _, mu := range updates {
    43  		prefixMetadata, ok := m.metadata[mu.Prefix]
    44  		if !ok {
    45  			prefixMetadata = make(map[ipcacheTypes.ResourceID]labels.Labels)
    46  		}
    47  
    48  		for _, aux := range mu.Metadata {
    49  			if lbls, ok := aux.(labels.Labels); ok {
    50  				prefixMetadata[mu.Resource] = lbls
    51  				break
    52  			}
    53  		}
    54  
    55  		m.metadata[mu.Prefix] = prefixMetadata
    56  	}
    57  
    58  	return 0
    59  }
    60  
    61  func (m *mockIPCache) RemoveMetadataBatch(updates ...ipcache.MU) (revision uint64) {
    62  	for _, mu := range updates {
    63  		for _, aux := range mu.Metadata {
    64  			if _, ok := aux.(labels.Labels); ok {
    65  				delete(m.metadata[mu.Prefix], mu.Resource)
    66  				break
    67  			}
    68  		}
    69  
    70  		if len(m.metadata[mu.Prefix]) == 0 {
    71  			delete(m.metadata, mu.Prefix)
    72  		}
    73  	}
    74  
    75  	return 0
    76  }
    77  
    78  func (m *mockIPCache) WaitForRevision(ctx context.Context, rev uint64) error {
    79  	return nil
    80  }
    81  
    82  func ipsFromPrefixes(t *testing.T, prefixes ...netip.Prefix) (ips []net.IP) {
    83  	t.Helper()
    84  
    85  	for _, p := range prefixes {
    86  		if !p.IsSingleIP() {
    87  			t.Fatalf("invalid prefix: %s", p)
    88  		}
    89  
    90  		ips = append(ips, p.Addr().AsSlice())
    91  	}
    92  
    93  	return ips
    94  }
    95  
    96  func TestNameManagerIPCacheUpdates(t *testing.T) {
    97  	ipc := newMockIPCache()
    98  	nameManager := NewNameManager(Config{
    99  		MinTTL:  1,
   100  		Cache:   NewDNSCache(0),
   101  		IPCache: ipc,
   102  	})
   103  
   104  	nameManager.RegisterFQDNSelector(ciliumIOSel)
   105  
   106  	// Simulate lookup for single selector
   107  	prefix := netip.MustParsePrefix("1.1.1.1/32")
   108  	nameManager.UpdateGenerateDNS(context.TODO(), time.Now(), map[string]*DNSIPRecords{dns.FQDN("cilium.io"): {TTL: 60, IPs: ipsFromPrefixes(t, prefix)}})
   109  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{ciliumIOSel.IdentityLabel()}))
   110  
   111  	// Add match pattern
   112  	nameManager.RegisterFQDNSelector(ciliumIOSelMatchPattern)
   113  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{ciliumIOSel.IdentityLabel(), ciliumIOSelMatchPattern.IdentityLabel()}))
   114  
   115  	// Remove cilium.io matchname, add github.com match name
   116  	nameManager.RegisterFQDNSelector(githubSel)
   117  	nameManager.UnregisterFQDNSelector(ciliumIOSel)
   118  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{ciliumIOSelMatchPattern.IdentityLabel()}))
   119  
   120  	// Same IP matched by two selectors
   121  	nameManager.UpdateGenerateDNS(context.TODO(), time.Now(), map[string]*DNSIPRecords{dns.FQDN("github.com"): {TTL: 60, IPs: ipsFromPrefixes(t, prefix)}})
   122  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{ciliumIOSelMatchPattern.IdentityLabel(), githubSel.IdentityLabel()}))
   123  
   124  	// Additional unique IPs for each selector
   125  	githubPrefix := netip.MustParsePrefix("10.0.0.2/32")
   126  	awesomePrefix := netip.MustParsePrefix("10.0.0.3/32")
   127  	nameManager.UpdateGenerateDNS(context.TODO(), time.Now(), map[string]*DNSIPRecords{
   128  		dns.FQDN("github.com"):       {TTL: 60, IPs: ipsFromPrefixes(t, githubPrefix)},
   129  		dns.FQDN("awesomecilium.io"): {TTL: 60, IPs: ipsFromPrefixes(t, awesomePrefix)},
   130  	})
   131  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{ciliumIOSelMatchPattern.IdentityLabel(), githubSel.IdentityLabel()}))
   132  	require.Equal(t, ipc.labelsForPrefix(githubPrefix), labels.FromSlice([]labels.Label{githubSel.IdentityLabel()}))
   133  	require.Equal(t, ipc.labelsForPrefix(awesomePrefix), labels.FromSlice([]labels.Label{ciliumIOSelMatchPattern.IdentityLabel()}))
   134  
   135  	// Removing selector should remove from IPCache
   136  	nameManager.UnregisterFQDNSelector(ciliumIOSelMatchPattern)
   137  	require.NotContains(t, ipc.metadata, awesomePrefix)
   138  	require.Equal(t, ipc.labelsForPrefix(prefix), labels.FromSlice([]labels.Label{githubSel.IdentityLabel()}))
   139  	require.Equal(t, ipc.labelsForPrefix(githubPrefix), labels.FromSlice([]labels.Label{githubSel.IdentityLabel()}))
   140  }
   141  
   142  func Test_deriveLabelsForNames(t *testing.T) {
   143  	ciliumIORe, err := ciliumIOSel.ToRegex()
   144  	require.NoError(t, err)
   145  	githubRe, err := githubSel.ToRegex()
   146  	require.NoError(t, err)
   147  	ciliumIOSelMatchPatternRe, err := ciliumIOSelMatchPattern.ToRegex()
   148  	require.NoError(t, err)
   149  
   150  	selectors := map[api.FQDNSelector]*regexp.Regexp{
   151  		ciliumIOSel:             ciliumIORe,
   152  		githubSel:               githubRe,
   153  		ciliumIOSelMatchPattern: ciliumIOSelMatchPatternRe,
   154  	}
   155  
   156  	nomatchIP := netip.MustParseAddr("10.10.0.1")
   157  	githubIP := netip.MustParseAddr("10.20.0.1")
   158  	ciliumIP1 := netip.MustParseAddr("10.30.0.1")
   159  	ciliumIP2 := netip.MustParseAddr("10.30.0.2")
   160  
   161  	names := map[string][]netip.Addr{
   162  		"nomatch.local.":    {nomatchIP},
   163  		"github.com.":       {githubIP},
   164  		"cilium.io.":        {ciliumIP1},
   165  		"awesomecilium.io.": {ciliumIP1, ciliumIP2},
   166  	}
   167  
   168  	require.Equal(t, deriveLabelsForNames(names, selectors), map[string]nameMetadata{
   169  		"nomatch.local.": {
   170  			addrs:  []netip.Addr{nomatchIP},
   171  			labels: labels.Labels{},
   172  		},
   173  		"github.com.": {
   174  			addrs:  []netip.Addr{githubIP},
   175  			labels: labels.NewLabelsFromSortedList("fqdn:github.com"),
   176  		},
   177  		"cilium.io.": {
   178  			addrs:  []netip.Addr{ciliumIP1},
   179  			labels: labels.NewLabelsFromSortedList("fqdn:*cilium.io.;fqdn:cilium.io"),
   180  		},
   181  		"awesomecilium.io.": {
   182  			addrs:  []netip.Addr{ciliumIP1, ciliumIP2},
   183  			labels: labels.NewLabelsFromSortedList("fqdn:*cilium.io."),
   184  		},
   185  	})
   186  }