github.com/mutagen-io/mutagen@v0.18.0-rc1/cmd/mutagen/sync/create.go (about) 1 package sync 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "strings" 9 10 "github.com/spf13/cobra" 11 "github.com/spf13/pflag" 12 13 "google.golang.org/grpc" 14 15 "github.com/dustin/go-humanize" 16 17 "github.com/mutagen-io/mutagen/cmd" 18 "github.com/mutagen-io/mutagen/cmd/mutagen/daemon" 19 20 "github.com/mutagen-io/mutagen/pkg/configuration/global" 21 "github.com/mutagen-io/mutagen/pkg/filesystem" 22 "github.com/mutagen-io/mutagen/pkg/filesystem/behavior" 23 "github.com/mutagen-io/mutagen/pkg/grpcutil" 24 "github.com/mutagen-io/mutagen/pkg/selection" 25 promptingsvc "github.com/mutagen-io/mutagen/pkg/service/prompting" 26 synchronizationsvc "github.com/mutagen-io/mutagen/pkg/service/synchronization" 27 "github.com/mutagen-io/mutagen/pkg/synchronization" 28 "github.com/mutagen-io/mutagen/pkg/synchronization/compression" 29 "github.com/mutagen-io/mutagen/pkg/synchronization/core" 30 "github.com/mutagen-io/mutagen/pkg/synchronization/core/ignore" 31 "github.com/mutagen-io/mutagen/pkg/synchronization/hashing" 32 "github.com/mutagen-io/mutagen/pkg/url" 33 ) 34 35 // loadAndValidateGlobalSynchronizationConfiguration loads a YAML-based global 36 // configuration, extracts the synchronization component, converts it to a 37 // Protocol Buffers session configuration, and validates it. 38 func loadAndValidateGlobalSynchronizationConfiguration(path string) (*synchronization.Configuration, error) { 39 // Load the YAML configuration. 40 yamlConfiguration, err := global.LoadConfiguration(path) 41 if err != nil { 42 return nil, err 43 } 44 45 // Convert the YAML configuration to a Protocol Buffers representation and 46 // validate it. 47 configuration := yamlConfiguration.Synchronization.Defaults.ToInternal() 48 if err := configuration.EnsureValid(false); err != nil { 49 return nil, fmt.Errorf("invalid configuration: %w", err) 50 } 51 52 // Success. 53 return configuration, nil 54 } 55 56 // CreateWithSpecification is an orchestration convenience method that performs 57 // a create operation using the provided daemon connection and session 58 // specification. 59 func CreateWithSpecification( 60 daemonConnection *grpc.ClientConn, 61 specification *synchronizationsvc.CreationSpecification, 62 ) (string, error) { 63 // Initiate command line prompting. 64 statusLinePrinter := &cmd.StatusLinePrinter{} 65 promptingCtx, promptingCancel := context.WithCancel(context.Background()) 66 prompter, promptingErrors, err := promptingsvc.Host( 67 promptingCtx, promptingsvc.NewPromptingClient(daemonConnection), 68 &cmd.StatusLinePrompter{Printer: statusLinePrinter}, true, 69 ) 70 if err != nil { 71 promptingCancel() 72 return "", fmt.Errorf("unable to initiate prompting: %w", err) 73 } 74 75 // Perform the create operation, cancel prompting, and handle errors. 76 synchronizationService := synchronizationsvc.NewSynchronizationClient(daemonConnection) 77 request := &synchronizationsvc.CreateRequest{ 78 Prompter: prompter, 79 Specification: specification, 80 } 81 response, err := synchronizationService.Create(context.Background(), request) 82 promptingCancel() 83 <-promptingErrors 84 if err != nil { 85 statusLinePrinter.BreakIfPopulated() 86 return "", grpcutil.PeelAwayRPCErrorLayer(err) 87 } else if err = response.EnsureValid(); err != nil { 88 statusLinePrinter.BreakIfPopulated() 89 return "", fmt.Errorf("invalid create response received: %w", err) 90 } 91 92 // Success. 93 statusLinePrinter.Clear() 94 return response.Session, nil 95 } 96 97 // createMain is the entry point for the create command. 98 func createMain(_ *cobra.Command, arguments []string) error { 99 // Validate, extract, and parse URLs. 100 if len(arguments) != 2 { 101 return errors.New("invalid number of endpoint URLs provided") 102 } 103 alpha, err := url.Parse(arguments[0], url.Kind_Synchronization, true) 104 if err != nil { 105 return fmt.Errorf("unable to parse alpha URL: %w", err) 106 } 107 beta, err := url.Parse(arguments[1], url.Kind_Synchronization, false) 108 if err != nil { 109 return fmt.Errorf("unable to parse beta URL: %w", err) 110 } 111 112 // Validate the name. 113 if err := selection.EnsureNameValid(createConfiguration.name); err != nil { 114 return fmt.Errorf("invalid session name: %w", err) 115 } 116 117 // Parse, validate, and record labels. 118 var labels map[string]string 119 if len(createConfiguration.labels) > 0 { 120 labels = make(map[string]string, len(createConfiguration.labels)) 121 } 122 for _, label := range createConfiguration.labels { 123 components := strings.SplitN(label, "=", 2) 124 var key, value string 125 key = components[0] 126 if len(components) == 2 { 127 value = components[1] 128 } 129 if err := selection.EnsureLabelKeyValid(key); err != nil { 130 return fmt.Errorf("invalid label key: %w", err) 131 } else if err := selection.EnsureLabelValueValid(value); err != nil { 132 return fmt.Errorf("invalid label value: %w", err) 133 } 134 labels[key] = value 135 } 136 137 // Create a default session configuration that will form the basis of our 138 // cumulative configuration. 139 configuration := &synchronization.Configuration{} 140 141 // Unless disabled, attempt to load configuration from the global 142 // configuration file and merge it into our cumulative configuration. 143 if !createConfiguration.noGlobalConfiguration { 144 // Compute the path to the global configuration file. 145 globalConfigurationPath, err := global.ConfigurationPath() 146 if err != nil { 147 return fmt.Errorf("unable to compute path to global configuration file: %w", err) 148 } 149 150 // Attempt to load the file. We allow it to not exist. 151 globalConfiguration, err := loadAndValidateGlobalSynchronizationConfiguration(globalConfigurationPath) 152 if err != nil { 153 if !os.IsNotExist(err) { 154 return fmt.Errorf("unable to load global configuration: %w", err) 155 } 156 } else { 157 configuration = synchronization.MergeConfigurations(configuration, globalConfiguration) 158 } 159 } 160 161 // If additional default configuration files have been specified, then load 162 // them and merge them into the cumulative configuration. 163 for _, configurationFile := range createConfiguration.configurationFiles { 164 if c, err := loadAndValidateGlobalSynchronizationConfiguration(configurationFile); err != nil { 165 return fmt.Errorf("unable to load configuration file (%s): %w", configurationFile, err) 166 } else { 167 configuration = synchronization.MergeConfigurations(configuration, c) 168 } 169 } 170 171 // Validate and convert the synchronization mode specification. 172 var synchronizationMode core.SynchronizationMode 173 if createConfiguration.synchronizationMode != "" { 174 if err := synchronizationMode.UnmarshalText([]byte(createConfiguration.synchronizationMode)); err != nil { 175 return fmt.Errorf("unable to parse synchronization mode: %w", err) 176 } 177 } 178 179 // Validate and convert the hashing algorithm specification. 180 var hashingAlgorithm hashing.Algorithm 181 if createConfiguration.hash != "" { 182 if err := hashingAlgorithm.UnmarshalText([]byte(createConfiguration.hash)); err != nil { 183 return fmt.Errorf("unable to parse hashing algorithm: %w", err) 184 } 185 } 186 187 // There's no need to validate the maximum entry count - any uint64 value is 188 // valid. 189 190 // Validate and convert the maximum staging file size. 191 var maximumStagingFileSize uint64 192 if createConfiguration.maximumStagingFileSize != "" { 193 if s, err := humanize.ParseBytes(createConfiguration.maximumStagingFileSize); err != nil { 194 return fmt.Errorf("unable to parse maximum staging file size: %w", err) 195 } else { 196 maximumStagingFileSize = s 197 } 198 } 199 200 // Validate and convert probe mode specifications. 201 var probeMode, probeModeAlpha, probeModeBeta behavior.ProbeMode 202 if createConfiguration.probeMode != "" { 203 if err := probeMode.UnmarshalText([]byte(createConfiguration.probeMode)); err != nil { 204 return fmt.Errorf("unable to parse probe mode: %w", err) 205 } 206 } 207 if createConfiguration.probeModeAlpha != "" { 208 if err := probeModeAlpha.UnmarshalText([]byte(createConfiguration.probeModeAlpha)); err != nil { 209 return fmt.Errorf("unable to parse probe mode for alpha: %w", err) 210 } 211 } 212 if createConfiguration.probeModeBeta != "" { 213 if err := probeModeBeta.UnmarshalText([]byte(createConfiguration.probeModeBeta)); err != nil { 214 return fmt.Errorf("unable to parse probe mode for beta: %w", err) 215 } 216 } 217 218 // Validate and convert scan mode specifications. 219 var scanMode, scanModeAlpha, scanModeBeta synchronization.ScanMode 220 if createConfiguration.scanMode != "" { 221 if err := scanMode.UnmarshalText([]byte(createConfiguration.scanMode)); err != nil { 222 return fmt.Errorf("unable to parse scan mode: %w", err) 223 } 224 } 225 if createConfiguration.scanModeAlpha != "" { 226 if err := scanModeAlpha.UnmarshalText([]byte(createConfiguration.scanModeAlpha)); err != nil { 227 return fmt.Errorf("unable to parse scan mode for alpha: %w", err) 228 } 229 } 230 if createConfiguration.scanModeBeta != "" { 231 if err := scanModeBeta.UnmarshalText([]byte(createConfiguration.scanModeBeta)); err != nil { 232 return fmt.Errorf("unable to parse scan mode for beta: %w", err) 233 } 234 } 235 236 // Validate and convert staging mode specifications. 237 var stageMode, stageModeAlpha, stageModeBeta synchronization.StageMode 238 if createConfiguration.stageMode != "" { 239 if err := stageMode.UnmarshalText([]byte(createConfiguration.stageMode)); err != nil { 240 return fmt.Errorf("unable to parse staging mode: %w", err) 241 } 242 } 243 if createConfiguration.stageModeAlpha != "" { 244 if err := stageModeAlpha.UnmarshalText([]byte(createConfiguration.stageModeAlpha)); err != nil { 245 return fmt.Errorf("unable to parse staging mode for alpha: %w", err) 246 } 247 } 248 if createConfiguration.stageModeBeta != "" { 249 if err := stageModeBeta.UnmarshalText([]byte(createConfiguration.stageModeBeta)); err != nil { 250 return fmt.Errorf("unable to parse staging mode for beta: %w", err) 251 } 252 } 253 254 // Validate and convert the symbolic link mode specification. 255 var symbolicLinkMode core.SymbolicLinkMode 256 if createConfiguration.symbolicLinkMode != "" { 257 if err := symbolicLinkMode.UnmarshalText([]byte(createConfiguration.symbolicLinkMode)); err != nil { 258 return fmt.Errorf("unable to parse symbolic link mode: %w", err) 259 } 260 } 261 262 // Validate and convert watch mode specifications. 263 var watchMode, watchModeAlpha, watchModeBeta synchronization.WatchMode 264 if createConfiguration.watchMode != "" { 265 if err := watchMode.UnmarshalText([]byte(createConfiguration.watchMode)); err != nil { 266 return fmt.Errorf("unable to parse watch mode: %w", err) 267 } 268 } 269 if createConfiguration.watchModeAlpha != "" { 270 if err := watchModeAlpha.UnmarshalText([]byte(createConfiguration.watchModeAlpha)); err != nil { 271 return fmt.Errorf("unable to parse watch mode for alpha: %w", err) 272 } 273 } 274 if createConfiguration.watchModeBeta != "" { 275 if err := watchModeBeta.UnmarshalText([]byte(createConfiguration.watchModeBeta)); err != nil { 276 return fmt.Errorf("unable to parse watch mode for beta: %w", err) 277 } 278 } 279 280 // There's no need to validate the watch polling intervals - any uint32 281 // values are valid. 282 283 // Validate and convert the ignore syntax specification. 284 var ignoreSyntax ignore.Syntax 285 if createConfiguration.ignoreSyntax != "" { 286 if err := ignoreSyntax.UnmarshalText([]byte(createConfiguration.ignoreSyntax)); err != nil { 287 return fmt.Errorf("unable to parse ignore syntax: %w", err) 288 } 289 } 290 291 // Unfortunately we can't validate ignore specifications in any meaningful 292 // way because we don't yet know the ignore syntax being used. This could be 293 // specified by the global YAML configuration or (more likely) determined by 294 // the default session version within the daemon. These ignores will 295 // eventually be validated at endpoint initialization time, but there's no 296 // convenient way to do it earlier in the session creation process. 297 298 // Validate and convert the VCS ignore mode specification. 299 var ignoreVCSMode ignore.IgnoreVCSMode 300 if createConfiguration.ignoreVCS && createConfiguration.noIgnoreVCS { 301 return errors.New("conflicting VCS ignore behavior specified") 302 } else if createConfiguration.ignoreVCS { 303 ignoreVCSMode = ignore.IgnoreVCSMode_IgnoreVCSModeIgnore 304 } else if createConfiguration.noIgnoreVCS { 305 ignoreVCSMode = ignore.IgnoreVCSMode_IgnoreVCSModePropagate 306 } 307 308 // Validate and convert the permissions mode specification. 309 var permissionsMode core.PermissionsMode 310 if createConfiguration.permissionsMode != "" { 311 if err := permissionsMode.UnmarshalText([]byte(createConfiguration.permissionsMode)); err != nil { 312 return fmt.Errorf("unable to parse permissions mode: %w", err) 313 } 314 } 315 316 // Compute the effective permissions mode. 317 // HACK: We technically don't know the daemon's default session version, so 318 // we compute the default permissions mode using the default session version 319 // for this executable, which (given our current distribution strategy) will 320 // be the same as that of the daemon. Of course, the daemon API will 321 // re-validate this, so validation here is merely best-effort and 322 // informational in any case. For more information on the reasoning behind 323 // this, see the note in synchronization.Version.DefaultPermissionsMode. 324 effectivePermissionsMode := permissionsMode 325 if effectivePermissionsMode.IsDefault() { 326 effectivePermissionsMode = synchronization.DefaultVersion.DefaultPermissionsMode() 327 } 328 329 // Validate and convert default file mode specifications. 330 var defaultFileMode, defaultFileModeAlpha, defaultFileModeBeta filesystem.Mode 331 if createConfiguration.defaultFileMode != "" { 332 if err := defaultFileMode.UnmarshalText([]byte(createConfiguration.defaultFileMode)); err != nil { 333 return fmt.Errorf("unable to parse default file mode: %w", err) 334 } else if err = core.EnsureDefaultFileModeValid(effectivePermissionsMode, defaultFileMode); err != nil { 335 return fmt.Errorf("invalid default file mode: %w", err) 336 } 337 } 338 if createConfiguration.defaultFileModeAlpha != "" { 339 if err := defaultFileModeAlpha.UnmarshalText([]byte(createConfiguration.defaultFileModeAlpha)); err != nil { 340 return fmt.Errorf("unable to parse default file mode for alpha: %w", err) 341 } else if err = core.EnsureDefaultFileModeValid(effectivePermissionsMode, defaultFileModeAlpha); err != nil { 342 return fmt.Errorf("invalid default file mode for alpha: %w", err) 343 } 344 } 345 if createConfiguration.defaultFileModeBeta != "" { 346 if err := defaultFileModeBeta.UnmarshalText([]byte(createConfiguration.defaultFileModeBeta)); err != nil { 347 return fmt.Errorf("unable to parse default file mode for beta: %w", err) 348 } else if err = core.EnsureDefaultFileModeValid(effectivePermissionsMode, defaultFileModeBeta); err != nil { 349 return fmt.Errorf("invalid default file mode for beta: %w", err) 350 } 351 } 352 353 // Validate and convert default directory mode specifications. 354 var defaultDirectoryMode, defaultDirectoryModeAlpha, defaultDirectoryModeBeta filesystem.Mode 355 if createConfiguration.defaultDirectoryMode != "" { 356 if err := defaultDirectoryMode.UnmarshalText([]byte(createConfiguration.defaultDirectoryMode)); err != nil { 357 return fmt.Errorf("unable to parse default directory mode: %w", err) 358 } else if err = core.EnsureDefaultDirectoryModeValid(effectivePermissionsMode, defaultDirectoryMode); err != nil { 359 return fmt.Errorf("invalid default directory mode: %w", err) 360 } 361 } 362 if createConfiguration.defaultDirectoryModeAlpha != "" { 363 if err := defaultDirectoryModeAlpha.UnmarshalText([]byte(createConfiguration.defaultDirectoryModeAlpha)); err != nil { 364 return fmt.Errorf("unable to parse default directory mode for alpha: %w", err) 365 } else if err = core.EnsureDefaultDirectoryModeValid(effectivePermissionsMode, defaultDirectoryModeAlpha); err != nil { 366 return fmt.Errorf("invalid default directory mode for alpha: %w", err) 367 } 368 } 369 if createConfiguration.defaultDirectoryModeBeta != "" { 370 if err := defaultDirectoryModeBeta.UnmarshalText([]byte(createConfiguration.defaultDirectoryModeBeta)); err != nil { 371 return fmt.Errorf("unable to parse default directory mode for beta: %w", err) 372 } else if err = core.EnsureDefaultDirectoryModeValid(effectivePermissionsMode, defaultDirectoryModeBeta); err != nil { 373 return fmt.Errorf("invalid default directory mode for beta: %w", err) 374 } 375 } 376 377 // Validate default file owner specifications. 378 if createConfiguration.defaultOwner != "" { 379 if kind, _ := filesystem.ParseOwnershipIdentifier( 380 createConfiguration.defaultOwner, 381 ); kind == filesystem.OwnershipIdentifierKindInvalid { 382 return errors.New("invalid ownership specification") 383 } 384 } 385 if createConfiguration.defaultOwnerAlpha != "" { 386 if kind, _ := filesystem.ParseOwnershipIdentifier( 387 createConfiguration.defaultOwnerAlpha, 388 ); kind == filesystem.OwnershipIdentifierKindInvalid { 389 return errors.New("invalid ownership specification for alpha") 390 } 391 } 392 if createConfiguration.defaultOwnerBeta != "" { 393 if kind, _ := filesystem.ParseOwnershipIdentifier( 394 createConfiguration.defaultOwnerBeta, 395 ); kind == filesystem.OwnershipIdentifierKindInvalid { 396 return errors.New("invalid ownership specification for beta") 397 } 398 } 399 400 // Validate default file group specifications. 401 if createConfiguration.defaultGroup != "" { 402 if kind, _ := filesystem.ParseOwnershipIdentifier( 403 createConfiguration.defaultGroup, 404 ); kind == filesystem.OwnershipIdentifierKindInvalid { 405 return errors.New("invalid group specification") 406 } 407 } 408 if createConfiguration.defaultGroupAlpha != "" { 409 if kind, _ := filesystem.ParseOwnershipIdentifier( 410 createConfiguration.defaultGroupAlpha, 411 ); kind == filesystem.OwnershipIdentifierKindInvalid { 412 return errors.New("invalid group specification for alpha") 413 } 414 } 415 if createConfiguration.defaultGroupBeta != "" { 416 if kind, _ := filesystem.ParseOwnershipIdentifier( 417 createConfiguration.defaultGroupBeta, 418 ); kind == filesystem.OwnershipIdentifierKindInvalid { 419 return errors.New("invalid group specification for beta") 420 } 421 } 422 423 // Validate and convert compression algorithm specifications. 424 var compressionAlgorithm, compressionAlgorithmAlpha, compressionAlgorithmBeta compression.Algorithm 425 if createConfiguration.compression != "" { 426 if err := compressionAlgorithm.UnmarshalText([]byte(createConfiguration.compression)); err != nil { 427 return fmt.Errorf("unable to parse compression algorithm: %w", err) 428 } 429 } 430 if createConfiguration.compressionAlpha != "" { 431 if err := compressionAlgorithmAlpha.UnmarshalText([]byte(createConfiguration.compressionAlpha)); err != nil { 432 return fmt.Errorf("unable to parse compression algorithm for alpha: %w", err) 433 } 434 } 435 if createConfiguration.compressionBeta != "" { 436 if err := compressionAlgorithmBeta.UnmarshalText([]byte(createConfiguration.compressionBeta)); err != nil { 437 return fmt.Errorf("unable to parse compression algorithm for beta: %w", err) 438 } 439 } 440 441 // Create the command line configuration and merge it into our cumulative 442 // configuration. 443 configuration = synchronization.MergeConfigurations(configuration, &synchronization.Configuration{ 444 SynchronizationMode: synchronizationMode, 445 HashingAlgorithm: hashingAlgorithm, 446 MaximumEntryCount: createConfiguration.maximumEntryCount, 447 MaximumStagingFileSize: maximumStagingFileSize, 448 ProbeMode: probeMode, 449 ScanMode: scanMode, 450 StageMode: stageMode, 451 SymbolicLinkMode: symbolicLinkMode, 452 WatchMode: watchMode, 453 WatchPollingInterval: createConfiguration.watchPollingInterval, 454 IgnoreSyntax: ignoreSyntax, 455 Ignores: createConfiguration.ignores, 456 IgnoreVCSMode: ignoreVCSMode, 457 PermissionsMode: permissionsMode, 458 DefaultFileMode: uint32(defaultFileMode), 459 DefaultDirectoryMode: uint32(defaultDirectoryMode), 460 DefaultOwner: createConfiguration.defaultOwner, 461 DefaultGroup: createConfiguration.defaultGroup, 462 CompressionAlgorithm: compressionAlgorithm, 463 }) 464 465 // Create the creation specification. 466 specification := &synchronizationsvc.CreationSpecification{ 467 Alpha: alpha, 468 Beta: beta, 469 Configuration: configuration, 470 ConfigurationAlpha: &synchronization.Configuration{ 471 ProbeMode: probeModeAlpha, 472 ScanMode: scanModeAlpha, 473 StageMode: stageModeAlpha, 474 WatchMode: watchModeAlpha, 475 WatchPollingInterval: createConfiguration.watchPollingIntervalAlpha, 476 DefaultFileMode: uint32(defaultFileModeAlpha), 477 DefaultDirectoryMode: uint32(defaultDirectoryModeAlpha), 478 DefaultOwner: createConfiguration.defaultOwnerAlpha, 479 DefaultGroup: createConfiguration.defaultGroupAlpha, 480 CompressionAlgorithm: compressionAlgorithmAlpha, 481 }, 482 ConfigurationBeta: &synchronization.Configuration{ 483 ProbeMode: probeModeBeta, 484 ScanMode: scanModeBeta, 485 StageMode: stageModeBeta, 486 WatchMode: watchModeBeta, 487 WatchPollingInterval: createConfiguration.watchPollingIntervalBeta, 488 DefaultFileMode: uint32(defaultFileModeBeta), 489 DefaultDirectoryMode: uint32(defaultDirectoryModeBeta), 490 DefaultOwner: createConfiguration.defaultOwnerBeta, 491 DefaultGroup: createConfiguration.defaultGroupBeta, 492 CompressionAlgorithm: compressionAlgorithmBeta, 493 }, 494 Name: createConfiguration.name, 495 Labels: labels, 496 Paused: createConfiguration.paused, 497 } 498 499 // Connect to the daemon and defer closure of the connection. 500 daemonConnection, err := daemon.Connect(true, true) 501 if err != nil { 502 return fmt.Errorf("unable to connect to daemon: %w", err) 503 } 504 defer daemonConnection.Close() 505 506 // Perform the create operation. 507 identifier, err := CreateWithSpecification(daemonConnection, specification) 508 if err != nil { 509 return err 510 } 511 512 // Print the session identifier. 513 fmt.Println("Created session", identifier) 514 515 // Success. 516 return nil 517 } 518 519 // createCommand is the create command. 520 var createCommand = &cobra.Command{ 521 Use: "create <alpha> <beta>", 522 Short: "Create and start a new synchronization session", 523 RunE: createMain, 524 SilenceUsage: true, 525 } 526 527 // createConfiguration stores configuration for the create command. 528 var createConfiguration struct { 529 // help indicates whether or not to show help information and exit. 530 help bool 531 // name is the name specification for the session. 532 name string 533 // labels are the label specifications for the session. 534 labels []string 535 // paused indicates whether or not to create the session in a pre-paused 536 // state. 537 paused bool 538 // noGlobalConfiguration specifies whether or not the global configuration 539 // file should be ignored. 540 noGlobalConfiguration bool 541 // configurationFiles stores paths of additional files from which to load 542 // default configuration. 543 configurationFiles []string 544 // synchronizationMode specifies the synchronization mode for the session. 545 synchronizationMode string 546 // hash specifies the hashing algorithm to use for the session. 547 hash string 548 // maximumEntryCount specifies the maximum number of filesystem entries that 549 // endpoints will tolerate managing. 550 maximumEntryCount uint64 551 // maximumStagingFileSize is the maximum file size that endpoints will 552 // stage. It can be specified in human-friendly units. 553 maximumStagingFileSize string 554 // probeMode specifies the filesystem probing mode to use for the session. 555 probeMode string 556 // probeModeAlpha specifies the filesystem probing mode to use for the 557 // session, taking priority over probeMode on alpha if specified. 558 probeModeAlpha string 559 // probeModeBeta specifies the filesystem probing mode to use for the 560 // session, taking priority over probeMode on beta if specified. 561 probeModeBeta string 562 // scanMode specifies the scan mode to use for the session. 563 scanMode string 564 // scanModeAlpha specifies the scan mode to use for the session, taking 565 // priority over scanMode on alpha if specified. 566 scanModeAlpha string 567 // scanModeBeta specifies the scan mode to use for the session, taking 568 // priority over scanMode on beta if specified. 569 scanModeBeta string 570 // stageMode specifies the file staging mode to use for the session. 571 stageMode string 572 // stageModeAlpha specifies the file staging mode to use for the session, 573 // taking priority over stageMode on alpha if specified. 574 stageModeAlpha string 575 // stageModeBeta specifies the file staging mode to use for the session, 576 // taking priority over stageMode on beta if specified. 577 stageModeBeta string 578 // symbolicLinkMode specifies the symbolic link handling mode to use for 579 // the session. 580 symbolicLinkMode string 581 // watchMode specifies the filesystem watching mode to use for the session. 582 watchMode string 583 // watchModeAlpha specifies the filesystem watching mode to use for the 584 // session, taking priority over watchMode on alpha if specified. 585 watchModeAlpha string 586 // watchModeBeta specifies the filesystem watching mode to use for the 587 // session, taking priority over watchMode on beta if specified. 588 watchModeBeta string 589 // watchPollingInterval specifies the polling interval to use if using 590 // poll-based or hybrid watching. 591 watchPollingInterval uint32 592 // watchPollingIntervalAlpha specifies the polling interval to use if using 593 // poll-based or hybrid watching, taking priority over watchPollingInterval 594 // on alpha if specified. 595 watchPollingIntervalAlpha uint32 596 // watchPollingIntervalBeta specifies the polling interval to use if using 597 // poll-based or hybrid watching, taking priority over watchPollingInterval 598 // on beta if specified. 599 watchPollingIntervalBeta uint32 600 // ignoreSyntax specifies the ignore syntax and semantics for the session. 601 ignoreSyntax string 602 // ignores is the list of ignore specifications for the session. 603 ignores []string 604 // ignoreVCS specifies whether or not to enable VCS ignores for the session. 605 ignoreVCS bool 606 // noIgnoreVCS specifies whether or not to disable VCS ignores for the 607 // session. 608 noIgnoreVCS bool 609 // permissionsMode specifies the permissions mode to use for the session. 610 permissionsMode string 611 // defaultFileMode specifies the default permission mode to use for new 612 // files in "portable" permission propagation mode, with endpoint-specific 613 // specifications taking priority. 614 defaultFileMode string 615 // defaultFileModeAlpha specifies the default permission mode to use for new 616 // files on alpha in "portable" permission propagation mode, taking priority 617 // over defaultFileMode on alpha if specified. 618 defaultFileModeAlpha string 619 // defaultFileModeBeta specifies the default permission mode to use for new 620 // files on beta in "portable" permission propagation mode, taking priority 621 // over defaultFileMode on beta if specified. 622 defaultFileModeBeta string 623 // defaultDirectoryMode specifies the default permission mode to use for new 624 // directories in "portable" permission propagation mode, with endpoint- 625 // specific specifications taking priority. 626 defaultDirectoryMode string 627 // defaultDirectoryModeAlpha specifies the default permission mode to use 628 // for new directories on alpha in "portable" permission propagation mode, 629 // taking priority over defaultDirectoryMode on alpha if specified. 630 defaultDirectoryModeAlpha string 631 // defaultDirectoryModeBeta specifies the default permission mode to use for 632 // new directories on beta in "portable" permission propagation mode, taking 633 // priority over defaultDirectoryMode on beta if specified. 634 defaultDirectoryModeBeta string 635 // defaultOwner specifies the default owner identifier to use when setting 636 // ownership of new files and directories in "portable" permission 637 // propagation mode, with endpoint-specific specifications taking priority. 638 defaultOwner string 639 // defaultOwnerAlpha specifies the default owner identifier to use when 640 // setting ownership of new files and directories on alpha in "portable" 641 // permission propagation mode, taking priority over defaultOwner on alpha 642 // if specified. 643 defaultOwnerAlpha string 644 // defaultOwnerBeta specifies the default owner identifier to use when 645 // setting ownership of new files and directories on beta in "portable" 646 // permission propagation mode, taking priority over defaultOwner on beta if 647 // specified. 648 defaultOwnerBeta string 649 // defaultGroup specifies the default group identifier to use when setting 650 // ownership of new files and directories in "portable" permission 651 // propagation mode, with endpoint-specific specifications taking priority. 652 defaultGroup string 653 // defaultGroupAlpha specifies the default group identifier to use when 654 // setting ownership of new files and directories on alpha in "portable" 655 // permission propagation mode, taking priority over defaultGroup on alpha 656 // if specified. 657 defaultGroupAlpha string 658 // defaultGroupBeta specifies the default group identifier to use when 659 // setting ownership of new files and directories on beta in "portable" 660 // permission propagation mode, taking priority over defaultGroup on beta if 661 // specified. 662 defaultGroupBeta string 663 // compression specifies the compression algorithm to use when communicating 664 // with remote endpoints. 665 compression string 666 // compressionAlpha specifies the compression algorithm to use when 667 // communicating with a remote alpha endpoint. 668 compressionAlpha string 669 // compressionBeta specifies the compression algorithm to use when 670 // communicating with a remote beta endpoint. 671 compressionBeta string 672 } 673 674 func init() { 675 // Grab a handle for the command line flags. 676 flags := createCommand.Flags() 677 678 // Disable alphabetical sorting of flags in help output. 679 flags.SortFlags = false 680 681 // Manually add a help flag to override the default message. Cobra will 682 // still implement its logic automatically. 683 flags.BoolVarP(&createConfiguration.help, "help", "h", false, "Show help information") 684 685 // Wire up name and label flags. 686 flags.StringVarP(&createConfiguration.name, "name", "n", "", "Specify a name for the session") 687 flags.StringSliceVarP(&createConfiguration.labels, "label", "l", nil, "Specify labels") 688 689 // Wire up paused flags. 690 flags.BoolVarP(&createConfiguration.paused, "paused", "p", false, "Create the session pre-paused") 691 692 // Wire up general configuration flags. 693 flags.BoolVar(&createConfiguration.noGlobalConfiguration, "no-global-configuration", false, "Ignore the global configuration file") 694 flags.StringSliceVarP(&createConfiguration.configurationFiles, "configuration-file", "c", nil, "Specify additional files from which to load (and merge) default configuration parameters") 695 696 // Wire up synchronization flags. 697 flags.StringVarP(&createConfiguration.synchronizationMode, "mode", "m", "", "Specify synchronization mode (two-way-safe|two-way-resolved|one-way-safe|one-way-replica)") 698 flags.StringVarP(&createConfiguration.hash, "hash", "H", "", "Specify content hashing algorithm ("+hashFlagOptions+")") 699 flags.Uint64Var(&createConfiguration.maximumEntryCount, "max-entry-count", 0, "Specify the maximum number of entries that endpoints will manage") 700 flags.StringVar(&createConfiguration.maximumStagingFileSize, "max-staging-file-size", "", "Specify the maximum (individual) file size that endpoints will stage") 701 flags.StringVar(&createConfiguration.probeMode, "probe-mode", "", "Specify probe mode (probe|assume)") 702 flags.StringVar(&createConfiguration.probeModeAlpha, "probe-mode-alpha", "", "Specify probe mode for alpha (probe|assume)") 703 flags.StringVar(&createConfiguration.probeModeBeta, "probe-mode-beta", "", "Specify probe mode for beta (probe|assume)") 704 flags.StringVar(&createConfiguration.scanMode, "scan-mode", "", "Specify scan mode (full|accelerated)") 705 flags.StringVar(&createConfiguration.scanModeAlpha, "scan-mode-alpha", "", "Specify scan mode for alpha (full|accelerated)") 706 flags.StringVar(&createConfiguration.scanModeBeta, "scan-mode-beta", "", "Specify scan mode for beta (full|accelerated)") 707 flags.StringVar(&createConfiguration.stageMode, "stage-mode", "", "Specify staging mode (mutagen|neighboring)") 708 flags.StringVar(&createConfiguration.stageModeAlpha, "stage-mode-alpha", "", "Specify staging mode for alpha (mutagen|neighboring)") 709 flags.StringVar(&createConfiguration.stageModeBeta, "stage-mode-beta", "", "Specify staging mode for beta (mutagen|neighboring)") 710 711 // Wire up symbolic link flags. 712 flags.StringVar(&createConfiguration.symbolicLinkMode, "symlink-mode", "", "Specify symlink mode (ignore|portable|posix-raw)") 713 714 // Wire up watch flags. 715 flags.StringVar(&createConfiguration.watchMode, "watch-mode", "", "Specify watch mode (portable|force-poll|no-watch)") 716 flags.StringVar(&createConfiguration.watchModeAlpha, "watch-mode-alpha", "", "Specify watch mode for alpha (portable|force-poll|no-watch)") 717 flags.StringVar(&createConfiguration.watchModeBeta, "watch-mode-beta", "", "Specify watch mode for beta (portable|force-poll|no-watch)") 718 flags.Uint32Var(&createConfiguration.watchPollingInterval, "watch-polling-interval", 0, "Specify watch polling interval in seconds") 719 flags.Uint32Var(&createConfiguration.watchPollingIntervalAlpha, "watch-polling-interval-alpha", 0, "Specify watch polling interval in seconds for alpha") 720 flags.Uint32Var(&createConfiguration.watchPollingIntervalBeta, "watch-polling-interval-beta", 0, "Specify watch polling interval in seconds for beta") 721 722 // Wire up ignore flags. 723 flags.StringVar(&createConfiguration.ignoreSyntax, "ignore-syntax", "", "Specify ignore syntax (mutagen|docker)") 724 flags.StringSliceVarP(&createConfiguration.ignores, "ignore", "i", nil, "Specify ignore paths") 725 flags.BoolVar(&createConfiguration.ignoreVCS, "ignore-vcs", false, "Ignore VCS directories") 726 flags.BoolVar(&createConfiguration.noIgnoreVCS, "no-ignore-vcs", false, "Propagate VCS directories") 727 728 // Wire up permission flags. 729 flags.StringVar(&createConfiguration.permissionsMode, "permissions-mode", "", "Specify permissions mode (portable|manual)") 730 flags.StringVar(&createConfiguration.defaultFileMode, "default-file-mode", "", "Specify default file permission mode") 731 flags.StringVar(&createConfiguration.defaultFileModeAlpha, "default-file-mode-alpha", "", "Specify default file permission mode for alpha") 732 flags.StringVar(&createConfiguration.defaultFileModeBeta, "default-file-mode-beta", "", "Specify default file permission mode for beta") 733 flags.StringVar(&createConfiguration.defaultDirectoryMode, "default-directory-mode", "", "Specify default directory permission mode") 734 flags.StringVar(&createConfiguration.defaultDirectoryModeAlpha, "default-directory-mode-alpha", "", "Specify default directory permission mode for alpha") 735 flags.StringVar(&createConfiguration.defaultDirectoryModeBeta, "default-directory-mode-beta", "", "Specify default directory permission mode for beta") 736 flags.StringVar(&createConfiguration.defaultOwner, "default-owner", "", "Specify default file/directory owner") 737 flags.StringVar(&createConfiguration.defaultOwnerAlpha, "default-owner-alpha", "", "Specify default file/directory owner for alpha") 738 flags.StringVar(&createConfiguration.defaultOwnerBeta, "default-owner-beta", "", "Specify default file/directory owner for beta") 739 flags.StringVar(&createConfiguration.defaultGroup, "default-group", "", "Specify default file/directory group") 740 flags.StringVar(&createConfiguration.defaultGroupAlpha, "default-group-alpha", "", "Specify default file/directory group for alpha") 741 flags.StringVar(&createConfiguration.defaultGroupBeta, "default-group-beta", "", "Specify default file/directory group for beta") 742 743 // Wire up compression flags. 744 flags.StringVarP(&createConfiguration.compression, "compression", "C", "", "Specify compression algorithm ("+compressionFlagOptions+")") 745 flags.StringVar(&createConfiguration.compressionAlpha, "compression-alpha", "", "Specify compression algorithm for alpha ("+compressionFlagOptions+")") 746 flags.StringVar(&createConfiguration.compressionBeta, "compression-beta", "", "Specify compression algorithm for beta ("+compressionFlagOptions+")") 747 748 // Set up flag normalization. This is only required to handle aliases. 749 flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName { 750 if name == "sync-mode" { 751 name = "mode" 752 } 753 return pflag.NormalizedName(name) 754 }) 755 }