Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
ollama
Commits
637f8bc6
Unverified
Commit
637f8bc6
authored
Sep 20, 2023
by
Michael Yang
Committed by
GitHub
Sep 20, 2023
Browse files
Merge pull request #536 from jmorganca/mxyng/redirect-uploads
explicitly follow upload redirects
parents
e35565c5
499e9007
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
49 additions
and
19 deletions
+49
-19
server/images.go
server/images.go
+2
-2
server/upload.go
server/upload.go
+47
-17
No files found.
server/images.go
View file @
637f8bc6
...
@@ -1154,7 +1154,7 @@ func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn fu
...
@@ -1154,7 +1154,7 @@ func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn fu
Total
:
layer
.
Size
,
Total
:
layer
.
Size
,
})
})
location
,
err
:=
startUpload
(
ctx
,
mp
,
layer
,
regOpts
)
location
,
chunkSize
,
err
:=
startUpload
(
ctx
,
mp
,
layer
,
regOpts
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"couldn't start upload: %v"
,
err
)
log
.
Printf
(
"couldn't start upload: %v"
,
err
)
return
err
return
err
...
@@ -1171,7 +1171,7 @@ func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn fu
...
@@ -1171,7 +1171,7 @@ func PushModel(ctx context.Context, name string, regOpts *RegistryOptions, fn fu
continue
continue
}
}
if
err
:=
uploadBlob
Chunked
(
ctx
,
location
,
layer
,
regOpts
,
fn
);
err
!=
nil
{
if
err
:=
uploadBlob
(
ctx
,
location
,
layer
,
chunkSize
,
regOpts
,
fn
);
err
!=
nil
{
log
.
Printf
(
"error uploading blob: %v"
,
err
)
log
.
Printf
(
"error uploading blob: %v"
,
err
)
return
err
return
err
}
}
...
...
server/upload.go
View file @
637f8bc6
...
@@ -14,7 +14,12 @@ import (
...
@@ -14,7 +14,12 @@ import (
"github.com/jmorganca/ollama/api"
"github.com/jmorganca/ollama/api"
)
)
func
startUpload
(
ctx
context
.
Context
,
mp
ModelPath
,
layer
*
Layer
,
regOpts
*
RegistryOptions
)
(
*
url
.
URL
,
error
)
{
const
(
redirectChunkSize
=
1024
*
1024
*
1024
regularChunkSize
=
95
*
1024
*
1024
)
func
startUpload
(
ctx
context
.
Context
,
mp
ModelPath
,
layer
*
Layer
,
regOpts
*
RegistryOptions
)
(
*
url
.
URL
,
int64
,
error
)
{
requestURL
:=
mp
.
BaseURL
()
requestURL
:=
mp
.
BaseURL
()
requestURL
=
requestURL
.
JoinPath
(
"v2"
,
mp
.
GetNamespaceRepository
(),
"blobs/uploads/"
)
requestURL
=
requestURL
.
JoinPath
(
"v2"
,
mp
.
GetNamespaceRepository
(),
"blobs/uploads/"
)
if
layer
.
From
!=
""
{
if
layer
.
From
!=
""
{
...
@@ -27,20 +32,26 @@ func startUpload(ctx context.Context, mp ModelPath, layer *Layer, regOpts *Regis
...
@@ -27,20 +32,26 @@ func startUpload(ctx context.Context, mp ModelPath, layer *Layer, regOpts *Regis
resp
,
err
:=
makeRequestWithRetry
(
ctx
,
"POST"
,
requestURL
,
nil
,
nil
,
regOpts
)
resp
,
err
:=
makeRequestWithRetry
(
ctx
,
"POST"
,
requestURL
,
nil
,
nil
,
regOpts
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"couldn't start upload: %v"
,
err
)
log
.
Printf
(
"couldn't start upload: %v"
,
err
)
return
nil
,
err
return
nil
,
0
,
err
}
}
defer
resp
.
Body
.
Close
()
defer
resp
.
Body
.
Close
()
// Extract UUID location from header
location
:=
resp
.
Header
.
Get
(
"Docker-Upload-Location"
)
location
:=
resp
.
Header
.
Get
(
"Location"
)
chunkSize
:=
redirectChunkSize
if
location
==
""
{
if
location
==
""
{
return
nil
,
fmt
.
Errorf
(
"location header is missing in response"
)
location
=
resp
.
Header
.
Get
(
"Location"
)
chunkSize
=
regularChunkSize
}
locationURL
,
err
:=
url
.
Parse
(
location
)
if
err
!=
nil
{
return
nil
,
0
,
err
}
}
return
url
.
Parse
(
location
)
return
locationURL
,
int64
(
chunkSize
),
nil
}
}
func
uploadBlob
Chunked
(
ctx
context
.
Context
,
requestURL
*
url
.
URL
,
layer
*
Layer
,
regOpts
*
RegistryOptions
,
fn
func
(
api
.
ProgressResponse
))
error
{
func
uploadBlob
(
ctx
context
.
Context
,
requestURL
*
url
.
URL
,
layer
*
Layer
,
chunkSize
int64
,
regOpts
*
RegistryOptions
,
fn
func
(
api
.
ProgressResponse
))
error
{
// TODO allow resumability
// TODO allow resumability
// TODO allow canceling uploads via DELETE
// TODO allow canceling uploads via DELETE
...
@@ -55,8 +66,6 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
...
@@ -55,8 +66,6 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
}
}
defer
f
.
Close
()
defer
f
.
Close
()
// 95MB chunk size
chunkSize
:=
95
*
1024
*
1024
pw
:=
ProgressWriter
{
pw
:=
ProgressWriter
{
status
:
fmt
.
Sprintf
(
"uploading %s"
,
layer
.
Digest
),
status
:
fmt
.
Sprintf
(
"uploading %s"
,
layer
.
Digest
),
digest
:
layer
.
Digest
,
digest
:
layer
.
Digest
,
...
@@ -70,10 +79,10 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
...
@@ -70,10 +79,10 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
chunk
=
int64
(
chunkSize
)
chunk
=
int64
(
chunkSize
)
}
}
resp
,
err
:=
uploadBlobChunk
(
ctx
,
requestURL
,
f
,
offset
,
chunk
,
regOpts
,
&
pw
)
resp
,
err
:=
uploadBlobChunk
(
ctx
,
http
.
MethodPatch
,
requestURL
,
f
,
offset
,
chunk
,
regOpts
,
&
pw
)
if
err
!=
nil
{
if
err
!=
nil
{
fn
(
api
.
ProgressResponse
{
fn
(
api
.
ProgressResponse
{
Status
:
fmt
.
Sprintf
(
"error uploading
limit
: %v"
,
err
),
Status
:
fmt
.
Sprintf
(
"error uploading
chunk
: %v"
,
err
),
Digest
:
layer
.
Digest
,
Digest
:
layer
.
Digest
,
Total
:
layer
.
Size
,
Total
:
layer
.
Size
,
Completed
:
int
(
offset
),
Completed
:
int
(
offset
),
...
@@ -83,12 +92,15 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
...
@@ -83,12 +92,15 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
}
}
offset
+=
chunk
offset
+=
chunk
location
,
err
:=
resp
.
Location
()
location
:=
resp
.
Header
.
Get
(
"Docker-Upload-Location"
)
if
location
==
""
{
location
=
resp
.
Header
.
Get
(
"Location"
)
}
requestURL
,
err
=
url
.
Parse
(
location
)
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
err
}
}
requestURL
=
location
}
}
values
:=
requestURL
.
Query
()
values
:=
requestURL
.
Query
()
...
@@ -114,22 +126,40 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
...
@@ -114,22 +126,40 @@ func uploadBlobChunked(ctx context.Context, requestURL *url.URL, layer *Layer, r
return
nil
return
nil
}
}
func
uploadBlobChunk
(
ctx
context
.
Context
,
requestURL
*
url
.
URL
,
r
io
.
ReaderAt
,
offset
,
limit
int64
,
opts
*
RegistryOptions
,
pw
*
ProgressWriter
)
(
*
http
.
Response
,
error
)
{
func
uploadBlobChunk
(
ctx
context
.
Context
,
method
string
,
requestURL
*
url
.
URL
,
r
io
.
ReaderAt
,
offset
,
limit
int64
,
opts
*
RegistryOptions
,
pw
*
ProgressWriter
)
(
*
http
.
Response
,
error
)
{
sectionReader
:=
io
.
NewSectionReader
(
r
,
int64
(
offset
),
limit
)
sectionReader
:=
io
.
NewSectionReader
(
r
,
int64
(
offset
),
limit
)
headers
:=
make
(
http
.
Header
)
headers
:=
make
(
http
.
Header
)
headers
.
Set
(
"Content-Type"
,
"application/octet-stream"
)
headers
.
Set
(
"Content-Type"
,
"application/octet-stream"
)
headers
.
Set
(
"Content-Length"
,
strconv
.
Itoa
(
int
(
limit
)))
headers
.
Set
(
"Content-Length"
,
strconv
.
Itoa
(
int
(
limit
)))
headers
.
Set
(
"Content-Range"
,
fmt
.
Sprintf
(
"%d-%d"
,
offset
,
offset
+
sectionReader
.
Size
()
-
1
))
headers
.
Set
(
"X-Redirect-Uploads"
,
"1"
)
if
method
==
http
.
MethodPatch
{
headers
.
Set
(
"Content-Range"
,
fmt
.
Sprintf
(
"%d-%d"
,
offset
,
offset
+
sectionReader
.
Size
()
-
1
))
}
for
try
:=
0
;
try
<
MaxRetries
;
try
++
{
for
try
:=
0
;
try
<
MaxRetries
;
try
++
{
resp
,
err
:=
makeRequest
(
ctx
,
"PATCH"
,
requestURL
,
headers
,
io
.
TeeReader
(
sectionReader
,
pw
),
opts
)
resp
,
err
:=
makeRequest
(
ctx
,
method
,
requestURL
,
headers
,
io
.
TeeReader
(
sectionReader
,
pw
),
opts
)
if
err
!=
nil
&&
!
errors
.
Is
(
err
,
io
.
EOF
)
{
if
err
!=
nil
&&
!
errors
.
Is
(
err
,
io
.
EOF
)
{
return
nil
,
err
return
nil
,
err
}
}
defer
resp
.
Body
.
Close
()
defer
resp
.
Body
.
Close
()
switch
{
switch
{
case
resp
.
StatusCode
==
http
.
StatusTemporaryRedirect
:
location
,
err
:=
resp
.
Location
()
if
err
!=
nil
{
return
nil
,
err
}
pw
.
completed
=
int
(
offset
)
if
_
,
err
:=
uploadBlobChunk
(
ctx
,
http
.
MethodPut
,
location
,
r
,
offset
,
limit
,
nil
,
pw
);
err
!=
nil
{
// retry
log
.
Printf
(
"retrying redirected upload: %v"
,
err
)
continue
}
return
resp
,
nil
case
resp
.
StatusCode
==
http
.
StatusUnauthorized
:
case
resp
.
StatusCode
==
http
.
StatusUnauthorized
:
auth
:=
resp
.
Header
.
Get
(
"www-authenticate"
)
auth
:=
resp
.
Header
.
Get
(
"www-authenticate"
)
authRedir
:=
ParseAuthRedirectString
(
auth
)
authRedir
:=
ParseAuthRedirectString
(
auth
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment