Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion pkg/extension/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (e *Extension) registerOperations() {
Description: "Timeout duration (e.g., 60s, 5m, default: 60s)",
},
},
Required: []string{"apiVersion", "kind", "metadata", "condition"},
Required: []string{"apiVersion", "kind", "metadata"},
}),
),
e.handleWait,
Expand Down
26 changes: 23 additions & 3 deletions pkg/extension/wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ func (e *Extension) handleWait(ctx context.Context, req *sdk.OperationRequest) (
}

condition, _ := args["condition"].(string)
if condition == "" {
return sdk.Failure(fmt.Errorf("condition is required")), nil
}

status, _ := args["status"].(string)
if status == "" {
Expand Down Expand Up @@ -66,6 +63,11 @@ func (e *Extension) handleWait(ctx context.Context, req *sdk.OperationRequest) (
return false, nil // Keep polling on transient errors
}

// No condition specified — resource exists, that's enough
if condition == "" {
return true, nil
}

conditions, found, err := unstructured.NestedSlice(obj.Object, "status", "conditions")
if err != nil || !found {
lastStatus = "NoConditions"
Expand All @@ -92,6 +94,16 @@ func (e *Extension) handleWait(ctx context.Context, req *sdk.OperationRequest) (
})

if err != nil {
if condition == "" {
e.LogError(ctx, "Resource wait timed out", map[string]any{
"kind": ref.kind,
"name": ref.name,
})
return sdk.FailureWithMessage(
fmt.Sprintf("Resource %s/%s not found", ref.kind, ref.name),
fmt.Errorf("timed out waiting for %s/%s to exist", ref.kind, ref.name),
), nil
}
e.LogError(ctx, "Condition wait timed out", map[string]any{
"kind": ref.kind,
"name": ref.name,
Expand All @@ -104,6 +116,14 @@ func (e *Extension) handleWait(ctx context.Context, req *sdk.OperationRequest) (
), nil
}

if condition == "" {
e.LogInfo(ctx, "Resource exists", map[string]any{
"kind": ref.kind,
"name": ref.name,
})
return sdk.Success(fmt.Sprintf("%s/%s exists", ref.kind, ref.name)), nil
}

e.LogInfo(ctx, "Condition met", map[string]any{
"kind": ref.kind,
"name": ref.name,
Expand Down
91 changes: 89 additions & 2 deletions pkg/extension/wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package extension

import (
"context"
"fmt"
"testing"

"github.com/mcpchecker/mcpchecker/pkg/extension/sdk"
Expand Down Expand Up @@ -65,13 +66,99 @@ func TestHandleWait(t *testing.T) {
wantSuccess: false,
},
{
name: "missing condition field",
name: "missing condition field checks existence",
args: map[string]any{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]any{"name": "test"},
"timeout": "1s",
},
client: &mockClient{
getFn: func(ctx context.Context, gvr schema.GroupVersionResource, name, namespace string) (*unstructured.Unstructured, error) {
return &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]any{"name": "test"},
},
}, nil
},
},
wantSuccess: true,
},
{
name: "no condition - resource not found times out",
args: map[string]any{
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]any{"name": "test"},
"timeout": "1s",
},
client: &mockClient{
getFn: func(ctx context.Context, gvr schema.GroupVersionResource, name, namespace string) (*unstructured.Unstructured, error) {
return nil, fmt.Errorf("not found")
},
},
wantSuccess: false,
},
{
name: "no condition - succeeds when resource exists",
args: map[string]any{
"apiVersion": "networking.istio.io/v1",
"kind": "Gateway",
"metadata": map[string]any{"name": "my-gateway", "namespace": "istio-system"},
"timeout": "2s",
},
client: &mockClient{
getFn: func(ctx context.Context, gvr schema.GroupVersionResource, name, namespace string) (*unstructured.Unstructured, error) {
return &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "networking.istio.io/v1",
"kind": "Gateway",
"metadata": map[string]any{
"name": "my-gateway",
"namespace": "istio-system",
},
"spec": map[string]any{
"selector": map[string]any{
"istio": "ingressgateway",
},
},
},
}, nil
},
},
wantSuccess: true,
},
{
name: "resource without status.conditions times out (e.g. Istio Gateway)",
args: map[string]any{
"apiVersion": "networking.istio.io/v1",
"kind": "Gateway",
"metadata": map[string]any{"name": "my-gateway", "namespace": "istio-system"},
"condition": "Available",
"status": "True",
"timeout": "2s",
},
client: &mockClient{
getFn: func(ctx context.Context, gvr schema.GroupVersionResource, name, namespace string) (*unstructured.Unstructured, error) {
return &unstructured.Unstructured{
Object: map[string]any{
"apiVersion": "networking.istio.io/v1",
"kind": "Gateway",
"metadata": map[string]any{
"name": "my-gateway",
"namespace": "istio-system",
},
"spec": map[string]any{
"selector": map[string]any{
"istio": "ingressgateway",
},
},
},
}, nil
},
},
client: &mockClient{},
wantSuccess: false,
},
}
Expand Down