diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go
index f80fd5921..18f025d73 100644
--- a/api/bolt/datastore.go
+++ b/api/bolt/datastore.go
@@ -26,7 +26,6 @@ import (
"github.com/portainer/portainer/api/bolt/tag"
"github.com/portainer/portainer/api/bolt/team"
"github.com/portainer/portainer/api/bolt/teammembership"
- "github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/api/bolt/version"
"github.com/portainer/portainer/api/bolt/webhook"
@@ -58,7 +57,6 @@ type Store struct {
TagService *tag.Service
TeamMembershipService *teammembership.Service
TeamService *team.Service
- TemplateService *template.Service
TunnelServerService *tunnelserver.Service
UserService *user.Service
VersionService *version.Service
@@ -137,7 +135,6 @@ func (store *Store) MigrateData() error {
StackService: store.StackService,
TagService: store.TagService,
TeamMembershipService: store.TeamMembershipService,
- TemplateService: store.TemplateService,
UserService: store.UserService,
VersionService: store.VersionService,
FileService: store.fileService,
@@ -246,12 +243,6 @@ func (store *Store) initServices() error {
}
store.TeamService = teamService
- templateService, err := template.NewService(store.db)
- if err != nil {
- return err
- }
- store.TemplateService = templateService
-
tunnelServerService, err := tunnelserver.NewService(store.db)
if err != nil {
return err
diff --git a/api/bolt/migrator/migrate_dbversion14.go b/api/bolt/migrator/migrate_dbversion14.go
index 5ec13cd9f..d5a205d4c 100644
--- a/api/bolt/migrator/migrate_dbversion14.go
+++ b/api/bolt/migrator/migrate_dbversion14.go
@@ -1,11 +1,5 @@
package migrator
-import (
- "strings"
-
- "github.com/portainer/portainer/api"
-)
-
func (m *Migrator) updateSettingsToDBVersion15() error {
legacySettings, err := m.settingsService.Settings()
if err != nil {
@@ -17,19 +11,6 @@ func (m *Migrator) updateSettingsToDBVersion15() error {
}
func (m *Migrator) updateTemplatesToVersion15() error {
- legacyTemplates, err := m.templateService.Templates()
- if err != nil {
- return err
- }
-
- for _, template := range legacyTemplates {
- template.Logo = strings.Replace(template.Logo, "https://portainer.io/images", portainer.AssetsServerURL, -1)
-
- err = m.templateService.UpdateTemplate(template.ID, &template)
- if err != nil {
- return err
- }
- }
-
+ // Removed with the entire template management layer, part of https://github.com/portainer/portainer/issues/3707
return nil
}
diff --git a/api/bolt/migrator/migrator.go b/api/bolt/migrator/migrator.go
index 055ffcdda..9fda169b8 100644
--- a/api/bolt/migrator/migrator.go
+++ b/api/bolt/migrator/migrator.go
@@ -15,7 +15,6 @@ import (
"github.com/portainer/portainer/api/bolt/stack"
"github.com/portainer/portainer/api/bolt/tag"
"github.com/portainer/portainer/api/bolt/teammembership"
- "github.com/portainer/portainer/api/bolt/template"
"github.com/portainer/portainer/api/bolt/user"
"github.com/portainer/portainer/api/bolt/version"
)
@@ -37,7 +36,6 @@ type (
stackService *stack.Service
tagService *tag.Service
teamMembershipService *teammembership.Service
- templateService *template.Service
userService *user.Service
versionService *version.Service
fileService portainer.FileService
@@ -59,7 +57,6 @@ type (
StackService *stack.Service
TagService *tag.Service
TeamMembershipService *teammembership.Service
- TemplateService *template.Service
UserService *user.Service
VersionService *version.Service
FileService portainer.FileService
@@ -82,7 +79,6 @@ func NewMigrator(parameters *Parameters) *Migrator {
settingsService: parameters.SettingsService,
tagService: parameters.TagService,
teamMembershipService: parameters.TeamMembershipService,
- templateService: parameters.TemplateService,
stackService: parameters.StackService,
userService: parameters.UserService,
versionService: parameters.VersionService,
diff --git a/api/bolt/template/template.go b/api/bolt/template/template.go
deleted file mode 100644
index e5f7a4cf5..000000000
--- a/api/bolt/template/template.go
+++ /dev/null
@@ -1,95 +0,0 @@
-package template
-
-import (
- "github.com/portainer/portainer/api"
- "github.com/portainer/portainer/api/bolt/internal"
-
- "github.com/boltdb/bolt"
-)
-
-const (
- // BucketName represents the name of the bucket where this service stores data.
- BucketName = "templates"
-)
-
-// Service represents a service for managing endpoint data.
-type Service struct {
- db *bolt.DB
-}
-
-// NewService creates a new instance of a service.
-func NewService(db *bolt.DB) (*Service, error) {
- err := internal.CreateBucket(db, BucketName)
- if err != nil {
- return nil, err
- }
-
- return &Service{
- db: db,
- }, nil
-}
-
-// Templates return an array containing all the templates.
-func (service *Service) Templates() ([]portainer.Template, error) {
- var templates = make([]portainer.Template, 0)
-
- err := service.db.View(func(tx *bolt.Tx) error {
- bucket := tx.Bucket([]byte(BucketName))
-
- cursor := bucket.Cursor()
- for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
- var template portainer.Template
- err := internal.UnmarshalObject(v, &template)
- if err != nil {
- return err
- }
- templates = append(templates, template)
- }
-
- return nil
- })
-
- return templates, err
-}
-
-// Template returns a template by ID.
-func (service *Service) Template(ID portainer.TemplateID) (*portainer.Template, error) {
- var template portainer.Template
- identifier := internal.Itob(int(ID))
-
- err := internal.GetObject(service.db, BucketName, identifier, &template)
- if err != nil {
- return nil, err
- }
-
- return &template, nil
-}
-
-// CreateTemplate creates a new template.
-func (service *Service) CreateTemplate(template *portainer.Template) error {
- return service.db.Update(func(tx *bolt.Tx) error {
- bucket := tx.Bucket([]byte(BucketName))
-
- id, _ := bucket.NextSequence()
- template.ID = portainer.TemplateID(id)
-
- data, err := internal.MarshalObject(template)
- if err != nil {
- return err
- }
-
- return bucket.Put(internal.Itob(int(template.ID)), data)
- })
-}
-
-// UpdateTemplate saves a template.
-func (service *Service) UpdateTemplate(ID portainer.TemplateID, template *portainer.Template) error {
- identifier := internal.Itob(int(ID))
- return internal.UpdateObject(service.db, BucketName, identifier, template)
-}
-
-// DeleteTemplate deletes a template.
-func (service *Service) DeleteTemplate(ID portainer.TemplateID) error {
- identifier := internal.Itob(int(ID))
- return internal.DeleteObject(service.db, BucketName, identifier)
-}
diff --git a/api/cli/cli.go b/api/cli/cli.go
index 775aa9242..91d4b3f47 100644
--- a/api/cli/cli.go
+++ b/api/cli/cli.go
@@ -20,7 +20,6 @@ const (
errInvalidEndpointProtocol = portainer.Error("Invalid endpoint protocol: Portainer only supports unix://, npipe:// or tcp://")
errSocketOrNamedPipeNotFound = portainer.Error("Unable to locate Unix socket or named pipe")
errEndpointsFileNotFound = portainer.Error("Unable to locate external endpoints file")
- errTemplateFileNotFound = portainer.Error("Unable to locate template file on disk")
errInvalidSyncInterval = portainer.Error("Invalid synchronization interval")
errInvalidSnapshotInterval = portainer.Error("Invalid snapshot interval")
errEndpointExcludeExternal = portainer.Error("Cannot use the -H flag mutually with --external-endpoints")
@@ -58,7 +57,6 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
Labels: pairs(kingpin.Flag("hide-label", "Hide containers with a specific label in the UI").Short('l')),
Logo: kingpin.Flag("logo", "URL for the logo displayed in the UI").String(),
Templates: kingpin.Flag("templates", "URL to the templates definitions.").Short('t').String(),
- TemplateFile: kingpin.Flag("template-file", "Path to the App templates definitions on the filesystem (deprecated)").Default(defaultTemplateFile).String(),
}
kingpin.Parse()
@@ -83,12 +81,7 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
return errEndpointExcludeExternal
}
- err := validateTemplateFile(*flags.TemplateFile)
- if err != nil {
- return err
- }
-
- err = validateEndpointURL(*flags.EndpointURL)
+ err := validateEndpointURL(*flags.EndpointURL)
if err != nil {
return err
}
@@ -173,16 +166,6 @@ func validateExternalEndpoints(externalEndpoints string) error {
return nil
}
-func validateTemplateFile(templateFile string) error {
- if _, err := os.Stat(templateFile); err != nil {
- if os.IsNotExist(err) {
- return errTemplateFileNotFound
- }
- return err
- }
- return nil
-}
-
func validateSyncInterval(syncInterval string) error {
if syncInterval != defaultSyncInterval {
_, err := time.ParseDuration(syncInterval)
diff --git a/api/cli/defaults.go b/api/cli/defaults.go
index 504742771..f644e1ff6 100644
--- a/api/cli/defaults.go
+++ b/api/cli/defaults.go
@@ -21,5 +21,4 @@ const (
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
- defaultTemplateFile = "/templates.json"
)
diff --git a/api/cli/defaults_windows.go b/api/cli/defaults_windows.go
index 4e7ce7c3e..b9e13d571 100644
--- a/api/cli/defaults_windows.go
+++ b/api/cli/defaults_windows.go
@@ -19,5 +19,4 @@ const (
defaultSyncInterval = "60s"
defaultSnapshot = "true"
defaultSnapshotInterval = "5m"
- defaultTemplateFile = "/templates.json"
)
diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go
index a606fefd4..39c9bac92 100644
--- a/api/cmd/portainer/main.go
+++ b/api/cmd/portainer/main.go
@@ -1,7 +1,6 @@
package main
import (
- "encoding/json"
"log"
"os"
"strings"
@@ -276,6 +275,7 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL
EnableHostManagementFeatures: false,
SnapshotInterval: *flags.SnapshotInterval,
EdgeAgentCheckinInterval: portainer.DefaultEdgeAgentCheckinIntervalInSeconds,
+ TemplatesURL: portainer.DefaultTemplatesURL,
}
if *flags.Templates != "" {
@@ -296,45 +296,6 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL
return nil
}
-func initTemplates(templateService portainer.TemplateService, fileService portainer.FileService, templateURL, templateFile string) error {
- if templateURL != "" {
- log.Printf("Portainer started with the --templates flag. Using external templates, template management will be disabled.")
- return nil
- }
-
- existingTemplates, err := templateService.Templates()
- if err != nil {
- return err
- }
-
- if len(existingTemplates) != 0 {
- log.Printf("Templates already registered inside the database. Skipping template import.")
- return nil
- }
-
- templatesJSON, err := fileService.GetFileContent(templateFile)
- if err != nil {
- log.Println("Unable to retrieve template definitions via filesystem")
- return err
- }
-
- var templates []portainer.Template
- err = json.Unmarshal(templatesJSON, &templates)
- if err != nil {
- log.Println("Unable to parse templates file. Please review your template definition file.")
- return err
- }
-
- for _, template := range templates {
- err := templateService.CreateTemplate(&template)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
func retrieveFirstEndpointFromDatabase(endpointService portainer.EndpointService) *portainer.Endpoint {
endpoints, err := endpointService.Endpoints()
if err != nil {
@@ -561,11 +522,6 @@ func main() {
composeStackManager := initComposeStackManager(*flags.Data, reverseTunnelService)
- err = initTemplates(store.TemplateService, fileService, *flags.Templates, *flags.TemplateFile)
- if err != nil {
- log.Fatal(err)
- }
-
err = initSettings(store.SettingsService, flags)
if err != nil {
log.Fatal(err)
@@ -674,7 +630,6 @@ func main() {
StackService: store.StackService,
ScheduleService: store.ScheduleService,
TagService: store.TagService,
- TemplateService: store.TemplateService,
WebhookService: store.WebhookService,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
diff --git a/api/go.sum b/api/go.sum
index 80c8f01f4..0d61838ad 100644
--- a/api/go.sum
+++ b/api/go.sum
@@ -175,6 +175,7 @@ github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2 h1:0PfgGLys9yH
github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2/go.mod h1:/wIeGwJOMYc1JplE/OvYMO5korce39HddIfI8VKGyAM=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33 h1:H8HR2dHdBf8HANSkUyVw4o8+4tegGcd+zyKZ3e599II=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33/go.mod h1:Y2TfgviWI4rT2qaOTHr+hq6MdKIE5YjgQAu7qwptTV0=
+github.com/portainer/portainer v0.10.1 h1:I8K345CjGWfUGsVA8c8/gqamwLCC6CIAjxZXSklAFq0=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
diff --git a/api/http/handler/edgetemplates/edgetemplate_list.go b/api/http/handler/edgetemplates/edgetemplate_list.go
index 4b82f19c2..f2ec34a4a 100644
--- a/api/http/handler/edgetemplates/edgetemplate_list.go
+++ b/api/http/handler/edgetemplates/edgetemplate_list.go
@@ -18,7 +18,7 @@ func (handler *Handler) edgeTemplateList(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
- url := portainer.EdgeTemplatesURL
+ url := portainer.DefaultTemplatesURL
if settings.TemplatesURL != "" {
url = settings.TemplatesURL
}
diff --git a/api/http/handler/settings/settings_public.go b/api/http/handler/settings/settings_public.go
index e70125afe..a5cd0ac57 100644
--- a/api/http/handler/settings/settings_public.go
+++ b/api/http/handler/settings/settings_public.go
@@ -17,7 +17,6 @@ type publicSettingsResponse struct {
AllowVolumeBrowserForRegularUsers bool `json:"AllowVolumeBrowserForRegularUsers"`
EnableHostManagementFeatures bool `json:"EnableHostManagementFeatures"`
EnableEdgeComputeFeatures bool `json:"EnableEdgeComputeFeatures"`
- ExternalTemplates bool `json:"ExternalTemplates"`
OAuthLoginURI string `json:"OAuthLoginURI"`
}
@@ -36,7 +35,6 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) *
AllowVolumeBrowserForRegularUsers: settings.AllowVolumeBrowserForRegularUsers,
EnableHostManagementFeatures: settings.EnableHostManagementFeatures,
EnableEdgeComputeFeatures: settings.EnableEdgeComputeFeatures,
- ExternalTemplates: false,
OAuthLoginURI: fmt.Sprintf("%s?response_type=code&client_id=%s&redirect_uri=%s&scope=%s&prompt=login",
settings.OAuthSettings.AuthorizationURI,
settings.OAuthSettings.ClientID,
@@ -44,9 +42,5 @@ func (handler *Handler) settingsPublic(w http.ResponseWriter, r *http.Request) *
settings.OAuthSettings.Scopes),
}
- if settings.TemplatesURL != "" {
- publicSettings.ExternalTemplates = true
- }
-
return response.JSON(w, publicSettings)
}
diff --git a/api/http/handler/templates/handler.go b/api/http/handler/templates/handler.go
index 3eac57b4a..994d27306 100644
--- a/api/http/handler/templates/handler.go
+++ b/api/http/handler/templates/handler.go
@@ -9,14 +9,9 @@ import (
"github.com/portainer/portainer/api/http/security"
)
-const (
- errTemplateManagementDisabled = portainer.Error("Template management is disabled")
-)
-
// Handler represents an HTTP API handler for managing templates.
type Handler struct {
*mux.Router
- TemplateService portainer.TemplateService
SettingsService portainer.SettingsService
}
@@ -28,29 +23,5 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
h.Handle("/templates",
bouncer.RestrictedAccess(httperror.LoggerHandler(h.templateList))).Methods(http.MethodGet)
- h.Handle("/templates",
- bouncer.AdminAccess(h.templateManagementCheck(httperror.LoggerHandler(h.templateCreate)))).Methods(http.MethodPost)
- h.Handle("/templates/{id}",
- bouncer.RestrictedAccess(h.templateManagementCheck(httperror.LoggerHandler(h.templateInspect)))).Methods(http.MethodGet)
- h.Handle("/templates/{id}",
- bouncer.AdminAccess(h.templateManagementCheck(httperror.LoggerHandler(h.templateUpdate)))).Methods(http.MethodPut)
- h.Handle("/templates/{id}",
- bouncer.AdminAccess(h.templateManagementCheck(httperror.LoggerHandler(h.templateDelete)))).Methods(http.MethodDelete)
return h
}
-
-func (handler *Handler) templateManagementCheck(next http.Handler) http.Handler {
- return httperror.LoggerHandler(func(rw http.ResponseWriter, r *http.Request) *httperror.HandlerError {
- settings, err := handler.SettingsService.Settings()
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
- }
-
- if settings.TemplatesURL != "" {
- return &httperror.HandlerError{http.StatusServiceUnavailable, "Portainer is configured to use external templates, template management is disabled", errTemplateManagementDisabled}
- }
-
- next.ServeHTTP(rw, r)
- return nil
- })
-}
diff --git a/api/http/handler/templates/template_create.go b/api/http/handler/templates/template_create.go
deleted file mode 100644
index 6c4d21a25..000000000
--- a/api/http/handler/templates/template_create.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package templates
-
-import (
- "net/http"
-
- "github.com/asaskevich/govalidator"
- httperror "github.com/portainer/libhttp/error"
- "github.com/portainer/libhttp/request"
- "github.com/portainer/libhttp/response"
- "github.com/portainer/portainer/api"
- "github.com/portainer/portainer/api/filesystem"
-)
-
-type templateCreatePayload struct {
- // Mandatory
- Type int
- Title string
- Description string
- AdministratorOnly bool
-
- // Opt stack/container
- Name string
- Logo string
- Note string
- Platform string
- Categories []string
- Env []portainer.TemplateEnv
-
- // Mandatory container
- Image string
-
- // Mandatory stack
- Repository portainer.TemplateRepository
-
- // Opt container
- Registry string
- Command string
- Network string
- Volumes []portainer.TemplateVolume
- Ports []string
- Labels []portainer.Pair
- Privileged bool
- Interactive bool
- RestartPolicy string
- Hostname string
-}
-
-func (payload *templateCreatePayload) Validate(r *http.Request) error {
- if payload.Type == 0 || (payload.Type != 1 && payload.Type != 2 && payload.Type != 3) {
- return portainer.Error("Invalid template type. Valid values are: 1 (container), 2 (Swarm stack template) or 3 (Compose stack template).")
- }
- if govalidator.IsNull(payload.Title) {
- return portainer.Error("Invalid template title")
- }
- if govalidator.IsNull(payload.Description) {
- return portainer.Error("Invalid template description")
- }
-
- if payload.Type == 1 {
- if govalidator.IsNull(payload.Image) {
- return portainer.Error("Invalid template image")
- }
- }
-
- if payload.Type == 2 || payload.Type == 3 {
- if govalidator.IsNull(payload.Repository.URL) {
- return portainer.Error("Invalid template repository URL")
- }
- if govalidator.IsNull(payload.Repository.StackFile) {
- payload.Repository.StackFile = filesystem.ComposeFileDefaultName
- }
- }
-
- return nil
-}
-
-// POST request on /api/templates
-func (handler *Handler) templateCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
- var payload templateCreatePayload
- err := request.DecodeAndValidateJSONPayload(r, &payload)
- if err != nil {
- return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
- }
-
- template := &portainer.Template{
- Type: portainer.TemplateType(payload.Type),
- Title: payload.Title,
- Description: payload.Description,
- AdministratorOnly: payload.AdministratorOnly,
- Name: payload.Name,
- Logo: payload.Logo,
- Note: payload.Note,
- Platform: payload.Platform,
- Categories: payload.Categories,
- Env: payload.Env,
- }
-
- if template.Type == portainer.ContainerTemplate {
- template.Image = payload.Image
- template.Registry = payload.Registry
- template.Command = payload.Command
- template.Network = payload.Network
- template.Volumes = payload.Volumes
- template.Ports = payload.Ports
- template.Labels = payload.Labels
- template.Privileged = payload.Privileged
- template.Interactive = payload.Interactive
- template.RestartPolicy = payload.RestartPolicy
- template.Hostname = payload.Hostname
- }
-
- if template.Type == portainer.SwarmStackTemplate || template.Type == portainer.ComposeStackTemplate {
- template.Repository = payload.Repository
- }
-
- err = handler.TemplateService.CreateTemplate(template)
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist the template inside the database", err}
- }
-
- return response.JSON(w, template)
-}
diff --git a/api/http/handler/templates/template_delete.go b/api/http/handler/templates/template_delete.go
deleted file mode 100644
index cf82e7889..000000000
--- a/api/http/handler/templates/template_delete.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package templates
-
-import (
- "net/http"
-
- httperror "github.com/portainer/libhttp/error"
- "github.com/portainer/libhttp/request"
- "github.com/portainer/libhttp/response"
- "github.com/portainer/portainer/api"
-)
-
-// DELETE request on /api/templates/:id
-func (handler *Handler) templateDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
- id, err := request.RetrieveNumericRouteVariableValue(r, "id")
- if err != nil {
- return &httperror.HandlerError{http.StatusBadRequest, "Invalid template identifier route variable", err}
- }
-
- err = handler.TemplateService.DeleteTemplate(portainer.TemplateID(id))
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to remove the template from the database", err}
- }
-
- return response.Empty(w)
-}
diff --git a/api/http/handler/templates/template_inspect.go b/api/http/handler/templates/template_inspect.go
deleted file mode 100644
index bac836421..000000000
--- a/api/http/handler/templates/template_inspect.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package templates
-
-import (
- "net/http"
-
- httperror "github.com/portainer/libhttp/error"
- "github.com/portainer/libhttp/request"
- "github.com/portainer/libhttp/response"
- "github.com/portainer/portainer/api"
-)
-
-// GET request on /api/templates/:id
-func (handler *Handler) templateInspect(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
- templateID, err := request.RetrieveNumericRouteVariableValue(r, "id")
- if err != nil {
- return &httperror.HandlerError{http.StatusBadRequest, "Invalid template identifier route variable", err}
- }
-
- template, err := handler.TemplateService.Template(portainer.TemplateID(templateID))
- if err == portainer.ErrObjectNotFound {
- return &httperror.HandlerError{http.StatusNotFound, "Unable to find a template with the specified identifier inside the database", err}
- } else if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a template with the specified identifier inside the database", err}
- }
-
- return response.JSON(w, template)
-}
diff --git a/api/http/handler/templates/template_list.go b/api/http/handler/templates/template_list.go
index 41013c8ff..5d86941dd 100644
--- a/api/http/handler/templates/template_list.go
+++ b/api/http/handler/templates/template_list.go
@@ -1,14 +1,10 @@
package templates
import (
- "encoding/json"
+ "io"
"net/http"
httperror "github.com/portainer/libhttp/error"
- "github.com/portainer/libhttp/response"
- "github.com/portainer/portainer/api"
- "github.com/portainer/portainer/api/http/client"
- "github.com/portainer/portainer/api/http/security"
)
// GET request on /api/templates
@@ -18,30 +14,17 @@ func (handler *Handler) templateList(w http.ResponseWriter, r *http.Request) *ht
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve settings from the database", err}
}
- var templates []portainer.Template
- if settings.TemplatesURL == "" {
- templates, err = handler.TemplateService.Templates()
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve templates from the database", err}
- }
- } else {
- var templateData []byte
- templateData, err = client.Get(settings.TemplatesURL, 0)
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve external templates", err}
- }
-
- err = json.Unmarshal(templateData, &templates)
- if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to parse external templates", err}
- }
- }
-
- securityContext, err := security.RetrieveRestrictedRequestContext(r)
+ resp, err := http.Get(settings.TemplatesURL)
if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve info from request context", err}
+ return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve templates via the network", err}
+ }
+ defer resp.Body.Close()
+
+ w.Header().Set("Content-Type", "application/json")
+ _, err = io.Copy(w, resp.Body)
+ if err != nil {
+ return &httperror.HandlerError{http.StatusInternalServerError, "Unable to write templates from templates URL", err}
}
- filteredTemplates := security.FilterTemplates(templates, securityContext)
- return response.JSON(w, filteredTemplates)
+ return nil
}
diff --git a/api/http/handler/templates/template_update.go b/api/http/handler/templates/template_update.go
deleted file mode 100644
index fb0568aa3..000000000
--- a/api/http/handler/templates/template_update.go
+++ /dev/null
@@ -1,164 +0,0 @@
-package templates
-
-import (
- "net/http"
-
- httperror "github.com/portainer/libhttp/error"
- "github.com/portainer/libhttp/request"
- "github.com/portainer/libhttp/response"
- "github.com/portainer/portainer/api"
-)
-
-type templateUpdatePayload struct {
- Title *string
- Description *string
- AdministratorOnly *bool
- Name *string
- Logo *string
- Note *string
- Platform *string
- Categories []string
- Env []portainer.TemplateEnv
- Image *string
- Registry *string
- Repository portainer.TemplateRepository
- Command *string
- Network *string
- Volumes []portainer.TemplateVolume
- Ports []string
- Labels []portainer.Pair
- Privileged *bool
- Interactive *bool
- RestartPolicy *string
- Hostname *string
-}
-
-func (payload *templateUpdatePayload) Validate(r *http.Request) error {
- return nil
-}
-
-// PUT request on /api/templates/:id
-func (handler *Handler) templateUpdate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
- templateID, err := request.RetrieveNumericRouteVariableValue(r, "id")
- if err != nil {
- return &httperror.HandlerError{http.StatusBadRequest, "Invalid template identifier route variable", err}
- }
-
- template, err := handler.TemplateService.Template(portainer.TemplateID(templateID))
- if err == portainer.ErrObjectNotFound {
- return &httperror.HandlerError{http.StatusNotFound, "Unable to find a template with the specified identifier inside the database", err}
- } else if err != nil {
- return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find a template with the specified identifier inside the database", err}
- }
-
- var payload templateUpdatePayload
- err = request.DecodeAndValidateJSONPayload(r, &payload)
- if err != nil {
- return &httperror.HandlerError{http.StatusBadRequest, "Invalid request payload", err}
- }
-
- updateTemplate(template, &payload)
-
- err = handler.TemplateService.UpdateTemplate(template.ID, template)
- if err != nil {
- return &httperror.HandlerError{http.StatusNotFound, "Unable to persist template changes inside the database", err}
- }
-
- return response.JSON(w, template)
-}
-
-func updateContainerProperties(template *portainer.Template, payload *templateUpdatePayload) {
- if payload.Image != nil {
- template.Image = *payload.Image
- }
-
- if payload.Registry != nil {
- template.Registry = *payload.Registry
- }
-
- if payload.Command != nil {
- template.Command = *payload.Command
- }
-
- if payload.Network != nil {
- template.Network = *payload.Network
- }
-
- if payload.Volumes != nil {
- template.Volumes = payload.Volumes
- }
-
- if payload.Ports != nil {
- template.Ports = payload.Ports
- }
-
- if payload.Labels != nil {
- template.Labels = payload.Labels
- }
-
- if payload.Privileged != nil {
- template.Privileged = *payload.Privileged
- }
-
- if payload.Interactive != nil {
- template.Interactive = *payload.Interactive
- }
-
- if payload.RestartPolicy != nil {
- template.RestartPolicy = *payload.RestartPolicy
- }
-
- if payload.Hostname != nil {
- template.Hostname = *payload.Hostname
- }
-}
-
-func updateStackProperties(template *portainer.Template, payload *templateUpdatePayload) {
- if payload.Repository.URL != "" && payload.Repository.StackFile != "" {
- template.Repository = payload.Repository
- }
-}
-
-func updateTemplate(template *portainer.Template, payload *templateUpdatePayload) {
- if payload.Title != nil {
- template.Title = *payload.Title
- }
-
- if payload.Description != nil {
- template.Description = *payload.Description
- }
-
- if payload.Name != nil {
- template.Name = *payload.Name
- }
-
- if payload.Logo != nil {
- template.Logo = *payload.Logo
- }
-
- if payload.Note != nil {
- template.Note = *payload.Note
- }
-
- if payload.Platform != nil {
- template.Platform = *payload.Platform
- }
-
- if payload.Categories != nil {
- template.Categories = payload.Categories
- }
-
- if payload.Env != nil {
- template.Env = payload.Env
- }
-
- if payload.AdministratorOnly != nil {
- template.AdministratorOnly = *payload.AdministratorOnly
- }
-
- if template.Type == portainer.ContainerTemplate {
- updateContainerProperties(template, payload)
- } else if template.Type == portainer.SwarmStackTemplate || template.Type == portainer.ComposeStackTemplate {
- updateStackProperties(template, payload)
- }
-}
diff --git a/api/http/security/filter.go b/api/http/security/filter.go
index ba7872c39..1716b043e 100644
--- a/api/http/security/filter.go
+++ b/api/http/security/filter.go
@@ -79,24 +79,6 @@ func FilterRegistries(registries []portainer.Registry, context *RestrictedReques
return filteredRegistries
}
-// FilterTemplates filters templates based on the user role.
-// Non-administrator template do not have access to templates where the AdministratorOnly flag is set to true.
-func FilterTemplates(templates []portainer.Template, context *RestrictedRequestContext) []portainer.Template {
- filteredTemplates := templates
-
- if !context.IsAdmin {
- filteredTemplates = make([]portainer.Template, 0)
-
- for _, template := range templates {
- if !template.AdministratorOnly {
- filteredTemplates = append(filteredTemplates, template)
- }
- }
- }
-
- return filteredTemplates
-}
-
// FilterEndpoints filters endpoints based on user role and team memberships.
// Non administrator users only have access to authorized endpoints (can be inherited via endoint groups).
func FilterEndpoints(endpoints []portainer.Endpoint, groups []portainer.EndpointGroup, context *RestrictedRequestContext) []portainer.Endpoint {
diff --git a/api/http/server.go b/api/http/server.go
index f1c98ee5f..214796070 100644
--- a/api/http/server.go
+++ b/api/http/server.go
@@ -78,7 +78,6 @@ type Server struct {
TagService portainer.TagService
TeamService portainer.TeamService
TeamMembershipService portainer.TeamMembershipService
- TemplateService portainer.TemplateService
UserService portainer.UserService
WebhookService portainer.WebhookService
Handler *handler.Handler
@@ -282,7 +281,6 @@ func (server *Server) Start() error {
var supportHandler = support.NewHandler(requestBouncer)
var templatesHandler = templates.NewHandler(requestBouncer)
- templatesHandler.TemplateService = server.TemplateService
templatesHandler.SettingsService = server.SettingsService
var uploadHandler = upload.NewHandler(requestBouncer)
diff --git a/api/portainer.go b/api/portainer.go
index c6a469d84..3b51f86c6 100644
--- a/api/portainer.go
+++ b/api/portainer.go
@@ -538,7 +538,8 @@ type (
AccessLevel ResourceAccessLevel `json:"AccessLevel"`
}
- // Template represents an application template
+ // Template represents an application template that can be used as an App Template
+ // or an Edge template
Template struct {
// Mandatory container/stack fields
ID TemplateID `json:"Id"`
@@ -553,7 +554,7 @@ type (
// Mandatory stack fields
Repository TemplateRepository `json:"repository"`
- // Mandatory edge stack fields
+ // Mandatory Edge stack fields
StackFile string `json:"stackFile"`
// Optional stack/container fields
@@ -943,15 +944,6 @@ type (
DeleteTeamMembershipByTeamID(teamID TeamID) error
}
- // TemplateService represents a service for managing template data
- TemplateService interface {
- Templates() ([]Template, error)
- Template(ID TemplateID) (*Template, error)
- CreateTemplate(template *Template) error
- UpdateTemplate(ID TemplateID, template *Template) error
- DeleteTemplate(ID TemplateID) error
- }
-
// TunnelServerService represents a service for managing data associated to the tunnel server
TunnelServerService interface {
Info() (*TunnelServerInfo, error)
@@ -1039,8 +1031,8 @@ const (
DefaultEdgeAgentCheckinIntervalInSeconds = 5
// LocalExtensionManifestFile represents the name of the local manifest file for extensions
LocalExtensionManifestFile = "/extensions.json"
- // EdgeTemplatesURL represents the URL used to retrieve Edge templates
- EdgeTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates-1.20.0.json"
+ // DefaultTemplatesURL represents the URL to the official templates supported by Portainer
+ DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json"
)
const (
diff --git a/app/portainer/__module.js b/app/portainer/__module.js
index 9605c670d..6d03c1d8f 100644
--- a/app/portainer/__module.js
+++ b/app/portainer/__module.js
@@ -533,28 +533,6 @@ angular.module('portainer.app', []).config([
},
};
- var template = {
- name: 'portainer.templates.template',
- url: '/:id',
- views: {
- 'content@': {
- templateUrl: './views/templates/edit/template.html',
- controller: 'TemplateController',
- },
- },
- };
-
- var templateCreation = {
- name: 'portainer.templates.new',
- url: '/new',
- views: {
- 'content@': {
- templateUrl: './views/templates/create/createtemplate.html',
- controller: 'CreateTemplateController',
- },
- },
- };
-
$stateRegistryProvider.register(root);
$stateRegistryProvider.register(portainer);
$stateRegistryProvider.register(about);
@@ -595,7 +573,5 @@ angular.module('portainer.app', []).config([
$stateRegistryProvider.register(teams);
$stateRegistryProvider.register(team);
$stateRegistryProvider.register(templates);
- $stateRegistryProvider.register(template);
- $stateRegistryProvider.register(templateCreation);
},
]);
diff --git a/app/portainer/components/template-list/template-item/template-item.js b/app/portainer/components/template-list/template-item/template-item.js
index 4bfabd437..7b398f8dc 100644
--- a/app/portainer/components/template-list/template-item/template-item.js
+++ b/app/portainer/components/template-list/template-item/template-item.js
@@ -3,8 +3,5 @@ angular.module('portainer.app').component('templateItem', {
bindings: {
model: '=',
onSelect: '<',
- onDelete: '<',
- showUpdateAction: '<',
- showDeleteAction: '<',
},
});
diff --git a/app/portainer/components/template-list/template-item/templateItem.html b/app/portainer/components/template-list/template-item/templateItem.html
index e287c36c9..cabab77ff 100644
--- a/app/portainer/components/template-list/template-item/templateItem.html
+++ b/app/portainer/components/template-list/template-item/templateItem.html
@@ -28,15 +28,6 @@
-
-
-
- Update
-
-