@@ -24,6 +24,7 @@ import (
24
24
25
25
au "github.com/nats-io/natscli/internal/auth"
26
26
iu "github.com/nats-io/natscli/internal/util"
27
+ "gopkg.in/yaml.v3"
27
28
28
29
"github.com/AlecAivazis/survey/v2"
29
30
"github.com/choria-io/fisk"
@@ -102,6 +103,10 @@ type authAccountCommand struct {
102
103
tags []string
103
104
rmTags []string
104
105
signingKey string
106
+ mapSource string
107
+ mapTarget string
108
+ mapWeight uint
109
+ inputFile string
105
110
}
106
111
107
112
func configureAuthAccountCommand (auth commandHost ) {
@@ -280,6 +285,30 @@ func configureAuthAccountCommand(auth commandHost) {
280
285
skrm .Flag ("key" , "The key to remove" ).StringVar (& c .skRole )
281
286
skrm .Flag ("operator" , "Operator to act on" ).StringVar (& c .operatorName )
282
287
skrm .Flag ("force" , "Removes without prompting" ).Short ('f' ).UnNegatableBoolVar (& c .force )
288
+
289
+ mappings := acct .Command ("mappings" , "Manage account level subject mapping and partitioning" ).Alias ("m" )
290
+
291
+ mappingsaAdd := mappings .Command ("add" , "Add a new mapping" ).Alias ("new" ).Alias ("a" ).Action (c .mappingAddAction )
292
+ mappingsaAdd .Arg ("account" , "Account to create the mappings on" ).StringVar (& c .accountName )
293
+ mappingsaAdd .Arg ("source" , "The source subject of the mapping" ).StringVar (& c .mapSource )
294
+ mappingsaAdd .Arg ("target" , "The target subject of the mapping" ).StringVar (& c .mapTarget )
295
+ mappingsaAdd .Arg ("weight" , "The weight (%) of the mappingmapping" ).Default ("100" ).UintVar (& c .mapWeight )
296
+ mappingsaAdd .Flag ("operator" , "Operator to act on" ).StringVar (& c .operatorName )
297
+ mappingsaAdd .Flag ("config" , "json or yaml file to read configuration from" ).ExistingFileVar (& c .inputFile )
298
+
299
+ mappingsls := mappings .Command ("ls" , "List mappings" ).Alias ("list" ).Action (c .mappingListAction )
300
+ mappingsls .Arg ("account" , "Account to list the mappings from" ).StringVar (& c .accountName )
301
+ mappingsls .Flag ("operator" , "Operator to act on" ).StringVar (& c .operatorName )
302
+
303
+ mappingsrm := mappings .Command ("rm" , "Remove a mapping" ).Action (c .mappingRmAction )
304
+ mappingsrm .Arg ("account" , "Account to remove the mappings from" ).StringVar (& c .accountName )
305
+ mappingsrm .Arg ("source" , "The source subject of the mapping" ).StringVar (& c .mapSource )
306
+ mappingsrm .Flag ("operator" , "Operator to act on" ).StringVar (& c .operatorName )
307
+
308
+ mappingsinfo := mappings .Command ("info" , "Show information about a mapping" ).Alias ("i" ).Alias ("show" ).Alias ("view" ).Action (c .mappingInfoAction )
309
+ mappingsinfo .Arg ("account" , "Account to inspect the mappings from" ).StringVar (& c .accountName )
310
+ mappingsinfo .Arg ("source" , "The source subject of the mapping" ).StringVar (& c .mapSource )
311
+ mappingsinfo .Flag ("operator" , "Operator to act on" ).StringVar (& c .operatorName )
283
312
}
284
313
285
314
func (c * authAccountCommand ) selectAccount (pick bool ) (* ab.AuthImpl , ab.Operator , ab.Account , error ) {
@@ -1101,3 +1130,210 @@ func (c *authAccountCommand) validTiers(acct ab.Account) []int8 {
1101
1130
1102
1131
return tiers
1103
1132
}
1133
+
1134
+ func (c * authAccountCommand ) loadMappingsConfig () (map [string ][]ab.Mapping , error ) {
1135
+ if c .inputFile != "" {
1136
+ f , err := os .ReadFile (c .inputFile )
1137
+ if err != nil {
1138
+ return nil , err
1139
+ }
1140
+
1141
+ var mappings map [string ][]ab.Mapping
1142
+ err = yaml .Unmarshal (f , & mappings )
1143
+ if err != nil {
1144
+ return nil , fmt .Errorf ("unable to load config file: %s" , err )
1145
+ }
1146
+ return mappings , nil
1147
+ }
1148
+ return nil , nil
1149
+
1150
+ }
1151
+
1152
+ func (c * authAccountCommand ) mappingAddAction (_ * fisk.ParseContext ) error {
1153
+ var err error
1154
+ mappings := map [string ][]ab.Mapping {}
1155
+ if c .inputFile != "" {
1156
+ mappings , err = c .loadMappingsConfig ()
1157
+ if err != nil {
1158
+ return err
1159
+ }
1160
+ }
1161
+
1162
+ auth , _ , acct , err := c .selectAccount (true )
1163
+ if err != nil {
1164
+ return err
1165
+ }
1166
+
1167
+ if c .inputFile == "" {
1168
+ if c .mapSource == "" {
1169
+ err := iu .AskOne (& survey.Input {
1170
+ Message : "Source subject" ,
1171
+ Help : "The source subject of the mapping" ,
1172
+ }, & c .mapSource , survey .WithValidator (survey .Required ))
1173
+ if err != nil {
1174
+ return err
1175
+ }
1176
+ }
1177
+
1178
+ if c .mapTarget == "" {
1179
+ err := iu .AskOne (& survey.Input {
1180
+ Message : "Target subject" ,
1181
+ Help : "The target subject of the mapping" ,
1182
+ }, & c .mapTarget , survey .WithValidator (survey .Required ))
1183
+ if err != nil {
1184
+ return err
1185
+ }
1186
+ }
1187
+
1188
+ mapping := ab.Mapping {Subject : c .mapTarget , Weight : uint8 (c .mapWeight )}
1189
+ // check if there are mappings already set for the source
1190
+ currentMappings := acct .SubjectMappings ().Get (c .mapSource )
1191
+ if len (currentMappings ) > 0 {
1192
+ // Check that we don't overwrite the current mapping
1193
+ for _ , m := range currentMappings {
1194
+ if m .Subject == c .mapTarget {
1195
+ return fmt .Errorf ("mapping %s -> %s already exists" , c .mapSource , c .mapTarget )
1196
+ }
1197
+ }
1198
+ }
1199
+ currentMappings = append (currentMappings , mapping )
1200
+ mappings [c .mapSource ] = currentMappings
1201
+ }
1202
+
1203
+ for subject , m := range mappings {
1204
+ err = acct .SubjectMappings ().Set (subject , m ... )
1205
+ if err != nil {
1206
+ return err
1207
+ }
1208
+ }
1209
+
1210
+ err = auth .Commit ()
1211
+ if err != nil {
1212
+ return err
1213
+ }
1214
+
1215
+ return c .fShowMappings (os .Stdout , mappings )
1216
+ }
1217
+
1218
+ func (c * authAccountCommand ) mappingInfoAction (_ * fisk.ParseContext ) error {
1219
+ _ , _ , acct , err := c .selectAccount (true )
1220
+ if err != nil {
1221
+ return err
1222
+ }
1223
+
1224
+ accountMappings := acct .SubjectMappings ().List ()
1225
+ if len (accountMappings ) == 0 {
1226
+ fmt .Println ("No mappings defined" )
1227
+ return nil
1228
+ }
1229
+
1230
+ if c .mapSource == "" {
1231
+ err = iu .AskOne (& survey.Select {
1232
+ Message : "Select a mapping to inspect" ,
1233
+ Options : accountMappings ,
1234
+ PageSize : iu .SelectPageSize (len (accountMappings )),
1235
+ }, & c .mapSource )
1236
+ if err != nil {
1237
+ return err
1238
+ }
1239
+ }
1240
+
1241
+ mappings := map [string ][]ab.Mapping {
1242
+ c .mapSource : acct .SubjectMappings ().Get (c .mapSource ),
1243
+ }
1244
+
1245
+ return c .fShowMappings (os .Stdout , mappings )
1246
+ }
1247
+
1248
+ func (c * authAccountCommand ) mappingListAction (_ * fisk.ParseContext ) error {
1249
+ _ , _ , acct , err := c .selectAccount (true )
1250
+ if err != nil {
1251
+ return err
1252
+ }
1253
+
1254
+ mappings := acct .SubjectMappings ().List ()
1255
+ if len (mappings ) == 0 {
1256
+ fmt .Println ("No mappings defined" )
1257
+ return nil
1258
+ }
1259
+
1260
+ tbl := iu .NewTableWriter (opts (), "Subject mappings for account %s" , acct .Name ())
1261
+ tbl .AddHeaders ("Source Subject" , "Target Subject" , "Weight" , "Cluster" )
1262
+
1263
+ for _ , fromMapping := range acct .SubjectMappings ().List () {
1264
+ subjectMaps := acct .SubjectMappings ().Get (fromMapping )
1265
+ for _ , m := range subjectMaps {
1266
+ tbl .AddRow (fromMapping , m .Subject , m .Weight , m .Cluster )
1267
+ }
1268
+ }
1269
+
1270
+ fmt .Println (tbl .Render ())
1271
+ return nil
1272
+ }
1273
+
1274
+ func (c * authAccountCommand ) mappingRmAction (_ * fisk.ParseContext ) error {
1275
+ auth , _ , acct , err := c .selectAccount (true )
1276
+ if err != nil {
1277
+ return err
1278
+ }
1279
+
1280
+ mappings := acct .SubjectMappings ().List ()
1281
+ if len (mappings ) == 0 {
1282
+ fmt .Println ("No mappings defined" )
1283
+ return nil
1284
+ }
1285
+
1286
+ if c .mapSource == "" {
1287
+ err = iu .AskOne (& survey.Select {
1288
+ Message : "Select a mapping to delete" ,
1289
+ Options : mappings ,
1290
+ PageSize : iu .SelectPageSize (len (mappings )),
1291
+ }, & c .mapSource )
1292
+ if err != nil {
1293
+ return err
1294
+ }
1295
+ }
1296
+
1297
+ err = acct .SubjectMappings ().Delete (c .mapSource )
1298
+ if err != nil {
1299
+ return err
1300
+ }
1301
+
1302
+ err = auth .Commit ()
1303
+ if err != nil {
1304
+ return err
1305
+ }
1306
+
1307
+ fmt .Printf ("Deleted mapping {%s}\n " , c .mapSource )
1308
+ return nil
1309
+ }
1310
+
1311
+ func (c * authAccountCommand ) fShowMappings (w io.Writer , mappings map [string ][]ab.Mapping ) error {
1312
+ out , err := c .showMappings (mappings )
1313
+ if err != nil {
1314
+ return err
1315
+ }
1316
+
1317
+ _ , err = fmt .Fprintln (w , out )
1318
+ return err
1319
+ }
1320
+
1321
+ func (c * authAccountCommand ) showMappings (mappings map [string ][]ab.Mapping ) (string , error ) {
1322
+ cols := newColumns ("Subject mappings" )
1323
+ cols .AddSectionTitle ("Configuration" )
1324
+ for source , m := range mappings {
1325
+ totalWeight := 0
1326
+ for _ , wm := range m {
1327
+ cols .AddRow ("Source" , source )
1328
+ cols .AddRow ("Target" , wm .Subject )
1329
+ cols .AddRow ("Weight" , wm .Weight )
1330
+ cols .AddRow ("Cluster" , wm .Cluster )
1331
+ cols .AddRow ("" , "" )
1332
+ totalWeight += int (wm .Weight )
1333
+ }
1334
+ cols .AddRow ("Total weight:" , totalWeight )
1335
+ cols .AddRow ("" , "" )
1336
+ }
1337
+
1338
+ return cols .Render ()
1339
+ }
0 commit comments