diff --git a/go.mod b/go.mod index f0029179595..c5e39ef976d 100644 --- a/go.mod +++ b/go.mod @@ -68,6 +68,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 + gotest.tools v2.2.0+incompatible k8s.io/api v0.31.1 k8s.io/apiextensions-apiserver v0.31.1 k8s.io/apimachinery v0.31.1 diff --git a/go.sum b/go.sum index e75e093e386..02da9dd10b4 100644 --- a/go.sum +++ b/go.sum @@ -1113,6 +1113,7 @@ gopkg.in/yaml.v3 v3.0.0-20200121175148-a6ecf24a6d71/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 401b2aa48e1..20fc9a78b3c 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -34,6 +34,7 @@ IMAGE_NAME="antrea/codegen:kubernetes-1.31.1-build.1" # to the "safe" list in the Git config when starting the container (required # because of user mismatch). if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then + echo ${git_status} echoerr "!!! Dirty tree. Clean up and try again." exit 1 fi diff --git a/multicluster/hack/update-codegen.sh b/multicluster/hack/update-codegen.sh index b49148257dd..267bdd90444 100755 --- a/multicluster/hack/update-codegen.sh +++ b/multicluster/hack/update-codegen.sh @@ -34,6 +34,7 @@ IMAGE_NAME="antrea/codegen:kubernetes-1.31.1-build.1" # to the "safe" list in the Git config when starting the container (required # because of user mismatch). if git_status=$(git status --porcelain --untracked=no 2>/dev/null) && [[ -n "${git_status}" ]]; then + echo ${git_status} echoerr "!!! Dirty tree. Clean up and try again." exit 1 fi diff --git a/pkg/agent/apis/types.go b/pkg/agent/apis/types.go index 0c4d275bab5..48f2406e355 100644 --- a/pkg/agent/apis/types.go +++ b/pkg/agent/apis/types.go @@ -15,10 +15,13 @@ package apis import ( + "net" "strconv" "strings" + "time" corev1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" "antrea.io/antrea/pkg/apis/crd/v1beta1" "antrea.io/antrea/pkg/util/printers" @@ -72,6 +75,31 @@ func (r AntreaAgentInfoResponse) SortRows() bool { return true } +type FqdnCacheResponse struct { + fqdnName string + ipAddress net.IP + expirationTime time.Time +} + +func (r FqdnCacheResponse) GetTableHeader() []string { + return []string{"FQDN", "ADDRESS", "EXPIRATION TIME"} +} + +func (r FqdnCacheResponse) GetTableRow(maxColumn int) []string { + klog.InfoS("DBUG: types.go GetTableRow() called") + return []string{ + r.fqdnName, + r.ipAddress.String(), + r.expirationTime.String(), + } +} + +func (r FqdnCacheResponse) SortRows() bool { + return false +} + +// maybe some helper funcs needed here to help parse through ^^^^ + type FeatureGateResponse struct { Component string `json:"component,omitempty"` Name string `json:"name,omitempty"` diff --git a/pkg/agent/apiserver/apiserver.go b/pkg/agent/apiserver/apiserver.go index aaf5e25e9d6..d74dafea717 100644 --- a/pkg/agent/apiserver/apiserver.go +++ b/pkg/agent/apiserver/apiserver.go @@ -40,6 +40,7 @@ import ( "antrea.io/antrea/pkg/agent/apiserver/handlers/bgppolicy" "antrea.io/antrea/pkg/agent/apiserver/handlers/bgproute" "antrea.io/antrea/pkg/agent/apiserver/handlers/featuregates" + "antrea.io/antrea/pkg/agent/apiserver/handlers/fqdncache" "antrea.io/antrea/pkg/agent/apiserver/handlers/memberlist" "antrea.io/antrea/pkg/agent/apiserver/handlers/multicast" "antrea.io/antrea/pkg/agent/apiserver/handlers/networkpolicy" @@ -104,6 +105,7 @@ func installHandlers(aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolic s.Handler.NonGoRestfulMux.HandleFunc("/bgppolicy", bgppolicy.HandleFunc(bgpq)) s.Handler.NonGoRestfulMux.HandleFunc("/bgppeers", bgppeer.HandleFunc(bgpq)) s.Handler.NonGoRestfulMux.HandleFunc("/bgproutes", bgproute.HandleFunc(bgpq)) + s.Handler.NonGoRestfulMux.HandleFunc("/fqdncache", fqdncache.HandleFunc(aq)) } func installAPIGroup(s *genericapiserver.GenericAPIServer, aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolicyInfoQuerier, v4Enabled, v6Enabled bool) error { diff --git a/pkg/agent/apiserver/handlers/fqdncache/handler.go b/pkg/agent/apiserver/handlers/fqdncache/handler.go new file mode 100644 index 00000000000..8a7c1497f04 --- /dev/null +++ b/pkg/agent/apiserver/handlers/fqdncache/handler.go @@ -0,0 +1,37 @@ +// Copyright 2025 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fqdncache + +import ( + "encoding/json" + "net/http" + + "k8s.io/klog/v2" + + agentquerier "antrea.io/antrea/pkg/agent/querier" +) + +func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + dnsEntryCache := aq.GetFqdnCache() + if dnsEntryCache == nil { + return + } + if err := json.NewEncoder(w).Encode(dnsEntryCache); err != nil { + http.Error(w, "Failed to encode response: "+err.Error(), http.StatusInternalServerError) + klog.ErrorS(err, "Failed to encode response") + } + } +} diff --git a/pkg/agent/apiserver/handlers/fqdncache/handler_test.go b/pkg/agent/apiserver/handlers/fqdncache/handler_test.go new file mode 100644 index 00000000000..6370717f8e1 --- /dev/null +++ b/pkg/agent/apiserver/handlers/fqdncache/handler_test.go @@ -0,0 +1,82 @@ +// Copyright 2025 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fqdncache + +import ( + "encoding/json" + "net" + "net/http" + "net/http/httptest" + "testing" + "time" + + queriertest "antrea.io/antrea/pkg/agent/querier/testing" + "antrea.io/antrea/pkg/agent/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func TestFqdnCacheQuery(t *testing.T) { + tests := []struct { + name string + expectedStatus int + expectedResponse []types.DnsCacheEntry + }{ + { + name: "FQDN cache exists", + expectedStatus: http.StatusOK, + expectedResponse: []types.DnsCacheEntry{ + { + FqdnName: "google.com", + IpAddress: net.ParseIP("10.0.0.1"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.UTC), + }, + }, + }, + { + name: "FQDN cache does not exist", + expectedStatus: http.StatusOK, + expectedResponse: []types.DnsCacheEntry{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctrl := gomock.NewController(t) + q := queriertest.NewMockAgentQuerier(ctrl) + q.EXPECT().GetFqdnCache().Return(tt.expectedResponse) + handler := HandleFunc(q) + req, err := http.NewRequest(http.MethodGet, "", nil) + require.NoError(t, err) + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + assert.Equal(t, tt.expectedStatus, recorder.Code) + if tt.expectedStatus == http.StatusOK { + var received []map[string]interface{} + err = json.Unmarshal(recorder.Body.Bytes(), &received) + require.NoError(t, err) + if len(received) > 0 { + parsedTime, err := time.Parse(time.RFC3339, received[0]["expirationTime"].(string)) + require.NoError(t, err) + assert.Equal(t, tt.expectedResponse, []types.DnsCacheEntry{{ + FqdnName: received[0]["fqdnName"].(string), + IpAddress: net.ParseIP(received[0]["ipAddress"].(string)), + ExpirationTime: parsedTime, + }}) + } + } + }) + } +} diff --git a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go index 7e0d792cef4..dbee2e2b740 100644 --- a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go +++ b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go @@ -538,6 +538,18 @@ func NewNetworkPolicyController(antreaClientGetter client.AntreaClientProvider, return c, nil } +// TODO add change here for how data is interpreted and sent +func (c *Controller) GetFqdnCache() []types.DnsCacheEntry { + cacheEntryList := []types.DnsCacheEntry{} + for fqdn, dnsMeta := range c.fqdnController.dnsEntryCache { + for _, ipWithExpiration := range dnsMeta.responseIPs { + entry := types.DnsCacheEntry{FqdnName: fqdn, IpAddress: ipWithExpiration.ip, ExpirationTime: ipWithExpiration.expirationTime} + cacheEntryList = append(cacheEntryList, entry) + } + } + return cacheEntryList +} + func (c *Controller) GetNetworkPolicyNum() int { return c.ruleCache.GetNetworkPolicyNum() } diff --git a/pkg/agent/querier/querier.go b/pkg/agent/querier/querier.go index bdd7d1ff363..ef2e7ffa066 100644 --- a/pkg/agent/querier/querier.go +++ b/pkg/agent/querier/querier.go @@ -26,6 +26,7 @@ import ( "antrea.io/antrea/pkg/agent/memberlist" "antrea.io/antrea/pkg/agent/openflow" "antrea.io/antrea/pkg/agent/proxy" + "antrea.io/antrea/pkg/agent/types" "antrea.io/antrea/pkg/apis/crd/v1beta1" "antrea.io/antrea/pkg/ovs/ovsconfig" "antrea.io/antrea/pkg/ovs/ovsctl" @@ -47,6 +48,7 @@ type AgentQuerier interface { GetMemberlistCluster() memberlist.Interface GetNodeLister() corelisters.NodeLister GetBGPPolicyInfoQuerier() querier.AgentBGPPolicyInfoQuerier + GetFqdnCache() []types.DnsCacheEntry } type agentQuerier struct { @@ -97,6 +99,12 @@ func NewAgentQuerier( } } +// GetFQDNCache returns dnsEntryCache within fqdnController +func (aq agentQuerier) GetFqdnCache() []types.DnsCacheEntry { + klog.InfoS("DBUG: fqdn agent querier GetFqdnCache() called") + return aq.networkPolicyInfoQuerier.GetFqdnCache() +} + // GetNodeLister returns NodeLister. func (aq agentQuerier) GetNodeLister() corelisters.NodeLister { return aq.nodeLister diff --git a/pkg/agent/querier/testing/mock_querier.go b/pkg/agent/querier/testing/mock_querier.go index 2aa363edbaa..da79ab2da75 100644 --- a/pkg/agent/querier/testing/mock_querier.go +++ b/pkg/agent/querier/testing/mock_querier.go @@ -32,6 +32,7 @@ import ( memberlist "antrea.io/antrea/pkg/agent/memberlist" openflow "antrea.io/antrea/pkg/agent/openflow" proxy "antrea.io/antrea/pkg/agent/proxy" + types "antrea.io/antrea/pkg/agent/types" v1beta1 "antrea.io/antrea/pkg/apis/crd/v1beta1" ovsctl "antrea.io/antrea/pkg/ovs/ovsctl" querier "antrea.io/antrea/pkg/querier" @@ -90,6 +91,20 @@ func (mr *MockAgentQuerierMockRecorder) GetBGPPolicyInfoQuerier() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBGPPolicyInfoQuerier", reflect.TypeOf((*MockAgentQuerier)(nil).GetBGPPolicyInfoQuerier)) } +// GetFqdnCache mocks base method. +func (m *MockAgentQuerier) GetFqdnCache() []types.DnsCacheEntry { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFqdnCache") + ret0, _ := ret[0].([]types.DnsCacheEntry) + return ret0 +} + +// GetFqdnCache indicates an expected call of GetFqdnCache. +func (mr *MockAgentQuerierMockRecorder) GetFqdnCache() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFqdnCache", reflect.TypeOf((*MockAgentQuerier)(nil).GetFqdnCache)) +} + // GetInterfaceStore mocks base method. func (m *MockAgentQuerier) GetInterfaceStore() interfacestore.InterfaceStore { m.ctrl.T.Helper() diff --git a/pkg/agent/types/networkpolicy.go b/pkg/agent/types/networkpolicy.go index 35116708239..582e34660ba 100644 --- a/pkg/agent/types/networkpolicy.go +++ b/pkg/agent/types/networkpolicy.go @@ -15,6 +15,9 @@ package types import ( + "net" + "time" + "k8s.io/apimachinery/pkg/util/sets" "antrea.io/antrea/pkg/apis/controlplane/v1beta2" @@ -22,6 +25,12 @@ import ( binding "antrea.io/antrea/pkg/ovs/openflow" ) +type DnsCacheEntry struct { + FqdnName string `json:"fqdnName,omitempty"` + IpAddress net.IP `json:"ipAddress,omitempty"` + ExpirationTime time.Time `json:"expirationTime,omitempty"` +} + type MatchKey struct { ofProtocol binding.Protocol valueCategory AddressCategory diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 78981fcfe86..f08a7713261 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -31,6 +31,7 @@ import ( "antrea.io/antrea/pkg/antctl/transform/addressgroup" "antrea.io/antrea/pkg/antctl/transform/appliedtogroup" "antrea.io/antrea/pkg/antctl/transform/controllerinfo" + "antrea.io/antrea/pkg/antctl/transform/fqdncache" "antrea.io/antrea/pkg/antctl/transform/networkpolicy" "antrea.io/antrea/pkg/antctl/transform/ovstracing" "antrea.io/antrea/pkg/antctl/transform/version" @@ -726,6 +727,27 @@ $ antctl get podmulticaststats pod -n namespace`, commandGroup: get, transformedResponse: reflect.TypeOf(agentapis.BGPRouteResponse{}), }, + { + use: "fqdncache", + short: "Print fqdn cache", + long: "Print effective fqdn cache information including fqdn name, IP addresses, and expiration time", + agentEndpoint: &endpoint{ + nonResourceEndpoint: &nonResourceEndpoint{ + path: "/fqdncache", + params: []flagInfo{ + { + name: "domain", + usage: "Get fqdn cache for only a specific domain", + shorthand: "d", + }, + }, + outputType: multiple, + }, + addonTransform: fqdncache.Transform, + }, + commandGroup: get, + transformedResponse: reflect.TypeOf(agentapis.FqdnCacheResponse{}), + }, }, rawCommands: []rawCommand{ { diff --git a/pkg/antctl/command_definition.go b/pkg/antctl/command_definition.go index ff463548295..fa8f00f62d4 100644 --- a/pkg/antctl/command_definition.go +++ b/pkg/antctl/command_definition.go @@ -424,6 +424,9 @@ func (cd *commandDefinition) output(resp io.Reader, writer io.Writer, ft formatt return fmt.Errorf("error when decoding response %v: %w", resp, err) } } else { + // var testobj []types.DnsCacheEntry + // err := json.NewDecoder(resp).Decode(&testobj) + // fmt.Printf("DBUG: about to transform %v via function %v\n", testobj, addonTransform) obj, err = addonTransform(resp, single, args) if err != nil { return fmt.Errorf("error when doing local transform: %w", err) diff --git a/pkg/antctl/command_list_test.go b/pkg/antctl/command_list_test.go index 509d9c8701d..a0ffd32bdc0 100644 --- a/pkg/antctl/command_list_test.go +++ b/pkg/antctl/command_list_test.go @@ -70,7 +70,7 @@ func TestGetDebugCommands(t *testing.T) { { name: "Antctl running against agent mode", mode: "agent", - expected: [][]string{{"version"}, {"get", "podmulticaststats"}, {"log-level"}, {"get", "networkpolicy"}, {"get", "appliedtogroup"}, {"get", "addressgroup"}, {"get", "agentinfo"}, {"get", "podinterface"}, {"get", "ovsflows"}, {"trace-packet"}, {"get", "serviceexternalip"}, {"get", "memberlist"}, {"get", "bgppolicy"}, {"get", "bgppeers"}, {"get", "bgproutes"}, {"supportbundle"}, {"traceflow"}, {"get", "featuregates"}}, + expected: [][]string{{"version"}, {"get", "podmulticaststats"}, {"log-level"}, {"get", "networkpolicy"}, {"get", "appliedtogroup"}, {"get", "addressgroup"}, {"get", "agentinfo"}, {"get", "podinterface"}, {"get", "ovsflows"}, {"trace-packet"}, {"get", "serviceexternalip"}, {"get", "memberlist"}, {"get", "bgppolicy"}, {"get", "bgppeers"}, {"get", "bgproutes"}, {"get", "fqdncache"}, {"supportbundle"}, {"traceflow"}, {"get", "featuregates"}}, }, { name: "Antctl running against flow-aggregator mode", diff --git a/pkg/antctl/transform/fqdncache/response.go b/pkg/antctl/transform/fqdncache/response.go new file mode 100644 index 00000000000..2448e900fb7 --- /dev/null +++ b/pkg/antctl/transform/fqdncache/response.go @@ -0,0 +1,68 @@ +// Copyright 2025 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fqdncache + +import ( + "encoding/json" + "io" + + "antrea.io/antrea/pkg/agent/types" +) + +type Response struct { + *types.DnsCacheEntry +} + +func Transform(reader io.Reader, single bool, opts map[string]string) (interface{}, error) { + b, err := io.ReadAll(reader) + if err != nil { + return nil, err + } + var resp []Response + err = json.Unmarshal(b, &resp) + if err != nil { + return nil, err + } + domain, exists := opts["domain"] + if exists { + var filteredResp []Response + for _, r := range resp { + if r.FqdnName == domain { + filteredResp = append(filteredResp, r) + } + } + resp = filteredResp + } + if len(resp) == 0 { + return "", nil + } + return resp, nil +} + +func (r Response) GetTableHeader() []string { + return []string{"FQDN", "ADDRESS", "EXPIRATION TIME"} +} + +func (r Response) GetTableRow(maxColumnLength int) []string { + return []string{ + r.FqdnName, + r.IpAddress.String(), + r.ExpirationTime.String(), + } +} + +func (r Response) SortRows() bool { + return false +} diff --git a/pkg/antctl/transform/fqdncache/response_test.go b/pkg/antctl/transform/fqdncache/response_test.go new file mode 100644 index 00000000000..2fe7f08a57c --- /dev/null +++ b/pkg/antctl/transform/fqdncache/response_test.go @@ -0,0 +1,121 @@ +// Copyright 2025 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fqdncache + +import ( + "bytes" + "encoding/json" + "fmt" + "net" + "testing" + "time" + + "antrea.io/antrea/pkg/agent/types" + "github.com/stretchr/testify/require" + "gotest.tools/assert" +) + +func TestTrasnform(t *testing.T) { + var fqdn1 = types.DnsCacheEntry{ + FqdnName: "google.com", + IpAddress: net.ParseIP("10.0.0.1"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.Now().Location()), + } + var fqdn2 = types.DnsCacheEntry{ + FqdnName: "google.com", + IpAddress: net.ParseIP("10.0.0.2"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.Now().Location()), + } + var fqdn3 = types.DnsCacheEntry{ + FqdnName: "google.com", + IpAddress: net.ParseIP("10.0.0.3"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.Now().Location()), + } + var fqdn4 = types.DnsCacheEntry{ + FqdnName: "example.com", + IpAddress: net.ParseIP("10.0.0.4"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.Now().Location()), + } + var fqdn5 = types.DnsCacheEntry{ + FqdnName: "antrea.io", + IpAddress: net.ParseIP("10.0.0.5"), + ExpirationTime: time.Date(2025, 12, 25, 15, 0, 0, 0, time.Now().Location()), + } + var fqdnList = []types.DnsCacheEntry{fqdn1, fqdn2, fqdn3, fqdn4, fqdn5} + + tests := []struct { + name string + opts map[string]string + fqdnList []types.DnsCacheEntry + expectedResponse interface{} + expectedError string + }{ + { + name: "all", + fqdnList: fqdnList, + expectedResponse: []Response{{&fqdn1}, {&fqdn2}, {&fqdn3}, {&fqdn4}, {&fqdn5}}, + }, + { + name: "only google.com domain name", + opts: map[string]string{ + "domain": "google.com", + }, + fqdnList: fqdnList, + expectedResponse: []Response{{&fqdn1}, {&fqdn2}, {&fqdn3}}, + }, + { + name: "only antrea.io domain name", + opts: map[string]string{ + "domain": "antrea.io", + }, + fqdnList: fqdnList, + expectedResponse: []Response{{&fqdn5}}, + }, + { + name: "domain name that doesn't exist", + opts: map[string]string{ + "domain": "bing.com", + }, + fqdnList: fqdnList, + expectedResponse: []Response{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + reqByte, _ := json.Marshal(tt.fqdnList) + reqReader := bytes.NewReader(reqByte) + result, err := Transform(reqReader, false, tt.opts) + if tt.expectedError == "" { + require.NoError(t, err) + fmt.Printf("expected: %v\nresult: %v\n", tt.expectedResponse.([]Response), result) + if result == "" { + assert.Equal(t, len(tt.expectedResponse.([]Response)), 0) + } else { + assert.Equal(t, len(tt.expectedResponse.([]Response)), len(result.([]Response))) + for i, resp := range tt.expectedResponse.([]Response) { + fmt.Printf("resp: %v\nresult: %v\n", *resp.DnsCacheEntry, *result.([]Response)[i].DnsCacheEntry) + assert.Equal(t, resp.DnsCacheEntry.FqdnName, *&result.([]Response)[i].DnsCacheEntry.FqdnName) + require.True(t, resp.DnsCacheEntry.IpAddress.Equal(result.([]Response)[i].DnsCacheEntry.IpAddress)) + require.True(t, resp.DnsCacheEntry.ExpirationTime.Equal(result.([]Response)[i].DnsCacheEntry.ExpirationTime)) + } + } + // assert.Equal(t, tt.expectedResponse, result) + } else { + assert.ErrorContains(t, err, tt.expectedError) + } + }) + } +} diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index e6e9ec0e78e..8b5ea515286 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -47,6 +47,7 @@ type AgentNetworkPolicyInfoQuerier interface { GetAppliedNetworkPolicies(pod, namespace string, npFilter *NetworkPolicyQueryFilter) []cpv1beta.NetworkPolicy GetNetworkPolicyByRuleFlowID(ruleFlowID uint32) *cpv1beta.NetworkPolicyReference GetRuleByFlowID(ruleFlowID uint32) *types.PolicyRule + GetFqdnCache() []types.DnsCacheEntry } type AgentMulticastInfoQuerier interface { diff --git a/pkg/querier/testing/mock_querier.go b/pkg/querier/testing/mock_querier.go index 59b14b6d757..667442107f8 100644 --- a/pkg/querier/testing/mock_querier.go +++ b/pkg/querier/testing/mock_querier.go @@ -147,6 +147,20 @@ func (mr *MockAgentNetworkPolicyInfoQuerierMockRecorder) GetControllerConnection return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetControllerConnectionStatus", reflect.TypeOf((*MockAgentNetworkPolicyInfoQuerier)(nil).GetControllerConnectionStatus)) } +// GetFqdnCache mocks base method. +func (m *MockAgentNetworkPolicyInfoQuerier) GetFqdnCache() []types.DnsCacheEntry { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetFqdnCache") + ret0, _ := ret[0].([]types.DnsCacheEntry) + return ret0 +} + +// GetFqdnCache indicates an expected call of GetFqdnCache. +func (mr *MockAgentNetworkPolicyInfoQuerierMockRecorder) GetFqdnCache() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetFqdnCache", reflect.TypeOf((*MockAgentNetworkPolicyInfoQuerier)(nil).GetFqdnCache)) +} + // GetNetworkPolicies mocks base method. func (m *MockAgentNetworkPolicyInfoQuerier) GetNetworkPolicies(npFilter *querier.NetworkPolicyQueryFilter) []v1beta2.NetworkPolicy { m.ctrl.T.Helper()