From a33c2f852b3f30b9b479b077a6c5699e59ca654e Mon Sep 17 00:00:00 2001 From: Enrico Giorio Date: Fri, 18 Oct 2024 18:32:51 +0000 Subject: [PATCH] fix: Fix generation of externalIPs facet Return a string of IPs when possible, otherwise a single string: - "None" when service != LoadBalancer and there are no external IPs - "Pending" when service is a LoadBalancer and there are no external IPs - "Unknown" when service is of an unknown type --- .../processor.go | 2 +- .../serviceactions.go | 84 ++++++--- .../serviceactions_test.go | 32 +++- .../testdata/serviceLoadBalancer.json | 97 ++++++++++ .../serviceLoadBalancerIngressEvent.json | 176 ------------------ .../testdata/serviceUnknown.json | 97 ++++++++++ 6 files changed, 276 insertions(+), 212 deletions(-) create mode 100644 components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancer.json delete mode 100644 components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancerIngressEvent.json create mode 100644 components/processors/observek8sattributesprocessor/testdata/serviceUnknown.json diff --git a/components/processors/observek8sattributesprocessor/processor.go b/components/processors/observek8sattributesprocessor/processor.go index a58195307..de3b5a197 100644 --- a/components/processors/observek8sattributesprocessor/processor.go +++ b/components/processors/observek8sattributesprocessor/processor.go @@ -84,7 +84,7 @@ func newK8sEventsProcessor(logger *zap.Logger, cfg component.Config) *K8sEventsP NewEndpointsStatusAction(), }, serviceActions: []serviceAction{ - NewServiceLBIngressAction(), NewServiceSelectorAction(), NewServicePortsAction(), + NewServiceLBIngressAction(), NewServiceSelectorAction(), NewServicePortsAction(), NewServiceExternalIPsAction(), }, serviceAccountActions: []serviceAccountAction{ NewServiceAccountSecretsNamesAction(), NewServiceAccountSecretsAction(), NewServiceAccountImagePullSecretsAction(), diff --git a/components/processors/observek8sattributesprocessor/serviceactions.go b/components/processors/observek8sattributesprocessor/serviceactions.go index 1f05828e0..b71312c1f 100644 --- a/components/processors/observek8sattributesprocessor/serviceactions.go +++ b/components/processors/observek8sattributesprocessor/serviceactions.go @@ -9,21 +9,13 @@ import ( ) const ( - ServiceLBIngressAttributeKey = "loadBalancerIngress" - ServicePortsAttributeKey = "ports" + ServiceLBIngressAttributeKey = "loadBalancerIngress" + ServiceExternalIPsAttributeKey = "externalIPs" + ServicePortsAttributeKey = "ports" ) -// ---------------------------------- Service "loadBalancerIngress" ---------------------------------- - -type ServiceLBIngressAction struct{} - -func NewServiceLBIngressAction() ServiceLBIngressAction { - return ServiceLBIngressAction{} -} - // loadBalancerStatusStringer behaves mostly like a string interface and converts the given status to a string. -// Adapted from https://github.com/kubernetes/kubernetes/blob/0d3b859af81e6a5f869a7766c8d45afd1c600b04/pkg/printers/internalversion/printers.go#L1275 -func loadBalancerStatusStringer(s corev1.LoadBalancerStatus) string { +func loadBalancerStatusStringer(s corev1.LoadBalancerStatus) []string { ingress := s.Ingress result := sets.NewString() for i := range ingress { @@ -34,25 +26,64 @@ func loadBalancerStatusStringer(s corev1.LoadBalancerStatus) string { } } - r := strings.Join(result.List(), ",") - return r + return result.List() } -// Adapted from https://github.com/kubernetes/kubernetes/blob/0d3b859af81e6a5f869a7766c8d45afd1c600b04/pkg/printers/internalversion/printers.go#L1293 -func getServiceExternalIP(svc *corev1.Service) string { - lbIps := loadBalancerStatusStringer(svc.Status.LoadBalancer) - if len(svc.Spec.ExternalIPs) > 0 { - results := []string{} - if len(lbIps) > 0 { - results = append(results, strings.Split(lbIps, ",")...) +// Generates the service's externalIPs, based on the service type +// Returns an array of externalIPs and TRUE if there is any external IP. +// Returns an array with a single string "None", "Pending" or "Unknown" and FALSE if there are no external IPs. +func getServiceExternalIPs(svc corev1.Service) ([]string, bool) { + switch svc.Spec.Type { + case corev1.ServiceTypeClusterIP: + if len(svc.Spec.ExternalIPs) > 0 { + return svc.Spec.ExternalIPs, true + } + return []string{"None"}, false + case corev1.ServiceTypeNodePort: + if len(svc.Spec.ExternalIPs) > 0 { + return svc.Spec.ExternalIPs, true } + return []string{"None"}, false + case corev1.ServiceTypeLoadBalancer: + results := loadBalancerStatusStringer(svc.Status.LoadBalancer) results = append(results, svc.Spec.ExternalIPs...) - return strings.Join(results, ",") + if len(results) > 0 { + return results, true + } + return []string{"Pending"}, false + case corev1.ServiceTypeExternalName: + return []string{svc.Spec.ExternalName}, true } - if len(lbIps) > 0 { - return lbIps + return []string{"Unknown"}, false +} + +type ServiceExternalIPsAction struct{} + +func NewServiceExternalIPsAction() ServiceExternalIPsAction { + return ServiceExternalIPsAction{} +} + +// Generates the Service "loadBalancerIngress" facet. +// We return an array of IPs (even if there's only a single IP) if there is at least one. +// We set the facet to a string (no array in this case) as follows: +// +// - "None" --> service != LoadBalancer and there are no external IPs +// - "Pending" --> service is a LoadBalancer and there are no external IPs +// - "Unknown" --> service is of an unknown type +func (ServiceExternalIPsAction) ComputeAttributes(service corev1.Service) (attributes, error) { + if externalIPs, ok := getServiceExternalIPs(service); ok { + return attributes{ServiceExternalIPsAttributeKey: externalIPs}, nil + } else { + return attributes{ServiceExternalIPsAttributeKey: externalIPs[0]}, nil } - return "" +} + +// ---------------------------------- Service "loadBalancerIngress" ---------------------------------- + +type ServiceLBIngressAction struct{} + +func NewServiceLBIngressAction() ServiceLBIngressAction { + return ServiceLBIngressAction{} } // Generates the Service "loadBalancerIngress" facet. @@ -61,7 +92,8 @@ func (ServiceLBIngressAction) ComputeAttributes(service corev1.Service) (attribu return attributes{}, nil } - return attributes{ServiceLBIngressAttributeKey: getServiceExternalIP(&service)}, nil + ingress := loadBalancerStatusStringer(service.Status.LoadBalancer) + return attributes{ServiceLBIngressAttributeKey: strings.Join(ingress, ",")}, nil } diff --git a/components/processors/observek8sattributesprocessor/serviceactions_test.go b/components/processors/observek8sattributesprocessor/serviceactions_test.go index 58ff70484..0b426278a 100644 --- a/components/processors/observek8sattributesprocessor/serviceactions_test.go +++ b/components/processors/observek8sattributesprocessor/serviceactions_test.go @@ -12,31 +12,45 @@ func TestServiceActions(t *testing.T) { }, }, { - name: "Service LB Ingress of non-LB service", + name: "LB Ingress (working Service)", + inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceLoadBalancer.json"), + expectedResults: []queryWithResult{ + {"observe_transform.facets.loadBalancerIngress", "someLoadBalancerIngressIdentifier.elb.us-west-2.amazonaws.com"}, + }, + }, + { + name: "Service ports", inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceClusterIPEvent.json"), expectedResults: []queryWithResult{ - {"observe_transform.facets.loadBalancerIngress", nil}, + {"observe_transform.facets.ports", "6379/TCP"}, + }, + }, + { + name: "External IPs", + inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceLoadBalancer.json"), + expectedResults: []queryWithResult{ + {"observe_transform.facets.externalIPs", []interface{}{"someLoadBalancerIngressIdentifier.elb.us-west-2.amazonaws.com"}}, }, }, { - name: "Service LB Ingress of initializing LB service", + name: "Pending LB", inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceLoadBalancerPendingEvent.json"), expectedResults: []queryWithResult{ - {"observe_transform.facets.loadBalancerIngress", ""}, + {"observe_transform.facets.externalIPs", "Pending"}, }, }, { - name: "Service LB Ingress of working LB service", - inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceLoadBalancerIngressEvent.json"), + name: "Unknown Service type", + inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceUnknown.json"), expectedResults: []queryWithResult{ - {"observe_transform.facets.loadBalancerIngress", "someLoadBalancerIdentifier.elb.us-west-2.amazonaws.com"}, + {"observe_transform.facets.externalIPs", "Unknown"}, }, }, { - name: "Service ports", + name: "No External IPs", inLogs: resourceLogsFromSingleJsonEvent("./testdata/serviceClusterIPEvent.json"), expectedResults: []queryWithResult{ - {"observe_transform.facets.ports", "6379/TCP"}, + {"observe_transform.facets.externalIPs", "None"}, }, }, } { diff --git a/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancer.json b/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancer.json new file mode 100644 index 000000000..bf57ddd3b --- /dev/null +++ b/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancer.json @@ -0,0 +1,97 @@ +{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"name\":\"frontend-external\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"targetPort\":8080}],\"selector\":{\"app\":\"frontend\"},\"type\":\"LoadBalancer\"}}\n" + }, + "creationTimestamp": "2024-07-10T05:34:45Z", + "finalizers": ["service.kubernetes.io/load-balancer-cleanup"], + "managedFields": [ + { + "apiVersion": "v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + } + }, + "f:spec": { + "f:allocateLoadBalancerNodePorts": {}, + "f:externalTrafficPolicy": {}, + "f:internalTrafficPolicy": {}, + "f:ports": { + ".": {}, + "k:{\"port\":80,\"protocol\":\"TCP\"}": { + ".": {}, + "f:name": {}, + "f:port": {}, + "f:protocol": {}, + "f:targetPort": {} + } + }, + "f:selector": {}, + "f:sessionAffinity": {}, + "f:type": {} + } + }, + "manager": "kubectl-client-side-apply", + "operation": "Update", + "time": "2024-07-10T05:34:45Z" + }, + { + "apiVersion": "v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:finalizers": { + ".": {}, + "v:\"service.kubernetes.io/load-balancer-cleanup\"": {} + } + }, + "f:status": { "f:loadBalancer": { "f:ingress": {} } } + }, + "manager": "aws-cloud-controller-manager", + "operation": "Update", + "subresource": "status", + "time": "2024-07-10T05:34:48Z" + } + ], + "name": "frontend-external", + "namespace": "default", + "resourceVersion": "16090058", + "uid": "8d10f98f-23ed-47cc-9617-9261b841f6cc" + }, + "spec": { + "allocateLoadBalancerNodePorts": true, + "clusterIP": "10.100.111.179", + "clusterIPs": ["10.100.111.179"], + "externalTrafficPolicy": "Cluster", + "internalTrafficPolicy": "Cluster", + "ipFamilies": ["IPv4"], + "ipFamilyPolicy": "SingleStack", + "ports": [ + { + "name": "http", + "nodePort": 32700, + "port": 80, + "protocol": "TCP", + "targetPort": 8080 + } + ], + "selector": { "app": "frontend" }, + "sessionAffinity": "None", + "type": "LoadBalancer" + }, + "status": { + "loadBalancer": { + "ingress": [ + { + "hostname": "someLoadBalancerIngressIdentifier.elb.us-west-2.amazonaws.com" + } + ] + } + } +} diff --git a/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancerIngressEvent.json b/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancerIngressEvent.json deleted file mode 100644 index acb474556..000000000 --- a/components/processors/observek8sattributesprocessor/testdata/serviceLoadBalancerIngressEvent.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "apiVersion": "v1", - "kind": "Service", - "metadata": { - "annotations": { - "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{\"o11y.io/audit\":\"2023-10-31\",\"o11y.io/team\":\"Infra\",\"observeinc.com/kubectl-nonce\":\"1\",\"service.beta.kubernetes.io/aws-load-balancer-backend-protocol\":\"tcp\",\"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout\":\"3600\",\"service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled\":\"true\",\"service.beta.kubernetes.io/aws-load-balancer-eip-allocations\":\"eipalloc-0dd802b2990f719c8,eipalloc-09cbf91fee3043bdc,eipalloc-09b3a54e1c8586fe6\",\"service.beta.kubernetes.io/aws-load-balancer-healthcheck-port\":\"31144\",\"service.beta.kubernetes.io/aws-load-balancer-type\":\"nlb\"},\"labels\":{\"app.kubernetes.io/component\":\"controller\",\"app.kubernetes.io/instance\":\"ingress-nginx-collector\",\"app.kubernetes.io/managed-by\":\"Helm\",\"app.kubernetes.io/name\":\"ingress-nginx-collector\",\"app.kubernetes.io/part-of\":\"ingress-nginx-collector\",\"app.kubernetes.io/version\":\"1.3.1\",\"helm.sh/chart\":\"ingress-nginx-4.2.5\",\"observeinc.com/app\":\"ingress-nginx-collector\",\"observeinc.com/environment\":\"eng\"},\"name\":\"ingress-nginx-collector\",\"namespace\":\"kube-ingress\"},\"spec\":{\"externalTrafficPolicy\":\"Local\",\"ipFamilies\":[\"IPv4\"],\"ipFamilyPolicy\":\"SingleStack\",\"ports\":[{\"appProtocol\":\"http\",\"name\":\"http\",\"nodePort\":31144,\"port\":80,\"protocol\":\"TCP\",\"targetPort\":\"http\"},{\"appProtocol\":\"https\",\"name\":\"https\",\"port\":443,\"protocol\":\"TCP\",\"targetPort\":\"https\"}],\"selector\":{\"app.kubernetes.io/component\":\"controller\",\"app.kubernetes.io/instance\":\"ingress-nginx-collector\",\"app.kubernetes.io/name\":\"ingress-nginx-collector\",\"observeinc.com/app\":\"ingress-nginx-collector\",\"observeinc.com/environment\":\"eng\"},\"type\":\"LoadBalancer\"}}\n", - "o11y.io/audit": "2023-10-31", - "o11y.io/team": "Infra", - "observeinc.com/kubectl-nonce": "1", - "service.beta.kubernetes.io/aws-load-balancer-backend-protocol": "tcp", - "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "3600", - "service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": "true", - "service.beta.kubernetes.io/aws-load-balancer-eip-allocations": "eipalloc-0dd802b2990f719c8,eipalloc-09cbf91fee3043bdc,eipalloc-09b3a54e1c8586fe6", - "service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": "31144", - "service.beta.kubernetes.io/aws-load-balancer-type": "nlb" - }, - "creationTimestamp": "2024-04-26T17:38:46Z", - "finalizers": [ - "service.kubernetes.io/load-balancer-cleanup" - ], - "labels": { - "app.kubernetes.io/component": "controller", - "app.kubernetes.io/instance": "ingress-nginx-collector", - "app.kubernetes.io/managed-by": "Helm", - "app.kubernetes.io/name": "ingress-nginx-collector", - "app.kubernetes.io/part-of": "ingress-nginx-collector", - "app.kubernetes.io/version": "1.3.1", - "helm.sh/chart": "ingress-nginx-4.2.5", - "observeinc.com/app": "ingress-nginx-collector", - "observeinc.com/environment": "eng" - }, - "managedFields": [ - { - "apiVersion": "v1", - "fieldsType": "FieldsV1", - "fieldsV1": { - "f:metadata": { - "f:annotations": { - ".": {}, - "f:kubectl.kubernetes.io/last-applied-configuration": {}, - "f:o11y.io/audit": {}, - "f:o11y.io/team": {}, - "f:observeinc.com/kubectl-nonce": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-backend-protocol": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-eip-allocations": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-healthcheck-port": {}, - "f:service.beta.kubernetes.io/aws-load-balancer-type": {} - }, - "f:labels": { - ".": {}, - "f:app.kubernetes.io/component": {}, - "f:app.kubernetes.io/instance": {}, - "f:app.kubernetes.io/managed-by": {}, - "f:app.kubernetes.io/name": {}, - "f:app.kubernetes.io/part-of": {}, - "f:app.kubernetes.io/version": {}, - "f:helm.sh/chart": {}, - "f:observeinc.com/app": {}, - "f:observeinc.com/environment": {} - } - }, - "f:spec": { - "f:allocateLoadBalancerNodePorts": {}, - "f:externalTrafficPolicy": {}, - "f:internalTrafficPolicy": {}, - "f:ipFamilies": {}, - "f:ipFamilyPolicy": {}, - "f:ports": { - ".": {}, - "k:{\"port\":443,\"protocol\":\"TCP\"}": { - ".": {}, - "f:appProtocol": {}, - "f:name": {}, - "f:port": {}, - "f:protocol": {}, - "f:targetPort": {} - }, - "k:{\"port\":80,\"protocol\":\"TCP\"}": { - ".": {}, - "f:appProtocol": {}, - "f:name": {}, - "f:nodePort": {}, - "f:port": {}, - "f:protocol": {}, - "f:targetPort": {} - } - }, - "f:selector": {}, - "f:sessionAffinity": {}, - "f:type": {} - } - }, - "manager": "kubectl-client-side-apply", - "operation": "Update", - "time": "2024-04-26T17:38:46Z" - }, - { - "apiVersion": "v1", - "fieldsType": "FieldsV1", - "fieldsV1": { - "f:metadata": { - "f:finalizers": { - ".": {}, - "v:\"service.kubernetes.io/load-balancer-cleanup\"": {} - } - }, - "f:status": { - "f:loadBalancer": { - "f:ingress": {} - } - } - }, - "manager": "aws-cloud-controller-manager", - "operation": "Update", - "subresource": "status", - "time": "2024-04-26T17:39:08Z" - } - ], - "name": "ingress-nginx-collector", - "namespace": "kube-ingress", - "resourceVersion": "739862", - "uid": "116d7794-d20c-4505-bda5-e722cdb326fc" - }, - "spec": { - "allocateLoadBalancerNodePorts": true, - "clusterIP": "172.19.137.140", - "clusterIPs": [ - "172.19.137.140" - ], - "externalTrafficPolicy": "Local", - "healthCheckNodePort": 30407, - "internalTrafficPolicy": "Cluster", - "ipFamilies": [ - "IPv4" - ], - "ipFamilyPolicy": "SingleStack", - "ports": [ - { - "appProtocol": "http", - "name": "http", - "nodePort": 31144, - "port": 80, - "protocol": "TCP", - "targetPort": "http" - }, - { - "appProtocol": "https", - "name": "https", - "nodePort": 30860, - "port": 443, - "protocol": "TCP", - "targetPort": "https" - } - ], - "selector": { - "app.kubernetes.io/component": "controller", - "app.kubernetes.io/instance": "ingress-nginx-collector", - "app.kubernetes.io/name": "ingress-nginx-collector", - "observeinc.com/app": "ingress-nginx-collector", - "observeinc.com/environment": "eng" - }, - "sessionAffinity": "None", - "type": "LoadBalancer" - }, - "status": { - "loadBalancer": { - "ingress": [ - { - "hostname": "someLoadBalancerIdentifier.elb.us-west-2.amazonaws.com" - } - ] - } - } -} \ No newline at end of file diff --git a/components/processors/observek8sattributesprocessor/testdata/serviceUnknown.json b/components/processors/observek8sattributesprocessor/testdata/serviceUnknown.json new file mode 100644 index 000000000..60a597d1b --- /dev/null +++ b/components/processors/observek8sattributesprocessor/testdata/serviceUnknown.json @@ -0,0 +1,97 @@ +{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "annotations": { + "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"name\":\"frontend-external\",\"namespace\":\"default\"},\"spec\":{\"ports\":[{\"name\":\"http\",\"port\":80,\"targetPort\":8080}],\"selector\":{\"app\":\"frontend\"},\"type\":\"LoadBalancer\"}}\n" + }, + "creationTimestamp": "2024-07-10T05:34:45Z", + "finalizers": ["service.kubernetes.io/load-balancer-cleanup"], + "managedFields": [ + { + "apiVersion": "v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:annotations": { + ".": {}, + "f:kubectl.kubernetes.io/last-applied-configuration": {} + } + }, + "f:spec": { + "f:allocateLoadBalancerNodePorts": {}, + "f:externalTrafficPolicy": {}, + "f:internalTrafficPolicy": {}, + "f:ports": { + ".": {}, + "k:{\"port\":80,\"protocol\":\"TCP\"}": { + ".": {}, + "f:name": {}, + "f:port": {}, + "f:protocol": {}, + "f:targetPort": {} + } + }, + "f:selector": {}, + "f:sessionAffinity": {}, + "f:type": {} + } + }, + "manager": "kubectl-client-side-apply", + "operation": "Update", + "time": "2024-07-10T05:34:45Z" + }, + { + "apiVersion": "v1", + "fieldsType": "FieldsV1", + "fieldsV1": { + "f:metadata": { + "f:finalizers": { + ".": {}, + "v:\"service.kubernetes.io/load-balancer-cleanup\"": {} + } + }, + "f:status": { "f:loadBalancer": { "f:ingress": {} } } + }, + "manager": "aws-cloud-controller-manager", + "operation": "Update", + "subresource": "status", + "time": "2024-07-10T05:34:48Z" + } + ], + "name": "frontend-external", + "namespace": "default", + "resourceVersion": "16090058", + "uid": "8d10f98f-23ed-47cc-9617-9261b841f6cc" + }, + "spec": { + "allocateLoadBalancerNodePorts": true, + "clusterIP": "10.100.111.179", + "clusterIPs": ["10.100.111.179"], + "externalTrafficPolicy": "Cluster", + "internalTrafficPolicy": "Cluster", + "ipFamilies": ["IPv4"], + "ipFamilyPolicy": "SingleStack", + "ports": [ + { + "name": "http", + "nodePort": 32700, + "port": 80, + "protocol": "TCP", + "targetPort": 8080 + } + ], + "selector": { "app": "frontend" }, + "sessionAffinity": "None", + "type": "SomeUnknownServiceType" + }, + "status": { + "loadBalancer": { + "ingress": [ + { + "hostname": "someLoadBalancerIngressIdentifier.elb.us-west-2.amazonaws.com" + } + ] + } + } +}