Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Caching the same provider with different versions from various sources #3935

Merged
merged 4 commits into from
Feb 27, 2025

Conversation

levkohimins
Copy link
Contributor

@levkohimins levkohimins commented Feb 26, 2025

Description

Fixes #3732.

TODOs

Read the Gruntwork contribution guidelines.

  • Update the docs.
  • Run the relevant tests successfully, including pre-commit checks.
  • Ensure any 3rd party code adheres with our license policy or delete this line if its not applicable.
  • Include release notes. If this PR is backward incompatible, include a migration guide.

Release Notes (draft)

Added / Removed / Updated [X].

Migration Guide

Summary by CodeRabbit

  • New Features

    • Enhanced provider caching and proxy retrieval capabilities improve performance and reliability of provider operations.
    • Introduced new Terraform configuration for managing AWS resources.
    • Added support for filesystem and network mirror providers.
  • Refactor

    • Consolidated provider management and streamlined error handling deliver a more consistent and responsive experience.
    • Improved cloning functionality for provider installation methods, enabling deeper copies of configurations.
  • Chore

    • Updated logging formats and refined configuration options (including HTTP status management) for clearer diagnostics and improved maintainability.

Copy link

vercel bot commented Feb 26, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
terragrunt-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 26, 2025 5:23pm

Copy link
Contributor

coderabbitai bot commented Feb 26, 2025

Walkthrough

The changes refactor the provider cache server initialization and request handling. Provider handlers are now initialized using a consolidated function that streamlines the logic for direct, proxy, filesystem, and network mirror installations. The codebase has been updated with new error types and improved logging and configuration, while obsolete mirror handler implementations have been removed. Controllers and server initialization now use explicit ProviderService and ProxyProviderHandler dependencies. Additional enhancements include a new HTTP client with caching and refined data models for provider versioning and platform details.

Changes

File(s) Change Summary
cli/provider_cache.go, cli/provider_cache_test.go Refactored InitProviderCacheServer to call NewProviderHandlers; updated proxy provider initialization and test options (logger, HTTP status code) for cleaner control flow.
pkg/log/format/format.go, pkg/log/format/placeholders/field.go Added new log fields and constants (CacheServerURLKeyName, CacheServerStatusKeyName) for standardized log output.
tf/cache/config.go Replaced WithServices with WithProviderService; added WithProxyProviderHandler and WithCacheProviderHTTPStatusCode; updated the Config struct accordingly.
tf/cache/controllers/downloader.go, tf/cache/controllers/provider.go Modified controllers to remove generic ProviderHandlers in favor of explicit ProviderService and ProxyProviderHandler; added Logger and HTTP status code in ProviderController.
tf/cache/handlers/common_provider.go, direct_provider.go, errors.go, filesystem_mirror_provider.go, network_mirror_provider.go, provider.go, proxy_provider.go, registry_urls.go, provider_filesystem_mirror.go, provider_network_mirror.go Overhauled provider handler implementations: introduced new handlers (Common, Direct, Proxy, Filesystem Mirror, Network Mirror) with updated interfaces and error handling; removed obsolete mirror handlers.
tf/cache/helpers/client.go, http.go, reverse_proxy.go Introduced a new HTTP client with caching; updated response decoding and error wrapping; changed package name and updated ReverseProxy field visibility.
tf/cache/middleware/logger.go Updated middleware logging to use placeholder keys instead of hardcoded strings and simplified the function signature.
tf/cache/models/provider.go Enhanced provider models by adding Versions and Platforms types and new String methods for formatted output.
tf/cache/server.go Revised server initialization to integrate updated ProviderService and ProxyProviderHandler into controllers; adjusted service slice setup.
tf/cache/services/provider_cache.go Added documentation comments to clarify the package’s role in running background services.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant CacheServer as ProviderCacheServer
    participant NewHandlers as NewProviderHandlers
    participant ProxyHandler as ProxyProviderHandler
    participant ProviderService
    participant ProviderController

    Client->>CacheServer: Request cache server initialization
    CacheServer->>NewHandlers: Call NewProviderHandlers(cliCfg, logger, registryNames)
    NewHandlers-->>CacheServer: Return list of Provider Handlers
    CacheServer->>ProxyHandler: Instantiate ProxyProviderHandler
    CacheServer->>ProviderService: Set up ProviderService
    CacheServer->>ProviderController: Configure with ProxyHandler & ProviderService
    Client->>ProviderController: Request provider data (versions/platform)
    ProviderController->>ProxyHandler: Proxy request for data
    ProxyHandler-->>ProviderController: Return modified response
    ProviderController-->>Client: Respond with provider information
