Skip to content

NO-JIRA: Fix issue in the downloads server: archives are not compressed#16677

Open
nunnatsa wants to merge 1 commit into
openshift:mainfrom
nunnatsa:downloads-fix-compression
Open

NO-JIRA: Fix issue in the downloads server: archives are not compressed#16677
nunnatsa wants to merge 1 commit into
openshift:mainfrom
nunnatsa:downloads-fix-compression

Conversation

@nunnatsa

@nunnatsa nunnatsa commented Jun 24, 2026

Copy link
Copy Markdown

The download server creates zip and tar archives on boot. However, when downloading these archives, their size is the same as the original non-compressed file.

This PR fixes this isssue. It also changes the tar file extension to "tar.gz".

Few more improvements:

embed the configuration file, instead of handling
the file: copy it to the image and write code around open and close it with error handling. embed the file makes it all much more simple.

deprecate the config-path as it is no longer required.

move the port parameter out of the download config as it's not belong there.

don't create archives for broken symlinks, to avoid let the user download them.

Analysis / Root cause:

Solution description:

Screenshots / screen recording:

Test setup:

Test cases:

Browser conformance:

  • Chrome
  • Firefox
  • Safari (or Epiphany on Linux)

Additional info:

Reviewers and assignees:

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Downloads are now served using embedded configuration, so runtime no longer requires a separate config file.
    • Archive downloads now generate and serve gzip-compressed tar archives (.tar.gz) in addition to .zip.
  • Bug Fixes

    • Improved request handling: readiness waiting now targets .tar.gz/.zip and returns 503 promptly when the request is canceled.
    • Added safer startup behavior for LICENSE symlink creation and improved error handling.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jun 24, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@nunnatsa: This pull request explicitly references no jira issue.

Details

In response to this:

The download server creates zip and tar archives on boot. However, when downloading these archives, their size is the same as the original non-compressed file.

This PR fixes this isssue. It also changes the tar file extension to "tar.gz".

Few more improvements:

embed the configuration file, instead of handling
the file: copy it to the image and write code around open and close it with error handling. embed the file makes it all much more simple.

deprecate the config-path as it is no longer required.

move the port parameter out of the download config as it's not belong there.

Analysis / Root cause:

Solution description:

Screenshots / screen recording:

Test setup:

Test cases:

Browser conformance:

  • Chrome
  • Firefox
  • Safari (or Epiphany on Linux)

Additional info:

Reviewers and assignees:

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

The downloads server now loads embedded YAML config bytes, generates .tar.gz archives, updates request handling for the new format, and removes the runtime config file copy from the container image.

Changes

Downloads server and archive changes

