Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
e5b6b18
Enhance add-access-rule command UX with intuitive name-based flags
rkoster Apr 10, 2026
f14301a
Remove access rule names per RFC updates
rkoster Apr 15, 2026
25a55f9
Refine access-rules output to show separate host, domain, and path co…
rkoster Apr 15, 2026
6121923
Rebrand RFC terminology: access rules → route policies, selector → so…
rkoster Apr 21, 2026
1df8f98
Add name-based source flags to remove-route-policy
rkoster Jun 17, 2026
6108fec
Add CAPI version check for route policy commands
rkoster Jun 17, 2026
08f4b62
Add route policies column to cf domains output
rkoster Jun 17, 2026
fed4288
Add cf/cli to .gitignore to prevent binary commits
rkoster Jun 17, 2026
3afb795
Add unit tests for route policy commands, actor, and ccv3 client
rkoster Jun 17, 2026
cc26579
Fix: reject --source-org with --source-app when --source-space is mis…
rkoster Jun 17, 2026
504b0ea
test: add scope/enforce coverage for CreatePrivateDomain in domain_te…
rkoster Jun 17, 2026
9125dd3
refactor: consolidate Add/RemoveRoutePolicyArgs into single RoutePoli…
rkoster Jun 17, 2026
e8fdbb3
test: add dedicated tests for route_policy_source_flags and create-pr…
rkoster Jun 17, 2026
2375f4d
test: add --enforce-route-policies and --scope coverage to create-sha…
rkoster Jun 17, 2026
64d47d4
refactor: extract shared --enforce-route-policies / --scope test beha…
rkoster Jun 17, 2026
59f8468
chore: remove devbox.json and devbox.lock from tracked files
rkoster Jun 17, 2026
cd4b60f
Remove whitespace-only changes from unrelated files
rkoster Jun 17, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ _testmain.go
# Built binaries
*.exe
/cli
/cf/cli

