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, ®istrypb.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 := ®istrypb.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 }