Loading

Suggested reviewers

  • denis256

Poem

In lines of code where logic flows,
New handlers rise as clarity shows.
From cache to logs, improvements sung,
A cleaner structure for old and young.
Cheers to progress—smooth ride ahead! 🚀

Tip

CodeRabbit's docstrings feature is now available as part of our Pro Plan! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request. We would love to hear your feedback on Discord.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@levkohimins levkohimins changed the title fix: Caching the same provider but with different versions from various sources. fix: Caching the same provider but with different versions from various sources Feb 26, 2025
Copy link
Contributor

@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: 4

🧹 Nitpick comments (13)
tf/cache/helpers/client.go (2)

15-21: Nice use of concurrency-safe map for caching responses.

Storing bytes in memory for all requests can grow large over time, though. You might want to implement an LRU or TTL to free up cache space and avoid hogging memory for an extended period.


23-29: Consider specifying timeouts on the underlying http.Client.

Right now, the default http.Client can hang forever if the remote side never responds. Setting some sensible timeouts (e.g., Timeout, Transport.IdleConnTimeout) can prevent resource leaks.

tf/cache/config.go (2)

61-66: Correct the “Porxy” spelling in the function name.

Seems like a simple slip of the keyboard. Let’s fix it to “Proxy” to avoid confusion.

