Skip to content

Sample CRs

Working examples of each CRD this operator manages. Apply with:

kubectl apply -n <namespace> -f config/samples/<namespaced-file>.yaml
kubectl apply -f config/samples/tenant_policy_sample.yaml

Namespaced samples intentionally omit metadata.namespace. Apply them to the namespace that should own the CR. TenantPolicy is cluster-scoped and must not carry a namespace.

For invalid-spec fixtures used by webhook tests, see config/samples/invalid/README.md.

Index

Collector: edge-host-42

Manually-managed Collector CR that overrides remote attributes for one specific collector by ID.

Source: config/samples/collector_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: Collector
metadata:
  name: edge-host-42
spec:
  # ID must match a collector that has registered with Fleet Management
  # (the collector calls RegisterCollector when it starts up). The operator
  # does not create collectors — it only manages their remote attributes.
  id: edge-host-42

  # remoteAttributes are key/value pairs the operator writes to Fleet
  # Management. Keys with the prefix "collector." are reserved by Fleet
  # for collector-reported (local) attributes and are rejected by the
  # admission webhook.
  remoteAttributes:
    env: prod
    region: us-east-1
    team: platform

CollectorDiscovery: prod-linux

CollectorDiscovery that polls Fleet Management every 5 minutes and creates a Collector CR for every Linux production collector.

Source: config/samples/collector_discovery_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: CollectorDiscovery
metadata:
  name: prod-linux
spec:
  # How often to call Fleet's ListCollectors. Webhook-enforced minimum
  # is 1 minute to protect the shared 3 req/s rate limiter.
  pollInterval: 5m

  # Server-side filter passed to ListCollectors. Empty selector mirrors
  # every collector — accepted but expensive on large fleets.
  selector:
    matchers:
      - "collector.os=linux"
      - "env=prod"

  # Where to create mirrored Collector CRs. Defaults to this
  # CollectorDiscovery's own namespace if omitted.
  #
  # Setting it to ANOTHER namespace is a privileged, cross-namespace action:
  # the operator uses its cluster-wide ServiceAccount to write Collector CRs
  # there (a confused-deputy vector). Restrict who can create CollectorDiscovery
  # CRs to platform/admin subjects, and consider enabling the manager flag
  # --enforce-cross-namespace-discovery-authz. See docs/security.md.
  # targetNamespace: fleet-mirror

  # Mirror collectors marked inactive in Fleet. Default false.
  # includeInactive: false

  policy:
    # Keep (default): a collector that vanishes from Fleet has its CR
    # marked stale via annotation, but the CR remains so user-added
    # spec.remoteAttributes survive transient outages.
    # Delete: removes the CR; the existing Collector finalizer issues
    # REMOVE ops to Fleet (which 404s for a vanished collector — net
    # no-op). Use this for clean-mirror semantics where preserving
    # user data is not a goal.
    onCollectorRemoved: Keep
    # v1 only ships Skip — leave conflicting CRs alone.
    onConflict: Skip

ExternalAttributeSync: cmdb-host-attributes

ExternalAttributeSync that pulls host metadata from a CMDB HTTP endpoint every 5 minutes and assigns env / region attributes per collector.

Source: config/samples/external_sync_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: ExternalAttributeSync
metadata:
  name: cmdb-host-attributes
spec:
  source:
    kind: HTTP
    http:
      url: https://cmdb.example.com/api/hosts
      method: GET
      # If the response is {"data": {"items": [...]}} use a dotted path:
      # recordsPath: data.items
    # Optional Secret with auth credentials. Recognized keys:
    #   bearer-token   -> Authorization: Bearer <token> (preferred)
    #   username + password -> HTTP Basic auth fallback
    secretRef:
      name: cmdb-credentials

  # Either a Go duration ("5m", "30s") or a 5-field cron expression
  # ("*/15 * * * *"). Required.
  schedule: 5m

  # Selector — same shape as RemoteAttributePolicy. Matches AND-ed; OR-ed
  # with the explicit collectorIDs list. Empty selector = matches nothing.
  selector:
    matchers:
      - "collector.os=linux"

  # Mapping projects each source record into one (collectorID, attributes)
  # tuple. The CMDB returns records like:
  #   {"hostname": "edge-host-42", "env": "prod", "region": "us-east-1"}
  mapping:
    collectorIDField: hostname
    attributeFields:
      env: env
      region: region
    requiredKeys:
      - hostname
      - env

  # Safety guard: when the source returns 0 records but the previous run
  # had >0, the controller preserves the prior claim and surfaces a
  # Stalled condition. Set true to opt out (e.g. when an empty result is
  # legitimate).
  allowEmptyResults: false

Pipeline: alloy-pipeline-sample

Alloy Pipeline that scrapes Alloy's own self-metrics and remote-writes them to Grafana Cloud, scoped to Linux collectors in production.

Source: config/samples/alloy_pipeline_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: Pipeline
metadata:
  labels:
    app.kubernetes.io/name: fleet-management-operator
    app.kubernetes.io/managed-by: kustomize
  name: alloy-pipeline-sample
