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

feat: adds agent config command that returns the agent configuration #4671

Merged
merged 7 commits into from
Oct 31, 2024

Conversation

frrist
Copy link
Member

@frrist frrist commented Oct 28, 2024

Example:

$ bacalhau agent config
API:
    Host: 0.0.0.0
    Port: 1234
    Auth:
        Methods:
            ClientKey:
                Type: challenge
NameProvider: puuid
DataDir: /home/frrist/.bacalhau
Orchestrator:
    Enabled: true
    Host: 0.0.0.0
    Port: 4222
    NodeManager:
        DisconnectTimeout: 1m0s
    Scheduler:
        WorkerCount: 32
        QueueBackoff: 1m0s
        HousekeepingInterval: 30s
        HousekeepingTimeout: 2m0s
    EvaluationBroker:
        VisibilityTimeout: 1m0s
        MaxRetryCount: 10
Compute:
    Orchestrators:
        - nats://127.0.0.1:4222
    Heartbeat:
        InfoUpdateInterval: 1m0s
        ResourceUpdateInterval: 30s
        Interval: 15s
    AllocatedCapacity:
        CPU: 70%
        Memory: 70%
        Disk: 70%
        GPU: 100%
    AllowListedLocalPaths: []
WebUI:
    Listen: 0.0.0.0:8438
InputSources:
    ReadTimeout: 5m0s
    MaxRetryCount: 3
Publishers:
    Types:
        Local:
            Address: 127.0.0.1
            Port: 6001
Engines:
    Types:
        Docker:
            ManifestCache:
                Size: 1000
                TTL: 1h0m0s
                Refresh: 1h0m0s
JobDefaults:
    Batch:
        Task:
            Resources:
                CPU: 500m
                Memory: 1Gb
    Ops:
        Task:
            Resources:
                CPU: 500m
                Memory: 1Gb
    Daemon:
        Task:
            Resources:
                CPU: 500m
                Memory: 1Gb
    Service:
        Task:
            Resources:
                CPU: 500m
                Memory: 1Gb
JobAdmissionControl:
    Locality: anywhere
Logging:
    Level: info
    Mode: default
    LogDebugInfoInterval: 30s
UpdateConfig:
    Interval: 24h0m0s

Summary by CodeRabbit

  • New Features

    • Introduced a command to retrieve the agent's configuration, allowing users to access configuration details easily.
    • Added support for customizable output formats (JSON and YAML) for the configuration command.
  • Bug Fixes

    • Enhanced error handling and sensitive data redaction in the configuration retrieval process.
  • Tests

    • Implemented tests to ensure the correct functionality and output of the new configuration command, including validation of sensitive data redaction.

@frrist frrist self-assigned this Oct 28, 2024
@frrist frrist requested review from jamlo and wdbaruni October 28, 2024 19:15
Copy link
Contributor

@jamlo jamlo left a comment

Choose a reason for hiding this comment

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

Nice. I believe this command will be very useful.