Layer / File(s) Summary
Embedded config bootstrap
cmd/downloads/config/downloads_config.go, cmd/downloads/main.go
loadArtifactsSpec now reads YAML from []byte; DownloadsServerConfig drops Port; NewDownloadsServerConfig takes embedded config bytes; main.go embeds config/defaultArtifactsConfig.yaml and passes it directly while keeping --config-path unused.
tar.gz archive pipeline
cmd/downloads/config/downloads_config.go
Archive helpers now take open readers plus file info; createArchive opens targets once; .tar.gz output is created with gzip over tar; directory URLs and background archive creation switch to .tar.gz.
Handler and container runtime
cmd/downloads/config/downloads_config.go, Dockerfile.downloads
The handler gates readiness for .tar.gz and .zip, rewrites .tar requests to .tar.gz, returns HTTP 503 on canceled waits, logs LICENSE symlink failures, and the Dockerfile removes the config file copy and config-path command argument.
Updated tests
cmd/downloads/config/downloads_config_test.go
Tests now expect .tar.gz URLs and fixtures, read tar entries through gzip, and call the config constructor with YAML bytes instead of a temp file path.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 12 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title matches the main fix, but it violates the required Jira-prefixed naming convention by using NO-JIRA. Rename it to a Jira-prefixed title, for example: OCPBUGS-XXXX: Fix downloads server archive compression.
Description check ⚠️ Warning The required template sections are mostly unfilled; Analysis, Solution, Tests, Browser conformance, and Reviewers are missing. Fill in each required section of the template with the root cause, solution, test setup/cases, browser conformance, and reviewers.
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (12 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed Modified tests use only static, deterministic titles; no dynamic pod/UUID/timestamp-like values were found.
Test Structure And Quality ✅ Passed No Ginkgo tests were added; the updated stdlib tests use t.TempDir/t.Cleanup and bounded timeouts, with explicit failure messages.
Microshift Test Compatibility ✅ Passed Changed tests are plain Go unit tests, not Ginkgo e2e, and they reference no MicroShift-unsupported OpenShift APIs or features.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added; the changed tests are plain Go unit tests in cmd/downloads/config and contain no SNO-sensitive cluster assumptions.
Topology-Aware Scheduling Compatibility ✅ Passed PR only changes downloads-server packaging/runtime code; no manifests, controllers, affinities, node selectors, or topology-dependent scheduling logic.
Ote Binary Stdout Contract ✅ Passed cmd/downloads main only uses klog, and vendored klog defaults to stderr; no fmt.Print* or os.Stdout usage in process-level code.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the changed tests are unit tests using httptest and relative paths only, with no IPv4-only or external-network assumptions.
No-Weak-Crypto ✅ Passed No weak crypto APIs or custom crypto/comparison code appear in the touched downloads files; changes are limited to archive/compression and config handling.
Container-Privileges ✅ Passed The only runtime/container change is Dockerfile.downloads using USER 1001; no privileged/hostPID/hostNetwork/hostIPC/SYS_ADMIN/allowPrivilegeEscalation settings were added.
No-Sensitive-Data-In-Logs ✅ Passed Only operational logs appear; they mention temp/artifact paths and startup/errors, with no secrets, PII, tokens, or customer data.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Trivy (0.69.3)

Trivy execution failed: 2026-06-24T11:07:36Z FATAL Fatal error run error: fs scan error: scan error: scan failed: failed analysis: post analysis error: post analysis error: kubernetes scan error: fs filter error: fs filter error: walk error range error: stat .golangci.yml: no such file or directory: range error: stat .golangci.yml: no such file or directory


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

@nunnatsa nunnatsa force-pushed the downloads-fix-compression branch from 6d1fb11 to 89d3e6b Compare June 24, 2026 10:07
@openshift-ci openshift-ci Bot requested review from jhadvig and spadgett June 24, 2026 10:07
@openshift-ci openshift-ci Bot added the component/backend Related to backend label Jun 24, 2026
@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: nunnatsa
Once this PR has been reviewed and has the lgtm label, please assign spadgett for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (1)
cmd/downloads/config/downloads_config.go (1)

224-247: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Handle archive writer close errors before publishing archives.

tar.Writer.Close, gzip.Writer.Close, and zip.Writer.Close finalize required archive metadata; ignoring those errors can leave a corrupt .tar.gz or .zip on disk while archivesReady is still closed. As per path instructions, **/*.go: “Never ignore error returns.”

Proposed fix
-func createArchive(pathToTargetFile string, format string) error {
+func createArchive(pathToTargetFile string, format string) (err error) {
 	targetFileInfo, err := os.Stat(pathToTargetFile)
 	if err != nil {
 		return err
 	}
 
 	targetFile, err := os.Open(pathToTargetFile)
 	if err != nil {
 		return err
 	}
-	defer targetFile.Close()
+	defer func() {
+		if closeErr := targetFile.Close(); closeErr != nil && err == nil {
+			err = fmt.Errorf("could not close target file %s: %w", pathToTargetFile, closeErr)
+		}
+	}()
@@
 	if format == ".tar.gz" {
 		compressWriter := gzip.NewWriter(file)
-		defer compressWriter.Close()
 		archiveWriter := tar.NewWriter(compressWriter)
-		defer archiveWriter.Close()
 
 		err = addFileToTar(archiveWriter, targetFile, targetFileInfo)
 		if err != nil {
 			return fmt.Errorf("could not create archive for %s: %w", pathToTargetFile, err)
 		}
+		if err := archiveWriter.Close(); err != nil {
+			return fmt.Errorf("could not finalize tar archive for %s: %w", pathToTargetFile, err)
+		}
+		if err := compressWriter.Close(); err != nil {
+			return fmt.Errorf("could not finalize gzip archive for %s: %w", pathToTargetFile, err)
+		}
 	} else if format == ".zip" {
 		archiveWriter := zip.NewWriter(file)
-		defer archiveWriter.Close()
 		err = addFileToZip(archiveWriter, targetFile, targetFileInfo)
 
 		if err != nil {
 			return fmt.Errorf("could not create archive for %s: %w", pathToTargetFile, err)
 		}
+		if err := archiveWriter.Close(); err != nil {
+			return fmt.Errorf("could not finalize zip archive for %s: %w", pathToTargetFile, err)
+		}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/config/downloads_config.go` around lines 224 - 247, Archive
writer close errors are being ignored in the download archive flow, which can
leave a bad .tar.gz or .zip marked as ready. Update the archive creation logic
in downloads_config.go around the tar.gz and zip branches in the
archive-building function to check and return errors from gzip.Writer.Close,
tar.Writer.Close, and zip.Writer.Close instead of deferring them blindly. Use
the existing archive writer setup and add explicit close handling so any
finalize failure prevents publishing a corrupt archive.

Sources: Path instructions, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/downloads/config/downloads_config_test.go`:
- Around line 159-163: The archive-reading setup in the test still ignores
errors from os.Open and gzip.NewReader and only closes the file handle, which
can hide corrupted archives and leak resources. Update the test around tarPath
to check and handle both open and gzip reader creation errors explicitly, and
ensure both f and gzw are closed after use. Use the existing tar.NewReader and
hdr, err flow to keep the reader setup clear while adding proper cleanup and
error handling.
- Line 218: The test requests in downloads_config_test use httptest.NewRequest,
which triggers noctx; update this request and the matching one below to use
httptest.NewRequestWithContext with context.Background(). Keep the change
localized to the request setup in the relevant test cases so the existing test
behavior stays the same.

In `@cmd/downloads/config/downloads_config.go`:
- Around line 406-411: The setupArtifactsDirectory function currently logs
symlink creation failures but still continues, which can leave
generateDirFileContents serving a broken LICENSE link. Update
setupArtifactsDirectory so that when os.Symlink fails for ocLicenseFile it
returns that error immediately instead of only calling klog.Errorf, and make
sure the caller path in DownloadsServerConfig respects the returned error and
aborts index generation when the symlink cannot be created.
- Around line 391-395: The download handler in downloads_config.go ignores the
error returned by fmt.Fprintln, which violates the no-ignored-errors rule.
Update the response path in the branch that checks r.Context().Done() to use
http.Error instead of manually calling WriteHeader plus Fprintln, and keep the
existing status code and message behavior in the handler.

In `@cmd/downloads/main.go`:
- Around line 4-11: The downloads server started in main should set a bounded
HTTP header read timeout because the current http.Server configuration leaves
header reads unbounded. Update the server construction in cmd/downloads/main.go
to include ReadHeaderTimeout with an appropriate value that matches normal
client behavior, keeping the rest of the server setup in main unchanged.
- Around line 27-28: The deprecated config-path flag is currently accepted but
ignored without any feedback, which can hide stale configuration. Update the
flag handling in main so that when fs.String("config-path", ...) receives a
non-empty value, the command emits a warning that the flag is deprecated and
ignored; keep the flag for backward compatibility, but ensure the warning is
tied to the parsed config-path value before proceeding.

---

Outside diff comments:
In `@cmd/downloads/config/downloads_config.go`:
- Around line 224-247: Archive writer close errors are being ignored in the
download archive flow, which can leave a bad .tar.gz or .zip marked as ready.
Update the archive creation logic in downloads_config.go around the tar.gz and
zip branches in the archive-building function to check and return errors from
gzip.Writer.Close, tar.Writer.Close, and zip.Writer.Close instead of deferring
them blindly. Use the existing archive writer setup and add explicit close
handling so any finalize failure prevents publishing a corrupt archive.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: fb72dbf9-ddab-4d2b-a9bb-5b79f17426cc

📥 Commits

Reviewing files that changed from the base of the PR and between 7d94137 and 89d3e6b.

📒 Files selected for processing (4)
  • Dockerfile.downloads
  • cmd/downloads/config/downloads_config.go
  • cmd/downloads/config/downloads_config_test.go
  • cmd/downloads/main.go

Comment thread cmd/downloads/config/downloads_config_test.go Outdated
done := make(chan int, 1)
go func() {
req := httptest.NewRequest(http.MethodGet, "/amd64/linux/oc.tar", nil)
req := httptest.NewRequest(http.MethodGet, "/amd64/linux/oc.tar.gz", nil)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

sed -n '180,390p' cmd/downloads/config/downloads_config_test.go

Repository: openshift/console

Length of output: 5813


🏁 Script executed:

rg -n "NewRequest\\(|NewRequestWithContext|noctx|httptest.NewRequest" cmd/downloads/config/downloads_config_test.go

Repository: openshift/console

Length of output: 374


🏁 Script executed:

sed -n '180,390p' cmd/downloads/config/downloads_config_test.go

Repository: openshift/console

Length of output: 5813


🏁 Script executed:

rg -n "NewRequest\\(|NewRequestWithContext|noctx|httptest.NewRequest" cmd/downloads/config/downloads_config_test.go

Repository: openshift/console

Length of output: 374


🌐 Web query:

golangci-lint noctx httptest.NewRequest NewRequestWithContext lint behavior

💡 Result:

The noctx linter in golangci-lint is designed to detect function and method calls that are missing a context.Context argument where one is expected [1][2]. Regarding httptest.NewRequest, the noctx linter explicitly flags it to encourage the use of httptest.NewRequestWithContext [3][4]. When noctx is enabled, using httptest.NewRequest will trigger a linting error with a message similar to: "must not be called. use net/http/httptest.NewRequestWithContext" [3][4]. This behavior was explicitly added to noctx in version 0.5.0 to improve context handling in tests [5]. To resolve this linting violation, you should update your code to use httptest.NewRequestWithContext whenever a context is available or required for the test case [3][6]. Top results: [1], [3], [5], [4]

Citations:


🌐 Web query:

golangci-lint noctx httptest.NewRequest NewRequestWithContext lint behavior

💡 Result:

golangci-lint’s noctx linter flags calls that create/send HTTP requests without a context.Context. In practice this means it will warn on net/http/httptest.NewRequest (and other request-creation APIs) because those use an underlying context.Background / no explicit context, while it will not warn when you use context-aware constructors like httptest.NewRequestWithContext (or http.NewRequestWithContext / Request.WithContext), since those explicitly provide a context.[1][2][3] Key behavior details 1) httptest.NewRequest (no explicit context) -> triggers noctx The net/http/httptest package documents that NewRequest “wraps NewRequestWithContext using context.Background,” i.e., there is an implicit context but it is not an explicit caller-supplied one.[3] Because noctx is meant to ensure you pass/propagate context, using this API is exactly the pattern it targets.[2][1] 2) httptest.NewRequestWithContext(ctx,...) (explicit context) -> should not trigger noctx Using the context-aware variant is how you satisfy noctx’s requirement to supply a context.Context to request creation.[3][2] 3) http.NewRequestWithContext(...) and Request.WithContext(...) The noctx project and its golangci-lint docs focus on detecting “function calls without context.Context” and list the net/http/constructors and Request.WithContext as relevant APIs.[2][1] Practical guidance / recommended pattern - In tests, prefer httptest.NewRequestWithContext(ctx, method, target, body) (or create a request with httptest.NewRequest and then call req.WithContext(ctx), depending on what your code is using). - If you need to use httptest.NewRequest for convenience, either accept the lint warning or explicitly document/suppress it (e.g., //nolint:noctx) since the linter intentionally enforces context propagation.[2][1] Note about the “context propagation over HTTP” concern If you create a request with a context, the Go context values are not automatically meaningful across network hops; HTTP protocols don’t carry Go context values directly. That’s separate from noctx’s static rule: noctx cares that a context is present on the request object, not that the context’s values are serialized over the wire.[4] If you share your exact snippet and golangci-lint version/config (especially whether you enable-all / default linters), I can tell you the exact expected diagnostic and the minimal code change to make it pass.

Citations:


Use httptest.NewRequestWithContext here and in the matching request below. httptest.NewRequest is flagged by noctx, so switch these test requests to httptest.NewRequestWithContext(context.Background(), ...) to avoid CI failures.

🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 218-218: net/http/httptest.NewRequest must not be called. use net/http/httptest.NewRequestWithContext

(noctx)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/config/downloads_config_test.go` at line 218, The test requests
in downloads_config_test use httptest.NewRequest, which triggers noctx; update
this request and the matching one below to use httptest.NewRequestWithContext
with context.Background(). Keep the change localized to the request setup in the
relevant test cases so the existing test behavior stays the same.

Source: Linters/SAST tools

Comment thread cmd/downloads/config/downloads_config.go Outdated
Comment on lines 406 to +411
func (artifactsConfig *DownloadsServerConfig) setupArtifactsDirectory() error {
// symlink file in the temporary directory that points to the openshift license
os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
err := os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
if err != nil {
klog.Errorf("can't create symlink to the LICENSE file in %s: %v", artifactsConfig.TempDir, err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Return the LICENSE symlink error instead of serving a broken license link.

generateDirFileContents always adds the license URL, so continuing after symlink failure leaves the generated index pointing at missing content.

Proposed fix
 	err := os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
 	if err != nil {
-		klog.Errorf("can't create symlink to the LICENSE file in %s: %v", artifactsConfig.TempDir, err)
+		return fmt.Errorf("can't create symlink to the LICENSE file in %s: %w", artifactsConfig.TempDir, err)
 	}
📝 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
func (artifactsConfig *DownloadsServerConfig) setupArtifactsDirectory() error {
// symlink file in the temporary directory that points to the openshift license
os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
err := os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
if err != nil {
klog.Errorf("can't create symlink to the LICENSE file in %s: %v", artifactsConfig.TempDir, err)
}
func (artifactsConfig *DownloadsServerConfig) setupArtifactsDirectory() error {
// symlink file in the temporary directory that points to the openshift license
err := os.Symlink(pathToOCLicense, filepath.Join(artifactsConfig.TempDir, ocLicenseFile))
if err != nil {
return fmt.Errorf("can't create symlink to the LICENSE file in %s: %w", artifactsConfig.TempDir, err)
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/config/downloads_config.go` around lines 406 - 411, The
setupArtifactsDirectory function currently logs symlink creation failures but
still continues, which can leave generateDirFileContents serving a broken
LICENSE link. Update setupArtifactsDirectory so that when os.Symlink fails for
ocLicenseFile it returns that error immediately instead of only calling
klog.Errorf, and make sure the caller path in DownloadsServerConfig respects the
returned error and aborts index generation when the symlink cannot be created.

Comment thread cmd/downloads/main.go
Comment on lines +4 to +11
_ "embed"
"flag"
"fmt"

"net/http"
"os"
"strconv"

"k8s.io/klog/v2"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files cmd/downloads/main.go
wc -l cmd/downloads/main.go
cat -n cmd/downloads/main.go

Repository: openshift/console

Length of output: 1830


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the downloads server implementation and similar HTTP servers for timeout patterns.
rg -n "ReadHeaderTimeout|ReadTimeout|WriteTimeout|IdleTimeout|http.Server{" cmd frontend pkg -g '!**/vendor/**' -g '!**/node_modules/**'

# Show the full downloads main file if it is short enough.
sed -n '1,220p' cmd/downloads/main.go

Repository: openshift/console

Length of output: 353


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the download server file and any related server timeout patterns.
sed -n '1,220p' cmd/downloads/main.go

printf '\n----\n'

rg -n "ReadHeaderTimeout|ReadTimeout|WriteTimeout|IdleTimeout|http.Server{" cmd pkg frontend -g '!**/vendor/**' -g '!**/node_modules/**'

Repository: openshift/console

Length of output: 1623


Add ReadHeaderTimeout to the downloads server. cmd/downloads/main.go starts an http.Server with no header-read timeout, so slow clients can hold connections open. Set a bounded timeout that matches expected client behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/main.go` around lines 4 - 11, The downloads server started in
main should set a bounded HTTP header read timeout because the current
http.Server configuration leaves header reads unbounded. Update the server
construction in cmd/downloads/main.go to include ReadHeaderTimeout with an
appropriate value that matches normal client behavior, keeping the rest of the
server setup in main unchanged.

Source: Path instructions

Comment thread cmd/downloads/main.go
Comment on lines +27 to +28
// leave it here for backward compatible
_ = fs.String("config-path", "", "this flag is deprecated and ignored")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Warn when the deprecated config flag is supplied.

Keeping --config-path for compatibility is fine, but silently ignoring a non-empty value can hide stale deployment configuration.

Proposed fix
-	_ = fs.String("config-path", "", "this flag is deprecated and ignored")
+	configPath := fs.String("config-path", "", "this flag is deprecated and ignored")
@@
 	if err := fs.Parse(os.Args[1:]); err != nil {
 		klog.Fatalf("Failed to parse flags: %v", err)
 		os.Exit(1)
 	}
+	if *configPath != "" {
+		klog.Warning("--config-path is deprecated and ignored; using embedded downloads config")
+	}
📝 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
// leave it here for backward compatible
_ = fs.String("config-path", "", "this flag is deprecated and ignored")
// leave it here for backward compatible
configPath := fs.String("config-path", "", "this flag is deprecated and ignored")
// ... later, after fs.Parse(...)
if *configPath != "" {
klog.Warning("--config-path is deprecated and ignored; using embedded downloads config")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/main.go` around lines 27 - 28, The deprecated config-path flag
is currently accepted but ignored without any feedback, which can hide stale
configuration. Update the flag handling in main so that when
fs.String("config-path", ...) receives a non-empty value, the command emits a
warning that the flag is deprecated and ignored; keep the flag for backward
compatibility, but ensure the warning is tied to the parsed config-path value
before proceeding.

The download server creates zip and tar archives on boot. However, when
downloading these archives, their size is the same as the original
non-compressed file.

This PR fixes this isssue. It also changes the tar file extension to
"tar.gz".

Few more improvements:

embed the configuration file, instead of handling
the file: copy it to the image and write code around open and close it
with error handling. embed the file makes it all much more simple.

deprecate the config-path as it is no longer required.

move the port parameter out of the download config as it's not belong
there.

don't create archives for broken symlinks, to avoid let the user download them.

Avoid keeping the handler waiting forever, creating handler leaks.

Signed-off-by: Nahshon Unna Tsameret <nunnatsa@redhat.com>
@nunnatsa nunnatsa force-pushed the downloads-fix-compression branch from 89d3e6b to 943d3e3 Compare June 24, 2026 11:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
cmd/downloads/config/downloads_config.go (1)

220-248: 🗄️ Data Integrity & Integration | 🟠 Major

Handle archive finalization errors. defer-ing Close() on the gzip/tar/zip writers drops flush/finalization errors, so createArchive can return success while producing a corrupt .tar.gz or .zip.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/config/downloads_config.go` around lines 220 - 248, The
createArchive flow in downloads_config.go is ignoring finalization errors from
the gzip, tar, and zip writers because their Close calls are deferred without
checking the returned error. Update createArchive (and the
addFileToTar/addFileToZip paths it uses) so writer Close/flush errors are
captured and returned, rather than allowing a successful return with a corrupt
archive. Make sure the archive writer and its wrapping gzip writer are finalized
explicitly and any error from those Close calls is propagated.

Sources: Path instructions, Linters/SAST tools

♻️ Duplicate comments (1)
cmd/downloads/config/downloads_config_test.go (1)

185-191: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle close errors for both readers in checkTarFile.

Line 185 and Line 191 ignore Close() errors, which can hide cleanup/stream issues and fail errcheck.

Suggested fix
-	defer f.Close()
+	defer func() {
+		if cerr := f.Close(); cerr != nil {
+			t.Errorf("failed to close archive %s: %v", tarPath, cerr)
+		}
+	}()
@@
-	defer gzw.Close()
+	defer func() {
+		if cerr := gzw.Close(); cerr != nil {
+			t.Errorf("failed to close gzip reader for %s: %v", tarPath, cerr)
+		}
+	}()

As per path instructions, **/*.go: “Never ignore error returns”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/downloads/config/downloads_config_test.go` around lines 185 - 191, In
checkTarFile, the deferred Close calls for both the file handle and the gzip
reader are ignoring returned errors, which can hide cleanup failures and violate
the never-ignore-errors rule. Update the deferred cleanup around f.Close and
gzw.Close to capture and report any close errors, using the existing
checkTarFile test flow so failures are surfaced in the same test context.

Sources: Path instructions, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@cmd/downloads/config/downloads_config.go`:
- Around line 220-248: The createArchive flow in downloads_config.go is ignoring
finalization errors from the gzip, tar, and zip writers because their Close
calls are deferred without checking the returned error. Update createArchive
(and the addFileToTar/addFileToZip paths it uses) so writer Close/flush errors
are captured and returned, rather than allowing a successful return with a
corrupt archive. Make sure the archive writer and its wrapping gzip writer are
finalized explicitly and any error from those Close calls is propagated.

---

Duplicate comments:
In `@cmd/downloads/config/downloads_config_test.go`:
- Around line 185-191: In checkTarFile, the deferred Close calls for both the
file handle and the gzip reader are ignoring returned errors, which can hide
cleanup failures and violate the never-ignore-errors rule. Update the deferred
cleanup around f.Close and gzw.Close to capture and report any close errors,
using the existing checkTarFile test flow so failures are surfaced in the same
test context.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 878be91e-128f-4bb5-a509-89d15564624d

📥 Commits

Reviewing files that changed from the base of the PR and between 89d3e6b and 943d3e3.

📒 Files selected for processing (4)
  • Dockerfile.downloads
  • cmd/downloads/config/downloads_config.go
  • cmd/downloads/config/downloads_config_test.go
  • cmd/downloads/main.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/downloads/main.go
  • Dockerfile.downloads

@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

@nunnatsa: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/images 943d3e3 link true /test images
ci/prow/e2e-gcp-console 943d3e3 link true /test e2e-gcp-console
ci/prow/e2e-playwright 943d3e3 link false /test e2e-playwright

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

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

Labels

component/backend Related to backend jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants