refactor(azure): remove Azure ACI endpoint support (#3803)

* feat(templates): remove template management features (#3719)

* feat(api): remove template management features

* feat(templates): remove template management features

* refactor(azure): remove Azure ACI endpoint support
pull/3802/head
Anthony Lapenna 2020-05-19 15:08:57 +12:00 committed by Anthony Lapenna
parent 665ecf7585
commit 27382c432c
65 changed files with 47 additions and 1791 deletions

View File

@ -53,7 +53,7 @@ func (runner *SnapshotJobRunner) Run() {
} }
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
if endpoint.Type == portainer.AzureEnvironment || endpoint.Type == portainer.EdgeAgentEnvironment { if endpoint.Type == portainer.EdgeAgentEnvironment {
continue continue
} }

View File

@ -35,9 +35,7 @@ func NewClientFactory(signatureService portainer.DigitalSignatureService, revers
// a specific endpoint configuration. The nodeName parameter can be used // a specific endpoint configuration. The nodeName parameter can be used
// with an agent enabled endpoint to target a specific node in an agent cluster. // with an agent enabled endpoint to target a specific node in an agent cluster.
func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string) (*client.Client, error) { func (factory *ClientFactory) CreateClient(endpoint *portainer.Endpoint, nodeName string) (*client.Client, error) {
if endpoint.Type == portainer.AzureEnvironment { if endpoint.Type == portainer.AgentOnDockerEnvironment {
return nil, unsupportedEnvironmentType
} else if endpoint.Type == portainer.AgentOnDockerEnvironment {
return createAgentClient(endpoint, factory.signatureService, nodeName) return createAgentClient(endpoint, factory.signatureService, nodeName)
} else if endpoint.Type == portainer.EdgeAgentEnvironment { } else if endpoint.Type == portainer.EdgeAgentEnvironment {
return createEdgeClient(endpoint, factory.reverseTunnelService, nodeName) return createEdgeClient(endpoint, factory.reverseTunnelService, nodeName)

View File

@ -39,11 +39,6 @@ const (
ErrEndpointAccessDenied = Error("Access denied to endpoint") ErrEndpointAccessDenied = Error("Access denied to endpoint")
) )
// Azure environment errors
const (
ErrAzureInvalidCredentials = Error("Invalid Azure credentials")
)
// Endpoint group errors. // Endpoint group errors.
const ( const (
ErrCannotRemoveDefaultGroup = Error("Cannot remove the default endpoint group") ErrCannotRemoveDefaultGroup = Error("Cannot remove the default endpoint group")

View File

@ -171,6 +171,7 @@ github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2 h1:0PfgGLys9yH
github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2/go.mod h1:/wIeGwJOMYc1JplE/OvYMO5korce39HddIfI8VKGyAM= github.com/portainer/libcrypto v0.0.0-20190723020515-23ebe86ab2c2/go.mod h1:/wIeGwJOMYc1JplE/OvYMO5korce39HddIfI8VKGyAM=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33 h1:H8HR2dHdBf8HANSkUyVw4o8+4tegGcd+zyKZ3e599II= github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33 h1:H8HR2dHdBf8HANSkUyVw4o8+4tegGcd+zyKZ3e599II=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33/go.mod h1:Y2TfgviWI4rT2qaOTHr+hq6MdKIE5YjgQAu7qwptTV0= github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33/go.mod h1:Y2TfgviWI4rT2qaOTHr+hq6MdKIE5YjgQAu7qwptTV0=
github.com/portainer/portainer v0.10.1 h1:I8K345CjGWfUGsVA8c8/gqamwLCC6CIAjxZXSklAFq0=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=

View File

@ -2,12 +2,9 @@ package client
import ( import (
"crypto/tls" "crypto/tls"
"encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"net/url"
"strings" "strings"
"time" "time"
@ -19,55 +16,6 @@ const (
defaultHTTPTimeout = 5 defaultHTTPTimeout = 5
) )
// HTTPClient represents a client to send HTTP requests.
type HTTPClient struct {
*http.Client
}
// NewHTTPClient is used to build a new HTTPClient.
func NewHTTPClient() *HTTPClient {
return &HTTPClient{
&http.Client{
Timeout: time.Second * time.Duration(defaultHTTPTimeout),
},
}
}
// AzureAuthenticationResponse represents an Azure API authentication response.
type AzureAuthenticationResponse struct {
AccessToken string `json:"access_token"`
ExpiresOn string `json:"expires_on"`
}
// ExecuteAzureAuthenticationRequest is used to execute an authentication request
// against the Azure API. It re-uses the same http.Client.
func (client *HTTPClient) ExecuteAzureAuthenticationRequest(credentials *portainer.AzureCredentials) (*AzureAuthenticationResponse, error) {
loginURL := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token", credentials.TenantID)
params := url.Values{
"grant_type": {"client_credentials"},
"client_id": {credentials.ApplicationID},
"client_secret": {credentials.AuthenticationKey},
"resource": {"https://management.azure.com/"},
}
response, err := client.PostForm(loginURL, params)
if err != nil {
return nil, err
}
if response.StatusCode != http.StatusOK {
return nil, portainer.ErrAzureInvalidCredentials
}
var token AzureAuthenticationResponse
err = json.NewDecoder(response.Body).Decode(&token)
if err != nil {
return nil, err
}
return &token, nil
}
// Get executes a simple HTTP GET to the specified URL and returns // Get executes a simple HTTP GET to the specified URL and returns
// the content of the response body. Timeout can be specified via the timeout parameter, // the content of the response body. Timeout can be specified via the timeout parameter,
// will default to defaultHTTPTimeout if set to 0. // will default to defaultHTTPTimeout if set to 0.

View File

@ -24,8 +24,6 @@ func NewHandler(bouncer *security.RequestBouncer) *Handler {
Router: mux.NewRouter(), Router: mux.NewRouter(),
requestBouncer: bouncer, requestBouncer: bouncer,
} }
h.PathPrefix("/{id}/azure").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToAzureAPI)))
h.PathPrefix("/{id}/docker").Handler( h.PathPrefix("/{id}/docker").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI))) bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.proxyRequestsToDockerAPI)))
h.PathPrefix("/{id}/storidge").Handler( h.PathPrefix("/{id}/storidge").Handler(

View File

@ -1,43 +0,0 @@
package endpointproxy
import (
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/portainer/api"
"net/http"
)
func (handler *Handler) proxyRequestsToAzureAPI(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
endpointID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil {
return &httperror.HandlerError{http.StatusBadRequest, "Invalid endpoint identifier route variable", err}
}
endpoint, err := handler.EndpointService.Endpoint(portainer.EndpointID(endpointID))
if err == portainer.ErrObjectNotFound {
return &httperror.HandlerError{http.StatusNotFound, "Unable to find an endpoint with the specified identifier inside the database", err}
} else if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to find an endpoint with the specified identifier inside the database", err}
}
err = handler.requestBouncer.AuthorizedEndpointOperation(r, endpoint, false)
if err != nil {
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
}
var proxy http.Handler
proxy = handler.ProxyManager.GetEndpointProxy(endpoint)
if proxy == nil {
proxy, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create proxy", err}
}
}
id := strconv.Itoa(endpointID)
http.StripPrefix("/"+id+"/azure", proxy).ServeHTTP(w, r)
return nil
}

View File

