github.com/qri-io/qri@v0.10.1-0.20220104210721-c771715036cb/cmd/connect.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"github.com/qri-io/ioes"
     9  	"github.com/qri-io/qri/api"
    10  	"github.com/qri-io/qri/lib"
    11  	"github.com/spf13/cobra"
    12  )
    13  
    14  // NewConnectCommand creates a new `qri connect` cobra command for connecting to the d.web, local api, and rpc server
    15  func NewConnectCommand(f Factory, ioStreams ioes.IOStreams) *cobra.Command {
    16  	o := ConnectOptions{IOStreams: ioStreams}
    17  	cmd := &cobra.Command{
    18  		Use:   "connect",
    19  		Short: "connect to the distributed web by spinning up a Qri node",
    20  		Annotations: map[string]string{
    21  			"group": "network",
    22  		},
    23  		Long: `While it’s not totally accurate, connect is like starting a server. Running 
    24  connect will start a process and stay there until you exit the process 
    25  (ctrl+c from the terminal, or killing the process using tools like activity 
    26  monitor on the mac, or the aptly-named “kill” command). Connect does three main 
    27  things:
    28  - Connect to the qri distributed network
    29  - Connect to IPFS
    30  - Start a local API server
    31  
    32  When you run connect you are connecting to the distributed web, interacting with
    33  peers & swapping data.`,
    34  		Args: cobra.NoArgs,
    35  		RunE: func(cmd *cobra.Command, args []string) error {
    36  			if err := o.Complete(f, args); err != nil {
    37  				return err
    38  			}
    39  			return o.Run()
    40  		},
    41  	}
    42  
    43  	cmd.Flags().BoolVarP(&o.Setup, "setup", "", false, "run setup if necessary, reading options from environment variables")
    44  	cmd.Flags().StringVarP(&o.Registry, "registry", "", "", "specify registry to setup with. only works when --setup is true")
    45  
    46  	return cmd
    47  }
    48  
    49  // ConnectOptions encapsulates state for the connect command
    50  type ConnectOptions struct {
    51  	ioes.IOStreams
    52  	inst     *lib.Instance
    53  	Registry string
    54  	Setup    bool
    55  }
    56  
    57  // Complete adds any missing configuration that can only be added just before calling Run
    58  func (o *ConnectOptions) Complete(f Factory, args []string) (err error) {
    59  
    60  	if o.Setup {
    61  		repoErr := lib.QriRepoExists(f.RepoPath())
    62  		if errors.Is(repoErr, lib.ErrNoRepo) {
    63  			so := &SetupOptions{
    64  				IOStreams: o.IOStreams,
    65  				IPFS:      true,
    66  				Registry:  o.Registry,
    67  				Anonymous: true,
    68  			}
    69  			if err = so.Complete(f, args); err != nil {
    70  				return
    71  			}
    72  			if err = so.DoSetup(f); err != nil {
    73  				return
    74  			}
    75  		} else if repoErr != nil {
    76  			return
    77  		}
    78  	}
    79  
    80  	if err = f.Init(); err != nil {
    81  		return
    82  	}
    83  
    84  	// This fails whenever `qri connect` runs but another instance of `qri connect` is already
    85  	// running. If early in the connection process, this call to ConnectionNode will return an
    86  	// error. If later in the process, ConnectionNode will return without error but also with
    87  	// no node allocated. Without this check, later code will fail or segfault, might as well
    88  	// fail early.
    89  	n, err := f.ConnectionNode()
    90  	if err != nil {
    91  		return fmt.Errorf("%s, is `qri connect` already running?", err)
    92  	}
    93  	if n == nil {
    94  		return fmt.Errorf("Cannot serve without a node (`qri connect` or Qri Desktop already running?)")
    95  	}
    96  
    97  	o.inst, err = f.Instance()
    98  	return
    99  }
   100  
   101  // Run executes the connect command with currently configured state
   102  func (o *ConnectOptions) Run() error {
   103  	ctx := context.Background()
   104  	// NOTE: the `Serve` context is not tied to the context of the instance itself
   105  	err := api.New(o.inst).Serve(ctx)
   106  	if err != nil && err.Error() == "http: Server closed" {
   107  		return nil
   108  	}
   109  	return err
   110  }