github.com/opendevstack/tailor@v1.3.5-0.20220119161809-cab064e60a67/cmd/tailor/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "runtime/debug" 8 9 "github.com/alecthomas/kingpin" 10 "github.com/opendevstack/tailor/pkg/cli" 11 "github.com/opendevstack/tailor/pkg/commands" 12 ) 13 14 var ( 15 app = kingpin.New( 16 "tailor", 17 "Tailor - Infrastructure as Code for OpenShift", 18 ).DefaultEnvars() 19 // App-wide flags 20 verboseFlag = app.Flag( 21 "verbose", 22 "Enable verbose output.", 23 ).Short('v').Bool() 24 debugFlag = app.Flag( 25 "debug", 26 "Enable debug output (implies verbose).", 27 ).Short('d').Bool() 28 nonInteractiveFlag = app.Flag( 29 "non-interactive", 30 "Disable interactive mode.", 31 ).Bool() 32 ocBinaryFlag = app.Flag( 33 "oc-binary", 34 "oc binary to use", 35 ).Default("oc").String() 36 fileFlag = app.Flag( 37 "file", 38 "Tailorfile with flags.", 39 ).Short('f').Default("Tailorfile").String() 40 forceFlag = app.Flag( 41 "force", 42 "Force to continue despite warning (e.g. deleting all resources).", 43 ).Bool() 44 namespaceFlag = app.Flag( 45 "namespace", 46 "Namespace (omit to use current)", 47 ).Short('n').String() 48 selectorFlag = app.Flag( 49 "selector", 50 "Selector (label query) to filter on. When using multiple labels (comma-separated), all need to be present (AND condition).", 51 ).Short('l').String() 52 excludeFlag = app.Flag( 53 "exclude", 54 "Exclude kinds, names and labels (repeatable or comma-separated)", 55 ).Short('e').Strings() 56 templateDirFlag = app.Flag( 57 "template-dir", 58 "Path to local templates", 59 ).Short('t').Default(".").String() 60 paramDirFlag = app.Flag( 61 "param-dir", 62 "Path to parameter files for local templates (defaults to <NAMESPACE> or working directory)", 63 ).Short('p').Default(".").String() 64 publicKeyDirFlag = app.Flag( 65 "public-key-dir", 66 "Path to public key files", 67 ).Default(".").String() 68 privateKeyFlag = app.Flag( 69 "private-key", 70 "Path to private key file", 71 ).Default("private.key").String() 72 passphraseFlag = app.Flag( 73 "passphrase", 74 "Passphrase to unlock key", 75 ).String() 76 77 versionCommand = app.Command( 78 "version", 79 "Show version", 80 ) 81 82 diffCommand = app.Command( 83 "diff", 84 "Show diff between remote and local", 85 ).Alias("status") 86 diffLabelsFlag = diffCommand.Flag( 87 "labels", 88 "Label to set in all resources for this template.", 89 ).String() 90 diffParamFlag = diffCommand.Flag( 91 "param", 92 "Specify a key-value pair (eg. -p FOO=BAR) to set/override a parameter value in the template.", 93 ).Strings() 94 diffParamFileFlag = diffCommand.Flag( 95 "param-file", 96 "File(s) containing template parameter values to set/override in the template.", 97 ).Strings() 98 diffIgnorePathFlag = diffCommand.Flag( 99 "ignore-path", 100 "DEPRECATED! Use --preserve instead.", 101 ).PlaceHolder("bc:foobar:/spec/output/to/name").Strings() 102 diffPreservePathFlag = diffCommand.Flag( 103 "preserve", 104 "Path(s) per kind/name for which to preserve current state (e.g. because they are externally modified) in RFC 6901 format.", 105 ).PlaceHolder("bc:foobar:/spec/output/to/name").Strings() 106 diffPreserveImmutableFieldsFlag = diffCommand.Flag( 107 "preserve-immutable-fields", 108 "Preserve current state of all immutable fields (such as host of a route, or storageClassName of a PVC).", 109 ).Bool() 110 diffIgnoreUnknownParametersFlag = diffCommand.Flag( 111 "ignore-unknown-parameters", 112 "If true, will not stop processing if a provided parameter does not exist in the template.", 113 ).Bool() 114 diffUpsertOnlyFlag = diffCommand.Flag( 115 "upsert-only", 116 "Don't delete resource, only create / update.", 117 ).Short('u').Bool() 118 diffAllowRecreateFlag = diffCommand.Flag( 119 "allow-recreate", 120 "Allow to recreate the whole resource when an immutable field is changed.", 121 ).Bool() 122 diffRevealSecretsFlag = diffCommand.Flag( 123 "reveal-secrets", 124 "Reveal drift of Secret resources (might show secret values in clear text).", 125 ).Bool() 126 diffResourceArg = diffCommand.Arg( 127 "resource", "Remote resource (defaults to all)", 128 ).String() 129 130 applyCommand = app.Command( 131 "apply", 132 "Update remote with local", 133 ).Alias("update") 134 applyLabelsFlag = applyCommand.Flag( 135 "labels", 136 "Label to set in all resources for this template.", 137 ).String() 138 applyParamFlag = applyCommand.Flag( 139 "param", 140 "Specify a key-value pair (eg. -p FOO=BAR) to set/override a parameter value in the template.", 141 ).Strings() 142 applyParamFileFlag = applyCommand.Flag( 143 "param-file", 144 "File(s) containing template parameter values to set/override in the template.", 145 ).Strings() 146 applyIgnorePathFlag = applyCommand.Flag( 147 "ignore-path", 148 "DEPRECATED! Use --preserve instead.", 149 ).PlaceHolder("bc:foobar:/spec/output/to/name").Strings() 150 applyPreservePathFlag = applyCommand.Flag( 151 "preserve", 152 "Path(s) per kind for which to preserve current state (e.g. because they are externally modified) in RFC 6901 format.", 153 ).PlaceHolder("bc:foobar:/spec/output/to/name").Strings() 154 applyPreserveImmutableFieldsFlag = applyCommand.Flag( 155 "preserve-immutable-fields", 156 "Preserve current state of all immutable fields (such as host of a route, or storageClassName of a PVC).", 157 ).Bool() 158 applyIgnoreUnknownParametersFlag = applyCommand.Flag( 159 "ignore-unknown-parameters", 160 "If true, will not stop processing if a provided parameter does not exist in the template.", 161 ).Bool() 162 applyUpsertOnlyFlag = applyCommand.Flag( 163 "upsert-only", 164 "Don't delete resource, only create / apply.", 165 ).Short('u').Bool() 166 applyAllowRecreateFlag = applyCommand.Flag( 167 "allow-recreate", 168 "Allow to recreate the whole resource when an immutable field is changed.", 169 ).Bool() 170 applyRevealSecretsFlag = applyCommand.Flag( 171 "reveal-secrets", 172 "Reveal drift of Secret resources (might show secret values in clear text).", 173 ).Bool() 174 applyVerifyFlag = applyCommand.Flag( 175 "verify", 176 "Verify if resources are in sync after changes are applied.", 177 ).Bool() 178 applyResourceArg = applyCommand.Arg( 179 "resource", "Remote resource (defaults to all)", 180 ).String() 181 182 exportCommand = app.Command( 183 "export", 184 "Export remote state as template", 185 ) 186 exportWithAnnotationsFlag = exportCommand.Flag( 187 "with-annotations", 188 "Export annotations as well.", 189 ).Bool() 190 exportWithHardcodedNamespaceFlag = exportCommand.Flag( 191 "with-hardcoded-namespace", 192 "Keep any occurences of hardcoded namespace instead of replacing with ${TAILOR_NAMESPACE} in template.", 193 ).Bool() 194 exportTrimAnnotationFlag = exportCommand.Flag( 195 "trim-annotation", 196 "Annotation (prefix) to trim on top of annotations trimmed by default. ", 197 ).PlaceHolder("template.openshift.io/").Strings() 198 exportResourceArg = exportCommand.Arg( 199 "resource", "Remote resource (defaults to all)", 200 ).String() 201 202 secretsCommand = app.Command( 203 "secrets", 204 "Work with secrets", 205 ) 206 editCommand = secretsCommand.Command( 207 "edit", 208 "Edit param file", 209 ) 210 editFileArg = editCommand.Arg( 211 "file", "File to edit", 212 ).Required().String() 213 214 reEncryptCommand = secretsCommand.Command( 215 "re-encrypt", 216 "Re-Encrypt param file(s)", 217 ) 218 reEncryptFileArg = reEncryptCommand.Arg( 219 "file", "File to re-encrypt", 220 ).String() 221 222 revealCommand = secretsCommand.Command( 223 "reveal", 224 "Show param file contents with revealed secrets", 225 ) 226 revealFileArg = revealCommand.Arg( 227 "file", "File to show", 228 ).Required().String() 229 230 generateKeyCommand = secretsCommand.Command( 231 "generate-key", 232 "Generate new keypair", 233 ) 234 generateKeyNameFlag = generateKeyCommand.Flag( 235 "name", 236 "Name for keypair", 237 ).String() 238 generateKeyEmailArg = generateKeyCommand.Arg( 239 "email", "Emil of keypair", 240 ).Required().String() 241 ) 242 243 func main() { 244 log.SetFlags(0) 245 246 defer func() { 247 err := recover() 248 if err != nil { 249 log.Fatalf("ERROR: An unexpected error occured. Please file a bug on GitHub (https://github.com/opendevstack/tailor/issues/new) with the following stack trace:\n\n%s\n\n%s", err, debug.Stack()) 250 } 251 }() 252 253 command := kingpin.MustParse(app.Parse(os.Args[1:])) 254 255 if command == versionCommand.FullCommand() { 256 fmt.Println("1.3.4+master") 257 return 258 } 259 260 clusterRequired := true 261 if command == editCommand.FullCommand() || 262 command == revealCommand.FullCommand() || 263 command == reEncryptCommand.FullCommand() || 264 command == generateKeyCommand.FullCommand() { 265 clusterRequired = false 266 } 267 268 globalOptions, err := cli.NewGlobalOptions( 269 clusterRequired, 270 *fileFlag, 271 *verboseFlag, 272 *debugFlag, 273 *nonInteractiveFlag, 274 *ocBinaryFlag, 275 *forceFlag, 276 ) 277 if err != nil { 278 log.Fatalln("Options could not be processed:", err) 279 } 280 281 switch command { 282 case editCommand.FullCommand(): 283 secretsOptions, err := cli.NewSecretsOptions( 284 globalOptions, 285 *paramDirFlag, 286 *publicKeyDirFlag, 287 *privateKeyFlag, 288 *passphraseFlag, 289 ) 290 if err != nil { 291 log.Fatalln("Options could not be processed:", err) 292 } 293 err = commands.Edit(secretsOptions, *editFileArg) 294 if err != nil { 295 log.Fatalf("Failed to edit file: %s.", err) 296 } 297 298 case reEncryptCommand.FullCommand(): 299 secretsOptions, err := cli.NewSecretsOptions( 300 globalOptions, 301 *paramDirFlag, 302 *publicKeyDirFlag, 303 *privateKeyFlag, 304 *passphraseFlag, 305 ) 306 if err != nil { 307 log.Fatalln("Options could not be processed:", err) 308 } 309 err = commands.ReEncrypt(secretsOptions, *reEncryptFileArg) 310 if err != nil { 311 log.Fatalf("Failed to re-encrypt: %s.", err) 312 } 313 314 case revealCommand.FullCommand(): 315 secretsOptions, err := cli.NewSecretsOptions( 316 globalOptions, 317 *paramDirFlag, 318 *publicKeyDirFlag, 319 *privateKeyFlag, 320 *passphraseFlag, 321 ) 322 if err != nil { 323 log.Fatalln("Options could not be processed:", err) 324 } 325 err = commands.Reveal(secretsOptions, *revealFileArg) 326 if err != nil { 327 log.Fatalf("Failed to reveal file: %s.", err) 328 } 329 330 case generateKeyCommand.FullCommand(): 331 secretsOptions, err := cli.NewSecretsOptions( 332 globalOptions, 333 *paramDirFlag, 334 *publicKeyDirFlag, 335 *privateKeyFlag, 336 *passphraseFlag, 337 ) 338 if err != nil { 339 log.Fatalln("Options could not be processed:", err) 340 } 341 err = commands.GenerateKey(secretsOptions, *generateKeyEmailArg, *generateKeyNameFlag) 342 if err != nil { 343 log.Fatalf("Failed to generate keypair: %s.", err) 344 } 345 346 case diffCommand.FullCommand(): 347 preservePathFlag := *diffPreservePathFlag 348 preservePathFlag = append(preservePathFlag, *diffIgnorePathFlag...) 349 compareOptions, err := cli.NewCompareOptions( 350 globalOptions, 351 *namespaceFlag, 352 *selectorFlag, 353 *excludeFlag, 354 *templateDirFlag, 355 *paramDirFlag, 356 *publicKeyDirFlag, 357 *privateKeyFlag, 358 *passphraseFlag, 359 *diffLabelsFlag, 360 *diffParamFlag, 361 *diffParamFileFlag, 362 preservePathFlag, 363 *diffPreserveImmutableFieldsFlag, 364 *diffIgnoreUnknownParametersFlag, 365 *diffUpsertOnlyFlag, 366 *diffAllowRecreateFlag, 367 *diffRevealSecretsFlag, 368 false, // verification only when changes are applied 369 *diffResourceArg, 370 ) 371 if err != nil { 372 log.Fatalln("Options could not be processed:", err) 373 } 374 375 driftDectected, err := commands.Diff(compareOptions) 376 if err != nil { 377 log.Fatalln(err) 378 } 379 if driftDectected { 380 os.Exit(3) 381 } 382 383 case applyCommand.FullCommand(): 384 preservePathFlag := *applyPreservePathFlag 385 preservePathFlag = append(preservePathFlag, *applyIgnorePathFlag...) 386 compareOptions, err := cli.NewCompareOptions( 387 globalOptions, 388 *namespaceFlag, 389 *selectorFlag, 390 *excludeFlag, 391 *templateDirFlag, 392 *paramDirFlag, 393 *publicKeyDirFlag, 394 *privateKeyFlag, 395 *passphraseFlag, 396 *applyLabelsFlag, 397 *applyParamFlag, 398 *applyParamFileFlag, 399 preservePathFlag, 400 *applyPreserveImmutableFieldsFlag, 401 *applyIgnoreUnknownParametersFlag, 402 *applyUpsertOnlyFlag, 403 *applyAllowRecreateFlag, 404 *applyRevealSecretsFlag, 405 *applyVerifyFlag, 406 *applyResourceArg, 407 ) 408 if err != nil { 409 log.Fatalln("Options could not be processed:", err) 410 } 411 412 ocClient := cli.NewOcClient(compareOptions.Namespace) 413 driftDectected, err := commands.Apply( 414 globalOptions.NonInteractive, 415 compareOptions, 416 ocClient, 417 os.Stdin, 418 ) 419 if err != nil { 420 log.Fatalln(err) 421 } 422 if driftDectected { 423 os.Exit(3) 424 } 425 426 case exportCommand.FullCommand(): 427 exportOptions, err := cli.NewExportOptions( 428 globalOptions, 429 *namespaceFlag, 430 *selectorFlag, 431 *excludeFlag, 432 *templateDirFlag, 433 *paramDirFlag, 434 *exportWithAnnotationsFlag, 435 *exportWithHardcodedNamespaceFlag, 436 *exportTrimAnnotationFlag, 437 *exportResourceArg, 438 ) 439 if err != nil { 440 log.Fatalln("Options could not be processed:", err) 441 } 442 err = commands.Export(exportOptions) 443 if err != nil { 444 log.Fatalln(err) 445 } 446 } 447 }