resolve conflicts

pull/5596/head
ArrisLee 2021-09-08 11:27:53 +12:00
commit 1991475437
62 changed files with 1765 additions and 484 deletions

54
.github/stale.yml vendored
View File

@ -1,54 +0,0 @@
# Config for Stalebot, limited to only `issues`
only: issues
# Issues config
issues:
daysUntilStale: 60
daysUntilClose: 7
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Issues with these labels will never be considered stale
exemptLabels:
- kind/enhancement
- kind/question
- kind/style
- kind/workaround
- kind/refactor
- bug/need-confirmation
- bug/confirmed
- status/discuss
# Only issues with all of these labels are checked if stale. Defaults to `[]` (disabled)
onlyLabels: []
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: true
# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: true
# Label to use when marking an issue as stale
staleLabel: status/stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been marked as stale as it has not had recent activity,
it will be closed if no further activity occurs in the next 7 days.
If you believe that it has been incorrectly labelled as stale,
leave a comment and the label will be removed.
# Comment to post when removing the stale label.
# unmarkComment: >
# Your comment here.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
Since no further activity has appeared on this issue it will be closed.
If you believe that it has been incorrectly closed, leave a comment
mentioning `ametdoohan`, `balasu` or `keverv` and one of our staff will then review the issue.
Note - If it is an old bug report, make sure that it is reproduceable in the
latest version of Portainer as it may have already been fixed.

27
.github/workflows/stale.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: Close Stale Issues
on:
schedule:
- cron: '0 12 * * *'
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v4.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Issue Config
days-before-issue-stale: 60
days-before-issue-close: 7
stale-issue-label: 'status/stale'
exempt-all-issue-milestones: true # Do not stale issues in a milestone
exempt-issue-labels: kind/enhancement, kind/style, kind/workaround, kind/refactor, bug/need-confirmation, bug/confirmed, status/discuss
stale-issue-message: 'This issue has been marked as stale as it has not had recent activity, it will be closed if no further activity occurs in the next 7 days. If you believe that it has been incorrectly labelled as stale, leave a comment and the label will be removed.'
close-issue-message: 'Since no further activity has appeared on this issue it will be closed. If you believe that it has been incorrectly closed, leave a comment mentioning `portainer/support` and one of our staff will then review the issue. Note - If it is an old bug report, make sure that it is reproduceable in the latest version of Portainer as it may have already been fixed.'
# Pull Request Config
days-before-pr-stale: -1 # Do not stale pull request
days-before-pr-close: -1 # Do not close pull request

View File

