Compare commits

...

55 Commits

Author SHA1 Message Date
Matt Hook
b83f660547 add newline at end of file 2021-09-24 16:53:46 +12:00
Matt Hook
9460ed8e47 Add new tool-versions json file to help devs choose the right versions. Allows querying from doc sites and CI build tools 2021-09-24 16:45:06 +12:00
testA113
5ad3cacefd Xt 321 automate k8s smoke test data cy attributes (#5734)
* added data-cy attributes for robust ui test automation
2021-09-24 13:00:55 +12:00
Richard Wei
6ac9c4367e show ip address of pod (#5613) 2021-09-23 14:34:24 +12:00
Simon Meng
8aa03bb81b Merge remote-tracking branch 'origin/release/2.9' into develop
# Conflicts:
#	app/kubernetes/views/applications/create/createApplication.html
#	app/kubernetes/views/configurations/create/createConfiguration.html
2021-09-23 12:09:13 +12:00
fhanportainer
d14c7b0309 fix(name): fixed namespace creation issue when a registry attached. (#5646)
* fix(name): fixed namespace creation issue when a registry attached.

* fix(name): moved copy object to upper level of the function
2021-09-23 09:13:25 +12:00
fhanportainer
cbeb13636c fix(name): fixed namespace creation issue when a registry attached. (#5675) 2021-09-23 09:13:19 +12:00
Hui
a6138dd5a3 fix(migration): add debug logging for volume migration (#5700)
* add debug logging

* Update api/bolt/migrator/migrate_dbversion31.go

* log resource control delete

Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
2021-09-23 09:12:39 +12:00
Hui
5752e74be6 add debug log (#5702) 2021-09-23 09:12:35 +12:00
Matt Hook
cb37497444 doc(readme) fix slack link (#5701)
* doc(readme) fix slack link

* Use shared invite link underneath
2021-09-23 08:59:29 +12:00
Chaim Lev-Ari
0b64250647 Revert "refactor(settings): backport auth views (#5672)" (#5704)
This reverts commit 45af1f3d8b.
2021-09-22 10:17:22 +03:00
Chaim Lev-Ari
45af1f3d8b refactor(settings): backport auth views (#5672) 2021-09-22 10:11:53 +03:00
Chaim Lev-Ari
fc52830c7d fix(customtemplates): show correct type (#5669) 2021-09-22 08:00:16 +03:00
Chaim Lev-Ari
4890f50443 fix(customtemplates): show correct type (#5668) 2021-09-22 08:00:11 +03:00
Chaim Lev-Ari
6d510c4f30 fix(k8s/apps): edit url deployed app (#5652) 2021-09-22 07:59:32 +03:00
Chaim Lev-Ari
cad530ec04 fix(k8s/apps): edit url deployed app (#5653) 2021-09-22 07:59:28 +03:00
Chaim Lev-Ari
e63732484a fix(registries): put anon docker at top (#5671) 2021-09-22 07:55:28 +03:00
Chaim Lev-Ari
ec3233fb09 fix(registries): put anon docker at top (#5670) 2021-09-22 07:55:25 +03:00
Richard Wei
bcdc342cbd fix(k8s): fixerror handling for namespace restricted user EE-1703 (#5693)
* fix error handler for namespace when user have no namespace access
2021-09-22 16:01:42 +12:00
Richard Wei
e1f725d01a fix(k8s): fix error handling for namespace restricted user EE-1703 (#5692)
* fix error handler for user has no namespace access
2021-09-22 16:01:28 +12:00
Richard Wei
b876f2d17d fix danger button hover color (#5605) 2021-09-22 15:17:52 +12:00
mariyam-portainer
b0ec67826c Rename portainerbusiness.yml to config.yml 2021-09-22 15:07:23 +12:00
mariyam-portainer
b89d828878 Rename Portainer Business to portainerbusiness.yml 2021-09-22 15:06:25 +12:00
mariyam-portainer
e59df8134d Create Portainer Business 2021-09-22 15:04:05 +12:00
zees-dev
092d217985 table settings propagated through nested tables (#5699) 2021-09-22 13:42:13 +12:00
zees-dev
ad94162019 table settings propagated through nested tables (#5698) 2021-09-22 13:42:04 +12:00
Richard Wei
0efbf5bbf3 rename endpoint to environment in wizard breadcrumb header (#5696) 2021-09-22 13:18:52 +12:00
Richard Wei
c26ba23c53 rename endpoint to environment in wizard breadcrumb header (#5697) 2021-09-22 13:18:42 +12:00
Richard Wei
69096f664d fit(ui): use new portainer in login page and license page EE-1637 (#5604)
* use new portainer in login page and license page
2021-09-22 11:16:12 +12:00
Richard Wei
48c762c98b fix(notification): fix error in kube application stack name with space EE-1726 (#5691)
* fix error in kube application stack name with space
2021-09-21 20:58:23 +12:00
Richard Wei
488d86d200 fix(notification): fix error in kube application stack name with space EE-1726 (#5690)
* fix error in kube application stack name with space
2021-09-21 20:58:08 +12:00
Richard Wei
f10e0e4124 fix application table background not working with dark mode (#5617) 2021-09-21 19:29:25 +12:00
cong meng
5316cca3de fix(edge) EE-1733 cant edit edge groups (#5689)
* fix(edge) EE-1733 cant edit edge groups

* fix(edge) EE-1733 correct json names of a few edge objects

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-09-21 17:41:27 +12:00
cong meng
4267304e50 fix(edge) EE-1733 cant edit edge groups (#5687)
* fix(edge) EE-1733 cant edit edge groups

* fix(edge) EE-1733 correct json names of a few edge objects

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-09-21 17:41:14 +12:00
Richard Wei
deecbadce1 fix(k8s):fix difficulties selecting mixed protocols when creating k8s application EE-1073 (#5591)
* fix difficulties selecting mixed protocols when creating k8s application
2021-09-21 16:20:22 +12:00
fhanportainer
ecc9813750 fix(stack): fixed issue cannot deploy git stack without username. (#5680) 2021-09-21 13:42:04 +12:00
fhanportainer
24f11902b2 fix(stack): fixed issue cannot deploy git stack without username. (#5681) 2021-09-21 13:42:01 +12:00
cong meng
33118babdd fix(k8s) keep tunnel alive for websocket connection EE-1690 (#5677)
* fix(k8s) EE-1690 keep tunnel alive for websocket connection

* fix(k8s) EE-1690 fix comment

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-09-21 13:12:37 +12:00
cong meng
2aec348814 fix(k8s) keep tunnel alive for websocket connection EE-1690 (#5679)
* fix(k8s) EE-1690 keep tunnel alive for websocket connection

* fix(k8s) EE-1690 fix comment

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-09-21 13:12:31 +12:00
Richard Wei
4d63459d67 fix edge heartbeat show red when use search filter (#5682) 2021-09-21 10:19:22 +12:00
Richard Wei
483559af09 fix edge heartbeat turn red when use search filter (#5683) 2021-09-21 10:19:18 +12:00
Richard Wei
1796545d2e fix authentication toggle on by default - set to off (#5666) 2021-09-20 22:36:22 +12:00
Richard Wei
a50795063c fix git stack authentication on by default - set to off (#5667) 2021-09-20 22:36:16 +12:00
Richard Wei
7c9f7a2a8b fix error description on stats for non-admin users (#5665) 2021-09-20 15:41:46 +12:00
Richard Wei
af8065e8c2 fix error description on stats for non-admin user (#5664) 2021-09-20 15:41:40 +12:00
Richard Wei
dc769b4c4d fix icon not displayed when template created via upload file (#5658) 2021-09-20 12:20:38 +12:00
Richard Wei
dd808bb7bd fix(swagger): fix swagger api docs endpoint(s) rename to environment(s) EE-1661 (#5629)
* fix swagger api docs endpoint(s) rename to environment(s)
2021-09-20 12:14:22 +12:00
zees-dev
d911c50f1b fixed k8s app edit config dropdown default (#5651) 2021-09-20 11:08:18 +12:00
zees-dev
f6f31b8872 fixed docker image pull text on error scenario (#5656) 2021-09-20 01:42:55 +12:00
Chaim Lev-Ari
1f4a7b32e3 fix(customtemplate): edit custom template [EE-1691] (#5633) 2021-09-17 09:24:23 +03:00
zees-dev
a781021072 docker image pull toast fix (#5644) 2021-09-17 18:22:57 +12:00
Matt Hook
9492e30dc2 feat(helm/tests): update libhelm with new search mock EE-1599 (#5615)
* feat(helm/tests) add repo search and update libhelm with new mock EE-1599

* also enable repo search test
2021-09-16 16:56:46 +12:00
zees-dev
d2cbdf935a using new app metadata property to distinguish helm apps (#5627) 2021-09-16 16:09:39 +12:00
zees-dev
05efac44f6 helm templates blog post link fix (#5626) 2021-09-16 10:00:55 +12:00
zees-dev
555c9f238f fix webpack dev server (#5631) 2021-09-15 17:55:06 +12:00
108 changed files with 1987 additions and 1584 deletions

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: Portainer Business
url: https://www.portainer.io/portainerbusiness
about: Would you and your co-workers benefit from our enterprise edition which provides functionality to deploy Portainer at scale?

View File

@@ -44,7 +44,7 @@ Portainer CE is an open source project and is supported by the community. You ca
Learn more about Portainers community support channels [here.](https://www.portainer.io/help_about)
- Issues: https://github.com/portainer/portainer/issues
- Slack (chat): https://portainer.io/slack/
- Slack (chat): [https://portainer.slack.com/](https://join.slack.com/t/portainer/shared_invite/zt-txh3ljab-52QHTyjCqbe5RibC2lcjKA)
You can join the Portainer Community by visiting community.portainer.io. This will give you advance notice of events, content and other related Portainer content.
@@ -59,7 +59,7 @@ You can join the Portainer Community by visiting community.portainer.io. This wi
## WORK FOR US
If you are a developer, and our code in this repo makes sense to you, we would love to hear from you. We are always on the hunt for awesome devs, either freelance or employed. Drop us a line to info@portainer.io with your details and we will be in touch.
If you are a developer, and our code in this repo makes sense to you, we would love to hear from you. We are always on the hunt for awesome devs, either freelance or employed. Drop us a line to info@portainer.io with your details and we will be in touch.
## Privacy

View File

@@ -2,6 +2,7 @@ package migrator
import (
"fmt"
"log"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/bolt/errors"
@@ -167,6 +168,7 @@ func (m *Migrator) updateVolumeResourceControlToDB32() error {
totalSnapshots := len(endpoint.Snapshots)
if totalSnapshots == 0 {
log.Println("[DEBUG] [volume migration] [message: no snapshot found]")
continue
}
@@ -179,6 +181,7 @@ func (m *Migrator) updateVolumeResourceControlToDB32() error {
if volumesData, done := snapshot.SnapshotRaw.Volumes.(map[string]interface{}); done {
if volumesData["Volumes"] == nil {
log.Println("[DEBUG] [volume migration] [message: no volume data found]")
continue
}
@@ -199,7 +202,7 @@ func (m *Migrator) updateVolumeResourceControlToDB32() error {
if err != nil {
return fmt.Errorf("failed deleting resource control %d: %w", resourceControl.ID, err)
}
log.Printf("[DEBUG] [volume migration] [message: legacy resource control(%s) has been deleted]", resourceControl.ResourceID)
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"net/http"
"strconv"
"time"
@@ -42,6 +43,55 @@ func NewService(dataStore portainer.DataStore, shutdownCtx context.Context) *Ser
}
}
// pingAgent ping the given agent so that the agent can keep the tunnel alive
func (service *Service) pingAgent(endpointID portainer.EndpointID) error{
tunnel := service.GetTunnelDetails(endpointID)
requestURL := fmt.Sprintf("http://127.0.0.1:%d/ping", tunnel.Port)
req, err := http.NewRequest(http.MethodHead, requestURL, nil)
if err != nil {
return err
}
httpClient := &http.Client{
Timeout: 3 * time.Second,
}
_, err = httpClient.Do(req)
if err != nil {
return err
}
return nil
}
// KeepTunnelAlive keeps the tunnel of the given environment for maxAlive duration, or until ctx is done
func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx context.Context, maxAlive time.Duration) {
go func() {
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: start for %.0f minutes]\n", endpointID, maxAlive.Minutes())
maxAliveTicker := time.NewTicker(maxAlive)
defer maxAliveTicker.Stop()
pingTicker := time.NewTicker(tunnelCleanupInterval)
defer pingTicker.Stop()
for {
select {
case <-pingTicker.C:
service.SetTunnelStatusToActive(endpointID)
err := service.pingAgent(endpointID)
if err != nil {
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [warning: ping agent err=%s]\n", endpointID, err)
}
case <-maxAliveTicker.C:
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as %.0f minutes timeout]\n", endpointID, maxAlive.Minutes())
return
case <-ctx.Done():
err := ctx.Err()
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as err=%s]\n", endpointID, err)
return
}
}
}()
}
// StartTunnelServer starts a tunnel server on the specified addr and port.
// It uses a seed to generate a new private/public key pair. If the seed cannot
// be found inside the database, it will generate a new one randomly and persist it.

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-20210906035629-b5635edd5d97
github.com/portainer/libhelm v0.0.0-20210913052337-365741c1c320
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,14 +206,12 @@ 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-20210906035629-b5635edd5d97 h1:ZcRVgWHTac8V7WU9TUBr73H3e5ajVFYTPjPl9TWULDA=
github.com/portainer/libhelm v0.0.0-20210906035629-b5635edd5d97/go.mod h1:YvYAk7krKTzB+rFwDr0jQ3sQu2BtiXK1AR0sZH7nhJA=
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/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

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

View File

@@ -9,13 +9,12 @@ 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

@@ -35,6 +35,9 @@ func (handler *Handler) proxyEdgeAgentWebsocketRequest(w http.ResponseWriter, r
}
handler.ReverseTunnelService.SetTunnelStatusToActive(params.endpoint.ID)
handler.ReverseTunnelService.KeepTunnelAlive(params.endpoint.ID, r.Context(), portainer.WebSocketKeepAlive)
proxy.ServeHTTP(w, r)
return nil

View File

@@ -21,9 +21,7 @@ const shellPodImage = "portainer/kubectl-shell"
// - The shell pod will be automatically removed after a specified max life (prevent zombie pods)
// - The shell pod will be automatically removed if request is cancelled (or client closes websocket connection)
func (kcl *KubeClient) CreateUserShellPod(ctx context.Context, serviceAccountName string) (*portainer.KubernetesShellPod, error) {
// Schedule the pod for automatic removal
maxPodKeepAlive := 1 * time.Hour
maxPodKeepAliveSecondsStr := fmt.Sprintf("%d", int(maxPodKeepAlive.Seconds()))
maxPodKeepAliveSecondsStr := fmt.Sprintf("%d", int(portainer.WebSocketKeepAlive.Seconds()))
podPrefix := userShellPodPrefix(serviceAccountName)
@@ -81,7 +79,7 @@ func (kcl *KubeClient) CreateUserShellPod(ctx context.Context, serviceAccountNam
// Handle pod lifecycle/cleanup - terminate pod after maxPodKeepAlive or upon request (long-lived) cancellation
go func() {
select {
case <-time.After(maxPodKeepAlive):
case <-time.After(portainer.WebSocketKeepAlive):
log.Println("[DEBUG] [internal,kubernetes/pod] [message: pod removal schedule duration exceeded]")
kcl.cli.CoreV1().Pods(portainerNamespace).Delete(shellPod.Name, nil)
case <-ctx.Done():

View File

@@ -148,7 +148,7 @@ type (
Name string `json:"Name"`
Dynamic bool `json:"Dynamic"`
TagIDs []TagID `json:"TagIds"`
Endpoints []EndpointID `json:"Environments"`
Endpoints []EndpointID `json:"Endpoints"`
PartialMatch bool `json:"PartialMatch"`
}
@@ -161,7 +161,7 @@ type (
ID EdgeJobID `json:"Id" example:"1"`
Created int64 `json:"Created"`
CronExpression string `json:"CronExpression"`
Endpoints map[EndpointID]EdgeJobEndpointMeta `json:"Environments"`
Endpoints map[EndpointID]EdgeJobEndpointMeta `json:"Endpoints"`
Name string `json:"Name"`
ScriptPath string `json:"ScriptPath"`
Recurring bool `json:"Recurring"`
@@ -188,7 +188,7 @@ type (
CronExpression string `json:"CronExpression"`
Script string `json:"Script"`
Version int `json:"Version"`
Endpoints []EndpointID `json:"Environments"`
Endpoints []EndpointID `json:"Endpoints"`
}
//EdgeStack represents an edge stack
@@ -1324,6 +1324,7 @@ type (
SetTunnelStatusToActive(endpointID EndpointID)
SetTunnelStatusToRequired(endpointID EndpointID) error
SetTunnelStatusToIdle(endpointID EndpointID)
KeepTunnelAlive(endpointID EndpointID, ctx context.Context, maxKeepAlive time.Duration)
GetTunnelDetails(endpointID EndpointID) *TunnelDetails
AddEdgeJob(endpointID EndpointID, edgeJob *EdgeJob)
RemoveEdgeJob(edgeJobID EdgeJobID)
@@ -1493,6 +1494,8 @@ const (
DefaultUserSessionTimeout = "8h"
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
DefaultKubeconfigExpiry = "0"
// WebSocketKeepAlive web socket keep alive for edge environments
WebSocketKeepAlive = 1 * time.Hour
)
const (

View File

@@ -150,6 +150,8 @@ html {
--bg-btn-focus: var(--grey-59);
--bg-boxselector-disabled-color: var(--white-color);
--bg-small-select-color: var(--white-color);
--bg-app-datatable-thead: var(--grey-23);
--bg-app-datatable-tbody: var(--grey-24);
--text-main-color: var(--grey-7);
--text-body-color: var(--grey-6);
@@ -318,6 +320,8 @@ html {
--bg-btn-focus: var(--grey-3);
--bg-boxselector-disabled-color: var(--grey-54);
--bg-small-select-color: var(--grey-2);
--bg-app-datatable-thead: var(--grey-1);
--bg-app-datatable-tbody: var(--grey-1);
--text-main-color: var(--white-color);
--text-body-color: var(--white-color);
@@ -485,6 +489,8 @@ html {
--bg-boxselector-color: var(--black-color);
--bg-boxselector-disabled-color: var(--black-color);
--bg-small-select-color: var(--black-color);
--bg-app-datatable-thead: var(--black-color);
--bg-app-datatable-tbody: var(--black-color);
--text-main-color: var(--white-color);
--text-body-color: var(--white-color);

View File

@@ -396,4 +396,8 @@ input:-webkit-autofill {
.btn-primary:hover {
color: var(--white-color) !important;
}
.btn-danger:hover {
color: var(--white-color);
}
/* Overide Vendor CSS */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1006 B

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

After

Width:  |  Height:  |  Size: 772 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 940 300" style="enable-background:new 0 0 940 300;" xml:space="preserve">
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#13BEF9;}
.st1{fill:#13BEF9;}
</style>
<g>
<polygon class="st0" points="84.3,76.6 80.3,76.6 80.3,97.3 84.3,97.3 84.3,76.6 "/>
<polygon class="st0" points="101.5,76.6 97.5,76.6 97.5,97.3 101.5,97.3 101.5,76.6 "/>
<polygon class="st0" points="125,37.1 120.9,30 52.5,69.5 56.6,76.6 125,37.1 "/>
<polygon class="st0" points="124.6,37.1 128.7,30 197.1,69.5 193,76.6 124.6,37.1 "/>
<polygon class="st0" points="209.2,76.7 209.2,68.5 21.8,68.5 21.8,76.7 209.2,76.7 "/>
<path class="st0" d="M135,192.5V71h8.2v127.4C141,195.9,138.2,194.1,135,192.5L135,192.5z"/>
<path class="st0" d="M121,190.4V19h8.2v172.4C126.9,190.3,121.3,190.4,121,190.4L121,190.4z"/>
<path class="st0" d="M43.3,207.5c-10-7.4-16.6-19.2-16.6-32.6c0-7.1,1.9-14.1,5.4-20.2h70c3.6,6.1,5.4,13.1,5.4,20.2
c0,6.2-0.8,12-3.3,17.2c-5.3-5.1-13.1-7.3-21-7.3c-14,0-26,8.7-29.1,21.7c-1.1-0.1-1.8-0.2-2.9-0.2
C48.5,206.4,45.9,206.8,43.3,207.5L43.3,207.5z"/>
<path class="st1" d="M219.8,115.5c-10.6,0-19.9,4.9-26.3,12.5v-11.4h-10.6v101.3h10.6v-42.7c6.3,7.8,15.7,12.8,26.3,12.8
c19.8,0,36.1-16.9,36.1-36.4C255.9,131.8,239.6,115.5,219.8,115.5L219.8,115.5z M220.1,177.5c-13.8,0-26-12.2-26-26
c0-14.1,12.2-25.6,26-25.6c14.1,0,24.7,11.5,24.7,25.6C244.8,165.3,234.2,177.5,220.1,177.5L220.1,177.5z"/>
<path class="st1" d="M302.3,187.9c19.8,0,36.1-16.9,36.1-36.4c0-19.8-16.3-36.1-36.1-36.1c-19.8,0-36.1,16.3-36.1,36.1
C266.2,171,282.5,187.9,302.3,187.9L302.3,187.9z M302.3,125.9c14.1,0,25,11.5,25,25.6c0,13.8-10.9,26-25,26c-14.1,0-25-12.2-25-26
C277.3,137.5,288.2,125.9,302.3,125.9L302.3,125.9z"/>
<path class="st1" d="M365.6,116.6H355v69.6h10.6v-38.5c0-14.2,11.2-21.8,23.6-21.8v-10.4c-9.6,0-17.9,4.1-23.6,10.6V116.6
L365.6,116.6z"/>
<polygon class="st1" points="433.8,126.2 433.8,116.6 418.1,116.6 418.1,89.2 407.5,89.2 407.5,116.6 397.1,116.6 397.1,126.2
407.5,126.2 407.5,186.2 418.1,186.2 418.1,126.2 433.8,126.2 "/>
<path class="st1" d="M478.6,187.9c10.6,0,19.9-5.1,26.3-12.8v11.4h10.6v-69.9h-10.6V128c-6.3-7.6-15.7-12.5-26.3-12.5
c-19.8,0-36.1,16.3-36.1,36.1C442.5,171,458.8,187.9,478.6,187.9L478.6,187.9z M478.2,177.5c-14.1,0-24.7-12.2-24.7-26
c0-14.1,10.6-25.6,24.7-25.6c13.8,0,26,11.5,26,25.6C504.2,165.3,492,177.5,478.2,177.5L478.2,177.5z"/>
<path class="st1" d="M543.6,102.5c4,0,7.4-3.3,7.4-7.6c0-3.8-3.5-7.3-7.4-7.3c-4.3,0-7.6,3.5-7.6,7.3
C536,99.2,539.3,102.5,543.6,102.5L543.6,102.5z M538.2,186.2h11.1v-69.6h-11.1V186.2L538.2,186.2z"/>
<path class="st1" d="M571.6,186.2h10.6v-37c0-15.7,8.7-23.6,22.8-23.3c11.6,0,17.9,6.8,17.9,20.6v39.7h10.6v-39.7
c0-22.2-8.5-31-28.5-31c-9.5,0-17.2,3.5-22.8,9.5v-8.4h-10.6V186.2L571.6,186.2z"/>
<path class="st1" d="M720.7,151.5c0-19.8-16.3-36.1-36.1-36.1c-19.8,0-36.1,16.3-36.1,36.1c0,19.5,16.3,36.4,36.1,36.4
c14.1,0,26.6-8.1,32.4-20.1h-13.1c-4.4,5.7-11.2,9.7-19.3,9.7c-12.3,0-22.3-9.5-24.5-21h60.6L720.7,151.5L720.7,151.5z
M684.6,125.9c12.2,0,22.2,8.9,24.5,20.4h-49.1C662.4,134.8,672.3,125.9,684.6,125.9L684.6,125.9z"/>
<path class="st1" d="M747.9,116.6h-10.6v69.6h10.6v-38.5c0-14.2,11.2-21.8,23.6-21.8v-10.4c-9.7,0-17.9,4.1-23.6,10.6V116.6
L747.9,116.6z"/>
<path class="st1" d="M787.5,187c4.7,0,8.7-4,8.7-8.9c0-4.7-4-8.7-8.7-8.7c-4.9,0-8.9,4-8.9,8.7C778.6,183,782.6,187,787.5,187
L787.5,187z"/>
<path class="st1" d="M823.5,102.5c4,0,7.4-3.3,7.4-7.6c0-3.8-3.5-7.3-7.4-7.3c-4.3,0-7.6,3.5-7.6,7.3
C816,99.2,819.3,102.5,823.5,102.5L823.5,102.5z M818.2,186.2h11.1v-69.6h-11.1V186.2L818.2,186.2z"/>
<path class="st1" d="M882.1,187.9c19.8,0,36.1-16.9,36.1-36.4c0-19.8-16.3-36.1-36.1-36.1c-19.8,0-36.1,16.3-36.1,36.1
C846,171,862.3,187.9,882.1,187.9L882.1,187.9z M882.1,125.9c14.1,0,25,11.5,25,25.6c0,13.8-10.9,26-25,26c-14.1,0-25-12.2-25-26
C857.1,137.5,868,125.9,882.1,125.9L882.1,125.9z"/>
<polygon class="st0" points="77.7,106.5 56.5,106.5 56.5,127.8 77.7,127.8 77.7,106.5 "/>
<polygon class="st0" points="53.8,106.5 32.6,106.5 32.6,127.8 53.8,127.8 53.8,106.5 "/>
<polygon class="st0" points="53.8,130.2 32.6,130.2 32.6,151.5 53.8,151.5 53.8,130.2 "/>
<polygon class="st0" points="77.7,130.2 56.5,130.2 56.5,151.5 77.7,151.5 77.7,130.2 "/>
<polygon class="st0" points="101.5,130.2 80.3,130.2 80.3,151.5 101.5,151.5 101.5,130.2 "/>
<polygon class="st0" points="101.5,95.1 80.3,95.1 80.3,116.4 101.5,116.4 101.5,95.1 "/>
<path class="st0" d="M57.6,210.7c2.9-12.3,14-21.5,27.2-21.5c8.5,0,16.1,3.8,21.3,9.8c4.5-3.1,9.9-4.9,15.8-4.9
c15.4,0,27.9,12.5,27.9,27.9c0,3.2-0.5,6.2-1.5,9.1c3.4,4.6,5.5,10.4,5.5,16.6c0,15.4-12.5,27.9-27.9,27.9c-6.8,0-13-2.4-17.8-6.4
c-5.1,7.1-13.4,11.8-22.8,11.8c-10.8,0-20.2-6.2-24.9-15.2c-1.9,0.4-3.8,0.6-5.8,0.6c-15.4,0-28-12.5-28-27.9s12.5-27.9,28-27.9
C55.6,210.5,56.6,210.5,57.6,210.7L57.6,210.7z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -101,7 +101,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -136,7 +136,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -102,7 +102,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -37,7 +37,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -295,7 +295,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -68,7 +68,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -204,7 +204,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -97,7 +97,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -189,7 +189,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -88,7 +88,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -153,7 +153,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -136,7 +136,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -219,7 +219,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -109,7 +109,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -201,7 +201,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -10,6 +10,7 @@
ng-model="$ctrl.model.Registry"
id="image_registry"
class="form-control"
data-cy="component-registrySelect"
></select>
</div>
<label for="image_name" ng-class="$ctrl.labelClass" class="margin-sm-top control-label text-left">Image</label>

View File

@@ -65,7 +65,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -65,7 +65,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -79,7 +79,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -70,7 +70,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -142,7 +142,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -94,7 +94,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -8,6 +8,11 @@ export class EditEdgeGroupController {
this.$state = $state;
this.$async = $async;
this.state = {
actionInProgress: false,
loaded: false,
};
this.updateGroup = this.updateGroup.bind(this);
this.updateGroupAsync = this.updateGroupAsync.bind(this);
}

View File

@@ -41,7 +41,7 @@
<!-- loading box logo -->
<div class="row">
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo" />
<img ng-if="!logo" src="${require('./assets/images/logo_alt.png')}" class="simple-box-logo" alt="Portainer" />
<img ng-if="!logo" src="${require('./assets/images/logo_alt.svg')}" class="simple-box-logo" alt="Portainer" />
</div>
<!-- !loading box logo -->
<!-- panel -->

View File

@@ -76,7 +76,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -124,7 +124,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -98,7 +98,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -68,7 +68,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -83,7 +83,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -99,6 +99,13 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Node' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('IP')">
Pod IP
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('CreationDate')">
Creation date
@@ -129,6 +136,7 @@
</span>
<span ng-if="!item.Node">-</span>
</td>
<td>{{ item.PodIP }}</td>
<td>{{ item.CreationDate | getisodate }}</td>
<td>
<a
@@ -161,7 +169,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -1,9 +1,9 @@
.secondary-heading {
background-color: #e7f6ff !important;
background-color: var(--bg-app-datatable-thead) !important;
}
.secondary-body {
background-color: #f1f9fd;
background-color: var(--bg-app-datatable-tbody);
}
.datatable-wide {

View File

@@ -231,6 +231,7 @@
<kubernetes-applications-datatable
dataset="item.KubernetesApplications"
table-key="{{ item.Id }}_table"
settings-key="{{ $ctrl.tableKey }}"
order-by="Name"
remove-action="$ctrl.removeAction"
refresh-callback="$ctrl.refreshCallback"
@@ -268,7 +269,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -8,6 +8,7 @@ angular.module('portainer.kubernetes').component('kubernetesApplicationsDatatabl
titleIcon: '@',
dataset: '<',
tableKey: '@',
settingsKey: '@',
orderBy: '@',
reverseOrder: '<',
removeAction: '<',

View File

@@ -147,7 +147,7 @@ angular.module('portainer.docker').controller('KubernetesApplicationsDatatableCo
this.expandItems(storedExpandedItems);
}
const storedSettings = DatatableService.getDataTableSettings(this.tableKey);
const storedSettings = DatatableService.getDataTableSettings(this.settingsKey);
if (storedSettings !== null) {
this.settings = storedSettings;
this.settings.open = false;

View File

@@ -243,7 +243,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -171,7 +171,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -110,7 +110,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -141,7 +141,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -152,7 +152,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -130,7 +130,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -163,7 +163,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -179,7 +179,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -39,7 +39,7 @@
Configuration
</div>
<!-- namespace-input -->
<div class="form-group">
<div class="form-group" ng-if="$ctrl.state.resourcePool">
<label for="resource-pool-selector" class="col-sm-2 control-label text-left">Namespace</label>
<div class="col-sm-10">
<select
@@ -52,13 +52,7 @@
></select>
</div>
</div>
<div class="form-group" ng-if="!$ctrl.state.resourcePool">
<div class="col-sm-12 small text-danger">
<i class="fa fa-exclamation-circle red-icon" aria-hidden="true" style="margin-right: 2px;"></i>
This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of the
namespace.
</div>
</div>
<div class="form-group" ng-if="!$ctrl.state.resourcePool">
<div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>

View File

@@ -118,4 +118,5 @@ export const KubernetesDeploymentTypes = Object.freeze({
GIT: 'git',
CONTENT: 'content',
APPLICATION_FORM: 'application form',
URL: 'url',
});

View File

@@ -26,7 +26,9 @@ class KubernetesNodeService {
const [details, yaml] = await Promise.all([this.KubernetesNodes().get(params).$promise, this.KubernetesNodes().getYaml(params).$promise]);
return KubernetesNodeConverter.apiToNodeDetails(details, yaml);
} catch (err) {
throw new PortainerError('Unable to retrieve node details', err);
// changing the structure of error message to fix [object, Object] issue
const errData = err.data;
throw new PortainerError('Unable to retrieve node details', errData);
}
}

View File

@@ -68,6 +68,7 @@ function computeContainers(data) {
const res = new KubernetesPodContainer();
res.Type = KubernetesPodContainerTypes.APP;
res.PodName = data.metadata.name;
res.PodIP = data.status.podIP;
res.Name = item.name;
res.Image = item.image;
res.ImagePullPolicy = item.imagePullPolicy;

View File

@@ -15,6 +15,7 @@
<kubernetes-applications-datatable
dataset="ctrl.state.applications"
table-key="kubernetes.applications"
settings-key="kubernetes.applications"
order-by="Name"
remove-action="ctrl.removeAction"
refresh-callback="ctrl.getApplications"

File diff suppressed because it is too large Load Diff

View File

@@ -790,15 +790,14 @@ class KubernetesCreateApplicationController {
}
isEditLBWithPorts() {
return this.formValues.PublishingType === KubernetesApplicationPublishingTypes.LOAD_BALANCER && _.filter(this.formValues.PublishedPorts, { IsNew: false }).length;
return this.formValues.PublishingType === KubernetesApplicationPublishingTypes.LOAD_BALANCER && _.filter(this.formValues.PublishedPorts, { IsNew: false }).length === 0;
}
isProtocolOptionDisabled(index, protocol) {
return (
this.disableLoadBalancerEdit() ||
(this.isEditAndNotNewPublishedPort(index) && this.formValues.PublishedPorts[index].Protocol !== protocol) ||
(this.isEditLBWithPorts() && this.formValues.PublishedPorts[index].Protocol !== protocol) ||
(this.isNewAndNotFirst(index) && this.formValues.PublishedPorts[index].Protocol !== protocol)
(this.isEditLBWithPorts() && this.formValues.PublishedPorts[index].Protocol !== protocol && this.isNewAndNotFirst(index))
);
}
@@ -1079,10 +1078,14 @@ class KubernetesCreateApplicationController {
);
if (this.application.ApplicationKind) {
this.state.appType = this.KubernetesDeploymentTypes[this.application.ApplicationKind.toUpperCase()];
this.state.appType = KubernetesDeploymentTypes[this.application.ApplicationKind.toUpperCase()];
if (this.application.ApplicationKind === KubernetesDeploymentTypes.URL) {
this.state.appType = KubernetesDeploymentTypes.CONTENT;
}
if (this.application.StackId) {
this.stack = await this.StackService.stack(this.application.StackId);
if (this.application.ApplicationKind === this.KubernetesDeploymentTypes.CONTENT) {
if (this.state.appType === KubernetesDeploymentTypes.CONTENT) {
this.stackFileContent = await this.StackService.getStackFile(this.application.StackId);
}
}

View File

@@ -26,7 +26,7 @@
</tr>
<tr>
<td>Stack</td>
<td>{{ ctrl.application.StackName || '-' }}</td>
<td data-cy="k8sAppDetail-stackName">{{ ctrl.application.StackName || '-' }}</td>
</tr>
<tr>
<td>Namespace</td>
@@ -37,15 +37,15 @@
</tr>
<tr>
<td>Application Type</td>
<td>
<td data-cy="k8sAppDetail-appType">
{{ ctrl.application.ApplicationType | kubernetesApplicationTypeText }}
</td>
</tr>
<tr>
<td>Status</td>
<td ng-if="ctrl.application.ApplicationType !== ctrl.KubernetesApplicationTypes.POD">
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.REPLICATED">Replicated</span>
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.GLOBAL">Global</span>
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.REPLICATED" data-cy="k8sAppDetail-deployType">Replicated</span>
<span ng-if="ctrl.application.DeploymentType === ctrl.KubernetesApplicationDeploymentTypes.GLOBAL" data-cy="k8sAppDetail-appType">Global</span>
<code data-cy="k8sAppDetail-runningPods">{{ ctrl.application.RunningPodsCount }}</code> /
<code data-cy="k8sAppDetail-totalPods">{{ ctrl.application.TotalPodsCount }}</code>
</td>
@@ -59,8 +59,10 @@
<div ng-if="ctrl.application.ApplicationType !== ctrl.KubernetesApplicationTypes.POD" class="text-muted small"> per instance </div>
</td>
<td>
<div ng-if="ctrl.application.Requests.Cpu">CPU {{ ctrl.application.Requests.Cpu | kubernetesApplicationCPUValue }}</div>
<div ng-if="ctrl.application.Requests.Memory">Memory {{ ctrl.application.Requests.Memory | humansize }}</div>
<div ng-if="ctrl.application.Requests.Cpu" data-cy="k8sAppDetail-cpuReservation"
>CPU {{ ctrl.application.Requests.Cpu | kubernetesApplicationCPUValue }}</div
>
<div ng-if="ctrl.application.Requests.Memory" data-cy="k8sAppDetail-memoryReservation">Memory {{ ctrl.application.Requests.Memory | humansize }}</div>
</td>
</tr>
<tr>
@@ -79,7 +81,7 @@
<div class="form-group">
<div class="col-sm-12">
<i class="fa fa-edit" aria-hidden="true"></i> Note
<button class="btn btn-xs btn-primary" ng-click="ctrl.state.expandedNote = !ctrl.state.expandedNote;"
<button class="btn btn-xs btn-primary" ng-click="ctrl.state.expandedNote = !ctrl.state.expandedNote;" data-cy="k8sAppDetail-expandNoteButton"
>{{ ctrl.state.expandedNote ? 'Collapse' : 'Expand' }}
<i class="fas {{ ctrl.state.expandedNote ? 'fa-angle-up' : 'fa-angle-down' }}" aria-hidden="true"></i
></button>
@@ -105,6 +107,7 @@
type="button"
ng-click="ctrl.updateApplication()"
ng-disabled="ctrl.formValues.Note === ctrl.application.Note"
data-cy="k8sAppDetail-saveNoteButton"
>{{ ctrl.application.Note ? 'Update' : 'Save' }} note</button
>
</div>
@@ -195,7 +198,14 @@
<rd-widget>
<rd-widget-body>
<div ng-if="!ctrl.isSystemNamespace()" style="margin-bottom: 15px;">
<button ng-if="!ctrl.isExternalApplication()" type="button" class="btn btn-sm btn-primary" ui-sref="kubernetes.applications.application.edit" style="margin-left: 0;">
<button
ng-if="!ctrl.isExternalApplication()"
type="button"
class="btn btn-sm btn-primary"
ui-sref="kubernetes.applications.application.edit"
style="margin-left: 0;"
data-cy="k8sAppDetail-editAppButton"
>
<i class="fa fa-file-code space-right" aria-hidden="true"></i>Edit this application
</button>
<button
@@ -204,6 +214,7 @@
class="btn btn-sm btn-primary"
style="margin-left: 0;"
ng-click="ctrl.redeployApplication()"
data-cy="k8sAppDetail-redeployButton"
>
<i class="fa fa-redo space-right" aria-hidden="true"></i>Redeploy
</button>
@@ -214,6 +225,7 @@
style="margin-left: 0;"
ng-click="ctrl.rollbackApplication()"
ng-disabled="ctrl.application.Revisions.length < 2 || ctrl.state.appType !== ctrl.KubernetesDeploymentTypes.APPLICATION_FORM"
data-cy="k8sAppDetail-rollbackButton"
>
<i class="fas fa-history space-right" aria-hidden="true"></i>Rollback to previous configuration
</button>
@@ -327,7 +339,7 @@
<span ng-if="ctrl.application.ServiceType === ctrl.KubernetesServiceTypes.NODE_PORT" data-cy="k8sAppDetail-nodePort">
{{ port.NodePort }}
</span>
<span ng-if="ctrl.application.ServiceType !== ctrl.KubernetesServiceTypes.NODE_PORT">
<span ng-if="ctrl.application.ServiceType !== ctrl.KubernetesServiceTypes.NODE_PORT" data-cy="k8sAppDetail-containerPort">
{{ port.Port }}
</span>
<a
@@ -335,6 +347,7 @@
ng-href="http://{{ ctrl.application.LoadBalancerIPAddress }}:{{ port.Port }}"
target="_blank"
style="margin-left: 5px;"
data-cy="k8sAppDetail-accessLink"
>
<i class="fa fa-external-link-alt" aria-hidden="true"></i> access
</a>
@@ -342,12 +355,12 @@
<td ng-if="!ctrl.portHasIngressRules(port)">-</td>
</tr>
<tr ng-repeat-end ng-repeat="rule in port.IngressRules">
<td>{{ port.TargetPort }}/{{ port.Protocol }}</td>
<td data-cy="k8sAppDetail-httpRoute">{{ port.TargetPort }}/{{ port.Protocol }}</td>
<td>
<span ng-if="ctrl.application.ServiceType === ctrl.KubernetesServiceTypes.NODE_PORT">
<span ng-if="ctrl.application.ServiceType === ctrl.KubernetesServiceTypes.NODE_PORT" data-cy="k8sAppDetail-nodePort">
{{ port.NodePort }}
</span>
<span ng-if="ctrl.application.ServiceType !== ctrl.KubernetesServiceTypes.NODE_PORT">
<span ng-if="ctrl.application.ServiceType !== ctrl.KubernetesServiceTypes.NODE_PORT" data-cy="k8sAppDetail-port">
{{ port.Port }}
</span>
<a
@@ -371,7 +384,7 @@
>pending
</span>
<span ng-if="ctrl.ruleCanBeDisplayed(rule)">
<a ng-href="{{ ctrl.buildIngressRuleURL(rule) }}" target="_blank">
<a ng-href="{{ ctrl.buildIngressRuleURL(rule) }}" target="_blank" data-cy="k8sAppDetail-httpRouteLink">
{{ ctrl.buildIngressRuleURL(rule) | stripprotocol }}
</a>
</span>
@@ -404,9 +417,9 @@
</td>
</tr>
<tr>
<td>{{ ctrl.application.AutoScaler.MinReplicas }}</td>
<td>{{ ctrl.application.AutoScaler.MaxReplicas }}</td>
<td>{{ ctrl.application.AutoScaler.TargetCPUUtilization }}%</td>
<td data-cy="k8sAppDetail-minReplicas">{{ ctrl.application.AutoScaler.MinReplicas }}</td>
<td data-cy="k8sAppDetail-maxReplicas">{{ ctrl.application.AutoScaler.MaxReplicas }}</td>
<td data-cy="k8sAppDetail-targetCPU">{{ ctrl.application.AutoScaler.TargetCPUUtilization }}%</td>
</tr>
</tbody>
</table>
@@ -431,7 +444,7 @@
</tr>
<tbody ng-repeat="container in ctrl.application.Containers" style="border-top: 0;">
<tr ng-repeat="envvar in container.Env | orderBy: 'name'">
<td>
<td data-cy="k8sAppDetail-containerName">
{{ container.Name }}
<span ng-if="container.Type === ctrl.KubernetesPodContainerTypes.INIT"
><i class="fa fa-asterisk" aria-hidden="true"></i> {{ envvar.valueFrom.fieldRef.fieldPath }} (<a
@@ -441,12 +454,16 @@
>)</span
>
</td>
<td>{{ envvar.name }}</td>
<td data-cy="k8sAppDetail-envVarName">{{ envvar.name }}</td>
<td>
<span ng-if="envvar.value">{{ envvar.value }}</span>
<span ng-if="envvar.valueFrom.configMapKeyRef"><i class="fa fa-key" aria-hidden="true"></i> {{ envvar.valueFrom.configMapKeyRef.key }}</span>
<span ng-if="envvar.valueFrom.secretKeyRef"><i class="fa fa-key" aria-hidden="true"></i> {{ envvar.valueFrom.secretKeyRef.key }}</span>
<span ng-if="envvar.valueFrom.fieldRef"
<span ng-if="envvar.value" data-cy="k8sAppDetail-envVarValue">{{ envvar.value }}</span>
<span ng-if="envvar.valueFrom.configMapKeyRef" data-cy="k8sAppDetail-envVarValue"
><i class="fa fa-key" aria-hidden="true"></i> {{ envvar.valueFrom.configMapKeyRef.key }}</span
>
<span ng-if="envvar.valueFrom.secretKeyRef" data-cy="k8sAppDetail-envVarValue"
><i class="fa fa-key" aria-hidden="true"></i> {{ envvar.valueFrom.secretKeyRef.key }}</span
>
<span ng-if="envvar.valueFrom.fieldRef" data-cy="k8sAppDetail-envVarValue"
><i class="fa fa-asterisk" aria-hidden="true"></i> {{ envvar.valueFrom.fieldRef.fieldPath }} (<a
href="https://kubernetes.io/docs/tasks/inject-data-application/downward-api-volume-expose-pod-information/#capabilities-of-the-downward-api"
target="_blank"
@@ -457,12 +474,12 @@
</td>
<td>
<span ng-if="envvar.value || envvar.valueFrom.fieldRef || (!envvar.valueFrom.secretKeyRef && !envvar.valueFrom.configMapKeyRef)">-</span>
<span ng-if="envvar.valueFrom.configMapKeyRef"
<span ng-if="envvar.valueFrom.configMapKeyRef" data-cy="k8sAppDetail-configName"
><a ui-sref="kubernetes.configurations.configuration({ name: envvar.valueFrom.configMapKeyRef.name, namespace: ctrl.application.ResourcePool })"
><i class="fa fa-file-code" aria-hidden="true"></i> {{ envvar.valueFrom.configMapKeyRef.name }}</a
></span
>
<span ng-if="envvar.valueFrom.secretKeyRef"
<span ng-if="envvar.valueFrom.secretKeyRef" data-cy="k8sAppDetail-configName"
><a ui-sref="kubernetes.configurations.configuration({ name: envvar.valueFrom.secretKeyRef.name, namespace: ctrl.application.ResourcePool })"
><i class="fa fa-file-code" aria-hidden="true"></i> {{ envvar.valueFrom.secretKeyRef.name }}</a
></span
@@ -529,11 +546,11 @@
</tr>
<tbody ng-repeat="container in ctrl.application.Containers" style="border-top: 0;">
<tr ng-repeat="volume in container.PersistedFolders track by $index">
<td>
<td data-cy="k8sAppDetail-volMountPath">
{{ volume.MountPath }}
</td>
<td ng-if="volume.PersistentVolumeClaimName">
<a ui-sref="kubernetes.volumes.volume({ name: volume.PersistentVolumeClaimName, namespace: ctrl.application.ResourcePool })"
<a ui-sref="kubernetes.volumes.volume({ name: volume.PersistentVolumeClaimName, namespace: ctrl.application.ResourcePool })" data-cy="k8sAppDetail-volClaimName"
><i class="fa fa-database" aria-hidden="true"></i> {{ volume.PersistentVolumeClaimName }}</a
>
</td>

View File

@@ -179,7 +179,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -49,7 +49,7 @@
</div>
<!-- resource-pool -->
<div class="form-group">
<div class="form-group" ng-if="ctrl.formValues.ResourcePool">
<label for="resource-pool-selector" class="col-sm-1 control-label text-left">Namespace</label>
<div class="col-sm-11">
<select
@@ -57,6 +57,7 @@
id="resource-pool-selector"
ng-model="ctrl.formValues.ResourcePool"
ng-options="resourcePool.Namespace.Name for resourcePool in ctrl.resourcePools"
ng-change="ctrl.onResourcePoolSelectionChange()"
data-cy="k8sConfigCreate-namespaceDropdown"
></select>
</div>
@@ -68,69 +69,76 @@
namespace.
</div>
</div>
<div class="form-group" ng-if="!ctrl.formValues.ResourcePool">
<div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div>
</div>
<!-- !resource-pool -->
<div class="col-sm-12 form-section-title">
Configuration type
</div>
<div class="form-group">
<div class="col-sm-12 small text-muted">
Select the type of data that you want to save in the configuration.
<div ng-if="ctrl.formValues.ResourcePool">
<div class="col-sm-12 form-section-title">
Configuration type
</div>
</div>
<!-- type options -->
<div class="form-group" style="margin-bottom: 0;">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationTypes.CONFIGMAP" ng-model="ctrl.formValues.Type" />
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
<div class="boxselector_header">
<i class="fa fa-file-code" aria-hidden="true" style="margin-right: 2px;"></i>
Non-sensitive
</div>
<p>This configuration holds non-sensitive information</p>
</label>
</div>
<div>
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationTypes.SECRET" ng-model="ctrl.formValues.Type" />
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
<div class="boxselector_header">
<i class="fa fa-user-secret" aria-hidden="true" style="margin-right: 2px;"></i>
Sensitive
</div>
<p>This configuration holds sensitive information</p>
</label>
<div class="form-group">
<div class="col-sm-12 small text-muted">
Select the type of data that you want to save in the configuration.
</div>
</div>
</div>
<!-- !type options -->
<div class="col-sm-12 form-section-title" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
Information
</div>
<div class="form-group" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
<div class="col-sm-12 small text-muted">
Creating a sensitive configuration will create a Kubernetes Secret of type <code>Opaque</code>. You can find more information about this in the
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" target="_blank">official documentation</a>.
<!-- type options -->
<div class="form-group" style="margin-bottom: 0;">
<div class="boxselector_wrapper">
<div>
<input type="radio" id="type_basic" ng-value="ctrl.KubernetesConfigurationTypes.CONFIGMAP" ng-model="ctrl.formValues.Type" />
<label for="type_basic" data-cy="k8sConfigCreate-nonSensitiveButton">
<div class="boxselector_header">
<i class="fa fa-file-code" aria-hidden="true" style="margin-right: 2px;"></i>
Non-sensitive
</div>
<p>This configuration holds non-sensitive information</p>
</label>
</div>
<div>
<input type="radio" id="type_secret" ng-value="ctrl.KubernetesConfigurationTypes.SECRET" ng-model="ctrl.formValues.Type" />
<label for="type_secret" data-cy="k8sConfigCreate-sensitiveButton">
<div class="boxselector_header">
<i class="fa fa-user-secret" aria-hidden="true" style="margin-right: 2px;"></i>
Sensitive
</div>
<p>This configuration holds sensitive information</p>
</label>
</div>
</div>
</div>
<!-- !type options -->
<div class="col-sm-12 form-section-title" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
Information
</div>
<div class="form-group" ng-if="ctrl.formValues.Type == ctrl.KubernetesConfigurationTypes.SECRET">
<div class="col-sm-12 small text-muted">
Creating a sensitive configuration will create a Kubernetes Secret of type <code>Opaque</code>. You can find more information about this in the
<a href="https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" target="_blank">official documentation</a>.
</div>
</div>
<kubernetes-configuration-data
ng-if="ctrl.formValues"
form-values="ctrl.formValues"
is-valid="ctrl.state.isDataValid"
is-creation="true"
is-editor-dirty="ctrl.state.isEditorDirty"
></kubernetes-configuration-data>
<!-- summary -->
<kubernetes-summary-view
ng-if="!(!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress)"
form-values="ctrl.formValues"
></kubernetes-summary-view>
</div>
<kubernetes-configuration-data
ng-if="ctrl.formValues"
form-values="ctrl.formValues"
is-valid="ctrl.state.isDataValid"
is-creation="true"
is-editor-dirty="ctrl.state.isEditorDirty"
></kubernetes-configuration-data>
<!-- summary -->
<kubernetes-summary-view
ng-if="!(!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress)"
form-values="ctrl.formValues"
></kubernetes-summary-view>
<!-- actions -->
<div class="col-sm-12 form-section-title" style="margin-top: 10px;">
Actions
@@ -140,7 +148,7 @@
<button
type="button"
class="btn btn-primary btn-sm"
ng-disabled="!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress"
ng-disabled="!kubernetesConfigurationCreationForm.$valid || !ctrl.isFormValid() || ctrl.state.actionInProgress || !ctrl.formValues.ResourcePool"
ng-click="ctrl.createConfiguration()"
button-spinner="ctrl.state.actionInProgress"
data-cy="k8sConfigCreate-CreateConfigButton"

View File

@@ -27,7 +27,9 @@
<label class="control-label text-left">
Allow users to use external load balancer
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.UseLoadBalancer" /><i></i> </label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="ctrl.formValues.UseLoadBalancer" /><i data-cy="kubeSetup-loadBalancerToggle"></i>
</label>
</div>
</div>
@@ -43,7 +45,7 @@
<div class="form-group">
<div class="col-sm-12">
<label class="control-label text-left">Ingress controller</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addIngressClass()">
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="ctrl.addIngressClass()" data-cy="kubeSetup-congifIngressButton">
<i class="fa fa-plus-circle" aria-hidden="true"></i> configure ingress controller
</span>
</div>
@@ -61,6 +63,7 @@
ng-pattern="/^[a-z]([-a-z0-9]*[a-z0-9])?$/"
ng-change="ctrl.onChangeIngressClassName($index)"
required
data-cy="kubeSetup-ingressClassName"
/>
</div>
<div class="col-sm-3 input-group input-group-sm" ng-class="{ striked: ingressClass.NeedsDeletion }">
@@ -71,12 +74,19 @@
ng-model="ingressClass.Type"
ng-options="value as value for (key, value) in ctrl.IngressClassTypes"
required
data-cy="kubeSetup-ingressType"
>
<option selected disabled hidden value="">Select a type</option>
</select>
</div>
<div class="col-sm-1 input-group input-group-sm">
<button ng-if="!ingressClass.NeedsDeletion" class="btn btn-sm btn-danger" type="button" ng-click="ctrl.removeIngressClass($index)">
<button
ng-if="!ingressClass.NeedsDeletion"
class="btn btn-sm btn-danger"
type="button"
ng-click="ctrl.removeIngressClass($index)"
data-cy="kubeSetup-deleteIngress"
>
<i class="fa fa-trash-alt" aria-hidden="true"></i>
</button>
<button ng-if="ingressClass.NeedsDeletion" class="btn btn-sm btn-primary" type="button" ng-click="ctrl.restoreIngressClass($index)">
@@ -145,7 +155,9 @@
<label class="control-label text-left">
Restrict access to the default namespace
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.RestrictDefaultNamespace" /><i></i> </label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="ctrl.formValues.RestrictDefaultNamespace" /><i data-cy="kubeSetup-restrictDefaultNsToggle"></i>
</label>
</div>
</div>
@@ -169,7 +181,7 @@
<label class="control-label text-left">
Allow resource over-commit
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" checked disabled /><i></i> </label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" checked disabled /><i data-cy="kubeSetup-resourceOverCommitToggle"></i> </label>
<span class="text-muted small" style="margin-left: 15px;">
<i class="fa fa-user" aria-hidden="true"></i>
This feature is available in <a href="https://www.portainer.io/business-upsell?from=k8s-setup-overcommit" target="_blank"> Portainer Business Edition</a>.
@@ -193,7 +205,7 @@
Enable features using the metrics API
</label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="ctrl.formValues.UseServerMetrics" ng-change="ctrl.enableMetricsServer()" /><i></i>
<input type="checkbox" ng-model="ctrl.formValues.UseServerMetrics" ng-change="ctrl.enableMetricsServer()" /><i data-cy="kubeSetup-metricsToggle"></i>
</label>
</div>
<div ng-if="ctrl.state.metrics.pending && ctrl.state.metrics.userClick" class="col-sm-12 small text-muted" style="margin-top: 5px;">
@@ -252,7 +264,9 @@
<tr ng-repeat="class in ctrl.StorageClasses">
<td>
<div style="margin: 5px;">
<label class="switch" style="margin-right: 10px;"> <input type="checkbox" ng-model="class.selected" /><i></i> </label>
<label class="switch" style="margin-right: 10px;">
<input type="checkbox" ng-model="class.selected" /><i data-cy="kubeSetup-storageToggle{{ class.Name }}"></i>
</label>
<span>{{ class.Name }}</span>
</div>
</td>
@@ -267,12 +281,15 @@
directive-id="{{ class.Name }}"
helper-elements=""
translation="{nothingSelected: 'Not configured'}"
data-cy="kubeSetup-storageAccessSelect{{ class.Name }}"
>
</span>
</td>
<td>
<div style="margin: 5px;">
<label class="switch"><input type="checkbox" ng-model="class.AllowVolumeExpansion" /><i></i> </label>
<label class="switch"
><input type="checkbox" ng-model="class.AllowVolumeExpansion" /><i data-cy="kubeSetup-storageExpansionToggle{{ class.Name }}"></i>
</label>
</div>
</td>
</tr>
@@ -304,6 +321,7 @@
analytics-category="kubernetes"
analytics-event="kubernetes-configure"
analytics-properties="{ metadata: { restrictAccessToDefaultNamespace: ctrl.formValues.RestrictDefaultNamespace } }"
data-cy="kubeSetup-saveConfigurationButton"
>
<span ng-hide="ctrl.state.actionInProgress">Save configuration</span>
<span ng-show="ctrl.state.actionInProgress">Saving configuration...</span>

View File

@@ -16,12 +16,18 @@
<uib-tab-heading> <i class="fa fa-code space-right" aria-hidden="true"></i> Deploy </uib-tab-heading>
<form class="form-horizontal" style="margin-top: 20px;">
<div class="form-group">
<div class="form-group" ng-if="ctrl.formValues.Namespace">
<label for="target_node" class="col-lg-1 col-sm-2 control-label text-left">Namespace</label>
<div class="col-lg-11 col-sm-10">
<select class="form-control" ng-model="ctrl.formValues.Namespace" ng-options="namespace.Name as namespace.Name for namespace in ctrl.namespaces"></select>
</div>
</div>
<div class="form-group" ng-if="!ctrl.formValues.Namespace">
<div class="col-sm-12 small text-muted">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
You do not have access to any namespace. Contact your administrator to get access to a namespace.
</div>
</div>
<div class="col-sm-12 form-section-title">
Build method

View File

@@ -107,7 +107,9 @@ class KubernetesDeployController {
this.state.BuildMethod === KubernetesDeployBuildMethods.WEB_EDITOR && _.isEmpty(this.formValues.EditorContent) && _.isEmpty(this.formValues.Namespace);
const isURLFormInvalid = this.state.BuildMethod == KubernetesDeployBuildMethods.WEB_EDITOR.URL && _.isEmpty(this.formValues.ManifestURL);
return isGitFormInvalid || isWebEditorInvalid || isURLFormInvalid || this.state.actionInProgress;
const isNamespaceInvalid = _.isEmpty(this.formValues.Namespace);
return isGitFormInvalid || isWebEditorInvalid || isURLFormInvalid || this.state.actionInProgress || isNamespaceInvalid;
}
onChangeFormValues(values) {
@@ -230,7 +232,9 @@ class KubernetesDeployController {
});
this.namespaces = namespaces;
this.formValues.Namespace = this.namespaces[0].Name;
if (this.namespaces.length > 0) {
this.formValues.Namespace = this.namespaces[0].Name;
}
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to load namespaces data');
}

View File

@@ -213,8 +213,8 @@
<div class="form-group" ng-if="$ctrl.formValues.IngressClasses.length === 0">
<div class="col-sm-12 small text-muted">
The ingress feature must be enabled in the
<a ui-sref="portainer.endpoints.endpoint.kubernetesConfig({id: $ctrl.endpoint.Id})">environment configuration view</a> to be able to register ingresses inside this
namespace.
<a ui-sref="portainer.endpoints.endpoint.kubernetesConfig({id: $ctrl.endpoint.Id})">environment configuration view</a> to be able to register ingresses inside
this namespace.
</div>
</div>
@@ -238,7 +238,9 @@
<label class="control-label text-left">
Allow users to use this ingress
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ic.Selected" /><i></i> </label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="ic.Selected" /><i data-cy="namespaceCreate-ingressToggle{{ ic.IngressClass.Name }}"></i>
</label>
</div>
</div>
@@ -253,7 +255,12 @@
>
</portainer-tooltip>
</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="$ctrl.addHostname(ic)">
<span
class="label label-default interactive"
style="margin-left: 10px;"
ng-click="$ctrl.addHostname(ic)"
data-cy="namespaceCreate-addHostButton{{ ic.IngressClass.Name }}"
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> add hostname
</span>
</div>
@@ -270,6 +277,7 @@
ng-change="$ctrl.onChangeIngressHostname()"
placeholder="foo"
required
data-cy="namespaceCreate-hostnameInput{{ ic.IngressClass.Name }}_{{ $index }}"
/>
</div>
<div class="col-sm-1 input-group input-group-sm" ng-if="$index > 0">
@@ -306,17 +314,19 @@
>
</portainer-tooltip>
</label>
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ic.RewriteTarget" /><i></i> </label>
<label class="switch" style="margin-left: 20px;">
<input type="checkbox" ng-model="ic.RewriteTarget" /><i data-cy="namespaceCreate-redirectRoutesToggle{{ ic.IngressClass.Name }}"></i>
</label>
</div>
</div>
</div>
<div ng-repeat-end class="form-group" ng-if="ic.Selected" style="margin-bottom: 20px;">
<div class="col-sm-12">
<p>
<a class="small interactive" ng-if="!ic.AdvancedConfig" ng-click="ic.AdvancedConfig = true">
<a class="small interactive" ng-if="!ic.AdvancedConfig" ng-click="ic.AdvancedConfig = true" data-cy="namespaceCreate-advancedConfig{{ ic.IngressClass.Name }}">
<i class="fa fa-plus space-right" aria-hidden="true"></i> Advanced configuration
</a>
<a class="small interactive" ng-if="ic.AdvancedConfig" ng-click="ic.AdvancedConfig = false">
<a class="small interactive" ng-if="ic.AdvancedConfig" ng-click="ic.AdvancedConfig = false" data-cy="namespaceCreate-hideConfig{{ ic.IngressClass.Name }}">
<i class="fa fa-minus space-right" aria-hidden="true"></i> Hide configuration
</a>
</p>
@@ -331,7 +341,12 @@
<div class="col-sm-12" ng-if="ic.AdvancedConfig">
<label class="control-label text-left">Annotations</label>
<span class="label label-default interactive" style="margin-left: 10px;" ng-click="$ctrl.addAnnotation(ic)">
<span
class="label label-default interactive"
style="margin-left: 10px;"
ng-click="$ctrl.addAnnotation(ic)"
data-cy="namespaceCreate-addAnnotation{{ ic.IngressClass.Name }}"
>
<i class="fa fa-plus-circle" aria-hidden="true"></i> add annotation
</span>
</div>
@@ -340,14 +355,33 @@
<div ng-repeat="annotation in ic.Annotations track by $index" style="margin-top: 2px;">
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">Key</span>
<input type="text" class="form-control" ng-model="annotation.Key" placeholder="nginx.ingress.kubernetes.io/rewrite-target" required />
<input
type="text"
class="form-control"
ng-model="annotation.Key"
placeholder="nginx.ingress.kubernetes.io/rewrite-target"
required
data-cy="namespaceCreate-annotationKey{{ ic.IngressClass.Name }}"
/>
</div>
<div class="input-group col-sm-5 input-group-sm">
<span class="input-group-addon">Value</span>
<input type="text" class="form-control" ng-model="annotation.Value" placeholder="/$1" required />
<input
type="text"
class="form-control"
ng-model="annotation.Value"
placeholder="/$1"
required
data-cy="namespaceCreate-annotationValue{{ ic.IngressClass.Name }}"
/>
</div>
<div class="col-sm-1 input-group input-group-sm">
<button class="btn btn-sm btn-danger" type="button" ng-click="$ctrl.removeAnnotation(ic, $index)">
<button
class="btn btn-sm btn-danger"
type="button"
ng-click="$ctrl.removeAnnotation(ic, $index)"
data-cy="namespaceCreate-deleteAnnotationButton{{ ic.IngressClass.Name }}"
>
<i class="fa fa-trash-alt" aria-hidden="true"></i>
</button>
</div>
@@ -392,6 +426,7 @@
helper-elements="filter"
search-property="Name"
translation="{nothingSelected: 'Select one or more registry', search: 'Search...'}"
data-cy="namespaceCreate-registrySelect"
>
</span>
</div>

View File

@@ -117,7 +117,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -20,7 +20,7 @@ class KubernetesSummaryController {
$scope.$watch(
'$ctrl.formValues',
(formValues) => {
this.state.resources = this.generateResourceSummaryList(formValues);
this.state.resources = this.generateResourceSummaryList(angular.copy(formValues));
},
true
);

View File

@@ -127,7 +127,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -13,14 +13,14 @@
<rd-widget-body classes="no-padding">
<uib-tabset active="ctrl.state.activeTab" justified="true" type="pills">
<uib-tab index="0" classes="btn-sm" select="ctrl.selectTab(0)">
<uib-tab-heading> <i class="fa fa-database space-right" aria-hidden="true"></i> Volume </uib-tab-heading>
<uib-tab-heading data-cy="k8sVolDetail-volTab"> <i class="fa fa-database space-right" aria-hidden="true"></i> Volume </uib-tab-heading>
<div style="padding: 20px;">
<table class="table">
<tbody>
<tr>
<td>Name</td>
<td>
<td data-cy="k8sVolDetail-volName">
{{ ctrl.volume.PersistentVolumeClaim.Name }}
<span class="label label-primary image-tag label-margins" ng-if="!ctrl.isSystemNamespace() && ctrl.isExternalVolume()">external</span>
<span class="label label-warning image-tag label-margins" ng-if="!ctrl.isSystemNamespace() && !ctrl.isUsed()">unused</span>
@@ -29,17 +29,19 @@
<tr>
<td>Namespace</td>
<td>
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: ctrl.volume.ResourcePool.Namespace.Name })">{{ ctrl.volume.ResourcePool.Namespace.Name }}</a>
<a ui-sref="kubernetes.resourcePools.resourcePool({ id: ctrl.volume.ResourcePool.Namespace.Name })" data-cy="k8sVolDetail-volNamespace">{{
ctrl.volume.ResourcePool.Namespace.Name
}}</a>
<span style="margin-left: 5px;" class="label label-info image-tag" ng-if="ctrl.isSystemNamespace()">system</span>
</td>
</tr>
<tr>
<td>Storage</td>
<td>{{ ctrl.volume.PersistentVolumeClaim.StorageClass.Name }}</td>
<td data-cy="k8sVolDetail-volStorageClassname">{{ ctrl.volume.PersistentVolumeClaim.StorageClass.Name }}</td>
</tr>
<tr>
<td>Shared Access Policy</td>
<td
<td data-cy="k8sVolDetail-volAccessPolicy"
>{{ ctrl.state.volumeSharedAccessPolicy }}
<portainer-tooltip
position="bottom"
@@ -50,11 +52,13 @@
</tr>
<tr>
<td>Provisioner</td>
<td>{{ ctrl.volume.PersistentVolumeClaim.StorageClass.Provisioner ? ctrl.volume.PersistentVolumeClaim.StorageClass.Provisioner : '-' }}</td>
<td data-cy="k8sVolDetail-volProvisioner">{{
ctrl.volume.PersistentVolumeClaim.StorageClass.Provisioner ? ctrl.volume.PersistentVolumeClaim.StorageClass.Provisioner : '-'
}}</td>
</tr>
<tr>
<td>Creation date</td>
<td>{{ ctrl.volume.PersistentVolumeClaim.CreationDate | getisodate }}</td>
<td data-cy="k8sVolDetail-volCreatedAt">{{ ctrl.volume.PersistentVolumeClaim.CreationDate | getisodate }}</td>
</tr>
<tr>
<td>Size</td>
@@ -65,6 +69,7 @@
class="btn btn-sm btn-primary"
ng-click="ctrl.state.increaseSize = true"
ng-if="ctrl.volume.PersistentVolumeClaim.StorageClass.AllowVolumeExpansion"
data-cy="k8sVolDetail-increaseSizeButton"
>Increase size</button
>
</td>
@@ -82,6 +87,7 @@
min="0"
ng-change="ctrl.onChangeSize()"
required
data-cy="k8sVolDetail-increaseSizeInput"
/>
<span class="input-group-addon" style="padding: 0;">
<select
@@ -89,13 +95,20 @@
ng-change="ctrl.onChangeSize()"
ng-options="unit for unit in ctrl.state.availableSizeUnits"
style="width: 100%; height: 100%;"
data-cy="k8sVolDetail-increaseSizeUnits"
></select>
</span>
</div>
<button type="button" class="btn btn-sm btn-primary" ng-disabled="!ctrl.sizeIsValid()" ng-click="ctrl.updateVolume()">
<button
type="button"
class="btn btn-sm btn-primary"
ng-disabled="!ctrl.sizeIsValid()"
ng-click="ctrl.updateVolume()"
data-cy="k8sVolDetail-updateSizeConfirm"
>
Update size
</button>
<button type="button" class="btn btn-sm btn-default" ng-click="ctrl.state.increaseSize = false">
<button type="button" class="btn btn-sm btn-default" ng-click="ctrl.state.increaseSize = false" data-cy="k8sVolDetail-cancelUpdateSize">
Cancel
</button>
</div>
@@ -119,7 +132,7 @@
</uib-tab>
<uib-tab index="1" classes="btn-sm" select="ctrl.selectTab(1)">
<uib-tab-heading>
<uib-tab-heading data-cy="k8sVolDetail-volEventsTab">
<i class="fa fa-history space-right" aria-hidden="true"></i> Events
<div ng-if="ctrl.hasEventWarnings()">
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
@@ -140,7 +153,7 @@
</uib-tab>
<uib-tab index="2" ng-if="ctrl.volume.PersistentVolumeClaim.Yaml" select="ctrl.showEditor()" classes="btn-sm">
<uib-tab-heading> <i class="fa fa-code space-right" aria-hidden="true"></i> YAML </uib-tab-heading>
<uib-tab-heading data-cy="k8sVolDetail-volYamlTab"> <i class="fa fa-code space-right" aria-hidden="true"></i> YAML </uib-tab-heading>
<div style="padding-right: 25px;" ng-if="ctrl.state.showEditorTab">
<kubernetes-yaml-inspector key="volume-yaml" data="ctrl.volume.PersistentVolumeClaim.Yaml"></kubernetes-yaml-inspector>
</div>

View File

@@ -81,7 +81,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -1,6 +1,6 @@
const CUSTOM_TEMPLATES_TYPES = {
STANDALONE: 1,
SWARM: 2,
SWARM: 1,
STANDALONE: 2,
KUBERNETES: 3,
};

View File

@@ -115,7 +115,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -82,7 +82,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -54,6 +54,21 @@
</tr>
</thead>
<tbody>
<tr>
<td>
<span class="md-checkbox" ng-if="$ctrl.isAdmin && !$ctrl.endpointType">
<input id="select_{{ $index }}" type="checkbox" disabled />
<label for="select_{{ $index }}"></label>
</span>
<span>DockerHub (anonymous)</span>
</td>
<td>
docker.io
</td>
<td>
-
</td>
</tr>
<tr
dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))"
ng-class="{ active: item.Checked }"
@@ -101,7 +116,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -231,7 +231,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -67,7 +67,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -67,7 +67,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -100,7 +100,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -35,10 +35,9 @@ class EndpointItemController {
return false;
}
const checkInInterval = this.model.EdgeCheckinInterval;
const now = Math.floor(Date.now() / 1000);
// give checkIn some wiggle room
return now - this.model.LastCheckInDate <= checkInInterval * 2 + 20;
return this.endpointInitTime - this.model.LastCheckInDate <= checkInInterval * 2;
}
$onInit() {

View File

@@ -10,6 +10,7 @@ angular.module('portainer.app').component('endpointItem', {
onEdit: '<',
isAdmin: '<',
tags: '<',
endpointInitTime: '<',
},
controller: EndpointItemController,
});

View File

@@ -14,5 +14,6 @@ angular.module('portainer.app').component('endpointList', {
isAdmin: '<',
totalCount: '<',
retrievePage: '<',
endpointInitTime: '<',
},
});

View File

@@ -38,6 +38,7 @@
on-edit="($ctrl.editAction)"
is-admin="$ctrl.isAdmin"
tags="$ctrl.tags"
endpoint-init-time="$ctrl.endpointInitTime"
></endpoint-item>
<endpoint-item
ng-if="!$ctrl.hasBackendPagination()"
@@ -47,6 +48,7 @@
on-edit="($ctrl.editAction)"
is-admin="$ctrl.isAdmin"
tags="$ctrl.tags"
endpoint-init-time="$ctrl.endpointInitTime"
></endpoint-item>
<div ng-if="$ctrl.state.loading" class="text-center text-muted" data-cy="home-loadingEndpoints">
Loading...

View File

@@ -25,7 +25,6 @@
placeholder="git username"
ng-change="$ctrl.onChangeUsername($ctrl.model.RepositoryUsername)"
data-cy="component-gitUsernameInput"
ng-required="$ctrl.model.RepositoryAuthentication"
/>
</div>
</div>

View File

@@ -76,7 +76,7 @@
<span style="margin-right: 5px;">
Items per page
</span>
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()">
<select class="form-control" ng-model="$ctrl.state.paginatedItemLimit" ng-change="$ctrl.changePaginationLimit()" data-cy="component-paginationSelect">
<option value="0">All</option>
<option value="10">10</option>
<option value="25">25</option>

View File

@@ -16,7 +16,7 @@ angular.module('portainer.app').factory('Notifications', [
service.error = function (title, e, fallbackText) {
var msg = fallbackText;
if (e.err && e.err.data && e.err.data.details) {
if (e.err && e.err.data && e.err.data.details && typeof e.err.data.details === 'string') {
msg = e.err.data.details;
} else if (e.err && e.err.data && e.err.data.message) {
msg = e.err.data.message;

View File

@@ -4,7 +4,7 @@
<div class="col-sm-6 col-sm-offset-3">
<!-- login box logo -->
<div class="row">
<img ng-if="!ctrl.logo" src="~@/assets/images/logo_alt.png" class="simple-box-logo" alt="Portainer" />
<img ng-if="!ctrl.logo" src="~@/assets/images/logo_alt.svg" class="simple-box-logo" alt="Portainer" />
<img ng-if="ctrl.logo" ng-src="{{ ctrl.logo }}" class="simple-box-logo" />
</div>
<!-- !login box logo -->

View File

@@ -1,5 +1,3 @@
import _ from 'lodash-es';
import { DockerHubViewModel } from 'Portainer/models/dockerhub';
import { RegistryTypes } from 'Portainer/models/registryTypes';
class EndpointRegistriesController {
@@ -19,9 +17,8 @@ class EndpointRegistriesController {
getRegistries() {
return this.$async(async () => {
try {
const dockerhub = new DockerHubViewModel();
const registries = await this.EndpointService.registries(this.endpointId);
this.registries = _.concat(dockerhub, registries);
this.registries = registries;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrieve registries');
}

View File

@@ -56,6 +56,7 @@
is-admin="isAdmin"
total-count="totalCount"
retrieve-page="getPaginatedEndpoints"
endpoint-init-time="state.homepageLoadTime"
></endpoint-list>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More