@@ -12,6 +12,7 @@ import (
12
12
"github.com/segmentio/topicctl/pkg/admin"
13
13
"github.com/segmentio/topicctl/pkg/cli"
14
14
"github.com/segmentio/topicctl/pkg/config"
15
+ "github.com/segmentio/topicctl/pkg/create"
15
16
log "github.com/sirupsen/logrus"
16
17
"github.com/spf13/cobra"
17
18
)
@@ -55,6 +56,7 @@ func init() {
55
56
addSharedFlags (createCmd , & createConfig .shared )
56
57
createCmd .AddCommand (
57
58
createACLsCmd (),
59
+ createUsersCmd (),
58
60
)
59
61
RootCmd .AddCommand (createCmd )
60
62
}
@@ -202,3 +204,143 @@ func clusterConfigForACLCreate(aclConfigPath string) (string, error) {
202
204
),
203
205
)
204
206
}
207
+
208
+ func createUsersCmd () * cobra.Command {
209
+ cmd := & cobra.Command {
210
+ Use : "user [user configs]" ,
211
+ Short : "creates SCRAM users from configuration files" ,
212
+ Args : cobra .MinimumNArgs (1 ),
213
+ RunE : createUserRun ,
214
+ PreRunE : createPreRun ,
215
+ }
216
+
217
+ return cmd
218
+ }
219
+
220
+ func createUserRun (cmd * cobra.Command , args []string ) error {
221
+ ctx , cancel := context .WithCancel (context .Background ())
222
+ defer cancel ()
223
+
224
+ sigChan := make (chan os.Signal , 1 )
225
+ signal .Notify (sigChan , os .Interrupt , syscall .SIGTERM )
226
+ go func () {
227
+ <- sigChan
228
+ cancel ()
229
+ }()
230
+
231
+ // Keep a cache of the admin clients with the cluster config path as the key
232
+ adminClients := map [string ]admin.Client {}
233
+
234
+ defer func () {
235
+ for _ , adminClient := range adminClients {
236
+ adminClient .Close ()
237
+ }
238
+ }()
239
+
240
+ matchCount := 0
241
+
242
+ for _ , arg := range args {
243
+ if createConfig .pathPrefix != "" && ! filepath .IsAbs (arg ) {
244
+ arg = filepath .Join (createConfig .pathPrefix , arg )
245
+ }
246
+
247
+ matches , err := filepath .Glob (arg )
248
+ if err != nil {
249
+ return err
250
+ }
251
+
252
+ for _ , match := range matches {
253
+ matchCount ++
254
+ if err := createUser (ctx , match , adminClients ); err != nil {
255
+ return err
256
+ }
257
+ }
258
+ }
259
+
260
+ if matchCount == 0 {
261
+ return fmt .Errorf ("No user configs match the provided args (%+v)" , args )
262
+ }
263
+
264
+ return nil
265
+ }
266
+
267
+ func createUser (
268
+ ctx context.Context ,
269
+ userConfigPath string ,
270
+ adminClients map [string ]admin.Client ,
271
+ ) error {
272
+ clusterConfigPath , err := clusterConfigForUserCreate (userConfigPath )
273
+ if err != nil {
274
+ return err
275
+ }
276
+
277
+ userConfigs , err := config .LoadUsersFile (userConfigPath )
278
+ if err != nil {
279
+ return err
280
+ }
281
+
282
+ clusterConfig , err := config .LoadClusterFile (clusterConfigPath , createConfig .shared .expandEnv )
283
+ if err != nil {
284
+ return err
285
+ }
286
+
287
+ adminClient , ok := adminClients [clusterConfigPath ]
288
+ if ! ok {
289
+ adminClient , err = clusterConfig .NewAdminClient (
290
+ ctx ,
291
+ nil ,
292
+ config.AdminClientOpts {
293
+ ReadOnly : createConfig .dryRun ,
294
+ UsernameOverride : createConfig .shared .saslUsername ,
295
+ PasswordOverride : createConfig .shared .saslPassword ,
296
+ SecretsManagerArnOverride : createConfig .shared .saslSecretsManagerArn ,
297
+ },
298
+ )
299
+ if err != nil {
300
+ return err
301
+ }
302
+ adminClients [clusterConfigPath ] = adminClient
303
+ }
304
+
305
+ for _ , userConfig := range userConfigs {
306
+ userConfig .SetDefaults ()
307
+ log .Infof (
308
+ "Processing user %s in config %s with cluster config %s" ,
309
+ userConfig .Meta .Name ,
310
+ userConfigPath ,
311
+ clusterConfigPath ,
312
+ )
313
+
314
+ userCreatorConfig := create.UserCreatorConfig {
315
+ DryRun : createConfig .dryRun ,
316
+ SkipConfirm : createConfig .skipConfirm ,
317
+ UserConfig : userConfig ,
318
+ ClusterConfig : clusterConfig ,
319
+ }
320
+
321
+ userCreator , err := create .NewUserCreator (ctx , adminClient , userCreatorConfig )
322
+ if err != nil {
323
+ return err
324
+ }
325
+
326
+ if err := userCreator .Create (ctx ); err != nil {
327
+ return err
328
+ }
329
+ }
330
+
331
+ return nil
332
+ }
333
+
334
+ func clusterConfigForUserCreate (userConfigPath string ) (string , error ) {
335
+ if createConfig .shared .clusterConfig != "" {
336
+ return createConfig .shared .clusterConfig , nil
337
+ }
338
+
339
+ return filepath .Abs (
340
+ filepath .Join (
341
+ filepath .Dir (userConfigPath ),
342
+ ".." ,
343
+ "cluster.yaml" ,
344
+ ),
345
+ )
346
+ }
0 commit comments