Merge pull request #1622 from micro/registry-multi-tenancy
Registry mutli-tenancy
This commit is contained in:
		
							
								
								
									
										21
									
								
								registry/service/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								registry/service/options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | package service | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v2/client" | ||||||
|  | 	"github.com/micro/go-micro/v2/registry" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type clientKey struct{} | ||||||
|  |  | ||||||
|  | // WithClient sets the RPC client | ||||||
|  | func WithClient(c client.Client) registry.Option { | ||||||
|  | 	return func(o *registry.Options) { | ||||||
|  | 		if o.Context == nil { | ||||||
|  | 			o.Context = context.Background() | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		o.Context = context.WithValue(o.Context, clientKey{}, c) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -46,6 +46,17 @@ func (s *serviceRegistry) Init(opts ...registry.Option) error { | |||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&s.opts) | 		o(&s.opts) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// extract the client from the context, fallback to grpc | ||||||
|  | 	var cli client.Client | ||||||
|  | 	if c, ok := s.opts.Context.Value(clientKey{}).(client.Client); ok { | ||||||
|  | 		cli = c | ||||||
|  | 	} else { | ||||||
|  | 		cli = grpc.NewClient() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.client = pb.NewRegistryService(DefaultService, cli) | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -171,21 +182,23 @@ func NewRegistry(opts ...registry.Option) registry.Registry { | |||||||
|  |  | ||||||
| 	// the registry address | 	// the registry address | ||||||
| 	addrs := options.Addrs | 	addrs := options.Addrs | ||||||
|  |  | ||||||
| 	if len(addrs) == 0 { | 	if len(addrs) == 0 { | ||||||
| 		addrs = []string{"127.0.0.1:8000"} | 		addrs = []string{"127.0.0.1:8000"} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// use mdns as a fall back in case its used | 	if options.Context == nil { | ||||||
| 	mReg := registry.NewRegistry() | 		options.Context = context.TODO() | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// create new client with mdns | 	// extract the client from the context, fallback to grpc | ||||||
| 	cli := grpc.NewClient( | 	var cli client.Client | ||||||
| 		client.Registry(mReg), | 	if c, ok := options.Context.Value(clientKey{}).(client.Client); ok { | ||||||
| 	) | 		cli = c | ||||||
|  | 	} else { | ||||||
|  | 		cli = grpc.NewClient() | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// service name | 	// service name. TODO: accept option | ||||||
| 	// TODO: accept option |  | ||||||
| 	name := DefaultService | 	name := DefaultService | ||||||
|  |  | ||||||
| 	return &serviceRegistry{ | 	return &serviceRegistry{ | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								service.go
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								service.go
									
									
									
									
									
								
							| @@ -1,6 +1,7 @@ | |||||||
| package micro | package micro | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	rtime "runtime" | 	rtime "runtime" | ||||||
| @@ -15,6 +16,7 @@ import ( | |||||||
| 	"github.com/micro/go-micro/v2/debug/trace" | 	"github.com/micro/go-micro/v2/debug/trace" | ||||||
| 	"github.com/micro/go-micro/v2/logger" | 	"github.com/micro/go-micro/v2/logger" | ||||||
| 	"github.com/micro/go-micro/v2/plugin" | 	"github.com/micro/go-micro/v2/plugin" | ||||||
|  | 	registrySrv "github.com/micro/go-micro/v2/registry/service" | ||||||
| 	"github.com/micro/go-micro/v2/runtime" | 	"github.com/micro/go-micro/v2/runtime" | ||||||
| 	"github.com/micro/go-micro/v2/server" | 	"github.com/micro/go-micro/v2/server" | ||||||
| 	"github.com/micro/go-micro/v2/store" | 	"github.com/micro/go-micro/v2/store" | ||||||
| @@ -115,7 +117,8 @@ func (s *service) Init(opts ...Option) { | |||||||
| 		s.opts.Store.Init(store.Table(name)) | 		s.opts.Store.Init(store.Table(name)) | ||||||
|  |  | ||||||
| 		// Set the client for the micro clients | 		// Set the client for the micro clients | ||||||
| 		// s.opts.Auth.Init(auth.WithClient(s.Client())) | 		s.opts.Auth.Init(auth.WithClient(s.Client())) | ||||||
|  | 		s.opts.Registry.Init(registrySrv.WithClient(s.Client())) | ||||||
| 		s.opts.Runtime.Init(runtime.WithClient(s.Client())) | 		s.opts.Runtime.Init(runtime.WithClient(s.Client())) | ||||||
| 		s.opts.Store.Init(store.WithClient(s.Client())) | 		s.opts.Store.Init(store.WithClient(s.Client())) | ||||||
| 	}) | 	}) | ||||||
| @@ -205,6 +208,11 @@ func (s *service) Run() error { | |||||||
| 		logger.Infof("Starting [service] %s", s.Name()) | 		logger.Infof("Starting [service] %s", s.Name()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// generate an auth account | ||||||
|  | 	if err := s.registerAuthAccount(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if err := s.Start(); err != nil { | 	if err := s.Start(); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -223,3 +231,27 @@ func (s *service) Run() error { | |||||||
|  |  | ||||||
| 	return s.Stop() | 	return s.Stop() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (s *service) registerAuthAccount() error { | ||||||
|  | 	// generate a new auth account for the service | ||||||
|  | 	name := fmt.Sprintf("%v-%v", s.Name(), s.Server().Options().Id) | ||||||
|  | 	opts := []auth.GenerateOption{ | ||||||
|  | 		auth.WithType("service"), | ||||||
|  | 		auth.WithRoles("service"), | ||||||
|  | 		auth.WithNamespace(s.Options().Auth.Options().Namespace), | ||||||
|  | 	} | ||||||
|  | 	acc, err := s.Options().Auth.Generate(name, opts...) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// generate a token | ||||||
|  | 	token, err := s.Options().Auth.Token(auth.WithCredentials(acc.ID, acc.Secret)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	s.Options().Auth.Init(auth.ClientToken(token), auth.Credentials(acc.ID, acc.Secret)) | ||||||
|  | 	logger.Infof("Auth [%v] Authenticated as %v", s.Options().Auth, name) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ package wrapper | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| @@ -182,7 +181,7 @@ func (a *authWrapper) Call(ctx context.Context, req client.Request, rsp interfac | |||||||
| 		return callWithToken(tok.AccessToken) | 		return callWithToken(tok.AccessToken) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// if we have credentials we can generate a new token for the account | 	// generate a new token if we have credentials | ||||||
| 	if len(aaOpts.ID) > 0 && len(aaOpts.Secret) > 0 { | 	if len(aaOpts.ID) > 0 && len(aaOpts.Secret) > 0 { | ||||||
| 		tok, err := aa.Token(auth.WithCredentials(aaOpts.ID, aaOpts.Secret)) | 		tok, err := aa.Token(auth.WithCredentials(aaOpts.ID, aaOpts.Secret)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| @@ -198,31 +197,8 @@ func (a *authWrapper) Call(ctx context.Context, req client.Request, rsp interfac | |||||||
| 		return callWithToken(token) | 		return callWithToken(token) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// determine the type of service from the name. we do this so we can allocate | 	// call without an auth token | ||||||
| 	// different roles depending on the type of services. e.g. we don't want web | 	return a.Client.Call(ctx, req, rsp, opts...) | ||||||
| 	// services talking directly to the runtime. TODO: find a better way to determine |  | ||||||
| 	// the type of service |  | ||||||
| 	serviceType := "service" |  | ||||||
| 	if strings.Contains(a.name, "api") { |  | ||||||
| 		serviceType = "api" |  | ||||||
| 	} else if strings.Contains(a.name, "web") { |  | ||||||
| 		serviceType = "web" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// generate a new auth account for the service |  | ||||||
| 	name := fmt.Sprintf("%v-%v", a.name, a.id) |  | ||||||
| 	acc, err := aa.Generate(name, auth.WithNamespace(aaOpts.Namespace), auth.WithRoles(serviceType)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	token, err := aa.Token(auth.WithCredentials(acc.ID, acc.Secret)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	aa.Init(auth.ClientToken(token)) |  | ||||||
|  |  | ||||||
| 	// use the token to execute the request |  | ||||||
| 	return callWithToken(token.AccessToken) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // AuthClient wraps requests with the auth header | // AuthClient wraps requests with the auth header | ||||||
| @@ -276,7 +252,9 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// There is an account, set it in the context | 			// There is an account, set it in the context | ||||||
| 			ctx = auth.ContextWithAccount(ctx, account) | 			if len(account.ID) > 0 { | ||||||
|  | 				ctx = auth.ContextWithAccount(ctx, account) | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			// The user is authorised, allow the call | 			// The user is authorised, allow the call | ||||||
| 			return h(ctx, req, rsp) | 			return h(ctx, req, rsp) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user