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()