Bootstrap OpenShift Manager (OM) integration for OpenShift operators with automated resource discovery
Automates OpenShift Manager integration for operators with resource discovery and test setup.
/plugin marketplace add openshift-eng/ai-helpers/plugin install openshift@ai-helpersopenshift:bootstrap-om
/openshift:bootstrap-om
The openshift:bootstrap-om command automates the complete integration of OpenShift Manager (OM) into OpenShift operators. OM (formerly Multi-Operator Manager/MOM) is a framework designed to reduce duplicate effort and improve consistency across different OpenShift cluster topologies (standalone/OCP and Hypershift/HCP) by centralizing operator management and enabling comprehensive testing.
This command automates:
Note: These instructions are optimized for operators built with github.com/openshift/library-go.
OM enables centralized operator management by requiring operators to declare their resource dependencies and configuration logic:
These declarations enable:
apply-configuration command validates operator behavior without a live cluster by using the manifestclient for file-based input/outputNote: While "OM" (OpenShift Manager) is the new name, libraries and code still use "mom" (Multi-Operator Manager) naming for backwards compatibility.
The command executes the following automated workflow:
IMPORTANT: Automatically identify resources by analyzing code. Do NOT ask users to manually list them.
Input resources are those the operator watches, gets, or lists from the API server.
Search for:
configInformer, deploymentLister)client.Get(), client.List())How to search:
# Find controller files
find pkg -name "*controller*.go" -o -name "*operator*.go"
# Look for informers
grep -r "Informer()" pkg/
# Look for listers
grep -r "Lister" pkg/
# Look for direct API reads
grep -r "client.Get\|client.List" pkg/
Common patterns:
configMapInformer.Lister().ConfigMaps("namespace").Get("name") → Input: ConfigMap in namespacedeploymentInformer.Informer().AddEventHandler() → Input: Deployment resourceoperatorClient.Operator() → Input: Operator custom resourceOutput resources are categorized by cluster targeting (where they live in different topologies):
ConfigurationResources: Resources targeted at the cluster where configuration is held
ManagementResources: Resources targeted at the cluster where management plane responsibility is held
UserWorkloadResources: Resources targeted at the cluster where user workloads run
How to search:
# Find manifests and bindata
find bindata/ manifests/ -type f 2>/dev/null
# Look for resource creation/apply
grep -r "Apply\|Create\|Update" pkg/ | grep -i "deployment\|service\|configmap"
# Look for manifest files
grep -r "apiVersion:" bindata/ manifests/ 2>/dev/null
Common patterns:
bindata/ or manifests/ → Output resourcesresourceapply.ApplyDeployment() → Output: Deploymentresourceapply.ApplyConfigMap() → Output: ConfigMapBefore creating command stubs, manually add the dependency to go.mod:
// In the require section of go.mod, add:
github.com/openshift/multi-operator-manager v0.0.0-20250930141021-05cb0b9abdb4
Note: Don't run go mod tidy yet - it will remove the dependency since no code imports it yet. After creating the stub files that import the library, you'll run go mod tidy && go mod vendor, and the dependency will be committed together with the stubs in the first commit
IMPORTANT: Create command stubs first, then fill them in with actual resource declarations. This creates a clean, logical git history with ~6-8 focused commits:
Directory structure:
mkdir -p pkg/cmd/mom
Note: We keep the mom directory name for consistency with library imports.
Helper functions for declaring resources (use in both input-resources and output-resources):
Common resources:
ExactConfigMap(namespace, name), ExactSecret(namespace, name), ExactDeployment(namespace, name)ExactService(namespace, name), ExactServiceAccount(namespace, name), ExactNamespace(name)ExactRole(namespace, name), ExactRoleBinding(namespace, name)OpenShift-specific:
ExactConfigResource(name) - For config.openshift.io resources (e.g., "oauths", "ingresses")ExactLowLevelOperator(name) - For operator.openshift.io resourcesExactClusterOperator(name) - For ClusterOperatorsCluster-scoped RBAC:
ExactClusterRole(name), ExactClusterRoleBinding(name)Other:
ExactPDB(namespace, name), ExactOAuthClient(name), GeneratedCSR(prefix)ExactResource(group, version, resource, namespace, name) - For any other resourceCLI integration pattern (add to cmd/*/main.go):
import (
"github.com/openshift/YOUR-OPERATOR/pkg/cmd/mom"
)
// In your main command function:
cmd.AddCommand(mom.NewInputResourcesCommand(ioStreams))
cmd.AddCommand(mom.NewOutputResourcesCommand(ioStreams))
cmd.AddCommand(mom.NewApplyConfigurationCommand(ioStreams))
Build/test pattern:
make build
./your-operator-binary <command-name> # Test the command
git add <files>
git commit -m "<title>
<optional description>
Generated with Claude Code"
Create pkg/cmd/mom/input_resources_command.go:
package mom
import (
"context"
"github.com/openshift/multi-operator-manager/pkg/library/libraryinputresources"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
)
func NewInputResourcesCommand(streams genericiooptions.IOStreams) *cobra.Command {
return libraryinputresources.NewInputResourcesCommand(runInputResources, runOutputResources, streams)
}
func runInputResources(ctx context.Context) (*libraryinputresources.InputResources, error) {
return &libraryinputresources.InputResources{
ApplyConfigurationResources: libraryinputresources.ResourceList{
ExactResources: []libraryinputresources.ExactResourceID{
// TODO: Fill in discovered resources
},
},
}, nil
}
Integrate into CLI (see Common Patterns), build, test, and commit with title: "Add OM input-resources command stub"
Analyze the codebase to discover all input resources (see Section 1 for search patterns).
Update runInputResources function with discovered resources:
func runInputResources(ctx context.Context) (*libraryinputresources.InputResources, error) {
return &libraryinputresources.InputResources{
ApplyConfigurationResources: libraryinputresources.ResourceList{
ExactResources: []libraryinputresources.ExactResourceID{
// Example discovered resources (see Helper Functions):
libraryinputresources.ExactLowLevelOperator("youroperators"),
libraryinputresources.ExactConfigResource("infrastructures"),
libraryinputresources.ExactNamespace("your-operator-namespace"),
libraryinputresources.ExactConfigMap("namespace", "config-name"),
// ... all discovered input resources
},
},
}, nil
}
Build, test, and commit with title: "Populate OM input-resources with discovered resources"
Create pkg/cmd/mom/output_resources_command.go:
package mom
import (
"context"
"github.com/openshift/multi-operator-manager/pkg/library/libraryoutputresources"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
)
func NewOutputResourcesCommand(streams genericiooptions.IOStreams) *cobra.Command {
return libraryoutputresources.NewOutputResourcesCommand(runOutputResources, streams)
}
func runOutputResources(ctx context.Context) (*libraryoutputresources.OutputResources, error) {
return &libraryoutputresources.OutputResources{
ConfigurationResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{},
},
ManagementResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{},
EventingNamespaces: []string{},
},
UserWorkloadResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{},
},
}, nil
}
Integrate into CLI, build, test, and commit with title: "Add OM output-resources command stub"
Analyze the codebase to discover all output resources (see Section 1 for search patterns and category descriptions).
Update runOutputResources function with discovered resources:
func runOutputResources(ctx context.Context) (*libraryoutputresources.OutputResources, error) {
return &libraryoutputresources.OutputResources{
ConfigurationResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{
libraryoutputresources.ExactConfigResource("ingresses"),
},
},
ManagementResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{
libraryoutputresources.ExactClusterOperator("operatorname"),
libraryoutputresources.ExactNamespace("namespace"),
libraryoutputresources.ExactDeployment("namespace", "name"),
// ... all discovered management resources
},
EventingNamespaces: []string{
"operator-namespace",
},
},
UserWorkloadResources: libraryoutputresources.ResourceList{
ExactResources: []libraryoutputresources.ExactResourceID{
libraryoutputresources.ExactClusterRole("name"),
libraryoutputresources.ExactClusterRoleBinding("name"),
},
},
}, nil
}
Build, test, and commit with title: "Populate OM output-resources with discovered resources"
This is the most complex command. It requires running your operator's reconciliation logic once using the manifestclient.
Create pkg/cmd/mom/apply_configuration_command.go:
package mom
import (
"context"
"fmt"
"github.com/openshift/multi-operator-manager/pkg/library/libraryapplyconfiguration"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericiooptions"
)
func NewApplyConfigurationCommand(streams genericiooptions.IOStreams) *cobra.Command {
return libraryapplyconfiguration.NewApplyConfigurationCommand(RunApplyConfiguration, runOutputResources, streams)
}
func RunApplyConfiguration(ctx context.Context, input libraryapplyconfiguration.ApplyConfigurationInput) (*libraryapplyconfiguration.ApplyConfigurationRunResult, libraryapplyconfiguration.AllDesiredMutationsGetter, error) {
// TODO: Implement operator reconciliation logic
//
// The manifestclient (input.ManagementClient) is a drop-in replacement for standard k8s clients.
// Pass it to your operator and run sync logic ONCE (not in a loop).
//
// Example: op := operator.NewOperator(input.ManagementClient, input.ManagementEventRecorder)
// if err := op.Sync(ctx); err != nil { return nil, nil, err }
// return &libraryapplyconfiguration.ApplyConfigurationRunResult{}, nil, nil
//
// See: github.com/openshift/cluster-authentication-operator/pkg/cmd/mom/apply_configuration_command.go
return nil, nil, fmt.Errorf("not yet implemented - see TODO above")
}
Integrate into CLI, build (verify it compiles), and commit with title: "Add OM apply-configuration command stub"
Note: If you cannot implement apply-configuration fully, that's acceptable! The stub provides instructions for manual implementation later.
IMPORTANT: Check if the project uses build-machinery-go before proceeding:
ls vendor/github.com/openshift/build-machinery-go/make/targets/openshift/operator/mom.mk
If build-machinery-go is NOT present:
build-machinery-gogithub.com/openshift/build-machinery-go firstIf build-machinery-go IS present:
Add to your Makefile:
include $(addprefix ./vendor/github.com/openshift/build-machinery-go/make/, \
targets/openshift/operator/mom.mk \
)
This provides:
make test-operator-integration - Runs OM tests and validates outputmake update-test-operator-integration - Updates expected test outputCreate test directory structure:
mkdir -p test-data/apply-configuration/overall/minimal-cluster/input-dir
Each test scenario needs:
test.yaml - Test metadata (binaryName, testName, description, testType, now)input-dir/ - Input resources in must-gather format organized by cluster-scoped-resources/<group>/<kind>.yaml or namespaces/<namespace>/<group>/<kind>/<name>.yamlRun initial test:
make build
make test-operator-integration
make update-test-operator-integration # Update expected output
make test-operator-integration # Should now pass
Commit Makefile (and test-data/ if created) with title: "Add OM test infrastructure"
make test-operator-integration - Should pass after updating expected outputStudy these files in cluster-authentication-operator:
pkg/cmd/mom/input_resources_command.go - Complete input resource declarationspkg/cmd/mom/output_resources_command.go - Complete output resource declarationspkg/cmd/mom/apply_configuration_command.go - Integration with operator core logiccmd/*/main.go - CLI integration patternMakefile - OM makefile inclusiontest-data/apply-configuration/overall/ - Multiple test scenario examplesDocumentation:
Key Concept - manifestclient:
Implements standard Kubernetes client interfaces (client.Client, kubernetes.Interface, dynamic.Interface). It's a drop-in replacement that reads from files instead of the API server - no refactoring needed!
Before considering OM integration complete:
pkg/cmd/mom/input_resources_command.go exists and lists all input resourcespkg/cmd/mom/output_resources_command.go exists and lists all output resourcespkg/cmd/mom/apply_configuration_command.go exists (stub or implementation)Makefile includes mom.mk (if build-machinery-go available)test-data/apply-configuration/overall/ (if apply-configuration implemented)make test-operator-integration passes (if tests created)Separate Dependency Commits - ALWAYS commit dependency changes separately. Any changes to go.mod, go.sum, or vendor/ must go in their own commits. NEVER mix vendor changes with code implementation.
Automatic Analysis - Analyze codebase automatically. Do NOT ask users to list resources.
apply-configuration Can Be a Stub - If you cannot implement it fully, create a comprehensive stub with TODO comments. This is acceptable!
Validate Early - Build and test after each command stub/fill-in to catch issues immediately.
The command should autonomously complete all steps from resource discovery through test infrastructure setup (if build-machinery-go is available), creating focused, logical commits that are easy to review.