Skip to content

Commit 111f238

Browse files
frristfrrist
and
frrist
authored
feat: adds agent config command that returns the agent configuration (#4671)
- closes #4113 Example: ```shell $ bacalhau agent config ``` ```yaml 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 ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## 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. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: frrist <[email protected]>
1 parent 0db0ff2 commit 111f238

14 files changed

+201
-21
lines changed

cmd/cli/agent/config.go

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package agent
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/spf13/cobra"
7+
8+
"github.com/bacalhau-project/bacalhau/cmd/util"
9+
"github.com/bacalhau-project/bacalhau/cmd/util/flags/cliflags"
10+
"github.com/bacalhau-project/bacalhau/cmd/util/output"
11+
"github.com/bacalhau-project/bacalhau/pkg/config/types"
12+
"github.com/bacalhau-project/bacalhau/pkg/publicapi/client/v2"
13+
)
14+
15+
func NewConfigCmd() *cobra.Command {
16+
o := output.NonTabularOutputOptions{
17+
Format: output.YAMLFormat,
18+
Pretty: true,
19+
}
20+
configCmd := &cobra.Command{
21+
Use: "config",
22+
Short: "Get the agent's configuration.",
23+
Args: cobra.NoArgs,
24+
RunE: func(cmd *cobra.Command, args []string) error {
25+
cfg, err := util.SetupRepoConfig(cmd)
26+
if err != nil {
27+
return fmt.Errorf("failed to setup repo: %w", err)
28+
}
29+
api, err := util.GetAPIClientV2(cmd, cfg)
30+
if err != nil {
31+
return fmt.Errorf("failed to create api client: %w", err)
32+
}
33+
return run(cmd, api, o)
34+
},
35+
}
36+
configCmd.Flags().AddFlagSet(cliflags.OutputNonTabularFormatFlags(&o))
37+
return configCmd
38+
}
39+
40+
func run(cmd *cobra.Command, api client.API, o output.NonTabularOutputOptions) error {
41+
ctx := cmd.Context()
42+
response, err := api.Agent().Config(ctx)
43+
if err != nil {
44+
return fmt.Errorf("cannot get agent config: %w", err)
45+
}
46+
47+
return output.OutputOneNonTabular[types.Bacalhau](cmd, o, response.Config)
48+
}

cmd/cli/agent/config_test.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//go:build unit || !integration
2+
3+
package agent_test
4+
5+
import (
6+
"encoding/json"
7+
"testing"
8+
9+
"github.com/stretchr/testify/suite"
10+
"gopkg.in/yaml.v3"
11+
12+
cmdtesting "github.com/bacalhau-project/bacalhau/cmd/testing"
13+
"github.com/bacalhau-project/bacalhau/cmd/util/output"
14+
"github.com/bacalhau-project/bacalhau/pkg/config/types"
15+
)
16+
17+
func TestConfigSuite(t *testing.T) {
18+
suite.Run(t, new(ConfigSuite))
19+
}
20+
21+
type ConfigSuite struct {
22+
cmdtesting.BaseSuite
23+
}
24+
25+
func (s *ConfigSuite) TestConfigJSONOutput() {
26+
_, out, err := s.ExecuteTestCobraCommand(
27+
"agent", "config", "--output", string(output.JSONFormat), "--pretty=false",
28+
)
29+
s.Require().NoError(err, "Could not request config with json output.")
30+
31+
var cfg types.Bacalhau
32+
err = json.Unmarshal([]byte(out), &cfg)
33+
s.Require().NoError(err, "Could not unmarshal the output into json - %+v", err)
34+
s.Require().True(cfg.Orchestrator.Enabled)
35+
}
36+
37+
func (s *ConfigSuite) TestConfigYAMLOutput() {
38+
// NB: the default output is yaml, thus we don't specify it here.
39+
_, out, err := s.ExecuteTestCobraCommand("agent", "config")
40+
s.Require().NoError(err, "Could not request config with yaml output.")
41+
42+
var cfg types.Bacalhau
43+
err = yaml.Unmarshal([]byte(out), &cfg)
44+
s.Require().NoError(err, "Could not unmarshal the output into yaml - %+v", out)
45+
s.Require().True(cfg.Orchestrator.Enabled)
46+
}

cmd/cli/agent/root.go

+1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ func NewCmd() *cobra.Command {
1616
cmd.AddCommand(NewAliveCmd())
1717
cmd.AddCommand(NewNodeCmd())
1818
cmd.AddCommand(NewVersionCmd())
19+
cmd.AddCommand(NewConfigCmd())
1920
return cmd
2021
}

pkg/config/types/bacalhau.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ type Bacalhau struct {
3434
Logging Logging `yaml:"Logging,omitempty" json:"Logging,omitempty"`
3535
UpdateConfig UpdateConfig `yaml:"UpdateConfig,omitempty" json:"UpdateConfig,omitempty"`
3636
FeatureFlags FeatureFlags `yaml:"FeatureFlags,omitempty" json:"FeatureFlags,omitempty"`
37-
DisableAnalytics bool `yaml:"DisableAnalytics,omitempty" json:"DisableAnalytics,omitempty"`
37+
// DisableAnalytics, when true, disables sharing anonymous analytics data with the Bacalhau development team
38+
DisableAnalytics bool `yaml:"DisableAnalytics,omitempty" json:"DisableAnalytics,omitempty"`
3839
}
3940

