mirror of https://github.com/portainer/portainer
Merge branch 'develop' into feat2240-host-view
commit
c780d52bcf
|
@ -77,14 +77,14 @@ The subject contains succinct description of the change:
|
|||
|
||||
## Contribution process
|
||||
|
||||
Our contribution process is described below. Some of the steps can be visualized inside Github via specific `contrib/` labels, such as `contrib/func-review-in-progress` or `contrib/tech-review-approved`.
|
||||
Our contribution process is described below. Some of the steps can be visualized inside Github via specific `status/` labels, such as `status/1-functional-review` or `status/2-technical-review`.
|
||||
|
||||
### Bug report
|
||||
|
||||

|
||||

|
||||
|
||||
### Feature request
|
||||
|
||||
The feature request process is similar to the bug report process but has an extra functional validation before the technical validation.
|
||||
The feature request process is similar to the bug report process but has an extra functional validation before the technical validation as well as a documentation validation before the testing phase.
|
||||
|
||||

|
||||

|
||||
|
|
|
@ -178,7 +178,7 @@ func (m *Migrator) Migrate() error {
|
|||
}
|
||||
}
|
||||
|
||||
// 1.19.2-dev
|
||||
// Portainer 1.19.2
|
||||
if m.currentDBVersion < 14 {
|
||||
err := m.updateResourceControlsToDBVersion14()
|
||||
if err != nil {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
const (
|
||||
errInvalidResponseStatus = portainer.Error("Invalid response status (expecting 200)")
|
||||
defaultHTTPTimeout = 5
|
||||
)
|
||||
|
||||
// HTTPClient represents a client to send HTTP requests.
|
||||
|
@ -26,7 +27,7 @@ type HTTPClient struct {
|
|||
func NewHTTPClient() *HTTPClient {
|
||||
return &HTTPClient{
|
||||
&http.Client{
|
||||
Timeout: time.Second * 5,
|
||||
Timeout: time.Second * time.Duration(defaultHTTPTimeout),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -67,10 +68,16 @@ func (client *HTTPClient) ExecuteAzureAuthenticationRequest(credentials *portain
|
|||
}
|
||||
|
||||
// Get executes a simple HTTP GET to the specified URL and returns
|
||||
// the content of the response body.
|
||||
func Get(url string) ([]byte, error) {
|
||||
// the content of the response body. Timeout can be specified via the timeout parameter,
|
||||
// will default to defaultHTTPTimeout if set to 0.
|
||||
func Get(url string, timeout int) ([]byte, error) {
|
||||
|
||||
if timeout == 0 {
|
||||
timeout = defaultHTTPTimeout
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Timeout: time.Second * 3,
|
||||
Timeout: time.Second * time.Duration(timeout),
|
||||
}
|
||||
|
||||
response, err := client.Get(url)
|
||||
|
|
|
@ -16,7 +16,7 @@ type motdResponse struct {
|
|||
|
||||
func (handler *Handler) motd(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
motd, err := client.Get(portainer.MessageOfTheDayURL)
|
||||
motd, err := client.Get(portainer.MessageOfTheDayURL, 0)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
|
|
|
@ -26,7 +26,7 @@ func (handler *Handler) templateList(w http.ResponseWriter, r *http.Request) *ht
|
|||
}
|
||||
} else {
|
||||
var templateData []byte
|
||||
templateData, err = client.Get(settings.TemplatesURL)
|
||||
templateData, err = client.Get(settings.TemplatesURL, 0)
|
||||
if err != nil {
|
||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve external templates", err}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,13 @@ type Service struct{}
|
|||
func searchUser(username string, conn *ldap.Conn, settings []portainer.LDAPSearchSettings) (string, error) {
|
||||
var userDN string
|
||||
found := false
|
||||
usernameEscaped := ldap.EscapeFilter(username)
|
||||
|
||||
for _, searchSettings := range settings {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
searchSettings.BaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&%s(%s=%s))", searchSettings.Filter, searchSettings.UserNameAttribute, username),
|
||||
fmt.Sprintf("(&%s(%s=%s))", searchSettings.Filter, searchSettings.UserNameAttribute, usernameEscaped),
|
||||
[]string{"dn"},
|
||||
nil,
|
||||
)
|
||||
|
@ -134,12 +136,13 @@ func (*Service) GetUserGroups(username string, settings *portainer.LDAPSettings)
|
|||
// Get a list of group names for specified user from LDAP/AD
|
||||
func getGroups(userDN string, conn *ldap.Conn, settings []portainer.LDAPGroupSearchSettings) []string {
|
||||
groups := make([]string, 0)
|
||||
userDNEscaped := ldap.EscapeFilter(userDN)
|
||||
|
||||
for _, searchSettings := range settings {
|
||||
searchRequest := ldap.NewSearchRequest(
|
||||
searchSettings.GroupBaseDN,
|
||||
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
|
||||
fmt.Sprintf("(&%s(%s=%s))", searchSettings.GroupFilter, searchSettings.GroupAttribute, userDN),
|
||||
fmt.Sprintf("(&%s(%s=%s))", searchSettings.GroupFilter, searchSettings.GroupAttribute, userDNEscaped),
|
||||
[]string{"cn"},
|
||||
nil,
|
||||
)
|
||||
|
|
|
@ -639,7 +639,7 @@ type (
|
|||
|
||||
const (
|
||||
// APIVersion is the version number of the Portainer API
|
||||
APIVersion = "1.19.2-dev"
|
||||
APIVersion = "1.20-dev"
|
||||
// DBVersion is the version number of the Portainer database
|
||||
DBVersion = 14
|
||||
// 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).
|
||||
|
||||
version: "1.19.2-dev"
|
||||
version: "1.20-dev"
|
||||
title: "Portainer API"
|
||||
contact:
|
||||
email: "info@portainer.io"
|
||||
|
@ -2816,7 +2816,7 @@ definitions:
|
|||
description: "Is analytics enabled"
|
||||
Version:
|
||||
type: "string"
|
||||
example: "1.19.2-dev"
|
||||
example: "1.20-dev"
|
||||
description: "Portainer API version"
|
||||
PublicSettingsInspectResponse:
|
||||
type: "object"
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
angular
|
||||
.module('portainer.docker')
|
||||
.controller('ContainerRestartPolicyController', [
|
||||
function ContainerRestartPolicyController() {
|
||||
var ctrl = this;
|
||||
.module('portainer.docker')
|
||||
.controller('ContainerRestartPolicyController', [function ContainerRestartPolicyController() {
|
||||
var ctrl = this;
|
||||
|
||||
this.state = {
|
||||
editMode :false,
|
||||
editModel :{}
|
||||
};
|
||||
|
||||
this.state = {
|
||||
editModel : {}
|
||||
};
|
||||
|
||||
ctrl.toggleEdit = toggleEdit;
|
||||
ctrl.save = save;
|
||||
ctrl.save = save;
|
||||
|
||||
function toggleEdit() {
|
||||
ctrl.state.editMode = true;
|
||||
ctrl.state.editModel = {
|
||||
name: ctrl.name,
|
||||
maximumRetryCount: ctrl.maximumRetryCount
|
||||
};
|
||||
}
|
||||
|
||||
function save() {
|
||||
if (ctrl.state.editModel.name === ctrl.name &&
|
||||
ctrl.state.editModel.maximumRetryCount === ctrl.maximumRetryCount) {
|
||||
ctrl.state.editMode = false;
|
||||
return;
|
||||
}
|
||||
ctrl.updateRestartPolicy(ctrl.state.editModel)
|
||||
.then(function onUpdateSucceed() {
|
||||
ctrl.state.editMode = false;
|
||||
});
|
||||
}
|
||||
function save() {
|
||||
if (ctrl.state.editModel.name === ctrl.name && ctrl.state.editModel.maximumRetryCount === ctrl.maximumRetryCount) {
|
||||
return;
|
||||
}
|
||||
]);
|
||||
ctrl.updateRestartPolicy(ctrl.state.editModel);
|
||||
}
|
||||
|
||||
this.$onInit = function() {
|
||||
ctrl.state.editModel = {
|
||||
name: ctrl.name ? ctrl.name : 'no',
|
||||
maximumRetryCount: ctrl.maximumRetryCount
|
||||
};
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
<div>
|
||||
<table class="table table-bordered table-condensed" ng-if="!$ctrl.state.editMode">
|
||||
<tr>
|
||||
<td class="col-md-3">
|
||||
<a href="" data-toggle="tooltip" title="Edit restart policy" ng-click="$ctrl.toggleEdit()">
|
||||
<i class="fa fa-edit"></i>
|
||||
</a>
|
||||
<span>Name</span>
|
||||
</td>
|
||||
<td>{{$ctrl.name }}</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.name === 'on-failure'">
|
||||
<td class="col-md-3">Maximum Retry Count</td>
|
||||
<td>
|
||||
{{ $ctrl.maximumRetryCount }}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<table class="table table-bordered table-condensed" ng-if="$ctrl.state.editMode">
|
||||
<table class="table table-bordered table-condensed">
|
||||
<tr>
|
||||
<td class="col-md-3">
|
||||
<span>Name</span>
|
||||
|
@ -31,15 +13,14 @@
|
|||
</select>
|
||||
</td>
|
||||
<td class="col-md-2">
|
||||
<button class="btn btn-success" ng-click="$ctrl.save()">Save</button>
|
||||
<button class="btn btn-sm btn-primary" ng-click="$ctrl.save()">Update</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="$ctrl.state.editModel.name === 'on-failure'">
|
||||
<td class="col-md-3">Maximum Retry Count</td>
|
||||
<td>
|
||||
<td colspan="2">
|
||||
<input type="number" class="form-control" ng-model="$ctrl.state.editModel.maximumRetryCount" />
|
||||
</td>
|
||||
<td class="col-md-2"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group" aria-label="..." >
|
||||
<a style="margin: 0 2.5px;" ui-sref="docker.containers.container.logs({ id: item.Container.Id, nodeName: item.Container.NodeName })" title="Logs" ng-if="$ctrl.agentProxy"><i class="fa fa-file-alt space-right" aria-hidden="true"></i></a>
|
||||
<a style="margin: 0 2.5px;" ui-sref="docker.containers.container.logs({ id: item.Container.Id, nodeName: item.Container.NodeName })" title="Logs" ng-if="$ctrl.agentProxy && item.Container.Id"><i class="fa fa-file-alt space-right" aria-hidden="true"></i></a>
|
||||
<a style="margin: 0 2.5px;" ui-sref="docker.tasks.task.logs({ id: item.Id })" title="Logs" ng-if="!$ctrl.agentProxy && $ctrl.showTaskLogsButton && item.Status.State|taskhaslogs"><i class="fa fa-file-alt space-right" aria-hidden="true"></i></a>
|
||||
<a style="margin: 0 2.5px;" ui-sref="docker.containers.container.console({ id: item.Container.Id, nodeName: item.Container.NodeName })" title="Console" ng-if="$ctrl.agentProxy && item.Status.State === 'running'"><i class="fa fa-terminal space-right" aria-hidden="true"></i></a>
|
||||
</div>
|
||||
|
|
|
@ -226,7 +226,7 @@
|
|||
<tr>
|
||||
<td>Restart policies</td>
|
||||
<td>
|
||||
<container-restart-policy
|
||||
<container-restart-policy ng-if="container"
|
||||
name="container.HostConfig.RestartPolicy.Name"
|
||||
maximum-retry-count="container.HostConfig.RestartPolicy.MaximumRetryCount"
|
||||
update-restart-policy="updateRestartPolicy(name, maximumRetryCount)">
|
||||
|
|
|
@ -323,6 +323,7 @@ function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, Co
|
|||
Name: restartPolicy,
|
||||
MaximumRetryCount: maximumRetryCount
|
||||
};
|
||||
Notifications.success('Restart policy updated');
|
||||
}
|
||||
|
||||
function notifyOnError(err) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
angular.module('portainer.app')
|
||||
.controller('CreateEndpointController', ['$q', '$scope', '$state', '$filter', 'EndpointService', 'GroupService', 'TagService', 'Notifications',
|
||||
function ($q, $scope, $state, $filter, EndpointService, GroupService, TagService, Notifications) {
|
||||
.controller('CreateEndpointController', ['$q', '$scope', '$state', '$filter', 'clipboard', 'EndpointService', 'GroupService', 'TagService', 'Notifications',
|
||||
function ($q, $scope, $state, $filter, clipboard, EndpointService, GroupService, TagService, Notifications) {
|
||||
|
||||
$scope.state = {
|
||||
EnvironmentType: 'docker',
|
||||
|
@ -19,6 +19,12 @@ function ($q, $scope, $state, $filter, EndpointService, GroupService, TagService
|
|||
Tags: []
|
||||
};
|
||||
|
||||
$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');
|
||||
$('#copyNotification').show();
|
||||
$('#copyNotification').fadeOut(2000);
|
||||
};
|
||||
|
||||
$scope.addDockerEndpoint = function() {
|
||||
var name = $scope.formValues.Name;
|
||||
var URL = $filter('stripprotocol')($scope.formValues.URL);
|
||||
|
|
|
@ -64,8 +64,16 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
If you have started Portainer in the same overlay network as the agent, you can use <code>tasks.AGENT_SERVICE_NAME:AGENT_SERVICE_PORT</code> as the
|
||||
endpoint URL format.
|
||||
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;">
|
||||
<code>
|
||||
curl -L https://portainer.io/download/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
|
||||
</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>
|
||||
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none;"></i>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
2
build.sh
2
build.sh
|
@ -24,7 +24,7 @@ function build_archive() {
|
|||
function build_all() {
|
||||
mkdir -pv "${ARCHIVE_BUILD_FOLDER}"
|
||||
for tag in $@; do
|
||||
grunt "release:`echo "$tag" | tr '-' ':'`"
|
||||
yarn grunt "release:`echo "$tag" | tr '-' ':'`"
|
||||
name="portainer"; if [ "$(echo "$tag" | cut -c1)" = "w" ]; then name="${name}.exe"; fi
|
||||
mv dist/portainer-$tag* dist/$name
|
||||
if [ `echo $tag | cut -d \- -f 1` == 'linux' ]; then build_and_push_images "$tag"; fi
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: portainer
|
||||
Version: 1.19.2-dev
|
||||
Version: 1.20-dev
|
||||
Release: 0
|
||||
License: Zlib
|
||||
Summary: A lightweight docker management UI
|
||||
|
|
|
@ -80,7 +80,7 @@ module.exports = function (grunt) {
|
|||
grunt.initConfig({
|
||||
root: 'dist',
|
||||
distdir: 'dist/public',
|
||||
shippedDockerVersion: '18.03.1-ce',
|
||||
shippedDockerVersion: '18.06.1-ce',
|
||||
shippedDockerVersionWindows: '17.09.0-ce',
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
config: gruntfile_cfg.config,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"author": "Portainer.io",
|
||||
"name": "portainer",
|
||||
"homepage": "http://portainer.io",
|
||||
"version": "1.19.2-dev",
|
||||
"version": "1.20-dev",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:portainer/portainer.git"
|
||||
|
@ -20,6 +20,7 @@
|
|||
}
|
||||
],
|
||||
"scripts": {
|
||||
"grunt": "grunt",
|
||||
"dev": "yarn grunt run-dev",
|
||||
"clean-all": "yarn grunt clean:all",
|
||||
"build": "yarn grunt build",
|
||||
|
|
Loading…
Reference in New Issue