out/
release/*
Expand Down
11 changes: 11 additions & 0 deletions actor/actionerror/route_policy_not_found_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package actionerror

import "fmt"

type RoutePolicyNotFoundError struct {
Source string
}

func (e RoutePolicyNotFoundError) Error() string {
return fmt.Sprintf("Route policy with source '%s' not found.", e.Source)
}
3 changes: 3 additions & 0 deletions actor/v7action/cloud_controller_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type CloudControllerClient interface {
CancelDeployment(deploymentGUID string) (ccv3.Warnings, error)
ContinueDeployment(deploymentGUID string) (ccv3.Warnings, error)
CopyPackage(sourcePackageGUID string, targetAppGUID string) (resources.Package, ccv3.Warnings, error)
CreateRoutePolicy(routePolicy resources.RoutePolicy) (resources.RoutePolicy, ccv3.Warnings, error)
CreateApplication(app resources.Application) (resources.Application, ccv3.Warnings, error)
CreateApplicationDeployment(dep resources.Deployment) (string, ccv3.Warnings, error)
CreateApplicationProcessScale(appGUID string, process resources.Process) (resources.Process, ccv3.Warnings, error)
Expand All @@ -42,6 +43,7 @@ type CloudControllerClient interface {
CreateSpace(space resources.Space) (resources.Space, ccv3.Warnings, error)
CreateSpaceQuota(spaceQuota resources.SpaceQuota) (resources.SpaceQuota, ccv3.Warnings, error)
CreateUser(userGUID string) (resources.User, ccv3.Warnings, error)
DeleteRoutePolicy(guid string) (ccv3.JobURL, ccv3.Warnings, error)
DeleteApplication(guid string) (ccv3.JobURL, ccv3.Warnings, error)
DeleteApplicationProcessInstance(appGUID string, processType string, instanceIndex int) (ccv3.Warnings, error)
DeleteBuildpack(buildpackGUID string) (ccv3.JobURL, ccv3.Warnings, error)
Expand All @@ -63,6 +65,7 @@ type CloudControllerClient interface {
DeleteUser(userGUID string) (ccv3.JobURL, ccv3.Warnings, error)
DownloadDroplet(dropletGUID string) ([]byte, ccv3.Warnings, error)
EntitleIsolationSegmentToOrganizations(isoGUID string, orgGUIDs []string) (resources.RelationshipList, ccv3.Warnings, error)
GetRoutePolicies(query ...ccv3.Query) ([]resources.RoutePolicy, ccv3.IncludedResources, ccv3.Warnings, error)
GetApplicationByNameAndSpace(appName string, spaceGUID string) (resources.Application, ccv3.Warnings, error)
GetApplicationDropletCurrent(appGUID string) (resources.Droplet, ccv3.Warnings, error)
GetApplicationEnvironment(appGUID string) (ccv3.Environment, ccv3.Warnings, error)
Expand Down
29 changes: 23 additions & 6 deletions actor/v7action/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (actor Actor) CheckRoute(domainName string, hostname string, path string, p
return matches, allWarnings, err
}

func (actor Actor) CreateSharedDomain(domainName string, internal bool, routerGroupName string) (Warnings, error) {
func (actor Actor) CreateSharedDomain(domainName string, internal bool, routerGroupName string, enforceAccessRules bool, accessRulesScope string) (Warnings, error) {
allWarnings := Warnings{}
routerGroupGUID := ""

Expand All @@ -37,28 +37,45 @@ func (actor Actor) CreateSharedDomain(domainName string, internal bool, routerGr
routerGroupGUID = routerGroup.GUID
}

_, warnings, err := actor.CloudControllerClient.CreateDomain(resources.Domain{
domain := resources.Domain{
Name: domainName,
Internal: types.NullBool{IsSet: true, Value: internal},
RouterGroup: routerGroupGUID,
})
}

// Set enforce_route_policies if specified
if enforceAccessRules {
domain.EnforceRoutePolicies = types.NullBool{IsSet: true, Value: true}
domain.RoutePoliciesScope = accessRulesScope
}

_, warnings, err := actor.CloudControllerClient.CreateDomain(domain)
allWarnings = append(allWarnings, Warnings(warnings)...)

return allWarnings, err
}

func (actor Actor) CreatePrivateDomain(domainName string, orgName string) (Warnings, error) {
func (actor Actor) CreatePrivateDomain(domainName string, orgName string, enforceAccessRules bool, accessRulesScope string) (Warnings, error) {
allWarnings := Warnings{}
organization, warnings, err := actor.GetOrganizationByName(orgName)
allWarnings = append(allWarnings, warnings...)

if err != nil {
return allWarnings, err
}
_, apiWarnings, err := actor.CloudControllerClient.CreateDomain(resources.Domain{

domain := resources.Domain{
Name: domainName,
OrganizationGUID: organization.GUID,
})
}

// Set enforce_route_policies if specified
if enforceAccessRules {
domain.EnforceRoutePolicies = types.NullBool{IsSet: true, Value: true}
domain.RoutePoliciesScope = accessRulesScope
}

_, apiWarnings, err := actor.CloudControllerClient.CreateDomain(domain)

actorWarnings := Warnings(apiWarnings)
allWarnings = append(allWarnings, actorWarnings...)
Expand Down
59 changes: 54 additions & 5 deletions actor/v7action/domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,21 @@ var _ = Describe("Domain Actions", func() {

Describe("CreateSharedDomain", func() {
var (
warnings Warnings
executeErr error
routerGroup string
warnings Warnings
executeErr error
routerGroup string
enforceRules bool
scope string
)

JustBeforeEach(func() {
warnings, executeErr = actor.CreateSharedDomain("the-domain-name", true, routerGroup)
warnings, executeErr = actor.CreateSharedDomain("the-domain-name", true, routerGroup, enforceRules, scope)
})

BeforeEach(func() {
routerGroup = ""
enforceRules = false
scope = ""
fakeCloudControllerClient.CreateDomainReturns(resources.Domain{}, ccv3.Warnings{"create-warning-1", "create-warning-2"}, errors.New("create-error"))
})

Expand Down Expand Up @@ -170,11 +174,40 @@ var _ = Describe("Domain Actions", func() {
))
})
})

Context("when enforce route policies is enabled with a scope", func() {
BeforeEach(func() {
enforceRules = true
scope = "org"
fakeCloudControllerClient.CreateDomainReturns(resources.Domain{}, ccv3.Warnings{"create-warning-1"}, nil)
})

It("passes EnforceRoutePolicies and RoutePoliciesScope to the client", func() {
Expect(executeErr).NotTo(HaveOccurred())

Expect(fakeCloudControllerClient.CreateDomainCallCount()).To(Equal(1))
passedDomain := fakeCloudControllerClient.CreateDomainArgsForCall(0)
Expect(passedDomain.EnforceRoutePolicies).To(Equal(types.NullBool{IsSet: true, Value: true}))
Expect(passedDomain.RoutePoliciesScope).To(Equal("org"))
})
})
})

Describe("CreatePrivateDomain", func() {
var (
warnings Warnings
executeErr error
enforceRules bool
scope string
)

JustBeforeEach(func() {
warnings, executeErr = actor.CreatePrivateDomain("private-domain-name", "org-name", enforceRules, scope)
})

BeforeEach(func() {
enforceRules = false
scope = ""
fakeCloudControllerClient.GetOrganizationsReturns(
[]resources.Organization{
{GUID: "org-guid"},
Expand All @@ -191,7 +224,6 @@ var _ = Describe("Domain Actions", func() {
})

It("delegates to the cloud controller client", func() {
warnings, executeErr := actor.CreatePrivateDomain("private-domain-name", "org-name")
Expect(executeErr).To(MatchError("create-error"))
Expect(warnings).To(ConsistOf("get-orgs-warning", "create-warning-1", "create-warning-2"))

Expand All @@ -205,6 +237,23 @@ var _ = Describe("Domain Actions", func() {
},
))
})

Context("when enforce route policies is enabled with a scope", func() {
BeforeEach(func() {
enforceRules = true
scope = "org"
fakeCloudControllerClient.CreateDomainReturns(resources.Domain{}, ccv3.Warnings{"create-warning-1"}, nil)
})

It("passes EnforceRoutePolicies and RoutePoliciesScope to the client", func() {
Expect(executeErr).NotTo(HaveOccurred())

Expect(fakeCloudControllerClient.CreateDomainCallCount()).To(Equal(1))
passedDomain := fakeCloudControllerClient.CreateDomainArgsForCall(0)
Expect(passedDomain.EnforceRoutePolicies).To(Equal(types.NullBool{IsSet: true, Value: true}))
Expect(passedDomain.RoutePoliciesScope).To(Equal("org"))
})
})
})

Describe("delete domain", func() {
Expand Down
Loading
Loading