@ -18,21 +18,18 @@ import (
) )
type endpointCreatePayload struct { type endpointCreatePayload struct {
Name string Name string
URL string URL string
EndpointType int EndpointType int
PublicURL string PublicURL string
GroupID int GroupID int
TLS bool TLS bool
TLSSkipVerify bool TLSSkipVerify bool
TLSSkipClientVerify bool TLSSkipClientVerify bool
TLSCACertFile []byte TLSCACertFile []byte
TLSCertFile []byte TLSCertFile []byte
TLSKeyFile []byte TLSKeyFile []byte
AzureApplicationID string TagIDs []portainer.TagID
AzureTenantID string
AzureAuthenticationKey string
TagIDs []portainer.TagID
} }
func (payload *endpointCreatePayload) Validate(r *http.Request) error { func (payload *endpointCreatePayload) Validate(r *http.Request) error {
@ -44,7 +41,7 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
endpointType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointType", false) endpointType, err := request.RetrieveNumericMultiPartFormValue(r, "EndpointType", false)
if err != nil || endpointType == 0 { if err != nil || endpointType == 0 {
return portainer.Error("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge Agent environment)") return portainer.Error("Invalid endpoint type value. Value must be one of: 1 (Docker environment), 2 (Agent environment) or 4 (Edge Agent environment)")
} }
payload.EndpointType = endpointType payload.EndpointType = endpointType
@ -96,35 +93,14 @@ func (payload *endpointCreatePayload) Validate(r *http.Request) error {
} }
} }
switch portainer.EndpointType(payload.EndpointType) { endpointURL, err := request.RetrieveMultiPartFormValue(r, "URL", true)
case portainer.AzureEnvironment: if err != nil {
azureApplicationID, err := request.RetrieveMultiPartFormValue(r, "AzureApplicationID", false) return portainer.Error("Invalid endpoint URL")
if err != nil {
return portainer.Error("Invalid Azure application ID")
}
payload.AzureApplicationID = azureApplicationID
azureTenantID, err := request.RetrieveMultiPartFormValue(r, "AzureTenantID", false)
if err != nil {
return portainer.Error("Invalid Azure tenant ID")
}
payload.AzureTenantID = azureTenantID
azureAuthenticationKey, err := request.RetrieveMultiPartFormValue(r, "AzureAuthenticationKey", false)
if err != nil {
return portainer.Error("Invalid Azure authentication key")
}
payload.AzureAuthenticationKey = azureAuthenticationKey
default:
url, err := request.RetrieveMultiPartFormValue(r, "URL", true)
if err != nil {
return portainer.Error("Invalid endpoint URL")
}
payload.URL = url
publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true)
payload.PublicURL = publicURL
} }
payload.URL = endpointURL
publicURL, _ := request.RetrieveMultiPartFormValue(r, "PublicURL", true)
payload.PublicURL = publicURL
return nil return nil
} }
@ -178,9 +154,7 @@ 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) {
if portainer.EndpointType(payload.EndpointType) == portainer.AzureEnvironment { if portainer.EndpointType(payload.EndpointType) == portainer.EdgeAgentEnvironment {
return handler.createAzureEndpoint(payload)
} else if portainer.EndpointType(payload.EndpointType) == portainer.EdgeAgentEnvironment {
return handler.createEdgeAgentEndpoint(payload) return handler.createEdgeAgentEndpoint(payload)
} }
@ -190,44 +164,6 @@ func (handler *Handler) createEndpoint(payload *endpointCreatePayload) (*portain
return handler.createUnsecuredEndpoint(payload) return handler.createUnsecuredEndpoint(payload)
} }
func (handler *Handler) createAzureEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
credentials := portainer.AzureCredentials{
ApplicationID: payload.AzureApplicationID,
TenantID: payload.AzureTenantID,
AuthenticationKey: payload.AzureAuthenticationKey,
}
httpClient := client.NewHTTPClient()
_, err := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate against Azure", err}
}
endpointID := handler.EndpointService.GetNextIdentifier()
endpoint := &portainer.Endpoint{
ID: portainer.EndpointID(endpointID),
Name: payload.Name,
URL: "https://management.azure.com",
Type: portainer.AzureEnvironment,
GroupID: portainer.EndpointGroupID(payload.GroupID),
PublicURL: payload.PublicURL,
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
Extensions: []portainer.EndpointExtension{},
AzureCredentials: credentials,
TagIDs: payload.TagIDs,
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.Snapshot{},
}
err = handler.saveEndpointAndUpdateAuthorizations(endpoint)
if err != nil {
return nil, &httperror.HandlerError{http.StatusInternalServerError, "An error occured while trying to create the endpoint", err}
}
return endpoint, nil
}
func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) { func (handler *Handler) createEdgeAgentEndpoint(payload *endpointCreatePayload) (*portainer.Endpoint, *httperror.HandlerError) {
endpointType := portainer.EdgeAgentEnvironment endpointType := portainer.EdgeAgentEnvironment
endpointID := handler.EndpointService.GetNextIdentifier() endpointID := handler.EndpointService.GetNextIdentifier()

View File

@ -23,10 +23,6 @@ func (handler *Handler) endpointSnapshot(w http.ResponseWriter, r *http.Request)
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}
} }
if endpoint.Type == portainer.AzureEnvironment {
return &httperror.HandlerError{http.StatusBadRequest, "Snapshots not supported for Azure endpoints", err}
}
snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(endpoint) snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(endpoint)
latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID) latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID)

View File

@ -17,10 +17,6 @@ func (handler *Handler) endpointSnapshots(w http.ResponseWriter, r *http.Request
} }
for _, endpoint := range endpoints { for _, endpoint := range endpoints {
if endpoint.Type == portainer.AzureEnvironment {
continue
}
snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(&endpoint) snapshot, snapshotError := handler.Snapshotter.CreateSnapshot(&endpoint)
latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID) latestEndpointReference, err := handler.EndpointService.Endpoint(endpoint.ID)

View File

@ -9,24 +9,20 @@ import (
"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/http/client"
) )
type endpointUpdatePayload struct { type endpointUpdatePayload struct {
Name *string Name *string
URL *string URL *string
PublicURL *string PublicURL *string
GroupID *int GroupID *int
TLS *bool TLS *bool
TLSSkipVerify *bool TLSSkipVerify *bool
TLSSkipClientVerify *bool TLSSkipClientVerify *bool
Status *int Status *int
AzureApplicationID *string TagIDs []portainer.TagID
AzureTenantID *string UserAccessPolicies portainer.UserAccessPolicies
AzureAuthenticationKey *string TeamAccessPolicies portainer.TeamAccessPolicies
TagIDs []portainer.TagID
UserAccessPolicies portainer.UserAccessPolicies
TeamAccessPolicies portainer.TeamAccessPolicies
} }
func (payload *endpointUpdatePayload) Validate(r *http.Request) error { func (payload *endpointUpdatePayload) Validate(r *http.Request) error {
@ -137,26 +133,6 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
} }
if endpoint.Type == portainer.AzureEnvironment {
credentials := endpoint.AzureCredentials
if payload.AzureApplicationID != nil {
credentials.ApplicationID = *payload.AzureApplicationID
}
if payload.AzureTenantID != nil {
credentials.TenantID = *payload.AzureTenantID
}
if payload.AzureAuthenticationKey != nil {
credentials.AuthenticationKey = *payload.AzureAuthenticationKey
}
httpClient := client.NewHTTPClient()
_, authErr := httpClient.ExecuteAzureAuthenticationRequest(&credentials)
if authErr != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to authenticate against Azure", authErr}
}
endpoint.AzureCredentials = credentials
}
if payload.TLS != nil { if payload.TLS != nil {
folder := strconv.Itoa(endpointID) folder := strconv.Itoa(endpointID)
@ -201,7 +177,7 @@ func (handler *Handler) endpointUpdate(w http.ResponseWriter, r *http.Request) *
} }
} }
if payload.URL != nil || payload.TLS != nil || endpoint.Type == portainer.AzureEnvironment { if payload.URL != nil || payload.TLS != nil {
_, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint) _, err = handler.ProxyManager.CreateAndRegisterEndpointProxy(endpoint)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to register HTTP proxy for the endpoint", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to register HTTP proxy for the endpoint", err}

View File

