mirror of
https://github.com/portainer/portainer.git
synced 2025-07-25 00:09:40 +02:00
feat(global): multi endpoint management (#407)
This commit is contained in:
parent
a08ea134fc
commit
d54d30a7be
47 changed files with 1837 additions and 161 deletions
|
@ -1,8 +1,9 @@
|
|||
package bolt
|
||||
|
||||
import (
|
||||
"github.com/boltdb/bolt"
|
||||
"time"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
)
|
||||
|
||||
// Store defines the implementation of portainer.DataStore using
|
||||
|
@ -12,23 +13,28 @@ type Store struct {
|
|||
Path string
|
||||
|
||||
// Services
|
||||
UserService *UserService
|
||||
UserService *UserService
|
||||
EndpointService *EndpointService
|
||||
|
||||
db *bolt.DB
|
||||
}
|
||||
|
||||
const (
|
||||
databaseFileName = "portainer.db"
|
||||
userBucketName = "users"
|
||||
databaseFileName = "portainer.db"
|
||||
userBucketName = "users"
|
||||
endpointBucketName = "endpoints"
|
||||
activeEndpointBucketName = "activeEndpoint"
|
||||
)
|
||||
|
||||
// NewStore initializes a new Store and the associated services
|
||||
func NewStore(storePath string) *Store {
|
||||
store := &Store{
|
||||
Path: storePath,
|
||||
UserService: &UserService{},
|
||||
Path: storePath,
|
||||
UserService: &UserService{},
|
||||
EndpointService: &EndpointService{},
|
||||
}
|
||||
store.UserService.store = store
|
||||
store.EndpointService.store = store
|
||||
return store
|
||||
}
|
||||
|
||||
|
@ -45,6 +51,14 @@ func (store *Store) Open() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.CreateBucketIfNotExists([]byte(endpointBucketName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.CreateBucketIfNotExists([]byte(activeEndpointBucketName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
162
api/bolt/endpoint_service.go
Normal file
162
api/bolt/endpoint_service.go
Normal file
|
@ -0,0 +1,162 @@
|
|||
package bolt
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer"
|
||||
"github.com/portainer/portainer/bolt/internal"
|
||||
|
||||
"github.com/boltdb/bolt"
|
||||
)
|
||||
|
||||
// EndpointService represents a service for managing users.
|
||||
type EndpointService struct {
|
||||
store *Store
|
||||
}
|
||||
|
||||
const (
|
||||
activeEndpointID = 0
|
||||
)
|
||||
|
||||
// Endpoint returns an endpoint by ID.
|
||||
func (service *EndpointService) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
|
||||
var data []byte
|
||||
err := service.store.db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(endpointBucketName))
|
||||
value := bucket.Get(internal.Itob(int(ID)))
|
||||
if value == nil {
|
||||
return portainer.ErrEndpointNotFound
|
||||
}
|
||||
|
||||
data = make([]byte, len(value))
|
||||
copy(data, value)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var endpoint portainer.Endpoint
|
||||
err = internal.UnmarshalEndpoint(data, &endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &endpoint, nil
|
||||
}
|
||||
|
||||
// Endpoints return an array containing all the endpoints.
|
||||
func (service *EndpointService) Endpoints() ([]portainer.Endpoint, error) {
|
||||
var endpoints []portainer.Endpoint
|
||||
err := service.store.db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(endpointBucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
var endpoint portainer.Endpoint
|
||||
err := internal.UnmarshalEndpoint(v, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
// CreateEndpoint assign an ID to a new endpoint and saves it.
|
||||
func (service *EndpointService) CreateEndpoint(endpoint *portainer.Endpoint) error {
|
||||
return service.store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(endpointBucketName))
|
||||
|
||||
id, _ := bucket.NextSequence()
|
||||
endpoint.ID = portainer.EndpointID(id)
|
||||
|
||||
data, err := internal.MarshalEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bucket.Put(internal.Itob(int(endpoint.ID)), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateEndpoint updates an endpoint.
|
||||
func (service *EndpointService) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
|
||||
data, err := internal.MarshalEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return service.store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(endpointBucketName))
|
||||
err = bucket.Put(internal.Itob(int(ID)), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteEndpoint deletes an endpoint.
|
||||
func (service *EndpointService) DeleteEndpoint(ID portainer.EndpointID) error {
|
||||
return service.store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(endpointBucketName))
|
||||
err := bucket.Delete(internal.Itob(int(ID)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// GetActive returns the active endpoint.
|
||||
func (service *EndpointService) GetActive() (*portainer.Endpoint, error) {
|
||||
var data []byte
|
||||
err := service.store.db.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(activeEndpointBucketName))
|
||||
value := bucket.Get(internal.Itob(activeEndpointID))
|
||||
if value == nil {
|
||||
return portainer.ErrEndpointNotFound
|
||||
}
|
||||
|
||||
data = make([]byte, len(value))
|
||||
copy(data, value)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var endpoint portainer.Endpoint
|
||||
err = internal.UnmarshalEndpoint(data, &endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &endpoint, nil
|
||||
}
|
||||
|
||||
// SetActive saves an endpoint as active.
|
||||
func (service *EndpointService) SetActive(endpoint *portainer.Endpoint) error {
|
||||
return service.store.db.Update(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(activeEndpointBucketName))
|
||||
|
||||
data, err := internal.MarshalEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bucket.Put(internal.Itob(activeEndpointID), data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -3,6 +3,7 @@ package internal
|
|||
import (
|
||||
"github.com/portainer/portainer"
|
||||
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
|
@ -15,3 +16,22 @@ func MarshalUser(user *portainer.User) ([]byte, error) {
|
|||
func UnmarshalUser(data []byte, user *portainer.User) error {
|
||||
return json.Unmarshal(data, user)
|
||||
}
|
||||
|
||||
// MarshalEndpoint encodes an endpoint to binary format.
|
||||
func MarshalEndpoint(endpoint *portainer.Endpoint) ([]byte, error) {
|
||||
return json.Marshal(endpoint)
|
||||
}
|
||||
|
||||
// UnmarshalEndpoint decodes an endpoint from a binary data.
|
||||
func UnmarshalEndpoint(data []byte, endpoint *portainer.Endpoint) error {
|
||||
return json.Unmarshal(data, endpoint)
|
||||
}
|
||||
|
||||
// Itob returns an 8-byte big endian representation of v.
|
||||
// This function is typically used for encoding integer IDs to byte slices
|
||||
// so that they can be used as BoltDB keys.
|
||||
func Itob(v int) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, uint64(v))
|
||||
return b
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue