Skip to content

Commit 0e2e9ea

Browse files
authored
fix(mock): global seed flag is ignored (#376)
* fix(mock): global seed flag is ignored * fix: misc, readme, tests, schema * fix: missing flag buffer-size in commands * refactor: flags * refactor: flags catchErrors, config * refactor: flags caches, emptyInput * refactor: flags pprof, mask * refactor: flags repeat, repeat-until, repeat-while * refactor: flags seed, serve * refactor: flags fix writing flag * refactor: flags skips * refactor: flags stats, xmlsubscriber * fix: add missing short command descriptions * fix: flags
1 parent 8d6f426 commit 0e2e9ea

File tree

11 files changed

+361
-71
lines changed

11 files changed

+361
-71
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ Types of changes
1414
- `Fixed` for any bug fixes.
1515
- `Security` in case of vulnerabilities.
1616

17+
## [1.29.1]
18+
19+
- `Fixed` mock command ignores global seed flag
20+
- `Fixed` missing flag `buffer-size` in `mock`, `xml` and `play` commands
21+
- `Fixed` missing flag `load-cache` in `mock` command
22+
- `Fixed` remove unused flags from `mock`, `xml`, `parquet`, `jsonschema`, `flow` and `play` commands
23+
1724
## [1.29.0]
1825

1926
- `Added` mask `apply` to externalize masks

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ You can use [LINO](https://github.com/CGI-FR/LINO) to extract sample data from a
1616
You can also generate data with a simple yaml configuration file.
1717

1818
**Capabilities**
19+
1920
- credibility : generated data is not distinguishable from real data
2021
- data synthesis : generate data from nothing
2122
- data masking, including
@@ -163,6 +164,7 @@ The following types of masks can be used :
163164
* [`fluxUri`](#fluxuri) is to replace by a sequence of values defined in an external resource.
164165
* [`replacement`](#replacement) is to mask a data with another data from the jsonline.
165166
* [`pipe`](#pipe) is a mask to handle complex nested array structures, it can read an array as an object stream and process it with a sub-pipeline.
167+
* [`apply`](#apply) process selected data with a sub-pipeline.
166168
* [`luhn`](#luhn) can generate valid numbers using the Luhn algorithm (e.g. french SIRET or SIREN).
167169
* [`markov`](#markov) can generate pseudo text based on a sample text.
168170
* [`findInCSV`](#findincsv) get one or multiple csv lines which matched with Json entry value from CSV files.
@@ -928,6 +930,24 @@ Be sure to check [demo](demo/demo8) to get more details about this mask.
928930

929931
[Return to list of masks](#possible-masks)
930932

933+
### Apply
934+
935+
[![Try it](https://img.shields.io/badge/-Try%20it%20in%20PIMO%20Play-brightgreen)](https://cgi-fr.github.io/pimo-play/#c=G4UwTgzglg9gdgLgAQCICMKBQBbAhhAayjgHMFMkkBaJCEAGxAGMAXGMcyrpAKwngAOuFgAtkKKACNccLNzyFO3JLgED6ATyXKkAVzBRxAOgD09KWFxgNJhUVJUpMoxuz0sQA&i=N4KABGBECWBGCGA7SAuKkQF8g)
936+
937+
This mask helps you organize your masking configuration in different files, enablig reuse and mutualisation of masks.
938+
939+
```yaml
940+
version: "1"
941+
masking:
942+
- selector:
943+
jsonpath: "iban"
944+
mask:
945+
apply:
946+
uri: "./library/masking-iban.yml" # list of mask to apply on iban is declared in an external masking file
947+
```
948+
949+
[Return to list of masks](#possible-masks)
950+
931951
### Luhn
932952

933953
[![Try it](https://img.shields.io/badge/-Try%20it%20in%20PIMO%20Play-brightgreen)](https://cgi-fr.github.io/pimo-play/#c=G4UwTgzglg9gdgLgAQCICMKBQEQgCbIAsATJgLYCGEA1lHAOYKZJIC0SOANiAMYAuMMExYikAKwjwADhT4ALZCmhgQfLKMo1hopJwCucxEgDeAXyA&i=N4KABGBEDOCWBOBTALpAXFAjAJgMwBYBWANgHYAOATgAYddIQBfIA)

cmd/pimo/flags.go

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (C) 2024 CGI France
2+
//
3+
// This file is part of PIMO.
4+
//
5+
// PIMO is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU General Public License as published by
7+
// the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// PIMO is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU General Public License
16+
// along with PIMO. If not, see <http://www.gnu.org/licenses/>.
17+
18+
package main
19+
20+
import (
21+
"os"
22+
23+
"github.com/spf13/cobra"
24+
)
25+
26+
type flag[T any] struct {
27+
name string // Name of the flag
28+
shorthand string // Optional short name
29+
variable *T // Pointer to the variable
30+
usage string // Description of the flag
31+
}
32+
33+
// flags with default values
34+
//
35+
//nolint:gochecknoglobals
36+
var (
37+
maxBufferCapacity = 64
38+
catchErrors = ""
39+
maskingFile = "masking.yml"
40+
mockConfigFile = "routes.yaml"
41+
cachesToDump = map[string]string{}
42+
cachesToLoad = map[string]string{}
43+
emptyInput = false
44+
maskingOneLiner = []string{}
45+
profiling = ""
46+
iteration = 1
47+
repeatUntil = ""
48+
repeatWhile = ""
49+
seedValue = int64(0)
50+
serve = ""
51+
skipLineOnError = false
52+
skipFieldOnError = false
53+
skipLogFile = ""
54+
statisticsDestination = os.Getenv("PIMO_STATS_URL")
55+
statsTemplate = os.Getenv("PIMO_STATS_TEMPLATE")
56+
xmlSubscriberName = map[string]string{}
57+
)
58+
59+
//nolint:gochecknoglobals
60+
var (
61+
flagBufferSize = flag[int]{name: "buffer-size", variable: &maxBufferCapacity, usage: "buffer size in kB to load data from uri for each line"}
62+
flagCatchErrors = flag[string]{name: "catch-errors", shorthand: "e", variable: &catchErrors, usage: "catch errors and write line in file, same as using skip-field-on-error + skip-log-file"}
63+
flagConfigMasking = flag[string]{name: "config", shorthand: "c", variable: &maskingFile, usage: "name and location of the masking config file"}
64+
flagConfigRoute = flag[string]{name: "config", shorthand: "c", variable: &mockConfigFile, usage: "name and location of the routes config file"}
65+
flagCachesToDump = flag[map[string]string]{name: "dump-cache", variable: &cachesToDump, usage: "path for dumping cache into file"}
66+
flagCachesToLoad = flag[map[string]string]{name: "load-cache", variable: &cachesToLoad, usage: "path for loading cache from file"}
67+
flagEmptyInput = flag[bool]{name: "empty-input", variable: &emptyInput, usage: "generate data without any input, to use with repeat flag"}
68+
flagMaskOneLiner = flag[[]string]{name: "mask", shorthand: "m", variable: &maskingOneLiner, usage: "one liner masking"}
69+
flagProfiling = flag[string]{name: "pprof", variable: &profiling, usage: "create a pprof file - use 'cpu' to create a CPU pprof file or 'mem' to create an memory pprof file"}
70+
flagRepeat = flag[int]{name: "repeat", shorthand: "r", variable: &iteration, usage: "number of iteration to mask each input"}
71+
flagRepeatUntil = flag[string]{name: "repeat-until", variable: &repeatUntil, usage: "mask each input repeatedly until the given condition is met"}
72+
flagRepeatWhile = flag[string]{name: "repeat-while", variable: &repeatWhile, usage: "mask each input repeatedly while the given condition is met"}
73+
flagSeed = flag[int64]{name: "seed", shorthand: "s", variable: &seedValue, usage: "set global seed"}
74+
flagServe = flag[string]{name: "serve", variable: &serve, usage: "listen/respond to HTTP interface and port instead of stdin/stdout, <ip>:<port> or :<port> to listen to all local networks"}
75+
flagSkipLineOnError = flag[bool]{name: "skip-line-on-error", variable: &skipLineOnError, usage: "skip a line if an error occurs while masking a field"}
76+
flagSkipFieldOnError = flag[bool]{name: "skip-field-on-error", variable: &skipFieldOnError, usage: "remove a field if an error occurs while masking this field"}
77+
flagSkipLogFile = flag[string]{name: "skip-log-file", variable: &skipLogFile, usage: "skipped lines will be written to this log file"}
78+
flagStatsDestination = flag[string]{name: "stats", variable: &statisticsDestination, usage: "generate execution statistics in the specified dump file"}
79+
flagStatsTemplate = flag[string]{name: "statsTemplate", variable: &statsTemplate, usage: "template string to format stats (to include them you have to specify them as `{{ .Stats }}` like `{\"software\":\"PIMO\",\"stats\":{{ .Stats }}}`)"}
80+
flagXMLSubscriberName = flag[map[string]string]{name: "subscriber", variable: &xmlSubscriberName, usage: "name of element to mask"}
81+
)
82+
83+
func addFlag[T any](cmd *cobra.Command, flag flag[T]) {
84+
switch variable := any(flag.variable).(type) {
85+
case *int:
86+
if len(flag.shorthand) > 0 {
87+
cmd.Flags().IntVarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
88+
} else {
89+
cmd.Flags().IntVar(variable, flag.name, *variable, flag.usage)
90+
}
91+
case *bool:
92+
if len(flag.shorthand) > 0 {
93+
cmd.Flags().BoolVarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
94+
} else {
95+
cmd.Flags().BoolVar(variable, flag.name, *variable, flag.usage)
96+
}
97+
case *string:
98+
if len(flag.shorthand) > 0 {
99+
cmd.Flags().StringVarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
100+
} else {
101+
cmd.Flags().StringVar(variable, flag.name, *variable, flag.usage)
102+
}
103+
case *int64:
104+
if len(flag.shorthand) > 0 {
105+
cmd.Flags().Int64VarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
106+
} else {
107+
cmd.Flags().Int64Var(variable, flag.name, *variable, flag.usage)
108+
}
109+
case *map[string]string:
110+
if len(flag.shorthand) > 0 {
111+
cmd.Flags().StringToStringVarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
112+
} else {
113+
cmd.Flags().StringToStringVar(variable, flag.name, *variable, flag.usage)
114+
}
115+
case *[]string:
116+
if len(flag.shorthand) > 0 {
117+
cmd.Flags().StringArrayVarP(variable, flag.name, flag.shorthand, *variable, flag.usage)
118+
} else {
119+
cmd.Flags().StringArrayVar(variable, flag.name, *variable, flag.usage)
120+
}
121+
}
122+
}

cmd/pimo/main.go

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -42,40 +42,21 @@ import (
4242
)
4343

4444
// Provisioned by ldflags
45-
// nolint: gochecknoglobals
45+
//
46+
//nolint:gochecknoglobals
4647
var (
4748
version string
4849
commit string
4950
buildDate string
5051
builtBy string
5152

52-
verbosity string
53-
debug bool
54-
jsonlog bool
55-
colormode string
56-
iteration int
57-
emptyInput bool
58-
maskingFile string
59-
cachesToDump map[string]string
60-
cachesToLoad map[string]string
61-
skipLineOnError bool
62-
skipFieldOnError bool
63-
skipLogFile string
64-
catchErrors string
65-
seedValue int64
66-
maskingOneLiner []string
67-
repeatUntil string
68-
repeatWhile string
69-
statisticsDestination string
70-
statsTemplate string
71-
statsDestinationEnv = os.Getenv("PIMO_STATS_URL")
72-
statsTemplateEnv = os.Getenv("PIMO_STATS_TEMPLATE")
73-
xmlSubscriberName map[string]string
74-
serve string
75-
maxBufferCapacity int
76-
profiling string
77-
parquetInput string
78-
parquetOutput string
53+
verbosity string
54+
debug bool
55+
jsonlog bool
56+
colormode string
57+
58+
parquetInput string
59+
parquetOutput string
7960
)
8061

8162
func main() {
@@ -102,28 +83,31 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
10283
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "add debug information to logs (very slow)")
10384
rootCmd.PersistentFlags().BoolVar(&jsonlog, "log-json", false, "output logs in JSON format")
10485
rootCmd.PersistentFlags().StringVar(&colormode, "color", "auto", "use colors in log outputs : yes, no or auto")
105-
rootCmd.PersistentFlags().IntVarP(&iteration, "repeat", "r", 1, "number of iteration to mask each input")
106-
rootCmd.PersistentFlags().BoolVar(&emptyInput, "empty-input", false, "generate data without any input, to use with repeat flag")
107-
rootCmd.PersistentFlags().StringVarP(&maskingFile, "config", "c", "masking.yml", "name and location of the masking-config file")
108-
rootCmd.PersistentFlags().StringToStringVar(&cachesToDump, "dump-cache", map[string]string{}, "path for dumping cache into file")
109-
rootCmd.PersistentFlags().StringToStringVar(&cachesToLoad, "load-cache", map[string]string{}, "path for loading cache from file")
110-
rootCmd.PersistentFlags().BoolVar(&skipLineOnError, "skip-line-on-error", false, "skip a line if an error occurs while masking a field")
111-
rootCmd.PersistentFlags().BoolVar(&skipFieldOnError, "skip-field-on-error", false, "remove a field if an error occurs while masking this field")
112-
rootCmd.PersistentFlags().StringVar(&skipLogFile, "skip-log-file", "", "skipped lines will be written to this log file")
113-
rootCmd.PersistentFlags().StringVarP(&catchErrors, "catch-errors", "e", "", "catch errors and write line in file, same as using skip-field-on-error + skip-log-file")
114-
rootCmd.Flags().Int64VarP(&seedValue, "seed", "s", 0, "set seed")
115-
rootCmd.PersistentFlags().StringArrayVarP(&maskingOneLiner, "mask", "m", []string{}, "one liner masking")
116-
rootCmd.PersistentFlags().StringVar(&repeatUntil, "repeat-until", "", "mask each input repeatedly until the given condition is met")
117-
rootCmd.PersistentFlags().StringVar(&repeatWhile, "repeat-while", "", "mask each input repeatedly while the given condition is met")
118-
rootCmd.PersistentFlags().StringVar(&statisticsDestination, "stats", statsDestinationEnv, "generate execution statistics in the specified dump file")
119-
rootCmd.PersistentFlags().StringVar(&statsTemplate, "statsTemplate", statsTemplateEnv, "template string to format stats (to include them you have to specify them as `{{ .Stats }}` like `{\"software\":\"PIMO\",\"stats\":{{ .Stats }}}`)")
120-
rootCmd.Flags().StringVar(&serve, "serve", "", "listen/respond to HTTP interface and port instead of stdin/stdout, <ip>:<port> or :<port> to listen to all local networks")
121-
rootCmd.Flags().IntVar(&maxBufferCapacity, "buffer-size", 64, "buffer size in kB to load data from uri for each line")
122-
rootCmd.Flags().StringVar(&profiling, "pprof", "", "create a pprof file - use 'cpu' to create a CPU pprof file or 'mem' to create an memory pprof file")
86+
87+
addFlag(rootCmd, flagBufferSize)
88+
addFlag(rootCmd, flagCatchErrors)
89+
addFlag(rootCmd, flagConfigMasking)
90+
addFlag(rootCmd, flagCachesToDump)
91+
addFlag(rootCmd, flagCachesToLoad)
92+
addFlag(rootCmd, flagEmptyInput)
93+
addFlag(rootCmd, flagMaskOneLiner)
94+
addFlag(rootCmd, flagProfiling)
95+
addFlag(rootCmd, flagRepeat)
96+
addFlag(rootCmd, flagRepeatUntil)
97+
addFlag(rootCmd, flagRepeatWhile)
98+
addFlag(rootCmd, flagSeed)
99+
addFlag(rootCmd, flagServe)
100+
addFlag(rootCmd, flagSkipFieldOnError)
101+
addFlag(rootCmd, flagSkipLineOnError)
102+
addFlag(rootCmd, flagSkipLogFile)
103+
addFlag(rootCmd, flagStatsDestination)
104+
addFlag(rootCmd, flagStatsTemplate)
123105

124106
rootCmd.AddCommand(&cobra.Command{
125-
Use: "jsonschema",
107+
Use: "jsonschema",
108+
Short: "Export schema of masking configuration",
126109
Run: func(cmd *cobra.Command, args []string) {
110+
initLog()
127111
jsonschema, err := pimo.GetJsonSchema()
128112
if err != nil {
129113
os.Exit(8)
@@ -135,8 +119,11 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
135119
xmlCmd := &cobra.Command{
136120
Use: "xml",
137121
Short: "Parsing and masking XML file",
138-
Run: func(cmd *cobra.Command, args []string) {
122+
Run: func(cmd *cobra.Command, _ []string) {
139123
initLog()
124+
if maxBufferCapacity > 0 {
125+
uri.MaxCapacityForEachLine = maxBufferCapacity * 1024
126+
}
140127
if len(catchErrors) > 0 {
141128
skipLineOnError = true
142129
skipLogFile = catchErrors
@@ -185,8 +172,18 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
185172
}
186173
},
187174
}
188-
xmlCmd.Flags().StringToStringVar(&xmlSubscriberName, "subscriber", map[string]string{}, "name of element to mask")
189-
xmlCmd.Flags().Int64VarP(&seedValue, "seed", "s", 0, "set seed")
175+
addFlag(xmlCmd, flagBufferSize)
176+
addFlag(xmlCmd, flagCatchErrors)
177+
addFlag(xmlCmd, flagCachesToDump)
178+
addFlag(xmlCmd, flagCachesToLoad)
179+
// addFlag(xmlCmd, flagProfiling) //could use
180+
addFlag(xmlCmd, flagSeed)
181+
addFlag(xmlCmd, flagSkipFieldOnError)
182+
addFlag(xmlCmd, flagSkipLineOnError)
183+
addFlag(xmlCmd, flagSkipLogFile)
184+
// addFlag(xmlCmd, flagStatsDestination) // could use
185+
// addFlag(xmlCmd, flagStatsTemplate) // could use
186+
addFlag(xmlCmd, flagXMLSubscriberName)
190187
rootCmd.AddCommand(xmlCmd)
191188

192189
// Add command for parquet transformer
@@ -206,12 +203,26 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
206203
run(cmd)
207204
},
208205
}
209-
parquetCmd.Flags().Int64VarP(&seedValue, "seed", "s", 0, "set seed")
206+
addFlag(parquetCmd, flagBufferSize)
207+
addFlag(parquetCmd, flagCatchErrors)
208+
addFlag(parquetCmd, flagConfigMasking)
209+
addFlag(parquetCmd, flagCachesToDump)
210+
addFlag(parquetCmd, flagCachesToLoad)
211+
addFlag(parquetCmd, flagMaskOneLiner)
212+
addFlag(parquetCmd, flagProfiling)
213+
addFlag(parquetCmd, flagSeed)
214+
addFlag(parquetCmd, flagSkipFieldOnError)
215+
addFlag(parquetCmd, flagSkipLineOnError)
216+
addFlag(parquetCmd, flagSkipLogFile)
217+
addFlag(parquetCmd, flagStatsDestination)
218+
addFlag(parquetCmd, flagStatsTemplate)
210219
rootCmd.AddCommand(parquetCmd)
211220

212-
rootCmd.AddCommand(&cobra.Command{
213-
Use: "flow",
221+
flowCmd := &cobra.Command{
222+
Use: "flow",
223+
Short: "Export masking configuration as graphviz diagram",
214224
Run: func(cmd *cobra.Command, args []string) {
225+
initLog()
215226
pdef, err := model.LoadPipelineDefinitionFromFile(maskingFile)
216227
if err != nil {
217228
log.Err(err).Msg("Cannot load pipeline definition from file")
@@ -224,15 +235,21 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
224235
}
225236
fmt.Println(flow)
226237
},
227-
})
238+
}
239+
rootCmd.AddCommand(flowCmd)
228240

229241
playPort := 3010
230242
playSecure := false
231243
playCmd := &cobra.Command{
232-
Use: "play",
244+
Use: "play",
245+
Short: "Start local website to play with PIMO",
233246
Run: func(cmd *cobra.Command, args []string) {
234247
initLog()
235248

249+
if maxBufferCapacity > 0 {
250+
uri.MaxCapacityForEachLine = maxBufferCapacity * 1024
251+
}
252+
236253
router := pimo.Play(playSecure)
237254
port := fmt.Sprintf("0.0.0.0:%d", playPort)
238255

@@ -243,6 +260,7 @@ There is NO WARRANTY, to the extent permitted by law.`, version, commit, buildDa
243260
}
244261
playCmd.PersistentFlags().IntVarP(&playPort, "port", "p", 3010, "port number")
245262
playCmd.PersistentFlags().BoolVarP(&playSecure, "secure", "s", false, "enable security features (use this flag if PIMO Play is publicly exposed)")
263+
addFlag(playCmd, flagBufferSize)
246264
rootCmd.AddCommand(playCmd)
247265

248266
setupMockCommand(rootCmd)

0 commit comments

Comments
 (0)