Skip to content

feat: basic implementation of secrets feature [DO NOT MERGE] #18775

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

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
88d5eec
feat: basic implementation of secrets feature
evgeniy-scherbina Jul 7, 2025
8fdc61b
test: fix migration tests
evgeniy-scherbina Jul 8, 2025
c6249d3
test: fix rbac tests
evgeniy-scherbina Jul 8, 2025
17326fd
Merge remote-tracking branch 'origin/main' into yevhenii/secrets-prot…
evgeniy-scherbina Jul 8, 2025
b5c904a
feat: add get-user-secret db query
evgeniy-scherbina Jul 8, 2025
122387f
feat: add list-user-secrets db query
evgeniy-scherbina Jul 8, 2025
147b22d
test: fix CI and dbmem
evgeniy-scherbina Jul 8, 2025
dc046ae
temporary commit
evgeniy-scherbina Jul 14, 2025
78bfa24
feat: implement API for creating & listing secrets
evgeniy-scherbina Jul 14, 2025
e416afe
Merge remote-tracking branch 'origin/main' into yevhenii/secrets-prot…
evgeniy-scherbina Jul 14, 2025
887f914
fix: delete dbmem after merge
evgeniy-scherbina Jul 14, 2025
991db6d
Merge remote-tracking branch 'origin/main' into yevhenii/secrets-prot…
evgeniy-scherbina Jul 15, 2025
6db3754
fix: migration numbers
evgeniy-scherbina Jul 15, 2025
f0c3a5e
ci: fix doc tests
evgeniy-scherbina Jul 15, 2025
137fb71
ci: fix doc tests
evgeniy-scherbina Jul 15, 2025
b580b5d
test: add TestUserSecrets test
evgeniy-scherbina Jul 15, 2025
b40ec25
test: improve TestUserSecrets test
evgeniy-scherbina Jul 15, 2025
ac8b0b6
refactor: add comments
evgeniy-scherbina Jul 15, 2025
b8712a6
feat: implement create command
evgeniy-scherbina Jul 16, 2025
1a716d2
feat: implement list command
evgeniy-scherbina Jul 16, 2025
e739809
feat: implement get command
evgeniy-scherbina Jul 16, 2025
e288818
feat: implement get --with-value command
evgeniy-scherbina Jul 16, 2025
dd2883e
make gen/golden-files
evgeniy-scherbina Jul 16, 2025
91b16de
ci: fix ci
evgeniy-scherbina Jul 17, 2025
39b0e41
feat: add params for auto-injection
evgeniy-scherbina Jul 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,965 changes: 1,034 additions & 931 deletions agent/proto/agent.pb.go

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions agent/proto/agent.proto
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ message Manifest {
repeated WorkspaceApp apps = 11;
repeated WorkspaceAgentMetadata.Description metadata = 12;
repeated WorkspaceAgentDevcontainer devcontainers = 17;

map<string,Secret> user_secrets = 19;
}

message Secret {
string name = 1;
string env_name = 2;
string file_path = 3;
}

message WorkspaceAgentDevcontainer {
Expand Down
1 change: 1 addition & 0 deletions cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ func (r *RootCmd) CoreSubcommands() []*serpent.Command {
r.portForward(),
r.publickey(),
r.resetPassword(),
r.secrets(),
r.state(),
r.templates(),
r.tokens(),
Expand Down
1 change: 1 addition & 0 deletions cli/testdata/coder_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ SUBCOMMANDS:
password
restart Restart a workspace
schedule Schedule automated start and stop times for workspaces
secrets Manage your user secrets
server Start a Coder server
show Display details of a workspace's resources and agents
speedtest Run upload and download tests from your machine to a
Expand Down
14 changes: 14 additions & 0 deletions cli/testdata/coder_secrets_--help.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
coder v0.0.0-devel

USAGE:
coder secrets

Manage your user secrets

SUBCOMMANDS:
create Create a new user secret
get Get user secret
list List user secrets

———
Run `coder --help` for a list of global options.
16 changes: 16 additions & 0 deletions cli/testdata/coder_secrets_create_--help.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
coder v0.0.0-devel

USAGE:
coder secrets create [flags] <name>

Create a new user secret

OPTIONS:
--description string
Description of the secret.

--value string
Value of the secret (required).

———
Run `coder --help` for a list of global options.
13 changes: 13 additions & 0 deletions cli/testdata/coder_secrets_get_--help.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
coder v0.0.0-devel

USAGE:
coder secrets get [flags] <name>

Get user secret

OPTIONS:
--with-value bool
Display value of the secret.

———
Run `coder --help` for a list of global options.
9 changes: 9 additions & 0 deletions cli/testdata/coder_secrets_list_--help.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
coder v0.0.0-devel

USAGE:
coder secrets list

List user secrets

———
Run `coder --help` for a list of global options.
150 changes: 150 additions & 0 deletions cli/user_secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cli

import (
"fmt"

"github.com/coder/coder/v2/codersdk"
"github.com/coder/serpent"
)

func (r *RootCmd) secrets() *serpent.Command {
return &serpent.Command{
Use: "secrets",
Short: "Manage your user secrets",
Handler: func(inv *serpent.Invocation) error {
return inv.Command.HelpHandler(inv)
},
Children: []*serpent.Command{
r.secretCreate(),
r.secretList(),
r.secretGet(),
},
}
}

func (r *RootCmd) secretCreate() *serpent.Command {
client := new(codersdk.Client)
var value string
var description string
var envName string
var filePath string
cmd := &serpent.Command{
Use: "create <name>",
Short: "Create a new user secret",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
name := inv.Args[0]
if value == "" {
return fmt.Errorf("--value is required")
}
secret, err := client.CreateUserSecret(inv.Context(), codersdk.CreateUserSecretRequest{
Name: name,
Value: value,
Description: description,
EnvName: envName,
FilePath: filePath,
})
if err != nil {
return err
}
fmt.Fprintf(inv.Stdout, "Created user secret %q (ID: %s)\n", secret.Name, secret.ID)

Check failure on line 53 in cli/user_secrets.go

View workflow job for this annotation

GitHub Actions / lint

unhandled-error: Unhandled error in call to function fmt.Fprintf (revive)
return nil
},
}
cmd.Options = serpent.OptionSet{
{
Flag: "value",
Description: "Value of the secret (required).",
Value: serpent.StringOf(&value),
},
{
Flag: "description",
Description: "Description of the secret.",
Value: serpent.StringOf(&description),
},
{
Flag: "env_name",
Description: "Environment variable name of the secret.",
Value: serpent.StringOf(&envName),
},
{
Flag: "file_path",
Description: "File path of the secret.",
Value: serpent.StringOf(&filePath),
},
}
return cmd
}

func (r *RootCmd) secretList() *serpent.Command {
client := new(codersdk.Client)
cmd := &serpent.Command{
Use: "list",
Short: "List user secrets",
Middleware: serpent.Chain(
serpent.RequireNArgs(0),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
secretList, err := client.ListUserSecrets(inv.Context())
if err != nil {
return err
}
fmt.Fprintf(inv.Stdout, "ID | Name | Description | EnvName | FilePath\n")

Check failure on line 96 in cli/user_secrets.go

View workflow job for this annotation

GitHub Actions / lint

unhandled-error: Unhandled error in call to function fmt.Fprintf (revive)
for _, secret := range secretList.Secrets {
fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v - %v\n",

Check failure on line 98 in cli/user_secrets.go

View workflow job for this annotation

GitHub Actions / lint

unhandled-error: Unhandled error in call to function fmt.Fprintf (revive)
secret.ID, secret.Name, secret.Description, secret.EnvName, secret.FilePath)
}
return nil
},
}
cmd.Options = serpent.OptionSet{}
return cmd
}

func (r *RootCmd) secretGet() *serpent.Command {
client := new(codersdk.Client)
var withValue bool
cmd := &serpent.Command{
Use: "get <name>",
Short: "Get user secret",
Middleware: serpent.Chain(
serpent.RequireNArgs(1),
r.InitClient(client),
),
Handler: func(inv *serpent.Invocation) error {
secretName := inv.Args[0]
secret, err := client.GetUserSecret(inv.Context(), secretName)
if err != nil {
return err
}

userSecretValue := codersdk.UserSecretValue{
Value: "hidden",
}
if withValue {
userSecretValue, err = client.GetUserSecretValue(inv.Context(), secretName)
if err != nil {
return err
}
}
value := userSecretValue.Value

fmt.Fprintf(inv.Stdout, "ID | Name | Description | Value | EnvName | FilePath\n")

Check failure on line 136 in cli/user_secrets.go

View workflow job for this annotation

GitHub Actions / lint

unhandled-error: Unhandled error in call to function fmt.Fprintf (revive)
fmt.Fprintf(inv.Stdout, "%v - %v - %v - %v - %v - %v\n",

Check failure on line 137 in cli/user_secrets.go

View workflow job for this annotation

GitHub Actions / lint

unhandled-error: Unhandled error in call to function fmt.Fprintf (revive)
secret.ID, secret.Name, secret.Description, value, secret.EnvName, secret.FilePath)
return nil
},
}
cmd.Options = serpent.OptionSet{
{
Flag: "with-value",
Description: "Display value of the secret.",
Value: serpent.BoolOf(&withValue),
},
}
return cmd
}
Loading
Loading