From e14453f73045397de142cbb751862c653be3ca37 Mon Sep 17 00:00:00 2001 From: Adam Martin Date: Mon, 22 Jan 2024 11:31:46 -0500 Subject: [PATCH 1/2] add fileserver option for `store serve` --- cmd/hauler/cli/store.go | 2 +- cmd/hauler/cli/store/serve.go | 103 +++++++++++++++++++++++++--------- internal/server/file.go | 3 +- 3 files changed, 80 insertions(+), 28 deletions(-) diff --git a/cmd/hauler/cli/store.go b/cmd/hauler/cli/store.go index 86a64661..7b699bee 100644 --- a/cmd/hauler/cli/store.go +++ b/cmd/hauler/cli/store.go @@ -112,7 +112,7 @@ func addStoreServe() *cobra.Command { cmd := &cobra.Command{ Use: "serve", - Short: "Expose the content of a local store through an OCI compliant server", + Short: "Expose the content of a local store through an OCI compliant registry or file server", RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() diff --git a/cmd/hauler/cli/store/serve.go b/cmd/hauler/cli/store/serve.go index 83586ce4..9dd0f1c5 100644 --- a/cmd/hauler/cli/store/serve.go +++ b/cmd/hauler/cli/store/serve.go @@ -17,6 +17,7 @@ import ( "github.com/rancherfederal/hauler/pkg/store" "github.com/rancherfederal/hauler/internal/server" + "github.com/rancherfederal/hauler/pkg/log" ) type ServeOpts struct { @@ -25,7 +26,8 @@ type ServeOpts struct { Port int RootDir string ConfigFile string - Daemon bool + + Files bool storedir string } @@ -33,45 +35,96 @@ type ServeOpts struct { func (o *ServeOpts) AddFlags(cmd *cobra.Command) { f := cmd.Flags() - f.IntVarP(&o.Port, "port", "p", 5000, "Port to listen on") - f.StringVar(&o.RootDir, "directory", "registry", "Directory to use for registry backend (defaults to '$PWD/registry')") + f.BoolVarP(&o.Files, "files", "f", false, "Toggle file server instead of registry") + f.IntVarP(&o.Port, "port", "p", 0, "Port to listen on. Defaults to 5000 for registry and 8080 for file server.") + f.StringVar(&o.RootDir, "directory", "", "Directory to use for backend. Defaults to $PWD/registry for registry and $PWD/store-files for file server.") f.StringVarP(&o.ConfigFile, "config", "c", "", "Path to a config file, will override all other configs") - f.BoolVarP(&o.Daemon, "daemon", "d", false, "Toggle serving as a daemon") + + cmd.PreRunE = func(cmd *cobra.Command, args []string) error { + if o.Port == 0 { + o.Port = getDefaultPort(o.Files) + } + if o.RootDir == "" { + o.RootDir = getDefaultDirectory(o.Files) + } + return nil + } +} + +func getDefaultPort(files bool) int { + if files { + return 8080 + } + return 5000 +} + +func getDefaultDirectory(files bool) string { + if files { + return "store-files" + } + return "registry" } // ServeCmd serves the embedded registry almost identically to how distribution/v3 does it func ServeCmd(ctx context.Context, o *ServeOpts, s *store.Layout) error { + l := log.FromContext(ctx) ctx = dcontext.WithVersion(ctx, version.Version) - tr := server.NewTempRegistry(ctx, o.RootDir) - if err := tr.Start(); err != nil { - return err - } + if o.Files { + opts := &CopyOpts{} + if err := CopyCmd(ctx, opts, s, "dir://"+o.RootDir); err != nil { + return err + } + + cfg := server.FileConfig{ + Root: o.RootDir, + Port: o.Port, + } + + f, err := server.NewFile(ctx, cfg) + if err != nil { + return err + } + + l.Infof("starting file server on port [%d]", o.Port) + if err := f.ListenAndServe(); err != nil { + return err + } - opts := &CopyOpts{} - if err := CopyCmd(ctx, opts, s, "registry://"+tr.Registry()); err != nil { - return err - } + } else { // start registry - tr.Close() + tr := server.NewTempRegistry(ctx, o.RootDir) + if err := tr.Start(); err != nil { + return err + } - cfg := o.defaultConfig() - if o.ConfigFile != "" { - ucfg, err := loadConfig(o.ConfigFile) - if err != nil { + opts := &CopyOpts{} + if err := CopyCmd(ctx, opts, s, "registry://"+tr.Registry()); err != nil { return err } - cfg = ucfg - } - r, err := server.NewRegistry(ctx, cfg) - if err != nil { - return err - } + tr.Close() - if err = r.ListenAndServe(); err != nil { - return err + cfg := o.defaultConfig() + if o.ConfigFile != "" { + ucfg, err := loadConfig(o.ConfigFile) + if err != nil { + return err + } + cfg = ucfg + } + + l.Infof("starting registry on port [%d]", o.Port) + r, err := server.NewRegistry(ctx, cfg) + if err != nil { + return err + } + + if err = r.ListenAndServe(); err != nil { + return err + } } + return nil } diff --git a/internal/server/file.go b/internal/server/file.go index 74a4ad2a..55a65155 100644 --- a/internal/server/file.go +++ b/internal/server/file.go @@ -21,8 +21,7 @@ type FileConfig struct { // TODO: Better configs func NewFile(ctx context.Context, cfg FileConfig) (Server, error) { r := mux.NewRouter() - r.Handle("/", handlers.LoggingHandler(os.Stdout, http.FileServer(http.Dir(cfg.Root)))) - + r.PathPrefix("/").Handler(handlers.LoggingHandler(os.Stdout, http.StripPrefix("/", http.FileServer(http.Dir(cfg.Root))))) if cfg.Root == "" { cfg.Root = "." } From d9e298b725cafee90435cc72d06df441a7f77aed Mon Sep 17 00:00:00 2001 From: Adam Martin Date: Mon, 22 Jan 2024 13:40:58 -0500 Subject: [PATCH 2/2] adjust to make registry and fileserver subcommands --- cmd/hauler/cli/store.go | 54 +++++++++++-- cmd/hauler/cli/store/serve.go | 143 ++++++++++++++++------------------ 2 files changed, 114 insertions(+), 83 deletions(-) diff --git a/cmd/hauler/cli/store.go b/cmd/hauler/cli/store.go index 7b699bee..4d1a9694 100644 --- a/cmd/hauler/cli/store.go +++ b/cmd/hauler/cli/store.go @@ -108,12 +108,28 @@ func addStoreLoad() *cobra.Command { } func addStoreServe() *cobra.Command { - o := &store.ServeOpts{RootOpts: rootStoreOpts} - cmd := &cobra.Command{ Use: "serve", Short: "Expose the content of a local store through an OCI compliant registry or file server", RunE: func(cmd *cobra.Command, args []string) error { + return cmd.Help() + }, + } + cmd.AddCommand( + addStoreServeRegistry(), + addStoreServeFiles(), + ) + + return cmd +} + +// RegistryCmd serves the embedded registry +func addStoreServeRegistry() *cobra.Command { + o := &store.ServeRegistryOpts{RootOpts: rootStoreOpts} + cmd := &cobra.Command{ + Use: "registry", + Short: "Serve the embedded registry", + RunE: func(cmd *cobra.Command, args []string) error { ctx := cmd.Context() s, err := o.Store(ctx) @@ -121,12 +137,36 @@ func addStoreServe() *cobra.Command { return err } - return store.ServeCmd(ctx, o, s) - }, - } - o.AddFlags(cmd) + return store.ServeRegistryCmd(ctx, o, s) + }, + } - return cmd + o.AddFlags(cmd) + + return cmd +} + +// FileServerCmd serves the file server +func addStoreServeFiles() *cobra.Command { + o := &store.ServeFilesOpts{RootOpts: rootStoreOpts} + cmd := &cobra.Command{ + Use: "fileserver", + Short: "Serve the file server", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + s, err := o.Store(ctx) + if err != nil { + return err + } + + return store.ServeFilesCmd(ctx, o, s) + }, + } + + o.AddFlags(cmd) + + return cmd } func addStoreSave() *cobra.Command { diff --git a/cmd/hauler/cli/store/serve.go b/cmd/hauler/cli/store/serve.go index 9dd0f1c5..50463932 100644 --- a/cmd/hauler/cli/store/serve.go +++ b/cmd/hauler/cli/store/serve.go @@ -20,109 +20,100 @@ import ( "github.com/rancherfederal/hauler/pkg/log" ) -type ServeOpts struct { +type ServeRegistryOpts struct { *RootOpts Port int RootDir string ConfigFile string - Files bool - storedir string } -func (o *ServeOpts) AddFlags(cmd *cobra.Command) { +func (o *ServeRegistryOpts) AddFlags(cmd *cobra.Command) { f := cmd.Flags() - f.BoolVarP(&o.Files, "files", "f", false, "Toggle file server instead of registry") - f.IntVarP(&o.Port, "port", "p", 0, "Port to listen on. Defaults to 5000 for registry and 8080 for file server.") - f.StringVar(&o.RootDir, "directory", "", "Directory to use for backend. Defaults to $PWD/registry for registry and $PWD/store-files for file server.") + f.IntVarP(&o.Port, "port", "p", 5000, "Port to listen on.") + f.StringVar(&o.RootDir, "directory", "registry", "Directory to use for backend. Defaults to $PWD/registry") f.StringVarP(&o.ConfigFile, "config", "c", "", "Path to a config file, will override all other configs") - - cmd.PreRunE = func(cmd *cobra.Command, args []string) error { - if o.Port == 0 { - o.Port = getDefaultPort(o.Files) - } - if o.RootDir == "" { - o.RootDir = getDefaultDirectory(o.Files) - } - return nil - } } -func getDefaultPort(files bool) int { - if files { - return 8080 +func ServeRegistryCmd(ctx context.Context, o *ServeRegistryOpts, s *store.Layout) error { + l := log.FromContext(ctx) + ctx = dcontext.WithVersion(ctx, version.Version) + + tr := server.NewTempRegistry(ctx, o.RootDir) + if err := tr.Start(); err != nil { + return err } - return 5000 -} -func getDefaultDirectory(files bool) string { - if files { - return "store-files" + opts := &CopyOpts{} + if err := CopyCmd(ctx, opts, s, "registry://"+tr.Registry()); err != nil { + return err } - return "registry" -} -// ServeCmd serves the embedded registry almost identically to how distribution/v3 does it -func ServeCmd(ctx context.Context, o *ServeOpts, s *store.Layout) error { - l := log.FromContext(ctx) - ctx = dcontext.WithVersion(ctx, version.Version) + tr.Close() - if o.Files { - opts := &CopyOpts{} - if err := CopyCmd(ctx, opts, s, "dir://"+o.RootDir); err != nil { - return err - } - - cfg := server.FileConfig{ - Root: o.RootDir, - Port: o.Port, - } - - f, err := server.NewFile(ctx, cfg) + cfg := o.defaultRegistryConfig() + if o.ConfigFile != "" { + ucfg, err := loadConfig(o.ConfigFile) if err != nil { return err } - - l.Infof("starting file server on port [%d]", o.Port) - if err := f.ListenAndServe(); err != nil { - return err - } + cfg = ucfg + } - } else { // start registry + l.Infof("starting registry on port [%d]", o.Port) + r, err := server.NewRegistry(ctx, cfg) + if err != nil { + return err + } + + if err = r.ListenAndServe(); err != nil { + return err + } - tr := server.NewTempRegistry(ctx, o.RootDir) - if err := tr.Start(); err != nil { - return err - } + return nil +} - opts := &CopyOpts{} - if err := CopyCmd(ctx, opts, s, "registry://"+tr.Registry()); err != nil { - return err - } +type ServeFilesOpts struct { + *RootOpts - tr.Close() + Port int + RootDir string - cfg := o.defaultConfig() - if o.ConfigFile != "" { - ucfg, err := loadConfig(o.ConfigFile) - if err != nil { - return err - } - cfg = ucfg - } + storedir string +} - l.Infof("starting registry on port [%d]", o.Port) - r, err := server.NewRegistry(ctx, cfg) - if err != nil { - return err - } - - if err = r.ListenAndServe(); err != nil { - return err - } +func (o *ServeFilesOpts) AddFlags(cmd *cobra.Command) { + f := cmd.Flags() + + f.IntVarP(&o.Port, "port", "p", 8080, "Port to listen on.") + f.StringVar(&o.RootDir, "directory", "store-files", "Directory to use for backend. Defaults to $PWD/store-files") +} + +func ServeFilesCmd(ctx context.Context, o *ServeFilesOpts, s *store.Layout) error { + l := log.FromContext(ctx) + ctx = dcontext.WithVersion(ctx, version.Version) + + opts := &CopyOpts{} + if err := CopyCmd(ctx, opts, s, "dir://"+o.RootDir); err != nil { + return err + } + + cfg := server.FileConfig{ + Root: o.RootDir, + Port: o.Port, + } + + f, err := server.NewFile(ctx, cfg) + if err != nil { + return err + } + + l.Infof("starting file server on port [%d]", o.Port) + if err := f.ListenAndServe(); err != nil { + return err } return nil @@ -137,7 +128,7 @@ func loadConfig(filename string) (*configuration.Configuration, error) { return configuration.Parse(f) } -func (o *ServeOpts) defaultConfig() *configuration.Configuration { +func (o *ServeRegistryOpts) defaultRegistryConfig() *configuration.Configuration { cfg := &configuration.Configuration{ Version: "0.1", Storage: configuration.Storage{