spec:
  # Unique pipeline name in Fleet Management. Optional: defaults to
  # metadata.name. No whitespace/control characters; max 253 characters.
  name: pipeline_sample

  # Alloy configuration for Prometheus self-monitoring
  contents: |
    prometheus.exporter.self "alloy" { }

    prometheus.scrape "alloy" {
      targets    = prometheus.exporter.self.alloy.targets
      forward_to = [prometheus.remote_write.grafanacloud.receiver]
      scrape_interval = "60s"
    }

    prometheus.remote_write "grafanacloud" {
      external_labels = {"collector_id" = constants.hostname}

      endpoint {
        url = env("PROMETHEUS_URL")

        basic_auth {
          username      = env("PROMETHEUS_USER")
          password_file = "/etc/secrets/prometheus-password"
        }
      }
    }

  # Assign to Linux collectors in production
  matchers:
    - collector.os=linux
    - environment=production

  # Enable the pipeline
  enabled: true

  # Configuration type (Alloy or OpenTelemetryCollector)
  configType: Alloy
  source:
    type: Git
    namespace: github.com/mbaykara/alloy-configs

Pipeline: otel-metrics-pipeline

OpenTelemetry Collector Pipeline that scrapes host metrics and exports OTLP to Grafana Cloud.

Source: config/samples/pipeline_otel_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: Pipeline
metadata:
  labels:
    app.kubernetes.io/name: fleet-management-operator
    app.kubernetes.io/managed-by: kustomize
  name: otel-metrics-pipeline
spec:
  # Unique pipeline name in Fleet Management. Optional: defaults to
  # metadata.name. No whitespace/control characters; max 253 characters.
  name: otel_metrics_pipeline

  # OpenTelemetry Collector configuration
  contents: |
    receivers:
      otlp:
        protocols:
          grpc:
            endpoint: 0.0.0.0:4317
          http:
            endpoint: 0.0.0.0:4318

    processors:
      batch:
        timeout: 10s
        send_batch_size: 1024

    exporters:
      prometheusremotewrite:
        endpoint: ${env:PROMETHEUS_URL}
        auth:
          authenticator: basicauth

    extensions:
      basicauth:
        client_auth:
          username: ${env:PROMETHEUS_USER}
          password: ${env:PROMETHEUS_PASSWORD}

    service:
      extensions: [basicauth]
      pipelines:
        metrics:
          receivers: [otlp]
          processors: [batch]
          exporters: [prometheusremotewrite]

  # Assign to collectors running OTEL
  matchers:
    - collector.type=otel
    - environment=production

  enabled: true

  # Must specify OpenTelemetryCollector for OTEL config
  configType: OpenTelemetryCollector

  # Optional: Source of the pipeline for tracking and grouping
  source:
    type: Terraform
    namespace: production-workspace

PipelineDiscovery: import-alloy-pipelines

PipelineDiscovery that imports enabled Alloy pipelines from Fleet Management as read-only Pipeline CRs.

Source: config/samples/pipeline_discovery_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: PipelineDiscovery
metadata:
  name: import-alloy-pipelines
spec:
  pollInterval: 5m
  importMode: ReadOnly
  selector:
    configType: Alloy
    enabled: true
  # Omit targetNamespace to create Pipeline CRs in this
  # PipelineDiscovery's own namespace.
  #
  # Setting it to ANOTHER namespace is a privileged, cross-namespace action:
  # the operator uses its cluster-wide ServiceAccount to write Pipeline CRs
  # there (a confused-deputy vector). Restrict who can create PipelineDiscovery
  # CRs to platform/admin subjects, and consider enabling the manager flag
  # --enforce-cross-namespace-discovery-authz. See docs/security.md.
  # targetNamespace: fleet-pipelines
  policy:
    onPipelineRemoved: Keep

RemoteAttributePolicy: linux-prod-defaults

RemoteAttributePolicy that assigns default region / team attributes to every Linux production collector. Per-Collector CRs override on key collisions.

Source: config/samples/policy_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: RemoteAttributePolicy
metadata:
  name: linux-prod-defaults
spec:
  # Selectors. A collector matches if all matchers are true (AND-ed) OR if
  # its ID appears in collectorIDs. An empty selector matches NOTHING — both
  # selector clauses must be intentional.
  selector:
    matchers:
      - "collector.os=linux"
      - "env=prod"
    # Optional explicit ID list, OR-ed with the matcher result.
    collectorIDs: []

  # Attributes applied to every matched collector. Per-Collector CRs win on
  # key collisions (Collector spec is the override layer; Policy is the
  # default). collector.* prefixed keys are reserved by Fleet Management.
  attributes:
    region: us-east-1
    team: platform

  # Higher priority wins among policies that match the same collector and
  # set the same key. Equal priority is broken alphabetically by namespaced
  # name. Default is 0.
  priority: 0

TenantPolicy: team-billing

TenantPolicy that requires the billing team's Pipelines, Policies, and Syncs to carry a team=billing matcher (RBAC tenancy guard).

Source: config/samples/tenant_policy_sample.yaml

apiVersion: fleetmanagement.grafana.com/v1alpha1
kind: TenantPolicy
metadata:
  name: team-billing
spec:
  # Subjects this policy applies to. Use the same forms as a RoleBinding:
  # User by username, Group by IDP group name, ServiceAccount by name and
  # namespace.
  subjects:
    - kind: Group
      name: team-billing-engineers
    - kind: ServiceAccount
      name: argocd-billing
      namespace: argocd
  # Members of any subject above must include at least one of these
  # matchers in Pipeline.spec.matchers (or the equivalent
  # spec.selector.matchers on RemoteAttributePolicy / ExternalAttributeSync)
  # to be allowed by the validating webhook.
  requiredMatchers:
    - team=billing
    - team=billing-shared
  # Optional: restrict the policy to specific namespaces by their labels.
  # Omit to apply in every namespace.
  namespaceSelector:
    matchLabels:
      tenant: billing