Add annotations to routes and apps (#866)

Adds 'annotations' attribute to Routes and Apps
This commit is contained in:
Owen Cliffe
2018-03-20 18:02:49 +00:00
committed by GitHub
parent 845f40ee86
commit d25b5af59d
31 changed files with 1838 additions and 669 deletions

View File

@@ -6,206 +6,497 @@ import (
"reflect"
"github.com/fnproject/fn/api/id"
"github.com/fnproject/fn_go/client/apps"
"github.com/fnproject/fn_go/client/routes"
"github.com/fnproject/fn_go/models"
)
func TestCreateRouteEmptyType(t *testing.T) {
func TestShouldRejectEmptyRouteType(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "",
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PostRoute(s.AppName, &models.Route{
Path: s.RoutePath,
Image: s.Image,
Type: "v",
Format: s.Format,
})
if err == nil {
t.Errorf("Should fail with Invalid route Type.")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestCanCreateRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PostRoute(s.AppName, &models.Route{
Path: s.RoutePath,
Image: s.Image,
Format: s.Format,
})
if err != nil {
t.Errorf("expected route success, got %v", err)
}
// TODO validate route returned matches request
}
func TestListRoutes(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
cfg := &routes.GetAppsAppRoutesParams{
App: s.AppName,
Context: s.Context,
}
routesResponse, err := s.Client.Routes.GetAppsAppRoutes(cfg)
if err != nil {
t.Fatalf("Expecting list routes to be successful, got %v", err)
}
if !assertContainsRoute(routesResponse.Payload.Routes, s.RoutePath) {
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestInspectRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
rObjects := []*models.Route{GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)}
if !assertContainsRoute(rObjects, s.RoutePath) {
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
s.GivenAppExists(t, &models.App{Name: s.AppName})
newRt := s.BasicRoute()
s.GivenRouteExists(t, s.AppName, newRt)
resp, err := s.Client.Routes.GetAppsAppRoutesRoute(&routes.GetAppsAppRoutesRouteParams{
App: s.AppName,
Route: newRt.Path[1:],
Context: s.Context,
})
if err != nil {
t.Fatalf("Failed to get route %s, %v", s.RoutePath, err)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
gotRt := resp.Payload.Route
AssertRouteMatches(t, newRt, gotRt)
}
func TestCanUpdateRouteType(t *testing.T) {
newRouteType := "sync"
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
routeResp, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, "")
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, newRouteType, s.Format)
DeleteApp(t, s.Context, s.Client, s.AppName)
var validRouteUpdates = []struct {
name string
update *models.Route
extract func(*models.Route) interface{}
}{
{"route type (sync)", &models.Route{Type: "sync"}, func(m *models.Route) interface{} { return m.Type }},
{"route type (async)", &models.Route{Type: "async"}, func(m *models.Route) interface{} { return m.Type }},
{"format (json)", &models.Route{Format: "json"}, func(m *models.Route) interface{} { return m.Format }},
{"format (default)", &models.Route{Format: "default"}, func(m *models.Route) interface{} { return m.Format }},
// ...
}
func TestCanUpdateRouteConfig(t *testing.T) {
func TestCanUpdateRouteAttributes(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
newRouteConf := map[string]string{
"A": "a",
for _, tci := range validRouteUpdates {
tc := tci
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Context: s.Context,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: tc.update,
},
},
)
if err != nil {
t.Fatalf("Failed to patch route, got %v", err)
}
got := tc.extract(routeResp.Payload.Route)
change := tc.extract(tc.update)
if !reflect.DeepEqual(got, change) {
t.Errorf("Expected value in response tobe %v but was %v", change, got)
}
})
}
routeResp, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, s.RouteType, s.Format,
s.Memory, newRouteConf, s.RouteHeaders, "")
}
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, s.RouteType, s.Format)
func TestRoutePatchConfig(t *testing.T) {
t.Parallel()
for _, tci := range updateConfigCases {
tc := tci
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
route := s.BasicRoute()
route.Config = tc.intialConfig
DeleteApp(t, s.Context, s.Client, s.AppName)
s.GivenRouteExists(t, s.AppName, route)
routeResp, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Config: tc.change,
},
},
Context: s.Context,
},
)
if err != nil {
t.Fatalf("Failed to patch route, got %v", err)
}
actual := routeResp.Payload.Route.Config
if !ConfigEquivalent(actual, tc.expected) {
t.Errorf("Expected config : %v after update, got %v", tc.expected, actual)
}
})
}
}
func TestSetRouteAnnotationsOnCreate(t *testing.T) {
t.Parallel()
for _, tci := range createAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
})
rt := s.BasicRoute()
rt.Annotations = tc.annotations
route, err := s.Client.Routes.PostAppsAppRoutes(&routes.PostAppsAppRoutesParams{
App: s.AppName,
Context: s.Context,
Body: &models.RouteWrapper{
Route: rt,
},
})
if err != nil {
t.Fatalf("Failed to create route with valid annotations %v got error %v", tc.annotations, err)
}
gotMd := route.Payload.Route.Annotations
if !AnnotationsEquivalent(gotMd, tc.annotations) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.annotations)
}
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
if !AnnotationsEquivalent(getRoute.Annotations, tc.annotations) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.annotations)
}
})
}
for _, tci := range createAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(ti *testing.T) {
ti.Parallel()
s := SetupHarness()
defer s.Cleanup()
_, err := s.PostApp(&models.App{
Name: s.AppName,
Annotations: tc.annotations,
})
if err == nil {
t.Fatalf("Created app with invalid annotations %v but expected error", tc.annotations)
}
if _, ok := err.(*apps.PostAppsBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestSetRouteMetadataOnPatch(t *testing.T) {
t.Parallel()
for _, tci := range updateAnnotationsValidCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("valid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
rt := s.BasicRoute()
rt.Annotations = tc.initial
s.GivenRouteExists(t, s.AppName, rt)
res, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath[1:],
Context: s.Context,
Body: &models.RouteWrapper{
Route: &models.Route{
Annotations: tc.change,
},
},
})
if err != nil {
t.Fatalf("Failed to patch annotations with %v on route: %v", tc.change, err)
}
gotMd := res.Payload.Route.Annotations
if !AnnotationsEquivalent(gotMd, tc.expected) {
t.Errorf("Returned annotations %v does not match set annotations %v", gotMd, tc.expected)
}
getRoute := s.RouteMustExist(t, s.AppName, s.RoutePath)
if !AnnotationsEquivalent(getRoute.Annotations, tc.expected) {
t.Errorf("GET annotations '%v' does not match set annotations %v", getRoute.Annotations, tc.expected)
}
})
}
for _, tci := range updateAnnotationsErrorCases {
// iterator mutation meets parallelism... pfft
tc := tci
t.Run("invalid_"+tc.name, func(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{
Name: s.AppName,
})
rt := s.BasicRoute()
rt.Annotations = tc.initial
s.GivenRouteExists(t, s.AppName, rt)
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath[1:],
Context: s.Context,
Body: &models.RouteWrapper{
Route: &models.Route{
Annotations: tc.change,
},
},
})
if err == nil {
t.Errorf("patched route with invalid annotations %v but expected error", tc.change)
}
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); !ok {
t.Errorf("Expecting bad request for invalid annotations, got %v", err)
}
})
}
}
func TestCantUpdateRoutePath(t *testing.T) {
t.Parallel()
newRoutePath := id.New().String()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
_, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, s.RouteType, s.Format,
s.Memory, s.RouteConfig, s.RouteHeaders, newRoutePath)
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
_, err := s.Client.Routes.PatchAppsAppRoutesRoute(
&routes.PatchAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Path: id.New().String(),
},
},
})
if err == nil {
t.Errorf("Route path suppose to be immutable, but it's not.")
t.Fatalf("Expected error when patching route")
}
if _, ok := err.(*routes.PatchAppsAppRoutesRouteBadRequest); ok {
t.Errorf("Error should be bad request when updating route path ")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestRouteDuplicate(t *testing.T) {
func TestRoutePreventsDuplicate(t *testing.T) {
t.Parallel()
newRouteType := "async"
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
_, err := s.PostRoute(s.AppName, s.BasicRoute())
_, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath,
newRouteType, s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
if err == nil {
t.Errorf("Route duplicate error should appear, but it didn't")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
if _, ok := err.(*routes.PostAppsAppRoutesConflict); !ok {
t.Errorf("Error should be a conflict when creating a new route, got %v", err)
}
}
func TestCanDeleteRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
}
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
func TestCantDeleteRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Context: s.Context,
})
_, err := deleteRoute(s.Context, s.Client, s.AppName, "dummy-route")
if err == nil {
t.Error("Delete from missing route must fail.")
if err != nil {
t.Errorf("Expected success when deleting existing route, got %v", err)
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}
func TestDeployNewApp(t *testing.T) {
func TestCantDeleteMissingRoute(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.Client.Routes.DeleteAppsAppRoutesRoute(&routes.DeleteAppsAppRoutesRouteParams{
App: s.AppName,
Route: s.RoutePath,
Context: s.Context,
})
if err == nil {
t.Fatalf("Expected error when deleting non-existing route, got none")
}
if _, ok := err.(*routes.DeleteAppsAppRoutesRouteNotFound); !ok {
t.Fatalf("Expected not-found when deleting non-existing route, got %v", err)
}
}
func TestDeployExistingApp(t *testing.T) {
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
func TestPutRouteCreatesNewApp(t *testing.T) {
t.Parallel()
s := SetupHarness()
defer s.Cleanup()
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
if err != nil {
t.Fatalf("Expected new route to be created, got %v", err)
}
s.AppMustExist(t, s.AppName)
s.RouteMustExist(t, s.AppName, s.RoutePath)
}
func TestDeployUpdate(t *testing.T) {
func TestPutRouteToExistingApp(t *testing.T) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
_, err := s.PutRoute(s.AppName, s.RoutePath, s.BasicRoute())
if err != nil {
t.Fatalf("Failed to create route, got error %v", err)
}
s.AppMustExist(t, s.AppName)
s.RouteMustExist(t, s.AppName, s.RoutePath)
}
func TestPutRouteUpdatesRoute(t *testing.T) {
newRouteType := "sync"
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.Timeout, s.IdleTimeout, s.RouteConfig, s.RouteHeaders)
s := SetupHarness()
defer s.Cleanup()
updatedRoute := DeployRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType,
s.Format, s.RouteConfig, s.RouteHeaders)
assertRouteFields(t, updatedRoute, s.RoutePath, s.Image, newRouteType, s.Format)
s.GivenAppExists(t, &models.App{Name: s.AppName})
s.GivenRouteExists(t, s.AppName, s.BasicRoute())
DeleteApp(t, s.Context, s.Client, s.AppName)
changed := s.BasicRoute()
changed.Type = newRouteType
updatedRoute, err := s.PutRoute(s.AppName, s.RoutePath, changed)
if err != nil {
t.Fatalf("Failed to update route, got %v", err)
}
got := updatedRoute.Payload.Route.Type
if got != newRouteType {
t.Errorf("expected type to be %v after update, got %v", newRouteType, got)
}
}
func TestMulpileDeployExistingApp(t *testing.T) {
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
func TestPutIsIdempotentForHeaders(t *testing.T) {
s := SetupHarness()
defer s.Cleanup()
s.GivenAppExists(t, &models.App{Name: s.AppName})
routeHeaders := map[string][]string{}
routeHeaders["A"] = []string{"a"}
routeHeaders["B"] = []string{"b"}
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
sameRoute := DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, routeHeaders)
if ok := reflect.DeepEqual(sameRoute.Headers, routeHeaders); !ok {
r1 := s.BasicRoute()
r1.Headers = routeHeaders
updatedRoute1, err := s.PutRoute(s.AppName, s.RoutePath, r1)
if err != nil {
t.Fatalf("Failed to update route, got %v", err)
}
if firstMatches := reflect.DeepEqual(routeHeaders, updatedRoute1.Payload.Route.Headers); !firstMatches {
t.Errorf("Route headers should remain the same after multiple deploys with exact the same parameters '%v' != '%v'", routeHeaders, updatedRoute1.Payload.Route.Headers)
}
updatedRoute2, err := s.PutRoute(s.AppName, s.RoutePath, r1)
if bothmatch := reflect.DeepEqual(updatedRoute1.Payload.Route.Headers, updatedRoute2.Payload.Route.Headers); !bothmatch {
t.Error("Route headers should remain the same after multiple deploys with exact the same parameters")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
}