Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement cf route command #2205

Merged
merged 1 commit into from
Sep 15, 2021
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
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"},
Copy link
Contributor

@jdgonzaleza jdgonzaleza Sep 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we are reorganizing this slice?
cc: @bepotts @a-b

},
},
{
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