Skip to content
This repository was archived by the owner on Jan 30, 2026. It is now read-only.

Fix the failure for lint/unit/integration#3

Closed
yingzhanredhat wants to merge 1 commit intoopenshift-hyperfleet:mainfrom
yingzhanredhat:ying-test
Closed

Fix the failure for lint/unit/integration#3
yingzhanredhat wants to merge 1 commit intoopenshift-hyperfleet:mainfrom
yingzhanredhat:ying-test

Conversation

@yingzhanredhat
Copy link

@yingzhanredhat yingzhanredhat commented Dec 18, 2025

  • Fix the lint failures
  • Add make test-integration with empty content
  • Add a unit test pkg/config/job_test.go for exmaple
  • Add owners file
make lint
Running golangci-lint...
0 issues.

Linting passed!

 yingzhan@yingzhan-mac  ~/ying-work/code/adapter-pull-secret   ying-test  make test
Running unit tests with coverage...
go test -v -race -coverprofile=coverage.txt -covermode=atomic $(go list ./... | grep -v '/test')
        gitlab.cee.redhat.com/service/hyperfleet/mvp/cmd/pull-secret            coverage: 0.0% of statements
        gitlab.cee.redhat.com/service/hyperfleet/mvp/cmd/pull-secret/jobs               coverage: 0.0% of statements
=== RUN   TestNewJobConfig
--- PASS: TestNewJobConfig (0.00s)
=== RUN   TestJobConfig_AddFlags
--- PASS: TestJobConfig_AddFlags (0.00s)
=== RUN   TestJobConfig_FlagParsing
--- PASS: TestJobConfig_FlagParsing (0.00s)
PASS
coverage: 100.0% of statements
ok      gitlab.cee.redhat.com/service/hyperfleet/mvp/pkg/config (cached)        coverage: 100.0% of statements
        gitlab.cee.redhat.com/service/hyperfleet/mvp/pkg/job            coverage: 0.0% of statements

Coverage report generated: coverage.txt
View HTML coverage: go tool cover -html=coverage.txt

 yingzhan@yingzhan-mac  ~/ying-work/code/adapter-pull-secret   ying-test  make test-integration
Running integration tests...
No integration tests found in ./test/
Create integration tests in ./test/ directory.

 yingzhan@yingzhan-mac  ~/ying-work/code/adapter-pull-secret   ying-test  make image
Building image quay.io/openshift-hyperfleet/pull-secret:883320c...
/opt/homebrew/bin/podman build -t quay.io/openshift-hyperfleet/pull-secret:883320c .
[1/2] STEP 1/8: FROM registry.access.redhat.com/ubi9/go-toolset:1.23 AS builder
[1/2] STEP 2/8: USER root
--> Using cache d48a51ac65b06308febd414962d2f082b2f4c39e0813cb8c7362add8026b5c8f
--> d48a51ac65b0
[1/2] STEP 3/8: WORKDIR /workspace
--> Using cache bee68afc08e81854f821696230d8f32a0ee491a7112a76d060ce1e8520181f4e
--> bee68afc08e8
[1/2] STEP 4/8: COPY --chown=default:root go.mod go.sum ./
--> Using cache 89fa0c6bc7532102666168d2828e88f7c5ed0471523e065fac9400cdecf779c4
--> 89fa0c6bc753
[1/2] STEP 5/8: RUN go mod download
--> Using cache 5bcc84908d7b121c8313f9543f5b24ce6963bd7bfdc39e9ef916b2c3cf1a4461
--> 5bcc84908d7b
[1/2] STEP 6/8: COPY --chown=default:root . .
--> 53c7bb7704e6
[1/2] STEP 7/8: USER default
--> 1eed5bc03805
[1/2] STEP 8/8: RUN CGO_ENABLED=0 go build     -o pull-secret     ./cmd/pull-secret
--> 96e3c0be596e
[2/2] STEP 1/9: FROM registry.access.redhat.com/ubi9/ubi-minimal:latest
[2/2] STEP 2/9: RUN microdnf install -y ca-certificates && microdnf clean all
--> Using cache 40b6b0114c56622d3402e1fb2339ce81955941c602bae493f19d0b7327c1a182
--> 40b6b0114c56
[2/2] STEP 3/9: RUN useradd -u 1000 -m -s /sbin/nologin pullsecret-job
--> Using cache 6273760f5d2b5237ee828bbce4244e557b6b581c1f8861e3000b47caa1f7a33f
--> 6273760f5d2b
[2/2] STEP 4/9: WORKDIR /app
--> Using cache edf0ec8f9c45a2c597c4e4262ec005b511b2a92870b750c49f7e3f492ccb0736
--> edf0ec8f9c45
[2/2] STEP 5/9: COPY --from=builder --chown=1000:1000 /workspace/pull-secret /usr/local/bin/pull-secret
--> ccd3290a7625
[2/2] STEP 6/9: RUN chmod 755 /usr/local/bin/pull-secret
--> 5465abce3b7f
[2/2] STEP 7/9: USER 1000
--> 180954078f88
[2/2] STEP 8/9: ENTRYPOINT ["/usr/local/bin/pull-secret"]
--> a47bc3ff1b25
[2/2] STEP 9/9: CMD ["run-job", "pull-secret"]
[2/2] COMMIT quay.io/openshift-hyperfleet/pull-secret:883320c
--> d286e0def503
Successfully tagged quay.io/openshift-hyperfleet/pull-secret:883320c
d286e0def503d3060c0837d7a969498cd25f77627960ad0dac9cb68512c44a1f