-func WithPorxyProviderHandler(handler *handlers.ProxyProviderHandler) Option {
+func WithProxyProviderHandler(handler *handlers.ProxyProviderHandler) Option {

68-73: Double-check default usage of cacheProviderHTTPStatusCode.

If this value defaults to 0 or is unset, you may return 0 as the status code. If that’s not intended, consider a safe default (like 423) to ensure a predictable workflow.

tf/cache/handlers/network_mirror_provider.go (2)

29-33: Validate error messages for clarity
Love that you're returning early if URL parsing fails. But it might be worth clarifying the error message to make sure it’s obvious what failed, for smoother debugging experiences.

-if err != nil {
-  return nil, errors.New(err)
+if err != nil {
+  return nil, errors.WithMessage(err, "failed to parse network mirror URL in NewNetworkMirrorProviderHandler")
 }

72-99: Unused parameter: 'downloaderController'
downloaderController isn’t referenced in GetPlatform. If not intended for future use, removing it might simplify the method signature and reduce confusion.

-func (handler *NetworkMirrorProviderHandler) GetPlatform(ctx context.Context, provider *models.Provider, downloaderController router.Controller) (*models.ResponseBody, error) {
+func (handler *NetworkMirrorProviderHandler) GetPlatform(ctx context.Context, provider *models.Provider) (*models.ResponseBody, error) {
tf/cache/handlers/direct_provider.go (1)

62-84: Provide clearer feedback if the platform is missing
If the requested platform doesn't exist, you return nil without an explicit error. Some folks might appreciate a friendlier error message about the missing platform or an empty response for debugging.

tf/cache/handlers/filesystem_mirror_provider.go (2)

78-81: Check for absolute paths on Windows
String-based checks like "://" versus local paths can get tricky on Windows with backslashes. You might want to systematically use filepath.ToSlash or similar to avoid confusion.


76-86: Return an error if the platform is missing
If archive doesn't exist for the requested platform, the method quietly returns nil. If that's not by design, consider an error or fallback strategy—especially if your usage relies on a guaranteed platform result.

cli/provider_cache.go (2)

101-103: Handle errors more gracefully.
When NewProviderHandlers returns an error, consider wrapping it to add more context, e.g. "creating provider handlers failed". This can help debug issues in real environments.

-   return nil, err
+   return nil, fmt.Errorf("creating provider handlers failed: %w", err)

110-110: Rename to match the rest of the codebase.
WithProviderService is consistent, but double-check that all references align with the naming convention used across your code (like AddProviderService or SetProviderService if that’s standard).

tf/cache/handlers/provider.go (1)

34-70: Check naming and logging.
The function NewProviderHandlers is well structured, but consider:

  • Renaming directIsdefined to directIsDefined for clarity.
  • Logging or commenting when a direct method is automatically added, so users don’t wonder why it’s suddenly there.
- directIsdefined bool
+ directIsDefined bool

...
if !directIsDefined {
    logger.Infof("No direct provider installation method defined; adding a default direct method...")
    providerHandlers = append(...)
}
tf/cache/handlers/proxy_provider.go (1)

38-47: Validate logger and credentials.
In NewProxyProviderHandler, it might help to verify logger and creds are non-nil or default them if they are. This keeps the handler from blowing up in corner cases.

func NewProxyProviderHandler(logger log.Logger, credsSource *cliconfig.CredentialsSource) *ProxyProviderHandler {
+   if logger == nil {
+       // fallback or error out
+   }
+   if credsSource == nil {
+       // fallback or error out
+   }

    return &ProxyProviderHandler{
        ...
    }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 27dd83b and 64c13d0.

📒 Files selected for processing (24)
  • cli/provider_cache.go (1 hunks)
  • cli/provider_cache_test.go (2 hunks)
  • pkg/log/format/format.go (1 hunks)
  • pkg/log/format/placeholders/field.go (1 hunks)
  • tf/cache/config.go (3 hunks)
  • tf/cache/controllers/downloader.go (3 hunks)
  • tf/cache/controllers/provider.go (4 hunks)
  • tf/cache/handlers/common_provider.go (1 hunks)
  • tf/cache/handlers/direct_provider.go (1 hunks)
  • tf/cache/handlers/errors.go (1 hunks)
  • tf/cache/handlers/filesystem_mirror_provider.go (1 hunks)
  • tf/cache/handlers/network_mirror_provider.go (1 hunks)
  • tf/cache/handlers/provider.go (3 hunks)
  • tf/cache/handlers/provider_filesystem_mirror.go (0 hunks)
  • tf/cache/handlers/provider_network_mirror.go (0 hunks)
  • tf/cache/handlers/proxy_provider.go (6 hunks)
  • tf/cache/handlers/registry_urls.go (4 hunks)
  • tf/cache/helpers/client.go (1 hunks)
  • tf/cache/helpers/http.go (4 hunks)
  • tf/cache/helpers/reverse_proxy.go (3 hunks)
  • tf/cache/middleware/logger.go (2 hunks)
  • tf/cache/models/provider.go (2 hunks)
  • tf/cache/server.go (2 hunks)
  • tf/cache/services/provider_cache.go (1 hunks)
💤 Files with no reviewable changes (2)
  • tf/cache/handlers/provider_network_mirror.go
  • tf/cache/handlers/provider_filesystem_mirror.go
✅ Files skipped from review due to trivial changes (1)
  • tf/cache/services/provider_cache.go
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Pull Request has non-contributor approval
🔇 Additional comments (44)
tf/cache/handlers/errors.go (1)

3-9: Well-structured error type for URL not found scenarios

Nice addition of a custom error type for handling not-found URLs. The structure is clean and follows Go's idiomatic error handling patterns. Having a specific error type will make error handling more precise throughout the codebase.

pkg/log/format/placeholders/field.go (1)

13-16: Good addition of cache server log constants

These new constants for cache server URL and status are well-documented and follow the existing naming convention. They'll make the logs more consistent and readable when dealing with provider caching operations.

pkg/log/format/format.go (1)

69-74: Log format enhanced with cache server fields

The addition of cache server URL and status fields to the log format is clean and consistent with the existing pattern. This will improve observability when debugging provider caching issues.

tf/cache/helpers/reverse_proxy.go (2)

22-22: Field visibility change improves modularity

Good call changing logger to Logger. Making this field exported allows for better integration with other packages that need to access or modify the logger, which aligns with the PR's goal of improving provider caching from various sources.


59-59: Updated to use exported Logger field

This line properly updates the reference to use the newly exported Logger field. Ensures logging functionality remains intact after the visibility change.

tf/cache/controllers/downloader.go (4)

10-10: Good addition of the services package import

This import is needed for the new ProviderService field that's being added to the DownloaderController struct. Makes sense given the refactoring to use a dedicated service.


21-22: Nice refactoring of the controller dependencies

Replacing the ProviderHandlers slice with specific ProviderService and ProxyProviderHandler fields gives clearer intentions and better separation of concerns. This approach makes the code more maintainable by explicitly declaring dependencies rather than working with a generic slice of handlers.


50-54: Clean caching implementation

The new implementation is more straightforward and easier to follow. Instead of iterating through multiple handlers, you're directly using the ProviderService to check for cached providers. The log message also provides helpful debugging information.


57-59: Simplified download logic

Using a dedicated ProxyProviderHandler for downloads rather than iterating through a slice of handlers makes the code more direct and easier to understand. This change aligns well with the PR objective of "Caching the same provider but with different versions from various sources."

tf/cache/helpers/http.go (4)

95-95: Consistent error handling - good change

Wrapping the error with errors.New(err) provides more consistent error handling throughout the codebase. This helps maintain a uniform error format that will make debugging easier.


113-113: Consistent error handling - good change

Just like the previous change, wrapping the error with errors.New(err) improves error consistency.


133-133: Consistent error handling - good change

Another good instance of using errors.New(err) for consistent error handling.


146-146: Consistent error handling - good change

Final instance of improving error handling consistency by using errors.New(err).

tf/cache/middleware/logger.go (2)

5-5: Good use of placeholders package

Adding this import allows the use of predefined key constants for logging, which is more maintainable than hardcoded strings.


16-22: Improved logging with placeholders

Nice refactoring here! Using the placeholder constants instead of hardcoded strings makes the code more maintainable. I also like that you explicitly marked the unused context parameter with an underscore. The structured logging approach with field names will make debugging and log analysis much easier.

tf/cache/models/provider.go (6)

55-55: Good addition of the Versions type

Creating a clear type definition for Versions improves code readability and makes it easier to add methods to this collection in the future if needed.


58-61: Clean refactoring of the Version struct

Using the new Platforms type for the Platforms field makes the code more consistent and clearer to understand.


63-65: Useful String() method addition

Adding a String() method to the Version struct makes debugging easier and improves logging output. The format including version, protocols, and platforms makes it easy to identify specific versions.


67-67: Good addition of the Platforms type

Similar to the Versions type, creating a dedicated type for Platforms improves code clarity and makes it easier to add methods to this collection in the future.


74-76: Useful String() method for Platform

Adding a String() method to the Platform struct improves logging and debugging capabilities. The format with OS and architecture is clear and concise.


143-147: Better String() implementation for Provider

Good improvement to conditionally include the version information only when it's available. This makes the string representation more accurate for incomplete Provider objects.

tf/cache/server.go (3)

33-36: Great improvement on explicitly injecting dependencies to DownloaderController

This change provides better clarity and maintainability by making the dependencies explicit rather than relying on the previous opaque ProviderHandlers field. This will make it easier to understand and track how the components interact.


38-46: Nice refactoring of ProviderController with explicit dependencies

The controller now has clear and explicit dependencies which improves readability and maintainability. I particularly like how you've:

  1. Added dedicated fields for different handler types
  2. Included the logger directly instead of retrieving it elsewhere
  3. Made the HTTP status code configurable

This approach makes the dependencies much clearer and follows good dependency injection practices.


63-63: Simplified services initialization

Using a slice with just the provider service makes the code more direct and less error-prone than using the previous cfg.services approach, which was likely a more general collection of services.

cli/provider_cache_test.go (1)

51-51: Good addition of HTTP status code option

This reflects the changes in the main code and ensures the tests appropriately verify this functionality.

tf/cache/handlers/registry_urls.go (4)

27-34: Good addition of common connection errors for offline detection

This comprehensive list of common connection errors will help the system properly identify offline situations, leading to better graceful degradation when connectivity is limited.


41-47: Improved String() method with error handling

The updated implementation properly handles JSON marshaling errors and provides a fallback representation when marshaling fails. This makes the code more robust.


65-65: Updated error type for better error handling

Using the structured NotFoundWellKnownURLError type instead of a string error provides better context and enables more sophisticated error handling downstream.


84-97: Well-implemented offline error detection function

This function nicely consolidates the logic for determining if an error relates to offline status. The dual approach of checking both by direct error comparison and string matching provides robust detection.

tf/cache/handlers/common_provider.go (4)

12-23: Good introduction of CommonProviderHandler struct

The struct is well-designed with clear documentation. I particularly like the use of xsync.MapOf instead of sync.Map for better performance and type safety.


25-43: Clear and robust constructor implementation

The constructor properly handles optional inputs (nil includes/excludes) and initializes all necessary fields. The use of models.ParseProviders to handle pattern conversion is clean.


45-55: Well-structured provider handling logic

The logic flow is clear and follows a sensible order:

  1. First check exclusions (which take priority)
  2. Then check inclusions if any exist
  3. Default to accepting all providers if no inclusions are specified

This approach makes the handler's behavior predictable and easy to understand.


57-78: Excellent caching implementation for registry URLs

The DiscoveryURL method effectively:

  1. Checks the cache first to avoid redundant network calls
  2. Handles offline errors gracefully by falling back to default URLs
  3. Provides helpful debug logging
  4. Updates the cache for future requests

This will significantly improve performance and resilience when handling provider requests, especially in environments with limited connectivity.

tf/cache/helpers/client.go (2)

31-63: Clarify behavior for non-200 responses.

Currently, decodeResponse returns (nil, nil) if the status isn’t 200, and the code caches that nil result. That means callers won’t see an error: they’ll just see an empty struct. Make sure that’s intended or handle non-200 codes more explicitly, so the consumer knows the request failed.


65-75: JSON Unmarshal flow looks great.

The function is straightforward and uses standard JSON decoding. It's a nice clean design.

tf/cache/controllers/provider.go (2)

74-98: Return a specific status when no versions are found.

This endpoint always responds with 200, even if no versions exist or handlers fail. You might consider returning 404 or a similar code if zero versions were found, so clients know it didn’t succeed in the usual sense.


121-148: Validate “nil” response scenario.

If all handlers fail, resp remains nil but we still proceed to cache. Depending on your logic, you might need to propagate an error or at least signal that no valid platform data was returned.

tf/cache/handlers/network_mirror_provider.go (1)

47-69: Consider how the 'availablePlatforms' property is populated
You're appending a models.Version using availablePlatforms, but the snippet doesn't show where availablePlatforms is declared or set. If it’s an uninitialized placeholder, you risk unexpected null references or empty data.

tf/cache/handlers/direct_provider.go (2)

24-29: Constructor looks good
This straightforward constructor approach is clear and neat. Great job.


39-60: Double-check partial or empty responses
When calling Do to retrieve versions, consider how you handle partial or empty JSON responses. If the returned versions.Versions is nil or empty, you might want to log a warning or handle it gracefully.

tf/cache/handlers/filesystem_mirror_provider.go (1)

94-95: Confirm desired behavior on missing files
readMirrorData returns nil when the file doesn’t exist. Make sure that silently ignoring a non-existent JSON is intentional. If the data is essential, returning an error might be more direct.

cli/provider_cache.go (1)

99-99: Great introduction of the proxy provider handler.
This new instantiation looks neat. Just ensure you’ve validated in at least one test that the proxy handler is wired correctly.
[approve]

tf/cache/handlers/proxy_provider.go (2)

88-88: Neat approach for response modification.
Wrapping the reverse proxy response in a function that tweaks JSON is a clever solution. Great job making it modular.


120-120: Double-check path joining.
In modifyDownloadURLsInJSONBody, combining the host and path can lead to double slashes if either ends in “/”. Consider normalizing or trimming slashes before path.Join to avoid weird behavior.

Also applies to: 142-142

@@ -1,4 +1,4 @@
package handlers
package helpers
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Package name change from "handlers" to "helpers"

The package name change seems appropriate given the utility nature of this code. Just make sure all imports throughout the codebase have been updated to reflect this change.


🏁 Script executed:

#!/bin/bash
# Check for any references to the old package path that might have been missed
rg "\"github.com/gruntwork-io/terragrunt/tf/cache/handlers\"" --type go

Length of output: 653


Attention: Update Remaining Import Paths for Package Renaming

The change in tf/cache/helpers/reverse_proxy.go from package handlers to package helpers looks good for its utility purpose. However, our search revealed that several files still reference the old package path "github.com/gruntwork-io/terragrunt/tf/cache/handlers". Please update the import paths in the following files to reflect the new package location:

  • cli/provider_cache_test.go
  • cli/provider_cache.go
  • tf/cache/server.go
  • tf/cache/handlers/provider_test.go
  • tf/cache/config.go
  • tf/cache/controllers/provider.go
  • tf/cache/controllers/downloader.go

Once these changes are made, the entire codebase will be consistent with the new helpers package naming. Thanks for handling this!

Comment on lines 115 to 125
logger := log.New()

providerService := services.NewProviderService(providerCacheDir, pluginCacheDir, nil, log.New())
providerHandler := handlers.NewProviderDirectHandler(providerService, cli.CacheProviderHTTPStatusCode, new(cliconfig.ProviderInstallationDirect), nil)
providerService := services.NewProviderService(providerCacheDir, pluginCacheDir, nil, logger)
providerHandler := handlers.NewDirectProviderHandler(logger, new(cliconfig.ProviderInstallationDirect), nil)
proxyProviderHandler := handlers.NewProxyProviderHandler(logger, nil)

testCase.opts = append(testCase.opts, cache.WithServices(providerService), cache.WithProviderHandlers(providerHandler))
testCase.opts = append(testCase.opts,
cache.WithProviderService(providerService),
cache.WithProviderHandlers(providerHandler),
cache.WithPorxyProviderHandler(proxyProviderHandler),
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Test setup refactored to match new initialization pattern, but watch for the typo

The test setup now properly aligns with the changes made in the main codebase, but there appears to be a typo in one of the method names.

 testCase.opts = append(testCase.opts,
   cache.WithProviderService(providerService),
   cache.WithProviderHandlers(providerHandler),
-  cache.WithPorxyProviderHandler(proxyProviderHandler),
+  cache.WithProxyProviderHandler(proxyProviderHandler),
 )

The method name has "Porxy" instead of "Proxy" which could cause errors.

📝 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
logger := log.New()
providerService := services.NewProviderService(providerCacheDir, pluginCacheDir, nil, log.New())
providerHandler := handlers.NewProviderDirectHandler(providerService, cli.CacheProviderHTTPStatusCode, new(cliconfig.ProviderInstallationDirect), nil)
providerService := services.NewProviderService(providerCacheDir, pluginCacheDir, nil, logger)
providerHandler := handlers.NewDirectProviderHandler(logger, new(cliconfig.ProviderInstallationDirect), nil)
proxyProviderHandler := handlers.NewProxyProviderHandler(logger, nil)
testCase.opts = append(testCase.opts, cache.WithServices(providerService), cache.WithProviderHandlers(providerHandler))
testCase.opts = append(testCase.opts,
cache.WithProviderService(providerService),
cache.WithProviderHandlers(providerHandler),
cache.WithPorxyProviderHandler(proxyProviderHandler),
)
logger := log.New()
providerService := services.NewProviderService(providerCacheDir, pluginCacheDir, nil, logger)
providerHandler := handlers.NewDirectProviderHandler(logger, new(cliconfig.ProviderInstallationDirect), nil)
proxyProviderHandler := handlers.NewProxyProviderHandler(logger, nil)
testCase.opts = append(testCase.opts,
cache.WithProviderService(providerService),
cache.WithProviderHandlers(providerHandler),
cache.WithProxyProviderHandler(proxyProviderHandler),
)

Comment on lines +77 to +95
func decodeResponse(resp *http.Response) ([]byte, error) {
if resp.StatusCode != http.StatusOK {
return nil, nil
}

buffer, err := ResponseBuffer(resp)
if err != nil {
return nil, err
}

bodyBytes, err := io.ReadAll(buffer)
if err != nil {
return nil, errors.New(err)
}

resp.Body = io.NopCloser(buffer)

return bodyBytes, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Handle non-200 status codes with an explicit error.

Returning (nil, nil) makes the caller assume success. Consider returning an error if resp.StatusCode != http.StatusOK so you don’t silently swallow server failures.

Comment on lines 112 to 113
cache.WithPorxyProviderHandler(proxyProviderHandler),
cache.WithCacheProviderHTTPStatusCode(CacheProviderHTTPStatusCode),
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix what looks like a spelling mistake.
cache.WithPorxyProviderHandler is probably missing an ‘r’ in “Proxy.” This would cause a compilation error.

- cache.WithPorxyProviderHandler(proxyProviderHandler),
+ cache.WithProxyProviderHandler(proxyProviderHandler),
📝 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
cache.WithPorxyProviderHandler(proxyProviderHandler),
cache.WithCacheProviderHTTPStatusCode(CacheProviderHTTPStatusCode),
cache.WithProxyProviderHandler(proxyProviderHandler),
cache.WithCacheProviderHTTPStatusCode(CacheProviderHTTPStatusCode),

Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
tf/cache/handlers/network_mirror_provider.go (2)

45-68: The GetVersions implementation could use a small improvement.

The method correctly fetches and processes versions from the network mirror, but it uses a global availablePlatforms variable (line 63) that isn't defined in this file.

Consider either:

  1. Creating a local platforms list based on the actual available platforms in the mirror data
  2. Adding a comment explaining where the global variable is defined

70-98: GetPlatform implementation looks solid, with a minor improvement opportunity.

The implementation correctly fetches platform-specific data and constructs the response, but it might benefit from adding a debug log when a platform isn't found in the mirror data.

Consider adding something like:

 if archive, ok := mirrorData.Archives[provider.Platform()]; ok {
     resp = (&models.ResponseBody{
         Filename:    filepath.Base(archive.URL),
         DownloadURL: archive.URL,
     }).ResolveRelativeReferences(handler.networkMirrorURL.ResolveReference(&url.URL{
         Path: path.Join(handler.networkMirrorURL.Path, provider.Address()),
     }))
+} else {
+    handler.logger.Debugf("Platform %q not found in network mirror for provider %s", provider.Platform(), provider.Address())
 }
tf/cache/handlers/filesystem_mirror_provider.go (1)

36-57: Potential improvement for platform availability

The GetVersions method currently assumes all versions support all available platforms by setting availablePlatforms for every version. This might not be accurate if some versions only support a subset of platforms.

Consider refining this to check which platforms are actually available for each version, perhaps by examining the directory structure or the version-specific JSON files.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f2d735 and 5abcf05.

📒 Files selected for processing (8)
  • cli/provider_cache.go (1 hunks)
  • cli/provider_cache_test.go (2 hunks)
  • tf/cache/config.go (3 hunks)
  • tf/cache/controllers/provider.go (4 hunks)
  • tf/cache/handlers/direct_provider.go (1 hunks)
  • tf/cache/handlers/filesystem_mirror_provider.go (1 hunks)
  • tf/cache/handlers/network_mirror_provider.go (1 hunks)
  • tf/cache/handlers/provider.go (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: unessential
  • GitHub Check: build-and-test
  • GitHub Check: Pull Request has non-contributor approval
🔇 Additional comments (19)
cli/provider_cache_test.go (2)

51-51: Good addition of the status code configuration.

The addition of cache.WithCacheProviderHTTPStatusCode aligns well with the changes in the controller implementation which now uses this status code when returning cache provider responses.


115-125: The test setup now matches the new architecture. Nice work.

The initialization pattern has been updated to follow the new architecture:

  1. Creating a dedicated logger instance
  2. Properly initializing the provider service with that logger
  3. Using the new direct provider handler constructor
  4. Adding the new proxy provider handler

This setup ensures the test properly exercises the refactored code, which should help catch any provider caching issues with different versions from various sources.

tf/cache/config.go (3)

47-52: Clear interface evolution from services array to specific service types.

The refactoring from WithServices to WithProviderService makes the API more explicit and type-safe by accepting a specific provider service instead of a generic slice of services.


61-73: Good additions to support the provider caching enhancement.

These new configuration options:

  1. WithProxyProviderHandler - Allows configuring a proxy provider handler
  2. WithCacheProviderHTTPStatusCode - Enables customizing the status code

These options directly support the PR's goal of better handling providers with different versions from various sources.


88-92: Config struct updated to match the new architectural approach.

The structure now properly reflects the more explicit architecture with dedicated fields for the provider service, handlers collection, proxy handler, and status code configuration.

tf/cache/controllers/provider.go (3)

25-34: Controller structure updated with explicit dependencies.

The controller now has explicit dependencies for everything it needs:

  • Logger for better diagnostics
  • Provider handlers collection for gathering data
  • Dedicated proxy handler for redirection
  • HTTP status code for caching responses

This makes the controller's responsibilities and dependencies much clearer.


74-97: Improved version collection with better error handling.

The new implementation properly:

  1. Collects versions from all available handlers
  2. Logs errors without failing completely
  3. Returns a well-structured JSON response

This is more resilient than before and will better handle providers with multiple versions from different sources.


120-147: Enhanced platform handling with proxy support.

The updated code now:

  1. Delegates to the proxy handler when not in caching mode
  2. Properly logs errors during platform retrieval
  3. Explicitly starts caching and returns the configured status code

This implementation better supports the PR goal of handling providers from various sources.

tf/cache/handlers/network_mirror_provider.go (1)

19-43: Good implementation of the network mirror handler.

The handler structure follows the established pattern and properly implements the ProviderHandler interface. The constructor properly parses and validates the network mirror URL.

tf/cache/handlers/direct_provider.go (4)

15-21: Clean interface implementation with clear structure

The DirectProviderHandler structure is well-designed with embedded CommonProviderHandler and a client field. Using the var _ ProviderHandler = new(DirectProviderHandler) pattern is a great way to ensure compile-time interface compliance.


23-28: Good constructor pattern

The constructor function follows good practices by accepting dependencies and initializing all fields. I particularly like how it reuses the CommonProviderHandler for shared functionality.


34-59: Well-implemented GetVersions method with good error handling

The implementation correctly:

  1. Fetches discovery URLs
  2. Constructs the request URL
  3. Makes the request and unmarshals the response
  4. Returns appropriate errors when things go wrong

Nice work on the detailed comment with the API documentation link!


61-83: Good implementation of GetPlatform with relative reference resolution

The method properly handles platform-specific provider information retrieval. I particularly like that you're resolving relative references in line 80, which helps ensure URLs are properly formatted regardless of how they're returned from the API.

cli/provider_cache.go (2)

99-104: Great refactoring that simplifies provider handler initialization

Using the new consolidated handlers.NewProviderHandlers function significantly improves the code by:

  1. Reducing duplication
  2. Making the provider handler creation process more maintainable
  3. Better error handling with a descriptive message

This is a solid improvement over manually constructing each handler type.


106-115: More explicit cache server configuration

The updated server initialization is more explicit about what's being configured:

  1. WithProviderService instead of the generic WithServices
  2. Adding WithProxyProviderHandler separately
  3. Explicitly setting the cache HTTP status code

This makes the code more self-documenting and easier to understand.

tf/cache/handlers/provider.go (2)

33-69: Well-implemented provider handler factory function

The NewProviderHandlers function centralizes the logic for creating different types of provider handlers based on configuration. I particularly like how it:

  1. Pre-allocates slices with capacity hints
  2. Handles different installation method types with a clean switch statement
  3. Ensures at least one direct provider handler is available as a fallback
  4. Properly propagates errors from network mirror handler creation

This consolidation makes the code much more maintainable.


86-95: Improved ProviderHandler interface

The interface changes represent good API design improvements:

  1. Using context.Context instead of framework-specific echo.Context makes the code more portable
  2. Returning concrete types (models.Versions, *models.ResponseBody) rather than just errors makes the API more explicit
  3. Removing the Download method suggests better separation of concerns

These changes make the interface cleaner and more focused.

tf/cache/handlers/filesystem_mirror_provider.go (2)

59-88: Well-implemented GetPlatform with URL handling

Nice job on checking whether the URL contains a scheme and constructing a full path when necessary. This handles both absolute URLs and relative paths elegantly.


90-107: Good error handling in readMirrorData

I like that this helper method:

  1. Gracefully handles missing files by returning nil
  2. Properly wraps and propagates errors
  3. Has a focused responsibility (read and unmarshal JSON data)

This makes the code more robust and easier to maintain.

@levkohimins levkohimins changed the title fix: Caching the same provider but with different versions from various sources fix: Caching the same provider with different versions from various sources Feb 26, 2025
@levkohimins levkohimins merged commit ab8a2f4 into main Feb 27, 2025
8 of 9 checks passed
@levkohimins levkohimins deleted the bug/provider-cache-multi-sources branch February 27, 2025 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Terragrunt Provider Cache Server does not search multiple provider sources
2 participants