@ -12,7 +12,6 @@ import (
) )
func hideFields(endpoint *portainer.Endpoint) { func hideFields(endpoint *portainer.Endpoint) {
endpoint.AzureCredentials = portainer.AzureCredentials{}
if len(endpoint.Snapshots) > 0 { if len(endpoint.Snapshots) > 0 {
endpoint.Snapshots[0].SnapshotRaw = portainer.SnapshotRaw{} endpoint.Snapshots[0].SnapshotRaw = portainer.SnapshotRaw{}
} }

View File

@ -90,8 +90,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/storidge/"): case strings.Contains(r.URL.Path, "/storidge/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/azure/"):
http.StripPrefix("/api/endpoints", h.EndpointProxyHandler).ServeHTTP(w, r)
case strings.Contains(r.URL.Path, "/edge/"): case strings.Contains(r.URL.Path, "/edge/"):
http.StripPrefix("/api/endpoints", h.EndpointEdgeHandler).ServeHTTP(w, r) http.StripPrefix("/api/endpoints", h.EndpointEdgeHandler).ServeHTTP(w, r)
default: default:

View File

@ -1,20 +0,0 @@
package factory
import (
"net/http"
"net/url"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/proxy/factory/azure"
)
func newAzureProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
remoteURL, err := url.Parse(azureAPIBaseURL)
if err != nil {
return nil, err
}
proxy := newSingleHostReverseProxyWithHostHeader(remoteURL)
proxy.Transport = azure.NewTransport(&endpoint.AzureCredentials)
return proxy, nil
}

View File

@ -1,80 +0,0 @@
package azure
import (
"net/http"
"strconv"
"sync"
"time"
"github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/client"
)
type (
azureAPIToken struct {
value string
expirationTime time.Time
}
Transport struct {
credentials *portainer.AzureCredentials
client *client.HTTPClient
token *azureAPIToken
mutex sync.Mutex
}
)
// NewTransport returns a pointer to a new instance of Transport that implements the HTTP Transport
// interface for proxying requests to the Azure API.
func NewTransport(credentials *portainer.AzureCredentials) *Transport {
return &Transport{
credentials: credentials,
client: client.NewHTTPClient(),
}
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *Transport) RoundTrip(request *http.Request) (*http.Response, error) {
err := transport.retrieveAuthenticationToken()
if err != nil {
return nil, err
}
request.Header.Set("Authorization", "Bearer "+transport.token.value)
return http.DefaultTransport.RoundTrip(request)
}
func (transport *Transport) authenticate() error {
token, err := transport.client.ExecuteAzureAuthenticationRequest(transport.credentials)
if err != nil {
return err
}
expiresOn, err := strconv.ParseInt(token.ExpiresOn, 10, 64)
if err != nil {
return err
}
transport.token = &azureAPIToken{
value: token.AccessToken,
expirationTime: time.Unix(expiresOn, 0),
}
return nil
}
func (transport *Transport) retrieveAuthenticationToken() error {
transport.mutex.Lock()
defer transport.mutex.Unlock()
if transport.token == nil {
return transport.authenticate()
}
timeLimit := time.Now().Add(-5 * time.Minute)
if timeLimit.After(transport.token.expirationTime) {
return transport.authenticate()
}
return nil
}

View File

@ -10,8 +10,6 @@ import (
"github.com/portainer/portainer/api/docker" "github.com/portainer/portainer/api/docker"
) )
const azureAPIBaseURL = "https://management.azure.com"
var extensionPorts = map[portainer.ExtensionID]string{ var extensionPorts = map[portainer.ExtensionID]string{
portainer.RegistryManagementExtension: "7001", portainer.RegistryManagementExtension: "7001",
portainer.OAuthAuthenticationExtension: "7002", portainer.OAuthAuthenticationExtension: "7002",
@ -100,11 +98,6 @@ func (factory *ProxyFactory) NewLegacyExtensionProxy(extensionAPIURL string) (ht
// NewEndpointProxy returns a new reverse proxy (filesystem based or HTTP) to an endpoint API server // NewEndpointProxy returns a new reverse proxy (filesystem based or HTTP) to an endpoint API server
func (factory *ProxyFactory) NewEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) { func (factory *ProxyFactory) NewEndpointProxy(endpoint *portainer.Endpoint) (http.Handler, error) {
switch endpoint.Type {
case portainer.AzureEnvironment:
return newAzureProxy(endpoint)
}
return factory.newDockerProxy(endpoint) return factory.newDockerProxy(endpoint)
} }

View File

@ -25,7 +25,7 @@ type (
Authorizations map[Authorization]bool Authorizations map[Authorization]bool
// AzureCredentials represents the credentials used to connect to an Azure // AzureCredentials represents the credentials used to connect to an Azure
// environment. // environment (deprecated).
AzureCredentials struct { AzureCredentials struct {
ApplicationID string `json:"ApplicationID"` ApplicationID string `json:"ApplicationID"`
TenantID string `json:"TenantID"` TenantID string `json:"TenantID"`
@ -140,7 +140,6 @@ type (
PublicURL string `json:"PublicURL"` PublicURL string `json:"PublicURL"`
TLSConfig TLSConfiguration `json:"TLSConfig"` TLSConfig TLSConfiguration `json:"TLSConfig"`
Extensions []EndpointExtension `json:"Extensions"` Extensions []EndpointExtension `json:"Extensions"`
AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty"`
TagIDs []TagID `json:"TagIds"` TagIDs []TagID `json:"TagIds"`
Status EndpointStatus `json:"Status"` Status EndpointStatus `json:"Status"`
Snapshots []Snapshot `json:"Snapshots"` Snapshots []Snapshot `json:"Snapshots"`
@ -161,6 +160,9 @@ type (
// Deprecated in DBVersion == 22 // Deprecated in DBVersion == 22
Tags []string `json:"Tags"` Tags []string `json:"Tags"`
// Deprecated in DBVersion == 24
AzureCredentials AzureCredentials `json:"AzureCredentials,omitempty"`
} }
// EndpointAuthorizations represents the authorizations associated to a set of endpoints // EndpointAuthorizations represents the authorizations associated to a set of endpoints
@ -1000,7 +1002,7 @@ const (
// APIVersion is the version number of the Portainer API // APIVersion is the version number of the Portainer API
APIVersion = "2.0.0-dev" APIVersion = "2.0.0-dev"
// DBVersion is the version number of the Portainer database // DBVersion is the version number of the Portainer database
DBVersion = 23 DBVersion = 24
// AssetsServerURL represents the URL of the Portainer asset server // AssetsServerURL represents the URL of the Portainer asset server
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com" AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved // MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
@ -1074,7 +1076,7 @@ const (
DockerEnvironment DockerEnvironment
// AgentOnDockerEnvironment represents an endpoint connected to a Portainer agent deployed on a Docker environment // AgentOnDockerEnvironment represents an endpoint connected to a Portainer agent deployed on a Docker environment
AgentOnDockerEnvironment AgentOnDockerEnvironment
// AzureEnvironment represents an endpoint connected to an Azure environment // AzureEnvironment represents an endpoint connected to an Azure environment (deprecated)
AzureEnvironment AzureEnvironment
// EdgeAgentEnvironment represents an endpoint connected to an Edge agent // EdgeAgentEnvironment represents an endpoint connected to an Edge agent
EdgeAgentEnvironment EdgeAgentEnvironment

View File

@ -254,7 +254,7 @@ paths:
- name: "EndpointType" - name: "EndpointType"
in: "formData" in: "formData"
type: "integer" type: "integer"
description: "Environment type. Value must be one of: 1 (Docker environment), 2 (Agent environment), 3 (Azure environment) or 4 (Edge agent environment)" description: "Environment type. Value must be one of: 1 (Docker environment), 2 (Agent environment) or 4 (Edge agent environment)"
required: true required: true
- name: "URL" - name: "URL"
in: "formData" in: "formData"
@ -294,18 +294,6 @@ paths:
in: "formData" in: "formData"
type: "file" type: "file"
description: "TLS client key file" description: "TLS client key file"
- name: "AzureApplicationID"
in: "formData"
type: "string"
description: "Azure application ID. Required if endpoint type is set to 3"
- name: "AzureTenantID"
in: "formData"
type: "string"
description: "Azure tenant ID. Required if endpoint type is set to 3"
- name: "AzureAuthenticationKey"
in: "formData"
type: "string"
description: "Azure authentication key. Required if endpoint type is set to 3"
responses: responses:
200: 200:
description: "Success" description: "Success"
@ -3221,21 +3209,6 @@ definitions:
type: "string" type: "string"
example: "/data/tls/key.pem" example: "/data/tls/key.pem"
description: "Path to the TLS client key file" description: "Path to the TLS client key file"
AzureCredentials:
type: "object"
properties:
ApplicationID:
type: "string"
example: "eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4"
description: "Azure application ID"
TenantID:
type: "string"
example: "34ddc78d-4fel-2358-8cc1-df84c8o839f5"
description: "Azure tenant ID"
AuthenticationKey:
type: "string"
example: "cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
description: "Azure authentication key"
LDAPSearchSettings: LDAPSearchSettings:
type: "object" type: "object"
properties: properties:
@ -3507,7 +3480,7 @@ definitions:
Type: Type:
type: "integer" type: "integer"
example: 1 example: 1
description: "Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment or 3 for an Azure environment." description: "Endpoint environment type. 1 for a Docker environment or 2 for an agent on Docker environment"
URL: URL:
type: "string" type: "string"
example: "docker.mydomain.tld:2375" example: "docker.mydomain.tld:2375"
@ -3536,8 +3509,6 @@ definitions:
description: "Team identifier" description: "Team identifier"
TLSConfig: TLSConfig:
$ref: "#/definitions/TLSConfiguration" $ref: "#/definitions/TLSConfiguration"
AzureCredentials:
$ref: "#/definitions/AzureCredentials"
EndpointSubset: EndpointSubset:
type: "object" type: "object"
properties: properties:
@ -3552,7 +3523,7 @@ definitions:
Type: Type:
type: "integer" type: "integer"
example: 1 example: 1
description: "Endpoint environment type. 1 for a Docker environment, 2 for an agent on Docker environment, 3 for an Azure environment." description: "Endpoint environment type. 1 for a Docker environment or 2 for an agent on Docker environment"
URL: URL:
type: "string" type: "string"
example: "docker.mydomain.tld:2375" example: "docker.mydomain.tld:2375"
@ -3732,18 +3703,6 @@ definitions:
type: "boolean" type: "boolean"
example: false example: false
description: "Skip client verification when using TLS" description: "Skip client verification when using TLS"
ApplicationID:
type: "string"
example: "eag7cdo9-o09l-9i83-9dO9-f0b23oe78db4"
description: "Azure application ID"
TenantID:
type: "string"
example: "34ddc78d-4fel-2358-8cc1-df84c8o839f5"
description: "Azure tenant ID"
AuthenticationKey:
type: "string"
example: "cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
description: "Azure authentication key"
UserAccessPolicies: UserAccessPolicies:
$ref: "#/definitions/UserAccessPolicies" $ref: "#/definitions/UserAccessPolicies"
TeamAccessPolicies: TeamAccessPolicies:

View File

@ -2,7 +2,6 @@ import '../assets/css/app.css';
import angular from 'angular'; import angular from 'angular';
import './agent/_module'; import './agent/_module';
import './azure/_module';
import './docker/__module'; import './docker/__module';
import './edge/__module'; import './edge/__module';
import './portainer/__module'; import './portainer/__module';
@ -28,7 +27,6 @@ angular.module('portainer', [
'luegg.directives', 'luegg.directives',
'portainer.app', 'portainer.app',
'portainer.agent', 'portainer.agent',
'portainer.azure',
'portainer.docker', 'portainer.docker',
'portainer.edge', 'portainer.edge',
'portainer.extensions', 'portainer.extensions',

View File

@ -1,51 +0,0 @@
angular.module('portainer.azure', ['portainer.app']).config([
'$stateRegistryProvider',
function ($stateRegistryProvider) {
'use strict';
var azure = {
name: 'azure',
url: '/azure',
parent: 'root',
abstract: true,
};
var containerInstances = {
name: 'azure.containerinstances',
url: '/containerinstances',
views: {
'content@': {
templateUrl: './views/containerinstances/containerinstances.html',
controller: 'AzureContainerInstancesController',
},
},
};
var containerInstanceCreation = {
name: 'azure.containerinstances.new',
url: '/new/',
views: {
'content@': {
templateUrl: './views/containerinstances/create/createcontainerinstance.html',
controller: 'AzureCreateContainerInstanceController',
},
},
};
var dashboard = {
name: 'azure.dashboard',
url: '/dashboard',
views: {
'content@': {
templateUrl: './views/dashboard/dashboard.html',
controller: 'AzureDashboardController',
},
},
};
$stateRegistryProvider.register(azure);
$stateRegistryProvider.register(containerInstances);
$stateRegistryProvider.register(containerInstanceCreation);
$stateRegistryProvider.register(dashboard);
},
]);

View File

@ -1,8 +0,0 @@
angular.module('portainer.azure').component('azureEndpointConfig', {
bindings: {
applicationId: '=',
tenantId: '=',
authenticationKey: '=',
},
templateUrl: './azureEndpointConfig.html',
});

View File

@ -1,36 +0,0 @@
<div>
<div class="col-sm-12 form-section-title">
Azure configuration
</div>
<!-- applicationId-input -->
<div class="form-group">
<label for="azure_credential_appid" class="col-sm-3 col-lg-2 control-label text-left">Application ID</label>
<div class="col-sm-9 col-lg-10">
<input type="text" class="form-control" name="azure_credential_appid" ng-model="$ctrl.applicationId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" required />
</div>
</div>
<!-- !applicationId-input -->
<!-- tenantId-input -->
<div class="form-group">
<label for="azure_credential_tenantid" class="col-sm-3 col-lg-2 control-label text-left">Tenant ID</label>
<div class="col-sm-9 col-lg-10">
<input type="text" class="form-control" name="azure_credential_tenantid" ng-model="$ctrl.tenantId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" required />
</div>
</div>
<!-- !tenantId-input -->
<!-- authenticationkey-input -->
<div class="form-group">
<label for="azure_credential_authkey" class="col-sm-3 col-lg-2 control-label text-left">Authentication key</label>
<div class="col-sm-9 col-lg-10">
<input
type="text"
class="form-control"
name="azure_credential_authkey"
ng-model="$ctrl.authenticationKey"
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
required
/>
</div>
</div>
<!-- !authenticationkey-input -->
</div>

View File

@ -1,3 +0,0 @@
angular.module('portainer.azure').component('azureSidebarContent', {
templateUrl: './azureSidebarContent.html',
});

View File

@ -1,6 +0,0 @@
<li class="sidebar-list">
<a ui-sref="azure.dashboard" ui-sref-active="active">Dashboard <span class="menu-icon fa fa-tachometer-alt fa-fw"></span></a>
</li>
<li class="sidebar-list">
<a ui-sref="azure.containerinstances" ui-sref-active="active">Container instances <span class="menu-icon fa fa-server fa-fw"></span></a>
</li>

View File

@ -1,105 +0,0 @@
<div class="datatable">
<rd-widget>
<rd-widget-body classes="no-padding">
<div class="toolBar">
<div class="toolBarTitle"> <i class="fa" ng-class="$ctrl.titleIcon" aria-hidden="true" style="margin-right: 2px;"></i> {{ $ctrl.titleText }} </div>
</div>
<div class="actionBar">
<button type="button" class="btn btn-sm btn-danger" ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
</button>
<button type="button" class="btn btn-sm btn-primary" ui-sref="azure.containerinstances.new">
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add container
</button>
</div>
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input
type="text"
class="searchInput"
ng-model="$ctrl.state.textFilter"
ng-change="$ctrl.onTextFilterChange()"
placeholder="Search..."
auto-focus
ng-model-options="{ debounce: 300 }"
/>
</div>
<div class="table-responsive">
<table class="table table-hover table-filters nowrap-cells">
<thead>
<tr>
<th>
<span class="md-checkbox">
<input id="select_all" type="checkbox" ng-model="$ctrl.state.selectAll" ng-change="$ctrl.selectAll()" />
<label for="select_all"></label>
</span>
<a ng-click="$ctrl.changeOrderBy('Name')">
Name
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Name' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('Location')">
Location
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Location' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Location' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
Published Ports
</th>
</tr>
</thead>
<tbody>
<tr
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
ng-class="{ active: item.Checked }"
>
<td>
<span class="md-checkbox">
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-click="$ctrl.selectItem(item, $event)" />
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="azure.containerinstances.container({ id: item.Id })">{{ item.Name | truncate: 50 }}</a>
</td>
<td>{{ item.Location }}</td>
<td>
<a ng-if="item.Ports.length > 0" ng-repeat="p in item.Ports" class="image-tag" ng-href="http://{{ item.IPAddress }}:{{ p.port }}" target="_blank">
<i class="fa fa-external-link-alt" aria-hidden="true"></i> :{{ p.port }}
</a>
<span ng-if="item.Ports.length == 0">-</span>
</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="3" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="3" class="text-center text-muted">No container available.</td>
</tr>
</tbody>
</table>
</div>
<div class="footer" ng-if="$ctrl.dataset">
<div class="infoBar" ng-if="$ctrl.state.selectedItemCount !== 0"> {{ $ctrl.state.selectedItemCount }} item(s) selected </div>
<div class="paginationControls">
<form class="form-inline">
<span class="limitSelector">
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
</select>
</span>
<dir-pagination-controls max-size="5"></dir-pagination-controls>
</form>
</div>
</div>
</rd-widget-body>
</rd-widget>
</div>

View File

@ -1,13 +0,0 @@
angular.module('portainer.azure').component('containergroupsDatatable', {
templateUrl: './containerGroupsDatatable.html',
controller: 'GenericDatatableController',
bindings: {
title: '@',
titleIcon: '@',
dataset: '<',
tableKey: '@',
orderBy: '@',
reverseOrder: '<',
removeAction: '<',
},
});

View File

@ -1,66 +0,0 @@
export function ContainerGroupDefaultModel() {
this.Location = '';
this.OSType = 'Linux';
this.Name = '';
this.Image = '';
this.AllocatePublicIP = true;
this.Ports = [
{
container: 80,
host: 80,
protocol: 'TCP',
},
];
this.CPU = 1;
this.Memory = 1;
}
export function ContainerGroupViewModel(data) {
this.Id = data.id;
this.Name = data.name;
this.Location = data.location;
this.IPAddress = data.properties.ipAddress.ip;
this.Ports = data.properties.ipAddress.ports;
}
export function CreateContainerGroupRequest(model) {
this.location = model.Location;
var containerPorts = [];
var addressPorts = [];
for (var i = 0; i < model.Ports.length; i++) {
var binding = model.Ports[i];
containerPorts.push({
port: binding.container,
});
addressPorts.push({
port: binding.host,
protocol: binding.protocol,
});
}
this.properties = {
osType: model.OSType,
containers: [
{
name: model.Name,
properties: {
image: model.Image,
ports: containerPorts,
resources: {
requests: {
cpu: model.CPU,
memoryInGB: model.Memory,
},
},
},
},
],
ipAddress: {
type: model.AllocatePublicIP ? 'Public' : 'Private',
ports: addressPorts,
},
};
}

View File

@ -1,6 +0,0 @@
export function LocationViewModel(data) {
this.Id = data.id;
this.SubscriptionId = data.subscriptionId;
this.DisplayName = data.displayName;
this.Name = data.name;
}

View File

@ -1,9 +0,0 @@
import _ from 'lodash-es';
export function ContainerInstanceProviderViewModel(data) {
this.Id = data.id;
this.Namespace = data.namespace;
var containerGroupType = _.find(data.resourceTypes, { resourceType: 'containerGroups' });
this.Locations = containerGroupType.locations;
}

View File

@ -1,6 +0,0 @@
export function ResourceGroupViewModel(data, subscriptionId) {
this.Id = data.id;
this.SubscriptionId = subscriptionId;
this.Name = data.name;
this.Location = data.location;
}

View File

@ -1,4 +0,0 @@
export function SubscriptionViewModel(data) {
this.Id = data.subscriptionId;
this.Name = data.displayName;
}

View File

@ -1,20 +0,0 @@
angular.module('portainer.azure').factory('Azure', [
'$http',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function AzureFactory($http, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
var service = {};
service.delete = function (id, apiVersion) {
var url = API_ENDPOINT_ENDPOINTS + '/' + EndpointProvider.endpointID() + '/azure' + id + '?api-version=' + apiVersion;
return $http({
method: 'DELETE',
url: url,
});
};
return service;
},
]);

View File

@ -1,45 +0,0 @@
angular.module('portainer.azure').factory('ContainerGroup', [
'$resource',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function ContainerGroupFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
var resource = {};
var base = $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/providers/Microsoft.ContainerInstance/containerGroups',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2018-04-01',
},
{
query: { method: 'GET', params: { subscriptionId: '@subscriptionId' } },
}
);
var withResourceGroup = $resource(
API_ENDPOINT_ENDPOINTS +
'/:endpointId/azure/subscriptions/:subscriptionId/resourceGroups/:resourceGroupName/providers/Microsoft.ContainerInstance/containerGroups/:containerGroupName',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2018-04-01',
},
{
create: {
method: 'PUT',
params: {
subscriptionId: '@subscriptionId',
resourceGroupName: '@resourceGroupName',
containerGroupName: '@containerGroupName',
},
},
}
);
resource.query = base.query;
resource.create = withResourceGroup.create;
return resource;
},
]);

View File

@ -1,18 +0,0 @@
angular.module('portainer.azure').factory('Location', [
'$resource',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function LocationFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/locations',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2016-06-01',
},
{
query: { method: 'GET', params: { subscriptionId: '@subscriptionId' } },
}
);
},
]);

View File

@ -1,18 +0,0 @@
angular.module('portainer.azure').factory('Provider', [
'$resource',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function ProviderFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/providers/:providerNamespace',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2018-02-01',
},
{
get: { method: 'GET', params: { subscriptionId: '@subscriptionId', providerNamespace: '@providerNamespace' } },
}
);
},
]);

