2018-06-11 13:13:19 +00:00
package endpoints
import (
"net/http"
2019-07-20 23:28:11 +00:00
"strconv"
"strings"
"github.com/portainer/libhttp/request"
2018-06-11 13:13:19 +00:00
2018-09-10 10:01:38 +00:00
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
2021-01-25 19:16:53 +00:00
portainer "github.com/portainer/portainer/api"
2019-03-21 01:20:14 +00:00
"github.com/portainer/portainer/api/http/security"
2018-06-11 13:13:19 +00:00
)
2021-02-23 03:21:39 +00:00
// @id EndpointList
2021-09-20 00:14:22 +00:00
// @summary List environments(endpoints)
// @description List all environments(endpoints) based on the current user authorizations. Will
// @description return all environments(endpoints) if using an administrator account otherwise it will
// @description only return authorized environments(endpoints).
2021-02-23 03:21:39 +00:00
// @description **Access policy**: restricted
// @tags endpoints
2021-11-30 02:31:16 +00:00
// @security ApiKeyAuth
2021-02-23 03:21:39 +00:00
// @security jwt
// @produce json
// @param start query int false "Start searching from"
// @param search query string false "Search query"
2021-09-20 00:14:22 +00:00
// @param groupId query int false "List environments(endpoints) of this group"
2021-02-23 03:21:39 +00:00
// @param limit query int false "Limit results to this value"
2021-09-20 00:14:22 +00:00
// @param types query []int false "List environments(endpoints) of this type"
// @param tagIds query []int false "search environments(endpoints) with these tags (depends on tagsPartialMatch)"
// @param tagsPartialMatch query bool false "If true, will return environment(endpoint) which has one of tagIds, if false (or missing) will return only environments(endpoints) that has all the tags"
// @param endpointIds query []int false "will return only these environments(endpoints)"
2021-02-23 03:21:39 +00:00
// @success 200 {array} portainer.Endpoint "Endpoints"
2021-09-13 03:42:53 +00:00
// @failure 500 "Server error"
2021-02-23 03:21:39 +00:00
// @router /endpoints [get]
2018-06-11 13:13:19 +00:00
func ( handler * Handler ) endpointList ( w http . ResponseWriter , r * http . Request ) * httperror . HandlerError {
2019-07-20 23:28:11 +00:00
start , _ := request . RetrieveNumericQueryParameter ( r , "start" , true )
if start != 0 {
start --
}
search , _ := request . RetrieveQueryParameter ( r , "search" , true )
if search != "" {
search = strings . ToLower ( search )
2018-06-11 13:13:19 +00:00
}
2019-07-20 23:28:11 +00:00
groupID , _ := request . RetrieveNumericQueryParameter ( r , "groupId" , true )
limit , _ := request . RetrieveNumericQueryParameter ( r , "limit" , true )
2021-07-21 07:16:22 +00:00
var endpointTypes [ ] int
request . RetrieveJSONQueryParameter ( r , "types" , & endpointTypes , true )
2019-07-20 23:28:11 +00:00
2020-04-08 09:14:50 +00:00
var tagIDs [ ] portainer . TagID
request . RetrieveJSONQueryParameter ( r , "tagIds" , & tagIDs , true )
2020-05-14 02:14:28 +00:00
tagsPartialMatch , _ := request . RetrieveBooleanQueryParameter ( r , "tagsPartialMatch" , true )
2020-04-08 09:14:50 +00:00
var endpointIDs [ ] portainer . EndpointID
request . RetrieveJSONQueryParameter ( r , "endpointIds" , & endpointIDs , true )
2020-05-20 05:23:15 +00:00
endpointGroups , err := handler . DataStore . EndpointGroup ( ) . EndpointGroups ( )
2018-06-11 13:13:19 +00:00
if err != nil {
2021-09-08 08:42:17 +00:00
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve environment groups from the database" , err }
2018-06-11 13:13:19 +00:00
}
2020-05-20 05:23:15 +00:00
endpoints , err := handler . DataStore . Endpoint ( ) . Endpoints ( )
2019-07-20 23:28:11 +00:00
if err != nil {
2021-09-08 08:42:17 +00:00
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve environments from the database" , err }
2019-07-20 23:28:11 +00:00
}
2021-03-11 20:00:05 +00:00
settings , err := handler . DataStore . Settings ( ) . Settings ( )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve settings from the database" , err }
}
2018-06-11 13:13:19 +00:00
securityContext , err := security . RetrieveRestrictedRequestContext ( r )
if err != nil {
return & httperror . HandlerError { http . StatusInternalServerError , "Unable to retrieve info from request context" , err }
}
filteredEndpoints := security . FilterEndpoints ( endpoints , endpointGroups , securityContext )
2022-03-08 12:14:23 +00:00
totalAvailableEndpoints := len ( filteredEndpoints )
2018-06-11 13:13:19 +00:00
2020-04-08 09:14:50 +00:00
if endpointIDs != nil {
filteredEndpoints = filteredEndpointsByIds ( filteredEndpoints , endpointIDs )
}
2019-07-20 23:28:11 +00:00
if groupID != 0 {
filteredEndpoints = filterEndpointsByGroupID ( filteredEndpoints , portainer . EndpointGroupID ( groupID ) )
}
2022-01-23 19:48:04 +00:00
edgeDeviceFilter , edgeDeviceFilterErr := request . RetrieveBooleanQueryParameter ( r , "edgeDeviceFilter" , false )
if edgeDeviceFilterErr == nil {
filteredEndpoints = filterEndpointsByEdgeDevice ( filteredEndpoints , edgeDeviceFilter )
}
2019-07-20 23:28:11 +00:00
if search != "" {
2020-05-20 05:23:15 +00:00
tags , err := handler . DataStore . Tag ( ) . Tags ( )
2020-03-29 09:54:14 +00:00
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 )
2019-07-20 23:28:11 +00:00
}
2021-07-21 07:16:22 +00:00
if endpointTypes != nil {
filteredEndpoints = filterEndpointsByTypes ( filteredEndpoints , endpointTypes )
2020-03-26 05:44:27 +00:00
}
2020-04-08 09:14:50 +00:00
if tagIDs != nil {
2020-05-14 02:14:28 +00:00
filteredEndpoints = filteredEndpointsByTags ( filteredEndpoints , tagIDs , endpointGroups , tagsPartialMatch )
2020-04-08 09:14:50 +00:00
}
2019-07-20 23:28:11 +00:00
filteredEndpointCount := len ( filteredEndpoints )
paginatedEndpoints := paginateEndpoints ( filteredEndpoints , start , limit )
for idx := range paginatedEndpoints {
hideFields ( & paginatedEndpoints [ idx ] )
2021-01-25 19:16:53 +00:00
paginatedEndpoints [ idx ] . ComposeSyntaxMaxVersion = handler . ComposeStackManager . ComposeSyntaxMaxVersion ( )
2021-03-11 20:00:05 +00:00
if paginatedEndpoints [ idx ] . EdgeCheckinInterval == 0 {
paginatedEndpoints [ idx ] . EdgeCheckinInterval = settings . EdgeAgentCheckinInterval
}
2019-07-20 23:28:11 +00:00
}
w . Header ( ) . Set ( "X-Total-Count" , strconv . Itoa ( filteredEndpointCount ) )
2022-03-08 12:14:23 +00:00
w . Header ( ) . Set ( "X-Total-Available" , strconv . Itoa ( totalAvailableEndpoints ) )
2019-07-20 23:28:11 +00:00
return response . JSON ( w , paginatedEndpoints )
}
func paginateEndpoints ( endpoints [ ] portainer . Endpoint , start , limit int ) [ ] portainer . Endpoint {
if limit == 0 {
return endpoints
}
endpointCount := len ( endpoints )
if start > endpointCount {
start = endpointCount
}
end := start + limit
if end > endpointCount {
end = endpointCount
}
return endpoints [ start : end ]
}
func filterEndpointsByGroupID ( endpoints [ ] portainer . Endpoint , endpointGroupID portainer . EndpointGroupID ) [ ] portainer . Endpoint {
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
for _ , endpoint := range endpoints {
if endpoint . GroupID == endpointGroupID {
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}
2020-03-29 09:54:14 +00:00
func filterEndpointsBySearchCriteria ( endpoints [ ] portainer . Endpoint , endpointGroups [ ] portainer . EndpointGroup , tagsMap map [ portainer . TagID ] string , searchCriteria string ) [ ] portainer . Endpoint {
2019-07-20 23:28:11 +00:00
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
for _ , endpoint := range endpoints {
2020-03-29 09:54:14 +00:00
endpointTags := convertTagIDsToTags ( tagsMap , endpoint . TagIDs )
if endpointMatchSearchCriteria ( & endpoint , endpointTags , searchCriteria ) {
2019-07-20 23:28:11 +00:00
filteredEndpoints = append ( filteredEndpoints , endpoint )
continue
}
2020-03-29 09:54:14 +00:00
if endpointGroupMatchSearchCriteria ( & endpoint , endpointGroups , tagsMap , searchCriteria ) {
2019-07-20 23:28:11 +00:00
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}
2020-03-29 09:54:14 +00:00
func endpointMatchSearchCriteria ( endpoint * portainer . Endpoint , tags [ ] string , searchCriteria string ) bool {
2019-07-20 23:28:11 +00:00
if strings . Contains ( strings . ToLower ( endpoint . Name ) , searchCriteria ) {
return true
}
if strings . Contains ( strings . ToLower ( endpoint . URL ) , searchCriteria ) {
return true
}
if endpoint . Status == portainer . EndpointStatusUp && searchCriteria == "up" {
return true
} else if endpoint . Status == portainer . EndpointStatusDown && searchCriteria == "down" {
return true
}
2020-03-29 09:54:14 +00:00
for _ , tag := range tags {
2019-07-20 23:28:11 +00:00
if strings . Contains ( strings . ToLower ( tag ) , searchCriteria ) {
return true
}
}
return false
}
2020-03-29 09:54:14 +00:00
func endpointGroupMatchSearchCriteria ( endpoint * portainer . Endpoint , endpointGroups [ ] portainer . EndpointGroup , tagsMap map [ portainer . TagID ] string , searchCriteria string ) bool {
2019-07-20 23:28:11 +00:00
for _ , group := range endpointGroups {
if group . ID == endpoint . GroupID {
if strings . Contains ( strings . ToLower ( group . Name ) , searchCriteria ) {
return true
}
2020-03-29 09:54:14 +00:00
tags := convertTagIDsToTags ( tagsMap , group . TagIDs )
for _ , tag := range tags {
2019-07-20 23:28:11 +00:00
if strings . Contains ( strings . ToLower ( tag ) , searchCriteria ) {
return true
}
}
}
2018-06-11 13:13:19 +00:00
}
2018-07-31 09:50:04 +00:00
2019-07-20 23:28:11 +00:00
return false
2018-06-11 13:13:19 +00:00
}
2020-03-26 05:44:27 +00:00
2021-07-21 07:16:22 +00:00
func filterEndpointsByTypes ( endpoints [ ] portainer . Endpoint , endpointTypes [ ] int ) [ ] portainer . Endpoint {
2020-03-26 05:44:27 +00:00
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
2021-07-21 07:16:22 +00:00
typeSet := map [ portainer . EndpointType ] bool { }
for _ , endpointType := range endpointTypes {
typeSet [ portainer . EndpointType ( endpointType ) ] = true
}
2020-03-26 05:44:27 +00:00
for _ , endpoint := range endpoints {
2021-07-21 07:16:22 +00:00
if typeSet [ endpoint . Type ] {
2020-03-26 05:44:27 +00:00
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}
2020-03-29 09:54:14 +00:00
2022-01-23 19:48:04 +00:00
func filterEndpointsByEdgeDevice ( endpoints [ ] portainer . Endpoint , edgeDeviceFilter bool ) [ ] portainer . Endpoint {
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
for _ , endpoint := range endpoints {
if edgeDeviceFilter == endpoint . IsEdgeDevice {
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}
2020-03-29 09:54:14 +00:00
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
}
2020-04-08 09:14:50 +00:00
2020-05-14 02:14:28 +00:00
func filteredEndpointsByTags ( endpoints [ ] portainer . Endpoint , tagIDs [ ] portainer . TagID , endpointGroups [ ] portainer . EndpointGroup , partialMatch bool ) [ ] portainer . Endpoint {
2020-04-08 09:14:50 +00:00
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
for _ , endpoint := range endpoints {
2020-05-14 02:14:28 +00:00
endpointGroup := getEndpointGroup ( endpoint . GroupID , endpointGroups )
endpointMatched := false
if partialMatch {
endpointMatched = endpointPartialMatchTags ( endpoint , endpointGroup , tagIDs )
} else {
endpointMatched = endpointFullMatchTags ( endpoint , endpointGroup , tagIDs )
2020-04-08 09:14:50 +00:00
}
2020-05-14 02:14:28 +00:00
if endpointMatched {
2020-04-08 09:14:50 +00:00
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}
2020-05-14 02:14:28 +00:00
func getEndpointGroup ( groupID portainer . EndpointGroupID , groups [ ] portainer . EndpointGroup ) portainer . EndpointGroup {
2020-04-08 09:14:50 +00:00
var endpointGroup portainer . EndpointGroup
for _ , group := range groups {
if group . ID == groupID {
endpointGroup = group
break
}
}
2020-05-14 02:14:28 +00:00
return endpointGroup
}
func endpointPartialMatchTags ( endpoint portainer . Endpoint , endpointGroup portainer . EndpointGroup , tagIDs [ ] portainer . TagID ) bool {
tagSet := make ( map [ portainer . TagID ] bool )
for _ , tagID := range tagIDs {
tagSet [ tagID ] = true
}
for _ , tagID := range endpoint . TagIDs {
if tagSet [ tagID ] {
return true
}
}
for _ , tagID := range endpointGroup . TagIDs {
if tagSet [ tagID ] {
return true
}
}
return false
}
func endpointFullMatchTags ( endpoint portainer . Endpoint , endpointGroup portainer . EndpointGroup , tagIDs [ ] portainer . TagID ) bool {
missingTags := make ( map [ portainer . TagID ] bool )
for _ , tagID := range tagIDs {
missingTags [ tagID ] = true
}
for _ , tagID := range endpoint . TagIDs {
if missingTags [ tagID ] {
delete ( missingTags , tagID )
}
}
2020-04-08 09:14:50 +00:00
for _ , tagID := range endpointGroup . TagIDs {
if missingTags [ tagID ] {
delete ( missingTags , tagID )
}
}
2020-05-14 02:14:28 +00:00
return len ( missingTags ) == 0
2020-04-08 09:14:50 +00:00
}
func filteredEndpointsByIds ( endpoints [ ] portainer . Endpoint , ids [ ] portainer . EndpointID ) [ ] portainer . Endpoint {
filteredEndpoints := make ( [ ] portainer . Endpoint , 0 )
idsSet := make ( map [ portainer . EndpointID ] bool )
for _ , id := range ids {
idsSet [ id ] = true
}
for _ , endpoint := range endpoints {
if idsSet [ endpoint . ID ] {
filteredEndpoints = append ( filteredEndpoints , endpoint )
}
}
return filteredEndpoints
}