github.com/pachyderm/pachyderm@v1.13.4/src/server/transaction/cmds/cmds.go (about)

     1  package cmds
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  
     7  	"github.com/gogo/protobuf/jsonpb"
     8  	"github.com/pachyderm/pachyderm/src/client"
     9  	"github.com/pachyderm/pachyderm/src/client/pkg/errors"
    10  	"github.com/pachyderm/pachyderm/src/client/pkg/grpcutil"
    11  	"github.com/pachyderm/pachyderm/src/client/transaction"
    12  	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
    13  	"github.com/pachyderm/pachyderm/src/server/pkg/tabwriter"
    14  	"github.com/pachyderm/pachyderm/src/server/transaction/pretty"
    15  	"github.com/spf13/cobra"
    16  	"github.com/spf13/pflag"
    17  )
    18  
    19  // Cmds returns the set of commands used for managing transactions with the
    20  // Pachyderm CLI tool pachctl.
    21  func Cmds() []*cobra.Command {
    22  	var commands []*cobra.Command
    23  
    24  	marshaller := &jsonpb.Marshaler{Indent: "  "}
    25  
    26  	raw := false
    27  	rawFlags := pflag.NewFlagSet("", pflag.ContinueOnError)
    28  	rawFlags.BoolVar(&raw, "raw", false, "disable pretty printing, print raw json")
    29  
    30  	fullTimestamps := false
    31  	fullTimestampsFlags := pflag.NewFlagSet("", pflag.ContinueOnError)
    32  	fullTimestampsFlags.BoolVar(&fullTimestamps, "full-timestamps", false, "Return absolute timestamps (as opposed to the default, relative timestamps).")
    33  
    34  	transactionDocs := &cobra.Command{
    35  		Short: "Docs for transactions.",
    36  		Long: `Transactions modify several Pachyderm objects in a single operation.
    37  
    38  The following pachctl commands are supported in transactions:
    39    create repo
    40    delete repo
    41    start commit
    42    finish commit
    43    delete commit
    44    create branch
    45    delete branch
    46    create pipeline
    47    update pipeline
    48  
    49  A transaction can be started with 'start transaction', after which the above
    50  commands will be stored in the transaction rather than immediately executed.
    51  The stored commands can be executed as a single operation with 'finish
    52  transaction' or cancelled with 'delete transaction'.`,
    53  	}
    54  	commands = append(commands, cmdutil.CreateDocsAlias(transactionDocs, "transaction", " transaction$"))
    55  
    56  	listTransaction := &cobra.Command{
    57  		Short: "List transactions.",
    58  		Long:  "List transactions.",
    59  		Run: cmdutil.RunFixedArgs(0, func([]string) error {
    60  			c, err := client.NewOnUserMachine("user")
    61  			if err != nil {
    62  				return err
    63  			}
    64  			defer c.Close()
    65  			transactions, err := c.ListTransaction()
    66  			if err != nil {
    67  				return err
    68  			}
    69  			if raw {
    70  				for _, transaction := range transactions {
    71  					if err := marshaller.Marshal(os.Stdout, transaction); err != nil {
    72  						return err
    73  					}
    74  				}
    75  				return nil
    76  			}
    77  			writer := tabwriter.NewWriter(os.Stdout, pretty.TransactionHeader)
    78  			for _, transaction := range transactions {
    79  				pretty.PrintTransactionInfo(writer, transaction, fullTimestamps)
    80  			}
    81  			return writer.Flush()
    82  		}),
    83  	}
    84  	listTransaction.Flags().AddFlagSet(rawFlags)
    85  	listTransaction.Flags().AddFlagSet(fullTimestampsFlags)
    86  	commands = append(commands, cmdutil.CreateAlias(listTransaction, "list transaction"))
    87  
    88  	startTransaction := &cobra.Command{
    89  		Short: "Start a new transaction.",
    90  		Long:  "Start a new transaction.",
    91  		Run: cmdutil.RunFixedArgs(0, func([]string) error {
    92  			c, err := client.NewOnUserMachine("user")
    93  			if err != nil {
    94  				return err
    95  			}
    96  			defer c.Close()
    97  			txn, err := getActiveTransaction()
    98  			if err != nil {
    99  				return err
   100  			}
   101  			if txn != nil {
   102  				return errors.Errorf("cannot start a new transaction, since transaction with ID %q already exists", txn.ID)
   103  			}
   104  
   105  			transaction, err := c.StartTransaction()
   106  			if err != nil {
   107  				return grpcutil.ScrubGRPC(err)
   108  			}
   109  			// TODO: use advisory locks on config so we don't have a race condition if
   110  			// two commands are run simultaneously
   111  			err = setActiveTransaction(transaction)
   112  			if err != nil {
   113  				return err
   114  			}
   115  			fmt.Printf("started new transaction: %q\n", transaction.ID)
   116  			return nil
   117  		}),
   118  	}
   119  	commands = append(commands, cmdutil.CreateAlias(startTransaction, "start transaction"))
   120  
   121  	stopTransaction := &cobra.Command{
   122  		Short: "Stop modifying the current transaction.",
   123  		Long:  "Stop modifying the current transaction.",
   124  		Run: cmdutil.RunFixedArgs(0, func([]string) error {
   125  			// TODO: use advisory locks on config so we don't have a race condition if
   126  			// two commands are run simultaneously
   127  			txn, err := requireActiveTransaction()
   128  			if err != nil {
   129  				return err
   130  			}
   131  
   132  			err = ClearActiveTransaction()
   133  			if err != nil {
   134  				return err
   135  			}
   136  
   137  			fmt.Printf("Cleared active transaction: %s\n", txn.ID)
   138  			return nil
   139  		}),
   140  	}
   141  	commands = append(commands, cmdutil.CreateAlias(stopTransaction, "stop transaction"))
   142  
   143  	finishTransaction := &cobra.Command{
   144  		Use:   "{{alias}} [<transaction>]",
   145  		Short: "Execute and clear the currently active transaction.",
   146  		Long:  "Execute and clear the currently active transaction.",
   147  		Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error {
   148  			c, err := client.NewOnUserMachine("user")
   149  			if err != nil {
   150  				return err
   151  			}
   152  			defer c.Close()
   153  
   154  			// TODO: use advisory locks on config so we don't have a race condition if
   155  			// two commands are run simultaneously
   156  			var txn *transaction.Transaction
   157  			if len(args) > 0 {
   158  				txn = &transaction.Transaction{ID: args[0]}
   159  			} else {
   160  				txn, err = requireActiveTransaction()
   161  				if err != nil {
   162  					return err
   163  				}
   164  			}
   165  
   166  			info, err := c.FinishTransaction(txn)
   167  			if err != nil {
   168  				return grpcutil.ScrubGRPC(err)
   169  			}
   170  
   171  			err = ClearActiveTransaction()
   172  			if err != nil {
   173  				return err
   174  			}
   175  
   176  			fmt.Printf("Completed transaction with %d requests: %s\n", len(info.Responses), info.Transaction.ID)
   177  			return nil
   178  		}),
   179  	}
   180  	commands = append(commands, cmdutil.CreateAlias(finishTransaction, "finish transaction"))
   181  
   182  	deleteTransaction := &cobra.Command{
   183  		Use:   "{{alias}} [<transaction>]",
   184  		Short: "Cancel and delete an existing transaction.",
   185  		Long:  "Cancel and delete an existing transaction.",
   186  		Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error {
   187  			c, err := client.NewOnUserMachine("user")
   188  			if err != nil {
   189  				return err
   190  			}
   191  			defer c.Close()
   192  
   193  			// TODO: use advisory locks on config so we don't have a race condition if
   194  			// two commands are run simultaneously
   195  			var txn *transaction.Transaction
   196  			isActive := false
   197  			if len(args) > 0 {
   198  				txn = &transaction.Transaction{ID: args[0]}
   199  
   200  				// Don't check err here, this is just a quality-of-life check to clean
   201  				// up the config after a successful delete
   202  				activeTxn, _ := requireActiveTransaction()
   203  				if activeTxn != nil {
   204  					isActive = txn.ID == activeTxn.ID
   205  				}
   206  			} else {
   207  				txn, err = requireActiveTransaction()
   208  				if err != nil {
   209  					return err
   210  				}
   211  				isActive = true
   212  			}
   213  
   214  			err = c.DeleteTransaction(txn)
   215  			if err != nil {
   216  				return grpcutil.ScrubGRPC(err)
   217  			}
   218  			if isActive {
   219  				// The active transaction was successfully deleted, clean it up so the
   220  				// user doesn't need to manually 'stop transaction' it.
   221  				ClearActiveTransaction()
   222  			}
   223  			return nil
   224  		}),
   225  	}
   226  	commands = append(commands, cmdutil.CreateAlias(deleteTransaction, "delete transaction"))
   227  
   228  	inspectTransaction := &cobra.Command{
   229  		Use:   "{{alias}} [<transaction>]",
   230  		Short: "Print information about an open transaction.",
   231  		Long:  "Print information about an open transaction.",
   232  		Run: cmdutil.RunBoundedArgs(0, 1, func(args []string) error {
   233  			c, err := client.NewOnUserMachine("user")
   234  			if err != nil {
   235  				return err
   236  			}
   237  			defer c.Close()
   238  
   239  			var txn *transaction.Transaction
   240  			if len(args) > 0 {
   241  				txn = &transaction.Transaction{ID: args[0]}
   242  			} else {
   243  				txn, err = requireActiveTransaction()
   244  				if err != nil {
   245  					return err
   246  				}
   247  			}
   248  
   249  			info, err := c.InspectTransaction(txn)
   250  			if err != nil {
   251  				return grpcutil.ScrubGRPC(err)
   252  			}
   253  			if info == nil {
   254  				return errors.Errorf("transaction %s not found", txn.ID)
   255  			}
   256  			if raw {
   257  				return marshaller.Marshal(os.Stdout, info)
   258  			}
   259  			return pretty.PrintDetailedTransactionInfo(&pretty.PrintableTransactionInfo{
   260  				TransactionInfo: info,
   261  				FullTimestamps:  fullTimestamps,
   262  			})
   263  		}),
   264  	}
   265  	inspectTransaction.Flags().AddFlagSet(rawFlags)
   266  	inspectTransaction.Flags().AddFlagSet(fullTimestampsFlags)
   267  	commands = append(commands, cmdutil.CreateAlias(inspectTransaction, "inspect transaction"))
   268  
   269  	resumeTransaction := &cobra.Command{
   270  		Use:   "{{alias}} <transaction>",
   271  		Short: "Set an existing transaction as active.",
   272  		Long:  "Set an existing transaction as active.",
   273  		Run: cmdutil.RunFixedArgs(1, func(args []string) error {
   274  			c, err := client.NewOnUserMachine("user")
   275  			if err != nil {
   276  				return err
   277  			}
   278  			defer c.Close()
   279  			info, err := c.InspectTransaction(&transaction.Transaction{ID: args[0]})
   280  			if err != nil {
   281  				return grpcutil.ScrubGRPC(err)
   282  			}
   283  			if info == nil {
   284  				return errors.Errorf("transaction %s not found", args[0])
   285  			}
   286  
   287  			err = setActiveTransaction(info.Transaction)
   288  			if err != nil {
   289  				return err
   290  			}
   291  
   292  			fmt.Printf("Resuming existing transaction with %d requests: %s\n", len(info.Requests), info.Transaction.ID)
   293  			return nil
   294  		}),
   295  	}
   296  	commands = append(commands, cmdutil.CreateAlias(resumeTransaction, "resume transaction"))
   297  
   298  	return commands
   299  }