Skip to content

Add exclude_services filter#753

Open
dogancanbakir wants to merge 4 commits into
devfrom
feat/exclude-services
Open

Add exclude_services filter#753
dogancanbakir wants to merge 4 commits into
devfrom
feat/exclude-services

Conversation

@dogancanbakir

@dogancanbakir dogancanbakir commented Jun 24, 2026

Copy link
Copy Markdown
Member

Adds an exclude_services option (YAML + -es/--exclude-service CLI) to skip services from the enumerated set, mirroring services. Resolution: start from services (or all supported), then drop excludes; unknown values are ignored.

Closes #749.

Summary by CodeRabbit

  • New Features
    • Added service filtering across providers using services (allowlist) and exclude_services (blocklist), including new CLI flags -s/--service and -es/--exclude-service.
    • Exclusions are applied to the effective service set, and inventory output includes exclude_services when applicable.
  • Bug Fixes
    • Improved parsing by trimming whitespace and ignoring unknown service values.
  • Documentation
    • Updated PRO​VIDERS and README with the new section, flags, and YAML examples.
  • Tests
    • Added coverage for exclude_services parsing and allowlist+blocklist combinations (including YAML).

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds exclude_services support across schema parsing, CLI wiring, runner propagation, provider filtering, and documentation.

Changes

exclude_services Filtering Feature

Layer / File(s) Summary
Schema parsing and resolution
pkg/schema/schema.go, pkg/schema/schema_test.go
Adds exclude_services parsing, shared list collection for service names, ResolveServices, and tests for parsing and service resolution.
CLI flag and inventory propagation
internal/runner/options.go, internal/runner/runner.go, README.md, PROVIDERS.md
Adds ExcludeServices to runner options, registers --exclude-service/-es, passes exclude_services into inventory items, and updates the filter documentation snippets.
Shared provider service resolution
pkg/providers/alibaba/..., pkg/providers/arvancloud/..., pkg/providers/azure/..., pkg/providers/cloudflare/..., pkg/providers/custom/..., pkg/providers/digitalocean/..., pkg/providers/dnssimple/..., pkg/providers/heroku/..., pkg/providers/hetzner/..., pkg/providers/k8s/..., pkg/providers/linode/..., pkg/providers/namecheap/..., pkg/providers/openstack/..., pkg/providers/ovh/..., pkg/providers/scaleway/..., pkg/providers/terraform/..., pkg/providers/vercel/...
Provider constructors now use ResolveServices, and the removed inline parsing imports and defaults are dropped.
AWS and GCP filtering
pkg/providers/aws/aws.go, pkg/providers/aws/aws_test.go, pkg/providers/gcp/gcp.go
AWS option parsing now resolves services through ResolveServices with coverage for exclude_services; GCP updates individual and organization service selection, including trimming, "all" expansion, exclusions, and a comment in OrganizationProvider.Resources.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I hop through filters, neat and spry,
exclude_services now reaches the sky.
Trimmed and sorted, the paths align,
Docs and code now march in line.
Hop hop, hooray! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 74.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding an exclude_services filter.
Linked Issues check ✅ Passed The PR implements YAML and CLI support, applies services then exclude_services, ignores unknown values, and preserves existing services behavior.
Out of Scope Changes check ✅ Passed The refactors and documentation updates are all tied to wiring the new exclude_services filter through schema, CLI, and providers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/exclude-services

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
pkg/providers/aws/aws.go (1)

104-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Exclude logic is correct; minor parsing inconsistency with the allowlist.

The exclude_services block trims whitespace (strings.TrimSpace(s)), but the services allowlist parsing above (Lines 92-96) does not. A user writing services: "ec2, s3" would silently drop s3 from the allowlist, while exclude_services: "ec2, s3" handles the space correctly. Aligning both to trim would make behavior predictable.

