diff --git a/docs/extensions/storage/ports.md b/docs/extensions/storage/ports.md index 83ef871b8..b50b0f6b3 100644 --- a/docs/extensions/storage/ports.md +++ b/docs/extensions/storage/ports.md @@ -34,6 +34,8 @@ For now, the storage service uses these ports to preconfigure those services: | 9159 | storage users debug | | 9160 | groups | | 9161 | groups debug | +| 9164 | appprovider | +| 9165 | appprovider debug | | 9178 | storage public link | | 9179 | storage public link data | | 9215 | storage meta grpc | diff --git a/ocis/pkg/command/appprovider.go b/ocis/pkg/command/appprovider.go new file mode 100644 index 000000000..f43a491e6 --- /dev/null +++ b/ocis/pkg/command/appprovider.go @@ -0,0 +1,45 @@ +// +build !simple + +package command + +import ( + "github.com/micro/cli/v2" + "github.com/owncloud/ocis/ocis-pkg/config" + "github.com/owncloud/ocis/ocis/pkg/register" + "github.com/owncloud/ocis/storage/pkg/command" + svcconfig "github.com/owncloud/ocis/storage/pkg/config" + "github.com/owncloud/ocis/storage/pkg/flagset" +) + +// AppProviderCommand is the entrypoint for the reva-gateway command. +func AppProviderCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "app-provider", + Usage: "Start appprovider for providing apps", + Category: "Extensions", + Flags: flagset.AppProviderWithConfig(cfg.Storage), + Action: func(c *cli.Context) error { + origCmd := command.AppProvider(configureAppProvider(cfg)) + return handleOriginalAction(c, origCmd) + }, + } +} + +func configureAppProvider(cfg *config.Config) *svcconfig.Config { + cfg.Storage.Log.Level = cfg.Log.Level + cfg.Storage.Log.Pretty = cfg.Log.Pretty + cfg.Storage.Log.Color = cfg.Log.Color + + if cfg.Tracing.Enabled { + cfg.Storage.Tracing.Enabled = cfg.Tracing.Enabled + cfg.Storage.Tracing.Type = cfg.Tracing.Type + cfg.Storage.Tracing.Endpoint = cfg.Tracing.Endpoint + cfg.Storage.Tracing.Collector = cfg.Tracing.Collector + } + + return cfg.Storage +} + +func init() { + register.AddCommand(AppProviderCommand) +} diff --git a/ocis/pkg/command/storageappprovider.go b/ocis/pkg/command/storageappprovider.go new file mode 100644 index 000000000..883069ab7 --- /dev/null +++ b/ocis/pkg/command/storageappprovider.go @@ -0,0 +1,45 @@ +// +build !simple + +package command + +import ( + "github.com/micro/cli/v2" + "github.com/owncloud/ocis/ocis-pkg/config" + "github.com/owncloud/ocis/ocis/pkg/register" + "github.com/owncloud/ocis/storage/pkg/command" + svcconfig "github.com/owncloud/ocis/storage/pkg/config" + "github.com/owncloud/ocis/storage/pkg/flagset" +) + +// StorageAppProviderCommand is the entrypoint for the reva-app-provider command. +func StorageAppProviderCommand(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "storage-app-provider", + Usage: "Start storage app-provider service", + Category: "Extensions", + Flags: flagset.AppProviderWithConfig(cfg.Storage), + Action: func(c *cli.Context) error { + origCmd := command.AppProvider(configureStorageAppProvider(cfg)) + return handleOriginalAction(c, origCmd) + }, + } +} + +func configureStorageAppProvider(cfg *config.Config) *svcconfig.Config { + cfg.Storage.Log.Level = cfg.Log.Level + cfg.Storage.Log.Pretty = cfg.Log.Pretty + cfg.Storage.Log.Color = cfg.Log.Color + + if cfg.Tracing.Enabled { + cfg.Storage.Tracing.Enabled = cfg.Tracing.Enabled + cfg.Storage.Tracing.Type = cfg.Tracing.Type + cfg.Storage.Tracing.Endpoint = cfg.Tracing.Endpoint + cfg.Storage.Tracing.Collector = cfg.Tracing.Collector + } + + return cfg.Storage +} + +func init() { + register.AddCommand(StorageAppProviderCommand) +} diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go index ab4229f65..6ff29a158 100644 --- a/ocis/pkg/runtime/service/service.go +++ b/ocis/pkg/runtime/service/service.go @@ -108,6 +108,7 @@ func NewService(options ...Option) (*Service, error) { s.ServicesRegistry["storage-home"] = storage.NewStorageHome s.ServicesRegistry["storage-users"] = storage.NewStorageUsers s.ServicesRegistry["storage-public-link"] = storage.NewStoragePublicLink + s.ServicesRegistry["storage-appprovider"] = storage.NewAppProvider // populate delayed services s.Delayed["storage-sharing"] = storage.NewSharing diff --git a/storage/pkg/command/appprovider.go b/storage/pkg/command/appprovider.go new file mode 100644 index 000000000..ef4eb3ccb --- /dev/null +++ b/storage/pkg/command/appprovider.go @@ -0,0 +1,148 @@ +package command + +import ( + "context" + "flag" + "os" + "path" + + "github.com/cs3org/reva/cmd/revad/runtime" + "github.com/gofrs/uuid" + "github.com/micro/cli/v2" + "github.com/oklog/run" + ociscfg "github.com/owncloud/ocis/ocis-pkg/config" + "github.com/owncloud/ocis/ocis-pkg/sync" + "github.com/owncloud/ocis/storage/pkg/config" + "github.com/owncloud/ocis/storage/pkg/flagset" + "github.com/owncloud/ocis/storage/pkg/server/debug" + "github.com/owncloud/ocis/storage/pkg/tracing" + "github.com/thejerf/suture/v4" +) + +// AppProvider is the entrypoint for the app provider command. +func AppProvider(cfg *config.Config) *cli.Command { + return &cli.Command{ + Name: "app-provider", + Usage: "Start appprovider for providing apps", + Flags: flagset.AppProviderWithConfig(cfg), + Before: func(c *cli.Context) error { + cfg.Reva.AppProvider.Services = c.StringSlice("service") + + return nil + }, + Action: func(c *cli.Context) error { + logger := NewLogger(cfg) + tracing.Configure(cfg, logger) + gr := run.Group{} + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + uuid := uuid.Must(uuid.NewV4()) + pidFile := path.Join(os.TempDir(), "revad-"+c.Command.Name+"-"+uuid.String()+".pid") + + rcfg := appProviderConfigFromStruct(c, cfg) + + gr.Add(func() error { + runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger)) + return nil + }, func(_ error) { + logger.Info(). + Str("server", c.Command.Name). + Msg("Shutting down server") + + cancel() + }) + + debugServer, err := debug.Server( + debug.Name(c.Command.Name+"-debug"), + debug.Addr(cfg.Reva.AppProvider.DebugAddr), + debug.Logger(logger), + debug.Context(ctx), + debug.Config(cfg), + ) + + if err != nil { + logger.Info().Err(err).Str("server", "debug").Msg("Failed to initialize server") + return err + } + + gr.Add(debugServer.ListenAndServe, func(_ error) { + cancel() + }) + + if !cfg.Reva.AppProvider.Supervised { + sync.Trap(&gr, cancel) + } + + return gr.Run() + }, + } +} + +// appProviderConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service. +func appProviderConfigFromStruct(c *cli.Context, cfg *config.Config) map[string]interface{} { + rcfg := map[string]interface{}{ + "core": map[string]interface{}{ + "max_cpus": cfg.Reva.AppProvider.MaxCPUs, + "tracing_enabled": cfg.Tracing.Enabled, + "tracing_endpoint": cfg.Tracing.Endpoint, + "tracing_collector": cfg.Tracing.Collector, + "tracing_service_name": c.Command.Name, + }, + "shared": map[string]interface{}{ + "jwt_secret": cfg.Reva.JWTSecret, + }, + "grpc": map[string]interface{}{ + "network": cfg.Reva.AppProvider.GRPCNetwork, + "address": cfg.Reva.AppProvider.GRPCAddr, + // TODO build services dynamically + "services": map[string]interface{}{ + "appprovider": map[string]interface{}{ + "driver": cfg.Reva.AppProvider.Driver, + "demo": map[string]interface{}{}, + "wopiinsecure": cfg.Reva.AppProvider.WopiInsecure, + "iopsecret": cfg.Reva.AppProvider.IopSecret, + "wopiurl": cfg.Reva.AppProvider.WopiUrl, + "wopibridgeurl": cfg.Reva.AppProvider.WopiBridgeUrl, + }, + }, + }, + } + return rcfg +} + +// AppProviderSutureService allows for the app-provider command to be embedded and supervised by a suture supervisor tree. +type AppProviderSutureService struct { + cfg *config.Config +} + +// NewAppProvider creates a new store.AppProviderSutureService +func NewAppProvider(cfg *ociscfg.Config) suture.Service { + if cfg.Mode == 0 { + cfg.Storage.Reva.AppProvider.Supervised = true + } + return AppProviderSutureService{ + cfg: cfg.Storage, + } +} + +func (s AppProviderSutureService) Serve(ctx context.Context) error { + s.cfg.Reva.AppProvider.Context = ctx + f := &flag.FlagSet{} + for k := range AppProvider(s.cfg).Flags { + if err := AppProvider(s.cfg).Flags[k].Apply(f); err != nil { + return err + } + } + cliCtx := cli.NewContext(nil, f, nil) + if AppProvider(s.cfg).Before != nil { + if err := AppProvider(s.cfg).Before(cliCtx); err != nil { + return err + } + } + if err := AppProvider(s.cfg).Action(cliCtx); err != nil { + return err + } + + return nil +} diff --git a/storage/pkg/command/gateway.go b/storage/pkg/command/gateway.go index 50032f2e7..0d7f03526 100644 --- a/storage/pkg/command/gateway.go +++ b/storage/pkg/command/gateway.go @@ -160,6 +160,15 @@ func gatewayConfigFromStruct(c *cli.Context, cfg *config.Config, logger log.Logg }, }, }, + "appregistry": map[string]interface{}{ + "driver": "static", + "static": map[string]interface{}{ + "rules": map[string]interface{}{ + //TODO: add types configuration + "application/vnd.oasis.opendocument.text": cfg.Reva.AppProvider.Endpoint, + }, + }, + }, "storageregistry": map[string]interface{}{ "driver": cfg.Reva.StorageRegistry.Driver, "drivers": map[string]interface{}{ diff --git a/storage/pkg/command/root.go b/storage/pkg/command/root.go index eb20e0f8d..1c99f2aad 100644 --- a/storage/pkg/command/root.go +++ b/storage/pkg/command/root.go @@ -76,6 +76,7 @@ func Execute(cfg *config.Config) error { Gateway(cfg), Users(cfg), Groups(cfg), + AppProvider(cfg), AuthBasic(cfg), AuthBearer(cfg), Sharing(cfg), diff --git a/storage/pkg/config/config.go b/storage/pkg/config/config.go index e24b21c91..9c9de1c46 100644 --- a/storage/pkg/config/config.go +++ b/storage/pkg/config/config.go @@ -39,6 +39,16 @@ type StorageRegistry struct { JSON string } +// AppProvider defines the available app provider configuration +type AppProvider struct { + Port + Driver string + IopSecret string + WopiInsecure bool + WopiUrl string + WopiBridgeUrl string +} + // Sharing defines the available sharing configuration. type Sharing struct { Port @@ -390,6 +400,7 @@ type Reva struct { StorageUsers StoragePort StoragePublicLink PublicStorage StorageMetadata StoragePort + AppProvider AppProvider // Configs can be used to configure the reva instance. // Services and Ports will be ignored if this is used Configs map[string]interface{} diff --git a/storage/pkg/flagset/appprovider.go b/storage/pkg/flagset/appprovider.go new file mode 100644 index 000000000..2a839b176 --- /dev/null +++ b/storage/pkg/flagset/appprovider.go @@ -0,0 +1,100 @@ +package flagset + +import ( + "github.com/micro/cli/v2" + "github.com/owncloud/ocis/ocis-pkg/flags" + "github.com/owncloud/ocis/storage/pkg/config" +) + +// AppProviderWithConfig applies cfg to the root flagset +func AppProviderWithConfig(cfg *config.Config) []cli.Flag { + flags := []cli.Flag{ + + // debug ports are the odd ports + &cli.StringFlag{ + Name: "debug-addr", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.DebugAddr, "0.0.0.0:9165"), + Usage: "Address to bind debug server", + EnvVars: []string{"APP_PROVIDER_BASIC_DEBUG_ADDR"}, + Destination: &cfg.Reva.AppProvider.DebugAddr, + }, + + // Auth + + // Services + + // AppProvider + + &cli.StringFlag{ + Name: "network", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.GRPCNetwork, "tcp"), + Usage: "Network to use for the storage auth-basic service, can be 'tcp', 'udp' or 'unix'", + EnvVars: []string{"APP_PROVIDER_BASIC_GRPC_NETWORK"}, + Destination: &cfg.Reva.AppProvider.GRPCNetwork, + }, + &cli.StringFlag{ + Name: "addr", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.GRPCAddr, "0.0.0.0:9164"), + Usage: "Address to bind storage service", + EnvVars: []string{"APP_PROVIDER_BASIC_GRPC_ADDR"}, + Destination: &cfg.Reva.AppProvider.GRPCAddr, + }, + &cli.StringSliceFlag{ + Name: "service", + Value: cli.NewStringSlice("appprovider"), + Usage: "--service appprovider [--service otherservice]", + EnvVars: []string{"APP_PROVIDER_BASIC_SERVICES"}, + }, + &cli.StringFlag{ + Name: "driver", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.Driver, "demo"), + Usage: "app provider driver", + EnvVars: []string{"APP_PROVIDER_DRIVER"}, + Destination: &cfg.Reva.AppProvider.Driver, + }, + &cli.StringFlag{ + Name: "iopsecret", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.IopSecret, ""), + Usage: "IOP Secret (Shared with WOPI server)", + EnvVars: []string{"APP_PROVIDER_IOP_SECRET"}, + Destination: &cfg.Reva.AppProvider.IopSecret, + }, + &cli.BoolFlag{ + Name: "wopiinsecure", + Value: flags.OverrideDefaultBool(cfg.Reva.AppProvider.WopiInsecure, false), + Usage: "Disable SSL certificate verification of WOPI server and WOPI bridge", + EnvVars: []string{"APP_PROVIDER_WOPI_INSECURE"}, + Destination: &cfg.Reva.AppProvider.WopiInsecure, + }, + &cli.StringFlag{ + Name: "wopiurl", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.WopiUrl, ""), + Usage: "WOPI server URL", + EnvVars: []string{"APP_PROVIDER_WOPI_URL"}, + Destination: &cfg.Reva.AppProvider.WopiUrl, + }, + &cli.StringFlag{ + Name: "wopibridgeurl", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.WopiBridgeUrl, ""), + Usage: "WOPI bridge URL", + EnvVars: []string{"APP_PROVIDER_WOPI_BRIDGE_URL"}, + Destination: &cfg.Reva.AppProvider.WopiBridgeUrl, + }, + + // Gateway + + &cli.StringFlag{ + Name: "gateway-url", + Value: flags.OverrideDefaultString(cfg.Reva.Gateway.Endpoint, "localhost:9142"), + Usage: "URL to use for the storage gateway service", + EnvVars: []string{"STORAGE_GATEWAY_ENDPOINT"}, + Destination: &cfg.Reva.Gateway.Endpoint, + }, + } + + flags = append(flags, TracingWithConfig(cfg)...) + flags = append(flags, DebugWithConfig(cfg)...) + flags = append(flags, SecretWithConfig(cfg)...) + + return flags +} diff --git a/storage/pkg/flagset/gateway.go b/storage/pkg/flagset/gateway.go index af603478f..ba0585996 100644 --- a/storage/pkg/flagset/gateway.go +++ b/storage/pkg/flagset/gateway.go @@ -62,7 +62,7 @@ func GatewayWithConfig(cfg *config.Config) []cli.Flag { }, &cli.StringSliceFlag{ Name: "service", - Value: cli.NewStringSlice("gateway", "authregistry", "storageregistry"), // TODO appregistry + Value: cli.NewStringSlice("gateway", "authregistry", "storageregistry", "appregistry"), Usage: "--service gateway [--service authregistry]", EnvVars: []string{"STORAGE_GATEWAY_SERVICES"}, }, @@ -193,6 +193,13 @@ func GatewayWithConfig(cfg *config.Config) []cli.Flag { EnvVars: []string{"STORAGE_SHARING_ENDPOINT"}, Destination: &cfg.Reva.Sharing.Endpoint, }, + &cli.StringFlag{ + Name: "appprovider-endpoint", + Value: flags.OverrideDefaultString(cfg.Reva.AppProvider.Endpoint, "localhost:9164"), + Usage: "endpoint to use for the app provider", + EnvVars: []string{"STORAGE_APPPROVIDER_ENDPOINT"}, + Destination: &cfg.Reva.AppProvider.Endpoint, + }, // register home storage