Summary by CodeRabbit

Release Notes

  • New Features

    • Job configuration now supports dry-run and worker-count settings
    • Introduced job registry functionality for managing jobs
    • Added metrics reporting and collection capabilities
    • Enhanced trace context features for improved debugging
  • Chores

    • Updated code linting configuration
    • Improved test infrastructure with integration test support

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 18, 2025

Walkthrough

This PR introduces a job execution framework and configuration system. It adds a JobConfig type with flag registration for job execution parameters (DryRun, WorkerCount). The CommandBuilder gains new setter methods for registry, context, lifecycle hooks, and metrics reporting. A new JobRegistry type is introduced to manage registered jobs. A metrics system with MetricsReporter interface and MetricsCollector implementation is added, along with a StdoutReporter for logging metrics. A trace context utility function AddTraceContext enables thread-safe context enrichment. Internal identifiers are renamed for clarity. Build tooling is updated with a new test-integration target, and linter configuration is restructured.

Sequence Diagram

sequenceDiagram
    participant User
    participant CommandBuilder as CommandBuilder<br/>(Setup)
    participant JobRegistry as JobRegistry
    participant Job as Job<br/>(Execution)
    participant Metrics as MetricsCollector
    participant Hooks as Lifecycle<br/>Hooks

    User->>CommandBuilder: SetRegistry(registry)
    User->>CommandBuilder: SetBeforeJob(fn)
    User->>CommandBuilder: SetAfterJob(fn)
    User->>CommandBuilder: SetMetricsReporter(reporter)
    
    User->>JobRegistry: AddJob(job)
    
    User->>CommandBuilder: Build() & Execute
    
    CommandBuilder->>Hooks: BeforeJob(ctx)
    Hooks-->>CommandBuilder: callback
    
    CommandBuilder->>Job: Run with TaskID<br/>from Registry
    
    Job->>Metrics: SetTaskTotal(count)
    Job->>Job: Execute Tasks
    Job->>Metrics: IncTaskSuccess()
    Job->>Metrics: IncTaskFailed()
    
    Job-->>CommandBuilder: completed
    
    CommandBuilder->>Hooks: AfterJob(ctx)
    Hooks-->>CommandBuilder: callback
    
    CommandBuilder->>Metrics: Snapshot()
    Metrics-->>CommandBuilder: report
    
    CommandBuilder-->>User: execution result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Key areas requiring attention:

  • pkg/job/job.go: Substantial changes to CommandBuilder, introduction of JobRegistry, and panic handler signature modification; verify thread-safety of registry and consistency of callback patterns
  • pkg/job/metrics.go: New MetricsReporter interface and MetricsCollector implementation; validate snapshot semantics and concurrency handling
  • pkg/config/job.go: Flag parsing and default value handling for new job configuration parameters
  • pkg/job/trace_context.go: Thread-safe context callback registration with mutex; ensure no race conditions in registration flow
  • Cross-component integration between CommandBuilder, JobRegistry, and MetricsReporter to confirm correct wiring

