Просмотр исходного кода

Network Insights to OpenCost Core (#3080)

Signed-off-by: Matt Bolt <mbolt35@gmail.com>
Matt Bolt 1 год назад
Родитель
Сommit
ae93ff32d7

+ 17 - 0
core/pkg/filter/networkinsight/fields.go

@@ -0,0 +1,17 @@
+package networkinsight
+
+type NetworkInsightField string
+type NetworkInsightDetailField string
+
+// Only support namespace, pod and cluster on day 1
+// Field used for Network Insight filtering
+const (
+	FieldClusterID NetworkInsightField = "cluster"
+	FieldNamespace NetworkInsightField = "namespace"
+	FieldPod       NetworkInsightField = "pod"
+)
+
+// Field used for Network Insight Details filtering
+const (
+	FieldEndPoint NetworkInsightDetailField = "endPoint"
+)

+ 76 - 0
core/pkg/filter/networkinsight/parser.go

@@ -0,0 +1,76 @@
+package networkinsight
+
+import (
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/ops"
+)
+
+// ast filter field map for network insights
+var networkInsightFilterFields []*ast.Field = []*ast.Field{
+	ast.NewField(FieldClusterID),
+	ast.NewField(FieldNamespace),
+	ast.NewField(FieldPod),
+}
+
+var fieldMap map[NetworkInsightField]*ast.Field
+
+func init() {
+	fieldMap = make(map[NetworkInsightField]*ast.Field, len(networkInsightFilterFields))
+	for _, f := range networkInsightFilterFields {
+		ff := *f
+		fieldMap[NetworkInsightField(ff.Name)] = &ff
+	}
+}
+
+// DefaultFieldByName returns only default network insight filter fields by name.
+func DefaultFieldByName(field NetworkInsightField) *ast.Field {
+	if af, ok := fieldMap[field]; ok {
+		afcopy := *af
+		return &afcopy
+	}
+
+	return nil
+}
+
+// NewNetworkInsightFilterParser creates a new `ast.FilterParser` implementation
+// which uses network insight specific fields
+func NewNetworkInsightFilterParser() ast.FilterParser {
+	return ast.NewFilterParser(networkInsightFilterFields)
+}
+
+var networkInsightDetailFilterFields []*ast.Field = []*ast.Field{
+	ast.NewField(FieldEndPoint),
+}
+
+var networkInsightDetailFieldMap map[NetworkInsightDetailField]*ast.Field
+
+// DefaultFieldByName returns only default network insight filter fields by name.
+func DefaultNetworkInsightDetailFieldByName(field NetworkInsightDetailField) *ast.Field {
+	if networkInsightDetailFieldMap == nil {
+		networkInsightDetailFieldMap = make(map[NetworkInsightDetailField]*ast.Field, len(networkInsightDetailFilterFields))
+		for _, f := range networkInsightDetailFilterFields {
+			ff := *f
+			networkInsightDetailFieldMap[NetworkInsightDetailField(ff.Name)] = &ff
+		}
+	}
+
+	if af, ok := networkInsightDetailFieldMap[field]; ok {
+		afcopy := *af
+		return &afcopy
+	}
+
+	return nil
+}
+
+// NewNetworkInsightDetailFilterParser creates a new `ast.FilterParser` implementation
+// which uses network insight details specific fields
+func NewNetworkInsightDetailFilterParser() ast.FilterParser {
+	return ast.NewFilterParser(networkInsightDetailFilterFields)
+}
+
+// use initialization function to assign field types to the ops package helper for programatically
+// building v2.1 filters
+func init() {
+	ops.RegisterDefaultFieldLookup[NetworkInsightField](DefaultFieldByName)
+	ops.RegisterDefaultFieldLookup[NetworkInsightDetailField](DefaultNetworkInsightDetailFieldByName)
+}

+ 9 - 0
core/pkg/opencost/bingen.go

@@ -70,4 +70,13 @@ package opencost
 // @bingen:generate:CloudCostLabels
 // @bingen:end
 
+// @bingen:set[name=NetworkInsight,version=1]
+// @bingen:generate:NetworkInsightSet
+// @bingen:generate:NetworkInsight
+// @bingen:generate:NetworkTrafficDirection
+// @bingen:generate:NetworkTrafficType
+// @bingen:generate:NetworkDetail
+// @bingen:generate:NetworkDetailsSet
+// @bingen:end
+
 //go:generate bingen -package=opencost -version=18 -buffer=github.com/opencost/opencost/core/pkg/util

+ 102 - 0
core/pkg/opencost/mock.go

@@ -828,3 +828,105 @@ func NewMockUnitSummaryAllocationSet(start time.Time, resolution time.Duration)
 
 	return sas
 }
