Compare commits

...

7 Commits

Author SHA1 Message Date
fhanportainer
3e8bfe003b fix(ingress): fixed hostname field when having multiple ingresses EE-1072 (#5273) 2021-07-08 11:43:32 +12:00
Stéphane Busso
5b68c4365e Merge branch 'release/2.6' of github.com:portainer/portainer into release/2.6 2021-07-08 11:39:21 +12:00
Stéphane Busso
9cd64664cc fix download logs (#5243) 2021-07-08 11:37:18 +12:00
yi-portainer
e831fa4a03 * update versions to 2.6.1 2021-07-07 17:20:18 +12:00
cong meng
2a3c807978 fix(ingress): EE-1049 Ingress config is lost when deleting an application deployed with ingress (#5264)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-07 14:08:20 +12:00
cong meng
a8265a44d0 fix EE-1078 Too strict form validation for docker environment variables (#5278)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-07 12:52:37 +12:00
Hui
71ad21598b remove expiry time copy logic (#5259) 2021-06-30 16:49:48 +12:00
16 changed files with 31 additions and 48 deletions

View File

@@ -5,7 +5,6 @@ import (
"log"
"net/http"
"strings"
"time"
"github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error"
@@ -134,14 +133,6 @@ func (handler *Handler) writeToken(w http.ResponseWriter, user *portainer.User)
return handler.persistAndWriteToken(w, composeTokenData(user))
}
func (handler *Handler) writeTokenForOAuth(w http.ResponseWriter, user *portainer.User, expiryTime *time.Time) *httperror.HandlerError {
token, err := handler.JWTService.GenerateTokenForOAuth(composeTokenData(user), expiryTime)
if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to generate JWT token", Err: err}
}
return response.JSON(w, &authenticateResponse{JWT: token})
}
func (handler *Handler) persistAndWriteToken(w http.ResponseWriter, tokenData *portainer.TokenData) *httperror.HandlerError {
token, err := handler.JWTService.GenerateToken(tokenData)
if err != nil {

View File

@@ -4,7 +4,6 @@ import (
"errors"
"log"
"net/http"
"time"
"github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error"
@@ -26,21 +25,21 @@ func (payload *oauthPayload) Validate(r *http.Request) error {
return nil
}
func (handler *Handler) authenticateOAuth(code string, settings *portainer.OAuthSettings) (string, *time.Time, error) {
func (handler *Handler) authenticateOAuth(code string, settings *portainer.OAuthSettings) (string, error) {
if code == "" {
return "", nil, errors.New("Invalid OAuth authorization code")
return "", errors.New("Invalid OAuth authorization code")
}
if settings == nil {
return "", nil, errors.New("Invalid OAuth configuration")
return "", errors.New("Invalid OAuth configuration")
}
username, expiryTime, err := handler.OAuthService.Authenticate(code, settings)
username, err := handler.OAuthService.Authenticate(code, settings)
if err != nil {
return "", nil, err
return "", err
}
return username, expiryTime, nil
return username, nil
}
// @id ValidateOAuth
@@ -70,7 +69,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{StatusCode: http.StatusForbidden, Message: "OAuth authentication is not enabled", Err: errors.New("OAuth authentication is not enabled")}
}
username, expiryTime, err := handler.authenticateOAuth(payload.Code, &settings.OAuthSettings)
username, err := handler.authenticateOAuth(payload.Code, &settings.OAuthSettings)
if err != nil {
log.Printf("[DEBUG] - OAuth authentication error: %s", err)
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to authenticate through OAuth", Err: httperrors.ErrUnauthorized}
@@ -111,5 +110,5 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
}
return handler.writeTokenForOAuth(w, user, expiryTime)
return handler.writeToken(w, user)
}

View File

@@ -67,7 +67,7 @@ type Handler struct {
}
// @title PortainerCE API
// @version 2.1.1
// @version 2.6.1
// @description.markdown api-description.md
// @termsOfService

View File

@@ -9,7 +9,6 @@ import (
"mime"
"net/http"
"net/url"
"time"
"golang.org/x/oauth2"
@@ -27,18 +26,18 @@ func NewService() *Service {
// Authenticate takes an access code and exchanges it for an access token from portainer OAuthSettings token endpoint.
// On success, it will then return the username and token expiry time associated to authenticated user by fetching this information
// from the resource server and matching it with the user identifier setting.
func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings) (string, *time.Time, error) {
func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings) (string, error) {
token, err := getOAuthToken(code, configuration)
if err != nil {
log.Printf("[DEBUG] - Failed retrieving access token: %v", err)
return "", nil, err
return "", err
}
username, err := getUsername(token.AccessToken, configuration)
if err != nil {
log.Printf("[DEBUG] - Failed retrieving oauth user name: %v", err)
return "", nil, err
return "", err
}
return username, &token.Expiry, nil
return username, nil
}
func getOAuthToken(code string, configuration *portainer.OAuthSettings) (*oauth2.Token, error) {

View File

@@ -1189,7 +1189,7 @@ type (
// OAuthService represents a service used to authenticate users using OAuth
OAuthService interface {
Authenticate(code string, configuration *OAuthSettings) (string, *time.Time, error)
Authenticate(code string, configuration *OAuthSettings) (string, error)
}
// RegistryService represents a service for managing registry data
@@ -1341,7 +1341,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.6.0"
APIVersion = "2.6.1"
// DBVersion is the version number of the Portainer database
DBVersion = 30
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax

View File

@@ -48,7 +48,7 @@ angular.module('portainer.docker').controller('LogViewerController', [
};
this.downloadLogs = function () {
const data = new Blob([_.reduce(this.state.filteredLogs, (acc, log) => acc + '\n' + log, '')]);
const data = new Blob([_.reduce(this.state.filteredLogs, (acc, log) => acc + '\n' + log.line, '')]);
FileSaver.saveAs(data, this.resourceName + '_logs.txt');
};
},

View File

@@ -261,7 +261,7 @@ class KubernetesApplicationConverter {
return res;
}
static applicationToFormValues(app, resourcePools, configurations, persistentVolumeClaims, nodesLabels) {
static applicationToFormValues(app, resourcePools, configurations, persistentVolumeClaims, nodesLabels, ingresses) {
const res = new KubernetesApplicationFormValues();
res.ApplicationType = app.ApplicationType;
res.ResourcePool = _.find(resourcePools, ['Namespace.Name', app.ResourcePool]);
@@ -278,7 +278,7 @@ class KubernetesApplicationConverter {
res.PersistedFolders = KubernetesApplicationHelper.generatePersistedFoldersFormValuesFromPersistedFolders(app.PersistedFolders, persistentVolumeClaims); // generate from PVC and app.PersistedFolders
res.Configurations = KubernetesApplicationHelper.generateConfigurationFormValuesFromEnvAndVolumes(app.Env, app.ConfigurationVolumes, configurations);
res.AutoScaler = KubernetesApplicationHelper.generateAutoScalerFormValueFromHorizontalPodAutoScaler(app.AutoScaler, res.ReplicaCount);
res.PublishedPorts = KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(app.ServiceType, app.PublishedPorts);
res.PublishedPorts = KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(app.ServiceType, app.PublishedPorts, ingresses);
res.Containers = app.Containers;
const isIngress = _.filter(res.PublishedPorts, (p) => p.IngressName).length;

View File

@@ -274,7 +274,7 @@ class KubernetesApplicationHelper {
/* #endregion */
/* #region PUBLISHED PORTS FV <> PUBLISHED PORTS */
static generatePublishedPortsFormValuesFromPublishedPorts(serviceType, publishedPorts) {
static generatePublishedPortsFormValuesFromPublishedPorts(serviceType, publishedPorts, ingress) {
const generatePort = (port, rule) => {
const res = new KubernetesApplicationPublishedPortFormValue();
res.IsNew = false;
@@ -282,6 +282,7 @@ class KubernetesApplicationHelper {
res.IngressName = rule.IngressName;
res.IngressRoute = rule.Path;
res.IngressHost = rule.Host;
res.IngressHosts = ingress.find((i) => i.Name === rule.IngressName).Hosts;
}
res.Protocol = port.Protocol;
res.ContainerPort = port.TargetPort;

View File

@@ -171,7 +171,7 @@ export class KubernetesIngressConverter {
res.spec.rules = [];
_.forEach(data.Hosts, (host) => {
if (!host.NeedsDeletion) {
res.spec.rules.push({ host: host.Host });
res.spec.rules.push({ host: host.Host || host });
}
});
} else {

View File

@@ -124,6 +124,7 @@ export function KubernetesApplicationPublishedPortFormValue() {
IngressName: undefined,
IngressRoute: undefined,
IngressHost: undefined,
IngressHosts: [],
};
}

View File

@@ -1368,7 +1368,7 @@
class="form-control"
name="ingress_hostname_{{ $index }}"
ng-model="publishedPort.IngressHost"
ng-options="host as (host | kubernetesApplicationIngressEmptyHostname) for host in ctrl.ingressHostnames"
ng-options="host as (host | kubernetesApplicationIngressEmptyHostname) for host in publishedPort.IngressHosts"
ng-change="ctrl.onChangePublishedPorts()"
ng-disabled="ctrl.disableLoadBalancerEdit() || ctrl.isEditAndNotNewPublishedPort($index)"
>

View File

@@ -321,6 +321,7 @@ class KubernetesCreateApplicationController {
const ingresses = this.filteredIngresses;
p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined;
p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined;
p.IngressHosts = ingresses && ingresses.length ? ingresses[0].Hosts : undefined;
if (this.formValues.PublishedPorts.length) {
p.Protocol = this.formValues.PublishedPorts[0].Protocol;
}
@@ -388,6 +389,7 @@ class KubernetesCreateApplicationController {
onChangePortMappingIngress(index) {
const publishedPort = this.formValues.PublishedPorts[index];
const ingress = _.find(this.filteredIngresses, { Name: publishedPort.IngressName });
publishedPort.IngressHosts = ingress.Hosts;
this.ingressHostnames = ingress.Hosts;
publishedPort.IngressHost = this.ingressHostnames.length ? this.ingressHostnames[0] : [];
this.onChangePublishedPorts();
@@ -972,7 +974,8 @@ class KubernetesCreateApplicationController {
this.resourcePools,
this.configurations,
this.persistentVolumeClaims,
this.nodesLabels
this.nodesLabels,
this.filteredIngresses
);
this.formValues.OriginalIngresses = this.filteredIngresses;
this.savedFormValues = angular.copy(this.formValues);

View File

@@ -1,12 +1,4 @@
import { KEY_REGEX, VALUE_REGEX } from '@/portainer/helpers/env-vars';
class EnvironmentVariablesSimpleModeItemController {
/* @ngInject */
constructor() {
this.KEY_REGEX = KEY_REGEX;
this.VALUE_REGEX = VALUE_REGEX;
}
onChangeName(name) {
const fieldIsInvalid = typeof name === 'undefined';
if (fieldIsInvalid) {

View File

@@ -9,7 +9,6 @@
placeholder="e.g. FOO"
ng-model="$ctrl.variable.name"
ng-disabled="$ctrl.variable.added"
ng-pattern="$ctrl.KEY_REGEX"
ng-change="$ctrl.onChangeName($ctrl.variable.name)"
required
/>
@@ -36,7 +35,6 @@
ng-model="$ctrl.variable.value"
placeholder="e.g. bar"
ng-trim="false"
ng-pattern="$ctrl.VALUE_REGEX"
name="value"
ng-change="$ctrl.onChangeValue($ctrl.variable.value)"
/>

View File

@@ -1,7 +1,6 @@
import _ from 'lodash-es';
export const KEY_REGEX = /[a-zA-Z]([-_a-zA-Z0-9]*[a-zA-Z0-9])?/.source;
export const KEY_REGEX = /(.+)/.source;
export const VALUE_REGEX = /(.*)?/.source;
const KEY_VALUE_REGEX = new RegExp(`^(${KEY_REGEX})\\s*=(${VALUE_REGEX})$`);
@@ -16,7 +15,7 @@ export function parseDotEnvFile(src) {
return parseArrayOfStrings(
_.compact(src.split(NEWLINES_REGEX))
.map((v) => v.trim())
.filter((v) => !v.startsWith('#'))
.filter((v) => !v.startsWith('#') && v !== '')
);
}
@@ -40,7 +39,7 @@ export function parseArrayOfStrings(array) {
const parsedKeyValArr = variableString.trim().match(KEY_VALUE_REGEX);
if (parsedKeyValArr != null && parsedKeyValArr.length > 4) {
return { name: parsedKeyValArr[1], value: parsedKeyValArr[3] || '' };
return { name: parsedKeyValArr[1].trim(), value: parsedKeyValArr[3].trim() || '' };
}
})
);

View File

@@ -2,7 +2,7 @@
"author": "Portainer.io",
"name": "portainer",
"homepage": "http://portainer.io",
"version": "2.6.0",
"version": "2.6.1",
"repository": {
"type": "git",
"url": "git@github.com:portainer/portainer.git"