reorder some source files
This commit is contained in:
@@ -21,3 +21,4 @@ DIST
|
|||||||
*.rpm
|
*.rpm
|
||||||
mstored.service
|
mstored.service
|
||||||
variant.go
|
variant.go
|
||||||
|
initrc/mstored
|
||||||
|
|||||||
+21
-11
@@ -7,10 +7,10 @@ bin_PROGRAMS = mstorectl
|
|||||||
sbin_PROGRAMS = mstored
|
sbin_PROGRAMS = mstored
|
||||||
|
|
||||||
mstorectl_SOURCES = \
|
mstorectl_SOURCES = \
|
||||||
cmd/mstorectl/main.go \
|
cmd/mstorectl/main.go
|
||||||
cmd/mstorectl/util.go
|
|
||||||
|
|
||||||
EXTRA_mstorectl_SOURCES = \
|
EXTRA_mstorectl_SOURCES = \
|
||||||
|
cmd/mstorectl/util/util.go \
|
||||||
cmd/mstorectl/accountcmd/acccmd.go \
|
cmd/mstorectl/accountcmd/acccmd.go \
|
||||||
cmd/mstorectl/accountcmd/createacc.go \
|
cmd/mstorectl/accountcmd/createacc.go \
|
||||||
cmd/mstorectl/accountcmd/creategrant.go \
|
cmd/mstorectl/accountcmd/creategrant.go \
|
||||||
@@ -34,8 +34,15 @@ EXTRA_mstorectl_SOURCES = \
|
|||||||
cmd/mstorectl/filecmd/listcolls.go \
|
cmd/mstorectl/filecmd/listcolls.go \
|
||||||
cmd/mstorectl/filecmd/listfiles.go \
|
cmd/mstorectl/filecmd/listfiles.go \
|
||||||
cmd/mstorectl/filecmd/putfile.go \
|
cmd/mstorectl/filecmd/putfile.go \
|
||||||
|
cmd/mstorectl/imagecmd/catimages.go \
|
||||||
|
cmd/mstorectl/imagecmd/delimage.go \
|
||||||
cmd/mstorectl/imagecmd/imagecmd.go \
|
cmd/mstorectl/imagecmd/imagecmd.go \
|
||||||
cmd/mstorectl/imagecmd/printresp.go
|
cmd/mstorectl/imagecmd/imageman.go \
|
||||||
|
cmd/mstorectl/imagecmd/imagetags.go \
|
||||||
|
cmd/mstorectl/imagecmd/printresp.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrimageconf.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrpullimage.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrpushimage.go
|
||||||
|
|
||||||
mstored_SOURCES = cmd/mstored/main.go
|
mstored_SOURCES = cmd/mstored/main.go
|
||||||
|
|
||||||
@@ -149,12 +156,12 @@ EXTRA_mstored_SOURCES += \
|
|||||||
pkg/auxutar/utar.go \
|
pkg/auxutar/utar.go \
|
||||||
pkg/auxuuid/uuid.go \
|
pkg/auxuuid/uuid.go \
|
||||||
pkg/auxx509/x509cert.go \
|
pkg/auxx509/x509cert.go \
|
||||||
pkg/client/client.go \
|
pkg/gcrcli/client.go \
|
||||||
pkg/client/imageaux.go \
|
pkg/gcrcli/imageaux.go \
|
||||||
pkg/client/imagedelete.go \
|
pkg/gcrcli/imagedelete.go \
|
||||||
pkg/client/imageinfo.go \
|
pkg/gcrcli/imageinfo.go \
|
||||||
pkg/client/imagepull.go \
|
pkg/gcrcli/imagepull.go \
|
||||||
pkg/client/imagepush.go \
|
pkg/gcrcli/imagepush.go \
|
||||||
pkg/descr/account.go \
|
pkg/descr/account.go \
|
||||||
pkg/descr/blob.go \
|
pkg/descr/blob.go \
|
||||||
pkg/descr/file.go \
|
pkg/descr/file.go \
|
||||||
@@ -179,16 +186,19 @@ EXTRA_mstored_SOURCES += \
|
|||||||
pkg/repocli/delblob.go \
|
pkg/repocli/delblob.go \
|
||||||
pkg/repocli/delman.go \
|
pkg/repocli/delman.go \
|
||||||
pkg/repocli/getblob.go \
|
pkg/repocli/getblob.go \
|
||||||
|
pkg/repocli/getcatalog.go \
|
||||||
pkg/repocli/getman.go \
|
pkg/repocli/getman.go \
|
||||||
|
pkg/repocli/getreferers.go \
|
||||||
|
pkg/repocli/gettags.go \
|
||||||
pkg/repocli/gettoken.go \
|
pkg/repocli/gettoken.go \
|
||||||
pkg/repocli/getupload.go \
|
pkg/repocli/getupload.go \
|
||||||
pkg/repocli/imager.go \
|
pkg/repocli/imager.go \
|
||||||
pkg/repocli/manexist.go \
|
pkg/repocli/manexist.go \
|
||||||
pkg/repocli/patchupload.go \
|
pkg/repocli/patchupload.go \
|
||||||
pkg/repocli/pullimage.go \
|
pkg/repocli/pullimage.go \
|
||||||
|
pkg/repocli/pushimage.go \
|
||||||
pkg/repocli/putman.go \
|
pkg/repocli/putman.go \
|
||||||
pkg/repocli/putupload.go \
|
pkg/repocli/putupload.go \
|
||||||
pkg/repocli/reference.go \
|
|
||||||
pkg/repocli/referer.go \
|
pkg/repocli/referer.go \
|
||||||
pkg/repocli/uuid.go \
|
pkg/repocli/uuid.go \
|
||||||
pkg/terms/terms.go
|
pkg/terms/terms.go
|
||||||
@@ -353,7 +363,7 @@ endif
|
|||||||
clean-local:
|
clean-local:
|
||||||
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
||||||
rm -rf autom4te.cache
|
rm -rf autom4te.cache
|
||||||
rm -f cmd/mstored/istored
|
rm -f cmd/mstored/mstored
|
||||||
rm -f cmd/mstorectl/mstorectl
|
rm -f cmd/mstorectl/mstorectl
|
||||||
# rm -rf tmp/
|
# rm -rf tmp/
|
||||||
|
|
||||||
|
|||||||
+22
-14
@@ -353,10 +353,10 @@ AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
|||||||
SUBDIRS = mans
|
SUBDIRS = mans
|
||||||
GOFLAGS = -v
|
GOFLAGS = -v
|
||||||
mstorectl_SOURCES = \
|
mstorectl_SOURCES = \
|
||||||
cmd/mstorectl/main.go \
|
cmd/mstorectl/main.go
|
||||||
cmd/mstorectl/util.go
|
|
||||||
|
|
||||||
EXTRA_mstorectl_SOURCES = \
|
EXTRA_mstorectl_SOURCES = \
|
||||||
|
cmd/mstorectl/util/util.go \
|
||||||
cmd/mstorectl/accountcmd/acccmd.go \
|
cmd/mstorectl/accountcmd/acccmd.go \
|
||||||
cmd/mstorectl/accountcmd/createacc.go \
|
cmd/mstorectl/accountcmd/createacc.go \
|
||||||
cmd/mstorectl/accountcmd/creategrant.go \
|
cmd/mstorectl/accountcmd/creategrant.go \
|
||||||
@@ -380,8 +380,15 @@ EXTRA_mstorectl_SOURCES = \
|
|||||||
cmd/mstorectl/filecmd/listcolls.go \
|
cmd/mstorectl/filecmd/listcolls.go \
|
||||||
cmd/mstorectl/filecmd/listfiles.go \
|
cmd/mstorectl/filecmd/listfiles.go \
|
||||||
cmd/mstorectl/filecmd/putfile.go \
|
cmd/mstorectl/filecmd/putfile.go \
|
||||||
|
cmd/mstorectl/imagecmd/catimages.go \
|
||||||
|
cmd/mstorectl/imagecmd/delimage.go \
|
||||||
cmd/mstorectl/imagecmd/imagecmd.go \
|
cmd/mstorectl/imagecmd/imagecmd.go \
|
||||||
cmd/mstorectl/imagecmd/printresp.go
|
cmd/mstorectl/imagecmd/imageman.go \
|
||||||
|
cmd/mstorectl/imagecmd/imagetags.go \
|
||||||
|
cmd/mstorectl/imagecmd/printresp.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrimageconf.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrpullimage.go \
|
||||||
|
cmd/mstorectl/imagecmd/gcrpushimage.go
|
||||||
|
|
||||||
mstored_SOURCES = cmd/mstored/main.go
|
mstored_SOURCES = cmd/mstored/main.go
|
||||||
EXTRA_mstored_SOURCES = cmd/mstored/starter/starter.go \
|
EXTRA_mstored_SOURCES = cmd/mstored/starter/starter.go \
|
||||||
@@ -431,10 +438,10 @@ EXTRA_mstored_SOURCES = cmd/mstored/starter/starter.go \
|
|||||||
pkg/auxtool/fileex.go pkg/auxtool/randstr.go \
|
pkg/auxtool/fileex.go pkg/auxtool/randstr.go \
|
||||||
pkg/auxtool/tmpfile.go pkg/auxtool/unixnow.go \
|
pkg/auxtool/tmpfile.go pkg/auxtool/unixnow.go \
|
||||||
pkg/auxutar/utar.go pkg/auxuuid/uuid.go \
|
pkg/auxutar/utar.go pkg/auxuuid/uuid.go \
|
||||||
pkg/auxx509/x509cert.go pkg/client/client.go \
|
pkg/auxx509/x509cert.go pkg/gcrcli/client.go \
|
||||||
pkg/client/imageaux.go pkg/client/imagedelete.go \
|
pkg/gcrcli/imageaux.go pkg/gcrcli/imagedelete.go \
|
||||||
pkg/client/imageinfo.go pkg/client/imagepull.go \
|
pkg/gcrcli/imageinfo.go pkg/gcrcli/imagepull.go \
|
||||||
pkg/client/imagepush.go pkg/descr/account.go pkg/descr/blob.go \
|
pkg/gcrcli/imagepush.go pkg/descr/account.go pkg/descr/blob.go \
|
||||||
pkg/descr/file.go pkg/descr/grant.go pkg/descr/manifest.go \
|
pkg/descr/file.go pkg/descr/grant.go pkg/descr/manifest.go \
|
||||||
pkg/descr/server.go pkg/digest/digest.go \
|
pkg/descr/server.go pkg/digest/digest.go \
|
||||||
pkg/filecli/authbas.go pkg/filecli/client.go \
|
pkg/filecli/authbas.go pkg/filecli/client.go \
|
||||||
@@ -445,13 +452,14 @@ EXTRA_mstored_SOURCES = cmd/mstored/starter/starter.go \
|
|||||||
pkg/filecli/referer.go pkg/repocli/blobexist.go \
|
pkg/filecli/referer.go pkg/repocli/blobexist.go \
|
||||||
pkg/repocli/client.go pkg/repocli/copywctx.go \
|
pkg/repocli/client.go pkg/repocli/copywctx.go \
|
||||||
pkg/repocli/delblob.go pkg/repocli/delman.go \
|
pkg/repocli/delblob.go pkg/repocli/delman.go \
|
||||||
pkg/repocli/getblob.go pkg/repocli/getman.go \
|
pkg/repocli/getblob.go pkg/repocli/getcatalog.go \
|
||||||
pkg/repocli/gettoken.go pkg/repocli/getupload.go \
|
pkg/repocli/getman.go pkg/repocli/getreferers.go \
|
||||||
pkg/repocli/imager.go pkg/repocli/manexist.go \
|
pkg/repocli/gettags.go pkg/repocli/gettoken.go \
|
||||||
pkg/repocli/patchupload.go pkg/repocli/pullimage.go \
|
pkg/repocli/getupload.go pkg/repocli/imager.go \
|
||||||
|
pkg/repocli/manexist.go pkg/repocli/patchupload.go \
|
||||||
|
pkg/repocli/pullimage.go pkg/repocli/pushimage.go \
|
||||||
pkg/repocli/putman.go pkg/repocli/putupload.go \
|
pkg/repocli/putman.go pkg/repocli/putupload.go \
|
||||||
pkg/repocli/reference.go pkg/repocli/referer.go \
|
pkg/repocli/referer.go pkg/repocli/uuid.go pkg/terms/terms.go
|
||||||
pkg/repocli/uuid.go pkg/terms/terms.go
|
|
||||||
CWD = $(shell pwd)
|
CWD = $(shell pwd)
|
||||||
EXTRA_DIST = vendor/* \
|
EXTRA_DIST = vendor/* \
|
||||||
\
|
\
|
||||||
@@ -1189,7 +1197,7 @@ install-data-local:
|
|||||||
clean-local:
|
clean-local:
|
||||||
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
||||||
rm -rf autom4te.cache
|
rm -rf autom4te.cache
|
||||||
rm -f cmd/mstored/istored
|
rm -f cmd/mstored/mstored
|
||||||
rm -f cmd/mstorectl/mstorectl
|
rm -f cmd/mstorectl/mstorectl
|
||||||
# rm -rf tmp/
|
# rm -rf tmp/
|
||||||
|
|
||||||
|
|||||||
@@ -1,376 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"mstore/app/handler"
|
|
||||||
"mstore/app/operator"
|
|
||||||
"mstore/app/router"
|
|
||||||
"mstore/app/server"
|
|
||||||
"mstore/pkg/auxhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAccountOperations(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
fmt.Printf("=== MakeServer ===\n")
|
|
||||||
srv, err := server.NewServer()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var srvport int64 = 10240 + rand.Int63n(1024)
|
|
||||||
srvdir := t.TempDir()
|
|
||||||
//srvaddr := fmt.Sprintf("127.0.0.1:%d", srvport)
|
|
||||||
|
|
||||||
{
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
useTmpdir := true
|
|
||||||
|
|
||||||
if useTmpdir {
|
|
||||||
srv.SetDatadir(srvdir)
|
|
||||||
srv.SetLogdir(srvdir)
|
|
||||||
srv.SetRundir(srvdir)
|
|
||||||
}
|
|
||||||
srv.SetPort(srvport)
|
|
||||||
|
|
||||||
err = srv.Build()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
username := "testuser"
|
|
||||||
password := "testpass"
|
|
||||||
{
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
reqPath := "/service/hello"
|
|
||||||
routePath := "/service/hello"
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
rout.Get(routePath, hand.SendHello)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("GET", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
request.Header.Add("Authorization", basic)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
var accountID string
|
|
||||||
var accountName = "testname1"
|
|
||||||
{
|
|
||||||
fmt.Printf("=== CreateAccount ===\n")
|
|
||||||
reqpath := `/v3/api/account/create`
|
|
||||||
routepath := `/v3/api/account/create`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.CreateAccountParams{
|
|
||||||
Username: accountName,
|
|
||||||
Password: "testpass",
|
|
||||||
}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
rout.Post(routepath, hand.CreateAccount)
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.CreateAccountResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
require.Equal(t, len(resp.Result.AccountID), 36)
|
|
||||||
accountID = resp.Result.AccountID
|
|
||||||
}
|
|
||||||
fmt.Printf("AccountID: %s\n", accountID)
|
|
||||||
{
|
|
||||||
fmt.Printf("=== UpdateAccount ===\n")
|
|
||||||
reqpath := `/v3/api/account/create`
|
|
||||||
routepath := `/v3/api/account/create`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.UpdateAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
NewPassword: "newpass",
|
|
||||||
}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
rout.Post(routepath, hand.UpdateAccount)
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.UpdateAccountResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
fmt.Printf("=== GetAccount ===\n")
|
|
||||||
reqpath := `/v3/api/account/get`
|
|
||||||
routepath := `/v3/api/account/get`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.GetAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.Post(routepath, hand.GetAccount)
|
|
||||||
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.GetAccountResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
require.Equal(t, resp.Result.Account.Username, accountName)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== ListAccounts ===\n")
|
|
||||||
reqpath := `/v3/api/accounts/list`
|
|
||||||
routepath := `/v3/api/accounts/list`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.ListAccountsParams{}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.Post(routepath, hand.ListAccounts)
|
|
||||||
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.ListAccountsResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
require.Equal(t, len(resp.Result.Accounts), 1)
|
|
||||||
require.Equal(t, resp.Result.Accounts[0].Username, accountName)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== DeleteAccount ===\n")
|
|
||||||
reqpath := `/v3/api/account/delete`
|
|
||||||
routepath := `/v3/api/account/delete`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.DeleteAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.Post(routepath, hand.DeleteAccount)
|
|
||||||
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.DeleteAccountResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== ListAccounts ===\n")
|
|
||||||
reqpath := `/v3/api/accounts/list`
|
|
||||||
routepath := `/v3/api/accounts/list`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
req := operator.ListAccountsParams{}
|
|
||||||
reqdata, err := json.Marshal(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reqsize := len(reqdata)
|
|
||||||
reqsrc := bytes.NewReader(reqdata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("POST", reqpath, reqsrc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(reqsize)
|
|
||||||
request.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.Post(routepath, hand.ListAccounts)
|
|
||||||
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
jsonBuffer := bytes.NewBuffer(nil)
|
|
||||||
err = json.Indent(jsonBuffer, bodyBytes, "", " ")
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("Formatted body: \n%s\n", jsonBuffer.String())
|
|
||||||
|
|
||||||
resp := handler.Response[operator.ListAccountsResult]{}
|
|
||||||
err = json.Unmarshal(bodyBytes, &resp)
|
|
||||||
require.False(t, resp.Error)
|
|
||||||
require.Equal(t, len(resp.Result.Accounts), 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"bytes"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"mstore/app/router"
|
|
||||||
"mstore/app/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func xxxTestFileOperations(t *testing.T) {
|
|
||||||
var err error
|
|
||||||
fmt.Printf("=== MakeServer ===\n")
|
|
||||||
srv, err := server.NewServer()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var srvport int64 = 10240 + rand.Int63n(1024)
|
|
||||||
srvdir := t.TempDir()
|
|
||||||
|
|
||||||
filename := `bare.bin`
|
|
||||||
|
|
||||||
{
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
useTmpdir := false
|
|
||||||
|
|
||||||
if useTmpdir {
|
|
||||||
srv.SetDatadir(srvdir)
|
|
||||||
srv.SetLogdir(srvdir)
|
|
||||||
srv.SetRundir(srvdir)
|
|
||||||
}
|
|
||||||
srv.SetPort(srvport)
|
|
||||||
|
|
||||||
err = srv.Build()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
reqPath := "/service/hello"
|
|
||||||
routePath := "/service/hello"
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
rout.Get(routePath, hand.SendHello)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("GET", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== PutFile ===\n")
|
|
||||||
reqPath := `/v3/api/file/` + filename
|
|
||||||
routePath := `/v3/api/file/{filepath}`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
rout.Put(routePath, hand.PutFile)
|
|
||||||
|
|
||||||
datasize := 64
|
|
||||||
filedata := make([]byte, datasize)
|
|
||||||
_, err = rand.Read(filedata)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
filedata = []byte(hex.EncodeToString(filedata))
|
|
||||||
datasize *= 2
|
|
||||||
|
|
||||||
source := bytes.NewReader(filedata)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("PUT", reqPath, source)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
request.ContentLength = int64(datasize)
|
|
||||||
request.Header.Set("Content-Type", "application/octet-stream")
|
|
||||||
request.Header.Set("Content-Size", strconv.FormatInt(int64(datasize), 10))
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== FileInfo ===\n")
|
|
||||||
|
|
||||||
reqPath := filepath.Join(`/v3/api/file`, filename)
|
|
||||||
routePath := `/v3/api/file/{filepath}`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
rout.Head(routePath, hand.FileInfo)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("HEAD", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
fmt.Printf("=== GetFile ===\n")
|
|
||||||
reqPath := filepath.Join(`/v3/api/file`, filename)
|
|
||||||
routePath := `/v3/api/file/{filepath}`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
rout.Get(routePath, hand.GetFile)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("GET", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
{
|
|
||||||
fmt.Printf("=== DeleteFile ===\n")
|
|
||||||
reqPath := filepath.Join(`/v3/api/file`, filename)
|
|
||||||
routePath := `/v3/api/file/{filepath}`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
rout.Delete(routePath, hand.DeleteFile)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("DELETE", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusOK, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
fmt.Printf("=== FileNotExists ===\n")
|
|
||||||
|
|
||||||
reqPath := filepath.Join(`/v3/api/file`, filename)
|
|
||||||
routePath := `/v3/api/file/{filepath}`
|
|
||||||
|
|
||||||
rout := router.NewRouter()
|
|
||||||
hand := srv.Handler()
|
|
||||||
require.NotNil(t, hand)
|
|
||||||
|
|
||||||
rout.Head(routePath, hand.FileInfo)
|
|
||||||
|
|
||||||
request, err := http.NewRequest("HEAD", reqPath, nil)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
rout.ServeHTTP(recorder, request)
|
|
||||||
require.Equal(t, http.StatusNotFound, recorder.Code)
|
|
||||||
|
|
||||||
fmt.Printf("Response code: %d\n", recorder.Code)
|
|
||||||
|
|
||||||
bodyReader := recorder.Body
|
|
||||||
bodyBytes, err := io.ReadAll(bodyReader)
|
|
||||||
|
|
||||||
fmt.Printf("Response body: %s\n", string(bodyBytes))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*
|
||||||
|
* This work is published and licensed under a Creative Commons
|
||||||
|
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
||||||
|
*
|
||||||
|
* Distribution of this work is permitted, but commercial use and
|
||||||
|
* modifications are strictly prohibited.
|
||||||
|
*/
|
||||||
|
package imagecmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"mstore/pkg/gcrcli"
|
||||||
|
|
||||||
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ImageConfig
|
||||||
|
type ImageConfigParams struct {
|
||||||
|
Imagepath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ImageConfigResult struct {
|
||||||
|
ImageConfig *ocispec.Image `json:"imageConfig"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ImageUtil) ImageConfig(cmd *cobra.Command, args []string) {
|
||||||
|
util.imageConfigParams.Imagepath = args[0]
|
||||||
|
res, err := util.imageConfig(&util.commonImageParams, &util.imageConfigParams)
|
||||||
|
printResponse(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ImageUtil) imageConfig(common *CommonImageParams, params *ImageConfigParams) (*ImageConfigResult, error) {
|
||||||
|
var err error
|
||||||
|
res := &ImageConfigResult{
|
||||||
|
ImageConfig: &ocispec.Image{},
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
cli := gcrcli.NewClient(common.SkipTLSVerify)
|
||||||
|
timeout := time.Duration(common.Timeout) * time.Second
|
||||||
|
|
||||||
|
params.Imagepath, err = packUserinfo(params.Imagepath, common.Username, common.Password)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||||
|
opres, err := cli.ImageConfig(ctx, params.Imagepath)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
res.ImageConfig = opres
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*
|
||||||
|
* This work is published and licensed under a Creative Commons
|
||||||
|
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
||||||
|
*
|
||||||
|
* Distribution of this work is permitted, but commercial use and
|
||||||
|
* modifications are strictly prohibited.
|
||||||
|
*/
|
||||||
|
package imagecmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"mstore/pkg/gcrcli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PullImage
|
||||||
|
type PullImageParams struct {
|
||||||
|
Imagepath string
|
||||||
|
Filepath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PullImageResult struct {
|
||||||
|
Filepath string `json:"filepath"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ImageUtil) PullImage(cmd *cobra.Command, args []string) {
|
||||||
|
util.pullImageParams.Imagepath = args[0]
|
||||||
|
util.pullImageParams.Filepath = args[1]
|
||||||
|
res, err := util.pullImage(&util.commonImageParams, &util.pullImageParams)
|
||||||
|
printResponse(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ImageUtil) pullImage(common *CommonImageParams, params *PullImageParams) (*PullImageResult, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
res := &PullImageResult{}
|
||||||
|
|
||||||
|
cli := gcrcli.NewClient(common.SkipTLSVerify)
|
||||||
|
timeout := time.Duration(common.Timeout) * time.Second
|
||||||
|
params.Imagepath, err = packUserinfo(params.Imagepath, common.Username, common.Password)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||||
|
err = cli.PullImage(ctx, params.Imagepath, params.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
filestat, err := os.Stat(params.Filepath)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
res.Size = filestat.Size()
|
||||||
|
res.Filepath = params.Filepath
|
||||||
|
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
||||||
|
*
|
||||||
|
* This work is published and licensed under a Creative Commons
|
||||||
|
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
||||||
|
*
|
||||||
|
* Distribution of this work is permitted, but commercial use and
|
||||||
|
* modifications are strictly prohibited.
|
||||||
|
*/
|
||||||
|
package imagecmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"mstore/pkg/gcrcli"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PushImage
|
||||||
|
type PushImageParams struct {
|
||||||
|
Imagepath string
|
||||||
|
Filepath string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PushImageResult struct{}
|
||||||
|
|
||||||
|
func (util *ImageUtil) PushImage(cmd *cobra.Command, args []string) {
|
||||||
|
util.pushImageParams.Filepath = args[0]
|
||||||
|
util.pushImageParams.Imagepath = args[1]
|
||||||
|
|
||||||
|
res, err := util.pushImage(&util.commonImageParams, &util.pushImageParams)
|
||||||
|
printResponse(res, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (util *ImageUtil) pushImage(common *CommonImageParams, params *PushImageParams) (*PushImageResult, error) {
|
||||||
|
var err error
|
||||||
|
ctx := context.Background()
|
||||||
|
res := &PushImageResult{}
|
||||||
|
|
||||||
|
cli := gcrcli.NewClient(common.SkipTLSVerify)
|
||||||
|
timeout := time.Duration(common.Timeout) * time.Second
|
||||||
|
params.Imagepath, err = packUserinfo(params.Imagepath, common.Username, common.Password)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
ctx, _ = context.WithTimeout(ctx, timeout)
|
||||||
|
err = cli.PushImage(ctx, params.Filepath, params.Imagepath)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
@@ -49,13 +49,13 @@ func (util *ImageUtil) imageManifest(common *CommonImageParams, params *ImageMan
|
|||||||
}
|
}
|
||||||
timeout := time.Duration(common.Timeout) * time.Second
|
timeout := time.Duration(common.Timeout) * time.Second
|
||||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||||
ref, err := repocli.ParseReference(params.Imagepath)
|
ref, err := repocli.NewReferer(params.Imagepath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
mw := repocli.NewBasicAuthMiddleware(ref.Userinfo())
|
mw := repocli.NewBasicAuthMiddleware(ref.Userinfo())
|
||||||
cli := repocli.NewClient(nil, mw)
|
cli := repocli.NewClient(nil, mw)
|
||||||
exists, mime, man, err := cli.GetRawManifest(ctx, ref.Repo())
|
exists, mime, man, _, err := cli.GetRawManifest(ctx, ref.RawRepo())
|
||||||
if !exists {
|
if !exists {
|
||||||
err = fmt.Errorf("Manifest not found")
|
err = fmt.Errorf("Manifest not found")
|
||||||
return res, err
|
return res, err
|
||||||
|
|||||||
@@ -10,12 +10,13 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mstore/cmd/mstorectl/util"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
util := NewUtil()
|
util := util.NewUtil()
|
||||||
err = util.Build()
|
err = util.Build()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package main
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"mstore/app/accoper"
|
|
||||||
"mstore/app/handler"
|
|
||||||
"mstore/pkg/descr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cli *Client) CreateAccount(ctx context.Context, hosturi, username, password string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
|
|
||||||
apiuri, err := setApiPath(hosturi, "/v3/api/account/create")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.CreateAccountParams{
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apiuri, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.CreateAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.AccountID
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) GetAccountByID(ctx context.Context, hosturi, accountID string) (*descr.AccountShort, error) {
|
|
||||||
var err error
|
|
||||||
res := &descr.AccountShort{}
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/get")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operParams := accoper.GetAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.GetAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Account
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) GetAccountByName(ctx context.Context, hosturi, username string) (*descr.AccountShort, error) {
|
|
||||||
var err error
|
|
||||||
res := &descr.AccountShort{}
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/get")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.GetAccountParams{
|
|
||||||
Username: username,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.GetAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Account
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) UpdateAccountByID(ctx context.Context, hosturi string, accountID, newUsername, newPassword string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/update")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.UpdateAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
NewUsername: newUsername,
|
|
||||||
NewPassword: newPassword,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operRes := handler.NewResponse[accoper.UpdateAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) UpdateAccountByName(ctx context.Context, hosturi, username, newUsername, newPassword string) error {
|
|
||||||
var err error
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/update")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.UpdateAccountParams{
|
|
||||||
Username: username,
|
|
||||||
NewUsername: newUsername,
|
|
||||||
NewPassword: newPassword,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operRes := handler.NewResponse[accoper.UpdateAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) DeleteAccountByName(ctx context.Context, hosturi, username string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/delete")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.DeleteAccountParams{
|
|
||||||
Username: username,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.DeleteAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) DeleteAccountByID(ctx context.Context, hosturi, accountID string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/account/delete")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.DeleteAccountParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.DeleteAccountResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ListAccounts(ctx context.Context, hosturi string) ([]descr.AccountShort, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]descr.AccountShort, 0)
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/accounts/list")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.ListAccountsParams{}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.ListAccountsResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Accounts
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -1,417 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"mstore/pkg/auxhttp"
|
|
||||||
"mstore/pkg/descr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cli *Client) FileInfo(ctx context.Context, fileuri string) (bool, *descr.File, error) {
|
|
||||||
var exists bool
|
|
||||||
var err error
|
|
||||||
file := &descr.File{}
|
|
||||||
fileuri, username, password, err := repackServiceURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
fileuri, err = convertFileURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodHead, fileuri, nil)
|
|
||||||
if err != nil {
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
file.Collection = resp.Header.Get("Content-Collection")
|
|
||||||
file.Name = resp.Header.Get("Content-Name")
|
|
||||||
contentSize := resp.Header.Get("Content-Size")
|
|
||||||
size, err := strconv.ParseInt(contentSize, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
file.Size = size
|
|
||||||
file.Type = resp.Header.Get("Content-Type")
|
|
||||||
file.Checksum = resp.Header.Get("Content-Digest")
|
|
||||||
exists = true
|
|
||||||
}
|
|
||||||
return exists, file, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) PutFile(ctx context.Context, filename, fileuri string) error {
|
|
||||||
var err error
|
|
||||||
fileuri, username, password, err := repackServiceURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fileuri, err = convertFileURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
file, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, fileuri, file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fileinfo, err := os.Stat(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
filesize := fileinfo.Size()
|
|
||||||
|
|
||||||
req.ContentLength = filesize
|
|
||||||
req.Header.Set("Content-Type", "application/octet-stream")
|
|
||||||
req.Header.Set("Content-Size", strconv.FormatInt(filesize, 10))
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) GetFile(ctx context.Context, fileuri, filename string) (int64, error) {
|
|
||||||
var err error
|
|
||||||
var size int64
|
|
||||||
|
|
||||||
fileuri, username, password, err := repackServiceURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
fileuri, err = convertFileURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileuri, nil)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
contentLength := resp.Header.Get("Content-Length")
|
|
||||||
if contentLength == "" {
|
|
||||||
err = fmt.Errorf("Empty Content-Length received")
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
declSize, err := strconv.ParseInt(contentLength, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Wrong Content-Length value: %v", err)
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
dirname := filepath.Dir(filename)
|
|
||||||
err = os.MkdirAll(dirname, 0750)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, 0640)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
size, err = io.Copy(file, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
if size != declSize {
|
|
||||||
err := fmt.Errorf("Mismatch Content-Length and recorded filesize")
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
return size, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) DeleteFile(ctx context.Context, fileuri string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
fileuri, username, password, err := repackServiceURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fileuri, err = convertFileURI(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, fileuri, nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ListFiles(ctx context.Context, catalogURI, usePathAs string) ([]descr.File, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]descr.File, 0)
|
|
||||||
|
|
||||||
catalogURI, username, password, err := repackServiceURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
catalogURI, err = convertFilesURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
// Add values
|
|
||||||
values := url.Values{}
|
|
||||||
if usePathAs != "" {
|
|
||||||
values.Add("pathAs", string(usePathAs))
|
|
||||||
}
|
|
||||||
encodedValues := values.Encode()
|
|
||||||
if encodedValues != "" {
|
|
||||||
catalogURI = catalogURI + "?" + encodedValues
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalogURI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
contentLength := resp.Header.Get("Content-Length")
|
|
||||||
if contentLength == "" {
|
|
||||||
err = fmt.Errorf("Empty Content-Length received")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
declSize, err := strconv.ParseInt(contentLength, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Wrong Content-Length value: %v", err)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBuffer := bytes.NewBuffer(nil)
|
|
||||||
size, err := io.Copy(respBuffer, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes := respBuffer.Bytes()
|
|
||||||
if size != declSize {
|
|
||||||
err := fmt.Errorf("Mismatch Content-Length and recorded filesize")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(respBytes, &res)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ListCollections(ctx context.Context, catalogURI, usePathAs string) ([]string, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]string, 0)
|
|
||||||
|
|
||||||
catalogURI, username, password, err := repackServiceURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
catalogURI, err = convertCollectionsURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add values
|
|
||||||
values := url.Values{}
|
|
||||||
if usePathAs != "" {
|
|
||||||
values.Add("pathAs", string(usePathAs))
|
|
||||||
}
|
|
||||||
encodedValues := values.Encode()
|
|
||||||
if encodedValues != "" {
|
|
||||||
catalogURI = catalogURI + "?" + encodedValues
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, catalogURI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
contentLength := resp.Header.Get("Content-Length")
|
|
||||||
if contentLength == "" {
|
|
||||||
err = fmt.Errorf("Empty Content-Length received")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
declSize, err := strconv.ParseInt(contentLength, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Wrong Content-Length value: %v", err)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBuffer := bytes.NewBuffer(nil)
|
|
||||||
size, err := io.Copy(respBuffer, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes := respBuffer.Bytes()
|
|
||||||
if size != declSize {
|
|
||||||
err := fmt.Errorf("Mismatch Content-Length and recorded filesize")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(respBytes, &res)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) DeleteCollection(ctx context.Context, catalogURI, usePathAs string, dryRun bool) ([]descr.File, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]descr.File, 0)
|
|
||||||
|
|
||||||
catalogURI, username, password, err := repackServiceURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
catalogURI, err = convertCollectionURI(catalogURI)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
// Add values
|
|
||||||
values := url.Values{}
|
|
||||||
if usePathAs != "" {
|
|
||||||
values.Add("pathAs", string(usePathAs))
|
|
||||||
}
|
|
||||||
if dryRun {
|
|
||||||
values.Add("dryRun", "true")
|
|
||||||
}
|
|
||||||
encodedValues := values.Encode()
|
|
||||||
if encodedValues != "" {
|
|
||||||
catalogURI = catalogURI + "?" + encodedValues
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, catalogURI, nil)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
client := makeHTTPClient(cli.skipTLSVerify)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
contentLength := resp.Header.Get("Content-Length")
|
|
||||||
if contentLength == "" {
|
|
||||||
err = fmt.Errorf("Empty Content-Length received")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Received wrong status code: %s", resp.Status)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
declSize, err := strconv.ParseInt(contentLength, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("Wrong Content-Length value: %v", err)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBuffer := bytes.NewBuffer(nil)
|
|
||||||
size, err := io.Copy(respBuffer, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes := respBuffer.Bytes()
|
|
||||||
if size != declSize {
|
|
||||||
err := fmt.Errorf("Mismatch Content-Length and recorded filesize")
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(respBytes, &res)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func makeHTTPClient(skipTLSVerify bool) *http.Client {
|
|
||||||
transport := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: skipTLSVerify,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: transport,
|
|
||||||
}
|
|
||||||
return client
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertFileURI(fileuri string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
uri, err := url.Parse(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
const fileAPI = "/v3/api/file/"
|
|
||||||
uri.Path, err = url.JoinPath(fileAPI, uri.Path)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res = uri.String()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertFilesURI(fileuri string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
uri, err := url.Parse(fileuri)
|
|
||||||
const filesAPI = "/v3/api/files/"
|
|
||||||
uri.Path, err = url.JoinPath(filesAPI, uri.Path)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = uri.String()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertCollectionsURI(fileuri string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
uri, err := url.Parse(fileuri)
|
|
||||||
const prefixAPI = "/v3/api/collections/"
|
|
||||||
uri.Path, err = url.JoinPath(prefixAPI, uri.Path)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = uri.String()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertCollectionURI(fileuri string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
uri, err := url.Parse(fileuri)
|
|
||||||
const prefixAPI = "/v3/api/collection/"
|
|
||||||
uri.Path, err = url.JoinPath(prefixAPI, uri.Path)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = uri.String()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func repackServiceURI(fileuri string) (string, string, string, error) {
|
|
||||||
var err error
|
|
||||||
var res, username, password string
|
|
||||||
if !strings.Contains(fileuri, "://") {
|
|
||||||
fileuri = "https://" + fileuri
|
|
||||||
}
|
|
||||||
uri, err := url.Parse(fileuri)
|
|
||||||
if err != nil {
|
|
||||||
return res, username, password, err
|
|
||||||
}
|
|
||||||
uri.Path = path.Clean(uri.Path)
|
|
||||||
if uri.User != nil {
|
|
||||||
username = uri.User.Username()
|
|
||||||
password, _ = uri.User.Password()
|
|
||||||
}
|
|
||||||
uri.User = nil
|
|
||||||
//uri.Scheme = "https"
|
|
||||||
res = uri.String()
|
|
||||||
return res, username, password, err
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"mstore/app/accoper"
|
|
||||||
"mstore/app/handler"
|
|
||||||
"mstore/pkg/descr"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (cli *Client) CreateGrantByAccountID(ctx context.Context, hosturi string, accountID, right, pattern string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
|
|
||||||
apiuri, err := setApiPath(hosturi, "/v3/api/grant/create")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.CreateGrantParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
Right: right,
|
|
||||||
Pattern: pattern,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apiuri, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.CreateGrantResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.GrantID
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) CreateGrantByUsername(ctx context.Context, hosturi, username string, right string, pattern string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
|
|
||||||
apiuri, err := setApiPath(hosturi, "/v3/api/grant/create")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.CreateGrantParams{
|
|
||||||
Username: username,
|
|
||||||
Right: right,
|
|
||||||
Pattern: pattern,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apiuri, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.CreateGrantResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.GrantID
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) GetGrant(ctx context.Context, hosturi, grantID string) (*descr.Grant, error) {
|
|
||||||
var err error
|
|
||||||
res := &descr.Grant{}
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/grant/get")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.GetGrantParams{
|
|
||||||
GrantID: grantID,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.GetGrantResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Grant
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) UpdateGrant(ctx context.Context, hosturi, grantID, newPattern string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/grant/update")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.UpdateGrantParams{
|
|
||||||
GrantID: grantID,
|
|
||||||
NewPattern: newPattern,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operRes := handler.NewResponse[accoper.UpdateGrantResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) DeleteGrant(ctx context.Context, hosturi, grantID string) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/grant/delete")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
operParams := accoper.DeleteGrantParams{
|
|
||||||
GrantID: grantID,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.DeleteGrantResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ListGrantsByAccountID(ctx context.Context, hosturi, accountID string) ([]descr.Grant, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]descr.Grant, 0)
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/grants/list")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.ListGrantsParams{
|
|
||||||
AccountID: accountID,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.ListGrantsResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Grants
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ListGrantsByUsername(ctx context.Context, hosturi, username string) ([]descr.Grant, error) {
|
|
||||||
var err error
|
|
||||||
res := make([]descr.Grant, 0)
|
|
||||||
|
|
||||||
apipath, err := setApiPath(hosturi, "/v3/api/grants/list")
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
operParams := accoper.ListGrantsParams{
|
|
||||||
Username: username,
|
|
||||||
}
|
|
||||||
paramsJson, err := json.Marshal(operParams)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
respBytes, err := doHTTPCall(ctx, cli.skipTLSVerify, apipath, paramsJson)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
operRes := handler.NewResponse[accoper.ListGrantsResult]()
|
|
||||||
err = json.Unmarshal(respBytes, operRes)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if operRes.Error {
|
|
||||||
err = fmt.Errorf("%s", operRes.Message)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = operRes.Result.Grants
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"mstore/pkg/auxhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setApiPath(base, apipath string) (string, error) {
|
|
||||||
var res string
|
|
||||||
if !strings.Contains(base, "://") {
|
|
||||||
base = "https://" + base
|
|
||||||
}
|
|
||||||
uri, err := url.Parse(base)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
uri.Path = "/"
|
|
||||||
uri.Path, err = url.JoinPath(uri.Path, apipath)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
res = uri.String()
|
|
||||||
return res, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func doHTTPCall(ctx context.Context, skipTLSVerify bool, apiuri string, reqBytes []byte) ([]byte, error) {
|
|
||||||
var err error
|
|
||||||
respBytes := make([]byte, 0)
|
|
||||||
|
|
||||||
apiuri, username, password, err := repackServiceURI(apiuri)
|
|
||||||
if err != nil {
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
reqBuffer := bytes.NewBuffer(reqBytes)
|
|
||||||
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, apiuri, reqBuffer)
|
|
||||||
if err != nil {
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
httpReq.Header.Set("Content-Type", "application/json")
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basicHeader := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
httpReq.Header.Add("Authorization", basicHeader)
|
|
||||||
}
|
|
||||||
transport := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: skipTLSVerify},
|
|
||||||
}
|
|
||||||
httpClient := &http.Client{
|
|
||||||
Transport: transport,
|
|
||||||
}
|
|
||||||
httpResp, err := httpClient.Do(httpReq)
|
|
||||||
if err != nil {
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
defer httpResp.Body.Close()
|
|
||||||
if httpResp.StatusCode != http.StatusOK {
|
|
||||||
err := fmt.Errorf("Wrong StatusCode header: %s", httpResp.Status)
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
respBuffer := bytes.NewBuffer(nil)
|
|
||||||
_, err = io.Copy(respBuffer, httpResp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
respBytes = respBuffer.Bytes()
|
|
||||||
return respBytes, err
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/tls"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"mstore/pkg/auxhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func convertServiceURI(ref string) (string, error) {
|
|
||||||
var err error
|
|
||||||
var res string
|
|
||||||
const serviceAPI = "/v3/api/service/"
|
|
||||||
const serviceScheme = "https"
|
|
||||||
uri, err := url.Parse(ref)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
uri.Path, err = url.JoinPath(serviceAPI, uri.Path)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
uri.Scheme = serviceScheme
|
|
||||||
res = uri.String()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cli *Client) ServiceHello(ctx context.Context, serviceuri string, timeout time.Duration) (bool, error) {
|
|
||||||
var res bool
|
|
||||||
var err error
|
|
||||||
ctx, _ = context.WithTimeout(ctx, timeout)
|
|
||||||
|
|
||||||
serviceuri, username, password, err := repackServiceURI(serviceuri)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
serviceuri, err = convertServiceURI(serviceuri)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, serviceuri, nil)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
if username != "" && password != "" {
|
|
||||||
basic := auxhttp.EncodeBasicAuth(username, password)
|
|
||||||
req.Header.Add("Authorization", basic)
|
|
||||||
}
|
|
||||||
transport := &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{
|
|
||||||
InsecureSkipVerify: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: transport,
|
|
||||||
}
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
res = true
|
|
||||||
}
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
skipTLSVerify bool
|
skipTLSVerify bool
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
* Distribution of this work is permitted, but commercial use and
|
* Distribution of this work is permitted, but commercial use and
|
||||||
* modifications are strictly prohibited.
|
* modifications are strictly prohibited.
|
||||||
*/
|
*/
|
||||||
package client
|
package gcrcli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"mstore/app/server"
|
|
||||||
"mstore/pkg/client"
|
|
||||||
"mstore/pkg/terms"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
yaml "go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAccountLife(t *testing.T) {
|
|
||||||
var srvport int64 = 10250
|
|
||||||
srvdir := t.TempDir()
|
|
||||||
srvaddr := fmt.Sprintf("mstore:mstore@127.0.0.1:%d", srvport)
|
|
||||||
|
|
||||||
srv, err := server.NewServer()
|
|
||||||
require.NoError(t, err)
|
|
||||||
{
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
useTmp := true
|
|
||||||
if useTmp {
|
|
||||||
srv.SetDatadir(srvdir)
|
|
||||||
srv.SetLogdir(srvdir)
|
|
||||||
srv.SetRundir(srvdir)
|
|
||||||
}
|
|
||||||
srv.SetPort(srvport)
|
|
||||||
|
|
||||||
err = srv.Build()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var svcWG sync.WaitGroup
|
|
||||||
errPipe := make(chan error, 5)
|
|
||||||
|
|
||||||
startFunc := func() {
|
|
||||||
err := srv.Service().Run()
|
|
||||||
errPipe <- err
|
|
||||||
svcWG.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
stopFunc := func() {
|
|
||||||
srv.Service().Stop()
|
|
||||||
svcWG.Wait()
|
|
||||||
err = <-errPipe
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
defer stopFunc()
|
|
||||||
|
|
||||||
svcWG.Add(1)
|
|
||||||
go startFunc()
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ServiceHello
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
helloRes, err := cli.ServiceHello(ctx, srvaddr+"/hello", 1*time.Second)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, helloRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
username := "testuser"
|
|
||||||
password := "testpass"
|
|
||||||
|
|
||||||
var accountID string
|
|
||||||
{
|
|
||||||
// CreateAccount
|
|
||||||
fmt.Printf("=== CreateAccount ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
accountID, err = cli.CreateAccount(ctx, srvaddr, username, password)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
var grantID string
|
|
||||||
{
|
|
||||||
// CreateGrant
|
|
||||||
fmt.Printf("=== CreateGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
grantID, err = cli.CreateGrantByAccountID(ctx, srvaddr, accountID, terms.RightReadAccounts, ".*")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, len(grantID), 36)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// UpdateGrant
|
|
||||||
fmt.Printf("=== UpdateGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err := cli.UpdateGrant(ctx, srvaddr, grantID, ".*")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// CreateGrant
|
|
||||||
fmt.Printf("=== CreateGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
grantID, err = cli.CreateGrantByAccountID(ctx, srvaddr, accountID, terms.RightWriteAccounts, ".*")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, len(grantID), 36)
|
|
||||||
fmt.Printf("grantID: %s\n", grantID)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// GetGrant
|
|
||||||
fmt.Printf("=== GetGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
grantDescr, err := cli.GetGrant(ctx, srvaddr, grantID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, len(grantID), 36)
|
|
||||||
fmt.Printf("grantID: %s\n", grantID)
|
|
||||||
grantYAML, err := yaml.Marshal(grantDescr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("account:\n%s\n", string(grantYAML))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// UpdateGrant
|
|
||||||
fmt.Printf("=== UpdateGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err := cli.UpdateGrant(ctx, srvaddr, grantID, "**")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// GetGrant
|
|
||||||
fmt.Printf("=== GetGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
grantDescr, err := cli.GetGrant(ctx, srvaddr, grantID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, len(grantID), 36)
|
|
||||||
fmt.Printf("grantID: %s\n", grantID)
|
|
||||||
grantYAML, err := yaml.Marshal(grantDescr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("account:\n%s\n", string(grantYAML))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// DeleteGrant
|
|
||||||
fmt.Printf("=== DeleteGrant ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err := cli.DeleteGrant(ctx, srvaddr, grantID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// GetAccount
|
|
||||||
fmt.Printf("=== GetAccount ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
accountDescr, err := cli.GetAccountByID(ctx, srvaddr, accountID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
accountYAML, err := yaml.Marshal(accountDescr)
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("account:\n%s\n", string(accountYAML))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ListAccounts
|
|
||||||
fmt.Printf("=== ListAccounts ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
accountDescrs, err := cli.ListAccounts(ctx, srvaddr+"/")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotZero(t, len(accountDescrs))
|
|
||||||
nameList := make([]string, 0)
|
|
||||||
for _, item := range accountDescrs {
|
|
||||||
nameList = append(nameList, item.Username)
|
|
||||||
}
|
|
||||||
accountsYAML, err := yaml.Marshal(nameList)
|
|
||||||
fmt.Printf("accounts:\n%s\n", string(accountsYAML))
|
|
||||||
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// DeleteAccount
|
|
||||||
fmt.Printf("=== DeleteAccount ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err = cli.DeleteAccountByID(ctx, srvaddr, accountID)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"mstore/app/server"
|
|
||||||
"mstore/pkg/client"
|
|
||||||
"mstore/pkg/terms"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestFileLife(t *testing.T) {
|
|
||||||
var srvport int64 = 10250
|
|
||||||
srvdir := t.TempDir()
|
|
||||||
srvaddr := fmt.Sprintf("mstore:mstore@127.0.0.1:%d", srvport)
|
|
||||||
|
|
||||||
srv, err := server.NewServer()
|
|
||||||
require.NoError(t, err)
|
|
||||||
{
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
useTmpDir := true
|
|
||||||
if useTmpDir {
|
|
||||||
srv.SetDatadir(srvdir)
|
|
||||||
srv.SetLogdir(srvdir)
|
|
||||||
srv.SetRundir(srvdir)
|
|
||||||
}
|
|
||||||
srv.SetPort(srvport)
|
|
||||||
|
|
||||||
err = srv.Build()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var svcWG sync.WaitGroup
|
|
||||||
errPipe := make(chan error, 5)
|
|
||||||
|
|
||||||
startFunc := func() {
|
|
||||||
err := srv.Service().Run()
|
|
||||||
errPipe <- err
|
|
||||||
svcWG.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
stopFunc := func() {
|
|
||||||
srv.Service().Stop()
|
|
||||||
svcWG.Wait()
|
|
||||||
err = <-errPipe
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
defer stopFunc()
|
|
||||||
|
|
||||||
svcWG.Add(1)
|
|
||||||
go startFunc()
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ServiceHello
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
helloRes, err := cli.ServiceHello(ctx, srvaddr+"/hello", 1*time.Second)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, helloRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
filesize := 32
|
|
||||||
{
|
|
||||||
// PutFile
|
|
||||||
tmpdir := t.TempDir()
|
|
||||||
tmpfile := filepath.Join(tmpdir, "foo.bin")
|
|
||||||
|
|
||||||
filedata := make([]byte, filesize)
|
|
||||||
_, err = rand.Read(filedata)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
err := os.WriteFile(tmpfile, filedata, 0666)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
fmt.Printf("=== PutFile ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err = cli.PutFile(ctx, tmpfile, srvaddr+"/foo.bin")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// FileInfo
|
|
||||||
fmt.Printf("=== FileInfo ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
exists, file, err := cli.FileInfo(ctx, srvaddr+"/foo.bin")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, exists)
|
|
||||||
require.NotNil(t, file)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// GetFile
|
|
||||||
fmt.Printf("=== GetFile ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
tmpdir := t.TempDir()
|
|
||||||
tmpfile := filepath.Join(tmpdir, "foo.bin")
|
|
||||||
|
|
||||||
recsize, err := cli.GetFile(ctx, srvaddr+"/foo.bin", tmpfile)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, int64(filesize), recsize)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ListFiles
|
|
||||||
fmt.Printf("=== ListFiles ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
files, err := cli.ListFiles(ctx, srvaddr+"/", terms.AsFinePath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotZero(t, len(files))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// DeleteFile
|
|
||||||
fmt.Printf("=== DeleteFile ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
err = cli.DeleteFile(ctx, srvaddr+"/foo.bin")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// !FileInfo
|
|
||||||
fmt.Printf("=== FileInfo ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
ctx, _ = context.WithTimeout(ctx, 1*time.Second)
|
|
||||||
|
|
||||||
exists, _, err := cli.FileInfo(ctx, srvaddr+"/foo.bin")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.False(t, exists)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
||||||
*
|
|
||||||
* This work is published and licensed under a Creative Commons
|
|
||||||
* Attribution-NonCommercial-NoDerivatives 4.0 International License.
|
|
||||||
*
|
|
||||||
* Distribution of this work is permitted, but commercial use and
|
|
||||||
* modifications are strictly prohibited.
|
|
||||||
*/
|
|
||||||
package test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"mstore/app/server"
|
|
||||||
"mstore/pkg/client"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
yaml "go.yaml.in/yaml/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestImageLife(t *testing.T) {
|
|
||||||
var srvport int64 = 10250
|
|
||||||
srvdir := t.TempDir()
|
|
||||||
srvaddr := fmt.Sprintf("mstore:mstore@127.0.0.1:%d", srvport)
|
|
||||||
|
|
||||||
srv, err := server.NewServer()
|
|
||||||
require.NoError(t, err)
|
|
||||||
{
|
|
||||||
err = srv.Configure()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
useTmp := true
|
|
||||||
if useTmp {
|
|
||||||
srv.SetDatadir(srvdir)
|
|
||||||
srv.SetLogdir(srvdir)
|
|
||||||
srv.SetRundir(srvdir)
|
|
||||||
}
|
|
||||||
srv.SetPort(srvport)
|
|
||||||
|
|
||||||
err = srv.Build()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var svcWG sync.WaitGroup
|
|
||||||
errPipe := make(chan error, 5)
|
|
||||||
|
|
||||||
startFunc := func() {
|
|
||||||
err := srv.Service().Run()
|
|
||||||
errPipe <- err
|
|
||||||
svcWG.Done()
|
|
||||||
}
|
|
||||||
|
|
||||||
stopFunc := func() {
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
srv.Service().Stop()
|
|
||||||
svcWG.Wait()
|
|
||||||
err = <-errPipe
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
defer stopFunc()
|
|
||||||
|
|
||||||
svcWG.Add(1)
|
|
||||||
go startFunc()
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ServiceHello
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
helloRes, err := cli.ServiceHello(ctx, srvaddr+"/hello", 1*time.Second)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, helloRes)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// PishImage
|
|
||||||
fmt.Printf("=== PushImage ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
err := cli.PushImage(ctx, "test-oci.tar", srvaddr+"/foo/test:123")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// ImageAmnifest
|
|
||||||
fmt.Printf("=== ImageManifest ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
info, err := cli.ImageManifest(ctx, srvaddr+"/foo/test:123")
|
|
||||||
require.NoError(t, err)
|
|
||||||
infoYaml, err := yaml.Marshal(info)
|
|
||||||
require.NoError(t, err)
|
|
||||||
fmt.Printf("imagemanifest:\n%s\n", string(infoYaml))
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// DeleteImage
|
|
||||||
fmt.Printf("=== DeleteImage ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx, _ := context.WithTimeout(context.Background(), 1*time.Second)
|
|
||||||
err := cli.DeleteImage(ctx, srvaddr+"/foo/test:123")
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+2
-14
@@ -10,18 +10,14 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
//"math/rand"
|
|
||||||
//"os"
|
|
||||||
//"path/filepath"
|
|
||||||
"bytes"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"mstore/app/server"
|
"mstore/app/server"
|
||||||
"mstore/pkg/client"
|
|
||||||
"mstore/pkg/filecli"
|
"mstore/pkg/filecli"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -70,15 +66,7 @@ func TestFileLife(t *testing.T) {
|
|||||||
go startFunc()
|
go startFunc()
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// ServiceHello
|
|
||||||
fmt.Printf("=== ServiceHello ===\n")
|
|
||||||
cli := client.NewClient(true)
|
|
||||||
ctx := context.Background()
|
|
||||||
helloRes, err := cli.ServiceHello(ctx, srvaddr+"/hello", 1*time.Second)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.True(t, helloRes)
|
|
||||||
}
|
|
||||||
fileaddr := srvaddr + "/foo2/bare.bin"
|
fileaddr := srvaddr + "/foo2/bare.bin"
|
||||||
filedata := []byte("Hello, World")
|
filedata := []byte("Hello, World")
|
||||||
filesize := int64(len(filedata))
|
filesize := int64(len(filedata))
|
||||||
|
|||||||
Reference in New Issue
Block a user