Compare commits

..

12 Commits

Author SHA1 Message Date
waysonwei
f0b58f57e5 fix authentication toggle on by default - set to off 2021-09-19 10:24:46 +12:00
Chaim Lev-Ari
689c2193c0 fix(customtemplate): edit custom template [EE-1691] (#5632) 2021-09-17 09:24:01 +03:00
cong meng
9121e8e69c fix(UI) EE-1657 Fix the agent version number in the UI (#5619)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-09-17 17:22:21 +12:00
zees-dev
53a2205f06 docker image pull toast fix (#5644) 2021-09-17 14:48:37 +12:00
zees-dev
a098e24cca using new app metadata property to distinguish helm apps (#5624) 2021-09-16 16:09:33 +12:00
zees-dev
5d8c23e3a6 helm templates blog post link fix (#5625) 2021-09-16 10:00:18 +12:00
zees-dev
52f9320952 fix webpack dev server (#5630) 2021-09-15 17:54:44 +12:00
zees-dev
e3f7561ced portainer version updates (#5612) 2021-09-14 10:20:26 +12:00
zees-dev
c7760b7d48 - setting port 9443 as primary (#5610)
- updated markdown files
- updated dockerfiles
- updated test files
- updated webpack
2021-09-14 09:46:59 +12:00
Yi Chen
1633eceed5 fix(swagger) Fix openapi issues (#5123)
* * fix api version
* fix license info
* fix error response schema
* fix other typos & mistakes

* * remove unused tag

* * fix helm issues
2021-09-13 15:42:53 +12:00
Matt Hook
e437a3b570 fix(docs): fix yarn build docs broken for helm (#5606)
* fix(docs): fix yarn build docs broken for helm

* ensure correct version of swag is used

* remove line that prevented swag from updating
2021-09-13 14:14:07 +12:00
Dmitry Salakhov
396a921b12 fix(stacks): allow root based compose file paths (#5564) 2021-09-13 11:11:22 +12:00
25 changed files with 114 additions and 59 deletions

View File

@@ -28,17 +28,15 @@ Briefly describe the problem you are having in a few paragraphs.
**Steps to reproduce the issue:**
1.
2.
3.
1. 2. 3.
Any other info e.g. Why do you consider this to be a bug? What did you expect to happen instead?
**Technical details:**
* Portainer version:
* Target Docker version (the host/cluster you manage):
* Platform (windows/linux):
* Command used to start Portainer (`docker run -p 9000:9000 portainer/portainer`):
* Target Swarm version (if applicable):
* Browser:
- Portainer version:
- Target Docker version (the host/cluster you manage):
- Platform (windows/linux):
- Command used to start Portainer (`docker run -p 9443:9443 portainer/portainer`):
- Target Swarm version (if applicable):
- Browser:

View File

@@ -4,7 +4,6 @@ about: Create a bug report
title: ''
labels: bug/need-confirmation, kind/bug
assignees: ''
---
<!--
@@ -46,7 +45,7 @@ You can see how [here](https://documentation.portainer.io/archive/1.23.2/faq/#ho
- Docker version (managed by Portainer):
- Kubernetes version (managed by Portainer):
- Platform (windows/linux):
- Command used to start Portainer (`docker run -p 9000:9000 portainer/portainer`):
- Command used to start Portainer (`docker run -p 9443:9443 portainer/portainer`):
- Browser:
- Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commerical setup.
- Have you reviewed our technical documentation and knowledge base? Yes/No

View File

@@ -91,7 +91,7 @@ Then build and run the project:
$ yarn start
```
Portainer can now be accessed at <http://localhost:9000>.
Portainer can now be accessed at <https://localhost:9443>.
Find more detailed steps at <https://documentation.portainer.io/contributing/instructions/>.

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
@@ -58,8 +59,8 @@ func (manager *ComposeStackManager) Up(ctx context.Context, stack *portainer.Sta
return errors.Wrap(err, "failed to create env file")
}
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
return manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath)
filePaths := getStackFiles(stack)
err = manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath)
return errors.Wrap(err, "failed to deploy a stack")
}
@@ -73,9 +74,9 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
defer proxy.Close()
}
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
return manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
filePaths := getStackFiles(stack)
err = manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
return errors.Wrap(err, "failed to remove a stack")
}
// NormalizeStackName returns a new stack name with unsupported characters replaced
@@ -116,3 +117,27 @@ func createEnvFile(stack *portainer.Stack) (string, error) {
return "stack.env", nil
}
// getStackFiles returns list of stack's confile file paths.
// items in the list would be sanitized according to following criterias:
// 1. no empty paths
// 2. no "../xxx" paths that are trying to escape stack folder
// 3. no dir paths
// 4. root paths would be made relative
func getStackFiles(stack *portainer.Stack) []string {
paths := make([]string, 0, len(stack.AdditionalFiles)+1)
for _, p := range append([]string{stack.EntryPoint}, stack.AdditionalFiles...) {
if strings.HasPrefix(p, "/") {
p = `.` + p
}
if p == `` || p == `.` || strings.HasPrefix(p, `..`) || strings.HasSuffix(p, string(filepath.Separator)) {
continue
}
paths = append(paths, p)
}
return paths
}

View File

@@ -64,3 +64,21 @@ func Test_createEnvFile(t *testing.T) {
})
}
}
func Test_getStackFiles(t *testing.T) {
stack := &portainer.Stack{
EntryPoint: "./file", // picks entry point
AdditionalFiles: []string{
``, // ignores empty string
`.`, // ignores .
`..`, // ignores ..
`./dir/`, // ignrores paths that end with trailing /
`/with-root-prefix`, // replaces "root" based paths with relative
`./relative`, // keeps relative paths
`../escape`, // prevents dir escape
},
}
filePaths := getStackFiles(stack)
assert.ElementsMatch(t, filePaths, []string{`./file`, `./with-root-prefix`, `./relative`})
}

View File

@@ -72,7 +72,7 @@ type Handler struct {
}
// @title PortainerCE API
// @version 2.6.3
// @version 2.9.0
// @description.markdown api-description.md
// @termsOfService

View File

@@ -1455,7 +1455,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.6.3"
APIVersion = "2.9.0"
// DBVersion is the version number of the Portainer database
DBVersion = 32
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax

View File

@@ -104,7 +104,7 @@ angular.module('portainer.docker').controller('ImageController', [
try {
const registryModel = await RegistryService.retrievePorRegistryModelFromRepository(repository, endpoint.Id);
await ImageService.pullImage(registryModel);
Notifications.success('Image successfully pushed', repository);
Notifications.success('Image successfully pulled', repository);
} catch (err) {
Notifications.error('Failure', err, 'Unable to push image to repository');
} finally {

View File

@@ -11,7 +11,7 @@
<span class="small text-muted">
<p>
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
This is a first version for Helm charts, for more information see this <a href="#">blog post.</a>
This is a first version for Helm charts, for more information see this <a href="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank">blog post</a>.
</p>
</span>
</information-panel>

View File

@@ -56,6 +56,7 @@ class KubernetesApplicationConverter {
const containers = data.spec.template ? _.without(_.concat(data.spec.template.spec.containers, data.spec.template.spec.initContainers), undefined) : data.spec.containers;
res.Id = data.metadata.uid;
res.Name = data.metadata.name;
res.Metadata = data.metadata;
if (data.metadata.labels) {
const { labels } = data.metadata;

View File

@@ -449,8 +449,8 @@ class KubernetesApplicationHelper {
// filter out all the applications that are managed by helm
// to identify the helm managed applications, we need to check if the applications pod labels include
// `app.kubernetes.io/instance` and `app.kubernetes.io/managed-by` = `helm`
const helmManagedApps = applications.filter((app) =>
app.Pods.flatMap((pod) => pod.Labels).some((label) => label && label[PodKubernetesInstanceLabel] && label[PodManagedByLabel] === 'Helm')
const helmManagedApps = applications.filter(
(app) => app.Metadata.labels && app.Metadata.labels[PodKubernetesInstanceLabel] && app.Metadata.labels[PodManagedByLabel] === 'Helm'
);
// groups the helm managed applications by helm release name
@@ -467,15 +467,12 @@ class KubernetesApplicationHelper {
const namespacedHelmReleases = {};
helmManagedApps.forEach((app) => {
const namespace = app.ResourcePool;
const labels = app.Pods.filter((p) => p.Labels).map((p) => p.Labels[PodKubernetesInstanceLabel]);
const uniqueLabels = [...new Set(labels)];
uniqueLabels.forEach((instanceStr) => {
if (namespacedHelmReleases[namespace]) {
namespacedHelmReleases[namespace][instanceStr] = [...(namespacedHelmReleases[namespace][instanceStr] || []), app];
} else {
namespacedHelmReleases[namespace] = { [instanceStr]: [app] };
}
});
const instanceLabel = app.Metadata.labels[PodKubernetesInstanceLabel];
if (namespacedHelmReleases[namespace]) {
namespacedHelmReleases[namespace][instanceLabel] = [...(namespacedHelmReleases[namespace][instanceLabel] || []), app];
} else {
namespacedHelmReleases[namespace] = { [instanceLabel]: [app] };
}
});
// `helmAppsEntriesList` object structure:
@@ -511,5 +508,25 @@ class KubernetesApplicationHelper {
return helmAppsList;
}
/**
* Get nested applications -
* @param {KubernetesApplication[]} applications Application list
* @returns {Object} { helmApplications: [app1, app2, ...], nonHelmApplications: [app3, app4, ...] }
*/
static getNestedApplications(applications) {
const helmApplications = KubernetesApplicationHelper.getHelmApplications(applications);
// filter out helm managed applications
const helmAppNames = [...new Set(helmApplications.map((hma) => hma.Name))]; // distinct helm app names
const nonHelmApplications = applications.filter((app) => {
if (app.Metadata.labels) {
return !helmAppNames.includes(app.Metadata.labels[PodKubernetesInstanceLabel]);
}
return true;
});
return { helmApplications, nonHelmApplications };
}
}
export default KubernetesApplicationHelper;

View File

@@ -16,6 +16,7 @@ const _KubernetesApplication = Object.freeze({
CreationDate: 0,
Pods: [],
Containers: [],
Metadata: {},
Limits: {},
ServiceType: '',
ServiceId: '',

View File

@@ -1,7 +1,7 @@
import angular from 'angular';
import _ from 'lodash-es';
import KubernetesStackHelper from 'Kubernetes/helpers/stackHelper';
import KubernetesApplicationHelper, { PodKubernetesInstanceLabel } from 'Kubernetes/helpers/application';
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
@@ -109,18 +109,9 @@ class KubernetesApplicationsController {
try {
const [applications, configurations] = await Promise.all([this.KubernetesApplicationService.get(), this.KubernetesConfigurationService.get()]);
const configuredApplications = KubernetesConfigurationHelper.getApplicationConfigurations(applications, configurations);
const helmApplications = KubernetesApplicationHelper.getHelmApplications(configuredApplications);
const { helmApplications, nonHelmApplications } = KubernetesApplicationHelper.getNestedApplications(configuredApplications);
// filter out multi-chart helm managed applications
const helmAppNames = [...new Set(helmApplications.map((hma) => hma.Name))]; // distinct helm app names
const nonHelmApps = configuredApplications.filter(
(app) =>
!app.Pods.flatMap((pod) => pod.Labels) // flatten pod labels
.filter((label) => label) // filter out empty labels
.some((label) => helmAppNames.includes(label[PodKubernetesInstanceLabel])) // check if label key is in helmAppNames
);
this.state.applications = [...nonHelmApps, ...helmApplications];
this.state.applications = [...helmApplications, ...nonHelmApplications];
this.state.stacks = KubernetesStackHelper.stacksFromApplications(applications);
this.state.ports = KubernetesApplicationHelper.portMappingsFromApplications(applications);
} catch (err) {

View File

@@ -30,7 +30,7 @@
>
<template-item-actions>
<div ng-if="$ctrl.isEditAllowed(template)" style="display: flex;">
<a ui-state="$ctrl.editPath" ui-state-params="({id: template.Id})" ng-click="$event.stopPropagation();" class="btn btn-primary btn-xs" style="margin-right: 10px;">
<a ui-state="$ctrl.editPath" ui-state-params="{id: template.Id}" ng-click="$event.stopPropagation();" class="btn btn-primary btn-xs" style="margin-right: 10px;">
Edit
</a>
<button class="btn btn-danger btn-xs" ng-click="$ctrl.onDeleteClick(template.Id); $event.stopPropagation();">Delete</button>

View File

@@ -65,6 +65,7 @@
templates="$ctrl.templates"
table-key="customTemplates"
create-path="docker.templates.custom.new"
edit-path="docker.templates.custom.edit"
is-edit-allowed="$ctrl.isEditAllowed"
on-select-click="($ctrl.selectTemplate)"
on-delete-click="($ctrl.confirmDelete)"

View File

@@ -130,8 +130,8 @@
<div class="form-group">
<span class="col-sm-12 text-muted small">
<p>
Allows you to create an environment that can be registered with an Edge agent. The Edge agent will initiate the communications with the Portainer instance. All the
required information on how to connect an Edge agent to this environment will be available after environment creation.
Allows you to create an environment that can be registered with an Edge agent. The Edge agent will initiate the communications with the Portainer instance. All
the required information on how to connect an Edge agent to this environment will be available after environment creation.
</p>
<p> You can read more about the Edge agent in the userguide available <a href="https://downloads.portainer.io/edge_agent_guide.pdf">here.</a> </p>
</span>
@@ -309,7 +309,7 @@
class="form-control"
name="endpoint_url"
ng-model="formValues.URL"
placeholder="e.g. 10.0.0.10:9000 or portainer.mydomain.com"
placeholder="e.g. 10.0.0.10:9443 or portainer.mydomain.com"
required
data-cy="endpointCreate-portainerServerUrlInput"
/>

View File

@@ -283,7 +283,7 @@ docker run -d \\
-e CAP_HOST_MANAGEMENT=1 \\
-e EDGE_INSECURE_POLL=${allowSelfSignedCerts ? 1 : 0} \\
--name portainer_edge_agent \\
portainer/agent:2.4.0`;
portainer/agent`;
}
function buildWindowsStandaloneCommand(edgeId, edgeKey, allowSelfSignedCerts) {

View File

@@ -34,7 +34,7 @@ angular
StackFile: null,
RepositoryURL: '',
RepositoryReferenceName: '',
RepositoryAuthentication: true,
RepositoryAuthentication: false,
RepositoryUsername: '',
RepositoryPassword: '',
Env: [],

View File

@@ -6,6 +6,7 @@ VOLUME /data
WORKDIR /
EXPOSE 9000
EXPOSE 9443
EXPOSE 8000
ENTRYPOINT ["/portainer"]

View File

@@ -6,6 +6,7 @@ VOLUME /data
WORKDIR /
EXPOSE 9000
EXPOSE 9443
EXPOSE 8000
ENTRYPOINT ["/portainer"]

View File

@@ -2,6 +2,7 @@ FROM ubuntu:20.04
# Expose port for the Portainer UI and Edge server
EXPOSE 9000
EXPOSE 9443
EXPOSE 8000
WORKDIR /src/portainer

View File

@@ -19,6 +19,7 @@ USER ContainerAdministrator
COPY dist /
EXPOSE 9000
EXPOSE 9443
EXPOSE 8000
ENTRYPOINT ["/portainer.exe"]

View File

@@ -2,7 +2,7 @@
"author": "Portainer.io",
"name": "portainer",
"homepage": "http://portainer.io",
"version": "2.6.3",
"version": "2.9.0",
"repository": {
"type": "git",
"url": "git@github.com:portainer/portainer.git"
@@ -20,8 +20,8 @@
"build": "grunt clean:all && grunt build",
"build:server": "grunt clean:server && grunt build:server",
"build:client": "grunt clean:client && grunt build:client",
"prebuild:docs": "(! command -v swag &> /dev/null) && go get -u github.com/swaggo/swag/cmd/swag",
"build:docs": "cd api && swag init -g ./http/handler/handler.go --markdownFiles ./",
"prebuild:docs": "go install github.com/swaggo/swag/cmd/swag@v1.7.1",
"build:docs": "cd api && swag init -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 --markdownFiles ./",
"clean": "grunt clean:all",
"start": "grunt start",
"start:clean": "grunt clean:all && grunt start",

View File

@@ -1,6 +1,6 @@
{
"projectId": "cgz5j5",
"baseUrl": "http://localhost:9000",
"baseUrl": "https://localhost:9443",
"video": false,
"experimentalNetworkStubbing": true
}

View File

@@ -29,8 +29,8 @@ describe('startContainerController', function () {
Ports: [
{
IP: '0.0.0.0',
PrivatePort: 9000,
PublicPort: 9000,
PrivatePort: 9443,
PublicPort: 9443,
Type: 'tcp',
},
],
@@ -46,11 +46,11 @@ describe('startContainerController', function () {
var expectedBody = {
name: 'container-name',
ExposedPorts: {
'9000/tcp': {},
'9443/tcp': {},
},
HostConfig: {
PortBindings: {
'9000/tcp': [
'9443/tcp': [
{
HostPort: '9999',
HostIp: '10.20.10.15',
@@ -76,7 +76,7 @@ describe('startContainerController', function () {
{
ip: '10.20.10.15',
extPort: '9999',
intPort: '9000',
intPort: '9443',
},
];