| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- package opencost
- import (
- "reflect"
- "testing"
- )
- func TestAllocationPropsIntersection(t *testing.T) {
- cases := map[string]struct {
- allocationProps1 *AllocationProperties
- allocationProps2 *AllocationProperties
- expected *AllocationProperties
- }{
- "intersection two allocation properties with empty labels/annotations": {
- allocationProps1: &AllocationProperties{
- Labels: map[string]string{},
- Annotations: map[string]string{},
- },
- allocationProps2: &AllocationProperties{
- Labels: map[string]string{},
- Annotations: map[string]string{},
- },
- expected: &AllocationProperties{
- Labels: nil,
- Annotations: nil,
- NamespaceLabels: map[string]string{},
- NamespaceAnnotations: map[string]string{},
- },
- },
- "nil intersection": {
- allocationProps1: nil,
- allocationProps2: nil,
- expected: nil,
- },
- "intersection, with labels/annotations, no aggregated metadata": {
- allocationProps1: &AllocationProperties{
- AggregatedMetadata: false,
- Node: "node1",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- },
- allocationProps2: &AllocationProperties{
- AggregatedMetadata: false,
- Node: "node1",
- Labels: map[string]string{"key3": "val3"},
- Annotations: map[string]string{"key4": "val4"},
- },
- expected: &AllocationProperties{
- AggregatedMetadata: false,
- Node: "node1",
- Labels: nil,
- Annotations: nil,
- NamespaceLabels: map[string]string{},
- NamespaceAnnotations: map[string]string{},
- },
- },
- "intersection, with labels/annotations, same values": {
- allocationProps1: &AllocationProperties{
- AggregatedMetadata: false,
- ControllerKind: "controller1",
- Namespace: "ns1",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- },
- allocationProps2: &AllocationProperties{
- AggregatedMetadata: true,
- ControllerKind: "controller2",
- Namespace: "ns1",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- },
- expected: &AllocationProperties{
- AggregatedMetadata: true,
- Namespace: "ns1",
- ControllerKind: "",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- NamespaceLabels: map[string]string{},
- NamespaceAnnotations: map[string]string{},
- },
- },
- "intersection, with labels/annotations, special case container": {
- allocationProps1: &AllocationProperties{
- AggregatedMetadata: false,
- Container: UnmountedSuffix,
- Namespace: "ns1",
- Labels: map[string]string{},
- Annotations: map[string]string{},
- },
- allocationProps2: &AllocationProperties{
- AggregatedMetadata: true,
- Container: "container3",
- Namespace: "ns1",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- },
- expected: &AllocationProperties{
- AggregatedMetadata: true,
- Namespace: "ns1",
- ControllerKind: "",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- NamespaceLabels: map[string]string{},
- NamespaceAnnotations: map[string]string{},
- },
- },
- "test services are nulled when intersecting": {
- allocationProps1: &AllocationProperties{
- AggregatedMetadata: false,
- Container: UnmountedSuffix,
- Namespace: "ns1",
- Services: []string{
- "cool",
- },
- Labels: map[string]string{},
- Annotations: map[string]string{},
- },
- allocationProps2: &AllocationProperties{
- AggregatedMetadata: true,
- Container: "container3",
- Namespace: "ns1",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- },
- expected: &AllocationProperties{
- AggregatedMetadata: true,
- Namespace: "ns1",
- ControllerKind: "",
- Labels: map[string]string{"key1": "val1"},
- Annotations: map[string]string{"key2": "val2"},
- NamespaceLabels: map[string]string{},
- NamespaceAnnotations: map[string]string{},
- },
- },
- }
- for name, tc := range cases {
- t.Run(name, func(t *testing.T) {
- actual := tc.allocationProps1.Intersection(tc.allocationProps2)
- if !reflect.DeepEqual(actual, tc.expected) {
- t.Fatalf("test case %s: expected %+v; got %+v", name, tc.expected, actual)
- }
- })
- }
- }
- func TestGenerateKey(t *testing.T) {
- customOwnerLabelConfig := NewLabelConfig()
- customOwnerLabelConfig.OwnerLabel = "example_com_project"
- cases := map[string]struct {
- aggregate []string
- allocationProps *AllocationProperties
- labelConfig *LabelConfig
- expected string
- }{
- "aggregate by owner without owner labels": {
- aggregate: []string{"owner"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"app": "cost-analyzer"},
- Annotations: map[string]string{"owner": "test owner 123"},
- },
- expected: "test owner 123",
- },
- "aggregate by owner without labels": {
- aggregate: []string{"owner"},
- allocationProps: &AllocationProperties{
- Annotations: map[string]string{"owner": "test owner 123"},
- },
- expected: "test owner 123",
- },
- "aggregate by owner with owner label and annotation": {
- aggregate: []string{"owner"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"owner": "owner-label"},
- Annotations: map[string]string{"owner": "owner-annotation"},
- },
- expected: "owner-label",
- },
- "aggregate by environment with environment label and annotation": {
- aggregate: []string{"environment"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"env": "environment-label"},
- Annotations: map[string]string{"env": "environment-annotation"},
- },
- expected: "environment-label",
- },
- "aggregate by department with department label and annotation": {
- aggregate: []string{"department"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"department": "department-label"},
- Annotations: map[string]string{"department": "department-annotation"},
- },
- expected: "department-label",
- },
- "aggregate by team with team label and annotation": {
- aggregate: []string{"team"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"team": "team-label"},
- Annotations: map[string]string{"team": "team-annotation"},
- },
- expected: "team-label",
- },
- "aggregate by product with product label and annotation": {
- aggregate: []string{"product"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"app": "product-label"},
- Annotations: map[string]string{"app": "product-annotation"},
- },
- expected: "product-label",
- },
- "aggregate by product and owner with multiple labels and annotations": {
- aggregate: []string{"product", "owner"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"app": "product-label", "owner": "owner-label", "team": "team-label"},
- Annotations: map[string]string{"app": "product-annotation", "owner": "owner-annotation", "team": "team-annotation"},
- },
- expected: "product-label/owner-label",
- },
- "user test": {
- aggregate: []string{"owner"},
- allocationProps: &AllocationProperties{
- Labels: map[string]string{"app_kubernetes_io_name": "x-mongo", "example_com_service_owner": "x", "component": "primary", "controller_revision_hash": "x-mongo-primary-x", "kubernetes_io_metadata_name": "app-microservices", "name": "app-microservices", "statefulset_kubernetes_io_pod_name": "x-mongo-primary-0"},
- Annotations: map[string]string{"example_com_project": "redacted"},
- },
- labelConfig: customOwnerLabelConfig,
- expected: "redacted",
- },
- }
- for name, tc := range cases {
- t.Run(name, func(t *testing.T) {
- lc := NewLabelConfig()
- if tc.labelConfig != nil {
- lc = tc.labelConfig
- }
- result := tc.allocationProps.GenerateKey(tc.aggregate, lc)
- if !reflect.DeepEqual(result, tc.expected) {
- t.Fatalf("expected %+v; got %+v", tc.expected, result)
- }
- })
- }
- }
- func TestIntersection(t *testing.T) {
- propsEmpty := AllocationProperties{}
- propsMedium := AllocationProperties{
- Cluster: "cluster1",
- Node: "Node1",
- Container: "container1",
- Controller: "controller1",
- ControllerKind: "controllerkind1",
- Namespace: "ns1",
- Pod: "pod1",
- Services: []string{"service1"},
- ProviderID: "provider1",
- }
- propsFull := AllocationProperties{
- Cluster: "cluster2",
- Node: "Node2",
- Container: "container2",
- Controller: "controller2",
- ControllerKind: "controllerkind2",
- Namespace: "ns2",
- Pod: "pod2",
- Services: []string{"service2"},
- ProviderID: "provider2",
- NamespaceLabels: AllocationLabels{"key1": "value1"},
- NamespaceAnnotations: AllocationAnnotations{"key2": "value2", "key5": "value5"},
- Labels: AllocationLabels{"key3": "value3"},
- Annotations: AllocationAnnotations{"key4": "value4"},
- }
- // Case 1: no intersection
- // expect empty result object
- testObj1 := AllocationProperties{}
- result := testObj1.Intersection(&propsEmpty)
- if !result.Equal(&propsEmpty) {
- t.Fatalf("Case 1: expected empty object, no intersection")
- }
- // Case 2: Only has labels/annotations
- // expect empty result object
- testObj2 := AllocationProperties{
- Labels: map[string]string{"app": "product-label-light"},
- Annotations: map[string]string{"app": "product-annotation-light"},
- }
- result = testObj2.Intersection(&propsMedium)
- if !result.Equal(&propsEmpty) {
- t.Fatalf("Case 2: expected empty object, no intersection")
- }
- // Case 3: Has non-label/annotations set
- // expect all non label/annotation/service string array fields to be unset
- // different container names should be omitted
- testObj3 := AllocationProperties{
- Cluster: "cluster1",
- Node: "Node1",
- Container: "container2",
- Controller: "controller1",
- ControllerKind: "controllerkind1",
- Namespace: "ns1",
- Pod: "pod1",
- Services: []string{"service1"},
- ProviderID: "provider1",
- }
- expectedResult := AllocationProperties{
- Cluster: "cluster1",
- Node: "Node1",
- Controller: "controller1",
- ControllerKind: "controllerkind1",
- Namespace: "ns1",
- Pod: "pod1",
- ProviderID: "provider1",
- }
- result = testObj3.Intersection(&propsMedium)
- if !result.Equal(&expectedResult) {
- t.Fatalf("Case 3: expected output %v does not match actual output %v", expectedResult, result)
- }
- // Case 4: Copy over NamespaceLabels/Annots when namespace is the same
- testObj4 := AllocationProperties{
- Cluster: "cluster2",
- Node: "NodeX",
- Container: "containerX",
- Controller: "controllerX",
- ControllerKind: "controllerkindX",
- Namespace: "ns2",
- Pod: "podX",
- Services: []string{"serviceX"},
- ProviderID: "providerX",
- NamespaceLabels: AllocationLabels{"key1": "value1"},
- NamespaceAnnotations: AllocationAnnotations{"key2": "value2", "key5": "value5"},
- Labels: AllocationLabels{"key3": "value3"},
- Annotations: AllocationAnnotations{"key4": "value4"},
- }
- expectedResult = AllocationProperties{
- Cluster: "cluster2",
- Namespace: "ns2",
- NamespaceLabels: AllocationLabels{"key1": "value1"},
- NamespaceAnnotations: AllocationAnnotations{"key2": "value2", "key5": "value5"},
- }
- result = testObj4.Intersection(&propsFull)
- if !result.Equal(&expectedResult) {
- t.Fatalf("Case 4: expected output %v does not match actual output %v", expectedResult, result)
- }
- }
|