Compare commits

..

3 Commits

Author SHA1 Message Date
yi-portainer
ea5a680167 * fix helm issues 2021-09-13 13:37:08 +12:00
yi-portainer
62f18d5c45 * remove unused tag 2021-09-13 10:52:54 +12:00
yi-portainer
6f14132bf3 * fix api version
* fix license info
* fix error response schema
* fix other typos & mistakes
2021-09-13 10:52:53 +12:00
28 changed files with 67 additions and 118 deletions

View File

@@ -28,15 +28,17 @@ 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 9443:9443 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 9000:9000 portainer/portainer`):
* Target Swarm version (if applicable):
* Browser:

View File

@@ -4,6 +4,7 @@ about: Create a bug report
title: ''
labels: bug/need-confirmation, kind/bug
assignees: ''
---
<!--
@@ -45,7 +46,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 9443:9443 portainer/portainer`):
- Command used to start Portainer (`docker run -p 9000:9000 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 <https://localhost:9443>.
Portainer can now be accessed at <http://localhost:9000>.
Find more detailed steps at <https://documentation.portainer.io/contributing/instructions/>.

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"regexp"
"strings"
@@ -59,8 +58,8 @@ func (manager *ComposeStackManager) Up(ctx context.Context, stack *portainer.Sta
return errors.Wrap(err, "failed to create env file")
}
filePaths := getStackFiles(stack)
err = manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath)
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
return manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath)
return errors.Wrap(err, "failed to deploy a stack")
}
@@ -74,9 +73,9 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
defer proxy.Close()
}
filePaths := getStackFiles(stack)
err = manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
return errors.Wrap(err, "failed to remove a stack")
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
return manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
}
// NormalizeStackName returns a new stack name with unsupported characters replaced
@@ -117,27 +116,3 @@ 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,21 +64,3 @@ 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

@@ -38,7 +38,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/portainer/docker-compose-wrapper v0.0.0-20210909083948-8be0d98451a1
github.com/portainer/libcrypto v0.0.0-20210422035235-c652195c5c3a
github.com/portainer/libhelm v0.0.0-20210913052337-365741c1c320
github.com/portainer/libhelm v0.0.0-20210906035629-b5635edd5d97
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33
github.com/robfig/cron/v3 v3.0.1
github.com/sirupsen/logrus v1.8.1

View File

@@ -206,12 +206,14 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/portainer/docker-compose-wrapper v0.0.0-20210909011155-9ff375eac059 h1:98v0k3x3ZXa09NaHP/HmSA83rcN8cuE/zTKo6xvNmoM=
github.com/portainer/docker-compose-wrapper v0.0.0-20210909011155-9ff375eac059/go.mod h1:WxDlJWZxCnicdLCPnLNEv7/gRhjeIVuCGmsv+iOPH3c=
github.com/portainer/docker-compose-wrapper v0.0.0-20210909083948-8be0d98451a1 h1:0ZGSu3Atz7RHMDsoITHV676igRfsb51mlgELGo37ELU=
github.com/portainer/docker-compose-wrapper v0.0.0-20210909083948-8be0d98451a1/go.mod h1:WxDlJWZxCnicdLCPnLNEv7/gRhjeIVuCGmsv+iOPH3c=
github.com/portainer/libcrypto v0.0.0-20210422035235-c652195c5c3a h1:qY8TbocN75n5PDl16o0uVr5MevtM5IhdwSelXEd4nFM=
github.com/portainer/libcrypto v0.0.0-20210422035235-c652195c5c3a/go.mod h1:n54EEIq+MM0NNtqLeCby8ljL+l275VpolXO0ibHegLE=
github.com/portainer/libhelm v0.0.0-20210913052337-365741c1c320 h1:wkmxoHYjWc7OB6JfSlt83mAVpnAo4/6TdL60PO4DlXk=
github.com/portainer/libhelm v0.0.0-20210913052337-365741c1c320/go.mod h1:YvYAk7krKTzB+rFwDr0jQ3sQu2BtiXK1AR0sZH7nhJA=
github.com/portainer/libhelm v0.0.0-20210906035629-b5635edd5d97 h1:ZcRVgWHTac8V7WU9TUBr73H3e5ajVFYTPjPl9TWULDA=
github.com/portainer/libhelm v0.0.0-20210906035629-b5635edd5d97/go.mod h1:YvYAk7krKTzB+rFwDr0jQ3sQu2BtiXK1AR0sZH7nhJA=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33 h1:H8HR2dHdBf8HANSkUyVw4o8+4tegGcd+zyKZ3e599II=
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33/go.mod h1:Y2TfgviWI4rT2qaOTHr+hq6MdKIE5YjgQAu7qwptTV0=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=

View File

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

View File

@@ -6,6 +6,7 @@ import (
"net/url"
"github.com/pkg/errors"
"github.com/portainer/libhelm"
"github.com/portainer/libhelm/options"
httperror "github.com/portainer/libhttp/error"
)
@@ -39,7 +40,7 @@ func (handler *Handler) helmRepoSearch(w http.ResponseWriter, r *http.Request) *
Repo: repo,
}
result, err := handler.helmPackageManager.SearchRepo(searchOpts)
result, err := libhelm.SearchRepo(searchOpts)
if err != nil {
return &httperror.HandlerError{
StatusCode: http.StatusInternalServerError,

View File

@@ -9,12 +9,13 @@ import (
"testing"
"github.com/portainer/libhelm/binary/test"
"github.com/stretchr/testify/assert"
helper "github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/assert"
)
func Test_helmRepoSearch(t *testing.T) {
helper.IntegrationTest(t)
is := assert.New(t)
helmPackageManager := test.NewMockHelmBinaryPackageManager("")

View File

@@ -1455,7 +1455,7 @@ type (
const (
// APIVersion is the version number of the Portainer API
APIVersion = "2.9.0"
APIVersion = "2.6.3"
// 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 pulled', repository);
Notifications.success('Image successfully pushed', 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="https://www.portainer.io/blog/portainer-now-with-helm-support" target="_blank">blog post</a>.
This is a first version for Helm charts, for more information see this <a href="#">blog post.</a>
</p>
</span>
</information-panel>

View File

@@ -56,7 +56,6 @@ 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.Metadata.labels && app.Metadata.labels[PodKubernetesInstanceLabel] && app.Metadata.labels[PodManagedByLabel] === 'Helm'
const helmManagedApps = applications.filter((app) =>
app.Pods.flatMap((pod) => pod.Labels).some((label) => label && label[PodKubernetesInstanceLabel] && label[PodManagedByLabel] === 'Helm')
);
// groups the helm managed applications by helm release name
@@ -467,12 +467,15 @@ class KubernetesApplicationHelper {
const namespacedHelmReleases = {};
helmManagedApps.forEach((app) => {
const namespace = app.ResourcePool;
const instanceLabel = app.Metadata.labels[PodKubernetesInstanceLabel];
if (namespacedHelmReleases[namespace]) {
namespacedHelmReleases[namespace][instanceLabel] = [...(namespacedHelmReleases[namespace][instanceLabel] || []), app];
} else {
namespacedHelmReleases[namespace] = { [instanceLabel]: [app] };
}
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] };
}
});
});
// `helmAppsEntriesList` object structure:
@@ -508,25 +511,5 @@ 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,7 +16,6 @@ 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 from 'Kubernetes/helpers/application';
import KubernetesApplicationHelper, { PodKubernetesInstanceLabel } from 'Kubernetes/helpers/application';
import KubernetesConfigurationHelper from 'Kubernetes/helpers/configurationHelper';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
@@ -109,9 +109,18 @@ class KubernetesApplicationsController {
try {
const [applications, configurations] = await Promise.all([this.KubernetesApplicationService.get(), this.KubernetesConfigurationService.get()]);
const configuredApplications = KubernetesConfigurationHelper.getApplicationConfigurations(applications, configurations);
const { helmApplications, nonHelmApplications } = KubernetesApplicationHelper.getNestedApplications(configuredApplications);
const helmApplications = KubernetesApplicationHelper.getHelmApplications(configuredApplications);
this.state.applications = [...helmApplications, ...nonHelmApplications];
// 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.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,7 +65,6 @@
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:9443 or portainer.mydomain.com"
placeholder="e.g. 10.0.0.10:9000 or portainer.mydomain.com"
required
data-cy="endpointCreate-portainerServerUrlInput"
/>

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,6 @@ 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,7 +19,6 @@ 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.9.0",
"version": "2.6.3",
"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": "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 ./",
"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 ./",
"clean": "grunt clean:all",
"start": "grunt start",
"start:clean": "grunt clean:all && grunt start",

View File

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

View File

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