View File

@ -1,18 +0,0 @@
angular.module('portainer.azure').factory('ResourceGroup', [
'$resource',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function ResourceGroupFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/resourcegroups',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2018-02-01',
},
{
query: { method: 'GET', params: { subscriptionId: '@subscriptionId' } },
}
);
},
]);

View File

@ -1,18 +0,0 @@
angular.module('portainer.azure').factory('Subscription', [
'$resource',
'API_ENDPOINT_ENDPOINTS',
'EndpointProvider',
function SubscriptionFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2016-06-01',
},
{
query: { method: 'GET' },
}
);
},
]);

View File

@ -1,72 +0,0 @@
angular.module('portainer.azure').factory('AzureService', [
'$q',
'Azure',
'SubscriptionService',
'ResourceGroupService',
'ContainerGroupService',
'ProviderService',
function AzureServiceFactory($q, Azure, SubscriptionService, ResourceGroupService, ContainerGroupService, ProviderService) {
'use strict';
var service = {};
service.deleteContainerGroup = function (id) {
return Azure.delete(id, '2018-04-01');
};
service.createContainerGroup = function (model, subscriptionId, resourceGroupName) {
return ContainerGroupService.create(model, subscriptionId, resourceGroupName);
};
service.subscriptions = function () {
return SubscriptionService.subscriptions();
};
service.containerInstanceProvider = function (subscriptions) {
return retrieveResourcesForEachSubscription(subscriptions, ProviderService.containerInstanceProvider);
};
service.resourceGroups = function (subscriptions) {
return retrieveResourcesForEachSubscription(subscriptions, ResourceGroupService.resourceGroups);
};
service.containerGroups = function (subscriptions) {
return retrieveResourcesForEachSubscription(subscriptions, ContainerGroupService.containerGroups);
};
service.aggregate = function (resourcesBySubcription) {
var aggregatedResources = [];
Object.keys(resourcesBySubcription).forEach(function (key) {
aggregatedResources = aggregatedResources.concat(resourcesBySubcription[key]);
});
return aggregatedResources;
};
function retrieveResourcesForEachSubscription(subscriptions, resourceQuery) {
var deferred = $q.defer();
var resources = {};
var resourceQueries = [];
for (var i = 0; i < subscriptions.length; i++) {
var subscription = subscriptions[i];
resourceQueries.push(resourceQuery(subscription.Id));
}
$q.all(resourceQueries)
.then(function success(data) {
for (var i = 0; i < data.length; i++) {
var result = data[i];
resources[subscriptions[i].Id] = result;
}
deferred.resolve(resources);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve resources', err: err });
});
return deferred.promise;
}
return service;
},
]);