Possibly related PRs

Suggested reviewers

  • ciaranRoche

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title is vague and generic, using non-descriptive terms that don't convey meaningful information about the main changes. Consider a more specific title that highlights the primary change, such as 'Add JobConfig, metrics, and testing infrastructure' or 'Refactor linting configuration and add test targets'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Makefile (1)

73-87: Pipeline failure: cmd/pull-secret directory does not exist.

The CI pipeline failed because the binary target references ./cmd/pull-secret which doesn't exist in the repository. This PR should either:

  1. Add the missing cmd/pull-secret directory with the main package, or
  2. Update the Makefile to reference the correct path
#!/bin/bash
# Verify the cmd directory structure
echo "Checking for cmd directories:"
fd -t d 'cmd' --max-depth 2
echo ""
echo "Checking for main.go files:"
fd 'main.go'
pkg/job/job.go (1)

319-319: Add getter methods to avoid breaking encapsulation.

Direct access to unexported metricsCollector.taskTotal and metricsCollector.taskFailed fields bypasses the MetricsCollector's thread-safety design. While safe in the current sequential access pattern (after pool.Run completes), this violates encapsulation and makes the code fragile to future changes.

🔎 Add getter methods to MetricsCollector:

In pkg/job/metrics.go, add these getter methods:

// GetTaskTotal returns the total number of tasks.
func (m *MetricsCollector) GetTaskTotal() uint32 {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.taskTotal
}

// GetTaskFailed returns the number of failed tasks.
func (m *MetricsCollector) GetTaskFailed() uint32 {
	m.mu.Lock()
	defer m.mu.Unlock()
	return m.taskFailed
}

Then update the access in pkg/job/job.go:

