mirror of https://github.com/portainer/portainer
refactor(api): remove duplicates of TxResponse + HandlerError detection (#1117)
parent
26a0c4e809
commit
326fdcf6ea
|
@ -5,15 +5,14 @@ import (
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/database/models"
|
"github.com/portainer/portainer/api/database/models"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStoreCreation(t *testing.T) {
|
func TestStoreCreation(t *testing.T) {
|
||||||
_, store := MustNewTestStore(t, true, true)
|
_, store := MustNewTestStore(t, true, true)
|
||||||
if store == nil {
|
require.NotNil(t, store)
|
||||||
t.Fatal("Expect to create a store")
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := store.VersionService.Version()
|
v, err := store.VersionService.Version()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
package errors
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TxResponse(err error, validResponse func() *httperror.HandlerError) *httperror.HandlerError {
|
|
||||||
if err != nil {
|
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return validResponse()
|
|
||||||
}
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
"github.com/portainer/portainer/api/docker/stats"
|
"github.com/portainer/portainer/api/docker/stats"
|
||||||
"github.com/portainer/portainer/api/http/errors"
|
|
||||||
"github.com/portainer/portainer/api/http/handler/docker/utils"
|
"github.com/portainer/portainer/api/http/handler/docker/utils"
|
||||||
"github.com/portainer/portainer/api/http/middlewares"
|
"github.com/portainer/portainer/api/http/middlewares"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
|
@ -164,7 +163,5 @@ func (h *Handler) dashboard(w http.ResponseWriter, r *http.Request) *httperror.H
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return errors.TxResponse(err, func() *httperror.HandlerError {
|
return response.TxResponse(w, resp, err)
|
||||||
return response.JSON(w, resp)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/roar"
|
"github.com/portainer/portainer/api/roar"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
type edgeGroupCreatePayload struct {
|
type edgeGroupCreatePayload struct {
|
||||||
|
@ -111,5 +112,5 @@ func (handler *Handler) edgeGroupCreate(w http.ResponseWriter, r *http.Request)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,16 +32,8 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request)
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
|
return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) error {
|
func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) error {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/roar"
|
"github.com/portainer/portainer/api/roar"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @id EdgeGroupInspect
|
// @id EdgeGroupInspect
|
||||||
|
@ -36,7 +37,7 @@ func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request)
|
||||||
|
|
||||||
edgeGroup.Endpoints = edgeGroup.EndpointIDs.ToSlice()
|
edgeGroup.Endpoints = edgeGroup.EndpointIDs.ToSlice()
|
||||||
|
|
||||||
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
|
func getEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
"github.com/portainer/portainer/api/roar"
|
"github.com/portainer/portainer/api/roar"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
type shadowedEdgeGroup struct {
|
type shadowedEdgeGroup struct {
|
||||||
|
@ -44,7 +45,7 @@ func (handler *Handler) edgeGroupList(w http.ResponseWriter, r *http.Request) *h
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, decoratedEdgeGroups, err)
|
return response.TxResponse(w, decoratedEdgeGroups, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEdgeGroupList(tx dataservices.DataStoreTx) ([]decoratedEdgeGroup, error) {
|
func getEdgeGroupList(tx dataservices.DataStoreTx) ([]decoratedEdgeGroup, error) {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/slicesx"
|
"github.com/portainer/portainer/api/slicesx"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
type edgeGroupUpdatePayload struct {
|
type edgeGroupUpdatePayload struct {
|
||||||
|
@ -158,7 +159,7 @@ func (handler *Handler) edgeGroupUpdate(w http.ResponseWriter, r *http.Request)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
return response.TxResponse(w, shadowedEdgeGroup{EdgeGroup: *edgeGroup}, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) updateEndpointStacks(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, edgeGroups []portainer.EdgeGroup, edgeStacks []portainer.EdgeStack) error {
|
func (handler *Handler) updateEndpointStacks(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, edgeGroups []portainer.EdgeGroup, edgeStacks []portainer.EdgeStack) error {
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package edgegroups
|
package edgegroups
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
@ -38,16 +36,3 @@ func NewHandler(bouncer security.BouncerService) *Handler {
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
|
|
||||||
if err != nil {
|
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, r)
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
"github.com/portainer/portainer/pkg/validate"
|
"github.com/portainer/portainer/pkg/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,19 +86,18 @@ func (payload *edgeJobCreateFromFileContentPayload) Validate(r *http.Request) er
|
||||||
// @router /edge_jobs/create/string [post]
|
// @router /edge_jobs/create/string [post]
|
||||||
func (handler *Handler) createEdgeJobFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
func (handler *Handler) createEdgeJobFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
var payload edgeJobCreateFromFileContentPayload
|
var payload edgeJobCreateFromFileContentPayload
|
||||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return httperror.BadRequest("Invalid request payload", err)
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var edgeJob *portainer.EdgeJob
|
var edgeJob *portainer.EdgeJob
|
||||||
|
var err error
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent))
|
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent))
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, edgeJob, err)
|
return response.TxResponse(w, edgeJob, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createEdgeJob(tx dataservices.DataStoreTx, payload *edgeJobBasePayload, fileContent []byte) (*portainer.EdgeJob, error) {
|
func (handler *Handler) createEdgeJob(tx dataservices.DataStoreTx, payload *edgeJobBasePayload, fileContent []byte) (*portainer.EdgeJob, error) {
|
||||||
|
@ -191,19 +191,18 @@ func (payload *edgeJobCreateFromFilePayload) Validate(r *http.Request) error {
|
||||||
// @router /edge_jobs/create/file [post]
|
// @router /edge_jobs/create/file [post]
|
||||||
func (handler *Handler) createEdgeJobFromFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
func (handler *Handler) createEdgeJobFromFile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
payload := &edgeJobCreateFromFilePayload{}
|
payload := &edgeJobCreateFromFilePayload{}
|
||||||
err := payload.Validate(r)
|
if err := payload.Validate(r); err != nil {
|
||||||
if err != nil {
|
|
||||||
return httperror.BadRequest("Invalid request payload", err)
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var edgeJob *portainer.EdgeJob
|
var edgeJob *portainer.EdgeJob
|
||||||
|
var err error
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File)
|
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, edgeJob, err)
|
return response.TxResponse(w, edgeJob, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createEdgeJobObjectFromPayload(tx dataservices.DataStoreTx, payload *edgeJobBasePayload) *portainer.EdgeJob {
|
func (handler *Handler) createEdgeJobObjectFromPayload(tx dataservices.DataStoreTx, payload *edgeJobBasePayload) *portainer.EdgeJob {
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package edgejobs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
|
"github.com/portainer/portainer/api/datastore"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockFileService struct {
|
||||||
|
mock.Mock
|
||||||
|
portainer.FileService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFileService) StoreEdgeJobFileFromBytes(id string, file []byte) (string, error) {
|
||||||
|
args := m.Called(id, file)
|
||||||
|
return args.String(0), args.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFileService) GetEdgeJobFolder(id string) string {
|
||||||
|
args := m.Called(id)
|
||||||
|
|
||||||
|
return args.String(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFileService) RemoveDirectory(path string) error {
|
||||||
|
args := m.Called(path)
|
||||||
|
|
||||||
|
return args.Error(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initStore(t *testing.T) *datastore.Store {
|
||||||
|
_, store := datastore.MustNewTestStore(t, true, true)
|
||||||
|
require.NotNil(t, store)
|
||||||
|
|
||||||
|
require.NoError(t, store.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
|
require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{
|
||||||
|
ID: 1,
|
||||||
|
Name: "endpoint-1",
|
||||||
|
EdgeID: "edge-id-1",
|
||||||
|
GroupID: 1,
|
||||||
|
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||||
|
UserTrusted: true,
|
||||||
|
}))
|
||||||
|
|
||||||
|
require.NoError(t, tx.Endpoint().Create(&portainer.Endpoint{
|
||||||
|
ID: 2,
|
||||||
|
Name: "endpoint-2",
|
||||||
|
EdgeID: "edge-id-2",
|
||||||
|
GroupID: 1,
|
||||||
|
Type: portainer.EdgeAgentOnDockerEnvironment,
|
||||||
|
UserTrusted: false,
|
||||||
|
}))
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_edgeJobCreate_StringMethod_Success(t *testing.T) {
|
||||||
|
store := initStore(t)
|
||||||
|
|
||||||
|
fileService := &mockFileService{}
|
||||||
|
fileService.On("StoreEdgeJobFileFromBytes", mock.Anything, mock.Anything).Return("testfile.txt", nil)
|
||||||
|
|
||||||
|
handler := &Handler{
|
||||||
|
DataStore: store,
|
||||||
|
FileService: fileService,
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := edgeJobCreateFromFileContentPayload{
|
||||||
|
edgeJobBasePayload: edgeJobBasePayload{
|
||||||
|
Name: "testjob",
|
||||||
|
CronExpression: "* * * * *",
|
||||||
|
Endpoints: []portainer.EndpointID{1, 2},
|
||||||
|
},
|
||||||
|
FileContent: "echo hello",
|
||||||
|
}
|
||||||
|
|
||||||
|
body, _ := json.Marshal(payload)
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/edge_jobs/create/string", bytes.NewReader(body))
|
||||||
|
req = mux.SetURLVars(req, map[string]string{"method": "string"})
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
// Call handler
|
||||||
|
errh := handler.edgeJobCreate(w, req)
|
||||||
|
require.Nil(t, errh)
|
||||||
|
require.Equal(t, http.StatusOK, w.Result().StatusCode)
|
||||||
|
|
||||||
|
// Get edge job ID from response
|
||||||
|
var resp struct {
|
||||||
|
ID int `json:"Id"`
|
||||||
|
}
|
||||||
|
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
|
||||||
|
|
||||||
|
edgeJob, err := store.EdgeJob().Read(portainer.EdgeJobID(resp.ID))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, edgeJob.Endpoints, 2)
|
||||||
|
require.Contains(t, edgeJob.Endpoints, portainer.EndpointID(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_edgeJobCreate_FileMethod_Success(t *testing.T) {
|
||||||
|
store := initStore(t)
|
||||||
|
|
||||||
|
fileService := &mockFileService{}
|
||||||
|
fileService.On("StoreEdgeJobFileFromBytes", mock.Anything, mock.Anything).Return("testfile.txt", nil)
|
||||||
|
|
||||||
|
handler := &Handler{
|
||||||
|
DataStore: store,
|
||||||
|
FileService: fileService,
|
||||||
|
}
|
||||||
|
|
||||||
|
var body bytes.Buffer
|
||||||
|
writer := multipart.NewWriter(&body)
|
||||||
|
require.NoError(t, writer.WriteField("Name", "testjob"))
|
||||||
|
require.NoError(t, writer.WriteField("CronExpression", "* * * * *"))
|
||||||
|
require.NoError(t, writer.WriteField("Endpoints", "[1,2]"))
|
||||||
|
|
||||||
|
fileWriter, err := writer.CreateFormFile("file", "test.txt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = io.Copy(fileWriter, strings.NewReader("echo hello"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, writer.Close())
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/edge_jobs/create/file", &body)
|
||||||
|
req = mux.SetURLVars(req, map[string]string{"method": "file"})
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
handlerErr := handler.edgeJobCreate(w, req)
|
||||||
|
require.Nil(t, handlerErr)
|
||||||
|
require.Equal(t, http.StatusOK, w.Result().StatusCode)
|
||||||
|
|
||||||
|
var resp struct {
|
||||||
|
ID int `json:"Id"`
|
||||||
|
}
|
||||||
|
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
|
||||||
|
|
||||||
|
edgeJob, err := store.EdgeJob().Read(portainer.EdgeJobID(resp.ID))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Len(t, edgeJob.Endpoints, 2)
|
||||||
|
require.Contains(t, edgeJob.Endpoints, portainer.EndpointID(1))
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package edgejobs
|
package edgejobs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"maps"
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -35,18 +34,11 @@ func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *h
|
||||||
return httperror.BadRequest("Invalid Edge job identifier route variable", err)
|
return httperror.BadRequest("Invalid Edge job identifier route variable", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID))
|
return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID))
|
||||||
}); err != nil {
|
})
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) deleteEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) error {
|
func (handler *Handler) deleteEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) error {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package edgejobs
|
package edgejobs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -54,7 +53,7 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error {
|
updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error {
|
||||||
mutationFn(edgeJob, endpointID, endpointsFromGroups)
|
mutationFn(edgeJob, endpointID, endpointsFromGroups)
|
||||||
|
|
||||||
|
@ -62,16 +61,9 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
|
return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
|
||||||
}); err != nil {
|
})
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, endpointID portainer.EndpointID, updateEdgeJob func(*portainer.EdgeJob, portainer.EndpointID, []portainer.EndpointID) error) error {
|
func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, endpointID portainer.EndpointID, updateEdgeJob func(*portainer.EdgeJob, portainer.EndpointID, []portainer.EndpointID) error) error {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package edgejobs
|
package edgejobs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque
|
||||||
return httperror.BadRequest("Invalid Task identifier route variable", err)
|
return httperror.BadRequest("Invalid Task identifier route variable", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
edgeJob, err := tx.EdgeJob().Read(portainer.EdgeJobID(edgeJobID))
|
edgeJob, err := tx.EdgeJob().Read(portainer.EdgeJobID(edgeJobID))
|
||||||
if tx.IsErrObjectNotFound(err) {
|
if tx.IsErrObjectNotFound(err) {
|
||||||
return httperror.NotFound("Unable to find an Edge job with the specified identifier inside the database", err)
|
return httperror.NotFound("Unable to find an Edge job with the specified identifier inside the database", err)
|
||||||
|
@ -81,14 +80,7 @@ func (handler *Handler) edgeJobTasksCollect(w http.ResponseWriter, r *http.Reque
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
})
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/internal/edge"
|
"github.com/portainer/portainer/api/internal/edge"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
type taskContainer struct {
|
type taskContainer struct {
|
||||||
|
@ -49,31 +50,33 @@ func (handler *Handler) edgeJobTasksList(w http.ResponseWriter, r *http.Request)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
results := filters.SearchOrderAndPaginate(tasks, params, filters.Config[*taskContainer]{
|
return response.TxFuncResponse(err, func() *httperror.HandlerError {
|
||||||
SearchAccessors: []filters.SearchAccessor[*taskContainer]{
|
results := filters.SearchOrderAndPaginate(tasks, params, filters.Config[*taskContainer]{
|
||||||
func(tc *taskContainer) (string, error) {
|
SearchAccessors: []filters.SearchAccessor[*taskContainer]{
|
||||||
switch tc.LogsStatus {
|
func(tc *taskContainer) (string, error) {
|
||||||
case portainer.EdgeJobLogsStatusPending:
|
switch tc.LogsStatus {
|
||||||
return "pending", nil
|
case portainer.EdgeJobLogsStatusPending:
|
||||||
case 0, portainer.EdgeJobLogsStatusIdle:
|
return "pending", nil
|
||||||
return "idle", nil
|
case 0, portainer.EdgeJobLogsStatusIdle:
|
||||||
case portainer.EdgeJobLogsStatusCollected:
|
return "idle", nil
|
||||||
return "collected", nil
|
case portainer.EdgeJobLogsStatusCollected:
|
||||||
}
|
return "collected", nil
|
||||||
return "", errors.New("unknown state")
|
}
|
||||||
|
return "", errors.New("unknown state")
|
||||||
|
},
|
||||||
|
func(tc *taskContainer) (string, error) {
|
||||||
|
return tc.EndpointName, nil
|
||||||
|
},
|
||||||
},
|
},
|
||||||
func(tc *taskContainer) (string, error) {
|
SortBindings: []filters.SortBinding[*taskContainer]{
|
||||||
return tc.EndpointName, nil
|
{Key: "EndpointName", Fn: func(a, b *taskContainer) int { return strings.Compare(a.EndpointName, b.EndpointName) }},
|
||||||
},
|
},
|
||||||
},
|
})
|
||||||
SortBindings: []filters.SortBinding[*taskContainer]{
|
|
||||||
{Key: "EndpointName", Fn: func(a, b *taskContainer) int { return strings.Compare(a.EndpointName, b.EndpointName) }},
|
filters.ApplyFilterResultsHeaders(&w, results)
|
||||||
},
|
|
||||||
|
return response.JSON(w, results.Items)
|
||||||
})
|
})
|
||||||
|
|
||||||
filters.ApplyFilterResultsHeaders(&w, results)
|
|
||||||
|
|
||||||
return txResponse(w, results.Items, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listEdgeJobTasks(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) ([]*taskContainer, error) {
|
func listEdgeJobTasks(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID) ([]*taskContainer, error) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
"github.com/portainer/portainer/pkg/validate"
|
"github.com/portainer/portainer/pkg/validate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ func (handler *Handler) edgeJobUpdate(w http.ResponseWriter, r *http.Request) *h
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, edgeJob, err)
|
return response.TxResponse(w, edgeJob, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) updateEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, payload edgeJobUpdatePayload) (*portainer.EdgeJob, error) {
|
func (handler *Handler) updateEdgeJob(tx dataservices.DataStoreTx, edgeJobID portainer.EdgeJobID, payload edgeJobUpdatePayload) (*portainer.EdgeJob, error) {
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package edgejobs
|
package edgejobs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
@ -60,16 +58,3 @@ func convertEndpointsToMetaObject(endpoints []portainer.EndpointID) map[portaine
|
||||||
|
|
||||||
return endpointsMap
|
return endpointsMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
|
|
||||||
if err != nil {
|
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, r)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package edgestacks
|
package edgestacks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
@ -30,18 +29,11 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request)
|
||||||
return httperror.BadRequest("Invalid edge stack identifier route variable", err)
|
return httperror.BadRequest("Invalid edge stack identifier route variable", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID))
|
return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID))
|
||||||
}); err != nil {
|
})
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) deleteEdgeStack(tx dataservices.DataStoreTx, edgeStackID portainer.EdgeStackID) error {
|
func (handler *Handler) deleteEdgeStack(tx dataservices.DataStoreTx, edgeStackID portainer.EdgeStackID) error {
|
||||||
|
|
|
@ -96,12 +96,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var httpErr *httperror.HandlerError
|
return response.TxErrorResponse(err)
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok, _ := strconv.ParseBool(r.Header.Get("X-Portainer-No-Body")); ok {
|
if ok, _ := strconv.ParseBool(r.Header.Get("X-Portainer-No-Body")); ok {
|
||||||
|
|
|
@ -66,12 +66,7 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
|
||||||
stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload)
|
stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload)
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var httpErr *httperror.HandlerError
|
return response.TxErrorResponse(err)
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := fillEdgeStackStatus(handler.DataStore, stack); err != nil {
|
if err := fillEdgeStackStatus(handler.DataStore, stack); err != nil {
|
||||||
|
|
|
@ -49,26 +49,18 @@ func (payload *endpointGroupCreatePayload) Validate(r *http.Request) error {
|
||||||
// @router /endpoint_groups [post]
|
// @router /endpoint_groups [post]
|
||||||
func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
var payload endpointGroupCreatePayload
|
var payload endpointGroupCreatePayload
|
||||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return httperror.BadRequest("Invalid request payload", err)
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var endpointGroup *portainer.EndpointGroup
|
var endpointGroup *portainer.EndpointGroup
|
||||||
|
var err error
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
endpointGroup, err = handler.createEndpointGroup(tx, payload)
|
endpointGroup, err = handler.createEndpointGroup(tx, payload)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxResponse(w, endpointGroup, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, endpointGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createEndpointGroup(tx dataservices.DataStoreTx, payload endpointGroupCreatePayload) (*portainer.EndpointGroup, error) {
|
func (handler *Handler) createEndpointGroup(tx dataservices.DataStoreTx, payload endpointGroupCreatePayload) (*portainer.EndpointGroup, error) {
|
||||||
|
|
|
@ -37,16 +37,8 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID))
|
return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) deleteEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID) error {
|
func (handler *Handler) deleteEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID) error {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package endpointgroups
|
package endpointgroups
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
@ -39,16 +38,8 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http.
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
|
return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) addEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {
|
func (handler *Handler) addEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package endpointgroups
|
package endpointgroups
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
@ -38,16 +37,8 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
|
return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) removeEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {
|
func (handler *Handler) removeEndpoint(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, endpointID portainer.EndpointID) error {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package endpointgroups
|
package endpointgroups
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
@ -61,20 +60,12 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
|
||||||
|
|
||||||
var endpointGroup *portainer.EndpointGroup
|
var endpointGroup *portainer.EndpointGroup
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
|
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxResponse(w, endpointGroup, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, endpointGroup)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, payload endpointGroupUpdatePayload) (*portainer.EndpointGroup, error) {
|
func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpointGroupID portainer.EndpointGroupID, payload endpointGroupUpdatePayload) (*portainer.EndpointGroup, error) {
|
||||||
|
|
|
@ -62,18 +62,11 @@ func (handler *Handler) endpointDelete(w http.ResponseWriter, r *http.Request) *
|
||||||
return httperror.BadRequest("Invalid boolean query parameter", err)
|
return httperror.BadRequest("Invalid boolean query parameter", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.deleteEndpoint(tx, portainer.EndpointID(endpointID), deleteCluster)
|
return handler.deleteEndpoint(tx, portainer.EndpointID(endpointID), deleteCluster)
|
||||||
}); err != nil {
|
})
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @id EndpointDeleteBatch
|
// @id EndpointDeleteBatch
|
||||||
|
|
|
@ -35,19 +35,12 @@ func (handler *Handler) endpointRegistriesList(w http.ResponseWriter, r *http.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
var registries []portainer.Registry
|
var registries []portainer.Registry
|
||||||
if err := handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
|
||||||
registries, err = handler.listRegistries(tx, r, portainer.EndpointID(endpointID))
|
registries, err = handler.listRegistries(tx, r, portainer.EndpointID(endpointID))
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxResponse(w, registries, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, registries)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) listRegistries(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID) ([]portainer.Registry, error) {
|
func (handler *Handler) listRegistries(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID) ([]portainer.Registry, error) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package endpoints
|
package endpoints
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
portainer "github.com/portainer/portainer/api"
|
portainer "github.com/portainer/portainer/api"
|
||||||
|
@ -53,16 +52,8 @@ func (handler *Handler) endpointRegistryAccess(w http.ResponseWriter, r *http.Re
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return handler.updateRegistryAccess(tx, r, portainer.EndpointID(endpointID), portainer.RegistryID(registryID))
|
return handler.updateRegistryAccess(tx, r, portainer.EndpointID(endpointID), portainer.RegistryID(registryID))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) updateRegistryAccess(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID, registryID portainer.RegistryID) error {
|
func (handler *Handler) updateRegistryAccess(tx dataservices.DataStoreTx, r *http.Request, endpointID portainer.EndpointID, registryID portainer.RegistryID) error {
|
||||||
|
|
|
@ -128,12 +128,7 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
var httpErr *httperror.HandlerError
|
return response.TxErrorResponse(err)
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hideFields(settings)
|
hideFields(settings)
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package tags
|
package tags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
"github.com/portainer/portainer/api/http/security"
|
"github.com/portainer/portainer/api/http/security"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
|
@ -32,16 +30,3 @@ func NewHandler(bouncer security.BouncerService) *Handler {
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
func txResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
|
|
||||||
if err != nil {
|
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, r)
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/portainer/portainer/api/dataservices"
|
"github.com/portainer/portainer/api/dataservices"
|
||||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||||
|
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tagCreatePayload struct {
|
type tagCreatePayload struct {
|
||||||
|
@ -38,18 +39,18 @@ func (payload *tagCreatePayload) Validate(r *http.Request) error {
|
||||||
// @router /tags [post]
|
// @router /tags [post]
|
||||||
func (handler *Handler) tagCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
func (handler *Handler) tagCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||||
var payload tagCreatePayload
|
var payload tagCreatePayload
|
||||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||||
if err != nil {
|
|
||||||
return httperror.BadRequest("Invalid request payload", err)
|
return httperror.BadRequest("Invalid request payload", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag *portainer.Tag
|
var tag *portainer.Tag
|
||||||
|
var err error
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
tag, err = createTag(tx, payload)
|
tag, err = createTag(tx, payload)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
return txResponse(w, tag, err)
|
return response.TxResponse(w, tag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTag(tx dataservices.DataStoreTx, payload tagCreatePayload) (*portainer.Tag, error) {
|
func createTag(tx dataservices.DataStoreTx, payload tagCreatePayload) (*portainer.Tag, error) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package tags
|
package tags
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
@ -37,16 +36,8 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe
|
||||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
return deleteTag(tx, portainer.TagID(id))
|
return deleteTag(tx, portainer.TagID(id))
|
||||||
})
|
})
|
||||||
if err != nil {
|
|
||||||
var handlerError *httperror.HandlerError
|
|
||||||
if errors.As(err, &handlerError) {
|
|
||||||
return handlerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxEmptyResponse(w, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.Empty(w)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func deleteTag(tx dataservices.DataStoreTx, tagID portainer.TagID) error {
|
func deleteTag(tx dataservices.DataStoreTx, tagID portainer.TagID) error {
|
||||||
|
|
|
@ -48,22 +48,13 @@ func (handler *Handler) teamCreate(w http.ResponseWriter, r *http.Request) *http
|
||||||
}
|
}
|
||||||
|
|
||||||
var team *portainer.Team
|
var team *portainer.Team
|
||||||
|
var err error
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
var err error
|
|
||||||
team, err = createTeam(tx, payload)
|
team, err = createTeam(tx, payload)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxResponse(w, team, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, team)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTeam(tx dataservices.DataStoreTx, payload teamCreatePayload) (*portainer.Team, error) {
|
func createTeam(tx dataservices.DataStoreTx, payload teamCreatePayload) (*portainer.Team, error) {
|
||||||
|
|
|
@ -55,22 +55,13 @@ func (handler *Handler) userCreate(w http.ResponseWriter, r *http.Request) *http
|
||||||
}
|
}
|
||||||
|
|
||||||
var user *portainer.User
|
var user *portainer.User
|
||||||
|
var err error
|
||||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||||
var err error
|
|
||||||
user, err = handler.createUser(tx, payload)
|
user, err = handler.createUser(tx, payload)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}); err != nil {
|
})
|
||||||
var httpErr *httperror.HandlerError
|
|
||||||
if errors.As(err, &httpErr) {
|
|
||||||
return httpErr
|
|
||||||
}
|
|
||||||
|
|
||||||
return httperror.InternalServerError("Unexpected error", err)
|
return response.TxResponse(w, user, err)
|
||||||
}
|
|
||||||
|
|
||||||
return response.JSON(w, user)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (handler *Handler) createUser(tx dataservices.DataStoreTx, payload userCreatePayload) (*portainer.User, error) {
|
func (handler *Handler) createUser(tx dataservices.DataStoreTx, payload userCreatePayload) (*portainer.User, error) {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package response
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TxResponse(w http.ResponseWriter, r any, err error) *httperror.HandlerError {
|
||||||
|
return TxFuncResponse(err, func() *httperror.HandlerError { return JSON(w, r) })
|
||||||
|
}
|
||||||
|
|
||||||
|
func TxEmptyResponse(w http.ResponseWriter, err error) *httperror.HandlerError {
|
||||||
|
if err != nil {
|
||||||
|
var handlerError *httperror.HandlerError
|
||||||
|
if errors.As(err, &handlerError) {
|
||||||
|
return handlerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return httperror.InternalServerError("Unexpected error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Empty(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TxFuncResponse(err error, validResponse func() *httperror.HandlerError) *httperror.HandlerError {
|
||||||
|
if err != nil {
|
||||||
|
var handlerError *httperror.HandlerError
|
||||||
|
if errors.As(err, &handlerError) {
|
||||||
|
return handlerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return httperror.InternalServerError("Unexpected error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return validResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TxErrorResponse(err error) *httperror.HandlerError {
|
||||||
|
var handlerError *httperror.HandlerError
|
||||||
|
if errors.As(err, &handlerError) {
|
||||||
|
return handlerError
|
||||||
|
}
|
||||||
|
|
||||||
|
return httperror.InternalServerError("Unexpected error", err)
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package response
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
httperrors "github.com/portainer/portainer/api/http/errors"
|
||||||
|
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTxResponse(t *testing.T) {
|
||||||
|
type sample struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
got := TxResponse(w, sample{Name: "Alice"}, nil)
|
||||||
|
require.Nil(t, got)
|
||||||
|
require.Equal(t, http.StatusOK, w.Result().StatusCode)
|
||||||
|
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
got = TxResponse(w, sample{}, httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusForbidden, got.StatusCode)
|
||||||
|
require.Equal(t, "Access denied to resource", got.Message)
|
||||||
|
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
got = TxResponse(w, sample{}, errors.New("Some error"))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
|
||||||
|
require.Equal(t, "Unexpected error", got.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxEmptyResponse(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
got := TxEmptyResponse(w, nil)
|
||||||
|
require.Nil(t, got)
|
||||||
|
require.Equal(t, http.StatusNoContent, w.Result().StatusCode)
|
||||||
|
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
got = TxEmptyResponse(w, httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusForbidden, got.StatusCode)
|
||||||
|
require.Equal(t, "Access denied to resource", got.Message)
|
||||||
|
|
||||||
|
w = httptest.NewRecorder()
|
||||||
|
got = TxEmptyResponse(w, errors.New("Some error"))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
|
||||||
|
require.Equal(t, "Unexpected error", got.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxFuncResponse(t *testing.T) {
|
||||||
|
got := TxFuncResponse(nil, func() *httperror.HandlerError { return nil })
|
||||||
|
require.Nil(t, got)
|
||||||
|
|
||||||
|
got = TxFuncResponse(httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied), func() *httperror.HandlerError { return nil })
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusForbidden, got.StatusCode)
|
||||||
|
require.Equal(t, "Access denied to resource", got.Message)
|
||||||
|
|
||||||
|
got = TxFuncResponse(errors.New("Some error"), func() *httperror.HandlerError { return nil })
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
|
||||||
|
require.Equal(t, "Unexpected error", got.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTxErrorResponse(t *testing.T) {
|
||||||
|
got := TxErrorResponse(nil)
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
|
||||||
|
require.Equal(t, "Unexpected error", got.Message)
|
||||||
|
|
||||||
|
got = TxErrorResponse(httperror.Forbidden("Access denied to resource", httperrors.ErrResourceAccessDenied))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusForbidden, got.StatusCode)
|
||||||
|
require.Equal(t, "Access denied to resource", got.Message)
|
||||||
|
|
||||||
|
got = TxErrorResponse(errors.New("Some error"))
|
||||||
|
require.NotNil(t, got)
|
||||||
|
require.Equal(t, http.StatusInternalServerError, got.StatusCode)
|
||||||
|
require.Equal(t, "Unexpected error", got.Message)
|
||||||
|
}
|
Loading…
Reference in New Issue