diff --git a/api/http/handler/endpoints/endpoint_create.go b/api/http/handler/endpoints/endpoint_create.go index e53d7f96d..d8a1bb898 100644 --- a/api/http/handler/endpoints/endpoint_create.go +++ b/api/http/handler/endpoints/endpoint_create.go @@ -30,6 +30,7 @@ type endpointCreatePayload struct { TLSCertFile []byte TLSKeyFile []byte TagIDs []portainer.TagID + EdgeCheckinInterval int } func (payload *endpointCreatePayload) Validate(r *http.Request) error { @@ -102,6 +103,9 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error { publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true) payload.PublicURL = publicURL + checkinInterval, _ := request.RetrieveNumericMultiPartFormValue(r, "CheckinInterval", true) + payload.EdgeCheckinInterval = checkinInterval + return nil } @@ -193,13 +197,14 @@ func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) TLSConfig: portainer.TLSConfiguration{ TLS: false, }, - AuthorizedUsers: []portainer.UserID{}, - AuthorizedTeams: []portainer.TeamID{}, - Extensions: []portainer.EndpointExtension{}, - TagIDs: payload.TagIDs, - Status: portainer.EndpointStatusUp, - Snapshots: []portainer.Snapshot{}, - EdgeKey: edgeKey, + AuthorizedUsers: []portainer.UserID{}, + AuthorizedTeams: []portainer.TeamID{}, + Extensions: []portainer.EndpointExtension{}, + TagIDs: payload.TagIDs, + Status: portainer.EndpointStatusUp, + Snapshots: []portainer.Snapshot{}, + EdgeKey: edgeKey, + EdgeCheckinInterval: payload.EdgeCheckinInterval, } err = handler.saveEndpointAndUpdateAuthorizations(endpoint) diff --git a/api/http/handler/endpoints/endpoint_status_inspect.go b/api/http/handler/endpoints/endpoint_status_inspect.go index b8b1c4d63..cb64bbfe8 100644 --- a/api/http/handler/endpoints/endpoint_status_inspect.go +++ b/api/http/handler/endpoints/endpoint_status_inspect.go @@ -60,11 +60,16 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req tunnel := handler.ReverseTunnelService.GetTunnelDetails(endpoint.ID) + checkinInterval := settings.EdgeAgentCheckinInterval + if endpoint.EdgeCheckinInterval != 0 { + checkinInterval = endpoint.EdgeCheckinInterval + } + statusResponse := endpointStatusInspectResponse{ Status: tunnel.Status, Port: tunnel.Port, Schedules: tunnel.Schedules, - CheckinInterval: settings.EdgeAgentCheckinInterval, + CheckinInterval: checkinInterval, Credentials: tunnel.Credentials, } diff --git a/api/http/handler/endpoints/endpoint_update.go b/api/http/handler/endpoints/endpoint_update.go index 6ba76c992..4752086c3 100644 --- a/api/http/handler/endpoints/endpoint_update.go +++ b/api/http/handler/endpoints/endpoint_update.go @@ -23,6 +23,7 @@ type endpointUpdatePayload struct { TagIDs []portainer.TagID UserAccessPolicies portainer.UserAccessPolicies TeamAccessPolicies portainer.TeamAccessPolicies + EdgeCheckinInterval *int } func (payload *endpointUpdatePayload) Validate(r *http.Request) error { @@ -61,6 +62,10 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) * endpoint.PublicURL = *payload.PublicURL } + if payload.EdgeCheckinInterval != nil { + endpoint.EdgeCheckinInterval = *payload.EdgeCheckinInterval + } + groupIDChanged := false if payload.GroupID != nil { groupID := portainer.EndpointGroupID(*payload.GroupID) diff --git a/api/portainer.go b/api/portainer.go index ae675174a..8386ce8f2 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -155,21 +155,23 @@ type ( // Endpoint represents a Docker endpoint with all the info required // to connect to it Endpoint struct { - ID EndpointID `json:"Id"` - Name string `json:"Name"` - Type EndpointType `json:"Type"` - URL string `json:"URL"` - GroupID EndpointGroupID `json:"GroupId"` - PublicURL string `json:"PublicURL"` - TLSConfig TLSConfiguration `json:"TLSConfig"` - Extensions []EndpointExtension `json:"Extensions"` - TagIDs []TagID `json:"TagIds"` - Status EndpointStatus `json:"Status"` - Snapshots []Snapshot `json:"Snapshots"` - UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"` - TeamAccessPolicies TeamAccessPolicies `json:"TeamAccessPolicies"` - EdgeID string `json:"EdgeID,omitempty"` - EdgeKey string `json:"EdgeKey"` + ID EndpointID `json:"Id"` + Name string `json:"Name"` + Type EndpointType `json:"Type"` + URL string `json:"URL"` + GroupID EndpointGroupID `json:"GroupId"` + PublicURL string `json:"PublicURL"` + TLSConfig TLSConfiguration `json:"TLSConfig"` + Extensions []EndpointExtension `json:"Extensions"` + TagIDs []TagID `json:"TagIds"` + Status EndpointStatus `json:"Status"` + Snapshots []Snapshot `json:"Snapshots"` + UserAccessPolicies UserAccessPolicies `json:"UserAccessPolicies"` + TeamAccessPolicies TeamAccessPolicies `json:"TeamAccessPolicies"` + EdgeID string `json:"EdgeID,omitempty"` + EdgeKey string `json:"EdgeKey"` + EdgeCheckinInterval int `json:"EdgeCheckinInterval"` + // Deprecated fields // Deprecated in DBVersion == 4 TLS bool `json:"TLS,omitempty"` diff --git a/app/portainer/services/api/endpointService.js b/app/portainer/services/api/endpointService.js index fd9b327b0..8d7cddd3f 100644 --- a/app/portainer/services/api/endpointService.js +++ b/app/portainer/services/api/endpointService.js @@ -65,7 +65,21 @@ angular.module('portainer.app').factory('EndpointService', [ return deferred.promise; }; - service.createRemoteEndpoint = function (name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + service.createRemoteEndpoint = function ( + name, + type, + URL, + PublicURL, + groupID, + tagIds, + TLS, + TLSSkipVerify, + TLSSkipClientVerify, + TLSCAFile, + TLSCertFile, + TLSKeyFile, + checkinInterval + ) { var deferred = $q.defer(); var endpointURL = URL; @@ -73,7 +87,21 @@ angular.module('portainer.app').factory('EndpointService', [ endpointURL = 'tcp://' + URL; } - FileUploadService.createEndpoint(name, type, endpointURL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) + FileUploadService.createEndpoint( + name, + type, + endpointURL, + PublicURL, + groupID, + tagIds, + TLS, + TLSSkipVerify, + TLSSkipClientVerify, + TLSCAFile, + TLSCertFile, + TLSKeyFile, + checkinInterval + ) .then(function success(response) { deferred.resolve(response.data); }) diff --git a/app/portainer/services/fileUpload.js b/app/portainer/services/fileUpload.js index 4f024d814..6477d75ba 100644 --- a/app/portainer/services/fileUpload.js +++ b/app/portainer/services/fileUpload.js @@ -91,12 +91,11 @@ angular.module('portainer.app').factory('FileUploadService', [ data: { file: file, Name: stackName, - EdgeGroups: Upload.json(edgeGroups) + EdgeGroups: Upload.json(edgeGroups), }, - ignoreLoadingBar: true + ignoreLoadingBar: true, }); }; - service.configureRegistry = function (registryId, registryManagementConfigurationModel) { return Upload.upload({ @@ -116,7 +115,7 @@ angular.module('portainer.app').factory('FileUploadService', [ }); }; - service.createEndpoint = function (name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + service.createEndpoint = function (name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile, checkinInterval) { return Upload.upload({ url: 'api/endpoints', data: { @@ -132,6 +131,7 @@ angular.module('portainer.app').factory('FileUploadService', [ TLSCACertFile: TLSCAFile, TLSCertFile: TLSCertFile, TLSKeyFile: TLSKeyFile, + CheckinInterval: checkinInterval, }, ignoreLoadingBar: true, }); diff --git a/app/portainer/views/endpoints/create/createEndpointController.js b/app/portainer/views/endpoints/create/createEndpointController.js index a1c6b6003..bea559dd7 100644 --- a/app/portainer/views/endpoints/create/createEndpointController.js +++ b/app/portainer/views/endpoints/create/createEndpointController.js @@ -12,6 +12,7 @@ angular EndpointService, GroupService, TagService, + SettingsService, Notifications, Authentication ) { @@ -19,6 +20,24 @@ angular EnvironmentType: 'agent', actionInProgress: false, allowCreateTag: Authentication.isAdmin(), + availableEdgeAgentCheckinOptions: [ + { key: 'Use default interval', value: 0 }, + { + key: '5 seconds', + value: 5, + }, + { + key: '10 seconds', + value: 10, + }, + { + key: '30 seconds', + value: 30, + }, + { key: '5 minutes', value: 300 }, + { key: '1 hour', value: 3600 }, + { key: '1 day', value: 86400 }, + ], }; $scope.formValues = { @@ -28,6 +47,7 @@ angular GroupId: 1, SecurityFormData: new EndpointSecurityFormData(), TagIds: [], + CheckinInterval: $scope.state.availableEdgeAgentCheckinOptions[0].value, }; $scope.copyAgentCommand = function () { @@ -79,7 +99,7 @@ angular var tagIds = $scope.formValues.TagIds; var URL = $scope.formValues.URL; - addEndpoint(name, 4, URL, '', groupId, tagIds, false, false, false, null, null, null); + addEndpoint(name, 4, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval); }; $scope.onCreateTag = function onCreateTag(tagName) { @@ -96,9 +116,23 @@ angular } } - function addEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { + function addEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile, CheckinInterval) { $scope.state.actionInProgress = true; - EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) + EndpointService.createRemoteEndpoint( + name, + type, + URL, + PublicURL, + groupId, + tagIds, + TLS, + TLSSkipVerify, + TLSSkipClientVerify, + TLSCAFile, + TLSCertFile, + TLSKeyFile, + CheckinInterval + ) .then(function success(data) { Notifications.success('Endpoint created', name); if (type === 4) { @@ -119,10 +153,14 @@ angular $q.all({ groups: GroupService.groups(), tags: TagService.tags(), + settings: SettingsService.settings(), }) .then(function success(data) { $scope.groups = data.groups; $scope.availableTags = data.tags; + + const settings = data.settings; + $scope.state.availableEdgeAgentCheckinOptions[0].key += ` (${settings.EdgeAgentCheckinInterval} seconds)`; }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to load groups'); diff --git a/app/portainer/views/endpoints/create/createendpoint.html b/app/portainer/views/endpoints/create/createendpoint.html index 4a366a22e..4ab68d840 100644 --- a/app/portainer/views/endpoints/create/createendpoint.html +++ b/app/portainer/views/endpoints/create/createendpoint.html @@ -145,8 +145,8 @@ -
+
+ +
+ +
+ +
+
-
diff --git a/app/portainer/views/endpoints/edit/endpoint.html b/app/portainer/views/endpoints/edit/endpoint.html index 6223629fd..ca4ca5ca7 100644 --- a/app/portainer/views/endpoints/edit/endpoint.html +++ b/app/portainer/views/endpoints/edit/endpoint.html @@ -36,10 +36,10 @@
- {{dockerCommands.standalone}} + {{ dockerCommands.standalone }} - {{dockerCommands.swarm}} + {{ dockerCommands.swarm }}
@@ -118,6 +118,23 @@
+
+ +
+ +
+
Metadata diff --git a/app/portainer/views/endpoints/edit/endpointController.js b/app/portainer/views/endpoints/edit/endpointController.js index 221c9282e..816a65734 100644 --- a/app/portainer/views/endpoints/edit/endpointController.js +++ b/app/portainer/views/endpoints/edit/endpointController.js @@ -17,13 +17,32 @@ angular TagService, EndpointProvider, Notifications, - Authentication + Authentication, + SettingsService ) { $scope.state = { uploadInProgress: false, actionInProgress: false, deploymentTab: 0, allowCreate: Authentication.isAdmin(), + availableEdgeAgentCheckinOptions: [ + { key: 'Use default interval', value: 0 }, + { + key: '5 seconds', + value: 5, + }, + { + key: '10 seconds', + value: 10, + }, + { + key: '30 seconds', + value: 30, + }, + { key: '5 minutes', value: 300 }, + { key: '1 hour', value: 3600 }, + { key: '1 day', value: 86400 }, + ], }; $scope.formValues = { @@ -83,6 +102,7 @@ angular PublicURL: endpoint.PublicURL, GroupID: endpoint.GroupId, TagIds: endpoint.TagIds, + EdgeCheckinInterval: endpoint.EdgeCheckinInterval, TLS: TLS, TLSSkipVerify: TLSSkipVerify, TLSSkipClientVerify: TLSSkipClientVerify, @@ -133,6 +153,7 @@ angular endpoint: EndpointService.endpoint($transition$.params().id), groups: GroupService.groups(), tags: TagService.tags(), + settings: SettingsService.settings(), }) .then(function success(data) { var endpoint = data.endpoint; @@ -149,6 +170,9 @@ angular standalone: buildStandaloneCommand($scope.randomEdgeID, endpoint.EdgeKey), swarm: buildSwarmCommand($scope.randomEdgeID, endpoint.EdgeKey), }; + + const settings = data.settings; + $scope.state.availableEdgeAgentCheckinOptions[0].key += ` (${settings.EdgeAgentCheckinInterval} seconds)`; } $scope.endpoint = endpoint; $scope.groups = data.groups; diff --git a/app/portainer/views/settings/settings.html b/app/portainer/views/settings/settings.html index 3401411e5..d6cb80db0 100644 --- a/app/portainer/views/settings/settings.html +++ b/app/portainer/views/settings/settings.html @@ -118,8 +118,11 @@