mirror of https://github.com/portainer/portainer
Merge branch 'develop' into oath-poc
commit
508352f4ea
|
@ -6,7 +6,7 @@
|
||||||
[![Docker Pulls](https://img.shields.io/docker/pulls/portainer/portainer.svg)](https://hub.docker.com/r/portainer/portainer/)
|
[![Docker Pulls](https://img.shields.io/docker/pulls/portainer/portainer.svg)](https://hub.docker.com/r/portainer/portainer/)
|
||||||
[![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer "Image size")
|
[![Microbadger](https://images.microbadger.com/badges/image/portainer/portainer.svg)](http://microbadger.com/images/portainer/portainer "Image size")
|
||||||
[![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=stable)](http://portainer.readthedocs.io/en/stable/?badge=stable)
|
[![Documentation Status](https://readthedocs.org/projects/portainer/badge/?version=stable)](http://portainer.readthedocs.io/en/stable/?badge=stable)
|
||||||
[![Build Status](https://semaphoreci.com/api/v1/portainer/portainer-ci/branches/develop/badge.svg)](https://semaphoreci.com/portainer/portainer-ci)
|
[![Build Status](https://portainer.visualstudio.com/Portainer%20CI/_apis/build/status/Portainer%20CI?branchName=develop)](https://portainer.visualstudio.com/Portainer%20CI/_build/latest?definitionId=3&branchName=develop)
|
||||||
[![Code Climate](https://codeclimate.com/github/portainer/portainer/badges/gpa.svg)](https://codeclimate.com/github/portainer/portainer)
|
[![Code Climate](https://codeclimate.com/github/portainer/portainer/badges/gpa.svg)](https://codeclimate.com/github/portainer/portainer)
|
||||||
[![Gitter](https://badges.gitter.im/portainer/Lobby.svg)](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[![Gitter](https://badges.gitter.im/portainer/Lobby.svg)](https://gitter.im/portainer/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||||
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6)
|
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YHXZJQNJQ36H6)
|
||||||
|
|
|
@ -139,6 +139,7 @@ func (store *Store) MigrateData() error {
|
||||||
DatabaseVersion: version,
|
DatabaseVersion: version,
|
||||||
EndpointGroupService: store.EndpointGroupService,
|
EndpointGroupService: store.EndpointGroupService,
|
||||||
EndpointService: store.EndpointService,
|
EndpointService: store.EndpointService,
|
||||||
|
ExtensionService: store.ExtensionService,
|
||||||
ResourceControlService: store.ResourceControlService,
|
ResourceControlService: store.ResourceControlService,
|
||||||
SettingsService: store.SettingsService,
|
SettingsService: store.SettingsService,
|
||||||
StackService: store.StackService,
|
StackService: store.StackService,
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package migrator
|
||||||
|
|
||||||
|
func (m *Migrator) updateExtensionsToDBVersion17() error {
|
||||||
|
legacyExtensions, err := m.extensionService.Extensions()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, extension := range legacyExtensions {
|
||||||
|
extension.License.Valid = true
|
||||||
|
|
||||||
|
err = m.extensionService.Persist(&extension)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/portainer/portainer"
|
"github.com/portainer/portainer"
|
||||||
"github.com/portainer/portainer/bolt/endpoint"
|
"github.com/portainer/portainer/bolt/endpoint"
|
||||||
"github.com/portainer/portainer/bolt/endpointgroup"
|
"github.com/portainer/portainer/bolt/endpointgroup"
|
||||||
|
"github.com/portainer/portainer/bolt/extension"
|
||||||
"github.com/portainer/portainer/bolt/resourcecontrol"
|
"github.com/portainer/portainer/bolt/resourcecontrol"
|
||||||
"github.com/portainer/portainer/bolt/settings"
|
"github.com/portainer/portainer/bolt/settings"
|
||||||
"github.com/portainer/portainer/bolt/stack"
|
"github.com/portainer/portainer/bolt/stack"
|
||||||
|
@ -20,6 +21,7 @@ type (
|
||||||
db *bolt.DB
|
db *bolt.DB
|
||||||
endpointGroupService *endpointgroup.Service
|
endpointGroupService *endpointgroup.Service
|
||||||
endpointService *endpoint.Service
|
endpointService *endpoint.Service
|
||||||
|
extensionService *extension.Service
|
||||||
resourceControlService *resourcecontrol.Service
|
resourceControlService *resourcecontrol.Service
|
||||||
settingsService *settings.Service
|
settingsService *settings.Service
|
||||||
stackService *stack.Service
|
stackService *stack.Service
|
||||||
|
@ -35,6 +37,7 @@ type (
|
||||||
DatabaseVersion int
|
DatabaseVersion int
|
||||||
EndpointGroupService *endpointgroup.Service
|
EndpointGroupService *endpointgroup.Service
|
||||||
EndpointService *endpoint.Service
|
EndpointService *endpoint.Service
|
||||||
|
ExtensionService *extension.Service
|
||||||
ResourceControlService *resourcecontrol.Service
|
ResourceControlService *resourcecontrol.Service
|
||||||
SettingsService *settings.Service
|
SettingsService *settings.Service
|
||||||
StackService *stack.Service
|
StackService *stack.Service
|
||||||
|
@ -52,6 +55,7 @@ func NewMigrator(parameters *Parameters) *Migrator {
|
||||||
currentDBVersion: parameters.DatabaseVersion,
|
currentDBVersion: parameters.DatabaseVersion,
|
||||||
endpointGroupService: parameters.EndpointGroupService,
|
endpointGroupService: parameters.EndpointGroupService,
|
||||||
endpointService: parameters.EndpointService,
|
endpointService: parameters.EndpointService,
|
||||||
|
extensionService: parameters.ExtensionService,
|
||||||
resourceControlService: parameters.ResourceControlService,
|
resourceControlService: parameters.ResourceControlService,
|
||||||
settingsService: parameters.SettingsService,
|
settingsService: parameters.SettingsService,
|
||||||
templateService: parameters.TemplateService,
|
templateService: parameters.TemplateService,
|
||||||
|
@ -210,5 +214,13 @@ func (m *Migrator) Migrate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Portainer 1.20.1
|
||||||
|
if m.currentDBVersion < 17 {
|
||||||
|
err := m.updateExtensionsToDBVersion17()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||||
}
|
}
|
||||||
|
|
|
@ -492,7 +492,10 @@ func initExtensionManager(fileService portainer.FileService, extensionService po
|
||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey)
|
err := extensionManager.EnableExtension(&extension, extension.License.LicenseKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.Printf("Unable to enable extension: %s [extension: %s]", err.Error(), extension.Name)
|
||||||
|
extension.Enabled = false
|
||||||
|
extension.License.Valid = false
|
||||||
|
extensionService.Persist(&extension)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,7 @@ func (manager *ExtensionManager) EnableExtension(extension *portainer.Extension,
|
||||||
LicenseKey: licenseKey,
|
LicenseKey: licenseKey,
|
||||||
Company: licenseDetails[0],
|
Company: licenseDetails[0],
|
||||||
Expiration: licenseDetails[1],
|
Expiration: licenseDetails[1],
|
||||||
|
Valid: true,
|
||||||
}
|
}
|
||||||
extension.Version = licenseDetails[2]
|
extension.Version = licenseDetails[2]
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (handler *Handler) extensionCreate(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, existingExtension := range extensions {
|
for _, existingExtension := range extensions {
|
||||||
if existingExtension.ID == extensionID {
|
if existingExtension.ID == extensionID && existingExtension.Enabled {
|
||||||
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
|
return &httperror.HandlerError{http.StatusConflict, "Unable to enable extension", portainer.ErrExtensionAlreadyEnabled}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func associateExtensionData(definition *portainer.Extension, extensions []portai
|
||||||
definition.Enabled = extension.Enabled
|
definition.Enabled = extension.Enabled
|
||||||
definition.License.Company = extension.License.Company
|
definition.License.Company = extension.License.Company
|
||||||
definition.License.Expiration = extension.License.Expiration
|
definition.License.Expiration = extension.License.Expiration
|
||||||
|
definition.License.Valid = extension.License.Valid
|
||||||
|
|
||||||
definitionVersion := semver.New(definition.Version)
|
definitionVersion := semver.New(definition.Version)
|
||||||
extensionVersion := semver.New(extension.Version)
|
extensionVersion := semver.New(extension.Version)
|
||||||
|
|
|
@ -67,7 +67,12 @@ func (handler *Handler) settingsUpdate(w http.ResponseWriter, r *http.Request) *
|
||||||
}
|
}
|
||||||
|
|
||||||
if payload.LDAPSettings != nil {
|
if payload.LDAPSettings != nil {
|
||||||
|
ldapPassword := settings.LDAPSettings.Password
|
||||||
|
if payload.LDAPSettings.Password != "" {
|
||||||
|
ldapPassword = payload.LDAPSettings.Password
|
||||||
|
}
|
||||||
settings.LDAPSettings = *payload.LDAPSettings
|
settings.LDAPSettings = *payload.LDAPSettings
|
||||||
|
settings.LDAPSettings.Password = ldapPassword
|
||||||
}
|
}
|
||||||
|
|
||||||
if payload.OAuthSettings != nil {
|
if payload.OAuthSettings != nil {
|
||||||
|
|
|
@ -517,6 +517,7 @@ type (
|
||||||
LicenseKey string `json:"LicenseKey,omitempty"`
|
LicenseKey string `json:"LicenseKey,omitempty"`
|
||||||
Company string `json:"Company,omitempty"`
|
Company string `json:"Company,omitempty"`
|
||||||
Expiration string `json:"Expiration,omitempty"`
|
Expiration string `json:"Expiration,omitempty"`
|
||||||
|
Valid bool `json:"Valid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLIService represents a service for managing CLI
|
// CLIService represents a service for managing CLI
|
||||||
|
@ -799,9 +800,9 @@ type (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// APIVersion is the version number of the Portainer API
|
// APIVersion is the version number of the Portainer API
|
||||||
APIVersion = "1.20.0"
|
APIVersion = "1.20.1"
|
||||||
// DBVersion is the version number of the Portainer database
|
// DBVersion is the version number of the Portainer database
|
||||||
DBVersion = 16
|
DBVersion = 17
|
||||||
// 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
|
||||||
|
|
|
@ -54,7 +54,7 @@ info:
|
||||||
|
|
||||||
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).
|
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).
|
||||||
|
|
||||||
version: "1.20.0"
|
version: "1.20.1"
|
||||||
title: "Portainer API"
|
title: "Portainer API"
|
||||||
contact:
|
contact:
|
||||||
email: "info@portainer.io"
|
email: "info@portainer.io"
|
||||||
|
@ -525,7 +525,7 @@ paths:
|
||||||
**Access policy**: administrator
|
**Access policy**: administrator
|
||||||
operationId: "EndpointJob"
|
operationId: "EndpointJob"
|
||||||
consumes:
|
consumes:
|
||||||
- "application/json"
|
- "multipart/form-data"
|
||||||
produces:
|
produces:
|
||||||
- "application/json"
|
- "application/json"
|
||||||
security:
|
security:
|
||||||
|
@ -1434,7 +1434,7 @@ paths:
|
||||||
**Access policy**: restricted
|
**Access policy**: restricted
|
||||||
operationId: "StackCreate"
|
operationId: "StackCreate"
|
||||||
consumes:
|
consumes:
|
||||||
- "application/json"
|
- "multipart/form-data"
|
||||||
produces:
|
produces:
|
||||||
- "application/json"
|
- "application/json"
|
||||||
security:
|
security:
|
||||||
|
@ -2733,7 +2733,7 @@ paths:
|
||||||
- "application/json"
|
- "application/json"
|
||||||
security:
|
security:
|
||||||
- jwt: []
|
- jwt: []
|
||||||
parameters:
|
parameters: []
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
description: "Success"
|
description: "Success"
|
||||||
|
@ -3018,7 +3018,7 @@ definitions:
|
||||||
description: "Is analytics enabled"
|
description: "Is analytics enabled"
|
||||||
Version:
|
Version:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "1.20.0"
|
example: "1.20.1"
|
||||||
description: "Portainer API version"
|
description: "Portainer API version"
|
||||||
PublicSettingsInspectResponse:
|
PublicSettingsInspectResponse:
|
||||||
type: "object"
|
type: "object"
|
||||||
|
@ -3146,7 +3146,7 @@ definitions:
|
||||||
$ref: "#/definitions/LDAPGroupSearchSettings"
|
$ref: "#/definitions/LDAPGroupSearchSettings"
|
||||||
AutoCreateUsers:
|
AutoCreateUsers:
|
||||||
type: "boolean"
|
type: "boolean"
|
||||||
example: "true"
|
example: true
|
||||||
description: "Automatically provision users and assign them to matching LDAP group names"
|
description: "Automatically provision users and assign them to matching LDAP group names"
|
||||||
|
|
||||||
Settings:
|
Settings:
|
||||||
|
@ -3606,6 +3606,7 @@ definitions:
|
||||||
- "Authentication"
|
- "Authentication"
|
||||||
- "Name"
|
- "Name"
|
||||||
- "Password"
|
- "Password"
|
||||||
|
- "Type"
|
||||||
- "URL"
|
- "URL"
|
||||||
- "Username"
|
- "Username"
|
||||||
properties:
|
properties:
|
||||||
|
@ -3613,6 +3614,10 @@ definitions:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "my-registry"
|
example: "my-registry"
|
||||||
description: "Name that will be used to identify this registry"
|
description: "Name that will be used to identify this registry"
|
||||||
|
Type:
|
||||||
|
type: "integer"
|
||||||
|
example: 1
|
||||||
|
description: "Registry Type. Valid values are: 1 (Quay.io), 2 (Azure container registry) or 3 (custom registry)"
|
||||||
URL:
|
URL:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "registry.mydomain.tld:2375"
|
example: "registry.mydomain.tld:2375"
|
||||||
|
@ -4037,7 +4042,7 @@ definitions:
|
||||||
description: "A list of categories associated to the template"
|
description: "A list of categories associated to the template"
|
||||||
items:
|
items:
|
||||||
type: "string"
|
type: "string"
|
||||||
exampe: "database"
|
example: "database"
|
||||||
registry:
|
registry:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "quay.io"
|
example: "quay.io"
|
||||||
|
@ -4133,7 +4138,7 @@ definitions:
|
||||||
description: "A list of categories associated to the template"
|
description: "A list of categories associated to the template"
|
||||||
items:
|
items:
|
||||||
type: "string"
|
type: "string"
|
||||||
exampe: "database"
|
example: "database"
|
||||||
registry:
|
registry:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "quay.io"
|
example: "quay.io"
|
||||||
|
@ -4233,7 +4238,7 @@ definitions:
|
||||||
description: "A list of categories associated to the template"
|
description: "A list of categories associated to the template"
|
||||||
items:
|
items:
|
||||||
type: "string"
|
type: "string"
|
||||||
exampe: "database"
|
example: "database"
|
||||||
registry:
|
registry:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "quay.io"
|
example: "quay.io"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"packageName": "portainer",
|
"packageName": "portainer",
|
||||||
"packageVersion": "1.20.0",
|
"packageVersion": "1.20.1",
|
||||||
"projectName": "portainer"
|
"projectName": "portainer"
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ function ($rootScope, $state, Authentication, authManager, StateManager, Endpoin
|
||||||
};
|
};
|
||||||
|
|
||||||
$transitions.onBefore({ to: 'docker.**' }, function() {
|
$transitions.onBefore({ to: 'docker.**' }, function() {
|
||||||
HttpRequestHelper.resetAgentTargetQueue();
|
HttpRequestHelper.resetAgentHeaders();
|
||||||
});
|
});
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ function initAuthentication(authManager, Authentication, $rootScope, $state) {
|
||||||
// to have more controls on which URL should trigger the unauthenticated state.
|
// to have more controls on which URL should trigger the unauthenticated state.
|
||||||
$rootScope.$on('unauthenticated', function (event, data) {
|
$rootScope.$on('unauthenticated', function (event, data) {
|
||||||
if (!_.includes(data.config.url, '/v2/')) {
|
if (!_.includes(data.config.url, '/v2/')) {
|
||||||
$state.go('portainer.auth', {error: 'Your session has expired'});
|
$state.go('portainer.auth', {error: 'Your session has expired', redirect: $state.current.name});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ angular.module('portainer')
|
||||||
request: function(config) {
|
request: function(config) {
|
||||||
if (config.url.indexOf('/docker/') > -1) {
|
if (config.url.indexOf('/docker/') > -1) {
|
||||||
config.headers['X-PortainerAgent-Target'] = HttpRequestHelper.portainerAgentTargetHeader();
|
config.headers['X-PortainerAgent-Target'] = HttpRequestHelper.portainerAgentTargetHeader();
|
||||||
|
if (HttpRequestHelper.portainerAgentManagerOperation()) {
|
||||||
|
config.headers['X-PortainerAgent-ManagerOperation'] = '1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,17 @@ angular.module('portainer.docker', ['portainer.app'])
|
||||||
var docker = {
|
var docker = {
|
||||||
name: 'docker',
|
name: 'docker',
|
||||||
parent: 'root',
|
parent: 'root',
|
||||||
abstract: true
|
abstract: true,
|
||||||
|
resolve: {
|
||||||
|
endpointID: ['EndpointProvider', '$state',
|
||||||
|
function (EndpointProvider, $state) {
|
||||||
|
var id = EndpointProvider.endpointID();
|
||||||
|
if (!id) {
|
||||||
|
return $state.go('portainer.home');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var configs = {
|
var configs = {
|
||||||
|
|
|
@ -38,12 +38,12 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr dir-paginate="(key, value) in $ctrl.dataset | itemsPerPage: $ctrl.state.paginatedItemLimit" ng-class="{active: item.Checked}">
|
<tr dir-paginate="(key, value) in $ctrl.dataset | itemsPerPage: $ctrl.state.paginatedItemLimit" ng-class="{active: item.Checked}">
|
||||||
<td><a ui-sref="docker.networks.network({ id: value.NetworkID, nodeName: $ctrl.nodeName })">{{ key }}</a></td>
|
<td><a ui-sref="docker.networks.network({ id: key, nodeName: $ctrl.nodeName })">{{ key }}</a></td>
|
||||||
<td>{{ value.IPAddress || '-' }}</td>
|
<td>{{ value.IPAddress || '-' }}</td>
|
||||||
<td>{{ value.Gateway || '-' }}</td>
|
<td>{{ value.Gateway || '-' }}</td>
|
||||||
<td>{{ value.MacAddress || '-' }}</td>
|
<td>{{ value.MacAddress || '-' }}</td>
|
||||||
<td>
|
<td>
|
||||||
<button type="button" class="btn btn-xs btn-danger" ng-disabled="$ctrl.leaveNetworkActionInProgress" button-spinner="$ctrl.leaveNetworkActionInProgress" ng-click="$ctrl.leaveNetworkAction($ctrl.container, value.NetworkID)">
|
<button type="button" class="btn btn-xs btn-danger" ng-disabled="$ctrl.leaveNetworkActionInProgress" button-spinner="$ctrl.leaveNetworkActionInProgress" ng-click="$ctrl.leaveNetworkAction($ctrl.container, key)">
|
||||||
<span ng-hide="$ctrl.leaveNetworkActionInProgress"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i> Leave network</span>
|
<span ng-hide="$ctrl.leaveNetworkActionInProgress"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i> Leave network</span>
|
||||||
<span ng-show="$ctrl.leaveNetworkActionInProgress">Leaving network...</span>
|
<span ng-show="$ctrl.leaveNetworkActionInProgress">Leaving network...</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
angular.module('portainer.docker')
|
angular.module('portainer.docker').factory('ContainerHelper', [function ContainerHelperFactory() {
|
||||||
.factory('ContainerHelper', [function ContainerHelperFactory() {
|
|
||||||
'use strict';
|
'use strict';
|
||||||
var helper = {};
|
var helper = {};
|
||||||
|
|
||||||
helper.commandStringToArray = function(command) {
|
helper.commandStringToArray = function(command) {
|
||||||
return splitargs(command, undefined, true);
|
return splitargs(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
helper.commandArrayToString = function(array) {
|
helper.commandArrayToString = function(array) {
|
||||||
|
|
|
@ -493,6 +493,19 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
$scope.formValues.capabilities.push(new ContainerCapability(cap, false));
|
$scope.formValues.capabilities.push(new ContainerCapability(cap, false));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasCapability(item) {
|
||||||
|
return item.capability === cap.capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
var capabilities = new ContainerCapabilities();
|
||||||
|
for (var i = 0; i < capabilities.length; i++) {
|
||||||
|
var cap = capabilities[i];
|
||||||
|
if (!_.find($scope.formValues.capabilities, hasCapability)) {
|
||||||
|
$scope.formValues.capabilities.push(cap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$scope.formValues.capabilities.sort(function(a, b) {
|
$scope.formValues.capabilities.sort(function(a, b) {
|
||||||
return a.capability < b.capability ? -1 : 1;
|
return a.capability < b.capability ? -1 : 1;
|
||||||
});
|
});
|
||||||
|
@ -509,6 +522,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
$scope.fromContainer = fromContainer;
|
$scope.fromContainer = fromContainer;
|
||||||
$scope.config = ContainerHelper.configFromContainer(fromContainer.Model);
|
$scope.config = ContainerHelper.configFromContainer(fromContainer.Model);
|
||||||
loadFromContainerCmd(d);
|
loadFromContainerCmd(d);
|
||||||
|
loadFromContainerLogging(d);
|
||||||
loadFromContainerPortBindings(d);
|
loadFromContainerPortBindings(d);
|
||||||
loadFromContainerVolumes(d);
|
loadFromContainerVolumes(d);
|
||||||
loadFromContainerNetworkConfig(d);
|
loadFromContainerNetworkConfig(d);
|
||||||
|
@ -525,6 +539,17 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function loadFromContainerLogging(config) {
|
||||||
|
var logConfig = config.HostConfig.LogConfig;
|
||||||
|
$scope.formValues.LogDriverName = logConfig.Type;
|
||||||
|
$scope.formValues.LogDriverOpts = _.map(logConfig.Config, function (value, name) {
|
||||||
|
return {
|
||||||
|
name: name,
|
||||||
|
value: value
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initView() {
|
function initView() {
|
||||||
var nodeName = $transition$.params().nodeName;
|
var nodeName = $transition$.params().nodeName;
|
||||||
$scope.formValues.NodeName = nodeName;
|
$scope.formValues.NodeName = nodeName;
|
||||||
|
@ -621,9 +646,9 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
function create() {
|
function create() {
|
||||||
var oldContainer = null;
|
var oldContainer = null;
|
||||||
|
|
||||||
|
|
||||||
HttpRequestHelper.setPortainerAgentTargetHeader($scope.formValues.NodeName);
|
HttpRequestHelper.setPortainerAgentTargetHeader($scope.formValues.NodeName);
|
||||||
return findCurrentContainer()
|
return findCurrentContainer()
|
||||||
|
.then(setOldContainer)
|
||||||
.then(confirmCreateContainer)
|
.then(confirmCreateContainer)
|
||||||
.then(startCreationProcess)
|
.then(startCreationProcess)
|
||||||
.catch(notifyOnError)
|
.catch(notifyOnError)
|
||||||
|
@ -633,6 +658,11 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
$scope.state.actionInProgress = false;
|
$scope.state.actionInProgress = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setOldContainer(container) {
|
||||||
|
oldContainer = container;
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
function findCurrentContainer() {
|
function findCurrentContainer() {
|
||||||
return Container.query({ all: 1, filters: { name: ['^/' + $scope.config.name + '$'] } })
|
return Container.query({ all: 1, filters: { name: ['^/' + $scope.config.name + '$'] } })
|
||||||
.$promise
|
.$promise
|
||||||
|
@ -640,8 +670,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
if (!containers.length) {
|
if (!containers.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
oldContainer = containers[0];
|
return containers[0];
|
||||||
return oldContainer;
|
|
||||||
})
|
})
|
||||||
.catch(notifyOnError);
|
.catch(notifyOnError);
|
||||||
|
|
||||||
|
@ -664,7 +693,36 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
|
||||||
.then(applyResourceControl)
|
.then(applyResourceControl)
|
||||||
.then(connectToExtraNetworks)
|
.then(connectToExtraNetworks)
|
||||||
.then(removeOldContainer)
|
.then(removeOldContainer)
|
||||||
.then(onSuccess);
|
.then(onSuccess)
|
||||||
|
.catch(onCreationProcessFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCreationProcessFail(error) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
removeNewContainer()
|
||||||
|
.then(restoreOldContainerName)
|
||||||
|
.then(function() {
|
||||||
|
deferred.reject(error);
|
||||||
|
})
|
||||||
|
.catch(function(restoreError) {
|
||||||
|
deferred.reject(restoreError);
|
||||||
|
});
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeNewContainer() {
|
||||||
|
return findCurrentContainer().then(function onContainerLoaded(container) {
|
||||||
|
if (container && (!oldContainer || container.Id !== oldContainer.Id)) {
|
||||||
|
return ContainerService.remove(container, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreOldContainerName() {
|
||||||
|
if (!oldContainer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return ContainerService.renameContainer(oldContainer.Id, oldContainer.Names[0].substring(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function confirmCreateContainer(container) {
|
function confirmCreateContainer(container) {
|
||||||
|
|
|
@ -162,7 +162,7 @@
|
||||||
<form class="form-horizontal" style="margin-top: 15px;">
|
<form class="form-horizontal" style="margin-top: 15px;">
|
||||||
<!-- command-input -->
|
<!-- command-input -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="container_command" class="col-sm-2 col-lg-1 control-label text-left">Command & logging</label>
|
<label for="container_command" class="col-sm-2 col-lg-1 control-label text-left">Command</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input type="text" class="form-control" ng-model="config.Cmd" id="container_command" placeholder="e.g. /usr/bin/nginx -t -c /mynginx.conf">
|
<input type="text" class="form-control" ng-model="config.Cmd" id="container_command" placeholder="e.g. /usr/bin/nginx -t -c /mynginx.conf">
|
||||||
</div>
|
</div>
|
||||||
|
@ -320,7 +320,7 @@
|
||||||
<span class="input-group-addon">volume</span>
|
<span class="input-group-addon">volume</span>
|
||||||
<select class="form-control" ng-model="volume.name">
|
<select class="form-control" ng-model="volume.name">
|
||||||
<option selected disabled hidden value="">Select a volume</option>
|
<option selected disabled hidden value="">Select a volume</option>
|
||||||
<option ng-repeat="vol in availableVolumes" ng-value="vol.Name">{{ vol.Name|truncate:30}}</option>
|
<option ng-repeat="vol in availableVolumes" ng-value="vol.Name">{{ vol.Name|truncate:30}} - {{ vol.Driver|truncate:30}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- !volume -->
|
<!-- !volume -->
|
||||||
|
|
|
@ -126,6 +126,7 @@ angular.module('portainer.docker')
|
||||||
|
|
||||||
function createNetwork(context) {
|
function createNetwork(context) {
|
||||||
HttpRequestHelper.setPortainerAgentTargetHeader(context.nodeName);
|
HttpRequestHelper.setPortainerAgentTargetHeader(context.nodeName);
|
||||||
|
HttpRequestHelper.setPortainerAgentManagerOperation(context.managerOperation);
|
||||||
|
|
||||||
$scope.state.actionInProgress = true;
|
$scope.state.actionInProgress = true;
|
||||||
NetworkService.create(context.networkConfiguration)
|
NetworkService.create(context.networkConfiguration)
|
||||||
|
@ -162,12 +163,17 @@ angular.module('portainer.docker')
|
||||||
|
|
||||||
var creationContext = {
|
var creationContext = {
|
||||||
nodeName: $scope.formValues.NodeName,
|
nodeName: $scope.formValues.NodeName,
|
||||||
|
managerOperation: false,
|
||||||
networkConfiguration: networkConfiguration,
|
networkConfiguration: networkConfiguration,
|
||||||
userDetails: userDetails,
|
userDetails: userDetails,
|
||||||
accessControlData: accessControlData,
|
accessControlData: accessControlData,
|
||||||
reload: true
|
reload: true
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ($scope.applicationState.endpoint.mode.agentProxy && $scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && $scope.config.Driver === 'overlay') {
|
||||||
|
creationContext.managerOperation = true;
|
||||||
|
}
|
||||||
|
|
||||||
if ($scope.config.Driver === 'macvlan') {
|
if ($scope.config.Driver === 'macvlan') {
|
||||||
if ($scope.formValues.Macvlan.Scope === 'local') {
|
if ($scope.formValues.Macvlan.Scope === 'local') {
|
||||||
modifyNetworkConfigurationForMacvlanConfigOnly(networkConfiguration);
|
modifyNetworkConfigurationForMacvlanConfigOnly(networkConfiguration);
|
||||||
|
|
|
@ -39,13 +39,9 @@
|
||||||
<td>Internal</td>
|
<td>Internal</td>
|
||||||
<td>{{ network.Internal }}</td>
|
<td>{{ network.Internal }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="network.IPAM.Config[0].Subnet">
|
<tr ng-if="network.IPAM.Config.length > 0" ng-repeat="config in network.IPAM.Config">
|
||||||
<td>Subnet</td>
|
<td>Subnet - {{ config.Subnet }}</td>
|
||||||
<td>{{ network.IPAM.Config[0].Subnet }}</td>
|
<td>Gateway - {{ config.Gateway }}</td>
|
||||||
</tr>
|
|
||||||
<tr ng-if="network.IPAM.Config[0].Gateway">
|
|
||||||
<td>Gateway</td>
|
|
||||||
<td>{{ network.IPAM.Config[0].Gateway }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -315,8 +315,9 @@
|
||||||
<!-- volume -->
|
<!-- volume -->
|
||||||
<div class="input-group input-group-sm col-sm-6" ng-if="volume.Type === 'volume'">
|
<div class="input-group input-group-sm col-sm-6" ng-if="volume.Type === 'volume'">
|
||||||
<span class="input-group-addon">volume</span>
|
<span class="input-group-addon">volume</span>
|
||||||
<select class="form-control" ng-model="volume.Source" ng-options="vol.Id|truncate:30 for vol in availableVolumes">
|
<select class="form-control" ng-model="volume.Source">
|
||||||
<option selected disabled hidden value="">Select a volume</option>
|
<option selected disabled hidden value="">Select a volume</option>
|
||||||
|
<option ng-repeat="vol in availableVolumes" ng-value="vol.Id">{{ vol.Id|truncate:30}} - {{ vol.Driver|truncate:30}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!-- !volume -->
|
<!-- !volume -->
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
angular.module('portainer.extensions.registrymanagement')
|
angular.module('portainer.extensions.registrymanagement')
|
||||||
.factory('RegistryCatalog', ['$resource', 'API_ENDPOINT_REGISTRIES', function RegistryCatalogFactory($resource, API_ENDPOINT_REGISTRIES) {
|
.factory('RegistryCatalog', ['$resource', 'API_ENDPOINT_REGISTRIES',
|
||||||
|
function RegistryCatalogFactory($resource, API_ENDPOINT_REGISTRIES) {
|
||||||
'use strict';
|
'use strict';
|
||||||
return $resource(API_ENDPOINT_REGISTRIES + '/:id/v2/:action', {},
|
return $resource(API_ENDPOINT_REGISTRIES + '/:id/v2/:action', {},
|
||||||
{
|
{
|
||||||
get: {
|
get: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
params: { id: '@id', action: '_catalog' }
|
params: { id: '@id', action: '_catalog' },
|
||||||
|
transformResponse: linkGetResponse
|
||||||
},
|
},
|
||||||
ping: {
|
ping: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
function linkGetResponse(data, headers) {
|
||||||
|
var response = angular.fromJson(data);
|
||||||
|
var link = headers('link');
|
||||||
|
if (link) {
|
||||||
|
var queryString = link.substring(link.indexOf('?') + 1).split('>;')[0];
|
||||||
|
var queries = queryString.split('&');
|
||||||
|
for (var i = 0; i < queries.length; i++) {
|
||||||
|
var kv = queries[i].split('=');
|
||||||
|
response[kv[0]] = kv[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
|
@ -11,16 +11,33 @@ function RegistryV2ServiceFactory($q, RegistryCatalog, RegistryTags, RegistryMan
|
||||||
return RegistryCatalog.ping({ id: id }).$promise;
|
return RegistryCatalog.ping({ id: id }).$promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getCatalog(id) {
|
||||||
|
var deferred = $q.defer();
|
||||||
|
var repositories = [];
|
||||||
|
|
||||||
|
_getCatalogPage({id: id}, deferred, repositories);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getCatalogPage(params, deferred, repositories) {
|
||||||
|
RegistryCatalog.get(params).$promise.then(function(data) {
|
||||||
|
repositories = _.concat(repositories, data.repositories);
|
||||||
|
if (data.last && data.n) {
|
||||||
|
_getCatalogPage({id: params.id, n: data.n, last: data.last}, deferred, repositories);
|
||||||
|
} else {
|
||||||
|
deferred.resolve(repositories);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
service.repositories = function (id) {
|
service.repositories = function (id) {
|
||||||
var deferred = $q.defer();
|
var deferred = $q.defer();
|
||||||
|
|
||||||
RegistryCatalog.get({
|
getCatalog(id).then(function success(data) {
|
||||||
id: id
|
|
||||||
}).$promise
|
|
||||||
.then(function success(data) {
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
for (var i = 0; i < data.repositories.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
var repository = data.repositories[i];
|
var repository = data[i];
|
||||||
promises.push(RegistryTags.get({
|
promises.push(RegistryTags.get({
|
||||||
id: id,
|
id: id,
|
||||||
repository: repository
|
repository: repository
|
||||||
|
|
|
@ -81,9 +81,17 @@ angular.module('portainer.app')
|
||||||
});
|
});
|
||||||
return $q.all(promises);
|
return $q.all(promises);
|
||||||
})
|
})
|
||||||
.then(function success() {
|
.then(function success(data) {
|
||||||
Notifications.success('Success', 'Tags successfully deleted');
|
Notifications.success('Success', 'Tags successfully deleted');
|
||||||
|
if (data.length === 0) {
|
||||||
|
$state.go('portainer.registries.registry.repositories', {
|
||||||
|
id: $scope.registryId
|
||||||
|
}, {
|
||||||
|
reload: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
$state.reload();
|
$state.reload();
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to delete tags');
|
Notifications.error('Failure', err, 'Unable to delete tags');
|
||||||
|
@ -127,9 +135,9 @@ angular.module('portainer.app')
|
||||||
})
|
})
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
$scope.registry = data.registry;
|
$scope.registry = data.registry;
|
||||||
$scope.repository.Tags = data.tags;
|
$scope.repository.Tags = [].concat(data.tags || []);
|
||||||
$scope.tags = [];
|
$scope.tags = [];
|
||||||
for (var i = 0; i < data.tags.length; i++) {
|
for (var i = 0; i < $scope.repository.Tags.length; i++) {
|
||||||
var tag = data.tags[i];
|
var tag = data.tags[i];
|
||||||
RegistryV2Service.tag(registryId, repository, tag)
|
RegistryV2Service.tag(registryId, repository, tag)
|
||||||
.then(function success(data) {
|
.then(function success(data) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ angular.module('portainer.app', [])
|
||||||
|
|
||||||
var authentication = {
|
var authentication = {
|
||||||
name: 'portainer.auth',
|
name: 'portainer.auth',
|
||||||
url: '/auth',
|
url: '/auth?redirect',
|
||||||
params: {
|
params: {
|
||||||
logout: false,
|
logout: false,
|
||||||
error: ''
|
error: ''
|
||||||
|
@ -327,6 +327,16 @@ angular.module('portainer.app', [])
|
||||||
templateUrl: 'app/portainer/views/stacks/stacks.html',
|
templateUrl: 'app/portainer/views/stacks/stacks.html',
|
||||||
controller: 'StacksController'
|
controller: 'StacksController'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
endpointID: ['EndpointProvider', '$state',
|
||||||
|
function (EndpointProvider, $state) {
|
||||||
|
var id = EndpointProvider.endpointID();
|
||||||
|
if (!id) {
|
||||||
|
return $state.go('portainer.home');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -342,7 +352,7 @@ angular.module('portainer.app', [])
|
||||||
};
|
};
|
||||||
|
|
||||||
var stackCreation = {
|
var stackCreation = {
|
||||||
name: 'portainer.newstack',
|
name: 'portainer.stacks.newstack',
|
||||||
url: '/newstack',
|
url: '/newstack',
|
||||||
views: {
|
views: {
|
||||||
'content@': {
|
'content@': {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
ng-disabled="$ctrl.state.selectedItemCount === 0" ng-click="$ctrl.removeAction($ctrl.state.selectedItems)">
|
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
|
<i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-sm btn-primary" ui-sref="portainer.newstack">
|
<button type="button" class="btn btn-sm btn-primary" ui-sref="portainer.stacks.newstack">
|
||||||
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add stack
|
<i class="fa fa-plus space-right" aria-hidden="true"></i>Add stack
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
|
|
||||||
<span class="small">
|
<span class="small" ng-if="$ctrl.model.GroupName">
|
||||||
Group: {{ $ctrl.model.GroupName }}
|
Group: {{ $ctrl.model.GroupName }}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -21,10 +21,10 @@
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<span class="label label-primary" ng-if="!$ctrl.model.Enabled && !$ctrl.model.Available">coming soon</span>
|
<span class="label label-primary" ng-if="!$ctrl.model.Enabled && !$ctrl.model.Available">coming soon</span>
|
||||||
<span class="label label-warning" ng-if="!$ctrl.model.Enabled && $ctrl.model.Deal">deal</span>
|
<span class="label label-warning" ng-if="!$ctrl.model.Enabled && $ctrl.model.Deal && !$ctrl.model.License.Expiration">deal</span>
|
||||||
<span class="label label-danger" ng-if="$ctrl.model.Enabled && $ctrl.model.Expired">expired</span>
|
<span class="label label-danger" ng-if="!$ctrl.model.Enabled && $ctrl.model.License.Expiration && !$ctrl.model.License.Valid">expired</span>
|
||||||
<span class="label label-success" ng-if="$ctrl.model.Enabled && !$ctrl.model.Expired">enabled</span>
|
<span class="label label-success" ng-if="$ctrl.model.Enabled && $ctrl.model.License.Valid">enabled</span>
|
||||||
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.UpdateAvailable && !$ctrl.model.Expired">update available</span>
|
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.License.Valid && $ctrl.model.UpdateAvailable">update available</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line1 -->
|
<!-- !blocklist-item-line1 -->
|
||||||
|
|
|
@ -3,7 +3,6 @@ angular.module('portainer.app')
|
||||||
function($state) {
|
function($state) {
|
||||||
|
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
ctrl.$onInit = $onInit;
|
|
||||||
ctrl.goToExtensionView = goToExtensionView;
|
ctrl.goToExtensionView = goToExtensionView;
|
||||||
|
|
||||||
function goToExtensionView() {
|
function goToExtensionView() {
|
||||||
|
@ -11,10 +10,4 @@ angular.module('portainer.app')
|
||||||
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function $onInit() {
|
|
||||||
if (ctrl.currentDate === ctrl.model.License.Expiration) {
|
|
||||||
ctrl.model.Expired = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="blocklist-item-box">
|
<div class="blocklist-item-box">
|
||||||
<!-- extension-image -->
|
<!-- extension-image -->
|
||||||
<span class="blocklist-item-logo">
|
<span class="blocklist-item-logo">
|
||||||
<img class="blocklist-item-logo" src="images/support_{{ $ctrl.model.Id }}.png" />
|
<img class="blocklist-item-logo" ng-src="images/support_{{ $ctrl.model.Id }}.png" />
|
||||||
</span>
|
</span>
|
||||||
<!-- !extension-image -->
|
<!-- !extension-image -->
|
||||||
<!-- extension-details -->
|
<!-- extension-details -->
|
||||||
|
@ -15,11 +15,6 @@
|
||||||
{{ $ctrl.model.Name }}
|
{{ $ctrl.model.Name }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
|
||||||
<span class="label label-danger" ng-if="$ctrl.model.Enabled && $ctrl.model.Expired">expired</span>
|
|
||||||
<span class="label label-success" ng-if="$ctrl.model.Enabled && !$ctrl.model.Expired">enabled</span>
|
|
||||||
<span class="label label-primary" ng-if="$ctrl.model.Enabled && $ctrl.model.UpdateAvailable && !$ctrl.model.Expired">update available</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line1 -->
|
<!-- !blocklist-item-line1 -->
|
||||||
<!-- blocklist-item-line2 -->
|
<!-- blocklist-item-line2 -->
|
||||||
|
@ -29,9 +24,6 @@
|
||||||
{{ $ctrl.model.ShortDescription }}
|
{{ $ctrl.model.ShortDescription }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span ng-if="$ctrl.model.License.Company">
|
|
||||||
<span class="small text-muted">Licensed to {{ $ctrl.model.License.Company }} - Expires on {{ $ctrl.model.License.Expiration }}</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<!-- !blocklist-item-line2 -->
|
<!-- !blocklist-item-line2 -->
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.factory('EndpointStatusInterceptor', ['$q', '$injector', 'EndpointProvider', function ($q, $injector, EndpointProvider) {
|
.factory('EndpointStatusInterceptor', ['$q', 'EndpointProvider', function ($q, EndpointProvider) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var interceptor = {};
|
var interceptor = {};
|
||||||
|
|
||||||
|
@ -18,21 +18,17 @@ angular.module('portainer.app')
|
||||||
}
|
}
|
||||||
|
|
||||||
function responseInterceptor(response) {
|
function responseInterceptor(response) {
|
||||||
var EndpointService = $injector.get('EndpointService');
|
|
||||||
var url = response.config.url;
|
var url = response.config.url;
|
||||||
if (response.status === 200 && canBeOffline(url) && EndpointProvider.offlineMode()) {
|
if (response.status === 200 && canBeOffline(url) && EndpointProvider.offlineMode()) {
|
||||||
EndpointProvider.setOfflineMode(false);
|
EndpointProvider.setOfflineMode(false);
|
||||||
EndpointService.updateEndpoint(EndpointProvider.endpointID(), {Status: EndpointProvider.endpointStatusFromOfflineMode(false)});
|
|
||||||
}
|
}
|
||||||
return response || $q.when(response);
|
return response || $q.when(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
function responseErrorInterceptor(rejection) {
|
function responseErrorInterceptor(rejection) {
|
||||||
var EndpointService = $injector.get('EndpointService');
|
|
||||||
var url = rejection.config.url;
|
var url = rejection.config.url;
|
||||||
if ((rejection.status === 502 || rejection.status === 503 || rejection.status === -1) && canBeOffline(url) && !EndpointProvider.offlineMode()) {
|
if ((rejection.status === 502 || rejection.status === 503 || rejection.status === -1) && canBeOffline(url) && !EndpointProvider.offlineMode()) {
|
||||||
EndpointProvider.setOfflineMode(true);
|
EndpointProvider.setOfflineMode(true);
|
||||||
EndpointService.updateEndpoint(EndpointProvider.endpointID(), {Status: EndpointProvider.endpointStatusFromOfflineMode(true)});
|
|
||||||
}
|
}
|
||||||
return $q.reject(rejection);
|
return $q.reject(rejection);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,10 +64,6 @@ angular.module('portainer.app')
|
||||||
return endpoint.OfflineMode;
|
return endpoint.OfflineMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
service.endpointStatusFromOfflineMode = function(isOffline) {
|
|
||||||
return isOffline ? 2 : 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
service.setOfflineMode = function(isOffline) {
|
service.setOfflineMode = function(isOffline) {
|
||||||
endpoint.OfflineMode = isOffline;
|
endpoint.OfflineMode = isOffline;
|
||||||
LocalStorage.storeOfflineMode(isOffline);
|
LocalStorage.storeOfflineMode(isOffline);
|
||||||
|
|
|
@ -5,6 +5,7 @@ angular.module('portainer.app')
|
||||||
var service = {};
|
var service = {};
|
||||||
var headers = {};
|
var headers = {};
|
||||||
headers.agentTargetQueue = [];
|
headers.agentTargetQueue = [];
|
||||||
|
headers.agentManagerOperation = false;
|
||||||
|
|
||||||
service.registryAuthenticationHeader = function() {
|
service.registryAuthenticationHeader = function() {
|
||||||
return headers.registryAuthentication;
|
return headers.registryAuthentication;
|
||||||
|
@ -36,9 +37,18 @@ angular.module('portainer.app')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
service.resetAgentTargetQueue = function() {
|
service.setPortainerAgentManagerOperation = function(set) {
|
||||||
|
headers.agentManagerOperation = set;
|
||||||
|
};
|
||||||
|
|
||||||
|
service.portainerAgentManagerOperation = function() {
|
||||||
|
return headers.agentManagerOperation;
|
||||||
|
};
|
||||||
|
|
||||||
|
service.resetAgentHeaders = function() {
|
||||||
headers.agentTargetQueue = [];
|
headers.agentTargetQueue = [];
|
||||||
delete headers.agentTargetLastValue;
|
delete headers.agentTargetLastValue;
|
||||||
|
headers.agentManagerOperation = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app').controller('AuthenticationController', ['$q', '$scope', '$state', '$stateParams', '$sanitize', 'Authentication', 'UserService', 'EndpointService', 'StateManager', 'Notifications', 'SettingsService', 'urlHelper',
|
||||||
.controller('AuthenticationController', ['urlHelper','$q', '$scope', '$state', '$stateParams', '$sanitize', 'Authentication', 'UserService', 'EndpointService', 'StateManager', 'Notifications', 'SettingsService',
|
function($q, $scope, $state, $stateParams, $sanitize, Authentication, UserService, EndpointService, StateManager, Notifications, SettingsService, urlHelper) {
|
||||||
function (urlHelper, $q, $scope, $state, $stateParams, $sanitize, Authentication, UserService, EndpointService, StateManager, Notifications, SettingsService) {
|
|
||||||
$scope.logo = StateManager.getState().application.logo;
|
$scope.logo = StateManager.getState().application.logo;
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
|
@ -43,7 +42,7 @@ function (urlHelper, $q, $scope, $state, $stateParams, $sanitize, Authentication
|
||||||
if (endpoints.length === 0) {
|
if (endpoints.length === 0) {
|
||||||
$state.go('portainer.init.endpoint');
|
$state.go('portainer.init.endpoint');
|
||||||
} else {
|
} else {
|
||||||
$state.go('portainer.home');
|
$state.go($stateParams.redirect || 'portainer.home');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
|
@ -72,7 +71,7 @@ function (urlHelper, $q, $scope, $state, $stateParams, $sanitize, Authentication
|
||||||
if (endpoints.length === 0 && userDetails.role === 1) {
|
if (endpoints.length === 0 && userDetails.role === 1) {
|
||||||
$state.go('portainer.init.endpoint');
|
$state.go('portainer.init.endpoint');
|
||||||
} else {
|
} else {
|
||||||
$state.go('portainer.home');
|
$state.go($stateParams.redirect || 'portainer.home');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(function error(err) {
|
.catch(function error(err) {
|
||||||
|
|
|
@ -20,7 +20,7 @@ function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService,
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.copyAgentCommand = function() {
|
$scope.copyAgentCommand = function() {
|
||||||
clipboard.copyText('curl -L https://portainer.io/download/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');
|
||||||
$('#copyNotification').show();
|
$('#copyNotification').show();
|
||||||
$('#copyNotification').fadeOut(2000);
|
$('#copyNotification').fadeOut(2000);
|
||||||
};
|
};
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
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. You can use execute the following command on any manager node to deploy it.
|
||||||
<div style="margin-top: 10px;">
|
<div style="margin-top: 10px;">
|
||||||
<code>
|
<code>
|
||||||
curl -L https://portainer.io/download/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>
|
||||||
<span class="btn btn-primary btn-sm space-left" ng-click="copyAgentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy</span>
|
<span class="btn btn-primary btn-sm space-left" ng-click="copyAgentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy</span>
|
||||||
<span>
|
<span>
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
<div class="form-group" style="margin-left: 40px;">
|
<div class="form-group" style="margin-left: 40px;">
|
||||||
|
|
||||||
<div style="font-size: 125%; border-bottom: 2px solid #2d3e63; padding-bottom: 5px;">
|
<div style="font-size: 125%; border-bottom: 2px solid #2d3e63; padding-bottom: 5px;">
|
||||||
{{ extension.Enabled ? 'Enabled' : extension.Price }}
|
<span ng-if="extension.Enabled">Enabled</span>
|
||||||
|
<span ng-if="!extension.Enabled && extension.License.Expiration && !extension.License.Valid">Expired</span>
|
||||||
|
<span ng-if="!extension.Enabled && !extension.License.Expiration">{{ extension.Price }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="small text-muted col-sm-12" style="margin: 15px 0 15px 0;" ng-if="!extension.Enabled">
|
<div class="small text-muted col-sm-12" style="margin: 15px 0 15px 0;" ng-if="!extension.Enabled">
|
||||||
|
|
|
@ -66,7 +66,7 @@ function ($q, $scope, $state, Authentication, EndpointService, EndpointHelper, G
|
||||||
EndpointProvider.setEndpointID(endpoint.Id);
|
EndpointProvider.setEndpointID(endpoint.Id);
|
||||||
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
EndpointProvider.setEndpointPublicURL(endpoint.PublicURL);
|
||||||
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
EndpointProvider.setOfflineModeFromStatus(endpoint.Status);
|
||||||
StateManager.updateEndpointState(endpoint.Name, endpoint.Type, [])
|
StateManager.updateEndpointState(endpoint, [])
|
||||||
.then(function success() {
|
.then(function success() {
|
||||||
$state.go('azure.dashboard');
|
$state.go('azure.dashboard');
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('TeamsController', ['$q', '$scope', '$state', '$sanitize', 'TeamService', 'UserService', 'ModalService', 'Notifications', 'Authentication',
|
.controller('TeamsController', ['$q', '$scope', '$state', 'TeamService', 'UserService', 'ModalService', 'Notifications', 'Authentication',
|
||||||
function ($q, $scope, $state, $sanitize, TeamService, UserService, ModalService, Notifications, Authentication) {
|
function ($q, $scope, $state, TeamService, UserService, ModalService, Notifications, Authentication) {
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
actionInProgress: false
|
actionInProgress: false
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,7 @@ function ($q, $scope, $state, $sanitize, TeamService, UserService, ModalService,
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.addTeam = function() {
|
$scope.addTeam = function() {
|
||||||
var teamName = $sanitize($scope.formValues.Name);
|
var teamName = $scope.formValues.Name;
|
||||||
var leaderIds = [];
|
var leaderIds = [];
|
||||||
angular.forEach($scope.formValues.Leaders, function(user) {
|
angular.forEach($scope.formValues.Leaders, function(user) {
|
||||||
leaderIds.push(user.Id);
|
leaderIds.push(user.Id);
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
version: 1.0.{build}
|
|
||||||
image:
|
|
||||||
- Visual Studio 2017
|
|
||||||
- Ubuntu
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- ARCH: amd64
|
|
||||||
- ARCH: arm
|
|
||||||
- ARCH: arm64
|
|
||||||
- ARCH: ppc64le
|
|
||||||
- ARCH: s390x
|
|
||||||
DOCKER_USER:
|
|
||||||
secure: JapmC7j5F0mY3j/MVzU+Cw==
|
|
||||||
DOCKER_PASS:
|
|
||||||
secure: QGlCLNWzPD0HL8ipkohVic45/yU3bVOdjn0IiV6NnSQ=
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: arm
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: arm64
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: ppc64le
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: s390x
|
|
||||||
branches:
|
|
||||||
except:
|
|
||||||
- master
|
|
||||||
stack:
|
|
||||||
- node 9, go 1.10
|
|
||||||
install:
|
|
||||||
- yarn install
|
|
||||||
- npm install -g rebase-docker-image
|
|
||||||
init:
|
|
||||||
- sh: export IMAGE=linux
|
|
||||||
- cmd: SET IMAGE=windows
|
|
||||||
- ps: >-
|
|
||||||
if (!(Test-Path ~/.docker)) { mkdir ~/.docker };
|
|
||||||
Set-Content -Value '{ "experimental": "enabled" }' -Path ~/.docker/config.json -Encoding Ascii
|
|
||||||
build_script:
|
|
||||||
- sh: yarn grunt appveyorbuild:$IMAGE:$ARCH
|
|
||||||
- cmd: yarn grunt appveyorbuild:%IMAGE%:%ARCH%
|
|
||||||
- sh: sudo bash build/ci-linux.sh $IMAGE $ARCH $DOCKER_USER $DOCKER_PASS $APPVEYOR_REPO_BRANCH $APPVEYOR_PULL_REQUEST_NUMBER
|
|
||||||
- cmd: powershell -Command "& .\\build\\ci-windows.ps1"
|
|
|
@ -1,61 +0,0 @@
|
||||||
version: 1.0.{build}
|
|
||||||
image:
|
|
||||||
- Visual Studio 2017
|
|
||||||
- Ubuntu
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- ARCH: amd64
|
|
||||||
- ARCH: arm
|
|
||||||
- ARCH: arm64
|
|
||||||
- ARCH: ppc64le
|
|
||||||
- ARCH: s390x
|
|
||||||
DOCKER_USER:
|
|
||||||
secure: JapmC7j5F0mY3j/MVzU+Cw==
|
|
||||||
DOCKER_PASS:
|
|
||||||
secure: QGlCLNWzPD0HL8ipkohVic45/yU3bVOdjn0IiV6NnSQ=
|
|
||||||
PORTAINER_VERSION: "1.19.2"
|
|
||||||
matrix:
|
|
||||||
exclude:
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: arm
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: arm64
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: ppc64le
|
|
||||||
- image: Visual Studio 2017
|
|
||||||
ARCH: s390x
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
stack: node 9, go 1.10
|
|
||||||
artifacts:
|
|
||||||
- path: 'portainer-$(PORTAINER_VERSION)-$(IMAGE)-$(ARCH).tar.gz'
|
|
||||||
type: file
|
|
||||||
- path: 'portainer-$(PORTAINER_VERSION)-$(IMAGE)-$(ARCH)-checksum.txt'
|
|
||||||
type: file
|
|
||||||
install:
|
|
||||||
- yarn install
|
|
||||||
- npm install -g rebase-docker-image
|
|
||||||
init:
|
|
||||||
- sh: export IMAGE=linux
|
|
||||||
- cmd: SET IMAGE=windows
|
|
||||||
- ps: >-
|
|
||||||
if (!(Test-Path ~/.docker)) { mkdir ~/.docker }
|
|
||||||
Set-Content -Value '{ "experimental": "enabled" }' -Path ~/.docker/config.json -Encoding Ascii
|
|
||||||
build_script:
|
|
||||||
- sh: yarn grunt appveyorbuild:$IMAGE:$ARCH
|
|
||||||
- cmd: yarn grunt appveyorbuild:%IMAGE%:%ARCH%
|
|
||||||
- sh: sudo bash build/release-linux.sh $IMAGE $ARCH $PORTAINER_VERSION $DOCKER_USER $DOCKER_PASS
|
|
||||||
- cmd: powershell -Command "& .\\build\\release-windows.ps1"
|
|
||||||
test: off
|
|
||||||
deploy:
|
|
||||||
release: Release $(PORTAINER_VERSION)
|
|
||||||
description: ''
|
|
||||||
provider: GitHub
|
|
||||||
auth_token:
|
|
||||||
secure: BRYVGj94QlFBCMoO8yhSu+AGqKNV1+03LJEFrNUTRzo5erXfUHUIi/rgztnxfSGW
|
|
||||||
artifact: /portainer-$(PORTAINER_VERSION)-$(IMAGE)-$(ARCH).*/
|
|
||||||
draft: true
|
|
||||||
prerelease: false
|
|
||||||
on:
|
|
||||||
branch: master
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
param (
|
||||||
|
[string]$platform,
|
||||||
|
[string]$arch
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop";
|
||||||
|
|
||||||
|
$binary = "portainer.exe"
|
||||||
|
$project_path = (Get-ITEM -Path env:APPVEYOR_BUILD_FOLDER).Value
|
||||||
|
|
||||||
|
New-Item -Name dist -Path "$project_path" -ItemType Directory | Out-Null
|
||||||
|
Set-Location -Path "$project_path\api\cmd\portainer"
|
||||||
|
|
||||||
|
C:\go\bin\go.exe get -t -d -v ./...
|
||||||
|
C:\go\bin\go.exe build -v
|
||||||
|
|
||||||
|
Move-Item -Path "$($binary)" -Destination "..\..\..\dist"
|
|
@ -0,0 +1,9 @@
|
||||||
|
binary="portainer"
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
cd 'api/cmd/portainer'
|
||||||
|
|
||||||
|
go get -t -d -v ./...
|
||||||
|
GOOS=$1 GOARCH=$2 CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags '-s'
|
||||||
|
|
||||||
|
mv "${binary}" "../../../dist/portainer"
|
|
@ -0,0 +1,24 @@
|
||||||
|
param (
|
||||||
|
[string]$platform,
|
||||||
|
[string]$arch
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop";
|
||||||
|
|
||||||
|
$binary = "portainer.exe"
|
||||||
|
$project_path = (Get-ITEM -Path env:BUILD_SOURCESDIRECTORY).Value
|
||||||
|
|
||||||
|
Set-Item env:GOPATH "$project_path\api"
|
||||||
|
|
||||||
|
New-Item -Name dist -Path "$project_path" -ItemType Directory | Out-Null
|
||||||
|
New-Item -Name portainer -Path "$project_path\api\src\github.com\" -ItemType Directory | Out-Null
|
||||||
|
|
||||||
|
Copy-Item -Path "$project_path\api" -Destination "$project_path\api\src\github.com\portainer" -Recurse -Force -ErrorAction:SilentlyContinue
|
||||||
|
Rename-Item -Path "$project_path\api\src\github.com\portainer\api" -NewName "portainer" -ErrorAction:SilentlyContinue
|
||||||
|
|
||||||
|
Set-Location -Path "$project_path\api\cmd\portainer"
|
||||||
|
|
||||||
|
go.exe get -t -d -v ./...
|
||||||
|
go.exe build -v
|
||||||
|
|
||||||
|
Move-Item -Path "$project_path\api\cmd\portainer\$($binary)" -Destination "$project_path\dist"
|
|
@ -0,0 +1,15 @@
|
||||||
|
export GOPATH="$BUILD_SOURCESDIRECTORY/api"
|
||||||
|
|
||||||
|
binary="portainer"
|
||||||
|
|
||||||
|
mkdir -p dist
|
||||||
|
mkdir -p api/src/github.com/portainer/
|
||||||
|
|
||||||
|
cp -R api/ api/src/github.com/portainer/portainer/
|
||||||
|
|
||||||
|
cd 'api/cmd/portainer'
|
||||||
|
|
||||||
|
go get -t -d -v ./...
|
||||||
|
GOOS=$1 GOARCH=$2 CGO_ENABLED=0 go build -a --installsuffix cgo --ldflags '-s'
|
||||||
|
|
||||||
|
mv "$BUILD_SOURCESDIRECTORY/api/cmd/portainer/$binary" "$BUILD_SOURCESDIRECTORY/dist/portainer"
|
|
@ -0,0 +1,13 @@
|
||||||
|
param (
|
||||||
|
[string]$docker_version
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop";
|
||||||
|
|
||||||
|
New-Item -Path "docker-binary" -ItemType Directory | Out-Null
|
||||||
|
|
||||||
|
$download_folder = "docker-binary"
|
||||||
|
|
||||||
|
Invoke-WebRequest -O "$($download_folder)/docker-binaries.zip" "https://download.docker.com/win/static/stable/x86_64/docker-$($docker_version).zip"
|
||||||
|
Expand-Archive -Path "$($download_folder)/docker-binaries.zip" -DestinationPath "$($download_folder)"
|
||||||
|
Move-Item -Path "$($download_folder)/docker/docker.exe" -Destination "dist"
|
|
@ -1,5 +1,5 @@
|
||||||
Name: portainer
|
Name: portainer
|
||||||
Version: 1.20.0
|
Version: 1.20.1
|
||||||
Release: 0
|
Release: 0
|
||||||
License: Zlib
|
License: Zlib
|
||||||
Summary: A lightweight docker management UI
|
Summary: A lightweight docker management UI
|
||||||
|
|
39
gruntfile.js
39
gruntfile.js
|
@ -51,6 +51,10 @@ module.exports = function(grunt) {
|
||||||
grunt.task.run(['config:prod', 'clean:all', 'shell:buildBinary:' + p + ':' + a, 'shell:downloadDockerBinary:' + p + ':' + a, 'before-copy', 'copy:assets', 'after-copy']);
|
grunt.task.run(['config:prod', 'clean:all', 'shell:buildBinary:' + p + ':' + a, 'shell:downloadDockerBinary:' + p + ':' + a, 'before-copy', 'copy:assets', 'after-copy']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
grunt.task.registerTask('devopsbuild', 'devopsbuild:<platform>:<arch>', function(p, a) {
|
||||||
|
grunt.task.run(['config:prod', 'clean:all', 'shell:buildBinaryOnDevOps:' + p + ':' + a, 'shell:downloadDockerBinary:' + p + ':' + a, 'before-copy', 'copy:assets', 'after-copy']);
|
||||||
|
});
|
||||||
|
|
||||||
grunt.registerTask('lint', ['eslint']);
|
grunt.registerTask('lint', ['eslint']);
|
||||||
grunt.registerTask('run-dev', ['build', 'shell:run:' + arch, 'watch:build']);
|
grunt.registerTask('run-dev', ['build', 'shell:run:' + arch, 'watch:build']);
|
||||||
grunt.registerTask('clear', ['clean:app']);
|
grunt.registerTask('clear', ['clean:app']);
|
||||||
|
@ -261,13 +265,31 @@ gruntfile_cfg.replace = {
|
||||||
|
|
||||||
function shell_buildBinary(p, a) {
|
function shell_buildBinary(p, a) {
|
||||||
var binfile = 'dist/portainer-' + p + '-' + a;
|
var binfile = 'dist/portainer-' + p + '-' + a;
|
||||||
|
if (p === 'linux') {
|
||||||
return [
|
return [
|
||||||
'if [ -f ' + ((p === 'windows') ? binfile + '.exe' : binfile) + ' ]; then',
|
'if [ -f ' + (binfile) + ' ]; then',
|
||||||
'echo "Portainer binary exists";',
|
'echo "Portainer binary exists";',
|
||||||
'else',
|
'else',
|
||||||
'build/build_in_container.sh ' + p + ' ' + a + ';',
|
'build/build_binary.sh ' + p + ' ' + a + ';',
|
||||||
'fi'
|
'fi'
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'powershell -Command "& {if (Get-Item -Path ' + binfile + '.exe -ErrorAction:SilentlyContinue) {',
|
||||||
|
'Write-Host "Portainer binary exists"',
|
||||||
|
'} else {',
|
||||||
|
'& ".\\build\\build_binary.ps1" -platform ' + p + ' -arch ' + a + '',
|
||||||
|
'}}"'
|
||||||
|
].join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shell_buildBinaryOnDevOps(p, a) {
|
||||||
|
if (p === 'linux') {
|
||||||
|
return 'build/build_binary_devops.sh ' + p + ' ' + a + ';';
|
||||||
|
} else {
|
||||||
|
return 'powershell -Command ".\\build\\build_binary_devops.ps1 -platform ' + p + ' -arch ' + a + '"';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function shell_run(arch) {
|
function shell_run(arch) {
|
||||||
|
@ -283,17 +305,28 @@ function shell_downloadDockerBinary(p, a) {
|
||||||
var ip = ((ps[p] === undefined) ? p : ps[p]);
|
var ip = ((ps[p] === undefined) ? p : ps[p]);
|
||||||
var ia = ((as[a] === undefined) ? a : as[a]);
|
var ia = ((as[a] === undefined) ? a : as[a]);
|
||||||
var binaryVersion = ((p === 'windows' ? '<%= shippedDockerVersionWindows %>' : '<%= shippedDockerVersion %>'));
|
var binaryVersion = ((p === 'windows' ? '<%= shippedDockerVersionWindows %>' : '<%= shippedDockerVersion %>'));
|
||||||
|
if (p === 'linux') {
|
||||||
return [
|
return [
|
||||||
'if [ -f ' + ((p === 'windows') ? 'dist/docker.exe' : 'dist/docker') + ' ]; then',
|
'if [ -f dist/docker ]; then',
|
||||||
'echo "Docker binary exists";',
|
'echo "Docker binary exists";',
|
||||||
'else',
|
'else',
|
||||||
'build/download_docker_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';',
|
'build/download_docker_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';',
|
||||||
'fi'
|
'fi'
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
'powershell -Command "& {if (Get-Item -Path dist/docker.exe -ErrorAction:SilentlyContinue) {',
|
||||||
|
'Write-Host "Docker binary exists"',
|
||||||
|
'} else {',
|
||||||
|
'& ".\\build\\download_docker_binary.ps1" -docker_version ' + binaryVersion + '',
|
||||||
|
'}}"'
|
||||||
|
].join(' ');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gruntfile_cfg.shell = {
|
gruntfile_cfg.shell = {
|
||||||
buildBinary: { command: shell_buildBinary },
|
buildBinary: { command: shell_buildBinary },
|
||||||
|
buildBinaryOnDevOps: { command: shell_buildBinaryOnDevOps },
|
||||||
run: { command: shell_run },
|
run: { command: shell_run },
|
||||||
downloadDockerBinary: { command: shell_downloadDockerBinary }
|
downloadDockerBinary: { command: shell_downloadDockerBinary }
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"author": "Portainer.io",
|
"author": "Portainer.io",
|
||||||
"name": "portainer",
|
"name": "portainer",
|
||||||
"homepage": "http://portainer.io",
|
"homepage": "http://portainer.io",
|
||||||
"version": "1.20.0",
|
"version": "1.20.1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:portainer/portainer.git"
|
"url": "git@github.com:portainer/portainer.git"
|
||||||
|
@ -52,7 +52,7 @@
|
||||||
"angularjs-scroll-glue": "^2.2.0",
|
"angularjs-scroll-glue": "^2.2.0",
|
||||||
"angularjs-slider": "^6.4.0",
|
"angularjs-slider": "^6.4.0",
|
||||||
"bootbox": "^4.4.0",
|
"bootbox": "^4.4.0",
|
||||||
"bootstrap": "~3.3.6",
|
"bootstrap": "^3.4.0",
|
||||||
"chart.js": "~2.6.0",
|
"chart.js": "~2.6.0",
|
||||||
"codemirror": "~5.30.0",
|
"codemirror": "~5.30.0",
|
||||||
"filesize": "~3.3.0",
|
"filesize": "~3.3.0",
|
||||||
|
|
|
@ -874,5 +874,18 @@
|
||||||
"label": "Datadog API key"
|
"label": "Datadog API key"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"title": "Sonatype Nexus3",
|
||||||
|
"description": "Sonatype Nexus3 registry manager",
|
||||||
|
"categories": ["docker"],
|
||||||
|
"platform": "linux",
|
||||||
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/sonatype.png",
|
||||||
|
"image": "sonatype/nexus3:latest",
|
||||||
|
"ports": [
|
||||||
|
"8081/tcp"
|
||||||
|
],
|
||||||
|
"volumes": [{ "container": "/nexus-data"}]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -139,7 +139,6 @@ angular-mocks@~1.5.0:
|
||||||
angular-moment-picker@^0.10.2:
|
angular-moment-picker@^0.10.2:
|
||||||
version "0.10.2"
|
version "0.10.2"
|
||||||
resolved "https://registry.yarnpkg.com/angular-moment-picker/-/angular-moment-picker-0.10.2.tgz#54c8b3c228b33dffa3b7b3d0773a585323815c33"
|
resolved "https://registry.yarnpkg.com/angular-moment-picker/-/angular-moment-picker-0.10.2.tgz#54c8b3c228b33dffa3b7b3d0773a585323815c33"
|
||||||
integrity sha512-WvmrQM0zEcFqi50yDELaF34Ilrx4PtL7mWLcpTZCJGQDvMlIsxJrB30LxOkoJv8yrrLxD2s6nnR3t1/SqioWWw==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
angular "^1.3"
|
angular "^1.3"
|
||||||
moment "^2.16.0"
|
moment "^2.16.0"
|
||||||
|
@ -167,7 +166,6 @@ angular@1.x, angular@~1.5.0:
|
||||||
angular@^1.3:
|
angular@^1.3:
|
||||||
version "1.7.5"
|
version "1.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/angular/-/angular-1.7.5.tgz#d1c1c01c6f5dc835638f3f9aa51012857bdac49e"
|
resolved "https://registry.yarnpkg.com/angular/-/angular-1.7.5.tgz#d1c1c01c6f5dc835638f3f9aa51012857bdac49e"
|
||||||
integrity sha512-760183yxtGzni740IBTieNuWLtPNAoMqvmC0Z62UoU0I3nqk+VJuO3JbQAXOyvo3Oy/ZsdNQwrSTh/B0OQZjNw==
|
|
||||||
|
|
||||||
angularjs-scroll-glue@^2.2.0:
|
angularjs-scroll-glue@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
|
@ -407,9 +405,9 @@ bootbox@^4.4.0:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/bootbox/-/bootbox-4.4.0.tgz#ff7f898fb87d4527e547feb64158f88450d1a0c9"
|
resolved "https://registry.yarnpkg.com/bootbox/-/bootbox-4.4.0.tgz#ff7f898fb87d4527e547feb64158f88450d1a0c9"
|
||||||
|
|
||||||
bootstrap@~3.3.6:
|
bootstrap@^3.4.0:
|
||||||
version "3.3.7"
|
version "3.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.3.7.tgz#5a389394549f23330875a3b150656574f8a9eb71"
|
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-3.4.0.tgz#f8d77540dd3062283d2ae7687e21c1e691961640"
|
||||||
|
|
||||||
brace-expansion@^1.0.0, brace-expansion@^1.1.7:
|
brace-expansion@^1.0.0, brace-expansion@^1.1.7:
|
||||||
version "1.1.8"
|
version "1.1.8"
|
||||||
|
@ -2921,7 +2919,6 @@ moment@^2.10.6:
|
||||||
moment@^2.16.0:
|
moment@^2.16.0:
|
||||||
version "2.22.2"
|
version "2.22.2"
|
||||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
|
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
|
||||||
integrity sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=
|
|
||||||
|
|
||||||
moment@^2.21.0:
|
moment@^2.21.0:
|
||||||
version "2.21.0"
|
version "2.21.0"
|
||||||
|
@ -4527,7 +4524,6 @@ xtend@~3.0.0:
|
||||||
xterm@^3.8.0:
|
xterm@^3.8.0:
|
||||||
version "3.8.0"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.8.0.tgz#55d1de518bdc9c9793823f5e4e97d6898972938d"
|
resolved "https://registry.yarnpkg.com/xterm/-/xterm-3.8.0.tgz#55d1de518bdc9c9793823f5e4e97d6898972938d"
|
||||||
integrity sha512-rS3HLryuMWbLsv98+jVVSUXCxmoyXPwqwJNC0ad0VSMdXgl65LefPztQVwfurkaF7kM7ZSgM8eJjnJ9kkdoR1w==
|
|
||||||
|
|
||||||
yargs@~3.10.0:
|
yargs@~3.10.0:
|
||||||
version "3.10.0"
|
version "3.10.0"
|
||||||
|
|
Loading…
Reference in New Issue