Merge pull request #1 from owncloud/experiment/account-interface

Better interfaces
This commit is contained in:
Alex Unger
2020-02-04 12:20:39 +01:00
committed by GitHub
11 changed files with 166 additions and 112 deletions
+2
View File
@@ -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
+1
View File
@@ -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=
+30
View File
@@ -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
View File
@@ -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
View File
@@ -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()
})
+13
View File
@@ -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{}
}
+5 -5
View File
@@ -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()
-21
View File
@@ -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
View File
@@ -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
}
+33 -43
View File
@@ -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
}
+5
View File
@@ -0,0 +1,5 @@
package store
import (
_ "github.com/owncloud/ocis-accounts/pkg/store/filesystem"
)