@ -212,12 +212,16 @@ type Registry struct {
Mask string
}
func ( r * Registry ) complet eName( name string ) names . Name {
func ( r * Registry ) pars eName( name string ) ( names . Name , error ) {
mask := defaultMask
if r . Mask != "" {
mask = names . Parse ( r . Mask )
}
return names . Merge ( names . Parse ( name ) , mask )
n := names . Merge ( names . Parse ( name ) , mask )
if ! n . IsFullyQualified ( ) {
return names . Name { } , fmt . Errorf ( "%w: %q" , ErrNameInvalid , name )
}
return n , nil
}
// DefaultRegistry returns a new Registry configured from the environment. The
@ -306,7 +310,7 @@ func (r *Registry) Push(ctx context.Context, c *blob.DiskCache, name string, p *
t := traceFromContext ( ctx )
scheme , n , _ , err := parseName ( name , r . Mask )
scheme , n , _ , err := r . parseNameExtended ( name )
if err != nil {
// This should never happen since ResolveLocal should have
// already validated the name.
@ -400,7 +404,7 @@ func canRetry(err error) bool {
// typically slower than splitting the model up across layers, and is mostly
// utilized for layers of type equal to "application/vnd.ollama.image".
func ( r * Registry ) Pull ( ctx context . Context , c * blob . DiskCache , name string ) error {
scheme , n , _ , err := parseName ( name , r . Mask )
scheme , n , _ , err := r . parseNameExtended ( name )
if err != nil {
return err
}
@ -551,9 +555,9 @@ func (r *Registry) Pull(ctx context.Context, c *blob.DiskCache, name string) err
// Unlink is like [blob.DiskCache.Unlink], but makes name fully qualified
// before attempting to unlink the model.
func ( r * Registry ) Unlink ( c * blob . DiskCache , name string ) ( ok bool , _ error ) {
n := r . complet eName( name )
if ! n . IsFullyQualified ( ) {
return false , fmt . Errorf ( "%w: %q" , ErrNameInvalid , name )
n , err := r . pars eName( name )
if err != nil {
return false , err
}
return c . Unlink ( n . String ( ) )
}
@ -626,10 +630,9 @@ type Layer struct {
Size int64 ` json:"size" `
}
// ResolveLocal resolves a name to a Manifest in the local cache. The name is
// parsed using [names.Split] but the scheme is ignored.
// ResolveLocal resolves a name to a Manifest in the local cache.
func ( r * Registry ) ResolveLocal ( c * blob . DiskCache , name string ) ( * Manifest , error ) {
_ , n , d , err := parseName ( name , r . Mask )
_ , n , d , err := r . parseNameExtended ( name )
if err != nil {
return nil , err
}
@ -655,7 +658,7 @@ func (r *Registry) ResolveLocal(c *blob.DiskCache, name string) (*Manifest, erro
// Resolve resolves a name to a Manifest in the remote registry.
func ( r * Registry ) Resolve ( ctx context . Context , name string ) ( * Manifest , error ) {
scheme , n , d , err := parseName ( name , r . Mask )
scheme , n , d , err := r . parseNameExtended ( name )
if err != nil {
return nil , err
}
@ -859,7 +862,7 @@ var supportedSchemes = []string{
var supportedSchemesMessage = fmt . Sprintf ( "supported schemes are %v" , strings . Join ( supportedSchemes , ", " ) )
// parseName parses and validates an extended name, returning the scheme, name,
// parseNameExtended parses and validates an extended name, returning the scheme, name,
// and digest.
//
// If the scheme is empty, scheme will be "https". If an unsupported scheme is
@ -870,8 +873,8 @@ var supportedSchemesMessage = fmt.Sprintf("supported schemes are %v", strings.Jo
//
// If the name is not, once merged with the mask, fully qualified,
// [ErrNameInvalid] wrapped with a display friendly message is returned.
func parseName ( s string , mask string ) ( scheme string , _ names . Name , _ blob . Digest , _ error ) {
scheme , name , digest := names . Split ( s )
func ( r * Registry ) parseNameExtended ( s string ) ( scheme string , _ names . Name , _ blob . Digest , _ error ) {
scheme , name , digest := splitExtended ( s )
scheme = cmp . Or ( scheme , "https" )
if ! slices . Contains ( supportedSchemes , scheme ) {
err := withPublicMessagef ( ErrNameInvalid , "unsupported scheme: %q: %s" , scheme , supportedSchemesMessage )
@ -894,13 +897,33 @@ func parseName(s string, mask string) (scheme string, _ names.Name, _ blob.Diges
}
}
maskName := defaultMask
if mask != "" {
maskName = names . Parse ( mask )
}
n := names . Merge ( names . Parse ( name ) , maskName )
if ! n . IsFullyQualified ( ) {
return "" , names . Name { } , blob . Digest { } , fmt . Errorf ( "%w: %q" , ErrNameInvalid , s )
n , err := r . parseName ( name )
if err != nil {
return "" , names . Name { } , blob . Digest { } , err
}
return scheme , n , d , nil
}
// splitExtended splits an extended name string into its scheme, name, and digest
// parts.
//
// Examples:
//
// http://ollama.com/bmizerany/smol:latest@digest
// https://ollama.com/bmizerany/smol:latest
// ollama.com/bmizerany/smol:latest@digest // returns "https" scheme.
// model@digest
// @digest
func splitExtended ( s string ) ( scheme , name , digest string ) {
i := strings . Index ( s , "://" )
if i >= 0 {
scheme = s [ : i ]
s = s [ i + 3 : ]
}
i = strings . LastIndex ( s , "@" )
if i >= 0 {
digest = s [ i + 1 : ]
s = s [ : i ]
}
return scheme , s , digest
}