Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate health for services #1707

Merged
merged 5 commits into from
Oct 8, 2020
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions pkg/kubernetes/status/health.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,44 @@ func IsHealthy(obj runtime.Object) (healthy bool, msg string, err error) {
}
return false, fmt.Sprintf("namespace %s is not active: %s", obj.Name, obj.Status.Phase), nil

case *corev1.Service:
return serviceHealthy(obj)

// unless we build logic for what a healthy object is, assume it's healthy when created.
default:
return true, fmt.Sprintf("unknown type %s is marked healthy by default", reflect.TypeOf(obj)), nil
}
}

// Service health depends on the ingress type.
// To be considered healthy, a service needs to be accessible by its cluster IP.
// If the service is load-balanced, the balancer need to have an ingress defined.
// Adapted from https://github.com/helm/helm/blob/v3.3.4/pkg/kube/wait.go#L185.
func serviceHealthy(obj *corev1.Service) (healthy bool, msg string, err error) {
nfnt marked this conversation as resolved.
Show resolved Hide resolved
// ExternalName services are external to cluster. KUDO shouldn't be checking to see if they're 'ready' (i.e. have an IP set).
if obj.Spec.Type == corev1.ServiceTypeExternalName {
return true, fmt.Sprintf("external name service %s/%s is marked healthy", obj.Namespace, obj.Name), nil
}

if obj.Spec.ClusterIP == "" {
return false, fmt.Sprintf("service %s/%s does not have cluster IP address", obj.Namespace, obj.Name), nil
}

// Check if the service has a LoadBalancer and that balancer has an Ingress defined.
if obj.Spec.Type == corev1.ServiceTypeLoadBalancer {
if len(obj.Spec.ExternalIPs) > 0 {
return true, fmt.Sprintf("service %s/%s has external IP addresses (%v), marked healthy", obj.Namespace, obj.Name, obj.Spec.ExternalIPs), nil
}

if obj.Status.LoadBalancer.Ingress == nil {
return false, fmt.Sprintf("service %s/%s does not have load balancer ingress IP address", obj.Namespace, obj.Name), nil
}
}

// If none of the above conditions are met, we can assume that the service is healthy.
return true, fmt.Sprintf("service %s/%s is marked healthy", obj.Namespace, obj.Name), nil
}

func toUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
unstructMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
if err != nil {
Expand Down