Merge branch 'develop' into feat2240-host-view

pull/2255/head
Chaim Lev-Ari 2018-09-27 09:50:30 +03:00
commit c780d52bcf
19 changed files with 77 additions and 80 deletions

View File

@ -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
![portainer_bugreport_workflow](https://user-images.githubusercontent.com/5485061/43569306-5571b3a0-9637-11e8-8559-786cfc82a14f.png)
![portainer_bugreport_workflow](https://user-images.githubusercontent.com/5485061/45727219-50190a00-bbf5-11e8-9fe8-3a563bb8d5d7.png)
### 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.
![portainer_featurerequest_workflow](https://user-images.githubusercontent.com/5485061/43569315-5d30a308-9637-11e8-8292-3c62b5612925.png)
![portainer_featurerequest_workflow](https://user-images.githubusercontent.com/5485061/45727229-5ad39f00-bbf5-11e8-9550-16ba66c50615.png)

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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}
}

View File

@ -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,
)

View File

@ -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

View File

@ -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"

View File

@ -1,36 +1,26 @@
angular
.module('portainer.docker')
.controller('ContainerRestartPolicyController', [
function ContainerRestartPolicyController() {
.module('portainer.docker')
.controller('ContainerRestartPolicyController', [function ContainerRestartPolicyController() {
var ctrl = this;
this.state = {
editMode :false,
editModel :{}
editModel : {}
};
ctrl.toggleEdit = toggleEdit;
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;
if (ctrl.state.editModel.name === ctrl.name && ctrl.state.editModel.maximumRetryCount === ctrl.maximumRetryCount) {
return;
}
ctrl.updateRestartPolicy(ctrl.state.editModel)
.then(function onUpdateSucceed() {
ctrl.state.editMode = false;
});
ctrl.updateRestartPolicy(ctrl.state.editModel);
}
}
]);
this.$onInit = function() {
ctrl.state.editModel = {
name: ctrl.name ? ctrl.name : 'no',
maximumRetryCount: ctrl.maximumRetryCount
};
};
}
]);

View File

@ -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>

View File

@ -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>

View File

@ -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)">

View File

@ -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) {

View File

@ -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);

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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",