View File

@ -1,41 +0,0 @@
import { ContainerGroupViewModel, CreateContainerGroupRequest } from '../models/container_group';
angular.module('portainer.azure').factory('ContainerGroupService', [
'$q',
'ContainerGroup',
function ContainerGroupServiceFactory($q, ContainerGroup) {
'use strict';
var service = {};
service.containerGroups = function (subscriptionId) {
var deferred = $q.defer();
ContainerGroup.query({ subscriptionId: subscriptionId })
.$promise.then(function success(data) {
var containerGroups = data.value.map(function (item) {
return new ContainerGroupViewModel(item);
});
deferred.resolve(containerGroups);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve container groups', err: err });
});
return deferred.promise;
};
service.create = function (model, subscriptionId, resourceGroupName) {
var payload = new CreateContainerGroupRequest(model);
return ContainerGroup.create(
{
subscriptionId: subscriptionId,
resourceGroupName: resourceGroupName,
containerGroupName: model.Name,
},
payload
).$promise;
};
return service;
},
]);

View File

@ -1,29 +0,0 @@
import { LocationViewModel } from '../models/location';
angular.module('portainer.azure').factory('LocationService', [
'$q',
'Location',
function LocationServiceFactory($q, Location) {
'use strict';
var service = {};
service.locations = function (subscriptionId) {
var deferred = $q.defer();
Location.query({ subscriptionId: subscriptionId })
.$promise.then(function success(data) {
var locations = data.value.map(function (item) {
return new LocationViewModel(item);
});
deferred.resolve(locations);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve locations', err: err });
});
return deferred.promise;
};
return service;
},
]);

View File

@ -1,27 +0,0 @@
import { ContainerInstanceProviderViewModel } from '../models/provider';
angular.module('portainer.azure').factory('ProviderService', [
'$q',
'Provider',
function ProviderServiceFactory($q, Provider) {
'use strict';
var service = {};
service.containerInstanceProvider = function (subscriptionId) {
var deferred = $q.defer();
Provider.get({ subscriptionId: subscriptionId, providerNamespace: 'Microsoft.ContainerInstance' })
.$promise.then(function success(data) {
var provider = new ContainerInstanceProviderViewModel(data);
deferred.resolve(provider);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve provider', err: err });
});
return deferred.promise;
};
return service;
},
]);

View File

@ -1,29 +0,0 @@
import { ResourceGroupViewModel } from '../models/resource_group';
angular.module('portainer.azure').factory('ResourceGroupService', [
'$q',
'ResourceGroup',
function ResourceGroupServiceFactory($q, ResourceGroup) {
'use strict';
var service = {};
service.resourceGroups = function (subscriptionId) {
var deferred = $q.defer();
ResourceGroup.query({ subscriptionId: subscriptionId })
.$promise.then(function success(data) {
var resourceGroups = data.value.map(function (item) {
return new ResourceGroupViewModel(item, subscriptionId);
});
deferred.resolve(resourceGroups);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve resource groups', err: err });
});
return deferred.promise;
};
return service;
},
]);

View File

@ -1,29 +0,0 @@
import { SubscriptionViewModel } from '../models/subscription';
angular.module('portainer.azure').factory('SubscriptionService', [
'$q',
'Subscription',
function SubscriptionServiceFactory($q, Subscription) {
'use strict';
var service = {};
service.subscriptions = function () {
var deferred = $q.defer();
Subscription.query({})
.$promise.then(function success(data) {
var subscriptions = data.value.map(function (item) {
return new SubscriptionViewModel(item);
});
deferred.resolve(subscriptions);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to retrieve subscriptions', err: err });
});
return deferred.promise;
};
return service;
},
]);

View File

@ -1,44 +0,0 @@
angular.module('portainer.azure').controller('AzureContainerInstancesController', [
'$scope',
'$state',
'AzureService',
'Notifications',
function ($scope, $state, AzureService, Notifications) {
function initView() {
AzureService.subscriptions()
.then(function success(data) {
var subscriptions = data;
return AzureService.containerGroups(subscriptions);
})
.then(function success(data) {
$scope.containerGroups = AzureService.aggregate(data);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load container groups');
});
}
$scope.deleteAction = function (selectedItems) {
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (item) {
AzureService.deleteContainerGroup(item.Id)
.then(function success() {
Notifications.success('Container group successfully removed', item.Name);
var index = $scope.containerGroups.indexOf(item);
$scope.containerGroups.splice(index, 1);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove container group');
})
.finally(function final() {
--actionCount;
if (actionCount === 0) {
$state.reload();
}
});
});
};
initView();
},
]);

View File

@ -1,21 +0,0 @@
<rd-header>
<rd-header-title title-text="Container list">
<a data-toggle="tooltip" title="Refresh" ui-sref="azure.containerinstances" ui-sref-opts="{reload: true}">
<i class="fa fa-sync" aria-hidden="true"></i>
</a>
</rd-header-title>
<rd-header-content>Container instances</rd-header-content>
</rd-header>
<div class="row">
<div class="col-sm-12">
<containergroups-datatable
title-text="Containers"
title-icon="fa-server"
dataset="containerGroups"
table-key="containergroups"
order-by="Name"
remove-action="deleteAction"
></containergroups-datatable>
</div>
</div>

View File

@ -1,93 +0,0 @@
import { ContainerGroupDefaultModel } from '../../../models/container_group';
angular.module('portainer.azure').controller('AzureCreateContainerInstanceController', [
'$q',
'$scope',
'$state',
'AzureService',
'Notifications',
function ($q, $scope, $state, AzureService, Notifications) {
var allResourceGroups = [];
var allProviders = [];
$scope.state = {
actionInProgress: false,
selectedSubscription: null,
selectedResourceGroup: null,
};
$scope.changeSubscription = function () {
var selectedSubscription = $scope.state.selectedSubscription;
updateResourceGroupsAndLocations(selectedSubscription, allResourceGroups, allProviders);
};
$scope.addPortBinding = function () {
$scope.model.Ports.push({ host: '', container: '', protocol: 'TCP' });
};
$scope.removePortBinding = function (index) {
$scope.model.Ports.splice(index, 1);
};
$scope.create = function () {
var model = $scope.model;
var subscriptionId = $scope.state.selectedSubscription.Id;
var resourceGroupName = $scope.state.selectedResourceGroup.Name;
$scope.state.actionInProgress = true;
AzureService.createContainerGroup(model, subscriptionId, resourceGroupName)
.then(function success() {
Notifications.success('Container successfully created', model.Name);
$state.go('azure.containerinstances');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to create container');
})
.finally(function final() {
$scope.state.actionInProgress = false;
});
};
function updateResourceGroupsAndLocations(subscription, resourceGroups, providers) {
$scope.state.selectedResourceGroup = resourceGroups[subscription.Id][0];
$scope.resourceGroups = resourceGroups[subscription.Id];
var currentSubLocations = providers[subscription.Id].Locations;
$scope.model.Location = currentSubLocations[0];
$scope.locations = currentSubLocations;
}
function initView() {
var model = new ContainerGroupDefaultModel();
AzureService.subscriptions()
.then(function success(data) {
var subscriptions = data;
$scope.state.selectedSubscription = subscriptions[0];
$scope.subscriptions = subscriptions;
return $q.all({
resourceGroups: AzureService.resourceGroups(subscriptions),
containerInstancesProviders: AzureService.containerInstanceProvider(subscriptions),
});
})
.then(function success(data) {
var resourceGroups = data.resourceGroups;
allResourceGroups = resourceGroups;
var containerInstancesProviders = data.containerInstancesProviders;
allProviders = containerInstancesProviders;
$scope.model = model;
var selectedSubscription = $scope.state.selectedSubscription;
updateResourceGroupsAndLocations(selectedSubscription, resourceGroups, containerInstancesProviders);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve Azure resources');
});
}
initView();
},
]);

