Skip to content

Commit

Permalink
Merge pull request #2205 from pet-forks/add_cf_route_command
Browse files Browse the repository at this point in the history
Implement `cf route` command
  • Loading branch information
jdgonzaleza authored Sep 15, 2021
2 parents b8c6519 + 424f1fa commit 67681a0
Show file tree
Hide file tree
Showing 10 changed files with 824 additions and 3 deletions.
11 changes: 11 additions & 0 deletions actor/v7action/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@ func (actor Actor) GetRoutesByOrg(orgGUID string, labelSelector string) ([]resou
return routes, allWarnings, nil
}

func (actor Actor) GetApplicationMapForRoute(route resources.Route) (map[string]resources.Application, Warnings, error) {
var v7Warning Warnings
apps, v7Warning, err := actor.GetApplicationsByGUIDs(extract.UniqueList("Destinations.App.GUID", route))

appMap := make(map[string]resources.Application)
for _, a := range apps {
appMap[a.GUID] = a
}
return appMap, v7Warning, err
}

func (actor Actor) GetRouteSummaries(routes []resources.Route) ([]RouteSummary, Warnings, error) {
var (
spaces []resources.Space
Expand Down
75 changes: 75 additions & 0 deletions actor/v7action/route_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,81 @@ var _ = Describe("Route Actions", func() {
})
})

Describe("GetApplicationMapForRoute", func() {
var (
appsByAppGuid map[string]resources.Application
app1 resources.Application
app2 resources.Application
route resources.Route
warnings Warnings
err error
)

BeforeEach(func() {
app1 = resources.Application{
GUID: "app-guid-1",
Name: "app-name-1",
}
app2 = resources.Application{
GUID: "app-guid-2",
Name: "app-name-2",
}
route = resources.Route{
Destinations: []resources.RouteDestination{
{
App: resources.RouteDestinationApp{
GUID: "app-guid-1",
},
},
{
App: resources.RouteDestinationApp{
GUID: "app-guid-2",
},
},
},
SpaceGUID: "fake-space-1-guid",
URL: "fake-url-1/fake-path-1:1",
Host: "fake-host-1",
Path: "fake-path-1",
Port: 1,
}
})

JustBeforeEach(func() {
appsByAppGuid, warnings, err = actor.GetApplicationMapForRoute(route)
})

When("CC successfully returns the response", func() {
BeforeEach(func() {
fakeCloudControllerClient.GetApplicationsReturns(
[]resources.Application{app1, app2},
ccv3.Warnings{"get-apps-warning"},
nil,
)
})
It("returns a mapping from apps guids to apps", func() {
Expect(appsByAppGuid).To(Equal(map[string]resources.Application{"app-guid-1": app1, "app-guid-2": app2}))
Expect(warnings).To(ConsistOf("get-apps-warning"))
Expect(err).ToNot(HaveOccurred())
})
})

When("CC errors", func() {
var cc_err = errors.New("failed to get route")
BeforeEach(func() {
fakeCloudControllerClient.GetApplicationsReturns(
[]resources.Application{},
ccv3.Warnings{"get-apps-warning"},
cc_err,
)
})
It("returns an error", func() {
Expect(warnings).To(ConsistOf("get-apps-warning"))
Expect(err).To(Equal(cc_err))
})
})
})

