117 lines
2.4 KiB
Go
117 lines
2.4 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/ollama/ollama/x/build"
|
|
"github.com/ollama/ollama/x/client/ollama/apitype"
|
|
"github.com/ollama/ollama/x/oweb"
|
|
"github.com/ollama/ollama/x/registry"
|
|
regtype "github.com/ollama/ollama/x/registry"
|
|
)
|
|
|
|
// Common API Errors
|
|
var (
|
|
errUnqualifiedRef = oweb.Invalid("invalid", "name", "must be fully qualified")
|
|
errRefNotFound = oweb.Invalid("not_found", "name", "no such model")
|
|
)
|
|
|
|
type Server struct {
|
|
Build *build.Server
|
|
}
|
|
|
|
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
oweb.Serve(s.serveHTTP, w, r)
|
|
}
|
|
|
|
func (s *Server) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
|
switch r.URL.Path {
|
|
case "/v1/push":
|
|
return s.handlePush(w, r)
|
|
default:
|
|
return oweb.ErrNotFound
|
|
}
|
|
}
|
|
|
|
func want(r *http.Request, method, path string) bool {
|
|
return r.Method == method && r.URL.Path == path
|
|
}
|
|
|
|
func (s *Server) handlePush(_ http.ResponseWriter, r *http.Request) error {
|
|
if r.Method != "POST" {
|
|
return oweb.ErrMethodNotAllowed
|
|
}
|
|
|
|
params, err := oweb.DecodeJSON[apitype.PushRequest](r.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if params.Name == "" {
|
|
return oweb.Missing("name")
|
|
}
|
|
|
|
const registryURLTODO = "http://localhost:8888"
|
|
|
|
man, err := s.Build.ManifestData(params.Name)
|
|
if err != nil {
|
|
if errors.Is(err, build.ErrNotFound) {
|
|
return errRefNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
c := registry.Client{BaseURL: registryURLTODO}
|
|
requirements, err := c.Push(r.Context(), params.Name, man, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var uploads []regtype.CompletePart
|
|
for _, rq := range requirements {
|
|
l, err := s.Build.LayerFile(rq.Digest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = func() error {
|
|
f, err := os.Open(l)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
etag, err := registry.PushLayer(r.Context(), rq.URL, rq.Offset, rq.Size, f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
uploads = append(uploads, regtype.CompletePart{
|
|
URL: rq.URL,
|
|
ETag: etag,
|
|
})
|
|
return nil
|
|
}()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// commit the manifest to the registry
|
|
requirements, err = c.Push(r.Context(), params.Name, man, ®istry.PushParams{
|
|
CompleteParts: uploads,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, r := range requirements {
|
|
err = errors.Join(err, fmt.Errorf("push failed for %q", r.Digest))
|
|
}
|
|
return err
|
|
|
|
}
|
|
|
|
func (s *Server) handlePull(w http.ResponseWriter, r *http.Request) error {
|
|
return oweb.ErrNotFound
|
|
}
|