View File

@ -1,167 +0,0 @@
<rd-header>
<rd-header-title title-text="Create container instance"></rd-header-title>
<rd-header-content> <a ui-sref="azure.containerinstances">Container instances</a> &gt; Add container </rd-header-content>
</rd-header>
<div class="row">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal" autocomplete="off">
<div class="col-sm-12 form-section-title">
Azure settings
</div>
<!-- subscription-input -->
<div class="form-group">
<label for="azure_subscription" class="col-sm-1 control-label text-left">Subscription</label>
<div class="col-sm-11">
<select
class="form-control"
name="azure_subscription"
ng-model="state.selectedSubscription"
ng-options="subscription.Name for subscription in subscriptions"
ng-change="changeSubscription()"
></select>
</div>
</div>
<!-- !subscription-input -->
<!-- resourcegroup-input -->
<div class="form-group">
<label for="azure_resourcegroup" class="col-sm-1 control-label text-left">Resource group</label>
<div class="col-sm-11">
<select
class="form-control"
name="azure_resourcegroup"
ng-model="state.selectedResourceGroup"
ng-options="resourceGroup.Name for resourceGroup in resourceGroups"
></select>
</div>
</div>
<!-- !resourcegroup-input -->
<!-- location-input -->
<div class="form-group">
<label for="azure_location" class="col-sm-1 control-label text-left">Location</label>
<div class="col-sm-11">
<select class="form-control" name="azure_location" ng-model="model.Location" ng-options="location for location in locations"></select>
</div>
</div>
<!-- !location-input -->
<div class="col-sm-12 form-section-title">
Container configuration
</div>
<!-- name-input -->
<div class="form-group">
<label for="container_name" class="col-sm-1 control-label text-left">Name</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="model.Name" name="container_name" placeholder="e.g. myContainer" />
</div>
</div>
<!-- !name-input -->
<!-- image-input -->
<div class="form-group">
<label for="image_name" class="col-sm-1 control-label text-left">Image</label>
<div class="col-sm-11">
<input type="text" class="form-control" ng-model="model.Image" name="image_name" placeholder="e.g. nginx:alpine" />
</div>
</div>
<!-- !image-input -->
<!-- os-input -->
<div class="form-group">
<label for="container_os" class="col-sm-1 control-label text-left">OS</label>
<div class="col-sm-11">
<select class="form-control" ng-model="model.OSType" name="container_os">
<option value="Linux">Linux</option>
<option value="Windows">Windows</option>
</select>
</div>
</div>
<!-- !os-input -->
<!-- port-mapping -->
<div class="form-group">
<div class="col-sm-12">
<label class="control-label text-left">Port mapping</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="addPortBinding()">
<i class="fa fa-plus-circle" aria-hidden="true"></i> map additional port
</span>
</div>
<!-- port-mapping-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="binding in model.Ports" style="margin-top: 2px;">
<!-- host-port -->
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">host</span>
<input type="text" class="form-control" ng-model="binding.host" placeholder="e.g. 80" />
</div>
<!-- !host-port -->
<span style="margin: 0 10px 0 10px;">
<i class="fa fa-long-arrow-alt-right" aria-hidden="true"></i>
</span>
<!-- container-port -->
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">container</span>
<input type="text" class="form-control" ng-model="binding.container" placeholder="e.g. 80" />
</div>
<!-- !container-port -->
<!-- protocol-actions -->
<div class="input-group col-sm-3 input-group-sm">
<div class="btn-group btn-group-sm">
<label class="btn btn-primary" ng-model="binding.protocol" uib-btn-radio="'TCP'">TCP</label>
<label class="btn btn-primary" ng-model="binding.protocol" uib-btn-radio="'UDP'">UDP</label>
</div>
<button class="btn btn-sm btn-danger" type="button" ng-click="removePortBinding($index)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
<!-- !protocol-actions -->
</div>
</div>
<!-- !port-mapping-input-list -->
</div>
<!-- !port-mapping -->
<!-- public-ip -->
<div class="form-group">
<div class="col-sm-12">
<label for="public_ip" class="control-label text-left">
Allocate public IP address
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" name="public_ip" ng-model="model.AllocatePublicIP" /><i></i> </label>
</div>
</div>
<!-- public-ip -->
<div class="col-sm-12 form-section-title">
Container resources
</div>
<!-- cpu-input -->
<div class="form-group">
<label for="container_cpu" class="col-sm-1 control-label text-left">CPU</label>
<div class="col-sm-11">
<input type="number" class="form-control" ng-model="model.CPU" name="container_cpu" placeholder="1" />
</div>
</div>
<!-- !cpu-input -->
<!-- memory-input -->
<div class="form-group">
<label for="container_memory" class="col-sm-1 control-label text-left">Memory</label>
<div class="col-sm-11">
<input type="number" class="form-control" ng-model="model.Memory" name="container_memory" placeholder="1" />
</div>
</div>
<!-- !memory-input -->
<!-- actions -->
<div class="col-sm-12 form-section-title">
Actions
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress" ng-click="create()" button-spinner="state.actionInProgress">
<span ng-hide="state.actionInProgress">Deploy the container</span>
<span ng-show="state.actionInProgress">Deployment in progress...</span>
</button>
</div>
</div>
<!-- !actions -->
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>

View File

@ -1,33 +0,0 @@
<rd-header>
<rd-header-title title-text="Home"></rd-header-title>
<rd-header-content>Dashboard</rd-header-content>
</rd-header>
<div class="row" ng-if="subscriptions">
<div class="col-sm-12 col-md-6">
<a ui-sref="azure.subscriptions">
<rd-widget>
<rd-widget-body>
<div class="widget-icon blue pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="title">{{ subscriptions.length }}</div>
<div class="comment">Subscriptions</div>
</rd-widget-body>
</rd-widget>
</a>
</div>
<div class="col-sm-12 col-md-6" ng-if="resourceGroups">
<a ui-sref="azure.resourceGroups">
<rd-widget>
<rd-widget-body>
<div class="widget-icon blue pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="title">{{ resourceGroups.length }}</div>
<div class="comment">Resource groups</div>
</rd-widget-body>
</rd-widget>
</a>
</div>
</div>

View File

@ -1,23 +0,0 @@
angular.module('portainer.azure').controller('AzureDashboardController', [
'$scope',
'AzureService',
'Notifications',
function ($scope, AzureService, Notifications) {
function initView() {
AzureService.subscriptions()
.then(function success(data) {
var subscriptions = data;
$scope.subscriptions = subscriptions;
return AzureService.resourceGroups(subscriptions);
})
.then(function success(data) {
$scope.resourceGroups = AzureService.aggregate(data);
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to load dashboard data');
});
}
initView();
},
]);

View File

@ -1,6 +1,6 @@
<div class="blocklist-item" ng-click="$ctrl.onSelect($ctrl.model)"> <div class="blocklist-item" ng-click="$ctrl.onSelect($ctrl.model)">
<div class="blocklist-item-box"> <div class="blocklist-item-box">
<span ng-class="['blocklist-item-logo', 'endpoint-item', { azure: $ctrl.model.Type === 3 }]"> <span class="blocklist-item-logo endpoint-item">
<i ng-if="$ctrl.model.Type !== 4" ng-class="$ctrl.model.Type | endpointtypeicon" class="fa-4x blue-icon" aria-hidden="true"></i> <i ng-if="$ctrl.model.Type !== 4" ng-class="$ctrl.model.Type | endpointtypeicon" class="fa-4x blue-icon" aria-hidden="true"></i>
<img ng-if="$ctrl.model.Type === 4" src="../../../../../assets/images/edge_endpoint.png" /> <img ng-if="$ctrl.model.Type === 4" src="../../../../../assets/images/edge_endpoint.png" />
</span> </span>

View File

@ -128,8 +128,6 @@ angular
return 'Docker'; return 'Docker';
} else if (type === 2) { } else if (type === 2) {
return 'Agent'; return 'Agent';
} else if (type === 3) {
return 'Azure ACI';
} else if (type === 4) { } else if (type === 4) {
return 'Edge Agent'; return 'Edge Agent';
} }

View File

@ -84,20 +84,6 @@ angular.module('portainer.app').factory('EndpointService', [
return deferred.promise; return deferred.promise;
}; };
service.createAzureEndpoint = function (name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
var deferred = $q.defer();
FileUploadService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds)
.then(function success(response) {
deferred.resolve(response.data);
})
.catch(function error(err) {
deferred.reject({ msg: 'Unable to connect to Azure', err: err });
});
return deferred.promise;
};
service.executeJobFromFileUpload = function (image, jobFile, endpointId, nodeName) { service.executeJobFromFileUpload = function (image, jobFile, endpointId, nodeName) {
return FileUploadService.executeEndpointJob(image, jobFile, endpointId, nodeName); return FileUploadService.executeEndpointJob(image, jobFile, endpointId, nodeName);
}; };

View File

@ -137,22 +137,6 @@ angular.module('portainer.app').factory('FileUploadService', [
}); });
}; };
service.createAzureEndpoint = function (name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
return Upload.upload({
url: 'api/endpoints',
data: {
Name: name,
EndpointType: 3,
GroupID: groupId,
TagIds: Upload.json(tagIds),
AzureApplicationID: applicationId,
AzureTenantID: tenantId,
AzureAuthenticationKey: authenticationKey,
},
ignoreLoadingBar: true,
});
};
service.uploadLDAPTLSFiles = function (TLSCAFile, TLSCertFile, TLSKeyFile) { service.uploadLDAPTLSFiles = function (TLSCAFile, TLSCertFile, TLSKeyFile) {
var queue = []; var queue = [];

View File

@ -168,14 +168,6 @@ angular.module('portainer.app').factory('StateManager', [
manager.updateEndpointState = function (endpoint, extensions) { manager.updateEndpointState = function (endpoint, extensions) {
var deferred = $q.defer(); var deferred = $q.defer();
if (endpoint.Type === 3) {
state.endpoint.name = endpoint.Name;
state.endpoint.mode = { provider: 'AZURE' };
LocalStorage.storeEndpointState(state.endpoint);
deferred.resolve();
return deferred.promise;
}
$q.all({ $q.all({
version: endpoint.Status === 1 ? SystemService.version() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Version), version: endpoint.Status === 1 ? SystemService.version() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Version),
info: endpoint.Status === 1 ? SystemService.info() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Info), info: endpoint.Status === 1 ? SystemService.info() : $q.when(endpoint.Snapshots[0].SnapshotRaw.Info),

View File

@ -27,9 +27,6 @@ angular
PublicURL: '', PublicURL: '',
GroupId: 1, GroupId: 1,
SecurityFormData: new EndpointSecurityFormData(), SecurityFormData: new EndpointSecurityFormData(),
AzureApplicationId: '',
AzureTenantId: '',
AzureAuthenticationKey: '',
TagIds: [], TagIds: [],
}; };
@ -85,17 +82,6 @@ angular
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.addAzureEndpoint = function () {
var name = $scope.formValues.Name;
var applicationId = $scope.formValues.AzureApplicationId;
var tenantId = $scope.formValues.AzureTenantId;
var authenticationKey = $scope.formValues.AzureAuthenticationKey;
var groupId = $scope.formValues.GroupId;
var tagIds = $scope.formValues.TagIds;
createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds);
};
$scope.onCreateTag = function onCreateTag(tagName) { $scope.onCreateTag = function onCreateTag(tagName) {
return $async(onCreateTagAsync, tagName); return $async(onCreateTagAsync, tagName);
}; };
@ -110,21 +96,6 @@ angular
} }
} }
function createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
$scope.state.actionInProgress = true;
EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds)
.then(function success() {
Notifications.success('Endpoint created', name);
$state.go('portainer.endpoints', {}, { reload: true });
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to create endpoint');
})
.finally(function final() {
$scope.state.actionInProgress = false;
});
}
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) {
$scope.state.actionInProgress = true; $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)

View File

@ -44,16 +44,6 @@
<p>Directly connect to the Docker API</p> <p>Directly connect to the Docker API</p>
</label> </label>
</div> </div>
<div>
<input type="radio" id="azure_endpoint" ng-model="state.EnvironmentType" value="azure" />
<label for="azure_endpoint">
<div class="boxselector_header">
<i class="fab fa-microsoft" aria-hidden="true" style="margin-right: 2px;"></i>
Azure
</div>
<p>Connect to Microsoft Azure ACI</p>
</label>
</div>
</div> </div>
</div> </div>
<div ng-if="state.EnvironmentType === 'docker'"> <div ng-if="state.EnvironmentType === 'docker'">
@ -97,29 +87,6 @@
</span> </span>
</div> </div>
</div> </div>
<div ng-if="state.EnvironmentType === 'azure'">
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="form-group">
<div class="col-sm-12">
<span class="small">
<p class="text-muted"> <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i> This feature is experimental. </p>
<p class="text-primary">
Connect to Microsoft Azure to manage Azure Container Instances (ACI).
</p>
<p class="text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Have a look at
<a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank"
>the Azure documentation</a
>
to retrieve the credentials required below.
</p>
</span>
</div>
</div>
</div>
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">
Environment details Environment details
</div> </div>
@ -214,76 +181,6 @@
</div> </div>
</div> </div>
<!-- !endpoint-public-url-input --> <!-- !endpoint-public-url-input -->
<!-- azure-details -->
<div ng-if="state.EnvironmentType === 'azure'">
<!-- applicationId-input -->
<div class="form-group">
<label for="azure_credential_appid" class="col-sm-3 col-lg-2 control-label text-left">Application ID</label>
<div class="col-sm-9 col-lg-10">
<input
type="text"
class="form-control"
name="azure_credential_appid"
ng-model="formValues.AzureApplicationId"
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
required
/>
</div>
</div>
<div class="form-group" ng-show="endpointCreationForm.azure_credential_appid.$invalid">
<div class="col-sm-12 small text-warning">
<div ng-messages="endpointCreationForm.azure_credential_appid.$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
</div>
</div>
</div>
<!-- !applicationId-input -->
<!-- tenantId-input -->
<div class="form-group">
<label for="azure_credential_tenantid" class="col-sm-3 col-lg-2 control-label text-left">Tenant ID</label>
<div class="col-sm-9 col-lg-10">
<input
type="text"
class="form-control"
name="azure_credential_tenantid"
ng-model="formValues.AzureTenantId"
placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
required
/>
</div>
</div>
<div class="form-group" ng-show="endpointCreationForm.azure_credential_tenantid.$invalid">
<div class="col-sm-12 small text-warning">
<div ng-messages="endpointCreationForm.azure_credential_tenantid.$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
</div>
</div>
</div>
<!-- !tenantId-input -->
<!-- authenticationkey-input -->
<div class="form-group">
<label for="azure_credential_authkey" class="col-sm-3 col-lg-2 control-label text-left">Authentication key</label>
<div class="col-sm-9 col-lg-10">
<input
type="text"
class="form-control"
name="azure_credential_authkey"
ng-model="formValues.AzureAuthenticationKey"
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
required
/>
</div>
</div>
<div class="form-group" ng-show="endpointCreationForm.azure_credential_authkey.$invalid">
<div class="col-sm-12 small text-warning">
<div ng-messages="endpointCreationForm.azure_credential_authkey.$error">
<p ng-message="required"><i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This field is required.</p>
</div>
</div>
</div>
<!-- !authenticationkey-input -->
</div>
<!-- !azure-details -->
<!-- endpoint-security --> <!-- endpoint-security -->
<por-endpoint-security ng-if="state.EnvironmentType === 'docker'" form-data="formValues.SecurityFormData"></por-endpoint-security> <por-endpoint-security ng-if="state.EnvironmentType === 'docker'" form-data="formValues.SecurityFormData"></por-endpoint-security>
<!-- !endpoint-security --> <!-- !endpoint-security -->
@ -350,17 +247,6 @@
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span> <span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</span> <span ng-show="state.actionInProgress">Creating endpoint...</span>
</button> </button>
<button
ng-if="state.EnvironmentType === 'azure'"
type="submit"
class="btn btn-primary btn-sm"
ng-disabled="state.actionInProgress || !endpointCreationForm.$valid"
ng-click="addAzureEndpoint()"
button-spinner="state.actionInProgress"
>
<span ng-hide="state.actionInProgress"><i class="fa fa-plus" aria-hidden="true"></i> Add endpoint</span>
<span ng-show="state.actionInProgress">Creating endpoint...</span>
</button>
</div> </div>
</div> </div>
<!-- !actions --> <!-- !actions -->

View File

@ -118,12 +118,6 @@
<input type="text" class="form-control" id="endpoint_public_url" ng-model="endpoint.PublicURL" placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com" /> <input type="text" class="form-control" id="endpoint_public_url" ng-model="endpoint.PublicURL" placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com" />
</div> </div>
</div> </div>
<azure-endpoint-config
ng-if="endpoint.Type === 3"
application-id="endpoint.AzureCredentials.ApplicationID"
tenant-id="endpoint.AzureCredentials.TenantID"
authentication-key="endpoint.AzureCredentials.AuthenticationKey"
></azure-endpoint-config>
<!-- !endpoint-public-url-input --> <!-- !endpoint-public-url-input -->
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">
Metadata Metadata

View File

