Skip to content

Commit 6d1d91d

Browse files
committed
TUN-7787: Refactor cloudflared to use new route endpoints based on route IDs
This commits makes sure that cloudflared starts using the new API endpoints for managing routes. Additionally, the delete route operation still allows deleting by CIDR and VNet but it is being marked as deprecated in favor of specifying the route ID. The goal of this change is to make it simpler for the user to delete routes without specifying Vnet.
1 parent fc0ecf4 commit 6d1d91d

File tree

6 files changed

+84
-54
lines changed

6 files changed

+84
-54
lines changed

cfapi/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type HostnameClient interface {
2222
type IPRouteClient interface {
2323
ListRoutes(filter *IpRouteFilter) ([]*DetailedRoute, error)
2424
AddRoute(newRoute NewRoute) (Route, error)
25-
DeleteRoute(params DeleteRouteParams) error
25+
DeleteRoute(id uuid.UUID) error
2626
GetByIP(params GetRouteByIpParams) (DetailedRoute, error)
2727
}
2828

cfapi/ip_route.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ type NewRoute struct {
7575
// MarshalJSON handles fields with non-JSON types (e.g. net.IPNet).
7676
func (r NewRoute) MarshalJSON() ([]byte, error) {
7777
return json.Marshal(&struct {
78+
Network string `json:"network"`
7879
TunnelID uuid.UUID `json:"tunnel_id"`
7980
Comment string `json:"comment"`
8081
VNetID *uuid.UUID `json:"virtual_network_id,omitempty"`
8182
}{
83+
Network: r.Network.String(),
8284
TunnelID: r.TunnelID,
8385
Comment: r.Comment,
8486
VNetID: r.VNetID,
@@ -87,6 +89,7 @@ func (r NewRoute) MarshalJSON() ([]byte, error) {
8789

8890
// DetailedRoute is just a Route with some extra fields, e.g. TunnelName.
8991
type DetailedRoute struct {
92+
ID uuid.UUID `json:"id"`
9093
Network CIDR `json:"network"`
9194
TunnelID uuid.UUID `json:"tunnel_id"`
9295
// Optional field. When unset, it means the DetailedRoute belongs to the default virtual network.
@@ -115,7 +118,8 @@ func (r DetailedRoute) TableString() string {
115118
}
116119

117120
return fmt.Sprintf(
118-
"%s\t%s\t%s\t%s\t%s\t%s\t%s\t",
121+
"%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t",
122+
r.ID,
119123
r.Network.String(),
120124
vnetColumn,
121125
r.Comment,
@@ -126,12 +130,6 @@ func (r DetailedRoute) TableString() string {
126130
)
127131
}
128132

129-
type DeleteRouteParams struct {
130-
Network net.IPNet
131-
// Optional field. If unset, backend will assume the default vnet for the account.
132-
VNetID *uuid.UUID
133-
}
134-
135133
type GetRouteByIpParams struct {
136134
Ip net.IP
137135
// Optional field. If unset, backend will assume the default vnet for the account.
@@ -158,7 +156,7 @@ func (r *RESTClient) ListRoutes(filter *IpRouteFilter) ([]*DetailedRoute, error)
158156
// AddRoute calls the Tunnelstore POST endpoint for a given route.
159157
func (r *RESTClient) AddRoute(newRoute NewRoute) (Route, error) {
160158
endpoint := r.baseEndpoints.accountRoutes
161-
endpoint.Path = path.Join(endpoint.Path, "network", url.PathEscape(newRoute.Network.String()))
159+
endpoint.Path = path.Join(endpoint.Path)
162160
resp, err := r.sendRequest("POST", endpoint, newRoute)
163161
if err != nil {
164162
return Route{}, errors.Wrap(err, "REST request failed")
@@ -173,10 +171,9 @@ func (r *RESTClient) AddRoute(newRoute NewRoute) (Route, error) {
173171
}
174172

175173
// DeleteRoute calls the Tunnelstore DELETE endpoint for a given route.
176-
func (r *RESTClient) DeleteRoute(params DeleteRouteParams) error {
174+
func (r *RESTClient) DeleteRoute(id uuid.UUID) error {
177175
endpoint := r.baseEndpoints.accountRoutes
178-
endpoint.Path = path.Join(endpoint.Path, "network", url.PathEscape(params.Network.String()))
179-
setVnetParam(&endpoint, params.VNetID)
176+
endpoint.Path = path.Join(endpoint.Path, url.PathEscape(id.String()))
180177

181178
resp, err := r.sendRequest("DELETE", endpoint, nil)
182179
if err != nil {

cfapi/ip_route_filter.go

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,47 +58,45 @@ type IpRouteFilter struct {
5858

5959
// NewIpRouteFilterFromCLI parses CLI flags to discover which filters should get applied.
6060
func NewIpRouteFilterFromCLI(c *cli.Context) (*IpRouteFilter, error) {
61-
f := &IpRouteFilter{
62-
queryParams: url.Values{},
63-
}
61+
f := NewIPRouteFilter()
6462

6563
// Set deletion filter
6664
if flag := filterIpRouteDeleted.Name; c.IsSet(flag) && c.Bool(flag) {
67-
f.deleted()
65+
f.Deleted()
6866
} else {
69-
f.notDeleted()
67+
f.NotDeleted()
7068
}
7169

7270
if subset, err := cidrFromFlag(c, filterSubsetIpRoute); err != nil {
7371
return nil, err
7472
} else if subset != nil {
75-
f.networkIsSupersetOf(*subset)
73+
f.NetworkIsSupersetOf(*subset)
7674
}
7775

7876
if superset, err := cidrFromFlag(c, filterSupersetIpRoute); err != nil {
7977
return nil, err
8078
} else if superset != nil {
81-
f.networkIsSupersetOf(*superset)
79+
f.NetworkIsSupersetOf(*superset)
8280
}
8381

8482
if comment := c.String(filterIpRouteComment.Name); comment != "" {
85-
f.commentIs(comment)
83+
f.CommentIs(comment)
8684
}
8785

8886
if tunnelID := c.String(filterIpRouteTunnelID.Name); tunnelID != "" {
8987
u, err := uuid.Parse(tunnelID)
9088
if err != nil {
9189
return nil, errors.Wrapf(err, "Couldn't parse UUID from %s", filterIpRouteTunnelID.Name)
9290
}
93-
f.tunnelID(u)
91+
f.TunnelID(u)
9492
}
9593

9694
if vnetId := c.String(filterIpRouteByVnet.Name); vnetId != "" {
9795
u, err := uuid.Parse(vnetId)
9896
if err != nil {
9997
return nil, errors.Wrapf(err, "Couldn't parse UUID from %s", filterIpRouteByVnet.Name)
10098
}
101-
f.vnetID(u)
99+
f.VNetID(u)
102100
}
103101

104102
if maxFetch := c.Int("max-fetch-size"); maxFetch > 0 {
@@ -124,35 +122,39 @@ func cidrFromFlag(c *cli.Context, flag cli.StringFlag) (*net.IPNet, error) {
124122
return subset, nil
125123
}
126124

127-
func (f *IpRouteFilter) commentIs(comment string) {
125+
func NewIPRouteFilter() *IpRouteFilter {
126+
return &IpRouteFilter{queryParams: url.Values{}}
127+
}
128+
129+
func (f *IpRouteFilter) CommentIs(comment string) {
128130
f.queryParams.Set("comment", comment)
129131
}
130132

131-
func (f *IpRouteFilter) notDeleted() {
133+
func (f *IpRouteFilter) NotDeleted() {
132134
f.queryParams.Set("is_deleted", "false")
133135
}
134136

135-
func (f *IpRouteFilter) deleted() {
137+
func (f *IpRouteFilter) Deleted() {
136138
f.queryParams.Set("is_deleted", "true")
137139
}
138140

139-
func (f *IpRouteFilter) networkIsSubsetOf(superset net.IPNet) {
141+
func (f *IpRouteFilter) NetworkIsSubsetOf(superset net.IPNet) {
140142
f.queryParams.Set("network_subset", superset.String())
141143
}
142144

143-
func (f *IpRouteFilter) networkIsSupersetOf(subset net.IPNet) {
145+
func (f *IpRouteFilter) NetworkIsSupersetOf(subset net.IPNet) {
144146
f.queryParams.Set("network_superset", subset.String())
145147
}
146148

147-
func (f *IpRouteFilter) existedAt(existedAt time.Time) {
149+
func (f *IpRouteFilter) ExistedAt(existedAt time.Time) {
148150
f.queryParams.Set("existed_at", existedAt.Format(time.RFC3339))
149151
}
150152

151-
func (f *IpRouteFilter) tunnelID(id uuid.UUID) {
153+
func (f *IpRouteFilter) TunnelID(id uuid.UUID) {
152154
f.queryParams.Set("tunnel_id", id.String())
153155
}
154156

155-
func (f *IpRouteFilter) vnetID(id uuid.UUID) {
157+
func (f *IpRouteFilter) VNetID(id uuid.UUID) {
156158
f.queryParams.Set("virtual_network_id", id.String())
157159
}
158160

cfapi/ip_route_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ func TestDetailedRouteJsonRoundtrip(t *testing.T) {
6969
}{
7070
{
7171
`{
72+
"id":"91ebc578-cc99-4641-9937-0fb630505fa0",
7273
"network":"10.1.2.40/29",
7374
"tunnel_id":"fba6ffea-807f-4e7a-a740-4184ee1b82c8",
7475
"comment":"test",
@@ -80,6 +81,7 @@ func TestDetailedRouteJsonRoundtrip(t *testing.T) {
8081
},
8182
{
8283
`{
84+
"id":"91ebc578-cc99-4641-9937-0fb630505fa0",
8385
"network":"10.1.2.40/29",
8486
"tunnel_id":"fba6ffea-807f-4e7a-a740-4184ee1b82c8",
8587
"virtual_network_id":"38c95083-8191-4110-8339-3f438d44fdb9",
@@ -167,9 +169,10 @@ func TestRouteTableString(t *testing.T) {
167169
require.NoError(t, err)
168170
require.NotNil(t, network)
169171
r := DetailedRoute{
172+
ID: uuid.Nil,
170173
Network: CIDR(*network),
171174
}
172175
row := r.TableString()
173176
fmt.Println(row)
174-
require.True(t, strings.HasPrefix(row, "1.2.3.4/32"))
177+
require.True(t, strings.HasPrefix(row, "00000000-0000-0000-0000-000000000000\t1.2.3.4/32"))
175178
}

cmd/cloudflared/tunnel/subcommand_context_teamnet.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package tunnel
22

33
import (
4+
"net"
5+
6+
"github.com/google/uuid"
47
"github.com/pkg/errors"
58

69
"github.com/cloudflare/cloudflared/cfapi"
@@ -24,12 +27,12 @@ func (sc *subcommandContext) addRoute(newRoute cfapi.NewRoute) (cfapi.Route, err
2427
return client.AddRoute(newRoute)
2528
}
2629

27-
func (sc *subcommandContext) deleteRoute(params cfapi.DeleteRouteParams) error {
30+
func (sc *subcommandContext) deleteRoute(id uuid.UUID) error {
2831
client, err := sc.client()
2932
if err != nil {
3033
return errors.Wrap(err, noClientMsg)
3134
}
32-
return client.DeleteRoute(params)
35+
return client.DeleteRoute(id)
3336
}
3437

3538
func (sc *subcommandContext) getRouteByIP(params cfapi.GetRouteByIpParams) (cfapi.DetailedRoute, error) {
@@ -39,3 +42,25 @@ func (sc *subcommandContext) getRouteByIP(params cfapi.GetRouteByIpParams) (cfap
3942
}
4043
return client.GetByIP(params)
4144
}
45+
46+
func (sc *subcommandContext) getRouteId(network net.IPNet, vnetId *uuid.UUID) (uuid.UUID, error) {
47+
filters := cfapi.NewIPRouteFilter()
48+
filters.NotDeleted()
49+
filters.NetworkIsSubsetOf(network)
50+
filters.NetworkIsSupersetOf(network)
51+
52+
if vnetId != nil {
53+
filters.VNetID(*vnetId)
54+
}
55+
56+
result, err := sc.listRoutes(filters)
57+
if err != nil {
58+
return uuid.Nil, err
59+
}
60+
61+
if len(result) != 1 {
62+
return uuid.Nil, errors.New("unable to find route for provided network and vnet")
63+
}
64+
65+
return result[0].ID, nil
66+
}

cmd/cloudflared/tunnel/teamnet_subcommands.go

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ var (
2121
Aliases: []string{"vn"},
2222
Usage: "The ID or name of the virtual network to which the route is associated to.",
2323
}
24+
25+
routeAddError = errors.New("You must supply exactly one argument, the ID or CIDR of the route you want to delete")
2426
)
2527

2628
func buildRouteIPSubcommand() *cli.Command {
@@ -68,11 +70,9 @@ which virtual network's routing table you want to add the route to with:
6870
Name: "delete",
6971
Action: cliutil.ConfiguredAction(deleteRouteCommand),
7072
Usage: "Delete a row from your organization's private routing table",
71-
UsageText: "cloudflared tunnel [--config FILEPATH] route ip delete [flags] [CIDR]",
72-
Description: `Deletes the row for a given CIDR from your routing table. That portion of your network
73-
will no longer be reachable by the WARP clients. Note that if you use virtual
74-
networks, then you have to tell which virtual network whose routing table you
75-
have a row deleted from.`,
73+
UsageText: "cloudflared tunnel [--config FILEPATH] route ip delete [flags] [Route ID or CIDR]",
74+
Description: `Deletes the row for the given route ID from your routing table. That portion of your network
75+
will no longer be reachable.`,
7676
Flags: []cli.Flag{vnetFlag},
7777
},
7878
{
@@ -187,33 +187,36 @@ func deleteRouteCommand(c *cli.Context) error {
187187
}
188188

189189
if c.NArg() != 1 {
190-
return errors.New("You must supply exactly one argument, the network whose route you want to delete (in CIDR form e.g. 1.2.3.4/32)")
190+
return routeAddError
191191
}
192192

193-
_, network, err := net.ParseCIDR(c.Args().First())
193+
var routeId uuid.UUID
194+
routeId, err = uuid.Parse(c.Args().First())
194195
if err != nil {
195-
return errors.Wrap(err, "Invalid network CIDR")
196-
}
197-
if network == nil {
198-
return errors.New("Invalid network CIDR")
199-
}
196+
_, network, err := net.ParseCIDR(c.Args().First())
197+
if err != nil || network == nil {
198+
return routeAddError
199+
}
200200

201-
params := cfapi.DeleteRouteParams{
202-
Network: *network,
203-
}
201+
var vnetId *uuid.UUID
202+
if c.IsSet(vnetFlag.Name) {
203+
id, err := getVnetId(sc, c.String(vnetFlag.Name))
204+
if err != nil {
205+
return err
206+
}
207+
vnetId = &id
208+
}
204209

205-
if c.IsSet(vnetFlag.Name) {
206-
vnetId, err := getVnetId(sc, c.String(vnetFlag.Name))
210+
routeId, err = sc.getRouteId(*network, vnetId)
207211
if err != nil {
208212
return err
209213
}
210-
params.VNetID = &vnetId
211214
}
212215

213-
if err := sc.deleteRoute(params); err != nil {
216+
if err := sc.deleteRoute(routeId); err != nil {
214217
return errors.Wrap(err, "API error")
215218
}
216-
fmt.Printf("Successfully deleted route for %s\n", network)
219+
fmt.Printf("Successfully deleted route with ID %s\n", routeId)
217220
return nil
218221
}
219222

@@ -269,7 +272,7 @@ func formatAndPrintRouteList(routes []*cfapi.DetailedRoute) {
269272
defer writer.Flush()
270273

271274
// Print column headers with tabbed columns
272-
_, _ = fmt.Fprintln(writer, "NETWORK\tVIRTUAL NET ID\tCOMMENT\tTUNNEL ID\tTUNNEL NAME\tCREATED\tDELETED\t")
275+
_, _ = fmt.Fprintln(writer, "ID\tNETWORK\tVIRTUAL NET ID\tCOMMENT\tTUNNEL ID\tTUNNEL NAME\tCREATED\tDELETED\t")
273276

274277
// Loop through routes, create formatted string for each, and print using tabwriter
275278
for _, route := range routes {

0 commit comments

Comments
 (0)