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 - - - Delete - - diff --git a/app/portainer/components/template-list/template-list.js b/app/portainer/components/template-list/template-list.js index 40f8784b6..6871b65ea 100644 --- a/app/portainer/components/template-list/template-list.js +++ b/app/portainer/components/template-list/template-list.js @@ -7,10 +7,6 @@ angular.module('portainer.app').component('templateList', { templates: '<', tableKey: '@', selectAction: '<', - deleteAction: '<', showSwarmStacks: '<', - showAddAction: '<', - showUpdateAction: '<', - showDeleteAction: '<', }, }); diff --git a/app/portainer/components/template-list/templateList.html b/app/portainer/components/template-list/templateList.html index 8983f0dbd..a2be808f8 100644 --- a/app/portainer/components/template-list/templateList.html +++ b/app/portainer/components/template-list/templateList.html @@ -49,10 +49,7 @@
Loading... diff --git a/app/portainer/models/settings.js b/app/portainer/models/settings.js index 7a6324005..8e0247db9 100644 --- a/app/portainer/models/settings.js +++ b/app/portainer/models/settings.js @@ -9,7 +9,6 @@ export function SettingsViewModel(data) { this.AllowVolumeBrowserForRegularUsers = data.AllowVolumeBrowserForRegularUsers; this.SnapshotInterval = data.SnapshotInterval; this.TemplatesURL = data.TemplatesURL; - this.ExternalTemplates = data.ExternalTemplates; this.EnableHostManagementFeatures = data.EnableHostManagementFeatures; this.EdgeAgentCheckinInterval = data.EdgeAgentCheckinInterval; this.EnableEdgeComputeFeatures = data.EnableEdgeComputeFeatures; @@ -21,7 +20,6 @@ export function PublicSettingsViewModel(settings) { this.AllowVolumeBrowserForRegularUsers = settings.AllowVolumeBrowserForRegularUsers; this.AuthenticationMethod = settings.AuthenticationMethod; this.EnableHostManagementFeatures = settings.EnableHostManagementFeatures; - this.ExternalTemplates = settings.ExternalTemplates; this.EnableEdgeComputeFeatures = settings.EnableEdgeComputeFeatures; this.LogoURL = settings.LogoURL; this.OAuthLoginURI = settings.OAuthLoginURI; diff --git a/app/portainer/models/template.js b/app/portainer/models/template.js index 218430612..f0bd75fd4 100644 --- a/app/portainer/models/template.js +++ b/app/portainer/models/template.js @@ -1,58 +1,6 @@ import _ from 'lodash-es'; import { PorImageRegistryModel } from 'Docker/models/porImageRegistry'; -export function TemplateDefaultModel() { - this.Type = 1; - this.AdministratorOnly = false; - this.Title = ''; - this.Description = ''; - this.Volumes = []; - this.Ports = []; - this.Env = []; - this.Labels = []; - this.RestartPolicy = 'always'; - this.RegistryModel = new PorImageRegistryModel(); -} - -export function TemplateCreateRequest(model) { - this.Type = model.Type; - this.Name = model.Name; - this.Hostname = model.Hostname; - this.Title = model.Title; - this.Description = model.Description; - this.Note = model.Note; - this.Categories = model.Categories; - this.Platform = model.Platform; - this.Logo = model.Logo; - this.Image = model.RegistryModel.Image; - this.Registry = model.RegistryModel.Registry.URL; - this.Command = model.Command; - this.Network = model.Network && model.Network.Name; - this.Privileged = model.Privileged; - this.Interactive = model.Interactive; - this.RestartPolicy = model.RestartPolicy; - this.Labels = model.Labels; - this.Repository = model.Repository; - this.Env = model.Env; - this.AdministratorOnly = model.AdministratorOnly; - - this.Ports = []; - for (var i = 0; i < model.Ports.length; i++) { - var binding = model.Ports[i]; - if (binding.containerPort && binding.protocol) { - var port = binding.hostPort ? binding.hostPort + ':' + binding.containerPort + '/' + binding.protocol : binding.containerPort + '/' + binding.protocol; - this.Ports.push(port); - } - } - - this.Volumes = model.Volumes; -} - -export function TemplateUpdateRequest(model) { - TemplateCreateRequest.call(this, model); - this.id = model.Id; -} - export function TemplateViewModel(data) { this.Id = data.Id; this.Title = data.title; diff --git a/app/portainer/rest/template.js b/app/portainer/rest/template.js index ece15b5dc..8d79157cb 100644 --- a/app/portainer/rest/template.js +++ b/app/portainer/rest/template.js @@ -6,11 +6,7 @@ angular.module('portainer.app').factory('Templates', [ API_ENDPOINT_TEMPLATES + '/:id', {}, { - create: { method: 'POST' }, - query: { method: 'GET', isArray: true }, - get: { method: 'GET', params: { id: '@id' } }, - update: { method: 'PUT', params: { id: '@id' } }, - remove: { method: 'DELETE', params: { id: '@id' } }, + query: { method: 'GET' }, } ); }, diff --git a/app/portainer/services/api/templateService.js b/app/portainer/services/api/templateService.js index ddcf0dfd3..7788ae19c 100644 --- a/app/portainer/services/api/templateService.js +++ b/app/portainer/services/api/templateService.js @@ -1,4 +1,4 @@ -import { TemplateViewModel, TemplateCreateRequest, TemplateUpdateRequest } from '../../models/template'; +import { TemplateViewModel } from '../../models/template'; angular.module('portainer.app').factory('TemplateService', [ '$q', @@ -21,7 +21,7 @@ angular.module('portainer.app').factory('TemplateService', [ dockerhub: DockerHubService.dockerhub(), }) .then(function success(data) { - const templates = data.templates.map(function (item) { + const templates = data.templates.templates.map(function (item) { const res = new TemplateViewModel(item); const registry = RegistryService.retrievePorRegistryModelFromRepositoryWithRegistries(res.RegistryModel.Registry.URL, data.registries, data.dockerhub); registry.Image = res.RegistryModel.Image; @@ -37,40 +37,6 @@ angular.module('portainer.app').factory('TemplateService', [ return deferred.promise; }; - service.template = function (id) { - var deferred = $q.defer(); - let template; - Templates.get({ id: id }) - .$promise.then(function success(data) { - template = new TemplateViewModel(data); - return RegistryService.retrievePorRegistryModelFromRepository(template.RegistryModel.Registry.URL); - }) - .then((registry) => { - registry.Image = template.RegistryModel.Image; - template.RegistryModel = registry; - deferred.resolve(template); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve template details', err: err }); - }); - - return deferred.promise; - }; - - service.delete = function (id) { - return Templates.remove({ id: id }).$promise; - }; - - service.create = function (model) { - var payload = new TemplateCreateRequest(model); - return Templates.create(payload).$promise; - }; - - service.update = function (model) { - var payload = new TemplateUpdateRequest(model); - return Templates.update(payload).$promise; - }; - service.createTemplateConfiguration = function (template, containerName, network) { var imageConfiguration = ImageHelper.createImageConfigForContainer(template.RegistryModel); var containerConfiguration = createContainerConfiguration(template, containerName, network); diff --git a/app/portainer/views/templates/create/createTemplateController.js b/app/portainer/views/templates/create/createTemplateController.js deleted file mode 100644 index 267ce0016..000000000 --- a/app/portainer/views/templates/create/createTemplateController.js +++ /dev/null @@ -1,53 +0,0 @@ -import { TemplateDefaultModel } from '../../../models/template'; - -angular.module('portainer.app').controller('CreateTemplateController', [ - '$q', - '$scope', - '$state', - 'TemplateService', - 'TemplateHelper', - 'NetworkService', - 'Notifications', - function ($q, $scope, $state, TemplateService, TemplateHelper, NetworkService, Notifications) { - $scope.state = { - actionInProgress: false, - }; - - $scope.create = function () { - var model = $scope.model; - - $scope.state.actionInProgress = true; - TemplateService.create(model) - .then(function success() { - Notifications.success('Template successfully created', model.Title); - $state.go('portainer.templates'); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to create template'); - }) - .finally(function final() { - $scope.state.actionInProgress = false; - }); - }; - - function initView() { - $scope.model = new TemplateDefaultModel(); - var provider = $scope.applicationState.endpoint.mode.provider; - var apiVersion = $scope.applicationState.endpoint.apiVersion; - - $q.all({ - templates: TemplateService.templates(), - networks: NetworkService.networks(provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE', false, provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25), - }) - .then(function success(data) { - $scope.categories = TemplateHelper.getUniqueCategories(data.templates); - $scope.networks = data.networks; - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to retrieve template details'); - }); - } - - initView(); - }, -]); diff --git a/app/portainer/views/templates/create/createtemplate.html b/app/portainer/views/templates/create/createtemplate.html deleted file mode 100644 index 25dd94bd7..000000000 --- a/app/portainer/views/templates/create/createtemplate.html +++ /dev/null @@ -1,22 +0,0 @@ - - - Templates > Add template - - -
-
- - - - - -
-
diff --git a/app/portainer/views/templates/edit/template.html b/app/portainer/views/templates/edit/template.html deleted file mode 100644 index faa42d789..000000000 --- a/app/portainer/views/templates/edit/template.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Templates > {{ ::template.Title }} - - -
-
- - - - - -
-
diff --git a/app/portainer/views/templates/edit/templateController.js b/app/portainer/views/templates/edit/templateController.js deleted file mode 100644 index 0db2a6435..000000000 --- a/app/portainer/views/templates/edit/templateController.js +++ /dev/null @@ -1,66 +0,0 @@ -import _ from 'lodash-es'; - -angular.module('portainer.app').controller('TemplateController', [ - '$q', - '$scope', - '$state', - '$transition$', - 'TemplateService', - 'TemplateHelper', - 'NetworkService', - 'Notifications', - function ($q, $scope, $state, $transition$, TemplateService, TemplateHelper, NetworkService, Notifications) { - $scope.state = { - actionInProgress: false, - }; - - $scope.update = function () { - var model = $scope.template; - - $scope.state.actionInProgress = true; - TemplateService.update(model) - .then(function success() { - Notifications.success('Template successfully updated', model.Title); - $state.go('portainer.templates'); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to update template'); - }) - .finally(function final() { - $scope.state.actionInProgress = false; - }); - }; - - function initView() { - var provider = $scope.applicationState.endpoint.mode.provider; - var apiVersion = $scope.applicationState.endpoint.apiVersion; - - var templateId = $transition$.params().id; - $q.all({ - templates: TemplateService.templates(), - template: TemplateService.template(templateId), - networks: NetworkService.networks(provider === 'DOCKER_STANDALONE' || provider === 'DOCKER_SWARM_MODE', false, provider === 'DOCKER_SWARM_MODE' && apiVersion >= 1.25), - }) - .then(function success(data) { - var template = data.template; - if (template.Network) { - template.Network = _.find(data.networks, function (o) { - return o.Name === template.Network; - }); - } else { - template.Network = _.find(data.networks, function (o) { - return o.Name === 'bridge'; - }); - } - $scope.categories = TemplateHelper.getUniqueCategories(data.templates); - $scope.template = data.template; - $scope.networks = data.networks; - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to retrieve template details'); - }); - } - - initView(); - }, -]); diff --git a/app/portainer/views/templates/templates.html b/app/portainer/views/templates/templates.html index 692f39f62..e0e8cae19 100644 --- a/app/portainer/views/templates/templates.html +++ b/app/portainer/views/templates/templates.html @@ -368,10 +368,6 @@ templates="templates" table-key="templates" select-action="selectTemplate" - delete-action="deleteTemplate" - show-add-action="state.templateManagement && isAdmin" - show-update-action="state.templateManagement && isAdmin" - show-delete-action="state.templateManagement && isAdmin" show-swarm-stacks="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER' && applicationState.endpoint.apiVersion >= 1.25" >
diff --git a/app/portainer/views/templates/templatesController.js b/app/portainer/views/templates/templatesController.js index d0d1238f9..6c6daddda 100644 --- a/app/portainer/views/templates/templatesController.js +++ b/app/portainer/views/templates/templatesController.js @@ -20,7 +20,6 @@ angular.module('portainer.app').controller('TemplatesController', [ 'SettingsService', 'StackService', 'EndpointProvider', - 'ModalService', function ( $scope, $q, @@ -39,15 +38,13 @@ angular.module('portainer.app').controller('TemplatesController', [ FormValidator, SettingsService, StackService, - EndpointProvider, - ModalService + EndpointProvider ) { $scope.state = { selectedTemplate: null, showAdvancedOptions: false, formValidationError: '', actionInProgress: false, - templateManagement: true, }; $scope.formValues = { @@ -255,27 +252,6 @@ angular.module('portainer.app').controller('TemplatesController', [ return TemplateService.createTemplateConfiguration(template, name, network); } - $scope.deleteTemplate = function (template) { - ModalService.confirmDeletion('Do you want to delete this template?', function onConfirm(confirmed) { - if (!confirmed) { - return; - } - deleteTemplate(template); - }); - }; - - function deleteTemplate(template) { - TemplateService.delete(template.Id) - .then(function success() { - Notifications.success('Template successfully deleted'); - var idx = $scope.templates.indexOf(template); - $scope.templates.splice(idx, 1); - }) - .catch(function error(err) { - Notifications.error('Failure', err, 'Unable to remove template'); - }); - } - function initView() { $scope.isAdmin = Authentication.isAdmin(); @@ -300,7 +276,6 @@ angular.module('portainer.app').controller('TemplatesController', [ $scope.availableNetworks = networks; var settings = data.settings; $scope.allowBindMounts = settings.AllowBindMountsForRegularUsers; - $scope.state.templateManagement = !settings.ExternalTemplates; }) .catch(function error(err) { $scope.templates = []; diff --git a/gruntfile.js b/gruntfile.js index 069440c4f..6794bd54e 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -154,7 +154,7 @@ function shell_run_container() { 'docker rm -f portainer', 'docker run -d -p 8000:8000 -p 9000:9000 -v $(pwd)/dist:/app -v ' + portainer_data + - ':/data -v /var/run/docker.sock:/var/run/docker.sock:z --name portainer portainer/base /app/portainer --no-analytics --template-file /app/templates.json', + ':/data -v /var/run/docker.sock:/var/run/docker.sock:z --name portainer portainer/base /app/portainer --no-analytics', ].join(';'); }