Skip to content
Open
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
26 changes: 22 additions & 4 deletions cmd/root/root_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,23 @@ func (customDeadlineExceededError) Error() string {
func (customDeadlineExceededError) Timeout() bool { return true }
func (customDeadlineExceededError) Temporary() bool { return true }

// effectiveTimeout returns the timeout to use for the given command. It
// disables the timeout for "validate input --server" since that starts a
// persistent HTTP server that should run indefinitely.
//
// Note: this couples the root command to a leaf subcommand's flag. Moving
// the logic to the subcommand's PreRunE is not practical because
// PersistentPreRun has already applied the timeout to the context by then.
func effectiveTimeout(cmd *cobra.Command, timeout time.Duration) time.Duration {
if cmd.Name() == "input" && cmd.Parent() != nil && cmd.Parent().Name() == "validate" {
if serverFlag := cmd.Flags().Lookup("server"); serverFlag != nil && serverFlag.Value.String() == "true" {
log.Debug("timeout disabled because --server flag is set")
return 0
}
}
return timeout
}

func NewRootCmd() *cobra.Command {
rootCmd := &cobra.Command{
Use: "ec",
Expand Down Expand Up @@ -116,11 +133,12 @@ func NewRootCmd() *cobra.Command {
// custom timeout can be used and traces can be added
ctx := cmd.Context()
var cancel context.CancelFunc
if globalTimeout > 0 {
ctx, cancel = context.WithTimeout(ctx, globalTimeout)
log.Debugf("globalTimeout is %s", time.Duration(globalTimeout))
timeout := effectiveTimeout(cmd, globalTimeout)
if timeout > 0 {
ctx, cancel = context.WithTimeout(ctx, timeout)
log.Debugf("globalTimeout is %s", time.Duration(timeout))
} else {
log.Debugf("globalTimeout is %d, no timeout used", globalTimeout)
log.Debugf("globalTimeout is %d, no timeout used", timeout)
}
ctx = tracing.WithTrace(ctx, enabledTraces)
cmd.SetContext(ctx)
Expand Down
36 changes: 36 additions & 0 deletions cmd/root/root_cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@
//
// SPDX-License-Identifier: Apache-2.0

//go:build unit

package root

import (
"context"
"testing"
"time"

"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"

"github.com/conforma/cli/internal/http"
Expand Down Expand Up @@ -79,6 +83,38 @@ func TestGlobalTimeout(t *testing.T) {
}
}

Comment thread
simonbaird marked this conversation as resolved.
Comment thread
simonbaird marked this conversation as resolved.
func TestGlobalTimeoutDisabledInServerMode(t *testing.T) {
Comment thread
qodo-for-conforma[bot] marked this conversation as resolved.
globalTimeout = 5 * time.Minute

var capturedCtx context.Context
cmd := NewRootCmd()

// Synthetic command tree to avoid pulling in the real validate input
// command and its dependencies. If the real flag or command name changes,
// the server-mode acceptance tests would catch it.
validate := &cobra.Command{Use: "validate"}
input := &cobra.Command{
Use: "input",
Run: func(cmd *cobra.Command, _ []string) {
capturedCtx = cmd.Context()
},
}
var server bool
input.Flags().BoolVar(&server, "server", false, "")
validate.AddCommand(input)
cmd.AddCommand(validate)

cmd.SetArgs([]string{"validate", "input", "--server"})

err := cmd.Execute()
assert.NoError(t, err)
// The package-level variable must not be mutated.
assert.Equal(t, 5*time.Minute, globalTimeout)
// The execution context must not carry a deadline.
_, hasDeadline := capturedCtx.Deadline()
assert.False(t, hasDeadline)
}

func TestRetryConfigurationFlags(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading