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
34 changes: 6 additions & 28 deletions e2e/e2e_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,10 @@ func TestCore_Run(t *testing.T) {
t.Fatal(err)
}

address, err := chooseOpenAddress(t)
if err != nil {
t.Fatal(err)
}
cmd := newCmd(t, "run", "--address", address)
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
cmd := newCmd(t, "run", "--json")
address := parseRunJSON(t, cmd)

// Wait for echo
if !waitFor(t, "http://"+address) {
if !waitFor(t, address) {
t.Fatal("service does not appear to have started correctly.")
}

Expand All @@ -87,10 +80,6 @@ func TestCore_Run(t *testing.T) {
fmt.Fprintf(os.Stderr, "error interrupting. %v", err)
}

// Wait for exit and error if anything other than 130 (^C/interrupt)
if err := cmd.Wait(); isAbnormalExit(t, err) {
t.Fatalf("function exited abnormally %v", err)
}
}

// TestCore_Deploy ensures that a function can be deployed to the cluster.
Expand Down Expand Up @@ -279,15 +268,9 @@ func TestCore_Invoke(t *testing.T) {
// ----------------------------------------
// Runs the function locally, which `func invoke` will invoke when
// it detects it is running.
address, err := chooseOpenAddress(t)
if err != nil {
t.Fatal(err)
}
cmd := newCmd(t, "run", "--json")
address := parseRunJSON(t, cmd)

cmd := newCmd(t, "run", "--address", address)
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
run := cmd // for the closure
defer func() {
// ^C the running function
Expand All @@ -296,12 +279,7 @@ func TestCore_Invoke(t *testing.T) {
}
}()

// TODO: complete implementation of `func run --json` structured output
// such that we can parse it for the actual listen address in the case
// that there is already something else running on 8080
// https://github.com/knative/func/issues/3198
// https://github.com/knative/func/issues/3199
if !waitFor(t, "http://"+address) {
if !waitFor(t, address) {
t.Fatal("service does not appear to have started correctly.")
}

Expand Down
22 changes: 4 additions & 18 deletions e2e/e2e_matrix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,11 @@ func TestMatrix_Run(t *testing.T) {
cleanImages(t, name)
})

// Choose an address ahead of time
address, err := chooseOpenAddress(t)
if err != nil {
t.Fatal(err)
}

// func init
init := []string{"init", "-l", runtime, "-t", template}

// func run
run := []string{"run", "--builder", builder, "--address", address}
// func run with --json to get dynamic address
run := []string{"run", "--builder", builder, "--json"}

// Language and architecture special treatment
// - Skips tests if the builder is not supported
Expand All @@ -66,15 +60,11 @@ func TestMatrix_Run(t *testing.T) {
}

// Run
// ---
cmd := newCmd(t, run...)
if err := cmd.Start(); err != nil {
t.Fatal(err)
}
address := parseRunJSON(t, cmd)

// Ensure the Function comes up

if !waitFor(t, "http://"+address,
if !waitFor(t, address,
withWaitTimeout(timeout),
withTemplate(template)) {
t.Fatal("service does not appear to have started correctly.")
Expand All @@ -85,10 +75,6 @@ func TestMatrix_Run(t *testing.T) {
fmt.Fprintf(os.Stderr, "error interrupting. %v", err)
}

// Wait for exit and error if anything other than 130 (^C/interrupt)
if err := cmd.Wait(); isAbnormalExit(t, err) {
t.Fatalf("function exited abnormally %v", err)
}
})
}

Expand Down
62 changes: 62 additions & 0 deletions e2e/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ See README.md for more details.
package e2e

import (
"bufio"
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -1154,3 +1156,63 @@ func chooseOpenAddress(t *testing.T) (address string, err error) {
defer l.Close()
return l.Addr().String(), nil
}

// parseRunJSON runs the command and extracts the function address from JSON output.
// We scan line-by-line because container builders (s2i/pack) may print build logs
// to stdout before the JSON, so we need to skip those lines to find valid JSON.
func parseRunJSON(t *testing.T, cmd *exec.Cmd) string {
t.Helper()

stdoutReader, stdoutWriter := io.Pipe()
stderr := &bytes.Buffer{}
cmd.Stdout = stdoutWriter
cmd.Stderr = stderr

type runOutput struct {
Address string `json:"address"`
Host string `json:"host"`
Port string `json:"port"`
}

addressChan := make(chan string, 1)
errChan := make(chan error, 1)

// Must spawn reader before starting the command, otherwise we may miss output
go func() {
scanner := bufio.NewScanner(stdoutReader)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(strings.TrimSpace(line), "{") {
var result runOutput
if err := json.Unmarshal([]byte(line), &result); err == nil && result.Address != "" {
addressChan <- result.Address
io.Copy(io.Discard, stdoutReader) // Prevent command from blocking on full pipe
return
}
}
}
if err := scanner.Err(); err != nil {
errChan <- fmt.Errorf("error reading stdout: %w", err)
} else {
errChan <- fmt.Errorf("no JSON output found in stdout")
}
}()

// Start the command
if err := cmd.Start(); err != nil {
t.Fatalf("failed to start command: %v", err)
}

var address string
select {
case address = <-addressChan:
t.Logf("Function running on %s (from JSON output)", address)
case err := <-errChan:
t.Fatalf("JSON parsing error: %v\nstderr: %s", err, stderr.String())
case <-time.After(5 * time.Minute):
t.Fatalf("timeout waiting for func run JSON output. stderr: %s", stderr.String())
}

t.Cleanup(func() { stdoutWriter.Close() })
return address
}
Loading