Compare commits
55 Commits
fix/EE-170
...
docs/add-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b83f660547 | ||
|
|
9460ed8e47 | ||
|
|
5ad3cacefd | ||
|
|
6ac9c4367e | ||
|
|
8aa03bb81b | ||
|
|
d14c7b0309 | ||
|
|
cbeb13636c | ||
|
|
a6138dd5a3 | ||
|
|
5752e74be6 | ||
|
|
cb37497444 | ||
|
|
0b64250647 | ||
|
|
45af1f3d8b | ||
|
|
fc52830c7d | ||
|
|
4890f50443 | ||
|
|
6d510c4f30 | ||
|
|
cad530ec04 | ||
|
|
e63732484a | ||
|
|
ec3233fb09 | ||
|
|
bcdc342cbd | ||
|
|
e1f725d01a | ||
|
|
b876f2d17d | ||
|
|
b0ec67826c | ||
|
|
b89d828878 | ||
|
|
e59df8134d | ||
|
|
092d217985 | ||
|
|
ad94162019 | ||
|
|
0efbf5bbf3 | ||
|
|
c26ba23c53 | ||
|
|
69096f664d | ||
|
|
48c762c98b | ||
|
|
488d86d200 | ||
|
|
f10e0e4124 | ||
|
|
5316cca3de | ||
|
|
4267304e50 | ||
|
|
deecbadce1 | ||
|
|
ecc9813750 | ||
|
|
24f11902b2 | ||
|
|
33118babdd | ||
|
|
2aec348814 | ||
|
|
4d63459d67 | ||
|
|
483559af09 | ||
|
|
1796545d2e | ||
|
|
a50795063c | ||
|
|
7c9f7a2a8b | ||
|
|
af8065e8c2 | ||
|
|
dc769b4c4d | ||
|
|
dd808bb7bd | ||
|
|
d911c50f1b | ||
|
|
f6f31b8872 | ||
|
|
1f4a7b32e3 | ||
|
|
a781021072 | ||
|
|
9492e30dc2 | ||
|
|
d2cbdf935a | ||
|
|
05efac44f6 | ||
|
|
555c9f238f |
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal 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?
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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("")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 8.2 KiB |
|
Before Width: | Height: | Size: 1006 B After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 8.5 KiB |
60
app/assets/images/logo_alt.svg
Normal 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 |
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -8,6 +8,7 @@ angular.module('portainer.kubernetes').component('kubernetesApplicationsDatatabl
|
||||
titleIcon: '@',
|
||||
dataset: '<',
|
||||
tableKey: '@',
|
||||
settingsKey: '@',
|
||||
orderBy: '@',
|
||||
reverseOrder: '<',
|
||||
removeAction: '<',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -118,4 +118,5 @@ export const KubernetesDeploymentTypes = Object.freeze({
|
||||
GIT: 'git',
|
||||
CONTENT: 'content',
|
||||
APPLICATION_FORM: 'application form',
|
||||
URL: 'url',
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const CUSTOM_TEMPLATES_TYPES = {
|
||||
STANDALONE: 1,
|
||||
SWARM: 2,
|
||||
SWARM: 1,
|
||||
STANDALONE: 2,
|
||||
KUBERNETES: 3,
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -10,6 +10,7 @@ angular.module('portainer.app').component('endpointItem', {
|
||||
onEdit: '<',
|
||||
isAdmin: '<',
|
||||
tags: '<',
|
||||
endpointInitTime: '<',
|
||||
},
|
||||
controller: EndpointItemController,
|
||||
});
|
||||
|
||||
@@ -14,5 +14,6 @@ angular.module('portainer.app').component('endpointList', {
|
||||
isAdmin: '<',
|
||||
totalCount: '<',
|
||||
retrievePage: '<',
|
||||
endpointInitTime: '<',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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...
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
is-admin="isAdmin"
|
||||
total-count="totalCount"
|
||||
retrieve-page="getPaginatedEndpoints"
|
||||
endpoint-init-time="state.homepageLoadTime"
|
||||
></endpoint-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||