Skip to content

Feature Request: Fleet should skip deployment when no targetCustomization matches the cluster #4482

@ziidu

Description

@ziidu

Is your feature request related to a problem?

Fleet should skip deployment when no targetCustomization matches the cluster

Problem Description

When a GitRepo monitors a path containing an application, but the application's fleet.yaml contains targetCustomizations that don't match the current cluster, Fleet still attempts to deploy the application instead of gracefully skipping it. This results in deployment errors that should not occur.

Current Behavior

GitRepo Configuration:

apiVersion: fleet.cattle.io/v1alpha1
kind: GitRepo
metadata:
  name: xgcd
  namespace: fleet-local
spec:
  repo: http://gerrit.guasemi.com/devops/xgcd.git
  branch: master
  paths:
    - apps/*    # Monitors all applications (including apps/2/*)
  targets:
    - clusterSelector:
        matchExpressions:
          - key: provider.cattle.io
            operator: NotIn
            values:
              - harvester    # Targets all non-harvester clusters (including local)

Application fleet.yaml:

# apps/2/10005/fleet.yaml
defaultNamespace: default

helm:
  chart: ./
  releaseName: app-2-10005

targetCustomizations:
  - name: prod
    clusterSelector:
      matchLabels:
        env: procluster    # Only matches downstream cluster
    namespace: default
    helm:
      valuesFiles:
        - values-prod.yaml

Result:
The local cluster matches the GitRepo's targets (it's not a harvester cluster), so Fleet creates a Bundle for it. However, the local cluster doesn't have the env: procluster label, so no targetCustomization matches. Instead of skipping the deployment, Fleet still attempts to deploy with incomplete configuration, resulting in errors like:

ErrApplied(1) [Cluster fleet-local/local: template: app-10005/templates/service.yaml:1:14: 
executing "app-10005/templates/service.yaml" at <.Values.service.enabled>: 
nil pointer evaluating interface {}.enabled]

Root Cause Analysis

Fleet has a two-tier filtering mechanism:

  1. GitRepo.spec.targets (Tier 1): Determines which clusters should receive the Bundle

    • In this case: All non-harvester clusters (including local)
  2. fleet.yaml.targetCustomizations (Tier 2): Determines which configuration to use for each cluster

    • In this case: Only clusters with env: procluster label

The Problem:
When a cluster passes Tier 1 filtering (GitRepo targets) but has no matching targetCustomization in Tier 2, Fleet still attempts to deploy instead of gracefully skipping. This creates a mismatch between "this cluster should receive the Bundle" (Tier 1 ✅) and "this cluster has no configuration" (Tier 2 ❌).

Expected Behavior

When a cluster doesn't match any targetCustomizations in the application's fleet.yaml, Fleet should:

  1. NOT create a BundleDeployment for that cluster
  2. Gracefully skip the deployment without errors
  3. Log an informational message (optional) indicating the application was skipped due to no matching targetCustomization

Steps to Reproduce

  1. Create a GitRepo in fleet-local namespace that monitors apps/*
  2. Create an application in apps/2/10005 with fleet.yaml containing only targetCustomizations for downstream clusters (e.g., env: procluster)
  3. Ensure the local cluster does NOT have the env: procluster label
  4. Observe that Fleet still attempts to deploy to the local cluster and fails

Impact

This behavior causes several issues:

  1. Confusing error messages: Users see deployment errors for clusters that shouldn't receive the application
  2. Workaround required: Users must either:
    • Add dummy targetCustomizations for every possible cluster (even if not intended)
    • Create multiple GitRepos with narrow paths filters
    • Use complex targets.clusterSelector in GitRepo
  3. Poor user experience: The error suggests a configuration problem when the configuration is actually correct

Current Workarounds

Workaround 1: Add targetCustomizations for all clusters

targetCustomizations:
  - name: test
    clusterSelector:
      matchLabels:
        env: prod        # Matches local cluster
    helm:
      valuesFiles:
        - values-test.yaml
  
  - name: prod
    clusterSelector:
      matchLabels:
        env: procluster  # Matches downstream cluster
    helm:
      valuesFiles:
        - values-prod.yaml

Pros: Works reliably
Cons: Forces deployment to clusters that may not need the application

Workaround 2: Use narrow GitRepo paths

# fleet-local GitRepo
paths:
  - apps/1/*    # Only system 1

# fleet-default GitRepo  
paths:
  - apps/2/*    # Only system 2

Pros: Clean separation
Cons: Requires multiple GitRepos, more complex configuration

Proposed Solution

Implement a filtering mechanism that checks targetCustomizations before creating BundleDeployments:

GitRepo discovers application
  ↓
Parse fleet.yaml
  ↓
For each target cluster:
  ↓
  Check if cluster matches any targetCustomization
  ↓
  YES → Create BundleDeployment with matched config
  NO  → Skip (do not create BundleDeployment)

Environment

  • Fleet Version: v0.14.0

Additional Context

This issue is particularly problematic in multi-cluster environments where:

  • A monorepo contains applications for different clusters
  • Different applications target different cluster types (dev/staging/prod)
  • GitRepo paths must be broad to monitor multiple applications

The current behavior forces users to tightly couple GitRepo configuration with application targeting, reducing flexibility and increasing maintenance overhead.

Related Issues

  • Similar discussion: [link if you find any related issues]

Is this behavior intentional, or would the Fleet team be open to a PR that implements the proposed filtering logic?

Solution you'd like

No response

Alternatives you've considered

No response

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned
    No fields configured for Feature.

    Projects

    Status

    ✅ Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions