// Package store implements the go-micro store interface package store import ( "context" "encoding/json" "log" "sync" "github.com/cs3org/reva/v2/pkg/storage/utils/metadata" "github.com/gofrs/uuid" olog "github.com/owncloud/ocis/v2/ocis-pkg/log" settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0" "github.com/owncloud/ocis/v2/services/settings/pkg/config" "github.com/owncloud/ocis/v2/services/settings/pkg/settings" "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults" ) var ( // Name is the default name for the settings store Name = "ocis-settings" managerName = "metadata" settingsSpaceID = "f1bdd61a-da7c-49fc-8203-0558109d1b4f" // uuid.Must(uuid.NewV4()).String() rootFolderLocation = "settings" bundleFolderLocation = "settings/bundles" accountsFolderLocation = "settings/accounts" valuesFolderLocation = "settings/values" ) // MetadataClient is the interface to talk to metadata service type MetadataClient interface { SimpleDownload(ctx context.Context, id string) ([]byte, error) SimpleUpload(ctx context.Context, id string, content []byte) error Delete(ctx context.Context, id string) error ReadDir(ctx context.Context, id string) ([]string, error) MakeDirIfNotExist(ctx context.Context, id string) error Init(ctx context.Context, id string) error } // Store interacts with the filesystem to manage settings information type Store struct { Logger olog.Logger mdc MetadataClient cfg *config.Config l *sync.Mutex } // Init initialize the store once, later calls are noops func (s *Store) Init() { if s.mdc != nil { return } s.l.Lock() defer s.l.Unlock() if s.mdc != nil { return } mdc := &CachedMDC{ next: NewMetadataClient(s.cfg.Metadata), cfg: s.cfg, logger: s.Logger, } if err := s.initMetadataClient(mdc); err != nil { s.Logger.Error().Err(err).Msg("error initializing metadata client") } } // New creates a new store func New(cfg *config.Config) settings.Manager { s := Store{ Logger: olog.NewLogger( olog.Color(cfg.Log.Color), olog.Pretty(cfg.Log.Pretty), olog.Level(cfg.Log.Level), olog.File(cfg.Log.File), ), cfg: cfg, l: &sync.Mutex{}, } return &s } // NewMetadataClient returns the MetadataClient func NewMetadataClient(cfg config.Metadata) MetadataClient { mdc, err := metadata.NewCS3Storage(cfg.GatewayAddress, cfg.StorageAddress, cfg.SystemUserID, cfg.SystemUserIDP, cfg.SystemUserAPIKey) if err != nil { log.Fatal("error connecting to mdc:", err) } return mdc } // we need to lazy initialize the MetadataClient because metadata service might not be ready func (s *Store) initMetadataClient(mdc MetadataClient) error { ctx := context.TODO() err := mdc.Init(ctx, settingsSpaceID) if err != nil { return err } for _, p := range []string{ rootFolderLocation, accountsFolderLocation, bundleFolderLocation, valuesFolderLocation, } { err = mdc.MakeDirIfNotExist(ctx, p) if err != nil { return err } } for _, p := range s.cfg.Bundles { b, err := json.Marshal(p) if err != nil { return err } err = mdc.SimpleUpload(ctx, bundlePath(p.Id), b) if err != nil { return err } } for _, p := range defaults.DefaultRoleAssignments(s.cfg) { accountUUID := p.AccountUuid roleID := p.RoleId err = mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID)) if err != nil { return err } assIDs, err := mdc.ReadDir(ctx, accountPath(accountUUID)) if err != nil { return err } if len(assIDs) > 0 { // There is already a role assignment for this ID, skip to the next continue } ass := &settingsmsg.UserRoleAssignment{ Id: uuid.Must(uuid.NewV4()).String(), AccountUuid: accountUUID, RoleId: roleID, } b, err := json.Marshal(ass) if err != nil { return err } err = mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b) if err != nil { return err } } s.mdc = mdc return nil } func init() { settings.Registry[managerName] = New }