diff --git a/api/bolt/datastore.go b/api/bolt/datastore.go index a857375f3..c6ce7db61 100644 --- a/api/bolt/datastore.go +++ b/api/bolt/datastore.go @@ -128,6 +128,7 @@ func (store *Store) MigrateData() error { ScheduleService: store.ScheduleService, SettingsService: store.SettingsService, StackService: store.StackService, + TagService: store.TagService, TeamMembershipService: store.TeamMembershipService, TemplateService: store.TemplateService, UserService: store.UserService, diff --git a/api/bolt/init.go b/api/bolt/init.go index bc6e39be5..8e1a0661c 100644 --- a/api/bolt/init.go +++ b/api/bolt/init.go @@ -16,7 +16,7 @@ func (store *Store) Init() error { Labels: []portainer.Pair{}, UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, - Tags: []string{}, + TagIDs: []portainer.TagID{}, } err = store.EndpointGroupService.CreateEndpointGroup(unassignedGroup) diff --git a/api/bolt/migrator/migrate_dbversion22.go b/api/bolt/migrator/migrate_dbversion22.go new file mode 100644 index 000000000..f2e3c2b6c --- /dev/null +++ b/api/bolt/migrator/migrate_dbversion22.go @@ -0,0 +1,57 @@ +package migrator + +import "github.com/portainer/portainer/api" + +func (m *Migrator) updateEndointsAndEndpointsGroupsToDBVersion23() error { + tags, err := m.tagService.Tags() + if err != nil { + return err + } + + tagsNameMap := make(map[string]portainer.TagID) + for _, tag := range tags { + tagsNameMap[tag.Name] = tag.ID + } + + endpoints, err := m.endpointService.Endpoints() + if err != nil { + return err + } + + for _, endpoint := range endpoints { + endpointTags := make([]portainer.TagID, 0) + for _, tagName := range endpoint.Tags { + tagID, ok := tagsNameMap[tagName] + if ok { + endpointTags = append(endpointTags, tagID) + } + } + endpoint.TagIDs = endpointTags + err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint) + if err != nil { + return err + } + } + + endpointGroups, err := m.endpointGroupService.EndpointGroups() + if err != nil { + return err + } + + for _, endpointGroup := range endpointGroups { + endpointGroupTags := make([]portainer.TagID, 0) + for _, tagName := range endpointGroup.Tags { + tagID, ok := tagsNameMap[tagName] + if ok { + endpointGroupTags = append(endpointGroupTags, tagID) + } + } + endpointGroup.TagIDs = endpointGroupTags + err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) + if err != nil { + return err + } + } + + return nil +} diff --git a/api/bolt/migrator/migrator.go b/api/bolt/migrator/migrator.go index 885cc9ab8..8f4aee085 100644 --- a/api/bolt/migrator/migrator.go +++ b/api/bolt/migrator/migrator.go @@ -12,6 +12,7 @@ import ( "github.com/portainer/portainer/api/bolt/schedule" "github.com/portainer/portainer/api/bolt/settings" "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" @@ -32,6 +33,7 @@ type ( scheduleService *schedule.Service settingsService *settings.Service stackService *stack.Service + tagService *tag.Service teamMembershipService *teammembership.Service templateService *template.Service userService *user.Service @@ -52,6 +54,7 @@ type ( ScheduleService *schedule.Service SettingsService *settings.Service StackService *stack.Service + TagService *tag.Service TeamMembershipService *teammembership.Service TemplateService *template.Service UserService *user.Service @@ -73,6 +76,7 @@ func NewMigrator(parameters *Parameters) *Migrator { roleService: parameters.RoleService, scheduleService: parameters.ScheduleService, settingsService: parameters.SettingsService, + tagService: parameters.TagService, teamMembershipService: parameters.TeamMembershipService, templateService: parameters.TemplateService, stackService: parameters.StackService, @@ -301,5 +305,13 @@ func (m *Migrator) Migrate() error { } } + // Portainer 1.24.0-dev + if m.currentDBVersion < 23 { + err := m.updateEndointsAndEndpointsGroupsToDBVersion23() + if err != nil { + return err + } + } + return m.versionService.StoreDBVersion(portainer.DBVersion) } diff --git a/api/bolt/tag/tag.go b/api/bolt/tag/tag.go index d54ee6b76..d4a5dc9de 100644 --- a/api/bolt/tag/tag.go +++ b/api/bolt/tag/tag.go @@ -52,6 +52,19 @@ func (service *Service) Tags() ([]portainer.Tag, error) { return tags, err } +// Tag returns a tag by ID. +func (service *Service) Tag(ID portainer.TagID) (*portainer.Tag, error) { + var tag portainer.Tag + identifier := internal.Itob(int(ID)) + + err := internal.GetObject(service.db, BucketName, identifier, &tag) + if err != nil { + return nil, err + } + + return &tag, nil +} + // CreateTag creates a new tag. func (service *Service) CreateTag(tag *portainer.Tag) error { return service.db.Update(func(tx *bolt.Tx) error { diff --git a/api/cmd/portainer/main.go b/api/cmd/portainer/main.go index 236b64d35..46c5bb5ca 100644 --- a/api/cmd/portainer/main.go +++ b/api/cmd/portainer/main.go @@ -259,7 +259,7 @@ func initSettings(settingsService portainer.SettingsService, flags *portainer.CL LogoURL: *flags.Logo, AuthenticationMethod: portainer.AuthenticationInternal, LDAPSettings: portainer.LDAPSettings{ - AnonymousMode: true, + AnonymousMode: true, AutoCreateUsers: true, TLSConfig: portainer.TLSConfiguration{}, SearchSettings: []portainer.LDAPSearchSettings{ @@ -397,7 +397,7 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, endpointService portain UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, Extensions: []portainer.EndpointExtension{}, - Tags: []string{}, + TagIDs: []portainer.TagID{}, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, } @@ -440,7 +440,7 @@ func createUnsecuredEndpoint(endpointURL string, endpointService portainer.Endpo UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, Extensions: []portainer.EndpointExtension{}, - Tags: []string{}, + TagIDs: []portainer.TagID{}, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, } diff --git a/api/http/handler/endpointgroups/endpointgroup_create.go b/api/http/handler/endpointgroups/endpointgroup_create.go index 32a617c92..34bdbe754 100644 --- a/api/http/handler/endpointgroups/endpointgroup_create.go +++ b/api/http/handler/endpointgroups/endpointgroup_create.go @@ -14,15 +14,15 @@ type endpointGroupCreatePayload struct { Name string Description string AssociatedEndpoints []portainer.EndpointID - Tags []string + TagIDs []portainer.TagID } func (payload *endpointGroupCreatePayload) Validate(r *http.Request) error { if govalidator.IsNull(payload.Name) { return portainer.Error("Invalid endpoint group name") } - if payload.Tags == nil { - payload.Tags = []string{} + if payload.TagIDs == nil { + payload.TagIDs = []portainer.TagID{} } return nil } @@ -40,7 +40,7 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque Description: payload.Description, UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, - Tags: payload.Tags, + TagIDs: payload.TagIDs, } err = handler.EndpointGroupService.CreateEndpointGroup(endpointGroup) diff --git a/api/http/handler/endpointgroups/endpointgroup_update.go b/api/http/handler/endpointgroups/endpointgroup_update.go index 58ea605fc..ef7b4edc4 100644 --- a/api/http/handler/endpointgroups/endpointgroup_update.go +++ b/api/http/handler/endpointgroups/endpointgroup_update.go @@ -13,7 +13,7 @@ import ( type endpointGroupUpdatePayload struct { Name string Description string - Tags []string + TagIDs []portainer.TagID UserAccessPolicies portainer.UserAccessPolicies TeamAccessPolicies portainer.TeamAccessPolicies } @@ -50,8 +50,8 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque endpointGroup.Description = payload.Description } - if payload.Tags != nil { - endpointGroup.Tags = payload.Tags + if payload.TagIDs != nil { + endpointGroup.TagIDs = payload.TagIDs } updateAuthorizations := false diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index 865923149..dfa030927 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -32,7 +32,7 @@ type endpointCreatePayload struct { AzureApplicationID string AzureTenantID string AzureAuthenticationKey string - Tags []string + TagIDs []portainer.TagID } func (payload *endpointCreatePayload) Validate(r *http.Request) error { @@ -54,14 +54,14 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { } payload.GroupID = groupID - var tags []string - err = request.RetrieveMultiPartFormJSONValue(r, "Tags", &tags, true) + var tagIDs []portainer.TagID + err = request.RetrieveMultiPartFormJSONValue(r, "TagIds", &tagIDs, true) if err != nil { - return portainer.Error("Invalid Tags parameter") + return portainer.Error("Invalid TagIds parameter") } - payload.Tags = tags - if payload.Tags == nil { - payload.Tags = make([]string, 0) + payload.TagIDs = tagIDs + if payload.TagIDs == nil { + payload.TagIDs = make([]portainer.TagID, 0) } useTLS, _ := request.RetrieveBooleanMultiPartFormValue(r, "TLS", true) @@ -187,7 +187,7 @@ func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*po TeamAccessPolicies: portainer.TeamAccessPolicies{}, Extensions: []portainer.EndpointExtension{}, AzureCredentials: credentials, - Tags: payload.Tags, + TagIDs: payload.TagIDs, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, } @@ -232,7 +232,7 @@ func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) AuthorizedUsers: []portainer.UserID{}, AuthorizedTeams: []portainer.TeamID{}, Extensions: []portainer.EndpointExtension{}, - Tags: payload.Tags, + TagIDs: payload.TagIDs, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, EdgeKey: edgeKey, @@ -278,7 +278,7 @@ func (handler *Handler) createUnsecuredEndpoint(payload *endpointCreatePayload) UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, Extensions: []portainer.EndpointExtension{}, - Tags: payload.Tags, + TagIDs: payload.TagIDs, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, } @@ -322,7 +322,7 @@ func (handler *Handler) createTLSSecuredEndpoint(payload *endpointCreatePayload) UserAccessPolicies: portainer.UserAccessPolicies{}, TeamAccessPolicies: portainer.TeamAccessPolicies{}, Extensions: []portainer.EndpointExtension{}, - Tags: payload.Tags, + TagIDs: payload.TagIDs, Status: portainer.EndpointStatusUp, Snapshots: []portainer.Snapshot{}, } diff --git a/api/http/handler/endpoints/endpoint_list.go b/api/http/handler/endpoints/endpoint_list.go index 403a3f668..1d98296bf 100644 --- a/api/http/handler/endpoints/endpoint_list.go +++ b/api/http/handler/endpoints/endpoint_list.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api" "github.com/portainer/libhttp/request" @@ -52,7 +52,15 @@ func (handler *Handler) endpointList(w http.ResponseWriter, r *http.Request) *ht } if search != "" { - filteredEndpoints = filterEndpointsBySearchCriteria(filteredEndpoints, endpointGroups, search) + tags, err := handler.TagsService.Tags() + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve tags from the database", err} + } + tagsMap := make(map[portainer.TagID]string) + for _, tag := range tags { + tagsMap[tag.ID] = tag.Name + } + filteredEndpoints = filterEndpointsBySearchCriteria(filteredEndpoints, endpointGroups, tagsMap, search) } if endpointType != 0 { @@ -102,17 +110,17 @@ func filterEndpointsByGroupID(endpoints []portainer.Endpoint, endpointGroupID po return filteredEndpoints } -func filterEndpointsBySearchCriteria(endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup, searchCriteria string) []portainer.Endpoint { +func filterEndpointsBySearchCriteria(endpoints []portainer.Endpoint, endpointGroups []portainer.EndpointGroup, tagsMap map[portainer.TagID]string, searchCriteria string) []portainer.Endpoint { filteredEndpoints := make([]portainer.Endpoint, 0) for _, endpoint := range endpoints { - - if endpointMatchSearchCriteria(&endpoint, searchCriteria) { + endpointTags := convertTagIDsToTags(tagsMap, endpoint.TagIDs) + if endpointMatchSearchCriteria(&endpoint, endpointTags, searchCriteria) { filteredEndpoints = append(filteredEndpoints, endpoint) continue } - if endpointGroupMatchSearchCriteria(&endpoint, endpointGroups, searchCriteria) { + if endpointGroupMatchSearchCriteria(&endpoint, endpointGroups, tagsMap, searchCriteria) { filteredEndpoints = append(filteredEndpoints, endpoint) } } @@ -120,7 +128,7 @@ func filterEndpointsBySearchCriteria(endpoints []portainer.Endpoint, endpointGro return filteredEndpoints } -func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, searchCriteria string) bool { +func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, tags []string, searchCriteria string) bool { if strings.Contains(strings.ToLower(endpoint.Name), searchCriteria) { return true } @@ -134,8 +142,7 @@ func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, searchCriteria st } else if endpoint.Status == portainer.EndpointStatusDown && searchCriteria == "down" { return true } - - for _, tag := range endpoint.Tags { + for _, tag := range tags { if strings.Contains(strings.ToLower(tag), searchCriteria) { return true } @@ -144,14 +151,14 @@ func endpointMatchSearchCriteria(endpoint *portainer.Endpoint, searchCriteria st return false } -func endpointGroupMatchSearchCriteria(endpoint *portainer.Endpoint, endpointGroups []portainer.EndpointGroup, searchCriteria string) bool { +func endpointGroupMatchSearchCriteria(endpoint *portainer.Endpoint, endpointGroups []portainer.EndpointGroup, tagsMap map[portainer.TagID]string, searchCriteria string) bool { for _, group := range endpointGroups { if group.ID == endpoint.GroupID { if strings.Contains(strings.ToLower(group.Name), searchCriteria) { return true } - - for _, tag := range group.Tags { + tags := convertTagIDsToTags(tagsMap, group.TagIDs) + for _, tag := range tags { if strings.Contains(strings.ToLower(tag), searchCriteria) { return true } @@ -172,3 +179,11 @@ func filterEndpointsByType(endpoints []portainer.Endpoint, endpointType portaine } return filteredEndpoints } + +func convertTagIDsToTags(tagsMap map[portainer.TagID]string, tagIDs []portainer.TagID) []string { + tags := make([]string, 0) + for _, tagID := range tagIDs { + tags = append(tags, tagsMap[tagID]) + } + return tags +} diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index dd87c22f2..7c02f5f67 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -24,7 +24,7 @@ type endpointUpdatePayload struct { AzureApplicationID *string AzureTenantID *string AzureAuthenticationKey *string - Tags []string + TagIDs []portainer.TagID UserAccessPolicies portainer.UserAccessPolicies TeamAccessPolicies portainer.TeamAccessPolicies } @@ -73,8 +73,8 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * endpoint.GroupID = portainer.EndpointGroupID(*payload.GroupID) } - if payload.Tags != nil { - endpoint.Tags = payload.Tags + if payload.TagIDs != nil { + endpoint.TagIDs = payload.TagIDs } updateAuthorizations := false diff --git a/api/http/handler/endpoints/handler.go b/api/http/handler/endpoints/handler.go index c655a0eef..6281a4a98 100644 --- a/api/http/handler/endpoints/handler.go +++ b/api/http/handler/endpoints/handler.go @@ -37,6 +37,7 @@ type Handler struct { JobService portainer.JobService ReverseTunnelService portainer.ReverseTunnelService SettingsService portainer.SettingsService + TagsService portainer.TagService AuthorizationService *portainer.AuthorizationService } diff --git a/api/http/handler/tags/handler.go b/api/http/handler/tags/handler.go index 33cb59c9d..b5dac0274 100644 --- a/api/http/handler/tags/handler.go +++ b/api/http/handler/tags/handler.go @@ -12,7 +12,9 @@ import ( // Handler is the HTTP handler used to handle tag operations. type Handler struct { *mux.Router - TagService portainer.TagService + TagService portainer.TagService + EndpointService portainer.EndpointService + EndpointGroupService portainer.EndpointGroupService } // NewHandler creates a handler to manage tag operations. @@ -23,7 +25,7 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler { h.Handle("/tags", bouncer.AdminAccess(httperror.LoggerHandler(h.tagCreate))).Methods(http.MethodPost) h.Handle("/tags", - bouncer.AdminAccess(httperror.LoggerHandler(h.tagList))).Methods(http.MethodGet) + bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.tagList))).Methods(http.MethodGet) h.Handle("/tags/{id}", bouncer.AdminAccess(httperror.LoggerHandler(h.tagDelete))).Methods(http.MethodDelete) diff --git a/api/http/handler/tags/tag_delete.go b/api/http/handler/tags/tag_delete.go index 9c9e9d4e3..2467a38fd 100644 --- a/api/http/handler/tags/tag_delete.go +++ b/api/http/handler/tags/tag_delete.go @@ -15,6 +15,39 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe if err != nil { return &httperror.HandlerError{http.StatusBadRequest, "Invalid tag identifier route variable", err} } + tagID := portainer.TagID(id) + + endpoints, err := handler.EndpointService.Endpoints() + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err} + } + + for _, endpoint := range endpoints { + tagIdx := findTagIndex(endpoint.TagIDs, tagID) + if tagIdx != -1 { + endpoint.TagIDs = removeElement(endpoint.TagIDs, tagIdx) + err = handler.EndpointService.UpdateEndpoint(endpoint.ID, &endpoint) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint", err} + } + } + } + + endpointGroups, err := handler.EndpointGroupService.EndpointGroups() + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve endpoints from the database", err} + } + + for _, endpointGroup := range endpointGroups { + tagIdx := findTagIndex(endpointGroup.TagIDs, tagID) + if tagIdx != -1 { + endpointGroup.TagIDs = removeElement(endpointGroup.TagIDs, tagIdx) + err = handler.EndpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to update endpoint group", err} + } + } + } err = handler.TagService.DeleteTag(portainer.TagID(id)) if err != nil { @@ -23,3 +56,21 @@ func (handler *Handler) tagDelete(w http.ResponseWriter, r *http.Request) *httpe return response.Empty(w) } + +func findTagIndex(tags []portainer.TagID, searchTagID portainer.TagID) int { + for idx, tagID := range tags { + if searchTagID == tagID { + return idx + } + } + return -1 +} + +func removeElement(arr []portainer.TagID, index int) []portainer.TagID { + if index < 0 { + return arr + } + lastTagIdx := len(arr) - 1 + arr[index] = arr[lastTagIdx] + return arr[:lastTagIdx] +} diff --git a/api/http/server.go b/api/http/server.go index 066bc7bef..eb4777071 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -7,7 +7,7 @@ import ( "github.com/portainer/portainer/api/http/handler/roles" - "github.com/portainer/portainer/api" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/docker" "github.com/portainer/portainer/api/http/handler" "github.com/portainer/portainer/api/http/handler/auth" @@ -222,6 +222,8 @@ func (server *Server) Start() error { var tagHandler = tags.NewHandler(requestBouncer) tagHandler.TagService = server.TagService + tagHandler.EndpointService = server.EndpointService + tagHandler.EndpointGroupService = server.EndpointGroupService var teamHandler = teams.NewHandler(requestBouncer) teamHandler.TeamService = server.TeamService diff --git a/api/portainer.go b/api/portainer.go index 23b149c82..f0339677d 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -260,7 +260,7 @@ type ( TLSConfig TLSConfiguration `json:"TLSConfig"` Extensions []EndpointExtension `json:"Extensions"` AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty"` - Tags []string `json:"Tags"` + TagIDs []TagID `json:"TagIds"` Status EndpointStatus `json:"Status"` Snapshots []Snapshot `json:"Snapshots"` UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"` @@ -277,6 +277,9 @@ type ( // Deprecated in DBVersion == 18 AuthorizedUsers []UserID `json:"AuthorizedUsers"` AuthorizedTeams []TeamID `json:"AuthorizedTeams"` + + // Deprecated in DBVersion == 22 + Tags []string `json:"Tags"` } // Authorization represents an authorization associated to an operation @@ -426,7 +429,7 @@ type ( Description string `json:"Description"` UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"` TeamAccessPolicies TeamAccessPolicies `json:"TeamAccessPolicies"` - Tags []string `json:"Tags"` + TagIDs []TagID `json:"TagIds"` // Deprecated fields Labels []Pair `json:"Labels"` @@ -434,6 +437,9 @@ type ( // Deprecated in DBVersion == 18 AuthorizedUsers []UserID `json:"AuthorizedUsers"` AuthorizedTeams []TeamID `json:"AuthorizedTeams"` + + // Deprecated in DBVersion == 22 + Tags []string `json:"Tags"` } // EndpointExtension represents a deprecated form of Portainer extension @@ -775,6 +781,7 @@ type ( // TagService represents a service for managing tag data TagService interface { Tags() ([]Tag, error) + Tag(ID TagID) (*Tag, error) CreateTag(tag *Tag) error DeleteTag(ID TagID) error } @@ -919,7 +926,7 @@ const ( // APIVersion is the version number of the Portainer API APIVersion = "1.24.0-dev" // DBVersion is the version number of the Portainer database - DBVersion = 22 + DBVersion = 23 // AssetsServerURL represents the URL of the Portainer asset server AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com" // MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved diff --git a/app/portainer/components/endpoint-list/endpoint-item/endpoint-item-controller.js b/app/portainer/components/endpoint-list/endpoint-item/endpoint-item-controller.js index 62433ee25..61be9faf0 100644 --- a/app/portainer/components/endpoint-list/endpoint-item/endpoint-item-controller.js +++ b/app/portainer/components/endpoint-list/endpoint-item/endpoint-item-controller.js @@ -1,12 +1,42 @@ -angular.module('portainer.app').controller('EndpointItemController', [ - function EndpointItemController() { - var ctrl = this; +import angular from 'angular'; +import _ from 'lodash-es'; +import PortainerEndpointTagHelper from 'Portainer/helpers/tagHelper'; - ctrl.editEndpoint = editEndpoint; - - function editEndpoint(event) { - event.stopPropagation(); - ctrl.onEdit(ctrl.model.Id); - } +class EndpointItemController { + /* @ngInject */ + constructor() { + this.editEndpoint = this.editEndpoint.bind(this); } -]); + + editEndpoint(event) { + event.stopPropagation(); + this.onEdit(this.model.Id); + } + + joinTags() { + if (!this.tags) { + return 'Loading tags...'; + } + + if (!this.model.TagIds || !this.model.TagIds.length) { + return ''; + } + + const tagNames = PortainerEndpointTagHelper.idsToTagNames(this.tags, this.model.TagIds); + return _.join(tagNames, ',') + } + + $onInit() { + this.endpointTags = this.joinTags(); + } + + $onChanges({ tags, model }) { + if ((!tags && !model) || (!tags.currentValue && !model.currentValue)) { + return; + } + this.endpointTags = this.joinTags(); + } +} + +angular.module('portainer.app').controller('EndpointItemController', EndpointItemController); +export default EndpointItemController; diff --git a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html index 5060848a0..c8d9f4457 100644 --- a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html +++ b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.html @@ -85,14 +85,12 @@ - - + No tags - + - - {{ tag }}{{ $last? '' : ', ' }} - + {{ $ctrl.endpointTags }} diff --git a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js index ab12b75a6..ec8f2b1fa 100644 --- a/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js +++ b/app/portainer/components/endpoint-list/endpoint-item/endpointItem.js @@ -1,10 +1,15 @@ +import angular from 'angular'; + +import EndpointItemController from './endpoint-item-controller'; + angular.module('portainer.app').component('endpointItem', { templateUrl: './endpointItem.html', bindings: { model: '<', onSelect: '<', onEdit: '<', - isAdmin:'<' + isAdmin: '<', + tags: '<', }, - controller: 'EndpointItemController' + controller: EndpointItemController, }); diff --git a/app/portainer/components/endpoint-list/endpoint-list-controller.js b/app/portainer/components/endpoint-list/endpoint-list-controller.js index 0abf59e8e..ca5fe52d9 100644 --- a/app/portainer/components/endpoint-list/endpoint-list-controller.js +++ b/app/portainer/components/endpoint-list/endpoint-list-controller.js @@ -29,12 +29,12 @@ angular.module('portainer.app').controller('EndpointListController', ['Datatable if (this.hasBackendPagination()) { this.paginationChangedAction(); } else { - this.state.filteredEndpoints = frontEndpointFilter(this.endpoints, filterValue); + this.state.filteredEndpoints = frontEndpointFilter(this.endpoints, this.tags, filterValue); this.state.loading = false; } } - function frontEndpointFilter(endpoints, filterValue) { + function frontEndpointFilter(endpoints, tags, filterValue) { if (!endpoints || !endpoints.length || !filterValue) { return endpoints; } @@ -47,8 +47,12 @@ angular.module('portainer.app').controller('EndpointListController', ['Datatable _.includes(endpoint.Name.toLowerCase(), lowerCaseKeyword) || _.includes(endpoint.GroupName.toLowerCase(), lowerCaseKeyword) || _.includes(endpoint.URL.toLowerCase(), lowerCaseKeyword) || - _.some(endpoint.Tags, function(tag) { - return _.includes(tag.toLowerCase(), lowerCaseKeyword); + _.some(endpoint.TagIds, tagId => { + const tag = tags.find(t => t.Id === tagId); + if (!tag) { + return false; + } + return _.includes(tag.Name.toLowerCase(), lowerCaseKeyword); }) || _.includes(statusString, keyword) ); diff --git a/app/portainer/components/endpoint-list/endpoint-list.js b/app/portainer/components/endpoint-list/endpoint-list.js index 06835c9e0..1bf184749 100644 --- a/app/portainer/components/endpoint-list/endpoint-list.js +++ b/app/portainer/components/endpoint-list/endpoint-list.js @@ -5,6 +5,7 @@ angular.module('portainer.app').component('endpointList', { titleText: '@', titleIcon: '@', endpoints: '<', + tags: '<', tableKey: '@', dashboardAction: '<', snapshotAction: '<', diff --git a/app/portainer/components/endpoint-list/endpointList.html b/app/portainer/components/endpoint-list/endpointList.html index 028ccc122..d6fd453a3 100644 --- a/app/portainer/components/endpoint-list/endpointList.html +++ b/app/portainer/components/endpoint-list/endpointList.html @@ -33,6 +33,7 @@ on-select="$ctrl.dashboardAction" on-edit="$ctrl.editAction" is-admin="$ctrl.isAdmin" + tags="$ctrl.tags" >
Loading... diff --git a/app/portainer/components/forms/group-form/groupForm.html b/app/portainer/components/forms/group-form/groupForm.html index f16d3bd2c..addd43a01 100644 --- a/app/portainer/components/forms/group-form/groupForm.html +++ b/app/portainer/components/forms/group-form/groupForm.html @@ -28,8 +28,9 @@
diff --git a/app/portainer/components/forms/schedule-form/schedule-form.js b/app/portainer/components/forms/schedule-form/schedule-form.js index 1ca8de98b..557331f24 100644 --- a/app/portainer/components/forms/schedule-form/schedule-form.js +++ b/app/portainer/components/forms/schedule-form/schedule-form.js @@ -69,6 +69,7 @@ angular.module('portainer.app').component('scheduleForm', { model: '=', endpoints: '<', groups: '<', + tags: '<', addLabelAction: '<', removeLabelAction: '<', formAction: '<', diff --git a/app/portainer/components/forms/schedule-form/scheduleForm.html b/app/portainer/components/forms/schedule-form/scheduleForm.html index 83772022d..86e8bdc00 100644 --- a/app/portainer/components/forms/schedule-form/scheduleForm.html +++ b/app/portainer/components/forms/schedule-form/scheduleForm.html @@ -265,9 +265,9 @@
diff --git a/app/portainer/components/multi-endpoint-selector/multi-endpoint-selector.js b/app/portainer/components/multi-endpoint-selector/multi-endpoint-selector.js index 63e0683c9..1862307f0 100644 --- a/app/portainer/components/multi-endpoint-selector/multi-endpoint-selector.js +++ b/app/portainer/components/multi-endpoint-selector/multi-endpoint-selector.js @@ -2,8 +2,9 @@ angular.module('portainer.app').component('multiEndpointSelector', { templateUrl: './multiEndpointSelector.html', controller: 'MultiEndpointSelectorController', bindings: { - 'model': '=', - 'endpoints': '<', - 'groups': '<' - } + model: '=', + endpoints: '<', + groups: '<', + tags: '<', + }, }); diff --git a/app/portainer/components/multi-endpoint-selector/multiEndpointSelector.html b/app/portainer/components/multi-endpoint-selector/multiEndpointSelector.html index 5acf2149d..534a3c91c 100644 --- a/app/portainer/components/multi-endpoint-selector/multiEndpointSelector.html +++ b/app/portainer/components/multi-endpoint-selector/multiEndpointSelector.html @@ -2,13 +2,13 @@ {{ $item.Name }} - ({{ $item.Tags | arraytostr }}) - + ({{ $ctrl.tagIdsToTagNames($item.TagIds) | arraytostr }}) +
{{ endpoint.Name }} - ({{ endpoint.Tags | arraytostr }}) - + ({{ $ctrl.tagIdsToTagNames(endpoint.TagIds) | arraytostr }}) +
diff --git a/app/portainer/components/multi-endpoint-selector/multiEndpointSelectorController.js b/app/portainer/components/multi-endpoint-selector/multiEndpointSelectorController.js index 3b67fd6be..4f3885825 100644 --- a/app/portainer/components/multi-endpoint-selector/multiEndpointSelectorController.js +++ b/app/portainer/components/multi-endpoint-selector/multiEndpointSelectorController.js @@ -1,37 +1,40 @@ import _ from 'lodash-es'; +import PortainerEndpointTagHelper from 'Portainer/helpers/tagHelper'; -angular.module('portainer.app') -.controller('MultiEndpointSelectorController', function () { - var ctrl = this; +import angular from 'angular'; - this.sortGroups = function(groups) { +class MultiEndpointSelectorController { + /* @ngInject */ + constructor() { + this.sortGroups = this.sortGroups.bind(this); + this.groupEndpoints = this.groupEndpoints.bind(this); + this.tagIdsToTagNames = this.tagIdsToTagNames.bind(this); + } + + sortGroups(groups) { return _.sortBy(groups, ['name']); - }; + } - this.groupEndpoints = function(endpoint) { - for (var i = 0; i < ctrl.availableGroups.length; i++) { - var group = ctrl.availableGroups[i]; + groupEndpoints(endpoint) { + for (var i = 0; i < this.availableGroups.length; i++) { + var group = this.availableGroups[i]; if (endpoint.GroupId === group.Id) { return group.Name; } } - }; - - this.$onInit = function() { - this.availableGroups = filterEmptyGroups(this.groups, this.endpoints); - }; - - function filterEmptyGroups(groups, endpoints) { - return groups.filter(function f(group) { - for (var i = 0; i < endpoints.length; i++) { - - var endpoint = endpoints[i]; - if (endpoint.GroupId === group.Id) { - return true; - } - } - return false; - }); } -}); + + tagIdsToTagNames(tagIds) { + return PortainerEndpointTagHelper.idsToTagNames(this.tags, tagIds); + } + + $onInit() { + this.availableGroups = _.filter(this.groups, group => + _.some(this.endpoints, endpoint => endpoint.GroupId == group.Id) + ); + } +} + +export default MultiEndpointSelectorController; +angular.module('portainer.app').controller('MultiEndpointSelectorController', MultiEndpointSelectorController); diff --git a/app/portainer/components/tag-selector/tag-selector.js b/app/portainer/components/tag-selector/tag-selector.js index a29a5eeaf..540b16902 100644 --- a/app/portainer/components/tag-selector/tag-selector.js +++ b/app/portainer/components/tag-selector/tag-selector.js @@ -3,6 +3,6 @@ angular.module('portainer.app').component('tagSelector', { controller: 'TagSelectorController', bindings: { tags: '<', - model: '=' - } + model: '=', + }, }); diff --git a/app/portainer/components/tag-selector/tagSelector.html b/app/portainer/components/tag-selector/tagSelector.html index be92e1aa4..d453f1acc 100644 --- a/app/portainer/components/tag-selector/tagSelector.html +++ b/app/portainer/components/tag-selector/tagSelector.html @@ -3,8 +3,8 @@ Selected tags
- - {{ tag }} + + {{ tag.Name }} @@ -20,10 +20,11 @@ type="text" ng-model="$ctrl.state.selectedValue" id="tags" class="form-control" placeholder="Select tags..." - uib-typeahead="tag for tag in $ctrl.tags | filter:$viewValue | limitTo:7" + uib-typeahead="tag.Id as tag.Name for tag in $ctrl.tags | filter: $ctrl.filterSelected | filter:$viewValue | limitTo:7" typeahead-on-select="$ctrl.selectTag($item, $model, $label)" typeahead-no-results="$ctrl.state.noResult" - typeahead-show-hint="true" typeahead-min-length="0" /> + typeahead-show-hint="true" typeahead-min-length="0" + />
diff --git a/app/portainer/components/tag-selector/tagSelectorController.js b/app/portainer/components/tag-selector/tagSelectorController.js index d575f8ab2..3d64485e3 100644 --- a/app/portainer/components/tag-selector/tagSelectorController.js +++ b/app/portainer/components/tag-selector/tagSelectorController.js @@ -1,32 +1,35 @@ import _ from 'lodash-es'; -angular.module('portainer.app') -.controller('TagSelectorController', function () { - - this.$onChanges = function(changes) { - if(angular.isDefined(changes.tags.currentValue)) { - this.tags = _.difference(changes.tags.currentValue, this.model); - } +angular.module('portainer.app').controller('TagSelectorController', function() { + this.$onInit = function() { + this.state.selectedTags = _.map(this.model, (id) => _.find(this.tags, (t) => t.Id === id)); }; this.state = { selectedValue: '', - noResult: false + selectedTags: [], + noResult: false, }; this.selectTag = function($item) { this.state.selectedValue = ''; - this.model.push($item); - this.tags = _.remove(this.tags, function(item) { - return item !== $item; - }); + this.model.push($item.Id); + this.state.selectedTags.push($item); }; - this.removeTag = function(tag) { - var idx = this.model.indexOf(tag); - if (idx > -1) { - this.model.splice(idx, 1); - this.tags.push(tag); - } + this.removeTag = function removeTag(tag) { + _.remove(this.state.selectedTags, { Id: tag.Id }); + _.remove(this.model, (id) => id === tag.Id); }; + + this.filterSelected = filterSelected.bind(this); + + function filterSelected($item) { + if (!this.model) { + return true; + } + return !_.includes(this.model, $item.Id); + } + window._remove = _.remove; }); + diff --git a/app/portainer/helpers/tagHelper.js b/app/portainer/helpers/tagHelper.js new file mode 100644 index 000000000..e07ba6c36 --- /dev/null +++ b/app/portainer/helpers/tagHelper.js @@ -0,0 +1,9 @@ +import _ from 'lodash'; + +export default class PortainerEndpointTagHelper { + static idsToTagNames(tags, ids) { + const filteredTags = _.filter(tags, tag => _.includes(ids, tag.Id)); + const tagNames = _.map(filteredTags, 'Name'); + return tagNames; + } +} diff --git a/app/portainer/models/group.js b/app/portainer/models/group.js index 79b9b3e53..ca806f582 100644 --- a/app/portainer/models/group.js +++ b/app/portainer/models/group.js @@ -1,14 +1,14 @@ export function EndpointGroupDefaultModel() { this.Name = ''; this.Description = ''; - this.Tags = []; + this.TagIds = []; } export function EndpointGroupModel(data) { this.Id = data.Id; this.Name = data.Name; this.Description = data.Description; - this.Tags = data.Tags; + this.TagIds = data.TagIds; this.AuthorizedUsers = data.AuthorizedUsers; this.AuthorizedTeams = data.AuthorizedTeams; this.UserAccessPolicies = data.UserAccessPolicies; @@ -18,7 +18,7 @@ export function EndpointGroupModel(data) { export function EndpointGroupCreateRequest(model, endpoints) { this.Name = model.Name; this.Description = model.Description; - this.Tags = model.Tags; + this.TagIds = model.TagIds; this.AssociatedEndpoints = endpoints; } @@ -26,7 +26,7 @@ export function EndpointGroupUpdateRequest(model, endpoints) { this.id = model.Id; this.Name = model.Name; this.Description = model.Description; - this.Tags = model.Tags; + this.TagIds = model.TagIds; this.AssociatedEndpoints = endpoints; this.UserAccessPolicies = model.UserAccessPolicies; this.TeamAccessPolicies = model.TeamAccessPolicies; diff --git a/app/portainer/services/api/endpointService.js b/app/portainer/services/api/endpointService.js index 7a43ee5b2..6de03711d 100644 --- a/app/portainer/services/api/endpointService.js +++ b/app/portainer/services/api/endpointService.js @@ -63,7 +63,7 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) { return deferred.promise; }; - service.createRemoteEndpoint = function(name, type, URL, PublicURL, groupID, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + service.createRemoteEndpoint = function(name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { var deferred = $q.defer(); var endpointURL = URL; @@ -71,7 +71,7 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) { endpointURL = 'tcp://' + URL; } - FileUploadService.createEndpoint(name, type, endpointURL, PublicURL, groupID, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) + FileUploadService.createEndpoint(name, type, endpointURL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) .then(function success(response) { deferred.resolve(response.data); }) @@ -82,10 +82,10 @@ function EndpointServiceFactory($q, Endpoints, FileUploadService) { return deferred.promise; }; - service.createAzureEndpoint = function(name, applicationId, tenantId, authenticationKey, groupId, tags) { + service.createAzureEndpoint = function(name, applicationId, tenantId, authenticationKey, groupId, tagIds) { var deferred = $q.defer(); - FileUploadService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tags) + FileUploadService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) .then(function success(response) { deferred.resolve(response.data); }) diff --git a/app/portainer/services/fileUpload.js b/app/portainer/services/fileUpload.js index 1f17916bc..07baed73f 100644 --- a/app/portainer/services/fileUpload.js +++ b/app/portainer/services/fileUpload.js @@ -100,7 +100,7 @@ angular.module('portainer.app') }); }; - service.createEndpoint = function(name, type, URL, PublicURL, groupID, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + service.createEndpoint = function(name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { return Upload.upload({ url: 'api/endpoints', data: { @@ -109,7 +109,7 @@ angular.module('portainer.app') URL: URL, PublicURL: PublicURL, GroupID: groupID, - Tags: Upload.json(tags), + TagIds: Upload.json(tagIds), TLS: TLS, TLSSkipVerify: TLSSkipVerify, TLSSkipClientVerify: TLSSkipClientVerify, @@ -121,14 +121,14 @@ angular.module('portainer.app') }); }; - service.createAzureEndpoint = function(name, applicationId, tenantId, authenticationKey, groupId, tags) { + service.createAzureEndpoint = function(name, applicationId, tenantId, authenticationKey, groupId, tagIds) { return Upload.upload({ url: 'api/endpoints', data: { Name: name, EndpointType: 3, GroupID: groupId, - Tags: Upload.json(tags), + TagIds: Upload.json(tagIds), AzureApplicationID: applicationId, AzureTenantID: tenantId, AzureAuthenticationKey: authenticationKey diff --git a/app/portainer/views/endpoints/create/createEndpointController.js b/app/portainer/views/endpoints/create/createEndpointController.js index 1cdf54409..968aa041b 100644 --- a/app/portainer/views/endpoints/create/createEndpointController.js +++ b/app/portainer/views/endpoints/create/createEndpointController.js @@ -18,7 +18,7 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, AzureApplicationId: '', AzureTenantId: '', AzureAuthenticationKey: '', - Tags: [] + TagIds: [] }; $scope.copyAgentCommand = function() { @@ -40,7 +40,7 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, var URL = $filter('stripprotocol')($scope.formValues.URL); var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL; var groupId = $scope.formValues.GroupId; - var tags = $scope.formValues.Tags; + var tagIds = $scope.formValues.TagIds; var securityData = $scope.formValues.SecurityFormData; var TLS = securityData.TLS; @@ -51,7 +51,7 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert; var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey; - addEndpoint(name, 1, URL, publicURL, groupId, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile); + addEndpoint(name, 1, URL, publicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile); }; $scope.addAgentEndpoint = function() { @@ -59,18 +59,18 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, var URL = $filter('stripprotocol')($scope.formValues.URL); var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL; var groupId = $scope.formValues.GroupId; - var tags = $scope.formValues.Tags; + var tagIds = $scope.formValues.TagIds; - addEndpoint(name, 2, URL, publicURL, groupId, tags, true, true, true, null, null, null); + addEndpoint(name, 2, URL, publicURL, groupId, tagIds, true, true, true, null, null, null); }; $scope.addEdgeAgentEndpoint = function() { var name = $scope.formValues.Name; var groupId = $scope.formValues.GroupId; - var tags = $scope.formValues.Tags; + var tagIds = $scope.formValues.TagIds; var URL = $scope.formValues.URL; - addEndpoint(name, 4, URL, "", groupId, tags, false, false, false, null, null, null); + addEndpoint(name, 4, URL, "", groupId, tagIds, false, false, false, null, null, null); }; $scope.addAzureEndpoint = function() { @@ -79,14 +79,14 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, var tenantId = $scope.formValues.AzureTenantId; var authenticationKey = $scope.formValues.AzureAuthenticationKey; var groupId = $scope.formValues.GroupId; - var tags = $scope.formValues.Tags; + var tagIds = $scope.formValues.TagIds; - createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tags); + createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds); }; - function createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tags) { + function createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) { $scope.state.actionInProgress = true; - EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tags) + EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) .then(function success() { Notifications.success('Endpoint created', name); $state.go('portainer.endpoints', {}, {reload: true}); @@ -99,9 +99,9 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, }); } - function addEndpoint(name, type, URL, PublicURL, groupId, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + function addEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { $scope.state.actionInProgress = true; - EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, groupId, tags, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) + EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) .then(function success(data) { Notifications.success('Endpoint created', name); if (type === 4) { @@ -121,7 +121,7 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, function initView() { $q.all({ groups: GroupService.groups(), - tags: TagService.tagNames() + tags: TagService.tags() }) .then(function success(data) { $scope.groups = data.groups; diff --git a/app/portainer/views/endpoints/create/createendpoint.html b/app/portainer/views/endpoints/create/createendpoint.html index b930e2518..89308e179 100644 --- a/app/portainer/views/endpoints/create/createendpoint.html +++ b/app/portainer/views/endpoints/create/createendpoint.html @@ -259,8 +259,9 @@
diff --git a/app/portainer/views/endpoints/edit/endpoint.html b/app/portainer/views/endpoints/edit/endpoint.html index 81f840a1c..d33e8bd7a 100644 --- a/app/portainer/views/endpoints/edit/endpoint.html +++ b/app/portainer/views/endpoints/edit/endpoint.html @@ -165,8 +165,9 @@
diff --git a/app/portainer/views/endpoints/edit/endpointController.js b/app/portainer/views/endpoints/edit/endpointController.js index aa023d509..b01a5f1ad 100644 --- a/app/portainer/views/endpoints/edit/endpointController.js +++ b/app/portainer/views/endpoints/edit/endpointController.js @@ -41,12 +41,12 @@ function ($q, $scope, $state, $transition$, $filter, clipboard, EndpointService, var TLSMode = securityData.TLSMode; var TLSSkipVerify = TLS && (TLSMode === 'tls_client_noca' || TLSMode === 'tls_only'); var TLSSkipClientVerify = TLS && (TLSMode === 'tls_ca' || TLSMode === 'tls_only'); - + var payload = { Name: endpoint.Name, PublicURL: endpoint.PublicURL, GroupID: endpoint.GroupId, - Tags: endpoint.Tags, + TagIds: endpoint.TagIds, TLS: TLS, TLSSkipVerify: TLSSkipVerify, TLSSkipClientVerify: TLSSkipClientVerify, @@ -96,7 +96,7 @@ function ($q, $scope, $state, $transition$, $filter, clipboard, EndpointService, $q.all({ endpoint: EndpointService.endpoint($transition$.params().id), groups: GroupService.groups(), - tags: TagService.tagNames() + tags: TagService.tags() }) .then(function success(data) { var endpoint = data.endpoint; diff --git a/app/portainer/views/groups/create/createGroupController.js b/app/portainer/views/groups/create/createGroupController.js index 0f1e86ad3..109e70ca1 100644 --- a/app/portainer/views/groups/create/createGroupController.js +++ b/app/portainer/views/groups/create/createGroupController.js @@ -32,7 +32,7 @@ function ($q, $scope, $state, GroupService, EndpointService, TagService, Notific }; function initView() { - TagService.tagNames() + TagService.tags() .then((tags) => { $scope.availableTags = tags; $scope.associatedEndpoints = []; diff --git a/app/portainer/views/groups/edit/groupController.js b/app/portainer/views/groups/edit/groupController.js index ed3967828..d173e40b1 100644 --- a/app/portainer/views/groups/edit/groupController.js +++ b/app/portainer/views/groups/edit/groupController.js @@ -28,7 +28,7 @@ function ($q, $scope, $state, $transition$, GroupService, TagService, Notificati $q.all({ group: GroupService.group(groupId), - tags: TagService.tagNames() + tags: TagService.tags() }) .then(function success(data) { $scope.group = data.group; diff --git a/app/portainer/views/home/home.html b/app/portainer/views/home/home.html index dea3dff6c..043cfa1f0 100644 --- a/app/portainer/views/home/home.html +++ b/app/portainer/views/home/home.html @@ -34,6 +34,7 @@