CI enhancements (e2e tests) and Go template fix (#970)

* Adding timeout and additional logs on e2e http test

* Better revision check on e2e http update test

* ci: Adding workflow to run e2e for all runtimes

* fix: server error 500 for Go cloudevents template
This commit is contained in:
Jefferson Ramos 2022-04-19 11:18:28 -03:00 committed by GitHub
parent e502d554c8
commit 01f113969a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 17925 additions and 17841 deletions

30
.github/workflows/test-e2e-runtime.yaml vendored Normal file
View File

@ -0,0 +1,30 @@
name: Func E2E Lifecycle Test
on: [pull_request]
concurrency:
group: ci-e1e-${{ github.ref }}-1
cancel-in-progress: true
jobs:
test:
name: E2E Test
strategy:
matrix:
runtime: ["go", "python", "quarkus", "springboot", "typescript"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: 1.16.x
- name: Install Binaries
run: ./hack/binaries.sh
- name: Allocate Cluster
run: ./hack/allocate.sh
- name: Local Registry
run: ./hack/registry.sh
- name: Build
run: make
- name: E2E runtime for ${{ matrix.runtime }}
run: make test-e2e-runtime runtime=${{ matrix.runtime }}

View File

@ -136,6 +136,8 @@ test-e2e: ## Run end-to-end tests using an available cluster.
./test/e2e_lifecycle_tests.sh node
./test/e2e_extended_tests.sh
test-e2e-runtime: ## Run end-to-end lifecycle tests using an available cluster for a single runtime.
./test/e2e_lifecycle_tests.sh $(runtime)
######################
##@ Release Artifacts

View File

@ -9,7 +9,7 @@ Develop new features by adding a test to [`handle_test.go`](handle_test.go) for
Update the running analog of the function using the `func` CLI or client library, and it can be invoked using a manually-created CloudEvent:
```console
curl -v -X POST -d 'hello' \
curl -v -X POST -d '{"message": "hello"}' \
-H'Content-type: application/json' \
-H'Ce-id: 1' \
-H'Ce-source: cloud-event-example' \

View File

@ -20,24 +20,28 @@ func Handle(ctx context.Context, event cloudevents.Event) (*event.Event, error)
*/
fmt.Printf("Incoming Event: %v\n", event) // print the received event to standard output
payload := ""
var payload Echo
err := json.Unmarshal(event.Data(), &payload)
if err != nil {
fmt.Printf("%v\n", err)
return nil, err
}
payload = "echo " + payload
payload.Message = "echo " + payload.Message
outputEvent := cloudevents.NewEvent()
outputEvent.SetSource("http://example.com/echo")
outputEvent.SetType("Echo")
outputEvent.SetData(cloudevents.ApplicationJSON, &payload)
outputEvent.SetData(cloudevents.ApplicationJSON, payload)
fmt.Printf("Outgoing Event: %v\n", outputEvent)
return &outputEvent, nil
}
type Echo struct {
Message string `json:"message"`
}
/*
Other supported function signatures:

View File

@ -18,8 +18,8 @@ func TestHandle(t *testing.T) {
event.SetType("MyEvent")
event.SetSource("http://localhost:8080/")
event.SetSubject("Echo")
input := "hello"
event.SetData(cloudevents.ApplicationJSON, &input)
input := Echo{Message: "hello"}
event.SetData(cloudevents.ApplicationJSON, input)
// Invoke the defined handler.
ce, err := Handle(context.Background(), event)
if err != nil {
@ -32,13 +32,13 @@ func TestHandle(t *testing.T) {
if ce.Type() != "Echo" {
t.Errorf("Wrong CloudEvent Type received: %v , expected Echo", ce.Type())
}
output := ""
var output Echo
err = json.Unmarshal(ce.Data(), &output)
if err != nil {
t.Fatal(err)
}
if output != "echo "+input {
t.Errorf("The expected output should be: 'echo hello and it was: %v", output)
if expected := "echo " + input.Message; output.Message != expected {
t.Errorf("The expected output should be: %v, and it was: %v", expected, output.Message)
}
}

View File

@ -4,16 +4,9 @@
package e2e
import (
"context"
"os"
"path/filepath"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"knative.dev/kn-plugin-func/k8s"
"testing"
)
@ -102,31 +95,3 @@ func TestConfigLabel(t *testing.T) {
t.Errorf("Expected label with name %v and value %v not found", labelKey3, testEnvValue)
}
}
// ** Helpers **
// RetrieveKnativeServiceResource wraps the logic to query knative serving resources in current namespace
func RetrieveKnativeServiceResource(t *testing.T, serviceName string) *unstructured.Unstructured {
// create k8s dynamic client
config, err := k8s.GetClientConfig().ClientConfig()
if err != nil {
t.Fatal(err.Error())
}
dynClient, err := dynamic.NewForConfig(config)
if err != nil {
t.Fatal(err.Error())
}
knativeServiceResource := schema.GroupVersionResource{
Group: "serving.knative.dev",
Version: "v1",
Resource: "services",
}
namespace, _, _ := k8s.GetClientConfig().Namespace()
resource, err := dynClient.Resource(knativeServiceResource).Namespace(namespace).Get(context.Background(), serviceName, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
return resource
}

48
test/_e2e/knative.go Normal file
View File

@ -0,0 +1,48 @@
package e2e
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"knative.dev/kn-plugin-func/k8s"
"testing"
)
// RetrieveKnativeServiceResource wraps the logic to query knative serving resources in current namespace
func RetrieveKnativeServiceResource(t *testing.T, serviceName string) *unstructured.Unstructured {
// create k8s dynamic client
config, err := k8s.GetClientConfig().ClientConfig()
if err != nil {
t.Fatal(err.Error())
}
dynClient, err := dynamic.NewForConfig(config)
if err != nil {
t.Fatal(err.Error())
}
knativeServiceResource := schema.GroupVersionResource{
Group: "serving.knative.dev",
Version: "v1",
Resource: "services",
}
namespace, _, _ := k8s.GetClientConfig().Namespace()
resource, err := dynClient.Resource(knativeServiceResource).Namespace(namespace).Get(context.Background(), serviceName, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
return resource
}
// GetCurrentServiceRevision retrieves current revision name for the deployed function
func GetCurrentServiceRevision(t *testing.T, project *FunctionTestProject) string {
resource := RetrieveKnativeServiceResource(t, project.FunctionName)
rootMap := resource.UnstructuredContent()
statusMap := rootMap["status"].(map[string]interface{})
latestReadyRevision := statusMap["latestReadyRevisionName"].(string)
return latestReadyRevision
}

View File

@ -20,3 +20,16 @@ func ReadyCheck(t *testing.T, knFunc *TestShellCmdRunner, project FunctionTestPr
t.Fatal()
}
}
// NewRevisionCheck waits for a new revision to report as ready
func NewRevisionCheck(t *testing.T, previousRevision string, project *FunctionTestProject) (newRevision string) {
err := wait.PollImmediate(5*time.Second, 1*time.Minute, func() (done bool, err error) {
newRevision = GetCurrentServiceRevision(t, project)
t.Logf("Waiting for new revision deployment (previous revision [%v], current revision [%v])", previousRevision, newRevision)
return newRevision != "" && newRevision != previousRevision, nil
})
if err != nil {
t.Fatal("Function new revision never got ready")
}
return newRevision
}

View File

@ -6,6 +6,7 @@ import (
"net/http"
"strings"
"testing"
"time"
)
type SimpleTestEvent struct {
@ -16,7 +17,7 @@ type SimpleTestEvent struct {
}
func (s SimpleTestEvent) pushTo(url string, t *testing.T) (body string, statusCode int, err error) {
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 15}
req, err := http.NewRequest("POST", url, strings.NewReader(s.Data))
req.Header.Add("Ce-Id", "message-1")
req.Header.Add("Ce-Specversion", "1.0")
@ -58,16 +59,30 @@ var defaultFunctionsCloudEventsValidators = map[string]FunctionCloudEventsValida
},
}
var defaultFunctionsCloudEventsMessage = map[string]SimpleTestEvent{
"default": {
Type: "e2e.test",
Source: "e2e:test",
ContentType: "text/plain",
Data: "hello",
},
"go": {
Type: "e2e.test",
Source: "e2e:test",
ContentType: "application/json",
Data: `{"message": "hello"}`,
},
}
// DefaultFunctionEventsTest executes a common test (applied for all runtimes) against a deployed
// functions that responds to CloudEvents
func DefaultFunctionEventsTest(t *testing.T, knFunc *TestShellCmdRunner, project FunctionTestProject) {
if project.Template == "cloudevents" && project.IsDeployed {
simpleEvent := SimpleTestEvent{
Type: "e2e.test",
Source: "e2e:test",
ContentType: "text/plain",
Data: "hello",
simpleEvent, ok := defaultFunctionsCloudEventsMessage[project.Runtime]
if !ok {
t.Log("Using default message template")
simpleEvent = defaultFunctionsCloudEventsMessage["default"]
}
targetUrl := project.FunctionURL

View File

@ -6,6 +6,7 @@ import (
"net/http"
"strings"
"testing"
"time"
)
// HTTP Based Function Test Validator
@ -37,7 +38,7 @@ func (f FunctionHttpResponsivenessValidator) Validate(t *testing.T, project Func
if f.contentType != "" {
req.Header.Add("Content-Type", f.contentType)
}
client := &http.Client{}
client := &http.Client{Timeout: time.Second * 15}
resp, err := client.Do(req)
// Http Response Handling
@ -57,7 +58,7 @@ func (f FunctionHttpResponsivenessValidator) Validate(t *testing.T, project Func
t.Fatalf("Expected status code 200, received %v", resp.StatusCode)
}
if f.expects != "" && !strings.Contains(string(body), f.expects) {
t.Fatalf("Body does not contains expected sentence [%v]", f.expects)
t.Fatalf("Body does not contains expected sentence [%v]\nBody received is: %v", f.expects, string(body))
}
if f.responseValidator != nil {
if err = f.responseValidator(string(body)); err != nil {

View File

@ -3,6 +3,7 @@ package e2e
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -32,11 +33,13 @@ func Update(t *testing.T, knFunc *TestShellCmdRunner, project *FunctionTestProje
t.Fatal("an error has occurred while updating project folder with new sources.", err.Error())
}
previousRevision := GetCurrentServiceRevision(t, project)
// Redeploy function
Deploy(t, knFunc, project)
// Waits to become ready
ReadyCheck(t, knFunc, *project)
// Waits New Revision to become ready
NewRevisionCheck(t, previousRevision, project)
// Indicates new project (from update templates) is in use
project.IsNewRevision = true

35577
zz_filesystem_generated.go generated

File diff suppressed because it is too large Load Diff