mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
chore(fdo): remove FDO code EE-7235 (#11981)
This commit is contained in:
parent
1a3db327c7
commit
19fa40286a
57 changed files with 3 additions and 2609 deletions
|
@ -1,43 +0,0 @@
|
|||
package fdoprofile
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "fdo_profiles"
|
||||
|
||||
// Service represents a service for managingFDO Profiles data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID]
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new FDO Profile and saves it.
|
||||
func (service *Service) Create(FDOProfile *portainer.FDOProfile) error {
|
||||
return service.Connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(FDOProfile.ID),
|
||||
FDOProfile,
|
||||
)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a FDO Profile.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.Connection.GetNextIdentifier(BucketName)
|
||||
}
|
|
@ -15,7 +15,6 @@ type (
|
|||
Endpoint() EndpointService
|
||||
EndpointGroup() EndpointGroupService
|
||||
EndpointRelation() EndpointRelationService
|
||||
FDOProfile() FDOProfileService
|
||||
HelmUserRepository() HelmUserRepositoryService
|
||||
Registry() RegistryService
|
||||
ResourceControl() ResourceControlService
|
||||
|
@ -120,12 +119,6 @@ type (
|
|||
BucketName() string
|
||||
}
|
||||
|
||||
// FDOProfileService represents a service to manage FDO Profiles
|
||||
FDOProfileService interface {
|
||||
BaseCRUD[portainer.FDOProfile, portainer.FDOProfileID]
|
||||
GetNextIdentifier() int
|
||||
}
|
||||
|
||||
// HelmUserRepositoryService represents a service to manage HelmUserRepositories
|
||||
HelmUserRepositoryService interface {
|
||||
BaseCRUD[portainer.HelmUserRepository, portainer.HelmUserRepositoryID]
|
||||
|
|
|
@ -13,7 +13,6 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices/endpointgroup"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointrelation"
|
||||
"github.com/portainer/portainer/api/dataservices/extension"
|
||||
"github.com/portainer/portainer/api/dataservices/fdoprofile"
|
||||
"github.com/portainer/portainer/api/dataservices/pendingactions"
|
||||
"github.com/portainer/portainer/api/dataservices/registry"
|
||||
"github.com/portainer/portainer/api/dataservices/resourcecontrol"
|
||||
|
@ -41,7 +40,6 @@ type (
|
|||
endpointService *endpoint.Service
|
||||
endpointRelationService *endpointrelation.Service
|
||||
extensionService *extension.Service
|
||||
fdoProfilesService *fdoprofile.Service
|
||||
registryService *registry.Service
|
||||
resourceControlService *resourcecontrol.Service
|
||||
roleService *role.Service
|
||||
|
@ -69,7 +67,6 @@ type (
|
|||
EndpointService *endpoint.Service
|
||||
EndpointRelationService *endpointrelation.Service
|
||||
ExtensionService *extension.Service
|
||||
FDOProfilesService *fdoprofile.Service
|
||||
RegistryService *registry.Service
|
||||
ResourceControlService *resourcecontrol.Service
|
||||
RoleService *role.Service
|
||||
|
@ -99,7 +96,6 @@ func NewMigrator(parameters *MigratorParameters) *Migrator {
|
|||
endpointService: parameters.EndpointService,
|
||||
endpointRelationService: parameters.EndpointRelationService,
|
||||
extensionService: parameters.ExtensionService,
|
||||
fdoProfilesService: parameters.FDOProfilesService,
|
||||
registryService: parameters.RegistryService,
|
||||
resourceControlService: parameters.ResourceControlService,
|
||||
roleService: parameters.RoleService,
|
||||
|
|
|
@ -17,7 +17,6 @@ import (
|
|||
"github.com/portainer/portainer/api/dataservices/endpointgroup"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointrelation"
|
||||
"github.com/portainer/portainer/api/dataservices/extension"
|
||||
"github.com/portainer/portainer/api/dataservices/fdoprofile"
|
||||
"github.com/portainer/portainer/api/dataservices/helmuserrepository"
|
||||
"github.com/portainer/portainer/api/dataservices/pendingactions"
|
||||
"github.com/portainer/portainer/api/dataservices/registry"
|
||||
|
@ -55,7 +54,6 @@ type Store struct {
|
|||
EndpointService *endpoint.Service
|
||||
EndpointRelationService *endpointrelation.Service
|
||||
ExtensionService *extension.Service
|
||||
FDOProfilesService *fdoprofile.Service
|
||||
HelmUserRepositoryService *helmuserrepository.Service
|
||||
RegistryService *registry.Service
|
||||
ResourceControlService *resourcecontrol.Service
|
||||
|
@ -138,12 +136,6 @@ func (store *Store) initServices() error {
|
|||
}
|
||||
store.ExtensionService = extensionService
|
||||
|
||||
fdoProfilesService, err := fdoprofile.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.FDOProfilesService = fdoProfilesService
|
||||
|
||||
helmUserRepositoryService, err := helmuserrepository.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -289,11 +281,6 @@ func (store *Store) EndpointRelation() dataservices.EndpointRelationService {
|
|||
return store.EndpointRelationService
|
||||
}
|
||||
|
||||
// FDOProfile gives access to the FDOProfile data management layer
|
||||
func (store *Store) FDOProfile() dataservices.FDOProfileService {
|
||||
return store.FDOProfilesService
|
||||
}
|
||||
|
||||
// HelmUserRepository access the helm user repository settings
|
||||
func (store *Store) HelmUserRepository() dataservices.HelmUserRepositoryService {
|
||||
return store.HelmUserRepositoryService
|
||||
|
|
|
@ -44,7 +44,6 @@ func (tx *StoreTx) EndpointRelation() dataservices.EndpointRelationService {
|
|||
return tx.store.EndpointRelationService.Tx(tx.tx)
|
||||
}
|
||||
|
||||
func (tx *StoreTx) FDOProfile() dataservices.FDOProfileService { return nil }
|
||||
func (tx *StoreTx) HelmUserRepository() dataservices.HelmUserRepositoryService { return nil }
|
||||
|
||||
func (tx *StoreTx) Registry() dataservices.RegistryService {
|
||||
|
|
|
@ -648,12 +648,6 @@
|
|||
"TemplatesURL": "",
|
||||
"TrustOnFirstConnect": false,
|
||||
"UserSessionTimeout": "8h",
|
||||
"fdoConfiguration": {
|
||||
"enabled": false,
|
||||
"ownerPassword": "",
|
||||
"ownerURL": "",
|
||||
"ownerUsername": ""
|
||||
},
|
||||
"openAMTConfiguration": {
|
||||
"certFileContent": "",
|
||||
"certFileName": "",
|
||||
|
|
|
@ -36,8 +36,6 @@ const (
|
|||
ManifestFileDefaultName = "k8s-deployment.yml"
|
||||
// EdgeStackStorePath represents the subfolder where edge stack files are stored in the file store folder.
|
||||
EdgeStackStorePath = "edge_stacks"
|
||||
// FDOProfileStorePath represents the subfolder where FDO profiles files are stored in the file store folder.
|
||||
FDOProfileStorePath = "fdo_profiles"
|
||||
// PrivateKeyFile represents the name on disk of the file containing the private key.
|
||||
PrivateKeyFile = "portainer.key"
|
||||
// PublicKeyFile represents the name on disk of the file containing the public key.
|
||||
|
@ -1003,23 +1001,6 @@ func MoveDirectory(originalPath, newPath string, overwriteTargetPath bool) error
|
|||
return os.Rename(originalPath, newPath)
|
||||
}
|
||||
|
||||
// StoreFDOProfileFileFromBytes creates a subfolder in the FDOProfileStorePath and stores a new file from bytes.
|
||||
// It returns the path to the folder where the file is stored.
|
||||
func (service *Service) StoreFDOProfileFileFromBytes(fdoProfileIdentifier string, data []byte) (string, error) {
|
||||
err := service.createDirectoryInStore(FDOProfileStorePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
filePath := JoinPaths(FDOProfileStorePath, fdoProfileIdentifier)
|
||||
err = service.createFileInStore(filePath, bytes.NewReader(data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return service.wrapFileStore(filePath), nil
|
||||
}
|
||||
|
||||
func CreateFile(path string, r io.Reader) error {
|
||||
out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/third_party/digest"
|
||||
)
|
||||
|
||||
type FDOOwnerClient struct {
|
||||
OwnerURL string
|
||||
Username string
|
||||
Password string
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
type ServiceInfo struct {
|
||||
Module string
|
||||
Var string
|
||||
Filename string
|
||||
Bytes []byte
|
||||
GUID string
|
||||
Device string
|
||||
Priority int
|
||||
OS string
|
||||
Version string
|
||||
Arch string
|
||||
CRID int
|
||||
Hash string
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) doDigestAuthReq(method, endpoint, contentType string, body io.Reader) (*http.Response, error) {
|
||||
transport := digest.NewTransport(c.Username, c.Password)
|
||||
|
||||
client, err := transport.Client()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client.Timeout = c.Timeout
|
||||
|
||||
e, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(c.OwnerURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u.ResolveReference(e).String(), body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if contentType != "" {
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
}
|
||||
|
||||
return client.Do(req)
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) PostVoucher(ov []byte) (string, error) {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPost,
|
||||
"api/v1/owner/vouchers",
|
||||
"application/cbor",
|
||||
bytes.NewReader(ov),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error {
|
||||
values := url.Values{}
|
||||
values.Set("module", info.Module)
|
||||
values.Set("var", info.Var)
|
||||
values.Set("filename", info.Filename)
|
||||
values.Set("guid", info.GUID)
|
||||
values.Set("device", info.Device)
|
||||
values.Set("priority", strconv.Itoa(info.Priority))
|
||||
values.Set("os", info.OS)
|
||||
values.Set("version", info.Version)
|
||||
values.Set("arch", info.Arch)
|
||||
values.Set("crid", strconv.Itoa(info.CRID))
|
||||
values.Set("hash", info.Hash)
|
||||
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPut,
|
||||
"api/v1/device/svi?"+values.Encode(),
|
||||
"application/octet-stream",
|
||||
strings.NewReader(string(info.Bytes)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodPut,
|
||||
"api/v1/device/svi?"+info.Encode(),
|
||||
"application/octet-stream",
|
||||
strings.NewReader(string(body)),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) GetVouchers() ([]string, error) {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodGet,
|
||||
"api/v1/owner/vouchers",
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
contents, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
guids := strings.FieldsFunc(
|
||||
strings.TrimSpace(string(contents)),
|
||||
func(c rune) bool {
|
||||
return c == ','
|
||||
},
|
||||
)
|
||||
|
||||
return guids, nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) DeleteVoucher(guid string) error {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodDelete,
|
||||
"api/v1/owner/vouchers?id="+guid,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) GetDeviceSVI(guid string) (string, error) {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodGet,
|
||||
"api/v1/device/svi?guid="+guid,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func (c FDOOwnerClient) DeleteDeviceSVI(id string) error {
|
||||
resp, err := c.doDigestAuthReq(
|
||||
http.MethodDelete,
|
||||
"api/v1/device/svi?id="+id,
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(http.StatusText(resp.StatusCode))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -19,7 +19,6 @@ import (
|
|||
"github.com/portainer/portainer/api/http/handler/file"
|
||||
"github.com/portainer/portainer/api/http/handler/gitops"
|
||||
"github.com/portainer/portainer/api/http/handler/helm"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/fdo"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/openamt"
|
||||
"github.com/portainer/portainer/api/http/handler/kubernetes"
|
||||
"github.com/portainer/portainer/api/http/handler/ldap"
|
||||
|
@ -69,7 +68,6 @@ type Handler struct {
|
|||
SettingsHandler *settings.Handler
|
||||
SSLHandler *ssl.Handler
|
||||
OpenAMTHandler *openamt.Handler
|
||||
FDOHandler *fdo.Handler
|
||||
StackHandler *stacks.Handler
|
||||
StorybookHandler *storybook.Handler
|
||||
SystemHandler *system.Handler
|
||||
|
@ -252,8 +250,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
http.StripPrefix("/api", h.SSLHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/open_amt"):
|
||||
http.StripPrefix("/api", h.OpenAMTHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/fdo"):
|
||||
http.StripPrefix("/api", h.FDOHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/teams"):
|
||||
http.StripPrefix("/api", h.TeamHandler).ServeHTTP(w, r)
|
||||
case strings.HasPrefix(r.URL.Path, "/api/team_memberships"):
|
||||
|
|
|
@ -1,201 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
deploymentScriptName = "fdo.sh"
|
||||
)
|
||||
|
||||
type deviceConfigurePayload struct {
|
||||
EdgeID string `json:"edgeID"`
|
||||
EdgeKey string `json:"edgeKey"`
|
||||
Name string `json:"name"`
|
||||
ProfileID int `json:"profile"`
|
||||
}
|
||||
|
||||
func (payload *deviceConfigurePayload) Validate(r *http.Request) error {
|
||||
if payload.EdgeID == "" {
|
||||
return errors.New("invalid edge ID provided")
|
||||
}
|
||||
|
||||
if payload.EdgeKey == "" {
|
||||
return errors.New("invalid edge key provided")
|
||||
}
|
||||
|
||||
if payload.Name == "" {
|
||||
return errors.New("the device name cannot be empty")
|
||||
}
|
||||
|
||||
if payload.ProfileID < 1 {
|
||||
return errors.New("invalid profile id provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// @id fdoConfigureDevice
|
||||
// @summary configures an FDO device
|
||||
// @description configures an FDO device
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @param guid path int true "Guid"
|
||||
// @param body body deviceConfigurePayload true "Device Configuration"
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/configure/{guid} [post]
|
||||
func (handler *Handler) fdoConfigureDevice(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
guid, err := request.RetrieveRouteVariableValue(r, "guid")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: request.RetrieveRouteVariableValue()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: guid not found", err)
|
||||
}
|
||||
|
||||
var payload deviceConfigurePayload
|
||||
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("invalid request payload")
|
||||
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(payload.ProfileID))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
fileContent, err := handler.FileService.GetFileContent(profile.FilePath, "")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: GetFileContent")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: GetFileContent", err)
|
||||
}
|
||||
|
||||
fdoClient, err := handler.newFDOClient()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: newFDOClient()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: newFDOClient()", err)
|
||||
}
|
||||
|
||||
// enable fdo_sys
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"0"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"active"},
|
||||
"bytes": []string{"F5"}, // this is "true" in CBOR
|
||||
}, []byte("")); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err)
|
||||
}
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_edgeid.txt"},
|
||||
}, []byte(payload.EdgeID)); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(edgeid)", err)
|
||||
}
|
||||
|
||||
// write down the edgekey
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_edgekey.txt"},
|
||||
}, []byte(payload.EdgeKey)); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(edgekey)", err)
|
||||
}
|
||||
|
||||
// write down the device name
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_name.txt"},
|
||||
}, []byte(payload.Name)); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw(name)")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw(name)", err)
|
||||
}
|
||||
|
||||
// write down the device GUID - used as the EDGE_DEVICE_GUID too
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{"DEVICE_GUID.txt"},
|
||||
}, []byte(guid)); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err)
|
||||
}
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"1"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"filedesc"},
|
||||
"filename": []string{deploymentScriptName},
|
||||
}, fileContent); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err)
|
||||
}
|
||||
|
||||
b, err := cbor.Marshal([]string{"/bin/sh", deploymentScriptName})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to marshal string to CBOR")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw() failed to encode", err)
|
||||
}
|
||||
|
||||
cborBytes := strings.ToUpper(hex.EncodeToString(b))
|
||||
log.Debug().Str("cbor", cborBytes).Str("string", deploymentScriptName).Msg("converted to CBOR")
|
||||
|
||||
if err = fdoClient.PutDeviceSVIRaw(url.Values{
|
||||
"guid": []string{guid},
|
||||
"priority": []string{"2"},
|
||||
"module": []string{"fdo_sys"},
|
||||
"var": []string{"exec"},
|
||||
"bytes": []string{cborBytes},
|
||||
}, []byte("")); err != nil {
|
||||
log.Error().Err(err).Msg("fdoConfigureDevice: PutDeviceSVIRaw()")
|
||||
|
||||
return httperror.InternalServerError("fdoConfigureDevice: PutDeviceSVIRaw()", err)
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/hostmanagement/fdo"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type fdoConfigurePayload portainer.FDOConfiguration
|
||||
|
||||
func validateURL(u string) error {
|
||||
p, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Scheme != "http" && p.Scheme != "https" {
|
||||
return errors.New("invalid scheme provided, must be 'http' or 'https'")
|
||||
}
|
||||
|
||||
if p.Host == "" {
|
||||
return errors.New("invalid host provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (payload *fdoConfigurePayload) Validate(r *http.Request) error {
|
||||
if payload.Enabled {
|
||||
if err := validateURL(payload.OwnerURL); err != nil {
|
||||
return fmt.Errorf("owner server URL: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (handler *Handler) saveSettings(config portainer.FDOConfiguration) error {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
settings.FDOConfiguration = config
|
||||
|
||||
return handler.DataStore.Settings().UpdateSettings(settings)
|
||||
}
|
||||
|
||||
func (handler *Handler) newFDOClient() (fdo.FDOOwnerClient, error) {
|
||||
settings, err := handler.DataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
return fdo.FDOOwnerClient{}, err
|
||||
}
|
||||
|
||||
return fdo.FDOOwnerClient{
|
||||
OwnerURL: settings.FDOConfiguration.OwnerURL,
|
||||
Username: settings.FDOConfiguration.OwnerUsername,
|
||||
Password: settings.FDOConfiguration.OwnerPassword,
|
||||
Timeout: 5 * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// @id fdoConfigure
|
||||
// @summary Enable Portainer's FDO capabilities
|
||||
// @description Enable Portainer's FDO capabilities
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @accept json
|
||||
// @produce json
|
||||
// @param body body fdoConfigurePayload true "FDO Settings"
|
||||
// @success 204 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo [post]
|
||||
func (handler *Handler) fdoConfigure(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload fdoConfigurePayload
|
||||
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("invalid request payload")
|
||||
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
settings := portainer.FDOConfiguration(payload)
|
||||
if err = handler.saveSettings(settings); err != nil {
|
||||
return httperror.BadRequest("Error saving FDO settings", err)
|
||||
}
|
||||
|
||||
profiles, err := handler.DataStore.FDOProfile().ReadAll()
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Error saving FDO settings", err)
|
||||
}
|
||||
|
||||
if len(profiles) == 0 {
|
||||
err = handler.addDefaultProfile()
|
||||
if err != nil {
|
||||
return httperror.InternalServerError(err.Error(), err)
|
||||
}
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
||||
|
||||
func (handler *Handler) addDefaultProfile() error {
|
||||
profileID := handler.DataStore.FDOProfile().GetNextIdentifier()
|
||||
profile := &portainer.FDOProfile{
|
||||
ID: portainer.FDOProfileID(profileID),
|
||||
Name: "Docker Standalone + Edge",
|
||||
}
|
||||
|
||||
filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(defaultProfileFileContent))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
profile.FilePath = filePath
|
||||
profile.DateCreated = time.Now().Unix()
|
||||
|
||||
return handler.DataStore.FDOProfile().Create(profile)
|
||||
}
|
||||
|
||||
const defaultProfileFileContent = `
|
||||
#!/bin/bash -ex
|
||||
|
||||
env > env.log
|
||||
|
||||
export AGENT_IMAGE=portainer/agent:2.11.0
|
||||
export GUID=$(cat DEVICE_GUID.txt)
|
||||
export DEVICE_NAME=$(cat DEVICE_name.txt)
|
||||
export EDGE_ID=$(cat DEVICE_edgeid.txt)
|
||||
export EDGE_KEY=$(cat DEVICE_edgekey.txt)
|
||||
export AGENTVOLUME=$(pwd)/data/portainer_agent_data/
|
||||
|
||||
mkdir -p ${AGENTVOLUME}
|
||||
|
||||
docker pull ${AGENT_IMAGE}
|
||||
|
||||
docker run -d \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
-v /var/lib/docker/volumes:/var/lib/docker/volumes \
|
||||
-v /:/host \
|
||||
-v ${AGENTVOLUME}:/data \
|
||||
--restart always \
|
||||
-e EDGE=1 \
|
||||
-e EDGE_ID=${EDGE_ID} \
|
||||
-e EDGE_KEY=${EDGE_KEY} \
|
||||
-e CAP_HOST_MANAGEMENT=1 \
|
||||
-e EDGE_INSECURE_POLL=1 \
|
||||
--name portainer_edge_agent \
|
||||
${AGENT_IMAGE}
|
||||
`
|
|
@ -1,40 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
*mux.Router
|
||||
DataStore dataservices.DataStore
|
||||
FileService portainer.FileService
|
||||
}
|
||||
|
||||
func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStore, fileService portainer.FileService) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
DataStore: dataStore,
|
||||
FileService: fileService,
|
||||
}
|
||||
|
||||
h.Handle("/fdo/configure", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoConfigure))).Methods(http.MethodPost)
|
||||
h.Handle("/fdo/list", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoListAll))).Methods(http.MethodGet)
|
||||
h.Handle("/fdo/register", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoRegisterDevice))).Methods(http.MethodPost)
|
||||
h.Handle("/fdo/configure/{guid}", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoConfigureDevice))).Methods(http.MethodPost)
|
||||
|
||||
h.Handle("/fdo/profiles", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoProfileList))).Methods(http.MethodGet)
|
||||
h.Handle("/fdo/profiles", bouncer.AdminAccess(httperror.LoggerHandler(h.createProfile))).Methods(http.MethodPost)
|
||||
h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.fdoProfileInspect))).Methods(http.MethodGet)
|
||||
h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.updateProfile))).Methods(http.MethodPut)
|
||||
h.Handle("/fdo/profiles/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.deleteProfile))).Methods(http.MethodDelete)
|
||||
h.Handle("/fdo/profiles/{id}/duplicate", bouncer.AdminAccess(httperror.LoggerHandler(h.duplicateProfile))).Methods(http.MethodPost)
|
||||
|
||||
return h
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// @id fdoListAll
|
||||
// @summary List all known FDO vouchers
|
||||
// @description List all known FDO vouchers
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/list [get]
|
||||
func (handler *Handler) fdoListAll(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
fdoClient, err := handler.newFDOClient()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("fdoListAll: newFDOClient()")
|
||||
|
||||
return httperror.InternalServerError("fdoRegisterDevice: newFDOClient()", err)
|
||||
}
|
||||
|
||||
// Get all vouchers
|
||||
guids, err := fdoClient.GetVouchers()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("fdoListAll: GetVouchers()")
|
||||
|
||||
return httperror.InternalServerError("fdoListAll: GetVouchers()", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, guids)
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
type createProfileFromFileContentPayload struct {
|
||||
Name string
|
||||
ProfileFileContent string
|
||||
}
|
||||
|
||||
func (payload *createProfileFromFileContentPayload) Validate(r *http.Request) error {
|
||||
if payload.Name == "" {
|
||||
return errors.New("profile name must be provided")
|
||||
}
|
||||
|
||||
if payload.ProfileFileContent == "" {
|
||||
return errors.New("profile file content must be provided")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// @id createProfile
|
||||
// @summary creates a new FDO Profile
|
||||
// @description creates a new FDO Profile
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 409 "Profile name already exists"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/profiles [post]
|
||||
func (handler *Handler) createProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
method, err := request.RetrieveQueryParameter(r, "method", false)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid query parameter: method", err)
|
||||
}
|
||||
|
||||
if method == "editor" {
|
||||
return handler.createFDOProfileFromFileContent(w, r)
|
||||
}
|
||||
|
||||
return httperror.BadRequest("Invalid method. Value must be one of: editor", errors.New("invalid method"))
|
||||
}
|
||||
|
||||
func (handler *Handler) createFDOProfileFromFileContent(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
var payload createProfileFromFileContentPayload
|
||||
|
||||
err := request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
isUnique, err := handler.checkUniqueProfileName(payload.Name, -1)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError(err.Error(), err)
|
||||
}
|
||||
|
||||
if !isUnique {
|
||||
return httperror.Conflict(fmt.Sprintf("A profile with the name '%s' already exists", payload.Name), errors.New("a profile already exists with this name"))
|
||||
}
|
||||
|
||||
profileID := handler.DataStore.FDOProfile().GetNextIdentifier()
|
||||
profile := &portainer.FDOProfile{
|
||||
ID: portainer.FDOProfileID(profileID),
|
||||
Name: payload.Name,
|
||||
}
|
||||
|
||||
filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(payload.ProfileFileContent))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist profile file on disk", err)
|
||||
}
|
||||
|
||||
profile.FilePath = filePath
|
||||
profile.DateCreated = time.Now().Unix()
|
||||
|
||||
err = handler.DataStore.FDOProfile().Create(profile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the profile inside the database", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, profile)
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
// @id deleteProfile
|
||||
// @summary deletes a FDO Profile
|
||||
// @description deletes a FDO Profile
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @param id path int true "FDO Profile identifier"
|
||||
// @produce json
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/profiles/{id} [delete]
|
||||
func (handler *Handler) deleteProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
id, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter"))
|
||||
}
|
||||
|
||||
err = handler.DataStore.FDOProfile().Delete(portainer.FDOProfileID(id))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to delete Profile", err)
|
||||
}
|
||||
|
||||
return response.Empty(w)
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
// @id duplicate
|
||||
// @summary duplicated an existing FDO Profile
|
||||
// @description duplicated an existing FDO Profile
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @param id path int true "FDO Profile identifier"
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/profiles/{id}/duplicate [post]
|
||||
func (handler *Handler) duplicateProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
id, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter"))
|
||||
}
|
||||
|
||||
originalProfile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
fileContent, err := handler.FileService.GetFileContent(originalProfile.FilePath, "")
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve Profile file content", err)
|
||||
}
|
||||
|
||||
profileID := handler.DataStore.FDOProfile().GetNextIdentifier()
|
||||
|
||||
newProfile := &portainer.FDOProfile{
|
||||
ID: portainer.FDOProfileID(profileID),
|
||||
Name: fmt.Sprintf("%s - copy", originalProfile.Name),
|
||||
}
|
||||
|
||||
filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(newProfile.ID)), fileContent)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist profile file on disk", err)
|
||||
}
|
||||
newProfile.FilePath = filePath
|
||||
newProfile.DateCreated = time.Now().Unix()
|
||||
|
||||
err = handler.DataStore.FDOProfile().Create(newProfile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to persist the profile inside the database", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, newProfile)
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
type fdoProfileResponse struct {
|
||||
Name string `json:"name"`
|
||||
FileContent string `json:"fileContent"`
|
||||
}
|
||||
|
||||
// @id fdoProfileInspect
|
||||
// @summary retrieves a given FDO profile information and content
|
||||
// @description retrieves a given FDO profile information and content
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @param id path int true "FDO Profile identifier"
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/profiles/{id} [get]
|
||||
func (handler *Handler) fdoProfileInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
id, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter"))
|
||||
}
|
||||
|
||||
profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve Profile", err)
|
||||
}
|
||||
|
||||
fileContent, err := handler.FileService.GetFileContent(profile.FilePath, "")
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to retrieve Profile file content", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, fdoProfileResponse{
|
||||
Name: profile.Name,
|
||||
FileContent: string(fileContent),
|
||||
})
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
// @id fdoProfileList
|
||||
// @summary retrieves all FDO profiles
|
||||
// @description retrieves all FDO profiles
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @failure 500 "Bad gateway"
|
||||
// @router /fdo/profiles [get]
|
||||
func (handler *Handler) fdoProfileList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
|
||||
profiles, err := handler.DataStore.FDOProfile().ReadAll()
|
||||
if err != nil {
|
||||
return httperror.InternalServerError(err.Error(), err)
|
||||
}
|
||||
|
||||
return response.JSON(w, profiles)
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
)
|
||||
|
||||
// @id updateProfile
|
||||
// @summary updates an existing FDO Profile
|
||||
// @description updates an existing FDO Profile
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @param id path int true "FDO Profile identifier"
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 409 "Profile name already exists"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/profiles/{id} [put]
|
||||
func (handler *Handler) updateProfile(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
id, err := request.RetrieveNumericRouteVariableValue(r, "id")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Bad request", errors.New("missing 'id' query parameter"))
|
||||
}
|
||||
|
||||
var payload createProfileFromFileContentPayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
profile, err := handler.DataStore.FDOProfile().Read(portainer.FDOProfileID(id))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find a FDO Profile with the specified identifier inside the database", err)
|
||||
}
|
||||
|
||||
isUnique, err := handler.checkUniqueProfileName(payload.Name, id)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError(err.Error(), err)
|
||||
}
|
||||
if !isUnique {
|
||||
return httperror.Conflict(fmt.Sprintf("A profile with the name '%s' already exists", payload.Name), errors.New("a profile already exists with this name"))
|
||||
}
|
||||
|
||||
filePath, err := handler.FileService.StoreFDOProfileFileFromBytes(strconv.Itoa(int(profile.ID)), []byte(payload.ProfileFileContent))
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to update profile", err)
|
||||
}
|
||||
profile.FilePath = filePath
|
||||
profile.Name = payload.Name
|
||||
|
||||
err = handler.DataStore.FDOProfile().Update(profile.ID, profile)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to update profile", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, profile)
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package fdo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/request"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type registerDeviceResponse struct {
|
||||
Guid string `json:"guid" example:"c6ea3343-229a-4c07-9096-beef7134e1d3"`
|
||||
}
|
||||
|
||||
// @id fdoRegisterDevice
|
||||
// @summary register an FDO device
|
||||
// @description register an FDO device
|
||||
// @description **Access policy**: administrator
|
||||
// @tags intel
|
||||
// @security jwt
|
||||
// @produce json
|
||||
// @success 200 "Success"
|
||||
// @failure 400 "Invalid request"
|
||||
// @failure 403 "Permission denied to access settings"
|
||||
// @failure 500 "Server error"
|
||||
// @router /fdo/register [post]
|
||||
func (handler *Handler) fdoRegisterDevice(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
|
||||
// Post a voucher
|
||||
ov, filename, err := request.RetrieveMultiPartFormFile(r, "voucher")
|
||||
if err != nil {
|
||||
log.Info().Str("filename", filename).Err(err).Msg("fdoRegisterDevice: readVoucher()")
|
||||
|
||||
return httperror.InternalServerError("fdoRegisterDevice: read Voucher()", err)
|
||||
}
|
||||
|
||||
fdoClient, err := handler.newFDOClient()
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msg("fdoRegisterDevice: newFDOClient()")
|
||||
|
||||
return httperror.InternalServerError("fdoRegisterDevice: newFDOClient()", err)
|
||||
}
|
||||
|
||||
guid, err := fdoClient.PostVoucher(ov)
|
||||
if err != nil {
|
||||
log.Info().Err(err).Msg("fdoRegisterDevice: PostVoucher()")
|
||||
|
||||
return httperror.InternalServerError("fdoRegisterDevice: PostVoucher()", err)
|
||||
}
|
||||
|
||||
return response.JSON(w, registerDeviceResponse{guid})
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package fdo
|
||||
|
||||
func (handler *Handler) checkUniqueProfileName(name string, id int) (bool, error) {
|
||||
profiles, err := handler.DataStore.FDOProfile().ReadAll()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, profile := range profiles {
|
||||
if profile.Name == name && (id == -1 || id != int(profile.ID)) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
|
@ -36,8 +36,6 @@ type publicSettingsResponse struct {
|
|||
// Whether team sync is enabled
|
||||
TeamSync bool `json:"TeamSync" example:"true"`
|
||||
|
||||
// Whether FDO is enabled
|
||||
IsFDOEnabled bool
|
||||
// Whether AMT is enabled
|
||||
IsAMTEnabled bool
|
||||
|
||||
|
@ -86,7 +84,6 @@ func generatePublicSettings(appSettings *portainer.Settings) *publicSettingsResp
|
|||
EnableTelemetry: appSettings.EnableTelemetry,
|
||||
KubeconfigExpiry: appSettings.KubeconfigExpiry,
|
||||
Features: featureflags.FeatureFlags(),
|
||||
IsFDOEnabled: appSettings.EnableEdgeComputeFeatures && appSettings.FDOConfiguration.Enabled,
|
||||
IsAMTEnabled: appSettings.EnableEdgeComputeFeatures && appSettings.OpenAMTConfiguration.Enabled,
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/portainer/portainer/api/http/handler/file"
|
||||
"github.com/portainer/portainer/api/http/handler/gitops"
|
||||
"github.com/portainer/portainer/api/http/handler/helm"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/fdo"
|
||||
"github.com/portainer/portainer/api/http/handler/hostmanagement/openamt"
|
||||
kubehandler "github.com/portainer/portainer/api/http/handler/kubernetes"
|
||||
"github.com/portainer/portainer/api/http/handler/ldap"
|
||||
|
@ -239,8 +238,6 @@ func (server *Server) Start() error {
|
|||
openAMTHandler.DataStore = server.DataStore
|
||||
openAMTHandler.DockerClientFactory = server.DockerClientFactory
|
||||
|
||||
fdoHandler := fdo.NewHandler(requestBouncer, server.DataStore, server.FileService)
|
||||
|
||||
var stackHandler = stacks.NewHandler(requestBouncer)
|
||||
stackHandler.DataStore = server.DataStore
|
||||
stackHandler.DockerClientFactory = server.DockerClientFactory
|
||||
|
@ -316,7 +313,6 @@ func (server *Server) Start() error {
|
|||
KubernetesHandler: kubernetesHandler,
|
||||
MOTDHandler: motdHandler,
|
||||
OpenAMTHandler: openAMTHandler,
|
||||
FDOHandler: fdoHandler,
|
||||
RegistryHandler: registryHandler,
|
||||
ResourceControlHandler: resourceControlHandler,
|
||||
SettingsHandler: settingsHandler,
|
||||
|
|
|
@ -17,7 +17,6 @@ type testDatastore struct {
|
|||
endpoint dataservices.EndpointService
|
||||
endpointGroup dataservices.EndpointGroupService
|
||||
endpointRelation dataservices.EndpointRelationService
|
||||
fdoProfile dataservices.FDOProfileService
|
||||
helmUserRepository dataservices.HelmUserRepositoryService
|
||||
registry dataservices.RegistryService
|
||||
resourceControl dataservices.ResourceControlService
|
||||
|
@ -55,10 +54,6 @@ func (d *testDatastore) EdgeStack() dataservices.EdgeStackService { re
|
|||
func (d *testDatastore) Endpoint() dataservices.EndpointService { return d.endpoint }
|
||||
func (d *testDatastore) EndpointGroup() dataservices.EndpointGroupService { return d.endpointGroup }
|
||||
|
||||
func (d *testDatastore) FDOProfile() dataservices.FDOProfileService {
|
||||
return d.fdoProfile
|
||||
}
|
||||
|
||||
func (d *testDatastore) EndpointRelation() dataservices.EndpointRelationService {
|
||||
return d.endpointRelation
|
||||
}
|
||||
|
|
|
@ -96,24 +96,6 @@ type (
|
|||
// PowerState represents an AMT managed device power state
|
||||
PowerState int
|
||||
|
||||
FDOConfiguration struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
OwnerURL string `json:"ownerURL"`
|
||||
OwnerUsername string `json:"ownerUsername"`
|
||||
OwnerPassword string `json:"ownerPassword"`
|
||||
}
|
||||
|
||||
// FDOProfileID represents a fdo profile id
|
||||
FDOProfileID int
|
||||
|
||||
FDOProfile struct {
|
||||
ID FDOProfileID `json:"id"`
|
||||
Name string `json:"name"`
|
||||
FilePath string `json:"filePath"`
|
||||
NumberDevices int `json:"numberDevices"`
|
||||
DateCreated int64 `json:"dateCreated"`
|
||||
}
|
||||
|
||||
// CLIFlags represents the available flags on the CLI
|
||||
CLIFlags struct {
|
||||
Addr *string
|
||||
|
@ -963,7 +945,6 @@ type (
|
|||
LDAPSettings LDAPSettings `json:"LDAPSettings"`
|
||||
OAuthSettings OAuthSettings `json:"OAuthSettings"`
|
||||
OpenAMTConfiguration OpenAMTConfiguration `json:"openAMTConfiguration"`
|
||||
FDOConfiguration FDOConfiguration `json:"fdoConfiguration"`
|
||||
FeatureFlagSettings map[featureflags.Feature]bool `json:"FeatureFlagSettings"`
|
||||
// The interval in which environment(endpoint) snapshots are created
|
||||
SnapshotInterval string `json:"SnapshotInterval" example:"5m"`
|
||||
|
@ -1456,7 +1437,6 @@ type (
|
|||
StoreSSLCertPair(cert, key []byte) (string, string, error)
|
||||
CopySSLCertPair(certPath, keyPath string) (string, string, error)
|
||||
CopySSLCACert(caCertPath string) (string, error)
|
||||
StoreFDOProfileFileFromBytes(fdoProfileIdentifier string, data []byte) (string, error)
|
||||
StoreMTLSCertificates(cert, caCert, key []byte) (string, string, string, error)
|
||||
GetDefaultChiselPrivateKeyPath() string
|
||||
StoreChiselPrivateKey(privateKey []byte) error
|
||||
|
@ -1646,13 +1626,7 @@ const (
|
|||
)
|
||||
|
||||
// List of supported features
|
||||
const (
|
||||
FeatureFdo = "fdo"
|
||||
)
|
||||
|
||||
var SupportedFeatureFlags = []featureflags.Feature{
|
||||
FeatureFdo,
|
||||
}
|
||||
var SupportedFeatureFlags = []featureflags.Feature{}
|
||||
|
||||
const (
|
||||
_ AuthenticationMethod = iota
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue