github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/cmd/cmd.go (about)

     1  package cmd
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"os"
    10  	"os/exec"
    11  	"runtime/debug"
    12  	"sort"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  	"unicode"
    17  
    18  	"github.com/google/uuid"
    19  	"github.com/tickoalcantara12/micro/v3/client/cli/namespace"
    20  	clitoken "github.com/tickoalcantara12/micro/v3/client/cli/token"
    21  	"github.com/tickoalcantara12/micro/v3/client/cli/util"
    22  	_ "github.com/tickoalcantara12/micro/v3/cmd/usage"
    23  	"github.com/tickoalcantara12/micro/v3/plugin"
    24  	"github.com/tickoalcantara12/micro/v3/profile"
    25  	"github.com/tickoalcantara12/micro/v3/service/auth"
    26  	"github.com/tickoalcantara12/micro/v3/service/broker"
    27  	"github.com/tickoalcantara12/micro/v3/service/client"
    28  	"github.com/tickoalcantara12/micro/v3/service/config"
    29  	configCli "github.com/tickoalcantara12/micro/v3/service/config/client"
    30  	storeConf "github.com/tickoalcantara12/micro/v3/service/config/store"
    31  	"github.com/tickoalcantara12/micro/v3/service/errors"
    32  	"github.com/tickoalcantara12/micro/v3/service/events"
    33  	"github.com/tickoalcantara12/micro/v3/service/logger"
    34  	"github.com/tickoalcantara12/micro/v3/service/metrics"
    35  	"github.com/tickoalcantara12/micro/v3/service/network"
    36  	"github.com/tickoalcantara12/micro/v3/service/registry"
    37  	"github.com/tickoalcantara12/micro/v3/service/router"
    38  	"github.com/tickoalcantara12/micro/v3/service/runtime"
    39  	"github.com/tickoalcantara12/micro/v3/service/server"
    40  	"github.com/tickoalcantara12/micro/v3/service/store"
    41  	uconf "github.com/tickoalcantara12/micro/v3/util/config"
    42  	"github.com/tickoalcantara12/micro/v3/util/helper"
    43  	"github.com/tickoalcantara12/micro/v3/util/report"
    44  	"github.com/tickoalcantara12/micro/v3/util/user"
    45  	"github.com/tickoalcantara12/micro/v3/util/wrapper"
    46  	"github.com/urfave/cli/v2"
    47  
    48  	muruntime "github.com/tickoalcantara12/micro/v3/service/runtime"
    49  
    50  	authSrv "github.com/tickoalcantara12/micro/v3/service/auth/client"
    51  	brokerSrv "github.com/tickoalcantara12/micro/v3/service/broker/client"
    52  	grpcCli "github.com/tickoalcantara12/micro/v3/service/client/grpc"
    53  	eventsSrv "github.com/tickoalcantara12/micro/v3/service/events/client"
    54  	noopMet "github.com/tickoalcantara12/micro/v3/service/metrics/noop"
    55  	mucpNet "github.com/tickoalcantara12/micro/v3/service/network/mucp"
    56  	registrySrv "github.com/tickoalcantara12/micro/v3/service/registry/client"
    57  	routerSrv "github.com/tickoalcantara12/micro/v3/service/router/client"
    58  	runtimeSrv "github.com/tickoalcantara12/micro/v3/service/runtime/client"
    59  	grpcSvr "github.com/tickoalcantara12/micro/v3/service/server/grpc"
    60  	storeSrv "github.com/tickoalcantara12/micro/v3/service/store/client"
    61  )
    62  
    63  type Cmd interface {
    64  	// Init initialises options
    65  	// Note: Use Run to parse command line
    66  	Init(opts ...Option) error
    67  	// Options set within this command
    68  	Options() Options
    69  	// The cli app within this cmd
    70  	App() *cli.App
    71  	// Run executes the command
    72  	Run() error
    73  	// Implementation
    74  	String() string
    75  }
    76  
    77  type command struct {
    78  	opts Options
    79  	app  *cli.App
    80  
    81  	// before is a function which should
    82  	// be called in Before if not nil
    83  	before cli.ActionFunc
    84  
    85  	// indicates whether this is a service
    86  	service bool
    87  }
    88  
    89  var (
    90  	DefaultCmd Cmd = New()
    91  
    92  	onceBefore sync.Once
    93  
    94  	// name of the binary
    95  	name = "micro"
    96  	// description of the binary
    97  	description = "A framework for cloud native development\n\n	 Use `micro [command] --help` to see command specific help."
    98  	// defaultFlags which are used on all commands
    99  	defaultFlags = []cli.Flag{
   100  		&cli.StringFlag{
   101  			Name:    "c",
   102  			Usage:   "Set the config file: Defaults to ~/.micro/config.json",
   103  			EnvVars: []string{"MICRO_CONFIG_FILE"},
   104  		},
   105  		&cli.StringFlag{
   106  			Name:    "env",
   107  			Aliases: []string{"e"},
   108  			Usage:   "Set the environment to operate in",
   109  			EnvVars: []string{"MICRO_ENV"},
   110  		},
   111  		&cli.StringFlag{
   112  			Name:    "profile",
   113  			Usage:   "Set the micro server profile: e.g. local or kubernetes",
   114  			EnvVars: []string{"MICRO_PROFILE"},
   115  		},
   116  		&cli.StringFlag{
   117  			Name:    "namespace",
   118  			EnvVars: []string{"MICRO_NAMESPACE"},
   119  			Usage:   "Namespace the service is operating in",
   120  			Value:   "micro",
   121  		},
   122  		&cli.StringFlag{
   123  			Name:    "auth_address",
   124  			EnvVars: []string{"MICRO_AUTH_ADDRESS"},
   125  			Usage:   "Comma-separated list of auth addresses",
   126  		},
   127  		&cli.StringFlag{
   128  			Name:    "auth_id",
   129  			EnvVars: []string{"MICRO_AUTH_ID"},
   130  			Usage:   "Account ID used for client authentication",
   131  		},
   132  		&cli.StringFlag{
   133  			Name:    "auth_secret",
   134  			EnvVars: []string{"MICRO_AUTH_SECRET"},
   135  			Usage:   "Account secret used for client authentication",
   136  		},
   137  		&cli.StringFlag{
   138  			Name:    "auth_public_key",
   139  			EnvVars: []string{"MICRO_AUTH_PUBLIC_KEY"},
   140  			Usage:   "Public key for JWT auth (base64 encoded PEM)",
   141  		},
   142  		&cli.StringFlag{
   143  			Name:    "auth_private_key",
   144  			EnvVars: []string{"MICRO_AUTH_PRIVATE_KEY"},
   145  			Usage:   "Private key for JWT auth (base64 encoded PEM)",
   146  		},
   147  		&cli.StringFlag{
   148  			Name:    "registry_address",
   149  			EnvVars: []string{"MICRO_REGISTRY_ADDRESS"},
   150  			Usage:   "Comma-separated list of registry addresses",
   151  		},
   152  		&cli.StringFlag{
   153  			Name:    "registry_tls_ca",
   154  			Usage:   "Certificate authority for TLS with registry",
   155  			EnvVars: []string{"MICRO_REGISTRY_TLS_CA"},
   156  		},
   157  		&cli.StringFlag{
   158  			Name:    "registry_tls_cert",
   159  			Usage:   "Client cert for TLS with registry",
   160  			EnvVars: []string{"MICRO_REGISTRY_TLS_CERT"},
   161  		},
   162  		&cli.StringFlag{
   163  			Name:    "registry_tls_key",
   164  			Usage:   "Client key for TLS with registry",
   165  			EnvVars: []string{"MICRO_REGISTRY_TLS_KEY"},
   166  		},
   167  		&cli.StringFlag{
   168  			Name:    "broker_address",
   169  			EnvVars: []string{"MICRO_BROKER_ADDRESS"},
   170  			Usage:   "Comma-separated list of broker addresses",
   171  		},
   172  		&cli.StringFlag{
   173  			Name:    "events_tls_ca",
   174  			Usage:   "Certificate authority for TLS with events",
   175  			EnvVars: []string{"MICRO_EVENTS_TLS_CA"},
   176  		},
   177  		&cli.StringFlag{
   178  			Name:    "events_tls_cert",
   179  			Usage:   "Client cert for TLS with events",
   180  			EnvVars: []string{"MICRO_EVENTS_TLS_CERT"},
   181  		},
   182  		&cli.StringFlag{
   183  			Name:    "events_tls_key",
   184  			Usage:   "Client key for TLS with events",
   185  			EnvVars: []string{"MICRO_EVENTS_TLS_KEY"},
   186  		},
   187  		&cli.StringFlag{
   188  			Name:    "broker_tls_ca",
   189  			Usage:   "Certificate authority for TLS with broker",
   190  			EnvVars: []string{"MICRO_BROKER_TLS_CA"},
   191  		},
   192  		&cli.StringFlag{
   193  			Name:    "broker_tls_cert",
   194  			Usage:   "Client cert for TLS with broker",
   195  			EnvVars: []string{"MICRO_BROKER_TLS_CERT"},
   196  		},
   197  		&cli.StringFlag{
   198  			Name:    "broker_tls_key",
   199  			Usage:   "Client key for TLS with broker",
   200  			EnvVars: []string{"MICRO_BROKER_TLS_KEY"},
   201  		},
   202  		&cli.StringFlag{
   203  			Name:    "store_address",
   204  			EnvVars: []string{"MICRO_STORE_ADDRESS"},
   205  			Usage:   "Comma-separated list of store addresses",
   206  		},
   207  		&cli.StringFlag{
   208  			Name:    "proxy_address",
   209  			Usage:   "Proxy requests via the HTTP address specified",
   210  			EnvVars: []string{"MICRO_PROXY"},
   211  		},
   212  		&cli.BoolFlag{
   213  			Name:    "report_usage",
   214  			Usage:   "Report usage statistics",
   215  			EnvVars: []string{"MICRO_REPORT_USAGE"},
   216  			Value:   true,
   217  		},
   218  		&cli.StringFlag{
   219  			Name:    "service_name",
   220  			Usage:   "Name of the micro service",
   221  			EnvVars: []string{"MICRO_SERVICE_NAME"},
   222  		},
   223  		&cli.StringFlag{
   224  			Name:    "service_version",
   225  			Usage:   "Version of the micro service",
   226  			EnvVars: []string{"MICRO_SERVICE_VERSION"},
   227  		},
   228  		&cli.StringFlag{
   229  			Name:    "service_address",
   230  			Usage:   "Address to run the service on",
   231  			EnvVars: []string{"MICRO_SERVICE_ADDRESS"},
   232  		},
   233  		&cli.StringFlag{
   234  			Name:    "config_secret_key",
   235  			Usage:   "Key to use when encoding/decoding secret config values. Will be generated and saved to file if not provided.",
   236  			Value:   "",
   237  			EnvVars: []string{"MICRO_CONFIG_SECRET_KEY"},
   238  		},
   239  		&cli.StringFlag{
   240  			Name:    "tracing_reporter_address",
   241  			Usage:   "The host:port of the opentracing agent e.g. localhost:6831",
   242  			EnvVars: []string{"MICRO_TRACING_REPORTER_ADDRESS"},
   243  		},
   244  	}
   245  )
   246  
   247  func init() {
   248  	rand.Seed(time.Now().Unix())
   249  
   250  	// configure defaults for all packages
   251  	setupDefaults()
   252  }
   253  
   254  // setupDefaults sets the default auth, broker etc implementations incase they arent configured by
   255  // a profile. The default implementations are always the RPC implementations.
   256  func setupDefaults() {
   257  	client.DefaultClient = grpcCli.NewClient()
   258  	server.DefaultServer = grpcSvr.NewServer()
   259  	network.DefaultNetwork = mucpNet.NewNetwork()
   260  	metrics.DefaultMetricsReporter = noopMet.New()
   261  
   262  	// setup rpc implementations after the client is configured
   263  	auth.DefaultAuth = authSrv.NewAuth()
   264  	broker.DefaultBroker = brokerSrv.NewBroker()
   265  	events.DefaultStream = eventsSrv.NewStream()
   266  	events.DefaultStore = eventsSrv.NewStore()
   267  	registry.DefaultRegistry = registrySrv.NewRegistry()
   268  	router.DefaultRouter = routerSrv.NewRouter()
   269  	store.DefaultStore = storeSrv.NewStore()
   270  	store.DefaultBlobStore = storeSrv.NewBlobStore()
   271  	runtime.DefaultRuntime = runtimeSrv.NewRuntime()
   272  }
   273  
   274  func formatErr(err error) string {
   275  	switch v := err.(type) {
   276  	case *errors.Error:
   277  		return upcaseInitial(v.Detail)
   278  	default:
   279  		return upcaseInitial(err.Error())
   280  	}
   281  }
   282  
   283  func upcaseInitial(str string) string {
   284  	for i, v := range str {
   285  		return string(unicode.ToUpper(v)) + str[i+1:]
   286  	}
   287  	return ""
   288  }
   289  
   290  // setupAuthForCLI handles exchanging refresh tokens to access tokens
   291  // The structure of the local micro userconfig file is the following:
   292  // micro.auth.[envName].token: temporary access token
   293  // micro.auth.[envName].refresh-token: long lived refresh token
   294  // micro.auth.[envName].expiry: expiration time of the access token, seconds since Unix epoch.
   295  func setupAuthForCLI(ctx *cli.Context) error {
   296  	env, err := util.GetEnv(ctx)
   297  	if err != nil {
   298  		return err
   299  	}
   300  	ns, err := namespace.Get(env.Name)
   301  	if err != nil {
   302  		return err
   303  	}
   304  
   305  	tok, err := clitoken.Get(ctx)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	// If there is no refresh token, do not try to refresh it
   311  	if len(tok.RefreshToken) == 0 {
   312  		return nil
   313  	}
   314  
   315  	// Check if token is valid
   316  	if time.Now().Before(tok.Expiry.Add(time.Minute * -1)) {
   317  		auth.DefaultAuth.Init(
   318  			auth.ClientToken(tok),
   319  			auth.Issuer(ns),
   320  		)
   321  		return nil
   322  	}
   323  
   324  	// Get new access token from refresh token if it's close to expiry
   325  	tok, err = auth.Token(
   326  		auth.WithToken(tok.RefreshToken),
   327  		auth.WithTokenIssuer(ns),
   328  		auth.WithExpiry(time.Minute*10),
   329  	)
   330  	if err != nil {
   331  		return nil
   332  	}
   333  
   334  	// Save the token to user config file
   335  	auth.DefaultAuth.Init(
   336  		auth.ClientToken(tok),
   337  		auth.Issuer(ns),
   338  	)
   339  	return clitoken.Save(ctx, tok)
   340  }
   341  
   342  // setupAuthForService generates auth credentials for the service
   343  func setupAuthForService() error {
   344  	opts := auth.DefaultAuth.Options()
   345  
   346  	// extract the account creds from options, these can be set by flags
   347  	accID := opts.ID
   348  	accSecret := opts.Secret
   349  
   350  	// if no credentials were provided, self generate an account
   351  	if len(accID) == 0 || len(accSecret) == 0 {
   352  		opts := []auth.GenerateOption{
   353  			auth.WithType("service"),
   354  			auth.WithScopes("service"),
   355  		}
   356  
   357  		acc, err := auth.Generate(uuid.New().String(), opts...)
   358  		if err != nil {
   359  			return err
   360  		}
   361  		if logger.V(logger.DebugLevel, logger.DefaultLogger) {
   362  			logger.Debugf("Auth [%v] Generated an auth account", auth.DefaultAuth.String())
   363  		}
   364  
   365  		accID = acc.ID
   366  		accSecret = acc.Secret
   367  	}
   368  
   369  	// generate the first token
   370  	token, err := auth.Token(
   371  		auth.WithCredentials(accID, accSecret),
   372  		auth.WithExpiry(time.Minute*10),
   373  	)
   374  	if err != nil {
   375  		return err
   376  	}
   377  
   378  	// set the credentials and token in auth options
   379  	auth.DefaultAuth.Init(
   380  		auth.ClientToken(token),
   381  		auth.Credentials(accID, accSecret),
   382  	)
   383  	return nil
   384  }
   385  
   386  // refreshAuthToken if it is close to expiring
   387  func refreshAuthToken() {
   388  	// can't refresh a token we don't have
   389  	if auth.DefaultAuth.Options().Token == nil {
   390  		return
   391  	}
   392  
   393  	t := time.NewTicker(time.Second * 15)
   394  	defer t.Stop()
   395  
   396  	for {
   397  		select {
   398  		case <-t.C:
   399  			// don't refresh the token if it's not close to expiring
   400  			tok := auth.DefaultAuth.Options().Token
   401  			if tok.Expiry.Unix() > time.Now().Add(time.Minute).Unix() {
   402  				continue
   403  			}
   404  
   405  			// generate the first token
   406  			tok, err := auth.Token(
   407  				auth.WithToken(tok.RefreshToken),
   408  				auth.WithExpiry(time.Minute*10),
   409  			)
   410  			if err == auth.ErrInvalidToken {
   411  				logger.Warnf("[Auth] Refresh token expired, regenerating using account credentials")
   412  
   413  				tok, err = auth.Token(
   414  					auth.WithCredentials(
   415  						auth.DefaultAuth.Options().ID,
   416  						auth.DefaultAuth.Options().Secret,
   417  					),
   418  					auth.WithExpiry(time.Minute*10),
   419  				)
   420  			} else if err != nil {
   421  				logger.Warnf("[Auth] Error refreshing token: %v", err)
   422  				continue
   423  			}
   424  
   425  			// set the token
   426  			logger.Debugf("Auth token refreshed, expires at %v", tok.Expiry.Format(time.UnixDate))
   427  			auth.DefaultAuth.Init(auth.ClientToken(tok))
   428  		}
   429  	}
   430  }
   431  
   432  func action(c *cli.Context) error {
   433  	if c.Args().Len() == 0 {
   434  		return helper.MissingCommand(c)
   435  	}
   436  
   437  	// if an executable is available with the name of
   438  	// the command, execute it with the arguments from
   439  	// index 1 on.
   440  	v, err := exec.LookPath("micro-" + c.Args().First())
   441  	if err == nil {
   442  		ce := exec.Command(v, c.Args().Slice()[1:]...)
   443  		ce.Stdout = os.Stdout
   444  		ce.Stderr = os.Stderr
   445  		return ce.Run()
   446  	}
   447  
   448  	// lookup the service, e.g. "micro config set" would
   449  	// firstly check to see if the service, e.g. config
   450  	// exists within the current namespace, then it would
   451  	// execute the Config.Set RPC, setting the flags in the
   452  	// request.
   453  	if srv, ns, err := util.LookupService(c); err != nil {
   454  		return util.CliError(err)
   455  	} else if srv != nil && util.ShouldRenderHelp(c) {
   456  		return cli.Exit(util.FormatServiceUsage(srv, c), 0)
   457  	} else if srv != nil {
   458  		err := util.CallService(srv, ns, c)
   459  		return util.CliError(err)
   460  	}
   461  
   462  	// srv == nil
   463  	return helper.UnexpectedCommand(c)
   464  }
   465  
   466  func New(opts ...Option) *command {
   467  	options := Options{}
   468  	for _, o := range opts {
   469  		o(&options)
   470  	}
   471  
   472  	cmd := new(command)
   473  	cmd.opts = options
   474  	cmd.app = cli.NewApp()
   475  	cmd.app.Name = name
   476  	cmd.app.Version = buildVersion()
   477  	cmd.app.Usage = description
   478  	cmd.app.Flags = defaultFlags
   479  	cmd.app.Action = action
   480  	cmd.app.Before = beforeFromContext(options.Context, cmd.Before)
   481  
   482  	// if this option has been set, we're running a service
   483  	// and no action needs to be performed. The CMD package
   484  	// is just being used to parse flags and configure micro.
   485  	if setupOnlyFromContext(options.Context) {
   486  		cmd.service = true
   487  		cmd.app.Action = func(ctx *cli.Context) error { return nil }
   488  	}
   489  
   490  	//flags to add
   491  	if len(options.Flags) > 0 {
   492  		cmd.app.Flags = append(cmd.app.Flags, options.Flags...)
   493  	}
   494  	//action to replace
   495  	if options.Action != nil {
   496  		cmd.app.Action = options.Action
   497  	}
   498  	// cmd to add to use registry
   499  
   500  	return cmd
   501  }
   502  
   503  func (c *command) App() *cli.App {
   504  	return c.app
   505  }
   506  
   507  func (c *command) Options() Options {
   508  	return c.opts
   509  }
   510  
   511  // Before is executed before any subcommand
   512  func (c *command) Before(ctx *cli.Context) error {
   513  	// set the config file if specified
   514  	if cf := ctx.String("c"); len(cf) > 0 {
   515  		uconf.SetConfig(cf)
   516  	}
   517  
   518  	// certain commands don't require loading
   519  	if ctx.Args().First() == "env" {
   520  		return nil
   521  	}
   522  
   523  	// default the profile for the server
   524  	prof := ctx.String("profile")
   525  
   526  	// if no profile is set then set one
   527  	if len(prof) == 0 {
   528  		switch ctx.Args().First() {
   529  		case "service", "server":
   530  			prof = "server"
   531  		default:
   532  			prof = "client"
   533  		}
   534  	}
   535  
   536  	// apply the profile
   537  	if profile, err := profile.Load(prof); err != nil {
   538  		logger.Fatal(err)
   539  	} else {
   540  		// load the profile
   541  		profile.Setup(ctx)
   542  	}
   543  
   544  	// set the proxy address
   545  	var proxy string
   546  	if c.service || ctx.IsSet("proxy_address") {
   547  		// use the proxy address passed as a flag, this is normally
   548  		// the micro network
   549  		proxy = ctx.String("proxy_address")
   550  	} else {
   551  		// for CLI, use the external proxy which is loaded from the
   552  		// local config
   553  		var err error
   554  		proxy, err = util.CLIProxyAddress(ctx)
   555  		if err != nil {
   556  			return err
   557  		}
   558  	}
   559  	if len(proxy) > 0 {
   560  		client.DefaultClient.Init(client.Proxy(proxy))
   561  	}
   562  
   563  	// use the internal network lookup
   564  	client.DefaultClient.Init(
   565  		client.Lookup(network.Lookup),
   566  	)
   567  
   568  	onceBefore.Do(func() {
   569  		// wrap the client
   570  		client.DefaultClient = wrapper.AuthClient(client.DefaultClient)
   571  		client.DefaultClient = wrapper.TraceCall(client.DefaultClient)
   572  		client.DefaultClient = wrapper.LogClient(client.DefaultClient)
   573  		client.DefaultClient = wrapper.OpentraceClient(client.DefaultClient)
   574  
   575  		// wrap the server
   576  		server.DefaultServer.Init(
   577  			server.WrapHandler(wrapper.AuthHandler()),
   578  			server.WrapHandler(wrapper.TraceHandler()),
   579  			server.WrapHandler(wrapper.HandlerStats()),
   580  			server.WrapHandler(wrapper.LogHandler()),
   581  			server.WrapHandler(wrapper.MetricsHandler()),
   582  			server.WrapHandler(wrapper.OpenTraceHandler()),
   583  		)
   584  	})
   585  
   586  	// setup auth
   587  	authOpts := []auth.Option{}
   588  	if len(ctx.String("namespace")) > 0 {
   589  		authOpts = append(authOpts, auth.Issuer(ctx.String("namespace")))
   590  	}
   591  	if len(ctx.String("auth_address")) > 0 {
   592  		authOpts = append(authOpts, auth.Addrs(ctx.String("auth_address")))
   593  	}
   594  	if len(ctx.String("auth_id")) > 0 || len(ctx.String("auth_secret")) > 0 {
   595  		authOpts = append(authOpts, auth.Credentials(
   596  			ctx.String("auth_id"), ctx.String("auth_secret"),
   597  		))
   598  	}
   599  
   600  	// load the jwt private and public keys, in the case of the server we want to generate them if not
   601  	// present. The server will inject these creds into the core services, if the services generated
   602  	// the credentials themselves then they wouldn't match
   603  	if len(ctx.String("auth_public_key")) > 0 || len(ctx.String("auth_private_key")) > 0 {
   604  		authOpts = append(authOpts, auth.PublicKey(ctx.String("auth_public_key")))
   605  		authOpts = append(authOpts, auth.PrivateKey(ctx.String("auth_private_key")))
   606  	} else if ctx.Args().First() == "server" || ctx.Args().First() == "service" {
   607  		privKey, pubKey, err := user.GetJWTCerts()
   608  		if err != nil {
   609  			logger.Fatalf("Error getting keys: %v", err)
   610  		}
   611  		authOpts = append(authOpts, auth.PublicKey(string(pubKey)), auth.PrivateKey(string(privKey)))
   612  	}
   613  
   614  	auth.DefaultAuth.Init(authOpts...)
   615  
   616  	// setup auth credentials, use local credentials for the CLI and injected creds
   617  	// for the service.
   618  	var err error
   619  	if c.service {
   620  		err = setupAuthForService()
   621  	} else {
   622  		err = setupAuthForCLI(ctx)
   623  	}
   624  	if err != nil {
   625  		logger.Fatalf("Error setting up auth: %v", err)
   626  	}
   627  	go refreshAuthToken()
   628  
   629  	// initialize the server with the namespace so it knows which domain to register in
   630  	server.DefaultServer.Init(server.Namespace(ctx.String("namespace")))
   631  
   632  	// setup registry
   633  	registryOpts := []registry.Option{}
   634  
   635  	// Parse registry TLS certs
   636  	if len(ctx.String("registry_tls_cert")) > 0 || len(ctx.String("registry_tls_key")) > 0 {
   637  		cert, err := tls.LoadX509KeyPair(ctx.String("registry_tls_cert"), ctx.String("registry_tls_key"))
   638  		if err != nil {
   639  			logger.Fatalf("Error loading registry tls cert: %v", err)
   640  		}
   641  
   642  		// load custom certificate authority
   643  		caCertPool := x509.NewCertPool()
   644  		if len(ctx.String("registry_tls_ca")) > 0 {
   645  			crt, err := ioutil.ReadFile(ctx.String("registry_tls_ca"))
   646  			if err != nil {
   647  				logger.Fatalf("Error loading registry tls certificate authority: %v", err)
   648  			}
   649  			caCertPool.AppendCertsFromPEM(crt)
   650  		}
   651  
   652  		cfg := &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: caCertPool}
   653  		registryOpts = append(registryOpts, registry.TLSConfig(cfg))
   654  	}
   655  	if len(ctx.String("registry_address")) > 0 {
   656  		addresses := strings.Split(ctx.String("registry_address"), ",")
   657  		registryOpts = append(registryOpts, registry.Addrs(addresses...))
   658  	}
   659  	if err := registry.DefaultRegistry.Init(registryOpts...); err != nil {
   660  		logger.Fatalf("Error configuring registry: %v", err)
   661  	}
   662  
   663  	// Setup broker options.
   664  	brokerOpts := []broker.Option{}
   665  	if len(ctx.String("broker_address")) > 0 {
   666  		brokerOpts = append(brokerOpts, broker.Addrs(ctx.String("broker_address")))
   667  	}
   668  
   669  	// Parse broker TLS certs
   670  	if len(ctx.String("broker_tls_cert")) > 0 || len(ctx.String("broker_tls_key")) > 0 {
   671  		cert, err := tls.LoadX509KeyPair(ctx.String("broker_tls_cert"), ctx.String("broker_tls_key"))
   672  		if err != nil {
   673  			logger.Fatalf("Error loading broker TLS cert: %v", err)
   674  		}
   675  
   676  		// load custom certificate authority
   677  		caCertPool := x509.NewCertPool()
   678  		if len(ctx.String("broker_tls_ca")) > 0 {
   679  			crt, err := ioutil.ReadFile(ctx.String("broker_tls_ca"))
   680  			if err != nil {
   681  				logger.Fatalf("Error loading broker TLS certificate authority: %v", err)
   682  			}
   683  			caCertPool.AppendCertsFromPEM(crt)
   684  		}
   685  
   686  		cfg := &tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: caCertPool}
   687  		brokerOpts = append(brokerOpts, broker.TLSConfig(cfg))
   688  	}
   689  	if err := broker.DefaultBroker.Init(brokerOpts...); err != nil {
   690  		logger.Fatalf("Error configuring broker: %v", err)
   691  	}
   692  	if err := broker.DefaultBroker.Connect(); err != nil {
   693  		logger.Fatalf("Error connecting to broker: %v", err)
   694  	}
   695  
   696  	// Setup runtime. This is a temporary fix to trigger the runtime to recreate
   697  	// its client now the client has been replaced with a wrapped one.
   698  	if err := muruntime.DefaultRuntime.Init(); err != nil {
   699  		logger.Fatalf("Error configuring runtime: %v", err)
   700  	}
   701  
   702  	// Setup store options
   703  	storeOpts := []store.StoreOption{}
   704  	if len(ctx.String("store_address")) > 0 {
   705  		storeOpts = append(storeOpts, store.Nodes(strings.Split(ctx.String("store_address"), ",")...))
   706  	}
   707  	if len(ctx.String("namespace")) > 0 {
   708  		storeOpts = append(storeOpts, store.Database(ctx.String("namespace")))
   709  	}
   710  	if len(ctx.String("service_name")) > 0 {
   711  		storeOpts = append(storeOpts, store.Table(ctx.String("service_name")))
   712  	}
   713  	if err := store.DefaultStore.Init(storeOpts...); err != nil {
   714  		logger.Fatalf("Error configuring store: %v", err)
   715  	}
   716  
   717  	// set the registry and broker in the client and server
   718  	client.DefaultClient.Init(
   719  		client.Broker(broker.DefaultBroker),
   720  		client.Registry(registry.DefaultRegistry),
   721  	)
   722  	server.DefaultServer.Init(
   723  		server.Broker(broker.DefaultBroker),
   724  		server.Registry(registry.DefaultRegistry),
   725  	)
   726  
   727  	// Setup config. Do this after auth is configured since it'll load the config
   728  	// from the service immediately. We only do this if the action is nil, indicating
   729  	// a service is being run
   730  	if c.service && config.DefaultConfig == nil {
   731  		config.DefaultConfig = configCli.NewConfig(ctx.String("namespace"))
   732  	} else if config.DefaultConfig == nil {
   733  		config.DefaultConfig, _ = storeConf.NewConfig(store.DefaultStore, ctx.String("namespace"))
   734  	}
   735  
   736  	// initialize plugins
   737  	for _, p := range plugin.Plugins() {
   738  		if err := p.Init(ctx); err != nil {
   739  			return err
   740  		}
   741  	}
   742  
   743  	return nil
   744  }
   745  
   746  func (c *command) Init(opts ...Option) error {
   747  	for _, o := range opts {
   748  		o(&c.opts)
   749  	}
   750  	if len(c.opts.Name) > 0 {
   751  		c.app.Name = c.opts.Name
   752  	}
   753  	if len(c.opts.Version) > 0 {
   754  		c.app.Version = c.opts.Version
   755  	}
   756  	c.app.HideVersion = len(c.opts.Version) == 0
   757  	c.app.Usage = c.opts.Description
   758  
   759  	//allow user's flags to add
   760  	if len(c.opts.Flags) > 0 {
   761  		c.app.Flags = append(c.app.Flags, c.opts.Flags...)
   762  	}
   763  	//action to replace
   764  	if c.opts.Action != nil {
   765  		c.app.Action = c.opts.Action
   766  	}
   767  
   768  	return nil
   769  }
   770  
   771  func (c *command) Run() error {
   772  	defer func() {
   773  		if r := recover(); r != nil {
   774  			report.Errorf(nil, fmt.Sprintf("panic: %v", string(debug.Stack())))
   775  			panic(r)
   776  		}
   777  	}()
   778  	return c.app.Run(os.Args)
   779  }
   780  
   781  func (c *command) String() string {
   782  	return "micro"
   783  }
   784  
   785  // Register CLI commands
   786  func Register(cmds ...*cli.Command) {
   787  	app := DefaultCmd.App()
   788  	app.Commands = append(app.Commands, cmds...)
   789  
   790  	// sort the commands so they're listed in order on the cli
   791  	// todo: move this to micro/cli so it's only run when the
   792  	// commands are printed during "help"
   793  	sort.Slice(app.Commands, func(i, j int) bool {
   794  		return app.Commands[i].Name < app.Commands[j].Name
   795  	})
   796  }
   797  
   798  // Run the default command
   799  func Run() {
   800  	if err := DefaultCmd.Run(); err != nil {
   801  		fmt.Println(formatErr(err))
   802  		os.Exit(1)
   803  	}
   804  }