github.com/cloudfoundry/cli@v7.1.0+incompatible/plugin/rpc/cli_rpc_server.go (about)

     1  package rpc
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  
     7  	"code.cloudfoundry.org/cli/cf/api"
     8  	"code.cloudfoundry.org/cli/cf/commandregistry"
     9  	"code.cloudfoundry.org/cli/cf/configuration/coreconfig"
    10  	"code.cloudfoundry.org/cli/cf/terminal"
    11  	"code.cloudfoundry.org/cli/plugin"
    12  	plugin_models "code.cloudfoundry.org/cli/plugin/models"
    13  	"code.cloudfoundry.org/cli/version"
    14  	"github.com/blang/semver"
    15  
    16  	"fmt"
    17  	"net"
    18  	"net/rpc"
    19  	"strconv"
    20  
    21  	"bytes"
    22  	"io"
    23  
    24  	"sync"
    25  
    26  	"code.cloudfoundry.org/cli/cf/trace"
    27  )
    28  
    29  var dialTimeout = os.Getenv("CF_DIAL_TIMEOUT")
    30  
    31  type CliRpcService struct {
    32  	listener net.Listener
    33  	stopCh   chan struct{}
    34  	Pinged   bool
    35  	RpcCmd   *CliRpcCmd
    36  	Server   *rpc.Server
    37  }
    38  
    39  type CliRpcCmd struct {
    40  	PluginMetadata       *plugin.PluginMetadata
    41  	MetadataMutex        *sync.RWMutex
    42  	outputCapture        OutputCapture
    43  	terminalOutputSwitch TerminalOutputSwitch
    44  	cliConfig            coreconfig.Repository
    45  	repoLocator          api.RepositoryLocator
    46  	newCmdRunner         CommandRunner
    47  	outputBucket         *bytes.Buffer
    48  	logger               trace.Printer
    49  	stdout               io.Writer
    50  }
    51  
    52  //go:generate counterfeiter . TerminalOutputSwitch
    53  
    54  type TerminalOutputSwitch interface {
    55  	DisableTerminalOutput(bool)
    56  }
    57  
    58  //go:generate counterfeiter . OutputCapture
    59  
    60  type OutputCapture interface {
    61  	SetOutputBucket(io.Writer)
    62  }
    63  
    64  func NewRpcService(
    65  	outputCapture OutputCapture,
    66  	terminalOutputSwitch TerminalOutputSwitch,
    67  	cliConfig coreconfig.Repository,
    68  	repoLocator api.RepositoryLocator,
    69  	newCmdRunner CommandRunner,
    70  	logger trace.Printer,
    71  	w io.Writer,
    72  	rpcServer *rpc.Server,
    73  ) (*CliRpcService, error) {
    74  	rpcService := &CliRpcService{
    75  		Server: rpcServer,
    76  		RpcCmd: &CliRpcCmd{
    77  			PluginMetadata:       &plugin.PluginMetadata{},
    78  			MetadataMutex:        &sync.RWMutex{},
    79  			outputCapture:        outputCapture,
    80  			terminalOutputSwitch: terminalOutputSwitch,
    81  			cliConfig:            cliConfig,
    82  			repoLocator:          repoLocator,
    83  			newCmdRunner:         newCmdRunner,
    84  			logger:               logger,
    85  			outputBucket:         &bytes.Buffer{},
    86  			stdout:               w,
    87  		},
    88  	}
    89  
    90  	err := rpcService.Server.Register(rpcService.RpcCmd)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	return rpcService, nil
    96  }
    97  
    98  func (cli *CliRpcService) Stop() {
    99  	close(cli.stopCh)
   100  	cli.listener.Close()
   101  }
   102  
   103  func (cli *CliRpcService) Port() string {
   104  	return strconv.Itoa(cli.listener.Addr().(*net.TCPAddr).Port)
   105  }
   106  
   107  func (cli *CliRpcService) Start() error {
   108  	var err error
   109  
   110  	cli.stopCh = make(chan struct{})
   111  
   112  	cli.listener, err = net.Listen("tcp", "127.0.0.1:0")
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	go func() {
   118  		for {
   119  			conn, err := cli.listener.Accept()
   120  			if err != nil {
   121  				select {
   122  				case <-cli.stopCh:
   123  					return
   124  				default:
   125  					fmt.Println(err)
   126  				}
   127  			} else {
   128  				go cli.Server.ServeConn(conn)
   129  			}
   130  		}
   131  	}()
   132  
   133  	return nil
   134  }
   135  
   136  func (cmd *CliRpcCmd) IsMinCliVersion(passedVersion string, retVal *bool) error {
   137  	if version.VersionString() == version.DefaultVersion {
   138  		*retVal = true
   139  		return nil
   140  	}
   141  
   142  	actualVersion, err := semver.Make(version.VersionString())
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	requiredVersion, err := semver.Make(passedVersion)
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	*retVal = actualVersion.GTE(requiredVersion)
   153  
   154  	return nil
   155  }
   156  
   157  func (cmd *CliRpcCmd) SetPluginMetadata(pluginMetadata plugin.PluginMetadata, retVal *bool) error {
   158  	cmd.MetadataMutex.Lock()
   159  	defer cmd.MetadataMutex.Unlock()
   160  
   161  	cmd.PluginMetadata = &pluginMetadata
   162  	*retVal = true
   163  	return nil
   164  }
   165  
   166  func (cmd *CliRpcCmd) DisableTerminalOutput(disable bool, retVal *bool) error {
   167  	cmd.terminalOutputSwitch.DisableTerminalOutput(disable)
   168  	*retVal = true
   169  	return nil
   170  }
   171  
   172  func (cmd *CliRpcCmd) CallCoreCommand(args []string, retVal *bool) error {
   173  	var err error
   174  	cmdRegistry := commandregistry.Commands
   175  
   176  	cmd.outputBucket = &bytes.Buffer{}
   177  	cmd.outputCapture.SetOutputBucket(cmd.outputBucket)
   178  
   179  	if cmdRegistry.CommandExists(args[0]) {
   180  		deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   181  
   182  		//set deps objs to be the one used by all other commands
   183  		//once all commands are converted, we can make fresh deps for each command run
   184  		deps.Config = cmd.cliConfig
   185  		deps.RepoLocator = cmd.repoLocator
   186  
   187  		//set command ui's TeePrinter to be the one used by RpcService, for output to be captured
   188  		deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.outputCapture.(*terminal.TeePrinter), cmd.logger)
   189  
   190  		err = cmd.newCmdRunner.Command(args, deps, false)
   191  	} else {
   192  		*retVal = false
   193  		return nil
   194  	}
   195  
   196  	if err != nil {
   197  		*retVal = false
   198  		return err
   199  	}
   200  
   201  	*retVal = true
   202  	return nil
   203  }
   204  
   205  func (cmd *CliRpcCmd) GetOutputAndReset(args bool, retVal *[]string) error {
   206  	v := strings.TrimSuffix(cmd.outputBucket.String(), "\n")
   207  	*retVal = strings.Split(v, "\n")
   208  	return nil
   209  }
   210  
   211  func (cmd *CliRpcCmd) GetCurrentOrg(args string, retVal *plugin_models.Organization) error {
   212  	retVal.Name = cmd.cliConfig.OrganizationFields().Name
   213  	retVal.Guid = cmd.cliConfig.OrganizationFields().GUID
   214  	return nil
   215  }
   216  
   217  func (cmd *CliRpcCmd) GetCurrentSpace(args string, retVal *plugin_models.Space) error {
   218  	retVal.Name = cmd.cliConfig.SpaceFields().Name
   219  	retVal.Guid = cmd.cliConfig.SpaceFields().GUID
   220  
   221  	return nil
   222  }
   223  
   224  func (cmd *CliRpcCmd) Username(args string, retVal *string) error {
   225  	*retVal = cmd.cliConfig.Username()
   226  
   227  	return nil
   228  }
   229  
   230  func (cmd *CliRpcCmd) UserGuid(args string, retVal *string) error {
   231  	*retVal = cmd.cliConfig.UserGUID()
   232  
   233  	return nil
   234  }
   235  
   236  func (cmd *CliRpcCmd) UserEmail(args string, retVal *string) error {
   237  	*retVal = cmd.cliConfig.UserEmail()
   238  
   239  	return nil
   240  }
   241  
   242  func (cmd *CliRpcCmd) IsLoggedIn(args string, retVal *bool) error {
   243  	*retVal = cmd.cliConfig.IsLoggedIn()
   244  
   245  	return nil
   246  }
   247  
   248  func (cmd *CliRpcCmd) IsSSLDisabled(args string, retVal *bool) error {
   249  	*retVal = cmd.cliConfig.IsSSLDisabled()
   250  
   251  	return nil
   252  }
   253  
   254  func (cmd *CliRpcCmd) HasOrganization(args string, retVal *bool) error {
   255  	*retVal = cmd.cliConfig.HasOrganization()
   256  
   257  	return nil
   258  }
   259  
   260  func (cmd *CliRpcCmd) HasSpace(args string, retVal *bool) error {
   261  	*retVal = cmd.cliConfig.HasSpace()
   262  
   263  	return nil
   264  }
   265  
   266  func (cmd *CliRpcCmd) ApiEndpoint(args string, retVal *string) error {
   267  	*retVal = cmd.cliConfig.APIEndpoint()
   268  
   269  	return nil
   270  }
   271  
   272  func (cmd *CliRpcCmd) HasAPIEndpoint(args string, retVal *bool) error {
   273  	*retVal = cmd.cliConfig.HasAPIEndpoint()
   274  
   275  	return nil
   276  }
   277  
   278  func (cmd *CliRpcCmd) ApiVersion(args string, retVal *string) error {
   279  	*retVal = cmd.cliConfig.APIVersion()
   280  
   281  	return nil
   282  }
   283  
   284  func (cmd *CliRpcCmd) LoggregatorEndpoint(args string, retVal *string) error {
   285  	*retVal = ""
   286  
   287  	return nil
   288  }
   289  
   290  func (cmd *CliRpcCmd) DopplerEndpoint(args string, retVal *string) error {
   291  	*retVal = cmd.cliConfig.DopplerEndpoint()
   292  
   293  	return nil
   294  }
   295  
   296  func (cmd *CliRpcCmd) AccessToken(args string, retVal *string) error {
   297  	token, err := cmd.repoLocator.GetAuthenticationRepository().RefreshAuthToken()
   298  	if err != nil {
   299  		return err
   300  	}
   301  
   302  	*retVal = token
   303  
   304  	return nil
   305  }
   306  
   307  func (cmd *CliRpcCmd) GetApp(appName string, retVal *plugin_models.GetAppModel) error {
   308  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   309  
   310  	//set deps objs to be the one used by all other commands
   311  	//once all commands are converted, we can make fresh deps for each command run
   312  	deps.Config = cmd.cliConfig
   313  	deps.RepoLocator = cmd.repoLocator
   314  	deps.PluginModels.Application = retVal
   315  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   316  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   317  
   318  	return cmd.newCmdRunner.Command([]string{"app", appName}, deps, true)
   319  }
   320  
   321  func (cmd *CliRpcCmd) GetApps(_ string, retVal *[]plugin_models.GetAppsModel) error {
   322  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   323  
   324  	//set deps objs to be the one used by all other commands
   325  	//once all commands are converted, we can make fresh deps for each command run
   326  	deps.Config = cmd.cliConfig
   327  	deps.RepoLocator = cmd.repoLocator
   328  	deps.PluginModels.AppsSummary = retVal
   329  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   330  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   331  
   332  	return cmd.newCmdRunner.Command([]string{"apps"}, deps, true)
   333  }
   334  
   335  func (cmd *CliRpcCmd) GetOrgs(_ string, retVal *[]plugin_models.GetOrgs_Model) error {
   336  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   337  
   338  	//set deps objs to be the one used by all other commands
   339  	//once all commands are converted, we can make fresh deps for each command run
   340  	deps.Config = cmd.cliConfig
   341  	deps.RepoLocator = cmd.repoLocator
   342  	deps.PluginModels.Organizations = retVal
   343  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   344  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   345  
   346  	return cmd.newCmdRunner.Command([]string{"orgs"}, deps, true)
   347  }
   348  
   349  func (cmd *CliRpcCmd) GetSpaces(_ string, retVal *[]plugin_models.GetSpaces_Model) error {
   350  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   351  
   352  	//set deps objs to be the one used by all other commands
   353  	//once all commands are converted, we can make fresh deps for each command run
   354  	deps.Config = cmd.cliConfig
   355  	deps.RepoLocator = cmd.repoLocator
   356  	deps.PluginModels.Spaces = retVal
   357  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   358  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   359  
   360  	return cmd.newCmdRunner.Command([]string{"spaces"}, deps, true)
   361  }
   362  
   363  func (cmd *CliRpcCmd) GetServices(_ string, retVal *[]plugin_models.GetServices_Model) error {
   364  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   365  
   366  	//set deps objs to be the one used by all other commands
   367  	//once all commands are converted, we can make fresh deps for each command run
   368  	//once all commands are converted, we can make fresh deps for each command run
   369  	deps.Config = cmd.cliConfig
   370  	deps.RepoLocator = cmd.repoLocator
   371  	deps.PluginModels.Services = retVal
   372  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   373  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   374  
   375  	return cmd.newCmdRunner.Command([]string{"services"}, deps, true)
   376  }
   377  
   378  func (cmd *CliRpcCmd) GetOrgUsers(args []string, retVal *[]plugin_models.GetOrgUsers_Model) error {
   379  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   380  
   381  	//set deps objs to be the one used by all other commands
   382  	//once all commands are converted, we can make fresh deps for each command run
   383  	deps.Config = cmd.cliConfig
   384  	deps.RepoLocator = cmd.repoLocator
   385  	deps.PluginModels.OrgUsers = retVal
   386  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   387  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   388  
   389  	return cmd.newCmdRunner.Command(append([]string{"org-users"}, args...), deps, true)
   390  }
   391  
   392  func (cmd *CliRpcCmd) GetSpaceUsers(args []string, retVal *[]plugin_models.GetSpaceUsers_Model) error {
   393  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   394  
   395  	//set deps objs to be the one used by all other commands
   396  	//once all commands are converted, we can make fresh deps for each command run
   397  	deps.Config = cmd.cliConfig
   398  	deps.RepoLocator = cmd.repoLocator
   399  	deps.PluginModels.SpaceUsers = retVal
   400  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   401  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   402  
   403  	return cmd.newCmdRunner.Command(append([]string{"space-users"}, args...), deps, true)
   404  }
   405  
   406  func (cmd *CliRpcCmd) GetOrg(orgName string, retVal *plugin_models.GetOrg_Model) error {
   407  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   408  
   409  	//set deps objs to be the one used by all other commands
   410  	//once all commands are converted, we can make fresh deps for each command run
   411  	deps.Config = cmd.cliConfig
   412  	deps.RepoLocator = cmd.repoLocator
   413  	deps.PluginModels.Organization = retVal
   414  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   415  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   416  
   417  	return cmd.newCmdRunner.Command([]string{"org", orgName}, deps, true)
   418  }
   419  
   420  func (cmd *CliRpcCmd) GetSpace(spaceName string, retVal *plugin_models.GetSpace_Model) error {
   421  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   422  
   423  	//set deps objs to be the one used by all other commands
   424  	//once all commands are converted, we can make fresh deps for each command run
   425  	deps.Config = cmd.cliConfig
   426  	deps.RepoLocator = cmd.repoLocator
   427  	deps.PluginModels.Space = retVal
   428  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   429  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   430  
   431  	return cmd.newCmdRunner.Command([]string{"space", spaceName}, deps, true)
   432  }
   433  
   434  func (cmd *CliRpcCmd) GetService(serviceInstance string, retVal *plugin_models.GetService_Model) error {
   435  	deps := commandregistry.NewDependency(cmd.stdout, cmd.logger, dialTimeout)
   436  
   437  	//set deps objs to be the one used by all other commands
   438  	//once all commands are converted, we can make fresh deps for each command run
   439  	deps.Config = cmd.cliConfig
   440  	deps.RepoLocator = cmd.repoLocator
   441  	deps.PluginModels.Service = retVal
   442  	cmd.terminalOutputSwitch.DisableTerminalOutput(true)
   443  	deps.UI = terminal.NewUI(os.Stdin, cmd.stdout, cmd.terminalOutputSwitch.(*terminal.TeePrinter), cmd.logger)
   444  
   445  	return cmd.newCmdRunner.Command([]string{"service", serviceInstance}, deps, true)
   446  }