From 668d73c35bd15f86a34919fe9a8cf0f2dc693b1f Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Mon, 4 Aug 2025 23:13:04 +0200 Subject: [PATCH] feat(backends install): allow to specify name and alias during manual installation Signed-off-by: Ettore Di Giacinto --- core/application/startup.go | 6 ++- core/cli/backends.go | 38 ++++++------- core/startup/backend_preload.go | 96 +++++++++++++++++++-------------- pkg/downloader/uri.go | 28 +++++----- 4 files changed, 96 insertions(+), 72 deletions(-) diff --git a/core/application/startup.go b/core/application/startup.go index 983807bb278d..cbb0be1eb53d 100644 --- a/core/application/startup.go +++ b/core/application/startup.go @@ -59,8 +59,10 @@ func New(opts ...config.AppOption) (*Application, error) { log.Error().Err(err).Msg("error installing models") } - if err := coreStartup.InstallExternalBackends(options.BackendGalleries, options.BackendsPath, nil, options.ExternalBackends...); err != nil { - log.Error().Err(err).Msg("error installing external backends") + for _, backend := range options.ExternalBackends { + if err := coreStartup.InstallExternalBackends(options.BackendGalleries, options.BackendsPath, nil, backend, "", ""); err != nil { + log.Error().Err(err).Msg("error installing external backend") + } } configLoaderOpts := options.ToConfigLoaderOptions() diff --git a/core/cli/backends.go b/core/cli/backends.go index d5fa84878ee5..59f0462cd47e 100644 --- a/core/cli/backends.go +++ b/core/cli/backends.go @@ -23,7 +23,9 @@ type BackendsList struct { } type BackendsInstall struct { - BackendArgs []string `arg:"" optional:"" name:"backends" help:"Backend configuration URLs to load"` + BackendArgs string `arg:"" optional:"" name:"backend" help:"Backend configuration URL to load"` + Name string `arg:"" optional:"" name:"name" help:"Name of the backend"` + Alias string `arg:"" optional:"" name:"alias" help:"Alias of the backend"` BackendsCMDFlags `embed:""` } @@ -66,27 +68,25 @@ func (bi *BackendsInstall) Run(ctx *cliContext.Context) error { log.Error().Err(err).Msg("unable to load galleries") } - for _, backendName := range bi.BackendArgs { - - progressBar := progressbar.NewOptions( - 1000, - progressbar.OptionSetDescription(fmt.Sprintf("downloading backend %s", backendName)), - progressbar.OptionShowBytes(false), - progressbar.OptionClearOnFinish(), - ) - progressCallback := func(fileName string, current string, total string, percentage float64) { - v := int(percentage * 10) - err := progressBar.Set(v) - if err != nil { - log.Error().Err(err).Str("filename", fileName).Int("value", v).Msg("error while updating progress bar") - } - } - - err := startup.InstallExternalBackends(galleries, bi.BackendsPath, progressCallback, backendName) + progressBar := progressbar.NewOptions( + 1000, + progressbar.OptionSetDescription(fmt.Sprintf("downloading backend %s", bi.BackendArgs)), + progressbar.OptionShowBytes(false), + progressbar.OptionClearOnFinish(), + ) + progressCallback := func(fileName string, current string, total string, percentage float64) { + v := int(percentage * 10) + err := progressBar.Set(v) if err != nil { - return err + log.Error().Err(err).Str("filename", fileName).Int("value", v).Msg("error while updating progress bar") } } + + err := startup.InstallExternalBackends(galleries, bi.BackendsPath, progressCallback, bi.BackendArgs, bi.Name, bi.Alias) + if err != nil { + return err + } + return nil } diff --git a/core/startup/backend_preload.go b/core/startup/backend_preload.go index 82f3089b93e7..1595a8e1c088 100644 --- a/core/startup/backend_preload.go +++ b/core/startup/backend_preload.go @@ -1,7 +1,6 @@ package startup import ( - "errors" "fmt" "path/filepath" "strings" @@ -13,49 +12,68 @@ import ( "github.com/rs/zerolog/log" ) -func InstallExternalBackends(galleries []config.Gallery, backendPath string, downloadStatus func(string, string, string, float64), backends ...string) error { - var errs error +func InstallExternalBackends(galleries []config.Gallery, backendPath string, downloadStatus func(string, string, string, float64), backend, name, alias string) error { systemState, err := system.GetSystemState() if err != nil { return fmt.Errorf("failed to get system state: %w", err) } - for _, backend := range backends { - uri := downloader.URI(backend) - switch { - case uri.LooksLikeDir(): - name := filepath.Base(backend) - log.Info().Str("backend", backend).Str("name", name).Msg("Installing backend from path") - if err := gallery.InstallBackend(backendPath, &gallery.GalleryBackend{ - Metadata: gallery.Metadata{ - Name: name, - }, - URI: backend, - }, downloadStatus); err != nil { - errs = errors.Join(err, fmt.Errorf("error installing backend %s", backend)) - } - case uri.LooksLikeOCI(): - name, err := uri.FilenameFromUrl() - if err != nil { - return fmt.Errorf("failed to get filename from URL: %w", err) - } - // strip extension if any - name = strings.TrimSuffix(name, filepath.Ext(name)) + uri := downloader.URI(backend) + switch { + case uri.LooksLikeDir(): + if name == "" { // infer it from the path + name = filepath.Base(backend) + } + log.Info().Str("backend", backend).Str("name", name).Msg("Installing backend from path") + if err := gallery.InstallBackend(backendPath, &gallery.GalleryBackend{ + Metadata: gallery.Metadata{ + Name: name, + }, + Alias: alias, + URI: backend, + }, downloadStatus); err != nil { + return fmt.Errorf("error installing backend %s: %w", backend, err) + } + case uri.LooksLikeOCI() && !uri.LooksLikeOCIFile(): + if name == "" { + return fmt.Errorf("specifying a name is required for OCI images") + } + log.Info().Str("backend", backend).Str("name", name).Msg("Installing backend from OCI image") + if err := gallery.InstallBackend(backendPath, &gallery.GalleryBackend{ + Metadata: gallery.Metadata{ + Name: name, + }, + Alias: alias, + URI: backend, + }, downloadStatus); err != nil { + return fmt.Errorf("error installing backend %s: %w", backend, err) + } + case uri.LooksLikeOCIFile(): + name, err := uri.FilenameFromUrl() + if err != nil { + return fmt.Errorf("failed to get filename from URL: %w", err) + } + // strip extension if any + name = strings.TrimSuffix(name, filepath.Ext(name)) - log.Info().Str("backend", backend).Str("name", name).Msg("Installing backend from OCI image") - if err := gallery.InstallBackend(backendPath, &gallery.GalleryBackend{ - Metadata: gallery.Metadata{ - Name: name, - }, - URI: backend, - }, downloadStatus); err != nil { - errs = errors.Join(err, fmt.Errorf("error installing backend %s", backend)) - } - default: - err := gallery.InstallBackendFromGallery(galleries, systemState, backend, backendPath, downloadStatus, true) - if err != nil { - errs = errors.Join(err, fmt.Errorf("error installing backend %s", backend)) - } + log.Info().Str("backend", backend).Str("name", name).Msg("Installing backend from OCI image") + if err := gallery.InstallBackend(backendPath, &gallery.GalleryBackend{ + Metadata: gallery.Metadata{ + Name: name, + }, + Alias: alias, + URI: backend, + }, downloadStatus); err != nil { + return fmt.Errorf("error installing backend %s: %w", backend, err) + } + default: + if name != "" || alias != "" { + return fmt.Errorf("specifying a name or alias is not supported for this backend") + } + err := gallery.InstallBackendFromGallery(galleries, systemState, backend, backendPath, downloadStatus, true) + if err != nil { + return fmt.Errorf("error installing backend %s: %w", backend, err) } } - return errs + + return nil } diff --git a/pkg/downloader/uri.go b/pkg/downloader/uri.go index 99c28e4260d9..8d9b1d936d96 100644 --- a/pkg/downloader/uri.go +++ b/pkg/downloader/uri.go @@ -98,19 +98,19 @@ func (uri URI) DownloadWithAuthorizationAndCallback(basePath string, authorizati } func (u URI) FilenameFromUrl() (string, error) { - f, err := filenameFromUrl(string(u)) - if err != nil || f == "" { - f = utils.MD5(string(u)) - if strings.HasSuffix(string(u), ".yaml") || strings.HasSuffix(string(u), ".yml") { - f = f + ".yaml" - } - err = nil + if f := filenameFromUrl(string(u)); f != "" { + return f, nil + } + + f := utils.MD5(string(u)) + if strings.HasSuffix(string(u), ".yaml") || strings.HasSuffix(string(u), ".yml") { + f = f + ".yaml" } - return f, err + return f, nil } -func filenameFromUrl(urlstr string) (string, error) { +func filenameFromUrl(urlstr string) string { // strip anything after @ if strings.Contains(urlstr, "@") { urlstr = strings.Split(urlstr, "@")[0] @@ -118,13 +118,13 @@ func filenameFromUrl(urlstr string) (string, error) { u, err := url.Parse(urlstr) if err != nil { - return "", fmt.Errorf("error due to parsing url: %w", err) + return "" } x, err := url.QueryUnescape(u.EscapedPath()) if err != nil { - return "", fmt.Errorf("error due to escaping: %w", err) + return "" } - return filepath.Base(x), nil + return filepath.Base(x) } func (u URI) LooksLikeURL() bool { @@ -158,6 +158,10 @@ func (s URI) LooksLikeOCI() bool { strings.HasPrefix(string(s), "docker.io") } +func (s URI) LooksLikeOCIFile() bool { + return strings.HasPrefix(string(s), OCIFilePrefix) +} + func (s URI) ResolveURL() string { switch { case strings.HasPrefix(string(s), GithubURI2):