diff --git a/.gitignore b/.gitignore index cef0e1b..bf955ea 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ DIST *.rpm mstored.service variant.go +initrc/mstored diff --git a/Makefile.am b/Makefile.am index 5719a16..79c6b53 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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/ diff --git a/Makefile.in b/Makefile.in index 21423a7..ccde69b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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/ diff --git a/attic/account_test.go b/attic/account_test.go deleted file mode 100644 index 15da075..0000000 --- a/attic/account_test.go +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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) - } - -} diff --git a/attic/file_test.go b/attic/file_test.go deleted file mode 100644 index 937dff5..0000000 --- a/attic/file_test.go +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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)) - } -} diff --git a/cmd/mstorectl/imagecmd/gcrimageconf.go b/cmd/mstorectl/imagecmd/gcrimageconf.go new file mode 100644 index 0000000..289dd1c --- /dev/null +++ b/cmd/mstorectl/imagecmd/gcrimageconf.go @@ -0,0 +1,59 @@ +/* + * Copyright 2026 Oleg Borodin + * + * 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 +} diff --git a/cmd/mstorectl/imagecmd/gcrpullimage.go b/cmd/mstorectl/imagecmd/gcrpullimage.go new file mode 100644 index 0000000..97fa540 --- /dev/null +++ b/cmd/mstorectl/imagecmd/gcrpullimage.go @@ -0,0 +1,65 @@ +/* + * Copyright 2026 Oleg Borodin + * + * 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 +} diff --git a/cmd/mstorectl/imagecmd/gcrpushimage.go b/cmd/mstorectl/imagecmd/gcrpushimage.go new file mode 100644 index 0000000..4ad311b --- /dev/null +++ b/cmd/mstorectl/imagecmd/gcrpushimage.go @@ -0,0 +1,54 @@ +/* + * Copyright 2026 Oleg Borodin + * + * 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 +} diff --git a/cmd/mstorectl/imagecmd/imageman.go b/cmd/mstorectl/imagecmd/imageman.go index a93a045..92cc41d 100644 --- a/cmd/mstorectl/imagecmd/imageman.go +++ b/cmd/mstorectl/imagecmd/imageman.go @@ -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 diff --git a/cmd/mstorectl/main.go b/cmd/mstorectl/main.go index b7831ea..9aa3e19 100644 --- a/cmd/mstorectl/main.go +++ b/cmd/mstorectl/main.go @@ -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) diff --git a/cmd/mstorectl/util.go b/cmd/mstorectl/util/util.go similarity index 99% rename from cmd/mstorectl/util.go rename to cmd/mstorectl/util/util.go index 8d0631a..5353112 100644 --- a/cmd/mstorectl/util.go +++ b/cmd/mstorectl/util/util.go @@ -7,7 +7,7 @@ * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ -package main +package util import ( "fmt" diff --git a/pkg/client/attic/account.go b/pkg/client/attic/account.go deleted file mode 100644 index 25f0037..0000000 --- a/pkg/client/attic/account.go +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/attic/file.go b/pkg/client/attic/file.go deleted file mode 100644 index aacce56..0000000 --- a/pkg/client/attic/file.go +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/attic/fileaux.go b/pkg/client/attic/fileaux.go deleted file mode 100644 index d980c18..0000000 --- a/pkg/client/attic/fileaux.go +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/attic/grant.go b/pkg/client/attic/grant.go deleted file mode 100644 index 5e98da9..0000000 --- a/pkg/client/attic/grant.go +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/attic/httpcall.go b/pkg/client/attic/httpcall.go deleted file mode 100644 index fc0d5f6..0000000 --- a/pkg/client/attic/httpcall.go +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/attic/service.go b/pkg/client/attic/service.go deleted file mode 100644 index 3cae199..0000000 --- a/pkg/client/attic/service.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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 -} diff --git a/pkg/client/client.go b/pkg/gcrcli/client.go similarity index 96% rename from pkg/client/client.go rename to pkg/gcrcli/client.go index 462b1bd..ab38b63 100644 --- a/pkg/client/client.go +++ b/pkg/gcrcli/client.go @@ -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 diff --git a/pkg/client/imageaux.go b/pkg/gcrcli/imageaux.go similarity index 99% rename from pkg/client/imageaux.go rename to pkg/gcrcli/imageaux.go index 37d8967..e7f3fdb 100644 --- a/pkg/client/imageaux.go +++ b/pkg/gcrcli/imageaux.go @@ -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" diff --git a/pkg/client/imagedelete.go b/pkg/gcrcli/imagedelete.go similarity index 99% rename from pkg/client/imagedelete.go rename to pkg/gcrcli/imagedelete.go index 0bab1ec..c83a4e3 100644 --- a/pkg/client/imagedelete.go +++ b/pkg/gcrcli/imagedelete.go @@ -7,7 +7,7 @@ * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ -package client +package gcrcli import ( "context" diff --git a/pkg/client/imageinfo.go b/pkg/gcrcli/imageinfo.go similarity index 99% rename from pkg/client/imageinfo.go rename to pkg/gcrcli/imageinfo.go index 4889c1f..9030ba8 100644 --- a/pkg/client/imageinfo.go +++ b/pkg/gcrcli/imageinfo.go @@ -7,7 +7,7 @@ * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ -package client +package gcrcli import ( "context" diff --git a/pkg/client/imagepull.go b/pkg/gcrcli/imagepull.go similarity index 99% rename from pkg/client/imagepull.go rename to pkg/gcrcli/imagepull.go index 5ee19ef..de936ce 100644 --- a/pkg/client/imagepull.go +++ b/pkg/gcrcli/imagepull.go @@ -7,7 +7,7 @@ * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ -package client +package gcrcli import ( "context" diff --git a/pkg/client/imagepush.go b/pkg/gcrcli/imagepush.go similarity index 99% rename from pkg/client/imagepush.go rename to pkg/gcrcli/imagepush.go index 3131b9d..0fb9187 100644 --- a/pkg/client/imagepush.go +++ b/pkg/gcrcli/imagepush.go @@ -7,7 +7,7 @@ * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ -package client +package gcrcli import ( "context" diff --git a/test/attic/account_test.go b/test/attic/account_test.go deleted file mode 100644 index 6cda33a..0000000 --- a/test/attic/account_test.go +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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) - } - -} diff --git a/test/attic/file_test.go b/test/attic/file_test.go deleted file mode 100644 index 0da2bc8..0000000 --- a/test/attic/file_test.go +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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) - - } - -} diff --git a/test/attic/image_test.go b/test/attic/image_test.go deleted file mode 100644 index 78e9ca8..0000000 --- a/test/attic/image_test.go +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2026 Oleg Borodin - * - * 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) - } -} diff --git a/test/file_test.go b/test/file_test.go index 34c6c32..061880e 100644 --- a/test/file_test.go +++ b/test/file_test.go @@ -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))