5
5
package cmd
6
6
7
7
import (
8
+ "context"
9
+ "errors"
8
10
"fmt"
9
11
"os"
10
12
"strings"
13
+ "sync"
14
+ "time"
11
15
12
16
"github.com/spf13/cobra"
17
+ "golang.org/x/xerrors"
18
+ "google.golang.org/grpc"
13
19
14
20
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/theialib"
21
+ serverapi "github.com/gitpod-io/gitpod/gitpod-protocol"
22
+ supervisor "github.com/gitpod-io/gitpod/supervisor/api"
15
23
)
16
24
17
25
var exportEnvs = false
@@ -42,82 +50,243 @@ delete environment variables with a repository pattern of */foo, foo/* or */*.
42
50
` ,
43
51
Args : cobra .ArbitraryArgs ,
44
52
Run : func (cmd * cobra.Command , args []string ) {
45
- fail := func (msg string ) {
46
- fmt .Fprintln (os .Stderr , msg )
47
- os .Exit (- 1 )
53
+ if len (args ) > 0 {
54
+ if unsetEnvs {
55
+ deleteEnvs (args )
56
+ return
57
+ }
58
+
59
+ setEnvs (args )
60
+ } else {
61
+ getEnvs ()
48
62
}
63
+ },
64
+ }
49
65
50
- service , err := theialib .NewServiceFromEnv ()
66
+ type connectToServerResult struct {
67
+ repositoryPattern string
68
+ client * serverapi.APIoverJSONRPC
69
+ }
70
+
71
+ func connectToServer (ctx context.Context ) (* connectToServerResult , error ) {
72
+ supervisorAddr := os .Getenv ("SUPERVISOR_ADDR" )
73
+ if supervisorAddr == "" {
74
+ supervisorAddr = "localhost:22999"
75
+ }
76
+ supervisorConn , err := grpc .Dial (supervisorAddr , grpc .WithInsecure ())
77
+ if err != nil {
78
+ return nil , xerrors .Errorf ("failed connecting to supervisor: %w" , err )
79
+ }
80
+ wsinfo , err := supervisor .NewInfoServiceClient (supervisorConn ).WorkspaceInfo (ctx , & supervisor.WorkspaceInfoRequest {})
81
+ if err != nil {
82
+ return nil , xerrors .Errorf ("failed getting workspace info from supervisor: %w" , err )
83
+ }
84
+ if wsinfo .Repository == nil {
85
+ return nil , xerrors .New ("workspace info is missing repository" )
86
+ }
87
+ if wsinfo .Repository .Owner == "" {
88
+ return nil , xerrors .New ("repository info is missing owner" )
89
+ }
90
+ if wsinfo .Repository .Name == "" {
91
+ return nil , xerrors .New ("repository info is missing name" )
92
+ }
93
+ repositoryPattern := wsinfo .Repository .Owner + "/" + wsinfo .Repository .Name
94
+ clientToken , err := supervisor .NewTokenServiceClient (supervisorConn ).GetToken (ctx , & supervisor.GetTokenRequest {
95
+ Host : wsinfo .GitpodApi .Host ,
96
+ Kind : "gitpod" ,
97
+ Scope : []string {
98
+ "function:getEnvVars" ,
99
+ "function:setEnvVar" ,
100
+ "function:deleteEnvVar" ,
101
+ "resource:envVar::" + repositoryPattern + "::create/get/update/delete" ,
102
+ },
103
+ })
104
+ if err != nil {
105
+ return nil , xerrors .Errorf ("failed getting token from supervisor: %w" , err )
106
+ }
107
+ client , err := serverapi .ConnectToServer (wsinfo .GitpodApi .Endpoint , serverapi.ConnectToServerOpts {Token : clientToken .Token , Context : ctx })
108
+ if err != nil {
109
+ return nil , xerrors .Errorf ("failed connecting to server: %w" , err )
110
+ }
111
+ return & connectToServerResult {repositoryPattern , client }, nil
112
+ }
113
+
114
+ func getEnvs () {
115
+ if ! isTheiaIDE () {
116
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Minute )
117
+ defer cancel ()
118
+ result , err := connectToServer (ctx )
51
119
if err != nil {
52
120
fail (err .Error ())
53
121
}
54
122
55
- setEnvs := func () {
56
- vars := make ([]theialib.EnvironmentVariable , len (args ))
57
- for i , arg := range args {
58
- kv := strings .Split (arg , "=" )
59
- if len (kv ) != 2 {
60
- fail (fmt .Sprintf ("%s has no value (correct format is %s=some_value)" , arg , arg ))
61
- }
123
+ vars , err := result .client .GetEnvVars (ctx )
124
+ if err != nil {
125
+ fail ("failed to fetch env vars from server: " + err .Error ())
126
+ }
62
127
63
- key := strings .TrimSpace (kv [0 ])
64
- if key == "" {
65
- fail (fmt .Sprintf ("variable must have a name" ))
66
- }
67
- // Do not trim value - the user might want whitespace here
68
- // Also do not check if the value is empty, as an empty value means we want to delete the variable
69
- val := kv [1 ]
70
- if val == "" {
71
- fail (fmt .Sprintf ("variable must have a value; use -u to unset a variable" ))
72
- }
128
+ for _ , v := range vars {
129
+ printVar (v , exportEnvs )
130
+ }
131
+ return
132
+ }
73
133
74
- vars [i ] = theialib.EnvironmentVariable {Name : key , Value : val }
75
- }
134
+ service , err := theialib .NewServiceFromEnv ()
135
+ if err != nil {
136
+ fail (err .Error ())
137
+ }
76
138
77
- _ , err = service .SetEnvVar (theialib.SetEnvvarRequest { Variables : vars })
78
- if err != nil {
79
- fail (fmt .Sprintf ("cannot set environment variables: %v" , err ))
80
- }
139
+ vars , err : = service .GetEnvVars (theialib.GetEnvvarsRequest { })
140
+ if err != nil {
141
+ fail (fmt .Sprintf ("cannot get environment variables: %v" , err ))
142
+ }
81
143
82
- for _ , v := range vars {
83
- printVar (v , exportEnvs )
84
- }
144
+ for _ , v := range vars .Variables {
145
+ printVarFromTheia (v , exportEnvs )
146
+ }
147
+ }
148
+
149
+ func setEnvs (args []string ) {
150
+ if ! isTheiaIDE () {
151
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Minute )
152
+ defer cancel ()
153
+ result , err := connectToServer (ctx )
154
+ if err != nil {
155
+ fail (err .Error ())
85
156
}
86
- getEnvs := func () {
87
- vars , err := service .GetEnvVars (theialib.GetEnvvarsRequest {})
88
- if err != nil {
89
- fail (fmt .Sprintf ("cannot get environment variables: %v" , err ))
157
+
158
+ vars := make ([]* serverapi.UserEnvVarValue , len (args ))
159
+ for i , arg := range args {
160
+ kv := strings .Split (arg , "=" )
161
+ if len (kv ) != 2 {
162
+ fail (fmt .Sprintf ("%s has no value (correct format is %s=some_value)" , arg , arg ))
90
163
}
91
164
92
- for _ , v := range vars .Variables {
93
- printVar (v , exportEnvs )
165
+ key := strings .TrimSpace (kv [0 ])
166
+ if key == "" {
167
+ fail (fmt .Sprintf ("variable must have a name" ))
94
168
}
95
- }
96
- doUnsetEnvs := func () {
97
- resp , err := service . DeleteEnvVar (theialib. DeleteEnvvarRequest { Variables : args })
98
- if err != nil {
99
- fail (fmt .Sprintf ("cannot unset environment variables: %v" , err ))
169
+ // Do not trim value - the user might want whitespace here
170
+ // Also do not check if the value is empty, as an empty value means we want to delete the variable
171
+ val := kv [ 1 ]
172
+ if val == "" {
173
+ fail (fmt .Sprintf ("variable must have a value; use -u to unset a variable" ))
100
174
}
101
175
102
- if len (resp .NotDeleted ) != 0 {
103
- fail (fmt .Sprintf ("cannot unset environment variables: %s" , strings .Join (resp .NotDeleted , ", " )))
104
- }
176
+ vars [i ] = & serverapi.UserEnvVarValue {Name : key , Value : val , RepositoryPattern : result .repositoryPattern }
105
177
}
106
178
107
- if len (args ) > 0 {
108
- if unsetEnvs {
109
- doUnsetEnvs ()
110
- return
111
- }
179
+ var exitCode int
180
+ var wg sync.WaitGroup
181
+ wg .Add (len (vars ))
182
+ for _ , v := range vars {
183
+ go func (v * serverapi.UserEnvVarValue ) {
184
+ err = result .client .SetEnvVar (ctx , v )
185
+ if err != nil {
186
+ fmt .Fprintln (os .Stderr , fmt .Sprintf ("cannot set %s: %v" , v .Name , err ))
187
+ exitCode = - 1
188
+ } else {
189
+ printVar (v , exportEnvs )
190
+ }
191
+ wg .Done ()
192
+ }(v )
193
+ }
194
+ wg .Wait ()
195
+ os .Exit (exitCode )
196
+ }
112
197
113
- setEnvs ()
114
- } else {
115
- getEnvs ()
198
+ service , err := theialib .NewServiceFromEnv ()
199
+ if err != nil {
200
+ fail (err .Error ())
201
+ }
202
+
203
+ vars := make ([]theialib.EnvironmentVariable , len (args ))
204
+ for i , arg := range args {
205
+ kv := strings .Split (arg , "=" )
206
+ if len (kv ) != 2 {
207
+ fail (fmt .Sprintf ("%s has no value (correct format is %s=some_value)" , arg , arg ))
116
208
}
117
- },
209
+
210
+ key := strings .TrimSpace (kv [0 ])
211
+ if key == "" {
212
+ fail (fmt .Sprintf ("variable must have a name" ))
213
+ }
214
+ // Do not trim value - the user might want whitespace here
215
+ // Also do not check if the value is empty, as an empty value means we want to delete the variable
216
+ val := kv [1 ]
217
+ if val == "" {
218
+ fail (fmt .Sprintf ("variable must have a value; use -u to unset a variable" ))
219
+ }
220
+
221
+ vars [i ] = theialib.EnvironmentVariable {Name : key , Value : val }
222
+ }
223
+
224
+ _ , err = service .SetEnvVar (theialib.SetEnvvarRequest {Variables : vars })
225
+ if err != nil {
226
+ fail (fmt .Sprintf ("cannot set environment variables: %v" , err ))
227
+ }
228
+
229
+ for _ , v := range vars {
230
+ printVarFromTheia (v , exportEnvs )
231
+ }
232
+ }
233
+
234
+ func deleteEnvs (args []string ) {
235
+ if ! isTheiaIDE () {
236
+ ctx , cancel := context .WithTimeout (context .Background (), 1 * time .Minute )
237
+ defer cancel ()
238
+ result , err := connectToServer (ctx )
239
+ if err != nil {
240
+ fail (err .Error ())
241
+ }
242
+
243
+ var exitCode int
244
+ var wg sync.WaitGroup
245
+ wg .Add (len (args ))
246
+ for _ , name := range args {
247
+ go func (name string ) {
248
+ err = result .client .DeleteEnvVar (ctx , & serverapi.UserEnvVarValue {Name : name , RepositoryPattern : result .repositoryPattern })
249
+ if err != nil {
250
+ fmt .Fprintln (os .Stderr , fmt .Sprintf ("cannot unset %s: %v" , name , err ))
251
+ exitCode = - 1
252
+ }
253
+ wg .Done ()
254
+ }(name )
255
+ }
256
+ wg .Wait ()
257
+ os .Exit (exitCode )
258
+ }
259
+
260
+ service , err := theialib .NewServiceFromEnv ()
261
+ if err != nil {
262
+ fail (err .Error ())
263
+ }
264
+
265
+ resp , err := service .DeleteEnvVar (theialib.DeleteEnvvarRequest {Variables : args })
266
+ if err != nil {
267
+ fail (fmt .Sprintf ("cannot unset environment variables: %v" , err ))
268
+ }
269
+
270
+ if len (resp .NotDeleted ) != 0 {
271
+ fail (fmt .Sprintf ("cannot unset environment variables: %s" , strings .Join (resp .NotDeleted , ", " )))
272
+ }
273
+ }
274
+
275
+ func fail (msg string ) {
276
+ fmt .Fprintln (os .Stderr , msg )
277
+ os .Exit (- 1 )
118
278
}
119
279
120
- func printVar (v theialib.EnvironmentVariable , export bool ) {
280
+ func printVar (v * serverapi.UserEnvVarValue , export bool ) {
281
+ val := strings .Replace (v .Value , "\" " , "\\ \" " , - 1 )
282
+ if export {
283
+ fmt .Printf ("export %s=\" %s\" \n " , v .Name , val )
284
+ } else {
285
+ fmt .Printf ("%s=%s\n " , v .Name , val )
286
+ }
287
+ }
288
+
289
+ func printVarFromTheia (v theialib.EnvironmentVariable , export bool ) {
121
290
val := strings .Replace (v .Value , "\" " , "\\ \" " , - 1 )
122
291
if export {
123
292
fmt .Printf ("export %s=\" %s\" \n " , v .Name , val )
@@ -132,3 +301,8 @@ func init() {
132
301
envCmd .Flags ().BoolVarP (& exportEnvs , "export" , "e" , false , "produce a script that can be eval'ed in Bash" )
133
302
envCmd .Flags ().BoolVarP (& unsetEnvs , "unset" , "u" , false , "deletes/unsets persisted environment variables" )
134
303
}
304
+
305
+ func isTheiaIDE () bool {
306
+ stat , err := os .Stat ("/theia" )
307
+ return ! errors .Is (os .ErrNotExist , err ) && stat != nil && stat .IsDir ()
308
+ }
0 commit comments