Describe("GetRoutesBySpace", func() {
var (
routes []resources.Route
Expand Down
1 change: 1 addition & 0 deletions command/common/command_list_v7.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ type commandList struct {
Restart v7.RestartCommand `command:"restart" alias:"rs" description:"Stop all instances of the app, then start them again."`
RestartAppInstance v7.RestartAppInstanceCommand `command:"restart-app-instance" description:"Terminate, then instantiate an app instance"`
RouterGroups v7.RouterGroupsCommand `command:"router-groups" description:"List router groups"`
Route v7.RouteCommand `command:"route" alias:"ro" description:"Display route details and mapped destinations"`
Routes v7.RoutesCommand `command:"routes" alias:"r" description:"List all routes in the current space or the current organization"`
RunTask v7.RunTaskCommand `command:"run-task" alias:"rt" description:"Run a one-off task on an app"`
RunningEnvironmentVariableGroup v7.RunningEnvironmentVariableGroupCommand `command:"running-environment-variable-group" alias:"revg" description:"Retrieve the contents of the running environment variable group"`
Expand Down
4 changes: 3 additions & 1 deletion command/common/internal/help_all_display.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ var HelpCategoryList = []HelpCategory{
{
CategoryName: "ROUTES:",
CommandList: [][]string{
{"routes", "create-route", "check-route", "map-route", "unmap-route", "delete-route", "delete-orphaned-routes"},
{"routes", "route"},
{"create-route", "check-route", "map-route", "unmap-route", "delete-route"},
{"delete-orphaned-routes"},
},
},
{
Expand Down
1 change: 1 addition & 0 deletions command/v7/actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ type Actor interface {
GetAppFeature(appGUID string, featureName string) (resources.ApplicationFeature, v7action.Warnings, error)
GetAppSummariesForSpace(spaceGUID string, labels string) ([]v7action.ApplicationSummary, v7action.Warnings, error)
GetApplicationByNameAndSpace(appName string, spaceGUID string) (resources.Application, v7action.Warnings, error)
GetApplicationMapForRoute(route resources.Route) (map[string]resources.Application, v7action.Warnings, error)
GetApplicationDroplets(appName string, spaceGUID string) ([]resources.Droplet, v7action.Warnings, error)
GetApplicationLabels(appName string, spaceGUID string) (map[string]types.NullString, v7action.Warnings, error)
GetApplicationPackages(appName string, spaceGUID string) ([]resources.Package, v7action.Warnings, error)
Expand Down
138 changes: 138 additions & 0 deletions command/v7/route_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package v7

import (
"code.cloudfoundry.org/cli/command/flag"
"code.cloudfoundry.org/cli/resources"

"strconv"
)

type RouteCommand struct {
BaseCommand

RequiredArgs flag.Domain `positional-args:"yes"`
Hostname string `long:"hostname" short:"n" description:"Hostname used to identify the HTTP route"`
Path flag.V7RoutePath `long:"path" description:"Path used to identify the HTTP route"`
Port int `long:"port" description:"Port used to identify the TCP route"`
relatedCommands interface{} `related_commands:"create-route, delete-route, routes"`
}

func (cmd RouteCommand) Usage() string {
return `
Display an HTTP route:
CF_NAME route DOMAIN [--hostname HOSTNAME] [--path PATH]
Display a TCP route:
CF_NAME route DOMAIN --port PORT`
}

func (cmd RouteCommand) Examples() string {
return `
CF_NAME route example.com # example.com
CF_NAME route example.com -n myhost --path foo # myhost.example.com/foo
CF_NAME route example.com --path foo # example.com/foo
CF_NAME route example.com --port 5000 # example.com:5000`
}

func (cmd RouteCommand) Execute(args []string) error {
err := cmd.SharedActor.CheckTarget(true, false)
if err != nil {
return err
}

user, err := cmd.Config.CurrentUser()
if err != nil {
return err
}

domain, warnings, err := cmd.Actor.GetDomainByName(cmd.RequiredArgs.Domain)

cmd.UI.DisplayWarnings(warnings)
if err != nil {
return err
}

hostName := ""
if cmd.Hostname != "" {
hostName = cmd.Hostname + "."
}

displayPort := ""
if cmd.Port != 0 {
displayPort = ":" + strconv.Itoa(cmd.Port)

}

cmd.UI.DisplayTextWithFlavor(" Showing route {{.HostName}}{{.DomainName}}{{.Port}}{{.PathName}} in org {{.OrgName}} / space {{.SpaceName}} as {{.Username}}...", map[string]interface{}{
"HostName": hostName,
"DomainName": cmd.RequiredArgs.Domain,
"PathName": cmd.Path.Path,
"Port": displayPort,
"OrgName": cmd.Config.TargetedOrganization().Name,
"SpaceName": cmd.Config.TargetedSpace().Name,
"Username": user.Name,
})
cmd.UI.DisplayNewline()

route, warnings, err := cmd.Actor.GetRouteByAttributes(domain, cmd.Hostname, cmd.Path.Path, cmd.Port)
cmd.UI.DisplayWarnings(warnings)
if err != nil {
return err
}

port := ""
if route.Port != 0 {
port = strconv.Itoa(route.Port)
}

appMap, warnings, err := cmd.Actor.GetApplicationMapForRoute(route)
cmd.UI.DisplayWarnings(warnings)
if err != nil {
return err
}

table := [][]string{
{cmd.UI.TranslateText("domain:"), domain.Name},
{cmd.UI.TranslateText("host:"), route.Host},
{cmd.UI.TranslateText("port:"), port},
{cmd.UI.TranslateText("path:"), route.Path},
{cmd.UI.TranslateText("protocol:"), route.Protocol},
}

cmd.UI.DisplayKeyValueTable("", table, 3)
cmd.UI.DisplayNewline()

cmd.UI.DisplayText("Destinations:")
cmd.displayDestinations(route, appMap)

return nil
}

func (cmd RouteCommand) displayDestinations(route resources.Route, appMap map[string]resources.Application) {
destinations := route.Destinations
if len(destinations) > 0 {
var keyValueTable = [][]string{
{
cmd.UI.TranslateText("app"),
cmd.UI.TranslateText("process"),
cmd.UI.TranslateText("port"),
cmd.UI.TranslateText("protocol"),
},
}

for _, destination := range destinations {
port := ""
if destination.Port != 0 {
port = strconv.Itoa(destination.Port)
}
keyValueTable = append(keyValueTable, []string{
appMap[destination.App.GUID].Name,
destination.App.Process.Type,
port,
destination.Protocol,
})
}

cmd.UI.DisplayKeyValueTable("\t", keyValueTable, 3)
}
}
Loading

0 comments on commit 67681a0

Please sign in to comment.