Skip to main content

create-gatherer

Create a new gathering function including implementation, tests, sample archive data, registration, and docs regeneration

Source facts

Repository
openshift/insights-operator
Last source activity
August 21, 2026 at 07:14
Detected SKILL.md language
English
Stars
42
Forks
122

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
create-gatherer
description
Create a new gathering function including implementation, tests, sample archive data, registration, and docs regeneration
# create-gatherer Create a new gathering function in the insights-operator, following the project's established patterns exactly. ## Usage ``` /create-gatherer <jira-ticket-or-description> ``` **Arguments:** - `<jira-ticket-or-description>` - Either a Jira ticket key (e.g., `CCXDEV-12345`) or a natural language description of what to gather. When a Jira ticket is provided, the skill fetches the ticket details using `acli` and extracts requirements from it. ## Examples ```bash # From a Jira ticket (preferred — pulls requirements automatically) /create-gatherer CCXDEV-16040 # Plain description — gather a single cluster-scoped OpenShift config resource /create-gatherer Gather the cluster DNS config (config.openshift.io/v1 DNS) # Plain description — gather a list of namespaced core K8s resources /create-gatherer Gather all Deployments in openshift-* namespaces, limit to 200 # Plain description — gather a custom resource using dynamic client /create-gatherer Gather VirtualMachines from kubevirt.io/v1, include only status ``` --- prompt: | You are creating a new gathering function for the insights-operator project. Follow EVERY convention below exactly. Do not deviate. ## Step 0: Fetch Jira ticket (if applicable) Check if the user's input matches a Jira ticket key pattern (e.g., `CCXDEV-12345`, `OCPBUGS-99999` — typically uppercase letters, a dash, and digits). If it is a Jira ticket key, fetch the ticket details: ```bash acli jira workitem view <TICKET_KEY> --fields summary,description,comment,labels,priority ``` Extract from the ticket: - **Summary**: the one-line title (often contains the resource type and API group) - **Description**: detailed requirements — look for resource kinds, API groups/versions, expected instance counts, backport requirements, linked Insights recommendations (INSIGHTOCP tickets), and any specific fields mentioned - **Comments**: may contain additional context, decisions, or clarifications from the team Use this information to pre-fill the requirements in Step 1. Present what you extracted to the user and ask them to confirm or correct before proceeding. If the input is NOT a Jira ticket key, treat it as a plain text description and proceed directly to Step 1. ## Step 1: Gather requirements from the user's input From the user's description (or the Jira ticket content from Step 0), determine: 1. **Resource type** (e.g., `DNS`, `Deployment`, `VirtualMachine`) 2. **API group/version** (e.g., `config.openshift.io/v1`, `apps/v1`, `kubevirt.io/v1`) 3. **Scope**: cluster-scoped singleton (`Get`) vs list of resources (`List`) 4. **Namespace filter**: all namespaces, specific namespace, or `openshift-*` prefix filter 5. **What to store**: full resource, status only, or specific fields 6. **Limits**: maximum number of records if listing many resources 7. **Which gatherer package** to add the function to (default: `clusterconfig`; other options: `workloads`, `conditional`) 8. **Backport requirements**: if the ticket specifies backport versions, note them for the doc comment If anything is ambiguous, ASK the user before proceeding. ## Step 2: Investigate the resource schema for sensitive data and data size BEFORE writing any code, research the resource type to identify: - Fields that may contain sensitive data requiring anonymization - The expected data volume and whether size mitigation is needed ### How to investigate: 1. **For OpenShift/Kubernetes API types**: Look up the Go type definition in the vendored dependencies or the OpenShift API repository. Search for the struct definition in `vendor/` (e.g., `vendor/github.com/openshift/api/config/v1/types.go` for config.openshift.io resources, or `vendor/k8s.io/api/` for core K8s types). 2. **For CRDs**: Search the web for the CRD schema documentation or look at the API reference. 3. **For any resource type**: Use `kubectl explain` or the OpenShift API docs to understand the fields. ### Sensitive data — fields that MUST be anonymized: - **IP addresses** (node IPs, cluster IPs, load balancer IPs, endpoint IPs) - **Hostnames and DNS names** (especially external-facing ones) - **URLs** (API server URLs, webhook URLs, external service URLs) - **Cloud provider identifiers** (AWS region, account ID, VPC ID; GCP project ID, region; Azure subscription, resource group, cloud name) - **Infrastructure names** (cluster name in infrastructure status) - **Certificate data** (PEM-encoded certificates, keys) - **Secret references** (values, not names) - **User identifiers** (usernames, email addresses, organization names) - **MAC addresses** - **Any field that could identify a specific customer or their infrastructure** ### Sensitive data — fields that do NOT need anonymization: - Resource names (metadata.name) — unless they contain user-chosen values that could identify the customer - Labels and annotations with well-known keys (e.g., `app.kubernetes.io/name`) - Enum/type fields (e.g., platform type, storage class provisioner name) - Kubernetes version strings - Resource versions, UIDs, timestamps - Boolean flags, counts, quantities ### Data size — evaluate and mitigate: Every gathered resource ends up in a tar.gz archive uploaded to Red Hat. Large data bloats the archive, increases upload time, and can cause failures. Evaluate the expected size: **Questions to consider:** - How many instances of this resource typically exist in a cluster? (1? 10? 1000? 10000+?) - How large is each resource when serialized to JSON? (1KB? 10KB? 100KB+?) - Does the resource contain large embedded data? (embedded certificates, logs, binary data, large status sections) **Size mitigation strategies (apply as needed):** - **Cap the number of records**: Use a `limit` constant (e.g., `const maxResources = 200`) and truncate with an error noting the truncation. See existing patterns like `GatherActiveAlerts` (max 1000). - **Strip unnecessary fields**: Remove `.metadata.annotations` (if large/noisy), `.metadata.labels` (if noisy), `.status.conditions[*].message` (if verbose), embedded specs of sub-resources. Only store what's needed for analysis. - **Store only .status or .spec**: If only part of the resource is useful, use `record.JSONMarshaller` with a custom struct containing only the needed fields rather than `record.ResourceMarshaller` with the full resource. - **Use field selectors or label selectors**: Filter at the API level to reduce the amount of data fetched. - **Paginate large lists**: Use `metav1.ListOptions{Limit: N}` for very large resource lists. **Size thresholds to flag:** - Single resource > 50KB serialized: consider stripping fields - Total gathered data for this function > 500KB: strongly consider limiting or filtering - More than 500 individual records: cap with a limit constant ### Present findings to the user BEFORE implementing: After investigating, present your findings: ``` Resource: {ResourceType} ({api-group/version}) Anonymization: Fields to anonymize: - .status.infrastructureName (contains cluster-identifying infrastructure name) - .status.platformStatus.aws.region (reveals cloud region) - ... Fields safe as-is: - .spec.platformSpec.type (enum value, not sensitive) - .metadata.name (well-known singleton name "cluster") - ... Data size assessment: - Expected instance count: {N} (singleton / ~10 / up to thousands) - Estimated size per resource: {X}KB - Total estimated archive contribution: {Y}KB - Mitigation needed: {yes/no} - {if yes: specific mitigation strategy} Concerns: - {any fields you're unsure about} - {any fields that might contain user-generated content} - {any size-related concerns} ``` **ASK the user to confirm** the anonymization and data size plan before proceeding. Wait for confirmation. ## Step 3: Determine the archive path Explore `docs/insights-archive-sample/` to understand the existing archive structure and determine where the new data should be placed. **Archive path conventions:** - `config/` — main cluster configuration data (most clusterconfig gatherer data goes here) - `config/{resource}.json` — singleton cluster-scoped resources (e.g., `config/infrastructure.json`, `config/proxy.json`) - `config/{resource_plural}/{name}.json` — list of cluster-scoped resources (e.g., `config/storage/storageclasses/standard-csi.json`) - `config/pod/{namespace}/{pod-name}.json` — pod definitions - `config/configmaps/{namespace}/{name}/{key}` — configmap data - `config/secrets/{namespace}/{name}/data.json` — secret metadata - `config/clusteroperator/{name}.json` — cluster operator statuses - `cluster-scoped-resources/{api-group}/{resource_plural}/{name}.json` — cluster-scoped CRDs not in the config.openshift.io API group (e.g., `cluster-scoped-resources/nmstate.io/nodenetworkstates/`) - `namespaces/{namespace}/{api-group}/{resource_plural}/{name}.json` — namespace-scoped custom resources (e.g., `namespaces/openstack/core.openstack.org/openstackcontrolplanes/`) - `customresources/{api-group}/{resource_plural}/{namespace}/{name}.json` — another pattern for custom resources - `aggregated/` — pre-aggregated or computed data - `conditional/` — conditional gatherer data - `events/` — Kubernetes events by namespace - `machinesets/` — MachineSet resources - `insights-operator/` — operator metadata The `Record.Name` field in Go code corresponds to the file path WITHOUT the extension. The extension comes from `Record.Item.GetExtension()` (`.json` for `ResourceMarshaller` and `JSONMarshaller`). ## Step 4: Choose the client type Pick ONE of these based on the API group: ### A. OpenShift config client (`configv1client`) Use for resources in `config.openshift.io` API group (Infrastructure, OAuth, Network, Ingress, Image, FeatureGate, APIServer, Authentication, Proxy, Scheduler, DNS). - KubeConfig: `g.gatherKubeConfig` - Client creation: `configv1client.NewForConfig(g.gatherKubeConfig)` - Inner function parameter type: `configv1client.ConfigV1Interface` - Fake client import: `configfake "github.com/openshift/client-go/config/clientset/versioned/fake"` - Fake client creation: `configfake.NewClientset(objects...)` - Reference: `pkg/gatherers/clusterconfig/gather_cluster_infrastructure.go` ### B. Core Kubernetes typed client (`kubernetes`) Use for core K8s resources (Pods, Nodes, Secrets, ConfigMaps, Services, PVs, etc.) and sub-APIs (StorageV1, PolicyV1, RBAC, etc.). - KubeConfig: `g.gatherKubeConfig` for JSON encoding, `g.gatherProtoKubeConfig` for protobuf encoding (use proto for high-volume resources like Nodes, Pods) - Client creation: `kubernetes.NewForConfig(g.gatherKubeConfig)` or `kubernetes.NewForConfig(g.gatherProtoKubeConfig)` - Inner function parameter type: the specific sub-client interface (e.g., `corev1client.CoreV1Interface`, `storagev1client.StorageV1Interface`) - Fake client import: `kubefake "k8s.io/client-go/kubernetes/fake"` (or just `"k8s.io/client-go/kubernetes/fake"`) - Fake client creation: `fake.NewClientset(objects...)` - Reference: `pkg/gatherers/clusterconfig/gather_storageclass.go` ### C. Dynamic client Use for custom resources (CRDs) not in the core K8s or OpenShift config APIs. - KubeConfig: `g.gatherKubeConfig` - Client creation: `dynamic.NewForConfig(g.gatherKubeConfig)` - Inner function parameter type: `dynamic.Interface` - Fake client import: `dynamicfake "k8s.io/client-go/dynamic/fake"` - GVR constant must be added to `pkg/gatherers/clusterconfig/const.go` - Reference: `pkg/gatherers/clusterconfig/gather_sap_config.go` ### D. REST client (special cases only) Use for non-standard APIs (Prometheus, Alertmanager). - KubeConfig: `g.metricsGatherKubeConfig` for Prometheus, `g.alertsGatherKubeConfig` for Alertmanager ## Step 5: Derive names From the resource type, derive: - **File name**: `gather_{snake_case_resource}.go` (e.g., `gather_cluster_dns.go`) - **Test file name**: `gather_{snake_case_resource}_test.go` - **Exported method**: `Gather{PascalCaseResource}` (e.g., `GatherClusterDNS`) - **Unexported inner function**: `gather{PascalCaseResource}` (e.g., `gatherClusterDNS`) - **Function ID** (for the registry map): `{snake_case}` (e.g., `cluster_dns`) - **Archive path**: determined in Step 3 (e.g., `config/dns` for singleton, `config/dns/{name}` for list) ## Step 6: Create the implementation file Create the file in the appropriate gatherer package directory (e.g., `pkg/gatherers/clusterconfig/gather_{name}.go`). The implementation MUST follow this exact three-layer structure. Read the reference files to match the pattern precisely: 1. **Exported method on `*Gatherer`** — creates the client from the Gatherer's rest.Config fields and delegates to the inner function. Does NO business logic. Has the structured doc comment with ALL mandatory sections. 2. **Unexported inner function** — accepts client interfaces as parameters (for testability), calls the Kubernetes API, returns `([]record.Record, []error)`. 3. **Anonymization function** (if needed based on Step 2) — unexported, mutates the resource in place to redact sensitive fields. ### Exported method signature (MUST be exactly this): ```go func (g *Gatherer) GatherXxx(ctx context.Context) ([]record.Record, []error) ``` ### Doc comment format (MANDATORY for `make docs` / `cmd/gendoc/main.go`): ```go // GatherXxx Collects the ... // // ### API Reference // - https://TODO_FILL_IN_API_REFERENCE // // ### Sample data // - docs/insights-archive-sample/{archive_path}.json // // ### Location in archive // - `{archive_path}.json` // // ### Config ID // `{gatherer_name}/{function_id}` // // ### Released version // - TODO_VERSION // // ### Backported versions // None (or list versions from Jira ticket if specified) // // ### Changes // None ``` The Config ID MUST use the format `{gatherer_name}/{function_id}` where `gatherer_name` is `clusterconfig`, `workloads`, or `conditional`, and `function_id` matches the key in the gathering functions registry. ### Record type selection: - `record.ResourceMarshaller{Resource: obj}` — for typed K8s/OpenShift objects. Automatically strips `managedFields`. Extension: `.json`. - `record.JSONMarshaller{Object: obj}` — for arbitrary Go structs, maps, or subsets of a resource. Extension: `.json`. - `marshal.Raw{Str: s}` — for raw string data (logs, metrics). No extension. Import: `"github.com/openshift/insights-operator/pkg/utils/marshal"`. ### Anonymization implementation (based on Step 2 findings): Use functions from `"github.com/openshift/insights-operator/pkg/utils/anonymize"`: - `anonymize.String(s)` — replaces each character with 'x', preserving length - `anonymize.URL(s)` — anonymizes URL components - `anonymize.Bytes(b)` — byte slice variant ### Import grouping order (MANDATORY): 1. Standard library 2. External dependencies (blank line separator) 3. Project packages (blank line separator) ## Step 7: Create the test file Create the test file alongside the implementation file. ### Test rules: - Tests call the **UNEXPORTED inner function**, never the exported method
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub