reorder some source files
This commit is contained in:
@@ -21,3 +21,4 @@ DIST
|
||||
*.rpm
|
||||
mstored.service
|
||||
variant.go
|
||||
initrc/mstored
|
||||
|
||||
+45
-35
@@ -7,35 +7,42 @@ bin_PROGRAMS = mstorectl
|
||||
sbin_PROGRAMS = mstored
|
||||
|
||||
mstorectl_SOURCES = \
|
||||
cmd/mstorectl/main.go \
|
||||
cmd/mstorectl/util.go
|
||||
cmd/mstorectl/main.go
|
||||
|
||||
EXTRA_mstorectl_SOURCES = \
|
||||
cmd/mstorectl/accountcmd/acccmd.go \
|
||||
cmd/mstorectl/accountcmd/createacc.go \
|
||||
cmd/mstorectl/accountcmd/creategrant.go \
|
||||
cmd/mstorectl/accountcmd/delacc.go \
|
||||
cmd/mstorectl/accountcmd/delgrant.go \
|
||||
cmd/mstorectl/accountcmd/getacc.go \
|
||||
cmd/mstorectl/accountcmd/getgrant.go \
|
||||
cmd/mstorectl/accountcmd/grantcmd.go \
|
||||
cmd/mstorectl/accountcmd/listacc.go \
|
||||
cmd/mstorectl/accountcmd/listgrant.go \
|
||||
cmd/mstorectl/accountcmd/printresp.go \
|
||||
cmd/mstorectl/accountcmd/updacc.go \
|
||||
cmd/mstorectl/accountcmd/updgrant.go \
|
||||
cmd/mstorectl/filecmd/delcoll.go \
|
||||
cmd/mstorectl/filecmd/delfile.go \
|
||||
cmd/mstorectl/filecmd/expfiles.go \
|
||||
cmd/mstorectl/filecmd/filecmd.go \
|
||||
cmd/mstorectl/filecmd/fileinfo.go \
|
||||
cmd/mstorectl/filecmd/getfile.go \
|
||||
cmd/mstorectl/filecmd/impfiles.go \
|
||||
cmd/mstorectl/filecmd/listcolls.go \
|
||||
cmd/mstorectl/filecmd/listfiles.go \
|
||||
cmd/mstorectl/filecmd/putfile.go \
|
||||
cmd/mstorectl/imagecmd/imagecmd.go \
|
||||
cmd/mstorectl/imagecmd/printresp.go
|
||||
cmd/mstorectl/util/util.go \
|
||||
cmd/mstorectl/accountcmd/acccmd.go \
|
||||
cmd/mstorectl/accountcmd/createacc.go \
|
||||
cmd/mstorectl/accountcmd/creategrant.go \
|
||||
cmd/mstorectl/accountcmd/delacc.go \
|
||||
cmd/mstorectl/accountcmd/delgrant.go \
|
||||
cmd/mstorectl/accountcmd/getacc.go \
|
||||
cmd/mstorectl/accountcmd/getgrant.go \
|
||||
cmd/mstorectl/accountcmd/grantcmd.go \
|
||||
cmd/mstorectl/accountcmd/listacc.go \
|
||||
cmd/mstorectl/accountcmd/listgrant.go \
|
||||
cmd/mstorectl/accountcmd/printresp.go \
|
||||
cmd/mstorectl/accountcmd/updacc.go \
|
||||
cmd/mstorectl/accountcmd/updgrant.go \
|
||||
cmd/mstorectl/filecmd/delcoll.go \
|
||||
cmd/mstorectl/filecmd/delfile.go \
|
||||
cmd/mstorectl/filecmd/expfiles.go \
|
||||
cmd/mstorectl/filecmd/filecmd.go \
|
||||
cmd/mstorectl/filecmd/fileinfo.go \
|
||||
cmd/mstorectl/filecmd/getfile.go \
|
||||
cmd/mstorectl/filecmd/impfiles.go \
|
||||
cmd/mstorectl/filecmd/listcolls.go \
|
||||
cmd/mstorectl/filecmd/listfiles.go \
|
||||
cmd/mstorectl/filecmd/putfile.go \
|
||||
cmd/mstorectl/imagecmd/catimages.go \
|
||||
cmd/mstorectl/imagecmd/delimage.go \
|
||||
cmd/mstorectl/imagecmd/imagecmd.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
|
||||
|
||||
@@ -149,12 +156,12 @@ EXTRA_mstored_SOURCES += \
|
||||
pkg/auxutar/utar.go \
|
||||
pkg/auxuuid/uuid.go \
|
||||
pkg/auxx509/x509cert.go \
|
||||
pkg/client/client.go \
|
||||
pkg/client/imageaux.go \
|
||||
pkg/client/imagedelete.go \
|
||||
pkg/client/imageinfo.go \
|
||||
pkg/client/imagepull.go \
|
||||
pkg/client/imagepush.go \
|
||||
pkg/gcrcli/client.go \
|
||||
pkg/gcrcli/imageaux.go \
|
||||
pkg/gcrcli/imagedelete.go \
|
||||
pkg/gcrcli/imageinfo.go \
|
||||
pkg/gcrcli/imagepull.go \
|
||||
pkg/gcrcli/imagepush.go \
|
||||
pkg/descr/account.go \
|
||||
pkg/descr/blob.go \
|
||||
pkg/descr/file.go \
|
||||
@@ -179,16 +186,19 @@ EXTRA_mstored_SOURCES += \
|
||||
pkg/repocli/delblob.go \
|
||||
pkg/repocli/delman.go \
|
||||
pkg/repocli/getblob.go \
|
||||
pkg/repocli/getcatalog.go \
|
||||
pkg/repocli/getman.go \
|
||||
pkg/repocli/getreferers.go \
|
||||
pkg/repocli/gettags.go \
|
||||
pkg/repocli/gettoken.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/reference.go \
|
||||
pkg/repocli/referer.go \
|
||||
pkg/repocli/uuid.go \
|
||||
pkg/terms/terms.go
|
||||
@@ -353,7 +363,7 @@ endif
|
||||
clean-local:
|
||||
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
||||
rm -rf autom4te.cache
|
||||
rm -f cmd/mstored/istored
|
||||
rm -f cmd/mstored/mstored
|
||||
rm -f cmd/mstorectl/mstorectl
|
||||
# rm -rf tmp/
|
||||
|
||||
|
||||
+46
-38
@@ -353,35 +353,42 @@ AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
|
||||
SUBDIRS = mans
|
||||
GOFLAGS = -v
|
||||
mstorectl_SOURCES = \
|
||||
cmd/mstorectl/main.go \
|
||||
cmd/mstorectl/util.go
|
||||
cmd/mstorectl/main.go
|
||||
|
||||
EXTRA_mstorectl_SOURCES = \
|
||||
cmd/mstorectl/accountcmd/acccmd.go \
|
||||
cmd/mstorectl/accountcmd/createacc.go \
|
||||
cmd/mstorectl/accountcmd/creategrant.go \
|
||||
cmd/mstorectl/accountcmd/delacc.go \
|
||||
cmd/mstorectl/accountcmd/delgrant.go \
|
||||
cmd/mstorectl/accountcmd/getacc.go \
|
||||
cmd/mstorectl/accountcmd/getgrant.go \
|
||||
cmd/mstorectl/accountcmd/grantcmd.go \
|
||||
cmd/mstorectl/accountcmd/listacc.go \
|
||||
cmd/mstorectl/accountcmd/listgrant.go \
|
||||
cmd/mstorectl/accountcmd/printresp.go \
|
||||
cmd/mstorectl/accountcmd/updacc.go \
|
||||
cmd/mstorectl/accountcmd/updgrant.go \
|
||||
cmd/mstorectl/filecmd/delcoll.go \
|
||||
cmd/mstorectl/filecmd/delfile.go \
|
||||
cmd/mstorectl/filecmd/expfiles.go \
|
||||
cmd/mstorectl/filecmd/filecmd.go \
|
||||
cmd/mstorectl/filecmd/fileinfo.go \
|
||||
cmd/mstorectl/filecmd/getfile.go \
|
||||
cmd/mstorectl/filecmd/impfiles.go \
|
||||
cmd/mstorectl/filecmd/listcolls.go \
|
||||
cmd/mstorectl/filecmd/listfiles.go \
|
||||
cmd/mstorectl/filecmd/putfile.go \
|
||||
cmd/mstorectl/imagecmd/imagecmd.go \
|
||||
cmd/mstorectl/imagecmd/printresp.go
|
||||
cmd/mstorectl/util/util.go \
|
||||
cmd/mstorectl/accountcmd/acccmd.go \
|
||||
cmd/mstorectl/accountcmd/createacc.go \
|
||||
cmd/mstorectl/accountcmd/creategrant.go \
|
||||
cmd/mstorectl/accountcmd/delacc.go \
|
||||
cmd/mstorectl/accountcmd/delgrant.go \
|
||||
cmd/mstorectl/accountcmd/getacc.go \
|
||||
cmd/mstorectl/accountcmd/getgrant.go \
|
||||
cmd/mstorectl/accountcmd/grantcmd.go \
|
||||
cmd/mstorectl/accountcmd/listacc.go \
|
||||
cmd/mstorectl/accountcmd/listgrant.go \
|
||||
cmd/mstorectl/accountcmd/printresp.go \
|
||||
cmd/mstorectl/accountcmd/updacc.go \
|
||||
cmd/mstorectl/accountcmd/updgrant.go \
|
||||
cmd/mstorectl/filecmd/delcoll.go \
|
||||
cmd/mstorectl/filecmd/delfile.go \
|
||||
cmd/mstorectl/filecmd/expfiles.go \
|
||||
cmd/mstorectl/filecmd/filecmd.go \
|
||||
cmd/mstorectl/filecmd/fileinfo.go \
|
||||
cmd/mstorectl/filecmd/getfile.go \
|
||||
cmd/mstorectl/filecmd/impfiles.go \
|
||||
cmd/mstorectl/filecmd/listcolls.go \
|
||||
cmd/mstorectl/filecmd/listfiles.go \
|
||||
cmd/mstorectl/filecmd/putfile.go \
|
||||
cmd/mstorectl/imagecmd/catimages.go \
|
||||
cmd/mstorectl/imagecmd/delimage.go \
|
||||
cmd/mstorectl/imagecmd/imagecmd.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
|
||||
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/tmpfile.go pkg/auxtool/unixnow.go \
|
||||
pkg/auxutar/utar.go pkg/auxuuid/uuid.go \
|
||||
pkg/auxx509/x509cert.go pkg/client/client.go \
|
||||
pkg/client/imageaux.go pkg/client/imagedelete.go \
|
||||
pkg/client/imageinfo.go pkg/client/imagepull.go \
|
||||
pkg/client/imagepush.go pkg/descr/account.go pkg/descr/blob.go \
|
||||
pkg/auxx509/x509cert.go pkg/gcrcli/client.go \
|
||||
pkg/gcrcli/imageaux.go pkg/gcrcli/imagedelete.go \
|
||||
pkg/gcrcli/imageinfo.go pkg/gcrcli/imagepull.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/server.go pkg/digest/digest.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/repocli/client.go pkg/repocli/copywctx.go \
|
||||
pkg/repocli/delblob.go pkg/repocli/delman.go \
|
||||
pkg/repocli/getblob.go pkg/repocli/getman.go \
|
||||
pkg/repocli/gettoken.go pkg/repocli/getupload.go \
|
||||
pkg/repocli/imager.go pkg/repocli/manexist.go \
|
||||
pkg/repocli/patchupload.go pkg/repocli/pullimage.go \
|
||||
pkg/repocli/getblob.go pkg/repocli/getcatalog.go \
|
||||
pkg/repocli/getman.go pkg/repocli/getreferers.go \
|
||||
pkg/repocli/gettags.go pkg/repocli/gettoken.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/reference.go pkg/repocli/referer.go \
|
||||
pkg/repocli/uuid.go pkg/terms/terms.go
|
||||
pkg/repocli/referer.go pkg/repocli/uuid.go pkg/terms/terms.go
|
||||
CWD = $(shell pwd)
|
||||
EXTRA_DIST = vendor/* \
|
||||
\
|
||||
@@ -1189,7 +1197,7 @@ install-data-local:
|
||||
clean-local:
|
||||
$(FIND) $(CWD) -name '*~' | $(XARGS) rm -f
|
||||
rm -rf autom4te.cache
|
||||
rm -f cmd/mstored/istored
|
||||
rm -f cmd/mstored/mstored
|
||||
rm -f cmd/mstorectl/mstorectl
|
||||
# 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
|
||||
ctx, _ := context.WithTimeout(context.Background(), timeout)
|
||||
ref, err := repocli.ParseReference(params.Imagepath)
|
||||
ref, err := repocli.NewReferer(params.Imagepath)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
mw := repocli.NewBasicAuthMiddleware(ref.Userinfo())
|
||||
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 {
|
||||
err = fmt.Errorf("Manifest not found")
|
||||
return res, err
|
||||
|
||||
@@ -10,12 +10,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"mstore/cmd/mstorectl/util"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
util := NewUtil()
|
||||
util := util.NewUtil()
|
||||
err = util.Build()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package main
|
||||
package util
|
||||
|
||||
import (
|
||||
"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
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
type Client struct {
|
||||
skipTLSVerify bool
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -7,7 +7,7 @@
|
||||
* Distribution of this work is permitted, but commercial use and
|
||||
* modifications are strictly prohibited.
|
||||
*/
|
||||
package client
|
||||
package gcrcli
|
||||
|
||||
import (
|
||||
"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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
//"math/rand"
|
||||
//"os"
|
||||
//"path/filepath"
|
||||
"bytes"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"mstore/app/server"
|
||||
"mstore/pkg/client"
|
||||
"mstore/pkg/filecli"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -70,15 +66,7 @@ func TestFileLife(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
fileaddr := srvaddr + "/foo2/bare.bin"
|
||||
filedata := []byte("Hello, World")
|
||||
filesize := int64(len(filedata))
|
||||
|
||||
Reference in New Issue
Block a user