diff --git a/actor/pushaction/actor.go b/actor/pushaction/actor.go index 9366a77148e..ecb7971ef7d 100644 --- a/actor/pushaction/actor.go +++ b/actor/pushaction/actor.go @@ -22,7 +22,7 @@ type Actor struct { } const ProtocolRegexp = "^https?://|^tcp://" -const URLRegexp = "^(?:https?://|tcp://)?(?:[\\w-]+\\.)+\\w+(?:\\:\\d+)?(?:/.*)*(?:\\.\\w+)?$" +const URLRegexp = "^(?:https?://|tcp://)?(?:(?:[\\w-]+\\.)|(?:[*]\\.))+\\w+(?:\\:\\d+)?(?:/.*)*(?:\\.\\w+)?$" // NewActor returns a new actor. func NewActor(v2Actor V2Actor, sharedActor SharedActor) *Actor { diff --git a/actor/pushaction/merge_and_validate_settings_and_manifest.go b/actor/pushaction/merge_and_validate_settings_and_manifest.go index 65f450bdd63..0f8cb0b6fa8 100644 --- a/actor/pushaction/merge_and_validate_settings_and_manifest.go +++ b/actor/pushaction/merge_and_validate_settings_and_manifest.go @@ -42,7 +42,13 @@ func (actor Actor) MergeAndValidateSettingsAndManifests(settings CommandLineSett mergedApps = actor.setSaneEndpoint(mergedApps) log.Debugf("merged app settings: %#v", mergedApps) - return mergedApps, actor.validateMergedSettings(mergedApps) + + err = actor.validateMergedSettings(mergedApps) + if err != nil { + log.Errorln("validation error post merge:", err) + return nil, err + } + return mergedApps, nil } func (Actor) selectApp(appName string, apps []manifest.Application) ([]manifest.Application, error) { @@ -150,6 +156,7 @@ func (Actor) validatePremergedSettings(settings CommandLineSettings, apps []mani func (actor Actor) validateMergedSettings(apps []manifest.Application) error { for i, app := range apps { + log.WithField("index", i).Info("validating app") if app.Name == "" { log.WithField("index", i).Error("does not contain an app name") return actionerror.MissingNameError{} diff --git a/actor/pushaction/merge_and_validate_settings_and_manifest_test.go b/actor/pushaction/merge_and_validate_settings_and_manifest_test.go index 52bb1e8539c..6ca3d9bd520 100644 --- a/actor/pushaction/merge_and_validate_settings_and_manifest_test.go +++ b/actor/pushaction/merge_and_validate_settings_and_manifest_test.go @@ -249,7 +249,7 @@ var _ = Describe("MergeAndValidateSettingsAndManifest", func() { {Name: "some-name-2"}, } - DescribeTable("validation errors", + DescribeTable("valid manifest settings", func(settings CommandLineSettings, apps []manifest.Application, expectedErr error) { currentDirectory, err := os.Getwd() Expect(err).ToNot(HaveOccurred()) @@ -265,11 +265,54 @@ var _ = Describe("MergeAndValidateSettingsAndManifest", func() { } _, err = actor.MergeAndValidateSettingsAndManifests(settings, apps) - if expectedErr == nil { - Expect(err).ToNot(HaveOccurred()) - } else { - Expect(err).To(MatchError(expectedErr)) + Expect(err).ToNot(HaveOccurred()) + }, + + Entry("valid route with a port", + CommandLineSettings{}, + []manifest.Application{{ + Name: "some-name-1", + Path: RealPath, + Routes: []string{"www.hardknox.cli.fun:1234"}, + }}, + nil), + + Entry("valid route with crazy characters", + CommandLineSettings{}, + []manifest.Application{{ + Name: "some-name-1", + Path: RealPath, + Routes: []string{"www.hardknox.cli.fun/foo_1+2.html"}, + }}, + nil), + + Entry("ValidRoute with a star", + CommandLineSettings{}, + []manifest.Application{{ + Name: "some-name-1", + Path: RealPath, + Routes: []string{"*.hardknox.cli.fun"}, + }}, + nil), + ) + + DescribeTable("validation errors", + func(settings CommandLineSettings, apps []manifest.Application, expectedErr error) { + currentDirectory, err := os.Getwd() + Expect(err).ToNot(HaveOccurred()) + + if settings.ProvidedAppPath == RealPath { + settings.ProvidedAppPath = currentDirectory + } + + for i, app := range apps { + if app.Path == RealPath { + apps[i].Path = currentDirectory + } } + + _, err = actor.MergeAndValidateSettingsAndManifests(settings, apps) + Expect(err).To(MatchError(expectedErr)) }, Entry("CommandLineOptionsWithMultipleAppsError", @@ -392,15 +435,6 @@ var _ = Describe("MergeAndValidateSettingsAndManifest", func() { }}, actionerror.DockerPasswordNotSetError{}), - Entry("ValidRoute", - CommandLineSettings{}, - []manifest.Application{{ - Name: "some-name-1", - Path: RealPath, - Routes: []string{"www.hardknox.cli.fun:1234/foo_1+2.html"}, - }}, - nil), - Entry("InvalidRoute", CommandLineSettings{}, []manifest.Application{{ diff --git a/actor/pushaction/route_test.go b/actor/pushaction/route_test.go index 960fb2b3d54..99c05f1600d 100644 --- a/actor/pushaction/route_test.go +++ b/actor/pushaction/route_test.go @@ -210,6 +210,8 @@ var _ = Describe("Routes", func() { "c.b.a.com", "d.c.b.a.com", "a.com/some-path", + "*.f.e.com", + "*.e.com", } orgGUID = "some-org-guid" spaceGUID = "some-space-guid" @@ -237,6 +239,8 @@ var _ = Describe("Routes", func() { fakeV2Actor.GetDomainsByNameAndOrganizationReturns([]v2action.Domain{ {GUID: "domain-guid-1", Name: "a.com"}, {GUID: "domain-guid-2", Name: "b.a.com"}, + {GUID: "domain-guid-3", Name: "f.e.com"}, + {GUID: "domain-guid-4", Name: "e.com"}, }, v2action.Warnings{"domain-warnings-1", "domains-warnings-2"}, nil) }) @@ -267,7 +271,7 @@ var _ = Describe("Routes", func() { It("returns new and existing routes", func() { Expect(executeErr).NotTo(HaveOccurred()) - Expect(warnings).To(ConsistOf("domain-warnings-1", "domains-warnings-2", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning")) + Expect(warnings).To(ConsistOf("domain-warnings-1", "domains-warnings-2", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning")) Expect(calculatedRoutes).To(ConsistOf( v2action.Route{ Domain: v2action.Domain{ @@ -317,14 +321,30 @@ var _ = Describe("Routes", func() { }, Path: "/some-path", SpaceGUID: spaceGUID, + }, + v2action.Route{ + Host: "*", + Domain: v2action.Domain{ + GUID: "domain-guid-3", + Name: "f.e.com", + }, + SpaceGUID: spaceGUID, + }, + v2action.Route{ + Host: "*", + Domain: v2action.Domain{ + GUID: "domain-guid-4", + Name: "e.com", + }, + SpaceGUID: spaceGUID, })) Expect(fakeV2Actor.GetDomainsByNameAndOrganizationCallCount()).To(Equal(1)) domains, passedOrgGUID := fakeV2Actor.GetDomainsByNameAndOrganizationArgsForCall(0) - Expect(domains).To(ConsistOf("a.com", "b.a.com", "c.b.a.com", "d.c.b.a.com")) + Expect(domains).To(ConsistOf("a.com", "b.a.com", "c.b.a.com", "d.c.b.a.com", "*.f.e.com", "f.e.com", "*.e.com", "e.com")) Expect(passedOrgGUID).To(Equal(orgGUID)) - Expect(fakeV2Actor.FindRouteBoundToSpaceWithSettingsCallCount()).To(Equal(5)) + Expect(fakeV2Actor.FindRouteBoundToSpaceWithSettingsCallCount()).To(Equal(7)) // One check is enough here - checking 4th call since it's the only // existing one. Expect(fakeV2Actor.FindRouteBoundToSpaceWithSettingsArgsForCall(3)).To(Equal(v2action.Route{ @@ -394,13 +414,15 @@ var _ = Describe("Routes", func() { fakeV2Actor.GetDomainsByNameAndOrganizationReturns([]v2action.Domain{ {GUID: "domain-guid-1", Name: "a.com"}, {GUID: "domain-guid-2", Name: "b.a.com"}, + {GUID: "domain-guid-3", Name: "f.e.com"}, + {GUID: "domain-guid-4", Name: "e.com"}, }, v2action.Warnings{"domain-warnings-1", "domains-warnings-2"}, nil) fakeV2Actor.FindRouteBoundToSpaceWithSettingsReturns(v2action.Route{}, v2action.Warnings{"find-route-warning"}, actionerror.RouteNotFoundError{}) }) It("does not lookup known routes", func() { Expect(executeErr).NotTo(HaveOccurred()) - Expect(warnings).To(ConsistOf("domain-warnings-1", "domains-warnings-2", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning")) + Expect(warnings).To(ConsistOf("domain-warnings-1", "domains-warnings-2", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning", "find-route-warning")) Expect(calculatedRoutes).To(ConsistOf( v2action.Route{ Domain: v2action.Domain{ @@ -441,11 +463,27 @@ var _ = Describe("Routes", func() { }, Path: "/some-path", SpaceGUID: spaceGUID, + }, + v2action.Route{ + Host: "*", + Domain: v2action.Domain{ + GUID: "domain-guid-3", + Name: "f.e.com", + }, + SpaceGUID: spaceGUID, + }, + v2action.Route{ + Host: "*", + Domain: v2action.Domain{ + GUID: "domain-guid-4", + Name: "e.com", + }, + SpaceGUID: spaceGUID, })) Expect(fakeV2Actor.GetDomainsByNameAndOrganizationCallCount()).To(Equal(1)) domains, passedOrgGUID := fakeV2Actor.GetDomainsByNameAndOrganizationArgsForCall(0) - Expect(domains).To(ConsistOf("a.com", "b.a.com", "c.b.a.com")) + Expect(domains).To(ConsistOf("a.com", "b.a.com", "c.b.a.com", "*.f.e.com", "f.e.com", "*.e.com", "e.com")) Expect(passedOrgGUID).To(Equal(orgGUID)) }) }) diff --git a/integration/push/http_routes_in_manifest_test.go b/integration/push/http_routes_in_manifest_test.go index 24b1c0bdb3f..b0c8aba498f 100644 --- a/integration/push/http_routes_in_manifest_test.go +++ b/integration/push/http_routes_in_manifest_test.go @@ -2,6 +2,7 @@ package push import ( "path/filepath" + "regexp" "code.cloudfoundry.org/cli/integration/helpers" @@ -19,6 +20,7 @@ var _ = Describe("HTTP routes in manifest", func() { route1 helpers.Route route2 helpers.Route routeWithPath helpers.Route + routeStar helpers.Route ) BeforeEach(func() { @@ -28,6 +30,7 @@ var _ = Describe("HTTP routes in manifest", func() { route1 = helpers.NewRoute(space, domain.Name, helpers.PrefixedRandomName("r1"), "") route2 = helpers.NewRoute(space, subdomain.Name, helpers.PrefixedRandomName("r2"), "") routeWithPath = helpers.NewRoute(space, domain.Name, helpers.PrefixedRandomName("r3"), helpers.PrefixedRandomName("p1")) + routeStar = helpers.NewRoute(space, subdomain.Name, "*", "") }) Context("when the domain exist", func() { @@ -47,17 +50,19 @@ var _ = Describe("HTTP routes in manifest", func() { "routes": []map[string]string{ {"route": route1.String()}, {"route": route2.String()}, + {"route": routeStar.String()}, }, }, }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) Eventually(session).Should(Say("\\+\\s+name:\\s+%s", app)) Eventually(session).Should(Say("\\s+routes:")) + Eventually(session).Should(Say("(?i)\\+\\s+%s", regexp.QuoteMeta(routeStar.String()))) Eventually(session).Should(Say("(?i)\\+\\s+%s", route1)) Eventually(session).Should(Say("(?i)\\+\\s+%s", route2)) Eventually(session).Should(Exit(0)) @@ -85,7 +90,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) @@ -124,7 +129,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) @@ -164,7 +169,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session.Err).Should(Say("The app cannot be mapped to route %s because the route is not in this space. Apps must be mapped to routes in the same space.", route2)) Eventually(session).Should(Exit(1)) @@ -192,7 +197,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session.Err).Should(Say("Port not allowed in HTTP domain %s", domain.Name)) Eventually(session).Should(Exit(1)) @@ -216,7 +221,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) @@ -255,7 +260,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) @@ -292,7 +297,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session).Should(Say("Creating app with these attributes\\.\\.\\.")) @@ -330,7 +335,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session.Err).Should(Say("The app cannot be mapped to route %s because the route is not in this space. Apps must be mapped to routes in the same space.", routeWithPath)) Eventually(session).Should(Exit(1)) @@ -356,7 +361,7 @@ var _ = Describe("HTTP routes in manifest", func() { }, }) - session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName) + session := helpers.CustomCF(helpers.CFEnv{WorkingDirectory: dir}, PushCommandName, "--no-start") Eventually(session).Should(Say("Getting app info\\.\\.\\.")) Eventually(session.Err).Should(Say("The route %s did not match any existing domains.", route1)) Eventually(session).Should(Exit(1))