+
+const (
+	mockCloudService1    = "MockCloudService1"
+	mockCloudService2    = "MockCloudService2"
+	mockCloudService3    = "MockCloudService3"
+	mockCloudServiceName = "MockCloudService"
+)
+
+type MockNetworkInsightImportantKeys struct {
+	Cluster    string
+	Namespace  string
+	Controller string
+	Pod        string
+	Node       string
+	Labels     map[string]string
+	Region     string
+	Zone       string
+}
+
+func createMockNetworkDetail(cost, bytes float64, endPoint string, trafficDirection NetworkTrafficDirection, trafficType NetworkTrafficType) (*NetworkDetail, error) {
+	return &NetworkDetail{
+		Cost:             cost,
+		Bytes:            bytes,
+		EndPoint:         endPoint,
+		TrafficDirection: trafficDirection,
+		TrafficType:      trafficType,
+	}, nil
+}
+
+func createMockNetworkInsight(mockInsight MockNetworkInsightImportantKeys, detailsSet NetworkDetailsSet) *NetworkInsight {
+	// Ingress isnt charges at the moment
+	internetCost := detailsSet.GetTotalInternetCost()
+	crossZoneCost := detailsSet.GetCrossZoneCost()
+	crossRegionCost := detailsSet.GetCrossRegionCost()
+	totalCost := internetCost + crossZoneCost + crossRegionCost
+	return NewNetworkInsight(
+		mockInsight.Cluster, mockInsight.Namespace, mockInsight.Controller,
+		mockInsight.Pod, mockInsight.Node, mockInsight.Labels, mockInsight.Region,
+		mockInsight.Zone, totalCost, crossZoneCost, crossRegionCost, internetCost, detailsSet)
+}
+
+func GenerateMockNetworkInsightSet(start time.Time, end time.Time) *NetworkInsightSet {
+	mockInsight1 := MockNetworkInsightImportantKeys{
+		Cluster:    "mockCluster1",
+		Namespace:  "mockNamespace1",
+		Controller: "",
+		Pod:        "mockPod1",
+		Node:       "",
+		Labels:     map[string]string{},
+		Region:     "",
+		Zone:       "",
+	}
+
+	mockInsight2 := MockNetworkInsightImportantKeys{
+		Cluster:    "mockCluster1",
+		Namespace:  "mockNamespace2",
+		Controller: "",
+		Pod:        "mockPod2",
+		Node:       "",
+		Labels:     map[string]string{},
+		Region:     "",
+		Zone:       "",
+	}
+
+	mcs1inIngress, _ := createMockNetworkDetail(0, 200000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs2inIngress, _ := createMockNetworkDetail(0, 400000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs3inIngress, _ := createMockNetworkDetail(0, 800000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs1inEgress, _ := createMockNetworkDetail(0.16, 300000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs2inEgress, _ := createMockNetworkDetail(0.24, 300000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs3inEgress, _ := createMockNetworkDetail(0.35, 300000, mockCloudService3, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	set1 := make(NetworkDetailsSet, 0)
+	set1[mcs1inIngress.Key()] = mcs1inIngress
+	set1[mcs2inIngress.Key()] = mcs2inIngress
+	set1[mcs3inIngress.Key()] = mcs3inIngress
+	set1[mcs1inEgress.Key()] = mcs1inEgress
+	set1[mcs2inEgress.Key()] = mcs2inEgress
+	set1[mcs3inEgress.Key()] = mcs3inEgress
+
+	set2 := make(NetworkDetailsSet, 0)
+	set2[mcs1inIngress.Key()] = mcs1inIngress.Clone()
+	set2[mcs2inIngress.Key()] = mcs2inIngress.Clone()
+	set2[mcs3inIngress.Key()] = mcs3inIngress.Clone()
+	set2[mcs1inEgress.Key()] = mcs1inEgress.Clone()
+	set2[mcs2inEgress.Key()] = mcs2inEgress.Clone()
+	set2[mcs3inEgress.Key()] = mcs3inEgress.Clone()
+
+	ni1 := createMockNetworkInsight(mockInsight1, set1)
+	ni2 := createMockNetworkInsight(mockInsight1, set2)
+	ni3 := createMockNetworkInsight(mockInsight2, set1.Clone())
+
+	nis := &NetworkInsightSet{
+		NetworkInsights: make(map[string]*NetworkInsight, 0),
+		Window:          NewClosedWindow(start, end),
+	}
+
+	nis.Insert(ni1, []NetworkInsightProperty{NetworkInsightsPod})
+	nis.Insert(ni2, []NetworkInsightProperty{NetworkInsightsPod})
+	nis.Insert(ni3, []NetworkInsightProperty{NetworkInsightsPod})
+
+	return nis
+}

+ 1025 - 0
core/pkg/opencost/networkinsight.go

@@ -0,0 +1,1025 @@
+package opencost
+
+import (
+	"fmt"
+	"math"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/opencost/opencost/core/pkg/filter"
+	"github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	"github.com/opencost/opencost/core/pkg/log"
+	"github.com/opencost/opencost/core/pkg/util/timeutil"
+)
+
+const (
+	NetworkInsightsServiceUnknown = "unknownService"
+	NetworkInsightsNamespace      = "namespace"
+	NetworkInsightsCluster        = "cluster"
+	NetworkInsightsPod            = "pod"
+)
+
+type NetworkInsightProperty string
+
+func ConvertNetworkInsightPropertiesToString(nips []NetworkInsightProperty) string {
+	aggString := make([]string, len(nips))
+	for i, agg := range nips {
+		aggString[i] = string(agg)
+	}
+	return strings.Join(aggString, "/")
+}
+
+// Alias for network traffic direction string
+type NetworkTrafficDirection string
+
+const (
+	NetworkTrafficDirectionNone    NetworkTrafficDirection = ""
+	NetworkTrafficDirectionEgress  NetworkTrafficDirection = "Egress"
+	NetworkTrafficDirectionIngress NetworkTrafficDirection = "Ingress"
+)
+
+// Alias for network traffic type string
+type NetworkTrafficType string
+
+const (
+	NetworkTrafficTypeNone        NetworkTrafficType = ""
+	NetworkTrafficTypeCrossZone   NetworkTrafficType = "CrossZone"
+	NetworkTrafficTypeCrossRegion NetworkTrafficType = "CrossRegion"
+	NetworkTrafficTypeInternet    NetworkTrafficType = "Internet"
+)
+
+// Struct to store the filter options applied on network
+// interaction in networkDetails of network insight
+type NetworkDetailsOptions struct {
+	ShowZeroCost         bool
+	FilterNetworkDetails filter.Filter
+}
+
+// struct to lowest level Ingress and Egress details, interaction
+// with the  endPoint, which is a source in case of Ingress and
+// destination in case of Egress and also stores Traffic type property,
+// which describes the traffic as either Internet, Cross Region or Cross Zone
+type NetworkDetail struct {
+	Cost             float64                 `json:"cost"`
+	Bytes            float64                 `json:"bytes"`
+	EndPoint         string                  `json:"endPoint"`
+	TrafficDirection NetworkTrafficDirection `json:"trafficDirection"`
+	TrafficType      NetworkTrafficType      `json:"trafficType"`
+}
+
+func NewNetworkDetail(cost float64,
+	bytes float64, endPoint string,
+	trafficDirection NetworkTrafficDirection, trafficType NetworkTrafficType) *NetworkDetail {
+	return &NetworkDetail{
+		Cost:             cost,
+		Bytes:            bytes,
+		EndPoint:         endPoint,
+		TrafficDirection: trafficDirection,
+		TrafficType:      trafficType,
+	}
+}
+
+func (nd *NetworkDetail) Clone() *NetworkDetail {
+	return &NetworkDetail{
+		Cost:             nd.Cost,
+		Bytes:            nd.Bytes,
+		EndPoint:         nd.EndPoint,
+		TrafficDirection: nd.TrafficDirection,
+		TrafficType:      nd.TrafficType,
+	}
+}
+
+func (nd *NetworkDetail) Key() string {
+	if nd == nil {
+		return ""
+	}
+	return fmt.Sprintf("%s/%s/%s", nd.EndPoint, nd.TrafficDirection, nd.TrafficType)
+}
+
+func (nd *NetworkDetail) Add(that *NetworkDetail) {
+	if nd == nil {
+		return
+	}
+
+	if nd.Key() != that.Key() {
+		log.Warnf("adding two NetworkDetail that dont match with key %s: %s", nd.Key(), that.Key())
+	}
+	nd.Cost += that.Cost
+	nd.Bytes += that.Bytes
+}
+
+func (nd *NetworkDetail) SanitizeNaN() {
+	if math.IsNaN(nd.Cost) {
+		log.DedupedWarningf(5, "NetworkDetail: Unexpected NaN found for Cost: name: %s", nd.Key())
+		nd.Cost = 0
+	}
+
+	if math.IsNaN(nd.Bytes) {
+		log.DedupedWarningf(5, "NetworkDetail: Unexpected NaN found for Bytes: name: %s", nd.Key())
+		nd.Bytes = 0
+	}
+}
+
+func (nd *NetworkDetail) IsZeroCost() bool {
+	if nd == nil {
+		log.DedupedWarningf(5, "nd.IsZeroCost called on a nil network detail")
+		return false
+	}
+	return nd.Cost == 0.0
+}
+
+type NetworkDetailsSet map[string]*NetworkDetail
+
+func (nds NetworkDetailsSet) Clone() NetworkDetailsSet {
+	retnids := make(NetworkDetailsSet, 0)
+	for name, nid := range nds {
+		retnids[name] = nid.Clone()
+	}
+	return retnids
+}
+
+func (nds NetworkDetailsSet) Add(nd *NetworkDetail) {
+	key := nd.Key()
+	if _, ok := nds[key]; ok {
+		nds[key].Add(nd)
+	} else {
+		nds[key] = nd.Clone()
+	}
+}
+
+// GetTotalInternetCost Gets the total internet cost in egress details i.e has internet flag true
+func (nds NetworkDetailsSet) GetTotalInternetCost() float64 {
+	totalCost := 0.0
+	for _, nd := range nds {
+		if nd.TrafficType == NetworkTrafficTypeInternet {
+			totalCost += nd.Cost
+		}
+	}
+	return totalCost
+}
+
+// Gets the total cross zone cost in egress details i.e has sameZone flag false
+func (nds NetworkDetailsSet) GetCrossZoneCost() float64 {
+	totalCost := 0.0
+	for _, nd := range nds {
+		if nd.TrafficType == NetworkTrafficTypeCrossZone {
+			totalCost += nd.Cost
+		}
+	}
+	return totalCost
+}
+
+// Gets the total cross region cost in egress details i.e has sameRegion flag false
+func (nds NetworkDetailsSet) GetCrossRegionCost() float64 {
+	totalCost := 0.0
+	for _, nd := range nds {
+		if nd.TrafficType == NetworkTrafficTypeCrossRegion {
+			totalCost += nd.Cost
+		}
+	}
+	return totalCost
+}
+
+func (nds NetworkDetailsSet) Combine(that NetworkDetailsSet) {
+	for _, nd := range that {
+		nds.Add(nd)
+	}
+}
+
+func (nds NetworkDetailsSet) SanitizeNaN() {
+	for _, nd := range nds {
+		nd.SanitizeNaN()
+	}
+}
+
+// filterZeroCost returns a new NetworkDetailsSet with all zero-cost details removed
+func (nds NetworkDetailsSet) filterZeroCost() NetworkDetailsSet {
+	newNds := make(map[string]*NetworkDetail, 0)
+	for key, nd := range nds {
+		if nd.IsZeroCost() {
+			continue
+		}
+		newNds[key] = nd.Clone()
+	}
+	return newNds
+}
+
+// NetworkInsight struct that stores pod interactions both egress and ingress
+// Currently only Cluster, namespace and pod will be populated from promsource.go
+// Rest are placeholders if we need to support single cluster via cost-model
+// using the same prometheus source.In aggregator we will join allocation of
+// same time window to get the controller, node, labels, region and zone data.
+type NetworkInsight struct {
+	Cluster                string            `json:"cluster"`
+	Namespace              string            `json:"namespace"`
+	Controller             string            `json:"controller"`
+	Pod                    string            `json:"pod"`
+	Node                   string            `json:"node"`
+	Labels                 map[string]string `json:"labels"`
+	Region                 string            `json:"region"`
+	Zone                   string            `json:"zone"`
+	NetworkTotalCost       float64           `json:"networkCost"`
+	NetworkCrossZoneCost   float64           `json:"networkCrossZoneCost"`
+	NetworkCrossRegionCost float64           `json:"networkCrossRegionCost"`
+	NetworkInternetCost    float64           `json:"networkInternetCost"`
+	NetworkDetails         NetworkDetailsSet `json:"networkDetails"`
+}
+
+func NewNetworkInsight(cluster string,
+	namespace string, controller string, pod string, node string,
+	labels map[string]string, region string, zone string,
+	networkTotalCost, networkCrossZoneCost, networkCrossRegionCost, networkInternetCost float64,
+	networkDetails map[string]*NetworkDetail) *NetworkInsight {
+
+	if networkDetails == nil {
+		networkDetails = make(map[string]*NetworkDetail, 0)
+	}
+
+	return &NetworkInsight{
+		Cluster:                cluster,
+		Namespace:              namespace,
+		Controller:             controller,
+		Pod:                    pod,
+		Node:                   node,
+		Labels:                 labels,
+		Region:                 region,
+		Zone:                   zone,
+		NetworkTotalCost:       networkTotalCost,
+		NetworkCrossZoneCost:   networkCrossZoneCost,
+		NetworkCrossRegionCost: networkCrossRegionCost,
+		NetworkInternetCost:    networkInternetCost,
+		NetworkDetails:         networkDetails,
+	}
+
+}
+
+func (ni *NetworkInsight) Clone() *NetworkInsight {
+	if ni == nil {
+		return nil
+	}
+	return &NetworkInsight{
+		Cluster:                ni.Cluster,
+		Namespace:              ni.Namespace,
+		Pod:                    ni.Pod,
+		Node:                   ni.Node,
+		Labels:                 ni.Labels,
+		Region:                 ni.Region,
+		Zone:                   ni.Zone,
+		NetworkTotalCost:       ni.NetworkTotalCost,
+		NetworkCrossZoneCost:   ni.NetworkCrossZoneCost,
+		NetworkCrossRegionCost: ni.NetworkCrossRegionCost,
+		NetworkInternetCost:    ni.NetworkInternetCost,
+		NetworkDetails:         ni.NetworkDetails.Clone(),
+	}
+}
+
+func (ni *NetworkInsight) add(that *NetworkInsight) {
+	if ni == nil {
+		log.Warnf("NetworkInsight.Add: trying to add a nil receiver")
+		return
+	}
+
+	if ni.Cluster != that.Cluster {
+		ni.Cluster = ""
+	}
+
+	if ni.Namespace != that.Namespace {
+		ni.Namespace = ""
+	}
+
+	if ni.Pod != that.Pod {
+		ni.Pod = ""
+	}
+
+	if ni.Controller != that.Controller {
+		ni.Controller = ""
+	}
+
+	if ni.Node != that.Node {
+		ni.Node = ""
+	}
+
+	if ni.Region != that.Region {
+		ni.Region = ""
+	}
+
+	if ni.Zone != that.Zone {
+		ni.Zone = ""
+	}
+
+	// TO-DO: Check for labels match if we support label in single cluster!
+
+	ni.NetworkTotalCost += that.NetworkTotalCost
+	ni.NetworkCrossZoneCost += that.NetworkCrossZoneCost
+	ni.NetworkCrossRegionCost += that.NetworkCrossRegionCost
+	ni.NetworkInternetCost += that.NetworkInternetCost
+	ni.NetworkDetails.Combine(that.NetworkDetails)
+}
+
+// Key takes a list of NetworkInsightProperty and creates a "/"
+// seperated key based on the values of the requested properties.
+// Invalid values and empty slice are set to default key.
+func (ni *NetworkInsight) Key(props []NetworkInsightProperty) (string, error) {
+	defaultString := fmt.Sprintf("%s/%s/%s", ni.Cluster, ni.Namespace, ni.Pod)
+	if len(props) == 0 {
+		return defaultString, nil
+	}
+	values := make([]string, len(props))
+	for i, prop := range props {
+		switch prop {
+		case NetworkInsightsNamespace:
+			values[i] = ni.Namespace
+		case NetworkInsightsPod:
+			values[i] = ni.Pod
+		case NetworkInsightsCluster:
+			values[i] = ni.Cluster
+		default:
+			return defaultString, nil
+		}
+	}
+	return strings.Join(values, "/"), nil
+}
+
+func (ni *NetworkInsight) GetTotalEgressByte() float64 {
+	totalByte := 0.0
+	for _, nd := range ni.NetworkDetails {
+		if nd == nil || nd.TrafficDirection != NetworkTrafficDirectionEgress {
+			continue
+		}
+		totalByte += nd.Bytes
+	}
+	return totalByte
+}
+
+func (ni *NetworkInsight) GetTotalIngressByte() float64 {
+	totalByte := 0.0
+	for _, nd := range ni.NetworkDetails {
+		if nd == nil || nd.TrafficDirection != NetworkTrafficDirectionIngress {
+			continue
+		}
+		totalByte += nd.Bytes
+	}
+	return totalByte
+}
+
+func (ni *NetworkInsight) SanitizeNaN() {
+	if ni == nil {
+		return
+	}
+
+	key, err := ni.Key([]NetworkInsightProperty{})
+	if err != nil {
+		log.DedupedWarningf(5, "NetworkInsight: unable to perform santization of network insight for cluster: %s, namespace: %s, pod: %s", ni.Cluster, ni.Namespace, ni.Pod)
+	}
+
+	if math.IsNaN(ni.NetworkTotalCost) {
+		log.DedupedWarningf(5, "NetworkInsight: Unexpected NaN found for NetworkTotalCost: name: %s", key)
+		ni.NetworkTotalCost = 0
+	}
+
+	if math.IsNaN(ni.NetworkCrossZoneCost) {
+		log.DedupedWarningf(5, "NetworkInsight: Unexpected NaN found for NetworkCrossZoneCost: name: %s", key)
+		ni.NetworkCrossZoneCost = 0
+	}
+
+	if math.IsNaN(ni.NetworkCrossRegionCost) {
+		log.DedupedWarningf(5, "NetworkInsight: Unexpected NaN found for NetworkCrossRegionCost: name: %s", key)
+		ni.NetworkCrossRegionCost = 0
+	}
+
+	if math.IsNaN(ni.NetworkInternetCost) {
+		log.DedupedWarningf(5, "NetworkInsight: Unexpected NaN found for NetworkInternetCost: name: %s", key)
+		ni.NetworkInternetCost = 0
+	}
+	ni.NetworkDetails.SanitizeNaN()
+}
+
+func (ni *NetworkInsight) filterZeroCost() {
+	if ni == nil {
+		return
+	}
+	ni.NetworkDetails = ni.NetworkDetails.filterZeroCost()
+}
+
+func (ni *NetworkInsight) filterNetworkDetails(networkDetailFilter NetworkInsightDetailMatcher) {
+	if ni == nil {
+		log.DedupedWarningf(5, "NetworkInsight:filterNetworkDetails called on nil network insight")
+		return
+	}
+	newNds := make(NetworkDetailsSet, 0)
+	for key, nd := range ni.NetworkDetails {
+		if networkDetailFilter.Matches(nd) {
+			newNds[key] = nd
+		}
+	}
+	ni.NetworkDetails = newNds
+}
+
+// SetWithNetworkInsightProperty sets the corresponding property
+// variable in the struct with the value passed to the function.
+func (ni *NetworkInsight) SetWithNetworkInsightProperty(property NetworkInsightProperty, value interface{}) error {
+	switch property {
+	case NetworkInsightsCluster:
+		ni.Cluster = value.(string)
+	case NetworkInsightsNamespace:
+		ni.Namespace = value.(string)
+	case NetworkInsightsPod:
+		ni.Pod = value.(string)
+	}
+	return fmt.Errorf("unsupported property: %s", string(property))
+}
+
+type NetworkInsightSet struct {
+	NetworkInsights map[string]*NetworkInsight `json:"networkInsights"`
+	Window          Window                     `json:"window"`
+}
+
+// NewNetworkInsightSet instantiates a new NetworkInsights set and, optionally, inserts
+// the given list of NetworkInsight
+func NewNetworkInsightSet(start, end time.Time, networkInsight ...*NetworkInsight) *NetworkInsightSet {
+	nis := &NetworkInsightSet{
+		NetworkInsights: make(map[string]*NetworkInsight, 0),
+		Window:          NewWindow(&start, &end),
+	}
+
+	for _, ni := range networkInsight {
+		nis.Insert(ni, []NetworkInsightProperty{})
+	}
+
+	return nis
+}
+
+func (nis *NetworkInsightSet) Add(that *NetworkInsightSet, keyProperties []NetworkInsightProperty) (*NetworkInsightSet, error) {
+	if (nis == nil || len(nis.NetworkInsights) == 0) && (that == nil || len(that.NetworkInsights) == 0) {
+		return nis, nil
+	}
+
+	if nis == nil || len(nis.NetworkInsights) == 0 {
+		return that, nil
+	}
+
+	if that == nil || len(that.NetworkInsights) == 0 {
+		return that, nil
+	}
+
+	start := *nis.Window.Start()
+	end := *nis.Window.End()
+
+	if that.Window.Start().Before(start) {
+		start = *that.Window.Start()
+	}
+
+	if that.Window.End().After(end) {
+		end = *that.Window.End()
+	}
+
+	acc := &NetworkInsightSet{
+		NetworkInsights: make(map[string]*NetworkInsight, len(nis.NetworkInsights)),
+		Window:          NewClosedWindow(start, end),
+	}
+
+	for _, ni := range nis.NetworkInsights {
+		err := acc.Insert(ni, keyProperties)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	for _, ni := range that.NetworkInsights {
+		err := acc.Insert(ni, keyProperties)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return acc, nil
+}
+
+func (nis *NetworkInsightSet) Insert(that *NetworkInsight, aggregateBy []NetworkInsightProperty) error {
+	if nis == nil {
+		return fmt.Errorf("cannot insert into nil networkInsightSet")
+	}
+
+	if nis.NetworkInsights == nil {
+		nis.NetworkInsights = map[string]*NetworkInsight{}
+	}
+
+	key, err := that.Key(aggregateBy)
+	if err != nil {
+		return fmt.Errorf("unable to generate key for aggregation: %v", err)
+	}
+	if _, ok := nis.NetworkInsights[key]; !ok {
+		nis.NetworkInsights[key] = that
+	} else {
+		nis.NetworkInsights[key].add(that)
+	}
+	return nil
+}
+
+func (nis *NetworkInsightSet) Clone() *NetworkInsightSet {
+	if nis == nil {
+		return nil
+	}
+
+	networkInsights := make(map[string]*NetworkInsight, len(nis.NetworkInsights))
+	for k, v := range nis.NetworkInsights {
+		networkInsights[k] = v.Clone()
+	}
+	return &NetworkInsightSet{
+		NetworkInsights: networkInsights,
+		Window:          nis.Window.Clone(),
+	}
+}
+
+func (nis *NetworkInsightSet) GetWindow() Window {
+	return nis.Window
+}
+
+func (nis *NetworkInsightSet) IsValid() bool {
+	if !nis.IsEmpty() {
+		return false
+	}
+
+	if nis.Window.IsOpen() {
+		return false
+	}
+
+	return true
+}
+
+func (nis *NetworkInsightSet) IsEmpty() bool {
+	if nis == nil || len(nis.NetworkInsights) == 0 {
+		return true
+	}
+	return false
+}
+
+func (nis *NetworkInsightSet) AggregateBy(aggregateBy []NetworkInsightProperty) error {
+	if nis.IsEmpty() {
+		return nil
+	}
+
+	aggSet := &NetworkInsightSet{}
+
+	for _, ni := range nis.NetworkInsights {
+		err := aggSet.Insert(ni, aggregateBy)
+		if err != nil {
+			return fmt.Errorf("NetworkInsightSet:AggregateBy failed with err: %v", err)
+		}
+	}
+
+	nis.NetworkInsights = aggSet.NetworkInsights
+
+	return nil
+}
+
+func (nis *NetworkInsightSet) Accumulate(that *NetworkInsightSet, keyProperties []NetworkInsightProperty) (*NetworkInsightSet, error) {
+	if nis.IsEmpty() {
+		return that.Clone(), nil
+	}
+
+	if that.IsEmpty() {
+		return nis.Clone(), nil
+	}
+
+	start := nis.Window.Start()
+	end := nis.Window.End()
+	if start.After(*that.Window.Start()) {
+		start = that.Window.Start()
+	}
+
+	if end.Before(*that.Window.End()) {
+		end = that.Window.End()
+	}
+	newNis := nis.Clone()
+	newNis.Window = NewClosedWindow(*start, *end)
+	for _, ni := range that.NetworkInsights {
+		err := newNis.Insert(ni, keyProperties)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return newNis, nil
+}
+
+func (nis *NetworkInsightSet) Length() int {
+	if nis == nil {
+		return 0
+	}
+
+	return len(nis.NetworkInsights)
+}
+
+func (nis *NetworkInsightSet) FilterOn(filter filter.Filter) error {
+	if nis.IsEmpty() {
+		return fmt.Errorf("NetworkInsightSet:FilterOn called on empty network insight set")
+	}
+	var networkInsightFilter NetworkInsightMatcher
+	if filter == nil {
+		networkInsightFilter = &matcher.AllPass[*NetworkInsight]{}
+	} else {
+		compiler := NewNetworkInsightMatchCompiler()
+		var err error
+		networkInsightFilter, err = compiler.Compile(filter)
+		if err != nil {
+			return fmt.Errorf("compiling filter '%s': %w", ast.ToPreOrderShortString(filter), err)
+		}
+	}
+
+	if networkInsightFilter == nil {
+		return fmt.Errorf("unexpected nil filter")
+	}
+
+	for key, ni := range nis.NetworkInsights {
+		if ni == nil {
+			continue
+		}
+		if !networkInsightFilter.Matches(ni) {
+			delete(nis.NetworkInsights, key)
+		}
+	}
+
+	return nil
+}
+
+// Resolution returns the NetworkInsightSet's window duration
+func (nis *NetworkInsightSet) Resolution() time.Duration {
+	if nis == nil {
+		return time.Duration(0)
+	}
+	return nis.Window.Duration()
+}
+
+func (nis *NetworkInsightSet) FilterNetworkDetails(opts *NetworkDetailsOptions) error {
+	if nis == nil {
+		return fmt.Errorf("filterNetworkDetails called on nil network insight set")
+	}
+	if opts == nil {
+		return nil
+	}
+	var networkDetailFilter NetworkInsightDetailMatcher
+	if opts.FilterNetworkDetails == nil {
+		networkDetailFilter = &matcher.AllPass[*NetworkDetail]{}
+	} else {
+		compiler := NewNetworkInsightDetailMatchCompiler()
+		var err error
+		networkDetailFilter, err = compiler.Compile(opts.FilterNetworkDetails)
+		if err != nil {
+			return fmt.Errorf("compiling filter '%s': %w", ast.ToPreOrderShortString(opts.FilterNetworkDetails), err)
+		}
+	}
+
+	if networkDetailFilter == nil {
+		return fmt.Errorf("unexpected nil filter")
+	}
+
+	for _, ni := range nis.NetworkInsights {
+		// filter network details that satisfy the
+		// network detail filter
+		ni.filterNetworkDetails(networkDetailFilter)
+		// filter zero cost network details
+		if !opts.ShowZeroCost {
+			ni.filterZeroCost()
+		}
+	}
+
+	return nil
+}
+
+func (nis *NetworkInsightSet) SanitizeNaN() {
+	if nis == nil {
+		return
+	}
+	for _, ni := range nis.NetworkInsights {
+		ni.SanitizeNaN()
+	}
+}
+
+type NetworkInsightSetRange struct {
+	sync.RWMutex
+	NetworkInsightsSet []*NetworkInsightSet `json:"networkInsightSet"`
+	Window             Window               `json:"window"`
+}
+
+func NewNetworkInsightSetRange(window Window, nis ...*NetworkInsightSet) *NetworkInsightSetRange {
+	return &NetworkInsightSetRange{
+		NetworkInsightsSet: nis,
+		Window:             window,
+	}
+}
+
+func (nisr *NetworkInsightSetRange) AggregateBy(aggregateBy []NetworkInsightProperty) error {
+	if nisr == nil || len(nisr.NetworkInsightsSet) == 0 {
+		return nil
+	}
+
+	if nisr.Window.IsOpen() {
+		return fmt.Errorf("cannot aggregate a NetworkInsightSetRange with an open window")
+	}
+
+	tempNis := &NetworkInsightSetRange{NetworkInsightsSet: []*NetworkInsightSet{}}
+
+	for _, ni := range nisr.NetworkInsightsSet {
+		err := ni.AggregateBy(aggregateBy)
+		if err != nil {
+			return err
+		}
+		tempNis.NetworkInsightsSet = append(tempNis.NetworkInsightsSet, ni)
+	}
+
+	nisr.NetworkInsightsSet = tempNis.NetworkInsightsSet
+	return nil
+}
+
+func (nisr *NetworkInsightSetRange) Append(that *NetworkInsightSet) {
+	if nisr == nil {
+		log.DedupedWarningf(5, "NetworkInsightSetRange:Append called on nil Network Insight Set Range")
+		return
+	}
+	nisr.Lock()
+	defer nisr.Unlock()
+
+	nisr.NetworkInsightsSet = append(nisr.NetworkInsightsSet, that)
+
+	// Adjust window
+	start := nisr.Window.Start()
+	end := nisr.Window.End()
+	if nisr.Window.Start() == nil || (that.Window.Start() != nil && that.Window.Start().Before(*nisr.Window.Start())) {
+		start = that.Window.Start()
+	}
+	if nisr.Window.End() == nil || (that.Window.End() != nil && that.Window.End().After(*nisr.Window.End())) {
+		end = that.Window.End()
+	}
+	nisr.Window = NewClosedWindow(*start, *end)
+}
+
+func (nisr *NetworkInsightSetRange) Clone() *NetworkInsightSetRange {
+	if nisr == nil {
+		return nil
+	}
+	nisrClone := NewNetworkInsightSetRange(nisr.Window)
+
+	for _, nis := range nisr.NetworkInsightsSet {
+		nisClone := nis.Clone()
+		nisrClone.Append(nisClone)
+	}
+
+	return nisrClone
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByNone(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	return nisr.Clone(), nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByAll(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	nis, err := nisr.newAccumulation(keyProperties)
+	if err != nil {
+		return nil, fmt.Errorf("error accumulating NetworkInsightSetRange:%w", err)
+	}
+	accumulated := NewNetworkInsightSetRange(nisr.Window, nis)
+	return accumulated, nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByHour(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:accumulateByHour called on nil set range")
+	}
+	// ensure that the network insight set have a 1-hour window and if a set exists
+	duration := nisr.Window.Duration()
+	if len(nisr.NetworkInsightsSet) > 0 && duration != time.Hour {
+		return nil, fmt.Errorf("window duration must equal 1 hour; got:%s", duration.String())
+	}
+
+	return nisr.Clone(), nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulate(keyProperties []NetworkInsightProperty) (*NetworkInsightSet, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:accumulate called on nil set range")
+	}
+	var result *NetworkInsightSet
+	var err error
+
+	nisr.RLock()
+	defer nisr.RUnlock()
+
+	for _, ni := range nisr.NetworkInsightsSet {
+		result, err = result.Add(ni, keyProperties)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return result, nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByDay(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:accumulateByDay called on nil set range")
+	}
+	// if the network insight set window is 1-day, just return the existing allocation set range
+	duration := nisr.Window.Duration()
+	if len(nisr.NetworkInsightsSet) > 0 && duration == timeutil.Day {
+		return nisr, nil
+	}
+
+	var toAccumulate *NetworkInsightSetRange
+	result := NewNetworkInsightSetRange(NewWindow(nil, nil))
+	for i, nis := range nisr.NetworkInsightsSet {
+
+		if nis.Window.Duration() != time.Hour {
+			return nil, fmt.Errorf("window duration must equal 1 hour; got:%s", nis.Window.Duration())
+		}
+
+		hour := nis.Window.Start().Hour()
+
+		if toAccumulate == nil {
+			toAccumulate = NewNetworkInsightSetRange(NewWindow(nil, nil))
+			nis = nis.Clone()
+		}
+		toAccumulate.Append(nis)
+		nis, err := toAccumulate.accumulate(keyProperties)
+		if err != nil {
+			return nil, fmt.Errorf("error accumulating result: %s", err)
+		}
+		if nis == nil {
+			continue
+		}
+		toAccumulate = NewNetworkInsightSetRange(nis.Window, nis)
+
+		if hour == 23 || i == len(nisr.NetworkInsightsSet)-1 {
+			if length := len(toAccumulate.NetworkInsightsSet); length != 1 {
+				return nil, fmt.Errorf("failed accumulation, detected %d sets instead of 1", length)
+			}
+			result.Append(toAccumulate.NetworkInsightsSet[0])
+			toAccumulate = nil
+		}
+	}
+	return result, nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByWeek(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:accumulateByWeek called on nil set range")
+	}
+	var toAccumulate *NetworkInsightSetRange
+	result := NewNetworkInsightSetRange(NewWindow(nil, nil))
+	for i, nis := range nisr.NetworkInsightsSet {
+
+		if nis.Window.Duration() != timeutil.Day {
+			return nil, fmt.Errorf("window duration must equal 24 hours; got:%s", nis.Window.Duration())
+		}
+
+		dayOfWeek := nis.Window.Start().Weekday()
+
+		if toAccumulate == nil {
+			toAccumulate = NewNetworkInsightSetRange(NewWindow(nil, nil))
+			nis = nis.Clone()
+		}
+
+		toAccumulate.Append(nis)
+		nis, err := toAccumulate.accumulate(keyProperties)
+		if err != nil {
+			return nil, fmt.Errorf("error accumulating result: %s", err)
+		}
+
+		if nis == nil {
+			continue
+		}
+		toAccumulate = NewNetworkInsightSetRange(nis.Window, nis)
+
+		if dayOfWeek == time.Saturday || i == len(nisr.NetworkInsightsSet)-1 {
+			if length := len(toAccumulate.NetworkInsightsSet); length != 1 {
+				return nil, fmt.Errorf("failed accumulation, detected %d sets instead of 1", length)
+			}
+			result.Append(toAccumulate.NetworkInsightsSet[0])
+			toAccumulate = nil
+		}
+	}
+	return result, nil
+}
+
+func (nisr *NetworkInsightSetRange) accumulateByMonth(keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:accumulateByMonth called on nil set range")
+	}
+	var toAccumulate *NetworkInsightSetRange
+	result := NewNetworkInsightSetRange(NewWindow(nil, nil))
+	for i, nis := range nisr.NetworkInsightsSet {
+
+		if nis.Window.Duration() != timeutil.Day {
+			return nil, fmt.Errorf("window duration must equal 24 hours; got:%s", nis.Window.Duration())
+		}
+
+		_, month, _ := nis.Window.Start().Date()
+		_, nextDayMonth, _ := nis.Window.Start().Add(time.Hour * 24).Date()
+
+		if toAccumulate == nil {
+			toAccumulate = NewNetworkInsightSetRange(NewWindow(nil, nil))
+			nis = nis.Clone()
+		}
+
+		toAccumulate.Append(nis)
+		nis, err := toAccumulate.accumulate(keyProperties)
+		if err != nil {
+			return nil, fmt.Errorf("error accumulating result: %s", err)
+		}
+		if nis == nil {
+			continue
+		}
+		toAccumulate = NewNetworkInsightSetRange(nis.Window, nis)
+
+		if month != nextDayMonth || i == len(nisr.NetworkInsightsSet)-1 {
+			if length := len(toAccumulate.NetworkInsightsSet); length != 1 {
+				return nil, fmt.Errorf("failed accumulation, detected %d sets instead of 1", length)
+			}
+			result.Append(toAccumulate.NetworkInsightsSet[0])
+			toAccumulate = nil
+		}
+	}
+	return result, nil
+}
+
+func (nisr *NetworkInsightSetRange) Accumulate(accumulateBy AccumulateOption, keyProperties []NetworkInsightProperty) (*NetworkInsightSetRange, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("NetworkInsightSetRange:Accumulate called on nil set range")
+	}
+	switch accumulateBy {
+	case AccumulateOptionNone:
+		return nisr.accumulateByNone(keyProperties)
+	case AccumulateOptionAll:
+		return nisr.accumulateByAll(keyProperties)
+	case AccumulateOptionHour:
+		return nisr.accumulateByHour(keyProperties)
+	case AccumulateOptionDay:
+		return nisr.accumulateByDay(keyProperties)
+	case AccumulateOptionWeek:
+		return nisr.accumulateByWeek(keyProperties)
+	case AccumulateOptionMonth:
+		return nisr.accumulateByMonth(keyProperties)
+	default:
+		// ideally, this should never happen
+		return nil, fmt.Errorf("unexpected error, invalid accumulateByType: %s", accumulateBy)
+	}
+}
+
+func (nisr *NetworkInsightSetRange) newAccumulation(keyProperties []NetworkInsightProperty) (*NetworkInsightSet, error) {
+	if nisr == nil {
+		return nil, fmt.Errorf("nil NetworkInsightSetRange in accumulation")
+	}
+
+	var networkInsigthSet *NetworkInsightSet
+	var err error
+	if len(nisr.NetworkInsightsSet) == 0 {
+		return nil, fmt.Errorf("NetworkInsightSetRange has empty NetworkInsightSet in accumulation")
+	}
+
+	for _, nis := range nisr.NetworkInsightsSet {
+		if networkInsigthSet == nil {
+			networkInsigthSet = nis.Clone()
+			continue
+		}
+
+		networkInsigthSet, err = networkInsigthSet.Accumulate(nis, keyProperties)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return networkInsigthSet, nil
+}
+
+func (nisr *NetworkInsightSetRange) FilterOn(filter filter.Filter) error {
+	if nisr == nil {
+		return fmt.Errorf("filter called on nil networkInsightSetRange")
+	}
+
+	for _, nis := range nisr.NetworkInsightsSet {
+		err := nis.FilterOn(filter)
+		if err != nil {
+			return fmt.Errorf("unable to filter nis for window: %s with err: %v", nis.Window.String(), err)
+		}
+	}
+	return nil
+}
+
+// FilterNetworkDetails for a given network insight set with the options applied.
+// When ShowZeroCost is set to false, all the network detail interactions with
+// zero cost are dropped and based on the applied filter only.
+func (nisr *NetworkInsightSetRange) FilterNetworkDetails(opts *NetworkDetailsOptions) error {
+	if opts == nil {
+		return nil
+	}
+	if nisr == nil {
+		return fmt.Errorf("filter called on nil networkInsightSetRange")
+	}
+	for _, nis := range nisr.NetworkInsightsSet {
+		err := nis.FilterNetworkDetails(opts)
+		if err != nil {
+			return fmt.Errorf("unable to filter network details in nis for window: %s with err: %v", nis.Window.String(), err)
+		}
+	}
+	return nil
+}

+ 256 - 0
core/pkg/opencost/networkinsight_test.go

@@ -0,0 +1,256 @@
+package opencost
+
+import (
+	"fmt"
+	"reflect"
+	"testing"
+	"time"
+
+	filter "github.com/opencost/opencost/core/pkg/filter/networkinsight"
+	"github.com/opencost/opencost/core/pkg/util"
+)
+
+const (
+	mockCluster1   = "mockCluster1"
+	mockNamespace1 = "mockNamespace1"
+	mockPod1       = "mockPod1"
+	mockNamespace2 = "mockNamespace2"
+	mockPod2       = "mockPod2"
+)
+
+func Test_NetworkDetailsCombineAndAdd(t *testing.T) {
+
+	mcsI1inSet1, _ := createMockNetworkDetail(0, 200000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsI2inSet1, _ := createMockNetworkDetail(0, 400000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsI3inSet1, _ := createMockNetworkDetail(0, 800000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsE1inSet1, _ := createMockNetworkDetail(0.12, 200000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcsE2inSet1, _ := createMockNetworkDetail(0.15, 400000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	mcsI1inSet2, _ := createMockNetworkDetail(0, 300000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsI2inSet2, _ := createMockNetworkDetail(0, 300000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsE1inSet2, _ := createMockNetworkDetail(0.16, 300000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcsE2inSet2, _ := createMockNetworkDetail(0.24, 300000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcsE3inSet2, _ := createMockNetworkDetail(0.35, 300000, mockCloudService3, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	mcsI1inCombineSet, _ := createMockNetworkDetail(0, 500000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsI2inCombineSet, _ := createMockNetworkDetail(0, 700000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcsE1inCombineSet, _ := createMockNetworkDetail(0.28, 500000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcsE2inCombineSet, _ := createMockNetworkDetail(0.39, 700000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	set1 := make(NetworkDetailsSet, 0)
+	set1[mcsI1inSet1.Key()] = mcsI1inSet1
+	set1[mcsI2inSet1.Key()] = mcsI2inSet1
+	set1[mcsI3inSet1.Key()] = mcsI3inSet1
+	set1[mcsE1inSet1.Key()] = mcsE1inSet1
+	set1[mcsE2inSet1.Key()] = mcsE2inSet1
+
+	set2 := make(NetworkDetailsSet, 0)
+	set2[mcsI1inSet2.Key()] = mcsI1inSet2
+	set2[mcsI2inSet2.Key()] = mcsI2inSet2
+	set2[mcsE1inSet2.Key()] = mcsE1inSet2
+	set2[mcsE2inSet2.Key()] = mcsE2inSet2
+	set2[mcsE3inSet2.Key()] = mcsE3inSet2
+
+	expected := make(NetworkDetailsSet, 0)
+	expected[mcsI1inCombineSet.Key()] = mcsI1inCombineSet
+	expected[mcsI2inCombineSet.Key()] = mcsI2inCombineSet
+	expected[mcsI3inSet1.Key()] = mcsI3inSet1
+	expected[mcsE1inCombineSet.Key()] = mcsE1inCombineSet
+	expected[mcsE2inCombineSet.Key()] = mcsE2inCombineSet
+	expected[mcsE3inSet2.Key()] = mcsE3inSet2
+
+	set1.Combine(set2)
+
+	if !reflect.DeepEqual(set1, expected) {
+		t.Fatalf("Test_NetworkIngressCombineAndAdd: NetworkIngressDetailSet:combine not working as expected")
+	}
+}
+
+func Test_NetworkInsightSetInsertFn(t *testing.T) {
+	mockInsight1 := MockNetworkInsightImportantKeys{
+		Cluster:    mockCluster1,
+		Namespace:  mockNamespace1,
+		Controller: "",
+		Pod:        mockPod1,
+		Node:       "",
+		Labels:     map[string]string{},
+		Region:     "",
+		Zone:       "",
+	}
+
+	mockInsight2 := MockNetworkInsightImportantKeys{
+		Cluster:    mockCluster1,
+		Namespace:  mockNamespace2,
+		Controller: "",
+		Pod:        mockPod2,
+		Node:       "",
+		Labels:     map[string]string{},
+		Region:     "",
+		Zone:       "",
+	}
+
+	mcs1inIngress, _ := createMockNetworkDetail(0, 200000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs2inIngress, _ := createMockNetworkDetail(0, 400000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs3inIngress, _ := createMockNetworkDetail(0, 800000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs1inEgress, _ := createMockNetworkDetail(0.16, 300000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs2inEgress, _ := createMockNetworkDetail(0.24, 300000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs3inEgress, _ := createMockNetworkDetail(0.35, 300000, mockCloudService3, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	set1 := make(NetworkDetailsSet, 0)
+	set1[mcs1inIngress.Key()] = mcs1inIngress
+	set1[mcs2inIngress.Key()] = mcs2inIngress
+	set1[mcs3inIngress.Key()] = mcs3inIngress
+	set1[mcs1inEgress.Key()] = mcs1inEgress
+	set1[mcs2inEgress.Key()] = mcs2inEgress
+	set1[mcs3inEgress.Key()] = mcs3inEgress
+
+	set2 := make(NetworkDetailsSet, 0)
+	set2[mcs1inIngress.Key()] = mcs1inIngress.Clone()
+	set2[mcs2inIngress.Key()] = mcs2inIngress.Clone()
+	set2[mcs3inIngress.Key()] = mcs3inIngress.Clone()
+	set2[mcs1inEgress.Key()] = mcs1inEgress.Clone()
+	set2[mcs2inEgress.Key()] = mcs2inEgress.Clone()
+	set2[mcs3inEgress.Key()] = mcs3inEgress.Clone()
+
+	ni1 := createMockNetworkInsight(mockInsight1, set1)
+	ni2 := createMockNetworkInsight(mockInsight1, set2)
+	ni3 := createMockNetworkInsight(mockInsight2, set1.Clone())
+
+	e := time.Now()
+	s := e.Add(-1 * time.Hour)
+	nis := &NetworkInsightSet{
+		NetworkInsights: make(map[string]*NetworkInsight, 0),
+		Window:          NewWindow(&s, &e),
+	}
+
+	nis.Insert(ni1, []NetworkInsightProperty{NetworkInsightsPod})
+	nis.Insert(ni2, []NetworkInsightProperty{NetworkInsightsPod})
+	nis.Insert(ni3, []NetworkInsightProperty{NetworkInsightsPod})
+
+	aggregatedNis := nis.NetworkInsights
+
+	if val, ok := aggregatedNis[mockPod1]; !ok {
+		t.Fatalf("Test_NetworkInsightSetInsertFn: %s not found after insert", mockPod1)
+	} else {
+		if !util.IsApproximately(val.NetworkDetails.GetTotalInternetCost(), 1.50) {
+			t.Logf("1 is %f", val.NetworkDetails.GetTotalInternetCost())
+			t.Fatalf("Test_NetworkInsightSetInsertFn: failed to insert and add properly for pod %s", mockPod1)
+		}
+	}
+	if val, ok := aggregatedNis[mockPod2]; !ok {
+		t.Fatalf("Test_NetworkInsightSetInsertFn: %s not found after insert", mockPod2)
+	} else {
+		if !util.IsApproximately(val.NetworkDetails.GetTotalInternetCost(), 0.75) {
+			t.Logf("2 is %f", val.NetworkDetails.GetTotalInternetCost())
+			t.Fatalf("Test_NetworkInsightSetInsertFn: failed to insert and add properly for pod %s", mockPod1)
+		}
+	}
+}
+
+func Test_NetworkDetailSetFilterZeroCost(t *testing.T) {
+	mcs1inIngress, _ := createMockNetworkDetail(0, 200000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs2inIngress, _ := createMockNetworkDetail(0, 400000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs3inIngress, _ := createMockNetworkDetail(0, 800000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs1inEgress, _ := createMockNetworkDetail(0.16, 300000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs2inEgress, _ := createMockNetworkDetail(0.24, 300000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs3inEgress, _ := createMockNetworkDetail(0.35, 300000, mockCloudService3, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	set1 := make(NetworkDetailsSet, 0)
+	set1[mcs1inIngress.Key()] = mcs1inIngress
+	set1[mcs2inIngress.Key()] = mcs2inIngress
+	set1[mcs3inIngress.Key()] = mcs3inIngress
+	set1[mcs1inEgress.Key()] = mcs1inEgress
+	set1[mcs2inEgress.Key()] = mcs2inEgress
+	set1[mcs3inEgress.Key()] = mcs3inEgress
+
+	set2 := make(NetworkDetailsSet, 0)
+	set2[mcs1inEgress.Key()] = mcs1inEgress
+	set2[mcs2inEgress.Key()] = mcs2inEgress
+	set2[mcs3inEgress.Key()] = mcs3inEgress
+
+	set3 := set1.filterZeroCost()
+	if !reflect.DeepEqual(set3, set2) {
+		t.Fatalf("Test_NetworkIngressCombineAndAdd: NetworkIngressDetailSet:filterZeroCost failed with showZeroCost:false option")
+	}
+}
+
+func Test_NetworkInsightFilterNetworkDetails(t *testing.T) {
+	parserForEqualsTest := filter.NewNetworkInsightDetailFilterParser()
+	compiler := NewNetworkInsightDetailMatchCompiler()
+	equalsTestRegex, err := parserForEqualsTest.Parse(fmt.Sprintf("endPoint:\"%s\"", mockCloudService1))
+	// if error encounter skip the test
+	if err != nil {
+		t.Logf("skipping test case to avoid intermittent test failure if not able to parse filter string")
+		t.Skip()
+	}
+	equalsTestFilter, err := compiler.Compile(equalsTestRegex)
+	// if error encounter skip the test
+	if err != nil {
+		t.Logf("skipping test case to avoid intermittent test failure if not able to parse filter string")
+		t.Skip()
+	}
+
+	parserForContainsTest := filter.NewNetworkInsightDetailFilterParser()
+	containsTestRegex, err := parserForContainsTest.Parse(fmt.Sprintf("endPoint~:\"%s\"", mockCloudServiceName))
+	// if error encounter skip the test
+	if err != nil {
+		t.Logf("skipping test case to avoid intermittent test failure if not able to parse filter string")
+		t.Skip()
+	}
+	containsTestFilter, err := compiler.Compile(containsTestRegex)
+	// if error encounter skip the test
+	if err != nil {
+		t.Logf("skipping test case to avoid intermittent test failure if not able to parse filter string")
+		t.Skip()
+	}
+
+	mcs1inIngress, _ := createMockNetworkDetail(0, 200000, mockCloudService1, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs2inIngress, _ := createMockNetworkDetail(0, 400000, mockCloudService2, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs3inIngress, _ := createMockNetworkDetail(0, 800000, mockCloudService3, NetworkTrafficDirectionIngress, NetworkTrafficTypeInternet)
+	mcs1inEgress, _ := createMockNetworkDetail(0.16, 300000, mockCloudService1, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs2inEgress, _ := createMockNetworkDetail(0.24, 300000, mockCloudService2, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+	mcs3inEgress, _ := createMockNetworkDetail(0.35, 300000, mockCloudService3, NetworkTrafficDirectionEgress, NetworkTrafficTypeInternet)
+
+	set1 := make(NetworkDetailsSet, 0)
+	set1[mcs1inIngress.Key()] = mcs1inIngress
+	set1[mcs2inIngress.Key()] = mcs2inIngress
+	set1[mcs3inIngress.Key()] = mcs3inIngress
+	set1[mcs1inEgress.Key()] = mcs1inEgress
+	set1[mcs2inEgress.Key()] = mcs2inEgress
+	set1[mcs3inEgress.Key()] = mcs3inEgress
+
+	// set2 with only mockservice1 network details for validation
+	set2 := make(NetworkDetailsSet, 0)
+	set2[mcs1inIngress.Key()] = mcs1inIngress
+	set2[mcs1inEgress.Key()] = mcs1inEgress
+
+	mockInsight1 := MockNetworkInsightImportantKeys{
+		Cluster:    mockCluster1,
+		Namespace:  mockNamespace1,
+		Controller: "",
+		Pod:        mockPod1,
+		Node:       "",
+		Labels:     map[string]string{},
+		Region:     "",
+		Zone:       "",
+	}
+
+	ni1 := createMockNetworkInsight(mockInsight1, set1)
+
+	ni1Copy := ni1.Clone()
+	ni1.filterNetworkDetails(equalsTestFilter)
+
+	// Check to see after filtering the network details
+	//only have filtered result that is set2
+	if !reflect.DeepEqual(ni1.NetworkDetails, set2) {
+		t.Fatalf("Test_NetworkIngressCombineAndAdd: NetworkIngressDetailSet:filterNetworkDetails failed with equal to filter")
+	}
+
+	// Check to see after filtering the network details only have filtered
+	// result that is original as a combine data that satisfy the regex
+	ni1Copy.filterNetworkDetails(containsTestFilter)
+	if !reflect.DeepEqual(ni1Copy.NetworkDetails, set1) {
+		t.Fatalf("Test_NetworkIngressCombineAndAdd: NetworkIngressDetailSet:filterNetworkDetails failed with contains to filter")
+	}
+
+}

+ 224 - 0
core/pkg/opencost/networkinsightgraph.go

@@ -0,0 +1,224 @@
+package opencost
+
+import (
+	"fmt"
+
+	"github.com/opencost/opencost/core/pkg/log"
+)
+
+const (
+	NetworkInsightsNodeUnknown  = "unknownNode"
+	NetworkInsightsCloudService = "cloudService"
+	NetworkInsightsOther        = "other"
+	NetworkInsightsInternet     = "internet"
+	NetworkInsightsSameZone     = "sameZone"
+	NetworkInsightsSameRegion   = "sameRegion"
+)
+
+type NetworkInsightsEdge struct {
+	Source string  `json:"source"`
+	Target string  `json:"target"`
+	Cost   float64 `json:"cost"`
+	Bytes  float64 `json:"BytesInGiB"`
+}
+
+func NewNetworkInsightsEdge(source string,
+	target string, cost float64, bytes float64) *NetworkInsightsEdge {
+	return &NetworkInsightsEdge{
+		Source: source,
+		Target: target,
+		Cost:   cost,
+		Bytes:  bytes,
+	}
+}
+
+func (nie *NetworkInsightsEdge) Key() string {
+	return fmt.Sprintf("%s/%s", nie.Source, nie.Target)
+}
+
+func (nie *NetworkInsightsEdge) add(that *NetworkInsightsEdge) {
+	nie.Cost += that.Cost
+	nie.Bytes += that.Bytes
+}
+
+type NetworkInsightsNode struct {
+	Name         string  `json:"name"`
+	Type         string  `json:"type"`
+	TotalEgress  float64 `json:"totalEgressInGiB"`
+	TotalIngress float64 `json:"totalIngressInGiB"`
+	Cost         float64 `json:"cost"`
+	Internet     bool    `json:"internet"`
+	SameZone     bool    `json:"sameZone"`
+	SameRegion   bool    `json:"sameRegion"`
+}
+
+func NewNetworkInsightsNode(name string,
+	networkType string, totalEgress float64, totalIngress float64, cost float64,
+	internet, sameZone, sameRegion bool) *NetworkInsightsNode {
+	return &NetworkInsightsNode{
+		Name:         name,
+		Type:         networkType,
+		TotalEgress:  totalEgress,
+		TotalIngress: totalIngress,
+		Cost:         cost,
+		Internet:     internet,
+		SameZone:     sameZone,
+		SameRegion:   sameRegion,
+	}
+}
+
+func (nin *NetworkInsightsNode) Key() string {
+	if nin.Name != NetworkInsightsServiceUnknown {
+		return nin.Name
+	}
+	if nin.Name == NetworkInsightsServiceUnknown && nin.Internet {
+		return NetworkInsightsInternet
+	}
+	if nin.Name == NetworkInsightsServiceUnknown && nin.SameZone {
+		return NetworkInsightsSameZone
+	}
+	if nin.Name == NetworkInsightsServiceUnknown && nin.SameRegion {
+		return NetworkInsightsSameRegion
+	}
+	return NetworkInsightsNodeUnknown
+}
+
+func (nin *NetworkInsightsNode) add(that *NetworkInsightsNode) {
+	nin.TotalEgress += that.TotalEgress
+	nin.TotalIngress += that.TotalIngress
+	nin.Cost += that.Cost
+}
+
+type NetworkInsightsGraphData struct {
+	Nodes map[string]*NetworkInsightsNode `json:"nodes"`
+	Edges map[string]*NetworkInsightsEdge `json:"edges"`
+}
+
+type NetworkInsightsGraphDataResponse struct {
+	Nodes map[string]*NetworkInsightsNode `json:"nodes"`
+	Edges []*NetworkInsightsEdge          `json:"edges"`
+}
+
+func NewNetworkInsightsGraphData() *NetworkInsightsGraphData {
+	return &NetworkInsightsGraphData{
+		Nodes: make(map[string]*NetworkInsightsNode, 0),
+		Edges: make(map[string]*NetworkInsightsEdge, 0),
+	}
+}
+
+func (nigd *NetworkInsightsGraphData) AddNode(node *NetworkInsightsNode) {
+	key := node.Key()
+	if _, ok := nigd.Nodes[key]; ok {
+		nigd.Nodes[key].add(node)
+	} else {
+		nigd.Nodes[key] = node
+	}
+}
+
+func (nigd *NetworkInsightsGraphData) AddEdge(edge *NetworkInsightsEdge) {
+	key := edge.Key()
+	if _, ok := nigd.Edges[key]; ok {
+		nigd.Edges[key].add(edge)
+	} else {
+		nigd.Edges[key] = edge
+	}
+}
+
+func (nigd *NetworkInsightsGraphData) ToGraphDataResponse() *NetworkInsightsGraphDataResponse {
+	if nigd == nil {
+		return nil
+	}
+	nigdr := &NetworkInsightsGraphDataResponse{
+		Nodes: nigd.Nodes,
+		Edges: make([]*NetworkInsightsEdge, 0),
+	}
+	for _, edge := range nigd.Edges {
+		nigdr.Edges = append(nigdr.Edges, edge)
+	}
+	return nigdr
+}
+
+// Converts the given NetworkInsightSetRange into the graph data to be presented.
+func (nisr *NetworkInsightSetRange) ToNetworkInsightsGraphData(aggregateBy []NetworkInsightProperty) (*NetworkInsightsGraphData, error) {
+	nis, err := nisr.newAccumulation(aggregateBy)
+	if err != nil {
+		return nil, err
+	}
+
+	if nis == nil {
+		return nil, err
+	}
+	// No graph data for empty network insight set
+	if len(nis.NetworkInsights) == 0 {
+		return &NetworkInsightsGraphData{}, err
+	}
+	data := NewNetworkInsightsGraphData()
+
+	graphNodeType := ConvertNetworkInsightPropertiesToString(aggregateBy)
+	for _, ni := range nis.NetworkInsights {
+		// When using filtering with network insight details
+		// if network detail set is empty dont create node and edges
+		if len(ni.NetworkDetails) == 0 {
+			continue
+		}
+		key, err := ni.Key(aggregateBy)
+		if err != nil {
+			return &NetworkInsightsGraphData{}, err
+		}
+		baseNodeCost := 0.0
+		for _, nd := range ni.NetworkDetails {
+			destination := nd.EndPoint
+			// TO-DO: At this time everything will be cloud service, can be a simply if to make it like IP etc here!
+			networkType := NetworkInsightsCloudService
+			sameZone, sameRegion, internet := getNetworkBools(nd.TrafficType)
+			if destination == NetworkInsightsServiceUnknown {
+				if internet {
+					destination = NetworkInsightsInternet
+				}
+				if sameZone {
+					destination = NetworkInsightsSameZone
+				}
+				if sameRegion {
+					destination = NetworkInsightsSameRegion
+				}
+				networkType = NetworkInsightsOther
+			}
+
+			var communicatingNode *NetworkInsightsNode
+			var edge *NetworkInsightsEdge
+			if nd.TrafficDirection == NetworkTrafficDirectionNone {
+				log.Warnf("ToNetworkInsightsGraphData: unknown traffic type: %s", nd.TrafficDirection)
+				continue
+			}
+			if nd.TrafficDirection == NetworkTrafficDirectionEgress {
+				communicatingNode = NewNetworkInsightsNode(destination, networkType, 0, nd.Bytes, 0, internet, sameZone, sameRegion)
+				edge = NewNetworkInsightsEdge(key, destination, nd.Cost, nd.Bytes)
+			}
+			if nd.TrafficDirection == NetworkTrafficDirectionIngress {
+				communicatingNode = NewNetworkInsightsNode(destination, networkType, nd.Bytes, 0, 0, internet, sameZone, sameRegion)
+				edge = NewNetworkInsightsEdge(destination, key, nd.Cost, nd.Bytes)
+			}
+			baseNodeCost += nd.Cost
+			data.AddNode(communicatingNode)
+			data.AddEdge(edge)
+		}
+		baseNode := NewNetworkInsightsNode(key, graphNodeType, ni.GetTotalEgressByte(), ni.GetTotalIngressByte(), baseNodeCost, false, false, false)
+		data.AddNode(baseNode)
+	}
+
+	return data, nil
+}
+
+func getNetworkBools(networkType NetworkTrafficType) (sameZone bool, sameRegion bool, internet bool) {
+	switch networkType {
+	case NetworkTrafficTypeCrossZone:
+		return false, true, false
+	case NetworkTrafficTypeCrossRegion:
+		return false, false, false
+	case NetworkTrafficTypeInternet:
+		return false, false, true
+	default:
+		log.Warnf("unknown string passed: %s defaulting to internet", networkType)
+		return false, false, true
+	}
+}

+ 121 - 0
core/pkg/opencost/networkinsightmatcher.go

@@ -0,0 +1,121 @@
+package opencost
+
+import (
+	"fmt"
+
+	ast "github.com/opencost/opencost/core/pkg/filter/ast"
+	"github.com/opencost/opencost/core/pkg/filter/matcher"
+	nfilter "github.com/opencost/opencost/core/pkg/filter/networkinsight"
+	"github.com/opencost/opencost/core/pkg/filter/transform"
+)
+
+// NetworkInsightMatcher is a matcher implementation for NetworkInsightSet,
+// compiled using the matcher.MatchCompiler.
+type NetworkInsightMatcher matcher.Matcher[*NetworkInsight]
+
+// NewNetworkInsightMatchCompiler creates a new instance of a
+// matcher.MatchCompiler[NetworkInsight] which can be used to compile filter.Filter
+// ASTs into matcher.Matcher[NetworkInsight] implementations.
+//
+// If the label config is nil, the compiler will fail to compile alias filters
+// if any are present in the AST.
+//
+// If storage interfaces every support querying natively by alias (e.g. if a
+// data store contained a "product" attribute on an Asset row), that should
+// be handled by a purpose-built AST compiler.
+
+func NewNetworkInsightMatchCompiler() *matcher.MatchCompiler[*NetworkInsight] {
+	passes := []transform.CompilerPass{}
+
+	return matcher.NewMatchCompiler(
+		networkInsightFieldMap,
+		networkInsightSliceFieldMap,
+		networkInsightMapFieldMap,
+		passes...,
+	)
+}
+
+// Maps fields from a network insight to a string value based on an identifier
+func networkInsightFieldMap(ni *NetworkInsight, identifier ast.Identifier) (string, error) {
+	if ni == nil {
+		return "", fmt.Errorf("cannot map field for nil Network insight")
+	}
+
+	if identifier.Field == nil {
+		return "", fmt.Errorf("cannot map field from identifier with nil field")
+	}
+
+	switch nfilter.NetworkInsightField(identifier.Field.Name) {
+	case nfilter.FieldClusterID:
+		return ni.Cluster, nil
+	case nfilter.FieldNamespace:
+		return ni.Namespace, nil
+	case nfilter.FieldPod:
+		return ni.Pod, nil
+	}
+
+	return "", fmt.Errorf("Failed to find string identifier on Network Insight: %s", identifier.Field.Name)
+}
+
+// Maps slice fields from a network insight to a []string value based on an identifier
+func networkInsightSliceFieldMap(ni *NetworkInsight, identifier ast.Identifier) ([]string, error) {
+	return nil, fmt.Errorf("NetworkInsights have no slice fields")
+}
+
+// Maps map fields from a network insight to a map[string]string value based on an identifier
+func networkInsightMapFieldMap(ni *NetworkInsight, identifier ast.Identifier) (map[string]string, error) {
+	return nil, fmt.Errorf("NetworkInsights have no map fields")
+}
+
+// NetworkInsightMatcher is a matcher implementation for NetworkInsightSet,
+// compiled using the matcher.MatchCompiler.
+type NetworkInsightDetailMatcher matcher.Matcher[*NetworkDetail]
+
+// NewNetworkInsightMatchCompiler creates a new instance of a
+// matcher.MatchCompiler[NetworkInsight] which can be used to compile filter.Filter
+// ASTs into matcher.Matcher[NetworkInsight] implementations.
+//
+// If the label config is nil, the compiler will fail to compile alias filters
+// if any are present in the AST.
+//
+// If storage interfaces every support querying natively by alias (e.g. if a
+// data store contained a "product" attribute on an Asset row), that should
+// be handled by a purpose-built AST compiler.
+
+func NewNetworkInsightDetailMatchCompiler() *matcher.MatchCompiler[*NetworkDetail] {
+	passes := []transform.CompilerPass{}
+
+	return matcher.NewMatchCompiler(
+		networkInsightDetailFieldMap,
+		networkInsightDetailSliceFieldMap,
+		networkInsightDetailMapFieldMap,
+		passes...,
+	)
+}
+
+// Maps fields from a network insight to a string value based on an identifier
+func networkInsightDetailFieldMap(nd *NetworkDetail, identifier ast.Identifier) (string, error) {
+	if nd == nil {
+		return "", fmt.Errorf("cannot map field for nil Network insight")
+	}
+
+	if identifier.Field == nil {
+		return "", fmt.Errorf("cannot map field from identifier with nil field")
+	}
+
+	switch nfilter.NetworkInsightDetailField(identifier.Field.Name) {
+	case nfilter.FieldEndPoint:
+		return nd.EndPoint, nil
+	}
+	return "", fmt.Errorf("Failed to find string identifier on Network Insight: %s", identifier.Field.Name)
+}
+
+// Maps slice fields from a network insight to a []string value based on an identifier
+func networkInsightDetailSliceFieldMap(nd *NetworkDetail, identifier ast.Identifier) ([]string, error) {
+	return nil, fmt.Errorf("NetworkInsights have no slice fields")
+}
+
+// Maps map fields from a network insight to a map[string]string value based on an identifier
+func networkInsightDetailMapFieldMap(nd *NetworkDetail, identifier ast.Identifier) (map[string]string, error) {
+	return nil, fmt.Errorf("NetworkInsights have no map fields")
+}

+ 734 - 1
core/pkg/opencost/opencost_codecs.go

@@ -18,7 +18,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/opencost/opencost/core/pkg/util"
+	util "github.com/opencost/opencost/core/pkg/util"
 )
 
 const (
@@ -34,6 +34,9 @@ const (
 )
 
 const (
+	// NetworkInsightCodecVersion is used for any resources listed in the NetworkInsight version set
+	NetworkInsightCodecVersion uint8 = 1
+
 	// DefaultCodecVersion is used for any resources listed in the Default version set
 	DefaultCodecVersion uint8 = 18
 
@@ -75,6 +78,9 @@ var typeMap map[string]reflect.Type = map[string]reflect.Type{
 	"LbAllocation":          reflect.TypeOf((*LbAllocation)(nil)).Elem(),
 	"LoadBalancer":          reflect.TypeOf((*LoadBalancer)(nil)).Elem(),
 	"Network":               reflect.TypeOf((*Network)(nil)).Elem(),
+	"NetworkDetail":         reflect.TypeOf((*NetworkDetail)(nil)).Elem(),
+	"NetworkInsight":        reflect.TypeOf((*NetworkInsight)(nil)).Elem(),
+	"NetworkInsightSet":     reflect.TypeOf((*NetworkInsightSet)(nil)).Elem(),
 	"Node":                  reflect.TypeOf((*Node)(nil)).Elem(),
 	"NodeOverhead":          reflect.TypeOf((*NodeOverhead)(nil)).Elem(),
 	"PVAllocation":          reflect.TypeOf((*PVAllocation)(nil)).Elem(),
@@ -5995,6 +6001,733 @@ func (target *Network) UnmarshalBinaryWithContext(ctx *DecodingContext) (err err
 	return nil
 }
 
+//--------------------------------------------------------------------------
+//  NetworkDetail
+//--------------------------------------------------------------------------
+
+// MarshalBinary serializes the internal properties of this NetworkDetail instance
+// into a byte array
+func (target *NetworkDetail) MarshalBinary() (data []byte, err error) {
+	ctx := &EncodingContext{
+		Buffer: util.NewBuffer(),
+		Table:  nil,
+	}
+
+	e := target.MarshalBinaryWithContext(ctx)
+	if e != nil {
+		return nil, e
+	}
+
+	encBytes := ctx.Buffer.Bytes()
+	return encBytes, nil
+}
+
+// MarshalBinaryWithContext serializes the internal properties of this NetworkDetail instance
+// into a byte array leveraging a predefined context.
+func (target *NetworkDetail) MarshalBinaryWithContext(ctx *EncodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	buff.WriteUInt8(NetworkInsightCodecVersion) // version
+
+	buff.WriteFloat64(target.Cost)  // write float64
+	buff.WriteFloat64(target.Bytes) // write float64
+	if ctx.IsStringTable() {
+		a := ctx.Table.AddOrGet(target.EndPoint)
+		buff.WriteInt(a) // write table index
+	} else {
+		buff.WriteString(target.EndPoint) // write string
+	}
+	// --- [begin][write][alias](NetworkTrafficDirection) ---
+	if ctx.IsStringTable() {
+		b := ctx.Table.AddOrGet(string(target.TrafficDirection))
+		buff.WriteInt(b) // write table index
+	} else {
+		buff.WriteString(string(target.TrafficDirection)) // write string
+	}
+	// --- [end][write][alias](NetworkTrafficDirection) ---
+
+	// --- [begin][write][alias](NetworkTrafficType) ---
+	if ctx.IsStringTable() {
+		c := ctx.Table.AddOrGet(string(target.TrafficType))
+		buff.WriteInt(c) // write table index
+	} else {
+		buff.WriteString(string(target.TrafficType)) // write string
+	}
+	// --- [end][write][alias](NetworkTrafficType) ---
+
+	return nil
+}
+
+// UnmarshalBinary uses the data passed byte array to set all the internal properties of
+// the NetworkDetail type
+func (target *NetworkDetail) UnmarshalBinary(data []byte) error {
+	var table []string
+	buff := util.NewBufferFromBytes(data)
+
+	// string table header validation
+	if isBinaryTag(data, BinaryTagStringTable) {
+		buff.ReadBytes(len(BinaryTagStringTable)) // strip tag length
+		tl := buff.ReadInt()                      // table length
+		if tl > 0 {
+			table = make([]string, tl, tl)
+			for i := 0; i < tl; i++ {
+				table[i] = buff.ReadString()
+			}
+		}
+	}
+
+	ctx := &DecodingContext{
+		Buffer: buff,
+		Table:  table,
+	}
+
+	err := target.UnmarshalBinaryWithContext(ctx)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// UnmarshalBinaryWithContext uses the context containing a string table and binary buffer to set all the internal properties of
+// the NetworkDetail type
+func (target *NetworkDetail) UnmarshalBinaryWithContext(ctx *DecodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	version := buff.ReadUInt8()
+
+	if version > NetworkInsightCodecVersion {
+		return fmt.Errorf("Invalid Version Unmarshaling NetworkDetail. Expected %d or less, got %d", NetworkInsightCodecVersion, version)
+	}
+
+	a := buff.ReadFloat64() // read float64
+	target.Cost = a
+
+	b := buff.ReadFloat64() // read float64
+	target.Bytes = b
+
+	var d string
+	if ctx.IsStringTable() {
+		e := buff.ReadInt() // read string index
+		d = ctx.Table[e]
+	} else {
+		d = buff.ReadString() // read string
+	}
+	c := d
+	target.EndPoint = c
+
+	// --- [begin][read][alias](NetworkTrafficDirection) ---
+	var f string
+	var h string
+	if ctx.IsStringTable() {
+		k := buff.ReadInt() // read string index
+		h = ctx.Table[k]
+	} else {
+		h = buff.ReadString() // read string
+	}
+	g := h
+	f = g
+
+	target.TrafficDirection = NetworkTrafficDirection(f)
+	// --- [end][read][alias](NetworkTrafficDirection) ---
+
+	// --- [begin][read][alias](NetworkTrafficType) ---
+	var l string
+	var n string
+	if ctx.IsStringTable() {
+		o := buff.ReadInt() // read string index
+		n = ctx.Table[o]
+	} else {
+		n = buff.ReadString() // read string
+	}
+	m := n
+	l = m
+
+	target.TrafficType = NetworkTrafficType(l)
+	// --- [end][read][alias](NetworkTrafficType) ---
+
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  NetworkInsight
+//--------------------------------------------------------------------------
+
+// MarshalBinary serializes the internal properties of this NetworkInsight instance
+// into a byte array
+func (target *NetworkInsight) MarshalBinary() (data []byte, err error) {
+	ctx := &EncodingContext{
+		Buffer: util.NewBuffer(),
+		Table:  nil,
+	}
+
+	e := target.MarshalBinaryWithContext(ctx)
+	if e != nil {
+		return nil, e
+	}
+
+	encBytes := ctx.Buffer.Bytes()
+	return encBytes, nil
+}
+
+// MarshalBinaryWithContext serializes the internal properties of this NetworkInsight instance
+// into a byte array leveraging a predefined context.
+func (target *NetworkInsight) MarshalBinaryWithContext(ctx *EncodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	buff.WriteUInt8(NetworkInsightCodecVersion) // version
+
+	if ctx.IsStringTable() {
+		a := ctx.Table.AddOrGet(target.Cluster)
+		buff.WriteInt(a) // write table index
+	} else {
+		buff.WriteString(target.Cluster) // write string
+	}
+	if ctx.IsStringTable() {
+		b := ctx.Table.AddOrGet(target.Namespace)
+		buff.WriteInt(b) // write table index
+	} else {
+		buff.WriteString(target.Namespace) // write string
+	}
+	if ctx.IsStringTable() {
+		c := ctx.Table.AddOrGet(target.Controller)
+		buff.WriteInt(c) // write table index
+	} else {
+		buff.WriteString(target.Controller) // write string
+	}
+	if ctx.IsStringTable() {
+		d := ctx.Table.AddOrGet(target.Pod)
+		buff.WriteInt(d) // write table index
+	} else {
+		buff.WriteString(target.Pod) // write string
+	}
+	if ctx.IsStringTable() {
+		e := ctx.Table.AddOrGet(target.Node)
+		buff.WriteInt(e) // write table index
+	} else {
+		buff.WriteString(target.Node) // write string
+	}
+	if target.Labels == nil {
+		buff.WriteUInt8(uint8(0)) // write nil byte
+	} else {
+		buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+		// --- [begin][write][map](map[string]string) ---
+		buff.WriteInt(len(target.Labels)) // map length
+		for v, z := range target.Labels {
+			if ctx.IsStringTable() {
+				f := ctx.Table.AddOrGet(v)
+				buff.WriteInt(f) // write table index
+			} else {
+				buff.WriteString(v) // write string
+			}
+			if ctx.IsStringTable() {
+				g := ctx.Table.AddOrGet(z)
+				buff.WriteInt(g) // write table index
+			} else {
+				buff.WriteString(z) // write string
+			}
+		}
+		// --- [end][write][map](map[string]string) ---
+
+	}
+	if ctx.IsStringTable() {
+		h := ctx.Table.AddOrGet(target.Region)
+		buff.WriteInt(h) // write table index
+	} else {
+		buff.WriteString(target.Region) // write string
+	}
+	if ctx.IsStringTable() {
+		k := ctx.Table.AddOrGet(target.Zone)
+		buff.WriteInt(k) // write table index
+	} else {
+		buff.WriteString(target.Zone) // write string
+	}
+	buff.WriteFloat64(target.NetworkTotalCost)       // write float64
+	buff.WriteFloat64(target.NetworkCrossZoneCost)   // write float64
+	buff.WriteFloat64(target.NetworkCrossRegionCost) // write float64
+	buff.WriteFloat64(target.NetworkInternetCost)    // write float64
+	// --- [begin][write][alias](NetworkDetailsSet) ---
+	if map[string]*NetworkDetail(target.NetworkDetails) == nil {
+		buff.WriteUInt8(uint8(0)) // write nil byte
+	} else {
+		buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+		// --- [begin][write][map](map[string]*NetworkDetail) ---
+		buff.WriteInt(len(map[string]*NetworkDetail(target.NetworkDetails))) // map length
+		for vv, zz := range map[string]*NetworkDetail(target.NetworkDetails) {
+			if ctx.IsStringTable() {
+				l := ctx.Table.AddOrGet(vv)
+				buff.WriteInt(l) // write table index
+			} else {
+				buff.WriteString(vv) // write string
+			}
+			if zz == nil {
+				buff.WriteUInt8(uint8(0)) // write nil byte
+			} else {
+				buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+				// --- [begin][write][struct](NetworkDetail) ---
+				buff.WriteInt(0) // [compatibility, unused]
+				errA := zz.MarshalBinaryWithContext(ctx)
+				if errA != nil {
+					return errA
+				}
+				// --- [end][write][struct](NetworkDetail) ---
+
+			}
+		}
+		// --- [end][write][map](map[string]*NetworkDetail) ---
+
+	}
+	// --- [end][write][alias](NetworkDetailsSet) ---
+
+	return nil
+}
+
+// UnmarshalBinary uses the data passed byte array to set all the internal properties of
+// the NetworkInsight type
+func (target *NetworkInsight) UnmarshalBinary(data []byte) error {
+	var table []string
+	buff := util.NewBufferFromBytes(data)
+
+	// string table header validation
+	if isBinaryTag(data, BinaryTagStringTable) {
+		buff.ReadBytes(len(BinaryTagStringTable)) // strip tag length
+		tl := buff.ReadInt()                      // table length
+		if tl > 0 {
+			table = make([]string, tl, tl)
+			for i := 0; i < tl; i++ {
+				table[i] = buff.ReadString()
+			}
+		}
+	}
+
+	ctx := &DecodingContext{
+		Buffer: buff,
+		Table:  table,
+	}
+
+	err := target.UnmarshalBinaryWithContext(ctx)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// UnmarshalBinaryWithContext uses the context containing a string table and binary buffer to set all the internal properties of
+// the NetworkInsight type
+func (target *NetworkInsight) UnmarshalBinaryWithContext(ctx *DecodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	version := buff.ReadUInt8()
+
+	if version > NetworkInsightCodecVersion {
+		return fmt.Errorf("Invalid Version Unmarshaling NetworkInsight. Expected %d or less, got %d", NetworkInsightCodecVersion, version)
+	}
+
+	var b string
+	if ctx.IsStringTable() {
+		c := buff.ReadInt() // read string index
+		b = ctx.Table[c]
+	} else {
+		b = buff.ReadString() // read string
+	}
+	a := b
+	target.Cluster = a
+
+	var e string
+	if ctx.IsStringTable() {
+		f := buff.ReadInt() // read string index
+		e = ctx.Table[f]
+	} else {
+		e = buff.ReadString() // read string
+	}
+	d := e
+	target.Namespace = d
+
+	var h string
+	if ctx.IsStringTable() {
+		k := buff.ReadInt() // read string index
+		h = ctx.Table[k]
+	} else {
+		h = buff.ReadString() // read string
+	}
+	g := h
+	target.Controller = g
+
+	var m string
+	if ctx.IsStringTable() {
+		n := buff.ReadInt() // read string index
+		m = ctx.Table[n]
+	} else {
+		m = buff.ReadString() // read string
+	}
+	l := m
+	target.Pod = l
+
+	var p string
+	if ctx.IsStringTable() {
+		q := buff.ReadInt() // read string index
+		p = ctx.Table[q]
+	} else {
+		p = buff.ReadString() // read string
+	}
+	o := p
+	target.Node = o
+
+	if buff.ReadUInt8() == uint8(0) {
+		target.Labels = nil
+	} else {
+		// --- [begin][read][map](map[string]string) ---
+		s := buff.ReadInt() // map len
+		r := make(map[string]string, s)
+		for i := 0; i < s; i++ {
+			var v string
+			var u string
+			if ctx.IsStringTable() {
+				w := buff.ReadInt() // read string index
+				u = ctx.Table[w]
+			} else {
+				u = buff.ReadString() // read string
+			}
+			t := u
+			v = t
+
+			var z string
+			var y string
+			if ctx.IsStringTable() {
+				aa := buff.ReadInt() // read string index
+				y = ctx.Table[aa]
+			} else {
+				y = buff.ReadString() // read string
+			}
+			x := y
+			z = x
+
+			r[v] = z
+		}
+		target.Labels = r
+		// --- [end][read][map](map[string]string) ---
+
+	}
+	var cc string
+	if ctx.IsStringTable() {
+		dd := buff.ReadInt() // read string index
+		cc = ctx.Table[dd]
+	} else {
+		cc = buff.ReadString() // read string
+	}
+	bb := cc
+	target.Region = bb
+
+	var ff string
+	if ctx.IsStringTable() {
+		gg := buff.ReadInt() // read string index
+		ff = ctx.Table[gg]
+	} else {
+		ff = buff.ReadString() // read string
+	}
+	ee := ff
+	target.Zone = ee
+
+	hh := buff.ReadFloat64() // read float64
+	target.NetworkTotalCost = hh
+
+	kk := buff.ReadFloat64() // read float64
+	target.NetworkCrossZoneCost = kk
+
+	ll := buff.ReadFloat64() // read float64
+	target.NetworkCrossRegionCost = ll
+
+	mm := buff.ReadFloat64() // read float64
+	target.NetworkInternetCost = mm
+
+	// --- [begin][read][alias](NetworkDetailsSet) ---
+	var nn map[string]*NetworkDetail
+	if buff.ReadUInt8() == uint8(0) {
+		nn = nil
+	} else {
+		// --- [begin][read][map](map[string]*NetworkDetail) ---
+		pp := buff.ReadInt() // map len
+		oo := make(map[string]*NetworkDetail, pp)
+		for j := 0; j < pp; j++ {
+			var vv string
+			var rr string
+			if ctx.IsStringTable() {
+				ss := buff.ReadInt() // read string index
+				rr = ctx.Table[ss]
+			} else {
+				rr = buff.ReadString() // read string
+			}
+			qq := rr
+			vv = qq
+
+			var zz *NetworkDetail
+			if buff.ReadUInt8() == uint8(0) {
+				zz = nil
+			} else {
+				// --- [begin][read][struct](NetworkDetail) ---
+				tt := &NetworkDetail{}
+				buff.ReadInt() // [compatibility, unused]
+				errA := tt.UnmarshalBinaryWithContext(ctx)
+				if errA != nil {
+					return errA
+				}
+				zz = tt
+				// --- [end][read][struct](NetworkDetail) ---
+
+			}
+			oo[vv] = zz
+		}
+		nn = oo
+		// --- [end][read][map](map[string]*NetworkDetail) ---
+
+	}
+	target.NetworkDetails = NetworkDetailsSet(nn)
+	// --- [end][read][alias](NetworkDetailsSet) ---
+
+	return nil
+}
+
+//--------------------------------------------------------------------------
+//  NetworkInsightSet
+//--------------------------------------------------------------------------
+
+// MarshalBinary serializes the internal properties of this NetworkInsightSet instance
+// into a byte array
+func (target *NetworkInsightSet) MarshalBinary() (data []byte, err error) {
+	ctx := &EncodingContext{
+		Buffer: util.NewBuffer(),
+		Table:  nil,
+	}
+
+	e := target.MarshalBinaryWithContext(ctx)
+	if e != nil {
+		return nil, e
+	}
+
+	encBytes := ctx.Buffer.Bytes()
+	return encBytes, nil
+}
+
+// MarshalBinaryWithContext serializes the internal properties of this NetworkInsightSet instance
+// into a byte array leveraging a predefined context.
+func (target *NetworkInsightSet) MarshalBinaryWithContext(ctx *EncodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	buff.WriteUInt8(NetworkInsightCodecVersion) // version
+
+	if target.NetworkInsights == nil {
+		buff.WriteUInt8(uint8(0)) // write nil byte
+	} else {
+		buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+		// --- [begin][write][map](map[string]*NetworkInsight) ---
+		buff.WriteInt(len(target.NetworkInsights)) // map length
+		for v, z := range target.NetworkInsights {
+			if ctx.IsStringTable() {
+				a := ctx.Table.AddOrGet(v)
+				buff.WriteInt(a) // write table index
+			} else {
+				buff.WriteString(v) // write string
+			}
+			if z == nil {
+				buff.WriteUInt8(uint8(0)) // write nil byte
+			} else {
+				buff.WriteUInt8(uint8(1)) // write non-nil byte
+
+				// --- [begin][write][struct](NetworkInsight) ---
+				buff.WriteInt(0) // [compatibility, unused]
+				errA := z.MarshalBinaryWithContext(ctx)
+				if errA != nil {
+					return errA
+				}
+				// --- [end][write][struct](NetworkInsight) ---
+
+			}
+		}
+		// --- [end][write][map](map[string]*NetworkInsight) ---
+
+	}
+	// --- [begin][write][struct](Window) ---
+	buff.WriteInt(0) // [compatibility, unused]
+	errB := target.Window.MarshalBinaryWithContext(ctx)
+	if errB != nil {
+		return errB
+	}
+	// --- [end][write][struct](Window) ---
+
+	return nil
+}
+
+// UnmarshalBinary uses the data passed byte array to set all the internal properties of
+// the NetworkInsightSet type
+func (target *NetworkInsightSet) UnmarshalBinary(data []byte) error {
+	var table []string
+	buff := util.NewBufferFromBytes(data)
+
+	// string table header validation
+	if isBinaryTag(data, BinaryTagStringTable) {
+		buff.ReadBytes(len(BinaryTagStringTable)) // strip tag length
+		tl := buff.ReadInt()                      // table length
+		if tl > 0 {
+			table = make([]string, tl, tl)
+			for i := 0; i < tl; i++ {
+				table[i] = buff.ReadString()
+			}
+		}
+	}
+
+	ctx := &DecodingContext{
+		Buffer: buff,
+		Table:  table,
+	}
+
+	err := target.UnmarshalBinaryWithContext(ctx)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// UnmarshalBinaryWithContext uses the context containing a string table and binary buffer to set all the internal properties of
+// the NetworkInsightSet type
+func (target *NetworkInsightSet) UnmarshalBinaryWithContext(ctx *DecodingContext) (err error) {
+	// panics are recovered and propagated as errors
+	defer func() {
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				err = e
+			} else if s, ok := r.(string); ok {
+				err = fmt.Errorf("Unexpected panic: %s", s)
+			} else {
+				err = fmt.Errorf("Unexpected panic: %+v", r)
+			}
+		}
+	}()
+
+	buff := ctx.Buffer
+	version := buff.ReadUInt8()
+
+	if version > NetworkInsightCodecVersion {
+		return fmt.Errorf("Invalid Version Unmarshaling NetworkInsightSet. Expected %d or less, got %d", NetworkInsightCodecVersion, version)
+	}
+
+	if buff.ReadUInt8() == uint8(0) {
+		target.NetworkInsights = nil
+	} else {
+		// --- [begin][read][map](map[string]*NetworkInsight) ---
+		b := buff.ReadInt() // map len
+		a := make(map[string]*NetworkInsight, b)
+		for i := 0; i < b; i++ {
+			var v string
+			var d string
+			if ctx.IsStringTable() {
+				e := buff.ReadInt() // read string index
+				d = ctx.Table[e]
+			} else {
+				d = buff.ReadString() // read string
+			}
+			c := d
+			v = c
+
+			var z *NetworkInsight
+			if buff.ReadUInt8() == uint8(0) {
+				z = nil
+			} else {
+				// --- [begin][read][struct](NetworkInsight) ---
+				f := &NetworkInsight{}
+				buff.ReadInt() // [compatibility, unused]
+				errA := f.UnmarshalBinaryWithContext(ctx)
+				if errA != nil {
+					return errA
+				}
+				z = f
+				// --- [end][read][struct](NetworkInsight) ---
+
+			}
+			a[v] = z
+		}
+		target.NetworkInsights = a
+		// --- [end][read][map](map[string]*NetworkInsight) ---
+
+	}
+	// --- [begin][read][struct](Window) ---
+	g := &Window{}
+	buff.ReadInt() // [compatibility, unused]
+	errB := g.UnmarshalBinaryWithContext(ctx)
+	if errB != nil {
+		return errB
+	}
+	target.Window = *g
+	// --- [end][read][struct](Window) ---
+
+	return nil
+}
+
 //--------------------------------------------------------------------------
 //  Node
 //--------------------------------------------------------------------------