I have few remarks/concerns:

  1. It looks like this PR has broken the /api/v1/agent/config endpoint. The response is now the binary encoding of the config {"Config":"QVBJOgogICAgSG9zdDogMC4wLjAuMAogICAgUG9yd..... . I know about that since some of the TestContainers tests are failing
  2. Are there any potential sensitive data that we might need to mask from that config? Maybe any connection strings with passwords in them, or similar items ?
  3. Should we add few tests to protect this feature?

@frrist
Copy link
Member Author

frrist commented Oct 28, 2024

It looks like this PR has broken the /api/v1/agent/config endpoint. The response is now the binary encoding of the config {"Config":"QVBJOgogICAgSG9zdDogMC4wLjAuMAogICAgUG9yd..... . I know about that since some of the TestContainers tests are failing

Yes, this is to be expected given the body of the request is now []byte. My aim here is to simplify the response type, as it stand right now, the body contains a yaml string reflecting the servers current configuration. Clients need only print the body, rather than try to interpret the body. Open to alternatives here. I suppose one alternative may be to return an actual string, instead of a binary encoded one.

Are there any potential sensitive data that we might need to mask from that config? Maybe any connection strings with passwords in them, or similar items ?

Yes, several fields of the config may contain tokens, for example the Compute config here. My personal opinion is that config files shouldn't contain sensitive data - it should live somewhere else. That point aside, I am not sure excluding them from the (returned) config is the right choice, perhaps it may be simpler to require auth to query this endpoint? Again, open to alternatives.

Should we add few tests to protect this feature

100%. Can you point me to the appropriate package to writing such tests?

@wdbaruni
Copy link
Member

The API should just return json representation of the config exactly the same how it works today. No binary and no need for nesting inside a Config field unless we think more fields can be returned, which I doubt it is the case. Parsing into yaml representation should only happen in the CLI. It should just work with curl localhost:1234/api/v1/agent/config

I agree with Jamil about having to mask sensitive data. The only fields we have today are Compute.Auth.Token and Orchestrator.Auth.Token. If running in debug mode, then we can return the values as they are. Otherwise we should mask them. The maintainable approach would be to have a dedicated field type for sensitive data instead of treating them as plain strings, and let the masking take place in one location when mashalling those fields. But that is a lot of work for now and the quick solution would be to mask those fields in the API handler or when mashalling the root config type

@jamlo
Copy link
Contributor

jamlo commented Oct 29, 2024

Yes, this is to be expected given the body of the request is now []byte. My aim here is to simplify the response type, as it stand right now, the body contains a yaml string reflecting the servers current configuration. Clients need only print the body, rather than try to interpret the body. Open to alternatives here. I suppose one alternative may be to return an actual string, instead of a binary encoded one.

I think it better to return the json representation of the YAML config, since it is much more standard API behavior. Although Json is a subset of YAML, and not the other way around, I believe the current structure of the config should convert to JSON with now issues.

Yes, several fields of the config may contain tokens, for example the Compute config here. My personal opinion is that config files shouldn't contain sensitive data - it should live somewhere else. That point aside, I am not sure excluding them from the (returned) config is the right choice, perhaps it may be simpler to require auth to query this endpoint? Again, open to alternatives.

I think requiring authentication is for sure needed for this endpoint, regardless if it contains sensitive data (exposing configuration publicly is a big security attack vector.)
Also, I think masking certain types as "redacted" will also minimize that attack vector, and if we set the boundary that no credentials can be even queried through API, a user should be on the node itself to see these creds.

100%. Can you point me to the appropriate package to writing such tests?

I was thinking of a basic unit test that lives beside the cmd/cli/agent/config.go file for now. And I will fix the TestContainers tests when the PR is merged.

@frrist
Copy link
Member Author

frrist commented Oct 29, 2024

The API should just return json representation of the config exactly the same how it works today
I think it better to return the json representation of the YAML config

Sounds good, will make the change.

the quick solution would be to mask those fields in the API handler or when mashalling the root config type
I think masking certain types as "redacted" will also minimize that attack vector

Also sounds good, will change.

I was thinking of a basic unit test that lives beside the cmd/cli/agent/config.go file for now. And I will fix the TestContainers tests when the PR is merged.

Sounds good to me.

@frrist
Copy link
Member Author

frrist commented Oct 29, 2024

@wdbaruni @jamlo how do we want the client/CLI to display the config to the users? Should it marshal the JSON payload to YAML?

@jamlo
Copy link
Contributor

jamlo commented Oct 29, 2024

@wdbaruni @jamlo how do we want the client/CLI to display the config to the users? Should it marshal the JSON payload to YAML?

I've noticed that some commands already have this flag:

--output format            The output format for the command (one of ["table" "csv" "json" "yaml"]) (default table)

Maybe in this situation csv and table does not apply. But we can give the user the option to choose the format, and default to any format you see fit (I prefer YAML since it is easier to read).

@frrist
Copy link
Member Author

frrist commented Oct 30, 2024

@jamlo in fe291ae I have (attempted to) fix the integration tests. I've been unable to verify this locally as the (new) integration tests fail on my Ubuntu host with the following error message:

2024/10/30 14:02:45 ====> Starting the whole test flow:  a0c4d041
2024/10/30 14:02:45 Error compiling the bacalhau binary:  compiling bacalhau: failed to start container: create container: Error response from daemon: client version 1.45 is too new. Maximum supported API version is 1.43

I suspect I either need to downgrade my client version or upgrade the version required by the testing framework. wdyt?

@jamlo
Copy link
Contributor

jamlo commented Oct 30, 2024

@frrist For the docker API version, you can configure that in the Tests when you run it locally here . I've set the APi version since Github Actions does not have the latest version, so I pin it.

Btw, I checked the TestContainers tests, and they are running fine on CI after you did the changes, except for one Test that is failing due to how I am calling curl (I should call it to only output the response body).

Since these tests are not a blocker to merge yet, I will fix it when you merge the PR.

@jamlo jamlo self-requested a review October 31, 2024 13:26
Copy link
Contributor

coderabbitai bot commented Oct 31, 2024

Walkthrough

A new command for retrieving the agent's configuration has been added to the Bacalhau project. This includes the implementation of a Cobra command in cmd/cli/agent/config.go, which sets up the repository configuration and creates an API client to fetch the agent's configuration. The changes also introduce a corresponding test suite in cmd/cli/agent/config_test.go to validate the command's output in both JSON and YAML formats. Additionally, modifications in several files enhance the API's ability to manage and return the agent's configuration, including error handling and sensitive data redaction.

Changes

File Path Change Summary
cmd/cli/agent/config.go Added NewConfigCmd function for the config command and run function for executing the API call.
cmd/cli/agent/config_test.go Introduced ConfigSuite for testing JSON and YAML output of the configuration command.
cmd/cli/agent/root.go Integrated NewConfigCmd into the command hierarchy.
pkg/config/types/bacalhau.go Added a comment for the DisableAnalytics field in the Bacalhau struct.
pkg/publicapi/apimodels/agent.go Introduced GetAgentConfigResponse type for encapsulating configuration details in API responses.
pkg/publicapi/client/v2/api_agent.go Added Config method to retrieve the agent's configuration.
pkg/publicapi/endpoint/agent/endpoint.go Enhanced config method to include error handling and redact sensitive information.
pkg/publicapi/endpoint/agent/endpoint_test.go Introduced TestEndpointConfigRedactFields to test sensitive data redaction in API responses.
test_integration/1_orchestrator_basic_config_suite_test.go Modified test logic to adapt to changes in JSON structure.
test_integration/2_orchestrator_config_override_suite_test.go Adjusted import order; no functional changes.
test_integration/3_orchestrator_config_override_and_flag_suite_test.go Adjusted import order; no functional changes.
test_integration/4_orchestrator_config_override_and_flag_and_config_flag_suite_test.go Modified test logic to adapt to changes in JSON structure.
test_integration/5_orchestrator_no_config_suite_test.go Modified test logic to adapt to changes in JSON structure.
test_integration/6_jobs_basic_runs_scenarios_suite_test.go Adjusted import order; no functional changes.

Assessment against linked issues

Objective Addressed Explanation
API to retrieve node config (#4113)

🐰 In the meadow, where bunnies hop,
A command was added, now we can't stop!
Configurations retrieved with a cheer,
JSON and YAML, all crystal clear.
With tokens redacted, our secrets stay tight,
Hooray for the changes that make things right! 🌼


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 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.

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: 5

🧹 Outside diff range and nitpick comments (7)
pkg/publicapi/client/v2/api_agent.go (1)

33-38: Consider adding support for sensitive data redaction.

While the implementation follows the established pattern in the codebase, there's a need to handle sensitive data in the configuration response. Consider:

  1. Adding options to the request to control sensitive data visibility
  2. Implementing server-side redaction of sensitive fields

Here's a suggested approach:

-func (c *Agent) Config(ctx context.Context) (*apimodels.GetAgentConfigResponse, error) {
+func (c *Agent) Config(ctx context.Context, req *apimodels.GetAgentConfigRequest) (*apimodels.GetAgentConfigResponse, error) {
 	var res apimodels.GetAgentConfigResponse
-	err := c.client.Get(ctx, "/api/v1/agent/config", &apimodels.BaseGetRequest{}, &res)
+	err := c.client.Get(ctx, "/api/v1/agent/config", req, &res)
 	return &res, err
 }

Where GetAgentConfigRequest could include:

type GetAgentConfigRequest struct {
    apimodels.BaseGetRequest
    IncludeSensitive bool `json:"include_sensitive"`
}
cmd/cli/agent/config_test.go (2)

5-15: Consider grouping imports for better readability.

While all necessary imports are present, consider grouping them with blank lines between standard library, third-party, and internal packages.

 import (
 	"encoding/json"
 	"testing"
+
 	"github.com/stretchr/testify/suite"
 	"gopkg.in/yaml.v3"
+
 	cmdtesting "github.com/bacalhau-project/bacalhau/cmd/testing"
 	"github.com/bacalhau-project/bacalhau/cmd/util/output"
 	"github.com/bacalhau-project/bacalhau/pkg/config/types"
 )

1-46: Consider restructuring tests for better maintainability.

The test suite could benefit from the following architectural improvements:

  1. Use table-driven tests to cover multiple config scenarios
  2. Create test helpers for common assertions to reduce code duplication
  3. Add a separate test file for integration tests that verify the actual API interaction
  4. Consider using a mock config provider for more controlled testing

Example structure:

type configTestCase struct {
    name           string
    args           []string
    expectedFields map[string]interface{}
    expectError    bool
}

func (s *ConfigSuite) TestConfigWithCases() {
    cases := []configTestCase{
        {
            name: "default yaml output",
            args: []string{"agent", "config"},
            expectedFields: map[string]interface{}{
                "Orchestrator.Enabled": true,
                "APIServer.Host":       "localhost",
            },
        },
        // Add more test cases
    }
    
    for _, tc := range cases {
        s.Run(tc.name, func() {
            // Test implementation
        })
    }
}
cmd/cli/agent/config.go (1)

15-38: Add documentation about output format options.

While the command implementation is solid, it would be helpful to document the available output formats in the command's description.

Apply this change:

 configCmd := &cobra.Command{
 	Use:   "config",
-	Short: "Get the agent's configuration.",
+	Short: "Get the agent's configuration in YAML or JSON format.",
+	Long: `Get the agent's configuration.
+
+The output format can be controlled using the --format flag:
+  - yaml: Human-readable YAML format (default)
+  - json: JSON format
+
+Example:
+  bacalhau agent config
+  bacalhau agent config --format json`,
 	Args:  cobra.NoArgs,
test_integration/5_orchestrator_no_config_suite_test.go (1)

Line range hint 60-77: Consider adding test cases for sensitive data handling.

The test validates basic configuration fields but doesn't verify the handling of sensitive data, which was a concern raised in the PR comments.

Would you like me to help generate additional test cases for:

  1. Verifying that sensitive fields are properly masked
  2. Testing authentication requirements for the config endpoint
pkg/publicapi/endpoint/agent/endpoint.go (2)

Line range hint 117-126: Update API documentation to match implementation.

The godoc needs the following improvements:

  1. The @success response type should be apimodels.GetAgentConfigResponse instead of types.Bacalhau.
  2. Add a note about sensitive data redaction in the @description.

Update the godoc block as follows:

 // config godoc
 //
 //	@ID			agent/config
 //	@Summary	Returns the current configuration of the node.
+//	@Description	Returns the node configuration with sensitive information (auth tokens) redacted.
 //	@Tags		Ops
 //	@Produce	json
-//	@Success	200	{object}	types.Bacalhau
+//	@Success	200	{object}	apimodels.GetAgentConfigResponse
 //	@Failure	500	{object}	string
 //	@Router		/api/v1/agent/config [get]

127-130: Add validation after config copy.

Consider validating the copied configuration before proceeding with redaction to ensure data integrity.

 cfg, err := e.bacalhauConfig.Copy()
 if err != nil {
   return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("could not copy bacalhau config: %s", err))
 }
+if cfg == nil {
+  return echo.NewHTTPError(http.StatusInternalServerError, "copied config is nil")
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 65dba8b and 239f351.

📒 Files selected for processing (14)
  • cmd/cli/agent/config.go (1 hunks)
  • cmd/cli/agent/config_test.go (1 hunks)
  • cmd/cli/agent/root.go (1 hunks)
  • pkg/config/types/bacalhau.go (1 hunks)
  • pkg/publicapi/apimodels/agent.go (2 hunks)
  • pkg/publicapi/client/v2/api_agent.go (1 hunks)
  • pkg/publicapi/endpoint/agent/endpoint.go (3 hunks)
  • pkg/publicapi/endpoint/agent/endpoint_test.go (1 hunks)
  • test_integration/1_orchestrator_basic_config_suite_test.go (2 hunks)
  • test_integration/2_orchestrator_config_override_suite_test.go (1 hunks)
  • test_integration/3_orchestrator_config_override_and_flag_suite_test.go (1 hunks)
  • test_integration/4_orchestrator_config_override_and_flag_and_config_flag_suite_test.go (2 hunks)
  • test_integration/5_orchestrator_no_config_suite_test.go (2 hunks)
  • test_integration/6_jobs_basic_runs_scenarios_suite_test.go (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • pkg/config/types/bacalhau.go
  • test_integration/2_orchestrator_config_override_suite_test.go
  • test_integration/3_orchestrator_config_override_and_flag_suite_test.go
  • test_integration/6_jobs_basic_runs_scenarios_suite_test.go
🔇 Additional comments (18)
cmd/cli/agent/root.go (1)

19-19: LGTM! Command registration follows established patterns.

The addition of NewConfigCmd() is clean and consistent with the existing command structure.

Let's verify the command implementation:

✅ Verification successful

Let me gather more information about the config command implementation and its test coverage.


Implementation verified and properly tested

The command implementation is well-structured and meets the requirements:

  • Properly handles both JSON and YAML output formats
  • Uses the API client to fetch configuration securely
  • Has comprehensive test coverage for both output formats
  • Follows the project's patterns for command implementation
  • Properly handles errors and provides clear error messages
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the config command implementation and its test coverage

# Test 1: Check if NewConfigCmd is properly defined
ast-grep --pattern 'func NewConfigCmd() *cobra.Command'

# Test 2: Verify test coverage
rg -l "TestConfig.*" --type go

Length of output: 2199


Script:

#!/bin/bash
# Check the implementation of the run function and test coverage

# Get the run function implementation
ast-grep --pattern 'func run(cmd *cobra.Command, api $_,$_) error {
  $$$
}'

# Get the test implementation
cat cmd/cli/agent/config_test.go

Length of output: 1990

pkg/publicapi/apimodels/agent.go (2)

3-6: LGTM: Import changes are appropriate

The added import for types package is necessary for the new GetAgentConfigResponse struct.


37-40: Add documentation and consider sensitive data handling

The struct definition looks good, but there are a few considerations:

  1. Add documentation comment for the struct explaining its purpose and usage
  2. Consider adding field tags or implementing custom marshaling to handle sensitive data redaction, as discussed in the PR comments

Let's verify if the Bacalhau type contains sensitive fields:

Suggested documentation and potential sensitive data handling:

+// GetAgentConfigResponse represents the response for agent configuration retrieval.
+// Note: This response may contain sensitive information and should be handled accordingly.
 type GetAgentConfigResponse struct {
 	BaseGetResponse
-	Config types.Bacalhau `json:"config"`
+	Config types.Bacalhau `json:"config" sensitive:"true"`
 }
pkg/publicapi/client/v2/api_agent.go (1)

33-38: Verify error handling consistency with other endpoints.

Let's ensure the error handling is consistent with other endpoints and the configuration endpoint is properly implemented.

✅ Verification successful

Let me gather more information about the error handling patterns in similar client methods to ensure consistency.


Let me try one more verification to check the error handling patterns in similar client methods using a different pattern.


Error handling is consistent with other Agent client methods

The implementation follows the same error handling pattern used across other Agent client methods (Alive, Version, Node):

  1. Declares a response variable of the appropriate type
  2. Uses client.Get with consistent parameter ordering
  3. Returns the response pointer and error directly

The endpoint is also properly defined in the API schema (swagger.json) and has corresponding test coverage.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check error handling patterns across the codebase
echo "Checking error handling patterns in API endpoints..."
rg -A 2 "func.*Config.*context.Context.*error" 

# Verify the endpoint implementation
echo "Checking configuration endpoint implementation..."
rg -A 5 '"/api/v1/agent/config"'

Length of output: 2811


Script:

#!/bin/bash
# Check error handling in other client methods in the same package
echo "Checking error handling in other client methods..."
ast-grep --pattern 'func \(c \*Agent\) $_\(ctx context.Context\) \(\$_, error\) {
  $$$
}'

# Check the client.Get implementation
echo "Checking client.Get implementation..."
ast-grep --pattern 'func \(c \*$_\) Get\(ctx context.Context, $$$\) error {'

Length of output: 367


Script:

#!/bin/bash
# Check other methods in the Agent client
echo "Checking other Agent client methods..."
rg -A 3 "func \(c \*Agent\)" pkg/publicapi/client/v2/

# Check the client implementation used by Agent
echo "Checking client implementation..."
rg -A 3 "type.*client.*struct" pkg/publicapi/client/v2/

Length of output: 1796

cmd/cli/agent/config_test.go (3)

1-4: LGTM! Build tags and package declaration are correct.

The build tags appropriately separate unit tests from integration tests, and the external test package is suitable for black-box testing.


17-23: LGTM! Test suite setup is well-structured.

The test suite is properly initialized using testify/suite, and the embedding of BaseSuite provides common testing functionality.


37-46: 🛠️ Refactor suggestion

Enhance test coverage for YAML output and verify potential issues.

Similar to the JSON test, this could benefit from more comprehensive assertions. Additionally, we should verify that the command handles various edge cases correctly.

 func (s *ConfigSuite) TestConfigYAMLOutput() {
 	// NB: the default output is yaml, thus we don't specify it here.
 	_, out, err := s.ExecuteTestCobraCommand("agent", "config")
 	s.Require().NoError(err, "Could not request config with yaml output.")
 
 	var cfg types.Bacalhau
 	err = yaml.Unmarshal([]byte(out), &cfg)
 	s.Require().NoError(err, "Could not unmarshal the output into yaml - %+v", out)
+	// Verify structure and essential fields
 	s.Require().True(cfg.Orchestrator.Enabled)
+	s.Require().NotEmpty(cfg.APIServer.Host, "API server host should be set")
+	s.Require().NotEmpty(cfg.APIServer.Port, "API server port should be set")
+
+	// Verify sensitive data is redacted
+	s.Require().Equal("[REDACTED]", cfg.Compute.IPFS.SwarmKey, "SwarmKey should be redacted")
+
+	// Verify YAML structure
+	s.Require().Contains(out, "APIServer:", "YAML should contain APIServer section")
+	s.Require().Contains(out, "Compute:", "YAML should contain Compute section")
 }
+
+func (s *ConfigSuite) TestConfigEdgeCases() {
+	// Test with invalid output format
+	_, _, err := s.ExecuteTestCobraCommand("agent", "config", "--output", "invalid")
+	s.Require().Error(err, "Should error on invalid output format")
+
+	// Test with both --pretty and --output=json
+	_, out, err := s.ExecuteTestCobraCommand("agent", "config", "--output", "json", "--pretty")
+	s.Require().NoError(err)
+	s.Require().Contains(out, "\n  ", "JSON output should be pretty-printed")
+}

Let's verify the command implementation to ensure it properly handles these test cases:

cmd/cli/agent/config.go (2)

1-13: LGTM! Package and imports are well organized.

The imports cover all necessary functionality for implementing the agent config command.


24-34: Consider handling sensitive data in configuration.

Based on PR discussions, there's a need to handle sensitive data in the configuration. Consider implementing redaction of sensitive fields before returning the configuration.

Let's check if there are any sensitive fields in the configuration type:

Consider implementing a configuration sanitizer that:

  1. Identifies sensitive fields (tokens, keys, credentials)
  2. Redacts or masks these values before display
  3. Optionally requires authentication for viewing full config
pkg/publicapi/endpoint/agent/endpoint_test.go (2)

1-17: LGTM! Build tags and imports are well-organized.

The build tags are correctly configured for unit testing, and the imports cover all necessary testing and API dependencies.


40-49: Verify JSON format aligns with API requirements.

The test confirms that the endpoint returns JSON format as discussed in the PR comments. Let's verify the complete API implementation:

✅ Verification successful

Let me gather more information about the JSON response format implementation.


Let me check one final aspect about the response handling and model structure.


Implementation correctly handles JSON format and sensitive data

The verification confirms that:

  • The endpoint is properly configured to return JSON using echo.MIMEApplicationJSON content type
  • Response is structured using GetAgentConfigResponse model with proper JSON tags
  • Sensitive auth tokens are consistently redacted before being sent in the response
  • The test correctly validates both JSON parsing and data redaction
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the API consistently returns JSON format and handles sensitive data properly

# Test 1: Check if the API endpoint is consistently using JSON
echo "Checking for JSON response handling..."
rg -l "json\." pkg/publicapi/endpoint/agent/

# Test 2: Verify sensitive field handling across the codebase
echo "Checking sensitive field handling..."
rg "Token.*redact" pkg/publicapi/

Length of output: 694


Script:

#!/bin/bash
# Check the endpoint implementation and response type
echo "Checking endpoint implementation..."
rg -A 5 "func.*Config" pkg/publicapi/endpoint/agent/endpoint.go

# Check the API models for response structure
echo "Checking API models..."
rg "type.*GetAgentConfigResponse" pkg/publicapi/

Length of output: 322


Script:

#!/bin/bash
# Check the API model structure and handler implementation
echo "Checking API model structure..."
cat pkg/publicapi/apimodels/agent.go

echo "Checking handler implementation..."
cat pkg/publicapi/endpoint/agent/endpoint.go

Length of output: 5080

test_integration/5_orchestrator_no_config_suite_test.go (2)

7-9: LGTM! Import organization looks good.

The import statements are properly organized and grouped.


60-60: Verify the new config response structure across all endpoints.

The change to access config via ["config"] key aligns with the PR's changes to the /api/v1/agent/config endpoint. However, let's verify this structure is consistent across all related endpoints.

✅ Verification successful

Let me gather more information about the response structure by checking the implementation of the endpoint and related tests.


Based on the gathered information, I can now provide the final response:

The config response structure with ["config"] key is consistent across all endpoints and tests.

The implementation and test files show that:

  • The /api/v1/agent/config endpoint consistently returns the configuration under the config key
  • All test files (test_integration/1_orchestrator_basic_config_suite_test.go and test_integration/5_orchestrator_no_config_suite_test.go) access the config using ["config"] key
  • The endpoint implementation in pkg/publicapi/endpoint/agent/endpoint.go returns the config directly, which is then wrapped with the config key at the API layer
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the consistency of config response structure across endpoints
# and test files.

# Test 1: Check for other endpoint definitions that might need similar updates
echo "Checking API endpoint definitions..."
rg -A 3 "/api/v1/agent/config"

# Test 2: Check for similar test patterns that might need updates
echo "Checking test patterns..."
ast-grep --pattern 'unmarshalledOutput.(map[string]interface{})["config"]'

Length of output: 3905


Script:

#!/bin/bash
# Check the implementation of the config endpoint
echo "Checking config endpoint implementation..."
rg -A 10 "func.*config\(.*echo\.Context\)" pkg/publicapi/endpoint/agent/endpoint.go

# Check all test files for config response structure
echo "Checking test assertions on config response..."
rg -A 5 "unmarshalledOutput|marshalledOutput" test_integration/

Length of output: 14969

test_integration/4_orchestrator_config_override_and_flag_and_config_flag_suite_test.go (2)

8-10: LGTM! Import organization improved.

The reorganization of imports improves code readability.


71-71: 🛠️ Refactor suggestion

Verify the new config structure and add error handling.

The test correctly verifies the new config structure with the additional config key. However, the deep type assertions could be made more robust.

Consider adding type assertion error handling:

-webuiEnabled := unmarshalledAgentOutput.(map[string]interface{})["config"].(map[string]interface{})["WebUI"].(map[string]interface{})["Enabled"].(bool)
+config, ok := unmarshalledAgentOutput.(map[string]interface{})
+s.Require().True(ok, "Expected agent output to be a map")
+configSection, ok := config["config"].(map[string]interface{})
+s.Require().True(ok, "Expected config section to be a map")
+webUI, ok := configSection["WebUI"].(map[string]interface{})
+s.Require().True(ok, "Expected WebUI section to be a map")
+webuiEnabled, ok := webUI["Enabled"].(bool)
+s.Require().True(ok, "Expected Enabled to be a boolean")

Let's verify the new config structure is consistent across the codebase:

✅ Verification successful

Let me search for more context about the config structure and type assertions in the codebase.


Let me check the WebUI config structure definition to understand the expected types.


Let me check one more thing about the test context to understand the unmarshal pattern being used.


Based on all the gathered context, I can now provide the final response:

Add error handling for WebUI config type assertions

The review comment is correct. The code performs multiple type assertions without error checking, while dealing with JSON response from an HTTP endpoint. The suggested refactoring with gradual type assertions and error handling is appropriate because:

  1. The WebUI config structure is well-defined in both legacy (pkg/config_legacy/types/node.go) and current (pkg/config/types/webui.go) implementations
  2. The code is part of an integration test that makes HTTP requests, where response validation is crucial
  3. This is the only instance of such deep type assertions in the test files, making it a good candidate for improvement
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the config structure is consistent across tests
# Expect: All test files should use the same path to access WebUI.Enabled

# Search for WebUI config access patterns in test files
rg -A 2 'WebUI.*Enabled' test_integration/

Length of output: 555


Script:

#!/bin/bash
# Search for similar config structure access patterns
rg -A 2 'unmarshalledAgentOutput.*config' test_integration/

# Search for type assertions with error handling patterns
rg -A 2 'ok\s*:=.*\.(map\[string\]interface{})' test_integration/

# Look for WebUI config structure definition
rg -l 'type.*WebUI.*struct' 

Length of output: 883


Script:

#!/bin/bash
# Check WebUI config structure in both legacy and current implementations
cat pkg/config_legacy/types/node.go pkg/config/types/webui.go

# Look for other test files that might be using similar unmarshal patterns
rg -A 2 'json\.Unmarshal.*AgentOutput' test_integration/

Length of output: 5298


Script:

#!/bin/bash
# Get more context around the unmarshalledAgentOutput usage
rg -B 5 -A 5 'unmarshalledAgentOutput' test_integration/4_orchestrator_config_override_and_flag_and_config_flag_suite_test.go

# Check if there are any other similar test files
fd -e go . test_integration/ -x grep -l "AgentOutput"

Length of output: 1065

pkg/publicapi/endpoint/agent/endpoint.go (1)

137-139: LGTM! Response structure follows API conventions.

The response is correctly wrapped in apimodels.GetAgentConfigResponse and uses proper HTTP status codes.

test_integration/1_orchestrator_basic_config_suite_test.go (2)

6-8: LGTM! Clean import organization.

The import statements are now properly organized and duplicates have been removed.


69-69: Verify API contract alignment.

The test has been updated to access the orchestrator configuration through a nested structure config.Orchestrator.Enabled. Let's verify this matches the new API contract and documentation.

✅ Verification successful

Let me gather more information about the config structure by searching for the configuration type definitions.


Let me search specifically for the Orchestrator configuration structure to verify the API contract.


Let me check the Bacalhau configuration structure to verify how the Orchestrator config is nested.


Based on the gathered information, I can now provide the final response:

API contract is correctly implemented in the test

The test's access pattern config.Orchestrator.Enabled aligns with the official API contract. This is verified by:

  1. The Bacalhau configuration structure in pkg/config/types/bacalhau.go which shows Orchestrator field as a top-level field
  2. The Orchestrator struct in pkg/config/types/orchestrator.go containing the Enabled boolean field
  3. Consistent usage across other parts of the codebase, including CLI flags and documentation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the API contract for agent config endpoint
# Expected: Find documentation or other tests that confirm the new structure

# Test 1: Look for API documentation
echo "Searching for API documentation..."
fd -g '*.md' -x rg -l 'api/v1/agent/config'

# Test 2: Look for similar test patterns
echo "Searching for similar test patterns..."
rg -A 2 'api/v1/agent/config' --type go

# Test 3: Look for struct definitions
echo "Searching for config structure definitions..."
ast-grep --pattern 'type $_ struct {
  Config struct {
    Orchestrator struct {
      Enabled bool
      $$$
    }
    $$$
  }
  $$$
}'

Length of output: 2651


Script:

#!/bin/bash
# Search for Orchestrator config structure definitions
echo "Searching for Orchestrator config structures..."
ast-grep --pattern 'type $_ struct {
  Orchestrator struct {
    Enabled bool
    $$$
  }
  $$$
}'

# Search for config related type definitions
echo "Searching for config type definitions..."
rg -l "type.*Config.*struct" --type go

# Look at the actual config implementation
echo "Examining config implementation..."
rg -A 5 "type.*Config.*struct" --type go

Length of output: 39121


Script:

#!/bin/bash
# Search for Orchestrator-related configuration structures
echo "Searching for Orchestrator configuration..."
rg -A 10 "type.*struct.*{.*Orchestrator.*}" --type go

# Search for the agent config endpoint implementation
echo "Searching for agent config endpoint implementation..."
rg -A 10 "func.*config.*echo\.Context.*" pkg/publicapi/endpoint/agent/endpoint.go

# Look for Orchestrator-related fields in configuration
echo "Searching for Orchestrator configuration fields..."
rg -A 3 "Orchestrator.*struct" --type go

Length of output: 3535


Script:

#!/bin/bash
# Search for Bacalhau configuration structure
echo "Searching for Bacalhau config structure..."
rg -A 15 "type Bacalhau struct" pkg/config/types/

# Search for other test files accessing the config
echo "Searching for config access patterns in tests..."
rg -A 3 'config.*Orchestrator.*Enabled' --type go

Length of output: 3753

Comment on lines +25 to +35
func (s *ConfigSuite) TestConfigJSONOutput() {
_, out, err := s.ExecuteTestCobraCommand(
"agent", "config", "--output", string(output.JSONFormat), "--pretty=false",
)
s.Require().NoError(err, "Could not request config with json output.")

var cfg types.Bacalhau
err = json.Unmarshal([]byte(out), &cfg)
s.Require().NoError(err, "Could not unmarshal the output into json - %+v", err)
s.Require().True(cfg.Orchestrator.Enabled)
}
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

Enhance test coverage for JSON output.

While the basic functionality is tested, consider adding more assertions:

  1. Verify sensitive data redaction (tokens, keys)
  2. Test more configuration fields beyond just orchestrator.Enabled
  3. Validate the structure and format of the JSON output
 func (s *ConfigSuite) TestConfigJSONOutput() {
 	_, out, err := s.ExecuteTestCobraCommand(
 		"agent", "config", "--output", string(output.JSONFormat), "--pretty=false",
 	)
 	s.Require().NoError(err, "Could not request config with json output.")
 
 	var cfg types.Bacalhau
 	err = json.Unmarshal([]byte(out), &cfg)
 	s.Require().NoError(err, "Could not unmarshal the output into json - %+v", err)
+	// Verify structure and essential fields
 	s.Require().True(cfg.Orchestrator.Enabled)
+	s.Require().NotEmpty(cfg.APIServer.Host, "API server host should be set")
+	s.Require().NotEmpty(cfg.APIServer.Port, "API server port should be set")
+
+	// Verify sensitive data is redacted
+	s.Require().Equal("[REDACTED]", cfg.Compute.IPFS.SwarmKey, "SwarmKey should be redacted")
+
+	// Verify JSON structure
+	s.Require().Contains(out, "\"APIServer\":", "JSON should contain APIServer section")
+	s.Require().Contains(out, "\"Compute\":", "JSON should contain Compute section")
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +40 to +48
func run(cmd *cobra.Command, api client.API, o output.NonTabularOutputOptions) error {
ctx := cmd.Context()
response, err := api.Agent().Config(ctx)
if err != nil {
return fmt.Errorf("cannot get agent config: %w", err)
}

return output.OutputOneNonTabular[types.Bacalhau](cmd, o, response.Config)
}
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

Enhance error handling and add timeout.

While the implementation is functional, consider these improvements:

  1. Add context timeout for the API call
  2. Enhance error handling for empty or invalid responses

Consider applying these changes:

 func run(cmd *cobra.Command, api client.API, o output.NonTabularOutputOptions) error {
-	ctx := cmd.Context()
+	ctx, cancel := context.WithTimeout(cmd.Context(), 30*time.Second)
+	defer cancel()
+
 	response, err := api.Agent().Config(ctx)
 	if err != nil {
 		return fmt.Errorf("cannot get agent config: %w", err)
 	}
+	if response == nil || response.Config == nil {
+		return fmt.Errorf("received empty configuration from agent")
+	}
 
 	return output.OutputOneNonTabular[types.Bacalhau](cmd, o, response.Config)
 }

Don't forget to add the imports:

import (
    "context"
    "time"
)

Comment on lines +19 to +52
// TestEndpointConfigRedactFields asserts that auth tokens in the config are redacted.
func TestEndpointConfigRedactFields(t *testing.T) {
router := echo.New()

// populate the fields that should be redacted with "secret" values.
NewEndpoint(EndpointParams{
Router: router,
BacalhauConfig: types.Bacalhau{
Orchestrator: types.Orchestrator{
Auth: types.OrchestratorAuth{
Token: "super-secret-orchestrator-token",
},
},
Compute: types.Compute{
Auth: types.ComputeAuth{
Token: "super-secret-orchestrator-token",
},
},
},
})

req := httptest.NewRequest(http.MethodGet, "/api/v1/agent/config", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)

require.Equal(t, http.StatusOK, rr.Code)

// assert the secret values are not present.
var payload apimodels.GetAgentConfigResponse
err := json.NewDecoder(rr.Body).Decode(&payload)
require.NoError(t, err)
assert.Equal(t, payload.Config.Orchestrator.Auth.Token, "<redacted>")
assert.Equal(t, payload.Config.Compute.Auth.Token, "<redacted>")
}
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

Consider enhancing test coverage with additional scenarios.

The current test effectively verifies the redaction of sensitive tokens. However, consider these improvements:

  1. Add table-driven tests to cover various configuration scenarios
  2. Include negative test cases (e.g., malformed config)
  3. Verify non-sensitive fields remain unchanged
  4. Add Content-Type header validation
  5. Assert the complete JSON structure matches the API model

Here's a suggested enhancement:

 func TestEndpointConfigRedactFields(t *testing.T) {
+    tests := []struct {
+        name           string
+        config        types.Bacalhau
+        expectedCode  int
+        expectRedacted bool
+    }{
+        {
+            name: "should redact sensitive fields",
+            config: types.Bacalhau{
+                Orchestrator: types.Orchestrator{
+                    Auth: types.OrchestratorAuth{
+                        Token: "secret-token",
+                    },
+                },
+                // Add more test fields here
+            },
+            expectedCode: http.StatusOK,
+            expectRedacted: true,
+        },
+        // Add more test cases
+    }
+
+    for _, tt := range tests {
+        t.Run(tt.name, func(t *testing.T) {
+            router := echo.New()
+            NewEndpoint(EndpointParams{
+                Router: router,
+                BacalhauConfig: tt.config,
+            })
+
+            req := httptest.NewRequest(http.MethodGet, "/api/v1/agent/config", nil)
+            rr := httptest.NewRecorder()
+            router.ServeHTTP(rr, req)
+
+            require.Equal(t, tt.expectedCode, rr.Code)
+            require.Equal(t, "application/json", rr.Header().Get("Content-Type"))
+
+            var payload apimodels.GetAgentConfigResponse
+            err := json.NewDecoder(rr.Body).Decode(&payload)
+            require.NoError(t, err)
+
+            // Verify sensitive fields are redacted
+            if tt.expectRedacted {
+                assert.Equal(t, "<redacted>", payload.Config.Orchestrator.Auth.Token)
+                assert.Equal(t, "<redacted>", payload.Config.Compute.Auth.Token)
+            }
+
+            // Verify non-sensitive fields remain unchanged
+            assert.Equal(t, tt.config.Orchestrator.Host, payload.Config.Orchestrator.Host)
+            // Add more field verifications
+        })
+    }
 }

Committable suggestion was skipped due to low confidence.

Comment on lines +131 to +136
if cfg.Compute.Auth.Token != "" {
cfg.Compute.Auth.Token = "<redacted>"
}
if cfg.Orchestrator.Auth.Token != "" {
cfg.Orchestrator.Auth.Token = "<redacted>"
}
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

Enhance sensitive data redaction.

Consider the following improvements to the redaction logic:

  1. Use a constant for the redaction text
  2. Consider redacting other potential sensitive fields (e.g., API keys, certificates)
+const (
+    redactedText = "<redacted>"
+)

-if cfg.Compute.Auth.Token != "" {
-    cfg.Compute.Auth.Token = "<redacted>"
-}
-if cfg.Orchestrator.Auth.Token != "" {
-    cfg.Orchestrator.Auth.Token = "<redacted>"
-}
+// Helper function to redact sensitive strings
+func redactIfNotEmpty(s string) string {
+    if s != "" {
+        return redactedText
+    }
+    return s
+}
+
+// Redact sensitive information
+cfg.Compute.Auth.Token = redactIfNotEmpty(cfg.Compute.Auth.Token)
+cfg.Orchestrator.Auth.Token = redactIfNotEmpty(cfg.Orchestrator.Auth.Token)
+// TODO: Consider redacting other sensitive fields like:
+// - API keys
+// - Certificates
+// - Private keys

Committable suggestion was skipped due to low confidence.

Comment on lines 68 to 71

orchestratorEnabled := marshalledOutput.(map[string]interface{})["Orchestrator"].(map[string]interface{})["Enabled"].(bool)
orchestratorEnabled := marshalledOutput.(map[string]interface{})["config"].(map[string]interface{})["Orchestrator"].(map[string]interface{})["Enabled"].(bool)
s.Require().Truef(orchestratorEnabled, "Expected orchestrator to be enabled, got: %t", orchestratorEnabled)
}
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

Consider expanding test coverage.

Given the PR objectives and discussions, consider adding test cases for:

  1. Verifying both JSON and YAML output formats
  2. Ensuring sensitive data is properly masked
  3. Testing error scenarios (e.g., invalid config, missing permissions)

Example test structure:

func (s *OrchestratorBasicConfigSuite) TestAgentConfigOutputFormats() {
    // Test JSON output
    jsonOutput, err := s.executeCommandInDefaultJumpbox([]string{
        "bacalhau", "agent", "config", "--output=json",
    })
    s.Require().NoError(err)
    // Verify JSON structure...

    // Test YAML output
    yamlOutput, err := s.executeCommandInDefaultJumpbox([]string{
        "bacalhau", "agent", "config", "--output=yaml",
    })
    s.Require().NoError(err)
    // Verify YAML structure...
}

func (s *OrchestratorBasicConfigSuite) TestSensitiveDataHandling() {
    output, err := s.executeCommandInDefaultJumpbox([]string{
        "bacalhau", "agent", "config",
    })
    s.Require().NoError(err)
    // Verify sensitive fields are masked...
}

@frrist frrist merged commit 111f238 into main Oct 31, 2024
14 checks passed
@frrist frrist deleted the frrist/cli/agent-config branch October 31, 2024 18:34
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.

API to retrive node config
3 participants