@ -28,7 +28,6 @@ import (
"github.com/portainer/portainer/api/kubernetes"
kubecli "github.com/portainer/portainer/api/kubernetes/cli"
"github.com/portainer/portainer/api/ldap"
"github.com/portainer/portainer/api/libcompose"
"github.com/portainer/portainer/api/oauth"
"github.com/portainer/portainer/api/scheduler"
"github.com/portainer/portainer/api/stacks"
@ -86,18 +85,17 @@ func shutdownDatastore(shutdownCtx context.Context, datastore portainer.DataStor
datastore.Close()
}
func initComposeStackManager(assetsPath string, dataStorePath string, reverseTunnelService portainer.ReverseTunnelService, proxyManager *proxy.Manager) portainer.ComposeStackManager {
composeWrapper, err := exec.NewComposeStackManager(assetsPath, dataStorePath, proxyManager)
func initComposeStackManager(assetsPath string, configPath string, reverseTunnelService portainer.ReverseTunnelService, proxyManager *proxy.Manager) portainer.ComposeStackManager {
composeWrapper, err := exec.NewComposeStackManager(assetsPath, configPath, proxyManager)
if err != nil {
log.Printf("[INFO] [main,compose] [message: falling-back to libcompose] [error: %s]", err)
return libcompose.NewComposeStackManager(dataStorePath, reverseTunnelService)
log.Fatalf("failed creating compose manager: %s", err)
}
return composeWrapper
}
func initSwarmStackManager(assetsPath string, dataStorePath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) (portainer.SwarmStackManager, error) {
return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService)
func initSwarmStackManager(assetsPath string, configPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) (portainer.SwarmStackManager, error) {
return exec.NewSwarmStackManager(assetsPath, configPath, signatureService, fileService, reverseTunnelService)
}
func initKubernetesDeployer(kubernetesTokenCacheManager *kubeproxy.TokenCacheManager, kubernetesClientFactory *kubecli.ClientFactory, dataStore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, proxyManager *proxy.Manager, assetsPath string) portainer.KubernetesDeployer {
@ -446,14 +444,17 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
authorizationService := authorization.NewService(dataStore)
authorizationService.K8sClientFactory = kubernetesClientFactory
swarmStackManager, err := initSwarmStackManager(*flags.Assets, *flags.Data, digitalSignatureService, fileService, reverseTunnelService)
if err != nil {
log.Fatalf("failed initializing swarm stack manager: %v", err)
}
kubernetesTokenCacheManager := kubeproxy.NewTokenCacheManager()
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
composeStackManager := initComposeStackManager(*flags.Assets, *flags.Data, reverseTunnelService, proxyManager)
dockerConfigPath := fileService.GetDockerConfigPath()
composeStackManager := initComposeStackManager(*flags.Assets, dockerConfigPath, reverseTunnelService, proxyManager)
swarmStackManager, err := initSwarmStackManager(*flags.Assets, dockerConfigPath, digitalSignatureService, fileService, reverseTunnelService)
if err != nil {
log.Fatalf("failed initializing swarm stack manager: %s", err)
}
kubernetesDeployer := initKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, digitalSignatureService, proxyManager, *flags.Assets)

View File

@ -1,6 +1,7 @@
package exec
import (
"context"
"fmt"
"os"
"path"
@ -8,7 +9,10 @@ import (
"strings"
"github.com/pkg/errors"
wrapper "github.com/portainer/docker-compose-wrapper"
libstack "github.com/portainer/docker-compose-wrapper"
"github.com/portainer/docker-compose-wrapper/compose"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/api/http/proxy/factory"
@ -16,33 +20,31 @@ import (
// ComposeStackManager is a wrapper for docker-compose binary
type ComposeStackManager struct {
wrapper *wrapper.ComposeWrapper
configPath string
deployer libstack.Deployer
proxyManager *proxy.Manager
}
// NewComposeStackManager returns a docker-compose wrapper if corresponding binary present, otherwise nil
func NewComposeStackManager(binaryPath string, configPath string, proxyManager *proxy.Manager) (*ComposeStackManager, error) {
wrap, err := wrapper.NewComposeWrapper(binaryPath)
deployer, err := compose.NewComposeDeployer(binaryPath, configPath)
if err != nil {
return nil, err
}
return &ComposeStackManager{
wrapper: wrap,
deployer: deployer,
proxyManager: proxyManager,
configPath: configPath,
}, nil
}
// ComposeSyntaxMaxVersion returns the maximum supported version of the docker compose syntax
func (w *ComposeStackManager) ComposeSyntaxMaxVersion() string {
func (manager *ComposeStackManager) ComposeSyntaxMaxVersion() string {
return portainer.ComposeSyntaxMaxVersion
}
// Up builds, (re)creates and starts containers in the background. Wraps `docker-compose up -d` command
func (w *ComposeStackManager) Up(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
url, proxy, err := w.fetchEndpointProxy(endpoint)
func (manager *ComposeStackManager) Up(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error {
url, proxy, err := manager.fetchEndpointProxy(endpoint)
if err != nil {
return errors.Wrap(err, "failed to fetch endpoint proxy")
}
@ -57,13 +59,12 @@ func (w *ComposeStackManager) Up(stack *portainer.Stack, endpoint *portainer.End
}
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
_, err = w.wrapper.Up(filePaths, stack.ProjectPath, url, stack.Name, envFilePath, w.configPath)
return errors.Wrap(err, "failed to deploy a stack")
return manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath)
}
// Down stops and removes containers, networks, images, and volumes. Wraps `docker-compose down --remove-orphans` command
func (w *ComposeStackManager) Down(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
url, proxy, err := w.fetchEndpointProxy(endpoint)
func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error {
url, proxy, err := manager.fetchEndpointProxy(endpoint)
if err != nil {
return err
}
@ -73,27 +74,26 @@ func (w *ComposeStackManager) Down(stack *portainer.Stack, endpoint *portainer.E
filePaths := append([]string{stack.EntryPoint}, stack.AdditionalFiles...)
_, err = w.wrapper.Down(filePaths, stack.ProjectPath, url, stack.Name)
return err
return manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
}
// NormalizeStackName returns a new stack name with unsupported characters replaced
func (w *ComposeStackManager) NormalizeStackName(name string) string {
func (manager *ComposeStackManager) NormalizeStackName(name string) string {
r := regexp.MustCompile("[^a-z0-9]+")
return r.ReplaceAllString(strings.ToLower(name), "")
}
func (w *ComposeStackManager) fetchEndpointProxy(endpoint *portainer.Endpoint) (string, *factory.ProxyServer, error) {
func (manager *ComposeStackManager) fetchEndpointProxy(endpoint *portainer.Endpoint) (string, *factory.ProxyServer, error) {
if strings.HasPrefix(endpoint.URL, "unix://") || strings.HasPrefix(endpoint.URL, "npipe://") {
return "", nil, nil
}
proxy, err := w.proxyManager.CreateAgentProxyServer(endpoint)
proxy, err := manager.proxyManager.CreateAgentProxyServer(endpoint)
if err != nil {
return "", nil, err
}
return fmt.Sprintf("http://127.0.0.1:%d", proxy.Port), proxy, nil
return fmt.Sprintf("tcp://127.0.0.1:%d", proxy.Port), proxy, nil
}
func createEnvFile(stack *portainer.Stack) (string, error) {

View File

@ -1,6 +1,7 @@
package exec
import (
"context"
"fmt"
"log"
"os"
@ -47,7 +48,9 @@ func Test_UpAndDown(t *testing.T) {
t.Fatalf("Failed creating manager: %s", err)
}
err = w.Up(stack, endpoint)
ctx := context.TODO()
err = w.Up(ctx, stack, endpoint)
if err != nil {
t.Fatalf("Error calling docker-compose up: %s", err)
}
@ -56,7 +59,7 @@ func Test_UpAndDown(t *testing.T) {
t.Fatal("container should exist")
}
err = w.Down(stack, endpoint)
err = w.Down(ctx, stack, endpoint)
if err != nil {
t.Fatalf("Error calling docker-compose down: %s", err)
}

View File

@ -19,7 +19,7 @@ import (
// SwarmStackManager represents a service for managing stacks.
type SwarmStackManager struct {
binaryPath string
dataPath string
configPath string
signatureService portainer.DigitalSignatureService
fileService portainer.FileService
reverseTunnelService portainer.ReverseTunnelService
@ -27,16 +27,16 @@ type SwarmStackManager struct {
// NewSwarmStackManager initializes a new SwarmStackManager service.
// It also updates the configuration of the Docker CLI binary.
func NewSwarmStackManager(binaryPath, dataPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) (*SwarmStackManager, error) {
func NewSwarmStackManager(binaryPath, configPath string, signatureService portainer.DigitalSignatureService, fileService portainer.FileService, reverseTunnelService portainer.ReverseTunnelService) (*SwarmStackManager, error) {
manager := &SwarmStackManager{
binaryPath: binaryPath,
dataPath: dataPath,
configPath: configPath,
signatureService: signatureService,
fileService: fileService,
reverseTunnelService: reverseTunnelService,
}
err := manager.updateDockerCLIConfiguration(dataPath)
err := manager.updateDockerCLIConfiguration(manager.configPath)
if err != nil {
return nil, err
}
@ -46,7 +46,7 @@ func NewSwarmStackManager(binaryPath, dataPath string, signatureService portaine
// Login executes the docker login command against a list of registries (including DockerHub).
func (manager *SwarmStackManager) Login(registries []portainer.Registry, endpoint *portainer.Endpoint) {
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint)
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
for _, registry := range registries {
if registry.Authentication {
registryArgs := append(args, "login", "--username", registry.Username, "--password", registry.Password, registry.URL)
@ -57,7 +57,7 @@ func (manager *SwarmStackManager) Login(registries []portainer.Registry, endpoin
// Logout executes the docker logout command.
func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error {
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint)
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
args = append(args, "logout")
return runCommandAndCaptureStdErr(command, args, nil, "")
}
@ -65,7 +65,7 @@ func (manager *SwarmStackManager) Logout(endpoint *portainer.Endpoint) error {
// Deploy executes the docker stack deploy command.
func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, endpoint *portainer.Endpoint) error {
filePaths := stackutils.GetStackFilePaths(stack)
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint)
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
if prune {
args = append(args, "stack", "deploy", "--prune", "--with-registry-auth")
@ -85,7 +85,7 @@ func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, end
// Remove executes the docker stack rm command.
func (manager *SwarmStackManager) Remove(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.dataPath, endpoint)
command, args := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
args = append(args, "stack", "rm", stack.Name)
return runCommandAndCaptureStdErr(command, args, nil, "")
}
@ -109,7 +109,7 @@ func runCommandAndCaptureStdErr(command string, args []string, env []string, wor
return nil
}
func (manager *SwarmStackManager) prepareDockerCommandAndArgs(binaryPath, dataPath string, endpoint *portainer.Endpoint) (string, []string) {
func (manager *SwarmStackManager) prepareDockerCommandAndArgs(binaryPath, configPath string, endpoint *portainer.Endpoint) (string, []string) {
// Assume Linux as a default
command := path.Join(binaryPath, "docker")
@ -118,7 +118,7 @@ func (manager *SwarmStackManager) prepareDockerCommandAndArgs(binaryPath, dataPa
}
args := make([]string, 0)
args = append(args, "--config", dataPath)
args = append(args, "--config", configPath)
endpointURL := endpoint.URL
if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment {
@ -145,8 +145,8 @@ func (manager *SwarmStackManager) prepareDockerCommandAndArgs(binaryPath, dataPa
return command, args
}
func (manager *SwarmStackManager) updateDockerCLIConfiguration(dataPath string) error {
configFilePath := path.Join(dataPath, "config.json")
func (manager *SwarmStackManager) updateDockerCLIConfiguration(configPath string) error {
configFilePath := path.Join(configPath, "config.json")
config, err := manager.retrieveConfigurationFromDisk(configFilePath)
if err != nil {
return err

View File

@ -43,6 +43,8 @@ const (
BinaryStorePath = "bin"
// EdgeJobStorePath represents the subfolder where schedule files are stored.
EdgeJobStorePath = "edge_jobs"
// DockerConfigPath represents the subfolder where docker configuration is stored.
DockerConfigPath = "docker_config"
// ExtensionRegistryManagementStorePath represents the subfolder where files related to the
// registry management extension are stored.
ExtensionRegistryManagementStorePath = "extensions"
@ -100,6 +102,11 @@ func NewService(dataStorePath, fileStorePath string) (*Service, error) {
return nil, err
}
err = service.createDirectoryInStore(DockerConfigPath)
if err != nil {
return nil, err
}
return service, nil
}
@ -108,6 +115,11 @@ func (service *Service) GetBinaryFolder() string {
return path.Join(service.fileStorePath, BinaryStorePath)
}
// GetDockerConfigPath returns the full path to the docker config store on the filesystem
func (service *Service) GetDockerConfigPath() string {
return path.Join(service.fileStorePath, DockerConfigPath)
}
// RemoveDirectory removes a directory on the filesystem.
func (service *Service) RemoveDirectory(directoryPath string) error {
return os.RemoveAll(directoryPath)

View File

@ -3,15 +3,21 @@ module github.com/portainer/portainer/api
go 1.16
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.4.16
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/boltdb/bolt v1.3.1
github.com/containerd/containerd v1.3.1 // indirect
github.com/coreos/go-semver v0.3.0
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/docker/cli v0.0.0-20191126203649-54d085b857e9
github.com/docker/docker v0.0.0-00010101000000-000000000000
github.com/docker/distribution v2.7.1+incompatible // indirect
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/g07cha/defender v0.0.0-20180505193036-5665c627c814
github.com/go-git/go-git/v5 v5.3.0
github.com/go-ldap/ldap/v3 v3.1.8
@ -21,23 +27,27 @@ require (
github.com/gorilla/websocket v1.4.1
github.com/joho/godotenv v1.3.0
github.com/jpillora/chisel v0.0.0-20190724232113-f3a8df20e389
github.com/json-iterator/go v1.1.8
github.com/json-iterator/go v1.1.10
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c
github.com/mattn/go-shellwords v1.0.6 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.1 // indirect
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6
github.com/pkg/errors v0.9.1
github.com/portainer/docker-compose-wrapper v0.0.0-20210810234209-d01bc85eb481
github.com/portainer/libcompose v0.5.3
github.com/portainer/docker-compose-wrapper v0.0.0-20210906052132-ef24824f7548
github.com/portainer/libcrypto v0.0.0-20210422035235-c652195c5c3a
github.com/portainer/libhttp v0.0.0-20190806161843-ba068f58be33
github.com/robfig/cron/v3 v3.0.1
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gotest.tools v2.2.0+incompatible // indirect
k8s.io/api v0.17.2
k8s.io/apimachinery v0.17.2
k8s.io/client-go v0.17.2

View File

@ -1,8 +1,8 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
@ -11,40 +11,31 @@ github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxB
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.3.8/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk=
github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 h1:AUNCr9CiJuwrRYS3XieqF+Z9B9gNxo/eANAJCF2eiN4=
github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZOs7ygH5BgQp4N+aYrZ2DNpWZ1KG3VOSOM=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4=
github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/containerd/containerd v1.3.1 h1:LdbWxLhkAIxGO7h3mATHkyav06WuDs/yTWxIljJOTks=
github.com/containerd/containerd v1.3.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -56,23 +47,16 @@ github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9 h1:74lLNRzvsdIlkTgfD
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/docker/cli v0.0.0-20190711175710-5b38d82aa076/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v0.0.0-20191126203649-54d085b857e9 h1:Q6D6b2iRKhvtL3Wj9p0SyPOvUDJ1ht62mbiBoNJ3Aus=
github.com/docker/cli v0.0.0-20191126203649-54d085b857e9/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
github.com/docker/engine v1.4.2-0.20200204220554-5f6d6f3f2203 h1:QeBh8wW8pIZKlXxlMOQ8hSCMdJA+2Z/bD/iDyCAS8XU=
github.com/docker/engine v1.4.2-0.20200204220554-5f6d6f3f2203/go.mod h1:3CPr2caMgTHxxIAZgEMd3uLYPDlRvPqCpyeRf6ncPcY=
github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o=
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82 h1:X0fj836zx99zFu83v/M79DuBn84IL/Syx1SY6Y5ZEMA=
github.com/docker/go-metrics v0.0.0-20181218153428-b84716841b82/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4=
github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
@ -82,7 +66,6 @@ github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@ -102,20 +85,15 @@ github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbK
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.3.0 h1:8WKMtJR2j8RntEXR/uvTKagfEt4GYlwQ7mntE4+0GWc=
github.com/go-git/go-git/v5 v5.3.0/go.mod h1:xdX4bWJ48aOrdhnl2XqHYstHbbp6+LFS4r4X+lNVprw=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.1.8 h1:5vU/2jOh9HqprwXp8aF915s9p6Z8wmbSEVF7/gdTFhM=
github.com/go-ldap/ldap/v3 v3.1.8/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@ -124,7 +102,6 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -142,8 +119,6 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v0.0.0-20160317213430-0eeaf8392f5b/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
@ -174,12 +149,10 @@ github.com/jpillora/requestlog v0.0.0-20181015073026-df8817be5f82/go.mod h1:w8bu
github.com/jpillora/sizestr v0.0.0-20160130011556-e2ea2fa42fb9 h1:0c9jcgBtHRtDU//jTrcCgWG6UHjMZytiq/3WhraNgUM=
github.com/jpillora/sizestr v0.0.0-20160130011556-e2ea2fa42fb9/go.mod h1:1ffp+CRe0eAwwRb0/BownUAjMBsmTLwgAvRbfj9dRwE=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck=
github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
@ -187,11 +160,9 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c h1:N7A4JCA2G+j5fuFxCsJqjFU/sZe0mj8H0sSoSwbaikw=
github.com/koding/websocketproxy v0.0.0-20181220232114-7ed82d81a28c/go.mod h1:Nn5wlyECw3iJrzi0AhIWg+AJUb4PlRQVW4/3XHH1LZA=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v0.0.0-20150511174710-5cf931ef8f76/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@ -199,8 +170,6 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
@ -212,10 +181,9 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@ -225,49 +193,29 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420 h1:Yu3681ykYHDfLoI6XVjL4JWmkE+3TX9yfIWwRCh1kFM=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/image-spec v0.0.0-20170515205857-f03dbe35d449 h1:Aq8iG72akPb/kszE7ksZ5ldV+JYPYii/KZOxlpJF07s=
github.com/opencontainers/image-spec v0.0.0-20170515205857-f03dbe35d449/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/runc v0.0.0-20161109192122-51371867a01c h1:iOMba/KmaXgSX5PFKu1u6s+DZXiq+EzPayawa76w6aA=
github.com/opencontainers/runc v0.0.0-20161109192122-51371867a01c/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 h1:lNCW6THrCKBiJBpz8kbVGjC7MgdCGKwuvBgc7LoD6sw=
github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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-20210810234209-d01bc85eb481 h1:5c8N9Gh21Ja/9EIpfyHFmQvTCKgOjnRhosmo0ZshkFk=
github.com/portainer/docker-compose-wrapper v0.0.0-20210810234209-d01bc85eb481/go.mod h1:WxDlJWZxCnicdLCPnLNEv7/gRhjeIVuCGmsv+iOPH3c=
github.com/portainer/libcompose v0.5.3 h1:tE4WcPuGvo+NKeDkDWpwNavNLZ5GHIJ4RvuZXsI9uI8=
github.com/portainer/libcompose v0.5.3/go.mod h1:7SKd/ho69rRKHDFSDUwkbMcol2TMKU5OslDsajr8Ro8=
github.com/portainer/docker-compose-wrapper v0.0.0-20210906052132-ef24824f7548 h1:5I9j0e6f9KG/RV6YBKWyks8LSHheE+ltJgpMyyWYUoo=
github.com/portainer/docker-compose-wrapper v0.0.0-20210906052132-ef24824f7548/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/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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@ -285,17 +233,15 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/urfave/cli v1.21.0/go.mod h1:lxDj6qX9Q6lWQxIrbrT0nwecwUtRnhVZAJjJZrVUZZQ=
github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI=
github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.1.0 h1:ngVtJC9TY/lg0AA/1k48FYhBrhRoFlEmWzsehpNAaZg=
github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181015023909-0c41d7ab0a0e/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@ -307,18 +253,15 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181017193950-04a2e542c03f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs=
@ -331,8 +274,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456 h1:ng0gs1AKnRRuEMZoTLLlbOd+C17zUDepwGQBb/n+JVg=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
@ -351,9 +292,7 @@ golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -363,9 +302,8 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7 h1:ZUjXAXmrAyrmmCPHgCA/vChHcpsX27MZ3yBonD/z1KE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -394,7 +332,6 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.17.2 h1:NF1UFXcKN7/OOv1uxdRz3qfra8AHsPav5M93hlV9+Dc=
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
k8s.io/apimachinery v0.17.2 h1:hwDQQFbdRlpnnsR64Asdi55GyCaIP/3WQpMmbNBeWr4=

View File

@ -1,6 +1,7 @@
package stacks
import (
"context"
"errors"
"fmt"
"net/http"
@ -177,7 +178,7 @@ func (handler *Handler) deleteStack(stack *portainer.Stack, endpoint *portainer.
return handler.SwarmStackManager.Remove(stack, endpoint)
}
if stack.Type == portainer.DockerComposeStack {
return handler.ComposeStackManager.Down(stack, endpoint)
return handler.ComposeStackManager.Down(context.TODO(), stack, endpoint)
}
if stack.Type == portainer.KubernetesStack {
return nil

View File

@ -1,6 +1,7 @@
package stacks
import (
"context"
"errors"
"fmt"
"net/http"
@ -120,7 +121,7 @@ func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *http
func (handler *Handler) startStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
switch stack.Type {
case portainer.DockerComposeStack:
return handler.ComposeStackManager.Up(stack, endpoint)
return handler.ComposeStackManager.Up(context.TODO(), stack, endpoint)
case portainer.DockerSwarmStack:
return handler.SwarmStackManager.Deploy(stack, true, endpoint)
}

View File

@ -1,6 +1,7 @@
package stacks
import (
"context"
"errors"
"net/http"
@ -104,7 +105,7 @@ func (handler *Handler) stackStop(w http.ResponseWriter, r *http.Request) *httpe
func (handler *Handler) stopStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
switch stack.Type {
case portainer.DockerComposeStack:
return handler.ComposeStackManager.Down(stack, endpoint)
return handler.ComposeStackManager.Down(context.TODO(), stack, endpoint)
case portainer.DockerSwarmStack:
return handler.SwarmStackManager.Remove(stack, endpoint)
}

View File

@ -17,6 +17,7 @@ import (
type userUpdatePayload struct {
Username string `validate:"required" example:"bob"`
Password string `validate:"required" example:"cg9Wgky3"`
UserTheme string `example:"dark"`
// User role (1 for administrator account and 2 for regular account)
Role int `validate:"required" enums:"1,2" example:"2"`
}
@ -104,6 +105,10 @@ func (handler *Handler) userUpdate(w http.ResponseWriter, r *http.Request) *http
user.Role = portainer.UserRole(payload.Role)
}
if payload.UserTheme != "" {
user.UserTheme = payload.UserTheme
}
err = handler.DataStore.User().UpdateUser(user.ID, user)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to persist user changes inside the database", err}

View File

@ -68,7 +68,7 @@ func (factory *ProxyFactory) NewAgentProxy(endpoint *portainer.Endpoint) (*Proxy
return nil, errors.Wrap(err, "failed starting proxy server")
}
return proxyServer, err
return proxyServer, nil
}
func (proxy *ProxyServer) start() error {

View File

@ -1,148 +0,0 @@
package libcompose
import (
"context"
"fmt"
"path"
"path/filepath"
"regexp"
"strings"
"github.com/portainer/libcompose/config"
"github.com/portainer/libcompose/docker"
"github.com/portainer/libcompose/docker/client"
"github.com/portainer/libcompose/docker/ctx"
"github.com/portainer/libcompose/lookup"
"github.com/portainer/libcompose/project"
"github.com/portainer/libcompose/project/options"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/stackutils"
)
const (
dockerClientVersion = "1.24"
composeSyntaxMaxVersion = "2"
)
// ComposeStackManager represents a service for managing compose stacks.
type ComposeStackManager struct {
dataPath string
reverseTunnelService portainer.ReverseTunnelService
}
// NewComposeStackManager initializes a new ComposeStackManager service.
func NewComposeStackManager(dataPath string, reverseTunnelService portainer.ReverseTunnelService) *ComposeStackManager {
return &ComposeStackManager{
dataPath: dataPath,
reverseTunnelService: reverseTunnelService,
}
}
func (manager *ComposeStackManager) createClient(endpoint *portainer.Endpoint) (client.Factory, error) {
endpointURL := endpoint.URL
if endpoint.Type == portainer.EdgeAgentOnDockerEnvironment {
tunnel := manager.reverseTunnelService.GetTunnelDetails(endpoint.ID)
endpointURL = fmt.Sprintf("tcp://127.0.0.1:%d", tunnel.Port)
}
clientOpts := client.Options{
Host: endpointURL,
APIVersion: dockerClientVersion,
}
if endpoint.TLSConfig.TLS {
clientOpts.TLS = endpoint.TLSConfig.TLS
clientOpts.TLSVerify = !endpoint.TLSConfig.TLSSkipVerify
clientOpts.TLSCAFile = endpoint.TLSConfig.TLSCACertPath
clientOpts.TLSCertFile = endpoint.TLSConfig.TLSCertPath
clientOpts.TLSKeyFile = endpoint.TLSConfig.TLSKeyPath
}
return client.NewDefaultFactory(clientOpts)
}
// ComposeSyntaxMaxVersion returns the maximum supported version of the docker compose syntax
func (manager *ComposeStackManager) ComposeSyntaxMaxVersion() string {
return composeSyntaxMaxVersion
}
// NormalizeStackName returns a new stack name with unsupported characters replaced
func (manager *ComposeStackManager) NormalizeStackName(name string) string {
// this is coming from libcompose
// https://github.com/portainer/libcompose/blob/master/project/context.go#L117-L120
r := regexp.MustCompile("[^a-z0-9]+")
return r.ReplaceAllString(strings.ToLower(name), "")
}
// Up will deploy a compose stack (equivalent of docker-compose up)
func (manager *ComposeStackManager) Up(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
clientFactory, err := manager.createClient(endpoint)
if err != nil {
return err
}
env := make(map[string]string)
for _, envvar := range stack.Env {
env[envvar.Name] = envvar.Value
}
filePaths := stackutils.GetStackFilePaths(stack)
proj, err := docker.NewProject(&ctx.Context{
ConfigDir: manager.dataPath,
Context: project.Context{
ComposeFiles: filePaths,
EnvironmentLookup: &lookup.ComposableEnvLookup{
Lookups: []config.EnvironmentLookup{
&lookup.EnvfileLookup{
Path: filepath.Join(stack.ProjectPath, ".env"),
},
&lookup.MapLookup{
Vars: env,
},
},
},
ProjectName: stack.Name,
},
ClientFactory: clientFactory,
}, nil)
if err != nil {
return err
}
return proj.Up(context.Background(), options.Up{})
}
// Down will shutdown a compose stack (equivalent of docker-compose down)
func (manager *ComposeStackManager) Down(stack *portainer.Stack, endpoint *portainer.Endpoint) error {
clientFactory, err := manager.createClient(endpoint)
if err != nil {
return err
}
var composeFiles []string
for _, file := range append([]string{stack.EntryPoint}, stack.AdditionalFiles...) {
composeFiles = append(composeFiles, path.Join(stack.ProjectPath, file))
}
proj, err := docker.NewProject(&ctx.Context{
Context: project.Context{
ComposeFiles: composeFiles,
ProjectName: stack.Name,
},
ClientFactory: clientFactory,
}, nil)
if err != nil {
return err
}
return proj.Down(context.Background(), options.Down{RemoveVolume: false, RemoveOrphans: true})
}
func stackFilePaths(stack *portainer.Stack) []string {
var filePaths []string
for _, file := range append([]string{stack.EntryPoint}, stack.AdditionalFiles...) {
filePaths = append(filePaths, path.Join(stack.ProjectPath, file))
}
return filePaths
}

View File

@ -1003,6 +1003,8 @@ type (
ID UserID `json:"Id" example:"1"`
Username string `json:"Username" example:"bob"`
Password string `json:"Password,omitempty" example:"passwd"`
// User Theme
UserTheme string `example:"dark"`
// User role (1 for administrator account and 2 for regular account)
Role UserRole `json:"Role" example:"1"`
@ -1054,8 +1056,8 @@ type (
ComposeStackManager interface {
ComposeSyntaxMaxVersion() string
NormalizeStackName(name string) string
Up(stack *Stack, endpoint *Endpoint) error
Down(stack *Stack, endpoint *Endpoint) error
Up(ctx context.Context, stack *Stack, endpoint *Endpoint) error
Down(ctx context.Context, stack *Stack, endpoint *Endpoint) error
}
// CryptoService represents a service for encrypting/hashing data
@ -1179,6 +1181,7 @@ type (
// FileService represents a service for managing files
FileService interface {
GetDockerConfigPath() string
GetFileContent(filePath string) ([]byte, error)
Rename(oldPath, newPath string) error
RemoveDirectory(directoryPath string) error

View File

@ -1,6 +1,7 @@
package stacks
import (
"context"
"os"
"sync"
@ -50,7 +51,7 @@ func (d *stackDeployer) DeployComposeStack(stack *portainer.Stack, endpoint *por
d.swarmStackManager.Login(registries, endpoint)
defer d.swarmStackManager.Logout(endpoint)
return d.composeStackManager.Up(stack, endpoint)
return d.composeStackManager.Up(context.TODO(), stack, endpoint)
}
func (d *stackDeployer) DeployKubernetesStack(stack *portainer.Stack, endpoint *portainer.Endpoint) error {

View File

@ -59,10 +59,10 @@ body,
}
.form-section-title {
border-bottom: 1px solid #777;
border-bottom: 1px solid var(--border-form-section-title-color);
margin-top: 5px;
margin-bottom: 15px;
color: #777;
color: var(--text-form-section-title-color);
padding-left: 0;
}
@ -108,12 +108,33 @@ a[ng-click] {
pointer-events: none;
}
.datatable-highlighted {
background-color: var(--bg-item-highlighted-color);
}
.datatable-unhighlighted {
background-color: var(--bg-item-highlighted-null-color);
}
.service-datatable {
background-color: var(--bg-item-highlighted-color);
padding: 2px;
}
.service-datatable thead {
background-color: var(--bg-service-datatable-thead) !important;
}
.service-datatable tbody {
background-color: var(--bg-service-datatable-tbody);
}
.tooltip.portainer-tooltip .tooltip-inner {
font-family: Montserrat;
background-color: #ffffff;
background-color: var(--bg-tooltip-color);
padding: 0.833em 1em;
color: #333333;
border: 1px solid #d4d4d5;
color: var(--text-tooltip-color);
border: 1px solid var(--border-tooltip-color);
border-radius: 0.14285714rem;
box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15);
}
@ -145,7 +166,7 @@ a[ng-click] {
.fa.blue-icon,
.fab.blue-icon {
color: #337ab7;
color: var(--blue-2);
}
.text-warning {
@ -207,25 +228,25 @@ a[ng-click] {
padding: 0.7rem;
margin-bottom: 0.7rem;
cursor: pointer;
border: 1px solid #cccccc;
border: 1px solid var(--border-blocklist-color);
border-radius: 2px;
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
box-shadow: var(--shadow-box-color);
}
.blocklist-item--disabled {
cursor: auto;
background-color: #ececec;
background-color: var(--grey-12);
}
.blocklist-item--selected {
border: 2px solid #bbbbbb;
background-color: #ececec;
color: #2d3e63;
background-color: var(--bg-blocklist-item-selected-color);
border: 2px solid var(--border-blocklist-item-selected-color);
color: var(--text-blocklist-item-selected-color);
}
.blocklist-item:hover {
background-color: #ececec;
color: #2d3e63;
background-color: var(--bg-blocklist-hover-color);
color: var(--text-blocklist-hover-color);
}
.blocklist-item-box {
@ -360,7 +381,7 @@ a[ng-click] {
.panel-body {
padding-top: 30px;
background-color: #ffffff;
background-color: var(--white-color) fff;
}
.pagination-controls {
@ -443,8 +464,8 @@ a[ng-click] {
display: inline-block;
padding: 0px 6px;
margin-left: 10px;
color: #555555;
background-color: #fff;
color: var(--text-small-select-color);
background-color: var(--bg-small-select-color);
background-image: none;
border-radius: 4px;
font-size: 14px;
@ -462,11 +483,11 @@ a[ng-click] {
}
.visualizer_container .node {
border: 1px dashed #337ab7;
border: 1px dashed var(--blue-2);
background-color: rgb(51, 122, 183);
background-color: rgba(51, 122, 183, 0.1);
border-radius: 4px;
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
box-shadow: 0 3px 10px -2px var(--grey-50);
padding: 15px;
margin: 5px;
}
@ -476,7 +497,7 @@ a[ng-click] {
flex-direction: column;
justify-content: center;
text-align: center;
border-bottom: 1px solid #777;
border-bottom: 1px solid var(--grey-26);
padding-bottom: 10px;
}
@ -486,7 +507,7 @@ a[ng-click] {
}
.visualizer_container .node .node_info .node_labels {
border-top: 1px solid #777;
border-top: 1px solid var(--grey-26);
padding-top: 10px;
margin-top: 10px;
}
@ -503,9 +524,9 @@ a[ng-click] {
}
.visualizer_container .node .tasks .task {
border: 1px solid #333333;
border: 1px solid var(--grey-6);
border-radius: 2px;
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
box-shadow: 0 3px 10px -2px var(--grey-50);
padding: 10px;
margin: 5px;
font-size: 10px;
@ -547,9 +568,9 @@ a[ng-click] {
.log_viewer {
height: 100%;
overflow-y: scroll;
color: black;
color: var(--text-log-viewer-color);
font-size: 0.85em;
background-color: white;
background-color: var(--bg-log-viewer-color);
}
.log_viewer.wrap_lines {
@ -568,7 +589,7 @@ a[ng-click] {
}
.log_viewer .line_selected {
background-color: #c5cae9;
background-color: var(--bg-log-line-selected-color);
}
.row.header .meta .page {
@ -578,7 +599,7 @@ a[ng-click] {
.tag {
padding: 2px 6px;
color: white;
background-color: #337ab7;
background-color: var(--blue-2);
border: 1px solid #2e6da4;
border-radius: 4px;
}
@ -588,7 +609,7 @@ a[ng-click] {
}
.line-separator {
border-bottom: 1px solid #777;
border-bottom: 1px solid var(--grey-26);
width: 50%;
margin: 20px auto 10px auto;
}
@ -613,7 +634,7 @@ a[ng-click] {
}
.striked::after {
border-bottom: 0.2em solid #777777;
border-bottom: 0.2em solid var(--grey-26);
content: '';
left: 0;
margin-top: calc(0.2em / 2 * -1);
@ -625,7 +646,7 @@ a[ng-click] {
.striketext:before,
.striketext:after {
background-color: #777777;
background-color: var(--grey-26);
content: '';
display: inline-block;
height: 1px;
@ -657,20 +678,51 @@ a[ng-click] {
/*angular-multi-select override*/
.multiSelect > button {
min-height: 30px !important;
background-color: unset;
background-image: unset;
background-image: var(--bg-image-multiselect-button);
border-color: var(--border-multiselect);
color: var(--text-multiselect);
background-color: var(--bg-multiselect-color);
}
.multiSelect > button:hover {
background-image: var(--bg-image-multiselect-hover);
}
.multiSelect .checkboxLayer {
border-color: var(--border-multiselect-checkboxlayer);
}
.multiSelect .checkBoxContainer {
background-color: var(--bg-multiselect-checkboxcontainer);
}
.multiSelect .multiSelectItem {
color: var(--text-multiselect-item);
}
.multiSelect .helperContainer {
background-color: var(--bg-multiselect-helpercontainer);
}
.multiSelect .multiSelectFocus {
background-image: var(--bg-image-multiselect);
}
.multiSelect .multiSelectItem:not(.multiSelectGroup).selected {
background-image: linear-gradient(#337ab7, #337ab7);
color: #fff;
background-image: var(--bg-image-multiselect);
color: var(--white-color);
border: none;
}
.multiSelect .multiSelectItem:hover,
.multiSelect .multiSelectGroup:hover {
background-image: linear-gradient(#337ab7, #337ab7) !important;
color: #fff !important;
border-color: var(--grey-3);
}
.multiSelect .multiSelectItem:hover,
.multiSelect .multiSelectGroup:hover {
background-image: var(--bg-image-multiselect) !important;
color: var(--white-color) !important;
}
.multiSelect .tickMark,
@ -696,7 +748,7 @@ a[ng-click] {
#loading-bar .bar {
position: relative;
height: 3px;
background: #738bc0;
background: var(--blue-3);
}
/*!angular-loading-bar override*/
@ -708,11 +760,11 @@ a[ng-click] {
/* json-tree override */
json-tree {
font-size: 13px;
color: #30426a;
color: var(--blue-5);
}
json-tree .key {
color: #738bc0;
color: var(--blue-3);
padding-right: 5px;
}
@ -725,7 +777,7 @@ json-tree .branch-preview {
/* uib-progressbar override */
.progress-bar {
color: #4e4e4e;
color: var(--text-progress-bar-color);
}
/* !uib-progressbar override */
@ -756,7 +808,7 @@ json-tree .branch-preview {
}
.sk-fold-cube:before {
background-color: #337ab7;
background-color: var(--blue-2);
}
/* !spinkit override */

View File

@ -1,2 +1,4 @@
import './rdash.css';
import './app.css';
import './theme.css';
import './vendor-override.css';

View File

@ -52,7 +52,8 @@
* Header
*/
.row.header {
background: #fff;
height: 60px;
background: var(--bg-row-header-color);
margin-bottom: 15px;
}
.row.header > div:last-child {
@ -202,9 +203,9 @@ html {
overflow-y: scroll;
}
body {
background: #f3f3f3;
background: var(--bg-body-color);
font-family: 'Montserrat';
color: #333333 !important;
color: var(--text-body-color) !important;
}
.row {
margin-left: 0 !important;
@ -236,7 +237,7 @@ body {
background: #23ae89 !important;
}
.blue {
background: #2361ae !important;
background: var(--blue-color) !important;
}
.orange {
background: #d3a938 !important;
@ -258,20 +259,20 @@ div.input-mask {
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
-moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
background: #ffffff;
background: var(--bg-widget-color);
border: 1px solid transparent;
border-radius: 2px;
border-color: #e9e9e9;
border-color: var(--border-widget-color);
}
.widget .widget-header .pagination,
.widget .widget-footer .pagination {
margin: 0;
}
.widget .widget-header {
color: #767676;
background-color: #f6f6f6;
color: var(--text-widget-header-color);
background-color: var(--bg-widget-header-color);
padding: 10px 15px;
border-bottom: 1px solid #e9e9e9;
border-bottom: 1px solid var(--border-widget-color);
line-height: 30px;
}
.widget .widget-header i {
@ -281,7 +282,7 @@ div.input-mask {
padding: 20px;
}
.widget .widget-body table thead {
background: #fafafa;
background: var(--bg-widget-table-color);
}
.widget .widget-body table thead * {
font-size: 14px !important;

576
app/assets/css/theme.css Normal file
View File

@ -0,0 +1,576 @@
/* Color Variable */
html {
--black-color: #000;
--white-color: #fff;
--grey-1: #212121;
--grey-2: #181818;
--grey-3: #383838;
--grey-4: #585858;
--grey-5: #323c48;
--grey-6: #333333;
--grey-7: #767676;
--grey-8: #aaa;
--grey-9: #f3f3f3;
--grey-10: #f6f6f6;
--grey-11: #eeeeee;
--grey-12: #ececec;
--grey-13: #fafafa;
--grey-14: #f5f5f5;
--grey-15: #f9f2f4;
--grey-16: #eee;
--grey-17: #f7f7f7;
--grey-18: #c5cae9;
--grey-19: #ddd;
--grey-20: #dae3f3;
--grey-21: #d5e8f3;
--grey-22: #c3c3e4;
--grey-23: #e7f6ff;
--grey-24: #f1f9fd;
--grey-25: #555555;
--grey-26: #777777;
--grey-27: #4e4e4e;
--grey-28: #262626;
--grey-29: #555;
--grey-30: #444;
--grey-31: #868686;
--grey-32: #65798e;
--grey-34: #314252;
--grey-35: #546477;
--grey-36: #55637d;
--grey-37: #2d3e63;
--grey-38: #434343;
--grey-39: #194973;
--grey-40: #cfddfc;
--grey-41: #b4b4b4;
--grey-42: #d2d1d1;
--grey-43: #e9e9e9;
--grey-44: #ccc;
--grey-45: #e5e5e5;
--grey-46: #bbbbbb;
--grey-47: #d4d4d5;
--grey-48: #c6c6c6;
--grey-49: rgba(0, 0, 0, 0.54);
--grey-50: rgba(161, 170, 166, 0.5);
--grey-51: rgba(0, 0, 0, 0.15);
--grey-52: rgba(255, 255, 255, 0.3);
--grey-53: rgba(255, 255, 255, 0.6);
--grey-54: rgb(54, 54, 54);
--grey-55: rgba(255, 255, 255, 0.8);
--grey-56: #b2bfdc;
--grey-57: #999;
--grey-58: #ebf4f8;
--grey-59: #e6e6e6;
--grey-60: #cacaca;
--blue-1: #219;
--blue-2: #337ab7;
--blue-3: #738bc0;
--blue-4: #23527c;
--blue-5: #30426a;
--blue-6: #577bc9;
--blue-7: #6b9aff;
--blue-8: #90ccff;
--blue-9: #3ea6ff;
--blue-10: #61b6ff;
--blue-11: #3ea5ff;
--blue-12: #41a6ff;
--blue-13: #2361ae;
--blue-14: #357ebd;
--red-1: #a94442;
--red-2: #c7254e;
--red-3: #a11;
--red-4: #d9534f;
--red-5: #ff2727;
--red-6: #ff00e0;
--red-7: #f00;
--green-1: #164;
--green-2: #1ec863;
}
:root {
--bg-card-color: var(--grey-10);
--bg-main-color: var(--white-color);
--bg-body-color: var(--grey-9);
--bg-checkbox-border-color: var(--grey-49);
--bg-sidebar-color: var(--grey-37);
--bg-sidebar-header-color: var(--grey-37);
--bg-widget-color: var(--white-color);
--bg-widget-header-color: var(--grey-10);
--bg-widget-table-color: var(--grey-13);
--bg-header-color: var(--white-color);
--bg-hover-table-color: var(--grey-14);
--bg-switch-box-color: var(--white-color);
--bg-input-group-addon-color: var(--grey-11);
--bg-btn-default-color: var(--white-color);
--bg-blocklist-hover-color: var(--grey-12);
--bg-boxselector-color: var(--white-color);
--bg-table-color: var(--white-color);
--bg-md-checkbox-color: var(--grey-12);
--bg-form-control-disabled-color: var(--grey-11);
--bg-modal-content-color: var(--white-color);
--bg-code-color: var(--grey-15);
--bg-navtabs-color: var(--white-color);
--bg-navtabs-hover-color: var(--grey-16);
--bg-table-selected-color: var(--grey-14);
--bg-codemirror-gutters-color: var(--grey-17);
--bg-dropdown-menu-color: var(--white-color);
--bg-log-viewer-color: var(--white-color);
--bg-log-line-selected-color: var(--grey-18);
--bg-pre-color: var(--grey-14);
--bg-blocklist-item-selected-color: var(--grey-12);
--bg-progress-color: var(--grey-14);
--bg-pagination-color: var(--white-color);
--bg-pagination-span-color: var(--white-color);
--bg-pagination-hover-color: var(--grey-11);
--bg-ui-select-hover-color: var(--grey-14);
--bg-motd-body-color: var(--grey-20);
--bg-item-highlighted-color: var(--grey-21);
--bg-item-highlighted-null-color: var(--grey-14);
--bg-row-header-color: var(--white-color);
--bg-image-multiselect-button: linear-gradient(var(--white-color), var(--grey-17));
--bg-multiselect-checkbox-color: var(--white-color);
--bg-sidebar-wrapper-color: var(--blue-5);
--bg-panel-body-color: var(--white-color);
--bg-codemirror-color: var(--white-color);
--bg-codemirror-selected-color: var(--grey-22);
--bg-multiselect-color: var(--white-color);
--bg-daterangepicker-color: var(--white-color);
--bg-calendar-color: var(--white-color);
--bg-calendar-table-color: var(--white-color);
--bg-daterangepicker-end-date: var(--white-color);
--bg-daterangepicker-hover: var(--grey-16);
--bg-daterangepicker-in-range: var(--grey-58);
--bg-daterangepicker-active: var(--blue-14);
--bg-tooltip-color: var(--white-color);
--bg-input-autofill-color: var(--white-color);
--bg-btn-default-hover-color: var(--grey-59);
--bg-btn-focus: var(--grey-59);
--bg-boxselector-disabled-color: var(--white-color);
--bg-small-select-color: var(--white-color);
--text-main-color: var(--grey-7);
--text-body-color: var(--grey-6);
--text-sidebar-title-color: var(--blue-3);
--text-widget-header-color: var(--grey-7);
--text-form-control-color: var(--grey-25);
--text-muted-color: var(--grey-26);
--text-link-color: var(--blue-2);
--text-link-hover-color: var(--blue-4);
--text-input-group-addon-color: var(--grey-25);
--text-btn-default-color: var(--grey-6);
--text-blocklist-hover-color: var(--grey-37);
--text-dashboard-item-color: var(--grey-32);
--text-danger-color: var(--red-1);
--text-code-color: var(--red-2);
--text-navtabs-color: var(--grey-25);
--text-form-section-title-color: var(--grey-26);
--text-cm-default-color: var(--blue-1);
--text-cm-meta-color: var(--black-color);
--text-cm-string-color: var(--red-3);
--text-cm-number-color: var(--green-1);
--text-codemirror-color: var(--black-color);
--text-dropdown-menu-color: var(--grey-6);
--text-log-viewer-color: var(--black-color);
--text-json-tree-color: var(--blue-3);
--text-json-tree-leaf-color: var(--blue-5);
--text-json-tree-branch-preview-color: var(--blue-5);
--text-pre-color: var(--grey-6);
--text-blocklist-item-selected-color: var(--grey-37);
--text-progress-bar-color: var(--grey-27);
--text-pagination-color: var(--grey-26);
--text-pagination-span-color: var(--blue-2);
--text-pagination-span-hover-color: var(--blue-4);
--text-ui-select-color: var(--grey-6);
--text-ui-select-hover-color: var(--grey-28);
--text-summary-color: var(--black-color);
--text-multiselect-button-color: var(--grey-29);
--text-multiselect-item-color: var(--grey-30);
--text-sidebar-list-color: var(--grey-56);
--text-rzslider-color: var(--grey-36);
--text-rzslider-limit-color: var(--grey-36);
--text-daterangepicker-end-date: var(--grey-57);
--text-daterangepicker-in-range: var(--black-color);
--text-daterangepicker-active: var(--white-color);
--text-tooltip-color: var(--grey-6);
--text-input-autofill-color: var(--black-color);
--text-button-hover-color: var(--grey-6);
--text-small-select-color: var(--grey-25);
--border-color: var(--grey-42);
--border-widget-color: var(--grey-43);
--border-sidebar-color: var(--white-color);
--border-form-control-color: var(--grey-44);
--border-table-color: var(--grey-19);
--border-table-top-color: var(--grey-19);
--border-datatable-top-color: var(--grey-10);
--border-blocklist-color: var(--grey-44) ccc;
--border-input-group-addon-color: var(--grey-44);
--border-btn-default-color: var(--grey-44);
--border-boxselector-color: var(--grey-6);
--border-md-checkbox-color: var(--grey-19);
--border-modal-header-color: var(--grey-45);
--border-navtabs-color: var(--grey-19);
--border-form-section-title-color: var(--grey-26);
--border-codemirror-cursor-color: var(--black-color);
--border-codemirror-gutters-color: var(--grey-19);
--border-pre-color: var(--grey-43);
--border-blocklist-item-selected-color: var(--grey-46);
--border-pagination-color: var(--grey-19);
--border-pagination-span-color: var(--grey-19);
--border-pagination-hover-color: var(--grey-19);
--border-multiselect-button-color: var(--grey-48);
--border-searchbar-color: var(--grey-10);
--border-panel-color: var(--white-color);
--border-daterangepicker-color: var(--grey-19);
--border-calendar-table: var(--white-color);
--border-daterangepicker: var(--grey-19);
--border-pre-next-month: var(--black-color);
--border-daterangepicker-after: var(--white-color);
--border-tooltip-color: var(--grey-47);
--border-modal: 0px;
--hover-sidebar-color: var(--grey-37);
--shadow-box-color: 0 3px 10px -2px var(--grey-50);
--shadow-boxselector-color: 0 3px 10px -2px var(--grey-50);
--blue-color: var(--blue-13);
--button-close-color: var(--black-color);
--button-opacity: 0.2;
--button-opacity-hover: 0.5;
--bg-boxselector-wrapper-color: var(--grey-6);
--bg-image-multiselect: linear-gradient(var(--blue-2), var(--blue-2));
--bg-image-multiselect-button: linear-gradient(var(--white-color), var(--grey-17));
--bg-image-multiselect-hover: linear-gradient(var(--white-color), var(--grey-43));
--border-multiselect: var(--grey-48);
--border-multiselect-checkboxlayer: var(--grey-51);
--text-multiselect: var(--grey-29);
--text-multiselect-selectitem: var(--white-color);
--bg-multiselect-checkboxcontainer: var(--white-color);
--text-multiselect-item: var(--grey-30);
--bg-multiselect-helpercontainer: var(--white-color);
--text-input-textarea: var(--white-color);
--bg-service-datatable-thead: var(--grey-23);
--bg-service-datatable-tbody: var(--grey-24);
}
:root[theme='dark'] {
--bg-card-color: var(--grey-1);
--bg-main-color: var(--grey-2);
--bg-body-color: var(--grey-2);
--bg-checkbox-border-color: var(--grey-8);
--bg-sidebar-color: var(--grey-3);
--bg-widget-color: var(--grey-1);
--bg-widget-header-color: var(--grey-1);
--bg-widget-table-color: var(--grey-1);
--bg-header-color: var(--grey-2);
--bg-hover-table-color: var(--grey-3);
--bg-switch-box-color: var(--grey-53);
--bg-input-group-addon-color: var(--grey-3);
--bg-btn-default-color: var(--grey-3);
--bg-blocklist-hover-color: var(--grey-3);
--bg-boxselector-color: var(--grey-54);
--bg-table-color: var(--grey-1);
--bg-md-checkbox-color: var(--grey-31);
--bg-form-control-disabled-color: var(--grey-3);
--bg-modal-content-color: var(--grey-1);
--bg-code-color: var(--red-4);
--bg-navtabs-color: var(--grey-3);
--bg-navtabs-hover-color: var(--grey-3);
--bg-table-selected-color: var(--grey-3);
--bg-codemirror-color: var(--grey-2);
--bg-codemirror-gutters-color: var(--grey-2);
--bg-dropdown-menu-color: var(--grey-1);
--bg-log-viewer-color: var(--grey-2);
--bg-log-line-selected-color: var(--grey-3);
--bg-pre-color: var(--grey-2);
--bg-blocklist-item-selected-color: var(--grey-3);
--bg-progress-color: var(--grey-3);
--bg-pagination-color: var(--grey-3);
--bg-pagination-span-color: var(--grey-3);
--bg-pagination-hover-color: var(--grey-4);
--bg-ui-select-hover-color: var(--grey-3);
--bg-motd-body-color: var(--grey-1);
--bg-item-highlighted-color: var(--grey-2);
--bg-item-highlighted-null-color: var(--grey-2);
--bg-row-header-color: var(--grey-2);
--bg-multiselect-button-color: var(--grey-3);
--bg-image-multiselect-button: none !important;
--bg-multiselect-checkbox-color: var(--grey-3);
--bg-sidebar-wrapper-color: var(--grey-1);
--bg-panel-body-color: var(--grey-1);
--bg-boxselector-wrapper-disabled-color: var(--grey-39);
--bg-codemirror-selected-color: var(--grey-3);
--bg-sidebar-header-color: var(--grey-1);
--bg-multiselect-color: var(--grey-1);
--bg-daterangepicker-color: var(--grey-3);
--bg-calendar-color: var(--grey-3);
--bg-calendar-table-color: var(--grey-3);
--bg-daterangepicker-end-date: var(--grey-4);
--bg-daterangepicker-hover: var(--grey-4);
--bg-daterangepicker-in-range: var(--grey-2);
--bg-daterangepicker-active: var(--blue-14);
--bg-tooltip-color: var(--grey-3);
--bg-input-autofill-color: var(--grey-2);
--bg-btn-default-hover-color: var(--grey-3);
--bg-btn-focus: var(--grey-3);
--bg-boxselector-disabled-color: var(--grey-54);
--bg-small-select-color: var(--grey-2);
--text-main-color: var(--white-color);
--text-body-color: var(--white-color);
--text-sidebar-title-color: var(--grey-8);
--text-widget-header-color: var(--white-color);
--text-form-control-color: var(--grey-8);
--text-muted-color: var(--grey-8);
--text-link-color: var(--blue-9);
--text-link-hover-color: var(--blue-2);
--text-input-group-addon-color: var(--grey-8);
--text-btn-default-color: var(--grey-8);
--text-blocklist-hover-color: var(--white-color);
--text-dashboard-item-color: var(--blue-2);
--text-danger-color: var(--red-4);
--text-code-color: var(--white-color);
--text-navtabs-color: var(--white-color);
--text-form-section-title-color: var(--grey-8);
--text-cm-default-color: var(--blue-10);
--text-cm-meta-color: var(--white-color);
--text-cm-string-color: var(--red-5);
--text-cm-number-color: var(--green-2);
--text-codemirror-color: var(--white-color);
--text-dropdown-menu-color: var(--white-color);
--text-log-viewer-color: var(--white-color);
--text-json-tree-color: var(--grey-40);
--text-json-tree-leaf-color: var(--blue-6);
--text-json-tree-branch-preview-color: var(--blue-7);
--text-pre-color: var(--white-color);
--text-blocklist-item-selected-color: var(--white-color);
--text-progress-bar-color: var(--white-color);
--text-pagination-color: var(--white-color);
--text-pagination-span-color: var(--white-color);
--text-pagination-span-hover-color: var(--white-color);
--text-ui-select-color: var(--white-color);
--text-ui-select-hover-color: var(--white-color);
--text-summary-color: var(--white-color);
--text-multiselect-button-color: var(--white-color);
--text-multiselect-item-color: var(--white-color);
--text-sidebar-list-color: var(--white-color);
--text-boxselector-wrapper-color: var(--white-color);
--text-daterangepicker-end-date: var(--grey-7);
--text-daterangepicker-in-range: var(--white-color);
--text-daterangepicker-active: var(--white-color);
--text-tooltip-color: var(--white-color);
--text-btn-default-color: var(--white-color);
--text-input-autofill-color: var(--grey-8);
--text-button-hover-color: var(--white-color);
--text-small-select-color: var(--grey-7);
--border-color: var(--grey-3);
--border-widget-color: var(--grey-1);
--border-sidebar-color: var(--blue-9);
--border-form-control-color: var(--grey-54);
--border-table-color: var(--grey-3);
--border-table-top-color: var(--grey-3);
--border-datatable-top-color: var(--grey-3);
--border-blocklist-color: var(--grey-3);
--border-input-group-addon-color: var(--grey-38);
--border-btn-default-color: var(--grey-38);
--border-boxselector-color: var(--grey-1);
--border-md-checkbox-color: var(--grey-41);
--border-modal-header-color: var(--grey-1);
--border-navtabs-color: var(--grey-38);
--border-form-section-title-color: var(--grey-8);
--border-codemirror-cursor-color: var(--white-color);
--border-codemirror-gutters-color: var(--grey-26);
--border-pre-color: var(--grey-3);
--border-blocklist-item-selected-color: var(--grey-38);
--border-pagination-color: var(--grey-3);
--border-pagination-span-color: var(--grey-3);
--border-pagination-hover-color: var(--grey-3);
--border-pagination-hover-color: var(--grey-3);
--border-multiselect-button-color: var(--grey-3);
--border-searchbar-color: var(--grey-1);
--border-panel-color: var(--grey-2);
--border-daterangepicker-color: var(--grey-3);
--border-calendar-table: var(--grey-3);
--border-daterangepicker: var(--grey-4);
--border-pre-next-month: var(--white-color);
--border-daterangepicker-after: var(--grey-3);
--border-tooltip-color: var(--grey-3);
--border-modal: 0px;
--hover-sidebar-color: var(--grey-3);
--blue-color: var(--blue-2);
--button-close-color: var(--white-color);
--button-opacity: 0.6;
--button-opacity-hover: 0.3;
--shadow-box-color: none;
--shadow-boxselector-color: none;
--bg-image-multiselect: linear-gradient(var(--grey-38), var(--grey-38));
--bg-image-multiselect-button: linear-gradient(var(--grey-1), var(--grey-1));
--bg-image-multiselect-hover: linear-gradient(var(--grey-3), var(--grey-3));
--border-multiselect: var(--grey-3);
--border-multiselect-checkboxlayer: var(--grey-3);
--text-multiselect: var(--white-color);
--bg-multiselect-checkboxcontainer: var(--grey-3);
--text-multiselect-item: var(--white-color);
--bg-multiselect-helpercontainer: var(--grey-1);
--text-input-textarea: var(--grey-1);
--bg-service-datatable-thead: var(--grey-1);
--bg-service-datatable-tbody: var(--grey-1);
}
:root[theme='highcontrast'] {
--bg-card-color: var(--black-color);
--bg-main-color: var(--black-color);
--bg-body-color: var(--black-color);
--bg-checkbox-border-color: var(--grey-8);
--bg-sidebar-color: var(--black-color);
--bg-widget-color: var(--black-color);
--bg-widget-header-color: var(--black-color);
--bg-widget-table-color: var(--black-color);
--bg-header-color: var(--black-color);
--bg-hover-table-color: var(--grey-3);
--bg-switch-box-color: var(--grey-53);
--bg-panel-body-color: var(--black-color);
--bg-boxselector-wrapper-disabled-color: var(--grey-39);
--bg-dropdown-menu-color: var(--black-color);
--bg-codemirror-selected-color: var(--grey-3);
--bg-row-header-color: var(--black-color);
--bg-sidebar-wrapper-color: var(--black-color);
--bg-motd-body-color: var(--black-color);
--bg-blocklist-hover-color: var(--black-color);
--bg-blocklist-item-selected-color: var(--black-color);
--bg-input-group-addon-color: var(--grey-1);
--bg-table-color: var(--black-color);
--bg-codemirror-gutters-color: var(--black-color);
--bg-codemirror-color: var(--black-color);
--bg-codemirror-selected-color: var(--grey-3);
--bg-log-viewer-color: var(--black-color);
--bg-log-line-selected-color: var(--grey-3);
--bg-sidebar-header-color: var(--black-color);
--bg-modal-content-color: var(--black-color);
--bg-form-control-disabled-color: var(--grey-1);
--bg-input-sm-color: var(--black-color);
--bg-item-highlighted-color: var(--black-color);
--bg-service-datatable-thead: var(--black-color);
--bg-service-datatable-tbody: var(--black-color);
--bg-pagination-color: var(--grey-3);
--bg-pagination-span-color: var(--grey-3);
--bg-multiselect-color: var(--grey-1);
--bg-daterangepicker-color: var(--black-color);
--bg-calendar-color: var(--black-color);
--bg-calendar-table-color: var(--black-color);
--bg-daterangepicker-end-date: var(--grey-3);
--bg-daterangepicker-hover: var(--grey-3);
--bg-daterangepicker-in-range: var(--grey-2);
--bg-daterangepicker-active: var(--blue-14);
--bg-tooltip-color: var(--black-color);
--bg-table-selected-color: var(--grey-3);
--bg-pre-color: var(--grey-2);
--bg-navtabs-hover-color: var(--grey-3);
--bg-btn-default-color: var(--black-color);
--bg-code-color: var(--red-4);
--bg-navtabs-color: var(--black-color);
--bg-input-autofill-color: var(--black-color);
--bg-code-color: var(--grey-2);
--bg-navtabs-color: var(--grey-2);
--bg-navtabs-hover-color: var(--grey-3);
--bg-btn-default-hover-color: var(--grey-3);
--bg-btn-default-color: var(--black-color);
--bg-btn-focus: var(--black-color);
--bg-boxselector-color: var(--black-color);
--bg-boxselector-disabled-color: var(--black-color);
--bg-small-select-color: var(--black-color);
--text-main-color: var(--white-color);
--text-body-color: var(--white-color);
--text-sidebar-title-color: var(--grey-8);
--text-widget-header-color: var(--white-color);
--text-link-color: var(--blue-9);
--text-link-hover-color: var(--blue-9);
--text-danger-color: var(--red-7);
--text-code-color: var(--red-7);
--text-form-control-color: var(--white-color);
--text-blocklist-hover-color: var(--blue-11);
--text-boxselector-wrapper-color: var(--white-color);
--text-dashboard-item-color: var(--blue-12);
--text-form-section-title-color: var(--white-color);
--text-muted-color: var(--white-color);
--text-tooltip-color: var(--white-color);
--text-blocklist-item-selected-color: var(--blue-9);
--text-input-group-addon-color: var(--white-color);
--text-codemirror-color: var(--white-color);
--text-log-viewer-color: var(--white-color);
--text-summary-color: var(--white-color);
--text-rzslider-color: var(--white-color);
--text-rzslider-limit-color: var(--white-color);
--text-pagination-color: var(--white-color);
--text-daterangepicker-end-date: var(--grey-7);
--text-daterangepicker-in-range: var(--white-color);
--text-daterangepicker-active: var(--white-color);
--text-sidebar-list-color: var(--white-color);
--text-ui-select-color: var(--white-color);
--text-btn-default-color: var(--white-color);
--text-json-tree-color: var(--white-color);
--text-json-tree-leaf-color: var(--white-color);
--text-json-tree-branch-preview-color: var(--white-color);
--text-pre-color: var(--white-color);
--text-navtabs-color: var(--white-color);
--text-input-autofill-color: var(--white-color);
--text-navtabs-color: var(--white-color);
--text-button-hover-color: var(--white-color);
--text-btn-default-color: var(--white-color);
--text-small-select-color: var(--white-color);
--border-color: var(--grey-55);
--border-widget-color: var(--white-color);
--border-sidebar-color: var(--blue-9);
--border-form-control-color: var(--grey-54);
--border-table-color: var(--grey-55);
--border-table-top-color: var(--grey-55);
--border-datatable-top-color: var(--grey-55);
--border-sidebar-high-contrast: 1px solid var(--blue-9);
--border-code-high-contrast: 1px solid var(--white-color);
--border-boxselector-wrapper: 3px solid var(--blue-2);
--border-boxselector-wrapper-hover: 3px solid var(--blue-8);
--border-panel-color: var(--white-color);
--border-input-group-addon-color: var(--grey-54);
--border-modal-header-color: var(--grey-3);
--border-input-sm-color: var(--white-color);
--border-pagination-color: var(--grey-3);
--border-pagination-span-color: var(--grey-3);
--border-daterangepicker-color: var(--white-color);
--border-calendar-table: var(--black-color);
--border-daterangepicker: var(--black-color);
--border-pre-next-month: var(--white-color);
--border-daterangepicker-after: var(--black-color);
--border-tooltip-color: var(--white-color);
--border-pre-color: var(--grey-3);
--border-codemirror-cursor-color: var(--white-color);
--border-modal: 1px solid var(--white-color);
--hover-sidebar-color: var(--blue-9);
--hover-sidebar-color: var(--black-color);
--shadow-box-color: none;
--shadow-boxselector-color: none;
--bg-image-multiselect: linear-gradient(var(--black-color), var(--black-color));
--bg-image-multiselect-button: linear-gradient(var(--grey-1), var(--grey-1));
--bg-image-multiselect-hover: linear-gradient(var(--grey-3), var(--grey-3));
--border-multiselect: var(--black-color);
--border-multiselect-checkboxlayer: var(--grey-3);
--text-multiselect: var(--white-color);
--bg-multiselect-checkboxcontainer: var(--grey-3);
--text-multiselect-item: var(--white-color);
--bg-multiselect-helpercontainer: var(--grey-1);
--text-input-textarea: var(--black-color);
--bg-item-highlighted-null-color: var(--grey-2);
--text-cm-default-color: var(--blue-9);
--text-cm-meta-color: var(--white-color);
--text-cm-string-color: var(--red-7);
--text-progress-bar-color: var(--black-color);
}

View File

@ -0,0 +1,399 @@
/* Overide Vendor CSS */
.form-control {
background-color: var(--bg-main-color) !important;
border: 1px solid var(--border-form-control-color);
color: var(--text-form-control-color);
}
.text-muted {
color: var(--text-muted-color);
}
.table > thead > tr > th {
border-bottom: 2px solid var(--border-table-color);
}
.table-hover > tbody > tr:hover {
background-color: var(--bg-hover-table-color);
}
.switch i,
.bootbox-form .checkbox i {
background: var(--bg-switch-box-color);
}
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
border-top: 1px solid var(--border-table-top-color);
}
a {
color: var(--text-link-color);
}
a:hover,
a:focus {
color: var(--text-link-hover-color);
}
.input-group-addon {
color: var(--text-input-group-addon-color);
background-color: var(--bg-input-group-addon-color);
border: 1px solid var(--border-input-group-addon-color);
}
.btn-default {
color: var(--text-btn-default-color);
background-color: var(--bg-btn-default-color);
border-color: var(--border-btn-default-color);
}
.text-danger {
color: var(--text-danger-color);
}
.table .table {
background-color: var(--bg-table-color);
}
.table-bordered {
border-color: var(--border-table-top-color);
}
.table-bordered > thead > tr > th,
.table-bordered > tbody > tr > th,
.table-bordered > tfoot > tr > th,
.table-bordered > thead > tr > td,
.table-bordered > tbody > tr > td,
.table-bordered > tfoot > tr > td {
border-color: var(--border-table-top-color);
}
.md-checkbox input[type='checkbox']:disabled + label:before {
background: var(--bg-md-checkbox-color) !important;
border-color: var(--border-md-checkbox-color) !important;
}
.form-control[disabled],
.form-control[readonly],
fieldset[disabled] .form-control {
background-color: var(--bg-form-control-disabled-color) !important;
}
.modal.in .modal-dialog {
border: var(--border-modal);
}
.modal-content {
background-color: var(--bg-modal-content-color);
}
.modal-header {
border-bottom: 1px solid var(--border-modal-header-color);
}
.modal-footer {
border-top: 1px solid var(--border-modal-header-color);
}
.close {
color: var(--button-close-color);
opacity: var(--button-opacity);
}
.close:hover,
.close:focus {
color: var(--button-close-color);
opacity: var(--button-opacity-hover);
}
code {
color: var(--text-code-color);
background-color: var(--bg-code-color);
}
.nav-tabs > li.active > a,
.nav-tabs > li.active > a:hover,
.nav-tabs > li.active > a:focus {
color: var(--text-navtabs-color);
background-color: var(--bg-navtabs-color);
border: 1px solid var(--border-navtabs-color);
}
.nav-tabs {
border-bottom: 1px solid var(--border-navtabs-color);
}
.nav-tabs > li > a:hover {
border-color: var(--border-navtabs-color);
}
.nav > li > a:hover,
.nav > li > a:focus {
background-color: var(--bg-navtabs-hover-color);
}
.table > thead > tr > td.active,
.table > tbody > tr > td.active,
.table > tfoot > tr > td.active,
.table > thead > tr > th.active,
.table > tbody > tr > th.active,
.table > tfoot > tr > th.active,
.table > thead > tr.active > td,
.table > tbody > tr.active > td,
.table > tfoot > tr.active > td,
.table > thead > tr.active > th,
.table > tbody > tr.active > th,
.table > tfoot > tr.active > th {
background-color: var(--bg-table-selected-color);
}
.table-hover > tbody > tr > td.active:hover,
.table-hover > tbody > tr > th.active:hover,
.table-hover > tbody > tr.active:hover > td,
.table-hover > tbody > tr:hover > .active,
.table-hover > tbody > tr.active:hover > th {
background-color: var(--bg-table-selected-color);
}
.CodeMirror-gutters {
background: var(--bg-codemirror-gutters-color);
border-right: 1px solid var(--border-codemirror-gutters-color);
}
.CodeMirror {
background: var(--bg-codemirror-color);
color: var(--text-codemirror-color);
}
.CodeMirror-selected {
background: var(--bg-codemirror-selected-color) !important;
}
.CodeMirror-cursor {
border-left: 1px solid var(--border-codemirror-cursor-color);
}
.cm-s-default .cm-atom {
color: var(--text-cm-default-color);
}
.cm-s-default .cm-meta {
color: var(--text-cm-meta-color);
}
.cm-s-default .cm-string {
color: var(--text-cm-string-color);
}
.cm-s-default .cm-number {
color: var(--text-cm-number-color);
}
.dropdown-menu {
background: var(--bg-dropdown-menu-color);
}
.dropdown-menu > li > a {
color: var(--text-dropdown-menu-color);
}
pre {
border: 1px solid var(--border-pre-color);
background-color: var(--bg-pre-color);
color: var(--text-pre-color);
}
json-tree .key {
color: var(--text-json-tree-color);
}
json-tree .leaf-value {
color: var(--text-json-tree-leaf-color);
}
json-tree .branch-preview {
color: var(--text-json-tree-branch-preview-color);
}
.progress {
background-color: var(--bg-progress-color);
}
.pagination > .disabled > span,
.pagination > .disabled > span:hover,
.pagination > .disabled > span:focus,
.pagination > .disabled > a,
.pagination > .disabled > a:hover,
.pagination > .disabled > a:focus {
color: var(--text-pagination-color);
background-color: var(--bg-pagination-color);
border-color: var(--border-pagination-color);
}
.pagination > li > a,
.pagination > li > span {
background-color: var(--bg-pagination-span-color);
border-color: var(--border-pagination-span-color);
color: var(--text-pagination-span-color);
}
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
background-color: var(--bg-pagination-hover-color);
border-color: var(--border-pagination-hover-color);
}
.pagination > li > a:hover,
.pagination > li > span:hover,
.pagination > li > a:focus,
.pagination > li > span:focus {
color: var(--text-pagination-span-hover-color);
}
.ui-select-bootstrap .ui-select-choices-row > span {
color: var(--text-ui-select-color);
}
.ui-select-bootstrap .ui-select-choices-row > span:hover,
.ui-select-bootstrap .ui-select-choices-row > span:focus {
background-color: var(--bg-ui-select-hover-color);
color: var(--text-ui-select-hover-color);
}
.motd-body {
background-color: var(--bg-motd-body-color) !important;
}
.panel-body {
background-color: var(--bg-panel-body-color) !important;
}
.panel {
border: 1px solid var(--border-panel-color);
}
.theme-information .col-sm-12 {
padding-left: 0px;
padding-right: 0px;
margin-top: 15px;
}
.theme-panel {
margin-top: 15px;
}
.summary {
color: var(--text-summary-color);
font-weight: 700;
}
.input-sm {
background-color: var(--bg-input-sm-color);
border: 1px solid var(--border-input-sm-color);
}
.rzslider .rz-bubble {
color: var(--text-rzslider-color);
}
.rzslider .rz-bubble.rz-limit {
color: var(--text-rzslider-limit-color);
}
input,
button,
select,
textarea {
background: var(--text-input-textarea);
}
.daterangepicker {
background-color: var(--bg-daterangepicker-color);
border: 1px solid var(--border-daterangepicker-color);
}
.daterangepicker .drp-calendar.left {
background: var(--bg-calendar-color);
}
.daterangepicker .drp-calendar.left .calendar-table {
background: var(--bg-calendar-table-color);
}
.daterangepicker .drp-calendar.right {
background: var(--bg-calendar-color);
}
.daterangepicker .drp-calendar.right .calendar-table {
background: var(--bg-calendar-table-color);
}
.daterangepicker .calendar-table {
border: 1px solid var(--border-calendar-table);
}
.daterangepicker td.off,
.daterangepicker td.off.in-range,
.daterangepicker td.off.start-date,
.daterangepicker td.off.end-date {
background-color: var(--bg-daterangepicker-end-date);
color: var(--text-daterangepicker-end-date);
}
.daterangepicker td.available:hover,
.daterangepicker th.available:hover {
background-color: var(--bg-daterangepicker-hover);
}
.daterangepicker td.in-range {
background-color: var(--bg-daterangepicker-in-range);
color: var(--text-daterangepicker-in-range);
}
.daterangepicker td.active,
.daterangepicker td.active:hover {
background-color: var(--bg-daterangepicker-active);
color: var(--text-daterangepicker-active);
}
.daterangepicker .drp-buttons {
border-top: 1px solid var(--border-daterangepicker);
}
.daterangepicker .calendar-table .next span,
.daterangepicker .calendar-table .prev span {
border-color: var(--border-pre-next-month);
}
.daterangepicker:after {
border-bottom: 6px solid var(--border-daterangepicker-after);
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-box-shadow: 0 0 0 30px var(--bg-input-autofill-color) inset !important;
box-shadow: 0 0 0 30px var(--bg-input-autofill-color) inset !important;
}
input:-webkit-autofill {
-webkit-text-fill-color: var(--text-input-autofill-color) !important;
}
.btn:hover {
color: var(--text-button-hover-color);
}
.btn-default:hover {
background-color: var(--bg-btn-default-hover-color);
}
.btn-primary:hover {
color: var(--white-color) !important;
}
/* Overide Vendor CSS */

View File

@ -73,7 +73,11 @@
</button>
</td>
</tr>
<tr dir-paginate-end ng-show="$ctrl.itemCanExpand(value) && value.Expanded" ng-style="{ background: value.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
dir-paginate-end
ng-show="$ctrl.itemCanExpand(value) && value.Expanded"
ng-class="{ 'datatable-highlighted': value.Highlighted, 'datatable-unhighlighted': !value.Highlighted }"
>
<td colspan="1"></td>
<td colspan="1">
{{ value.GlobalIPv6Address }}

View File

@ -171,7 +171,7 @@
allow-checkbox="true"
>
</tr>
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="it in item.Subs" style="background: #d5e8f3;" network-row-content item="it" parent-ctrl="$ctrl"> </tr>
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="it in item.Subs" class="datatable-highlighted" network-row-content item="it" parent-ctrl="$ctrl"> </tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="9" class="text-center text-muted">Loading...</td>
</tr>

View File

@ -1,6 +1,6 @@
<div style="background-color: #d5e8f3; padding: 2px;">
<div class="service-datatable">
<table class="table table-condensed table-hover nowrap-cells">
<thead style="background-color: #e7f6ff;">
<thead>
<tr>
<th uib-dropdown dropdown-append-to-body auto-close="disabled" is-open="$ctrl.filters.state.open" style="width: 10%;">
<a ng-click="$ctrl.changeOrderBy('Status.State')">
@ -54,7 +54,7 @@
</th>
</tr>
</thead>
<tbody style="background-color: #f1f9fd;">
<tbody>
<tr
ng-repeat="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter: $ctrl.applyFilters | filter:$ctrl.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder))"
>

View File

@ -105,8 +105,7 @@
<!-- dir-paginate-start track by $index -->
<tr
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
ng-class="{ active: item.Checked }"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
ng-click="$ctrl.expandItem(item, !item.Expanded)"
pagination-id="$ctrl.tableKey"
>
@ -177,7 +176,7 @@
</td>
</tr>
<!-- sub rows -->
<tr ng-show="item.Expanded" ng-repeat-start="port in item.Ports" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr ng-show="item.Expanded" ng-repeat-start="port in item.Ports" ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }">
<td ng-if="!$ctrl.portHasIngressRules(port)"></td>
<td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
<td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
@ -190,7 +189,12 @@
<td ng-if="!$ctrl.portHasIngressRules(port)">{{ port.TargetPort }}/{{ port.Protocol }}</td>
<td ng-if="!$ctrl.portHasIngressRules(port)">-</td>
</tr>
<tr ng-show="item.Expanded" ng-repeat-end ng-repeat="rule in port.IngressRules" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
ng-show="item.Expanded"
ng-repeat-end
ng-repeat="rule in port.IngressRules"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td></td>
<td>-</td>
<td>-</td>

View File

@ -109,8 +109,7 @@
<tbody>
<tr
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
ng-class="{ active: item.Checked }"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
ng-click="$ctrl.expandItem(item, !item.Expanded)"
pagination-id="$ctrl.tableKey"
>
@ -141,7 +140,12 @@
<a ui-sref="kubernetes.stacks.stack.logs({ namespace: item.ResourcePool, name: item.Name })"> <i class="fa fa-file-alt" aria-hidden="true"></i> Logs </a>
</td>
</tr>
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="app in item.Applications" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
dir-paginate-end
ng-show="item.Expanded"
ng-repeat="app in item.Applications"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td></td>
<td colspan="4">
<a ui-sref="kubernetes.applications.application({ name: app.Name, namespace: app.ResourcePool })">{{ app.Name }}</a>

View File

@ -2,9 +2,10 @@ import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
export default class KubernetesRegistryAccessController {
/* @ngInject */
constructor($async, $state, EndpointService, Notifications, KubernetesResourcePoolService) {
constructor($async, $state, ModalService, EndpointService, Notifications, KubernetesResourcePoolService) {
this.$async = $async;
this.$state = $state;
this.ModalService = ModalService;
this.Notifications = Notifications;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.EndpointService = EndpointService;
@ -26,8 +27,15 @@ export default class KubernetesRegistryAccessController {
handleRemove(namespaces) {
const removeNamespaces = namespaces.map(({ value }) => value);
const nsToUpdate = this.savedResourcePools.map(({ value }) => value).filter((value) => !removeNamespaces.includes(value));
return this.updateNamespaces(this.savedResourcePools.map(({ value }) => value).filter((value) => !removeNamespaces.includes(value)));
const displayedMessage =
'This registry might be used by one or more applications inside this environment. Removing the registry access could lead to a service interruption for these applications.<br/><br/>Do you wish to continue?';
this.ModalService.confirmDeletion(displayedMessage, (confirmed) => {
if (confirmed) {
return this.updateNamespaces(nsToUpdate);
}
});
}
updateNamespaces(namespaces) {

View File

@ -75,8 +75,7 @@
<tbody>
<tr
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | filter: $ctrl.isDisplayed | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
ng-class="{ active: item.Checked }"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
ng-click="$ctrl.expandItem(item, !item.Expanded)"
pagination-id="$ctrl.tableKey"
>
@ -92,14 +91,23 @@
</td>
</tr>
<!-- ADMIN + UNMET TAINTS -->
<tr ng-if="$ctrl.isAdmin" ng-show="item.Expanded" ng-repeat="taint in item.UnmetTaints" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
ng-if="$ctrl.isAdmin"
ng-show="item.Expanded"
ng-repeat="taint in item.UnmetTaints"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td colspan="2">
This application is missing a toleration for the taint <code>{{ taint.Key }}{{ taint.Value ? '=' + taint.Value : '' }}:{{ taint.Effect }}</code>
</td>
</tr>
<!-- !ADMIN + UNMET TAINTS -->
<!-- USER + UNMET TAINTS -->
<tr ng-if="!$ctrl.isAdmin && item.UnmetTaints.length" ng-show="item.Expanded" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
ng-if="!$ctrl.isAdmin && item.UnmetTaints.length"
ng-show="item.Expanded"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td colspan="2">
Placement constraint not respected for that node.
</td>
@ -110,7 +118,7 @@
ng-if="$ctrl.isAdmin"
ng-show="item.Expanded"
ng-repeat="label in item.UnmatchedNodeSelectorLabels"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td colspan="2">
This application can only be scheduled on a node where the label <code>{{ label.key }}</code> is set to <code>{{ label.value }}</code>
@ -121,7 +129,7 @@
<tr
ng-if="!$ctrl.isAdmin && (item.UnmatchedNodeSelectorLabels.length || item.UnmatchedNodeAffinities.length)"
ng-show="item.Expanded"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td colspan="2">
Placement label not respected for that node.
@ -129,7 +137,11 @@
</tr>
<!-- ! USER + UNMET NODE SELECTOR LABELS || UNMET NODE AFFINITIES -->
<!-- ADMIN + UNMET NODE AFFINITIES -->
<tr ng-if="$ctrl.isAdmin" ng-show="item.Expanded && item.UnmatchedNodeAffinities.length" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
ng-if="$ctrl.isAdmin"
ng-show="item.Expanded && item.UnmatchedNodeAffinities.length"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td colspan="2">
This application can only be scheduled on nodes respecting one of the following labels combination:
</td>
@ -139,7 +151,7 @@
ng-if="$ctrl.isAdmin"
ng-show="item.Expanded"
ng-repeat="aff in item.UnmatchedNodeAffinities"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td></td>
<td>

View File

@ -1,4 +1,4 @@
<kubernetes-view-header title="Create a namespace" state="kubernetes.resourcePools.new" view-ready="ctrl.state.viewReady">
<kubernetes-view-header title="Create a namespace" state="kubernetes.resourcePools.new" view-ready="$ctrl.state.viewReady">
<a ui-sref="kubernetes.resourcePools">Namespaces</a> &gt; Create a namespace
</kubernetes-view-header>

View File

@ -73,8 +73,7 @@
<tbody>
<tr
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
ng-class="{ active: item.Checked }"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
ng-click="$ctrl.expandItem(item, !item.Expanded)"
pagination-id="$ctrl.tableKey"
>
@ -84,7 +83,12 @@
>{{ item.Name }}
</td>
</tr>
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="path in item.Paths" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
dir-paginate-end
ng-show="item.Expanded"
ng-repeat="path in item.Paths"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td>
<a style="margin-left: 15px;" ng-href="http://{{ path.Host ? path.Host : path.IP }}{{ path.Path }}" target="_blank">
{{ path.Host ? path.Host : path.IP }}{{ path.Path }}

View File

@ -185,20 +185,26 @@ class KubernetesResourcePoolController {
}
updateResourcePool() {
const willBeDeleted = _.filter(this.formValues.IngressClasses, { WasSelected: true, Selected: false });
const ingressesToDelete = _.filter(this.formValues.IngressClasses, { WasSelected: true, Selected: false });
const registriesToDelete = _.filter(this.registries, { WasChecked: true, Checked: false });
const warnings = {
quota: this.hasResourceQuotaBeenReduced(),
ingress: willBeDeleted.length !== 0,
ingress: ingressesToDelete.length !== 0,
registries: registriesToDelete.length !== 0,
};
if (warnings.quota || warnings.ingress) {
if (warnings.quota || warnings.ingress || warnings.registries) {
const messages = {
quota:
'Reducing the quota assigned to an "in-use" namespace may have unintended consequences, including preventing running applications from functioning correctly and potentially even blocking them from running at all.',
ingress: 'Deactivating ingresses may cause applications to be unaccessible. All ingress configurations from affected applications will be removed.',
registries:
'Some registries you removed might be used by one or more applications inside this environment. Removing the registries access could lead to a service interruption for these applications.',
};
const displayedMessage = `${warnings.quota ? messages.quota : ''}${warnings.quota && warnings.ingress ? '<br/><br/>' : ''}
${warnings.ingress ? messages.ingress : ''}<br/><br/>Do you wish to continue?`;
const displayedMessage = `${warnings.quota ? messages.quota + '<br/><br/>' : ''}
${warnings.ingress ? messages.ingress + '<br/><br/>' : ''}
${warnings.registries ? messages.registries + '<br/><br/>' : ''}
Do you wish to continue?`;
this.ModalService.confirmUpdate(displayedMessage, (confirmed) => {
if (confirmed) {
return this.$async(this.updateResourcePoolAsync, this.savedFormValues, this.formValues);
@ -322,6 +328,7 @@ class KubernetesResourcePoolController {
this.registries.forEach((reg) => {
if (reg.RegistryAccesses && reg.RegistryAccesses[this.endpoint.Id] && reg.RegistryAccesses[this.endpoint.Id].Namespaces.includes(namespace)) {
reg.Checked = true;
reg.WasChecked = true;
this.formValues.Registries.push(reg);
}
});

View File

@ -22,7 +22,7 @@
<li ng-repeat="summary in $ctrl.state.resources" ng-if="summary.action && summary.kind && summary.name">
{{ summary.action }}
{{ $ctrl.getArticle(summary.kind, summary.action) }}
<span style="color: black; font-weight: 700;">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
<span class="summary">{{ summary.kind }}</span> named <code>{{ summary.name }}</code>
<span ng-if="summary.type">
of type <code>{{ summary.type }}</code></span
>

View File

@ -82,8 +82,7 @@
<tbody>
<tr
dir-paginate-start="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit: $ctrl.tableKey))"
ng-class="{ active: item.Checked }"
ng-style="{ background: item.Highlighted ? '#d5e8f3' : '' }"
ng-class="{ active: item.Checked, 'datatable-highlighted': item.Highlighted }"
ng-click="$ctrl.expandItem(item, !item.Expanded)"
pagination-id="$ctrl.tableKey"
>
@ -95,7 +94,12 @@
<td>{{ item.Name }}</td>
<td>{{ item.Size }}</td>
</tr>
<tr dir-paginate-end ng-show="item.Expanded" ng-repeat="vol in item.Volumes" ng-style="{ background: item.Highlighted ? '#d5e8f3' : '#f5f5f5' }">
<tr
dir-paginate-end
ng-show="item.Expanded"
ng-repeat="vol in item.Volumes"
ng-class="{ 'datatable-highlighted': item.Highlighted, 'datatable-unhighlighted': !item.Highlighted }"
>
<td></td>
<td>
<a ui-sref="kubernetes.volumes.volume({ name: vol.PersistentVolumeClaim.Name, namespace: vol.PersistentVolumeClaim.Namespace })">

View File

@ -27,22 +27,27 @@
.boxselector_wrapper input[type='radio']:not(:disabled) ~ label {
cursor: pointer;
background-color: var(--bg-boxselector-wrapper-disabled-color);
}
.boxselector_wrapper input[type='radio']:not(:disabled):hover ~ label:hover {
cursor: pointer;
}
.boxselector_wrapper label {
font-weight: normal;
font-size: 12px;
display: block;
background: white;
border: 1px solid #333333;
background: var(--bg-boxselector-color);
border: 1px solid var(--border-boxselector-color);
border-radius: 2px;
padding: 10px 10px 0 10px;
text-align: center;
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
box-shadow: var(--shadow-boxselector-color);
position: relative;
}
.boxselector_wrapper label.boxselector_disabled {
background: #cacaca;
background: var(--bg-boxselector-disabled-color) !important;
border-color: #787878;
color: #787878;
cursor: not-allowed;

View File

@ -8,7 +8,7 @@
<button type="button" class="btn btn-sm btn-primary" ui-state="$ctrl.createPath"> <i class="fa fa-plus space-right" aria-hidden="true"></i>Add Custom Template </button>
</div>
<div class="searchBar" style="border-top: 2px solid #f6f6f6;">
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input
type="text"

View File

@ -1,6 +1,6 @@
.datatable .toolBar {
background-color: #f6f6f6;
color: #767676;
background-color: var(--bg-card-color);
color: var(--text-main-color);
overflow: auto;
padding: 10px;
}
@ -30,9 +30,10 @@
}
.datatable .searchBar {
border-top: 1px solid #d2d1d1;
border-bottom: 1px solid #d2d1d1;
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
padding: 8px;
background: var(--bg-main-color);
}
.datatable .searchInput {
@ -60,9 +61,10 @@
}
.datatable .footer {
background-color: #f6f6f6;
color: #767676;
background-color: var(--bg-card-color);
color: var(--text-main-color);
overflow: auto;
border-top: 1px solid var(--border-datatable-top-color);
}
.datatable .footer .infoBar {
@ -152,9 +154,8 @@
}
.md-checkbox label:before {
background: #fff;
border: 2px solid black;
border: 2px solid rgba(0, 0, 0, 0.54);
background: var(--bg-main-color);
border: 2px solid var(--bg-checkbox-border-color);
border-radius: 2px;
cursor: pointer;
height: 16px;

View File

@ -0,0 +1,96 @@
class KubernetesAppGitFormController {
/* @ngInject */
constructor($async, $state, StackService, ModalService, Notifications) {
this.$async = $async;
this.$state = $state;
this.StackService = StackService;
this.ModalService = ModalService;
this.Notifications = Notifications;
this.state = {
saveGitSettingsInProgress: false,
redeployInProgress: false,
showConfig: true,
isEdit: false,
};
this.formValues = {
RefName: '',
RepositoryAuthentication: false,
RepositoryUsername: '',
RepositoryPassword: '',
};
this.onChange = this.onChange.bind(this);
this.onChangeRef = this.onChangeRef.bind(this);
}
onChangeRef(value) {
this.onChange({ RefName: value });
}
onChange(values) {
this.formValues = {
...this.formValues,
...values,
};
}
async pullAndRedeployApplication() {
return this.$async(async () => {
try {
const confirmed = await this.ModalService.confirmAsync({
title: 'Are you sure?',
message: 'Any changes to this application will be overriden by the definition in git and may cause a service interruption. Do you wish to continue?',
buttons: {
confirm: {
label: 'Update',
className: 'btn-warning',
},
},
});
if (!confirmed) {
return;
}
this.state.redeployInProgress = true;
await this.StackService.updateKubeGit(this.stack.Id, this.stack.EndpointId, this.namespace, this.formValues);
this.Notifications.success('Pulled and redeployed stack successfully');
await this.$state.reload();
} catch (err) {
this.Notifications.error('Failure', err, 'Failed redeploying application');
} finally {
this.state.redeployInProgress = false;
}
});
}
async saveGitSettings() {
return this.$async(async () => {
try {
this.state.saveGitSettingsInProgress = true;
await this.StackService.updateKubeStack({ EndpointId: this.stack.EndpointId, Id: this.stack.Id }, null, this.formValues);
this.Notifications.success('Save stack settings successfully');
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to save application settings');
} finally {
this.state.saveGitSettingsInProgress = false;
}
});
}
isSubmitButtonDisabled() {
return this.state.saveGitSettingsInProgress || this.state.redeployInProgress;
}
$onInit() {
console.log(this);
this.formValues.RefName = this.stack.GitConfig.ReferenceName;
if (this.stack.GitConfig && this.stack.GitConfig.Authentication) {
this.formValues.RepositoryUsername = this.stack.GitConfig.Authentication.Username;
this.formValues.RepositoryAuthentication = true;
this.state.isEdit = true;
}
}
}
export default KubernetesAppGitFormController;

View File

@ -0,0 +1,59 @@
<form name="$ctrl.redeployGitForm">
<div class="col-sm-12 form-section-title">
Redeploy from git repository
</div>
<div class="form-group text-muted">
<div class="col-sm-12">
<p>
Pull the latest manifest from git and redeploy the application.
</p>
</div>
</div>
<div class="form-group">
<div class="col-sm-12">
<p>
<a class="small interactive" ng-click="$ctrl.state.showConfig = !$ctrl.state.showConfig">
<i ng-class="['fa space-right', { 'fa-minus': $ctrl.state.showConfig, 'fa-plus': !$ctrl.state.showConfig }]" aria-hidden="true"></i>
{{ $ctrl.state.showConfig ? 'Hide' : 'Advanced' }} configuration
</a>
</p>
</div>
</div>
<git-form-ref-field ng-if="$ctrl.state.showConfig" value="$ctrl.formValues.RefName" on-change="($ctrl.onChangeRef)"></git-form-ref-field>
<git-form-auth-fieldset
ng-if="$ctrl.state.showConfig"
model="$ctrl.formValues"
is-edit="$ctrl.state.isEdit"
on-change="($ctrl.onChange)"
show-auth-explanation="true"
></git-form-auth-fieldset>
<div class="col-sm-12 form-section-title">
Actions
</div>
<!-- #Git buttons -->
<button
class="btn btn-sm btn-primary"
ng-click="$ctrl.pullAndRedeployApplication()"
ng-disabled="$ctrl.isSubmitButtonDisabled() || !$ctrl.redeployGitForm.$valid"
style="margin-top: 7px; margin-left: 0;"
button-spinner="ctrl.state.redeployInProgress"
analytics-on
analytics-category="kubernetes"
analytics-event="kubernetes-application-edit-git-pull"
>
<span ng-show="!$ctrl.state.redeployInProgress"> <i class="fa fa-sync space-right" aria-hidden="true"></i> Pull and update application </span>
<span ng-show="$ctrl.state.redeployInProgress">In progress...</span>
</button>
<button
class="btn btn-sm btn-primary"
ng-click="$ctrl.saveGitSettings()"
ng-disabled="$ctrl.isSubmitButtonDisabled() || !$ctrl.redeployGitForm.$valid"
style="margin-top: 7px; margin-left: 0;"
button-spinner="$ctrl.state.saveGitSettingsInProgress"
>
<span ng-show="!$ctrl.state.saveGitSettingsInProgress"> Save settings </span>
<span ng-show="$ctrl.state.saveGitSettingsInProgress">In progress...</span>
</button>
</form>

View File

@ -0,0 +1,13 @@
import angular from 'angular';
import controller from './kubernetes-app-git-form.controller';
const kubernetesAppGitForm = {
templateUrl: './kubernetes-app-git-form.html',
controller,
bindings: {
namespace: '<',
stack: '<',
},
};
angular.module('portainer.app').component('kubernetesAppGitForm', kubernetesAppGitForm);

View File

@ -13,7 +13,7 @@
* Sidebar
*/
#sidebar-wrapper {
background: #30426a;
background: var(--bg-sidebar-wrapper-color);
}
ul.sidebar .sidebar-main a,
@ -21,7 +21,7 @@ ul.sidebar .sidebar-main a,
ul.sidebar .sidebar-list a:hover,
#page-wrapper:not(.open) ul.sidebar .sidebar-title.separator {
/* Sidebar header and footer color */
background: #2d3e63;
background: var(--hover-sidebar-color);
}
ul.sidebar {
@ -63,7 +63,7 @@ ul.sidebar .sidebar-main .menu-icon {
}
ul.sidebar .sidebar-title {
color: #738bc0;
color: var(--text-sidebar-title-color);
font-size: 12px;
height: 35px;
line-height: 40px;
@ -74,7 +74,7 @@ ul.sidebar .sidebar-title {
ul.sidebar .sidebar-list a {
text-indent: 25px;
font-size: 15px;
color: #b2bfdc;
color: var(--text-sidebar-list-color);
line-height: 40px;
padding-left: 5px;
border-left: 3px solid transparent;
@ -132,7 +132,7 @@ ul.sidebar .sidebar-list .menu-icon {
}
.sidebar-footer div a {
color: #b2bfdc;
color: var(--text-sidebar-list-color);
font-size: 12px;
line-height: 43px;
}
@ -172,8 +172,8 @@ ul.sidebar .sidebar-list a {
ul.sidebar .sidebar-list a.active {
color: #fff;
border-left-color: #fff;
background: #2d3e63;
border-left-color: var(--border-sidebar-color);
background: var(--hover-sidebar-color);
}
.sidebar-header {
@ -181,7 +181,7 @@ ul.sidebar .sidebar-list a.active {
list-style: none;
text-indent: 20px;
font-size: 18px;
background: #2d3e63;
background: var(--bg-sidebar-header-color);
}
.sidebar-header a {
@ -255,7 +255,7 @@ ul.sidebar .sidebar-list a.active .menu-icon {
ul.sidebar .sidebar-list .sidebar-sublist a {
text-indent: 35px;
font-size: 12px;
color: #b2bfdc;
color: var(--text-sidebar-list-color);
line-height: 36px;
}
@ -280,7 +280,7 @@ ul.sidebar .sidebar-list .menu-icon {
ul.sidebar .sidebar-list .sidebar-sublist a.active {
color: #fff;
border-left: 3px solid #fff;
background: #2d3e63;
background: var(--bg-sidebar-color);
}
@media (max-height: 785px) {

View File

@ -32,7 +32,7 @@
</div>
</div>
<div class="searchBar" style="border-top: 2px solid #f6f6f6;">
<div class="searchBar">
<i class="fa fa-search searchIcon" aria-hidden="true"></i>
<input
type="text"

View File

@ -0,0 +1,70 @@
import { buildOption } from '@/portainer/components/box-selector';
export default class ThemeSettingsController {
/* @ngInject */
constructor($async, Authentication, ThemeManager, StateManager, UserService, Notifications) {
this.$async = $async;
this.Authentication = Authentication;
this.ThemeManager = ThemeManager;
this.StateManager = StateManager;
this.UserService = UserService;
this.Notifications = Notifications;
this.setTheme = this.setTheme.bind(this);
}
/** Theme Settings Panel */
async updateTheme() {
try {
await this.UserService.updateUserTheme(this.state.userId, this.state.userTheme);
this.state.themeInProgress = false;
this.Notifications.success('Success', 'User theme successfully updated');
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to update user theme');
}
}
setTheme(theme) {
this.ThemeManager.setTheme(theme);
this.state.themeInProgress = true;
}
$onInit() {
return this.$async(async () => {
this.state = {
userId: null,
userTheme: '',
initTheme: '',
defaultTheme: 'light',
themeInProgress: false,
};
this.state.availableThemes = [
buildOption('light', 'fas fa-sun', 'Light Theme', 'Default color mode', 'light'),
buildOption('dark', 'fas fa-moon', 'Dark Theme', 'Dark color mode', 'dark'),
buildOption('highcontrast', 'fas fa-adjust', 'High Contrast', 'High contrast color mode', 'highcontrast'),
];
this.state.availableTheme = {
light: 'light',
dark: 'dark',
highContrast: 'highcontrast',
};
try {
this.state.userId = await this.Authentication.getUserDetails().ID;
const data = await this.UserService.user(this.state.userId);
this.state.userTheme = data.UserTheme || this.state.defaultTheme;
this.state.initTheme = this.state.userTheme;
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to get user details');
}
});
}
$onDestroy() {
if (this.state.themeInProgress) {
this.ThemeManager.setTheme(this.state.initTheme);
}
}
}

View File

@ -0,0 +1,19 @@
<information-panel class="theme-information" title-text="Information">
<span class="small text-muted">
<p>
<i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Dark and High-contrast theme are experimental. Some UI components might not display properly.
</p>
</span>
</information-panel>
<rd-widget>
<rd-widget-header icon="fa-palette" title-text="Change user theme"></rd-widget-header>
<rd-widget-body>
<form class="theme-panel">
<!-- Theme -->
<box-selector radio-name="theme" ng-model="$ctrl.state.userTheme" options="$ctrl.state.availableThemes" on-change="($ctrl.setTheme)"></box-selector>
<button ng-click="$ctrl.updateTheme()" class="btn btn-primary btn-sm">Update theme</button>
<!-- !Theme -->
</form>
</rd-widget-body>
</rd-widget>

View File

@ -0,0 +1,7 @@
import angular from 'angular';
import controller from './theme-settings.controller';
angular.module('portainer.app').component('themeSettings', {
templateUrl: './theme-settings.html',
controller,
});

View File

@ -1,6 +1,6 @@
import _ from 'lodash-es';
export const KEY_REGEX = /(.+)/.source;
export const KEY_REGEX = /(.+?)/.source;
export const VALUE_REGEX = /(.*)?/.source;
const KEY_VALUE_REGEX = new RegExp(`^(${KEY_REGEX})\\s*=(${VALUE_REGEX})$`);

View File

@ -2,6 +2,7 @@ export function UserViewModel(data) {
this.Id = data.Id;
this.Username = data.Username;
this.Role = data.Role;
this.UserTheme = data.UserTheme;
if (data.Role === 1) {
this.RoleName = 'administrator';
} else {

View File

@ -12,6 +12,7 @@ angular.module('portainer.app').factory('Users', [
get: { method: 'GET', params: { id: '@id' } },
update: { method: 'PUT', params: { id: '@id' }, ignoreLoadingBar: true },
updatePassword: { method: 'PUT', params: { id: '@id', entity: 'passwd' } },
updateTheme: { method: 'PUT', params: { id: '@id' } },
remove: { method: 'DELETE', params: { id: '@id' } },
queryMemberships: { method: 'GET', isArray: true, params: { id: '@id', entity: 'memberships' } },
checkAdminUser: { method: 'GET', params: { id: 'admin', entity: 'check' }, isArray: true, ignoreLoadingBar: true },

View File

@ -137,10 +137,10 @@ angular.module('portainer.app').factory('EndpointService', [
return deferred.promise;
};
service.createLocalKubernetesEndpoint = function (name = 'local') {
service.createLocalKubernetesEndpoint = function (name = 'local', tagIds = []) {
var deferred = $q.defer();
FileUploadService.createEndpoint(name, PortainerEndpointCreationTypes.LocalKubernetesEnvironment, '', '', 1, [], true, true, true)
FileUploadService.createEndpoint(name, PortainerEndpointCreationTypes.LocalKubernetesEnvironment, '', '', 1, tagIds, true, true, true)
.then(function success(response) {
deferred.resolve(response.data);
})

View File

@ -91,6 +91,10 @@ angular.module('portainer.app').factory('UserService', [
return Users.updatePassword({ id: id }, payload).$promise;
};
service.updateUserTheme = function (id, userTheme) {
return Users.updateTheme({ id }, { userTheme }).$promise;
};
service.userMemberships = function (id) {
var deferred = $q.defer();

View File

@ -9,7 +9,9 @@ angular.module('portainer.app').factory('Authentication', [
'LocalStorage',
'StateManager',
'EndpointProvider',
function AuthenticationFactory($async, $state, Auth, OAuth, jwtHelper, LocalStorage, StateManager, EndpointProvider) {
'UserService',
'ThemeManager',
function AuthenticationFactory($async, $state, Auth, OAuth, jwtHelper, LocalStorage, StateManager, EndpointProvider, UserService, ThemeManager) {
'use strict';
var service = {};
@ -82,12 +84,20 @@ angular.module('portainer.app').factory('Authentication', [
return user;
}
async function setUserTheme() {
const data = await UserService.user(user.ID);
// Initialize user theme base on Usertheme from database
const userTheme = data.UserTheme;
ThemeManager.setTheme(userTheme);
}
async function setUser(jwt) {
LocalStorage.storeJWT(jwt);
var tokenPayload = jwtHelper.decodeToken(jwt);
user.username = tokenPayload.username;
user.ID = tokenPayload.id;
user.role = tokenPayload.role;
await setUserTheme();
}
function isAdmin() {

View File

@ -54,6 +54,11 @@ angular.module('portainer.app').factory('StateManager', [
LocalStorage.storeApplicationState(state.application);
};
manager.updateTheme = function (theme) {
state.application.theme = theme;
LocalStorage.storeApplicationState(state.application);
};
manager.updateSnapshotInterval = function (interval) {
state.application.snapshotInterval = interval;
LocalStorage.storeApplicationState(state.application);

View File

@ -0,0 +1,23 @@
angular.module('portainer.app').service('ThemeManager', ThemeManager);
/* @ngInject */
export function ThemeManager(StateManager) {
return {
setTheme,
defaultTheme,
};
function setTheme(theme) {
if (!theme) {
document.documentElement.removeAttribute('theme');
} else {
document.documentElement.setAttribute('theme', theme);
}
StateManager.updateTheme(theme);
}
function defaultTheme() {
document.documentElement.removeAttribute('theme');
}
}

View File

@ -78,5 +78,6 @@
</form>
</rd-widget-body>
</rd-widget>
<theme-settings></theme-settings>
</div>
</div>

View File

@ -5,11 +5,14 @@ angular.module('portainer.app').controller('AccountController', [
'UserService',
'Notifications',
'SettingsService',
function ($scope, $state, Authentication, UserService, Notifications, SettingsService) {
'StateManager',
'ThemeManager',
function ($scope, $state, Authentication, UserService, Notifications, SettingsService, StateManager, ThemeManager) {
$scope.formValues = {
currentPassword: '',
newPassword: '',
confirmPassword: '',
userTheme: '',
};
$scope.updatePassword = function () {
@ -23,8 +26,30 @@ angular.module('portainer.app').controller('AccountController', [
});
};
function initView() {
// Update DOM for theme attribute & LocalStorage
$scope.setTheme = function (theme) {
ThemeManager.setTheme(theme);
StateManager.updateTheme(theme);
};
// Rest API Call to update theme with userID in DB
$scope.updateTheme = function () {
UserService.updateUserTheme($scope.userID, $scope.formValues.userTheme)
.then(function success() {
Notifications.success('Success', 'User theme successfully updated');
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, err.msg);
});
};
async function initView() {
$scope.userID = Authentication.getUserDetails().ID;
const data = await UserService.user($scope.userID);
$scope.formValues.userTheme = data.Usertheme;
SettingsService.publicSettings()
.then(function success(data) {
$scope.AuthenticationMethod = data.AuthenticationMethod;

View File

@ -154,8 +154,9 @@ angular
$scope.addKubernetesEndpoint = function () {
var name = $scope.formValues.Name;
var tagIds = $scope.formValues.TagIds;
$scope.state.actionInProgress = true;
EndpointService.createLocalKubernetesEndpoint(name)
EndpointService.createLocalKubernetesEndpoint(name, tagIds)
.then(function success(result) {
Notifications.success('Endpoint created', name);
$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: result.Id });

View File

@ -168,10 +168,14 @@ function EndpointController(
payload.URL = 'tcp://' + endpoint.URL;
}
if (endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment || endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment) {
if (endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment) {
payload.URL = endpoint.URL;
}
if (endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment) {
payload.URL = 'https://' + endpoint.URL;
}
$scope.state.actionInProgress = true;
EndpointService.updateEndpoint(endpoint.Id, payload).then(
function success() {

View File

@ -2,7 +2,7 @@ import angular from 'angular';
class LogoutController {
/* @ngInject */
constructor($async, $state, $transition$, $window, Authentication, StateManager, Notifications, LocalStorage, SettingsService) {
constructor($async, $state, $transition$, $window, Authentication, StateManager, Notifications, LocalStorage, SettingsService, ThemeManager) {
this.$async = $async;
this.$state = $state;
this.$transition$ = $transition$;
@ -13,6 +13,7 @@ class LogoutController {
this.Notifications = Notifications;
this.LocalStorage = LocalStorage;
this.SettingsService = SettingsService;
this.ThemeManager = ThemeManager;
this.logo = this.StateManager.getState().application.logo;
this.logoutAsync = this.logoutAsync.bind(this);
@ -28,6 +29,8 @@ class LogoutController {
const performApiLogout = this.$transition$.params().performApiLogout;
const settings = await this.SettingsService.publicSettings();
this.ThemeManager.defaultTheme();
try {
await this.Authentication.logout(performApiLogout);
} finally {

View File

@ -3,6 +3,7 @@ angular.module('portainer.app').controller('MainController', [
'LocalStorage',
'StateManager',
'EndpointProvider',
'ThemeManager',
function ($scope, LocalStorage, StateManager, EndpointProvider) {
/**
* Sidebar Toggle & Cookie Control

View File

@ -21,7 +21,11 @@ angular.module('portainer.app').controller('RegistriesController', [
};
$scope.removeAction = function (selectedItems) {
ModalService.confirmDeletion('Do you want to remove the selected registries?', function onConfirm(confirmed) {
const regAttrMsg = selectedItems.length > 1 ? 'hese' : 'his';
const registriesMsg = selectedItems.length > 1 ? 'registries' : 'registry';
const msg = `T${regAttrMsg} ${registriesMsg} might be used by applications inside one or more endpoints. Removing the ${registriesMsg} could lead to a service interruption for the applications using t${regAttrMsg} ${registriesMsg}. Do you want to remove the selected ${registriesMsg}?`;
ModalService.confirmDeletion(msg, function onConfirm(confirmed) {
if (!confirmed) {
return;
}

View File

@ -1,18 +1,57 @@
#!/usr/bin/env bash
#!/bin/bash
set -euo pipefail
IFS=$'\n\t'
PLATFORM=$1
ARCH=$2
DOCKER_COMPOSE_VERSION=$3
if [ "${PLATFORM}" == 'linux' ] && [ "${ARCH}" == 'amd64' ]; then
wget -O "dist/docker-compose" "https://github.com/portainer/docker-compose-linux-amd64-static-binary/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose"
function download_binary() {
local PLATFORM=$1
local ARCH=$2
local BINARY_VERSION=$3
if [ "${PLATFORM}" == 'linux' ] && [ "${ARCH}" == 'amd64' ]; then
wget -O "dist/docker-compose" "https://github.com/portainer/docker-compose-linux-amd64-static-binary/releases/download/${BINARY_VERSION}/docker-compose"
chmod +x "dist/docker-compose"
elif [ "${PLATFORM}" == 'mac' ]; then
wget -O "dist/docker-compose" "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-Darwin-x86_64"
return
fi
if [ "${PLATFORM}" == 'mac' ]; then
wget -O "dist/docker-compose" "https://github.com/docker/compose/releases/download/${BINARY_VERSION}/docker-compose-Darwin-x86_64"
chmod +x "dist/docker-compose"
elif [ "${PLATFORM}" == 'win' ]; then
wget -O "dist/docker-compose.exe" "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-Windows-x86_64.exe"
return
fi
if [ "${PLATFORM}" == 'win' ]; then
wget -O "dist/docker-compose.exe" "https://github.com/docker/compose/releases/download/${BINARY_VERSION}/docker-compose-Windows-x86_64.exe"
chmod +x "dist/docker-compose.exe"
return
fi
}
function download_plugin() {
local PLATFORM=$1
local ARCH=$2
local PLUGIN_VERSION=$3
if [ "${PLATFORM}" == 'mac' ]; then
PLATFORM="darwin"
fi
FILENAME="docker-compose-${PLATFORM}-${ARCH}"
TARGET_FILENAME="docker-compose.plugin"
if [[ "$PLATFORM" == "windows" ]]; then
FILENAME="$FILENAME.exe"
TARGET_FILENAME="$TARGET_FILENAME.exe"
fi
wget -O "dist/$TARGET_FILENAME" "https://github.com/docker/compose-cli/releases/download/v$PLUGIN_VERSION/$FILENAME"
chmod +x "dist/$TARGET_FILENAME"
}
if [ "${PLATFORM}" == 'linux' ] && [ "${ARCH}" != 'amd64' ]; then
download_plugin "$PLATFORM" "$ARCH" "$DOCKER_COMPOSE_VERSION"
fi
exit 0
download_binary "$PLATFORM" "$ARCH" "$DOCKER_COMPOSE_VERSION"

View File

@ -22,6 +22,7 @@ module.exports = function (grunt) {
dockerWindowsVersion: '19-03-12',
dockerLinuxComposeVersion: '1.27.4',
dockerWindowsComposeVersion: '1.28.0',
dockerComposePluginVersion: '2.0.0-beta.6',
komposeVersion: 'v1.22.0',
kubectlVersion: 'v1.18.0',
},
@ -214,13 +215,24 @@ function shell_download_docker_compose_binary(p, a) {
var ia = as[a] || a;
var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsComposeVersion %>' : '<%= binaries.dockerLinuxComposeVersion %>';
return [
'if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ]; then',
'echo "Docker Compose binary exists";',
'else',
'build/download_docker_compose_binary.sh ' + ip + ' ' + ia + ' ' + binaryVersion + ';',
'fi',
].join(' ');
// plugin
if (p === 'linux' && a !== 'amd64') {
if (a === 'arm64') {
ia = 'arm64';
}
if (a === 'arm') {
ia = 'armv7';
}
binaryVersion = '<%= binaries.dockerComposePluginVersion %>';
}
return `
if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ] || [ -f dist/docker-compose.plugin ] || [ -f dist/docker-compose.plugin.exe ]; then
echo "Docker Compose binary exists";
else
build/download_docker_compose_binary.sh ${ip} ${ia} ${binaryVersion};
fi`;
}
function shell_download_kompose_binary(p, a) {