4041
// Copy returns a deep copy of the Bacalhau configuration.

pkg/publicapi/apimodels/agent.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package apimodels
22

3-
import "github.com/bacalhau-project/bacalhau/pkg/models"
3+
import (
4+
"github.com/bacalhau-project/bacalhau/pkg/config/types"
5+
"github.com/bacalhau-project/bacalhau/pkg/models"
6+
)
47

58
// IsAliveResponse is the response to the IsAlive request.
69
type IsAliveResponse struct {
@@ -30,3 +33,8 @@ type GetAgentNodeResponse struct {
3033
BaseGetResponse
3134
*models.NodeState
3235
}
36+
37+
type GetAgentConfigResponse struct {
38+
BaseGetResponse
39+
Config types.Bacalhau `json:"config"`
40+
}

pkg/publicapi/client/v2/api_agent.go

+6
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ func (c *Agent) Node(ctx context.Context, req *apimodels.GetAgentNodeRequest) (*
3030
err := c.client.Get(ctx, "/api/v1/agent/node", req, &res)
3131
return &res, err
3232
}
33+
34+
func (c *Agent) Config(ctx context.Context) (*apimodels.GetAgentConfigResponse, error) {
35+
var res apimodels.GetAgentConfigResponse
36+
err := c.client.Get(ctx, "/api/v1/agent/config", &apimodels.BaseGetRequest{}, &res)
37+
return &res, err
38+
}

pkg/publicapi/endpoint/agent/endpoint.go

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package agent
22

33
import (
4+
"fmt"
45
"net/http"
56

67
"github.com/labstack/echo/v4"
@@ -113,7 +114,7 @@ func (e *Endpoint) debug(c echo.Context) error {
113114
return c.JSON(http.StatusOK, debugInfoMap)
114115
}
115116

116-
// debug godoc
117+
// config godoc
117118
//
118119
// @ID agent/config
119120
// @Summary Returns the current configuration of the node.
@@ -123,6 +124,17 @@ func (e *Endpoint) debug(c echo.Context) error {
123124
// @Failure 500 {object} string
124125
// @Router /api/v1/agent/config [get]
125126
func (e *Endpoint) config(c echo.Context) error {
126-
cfg := e.bacalhauConfig
127-
return c.JSON(http.StatusOK, cfg)
127+
cfg, err := e.bacalhauConfig.Copy()
128+
if err != nil {
129+
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("could not copy bacalhau config: %s", err))
130+
}
131+
if cfg.Compute.Auth.Token != "" {
132+
cfg.Compute.Auth.Token = "<redacted>"
133+
}
134+
if cfg.Orchestrator.Auth.Token != "" {
135+
cfg.Orchestrator.Auth.Token = "<redacted>"
136+
}
137+
return c.JSON(http.StatusOK, apimodels.GetAgentConfigResponse{
138+
Config: cfg,
139+
})
128140
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//go:build unit || !integration
2+
3+
package agent
4+
5+
import (
6+
"encoding/json"
7+
"net/http"
8+
"net/http/httptest"
9+
"testing"
10+
11+
"github.com/labstack/echo/v4"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/bacalhau-project/bacalhau/pkg/config/types"
16+
"github.com/bacalhau-project/bacalhau/pkg/publicapi/apimodels"
17+
)
18+
19+
// TestEndpointConfigRedactFields asserts that auth tokens in the config are redacted.
20+
func TestEndpointConfigRedactFields(t *testing.T) {
21+
router := echo.New()
22+
23+
// populate the fields that should be redacted with "secret" values.
24+
NewEndpoint(EndpointParams{
25+
Router: router,
26+
BacalhauConfig: types.Bacalhau{
27+
Orchestrator: types.Orchestrator{
28+
Auth: types.OrchestratorAuth{
29+
Token: "super-secret-orchestrator-token",
30+
},
31+
},
32+
Compute: types.Compute{
33+
Auth: types.ComputeAuth{
34+
Token: "super-secret-orchestrator-token",
35+
},
36+
},
37+
},
38+
})
39+
40+
req := httptest.NewRequest(http.MethodGet, "/api/v1/agent/config", nil)
41+
rr := httptest.NewRecorder()
42+
router.ServeHTTP(rr, req)
43+
44+
require.Equal(t, http.StatusOK, rr.Code)
45+
46+
// assert the secret values are not present.
47+
var payload apimodels.GetAgentConfigResponse
48+
err := json.NewDecoder(rr.Body).Decode(&payload)
49+
require.NoError(t, err)
50+
assert.Equal(t, payload.Config.Orchestrator.Auth.Token, "<redacted>")
51+
assert.Equal(t, payload.Config.Compute.Auth.Token, "<redacted>")
52+
}

test_integration/1_orchestrator_basic_config_suite_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ package test_integration
33
import (
44
"context"
55
"fmt"
6+
"strings"
7+
"testing"
8+
69
"github.com/google/uuid"
710
"github.com/stretchr/testify/suite"
811
"github.com/testcontainers/testcontainers-go/exec"
9-
"strings"
10-
"testing"
1112
)
1213

1314
type OrchestratorBasicConfigSuite struct {
@@ -65,7 +66,7 @@ func (s *OrchestratorBasicConfigSuite) TestOrchestratorNodeUpAndEnabled() {
6566
marshalledOutput, err := s.unmarshalJSONString(agentConfigOutput, JSONObject)
6667
s.Require().NoErrorf(err, "Error unmarshalling response: %q", err)
6768

68-
orchestratorEnabled := marshalledOutput.(map[string]interface{})["Orchestrator"].(map[string]interface{})["Enabled"].(bool)
69+
orchestratorEnabled := marshalledOutput.(map[string]interface{})["config"].(map[string]interface{})["Orchestrator"].(map[string]interface{})["Enabled"].(bool)
6970
s.Require().Truef(orchestratorEnabled, "Expected orchestrator to be enabled, got: %t", orchestratorEnabled)
7071
}
7172

test_integration/2_orchestrator_config_override_suite_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package test_integration
33
import (
44
"context"
55
"fmt"
6-
"github.com/google/uuid"
7-
"github.com/stretchr/testify/suite"
86
"strings"
97
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/suite"
1011
)
1112

1213
type OrchestratorConfigOverrideSuite struct {

test_integration/3_orchestrator_config_override_and_flag_suite_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package test_integration
33
import (
44
"context"
55
"fmt"
6-
"github.com/google/uuid"
7-
"github.com/stretchr/testify/suite"
86
"strings"
97
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/suite"
1011
)
1112

1213
type OrchestratorConfigOverrideAndFlagSuite struct {

test_integration/4_orchestrator_config_override_and_flag_and_config_flag_suite_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package test_integration
33
import (
44
"context"
55
"fmt"
6-
"github.com/google/uuid"
7-
"github.com/stretchr/testify/suite"
86
"strings"
97
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/suite"
1011
)
1112

1213
type OrchestratorConfigOverrideAndFlagAndConfigFlagSuite struct {
@@ -67,7 +68,7 @@ func (s *OrchestratorConfigOverrideAndFlagAndConfigFlagSuite) TestConfigOverride
6768
unmarshalledAgentOutput, err := s.unmarshalJSONString(agentConfigOutput, JSONObject)
6869
s.Require().NoErrorf(err, "Error unmarshalling response: %q", err)
6970

70-
webuiEnabled := unmarshalledAgentOutput.(map[string]interface{})["WebUI"].(map[string]interface{})["Enabled"].(bool)
71+
webuiEnabled := unmarshalledAgentOutput.(map[string]interface{})["config"].(map[string]interface{})["WebUI"].(map[string]interface{})["Enabled"].(bool)
7172
s.Require().Truef(webuiEnabled, "Expected orchestrator to be enabled, got: %t", webuiEnabled)
7273
}
7374

test_integration/5_orchestrator_no_config_suite_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package test_integration
22

33
import (
44
"context"
5-
"github.com/google/uuid"
6-
"github.com/stretchr/testify/suite"
75
"strings"
86
"testing"
7+
8+
"github.com/google/uuid"
9+
"github.com/stretchr/testify/suite"
910
)
1011

1112
type OrchestratorNoConfigSuite struct {
@@ -56,7 +57,7 @@ func (s *OrchestratorNoConfigSuite) TestStartingOrchestratorNodeWithConfigFile()
5657
unmarshalledOutput, err := s.unmarshalJSONString(agentConfigOutput, JSONObject)
5758
s.Require().NoErrorf(err, "Error unmarshalling response: %q", err)
5859

59-
unmarshalledOutputMap := unmarshalledOutput.(map[string]interface{})
60+
unmarshalledOutputMap := unmarshalledOutput.(map[string]interface{})["config"].(map[string]interface{})
6061

6162
orchestratorEnabled := unmarshalledOutputMap["Orchestrator"].(map[string]interface{})["Enabled"].(bool)
6263
s.Require().Truef(orchestratorEnabled, "Expected orchestrator to be enabled, got: %t", orchestratorEnabled)

test_integration/6_jobs_basic_runs_scenarios_suite_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package test_integration
22

33
import (
4-
"bacalhau/integration_tests/utils"
54
"context"
65
"fmt"
7-
"github.com/google/uuid"
8-
"github.com/stretchr/testify/suite"
96
"strings"
107
"testing"
118
"time"
9+
10+
"bacalhau/integration_tests/utils"
11+
"github.com/google/uuid"
12+
"github.com/stretchr/testify/suite"
1213
)
1314

1415
type JobsBasicRunsScenariosSuite struct {

0 commit comments

Comments
 (0)