@ -89,9 +89,6 @@ angular
TLSCACert: TLSSkipVerify || securityData.TLSCACert === endpoint.TLSConfig.TLSCACert ? null : securityData.TLSCACert, TLSCACert: TLSSkipVerify || securityData.TLSCACert === endpoint.TLSConfig.TLSCACert ? null : securityData.TLSCACert,
TLSCert: TLSSkipClientVerify || securityData.TLSCert === endpoint.TLSConfig.TLSCert ? null : securityData.TLSCert, TLSCert: TLSSkipClientVerify || securityData.TLSCert === endpoint.TLSConfig.TLSCert ? null : securityData.TLSCert,
TLSKey: TLSSkipClientVerify || securityData.TLSKey === endpoint.TLSConfig.TLSKey ? null : securityData.TLSKey, TLSKey: TLSSkipClientVerify || securityData.TLSKey === endpoint.TLSConfig.TLSKey ? null : securityData.TLSKey,
AzureApplicationID: endpoint.AzureCredentials.ApplicationID,
AzureTenantID: endpoint.AzureCredentials.TenantID,
AzureAuthenticationKey: endpoint.AzureCredentials.AuthenticationKey,
}; };
if ($scope.endpointType !== 'local' && endpoint.Type !== 3) { if ($scope.endpointType !== 'local' && endpoint.Type !== 3) {

View File

@ -26,9 +26,7 @@ angular
}; };
$scope.goToDashboard = function (endpoint) { $scope.goToDashboard = function (endpoint) {
if (endpoint.Type === 3) { if (endpoint.Type === 4) {
return switchToAzureEndpoint(endpoint);
} else if (endpoint.Type === 4) {
return switchToEdgeEndpoint(endpoint); return switchToEdgeEndpoint(endpoint);
} }
@ -89,19 +87,6 @@ angular
return deferred.promise; return deferred.promise;
} }
function switchToAzureEndpoint(endpoint) {
EndpointProvider.setEndpointID(endpoint.Id);
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
StateManager.updateEndpointState(endpoint, [])
.then(function success() {
$state.go('azure.dashboard');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to connect to the Azure endpoint');
});
}
function switchToEdgeEndpoint(endpoint) { function switchToEdgeEndpoint(endpoint) {
if (!endpoint.EdgeID) { if (!endpoint.EdgeID) {
$state.go('portainer.endpoints.endpoint', { id: endpoint.Id }); $state.go('portainer.endpoints.endpoint', { id: endpoint.Id });

View File

@ -55,16 +55,6 @@
<p>Connect to a Portainer agent</p> <p>Connect to a Portainer agent</p>
</label> </label>
</div> </div>
<div>
<input type="radio" id="azure_endpoint" ng-model="formValues.EndpointType" value="azure" />
<label for="azure_endpoint">
<div class="boxselector_header">
<i class="fab fa-microsoft" aria-hidden="true" style="margin-right: 2px;"></i>
Azure
</div>
<p>Connect to Microsoft Azure ACI</p>
</label>
</div>
</div> </div>
</div> </div>
<!-- !endpoint-type --> <!-- !endpoint-type -->
@ -168,91 +158,6 @@
<!-- !actions --> <!-- !actions -->
</div> </div>
<!-- !agent-endpoint --> <!-- !agent-endpoint -->
<!-- azure-endpoint -->
<div ng-if="formValues.EndpointType === 'azure'">
<div class="col-sm-12 form-section-title">
Information
</div>
<div class="form-group">
<div class="col-sm-12">
<span class="small">
<p class="text-muted"> <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i> This feature is experimental. </p>
<p class="text-primary">
Connect to Microsoft Azure to manage Azure Container Instances (ACI).
</p>
<p class="text-muted">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Have a look at
<a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank"
>the Azure documentation</a
>
to retrieve the credentials required below.
</p>
</span>
</div>
</div>
<div class="col-sm-12 form-section-title">
Environment
</div>
<!-- name-input -->
<div class="form-group">
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="endpoint_name" ng-model="formValues.Name" placeholder="e.g. azure-01" />
</div>
</div>
<!-- !name-input -->
<div class="col-sm-12 form-section-title">
Azure credentials
</div>
<!-- applicationId-input -->
<div class="form-group">
<label for="azure_credential_appid" class="col-sm-4 col-lg-3 control-label text-left">Application ID</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="azure_credential_appid" ng-model="formValues.AzureApplicationId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
</div>
</div>
<!-- !applicationId-input -->
<!-- tenantId-input -->
<div class="form-group">
<label for="azure_credential_tenantid" class="col-sm-4 col-lg-3 control-label text-left">Tenant ID</label>
<div class="col-sm-8 col-lg-9">
<input type="text" class="form-control" id="azure_credential_tenantid" ng-model="formValues.AzureTenantId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
</div>
</div>
<!-- !tenantId-input -->
<!-- authenticationkey-input -->
<div class="form-group">
<label for="azure_credential_authkey" class="col-sm-4 col-lg-3 control-label text-left">Authentication key</label>
<div class="col-sm-8 col-lg-9">
<input
type="text"
class="form-control"
id="azure_credential_authkey"
ng-model="formValues.AzureAuthenticationKey"
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
/>
</div>
</div>
<!-- !authenticationkey-input -->
<!-- actions -->
<div class="form-group">
<div class="col-sm-12">
<button
type="submit"
class="btn btn-primary btn-sm"
ng-disabled="state.actionInProgress || !formValues.Name || !formValues.AzureApplicationId || !formValues.AzureTenantId || !formValues.AzureAuthenticationKey"
ng-click="createAzureEndpoint()"
button-spinner="state.actionInProgress"
>
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
<span ng-show="state.actionInProgress">Connecting...</span>
</button>
</div>
</div>
<!-- !actions -->
</div>
<!-- !azure-endpoint -->
<!-- remote-endpoint --> <!-- remote-endpoint -->
<div ng-if="formValues.EndpointType === 'remote'"> <div ng-if="formValues.EndpointType === 'remote'">
<div class="col-sm-12 form-section-title"> <div class="col-sm-12 form-section-title">

View File

@ -28,9 +28,6 @@ angular.module('portainer.app').controller('InitEndpointController', [
TLSCACert: null, TLSCACert: null,
TLSCert: null, TLSCert: null,
TLSKey: null, TLSKey: null,
AzureApplicationId: '',
AzureTenantId: '',
AzureAuthenticationKey: '',
}; };
$scope.createLocalEndpoint = function () { $scope.createLocalEndpoint = function () {
@ -47,15 +44,6 @@ angular.module('portainer.app').controller('InitEndpointController', [
}); });
}; };
$scope.createAzureEndpoint = function () {
var name = $scope.formValues.Name;
var applicationId = $scope.formValues.AzureApplicationId;
var tenantId = $scope.formValues.AzureTenantId;
var authenticationKey = $scope.formValues.AzureAuthenticationKey;
createAzureEndpoint(name, applicationId, tenantId, authenticationKey);
};
$scope.createAgentEndpoint = function () { $scope.createAgentEndpoint = function () {
var name = $scope.formValues.Name; var name = $scope.formValues.Name;
var URL = $scope.formValues.URL; var URL = $scope.formValues.URL;
@ -78,20 +66,6 @@ angular.module('portainer.app').controller('InitEndpointController', [
createRemoteEndpoint(name, 1, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile); createRemoteEndpoint(name, 1, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
}; };
function createAzureEndpoint(name, applicationId, tenantId, authenticationKey) {
$scope.state.actionInProgress = true;
EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, 1, [])
.then(function success() {
$state.go('portainer.home');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to connect to the Azure environment');
})
.finally(function final() {
$scope.state.actionInProgress = false;
});
}
function createRemoteEndpoint(name, type, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) { function createRemoteEndpoint(name, type, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) {
$scope.state.actionInProgress = true; $scope.state.actionInProgress = true;
EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, 1, [], TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, 1, [], TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)

View File

@ -13,9 +13,7 @@
<a ui-sref="portainer.home" ui-sref-active="active">Home <span class="menu-icon fa fa-home fa-fw"></span></a> <a ui-sref="portainer.home" ui-sref-active="active">Home <span class="menu-icon fa fa-home fa-fw"></span></a>
</li> </li>
<li class="sidebar-title endpoint-name" ng-if="applicationState.endpoint.name"> <span class="fa fa-plug space-right"></span>{{ applicationState.endpoint.name }} </li> <li class="sidebar-title endpoint-name" ng-if="applicationState.endpoint.name"> <span class="fa fa-plug space-right"></span>{{ applicationState.endpoint.name }} </li>
<azure-sidebar-content ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'AZURE'"> </azure-sidebar-content>
<docker-sidebar-content <docker-sidebar-content
ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider !== 'AZURE'"
endpoint-api-version="applicationState.endpoint.apiVersion" endpoint-api-version="applicationState.endpoint.apiVersion"
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'" swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'" standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE' || applicationState.endpoint.mode.provider === 'VMWARE_VIC'"

View File

@ -236,10 +236,6 @@ a[ng-click] {
margin: 10px 4px 0 6px; margin: 10px 4px 0 6px;
} }
.blocklist-item-logo.endpoint-item.azure {
margin: 0 0 0 10px;
}
.blocklist-item-title { .blocklist-item-title {
font-size: 1.8em; font-size: 1.8em;
font-weight: bold; font-weight: bold;

View File

@ -5,7 +5,6 @@
"baseUrl": "app", "baseUrl": "app",
"paths": { "paths": {
"Agent/*": ["agent/*"], "Agent/*": ["agent/*"],
"Azure/*": ["azure/*"],
"Docker/*": ["docker/*"], "Docker/*": ["docker/*"],
"Extensions/*": ["extensions/*"], "Extensions/*": ["extensions/*"],
"Portainer/*": ["portainer/*"] "Portainer/*": ["portainer/*"]

View File

@ -112,7 +112,6 @@ module.exports = {
resolve: { resolve: {
alias: { alias: {
Agent: path.resolve(projectRoot, 'app/agent'), Agent: path.resolve(projectRoot, 'app/agent'),
Azure: path.resolve(projectRoot, 'app/azure'),
Docker: path.resolve(projectRoot, 'app/docker'), Docker: path.resolve(projectRoot, 'app/docker'),
Extensions: path.resolve(projectRoot, 'app/extensions'), Extensions: path.resolve(projectRoot, 'app/extensions'),
Portainer: path.resolve(projectRoot, 'app/portainer'), Portainer: path.resolve(projectRoot, 'app/portainer'),