From 3a6ea9c41872ae625eea1f8a15939eb305a8ba90 Mon Sep 17 00:00:00 2001 From: Dyanngg Date: Tue, 18 Jun 2024 11:30:30 -0700 Subject: [PATCH] Add antctl query for policy conjunction ID The antctl query will be available in Antrea agents. It returns the OpenFlow conjunction ID(s) assigned for the queried policy's rules on that specific Node. Note that if a policy does not apply to any Pods on that Node, this antctl command will return a http NotFound since the Antrea agent will not have information of that policy. Signed-off-by: Dyanngg --- pkg/agent/apis/types.go | 7 ++ pkg/agent/apiserver/apiserver.go | 2 + .../handlers/networkpolicy/handler.go | 5 +- .../handlers/policyconjunctions/handler.go | 79 ++++++++++++++ .../policyconjunctions/handler_test.go | 99 +++++++++++++++++ .../networkpolicy/networkpolicy_controller.go | 5 + .../networkpolicy_controller_test.go | 5 + .../networkpolicy/node_reconciler_linux.go | 5 + .../node_reconciler_unsupported.go | 5 + .../networkpolicy/pod_reconciler.go | 27 ++++- .../networkpolicy/pod_reconciler_test.go | 100 +++++++++++++++++- pkg/antctl/antctl.go | 42 +++++++- pkg/antctl/command_list_test.go | 2 +- pkg/querier/querier.go | 1 + pkg/querier/testing/mock_querier.go | 15 +++ 15 files changed, 391 insertions(+), 8 deletions(-) create mode 100644 pkg/agent/apiserver/handlers/policyconjunctions/handler.go create mode 100644 pkg/agent/apiserver/handlers/policyconjunctions/handler_test.go diff --git a/pkg/agent/apis/types.go b/pkg/agent/apis/types.go index 70cd8875b88..aa5d0b7326a 100644 --- a/pkg/agent/apis/types.go +++ b/pkg/agent/apis/types.go @@ -170,6 +170,13 @@ func (r PodInterfaceResponse) SortRows() bool { return true } +// PolicyRuleConjunctionIDsResponse describes the response struct of a policy-rule-conjunction-ids command. +type PolicyRuleConjunctionIDsResponse struct { + RuleName string `json:"name,omitempty"` + Direction string `json:"direction,omitempty"` + ConjunctionIDs []uint32 `json:"conjunctionIDs,omitempty"` +} + // ServiceExternalIPInfo contains the essential information for Services with type of Loadbalancer managed by Antrea. type ServiceExternalIPInfo struct { ServiceName string `json:"serviceName,omitempty" antctl:"name,Name of the Service"` diff --git a/pkg/agent/apiserver/apiserver.go b/pkg/agent/apiserver/apiserver.go index e19c5c6acef..953267b86c4 100644 --- a/pkg/agent/apiserver/apiserver.go +++ b/pkg/agent/apiserver/apiserver.go @@ -42,6 +42,7 @@ import ( "antrea.io/antrea/pkg/agent/apiserver/handlers/ovsflows" "antrea.io/antrea/pkg/agent/apiserver/handlers/ovstracing" "antrea.io/antrea/pkg/agent/apiserver/handlers/podinterface" + "antrea.io/antrea/pkg/agent/apiserver/handlers/policyconjunctions" "antrea.io/antrea/pkg/agent/apiserver/handlers/serviceexternalip" agentquerier "antrea.io/antrea/pkg/agent/querier" systeminstall "antrea.io/antrea/pkg/apis/system/install" @@ -93,6 +94,7 @@ func installHandlers(aq agentquerier.AgentQuerier, npq querier.AgentNetworkPolic s.Handler.NonGoRestfulMux.HandleFunc("/networkpolicies", networkpolicy.HandleFunc(aq)) s.Handler.NonGoRestfulMux.HandleFunc("/appliedtogroups", appliedtogroup.HandleFunc(npq)) s.Handler.NonGoRestfulMux.HandleFunc("/addressgroups", addressgroup.HandleFunc(npq)) + s.Handler.NonGoRestfulMux.HandleFunc("/policyconjunctions", policyconjunctions.HandleFunc(aq)) s.Handler.NonGoRestfulMux.HandleFunc("/ovsflows", ovsflows.HandleFunc(aq)) s.Handler.NonGoRestfulMux.HandleFunc("/ovstracing", ovstracing.HandleFunc(aq)) s.Handler.NonGoRestfulMux.HandleFunc("/serviceexternalip", serviceexternalip.HandleFunc(seipq)) diff --git a/pkg/agent/apiserver/handlers/networkpolicy/handler.go b/pkg/agent/apiserver/handlers/networkpolicy/handler.go index 7490d88d489..bec8fb44486 100644 --- a/pkg/agent/apiserver/handlers/networkpolicy/handler.go +++ b/pkg/agent/apiserver/handlers/networkpolicy/handler.go @@ -59,11 +59,10 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { // From user shorthand input to cpv1beta1.NetworkPolicyType var mapToNetworkPolicyType = map[string]cpv1beta.NetworkPolicyType{ - "NP": cpv1beta.K8sNetworkPolicy, "K8SNP": cpv1beta.K8sNetworkPolicy, "ACNP": cpv1beta.AntreaClusterNetworkPolicy, "ANNP": cpv1beta.AntreaNetworkPolicy, - "ANP": cpv1beta.AntreaNetworkPolicy, + "ANP": cpv1beta.AdminNetworkPolicy, } // Create a Network Policy Filter from URL Query @@ -79,7 +78,7 @@ func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, strSourceType := strings.ToUpper(query.Get("type")) npSourceType, ok := mapToNetworkPolicyType[strSourceType] if strSourceType != "" && !ok { - return nil, "", fmt.Errorf("invalid policy source type. Valid values are K8sNP, ACNP, ANNP and ANP (deprecated)") + return nil, "", fmt.Errorf("invalid policy source type. Valid values are K8sNP, ACNP, ANNP and ANP") } source := query.Get("source") name := query.Get("name") diff --git a/pkg/agent/apiserver/handlers/policyconjunctions/handler.go b/pkg/agent/apiserver/handlers/policyconjunctions/handler.go new file mode 100644 index 00000000000..b0ff13bfe77 --- /dev/null +++ b/pkg/agent/apiserver/handlers/policyconjunctions/handler.go @@ -0,0 +1,79 @@ +// Copyright 2024 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 policyconjunctions + +import ( + "encoding/json" + "net/http" + "strings" + + "k8s.io/apimachinery/pkg/util/sets" + + agentquerier "antrea.io/antrea/pkg/agent/querier" + cpv1beta "antrea.io/antrea/pkg/apis/controlplane/v1beta2" + "antrea.io/antrea/pkg/querier" +) + +// From user shorthand input to cpv1beta1.NetworkPolicyType +var mapToNetworkPolicyType = map[string]cpv1beta.NetworkPolicyType{ + "K8SNP": cpv1beta.K8sNetworkPolicy, + "ACNP": cpv1beta.AntreaClusterNetworkPolicy, + "ANNP": cpv1beta.AntreaNetworkPolicy, + "ANP": cpv1beta.AdminNetworkPolicy, +} + +var clusterScopedResources = sets.New[string]("ACNP", "ANP") + +// HandleFunc creates a http.HandlerFunc which uses an AgentNetworkPolicyInfoQuerier +// to query network policy rules in current agent. +func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + query := r.URL.Query() + uid := query.Get("uid") + npFilter := &querier.NetworkPolicyQueryFilter{Name: uid} + if uid == "" { + if query.Get("source") == "" { + http.Error(w, "policy uid or name must be provided", http.StatusBadRequest) + return + } + policyType := strings.ToUpper(query.Get("type")) + cpType, ok := mapToNetworkPolicyType[policyType] + if !ok { + http.Error(w, "valid policy type must be provided with policy name", http.StatusBadRequest) + return + } + if !clusterScopedResources.Has(policyType) && query.Get("namespace") == "" { + http.Error(w, "policy Namespace must be provided for policy type "+policyType, http.StatusBadRequest) + return + } + npFilter = &querier.NetworkPolicyQueryFilter{ + SourceName: query.Get("source"), + Namespace: query.Get("namespace"), + SourceType: cpType, + } + } + npq := aq.GetNetworkPolicyInfoQuerier() + policies := npq.GetNetworkPolicies(npFilter) + if len(policies) == 0 { + w.WriteHeader(http.StatusNotFound) + return + } + uid = string(policies[0].SourceRef.UID) + realizedRules := npq.GetRealizedRulesByPolicy(uid) + if err := json.NewEncoder(w).Encode(realizedRules); err != nil { + http.Error(w, "Failed to encode response: "+err.Error(), http.StatusInternalServerError) + } + } +} diff --git a/pkg/agent/apiserver/handlers/policyconjunctions/handler_test.go b/pkg/agent/apiserver/handlers/policyconjunctions/handler_test.go new file mode 100644 index 00000000000..7c6bf78c404 --- /dev/null +++ b/pkg/agent/apiserver/handlers/policyconjunctions/handler_test.go @@ -0,0 +1,99 @@ +// Copyright 2024 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 policyconjunctions + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/mock/gomock" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + aqtest "antrea.io/antrea/pkg/agent/querier/testing" + cpv1beta "antrea.io/antrea/pkg/apis/controlplane/v1beta2" + qtest "antrea.io/antrea/pkg/querier/testing" +) + +func TestBadRequests(t *testing.T) { + badRequests := map[string]string{ + "Policy name only": "?source=allow-http", + "No policy type": "?source=allow-http&namespace=ns1", + "No namespace for ANNP": "?source=allow-http&type=ANNP", + "No namespace for K8s NP": "?source=allow-http&type=K8sNP", + } + handler := HandleFunc(nil) + for k, r := range badRequests { + req, err := http.NewRequest(http.MethodGet, r, nil) + assert.Nil(t, err) + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + assert.Equal(t, http.StatusBadRequest, recorder.Code, k) + } +} + +func TestPolicyConjunctionsQuery(t *testing.T) { + c := gomock.NewController(t) + tests := []struct { + name string + query string + policiesReturned []cpv1beta.NetworkPolicy + expectedStatus int + }{ + { + name: "policy found", + query: "?uid=uid1", + policiesReturned: []cpv1beta.NetworkPolicy{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "uid1", + }, + SourceRef: &cpv1beta.NetworkPolicyReference{ + Type: cpv1beta.AntreaClusterNetworkPolicy, + Name: "test-acnp", + UID: "uid1", + }, + }, + }, + expectedStatus: http.StatusOK, + }, + { + name: "policy not found", + query: "?uid=uid2", + policiesReturned: []cpv1beta.NetworkPolicy{}, + expectedStatus: http.StatusNotFound, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + npQuerier := qtest.NewMockAgentNetworkPolicyInfoQuerier(c) + npQuerier.EXPECT().GetNetworkPolicies(gomock.Any()).Return(tt.policiesReturned).Times(1) + if len(tt.policiesReturned) == 1 { + npQuerier.EXPECT().GetRealizedRulesByPolicy(string(tt.policiesReturned[0].SourceRef.UID)).Times(1) + } + aq := aqtest.NewMockAgentQuerier(c) + aq.EXPECT().GetNetworkPolicyInfoQuerier().Return(npQuerier).Times(1) + + handler := HandleFunc(aq) + req, err := http.NewRequest(http.MethodGet, tt.query, nil) + assert.Nil(t, err) + + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + assert.Equal(t, tt.expectedStatus, recorder.Code) + }) + } +} diff --git a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go index b95a8e1ec0f..357c09b3c99 100644 --- a/pkg/agent/controller/networkpolicy/networkpolicy_controller.go +++ b/pkg/agent/controller/networkpolicy/networkpolicy_controller.go @@ -34,6 +34,7 @@ import ( "k8s.io/client-go/util/workqueue" "k8s.io/klog/v2" + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/client" "antrea.io/antrea/pkg/agent/config" "antrea.io/antrea/pkg/agent/controller/networkpolicy/l7engine" @@ -583,6 +584,10 @@ func (c *Controller) GetRuleByFlowID(ruleFlowID uint32) *types.PolicyRule { return rule } +func (c *Controller) GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse { + return c.podReconciler.GetRealizedRulesByPolicy(uid) +} + func (c *Controller) GetControllerConnectionStatus() bool { // When the watchers are connected, controller connection status is true. Otherwise, it is false. return c.addressGroupWatcher.isConnected() && c.appliedToGroupWatcher.isConnected() && c.networkPolicyWatcher.isConnected() diff --git a/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go b/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go index 50b2fc3a16c..976b0d6db7c 100644 --- a/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go +++ b/pkg/agent/controller/networkpolicy/networkpolicy_controller_test.go @@ -36,6 +36,7 @@ import ( k8stesting "k8s.io/client-go/testing" "k8s.io/component-base/metrics/legacyregistry" + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/config" "antrea.io/antrea/pkg/agent/controller/networkpolicy/l7engine" "antrea.io/antrea/pkg/agent/metrics" @@ -169,6 +170,10 @@ func (r *mockReconciler) GetRuleByFlowID(_ uint32) (*agenttypes.PolicyRule, bool return nil, false, nil } +func (r *mockReconciler) GetRealizedRulesByPolicy(_ string) []apis.PolicyRuleConjunctionIDsResponse { + return nil +} + func (r *mockReconciler) getLastRealized(ruleID string) (*CompletedRule, bool) { r.Lock() defer r.Unlock() diff --git a/pkg/agent/controller/networkpolicy/node_reconciler_linux.go b/pkg/agent/controller/networkpolicy/node_reconciler_linux.go index f3c2d3982ee..c36a50e6c36 100644 --- a/pkg/agent/controller/networkpolicy/node_reconciler_linux.go +++ b/pkg/agent/controller/networkpolicy/node_reconciler_linux.go @@ -28,6 +28,7 @@ import ( "k8s.io/klog/v2" utilnet "k8s.io/utils/net" + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/config" "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" @@ -320,6 +321,10 @@ func (r *nodeReconciler) GetRuleByFlowID(ruleFlowID uint32) (*types.PolicyRule, return nil, false, nil } +func (r *nodeReconciler) GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse { + return nil +} + func (r *nodeReconciler) computeIPTRules(rule *CompletedRule) (map[iptables.Protocol]*types.NodePolicyRule, *nodePolicyLastRealized) { ruleID := rule.ID lastRealized := newNodePolicyLastRealized() diff --git a/pkg/agent/controller/networkpolicy/node_reconciler_unsupported.go b/pkg/agent/controller/networkpolicy/node_reconciler_unsupported.go index deac59eeb57..02ace7df430 100644 --- a/pkg/agent/controller/networkpolicy/node_reconciler_unsupported.go +++ b/pkg/agent/controller/networkpolicy/node_reconciler_unsupported.go @@ -18,6 +18,7 @@ package networkpolicy import ( + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/route" "antrea.io/antrea/pkg/agent/types" ) @@ -44,6 +45,10 @@ func (r *nodeReconciler) GetRuleByFlowID(ruleID uint32) (*types.PolicyRule, bool return nil, false, nil } +func (r *nodeReconciler) GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse { + return nil +} + func (r *nodeReconciler) RunIDAllocatorWorker(stopCh <-chan struct{}) { } diff --git a/pkg/agent/controller/networkpolicy/pod_reconciler.go b/pkg/agent/controller/networkpolicy/pod_reconciler.go index 1bf64e4a84e..060279df6f5 100644 --- a/pkg/agent/controller/networkpolicy/pod_reconciler.go +++ b/pkg/agent/controller/networkpolicy/pod_reconciler.go @@ -26,6 +26,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/interfacestore" "antrea.io/antrea/pkg/agent/openflow" proxytypes "antrea.io/antrea/pkg/agent/proxy/types" @@ -69,6 +70,9 @@ type Reconciler interface { // GetRuleByFlowID returns the rule from the async rule cache in idAllocator cache. GetRuleByFlowID(ruleID uint32) (*types.PolicyRule, bool, error) + // GetRealizedRulesByPolicy returns the conjunction info of the queried policy from the lastRealized cache. + GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse + // RunIDAllocatorWorker runs the worker that deletes the rules from the cache // in idAllocator. RunIDAllocatorWorker(stopCh <-chan struct{}) @@ -162,7 +166,7 @@ type podPolicyLastRealized struct { // It's same in all Openflow rules, because named port is only for // destination Pods. podIPs sets.Set[string] - // fqdnIPaddresses tracks the last realized set of IP addresses resolved for + // fqdnIPAddresses tracks the last realized set of IP addresses resolved for // the fqdn selector of this policy rule. It must be empty for policy rule // that is not egress and does not have toFQDN field. fqdnIPAddresses sets.Set[string] @@ -1046,6 +1050,27 @@ func (r *podReconciler) GetRuleByFlowID(ruleFlowID uint32) (*types.PolicyRule, b return r.idAllocator.getRuleFromAsyncCache(ruleFlowID) } +func (r *podReconciler) GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse { + var responses []apis.PolicyRuleConjunctionIDsResponse + r.lastRealizeds.Range(func(k, v interface{}) bool { + r := v.(*podPolicyLastRealized) + if string(r.SourceRef.UID) == uid { + resp := apis.PolicyRuleConjunctionIDsResponse{ + RuleName: r.Name, + Direction: string(r.Direction), + } + ofIDs := make([]uint32, 0, len(r.ofIDs)) + for _, ofID := range r.ofIDs { + ofIDs = append(ofIDs, ofID) + } + resp.ConjunctionIDs = ofIDs + responses = append(responses, resp) + } + return true + }) + return responses +} + func (r *podReconciler) getOFPorts(members v1beta2.GroupMemberSet) sets.Set[int32] { ofPorts := sets.New[int32]() for _, m := range members { diff --git a/pkg/agent/controller/networkpolicy/pod_reconciler_test.go b/pkg/agent/controller/networkpolicy/pod_reconciler_test.go index ec31137a918..d325cc1ca15 100644 --- a/pkg/agent/controller/networkpolicy/pod_reconciler_test.go +++ b/pkg/agent/controller/networkpolicy/pod_reconciler_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/sets" + "antrea.io/antrea/pkg/agent/apis" "antrea.io/antrea/pkg/agent/interfacestore" "antrea.io/antrea/pkg/agent/openflow" openflowtest "antrea.io/antrea/pkg/agent/openflow/testing" @@ -78,6 +79,8 @@ var ( servicesKey1 = normalizeServices(services1) services2 = []v1beta2.Service{serviceTCP} servicesKey2 = normalizeServices(services2) + services3 = []v1beta2.Service{serviceTCP443} + servicesKey3 = normalizeServices(services3) policyPriority = float64(1) tierPriority = int32(1) @@ -91,12 +94,12 @@ var ( cnp1 = v1beta2.NetworkPolicyReference{ Type: v1beta2.AntreaClusterNetworkPolicy, Name: "name1", - UID: "uid1", + UID: "uid2", } anp1 = v1beta2.NetworkPolicyReference{ Type: v1beta2.AdminNetworkPolicy, Name: "anp1", - UID: "uid2", + UID: "uid3", } transientError = errors.New("Transient OVS error") @@ -622,6 +625,99 @@ func TestReconcilerReconcile(t *testing.T) { } } +func TestGetRealizedRulesByPolicy(t *testing.T) { + ifaceStore := interfacestore.NewInterfaceStore() + tests := []struct { + name string + lastRealizeds map[string]*podPolicyLastRealized + args string + expectedResp []apis.PolicyRuleConjunctionIDsResponse + }{ + { + name: "single-rule", + lastRealizeds: map[string]*podPolicyLastRealized{ + "foo": { + ofIDs: map[servicesKey]uint32{servicesKey1: 8}, + CompletedRule: &CompletedRule{ + rule: &rule{Direction: v1beta2.DirectionIn, SourceRef: &np1}, + }, + }, + }, + args: "uid1", + expectedResp: []apis.PolicyRuleConjunctionIDsResponse{ + { + Direction: "In", + ConjunctionIDs: []uint32{8}, + }, + }, + }, + { + name: "multiple-policies", + lastRealizeds: map[string]*podPolicyLastRealized{ + "foo": { + ofIDs: map[servicesKey]uint32{servicesKey1: 8}, + CompletedRule: &CompletedRule{ + rule: &rule{Direction: v1beta2.DirectionIn, SourceRef: &np1}, + }, + }, + "bar": { + ofIDs: map[servicesKey]uint32{servicesKey2: 9}, + CompletedRule: &CompletedRule{ + rule: &rule{Direction: v1beta2.DirectionIn, SourceRef: &cnp1}, + }, + }, + }, + args: "uid2", + expectedResp: []apis.PolicyRuleConjunctionIDsResponse{ + { + Direction: "In", + ConjunctionIDs: []uint32{9}, + }, + }, + }, + { + name: "multiple-rules", + lastRealizeds: map[string]*podPolicyLastRealized{ + "foo": { + ofIDs: map[servicesKey]uint32{servicesKey1: 8, servicesKey2: 9}, + CompletedRule: &CompletedRule{ + rule: &rule{Direction: v1beta2.DirectionIn, SourceRef: &np1}, + }, + }, + "bar": { + ofIDs: map[servicesKey]uint32{servicesKey3: 10}, + CompletedRule: &CompletedRule{ + rule: &rule{Direction: v1beta2.DirectionOut, SourceRef: &np1}, + }, + }, + }, + args: "uid1", + expectedResp: []apis.PolicyRuleConjunctionIDsResponse{ + { + Direction: "In", + ConjunctionIDs: []uint32{8, 9}, + }, + { + Direction: "Out", + ConjunctionIDs: []uint32{10}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + controller := gomock.NewController(t) + mockOFClient := openflowtest.NewMockClient(controller) + r := newTestReconciler(t, controller, ifaceStore, mockOFClient, true, false) + for key, value := range tt.lastRealizeds { + r.lastRealizeds.Store(key, value) + } + resps := r.GetRealizedRulesByPolicy(tt.args) + assert.ElementsMatch(t, tt.expectedResp, resps) + }) + } +} + func TestReconcilerReconcileServiceRelatedRule(t *testing.T) { ifaceStore := interfacestore.NewInterfaceStore() ifaceStore.AddInterface(&interfacestore.InterfaceConfig{ diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 946dbb3d53e..3c3260fa1ea 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -216,7 +216,7 @@ $ antctl get podmulticaststats pod -n namespace`, }, { name: "type", - usage: "Get NetworkPolicies with specific type. Type means the type of its source NetworkPolicy: K8sNP, ACNP, ANNP", + usage: "Get NetworkPolicies with specific type. Type means the type of its source NetworkPolicy: K8sNP, ACNP, ANNP or ANP", shorthand: "T", }, }, getSortByFlag()), @@ -357,6 +357,46 @@ $ antctl get podmulticaststats pod -n namespace`, commandGroup: get, transformedResponse: reflect.TypeOf(agentapis.PodInterfaceResponse{}), }, + { + use: "policyconjunctions", + aliases: []string{"policyconj"}, + short: "Print conjunction IDs assigned to policy rules", + long: "Print all the conjunction IDs assigned by the Antrea agent for the specified policy or rule", + example: ` Get conjunction IDs assigned to a policy by UID + $ antctl get policyconjunctions 6001549b-ba63-4752-8267-30f52b4332db + Get conjunction IDs assigned to a policy by name, namespace and type. For cluster scope resources, namespace can be omitted. + $ antctl get policyconjunctions -S allow-http -n test -T annp`, + agentEndpoint: &endpoint{ + nonResourceEndpoint: &nonResourceEndpoint{ + path: "/policyconjunctions", + params: []flagInfo{ + { + name: "uid", + usage: "Retrieve Conjunction IDs by policy UID.", + arg: true, + }, + { + name: "source", + usage: "Retrieve Conjunction IDs by source policy name. If present, source type must be provided. Namespace must also be provided if the source policy is Namespace scoped.", + shorthand: "S", + }, + { + name: "namespace", + usage: "Namespace of the source policy being queried. Required if the source policy is Namespace scoped.", + shorthand: "n", + }, + { + name: "type", + usage: "Type of the source policy being queried. Valid values are K8sNP, ACNP, ANNP and ANP.", + shorthand: "T", + }, + }, + outputType: multiple, + }, + }, + commandGroup: get, + transformedResponse: reflect.TypeOf(agentapis.PolicyRuleConjunctionIDsResponse{}), + }, { use: "ovsflows", aliases: []string{"of"}, diff --git a/pkg/antctl/command_list_test.go b/pkg/antctl/command_list_test.go index 8a87fc666a3..23357af3799 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"}, {"supportbundle"}, {"traceflow"}, {"get", "featuregates"}}, + expected: [][]string{{"version"}, {"get", "podmulticaststats"}, {"log-level"}, {"get", "networkpolicy"}, {"get", "appliedtogroup"}, {"get", "addressgroup"}, {"get", "agentinfo"}, {"get", "podinterface"}, {"get", "policyconjunctions"}, {"get", "ovsflows"}, {"trace-packet"}, {"get", "serviceexternalip"}, {"get", "memberlist"}, {"supportbundle"}, {"traceflow"}, {"get", "featuregates"}}, }, { name: "Antctl running against flow-aggregator mode", diff --git a/pkg/querier/querier.go b/pkg/querier/querier.go index 1ebf4fa0f06..1f24f96d634 100644 --- a/pkg/querier/querier.go +++ b/pkg/querier/querier.go @@ -42,6 +42,7 @@ type AgentNetworkPolicyInfoQuerier interface { GetAppliedNetworkPolicies(pod, namespace string, npFilter *NetworkPolicyQueryFilter) []cpv1beta.NetworkPolicy GetNetworkPolicyByRuleFlowID(ruleFlowID uint32) *cpv1beta.NetworkPolicyReference GetRuleByFlowID(ruleFlowID uint32) *types.PolicyRule + GetRealizedRulesByPolicy(uid string) []apis.PolicyRuleConjunctionIDsResponse } type AgentMulticastInfoQuerier interface { diff --git a/pkg/querier/testing/mock_querier.go b/pkg/querier/testing/mock_querier.go index 03541e73b4a..2c59d7df3a2 100644 --- a/pkg/querier/testing/mock_querier.go +++ b/pkg/querier/testing/mock_querier.go @@ -26,6 +26,7 @@ package testing import ( reflect "reflect" + apis "antrea.io/antrea/pkg/agent/apis" interfacestore "antrea.io/antrea/pkg/agent/interfacestore" multicast "antrea.io/antrea/pkg/agent/multicast" types "antrea.io/antrea/pkg/agent/types" @@ -184,6 +185,20 @@ func (mr *MockAgentNetworkPolicyInfoQuerierMockRecorder) GetNetworkPolicyNum() * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNetworkPolicyNum", reflect.TypeOf((*MockAgentNetworkPolicyInfoQuerier)(nil).GetNetworkPolicyNum)) } +// GetRealizedRulesByPolicy mocks base method. +func (m *MockAgentNetworkPolicyInfoQuerier) GetRealizedRulesByPolicy(arg0 string) []apis.PolicyRuleConjunctionIDsResponse { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetRealizedRulesByPolicy", arg0) + ret0, _ := ret[0].([]apis.PolicyRuleConjunctionIDsResponse) + return ret0 +} + +// GetRealizedRulesByPolicy indicates an expected call of GetRealizedRulesByPolicy. +func (mr *MockAgentNetworkPolicyInfoQuerierMockRecorder) GetRealizedRulesByPolicy(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetRealizedRulesByPolicy", reflect.TypeOf((*MockAgentNetworkPolicyInfoQuerier)(nil).GetRealizedRulesByPolicy), arg0) +} + // GetRuleByFlowID mocks base method. func (m *MockAgentNetworkPolicyInfoQuerier) GetRuleByFlowID(arg0 uint32) *types.PolicyRule { m.ctrl.T.Helper()