Merge pull request #1 from owncloud/experiment/account-interface
Better interfaces
This commit is contained in:
@@ -3,9 +3,11 @@ module github.com/owncloud/ocis-accounts
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/coreos/etcd v3.3.18+incompatible
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/uuid v1.1.1
|
||||
github.com/micro/cli v0.2.0
|
||||
github.com/micro/cli/v2 v2.1.1
|
||||
github.com/micro/go-micro v1.18.0
|
||||
github.com/micro/go-micro/v2 v2.0.0
|
||||
github.com/oklog/run v1.1.0
|
||||
|
||||
@@ -381,6 +381,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/joncalhoun/qson v0.0.0-20170526102502-8a9cab3a62b1/go.mod h1:DFXrEwSRX0p/aSvxE21319menCBFeQO0jXpRj7LEZUA=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package account
|
||||
|
||||
import "github.com/owncloud/ocis-accounts/pkg/config"
|
||||
|
||||
var (
|
||||
// Registry uses the strategy pattern as a registry
|
||||
Registry = map[string]RegisterFunc{}
|
||||
|
||||
// DefaultManager defines the default accounts manager
|
||||
DefaultManager = "filesystem"
|
||||
)
|
||||
|
||||
// RegisterFunc stores store constructors
|
||||
type RegisterFunc func(*config.Config) Manager
|
||||
|
||||
// Manager is an accounts service interface
|
||||
type Manager interface {
|
||||
// Read a record
|
||||
Read(key string) *Record
|
||||
// Write a record
|
||||
Write(*Record) *Record
|
||||
// List all records
|
||||
List() []*Record
|
||||
}
|
||||
|
||||
// Record is an entry in the account storage
|
||||
type Record struct {
|
||||
Key string
|
||||
Value []byte
|
||||
}
|
||||
+8
-5
@@ -3,9 +3,12 @@ package command
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/micro/cli"
|
||||
_ "github.com/owncloud/ocis-accounts/pkg/registry"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-hello/pkg/version"
|
||||
|
||||
// init store manager
|
||||
_ "github.com/owncloud/ocis-accounts/pkg/store"
|
||||
)
|
||||
|
||||
// Execute is the entry point for the ocis-accounts command.
|
||||
@@ -15,15 +18,15 @@ func Execute() error {
|
||||
Version: version.String,
|
||||
Usage: "Example service for Reva/oCIS",
|
||||
|
||||
Authors: []cli.Author{
|
||||
Authors: []*cli.Author{
|
||||
{
|
||||
Name: "ownCloud GmbH",
|
||||
Email: "support@owncloud.com",
|
||||
},
|
||||
},
|
||||
|
||||
Commands: []cli.Command{
|
||||
Server(),
|
||||
Commands: []*cli.Command{
|
||||
Server(config.New()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
+27
-5
@@ -3,29 +3,51 @@ package command
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/micro/cli"
|
||||
"github.com/micro/cli/v2"
|
||||
"github.com/oklog/run"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/micro/grpc"
|
||||
)
|
||||
|
||||
// Server is the entry point for the server command.
|
||||
func Server() cli.Command {
|
||||
return cli.Command{
|
||||
func Server(cfg *config.Config) *cli.Command {
|
||||
baseDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
|
||||
return &cli.Command{
|
||||
Name: "server",
|
||||
Usage: "Start accounts service",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "manager",
|
||||
DefaultText: "filesystem",
|
||||
Usage: "store controller driver. eg: filesystem",
|
||||
Value: "filesystem",
|
||||
EnvVars: []string{"OCIS_ACCOUNTS_MANAGER"},
|
||||
Destination: &cfg.Manager,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "mount-path",
|
||||
DefaultText: "binary default running location",
|
||||
Usage: "where to mount the ocis accounts store",
|
||||
Value: baseDir,
|
||||
EnvVars: []string{"OCIS_ACCOUNTS_MOUNT_PATH"},
|
||||
Destination: &cfg.MountPath,
|
||||
},
|
||||
},
|
||||
Action: func(c *cli.Context) error {
|
||||
gr := run.Group{}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
defer cancel()
|
||||
service := grpc.NewService(ctx)
|
||||
service := grpc.NewService(ctx, cfg)
|
||||
|
||||
gr.Add(func() error {
|
||||
return service.Run()
|
||||
}, func(_ error) {
|
||||
|
||||
fmt.Println("shutting down grpc server")
|
||||
cancel()
|
||||
})
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Package config should be moved to internal
|
||||
package config
|
||||
|
||||
// Config captures ocis-accounts configuration parameters
|
||||
type Config struct {
|
||||
MountPath string
|
||||
Manager string
|
||||
}
|
||||
|
||||
// New returns a new config
|
||||
func New() *Config {
|
||||
return &Config{}
|
||||
}
|
||||
@@ -1,25 +1,25 @@
|
||||
package grpc
|
||||
|
||||
// package grpc uses `ocis-pkg` to start a go-micro service
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
svc "github.com/owncloud/ocis-accounts/pkg/service/v0"
|
||||
"github.com/owncloud/ocis-pkg/service/grpc"
|
||||
)
|
||||
|
||||
// NewService initializes a new go-micro service ready to run
|
||||
func NewService(c context.Context) grpc.Service {
|
||||
// NewService creates a grpc service
|
||||
func NewService(c context.Context, cfg *config.Config) grpc.Service {
|
||||
service := grpc.NewService(
|
||||
// TODO options come from configuration
|
||||
grpc.Name("accounts"),
|
||||
grpc.Namespace("com.owncloud"),
|
||||
grpc.Address("localhost:9999"),
|
||||
grpc.Context(c),
|
||||
)
|
||||
|
||||
// add a handler to the service
|
||||
hdlr := svc.New()
|
||||
hdlr := svc.New(cfg)
|
||||
proto.RegisterSettingsServiceHandler(service.Server(), hdlr)
|
||||
|
||||
service.Init()
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Package registry provides accessors to runtime services
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
mstore "github.com/micro/go-micro/v2/store"
|
||||
store "github.com/owncloud/ocis-accounts/pkg/store/filesystem"
|
||||
)
|
||||
|
||||
var (
|
||||
once *sync.Once = &sync.Once{}
|
||||
// Store is a micro store implementation
|
||||
Store mstore.Store
|
||||
)
|
||||
|
||||
func init() {
|
||||
once.Do(func() {
|
||||
Store = store.New()
|
||||
})
|
||||
}
|
||||
+42
-33
@@ -5,21 +5,35 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
mstore "github.com/micro/go-micro/v2/store"
|
||||
"github.com/owncloud/ocis-accounts/pkg/account"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
"github.com/owncloud/ocis-accounts/pkg/proto/v0"
|
||||
|
||||
"github.com/owncloud/ocis-accounts/pkg/registry"
|
||||
olog "github.com/owncloud/ocis-pkg/log"
|
||||
)
|
||||
|
||||
// New returns a new instance of Service
|
||||
func New() Service {
|
||||
return Service{}
|
||||
func New(cfg *config.Config) Service {
|
||||
s := Service{
|
||||
Config: cfg,
|
||||
}
|
||||
|
||||
if newReg, ok := account.Registry[cfg.Manager]; ok {
|
||||
s.Manager = newReg(cfg)
|
||||
} else {
|
||||
l := olog.NewLogger(olog.Name("ocis-accounts"))
|
||||
l.Fatal().Msgf("unknown manager: %v", cfg.Manager)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Service implements the SettingsServiceHandler interface generated on accounts.pb.micro.go
|
||||
type Service struct{}
|
||||
// Service implements the SettingsServiceHandler interface
|
||||
type Service struct {
|
||||
Config *config.Config
|
||||
Manager account.Manager
|
||||
}
|
||||
|
||||
// Set implements the SettingsServiceHandler interface generated on accounts.pb.micro.go
|
||||
// Set implements the SettingsServiceHandler interface
|
||||
// This implementation replaces the existent data with the requested. It does not calculate diff
|
||||
func (s Service) Set(c context.Context, req *proto.Record, res *proto.Record) error {
|
||||
settingsJSON, err := json.Marshal(req.Payload)
|
||||
@@ -27,45 +41,40 @@ func (s Service) Set(c context.Context, req *proto.Record, res *proto.Record) er
|
||||
return err
|
||||
}
|
||||
|
||||
record := mstore.Record{
|
||||
s.Manager.Write(&account.Record{
|
||||
Key: req.Key,
|
||||
Value: settingsJSON,
|
||||
}
|
||||
})
|
||||
|
||||
return registry.Store.Write(&record)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get implements the SettingsServiceHandler interface generated on accounts.pb.micro.go
|
||||
// Get implements the SettingsServiceHandler interface
|
||||
func (s Service) Get(c context.Context, req *proto.Query, res *proto.Record) error {
|
||||
contents, err := registry.Store.Read(req.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contents := s.Manager.Read(req.Key)
|
||||
|
||||
if len(contents) > 0 {
|
||||
r := &proto.Payload{}
|
||||
json.Unmarshal(contents[0].Value, r)
|
||||
res.Payload = r
|
||||
}
|
||||
r := &proto.Payload{}
|
||||
json.Unmarshal(contents.Value, r)
|
||||
res.Payload = r
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// List implements the SettingsServiceHandler interface generated on accounts.pb.micro.go
|
||||
// List implements the SettingsServiceHandler interface
|
||||
func (s Service) List(ctx context.Context, in *empty.Empty, res *proto.Records) error {
|
||||
r := &proto.Records{}
|
||||
contents, err := registry.Store.List()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// r := &proto.Records{}
|
||||
// contents, err := registry.Store.List()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
for _, v := range contents {
|
||||
r.Records = append(r.Records, &proto.Record{
|
||||
Key: v.Key,
|
||||
})
|
||||
}
|
||||
// for _, v := range contents {
|
||||
// r.Records = append(r.Records, &proto.Record{
|
||||
// Key: v.Key,
|
||||
// })
|
||||
// }
|
||||
|
||||
res.Records = r.Records
|
||||
// res.Records = r.Records
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,18 +2,23 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
mstore "github.com/micro/go-micro/v2/store"
|
||||
"github.com/owncloud/ocis-accounts/pkg/account"
|
||||
"github.com/owncloud/ocis-accounts/pkg/config"
|
||||
olog "github.com/owncloud/ocis-pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
// StoreName is the default name for the accounts store
|
||||
StoreName string = "ocis-store"
|
||||
managerName = "filesystem"
|
||||
)
|
||||
|
||||
// StoreName is the default name for the store container
|
||||
var StoreName string = "ocis-store"
|
||||
|
||||
// Store interacts with the filesystem to manage account information
|
||||
type Store struct {
|
||||
@@ -21,90 +26,75 @@ type Store struct {
|
||||
Logger olog.Logger
|
||||
}
|
||||
|
||||
// New returns a new stor. TODO add mountPath as a flag. Accept a *config argument
|
||||
func New() *Store {
|
||||
// New creates a new store
|
||||
func New(cfg *config.Config) account.Manager {
|
||||
s := Store{
|
||||
Logger: olog.NewLogger(),
|
||||
Logger: olog.NewLogger(olog.Name("ocis-accounts")),
|
||||
}
|
||||
|
||||
// default to the current working directory if not configured
|
||||
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msg("initializing accounts store")
|
||||
}
|
||||
|
||||
dest := filepath.Join(dir, StoreName)
|
||||
dest := filepath.Join(cfg.MountPath, StoreName)
|
||||
if _, err := os.Stat(dest); err != nil {
|
||||
s.Logger.Info().Msgf("creating container on %v", dest)
|
||||
os.Mkdir(dest, 0700)
|
||||
err := os.MkdirAll(dest, 0700)
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msgf("providing container on %v", dest)
|
||||
}
|
||||
}
|
||||
|
||||
s.mountPath = dest
|
||||
return &s
|
||||
}
|
||||
|
||||
// Init implements the store interface
|
||||
func (s Store) Init(...mstore.Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List returns all the identities in the mountPath folder
|
||||
func (s Store) List() ([]*mstore.Record, error) {
|
||||
records := []*mstore.Record{}
|
||||
func (s Store) List() []*account.Record {
|
||||
records := []*account.Record{}
|
||||
identities, err := ioutil.ReadDir(s.mountPath)
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msgf("error reading %v", s.mountPath)
|
||||
return records
|
||||
}
|
||||
|
||||
s.Logger.Info().Msg("listing identities")
|
||||
for _, v := range identities {
|
||||
records = append(records, &mstore.Record{
|
||||
records = append(records, &account.Record{
|
||||
Key: v.Name(),
|
||||
})
|
||||
}
|
||||
|
||||
return records, nil
|
||||
return records
|
||||
}
|
||||
|
||||
// Read implements the store interface. This implementation only reads by id.
|
||||
func (s Store) Read(key string, opts ...mstore.ReadOption) ([]*mstore.Record, error) {
|
||||
func (s Store) Read(key string) *account.Record {
|
||||
contents, err := ioutil.ReadFile(path.Join(s.mountPath, key))
|
||||
if err != nil {
|
||||
s.Logger.Err(err).Msgf("error reading contents of key %v: file not found", key)
|
||||
return []*mstore.Record{}, err
|
||||
return &account.Record{}
|
||||
}
|
||||
|
||||
return []*mstore.Record{
|
||||
&mstore.Record{
|
||||
Key: key,
|
||||
Value: contents,
|
||||
},
|
||||
}, nil
|
||||
return &account.Record{
|
||||
Key: key,
|
||||
Value: contents,
|
||||
}
|
||||
}
|
||||
|
||||
// Write implements the store interface
|
||||
func (s Store) Write(rec *mstore.Record) error {
|
||||
func (s Store) Write(rec *account.Record) *account.Record {
|
||||
path := filepath.Join(s.mountPath, rec.Key)
|
||||
|
||||
if len(rec.Key) < 1 {
|
||||
s.Logger.Error().Msg("key cannot be empty")
|
||||
return fmt.Errorf("%v", "key is empty")
|
||||
return &account.Record{}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(path, rec.Value, 0644); err != nil {
|
||||
return err
|
||||
return &account.Record{}
|
||||
}
|
||||
|
||||
s.Logger.Info().Msgf("%v bytes written to %v", len(rec.Value), path)
|
||||
return nil
|
||||
return rec
|
||||
}
|
||||
|
||||
// Delete implements the store interface
|
||||
func (s Store) Delete(key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements the store interface, and the stringer interface
|
||||
func (s Store) String() string {
|
||||
return "store"
|
||||
func init() {
|
||||
account.Registry[managerName] = New
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
_ "github.com/owncloud/ocis-accounts/pkg/store/filesystem"
|
||||
)
|
||||
Reference in New Issue
Block a user