github.com/cs3org/reva/v2@v2.27.7/pkg/app/registry/micro/manager.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //	http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package micro
    20  
    21  import (
    22  	"context"
    23  	"sort"
    24  	"strconv"
    25  	"sync"
    26  	"time"
    27  
    28  	registrypb "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
    29  	typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
    30  	"github.com/cs3org/reva/v2/pkg/app"
    31  	"github.com/cs3org/reva/v2/pkg/appctx"
    32  	"github.com/cs3org/reva/v2/pkg/errtypes"
    33  	oreg "github.com/owncloud/ocis/v2/ocis-pkg/registry"
    34  	"github.com/rs/zerolog/log"
    35  	mreg "go-micro.dev/v4/registry"
    36  )
    37  
    38  type manager struct {
    39  	namespace string
    40  	sync.RWMutex
    41  	cancelFunc context.CancelFunc
    42  	mimeTypes  map[string][]*registrypb.ProviderInfo
    43  	providers  []*registrypb.ProviderInfo
    44  	config     *config
    45  }
    46  
    47  // New returns an implementation of the app.Registry interface.
    48  func New(m map[string]interface{}) (app.Registry, error) {
    49  	c, err := parseConfig(m)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	c.init()
    54  
    55  	ctx, cancelFunc := context.WithCancel(context.Background())
    56  
    57  	newManager := manager{
    58  		namespace:  c.Namespace,
    59  		cancelFunc: cancelFunc,
    60  		config:     c,
    61  	}
    62  
    63  	err = newManager.updateProvidersFromMicroRegistry()
    64  	if err != nil {
    65  		if _, ok := err.(errtypes.NotFound); !ok {
    66  			return nil, err
    67  		}
    68  	}
    69  
    70  	t := time.NewTicker(time.Second * 30)
    71  
    72  	go func() {
    73  		for {
    74  			select {
    75  			case <-t.C:
    76  				log.Debug().Msg("app provider tick, updating local app list")
    77  				err = newManager.updateProvidersFromMicroRegistry()
    78  				if err != nil {
    79  					log.Error().Err(err).Msg("could not update the local provider cache")
    80  					continue
    81  				}
    82  			case <-ctx.Done():
    83  				log.Debug().Msg("app provider stopped")
    84  				t.Stop()
    85  			}
    86  		}
    87  	}()
    88  
    89  	return &newManager, nil
    90  }
    91  
    92  // AddProvider does not do anything for this registry, it is a placeholder to satisfy the interface
    93  func (m *manager) AddProvider(ctx context.Context, p *registrypb.ProviderInfo) error {
    94  	log := appctx.GetLogger(ctx)
    95  
    96  	log.Info().Interface("provider", p).Msg("Tried to register through cs3 api, make sure the provider registers directly through go-micro")
    97  
    98  	return nil
    99  }
   100  
   101  // FindProvider returns all providers that can provide an app for the given mimeType
   102  func (m *manager) FindProviders(ctx context.Context, mimeType string) ([]*registrypb.ProviderInfo, error) {
   103  	m.RLock()
   104  	defer m.RUnlock()
   105  
   106  	if len(m.mimeTypes[mimeType]) < 1 {
   107  		return nil, mreg.ErrNotFound
   108  	}
   109  
   110  	return m.mimeTypes[mimeType], nil
   111  }
   112  
   113  // GetDefaultProviderForMimeType returns the default provider for the given mimeType
   114  func (m *manager) GetDefaultProviderForMimeType(ctx context.Context, mimeType string) (*registrypb.ProviderInfo, error) {
   115  	m.RLock()
   116  	defer m.RUnlock()
   117  
   118  	for _, mt := range m.config.MimeTypes {
   119  		if mt.MimeType != mimeType {
   120  			continue
   121  		}
   122  		for _, p := range m.mimeTypes[mimeType] {
   123  			if p.Name == mt.DefaultApp {
   124  				return p, nil
   125  			}
   126  		}
   127  	}
   128  
   129  	return nil, mreg.ErrNotFound
   130  }
   131  
   132  // ListProviders lists all registered Providers
   133  func (m *manager) ListProviders(ctx context.Context) ([]*registrypb.ProviderInfo, error) {
   134  	return m.providers, nil
   135  }
   136  
   137  // ListSupportedMimeTypes lists all supported mimeTypes
   138  func (m *manager) ListSupportedMimeTypes(ctx context.Context) ([]*registrypb.MimeTypeInfo, error) {
   139  	m.RLock()
   140  	defer m.RUnlock()
   141  
   142  	res := []*registrypb.MimeTypeInfo{}
   143  	for _, mime := range m.config.MimeTypes {
   144  		res = append(res, &registrypb.MimeTypeInfo{
   145  			MimeType:           mime.MimeType,
   146  			Ext:                mime.Extension,
   147  			Name:               mime.Name,
   148  			Description:        mime.Description,
   149  			Icon:               mime.Icon,
   150  			AppProviders:       m.mimeTypes[mime.MimeType],
   151  			AllowCreation:      mime.AllowCreation,
   152  			DefaultApplication: mime.DefaultApp,
   153  		})
   154  	}
   155  	return res, nil
   156  }
   157  
   158  // SetDefaultProviderForMimeType sets the default provider for the given mimeType
   159  func (m *manager) SetDefaultProviderForMimeType(ctx context.Context, mimeType string, p *registrypb.ProviderInfo) error {
   160  	m.Lock()
   161  	defer m.Unlock()
   162  	// NOTE: this is a dirty workaround:
   163  
   164  	for _, mt := range m.config.MimeTypes {
   165  		if mt.MimeType == mimeType {
   166  			mt.DefaultApp = p.Name
   167  			return nil
   168  		}
   169  	}
   170  
   171  	log.Info().Msgf("default provider for app is not set through the provider, but defined for the app")
   172  	return mreg.ErrNotFound
   173  }
   174  
   175  func (m *manager) getProvidersFromMicroRegistry(ctx context.Context) ([]*registrypb.ProviderInfo, error) {
   176  	reg := oreg.GetRegistry()
   177  	services, err := reg.GetService(m.namespace+".api.app-provider", mreg.GetContext(ctx))
   178  	if err != nil {
   179  		log.Warn().Err(err).Msg("getProvidersFromMicroRegistry")
   180  	}
   181  
   182  	if len(services) == 0 {
   183  		return nil, errtypes.NotFound("no application provider service registered")
   184  	}
   185  	if len(services) > 1 {
   186  		return nil, errtypes.InternalError("more than one application provider services registered")
   187  	}
   188  
   189  	providers := make([]*registrypb.ProviderInfo, 0, len(services[0].Nodes))
   190  	for _, node := range services[0].Nodes {
   191  		p := m.providerFromMetadata(node.Metadata)
   192  		p.Address = node.Address
   193  		providers = append(providers, p)
   194  	}
   195  	return providers, nil
   196  }
   197  
   198  func (m *manager) providerFromMetadata(metadata map[string]string) *registrypb.ProviderInfo {
   199  	p := &registrypb.ProviderInfo{
   200  		MimeTypes: splitMimeTypes(metadata[m.namespace+".app-provider.mime_type"]),
   201  		//		Address:     node.Address,
   202  		Name:        metadata[m.namespace+".app-provider.name"],
   203  		Description: metadata[m.namespace+".app-provider.description"],
   204  		Icon:        metadata[m.namespace+".app-provider.icon"],
   205  		DesktopOnly: metadata[m.namespace+".app-provider.desktop_only"] == "true",
   206  		Capability:  registrypb.ProviderInfo_Capability(registrypb.ProviderInfo_Capability_value[metadata[m.namespace+".app-provider.capability"]]),
   207  	}
   208  	if metadata[m.namespace+".app-provider.priority"] != "" {
   209  		p.Opaque = &typesv1beta1.Opaque{Map: map[string]*typesv1beta1.OpaqueEntry{
   210  			"priority": {
   211  				Decoder: "plain",
   212  				Value:   []byte(metadata[m.namespace+".app-provider.priority"]),
   213  			},
   214  		}}
   215  	}
   216  	return p
   217  }
   218  
   219  func (m *manager) updateProvidersFromMicroRegistry() error {
   220  	lst, err := m.getProvidersFromMicroRegistry(context.Background())
   221  	ma := map[string][]*registrypb.ProviderInfo{}
   222  	if err != nil {
   223  		return err
   224  	}
   225  	sortByPriority(lst)
   226  	for _, outer := range lst {
   227  		for _, inner := range outer.MimeTypes {
   228  			ma[inner] = append(ma[inner], outer)
   229  		}
   230  	}
   231  	m.Lock()
   232  	defer m.Unlock()
   233  	m.mimeTypes = ma
   234  	m.providers = lst
   235  	return nil
   236  }
   237  
   238  func equalsProviderInfo(p1, p2 *registrypb.ProviderInfo) bool {
   239  	sameName := p1.Name == p2.Name
   240  	sameAddress := p1.Address == p2.Address
   241  
   242  	if sameName && sameAddress {
   243  		return true
   244  	}
   245  	return false
   246  }
   247  
   248  func getPriority(p *registrypb.ProviderInfo) string {
   249  	if p.Opaque != nil && len(p.Opaque.Map) != 0 {
   250  		if priority, ok := p.Opaque.Map["priority"]; ok {
   251  			return string(priority.GetValue())
   252  		}
   253  	}
   254  	return defaultPriority
   255  }
   256  
   257  func sortByPriority(providers []*registrypb.ProviderInfo) {
   258  	less := func(i, j int) bool {
   259  		prioI, _ := strconv.ParseInt(getPriority(providers[i]), 10, 64)
   260  		prioJ, _ := strconv.ParseInt(getPriority(providers[j]), 10, 64)
   261  		return prioI < prioJ
   262  	}
   263  
   264  	sort.Slice(providers, less)
   265  }