github.com/newrelic/go-agent@v3.26.0+incompatible/internal/utilization/utilization_test.go (about) 1 // Copyright 2020 New Relic Corporation. All rights reserved. 2 // SPDX-License-Identifier: Apache-2.0 3 4 package utilization 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "errors" 10 "net/http" 11 "testing" 12 13 "github.com/newrelic/go-agent/internal/crossagent" 14 "github.com/newrelic/go-agent/internal/logger" 15 ) 16 17 func TestJSONMarshalling(t *testing.T) { 18 ramInitializer := new(uint64) 19 *ramInitializer = 1024 20 actualProcessors := 4 21 configProcessors := 16 22 u := Data{ 23 MetadataVersion: metadataVersion, 24 LogicalProcessors: &actualProcessors, 25 RAMMiB: ramInitializer, 26 Hostname: "localhost", 27 Vendors: &vendors{ 28 AWS: &aws{ 29 InstanceID: "8BADFOOD", 30 InstanceType: "t2.micro", 31 AvailabilityZone: "us-west-1", 32 }, 33 Docker: &docker{ID: "47cbd16b77c50cbf71401"}, 34 Kubernetes: &kubernetes{Host: "10.96.0.1"}, 35 }, 36 Config: &override{ 37 LogicalProcessors: &configProcessors, 38 }, 39 } 40 41 expect := `{ 42 "metadata_version": 5, 43 "logical_processors": 4, 44 "total_ram_mib": 1024, 45 "hostname": "localhost", 46 "config": { 47 "logical_processors": 16 48 }, 49 "vendors": { 50 "aws": { 51 "instanceId": "8BADFOOD", 52 "instanceType": "t2.micro", 53 "availabilityZone": "us-west-1" 54 }, 55 "docker": { 56 "id": "47cbd16b77c50cbf71401" 57 }, 58 "kubernetes": { 59 "kubernetes_service_host": "10.96.0.1" 60 } 61 } 62 }` 63 64 j, err := json.MarshalIndent(u, "", "\t") 65 if err != nil { 66 t.Error(err) 67 } 68 if string(j) != expect { 69 t.Errorf("strings don't match; \nexpected: %s\n actual: %s\n", expect, string(j)) 70 } 71 72 // Test that we marshal not-present values to nil. 73 u.RAMMiB = nil 74 u.Hostname = "" 75 u.Config = nil 76 expect = `{ 77 "metadata_version": 5, 78 "logical_processors": 4, 79 "total_ram_mib": null, 80 "hostname": "", 81 "vendors": { 82 "aws": { 83 "instanceId": "8BADFOOD", 84 "instanceType": "t2.micro", 85 "availabilityZone": "us-west-1" 86 }, 87 "docker": { 88 "id": "47cbd16b77c50cbf71401" 89 }, 90 "kubernetes": { 91 "kubernetes_service_host": "10.96.0.1" 92 } 93 } 94 }` 95 96 j, err = json.MarshalIndent(u, "", "\t") 97 if err != nil { 98 t.Error(err) 99 } 100 if string(j) != expect { 101 t.Errorf("strings don't match; \nexpected: %s\n actual: %s\n", expect, string(j)) 102 } 103 104 } 105 106 type errorRoundTripper struct{ error } 107 108 func (e errorRoundTripper) RoundTrip(*http.Request) (*http.Response, error) { return nil, e } 109 110 // Smoke test the Gather method. 111 func TestUtilizationHash(t *testing.T) { 112 config := Config{ 113 DetectAWS: true, 114 DetectAzure: true, 115 DetectDocker: true, 116 } 117 client := &http.Client{ 118 Transport: errorRoundTripper{errors.New("timed out")}, 119 } 120 data := gatherWithClient(config, logger.ShimLogger{}, client) 121 if data.MetadataVersion == 0 || 122 nil == data.LogicalProcessors || 123 0 == *data.LogicalProcessors || 124 data.RAMMiB == nil || 125 *data.RAMMiB == 0 || 126 data.Hostname == "" { 127 t.Errorf("utilization data unexpected fields: %+v", data) 128 } 129 } 130 131 func TestOverrideFromConfig(t *testing.T) { 132 testcases := []struct { 133 config Config 134 expect string 135 }{ 136 {Config{}, `null`}, 137 {Config{LogicalProcessors: 16}, `{"logical_processors":16}`}, 138 {Config{TotalRAMMIB: 1024}, `{"total_ram_mib":1024}`}, 139 {Config{BillingHostname: "localhost"}, `{"hostname":"localhost"}`}, 140 {Config{ 141 LogicalProcessors: 16, 142 TotalRAMMIB: 1024, 143 BillingHostname: "localhost", 144 }, `{"logical_processors":16,"total_ram_mib":1024,"hostname":"localhost"}`}, 145 } 146 147 for _, tc := range testcases { 148 ov := overrideFromConfig(tc.config) 149 js, err := json.Marshal(ov) 150 if nil != err { 151 t.Error(tc.expect, err) 152 continue 153 } 154 if string(js) != tc.expect { 155 t.Error(tc.expect, string(js)) 156 } 157 } 158 } 159 160 type utilizationCrossAgentTestcase struct { 161 Name string `json:"testname"` 162 RAMMIB *uint64 `json:"input_total_ram_mib"` 163 LogicalProcessors *int `json:"input_logical_processors"` 164 Hostname string `json:"input_hostname"` 165 FullHostname string `json:"input_full_hostname"` 166 Addresses []string `json:"input_ip_address"` 167 BootID string `json:"input_boot_id"` 168 AWSID string `json:"input_aws_id"` 169 AWSType string `json:"input_aws_type"` 170 AWSZone string `json:"input_aws_zone"` 171 AzureLocation string `json:"input_azure_location"` 172 AzureName string `json:"input_azure_name"` 173 AzureID string `json:"input_azure_id"` 174 AzureSize string `json:"input_azure_size"` 175 GCPID json.Number `json:"input_gcp_id"` 176 GCPType string `json:"input_gcp_type"` 177 GCPName string `json:"input_gcp_name"` 178 GCPZone string `json:"input_gcp_zone"` 179 PCFGUID string `json:"input_pcf_guid"` 180 PCFIP string `json:"input_pcf_ip"` 181 PCFMemLimit string `json:"input_pcf_mem_limit"` 182 ExpectedOutput json.RawMessage `json:"expected_output_json"` 183 Config struct { 184 LogicalProcessors json.RawMessage `json:"NEW_RELIC_UTILIZATION_LOGICAL_PROCESSORS"` 185 RAWMMIB json.RawMessage `json:"NEW_RELIC_UTILIZATION_TOTAL_RAM_MIB"` 186 Hostname string `json:"NEW_RELIC_UTILIZATION_BILLING_HOSTNAME"` 187 KubernetesHost string `json:"KUBERNETES_SERVICE_HOST"` 188 } `json:"input_environment_variables"` 189 } 190 191 func crossAgentVendors(tc utilizationCrossAgentTestcase) *vendors { 192 v := &vendors{} 193 194 if tc.AWSID != "" && tc.AWSType != "" && tc.AWSZone != "" { 195 v.AWS = &aws{ 196 InstanceID: tc.AWSID, 197 InstanceType: tc.AWSType, 198 AvailabilityZone: tc.AWSZone, 199 } 200 v.AWS.validate() 201 } 202 203 if tc.AzureLocation != "" && tc.AzureName != "" && tc.AzureID != "" && tc.AzureSize != "" { 204 v.Azure = &azure{ 205 Location: tc.AzureLocation, 206 Name: tc.AzureName, 207 VMID: tc.AzureID, 208 VMSize: tc.AzureSize, 209 } 210 v.Azure.validate() 211 } 212 213 if tc.GCPID.String() != "" && tc.GCPType != "" && tc.GCPName != "" && tc.GCPZone != "" { 214 v.GCP = &gcp{ 215 ID: numericString(tc.GCPID.String()), 216 MachineType: tc.GCPType, 217 Name: tc.GCPName, 218 Zone: tc.GCPZone, 219 } 220 v.GCP.validate() 221 } 222 223 if tc.PCFIP != "" && tc.PCFGUID != "" && tc.PCFMemLimit != "" { 224 v.PCF = &pcf{ 225 InstanceGUID: tc.PCFGUID, 226 InstanceIP: tc.PCFIP, 227 MemoryLimit: tc.PCFMemLimit, 228 } 229 v.PCF.validate() 230 } 231 232 gatherKubernetes(v, func(key string) string { 233 if key == "KUBERNETES_SERVICE_HOST" { 234 return tc.Config.KubernetesHost 235 } 236 return "" 237 }) 238 239 if v.isEmpty() { 240 return nil 241 } 242 return v 243 } 244 245 func compactJSON(js []byte) []byte { 246 buf := new(bytes.Buffer) 247 if err := json.Compact(buf, js); err != nil { 248 return nil 249 } 250 return buf.Bytes() 251 } 252 253 func runUtilizationCrossAgentTestcase(t *testing.T, tc utilizationCrossAgentTestcase) { 254 var ConfigRAWMMIB int 255 if nil != tc.Config.RAWMMIB { 256 json.Unmarshal(tc.Config.RAWMMIB, &ConfigRAWMMIB) 257 } 258 var ConfigLogicalProcessors int 259 if nil != tc.Config.LogicalProcessors { 260 json.Unmarshal(tc.Config.LogicalProcessors, &ConfigLogicalProcessors) 261 } 262 263 cfg := Config{ 264 LogicalProcessors: ConfigLogicalProcessors, 265 TotalRAMMIB: ConfigRAWMMIB, 266 BillingHostname: tc.Config.Hostname, 267 } 268 269 data := &Data{ 270 MetadataVersion: metadataVersion, 271 LogicalProcessors: tc.LogicalProcessors, 272 RAMMiB: tc.RAMMIB, 273 Hostname: tc.Hostname, 274 BootID: tc.BootID, 275 Vendors: crossAgentVendors(tc), 276 Config: overrideFromConfig(cfg), 277 FullHostname: tc.FullHostname, 278 Addresses: tc.Addresses, 279 } 280 281 js, err := json.Marshal(data) 282 if nil != err { 283 t.Error(tc.Name, err) 284 } 285 286 expect := string(compactJSON(tc.ExpectedOutput)) 287 if string(js) != expect { 288 t.Error(tc.Name, string(js), expect) 289 } 290 } 291 292 func TestUtilizationCrossAgent(t *testing.T) { 293 var tcs []utilizationCrossAgentTestcase 294 295 input, err := crossagent.ReadFile(`utilization/utilization_json.json`) 296 if nil != err { 297 t.Fatal(err) 298 } 299 300 err = json.Unmarshal(input, &tcs) 301 if nil != err { 302 t.Fatal(err) 303 } 304 for _, tc := range tcs { 305 runUtilizationCrossAgentTestcase(t, tc) 306 } 307 } 308 309 func TestVendorsIsEmpty(t *testing.T) { 310 v := &vendors{} 311 312 if !v.isEmpty() { 313 t.Fatal("default vendors does not register as empty") 314 } 315 316 v.AWS = &aws{} 317 v.Azure = &azure{} 318 v.PCF = &pcf{} 319 v.GCP = &gcp{} 320 if v.isEmpty() { 321 t.Fatal("non-empty vendors registers as empty") 322 } 323 324 var nilVendors *vendors 325 if !nilVendors.isEmpty() { 326 t.Fatal("nil vendors should be empty") 327 } 328 }