diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go index 9df787d71..77e6adac1 100644 --- a/api/bolt/datastore.go +++ b/api/bolt/datastore.go @@ -6,7 +6,7 @@ import ( "time" "github.com/boltdb/bolt" - "github.com/portainer/portainer/api" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/bolt/customtemplate" "github.com/portainer/portainer/api/bolt/dockerhub" "github.com/portainer/portainer/api/bolt/edgegroup" @@ -69,6 +69,14 @@ type Store struct { WebhookService *webhook.Service } +func (store *Store) edition() portainer.SoftwareEdition { + edition, err := store.VersionService.Edition() + if err == errors.ErrObjectNotFound { + edition = portainer.PortainerCE + } + return edition +} + // NewStore initializes a new Store and the associated services func NewStore(storePath string, fileService portainer.FileService) (*Store, error) { store := &Store{ @@ -116,6 +124,14 @@ func (store *Store) IsNew() bool { return store.isNew } +// CheckCurrentEdition checks if current edition is community edition +func (store *Store) CheckCurrentEdition() error { + if store.edition() != portainer.PortainerCE { + return errors.ErrWrongDBEdition + } + return nil +} + // MigrateData automatically migrate the data based on the DBVersion. // This process is only triggered on an existing database, not if the database was just created. func (store *Store) MigrateData() error { diff --git a/api/bolt/errors/errors.go b/api/bolt/errors/errors.go index c9a142189..8c171686d 100644 --- a/api/bolt/errors/errors.go +++ b/api/bolt/errors/errors.go @@ -4,4 +4,5 @@ import "errors" var ( ErrObjectNotFound = errors.New("Object not found inside the database") + ErrWrongDBEdition = errors.New("The Portainer database is set for Portainer Business Edition, please follow the instructions in our documention to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/") ) diff --git a/api/bolt/version/version.go b/api/bolt/version/version.go index eca755a57..f879d2759 100644 --- a/api/bolt/version/version.go +++ b/api/bolt/version/version.go @@ -4,6 +4,7 @@ import ( "strconv" "github.com/boltdb/bolt" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/bolt/errors" "github.com/portainer/portainer/api/bolt/internal" ) @@ -13,6 +14,7 @@ const ( BucketName = "version" versionKey = "DB_VERSION" instanceKey = "INSTANCE_ID" + editionKey = "EDITION" ) // Service represents a service to manage stored versions. @@ -56,6 +58,21 @@ func (service *Service) DBVersion() (int, error) { return strconv.Atoi(string(data)) } +// Edition retrieves the stored portainer edition. +func (service *Service) Edition() (portainer.SoftwareEdition, error) { + editionData, err := service.getKey(editionKey) + if err != nil { + return 0, err + } + + edition, err := strconv.Atoi(string(editionData)) + if err != nil { + return 0, err + } + + return portainer.SoftwareEdition(edition), nil +} + // StoreDBVersion store the database version. func (service *Service) StoreDBVersion(version int) error { return service.db.Update(func(tx *bolt.Tx) error { @@ -99,3 +116,36 @@ func (service *Service) StoreInstanceID(ID string) error { return bucket.Put([]byte(instanceKey), data) }) } + +func (service *Service) getKey(key string) ([]byte, error) { + var data []byte + + err := service.db.View(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(BucketName)) + + value := bucket.Get([]byte(key)) + if value == nil { + return errors.ErrObjectNotFound + } + + data = make([]byte, len(value)) + copy(data, value) + + return nil + }) + + if err != nil { + return nil, err + } + + return data, nil +} + +func (service *Service) setKey(key string, value string) error { + return service.db.Update(func(tx *bolt.Tx) error { + bucket := tx.Bucket([]byte(BucketName)) + + data := []byte(value) + return bucket.Put([]byte(key), data) + }) +} diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index d8ddf0cdd..d7317b829 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -375,6 +375,10 @@ func main() { dataStore := initDataStore(*flags.Data, fileService) defer dataStore.Close() + if err := dataStore.CheckCurrentEdition(); err != nil { + log.Fatal(err) + } + jwtService, err := initJWTService(dataStore) if err != nil { log.Fatal(err) diff --git a/api/portainer.go b/api/portainer.go index 5eccbcbda..e2f1d7260 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -559,6 +559,9 @@ type ( // SnapshotJob represents a scheduled job that can create endpoint snapshots SnapshotJob struct{} + // SoftwareEdition represents an edition of Portainer + SoftwareEdition int + // Stack represents a Docker stack created via docker stack deploy Stack struct { ID StackID `json:"Id"` @@ -822,6 +825,7 @@ type ( Close() error IsNew() bool MigrateData() error + CheckCurrentEdition() error DockerHub() DockerHubService CustomTemplate() CustomTemplateService @@ -1122,8 +1126,9 @@ type ( // VersionService represents a service for managing version data VersionService interface { DBVersion() (int, error) - StoreDBVersion(version int) error + Edition() (SoftwareEdition, error) InstanceID() (string, error) + StoreDBVersion(version int) error StoreInstanceID(ID string) error } @@ -1268,6 +1273,16 @@ const ( TeamMember ) +const ( + _ SoftwareEdition = iota + // PortainerCE represents the community edition of Portainer + PortainerCE + // PortainerBE represents the business edition of Portainer + PortainerBE + // PortainerEE represents the business edition of Portainer + PortainerEE +) + const ( _ RegistryType = iota // QuayRegistry represents a Quay.io registry