fix(agent/agentcontainers): treat customizations as array (#18357)

This PR fixes a mistake from the previous PR
https://github.com/coder/coder/pull/18342. Merged configuration results
in the customization being an array not an object.

This PR also moves `displayApps` from being an array to being an object,
like the terraform provider has.
This commit is contained in:
Danielle Maywood
2025-06-13 14:48:12 +01:00
committed by GitHub
parent 949ab4b2f6
commit 0ef62264ba
5 changed files with 97 additions and 21 deletions

View File

@ -1099,14 +1099,33 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
directory = DevcontainerDefaultContainerWorkspaceFolder
}
var displayApps []codersdk.DisplayApp
displayAppsMap := map[codersdk.DisplayApp]bool{
// NOTE(DanielleMaywood):
// We use the same defaults here as set in terraform-provider-coder.
// https://github.com/coder/terraform-provider-coder/blob/c1c33f6d556532e75662c0ca373ed8fdea220eb5/provider/agent.go#L38-L51
codersdk.DisplayAppVSCodeDesktop: true,
codersdk.DisplayAppVSCodeInsiders: false,
codersdk.DisplayAppWebTerminal: true,
codersdk.DisplayAppSSH: true,
codersdk.DisplayAppPortForward: true,
}
if config, err := api.dccli.ReadConfig(ctx, dc.WorkspaceFolder, dc.ConfigPath); err != nil {
api.logger.Error(ctx, "unable to read devcontainer config", slog.Error(err))
} else {
coderCustomization := config.MergedConfiguration.Customizations.Coder
if coderCustomization != nil {
displayApps = coderCustomization.DisplayApps
for _, customization := range coderCustomization {
for app, enabled := range customization.DisplayApps {
displayAppsMap[app] = enabled
}
}
}
displayApps := make([]codersdk.DisplayApp, 0, len(displayAppsMap))
for app, enabled := range displayAppsMap {
if enabled {
displayApps = append(displayApps, app)
}
}

View File

@ -1450,7 +1450,7 @@ func TestAPI(t *testing.T) {
tests := []struct {
name string
customization *agentcontainers.CoderCustomization
customization []agentcontainers.CoderCustomization
afterCreate func(t *testing.T, subAgent agentcontainers.SubAgent)
}{
{
@ -1458,19 +1458,68 @@ func TestAPI(t *testing.T) {
customization: nil,
},
{
name: "WithDisplayApps",
customization: &agentcontainers.CoderCustomization{
DisplayApps: []codersdk.DisplayApp{
codersdk.DisplayAppSSH,
codersdk.DisplayAppWebTerminal,
codersdk.DisplayAppVSCodeInsiders,
name: "WithDefaultDisplayApps",
customization: []agentcontainers.CoderCustomization{},
afterCreate: func(t *testing.T, subAgent agentcontainers.SubAgent) {
require.Len(t, subAgent.DisplayApps, 4)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppVSCodeDesktop)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppWebTerminal)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppSSH)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppPortForward)
},
},
{
name: "WithAllDisplayApps",
customization: []agentcontainers.CoderCustomization{
{
DisplayApps: map[codersdk.DisplayApp]bool{
codersdk.DisplayAppSSH: true,
codersdk.DisplayAppWebTerminal: true,
codersdk.DisplayAppVSCodeDesktop: true,
codersdk.DisplayAppVSCodeInsiders: true,
codersdk.DisplayAppPortForward: true,
},
},
},
afterCreate: func(t *testing.T, subAgent agentcontainers.SubAgent) {
require.Len(t, subAgent.DisplayApps, 3)
assert.Equal(t, codersdk.DisplayAppSSH, subAgent.DisplayApps[0])
assert.Equal(t, codersdk.DisplayAppWebTerminal, subAgent.DisplayApps[1])
assert.Equal(t, codersdk.DisplayAppVSCodeInsiders, subAgent.DisplayApps[2])
require.Len(t, subAgent.DisplayApps, 5)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppSSH)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppWebTerminal)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppVSCodeDesktop)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppVSCodeInsiders)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppPortForward)
},
},
{
name: "WithSomeDisplayAppsDisabled",
customization: []agentcontainers.CoderCustomization{
{
DisplayApps: map[codersdk.DisplayApp]bool{
codersdk.DisplayAppSSH: false,
codersdk.DisplayAppWebTerminal: false,
codersdk.DisplayAppVSCodeInsiders: false,
// We'll enable vscode in this layer, and disable
// it in the next layer to ensure a layer can be
// disabled.
codersdk.DisplayAppVSCodeDesktop: true,
// We disable port-forward in this layer, and
// then re-enable it in the next layer to ensure
// that behavior works.
codersdk.DisplayAppPortForward: false,
},
},
{
DisplayApps: map[codersdk.DisplayApp]bool{
codersdk.DisplayAppVSCodeDesktop: false,
codersdk.DisplayAppPortForward: true,
},
},
},
afterCreate: func(t *testing.T, subAgent agentcontainers.SubAgent) {
require.Len(t, subAgent.DisplayApps, 1)
assert.Contains(t, subAgent.DisplayApps, codersdk.DisplayAppPortForward)
},
},
}

View File

@ -27,11 +27,11 @@ type DevcontainerConfiguration struct {
}
type DevcontainerCustomizations struct {
Coder *CoderCustomization `json:"coder,omitempty"`
Coder []CoderCustomization `json:"coder,omitempty"`
}
type CoderCustomization struct {
DisplayApps []codersdk.DisplayApp `json:"displayApps,omitempty"`
DisplayApps map[codersdk.DisplayApp]bool `json:"displayApps,omitempty"`
}
// DevcontainerCLI is an interface for the devcontainer CLI.

View File

@ -258,10 +258,18 @@ func TestDevcontainerCLI_ArgsAndParsing(t *testing.T) {
wantConfig: agentcontainers.DevcontainerConfig{
MergedConfiguration: agentcontainers.DevcontainerConfiguration{
Customizations: agentcontainers.DevcontainerCustomizations{
Coder: &agentcontainers.CoderCustomization{
DisplayApps: []codersdk.DisplayApp{
codersdk.DisplayAppVSCodeDesktop,
codersdk.DisplayAppWebTerminal,
Coder: []agentcontainers.CoderCustomization{
{
DisplayApps: map[codersdk.DisplayApp]bool{
codersdk.DisplayAppVSCodeDesktop: true,
codersdk.DisplayAppWebTerminal: true,
},
},
{
DisplayApps: map[codersdk.DisplayApp]bool{
codersdk.DisplayAppVSCodeInsiders: true,
codersdk.DisplayAppWebTerminal: false,
},
},
},
},

View File

@ -5,4 +5,4 @@
{"type":"stop","level":2,"timestamp":1749557820039,"text":"Run: docker ps -q -a --filter label=devcontainer.local_folder=/home/coder/coder --filter label=devcontainer.config_file=/home/coder/coder/.devcontainer/devcontainer.json","startTimestamp":1749557820023}
{"type":"start","level":2,"timestamp":1749557820039,"text":"Run: docker ps -q -a --filter label=devcontainer.local_folder=/home/coder/coder"}
{"type":"stop","level":2,"timestamp":1749557820054,"text":"Run: docker ps -q -a --filter label=devcontainer.local_folder=/home/coder/coder","startTimestamp":1749557820039}
{"mergedConfiguration":{"customizations":{"coder":{"displayApps":["vscode", "web_terminal"]}}}}
{"mergedConfiguration":{"customizations":{"coder":[{"displayApps":{"vscode":true,"web_terminal":true}},{"displayApps":{"vscode_insiders":true,"web_terminal":false}}]}}}