-	if metricsCollector.taskTotal == 0 {
+	if metricsCollector.GetTaskTotal() == 0 {
 		// this can happen when there are no tasks!
 		ulog.Contextual().Info("No tasks to run!")
 		return nil
 	}
 	// For now return error when all tasks fail. This can be configurable for e.g. return error when 80% of tasks fail.
-	if metricsCollector.taskFailed == metricsCollector.taskTotal {
+	if metricsCollector.GetTaskFailed() == metricsCollector.GetTaskTotal() {
 		err := errors.New("all tasks failed")
 		return err
 	}

Also applies to: 325-325

🧹 Nitpick comments (3)
Makefile (1)

103-116: Integration test target looks good with a minor suggestion.

The implementation correctly checks for the existence of test files before running. Consider adding a non-zero exit if no tests are found when running in CI, to catch missing integration tests intentionally.

🔎 Optional: Exit with error in CI when no integration tests exist
 test-integration:
 	@echo "Running integration tests..."
 	@if [ -n "$$(find ./test -name '*_test.go' 2>/dev/null)" ]; then \
 		go test -v -race ./test/...; \
 		echo ""; \
 		echo "Integration tests complete."; \
 		echo ""; \
 	else \
 		echo "No integration tests found in ./test/"; \
 		echo "Create integration tests in ./test/ directory."; \
 		echo ""; \
+		if [ -n "$$CI" ]; then exit 1; fi; \
 	fi
pkg/config/job.go (1)

20-24: Consider adding validation for WorkerCount.

The WorkerCount field accepts any integer value including zero or negative numbers, which could cause issues at runtime. Consider adding a Validate() method or bounds checking.

🔎 Optional: Add a Validate method
// Validate checks if the JobConfig values are valid.
func (c *JobConfig) Validate() error {
	if c.WorkerCount < 1 {
		return fmt.Errorf("worker-count must be at least 1, got %d", c.WorkerCount)
	}
	return nil
}
pkg/config/job_test.go (1)

43-64: LGTM on flag parsing tests.

The test validates that parsed flag values are correctly bound to the config struct. The TODO for edge case tests (line 63) aligns with the optional validation suggestion for WorkerCount.

Would you like me to generate the edge case tests mentioned in the TODO comment (negative worker count, zero values, etc.)?

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cbf7c08 and 85fb4be.

📒 Files selected for processing (8)
  • .golangci.yml (2 hunks)
  • Makefile (3 hunks)
  • OWNERS (1 hunks)
  • pkg/config/job.go (1 hunks)
  • pkg/config/job_test.go (1 hunks)
  • pkg/job/job.go (11 hunks)
  • pkg/job/metrics.go (3 hunks)
  • pkg/job/trace_context.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
pkg/config/job_test.go (1)
pkg/config/job.go (1)
  • NewJobConfig (13-18)
pkg/job/job.go (2)
pkg/job/metrics.go (3)
  • MetricsReporter (11-13)
  • NewMetricsCollector (26-28)
  • MetricsCollector (17-23)
pkg/job/trace_context.go (1)
  • AddTraceContext (29-41)
🪛 checkmake (0.2.2)
Makefile

[warning] 94-94: Target body for "test" exceeds allowed length of 5 (6).

(maxbodylength)

🪛 GitHub Actions: CI
Makefile

[error] 1-1: Binary build failed: directory not found for output path '/home/runner/work/adapter-pull-secret/adapter-pull-secret/cmd/pull-secret'. Command: 'CGO_ENABLED=0 go build -o pull-secret ./cmd/pull-secret' (triggered by 'make binary'). Exit code: 2.

🔇 Additional comments (16)
OWNERS (2)

2-2: Verify the numeric user ID works with your OWNERS tooling.

The entry "86254860" appears to be a GitHub user ID rather than a username. Depending on your Prow/OWNERS configuration, numeric IDs may not be resolved correctly for approvals and review assignments. Consider using the actual GitHub username instead.

Also applies to: 19-19


1-33: LGTM overall.

The OWNERS file structure is correct for Prow-based approval workflows. The approvers and reviewers lists are identical, which is a valid configuration.

Makefile (1)

93-101: LGTM on test target modification.

Excluding the ./test directory from unit tests is appropriate to separate unit tests from integration tests. The grep -v '/test' pattern correctly filters out the test directory.

.golangci.yml (2)

4-5: LGTM on golangci-lint v2 migration.

The configuration correctly uses the v2 format with the new formatters section for gofmt and goimports, which are no longer regular linters in v2.

Also applies to: 22-25


29-33: Remove the check-shadowing entry from the govet disable list.

check-shadowing is v1 syntax that was replaced with enable: - shadow in golangci-lint v2. The line disable: - check-shadowing is redundant—only enable: - shadow is needed.

Likely an incorrect or invalid review comment.

pkg/config/job.go (1)

6-10: LGTM on JobConfig struct.

Clean struct definition with appropriate JSON tags for serialization.

pkg/config/job_test.go (2)

9-23: LGTM on default value tests.

Good coverage of the constructor defaults and nil check.


25-41: LGTM on flag registration tests.

The test correctly verifies that both flags are registered with the FlagSet.

pkg/job/trace_context.go (1)

27-41: LGTM! Thread-safe context enrichment implementation.

The lazy callback registration with mutex protection ensures thread-safe operation while avoiding initialization dependencies. The implementation correctly enriches the context with trace information.

pkg/job/metrics.go (3)

35-47: LGTM! Properly synchronized counter increments.

Both IncTaskSuccess and IncTaskFailed correctly use mutex locks to ensure thread-safe increments from concurrent workers.


49-61: LGTM! Thread-safe snapshot implementation.

The Snapshot method correctly locks the mutex to capture a consistent point-in-time view of all metrics fields.


67-72: LGTM! Snapshot ensures consistent metrics reporting.

The reporter correctly obtains a snapshot before logging, which guarantees that all reported values represent the same point in time.

pkg/job/job.go (4)

51-85: LGTM! Clean fluent builder pattern.

The setter methods correctly return *CommandBuilder for method chaining and provide a clear API for configuring job execution.


106-108: LGTM! Good defensive programming with default reporter.

Setting a default StdoutReporter when none is provided ensures metrics are always reported, preventing silent failures.


130-148: LGTM! Straightforward job registry implementation.

The registry provides a simple and effective way to manage job registration with proper nil checking.


260-332: LGTM! Well-structured job execution with proper lifecycle management.

The jobRunner correctly orchestrates:

  • Lifecycle hooks (BeforeJob/AfterJob)
  • Panic handling with appropriate re-throw behavior in the main goroutine
  • Worker pool coordination with WaitGroup
  • Metrics reporting after task completion

taskCtx := AddTraceContext(ctx, "workerId", strconv.Itoa(workerId))
taskCtx = AddTraceContext(taskCtx, "taskName", task.TaskName())
taskCtx = AddTraceContext(taskCtx, "taskId", taskId)
taskCtx = AddTraceContext(taskCtx, "taskId", taskID)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Complete the taskId → taskID refactoring.

The context key still uses "taskId" with lowercase 'd', but Go naming conventions prefer "taskID" with uppercase 'ID'. The AI summary indicates this refactoring was intended but appears incomplete at these locations.

🔎 Apply this diff to use consistent naming:
-					taskCtx = AddTraceContext(taskCtx, "taskId", taskID)
+					taskCtx = AddTraceContext(taskCtx, "taskID", taskID)
-					logger.NewOCMLogger(taskCtx).Contextual().Info("Processing task", "workerId", workerId, "taskId", taskID)
+					logger.NewOCMLogger(taskCtx).Contextual().Info("Processing task", "workerId", workerId, "taskID", taskID)

Also applies to: 225-225

🤖 Prompt for AI Agents
In pkg/job/job.go around lines 213 and 225 the context key still uses "taskId"
(lowercase 'd') while the refactor intends "taskID" (uppercase 'ID'); change the
string literal keys passed to AddTraceContext from "taskId" to "taskID" at both
locations so the context key naming is consistent with Go conventions and the
earlier refactor.

pool := workerPool{Queue: taskQueue, Workers: workerCount, PanicHandler: nil, MetricsCollector: metricsCollector}
pool.Run(ctx)

if metricsCollector.taskTotal == 0 {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Use getter methods for consistent encapsulation.

Same encapsulation issue as in jobRunner: direct field access should be replaced with getter methods to maintain the thread-safety contract of MetricsCollector.

🔎 Update TestRunner to use getter methods:
-	if metricsCollector.taskTotal == 0 {
+	if metricsCollector.GetTaskTotal() == 0 {
 		// No tasks to run
 		return nil
 	}
 	// For now return error when all tasks fail. This can be configurable for e.g. return error when 80% of tasks fail.
-	if metricsCollector.taskFailed == metricsCollector.taskTotal {
+	if metricsCollector.GetTaskFailed() == metricsCollector.GetTaskTotal() {
 		err := errors.New("all tasks failed")
 		return err
 	}

Also applies to: 362-362

🤖 Prompt for AI Agents
In pkg/job/job.go around lines 357 and 362, the code accesses MetricsCollector
fields directly (e.g., metricsCollector.taskTotal) which violates the
encapsulation/thread-safety contract; replace direct field reads with the
provided getter methods (e.g., metricsCollector.TaskTotal() or the actual getter
name used in the type) so all reads go through the thread-safe accessors,
updating both occurrences at lines 357 and 362 to call the getters.

Comment on lines +30 to 33
// SetTaskTotal sets the total number of tasks.
func (m *MetricsCollector) SetTaskTotal(total uint32) {
m.taskTotal = total
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add mutex lock to ensure thread-safety consistency.

SetTaskTotal doesn't lock the mutex while modifying taskTotal, but Snapshot() locks when reading it. While the current usage happens to be safe (called before workers start), this creates an inconsistent thread-safety contract for the MetricsCollector API.

🔎 Apply this diff to add proper locking:
 func (m *MetricsCollector) SetTaskTotal(total uint32) {
+	m.mu.Lock()
+	defer m.mu.Unlock()
 	m.taskTotal = total
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// SetTaskTotal sets the total number of tasks.
func (m *MetricsCollector) SetTaskTotal(total uint32) {
m.taskTotal = total
}
// SetTaskTotal sets the total number of tasks.
func (m *MetricsCollector) SetTaskTotal(total uint32) {
m.mu.Lock()
defer m.mu.Unlock()
m.taskTotal = total
}
🤖 Prompt for AI Agents
In pkg/job/metrics.go around lines 30 to 33, SetTaskTotal modifies m.taskTotal
without acquiring the mutex while Snapshot reads it under lock; add proper
locking to make the API thread-safe by acquiring m.mu.Lock() at the start of
SetTaskTotal and deferring m.mu.Unlock() (or unlocking after assignment) so the
write is protected consistently with Snapshot.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant