As we reevaluate how to best support and maintain Staging Ref in the future, we encourage development teams using this environment to highlight their use cases in the following issue: https://gitlab.com/gitlab-com/gl-infra/software-delivery/framework/software-delivery-framework-issue-tracker/-/issues/36.

Skip to content
Snippets Groups Projects
Commit 66cb396d authored by Kyle Edwards's avatar Kyle Edwards Committed by Joseph Snyder
Browse files

git-lfs-transfer: Add support for unlock

parent 4f2a5d78
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -263,7 +263,17 @@ type gitlabLock struct {
}
func (l *gitlabLock) Unlock() error {
return newErrUnsupported("unlock")
lock, err := l.gitlabLockBackend.client.Unlock(l.id, l.gitlabLockBackend.args["force"] == "true", l.gitlabLockBackend.args["refname"])
if err != nil {
return err
}
l.id = lock.ID
l.path = lock.Path
l.timestamp = lock.LockedAt
if lock.Owner != nil {
l.owner = lock.Owner.Name
}
return nil
}
func (l *gitlabLock) AsArguments() []string {
Loading
Loading
Loading
Loading
@@ -19,6 +19,7 @@ import (
var (
capabilities = []string{
"version=1",
"locking",
}
)
Loading
Loading
Loading
Loading
@@ -109,6 +109,7 @@ func readCapabilities(t *testing.T, pl *pktline.Pktline) {
}
require.Equal(t, []string{
"version=1",
"locking",
}, caps)
}
Loading
Loading
@@ -814,12 +815,40 @@ func TestLfsTransferUnlock(t *testing.T) {
wg := setupWaitGroupForExecute(t, cmd)
negotiateVersion(t, pl)
writeCommand(t, pl, "unlock lock1")
writeCommandArgs(t, pl, "unlock lock1", []string{"refname=refs/heads/main"})
status, args := readStatusArgs(t, pl)
require.Equal(t, "status 200", status)
require.Equal(t, []string{
"id=lock1",
"path=/large/file/1",
"locked-at=2023-10-03T13:56:20Z",
"ownername=johndoe",
}, args)
writeCommandArgs(t, pl, "unlock lock2", []string{"force=true"})
status, args = readStatusArgs(t, pl)
require.Equal(t, "status 200", status)
require.Equal(t, []string{
"id=lock2",
"path=/large/file/2",
"locked-at=1955-11-12T22:04:00Z",
"ownername=marty",
}, args)
writeCommand(t, pl, "unlock lock3")
status, args, data := readStatusArgsAndTextData(t, pl)
require.Equal(t, "status 405", status)
require.Equal(t, "status 403", status)
require.Empty(t, args)
require.Equal(t, []string{
"error: unlock is not yet supported by git-lfs-transfer. See https://gitlab.com/groups/gitlab-org/-/epics/11872 to track progress.",
"error: forbidden",
}, data)
writeCommand(t, pl, "unlock lock4")
status, args, data = readStatusArgsAndTextData(t, pl)
require.Equal(t, "status 404", status)
require.Empty(t, args)
require.Equal(t, []string{
"error: not found",
}, data)
quit(t, pl)
Loading
Loading
@@ -1347,6 +1376,93 @@ func setup(t *testing.T, keyID string, repo string, op string) (string, *Command
}
},
},
{
Path: "/group/repo/info/lfs/locks/lock1/unlock",
Handler: func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
var body map[string]interface{}
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
require.Equal(t, map[string]interface{}{
"ref": map[string]interface{}{
"name": "refs/heads/main",
},
"force": false,
}, body)
lock := map[string]interface{}{
"lock": map[string]interface{}{
"id": "lock1",
"path": "/large/file/1",
"locked_at": time.Date(2023, 10, 3, 13, 56, 20, 0, time.UTC).Format(time.RFC3339),
"owner": map[string]interface{}{
"name": "johndoe",
},
},
}
writer := json.NewEncoder(w)
writer.Encode(lock)
},
},
{
Path: "/group/repo/info/lfs/locks/lock2/unlock",
Handler: func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
var body map[string]interface{}
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
require.Equal(t, map[string]interface{}{
"force": true,
}, body)
lock := map[string]interface{}{
"lock": map[string]interface{}{
"id": "lock2",
"path": "/large/file/2",
"locked_at": time.Date(1955, 11, 12, 22, 4, 0, 0, time.UTC).Format(time.RFC3339),
"owner": map[string]interface{}{
"name": "marty",
},
},
}
writer := json.NewEncoder(w)
writer.Encode(lock)
},
},
{
Path: "/group/repo/info/lfs/locks/lock3/unlock",
Handler: func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
var body map[string]interface{}
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
require.Equal(t, map[string]interface{}{
"force": false,
}, body)
lock := map[string]interface{}{
"message": "forbidden",
}
w.WriteHeader(http.StatusForbidden)
writer := json.NewEncoder(w)
writer.Encode(lock)
},
},
{
Path: "/group/repo/info/lfs/locks/lock4/unlock",
Handler: func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
var body map[string]interface{}
require.NoError(t, json.NewDecoder(r.Body).Decode(&body))
require.Equal(t, map[string]interface{}{
"force": false,
}, body)
lock := map[string]interface{}{
"message": "not found",
}
w.WriteHeader(http.StatusNotFound)
writer := json.NewEncoder(w)
writer.Encode(lock)
},
},
}
url = testserver.StartHttpServer(t, requests)
Loading
Loading
Loading
Loading
@@ -110,6 +110,15 @@ type lockResponse struct {
Lock *Lock `json:"lock"`
}
type unlockRequest struct {
Force bool `json:"force"`
Ref *batchRef `json:"ref,omitempty"`
}
type unlockResponse struct {
Lock *Lock `json:"lock"`
}
type listLocksVerifyRequest struct {
Cursor string `json:"cursor,omitempty"`
Limit int `json:"limit"`
Loading
Loading
@@ -309,6 +318,56 @@ func (c *Client) Lock(path, refname string) (*Lock, error) {
}
}
func (c *Client) Unlock(id string, force bool, refname string) (*Lock, error) {
var ref *batchRef
if refname != "" {
ref = &batchRef{
Name: refname,
}
}
body := &unlockRequest{
Force: force,
Ref: ref,
}
jsonData, err := json.Marshal(body)
if err != nil {
return nil, err
}
jsonReader := bytes.NewReader(jsonData)
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/locks/%s/unlock", c.href, id), jsonReader)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/vnd.git-lfs+json")
req.Header.Set("Authorization", c.auth)
client := http.Client{}
res, err := client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
switch {
case res.StatusCode >= 200 && res.StatusCode <= 299:
response := &unlockResponse{}
if err := gitlabnet.ParseJSON(res, response); err != nil {
return nil, err
}
return response.Lock, nil
case res.StatusCode == http.StatusForbidden:
return nil, transfer.ErrForbidden
case res.StatusCode == http.StatusNotFound:
return nil, transfer.ErrNotFound
default:
return nil, fmt.Errorf("internal error")
}
}
func (c *Client) ListLocksVerify(path, id, cursor string, limit int, ref string) (*ListLocksVerifyResponse, error) {
url, err := url.Parse(c.href)
if err != nil {
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment