Skip to content

Commit 933c703

Browse files
authored
[secrets] Add secrets download and upload (#1720)
## Summary Adds ``` devbox secrets download devbox secrets upload ``` Also, updated envsec dependency that has a simpler API. ## How was it tested? ``` devbox secrets download foo.json # modified foo.json devbox secrets upload foo.json ```
1 parent b97c7b8 commit 933c703

File tree

5 files changed

+113
-89
lines changed

5 files changed

+113
-89
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ require (
3939
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a
4040
github.com/wk8/go-ordered-map/v2 v2.1.8
4141
github.com/zealic/go2node v0.1.0
42-
go.jetpack.io/envsec v0.0.16-0.20240109233012-3e97a7fe973f
42+
go.jetpack.io/envsec v0.0.16-0.20240111222345-e1fd0e1204ca
4343
go.jetpack.io/pkg v0.0.0-20240108193620-a28b84329d15
4444
golang.org/x/exp v0.0.0-20240103183307-be819d1f06fc
4545
golang.org/x/mod v0.14.0

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,8 +378,8 @@ github.com/yuin/gopher-lua v0.0.0-20190514113301-1cd887cd7036/go.mod h1:gqRgreBU
378378
github.com/zaffka/mongodb-boltdb-mock v0.0.0-20221014194232-b4bb03fbe3a0/go.mod h1:GsDD1qsG+86MeeCG7ndi6Ei3iGthKL3wQ7PTFigDfNY=
379379
github.com/zealic/go2node v0.1.0 h1:ofxpve08cmLJBwFdI0lPCk9jfwGWOSD+s6216x0oAaA=
380380
github.com/zealic/go2node v0.1.0/go.mod h1:GrkFr+HctXwP7vzcU9RsgtAeJjTQ6Ud0IPCQAqpTfBg=
381-
go.jetpack.io/envsec v0.0.16-0.20240109233012-3e97a7fe973f h1:ELgyeQw+eZn0BSSlhLnBkC7wTrYbluTsYx6vPa0YTds=
382-
go.jetpack.io/envsec v0.0.16-0.20240109233012-3e97a7fe973f/go.mod h1:8U0fUaXMaoNRqz8J7VaV8kP7N7hayJ9tqOd9OS0epEQ=
381+
go.jetpack.io/envsec v0.0.16-0.20240111222345-e1fd0e1204ca h1:7Ocnr+mVZTCG8MHlcAdW5d8ir7eqZTmD8S/aEskjXWk=
382+
go.jetpack.io/envsec v0.0.16-0.20240111222345-e1fd0e1204ca/go.mod h1:kCgRGNSHU5AgQXQGUenohPPDoUd87SRL00uOzQsa+Q8=
383383
go.jetpack.io/pkg v0.0.0-20240108193620-a28b84329d15 h1:ztX3CydpNKLePPMRmWgdi4HcxE/AXY6HViOJOmmYNiA=
384384
go.jetpack.io/pkg v0.0.0-20240108193620-a28b84329d15/go.mod h1:kGUL8aZ7ddvoGro0AQxXos9GKn5Qw0J18qW7d5FP4Ws=
385385
go.jetpack.io/typeid v1.0.0 h1:8gQ+iYGdyiQ0Pr40ydSB/PzMOIwlXX5DTojp1CBeSPQ=

internal/boxcli/secrets.go

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ type secretsFlags struct {
1616
config configFlags
1717
}
1818

19+
func (f *secretsFlags) envsec(cmd *cobra.Command) (*envsec.Envsec, error) {
20+
box, err := devbox.Open(&devopt.Opts{
21+
Dir: f.config.path,
22+
Environment: f.config.environment,
23+
Stderr: cmd.ErrOrStderr(),
24+
})
25+
if err != nil {
26+
return nil, errors.WithStack(err)
27+
}
28+
29+
return box.Secrets(cmd.Context())
30+
}
31+
1932
type secretsInitCmdFlags struct {
2033
force bool
2134
}
@@ -25,6 +38,14 @@ type secretsListFlags struct {
2538
format string
2639
}
2740

41+
type secretsDownloadFlags struct {
42+
format string
43+
}
44+
45+
type secretsUploadFlags struct {
46+
format string
47+
}
48+
2849
func secretsCmd() *cobra.Command {
2950
flags := &secretsFlags{}
3051
cmd := &cobra.Command{
@@ -33,10 +54,12 @@ func secretsCmd() *cobra.Command {
3354
Short: "Interact with devbox secrets in jetpack cloud.",
3455
PersistentPreRunE: ensureNixInstalled,
3556
}
57+
cmd.AddCommand(secretsDownloadCmd(flags))
3658
cmd.AddCommand(secretsInitCmd(flags))
3759
cmd.AddCommand(secretsListCmd(flags))
3860
cmd.AddCommand(secretsRemoveCmd(flags))
3961
cmd.AddCommand(secretsSetCmd(flags))
62+
cmd.AddCommand(secretsUploadCmd(flags))
4063
cmd.Hidden = true
4164

4265
flags.config.registerPersistent(cmd)
@@ -76,27 +99,12 @@ func secretsSetCmd(flags *secretsFlags) *cobra.Command {
7699
return envsec.ValidateSetArgs(args)
77100
},
78101
RunE: func(cmd *cobra.Command, args []string) error {
79-
ctx := cmd.Context()
80-
box, err := devbox.Open(&devopt.Opts{
81-
Dir: flags.config.path,
82-
Environment: flags.config.environment,
83-
Stderr: cmd.ErrOrStderr(),
84-
})
85-
if err != nil {
86-
return errors.WithStack(err)
87-
}
88-
89-
secrets, err := box.Secrets(ctx)
90-
if err != nil {
91-
return errors.WithStack(err)
92-
}
93-
94-
envID, err := secrets.EnvID()
102+
secrets, err := flags.envsec(cmd)
95103
if err != nil {
96104
return errors.WithStack(err)
97105
}
98106

99-
return secrets.SetFromArgs(ctx, envID, args)
107+
return secrets.SetFromArgs(cmd.Context(), args)
100108
},
101109
}
102110
}
@@ -108,27 +116,12 @@ func secretsRemoveCmd(flags *secretsFlags) *cobra.Command {
108116
Aliases: []string{"rm"},
109117
Args: cobra.MinimumNArgs(1),
110118
RunE: func(cmd *cobra.Command, args []string) error {
111-
ctx := cmd.Context()
112-
box, err := devbox.Open(&devopt.Opts{
113-
Dir: flags.config.path,
114-
Environment: flags.config.environment,
115-
Stderr: cmd.ErrOrStderr(),
116-
})
117-
if err != nil {
118-
return errors.WithStack(err)
119-
}
120-
121-
secrets, err := box.Secrets(ctx)
122-
if err != nil {
123-
return errors.WithStack(err)
124-
}
125-
126-
envID, err := secrets.EnvID()
119+
secrets, err := flags.envsec(cmd)
127120
if err != nil {
128121
return errors.WithStack(err)
129122
}
130123

131-
return secrets.DeleteAll(ctx, envID, args...)
124+
return secrets.DeleteAll(cmd.Context(), args...)
132125
},
133126
}
134127
}
@@ -141,33 +134,18 @@ func secretsListCmd(commonFlags *secretsFlags) *cobra.Command {
141134
Short: "List all secrets",
142135
Args: cobra.ExactArgs(0),
143136
RunE: func(cmd *cobra.Command, args []string) error {
144-
ctx := cmd.Context()
145-
box, err := devbox.Open(&devopt.Opts{
146-
Dir: commonFlags.config.path,
147-
Environment: commonFlags.config.environment,
148-
Stderr: cmd.ErrOrStderr(),
149-
})
137+
secrets, err := commonFlags.envsec(cmd)
150138
if err != nil {
151139
return errors.WithStack(err)
152140
}
153141

154-
secrets, err := box.Secrets(ctx)
155-
if err != nil {
156-
return errors.WithStack(err)
157-
}
158-
159-
envID, err := secrets.EnvID()
160-
if err != nil {
161-
return errors.WithStack(err)
162-
}
163-
164-
vars, err := secrets.List(ctx, envID)
142+
vars, err := secrets.List(cmd.Context())
165143
if err != nil {
166144
return err
167145
}
168146

169-
return envsec.PrintEnvVars(
170-
vars, cmd.OutOrStdout(), flags.show, flags.format)
147+
return envsec.PrintEnvVar(
148+
cmd.OutOrStdout(), secrets.EnvID, vars, flags.show, flags.format)
171149
},
172150
}
173151

@@ -188,6 +166,60 @@ func secretsListCmd(commonFlags *secretsFlags) *cobra.Command {
188166
return cmd
189167
}
190168

169+
func secretsDownloadCmd(commonFlags *secretsFlags) *cobra.Command {
170+
flags := secretsDownloadFlags{}
171+
command := &cobra.Command{
172+
Use: "download <file1>",
173+
Short: "Download environment variables into the specified file",
174+
Args: cobra.ExactArgs(1),
175+
PreRunE: func(cmd *cobra.Command, args []string) error {
176+
return envsec.ValidateFormat(flags.format)
177+
},
178+
RunE: func(cmd *cobra.Command, args []string) error {
179+
secrets, err := commonFlags.envsec(cmd)
180+
if err != nil {
181+
return errors.WithStack(err)
182+
}
183+
if err != nil {
184+
return errors.WithStack(err)
185+
}
186+
return secrets.Download(cmd.Context(), args[0], flags.format)
187+
},
188+
}
189+
190+
command.Flags().StringVarP(
191+
&flags.format, "format", "f", "", "file format: dotenv or json")
192+
193+
return command
194+
}
195+
196+
func secretsUploadCmd(commonFlags *secretsFlags) *cobra.Command {
197+
flags := &secretsUploadFlags{}
198+
command := &cobra.Command{
199+
Use: "upload <file1> [<fileN>]...",
200+
Short: "Upload variables defined in one or more .env files.",
201+
Args: cobra.MinimumNArgs(1),
202+
PreRunE: func(cmd *cobra.Command, args []string) error {
203+
return envsec.ValidateFormat(flags.format)
204+
},
205+
RunE: func(cmd *cobra.Command, paths []string) error {
206+
secrets, err := commonFlags.envsec(cmd)
207+
if err != nil {
208+
return errors.WithStack(err)
209+
}
210+
if err != nil {
211+
return errors.WithStack(err)
212+
}
213+
return secrets.Upload(cmd.Context(), paths, flags.format)
214+
},
215+
}
216+
217+
command.Flags().StringVarP(
218+
&flags.format, "format", "f", "", "File format: dotenv or json")
219+
220+
return command
221+
}
222+
191223
func secretsInitFunc(
192224
cmd *cobra.Command,
193225
flags secretsInitCmdFlags,
@@ -201,10 +233,10 @@ func secretsInitFunc(
201233
if err != nil {
202234
return errors.WithStack(err)
203235
}
204-
secrets, err := box.Secrets(ctx)
205-
if err != nil {
206-
return errors.WithStack(err)
207-
}
236+
237+
// devbox.Secrets() by default assumes project is initialized (and shows
238+
// error if not). So we use UninitializedSecrets() here instead.
239+
secrets := box.UninitializedSecrets(ctx)
208240

209241
if _, err := secrets.ProjectConfig(); err == nil &&
210242
box.Config().EnvFrom != "jetpack-cloud" {

internal/devbox/devbox.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,20 +1111,16 @@ func (d *Devbox) configEnvs(
11111111
if err != nil {
11121112
return nil, err
11131113
}
1114-
envID, err := secrets.EnvID()
1115-
if err != nil {
1116-
return nil, err
1117-
}
11181114

1119-
cloudSecrets, err := secrets.List(ctx, envID)
1115+
cloudSecrets, err := secrets.List(ctx)
11201116
if err != nil {
11211117
ux.Fwarning(
11221118
os.Stderr,
11231119
"Error reading secrets from jetpack cloud: %s\n\n",
11241120
err,
11251121
)
11261122
} else {
1127-
for _, secret := range cloudSecrets[envID] {
1123+
for _, secret := range cloudSecrets {
11281124
env[secret.Name] = secret.Value
11291125
}
11301126
}

internal/devbox/secrets.go

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,37 @@ import (
99
"go.jetpack.io/pkg/envvar"
1010
)
1111

12-
type secrets struct {
13-
envsec.Envsec
14-
EnvName string
15-
}
16-
17-
func (d *Devbox) Secrets(ctx context.Context) (*secrets, error) {
18-
envsecInstance := envsec.Envsec{
12+
func (d *Devbox) UninitializedSecrets(ctx context.Context) *envsec.Envsec {
13+
return &envsec.Envsec{
1914
APIHost: build.JetpackAPIHost(),
2015
Auth: envsec.AuthConfig{
2116
ClientID: envvar.Get("ENVSEC_CLIENT_ID", build.ClientID()),
2217
Issuer: envvar.Get("ENVSEC_ISSUER", build.Issuer()),
2318
},
2419
IsDev: build.IsDev,
2520
Stderr: d.stderr,
21+
Store: &jetstore.JetpackAPIStore{},
2622
WorkingDir: d.ProjectDir(),
2723
}
28-
29-
store := &jetstore.JetpackAPIStore{}
30-
if err := envsecInstance.SetStore(ctx, store); err != nil {
31-
return nil, err
32-
}
33-
return &secrets{
34-
Envsec: envsecInstance,
35-
EnvName: d.environment,
36-
}, nil
3724
}
3825

39-
func (s *secrets) EnvID() (envsec.EnvID, error) {
40-
project, err := s.ProjectConfig()
26+
func (d *Devbox) Secrets(ctx context.Context) (*envsec.Envsec, error) {
27+
envsecInstance := d.UninitializedSecrets(ctx)
28+
29+
project, err := envsecInstance.ProjectConfig()
4130
if err != nil {
42-
return envsec.EnvID{}, err
31+
return nil, err
4332
}
44-
return envsec.EnvID{
45-
EnvName: s.EnvName,
46-
ProjectID: project.ProjectID.String(),
33+
34+
envsecInstance.EnvID = envsec.EnvID{
35+
EnvName: d.environment,
4736
OrgID: project.OrgID.String(),
48-
}, nil
37+
ProjectID: project.ProjectID.String(),
38+
}
39+
40+
if _, err := envsecInstance.InitForUser(ctx); err != nil {
41+
return nil, err
42+
}
43+
44+
return envsecInstance, nil
4945
}

0 commit comments

Comments
 (0)