feat(agent): add auto agent platform detection (#4132)

* feat(endpoint): check endpoint type on creation

* feat(edge): check edge endpoint type

* feat(endpoint): send endpoint creation type

* feat(endpoint): pass tls config

* feat(endpoint): show connect errors

* fix(endpoint): set correct endpoint type

* feat(endpoint): support endpoint creation

* style(endpoint): remove todo comment

* feat(endpoint): set protocol for endpoint url

* feat(endpoint): change scheme of url

* fix(endpoint): toggle code block

* feat(edge): report missing agent platform header

* fix(api/endpoints): fix an issue with agent on kubernetes endpoint

* feat(core/endpoints): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
pull/4147/head
Chaim Lev-Ari 2020-08-04 03:44:17 +03:00 committed by GitHub
parent 490b7ad26f
commit bd7d7dcef5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 226 additions and 76 deletions

View File

@ -2,17 +2,20 @@ package endpoints
import ( import (
"errors" "errors"
"fmt"
"net" "net"
"net/http" "net/http"
"net/url" "net/url"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/http/client" "github.com/portainer/portainer/api/http/client"
"github.com/portainer/portainer/api/internal/edge" "github.com/portainer/portainer/api/internal/edge"
) )
@ -20,7 +23,7 @@ import (
type endpointCreatePayload struct { type endpointCreatePayload struct {
Name string Name string
URL string URL string
EndpointType int EndpointCreationType endpointCreationEnum
PublicURL string PublicURL string
GroupID int GroupID int
TLS bool TLS bool
@ -36,6 +39,17 @@ type endpointCreatePayload struct {
EdgeCheckinInterval int EdgeCheckinInterval int
} }
type endpointCreationEnum int
const (
_ endpointCreationEnum = iota
localDockerEnvironment
agentEnvironment
azureEnvironment
edgeAgentEnvironment
localKubernetesEnvironment
)
func (payload *endpointCreatePayload) Validate(r *http.Request) error { func (payload *endpointCreatePayload) Validate(r *http.Request) error {
name, err := request.RetrieveMultiPartFormValue(r, "Name", false) name, err := request.RetrieveMultiPartFormValue(r, "Name", false)
if err != nil { if err != nil {
@ -43,11 +57,11 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
} }
payload.Name = name payload.Name = name
endpointType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointType", false) endpointCreationType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointCreationType", false)
if err != nil || endpointType == 0 { if err != nil || endpointCreationType == 0 {
return errors.New("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge Agent environment)") return errors.New("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment), 4 (Edge Agent environment) or 5 (Local Kubernetes environment)")
} }
payload.EndpointType = endpointType payload.EndpointCreationType = endpointCreationEnum(endpointCreationType)
groupID, _ := request.RetrieveNumericMultiPartFormValue(r, "GroupID", true) groupID, _ := request.RetrieveNumericMultiPartFormValue(r, "GroupID", true)
if groupID == 0 { if groupID == 0 {
@ -97,8 +111,8 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
} }
} }
switch portainer.EndpointType(payload.EndpointType) { switch payload.EndpointCreationType {
case portainer.AzureEnvironment: case azureEnvironment:
azureApplicationID, err := request.RetrieveMultiPartFormValue(r, "AzureApplicationID", false) azureApplicationID, err := request.RetrieveMultiPartFormValue(r, "AzureApplicationID", false)
if err != nil { if err != nil {
return errors.New("Invalid Azure application ID") return errors.New("Invalid Azure application ID")
@ -182,22 +196,34 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
} }
func (handler *Handler) createEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) { func (handler *Handler) createEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
switch portainer.EndpointType(payload.EndpointType) { switch payload.EndpointCreationType {
case portainer.AzureEnvironment: case azureEnvironment:
return handler.createAzureEndpoint(payload) return handler.createAzureEndpoint(payload)
case portainer.EdgeAgentOnDockerEnvironment: case edgeAgentEnvironment:
return handler.createEdgeAgentEndpoint(payload, portainer.EdgeAgentOnDockerEnvironment) return handler.createEdgeAgentEndpoint(payload)
case portainer.KubernetesLocalEnvironment: case localKubernetesEnvironment:
return handler.createKubernetesEndpoint(payload) return handler.createKubernetesEndpoint(payload)
}
case portainer.EdgeAgentOnKubernetesEnvironment: endpointType := portainer.DockerEnvironment
return handler.createEdgeAgentEndpoint(payload, portainer.EdgeAgentOnKubernetesEnvironment) if payload.EndpointCreationType == agentEnvironment {
agentPlatform, err := handler.pingAndCheckPlatform(payload)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to get endpoint type", err}
}
if agentPlatform == portainer.AgentPlatformDocker {
endpointType = portainer.AgentOnDockerEnvironment
} else if agentPlatform == portainer.AgentPlatformKubernetes {
endpointType = portainer.AgentOnKubernetesEnvironment
payload.URL = strings.TrimPrefix(payload.URL, "tcp://")
}
} }
if payload.TLS { if payload.TLS {
return handler.createTLSSecuredEndpoint(payload, portainer.EndpointType(payload.EndpointType)) return handler.createTLSSecuredEndpoint(payload, endpointType)
} }
return handler.createUnsecuredEndpoint(payload) return handler.createUnsecuredEndpoint(payload)
} }
@ -241,7 +267,7 @@ func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*po
return endpoint, nil return endpoint, nil
} }
func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload, endpointType portainer.EndpointType) (*portainer.Endpoint, *httperror.HandlerError) { func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
endpointID := handler.DataStore.Endpoint().GetNextIdentifier() endpointID := handler.DataStore.Endpoint().GetNextIdentifier()
portainerURL, err := url.Parse(payload.URL) portainerURL, err := url.Parse(payload.URL)
@ -264,7 +290,7 @@ func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload,
ID: portainer.EndpointID(endpointID), ID: portainer.EndpointID(endpointID),
Name: payload.Name, Name: payload.Name,
URL: portainerHost, URL: portainerHost,
Type: endpointType, Type: portainer.EdgeAgentOnDockerEnvironment,
GroupID: portainer.EndpointGroupID(payload.GroupID), GroupID: portainer.EndpointGroupID(payload.GroupID),
TLSConfig: portainer.TLSConfiguration{ TLSConfig: portainer.TLSConfiguration{
TLS: false, TLS: false,
@ -472,3 +498,58 @@ func (handler *Handler) storeTLSFiles(endpoint *portainer.Endpoint, payload *end
return nil return nil
} }
func (handler *Handler) pingAndCheckPlatform(payload *endpointCreatePayload) (portainer.AgentPlatform, error) {
httpCli := &http.Client{
Timeout: 3 * time.Second,
}
if payload.TLS {
tlsConfig, err := crypto.CreateTLSConfigurationFromBytes(payload.TLSCACertFile, payload.TLSCertFile, payload.TLSKeyFile, payload.TLSSkipVerify, payload.TLSSkipClientVerify)
if err != nil {
return 0, err
}
httpCli.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
url, err := url.Parse(fmt.Sprintf("%s/ping", payload.URL))
if err != nil {
return 0, err
}
url.Scheme = "https"
req, err := http.NewRequest(http.MethodGet, url.String(), nil)
if err != nil {
return 0, err
}
resp, err := httpCli.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusNoContent {
return 0, fmt.Errorf("Failed request with status %d", resp.StatusCode)
}
agentPlatformHeader := resp.Header.Get(portainer.HTTPResponseAgentPlatform)
if agentPlatformHeader == "" {
return 0, errors.New("Agent Platform Header is missing")
}
agentPlatformNumber, err := strconv.Atoi(agentPlatformHeader)
if err != nil {
return 0, err
}
if agentPlatformNumber == 0 {
return 0, errors.New("Agent platform is invalid")
}
return portainer.AgentPlatform(agentPlatformNumber), nil
}

View File

@ -2,13 +2,15 @@ package endpoints
import ( import (
"encoding/base64" "encoding/base64"
"errors"
"net/http" "net/http"
"strconv"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response" "github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api" "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors" bolterrors "github.com/portainer/portainer/api/bolt/errors"
) )
type stackStatusResponse struct { type stackStatusResponse struct {
@ -41,7 +43,7 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req
} }
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID)) endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
if err == errors.ErrObjectNotFound { if err == bolterrors.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil { } else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
@ -54,10 +56,27 @@ func (handler *Handler) endpointStatusInspect(w http.ResponseWriter, r *http.Req
if endpoint.EdgeID == "" { if endpoint.EdgeID == "" {
edgeIdentifier := r.Header.Get(portainer.PortainerAgentEdgeIDHeader) edgeIdentifier := r.Header.Get(portainer.PortainerAgentEdgeIDHeader)
endpoint.EdgeID = edgeIdentifier endpoint.EdgeID = edgeIdentifier
err := handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint) agentPlatformHeader := r.Header.Get(portainer.HTTPResponseAgentPlatform)
if agentPlatformHeader == "" {
return &httperror.HandlerError{http.StatusInternalServerError, "Agent Platform Header is missing", errors.New("Agent Platform Header is missing")}
}
agentPlatformNumber, err := strconv.Atoi(agentPlatformHeader)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to parse agent platform header", err}
}
agentPlatform := portainer.AgentPlatform(agentPlatformNumber)
if agentPlatform == portainer.AgentPlatformDocker {
endpoint.Type = portainer.EdgeAgentOnDockerEnvironment
} else if agentPlatform == portainer.AgentPlatformKubernetes {
endpoint.Type = portainer.EdgeAgentOnKubernetesEnvironment
}
err = handler.DataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to Unable to persist endpoint changes inside the database", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to Unable to persist endpoint changes inside the database", err}
} }