♻️ Optional: trim allowlist entries too
 	if ss, ok := block.GetMetadata("services"); ok {
 		for _, s := range strings.Split(ss, ",") {
-			if _, ok := supportedServicesMap[s]; ok {
-				services[s] = struct{}{}
+			s = strings.TrimSpace(s)
+			if _, ok := supportedServicesMap[s]; ok {
+				services[s] = struct{}{}
 			}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/aws/aws.go` around lines 104 - 108, The allowlist parsing in
aws.go is inconsistent with the exclude list because the `services` handling
does not trim comma-separated entries while `exclude_services` does. Update the
`services` parsing path (near the `exclude_services` logic in
`GetServices`/metadata handling) to apply `strings.TrimSpace` to each item
before using it, so both allowlist and exclude behavior match when users include
spaces.
pkg/providers/aws/aws_test.go (1)

26-31: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Weak assertion in "exclude composes with services allowlist" case.

route53 in wantAbsent is trivially absent because the allowlist (ec2,s3,lambda) never contained it — this assertion passes regardless of the exclude logic and doesn't strengthen the test. The meaningful assertion is s3 being absent, which is already present. Consider dropping route53 here or adding a case that exercises trimming (e.g. exclude_services: "ec2, s3") to cover the new TrimSpace behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/aws/aws_test.go` around lines 26 - 31, The "exclude composes
with services allowlist" test in aws_test.go has a weak assertion because
route53 was never in the allowlist, so it does not validate exclude_services
behavior. Update the test case around the schema.OptionBlock setup to either
remove route53 from wantAbsent or change the exclude_services input in the
relevant test to exercise trimming with spaces, so the assertion meaningfully
covers the trim logic in the services filtering path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@PROVIDERS.md`:
- Line 3: The heading structure in the Providers document skips a level from the
main title to the Service filtering section, which triggers the markdownlint
heading-increment warning. Update the Service filtering heading to use the H2
level so it matches the outline under the top-level Providers heading and keeps
the section hierarchy consistent.

In `@README.md`:
- Line 71: The service filter help text contains a stray extra closing
parenthesis, so update the documented CLI usage string for the -s/-service
option to remove the redundant “)” after “comma-separated” and keep the help
output clean and consistent.

---

Nitpick comments:
In `@pkg/providers/aws/aws_test.go`:
- Around line 26-31: The "exclude composes with services allowlist" test in
aws_test.go has a weak assertion because route53 was never in the allowlist, so
it does not validate exclude_services behavior. Update the test case around the
schema.OptionBlock setup to either remove route53 from wantAbsent or change the
exclude_services input in the relevant test to exercise trimming with spaces, so
the assertion meaningfully covers the trim logic in the services filtering path.

In `@pkg/providers/aws/aws.go`:
- Around line 104-108: The allowlist parsing in aws.go is inconsistent with the
exclude list because the `services` handling does not trim comma-separated
entries while `exclude_services` does. Update the `services` parsing path (near
the `exclude_services` logic in `GetServices`/metadata handling) to apply
`strings.TrimSpace` to each item before using it, so both allowlist and exclude
behavior match when users include spaces.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f21e6a61-899f-45cf-b717-3735755f9313

📥 Commits

Reviewing files that changed from the base of the PR and between 952ca94 and 4523fb2.

📒 Files selected for processing (26)
  • PROVIDERS.md
  • README.md
  • internal/runner/options.go
  • internal/runner/runner.go
  • pkg/providers/alibaba/alibaba.go
  • pkg/providers/arvancloud/arvancloud.go
  • pkg/providers/aws/aws.go
  • pkg/providers/aws/aws_test.go
  • pkg/providers/azure/azure.go
  • pkg/providers/cloudflare/cloudflare.go
  • pkg/providers/custom/custom.go
  • pkg/providers/digitalocean/digitalocean.go
  • pkg/providers/dnssimple/dnssimple.go
  • pkg/providers/gcp/gcp.go
  • pkg/providers/heroku/heroku.go
  • pkg/providers/hetzner/hetzner.go
  • pkg/providers/k8s/kubernetes.go
  • pkg/providers/linode/linode.go
  • pkg/providers/namecheap/namecheap.go
  • pkg/providers/openstack/openstack.go
  • pkg/providers/ovh/ovh.go
  • pkg/providers/scaleway/scaleway.go
  • pkg/providers/terraform/terraform.go
  • pkg/providers/vercel/vercel.go
  • pkg/schema/schema.go
  • pkg/schema/schema_test.go

Comment thread PROVIDERS.md Outdated
Comment thread README.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/providers/gcp/gcp.go (1)

652-677: 📐 Maintainability & Code Quality | 🔵 Trivial

Extract shared service resolution The org path needs the all sentinel for Asset API discovery, but the allowlist/exclude handling still duplicates ResolveServices. Factor the common logic into a shared helper and apply the all expansion on top.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/gcp/gcp.go` around lines 652 - 677, The org-level service
selection logic in the GCP provider duplicates the allowlist/exclude resolution
that already exists in ResolveServices, with only the “all” sentinel behavior
layered on top. Extract the shared service resolution into a common helper used
by both the org path and ResolveServices, then keep the Asset API-specific “all”
expansion and exclusions in the org flow after the shared helper runs,
referencing the service selection code in gcp.go and the ResolveServices path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/providers/alibaba/alibaba.go`:
- Line 44: Preserve the caller’s explicit allowlist in the Alibaba provider: the
current use of ResolveServices(Services) causes empty or fully invalid filters
to fall back to every supported service, which breaks service filtering. Update
the service resolution path around Services() and the provider’s services
assignment so unknown entries are ignored but an explicitly empty result stays
empty, and only default to all services when no filter was supplied at all.

In `@pkg/providers/digitalocean/digitalocean.go`:
- Line 28: The DigitalOcean provider is only honoring one alias for droplet
exclusion, so `exclude_services: droplet` still leaves `instance` enabled and
resource collection continues in `Services()`/the droplet enumeration path.
Update `ResolveServices` usage in `digitalocean.go` and the resource gating
around the droplet listing logic so `droplet` and `instance` are treated as the
same service (either by canonicalizing the alias up front or by checking both
names before enumerating). Ensure the provider’s service filtering behavior
stays consistent with `Services()` and the `-s` filter contract.

---

Nitpick comments:
In `@pkg/providers/gcp/gcp.go`:
- Around line 652-677: The org-level service selection logic in the GCP provider
duplicates the allowlist/exclude resolution that already exists in
ResolveServices, with only the “all” sentinel behavior layered on top. Extract
the shared service resolution into a common helper used by both the org path and
ResolveServices, then keep the Asset API-specific “all” expansion and exclusions
in the org flow after the shared helper runs, referencing the service selection
code in gcp.go and the ResolveServices path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 94527c0b-35df-4d62-a268-d5d3359cb91b

📥 Commits

Reviewing files that changed from the base of the PR and between 2a64fd2 and ea386f4.

📒 Files selected for processing (21)
  • pkg/providers/alibaba/alibaba.go
  • pkg/providers/arvancloud/arvancloud.go
  • pkg/providers/aws/aws.go
  • pkg/providers/azure/azure.go
  • pkg/providers/cloudflare/cloudflare.go
  • pkg/providers/custom/custom.go
  • pkg/providers/digitalocean/digitalocean.go
  • pkg/providers/dnssimple/dnssimple.go
  • pkg/providers/gcp/gcp.go
  • pkg/providers/heroku/heroku.go
  • pkg/providers/hetzner/hetzner.go
  • pkg/providers/k8s/kubernetes.go
  • pkg/providers/linode/linode.go
  • pkg/providers/namecheap/namecheap.go
  • pkg/providers/openstack/openstack.go
  • pkg/providers/ovh/ovh.go
  • pkg/providers/scaleway/scaleway.go
  • pkg/providers/terraform/terraform.go
  • pkg/providers/vercel/vercel.go
  • pkg/schema/schema.go
  • pkg/schema/schema_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/providers/ovh/ovh.go
  • pkg/providers/k8s/kubernetes.go

services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve an explicit empty allowlist instead of defaulting to all services.

ResolveServices currently defaults to all supported services whenever the parsed allowlist is empty. With this shared call, services: typo or an allowlist containing only unknown values enumerates every supported service, instead of using the provided allowlist with unknowns ignored.

Suggested fix in pkg/schema/schema.go
 services := make(ServiceMap)
 if allow, ok := o.GetMetadata("services"); ok {
 	for _, s := range strings.Split(allow, ",") {
 		s = strings.TrimSpace(s)
 		if _, ok := supportedSet[s]; ok {
 			services[s] = struct{}{}
 		}
 	}
-}
-
-// default to all supported services when no allowlist is provided
-if len(services) == 0 {
+} else {
 	for _, s := range supported {
 		services[s] = struct{}{}
 	}
 }

As per coding guidelines, Respect service filtering: Services() must list supported services, and providers should honor -s filters when gathering resources.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/alibaba/alibaba.go` at line 44, Preserve the caller’s explicit
allowlist in the Alibaba provider: the current use of ResolveServices(Services)
causes empty or fully invalid filters to fall back to every supported service,
which breaks service filtering. Update the service resolution path around
Services() and the provider’s services assignment so unknown entries are ignored
but an explicitly empty result stays empty, and only default to all services
when no filter was supplied at all.

Source: Coding guidelines

services[s] = struct{}{}
}
}
services := options.ResolveServices(Services)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Exclude both DigitalOcean droplet aliases.

exclude_services: droplet deletes only droplet, but instance remains enabled and line 67 still enumerates droplets. Treat droplet and instance as the same exclusion, or canonicalize the alias before resource gating.

Suggested local fix
+import "strings"
+
 services := options.ResolveServices(Services)
+if exclude, ok := options.GetMetadata("exclude_services"); ok {
+	for _, service := range strings.Split(exclude, ",") {
+		switch strings.TrimSpace(service) {
+		case "droplet", "instance":
+			delete(services, "droplet")
+			delete(services, "instance")
+		}
+	}
+}

As per coding guidelines, Respect service filtering: Services() must list supported services, and providers should honor -s filters when gathering resources.

Also applies to: 67-68

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/providers/digitalocean/digitalocean.go` at line 28, The DigitalOcean
provider is only honoring one alias for droplet exclusion, so `exclude_services:
droplet` still leaves `instance` enabled and resource collection continues in
`Services()`/the droplet enumeration path. Update `ResolveServices` usage in
`digitalocean.go` and the resource gating around the droplet listing logic so
`droplet` and `instance` are treated as the same service (either by
canonicalizing the alias up front or by checking both names before enumerating).
Ensure the provider’s service filtering behavior stays consistent with
`Services()` and the `-s` filter contract.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] Add exclude_services support

2 participants