diff --git a/pkg/agent/apiserver/handlers/networkpolicy/handler.go b/pkg/agent/apiserver/handlers/networkpolicy/handler.go index 8d7748064c7..0ef19448395 100644 --- a/pkg/agent/apiserver/handlers/networkpolicy/handler.go +++ b/pkg/agent/apiserver/handlers/networkpolicy/handler.go @@ -19,6 +19,7 @@ import ( "fmt" "net/http" "net/url" + "sort" "strings" agentquerier "github.com/vmware-tanzu/antrea/pkg/agent/querier" @@ -30,7 +31,7 @@ import ( // to query network policy rules in current agent. func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - npFilter, err := newFilterFromURLQuery(r.URL.Query()) + npFilter, err := parseURLQuery(r.URL.Query()) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return @@ -48,8 +49,12 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { } else { nps = npq.GetNetworkPolicies(npFilter) } - - obj = cpv1beta.NetworkPolicyList{Items: nps} + npSorter := &NPSorter{ + networkPolicies: nps, + sortBy: r.URL.Query().Get("sort-by"), + } + sort.Sort(sort.Reverse(npSorter)) + obj = cpv1beta.NetworkPolicyList{Items: npSorter.networkPolicies} if err := json.NewEncoder(w).Encode(obj); err != nil { http.Error(w, "Failed to encode response: "+err.Error(), http.StatusInternalServerError) @@ -57,6 +62,55 @@ func HandleFunc(aq agentquerier.AgentQuerier) http.HandlerFunc { } } +var ( + sortByEffectivePriority = "effectivePriority" + // TODO: this is a hack, but works for now. 251 is a tierPriority value in + // between the application tier and the baseline tier, which can be used + // to sort policies by tier. + effectiveTierPriorityK8sNP = int32(251) +) + +type NPSorter struct { + networkPolicies []cpv1beta.NetworkPolicy + sortBy string +} + +func (nps *NPSorter) Len() int { return len(nps.networkPolicies) } +func (nps *NPSorter) Swap(i, j int) { + nps.networkPolicies[i], nps.networkPolicies[j] = nps.networkPolicies[j], nps.networkPolicies[i] +} +func (nps *NPSorter) Less(i, j int) bool { + var ti, tj int32 + if nps.networkPolicies[i].TierPriority == nil { + ti = effectiveTierPriorityK8sNP + } else { + ti = *nps.networkPolicies[i].TierPriority + } + if nps.networkPolicies[j].TierPriority == nil { + tj = effectiveTierPriorityK8sNP + } else { + tj = *nps.networkPolicies[j].TierPriority + } + pi, pj := nps.networkPolicies[i].Priority, nps.networkPolicies[j].Priority + switch nps.sortBy { + case sortByEffectivePriority: + if ti == tj { + if (pi == nil && pj == nil) || (pi != nil && pj != nil && *pi == *pj) { + return nps.networkPolicies[i].Name > nps.networkPolicies[j].Name + } + // If two NetworkPolicies are in the same tier and have different priorities + // (K8s NetworkPolicy will not apply since pi == pj == nil), the priorities + // of those NetworkPolicies must not be nil. + return *pi > *pj + } + return ti > tj + default: + // Do not need a tie-breaker here since NetworkPolicy names are set as UID + // of the source policy and will be unique. + return nps.networkPolicies[i].Name > nps.networkPolicies[j].Name + } +} + // From user shorthand input to cpv1beta1.NetworkPolicyType var mapToNetworkPolicyType = map[string]cpv1beta.NetworkPolicyType{ "NP": cpv1beta.K8sNetworkPolicy, @@ -66,7 +120,7 @@ var mapToNetworkPolicyType = map[string]cpv1beta.NetworkPolicyType{ } // Create a Network Policy Filter from URL Query -func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, error) { +func parseURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, error) { namespace := query.Get("namespace") pod := query.Get("pod") if pod != "" && namespace == "" { @@ -80,12 +134,15 @@ func newFilterFromURLQuery(query url.Values) (*querier.NetworkPolicyQueryFilter, } source := query.Get("source") - name := query.Get("name") if name != "" && (source != "" || namespace != "" || pod != "" || strSourceType != "") { return nil, fmt.Errorf("with a name, none of the other fields can be set") } + sortBy := query.Get("sort-by") + if sortBy != "" && sortBy != sortByEffectivePriority { + return nil, fmt.Errorf("unsupported sort-by option. Supported value is %s", sortByEffectivePriority) + } return &querier.NetworkPolicyQueryFilter{ Name: name, SourceName: source, diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 3c363f32327..b644e56a913 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -109,6 +109,8 @@ var CommandList = &commandList{ $ antctl get networkpolicy 6001549b-ba63-4752-8267-30f52b4332db Get the list of all control plane NetworkPolicies $ antctl get networkpolicy + Get the list of all control plane NetworkPolicies, sorted by effective priority (the order in which the policies are evaluated) + $ antctl get networkpolicy --sort-by=effectivePriority Get the control plane NetworkPolicy with a specific source (supported by agent only) $ antctl get networkpolicy -S allow-http -n ns1 Get the list of control plane NetworkPolicies whose source NetworkPolicies are in a Namespace (supported by agent only) @@ -153,6 +155,11 @@ var CommandList = &commandList{ usage: "Get NetworkPolicies with specific type. Type means the type of its source network policy: K8sNP, ACNP, ANP", shorthand: "T", }, + { + name: "sort-by", + usage: "Get NetworkPolicies by specific order. Current supported value is effectivePriority. If not specified, results are sorted by name by default.", + shorthand: "O", + }, }, outputType: multiple, }, diff --git a/pkg/antctl/command_definition_test.go b/pkg/antctl/command_definition_test.go index 35184d0f614..c7b88694bac 100644 --- a/pkg/antctl/command_definition_test.go +++ b/pkg/antctl/command_definition_test.go @@ -192,9 +192,9 @@ foo2 }, }, }, - expected: `NAME APPLIED-TO RULES SOURCE -6001549b-ba63-4752-8267-30f52b4332db 32ef631b-6817-5a18-86eb-93f4abf0467c + 1 more... 1 K8sNetworkPolicy:default/allow-all -880db7e8-fc2a-4030-aefe-09afc5f341ad 32ef631b-6817-5a18-86eb-93f4abf0467c 2 AntreaNetworkPolicy:default/allow-all + expected: `NAME APPLIED-TO RULES SOURCE TIER-PRIORITY PRIORITY +6001549b-ba63-4752-8267-30f52b4332db 32ef631b-6817-5a18-86eb-93f4abf0467c + 1 more... 1 K8sNetworkPolicy:default/allow-all +880db7e8-fc2a-4030-aefe-09afc5f341ad 32ef631b-6817-5a18-86eb-93f4abf0467c 2 AntreaNetworkPolicy:default/allow-all `, }, { diff --git a/pkg/antctl/transform/networkpolicy/transform.go b/pkg/antctl/transform/networkpolicy/transform.go index d2cb671a249..4519199d293 100644 --- a/pkg/antctl/transform/networkpolicy/transform.go +++ b/pkg/antctl/transform/networkpolicy/transform.go @@ -51,16 +51,31 @@ func Transform(reader io.Reader, single bool) (interface{}, error) { )(reader, single) } +func priorityToString(p interface{}) string { + if reflect.ValueOf(p).IsNil() { + return "" + } else if pInt32, ok := p.(*int32); ok { + return strconv.Itoa(int(*pInt32)) + } else { + pFloat64, _ := p.(*float64) + return strconv.FormatFloat(*pFloat64, 'f', -1, 64) + } +} + var _ common.TableOutput = new(Response) func (r Response) GetTableHeader() []string { - return []string{"NAME", "APPLIED-TO", "RULES", "SOURCE"} + return []string{"NAME", "APPLIED-TO", "RULES", "SOURCE", "TIER-PRIORITY", "PRIORITY"} } func (r Response) GetTableRow(maxColumnLength int) []string { - return []string{r.Name, common.GenerateTableElementWithSummary(r.AppliedToGroups, maxColumnLength), strconv.Itoa(len(r.Rules)), r.SourceRef.ToString()} + return []string{ + r.Name, common.GenerateTableElementWithSummary(r.AppliedToGroups, maxColumnLength), + strconv.Itoa(len(r.Rules)), r.SourceRef.ToString(), + priorityToString(r.TierPriority), priorityToString(r.Priority), + } } func (r Response) SortRows() bool { - return true + return false }