|  | 
|  | 1 | +package readiness | 
|  | 2 | + | 
|  | 3 | +import ( | 
|  | 4 | +	"fmt" | 
|  | 5 | +	"slices" | 
|  | 6 | +	"strings" | 
|  | 7 | + | 
|  | 8 | +	appsv1 "k8s.io/api/apps/v1" | 
|  | 9 | +	batchv1 "k8s.io/api/batch/v1" | 
|  | 10 | +	networkingv1 "k8s.io/api/networking/v1" | 
|  | 11 | +	gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" | 
|  | 12 | +) | 
|  | 13 | + | 
|  | 14 | +type CheckResult []string | 
|  | 15 | + | 
|  | 16 | +func (r CheckResult) IsReady() bool { | 
|  | 17 | +	return len(r) == 0 | 
|  | 18 | +} | 
|  | 19 | + | 
|  | 20 | +func (r CheckResult) Message() string { | 
|  | 21 | +	return strings.Join(r, ", ") | 
|  | 22 | +} | 
|  | 23 | + | 
|  | 24 | +func NewReadyResult() CheckResult { | 
|  | 25 | +	return CheckResult{} | 
|  | 26 | +} | 
|  | 27 | + | 
|  | 28 | +func NewNotReadyResult(message string) CheckResult { | 
|  | 29 | +	return CheckResult{message} | 
|  | 30 | +} | 
|  | 31 | + | 
|  | 32 | +func NewFailedResult(err error) CheckResult { | 
|  | 33 | +	return NewNotReadyResult(fmt.Sprintf("readiness check failed: %v", err)) | 
|  | 34 | +} | 
|  | 35 | + | 
|  | 36 | +func Aggregate(results ...CheckResult) CheckResult { | 
|  | 37 | +	return slices.Concat(results...) | 
|  | 38 | +} | 
|  | 39 | + | 
|  | 40 | +// CheckDeployment checks the readiness of a deployment. | 
|  | 41 | +func CheckDeployment(dp *appsv1.Deployment) CheckResult { | 
|  | 42 | +	if dp.Status.ObservedGeneration < dp.Generation { | 
|  | 43 | +		return NewNotReadyResult(fmt.Sprintf("deployment %s/%s not ready: observed generation outdated", dp.Namespace, dp.Name)) | 
|  | 44 | +	} | 
|  | 45 | + | 
|  | 46 | +	var specReplicas int32 = 0 | 
|  | 47 | +	if dp.Spec.Replicas != nil { | 
|  | 48 | +		specReplicas = *dp.Spec.Replicas | 
|  | 49 | +	} | 
|  | 50 | + | 
|  | 51 | +	if dp.Generation != dp.Status.ObservedGeneration || | 
|  | 52 | +		specReplicas != dp.Status.Replicas || | 
|  | 53 | +		specReplicas != dp.Status.UpdatedReplicas || | 
|  | 54 | +		specReplicas != dp.Status.AvailableReplicas { | 
|  | 55 | +		return NewNotReadyResult(fmt.Sprintf("deployment %s/%s is not ready", dp.Namespace, dp.Name)) | 
|  | 56 | +	} | 
|  | 57 | + | 
|  | 58 | +	return NewReadyResult() | 
|  | 59 | +} | 
|  | 60 | + | 
|  | 61 | +// CheckJob checks the completion status of a Kubernetes Job. | 
|  | 62 | +func CheckJob(job *batchv1.Job) CheckResult { | 
|  | 63 | +	for _, condition := range job.Status.Conditions { | 
|  | 64 | +		if condition.Type == batchv1.JobComplete && condition.Status == "True" { | 
|  | 65 | +			return NewReadyResult() | 
|  | 66 | +		} | 
|  | 67 | +		if condition.Type == batchv1.JobFailed && condition.Status == "True" { | 
|  | 68 | +			return NewNotReadyResult(fmt.Sprintf("job %s/%s failed: %s", job.Namespace, job.Name, condition.Message)) | 
|  | 69 | +		} | 
|  | 70 | +	} | 
|  | 71 | + | 
|  | 72 | +	return NewNotReadyResult(fmt.Sprintf("job %s/%s is not completed", job.Namespace, job.Name)) | 
|  | 73 | +} | 
|  | 74 | + | 
|  | 75 | +// CheckJobFailed checks if a Kubernetes Job has failed. | 
|  | 76 | +func CheckJobFailed(job *batchv1.Job) bool { | 
|  | 77 | +	for _, condition := range job.Status.Conditions { | 
|  | 78 | +		if condition.Type == batchv1.JobFailed && condition.Status == "True" { | 
|  | 79 | +			return true | 
|  | 80 | +		} | 
|  | 81 | +	} | 
|  | 82 | +	return false | 
|  | 83 | +} | 
|  | 84 | + | 
|  | 85 | +// CheckIngress checks the readiness of a Kubernetes Ingress. | 
|  | 86 | +func CheckIngress(ingress *networkingv1.Ingress) CheckResult { | 
|  | 87 | +	if len(ingress.Status.LoadBalancer.Ingress) > 0 { | 
|  | 88 | +		return NewReadyResult() | 
|  | 89 | +	} | 
|  | 90 | +	return NewNotReadyResult(fmt.Sprintf("ingress %s/%s is not ready: no load balancer ingress entries", ingress.Namespace, ingress.Name)) | 
|  | 91 | +} | 
|  | 92 | + | 
|  | 93 | +// CheckGateway checks the readiness of a Kubernetes Gateway. | 
|  | 94 | +func CheckGateway(gateway *gatewayv1.Gateway) CheckResult { | 
|  | 95 | +	for _, condition := range gateway.Status.Conditions { | 
|  | 96 | +		if condition.Type == string(gatewayv1.GatewayConditionReady) && condition.Status == "True" { | 
|  | 97 | +			return NewReadyResult() | 
|  | 98 | +		} | 
|  | 99 | +	} | 
|  | 100 | +	return NewNotReadyResult(fmt.Sprintf("gateway %s/%s is not ready: no ready condition with status True", gateway.Namespace, gateway.Name)) | 
|  | 101 | +} | 
0 commit comments