View File

@ -11,6 +11,9 @@ type (
RoleID RoleID `json:"RoleId"` RoleID RoleID `json:"RoleId"`
} }
// AgentPlatform represents a platform type for an Agent
AgentPlatform int
// APIOperationAuthorizationRequest represent an request for the authorization to execute an API operation // APIOperationAuthorizationRequest represent an request for the authorization to execute an API operation
APIOperationAuthorizationRequest struct { APIOperationAuthorizationRequest struct {
Path string Path string
@ -1141,6 +1144,8 @@ const (
PortainerAgentHeader = "Portainer-Agent" PortainerAgentHeader = "Portainer-Agent"
// PortainerAgentEdgeIDHeader represent the name of the header containing the Edge ID associated to an agent/agent cluster // PortainerAgentEdgeIDHeader represent the name of the header containing the Edge ID associated to an agent/agent cluster
PortainerAgentEdgeIDHeader = "X-PortainerAgent-EdgeID" PortainerAgentEdgeIDHeader = "X-PortainerAgent-EdgeID"
// HTTPResponseAgentPlatform represents the name of the header containing the Agent platform
HTTPResponseAgentPlatform = "Portainer-Agent-Platform"
// PortainerAgentTargetHeader represent the name of the header containing the target node name // PortainerAgentTargetHeader represent the name of the header containing the target node name
PortainerAgentTargetHeader = "X-PortainerAgent-Target" PortainerAgentTargetHeader = "X-PortainerAgent-Target"
// PortainerAgentSignatureHeader represent the name of the header containing the digital signature // PortainerAgentSignatureHeader represent the name of the header containing the digital signature
@ -1174,6 +1179,14 @@ const (
AuthenticationOAuth AuthenticationOAuth
) )
const (
_ AgentPlatform = iota
// AgentPlatformDocker represent the Docker platform (Standalone/Swarm)
AgentPlatformDocker
// AgentPlatformKubernetes represent the Kubernetes platform
AgentPlatformKubernetes
)
const ( const (
_ EdgeJobLogsStatus = iota _ EdgeJobLogsStatus = iota
// EdgeJobLogsStatusIdle represents an idle log collection job // EdgeJobLogsStatusIdle represents an idle log collection job

View File

@ -18,6 +18,17 @@ export const PortainerEndpointTypes = Object.freeze({
EdgeAgentOnKubernetesEnvironment: 7, EdgeAgentOnKubernetesEnvironment: 7,
}); });
/**
* JS reference of endpoint_create.go#EndpointCreationType iota
*/
export const PortainerEndpointCreationTypes = Object.freeze({
LocalDockerEnvironment: 1,
AgentEnvironment: 2,
AzureEnvironment: 3,
EdgeAgentEnvironment: 4,
LocalKubernetesEnvironment: 5,
});
export const PortainerEndpointConnectionTypes = Object.freeze({ export const PortainerEndpointConnectionTypes = Object.freeze({
DOCKER_LOCAL: 1, DOCKER_LOCAL: 1,
KUBERNETES_LOCAL: 2, KUBERNETES_LOCAL: 2,

View File

@ -1,4 +1,4 @@
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models'; import { PortainerEndpointCreationTypes } from 'Portainer/models/endpoint/models';
angular.module('portainer.app').factory('EndpointService', [ angular.module('portainer.app').factory('EndpointService', [
'$q', '$q',
@ -59,7 +59,7 @@ angular.module('portainer.app').factory('EndpointService', [
service.createLocalEndpoint = function () { service.createLocalEndpoint = function () {
var deferred = $q.defer(); var deferred = $q.defer();
FileUploadService.createEndpoint('local', PortainerEndpointTypes.DockerEnvironment, '', '', 1, [], false) FileUploadService.createEndpoint('local', PortainerEndpointCreationTypes.LocalDockerEnvironment, '', '', 1, [], false)
.then(function success(response) { .then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}) })
@ -72,7 +72,7 @@ angular.module('portainer.app').factory('EndpointService', [
service.createRemoteEndpoint = function ( service.createRemoteEndpoint = function (
name, name,
type, creationType,
URL, URL,
PublicURL, PublicURL,
groupID, groupID,
@ -88,17 +88,13 @@ angular.module('portainer.app').factory('EndpointService', [
var deferred = $q.defer(); var deferred = $q.defer();
var endpointURL = URL; var endpointURL = URL;
if ( if (creationType !== PortainerEndpointCreationTypes.EdgeAgentEnvironment) {
type !== PortainerEndpointTypes.EdgeAgentOnDockerEnvironment &&
type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment &&
type !== PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
) {
endpointURL = 'tcp://' + URL; endpointURL = 'tcp://' + URL;
} }
FileUploadService.createEndpoint( FileUploadService.createEndpoint(
name, name,
type, creationType,
endpointURL, endpointURL,
PublicURL, PublicURL,
groupID, groupID,
@ -124,7 +120,7 @@ angular.module('portainer.app').factory('EndpointService', [
service.createLocalKubernetesEndpoint = function () { service.createLocalKubernetesEndpoint = function () {
var deferred = $q.defer(); var deferred = $q.defer();
FileUploadService.createEndpoint('local', 5, '', '', 1, [], true, true, true) FileUploadService.createEndpoint('local', PortainerEndpointCreationTypes.LocalKubernetesEnvironment, '', '', 1, [], true, true, true)
.then(function success(response) { .then(function success(response) {
deferred.resolve(response.data); deferred.resolve(response.data);
}) })

View File

@ -1,3 +1,4 @@
import { PortainerEndpointCreationTypes } from 'Portainer/models/endpoint/models';
import { genericHandler, jsonObjectsToArrayHandler } from '../../docker/rest/response/handlers'; import { genericHandler, jsonObjectsToArrayHandler } from '../../docker/rest/response/handlers';
angular.module('portainer.app').factory('FileUploadService', [ angular.module('portainer.app').factory('FileUploadService', [
@ -112,12 +113,26 @@ angular.module('portainer.app').factory('FileUploadService', [
}); });
}; };
service.createEndpoint = function (name, type, URL, PublicURL, groupID, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile, checkinInterval) { service.createEndpoint = function (
name,
creationType,
URL,
PublicURL,
groupID,
tagIds,
TLS,
TLSSkipVerify,
TLSSkipClientVerify,
TLSCAFile,
TLSCertFile,
TLSKeyFile,
checkinInterval
) {
return Upload.upload({ return Upload.upload({
url: 'api/endpoints', url: 'api/endpoints',
data: { data: {
Name: name, Name: name,
EndpointType: type, EndpointCreationType: creationType,
URL: URL, URL: URL,
PublicURL: PublicURL, PublicURL: PublicURL,
GroupID: groupID, GroupID: groupID,
@ -139,7 +154,7 @@ angular.module('portainer.app').factory('FileUploadService', [
url: 'api/endpoints', url: 'api/endpoints',
data: { data: {
Name: name, Name: name,
EndpointType: 3, EndpointCreationType: PortainerEndpointCreationTypes.Azure,
GroupID: groupId, GroupID: groupId,
TagIds: Upload.json(tagIds), TagIds: Upload.json(tagIds),
AzureApplicationID: applicationId, AzureApplicationID: applicationId,

View File

@ -1,4 +1,4 @@
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models'; import { PortainerEndpointCreationTypes, PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel'; import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel';
angular angular
@ -56,7 +56,7 @@ angular
}; };
$scope.copyAgentCommand = function () { $scope.copyAgentCommand = function () {
if ($scope.state.deploymentTab === 0) { if ($scope.state.deploymentTab === 1) {
clipboard.copyText('curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent'); clipboard.copyText('curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent');
} else { } else {
clipboard.copyText('curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml'); clipboard.copyText('curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml');
@ -102,19 +102,31 @@ angular
var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert; var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert;
var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey; var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey;
addEndpoint(name, PortainerEndpointTypes.DockerEnvironment, URL, publicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile); addEndpoint(
name,
PortainerEndpointCreationTypes.LocalDockerEnvironment,
URL,
publicURL,
groupId,
tagIds,
TLS,
TLSSkipVerify,
TLSSkipClientVerify,
TLSCAFile,
TLSCertFile,
TLSKeyFile
);
}; };
$scope.addAgentEndpoint = function () { $scope.addAgentEndpoint = function () {
var name = $scope.formValues.Name; var name = $scope.formValues.Name;
var URL = $filter('stripprotocol')($scope.formValues.URL); // var URL = $filter('stripprotocol')($scope.formValues.URL);
var URL = $scope.formValues.URL;
var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL; var publicURL = $scope.formValues.PublicURL === '' ? URL.split(':')[0] : $scope.formValues.PublicURL;
var groupId = $scope.formValues.GroupId; var groupId = $scope.formValues.GroupId;
var tagIds = $scope.formValues.TagIds; var tagIds = $scope.formValues.TagIds;
addEndpoint(name, PortainerEndpointTypes.AgentOnDockerEnvironment, URL, publicURL, groupId, tagIds, true, true, true, null, null, null); addEndpoint(name, PortainerEndpointCreationTypes.AgentEnvironment, URL, publicURL, groupId, tagIds, true, true, true, null, null, null);
// TODO: k8s merge - temporarily updated to AgentOnKubernetesEnvironment, breaking Docker agent support
// addEndpoint(name, PortainerEndpointTypes.AgentOnKubernetesEnvironment, URL, publicURL, groupId, tags, true, true, true, null, null, null);
}; };
$scope.addEdgeAgentEndpoint = function () { $scope.addEdgeAgentEndpoint = function () {
@ -123,9 +135,7 @@ angular
var tagIds = $scope.formValues.TagIds; var tagIds = $scope.formValues.TagIds;
var URL = $scope.formValues.URL; var URL = $scope.formValues.URL;
addEndpoint(name, PortainerEndpointTypes.EdgeAgentOnDockerEnvironment, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval); addEndpoint(name, PortainerEndpointCreationTypes.EdgeAgentEnvironment, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval);
// TODO: k8s merge - temporarily updated to EdgeAgentOnKubernetesEnvironment, breaking Docker Edge agent support
// addEndpoint(name, PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment, URL, "", groupId, tags, false, false, false, null, null, null);
}; };
$scope.addAzureEndpoint = function () { $scope.addAzureEndpoint = function () {
@ -154,11 +164,11 @@ angular
}); });
} }
function addEndpoint(name, type, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile, CheckinInterval) { function addEndpoint(name, creationType, URL, PublicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile, CheckinInterval) {
$scope.state.actionInProgress = true; $scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint( EndpointService.createRemoteEndpoint(
name, name,
type, creationType,
URL, URL,
PublicURL, PublicURL,
groupId, groupId,
@ -171,14 +181,19 @@ angular
TLSKeyFile, TLSKeyFile,
CheckinInterval CheckinInterval
) )
.then(function success(data) { .then(function success(endpoint) {
Notifications.success('Endpoint created', name); Notifications.success('Endpoint created', name);
if (type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) { switch (endpoint.Type) {
$state.go('portainer.endpoints.endpoint', { id: data.Id }); case PortainerEndpointTypes.EdgeAgentOnDockerEnvironment:
} else if (type === PortainerEndpointTypes.AgentOnKubernetesEnvironment) { case PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment:
$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: data.Id }); $state.go('portainer.endpoints.endpoint', { id: endpoint.Id });
} else { break;
$state.go('portainer.endpoints', {}, { reload: true }); case PortainerEndpointTypes.AgentOnKubernetesEnvironment:
$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: endpoint.Id });
break;
default:
$state.go('portainer.endpoints', {}, { reload: true });
break;
} }
}) })
.catch(function error(err) { .catch(function error(err) {

View File

@ -73,19 +73,19 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<span class="col-sm-12 text-muted small"> <span class="col-sm-12 text-muted small">
Ensure that you have deployed the Portainer agent in your cluster first. You can use execute the following command on any manager node to deploy it. Ensure that you have deployed the Portainer agent in your cluster first. Refer to the platform related command below to deploy it.
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<uib-tabset active="state.deploymentTab"> <uib-tabset active="state.deploymentTab">
<uib-tab index="0" heading="Kubernetes"> <uib-tab index="0" heading="Kubernetes">
<code style="display: block; white-space: pre-wrap;"> <code style="display: block; white-space: pre-wrap; padding: 16px 90px;"
curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml >curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml</code
</code> >
</uib-tab> </uib-tab>
<uib-tab index="0" heading="Docker"> <uib-tab index="1" heading="Docker Swarm">
<code> <code style="display: block; white-space: pre-wrap; padding: 16px 90px;"
curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent >curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent</code
</code> >
</uib-tab> </uib-tab>
</uib-tabset> </uib-tabset>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">

View File

@ -28,23 +28,23 @@
<span class="small text-muted"> <span class="small text-muted">
<p> <p>
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i> <i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Deploy the Edge agent on your remote Docker/Kubernetes environment using the following command(s) Refer to the platform related command below to deploy the Edge agent in your remote cluster.
</p> </p>
<p> <p>
The agent will communicate with Portainer via <u>{{ edgeKeyDetails.instanceURL }}</u> and <u>tcp://{{ edgeKeyDetails.tunnelServerAddr }}</u> The agent will communicate with Portainer via <u>{{ edgeKeyDetails.instanceURL }}</u> and <u>tcp://{{ edgeKeyDetails.tunnelServerAddr }}</u>
</p> </p>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">
<uib-tabset active="state.deploymentTab"> <uib-tabset active="state.deploymentTab">
<uib-tab index="0" heading="Standalone"> <uib-tab index="0" heading="Kubernetes">
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.standalone }}</code> <code style="display: block; white-space: pre-wrap; padding: 16px 90px;"
>curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | sudo bash -s -- {{ randomEdgeID }} {{ endpoint.EdgeKey }}</code
>
</uib-tab> </uib-tab>
<uib-tab index="1" heading="Swarm"> <uib-tab index="1" heading="Docker Swarm">
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.swarm }}</code> <code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.swarm }}</code>
</uib-tab> </uib-tab>
<uib-tab index="0" heading="Kubernetes"> <uib-tab index="2" heading="Docker Standalone">
<code style="display: block; white-space: pre-wrap;"> <code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.standalone }}</code>
curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | sudo bash -s -- {{ randomEdgeID }} {{ endpoint.EdgeKey }}
</code>
</uib-tab> </uib-tab>
</uib-tabset> </uib-tabset>
<div style="margin-top: 10px;"> <div style="margin-top: 10px;">

View File

@ -55,7 +55,7 @@ angular
}; };
$scope.copyEdgeAgentDeploymentCommand = function () { $scope.copyEdgeAgentDeploymentCommand = function () {
if ($scope.state.deploymentTab === 0) { if ($scope.state.deploymentTab === 2) {
clipboard.copyText( clipboard.copyText(
'docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes -v /:/host --restart always -e EDGE=1 -e EDGE_ID=' + 'docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes -v /:/host --restart always -e EDGE=1 -e EDGE_ID=' +
$scope.randomEdgeID + $scope.randomEdgeID +

View File

@ -1,7 +1,7 @@
import _ from 'lodash-es'; import _ from 'lodash-es';
import angular from 'angular'; import angular from 'angular';
import { PortainerEndpointInitFormValueEndpointSections, PortainerEndpointInitFormValues } from 'Portainer/models/endpoint/formValues'; import { PortainerEndpointInitFormValueEndpointSections, PortainerEndpointInitFormValues } from 'Portainer/models/endpoint/formValues';
import { PortainerEndpointConnectionTypes, PortainerEndpointTypes } from 'Portainer/models/endpoint/models'; import { PortainerEndpointConnectionTypes, PortainerEndpointCreationTypes, PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
require('./includes/localDocker.html'); require('./includes/localDocker.html');
require('./includes/localKubernetes.html'); require('./includes/localKubernetes.html');
@ -61,7 +61,7 @@ class InitEndpointController {
case PortainerEndpointConnectionTypes.AGENT: case PortainerEndpointConnectionTypes.AGENT:
return this.createAgentEndpoint(); return this.createAgentEndpoint();
default: default:
this.Notifications.error('Failure', 'Unable to determine wich action to do'); this.Notifications.error('Failure', 'Unable to determine which action to do');
} }
} }
@ -112,10 +112,10 @@ class InitEndpointController {
const name = this.formValues.Name; const name = this.formValues.Name;
const URL = this.formValues.URL; const URL = this.formValues.URL;
const PublicURL = URL.split(':')[0]; const PublicURL = URL.split(':')[0];
// TODO: k8s merge - change type ID for agent on kube (6) or agent on swarm (2)
const endpoint = await this.EndpointService.createRemoteEndpoint( const endpoint = await this.EndpointService.createRemoteEndpoint(
name, name,
PortainerEndpointTypes.AgentOnKubernetesEnvironment, PortainerEndpointCreationTypes.AgentEnvironment,
URL, URL,
PublicURL, PublicURL,
1, 1,
@ -127,8 +127,8 @@ class InitEndpointController {
null, null,
null null
); );
// TODO: k8s merge - go on home whith agent on swarm (2) const routeName = endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ? 'portainer.endpoints.endpoint.kubernetesConfig' : 'portainer.home';
this.$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: endpoint.Id }); this.$state.go(routeName, { id: endpoint.Id });
} catch (err) { } catch (err) {
this.Notifications.error('Failure', err, 'Unable to connect to the Docker environment'); this.Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
} finally { } finally {