Compare commits
22 Commits
release/2.
...
debug-api-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da3856503f | ||
|
|
0b6362a4bd | ||
|
|
8bbb6e5a6e | ||
|
|
ea71ce44fa | ||
|
|
a3f2b4b0af | ||
|
|
f4c7896046 | ||
|
|
b1272b9da3 | ||
|
|
943c0a6256 | ||
|
|
d245e196c1 | ||
|
|
d6abf03d42 | ||
|
|
f98585d832 | ||
|
|
bc3e973830 | ||
|
|
0292523855 | ||
|
|
044d756626 | ||
|
|
a9c0c5f835 | ||
|
|
2b0a519c36 | ||
|
|
871da94da0 | ||
|
|
81faf20f20 | ||
|
|
b8757ac8eb | ||
|
|
b927e08d5e | ||
|
|
e2188edc9d | ||
|
|
97d2a3bdf3 |
@@ -31,12 +31,7 @@ rules:
|
||||
[
|
||||
'error',
|
||||
{
|
||||
pathGroups:
|
||||
[
|
||||
{ pattern: '@@/**', group: 'internal', position: 'after' },
|
||||
{ pattern: '@/**', group: 'internal' },
|
||||
{ pattern: '{Kubernetes,Portainer,Agent,Azure,Docker}/**', group: 'internal' },
|
||||
],
|
||||
pathGroups: [{ pattern: '@/**', group: 'internal' }, { pattern: '{Kubernetes,Portainer,Agent,Azure,Docker}/**', group: 'internal' }],
|
||||
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
||||
pathGroupsExcludedImportTypes: ['internal'],
|
||||
},
|
||||
@@ -46,7 +41,6 @@ settings:
|
||||
'import/resolver':
|
||||
alias:
|
||||
map:
|
||||
- ['@@', './app/react/components']
|
||||
- ['@', './app']
|
||||
extensions: ['.js', '.ts', '.tsx']
|
||||
|
||||
@@ -58,7 +52,6 @@ overrides:
|
||||
parser: '@typescript-eslint/parser'
|
||||
plugins:
|
||||
- '@typescript-eslint'
|
||||
- 'regex'
|
||||
extends:
|
||||
- airbnb
|
||||
- airbnb-typescript
|
||||
@@ -75,14 +68,7 @@ overrides:
|
||||
version: 'detect'
|
||||
rules:
|
||||
import/order:
|
||||
[
|
||||
'error',
|
||||
{
|
||||
pathGroups: [{ pattern: '@@/**', group: 'internal', position: 'after' }, { pattern: '@/**', group: 'internal' }],
|
||||
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
|
||||
'newlines-between': 'always',
|
||||
},
|
||||
]
|
||||
['error', { pathGroups: [{ pattern: '@/**', group: 'internal' }], groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], 'newlines-between': 'always' }]
|
||||
func-style: [error, 'declaration']
|
||||
import/prefer-default-export: off
|
||||
no-use-before-define: ['error', { functions: false }]
|
||||
@@ -104,7 +90,6 @@ overrides:
|
||||
'react/jsx-no-bind': off
|
||||
'no-await-in-loop': 'off'
|
||||
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }]
|
||||
'regex/invalid': ['error', [{ 'regex': 'data-feather="(.*)"', 'message': 'Please use `react-feather` package instead' }]]
|
||||
- files:
|
||||
- app/**/*.test.*
|
||||
extends:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,7 +7,6 @@ storybook-static
|
||||
.tmp
|
||||
**/.vscode/settings.json
|
||||
**/.vscode/tasks.json
|
||||
.vscode
|
||||
*.DS_Store
|
||||
|
||||
.eslintcache
|
||||
|
||||
@@ -3,7 +3,6 @@ import '../app/assets/css';
|
||||
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
|
||||
import { initialize as initMSW, mswDecorator } from 'msw-storybook-addon';
|
||||
import { handlers } from '@/setup-tests/server-handlers';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
// Initialize MSW
|
||||
initMSW({
|
||||
@@ -32,17 +31,11 @@ export const parameters = {
|
||||
},
|
||||
};
|
||||
|
||||
const testQueryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
export const decorators = [
|
||||
(Story) => (
|
||||
<QueryClientProvider client={testQueryClient}>
|
||||
<UIRouter plugins={[pushStateLocationPlugin]}>
|
||||
<Story />
|
||||
</UIRouter>
|
||||
</QueryClientProvider>
|
||||
<UIRouter plugins={[pushStateLocationPlugin]}>
|
||||
<Story />
|
||||
</UIRouter>
|
||||
),
|
||||
mswDecorator,
|
||||
];
|
||||
|
||||
10
README.md
10
README.md
@@ -12,15 +12,21 @@ Portainer consists of a single container that can run on any cluster. It can be
|
||||
- [Take5 – get 5 free nodes of Portainer Business for as long as you want them](https://portainer.io/pricing/take5)
|
||||
- [Portainer BE install guide](https://install.portainer.io)
|
||||
|
||||
## Demo
|
||||
|
||||
You can try out the public demo instance: http://demo.portainer.io/ (login with the username **admin** and the password **tryportainer**).
|
||||
|
||||
Please note that the public demo cluster is **reset every 15min**.
|
||||
|
||||
## Latest Version
|
||||
|
||||
Portainer CE is updated regularly. We aim to do an update release every couple of months.
|
||||
|
||||
[](https://github.com/portainer/portainer/releases/latest)
|
||||
**The latest version of Portainer is 2.9.x**. Portainer is on version 2, the second number denotes the month of release.
|
||||
|
||||
## Getting started
|
||||
|
||||
- [Deploy Portainer](https://docs.portainer.io/start/install)
|
||||
- [Deploy Portainer](https://docs.portainer.io/v/ce-2.9/start/install)
|
||||
- [Documentation](https://documentation.portainer.io)
|
||||
- [Contribute to the project](https://documentation.portainer.io/contributing/instructions/)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package adminmonitor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -10,10 +11,10 @@ import (
|
||||
httperror "github.com/portainer/libhttp/error"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var logFatalf = log.Fatalf
|
||||
|
||||
const RedirectReasonAdminInitTimeout string = "AdminInitTimeout"
|
||||
|
||||
type Monitor struct {
|
||||
@@ -48,28 +49,24 @@ func (m *Monitor) Start() {
|
||||
m.cancellationFunc = cancellationFunc
|
||||
|
||||
go func() {
|
||||
log.Debug().Msg("start initialization monitor")
|
||||
|
||||
log.Println("[DEBUG] [internal,init] [message: start initialization monitor ]")
|
||||
select {
|
||||
case <-time.After(m.timeout):
|
||||
initialized, err := m.WasInitialized()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
logFatalf("%s", err)
|
||||
}
|
||||
|
||||
if !initialized {
|
||||
log.Info().Msg("the Portainer instance timed out for security purposes, to re-enable your Portainer instance, you will need to restart Portainer")
|
||||
|
||||
log.Println("[INFO] [internal,init] The Portainer instance timed out for security purposes. To re-enable your Portainer instance, you will need to restart Portainer")
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.adminInitDisabled = true
|
||||
return
|
||||
}
|
||||
case <-cancellationCtx.Done():
|
||||
log.Debug().Msg("canceling initialization monitor")
|
||||
log.Println("[DEBUG] [internal,init] [message: canceling initialization monitor]")
|
||||
case <-m.shutdownCtx.Done():
|
||||
log.Debug().Msg("shutting down initialization monitor")
|
||||
log.Println("[DEBUG] [internal,init] [message: shutting down initialization monitor]")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/url"
|
||||
)
|
||||
|
||||
// GetAgentVersionAndPlatform returns the agent version and platform
|
||||
//
|
||||
// it sends a ping to the agent and parses the version and platform from the headers
|
||||
func GetAgentVersionAndPlatform(endpointUrl string, tlsConfig *tls.Config) (portainer.AgentPlatform, string, error) {
|
||||
httpCli := &http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
httpCli.Transport = &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
}
|
||||
|
||||
parsedURL, err := url.ParseURL(endpointUrl + "/ping")
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
parsedURL.Scheme = "https"
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, parsedURL.String(), nil)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
resp, err := httpCli.Do(req)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
return 0, "", fmt.Errorf("Failed request with status %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
version := resp.Header.Get(portainer.PortainerAgentHeader)
|
||||
if version == "" {
|
||||
return 0, "", errors.New("Version Header is missing")
|
||||
}
|
||||
|
||||
agentPlatformHeader := resp.Header.Get(portainer.HTTPResponseAgentPlatform)
|
||||
if agentPlatformHeader == "" {
|
||||
return 0, "", errors.New("Agent Platform Header is missing")
|
||||
}
|
||||
|
||||
agentPlatformNumber, err := strconv.Atoi(agentPlatformHeader)
|
||||
if err != nil {
|
||||
return 0, "", err
|
||||
}
|
||||
|
||||
if agentPlatformNumber == 0 {
|
||||
return 0, "", errors.New("Agent platform is invalid")
|
||||
}
|
||||
|
||||
return portainer.AgentPlatform(agentPlatformNumber), version, nil
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package apikey
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,8 +10,6 @@ import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func Test_SatisfiesAPIKeyServiceInterface(t *testing.T) {
|
||||
@@ -21,7 +20,7 @@ func Test_SatisfiesAPIKeyServiceInterface(t *testing.T) {
|
||||
func Test_GenerateApiKey(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -75,7 +74,7 @@ func Test_GenerateApiKey(t *testing.T) {
|
||||
func Test_GetAPIKey(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -95,7 +94,7 @@ func Test_GetAPIKey(t *testing.T) {
|
||||
func Test_GetAPIKeys(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -116,7 +115,7 @@ func Test_GetAPIKeys(t *testing.T) {
|
||||
func Test_GetDigestUserAndKey(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -152,7 +151,7 @@ func Test_GetDigestUserAndKey(t *testing.T) {
|
||||
func Test_UpdateAPIKey(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -170,8 +169,8 @@ func Test_UpdateAPIKey(t *testing.T) {
|
||||
_, apiKeyGot, err := service.GetDigestUserAndKey(apiKey.Digest)
|
||||
is.NoError(err)
|
||||
|
||||
log.Debug().Msgf("%+v", apiKey)
|
||||
log.Debug().Msgf("%+v", apiKeyGot)
|
||||
log.Println(apiKey)
|
||||
log.Println(apiKeyGot)
|
||||
|
||||
is.Equal(apiKey.LastUsed, apiKeyGot.LastUsed)
|
||||
|
||||
@@ -200,7 +199,7 @@ func Test_UpdateAPIKey(t *testing.T) {
|
||||
func Test_DeleteAPIKey(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
@@ -241,7 +240,7 @@ func Test_DeleteAPIKey(t *testing.T) {
|
||||
func Test_InvalidateUserKeyCache(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
|
||||
|
||||
@@ -34,45 +34,3 @@ func TarFileInBuffer(fileContent []byte, fileName string, mode int64) ([]byte, e
|
||||
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// tarFileInBuffer represents a tar archive buffer.
|
||||
type tarFileInBuffer struct {
|
||||
b *bytes.Buffer
|
||||
w *tar.Writer
|
||||
}
|
||||
|
||||
func NewTarFileInBuffer() *tarFileInBuffer {
|
||||
var b bytes.Buffer
|
||||
return &tarFileInBuffer{
|
||||
b: &b,
|
||||
w: tar.NewWriter(&b),
|
||||
}
|
||||
}
|
||||
|
||||
// Put puts a single file to tar archive buffer.
|
||||
func (t *tarFileInBuffer) Put(fileContent []byte, fileName string, mode int64) error {
|
||||
hdr := &tar.Header{
|
||||
Name: fileName,
|
||||
Mode: mode,
|
||||
Size: int64(len(fileContent)),
|
||||
}
|
||||
|
||||
if err := t.w.WriteHeader(hdr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := t.w.Write(fileContent); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Bytes returns the archive as a byte array.
|
||||
func (t *tarFileInBuffer) Bytes() []byte {
|
||||
return t.b.Bytes()
|
||||
}
|
||||
|
||||
func (t *tarFileInBuffer) Close() error {
|
||||
return t.w.Close()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -26,7 +27,9 @@ func listFiles(dir string) []string {
|
||||
}
|
||||
|
||||
func Test_shouldCreateArhive(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutils.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
content := []byte("content")
|
||||
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
|
||||
os.MkdirAll(path.Join(tmpdir, "dir"), 0700)
|
||||
@@ -37,7 +40,9 @@ func Test_shouldCreateArhive(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, filepath.Join(tmpdir, fmt.Sprintf("%s.tar.gz", filepath.Base(tmpdir))), gzPath)
|
||||
|
||||
extractionDir := t.TempDir()
|
||||
extractionDir, _ := ioutils.TempDir("", "extract")
|
||||
defer os.RemoveAll(extractionDir)
|
||||
|
||||
cmd := exec.Command("tar", "-xzf", gzPath, "-C", extractionDir)
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
@@ -58,7 +63,9 @@ func Test_shouldCreateArhive(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_shouldCreateArhiveXXXXX(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutils.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
content := []byte("content")
|
||||
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
|
||||
os.MkdirAll(path.Join(tmpdir, "dir"), 0700)
|
||||
@@ -69,7 +76,9 @@ func Test_shouldCreateArhiveXXXXX(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, filepath.Join(tmpdir, fmt.Sprintf("%s.tar.gz", filepath.Base(tmpdir))), gzPath)
|
||||
|
||||
extractionDir := t.TempDir()
|
||||
extractionDir, _ := ioutils.TempDir("", "extract")
|
||||
defer os.RemoveAll(extractionDir)
|
||||
|
||||
r, _ := os.Open(gzPath)
|
||||
ExtractTarGz(r, extractionDir)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,12 +2,16 @@ package archive
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnzipFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
dir, err := ioutil.TempDir("", "unzip-test-")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
/*
|
||||
Archive structure.
|
||||
├── 0
|
||||
@@ -17,7 +21,7 @@ func TestUnzipFile(t *testing.T) {
|
||||
└── 0.txt
|
||||
*/
|
||||
|
||||
err := UnzipFile("./testdata/sample_archive.zip", dir)
|
||||
err = UnzipFile("./testdata/sample_archive.zip", dir)
|
||||
|
||||
assert.NoError(t, err)
|
||||
archiveDir := dir + "/sample_archive"
|
||||
|
||||
@@ -7,14 +7,13 @@ import (
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
"github.com/portainer/portainer/api/http/offlinegate"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const rwxr__r__ os.FileMode = 0744
|
||||
@@ -48,9 +47,9 @@ func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datasto
|
||||
|
||||
err := datastore.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("filename", exportFilename).Msg("failed to export")
|
||||
logrus.WithError(err).Debugf("failed to export to %s", exportFilename)
|
||||
} else {
|
||||
log.Debug().Str("filename", exportFilename).Msg("file exported")
|
||||
logrus.Debugf("exported to %s", exportFilename)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
package build
|
||||
|
||||
// Variables to be set during the build time
|
||||
var BuildNumber string
|
||||
var ImageTag string
|
||||
var NodejsVersion string
|
||||
var YarnVersion string
|
||||
var WebpackVersion string
|
||||
var GoVersion string
|
||||
@@ -3,17 +3,16 @@ package chisel
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
chserver "github.com/jpillora/chisel/server"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
chserver "github.com/jpillora/chisel/server"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -65,11 +64,7 @@ func (service *Service) pingAgent(endpointID portainer.EndpointID) error {
|
||||
// KeepTunnelAlive keeps the tunnel of the given environment for maxAlive duration, or until ctx is done
|
||||
func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx context.Context, maxAlive time.Duration) {
|
||||
go func() {
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Float64("max_alive_minutes", maxAlive.Minutes()).
|
||||
Msg("start")
|
||||
|
||||
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: start for %.0f minutes]\n", endpointID, maxAlive.Minutes())
|
||||
maxAliveTicker := time.NewTicker(maxAlive)
|
||||
defer maxAliveTicker.Stop()
|
||||
pingTicker := time.NewTicker(tunnelCleanupInterval)
|
||||
@@ -81,25 +76,14 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
|
||||
service.SetTunnelStatusToActive(endpointID)
|
||||
err := service.pingAgent(endpointID)
|
||||
if err != nil {
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Err(err).
|
||||
Msg("ping agent")
|
||||
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [warning: ping agent err=%s]\n", endpointID, err)
|
||||
}
|
||||
case <-maxAliveTicker.C:
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Float64("timeout_minutes", maxAlive.Minutes()).
|
||||
Msg("tunnel keep alive timeout")
|
||||
|
||||
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as %.0f minutes timeout]\n", endpointID, maxAlive.Minutes())
|
||||
return
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Err(err).
|
||||
Msg("tunnel stop")
|
||||
|
||||
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as err=%s]\n", endpointID, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -178,10 +162,7 @@ func (service *Service) retrievePrivateKeySeed() (string, error) {
|
||||
}
|
||||
|
||||
func (service *Service) startTunnelVerificationLoop() {
|
||||
log.Debug().
|
||||
Float64("check_interval_seconds", tunnelCleanupInterval.Seconds()).
|
||||
Msg("starting tunnel management process")
|
||||
|
||||
log.Printf("[DEBUG] [chisel, monitoring] [check_interval_seconds: %f] [message: starting tunnel management process]", tunnelCleanupInterval.Seconds())
|
||||
ticker := time.NewTicker(tunnelCleanupInterval)
|
||||
|
||||
for {
|
||||
@@ -189,12 +170,10 @@ func (service *Service) startTunnelVerificationLoop() {
|
||||
case <-ticker.C:
|
||||
service.checkTunnels()
|
||||
case <-service.shutdownCtx.Done():
|
||||
log.Debug().Msg("shutting down tunnel service")
|
||||
|
||||
log.Println("[DEBUG] Shutting down tunnel service")
|
||||
if err := service.StopTunnelServer(); err != nil {
|
||||
log.Debug().Err(err).Msg("stopped tunnel service")
|
||||
log.Printf("Stopped tunnel service: %s", err)
|
||||
}
|
||||
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
@@ -216,40 +195,22 @@ func (service *Service) checkTunnels() {
|
||||
}
|
||||
|
||||
elapsed := time.Since(tunnel.LastActivity)
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Str("status", tunnel.Status).
|
||||
Float64("status_time_seconds", elapsed.Seconds()).
|
||||
Msg("environment tunnel monitoring")
|
||||
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %d] [status: %s] [status_time_seconds: %f] [message: environment tunnel monitoring]", endpointID, tunnel.Status, elapsed.Seconds())
|
||||
|
||||
if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() < requiredTimeout.Seconds() {
|
||||
continue
|
||||
} else if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() > requiredTimeout.Seconds() {
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Str("status", tunnel.Status).
|
||||
Float64("status_time_seconds", elapsed.Seconds()).
|
||||
Float64("timeout_seconds", requiredTimeout.Seconds()).
|
||||
Msg("REQUIRED state timeout exceeded")
|
||||
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %d] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: REQUIRED state timeout exceeded]", endpointID, tunnel.Status, elapsed.Seconds(), requiredTimeout.Seconds())
|
||||
}
|
||||
|
||||
if tunnel.Status == portainer.EdgeAgentActive && elapsed.Seconds() < activeTimeout.Seconds() {
|
||||
continue
|
||||
} else if tunnel.Status == portainer.EdgeAgentActive && elapsed.Seconds() > activeTimeout.Seconds() {
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Str("status", tunnel.Status).
|
||||
Float64("status_time_seconds", elapsed.Seconds()).
|
||||
Float64("timeout_seconds", activeTimeout.Seconds()).
|
||||
Msg("ACTIVE state timeout exceeded")
|
||||
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %d] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: ACTIVE state timeout exceeded]", endpointID, tunnel.Status, elapsed.Seconds(), activeTimeout.Seconds())
|
||||
|
||||
err := service.snapshotEnvironment(endpointID, tunnel.Port)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Int("endpoint_id", int(endpointID)).Err(
|
||||
|
||||
err).
|
||||
Msg("unable to snapshot Edge environment")
|
||||
log.Printf("[ERROR] [snapshot] Unable to snapshot Edge environment (id: %d): %s", endpointID, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,15 @@ package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
@@ -34,7 +35,6 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
|
||||
TunnelPort: kingpin.Flag("tunnel-port", "Port to serve the tunnel server").Default(defaultTunnelServerPort).String(),
|
||||
Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(),
|
||||
Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(),
|
||||
DemoEnvironment: kingpin.Flag("demo", "Demo environment").Bool(),
|
||||
EndpointURL: kingpin.Flag("host", "Environment URL").Short('H').String(),
|
||||
FeatureFlags: BoolPairs(kingpin.Flag("feat", "List of feature flags").Hidden()),
|
||||
EnableEdgeComputeFeatures: kingpin.Flag("edge-compute", "Enable Edge Compute features").Bool(),
|
||||
@@ -61,8 +61,6 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
|
||||
MaxBatchSize: kingpin.Flag("max-batch-size", "Maximum size of a batch").Int(),
|
||||
MaxBatchDelay: kingpin.Flag("max-batch-delay", "Maximum delay before a batch starts").Duration(),
|
||||
SecretKeyName: kingpin.Flag("secret-key-name", "Secret key name for encryption and will be used as /run/secrets/<secret-key-name>.").Default(defaultSecretKeyName).String(),
|
||||
LogLevel: kingpin.Flag("log-level", "Set the minimum logging level to show").Default("INFO").Enum("DEBUG", "INFO", "WARN", "ERROR"),
|
||||
LogMode: kingpin.Flag("log-mode", "Set the logging output mode").Default("PRETTY").Enum("PRETTY", "JSON"),
|
||||
}
|
||||
|
||||
kingpin.Parse()
|
||||
@@ -102,11 +100,11 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
|
||||
|
||||
func displayDeprecationWarnings(flags *portainer.CLIFlags) {
|
||||
if *flags.NoAnalytics {
|
||||
log.Warn().Msg("the --no-analytics flag has been kept to allow migration of instances running a previous version of Portainer with this flag enabled, to version 2.0 where enabling this flag will have no effect")
|
||||
log.Println("Warning: The --no-analytics flag has been kept to allow migration of instances running a previous version of Portainer with this flag enabled, to version 2.0 where enabling this flag will have no effect.")
|
||||
}
|
||||
|
||||
if *flags.SSL {
|
||||
log.Warn().Msg("SSL is enabled by default and there is no need for the --ssl flag, it has been kept to allow migration of instances running a previous version of Portainer with this flag enabled")
|
||||
log.Println("Warning: SSL is enabled by default and there is no need for the --ssl flag. It has been kept to allow migration of instances running a previous version of Portainer with this flag enabled")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func importFromJson(fileService portainer.FileService, store *datastore.Store) {
|
||||
@@ -12,17 +13,17 @@ func importFromJson(fileService portainer.FileService, store *datastore.Store) {
|
||||
importFile := "/data/import.json"
|
||||
if exists, _ := fileService.FileExists(importFile); exists {
|
||||
if err := store.Import(importFile); err != nil {
|
||||
log.Error().Str("filename", importFile).Err(err).Msg("import failed")
|
||||
logrus.WithError(err).Debugf("Import %s failed", importFile)
|
||||
|
||||
// TODO: should really rollback on failure, but then we have nothing.
|
||||
} else {
|
||||
log.Info().Str("filename", importFile).Msg("successfully imported the file to a new portainer database")
|
||||
logrus.Printf("Successfully imported %s to new portainer database", importFile)
|
||||
}
|
||||
|
||||
// TODO: this is bad - its to ensure that any defaults that were broken in import, or migrations get set back to what we want
|
||||
// I also suspect that everything from "Init to Init" is potentially a migration
|
||||
err := store.Init()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing data store")
|
||||
log.Fatalf("Failed initializing data store: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"log"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/rs/zerolog/pkgerrors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func configureLogger() {
|
||||
zerolog.ErrorStackFieldName = "stack_trace"
|
||||
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
|
||||
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
|
||||
logger := logrus.New() // logger is to implicitly substitute stdlib's log
|
||||
log.SetOutput(logger.Writer())
|
||||
|
||||
stdlog.SetFlags(0)
|
||||
stdlog.SetOutput(log.Logger)
|
||||
formatter := &logrus.TextFormatter{DisableTimestamp: false, DisableLevelTruncation: true}
|
||||
|
||||
log.Logger = log.Logger.With().Caller().Stack().Logger()
|
||||
}
|
||||
|
||||
func setLoggingLevel(level string) {
|
||||
switch level {
|
||||
case "ERROR":
|
||||
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
|
||||
case "WARN":
|
||||
zerolog.SetGlobalLevel(zerolog.WarnLevel)
|
||||
case "INFO":
|
||||
zerolog.SetGlobalLevel(zerolog.InfoLevel)
|
||||
case "DEBUG":
|
||||
zerolog.SetGlobalLevel(zerolog.DebugLevel)
|
||||
}
|
||||
}
|
||||
|
||||
func setLoggingMode(mode string) {
|
||||
switch mode {
|
||||
case "PRETTY":
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{
|
||||
Out: os.Stderr,
|
||||
NoColor: true,
|
||||
TimeFormat: "2006/01/02 03:04PM",
|
||||
FormatMessage: formatMessage})
|
||||
case "JSON":
|
||||
log.Logger = log.Output(os.Stderr)
|
||||
}
|
||||
}
|
||||
|
||||
func formatMessage(i interface{}) string {
|
||||
if i == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s |", i)
|
||||
logger.SetFormatter(formatter)
|
||||
logrus.SetFormatter(formatter)
|
||||
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,18 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/portainer/libhelm"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/apikey"
|
||||
"github.com/portainer/portainer/api/build"
|
||||
"github.com/portainer/portainer/api/chisel"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
@@ -21,7 +23,6 @@ import (
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/demo"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
"github.com/portainer/portainer/api/exec"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
@@ -42,38 +43,34 @@ import (
|
||||
"github.com/portainer/portainer/api/oauth"
|
||||
"github.com/portainer/portainer/api/scheduler"
|
||||
"github.com/portainer/portainer/api/stacks"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func initCLI() *portainer.CLIFlags {
|
||||
var cliService portainer.CLIService = &cli.Service{}
|
||||
flags, err := cliService.ParseFlags(portainer.APIVersion)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed parsing flags")
|
||||
logrus.Fatalf("Failed parsing flags: %v", err)
|
||||
}
|
||||
|
||||
err = cliService.ValidateFlags(flags)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed validating flags")
|
||||
logrus.Fatalf("Failed validating flags:%v", err)
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func initFileService(dataStorePath string) portainer.FileService {
|
||||
fileService, err := filesystem.NewService(dataStorePath, "")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating file service")
|
||||
logrus.Fatalf("Failed creating file service: %v", err)
|
||||
}
|
||||
|
||||
return fileService
|
||||
}
|
||||
|
||||
func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService portainer.FileService, shutdownCtx context.Context) dataservices.DataStore {
|
||||
connection, err := database.NewDatabase("boltdb", *flags.Data, secretKey)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating database connection")
|
||||
logrus.Fatalf("failed creating database connection: %s", err)
|
||||
}
|
||||
|
||||
if bconn, ok := connection.(*boltdb.DbConnection); ok {
|
||||
@@ -81,31 +78,30 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
bconn.MaxBatchDelay = *flags.MaxBatchDelay
|
||||
bconn.InitialMmapSize = *flags.InitialMmapSize
|
||||
} else {
|
||||
log.Fatal().Msg("failed creating database connection: expecting a boltdb database type but a different one was received")
|
||||
logrus.Fatalf("failed creating database connection: expecting a boltdb database type but a different one was received")
|
||||
}
|
||||
|
||||
store := datastore.NewStore(*flags.Data, fileService, connection)
|
||||
isNew, err := store.Open()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed opening store")
|
||||
logrus.Fatalf("Failed opening store: %v", err)
|
||||
}
|
||||
|
||||
if *flags.Rollback {
|
||||
err := store.Rollback(false)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed rolling back")
|
||||
logrus.Fatalf("Failed rolling back: %v", err)
|
||||
}
|
||||
|
||||
log.Info().Msg("exiting rollback")
|
||||
logrus.Println("Exiting rollback")
|
||||
os.Exit(0)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init sets some defaults - it's basically a migration
|
||||
err = store.Init()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing data store")
|
||||
logrus.Fatalf("Failed initializing data store: %v", err)
|
||||
}
|
||||
|
||||
if isNew {
|
||||
@@ -114,25 +110,24 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
|
||||
err := updateSettingsFromFlags(store, flags)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed updating settings from flags")
|
||||
logrus.Fatalf("Failed updating settings from flags: %v", err)
|
||||
}
|
||||
} else {
|
||||
storedVersion, err := store.VersionService.DBVersion()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failure during creation of new database")
|
||||
logrus.Fatalf("Something Failed during creation of new database: %v", err)
|
||||
}
|
||||
|
||||
if storedVersion != portainer.DBVersion {
|
||||
err = store.MigrateData()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed migration")
|
||||
logrus.Fatalf("Failed migration: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = updateSettingsFromFlags(store, flags)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed updating settings from flags")
|
||||
log.Fatalf("Failed updating settings from flags: %v", err)
|
||||
}
|
||||
|
||||
// this is for the db restore functionality - needs more tests.
|
||||
@@ -144,19 +139,19 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
|
||||
err := store.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Str("filename", exportFilename).Err(err).Msg("failed to export")
|
||||
logrus.WithError(err).Debugf("Failed to export to %s", exportFilename)
|
||||
} else {
|
||||
log.Debug().Str("filename", exportFilename).Msg("exported")
|
||||
logrus.Debugf("exported to %s", exportFilename)
|
||||
}
|
||||
connection.Close()
|
||||
}()
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
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.Fatal().Err(err).Msg("failed creating compose manager")
|
||||
logrus.Fatalf("Failed creating compose manager: %v", err)
|
||||
}
|
||||
|
||||
return composeWrapper
|
||||
@@ -190,7 +185,6 @@ func initJWTService(userSessionTimeout string, dataStore dataservices.DataStore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return jwtService, nil
|
||||
}
|
||||
|
||||
@@ -210,8 +204,8 @@ func initOAuthService() portainer.OAuthService {
|
||||
return oauth.NewService()
|
||||
}
|
||||
|
||||
func initGitService(ctx context.Context) portainer.GitService {
|
||||
return git.NewService(ctx)
|
||||
func initGitService() portainer.GitService {
|
||||
return git.NewService()
|
||||
}
|
||||
|
||||
func initSSLService(addr, certPath, keyPath string, fileService portainer.FileService, dataStore dataservices.DataStore, shutdownTrigger context.CancelFunc) (*ssl.Service, error) {
|
||||
@@ -235,8 +229,8 @@ func initDockerClientFactory(signatureService portainer.DigitalSignatureService,
|
||||
return docker.NewClientFactory(signatureService, reverseTunnelService)
|
||||
}
|
||||
|
||||
func initKubernetesClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, dataStore dataservices.DataStore, instanceID, addrHTTPS, userSessionTimeout string) (*kubecli.ClientFactory, error) {
|
||||
return kubecli.NewClientFactory(signatureService, reverseTunnelService, dataStore, instanceID, addrHTTPS, userSessionTimeout)
|
||||
func initKubernetesClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, instanceID string, dataStore dataservices.DataStore) *kubecli.ClientFactory {
|
||||
return kubecli.NewClientFactory(signatureService, reverseTunnelService, instanceID, dataStore)
|
||||
}
|
||||
|
||||
func initSnapshotService(snapshotIntervalFromFlag string, dataStore dataservices.DataStore, dockerClientFactory *docker.ClientFactory, kubernetesClientFactory *kubecli.ClientFactory, shutdownCtx context.Context) (portainer.SnapshotService, error) {
|
||||
@@ -345,7 +339,11 @@ func enableFeaturesFromFlags(dataStore dataservices.DataStore, flags *portainer.
|
||||
return fmt.Errorf("feature flag's '%s' value should be true or false", feat.Name)
|
||||
}
|
||||
|
||||
log.Info().Str("feature", string(*correspondingFeature)).Bool("state", featureState).Msg("")
|
||||
if featureState {
|
||||
logrus.Printf("Feature %v : on", *correspondingFeature)
|
||||
} else {
|
||||
logrus.Printf("Feature %v : off", *correspondingFeature)
|
||||
}
|
||||
|
||||
settings.FeatureFlagSettings[*correspondingFeature] = featureState
|
||||
}
|
||||
@@ -373,7 +371,7 @@ func generateAndStoreKeyPair(fileService portainer.FileService, signatureService
|
||||
func initKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
|
||||
existingKeyPair, err := fileService.KeyPairFilesExist()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed checking for existing key pair")
|
||||
logrus.Fatalf("Failed checking for existing key pair: %v", err)
|
||||
}
|
||||
|
||||
if existingKeyPair {
|
||||
@@ -443,11 +441,7 @@ func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore dataservices.
|
||||
|
||||
err := snapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("endpoint", endpoint.Name).
|
||||
Str("URL", endpoint.URL).
|
||||
Err(err).
|
||||
Msg("environment snapshot error")
|
||||
logrus.Printf("http error: environment snapshot error (environment=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
||||
}
|
||||
|
||||
return dataStore.Endpoint().Create(endpoint)
|
||||
@@ -492,10 +486,7 @@ func createUnsecuredEndpoint(endpointURL string, dataStore dataservices.DataStor
|
||||
|
||||
err := snapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("endpoint", endpoint.Name).
|
||||
Str("URL", endpoint.URL).Err(err).
|
||||
Msg("environment snapshot error")
|
||||
logrus.Printf("http error: environment snapshot error (environment=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
||||
}
|
||||
|
||||
return dataStore.Endpoint().Create(endpoint)
|
||||
@@ -512,8 +503,7 @@ func initEndpoint(flags *portainer.CLIFlags, dataStore dataservices.DataStore, s
|
||||
}
|
||||
|
||||
if len(endpoints) > 0 {
|
||||
log.Info().Msg("instance already has defined environments, skipping the environment defined via CLI")
|
||||
|
||||
logrus.Println("Instance already has defined environments. Skipping the environment defined via CLI.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -527,9 +517,9 @@ func loadEncryptionSecretKey(keyfilename string) []byte {
|
||||
content, err := os.ReadFile(path.Join("/run/secrets", keyfilename))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
log.Info().Str("filename", keyfilename).Msg("encryption key file not present")
|
||||
logrus.Printf("Encryption key file `%s` not present", keyfilename)
|
||||
} else {
|
||||
log.Info().Err(err).Msg("error reading encryption key file")
|
||||
logrus.Printf("Error reading encryption key file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -546,71 +536,67 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
fileService := initFileService(*flags.Data)
|
||||
encryptionKey := loadEncryptionSecretKey(*flags.SecretKeyName)
|
||||
if encryptionKey == nil {
|
||||
log.Info().Msg("proceeding without encryption key")
|
||||
logrus.Println("Proceeding without encryption key")
|
||||
}
|
||||
|
||||
dataStore := initDataStore(flags, encryptionKey, fileService, shutdownCtx)
|
||||
|
||||
if err := dataStore.CheckCurrentEdition(); err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
instanceID, err := dataStore.Version().InstanceID()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed getting instance id")
|
||||
logrus.Fatalf("Failed getting instance id: %v", err)
|
||||
}
|
||||
|
||||
apiKeyService := initAPIKeyService(dataStore)
|
||||
|
||||
settings, err := dataStore.Settings().Settings()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
jwtService, err := initJWTService(settings.UserSessionTimeout, dataStore)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing JWT service")
|
||||
logrus.Fatalf("Failed initializing JWT service: %v", err)
|
||||
}
|
||||
|
||||
err = enableFeaturesFromFlags(dataStore, flags)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed enabling feature flag")
|
||||
logrus.Fatalf("Failed enabling feature flag: %v", err)
|
||||
}
|
||||
|
||||
ldapService := initLDAPService()
|
||||
|
||||
oauthService := initOAuthService()
|
||||
gitService := initGitService(shutdownCtx)
|
||||
gitService := initGitService()
|
||||
|
||||
openAMTService := openamt.NewService()
|
||||
|
||||
cryptoService := initCryptoService()
|
||||
|
||||
digitalSignatureService := initDigitalSignatureService()
|
||||
|
||||
sslService, err := initSSLService(*flags.AddrHTTPS, *flags.SSLCert, *flags.SSLKey, fileService, dataStore, shutdownTrigger)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
|
||||
sslSettings, err := sslService.GetSSLSettings()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to get SSL settings")
|
||||
logrus.Fatalf("Failed to get ssl settings: %s", err)
|
||||
}
|
||||
|
||||
err = initKeyPair(fileService, digitalSignatureService)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing key pair")
|
||||
logrus.Fatalf("Failed initializing key pair: %v", err)
|
||||
}
|
||||
|
||||
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx)
|
||||
|
||||
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
|
||||
kubernetesClientFactory, err := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, dataStore, instanceID, *flags.AddrHTTPS, settings.UserSessionTimeout)
|
||||
kubernetesClientFactory := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, instanceID, dataStore)
|
||||
|
||||
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing snapshot service")
|
||||
logrus.Fatalf("Failed initializing snapshot service: %v", err)
|
||||
}
|
||||
snapshotService.Start()
|
||||
|
||||
@@ -621,7 +607,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService(*flags.BaseURL, *flags.AddrHTTPS, sslSettings.CertPath)
|
||||
|
||||
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService)
|
||||
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
|
||||
|
||||
reverseTunnelService.ProxyManager = proxyManager
|
||||
|
||||
@@ -631,46 +617,37 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
|
||||
swarmStackManager, err := initSwarmStackManager(*flags.Assets, dockerConfigPath, digitalSignatureService, fileService, reverseTunnelService, dataStore)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing swarm stack manager")
|
||||
logrus.Fatalf("Failed initializing swarm stack manager: %v", err)
|
||||
}
|
||||
|
||||
kubernetesDeployer := initKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, digitalSignatureService, proxyManager, *flags.Assets)
|
||||
|
||||
helmPackageManager, err := initHelmPackageManager(*flags.Assets)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing helm package manager")
|
||||
logrus.Fatalf("Failed initializing helm package manager: %v", err)
|
||||
}
|
||||
|
||||
err = edge.LoadEdgeJobs(dataStore, reverseTunnelService)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed loading edge jobs from database")
|
||||
logrus.Fatalf("Failed loading edge jobs from database: %v", err)
|
||||
}
|
||||
|
||||
applicationStatus := initStatus(instanceID)
|
||||
|
||||
demoService := demo.NewService()
|
||||
if *flags.DemoEnvironment {
|
||||
err := demoService.Init(dataStore, cryptoService)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing demo environment")
|
||||
}
|
||||
}
|
||||
|
||||
err = initEndpoint(flags, dataStore, snapshotService)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing environment")
|
||||
logrus.Fatalf("Failed initializing environment: %v", err)
|
||||
}
|
||||
|
||||
adminPasswordHash := ""
|
||||
if *flags.AdminPasswordFile != "" {
|
||||
content, err := fileService.GetFileContent(*flags.AdminPasswordFile, "")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed getting admin password file")
|
||||
logrus.Fatalf("Failed getting admin password file: %v", err)
|
||||
}
|
||||
|
||||
adminPasswordHash, err = cryptoService.Hash(strings.TrimSuffix(string(content), "\n"))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed hashing admin password")
|
||||
logrus.Fatalf("Failed hashing admin password: %v", err)
|
||||
}
|
||||
} else if *flags.AdminPassword != "" {
|
||||
adminPasswordHash = *flags.AdminPassword
|
||||
@@ -679,57 +656,39 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
if adminPasswordHash != "" {
|
||||
users, err := dataStore.User().UsersByRole(portainer.AdministratorRole)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed getting admin user")
|
||||
logrus.Fatalf("Failed getting admin user: %v", err)
|
||||
}
|
||||
|
||||
if len(users) == 0 {
|
||||
log.Info().Msg("created admin user with the given password.")
|
||||
logrus.Println("Created admin user with the given password.")
|
||||
user := &portainer.User{
|
||||
Username: "admin",
|
||||
Role: portainer.AdministratorRole,
|
||||
Password: adminPasswordHash,
|
||||
}
|
||||
|
||||
err := dataStore.User().Create(user)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating admin user")
|
||||
logrus.Fatalf("Failed creating admin user: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Info().Msg("instance already has an administrator user defined, skipping admin password related flags.")
|
||||
logrus.Println("Instance already has an administrator user defined. Skipping admin password related flags.")
|
||||
}
|
||||
}
|
||||
|
||||
err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotService)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed starting tunnel server")
|
||||
logrus.Fatalf("Failed starting tunnel server: %v", err)
|
||||
}
|
||||
|
||||
sslDBSettings, err := dataStore.SSLSettings().Settings()
|
||||
if err != nil {
|
||||
log.Fatal().Msg("failed to fetch SSL settings from DB")
|
||||
logrus.Fatalf("Failed to fetch ssl settings from DB")
|
||||
}
|
||||
|
||||
scheduler := scheduler.NewScheduler(shutdownCtx)
|
||||
stackDeployer := stacks.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer)
|
||||
stacks.StartStackSchedules(scheduler, stackDeployer, dataStore, gitService)
|
||||
|
||||
// FIXME: In 2.16 we changed the way ingress controller permissions are
|
||||
// stored. Instead of being stored as annotation on an ingress rule, we keep
|
||||
// them in our database. However, in order to run the migration we need an
|
||||
// admin kube client to run lookup the old ingress rules and compare them
|
||||
// with the current existing ingress classes.
|
||||
//
|
||||
// Unfortunately, our migrations run as part of the database initialization
|
||||
// and our kubeclients require an initialized database. So it is not
|
||||
// possible to do this migration as part of our normal flow. We DO have a
|
||||
// migration which toggles a boolean in kubernetes configuration that
|
||||
// indicated that this "post init" migration should be run. If/when this is
|
||||
// resolved we can remove this function.
|
||||
err = kubernetesClientFactory.PostInitMigrateIngresses()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failure during creation of new database")
|
||||
}
|
||||
|
||||
return &http.Server{
|
||||
AuthorizationService: authorizationService,
|
||||
ReverseTunnelService: reverseTunnelService,
|
||||
@@ -763,32 +722,18 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
ShutdownCtx: shutdownCtx,
|
||||
ShutdownTrigger: shutdownTrigger,
|
||||
StackDeployer: stackDeployer,
|
||||
DemoService: demoService,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
configureLogger()
|
||||
setLoggingMode("PRETTY")
|
||||
|
||||
flags := initCLI()
|
||||
|
||||
setLoggingLevel(*flags.LogLevel)
|
||||
setLoggingMode(*flags.LogMode)
|
||||
configureLogger()
|
||||
|
||||
for {
|
||||
server := buildServer(flags)
|
||||
log.Info().
|
||||
Str("version", portainer.APIVersion).
|
||||
Str("build_number", build.BuildNumber).
|
||||
Str("image_tag", build.ImageTag).
|
||||
Str("nodejs_version", build.NodejsVersion).
|
||||
Str("yarn_version", build.YarnVersion).
|
||||
Str("webpack_version", build.WebpackVersion).
|
||||
Str("go_version", build.GoVersion).
|
||||
Msg("starting Portainer")
|
||||
|
||||
logrus.Printf("[INFO] [cmd,main] Starting Portainer version %s\n", portainer.APIVersion)
|
||||
err := server.Start()
|
||||
log.Info().Err(err).Msg("HTTP server exited")
|
||||
logrus.Printf("[INFO] [cmd,main] Http server exited: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func (m mockKingpinSetting) SetValue(value kingpin.Value) {
|
||||
func Test_enableFeaturesFromFlags(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
tests := []struct {
|
||||
@@ -76,7 +76,7 @@ func Test_optionalFeature(t *testing.T) {
|
||||
|
||||
is := assert.New(t)
|
||||
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
// Enable the test feature
|
||||
|
||||
@@ -7,11 +7,13 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutils.TempDir("", "encrypt")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var (
|
||||
originFilePath = filepath.Join(tmpdir, "origin")
|
||||
@@ -50,7 +52,8 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutils.TempDir("", "encrypt")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var (
|
||||
originFilePath = filepath.Join(tmpdir, "origin")
|
||||
@@ -89,7 +92,8 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutils.TempDir("", "encrypt")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
var (
|
||||
originFilePath = filepath.Join(tmpdir, "origin")
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -121,7 +120,7 @@ func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
|
||||
// Open opens and initializes the BoltDB database.
|
||||
func (connection *DbConnection) Open() error {
|
||||
|
||||
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
||||
logrus.Infof("Loading PortainerDB: %s", connection.GetDatabaseFileName())
|
||||
|
||||
// Now we open the db
|
||||
databasePath := connection.GetDatabaseFilePath()
|
||||
@@ -349,7 +348,6 @@ func (connection *DbConnection) CreateObjectWithSetSequence(bucketName string, i
|
||||
func (connection *DbConnection) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
bucket := tx.Bucket([]byte(bucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
err := connection.UnmarshalObject(v, obj)
|
||||
@@ -364,7 +362,6 @@ func (connection *DbConnection) GetAll(bucketName string, obj interface{}, appen
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -414,7 +411,7 @@ func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error
|
||||
for bucketName, v := range s {
|
||||
id, ok := v.(float64) // JSON ints are unmarshalled to interface as float64. See: https://pkg.go.dev/encoding/json#Decoder.Decode
|
||||
if !ok {
|
||||
log.Error().Str("bucket", bucketName).Msg("failed to restore metadata to bucket, skipped")
|
||||
logrus.Errorf("Failed to restore metadata to bucket %s, skipped", bucketName)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -423,7 +420,6 @@ func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bucket.SetSequence(uint64(id))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
@@ -28,11 +28,11 @@ func backupMetadata(connection *bolt.DB) (map[string]interface{}, error) {
|
||||
|
||||
// ExportJSON creates a JSON representation from a DbConnection. You can include
|
||||
// the database's metadata or ignore it. Ensure the database is closed before
|
||||
// using this function.
|
||||
// using this function
|
||||
// inspired by github.com/konoui/boltdb-exporter (which has no license)
|
||||
// but very much simplified, based on how we use boltdb
|
||||
func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, error) {
|
||||
log.Debug().Str("databasePath", databasePath).Msg("exportJson")
|
||||
logrus.WithField("databasePath", databasePath).Infof("exportJson")
|
||||
|
||||
connection, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true})
|
||||
if err != nil {
|
||||
@@ -44,9 +44,8 @@ func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, e
|
||||
if metadata {
|
||||
meta, err := backupMetadata(connection)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed exporting metadata")
|
||||
logrus.WithError(err).Errorf("Failed exporting metadata: %v", err)
|
||||
}
|
||||
|
||||
backup["__metadata"] = meta
|
||||
}
|
||||
|
||||
@@ -60,31 +59,22 @@ func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, e
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var obj interface{}
|
||||
err := c.UnmarshalObject(v, &obj)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("bucket", bucketName).
|
||||
Str("object", string(v)).
|
||||
Err(err).
|
||||
Msg("failed to unmarshal")
|
||||
|
||||
logrus.WithError(err).Errorf("Failed to unmarshal (bucket %s): %v", bucketName, string(v))
|
||||
obj = v
|
||||
}
|
||||
|
||||
if bucketName == "version" {
|
||||
version[string(k)] = string(v)
|
||||
} else {
|
||||
list = append(list, obj)
|
||||
}
|
||||
}
|
||||
|
||||
if bucketName == "version" {
|
||||
backup[bucketName] = version
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
if bucketName == "ssl" ||
|
||||
bucketName == "settings" ||
|
||||
@@ -101,10 +91,8 @@ func (c *DbConnection) ExportJson(databasePath string, metadata bool) ([]byte, e
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return []byte("{}"), err
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"InternalAuthSettings": {"RequiredPasswordLength": 12}"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
|
||||
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
|
||||
passphrase = "my secret key"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package models
|
||||
|
||||
type (
|
||||
K8sConfigMapOrSecret struct {
|
||||
UID string `json:"UID"`
|
||||
Name string `json:"Name"`
|
||||
Namespace string `json:"Namespace"`
|
||||
CreationDate string `json:"CreationDate"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
Data map[string]string `json:"Data"`
|
||||
Applications []string `json:"Applications"`
|
||||
IsSecret bool `json:"IsSecret"`
|
||||
|
||||
// SecretType will be an empty string for config maps.
|
||||
SecretType string `json:"SecretType"`
|
||||
}
|
||||
)
|
||||
@@ -1,75 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
K8sIngressController struct {
|
||||
Name string `json:"Name"`
|
||||
ClassName string `json:"ClassName"`
|
||||
Type string `json:"Type"`
|
||||
Availability bool `json:"Availability"`
|
||||
New bool `json:"New"`
|
||||
Used bool `json:"Used"`
|
||||
}
|
||||
|
||||
K8sIngressControllers []K8sIngressController
|
||||
|
||||
K8sIngressInfo struct {
|
||||
Name string `json:"Name"`
|
||||
UID string `json:"UID"`
|
||||
Type string `json:"Type"`
|
||||
Namespace string `json:"Namespace"`
|
||||
ClassName string `json:"ClassName"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
Hosts []string `json:"Hosts"`
|
||||
Paths []K8sIngressPath `json:"Paths"`
|
||||
TLS []K8sIngressTLS `json:"TLS"`
|
||||
}
|
||||
|
||||
K8sIngressTLS struct {
|
||||
Hosts []string `json:"Hosts"`
|
||||
SecretName string `json:"SecretName"`
|
||||
}
|
||||
|
||||
K8sIngressPath struct {
|
||||
IngressName string `json:"IngressName"`
|
||||
Host string `json:"Host"`
|
||||
ServiceName string `json:"ServiceName"`
|
||||
Port int `json:"Port"`
|
||||
Path string `json:"Path"`
|
||||
PathType string `json:"PathType"`
|
||||
}
|
||||
|
||||
// K8sIngressDeleteRequests is a mapping of namespace names to a slice of
|
||||
// ingress names.
|
||||
K8sIngressDeleteRequests map[string][]string
|
||||
)
|
||||
|
||||
func (r K8sIngressControllers) Validate(request *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r K8sIngressInfo) Validate(request *http.Request) error {
|
||||
if r.Name == "" {
|
||||
return errors.New("missing ingress name from the request payload")
|
||||
}
|
||||
if r.Namespace == "" {
|
||||
return errors.New("missing ingress Namespace from the request payload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r K8sIngressDeleteRequests) Validate(request *http.Request) error {
|
||||
if len(r) == 0 {
|
||||
return errors.New("missing deletion request list in payload")
|
||||
}
|
||||
for ns := range r {
|
||||
if len(ns) == 0 {
|
||||
return errors.New("deletion given with empty namespace")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package models
|
||||
|
||||
import "net/http"
|
||||
|
||||
type K8sNamespaceDetails struct {
|
||||
Name string `json:"Name"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
}
|
||||
|
||||
func (r *K8sNamespaceDetails) Validate(request *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type (
|
||||
K8sServiceInfo struct {
|
||||
Name string `json:"Name"`
|
||||
UID string `json:"UID"`
|
||||
Type string `json:"Type"`
|
||||
Namespace string `json:"Namespace"`
|
||||
Annotations map[string]string `json:"Annotations"`
|
||||
CreationTimestamp string `json:"CreationTimestamp"`
|
||||
Labels map[string]string `json:"Labels"`
|
||||
AllocateLoadBalancerNodePorts *bool `json:"AllocateLoadBalancerNodePorts,omitempty"`
|
||||
Ports []K8sServicePort `json:"Ports"`
|
||||
Selector map[string]string `json:"Selector"`
|
||||
IngressStatus []K8sServiceIngress `json:"IngressStatus"`
|
||||
}
|
||||
|
||||
K8sServicePort struct {
|
||||
Name string `json:"Name"`
|
||||
NodePort int `json:"NodePort"`
|
||||
Port int `json:"Port"`
|
||||
Protocol string `json:"Protocol"`
|
||||
TargetPort int `json:"TargetPort"`
|
||||
}
|
||||
|
||||
K8sServiceIngress struct {
|
||||
IP string `json:"IP"`
|
||||
Host string `json:"Host"`
|
||||
}
|
||||
|
||||
// K8sServiceDeleteRequests is a mapping of namespace names to a slice of
|
||||
// service names.
|
||||
K8sServiceDeleteRequests map[string][]string
|
||||
)
|
||||
|
||||
func (s *K8sServiceInfo) Validate(request *http.Request) error {
|
||||
if s.Name == "" {
|
||||
return errors.New("missing service name from the request payload")
|
||||
}
|
||||
if s.Namespace == "" {
|
||||
return errors.New("missing service namespace from the request payload")
|
||||
}
|
||||
if s.Ports == nil {
|
||||
return errors.New("missing service ports from the request payload")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r K8sServiceDeleteRequests) Validate(request *http.Request) error {
|
||||
if len(r) == 0 {
|
||||
return errors.New("missing deletion request list in payload")
|
||||
}
|
||||
for ns := range r {
|
||||
if len(ns) == 0 {
|
||||
return errors.New("deletion given with empty namespace")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6,8 +6,7 @@ import (
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -42,14 +41,12 @@ func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
record, ok := obj.(*portainer.APIKey)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to APIKey object")
|
||||
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
|
||||
if record.UserID == userID {
|
||||
result = append(result, *record)
|
||||
}
|
||||
|
||||
return &portainer.APIKey{}, nil
|
||||
})
|
||||
|
||||
@@ -67,21 +64,18 @@ func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, err
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
key, ok := obj.(*portainer.APIKey)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to APIKey object")
|
||||
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
if bytes.Equal(key.Digest, digest) {
|
||||
k = key
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.APIKey{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return k, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,11 +44,10 @@ func (service *Service) CustomTemplates() ([]portainer.CustomTemplate, error) {
|
||||
//var tag portainer.Tag
|
||||
customTemplate, ok := obj.(*portainer.CustomTemplate)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to CustomTemplate object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to CustomTemplate object")
|
||||
return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj)
|
||||
}
|
||||
customTemplates = append(customTemplates, *customTemplate)
|
||||
|
||||
return &portainer.CustomTemplate{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,11 +43,10 @@ func (service *Service) EdgeGroups() ([]portainer.EdgeGroup, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
group, ok := obj.(*portainer.EdgeGroup)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeGroup object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeGroup object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj)
|
||||
}
|
||||
groups = append(groups, *group)
|
||||
|
||||
return &portainer.EdgeGroup{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -42,14 +41,13 @@ func (service *Service) EdgeJobs() ([]portainer.EdgeJob, error) {
|
||||
BucketName,
|
||||
&portainer.EdgeJob{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
//var tag portainer.Tag
|
||||
job, ok := obj.(*portainer.EdgeJob)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeJob object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeJob object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeJob object: %s", obj)
|
||||
}
|
||||
|
||||
edgeJobs = append(edgeJobs, *job)
|
||||
|
||||
return &portainer.EdgeJob{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -45,12 +44,10 @@ func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) {
|
||||
//var tag portainer.Tag
|
||||
stack, ok := obj.(*portainer.EdgeStack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeStack object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeStack object")
|
||||
return nil, fmt.Errorf("Failed to convert to EdgeStack object: %s", obj)
|
||||
}
|
||||
|
||||
stacks = append(stacks, *stack)
|
||||
|
||||
return &portainer.EdgeStack{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
package edgeupdateschedule
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/edgetypes"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "edge_update_schedule"
|
||||
)
|
||||
|
||||
// Service represents a service for managing Edge Update Schedule data.
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
|
||||
mu sync.Mutex
|
||||
idxActiveSchedules map[portainer.EndpointID]*edgetypes.EndpointUpdateScheduleRelation
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
service := &Service{
|
||||
connection: connection,
|
||||
}
|
||||
|
||||
service.idxActiveSchedules = map[portainer.EndpointID]*edgetypes.EndpointUpdateScheduleRelation{}
|
||||
|
||||
schedules, err := service.List()
|
||||
if err != nil {
|
||||
return nil, errors.WithMessage(err, "Unable to list schedules")
|
||||
}
|
||||
|
||||
for _, schedule := range schedules {
|
||||
service.setRelation(&schedule)
|
||||
}
|
||||
|
||||
return service, nil
|
||||
}
|
||||
|
||||
func (service *Service) ActiveSchedule(environmentID portainer.EndpointID) *edgetypes.EndpointUpdateScheduleRelation {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
return service.idxActiveSchedules[environmentID]
|
||||
}
|
||||
|
||||
func (service *Service) ActiveSchedules(environmentsIDs []portainer.EndpointID) []edgetypes.EndpointUpdateScheduleRelation {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
schedules := []edgetypes.EndpointUpdateScheduleRelation{}
|
||||
|
||||
for _, environmentID := range environmentsIDs {
|
||||
if s, ok := service.idxActiveSchedules[environmentID]; ok {
|
||||
schedules = append(schedules, *s)
|
||||
}
|
||||
}
|
||||
|
||||
return schedules
|
||||
}
|
||||
|
||||
// List return an array containing all the items in the bucket.
|
||||
func (service *Service) List() ([]edgetypes.UpdateSchedule, error) {
|
||||
var list = make([]edgetypes.UpdateSchedule, 0)
|
||||
|
||||
err := service.connection.GetAll(
|
||||
BucketName,
|
||||
&edgetypes.UpdateSchedule{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
item, ok := obj.(*edgetypes.UpdateSchedule)
|
||||
if !ok {
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeUpdateSchedule object")
|
||||
return nil, fmt.Errorf("failed to convert to EdgeUpdateSchedule object: %s", obj)
|
||||
}
|
||||
list = append(list, *item)
|
||||
return &edgetypes.UpdateSchedule{}, nil
|
||||
})
|
||||
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Item returns a item by ID.
|
||||
func (service *Service) Item(ID edgetypes.UpdateScheduleID) (*edgetypes.UpdateSchedule, error) {
|
||||
var item edgetypes.UpdateSchedule
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &item)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new object and saves it.
|
||||
func (service *Service) Create(item *edgetypes.UpdateSchedule) error {
|
||||
err := service.connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
item.ID = edgetypes.UpdateScheduleID(id)
|
||||
return int(item.ID), item
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return service.setRelation(item)
|
||||
}
|
||||
|
||||
// Update updates an item.
|
||||
func (service *Service) Update(id edgetypes.UpdateScheduleID, item *edgetypes.UpdateSchedule) error {
|
||||
identifier := service.connection.ConvertToKey(int(id))
|
||||
err := service.connection.UpdateObject(BucketName, identifier, item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.cleanRelation(id)
|
||||
|
||||
return service.setRelation(item)
|
||||
}
|
||||
|
||||
// Delete deletes an item.
|
||||
func (service *Service) Delete(id edgetypes.UpdateScheduleID) error {
|
||||
|
||||
service.cleanRelation(id)
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(id))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
}
|
||||
|
||||
func (service *Service) cleanRelation(id edgetypes.UpdateScheduleID) {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
for _, schedule := range service.idxActiveSchedules {
|
||||
if schedule != nil && schedule.ScheduleID == id {
|
||||
delete(service.idxActiveSchedules, schedule.EnvironmentID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) setRelation(schedule *edgetypes.UpdateSchedule) error {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
for environmentID, environmentStatus := range schedule.Status {
|
||||
if environmentStatus.Status != edgetypes.UpdateScheduleStatusPending {
|
||||
continue
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if service.idxActiveSchedules[environmentID] != nil && service.idxActiveSchedules[environmentID].ScheduleID != schedule.ID {
|
||||
return errors.New("Multiple schedules are pending for the same environment")
|
||||
}
|
||||
|
||||
service.idxActiveSchedules[environmentID] = &edgetypes.EndpointUpdateScheduleRelation{
|
||||
EnvironmentID: environmentID,
|
||||
ScheduleID: schedule.ID,
|
||||
TargetVersion: environmentStatus.TargetVersion,
|
||||
Status: environmentStatus.Status,
|
||||
Error: environmentStatus.Error,
|
||||
Type: schedule.Type,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -69,12 +69,10 @@ func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
endpoint, ok := obj.(*portainer.Endpoint)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Endpoint object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Endpoint object")
|
||||
return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj)
|
||||
}
|
||||
|
||||
endpoints = append(endpoints, *endpoint)
|
||||
|
||||
return &portainer.Endpoint{}, nil
|
||||
})
|
||||
|
||||
@@ -86,6 +84,15 @@ func (service *Service) Create(endpoint *portainer.Endpoint) error {
|
||||
return service.connection.CreateObjectWithSetSequence(BucketName, int(endpoint.ID), endpoint)
|
||||
}
|
||||
|
||||
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
|
||||
func (service *Service) CreateWithCallback(endpoint *portainer.Endpoint, fn func(id uint64) (int, interface{})) error {
|
||||
if endpoint.ID > 0 {
|
||||
return errors.New("the endpoint must not have an ID")
|
||||
}
|
||||
|
||||
return service.connection.CreateObject(BucketName, fn)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -67,14 +66,13 @@ func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
|
||||
BucketName,
|
||||
&portainer.EndpointGroup{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
//var tag portainer.Tag
|
||||
endpointGroup, ok := obj.(*portainer.EndpointGroup)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointGroup object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EndpointGroup object")
|
||||
return nil, fmt.Errorf("Failed to convert to EndpointGroup object: %s", obj)
|
||||
}
|
||||
|
||||
endpointGroups = append(endpointGroups, *endpointGroup)
|
||||
|
||||
return &portainer.EndpointGroup{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +33,7 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EndpointRelations returns an array of all EndpointRelations
|
||||
//EndpointRelations returns an array of all EndpointRelations
|
||||
func (service *Service) EndpointRelations() ([]portainer.EndpointRelation, error) {
|
||||
var all = make([]portainer.EndpointRelation, 0)
|
||||
|
||||
@@ -44,12 +43,10 @@ func (service *Service) EndpointRelations() ([]portainer.EndpointRelation, error
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
r, ok := obj.(*portainer.EndpointRelation)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EndpointRelation object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to EndpointRelation object")
|
||||
return nil, fmt.Errorf("Failed to convert to EndpointRelation object: %s", obj)
|
||||
}
|
||||
|
||||
all = append(all, *r)
|
||||
|
||||
return &portainer.EndpointRelation{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,12 +56,10 @@ func (service *Service) Extensions() ([]portainer.Extension, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
extension, ok := obj.(*portainer.Extension)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Extension object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Extension object")
|
||||
return nil, fmt.Errorf("Failed to convert to Extension object: %s", obj)
|
||||
}
|
||||
|
||||
extensions = append(extensions, *extension)
|
||||
|
||||
return &portainer.Extension{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,9 +43,8 @@ func (service *Service) FDOProfiles() ([]portainer.FDOProfile, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
fdoProfile, ok := obj.(*portainer.FDOProfile)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to FDOProfile object")
|
||||
|
||||
return nil, fmt.Errorf("Failed to convert to FDOProfile object: %s", obj)
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to FDOProfile object")
|
||||
return nil, fmt.Errorf("failed to convert to FDOProfile object: %s", obj)
|
||||
}
|
||||
fdoProfiles = append(fdoProfiles, *fdoProfile)
|
||||
return &portainer.FDOProfile{}, nil
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +33,7 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HelmUserRepository returns an array of all HelmUserRepository
|
||||
//HelmUserRepository returns an array of all HelmUserRepository
|
||||
func (service *Service) HelmUserRepositories() ([]portainer.HelmUserRepository, error) {
|
||||
var repos = make([]portainer.HelmUserRepository, 0)
|
||||
|
||||
@@ -44,12 +43,10 @@ func (service *Service) HelmUserRepositories() ([]portainer.HelmUserRepository,
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
r, ok := obj.(*portainer.HelmUserRepository)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to HelmUserRepository object")
|
||||
return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
}
|
||||
|
||||
repos = append(repos, *r)
|
||||
|
||||
return &portainer.HelmUserRepository{}, nil
|
||||
})
|
||||
|
||||
@@ -66,14 +63,12 @@ func (service *Service) HelmUserRepositoryByUserID(userID portainer.UserID) ([]p
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
record, ok := obj.(*portainer.HelmUserRepository)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to HelmUserRepository object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to HelmUserRepository object")
|
||||
return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
}
|
||||
|
||||
if record.UserID == userID {
|
||||
result = append(result, *record)
|
||||
}
|
||||
|
||||
return &portainer.HelmUserRepository{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/edgetypes"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
@@ -29,7 +28,6 @@ type (
|
||||
EdgeGroup() EdgeGroupService
|
||||
EdgeJob() EdgeJobService
|
||||
EdgeStack() EdgeStackService
|
||||
EdgeUpdateSchedule() EdgeUpdateScheduleService
|
||||
Endpoint() EndpointService
|
||||
EndpointGroup() EndpointGroupService
|
||||
EndpointRelation() EndpointRelationService
|
||||
@@ -40,7 +38,6 @@ type (
|
||||
Role() RoleService
|
||||
APIKeyRepository() APIKeyRepository
|
||||
Settings() SettingsService
|
||||
Snapshot() SnapshotService
|
||||
SSLSettings() SSLSettingsService
|
||||
Stack() StackService
|
||||
Tag() TagService
|
||||
@@ -84,17 +81,6 @@ type (
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
EdgeUpdateScheduleService interface {
|
||||
ActiveSchedule(environmentID portainer.EndpointID) *edgetypes.EndpointUpdateScheduleRelation
|
||||
ActiveSchedules(environmentIDs []portainer.EndpointID) []edgetypes.EndpointUpdateScheduleRelation
|
||||
List() ([]edgetypes.UpdateSchedule, error)
|
||||
Item(ID edgetypes.UpdateScheduleID) (*edgetypes.UpdateSchedule, error)
|
||||
Create(edgeUpdateSchedule *edgetypes.UpdateSchedule) error
|
||||
Update(ID edgetypes.UpdateScheduleID, edgeUpdateSchedule *edgetypes.UpdateSchedule) error
|
||||
Delete(ID edgetypes.UpdateScheduleID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EdgeStackService represents a service to manage Edge stacks
|
||||
EdgeStackService interface {
|
||||
EdgeStacks() ([]portainer.EdgeStack, error)
|
||||
@@ -111,6 +97,7 @@ type (
|
||||
Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error)
|
||||
Endpoints() ([]portainer.Endpoint, error)
|
||||
Create(endpoint *portainer.Endpoint) error
|
||||
CreateWithCallback(endpoint *portainer.Endpoint, fn func(uint64) (int, interface{})) error
|
||||
UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error
|
||||
DeleteEndpoint(ID portainer.EndpointID) error
|
||||
GetNextIdentifier() int
|
||||
@@ -215,15 +202,6 @@ type (
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
SnapshotService interface {
|
||||
Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error)
|
||||
Snapshots() ([]portainer.Snapshot, error)
|
||||
UpdateSnapshot(snapshot *portainer.Snapshot) error
|
||||
DeleteSnapshot(endpointID portainer.EndpointID) error
|
||||
Create(snapshot *portainer.Snapshot) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// SSLSettingsService represents a service for managing application settings
|
||||
SSLSettingsService interface {
|
||||
Settings() (*portainer.SSLSettings, error)
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,12 +56,10 @@ func (service *Service) Registries() ([]portainer.Registry, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
registry, ok := obj.(*portainer.Registry)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Registry object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Registry object")
|
||||
return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj)
|
||||
}
|
||||
|
||||
registries = append(registries, *registry)
|
||||
|
||||
return &portainer.Registry{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -59,7 +58,7 @@ func (service *Service) ResourceControlByResourceIDAndType(resourceID string, re
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
rc, ok := obj.(*portainer.ResourceControl)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to ResourceControl object")
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
|
||||
@@ -74,7 +73,6 @@ func (service *Service) ResourceControlByResourceIDAndType(resourceID string, re
|
||||
return nil, stop
|
||||
}
|
||||
}
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
@@ -94,12 +92,10 @@ func (service *Service) ResourceControls() ([]portainer.ResourceControl, error)
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
rc, ok := obj.(*portainer.ResourceControl)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to ResourceControl object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to ResourceControl object")
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
|
||||
rcs = append(rcs, *rc)
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,12 +56,10 @@ func (service *Service) Roles() ([]portainer.Role, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
set, ok := obj.(*portainer.Role)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Role object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Role object")
|
||||
return nil, fmt.Errorf("Failed to convert to Role object: %s", obj)
|
||||
}
|
||||
|
||||
sets = append(sets, *set)
|
||||
|
||||
return &portainer.Role{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -69,12 +68,10 @@ func (service *Service) Schedules() ([]portainer.Schedule, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
schedule, ok := obj.(*portainer.Schedule)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Schedule object")
|
||||
return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
}
|
||||
|
||||
schedules = append(schedules, *schedule)
|
||||
|
||||
return &portainer.Schedule{}, nil
|
||||
})
|
||||
|
||||
@@ -92,14 +89,12 @@ func (service *Service) SchedulesByJobType(jobType portainer.JobType) ([]portain
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
schedule, ok := obj.(*portainer.Schedule)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Schedule object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Schedule object")
|
||||
return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
}
|
||||
|
||||
if schedule.JobType == jobType {
|
||||
schedules = append(schedules, *schedule)
|
||||
}
|
||||
|
||||
return &portainer.Schedule{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
@@ -13,6 +15,30 @@ const (
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
cache *portainer.Settings
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func cloneSettings(src *portainer.Settings) *portainer.Settings {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := *src
|
||||
|
||||
if c.BlackListedLabels != nil {
|
||||
c.BlackListedLabels = make([]portainer.Pair, len(src.BlackListedLabels))
|
||||
copy(c.BlackListedLabels, src.BlackListedLabels)
|
||||
}
|
||||
|
||||
if src.FeatureFlagSettings != nil {
|
||||
c.FeatureFlagSettings = make(map[portainer.Feature]bool)
|
||||
for k, v := range src.FeatureFlagSettings {
|
||||
c.FeatureFlagSettings[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
@@ -33,6 +59,18 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
|
||||
// Settings retrieve the settings object.
|
||||
func (service *Service) Settings() (*portainer.Settings, error) {
|
||||
service.mu.RLock()
|
||||
if service.cache != nil {
|
||||
s := cloneSettings(service.cache)
|
||||
service.mu.RUnlock()
|
||||
|
||||
return s, nil
|
||||
}
|
||||
service.mu.RUnlock()
|
||||
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
var settings portainer.Settings
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(settingsKey), &settings)
|
||||
@@ -40,12 +78,24 @@ func (service *Service) Settings() (*portainer.Settings, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
service.cache = cloneSettings(&settings)
|
||||
|
||||
return &settings, nil
|
||||
}
|
||||
|
||||
// UpdateSettings persists a Settings object.
|
||||
func (service *Service) UpdateSettings(settings *portainer.Settings) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(settingsKey), settings)
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
err := service.connection.UpdateObject(BucketName, []byte(settingsKey), settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.cache = cloneSettings(settings)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) IsFeatureFlagEnabled(feature portainer.Feature) bool {
|
||||
@@ -61,3 +111,9 @@ func (service *Service) IsFeatureFlagEnabled(feature portainer.Feature) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (service *Service) InvalidateCache() {
|
||||
service.mu.Lock()
|
||||
service.cache = nil
|
||||
service.mu.Unlock()
|
||||
}
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package snapshot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
BucketName = "snapshots"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||
var snapshot portainer.Snapshot
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &snapshot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &snapshot, nil
|
||||
}
|
||||
|
||||
func (service *Service) Snapshots() ([]portainer.Snapshot, error) {
|
||||
var snapshots = make([]portainer.Snapshot, 0)
|
||||
|
||||
err := service.connection.GetAllWithJsoniter(
|
||||
BucketName,
|
||||
&portainer.Snapshot{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
snapshot, ok := obj.(*portainer.Snapshot)
|
||||
if !ok {
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Snapshot object")
|
||||
return nil, fmt.Errorf("failed to convert to Snapshot object: %s", obj)
|
||||
}
|
||||
snapshots = append(snapshots, *snapshot)
|
||||
return &portainer.Snapshot{}, nil
|
||||
})
|
||||
|
||||
return snapshots, err
|
||||
}
|
||||
|
||||
func (service *Service) UpdateSnapshot(snapshot *portainer.Snapshot) error {
|
||||
identifier := service.connection.ConvertToKey(int(snapshot.EndpointID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, snapshot)
|
||||
}
|
||||
|
||||
func (service *Service) DeleteSnapshot(endpointID portainer.EndpointID) error {
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
}
|
||||
|
||||
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -60,15 +60,13 @@ func (service *Service) StackByName(name string) (*portainer.Stack, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.Name == name {
|
||||
s = stack
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
@@ -91,14 +89,12 @@ func (service *Service) StacksByName(name string) ([]portainer.Stack, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.Name == name {
|
||||
stacks = append(stacks, stack)
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
@@ -115,12 +111,10 @@ func (service *Service) Stacks() ([]portainer.Stack, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
stacks = append(stacks, *stack)
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
@@ -162,15 +156,13 @@ func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) {
|
||||
s, ok = obj.(*portainer.Stack)
|
||||
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Stack object")
|
||||
return &portainer.Stack{}, nil
|
||||
}
|
||||
|
||||
if s.AutoUpdate != nil && strings.EqualFold(s.AutoUpdate.Webhook, id) {
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
@@ -194,14 +186,12 @@ func (service *Service) RefreshableStacks() ([]portainer.Stack, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.Stack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Stack object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Stack object")
|
||||
return nil, fmt.Errorf("Failed to convert to Stack object: %s", obj)
|
||||
}
|
||||
|
||||
if stack.AutoUpdate != nil && stack.AutoUpdate.Interval != "" {
|
||||
stacks = append(stacks, *stack)
|
||||
}
|
||||
|
||||
return &portainer.Stack{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestService_StackByWebhookID(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode. Normally takes ~1s to run.")
|
||||
}
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
b := stackBuilder{t: t, store: store}
|
||||
@@ -87,7 +87,7 @@ func Test_RefreshableStacks(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping test in short mode. Normally takes ~1s to run.")
|
||||
}
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
staticStack := portainer.Stack{ID: 1}
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,12 +43,10 @@ func (service *Service) Tags() ([]portainer.Tag, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
tag, ok := obj.(*portainer.Tag)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Tag object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Tag object")
|
||||
return nil, fmt.Errorf("Failed to convert to Tag object: %s", obj)
|
||||
}
|
||||
|
||||
tags = append(tags, *tag)
|
||||
|
||||
return &portainer.Tag{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -60,15 +60,13 @@ func (service *Service) TeamByName(name string) (*portainer.Team, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
team, ok := obj.(*portainer.Team)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Team object")
|
||||
return nil, fmt.Errorf("Failed to convert to Team object: %s", obj)
|
||||
}
|
||||
|
||||
if strings.EqualFold(team.Name, name) {
|
||||
t = team
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Team{}, nil
|
||||
})
|
||||
if err == stop {
|
||||
@@ -91,12 +89,10 @@ func (service *Service) Teams() ([]portainer.Team, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
team, ok := obj.(*portainer.Team)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Team object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Team object")
|
||||
return nil, fmt.Errorf("Failed to convert to Team object: %s", obj)
|
||||
}
|
||||
|
||||
teams = append(teams, *team)
|
||||
|
||||
return &portainer.Team{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
func Test_teamByName(t *testing.T) {
|
||||
t.Run("When store is empty should return ErrObjectNotFound", func(t *testing.T) {
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
_, err := store.Team().TeamByName("name")
|
||||
@@ -19,7 +19,7 @@ func Test_teamByName(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When there is no object with the same name should return ErrObjectNotFound", func(t *testing.T) {
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
teamBuilder := teamBuilder{
|
||||
@@ -35,7 +35,7 @@ func Test_teamByName(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("When there is an object with the same name should return the object", func(t *testing.T) {
|
||||
_, store, teardown := datastore.MustNewTestStore(t, true, true)
|
||||
_, store, teardown := datastore.MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
teamBuilder := teamBuilder{
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,12 +56,10 @@ func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
memberships = append(memberships, *membership)
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
@@ -79,14 +76,12 @@ func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]port
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
if membership.UserID == userID {
|
||||
memberships = append(memberships, *membership)
|
||||
}
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
@@ -103,14 +98,12 @@ func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]port
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
membership, ok := obj.(*portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to TeamMembership object")
|
||||
return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
}
|
||||
|
||||
if membership.TeamID == teamID {
|
||||
memberships = append(memberships, *membership)
|
||||
}
|
||||
|
||||
return &portainer.TeamMembership{}, nil
|
||||
})
|
||||
|
||||
@@ -147,15 +140,13 @@ func (service *Service) DeleteTeamMembershipByUserID(userID portainer.UserID) er
|
||||
func(obj interface{}) (id int, ok bool) {
|
||||
membership, ok := obj.(portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to TeamMembership object")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if membership.UserID == userID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
|
||||
return -1, false
|
||||
})
|
||||
}
|
||||
@@ -167,15 +158,13 @@ func (service *Service) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) er
|
||||
func(obj interface{}) (id int, ok bool) {
|
||||
membership, ok := obj.(portainer.TeamMembership)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to TeamMembership object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to TeamMembership object")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if membership.TeamID == teamID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
|
||||
return -1, false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -59,23 +59,18 @@ func (service *Service) UserByUsername(username string) (*portainer.User, error)
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to User object")
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
if strings.EqualFold(user.Username, username) {
|
||||
u = user
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return u, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
@@ -93,13 +88,10 @@ func (service *Service) Users() ([]portainer.User, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to User object")
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
users = append(users, *user)
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
@@ -116,15 +108,12 @@ func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User,
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
user, ok := obj.(*portainer.User)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to User object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to User object")
|
||||
return nil, fmt.Errorf("Failed to convert to User object: %s", obj)
|
||||
}
|
||||
|
||||
if user.Role == role {
|
||||
users = append(users, *user)
|
||||
}
|
||||
|
||||
return &portainer.User{}, nil
|
||||
})
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@ import (
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,7 +34,7 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Webhooks returns an array of all webhooks
|
||||
//Webhooks returns an array of all webhooks
|
||||
func (service *Service) Webhooks() ([]portainer.Webhook, error) {
|
||||
var webhooks = make([]portainer.Webhook, 0)
|
||||
|
||||
@@ -45,12 +44,10 @@ func (service *Service) Webhooks() ([]portainer.Webhook, error) {
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Webhook object")
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
webhooks = append(webhooks, *webhook)
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
@@ -80,23 +77,18 @@ func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, erro
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Webhook object")
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
if webhook.ResourceID == ID {
|
||||
w = webhook
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
@@ -114,23 +106,18 @@ func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error)
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
webhook, ok := obj.(*portainer.Webhook)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to Webhook object")
|
||||
|
||||
logrus.WithField("obj", obj).Errorf("Failed to convert to Webhook object")
|
||||
return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
}
|
||||
|
||||
if webhook.Token == token {
|
||||
w = webhook
|
||||
return nil, stop
|
||||
}
|
||||
|
||||
return &portainer.Webhook{}, nil
|
||||
})
|
||||
|
||||
if err == stop {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, errors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
plog "github.com/portainer/portainer/api/datastore/log"
|
||||
)
|
||||
|
||||
var backupDefaults = struct {
|
||||
@@ -17,6 +17,8 @@ var backupDefaults = struct {
|
||||
"common",
|
||||
}
|
||||
|
||||
var backupLog = plog.NewScopedLog("database, backup")
|
||||
|
||||
//
|
||||
// Backup Helpers
|
||||
//
|
||||
@@ -27,7 +29,7 @@ func (store *Store) createBackupFolders() {
|
||||
commonDir := store.commonBackupDir()
|
||||
if exists, _ := store.fileService.FileExists(commonDir); !exists {
|
||||
if err := os.MkdirAll(commonDir, 0700); err != nil {
|
||||
log.Error().Err(err).Msg("error while creating common backup folder")
|
||||
backupLog.Error("Error while creating common backup folder", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,13 +43,11 @@ func (store *Store) commonBackupDir() string {
|
||||
}
|
||||
|
||||
func (store *Store) copyDBFile(from string, to string) error {
|
||||
log.Info().Str("from", from).Str("to", to).Msg("copying DB file")
|
||||
|
||||
backupLog.Info(fmt.Sprintf("Copying db file from %s to %s", from, to))
|
||||
err := store.fileService.Copy(from, to, true)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed")
|
||||
backupLog.Error("Failed", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -99,32 +99,12 @@ func (store *Store) setupOptions(options *BackupOptions) *BackupOptions {
|
||||
|
||||
// BackupWithOptions backup current database with options
|
||||
func (store *Store) backupWithOptions(options *BackupOptions) (string, error) {
|
||||
log.Info().Msg("creating DB backup")
|
||||
|
||||
backupLog.Info("creating db backup")
|
||||
store.createBackupFolders()
|
||||
|
||||
options = store.setupOptions(options)
|
||||
dbPath := store.databasePath()
|
||||
|
||||
if err := store.Close(); err != nil {
|
||||
return options.BackupPath, fmt.Errorf(
|
||||
"error closing datastore before creating backup: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
if err := store.copyDBFile(dbPath, options.BackupPath); err != nil {
|
||||
return options.BackupPath, err
|
||||
}
|
||||
|
||||
if _, err := store.Open(); err != nil {
|
||||
return options.BackupPath, fmt.Errorf(
|
||||
"error opening datastore after creating backup: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return options.BackupPath, nil
|
||||
return options.BackupPath, store.copyDBFile(store.databasePath(), options.BackupPath)
|
||||
}
|
||||
|
||||
// RestoreWithOptions previously saved backup for the current Edition with options
|
||||
@@ -137,19 +117,17 @@ func (store *Store) restoreWithOptions(options *BackupOptions) error {
|
||||
// Check if backup file exist before restoring
|
||||
_, err := os.Stat(options.BackupPath)
|
||||
if os.IsNotExist(err) {
|
||||
log.Error().Str("path", options.BackupPath).Err(err).Msg("backup file to restore does not exist %s")
|
||||
|
||||
backupLog.Error(fmt.Sprintf("Backup file to restore does not exist %s", options.BackupPath), err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.Close()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error while closing store before restore")
|
||||
|
||||
backupLog.Error("Error while closing store before restore", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("restoring DB backup")
|
||||
backupLog.Info("Restoring db backup")
|
||||
err = store.copyDBFile(options.BackupPath, store.databasePath())
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -161,22 +139,20 @@ func (store *Store) restoreWithOptions(options *BackupOptions) error {
|
||||
|
||||
// RemoveWithOptions removes backup database based on supplied options
|
||||
func (store *Store) removeWithOptions(options *BackupOptions) error {
|
||||
log.Info().Msg("removing DB backup")
|
||||
backupLog.Info("Removing db backup")
|
||||
|
||||
options = store.setupOptions(options)
|
||||
_, err := os.Stat(options.BackupPath)
|
||||
|
||||
if os.IsNotExist(err) {
|
||||
log.Error().Str("path", options.BackupPath).Err(err).Msg("backup file to remove does not exist")
|
||||
|
||||
backupLog.Error(fmt.Sprintf("Backup file to remove does not exist %s", options.BackupPath), err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Str("path", options.BackupPath).Msg("removing DB file")
|
||||
backupLog.Info(fmt.Sprintf("Removing db file at %s", options.BackupPath))
|
||||
err = os.Remove(options.BackupPath)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed")
|
||||
|
||||
backupLog.Error("Failed", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestCreateBackupFolders(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
connection := store.GetConnection()
|
||||
@@ -27,7 +27,7 @@ func TestCreateBackupFolders(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStoreCreation(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
_, store, teardown := MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
if store == nil {
|
||||
@@ -40,7 +40,7 @@ func TestStoreCreation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBackup(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
_, store, teardown := MustNewTestStore(true, true)
|
||||
connection := store.GetConnection()
|
||||
defer teardown()
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestBackup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRemoveWithOptions(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
_, store, teardown := MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
t.Run("successfully removes file if existent", func(t *testing.T) {
|
||||
|
||||
@@ -9,8 +9,7 @@ import (
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
portainerErrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (store *Store) version() (int, error) {
|
||||
@@ -74,8 +73,7 @@ func (store *Store) Open() (newStore bool, err error) {
|
||||
}
|
||||
|
||||
if version > 0 {
|
||||
log.Debug().Int("version", version).Msg("opened existing store")
|
||||
|
||||
logrus.WithField("version", version).Infof("Opened existing store")
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -123,21 +121,19 @@ func (store *Store) encryptDB() error {
|
||||
|
||||
// The DB is not currently encrypted. First save the encrypted db filename
|
||||
oldFilename := store.connection.GetDatabaseFilePath()
|
||||
log.Info().Msg("encrypting database")
|
||||
logrus.Infof("Encrypting database")
|
||||
|
||||
// export file path for backup
|
||||
exportFilename := path.Join(store.databasePath() + "." + fmt.Sprintf("backup-%d.json", time.Now().Unix()))
|
||||
|
||||
log.Info().Str("filename", exportFilename).Msg("exporting database backup")
|
||||
|
||||
logrus.Infof("Exporting database backup to %s", exportFilename)
|
||||
err = store.Export(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Str("filename", exportFilename).Err(err).Msg("failed to export")
|
||||
|
||||
logrus.WithError(err).Debugf("Failed to export to %s", exportFilename)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("database backup exported")
|
||||
logrus.Infof("Database backup exported")
|
||||
|
||||
// Close existing un-encrypted db so that we can delete the file later
|
||||
store.connection.Close()
|
||||
@@ -156,23 +152,22 @@ func (store *Store) encryptDB() error {
|
||||
if err != nil {
|
||||
// Remove the new encrypted file that we failed to import
|
||||
os.Remove(store.connection.GetDatabaseFilePath())
|
||||
log.Fatal().Err(portainerErrors.ErrDBImportFailed).Msg("")
|
||||
logrus.Fatal(portainerErrors.ErrDBImportFailed.Error())
|
||||
}
|
||||
|
||||
err = os.Remove(oldFilename)
|
||||
if err != nil {
|
||||
log.Error().Msg("failed to remove the un-encrypted db file")
|
||||
logrus.Errorf("Failed to remove the un-encrypted db file")
|
||||
}
|
||||
|
||||
err = os.Remove(exportFilename)
|
||||
if err != nil {
|
||||
log.Error().Msg("failed to remove the json backup file")
|
||||
logrus.Errorf("Failed to remove the json backup file")
|
||||
}
|
||||
|
||||
// Close db connection
|
||||
store.connection.Close()
|
||||
|
||||
log.Info().Msg("database successfully encrypted")
|
||||
|
||||
logrus.Info("Database successfully encrypted")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ const (
|
||||
// TestStoreFull an eventually comprehensive set of tests for the Store.
|
||||
// The idea is what we write to the store, we should read back.
|
||||
func TestStoreFull(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
_, store, teardown := MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
testCases := map[string]func(t *testing.T){
|
||||
@@ -177,7 +177,7 @@ func (store *Store) CreateEndpoint(t *testing.T, name string, endpointType porta
|
||||
func (store *Store) CreateEndpointRelation(id portainer.EndpointID) {
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: id,
|
||||
EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
EdgeStacks: map[portainer.EdgeStackID]portainer.EdgeStackStatus{},
|
||||
}
|
||||
|
||||
store.EndpointRelation().Create(relation)
|
||||
|
||||
@@ -44,12 +44,9 @@ func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
settings, err := store.SettingsService.Settings()
|
||||
if store.IsErrObjectNotFound(err) {
|
||||
defaultSettings := &portainer.Settings{
|
||||
EnableTelemetry: false,
|
||||
EnableTelemetry: true,
|
||||
AuthenticationMethod: portainer.AuthenticationInternal,
|
||||
BlackListedLabels: make([]portainer.Pair, 0),
|
||||
InternalAuthSettings: portainer.InternalAuthSettings{
|
||||
RequiredPasswordLength: 12,
|
||||
},
|
||||
LDAPSettings: portainer.LDAPSettings{
|
||||
AnonymousMode: true,
|
||||
AutoCreateUsers: true,
|
||||
|
||||
51
api/datastore/log/log.go
Normal file
51
api/datastore/log/log.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
const (
|
||||
INFO = "INFO"
|
||||
ERROR = "ERROR"
|
||||
DEBUG = "DEBUG"
|
||||
FATAL = "FATAL"
|
||||
)
|
||||
|
||||
type ScopedLog struct {
|
||||
scope string
|
||||
}
|
||||
|
||||
func NewScopedLog(scope string) *ScopedLog {
|
||||
return &ScopedLog{scope: scope}
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) print(kind string, message string) {
|
||||
log.Printf("[%s] [%s] %s", kind, slog.scope, message)
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) Debug(message string) {
|
||||
slog.print(DEBUG, fmt.Sprintf("[message: %s]", message))
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) Debugf(message string, vars ...interface{}) {
|
||||
message = fmt.Sprintf(message, vars...)
|
||||
slog.print(DEBUG, fmt.Sprintf("[message: %s]", message))
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) Info(message string) {
|
||||
slog.print(INFO, fmt.Sprintf("[message: %s]", message))
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) Infof(message string, vars ...interface{}) {
|
||||
message = fmt.Sprintf(message, vars...)
|
||||
slog.print(INFO, fmt.Sprintf("[message: %s]", message))
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) Error(message string, err error) {
|
||||
slog.print(ERROR, fmt.Sprintf("[message: %s] [error: %s]", message, err))
|
||||
}
|
||||
|
||||
func (slog *ScopedLog) NotImplemented(method string) {
|
||||
log.Fatalf("[%s] [%s] [%s]", FATAL, slog.scope, fmt.Sprintf("%s is not yet implemented", method))
|
||||
}
|
||||
@@ -4,18 +4,21 @@ import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
plog "github.com/portainer/portainer/api/datastore/log"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
werrors "github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const beforePortainerVersionUpgradeBackup = "portainer.db.bak"
|
||||
|
||||
var migrateLog = plog.NewScopedLog("database, migrate")
|
||||
|
||||
func (store *Store) MigrateData() error {
|
||||
version, err := store.version()
|
||||
if err != nil {
|
||||
@@ -28,6 +31,8 @@ func (store *Store) MigrateData() error {
|
||||
return werrors.Wrap(err, "while backing up db before migration")
|
||||
}
|
||||
|
||||
store.SettingsService.InvalidateCache()
|
||||
|
||||
migratorParams := &migrator.MigratorParameters{
|
||||
DatabaseVersion: version,
|
||||
EndpointGroupService: store.EndpointGroupService,
|
||||
@@ -40,7 +45,6 @@ func (store *Store) MigrateData() error {
|
||||
RoleService: store.RoleService,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
SnapshotService: store.SnapshotService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
@@ -54,19 +58,20 @@ func (store *Store) MigrateData() error {
|
||||
// restore on error
|
||||
err = store.connectionMigrateData(migratorParams)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("while DB migration, restoring DB")
|
||||
|
||||
logrus.Errorf("While DB migration %v. Restoring DB", err)
|
||||
// Restore options
|
||||
options := BackupOptions{
|
||||
BackupPath: backupPath,
|
||||
}
|
||||
|
||||
err := store.restoreWithOptions(&options)
|
||||
if err != nil {
|
||||
log.Fatal().
|
||||
Str("database_file", store.databasePath()).
|
||||
Str("backup", options.BackupPath).Err(err).
|
||||
Msg("failed restoring the backup, Portainer database file needs to restored manually by replacing the database file with a recent backup")
|
||||
logrus.Fatalf(
|
||||
"Failed restoring the backup. portainer database file needs to restored manually by "+
|
||||
"replacing %s database file with recent backup %s. Error %v",
|
||||
store.databasePath(),
|
||||
options.BackupPath,
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,15 +113,10 @@ func (store *Store) connectionMigrateData(migratorParams *migrator.MigratorParam
|
||||
}
|
||||
|
||||
if migrator.Version() < portainer.DBVersion {
|
||||
log.Info().
|
||||
Int("migrator_version", migrator.Version()).
|
||||
Int("db_version", portainer.DBVersion).
|
||||
Msg("migrating database")
|
||||
|
||||
migrateLog.Info(fmt.Sprintf("Migrating database from version %v to %v.\n", migrator.Version(), portainer.DBVersion))
|
||||
err = store.FailSafeMigrate(migrator)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during database migration")
|
||||
|
||||
migrateLog.Error("An error occurred during database migration", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -126,19 +126,17 @@ func (store *Store) connectionMigrateData(migratorParams *migrator.MigratorParam
|
||||
|
||||
// backupVersion will backup the database or panic if any errors occur
|
||||
func (store *Store) backupVersion(migrator *migrator.Migrator) error {
|
||||
log.Info().Msg("backing up database prior to version upgrade")
|
||||
migrateLog.Info("Backing up database prior to version upgrade...")
|
||||
|
||||
options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
_, err := store.backupWithOptions(options)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during database backup")
|
||||
|
||||
migrateLog.Error("An error occurred during database backup", err)
|
||||
removalErr := store.removeWithOptions(options)
|
||||
if removalErr != nil {
|
||||
log.Error().Err(err).Msg("an error occurred during store removal prior to backup")
|
||||
migrateLog.Error("An error occurred during store removal prior to backup", err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,15 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// testVersion is a helper which tests current store version against wanted version
|
||||
@@ -35,9 +34,9 @@ func TestMigrateData(t *testing.T) {
|
||||
wantPath string
|
||||
}{
|
||||
{
|
||||
testName: "migrate version 24 to latest",
|
||||
testName: "migrate version 24 to 35",
|
||||
srcPath: "test_data/input_24.json",
|
||||
wantPath: "test_data/output_24_to_latest.json",
|
||||
wantPath: "test_data/output_35.json",
|
||||
},
|
||||
}
|
||||
for _, test := range snapshotTests {
|
||||
@@ -54,7 +53,7 @@ func TestMigrateData(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("MigrateData for New Store & Re-Open Check", func(t *testing.T) {
|
||||
newStore, store, teardown := MustNewTestStore(t, false, true)
|
||||
newStore, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
if !newStore {
|
||||
@@ -81,7 +80,7 @@ func TestMigrateData(t *testing.T) {
|
||||
{version: 21, expectedVersion: portainer.DBVersion},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
_, store, teardown := MustNewTestStore(t, true, true)
|
||||
_, store, teardown := MustNewTestStore(true, true)
|
||||
defer teardown()
|
||||
|
||||
// Setup data
|
||||
@@ -106,7 +105,7 @@ func TestMigrateData(t *testing.T) {
|
||||
}
|
||||
|
||||
t.Run("Error in MigrateData should restore backup before MigrateData", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
version := 17
|
||||
@@ -118,7 +117,7 @@ func TestMigrateData(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("MigrateData should create backup file upon update", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
store.VersionService.StoreDBVersion(0)
|
||||
|
||||
@@ -132,7 +131,7 @@ func TestMigrateData(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("MigrateData should fail to create backup if database file is set to updating", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
store.VersionService.StoreIsUpdating(true)
|
||||
@@ -147,7 +146,7 @@ func TestMigrateData(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("MigrateData should not create backup on startup if portainer version matches db", func(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
store.MigrateData()
|
||||
@@ -158,48 +157,48 @@ func TestMigrateData(t *testing.T) {
|
||||
t.Errorf("Backup file should not exist for dirty database; file=%s", options.BackupPath)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_getBackupRestoreOptions(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
wantDir := store.commonBackupDir()
|
||||
if !strings.HasSuffix(options.BackupDir, wantDir) {
|
||||
log.Fatal().Str("got", options.BackupDir).Str("want", wantDir).Msg("incorrect backup dir")
|
||||
log.Fatalf("incorrect backup dir; got=%s, want=%s", options.BackupDir, wantDir)
|
||||
}
|
||||
|
||||
wantFilename := "portainer.db.bak"
|
||||
if options.BackupFileName != wantFilename {
|
||||
log.Fatal().Str("got", options.BackupFileName).Str("want", wantFilename).Msg("incorrect backup file")
|
||||
log.Fatalf("incorrect backup file; got=%s, want=%s", options.BackupFileName, wantFilename)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
t.Run("Rollback should restore upgrade after backup", func(t *testing.T) {
|
||||
version := 21
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
store.VersionService.StoreDBVersion(version)
|
||||
|
||||
_, err := store.backupWithOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Change the current edition
|
||||
err = store.VersionService.StoreDBVersion(version + 10)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = store.Rollback(true)
|
||||
if err != nil {
|
||||
t.Logf("Rollback failed: %s", err)
|
||||
t.Fail()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -227,7 +226,7 @@ func migrateDBTestHelper(t *testing.T, srcPath, wantPath string) error {
|
||||
}
|
||||
|
||||
// Parse source json to db.
|
||||
_, store, teardown := MustNewTestStore(t, true, false)
|
||||
_, store, teardown := MustNewTestStore(true, false)
|
||||
defer teardown()
|
||||
err = importJSON(t, bytes.NewReader(srcJSON), store)
|
||||
if err != nil {
|
||||
|
||||
@@ -33,7 +33,7 @@ func setup(store *Store) error {
|
||||
}
|
||||
|
||||
func TestMigrateSettings(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
err := setup(store)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func TestMigrateStackEntryPoint(t *testing.T) {
|
||||
_, store, teardown := MustNewTestStore(t, false, true)
|
||||
_, store, teardown := MustNewTestStore(false, true)
|
||||
defer teardown()
|
||||
|
||||
stackService := store.Stack()
|
||||
|
||||
@@ -5,10 +5,8 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
werrors "github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
type migration struct {
|
||||
@@ -102,18 +100,6 @@ func (m *Migrator) Migrate() error {
|
||||
|
||||
// Portainer 2.13
|
||||
newMigration(40, m.migrateDBVersionToDB40),
|
||||
|
||||
// Portainer 2.14
|
||||
newMigration(50, m.migrateDBVersionToDB50),
|
||||
|
||||
// Portainer 2.15
|
||||
newMigration(60, m.migrateDBVersionToDB60),
|
||||
|
||||
// Portainer 2.16
|
||||
newMigration(70, m.migrateDBVersionToDB70),
|
||||
|
||||
// Portainer 2.16.1
|
||||
newMigration(71, m.migrateDBVersionToDB71),
|
||||
}
|
||||
|
||||
var lastDbVersion int
|
||||
@@ -122,7 +108,7 @@ func (m *Migrator) Migrate() error {
|
||||
|
||||
// Print the next line only when the version changes
|
||||
if migration.dbversion > lastDbVersion {
|
||||
log.Info().Int("to_version", migration.dbversion).Msg("migrating DB")
|
||||
migrateLog.Infof("Migrating DB to version %d", migration.dbversion)
|
||||
}
|
||||
|
||||
err := migration.migrate()
|
||||
@@ -133,14 +119,12 @@ func (m *Migrator) Migrate() error {
|
||||
lastDbVersion = migration.dbversion
|
||||
}
|
||||
|
||||
log.Info().Int("version", portainer.DBVersion).Msg("setting DB version")
|
||||
|
||||
migrateLog.Infof("Setting DB version to %d", portainer.DBVersion)
|
||||
err = m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||
if err != nil {
|
||||
return migrationError(err, "StoreDBVersion")
|
||||
}
|
||||
|
||||
log.Info().Int("version", portainer.DBVersion).Msg("updated DB version")
|
||||
migrateLog.Infof("Updated DB version to %d", portainer.DBVersion)
|
||||
|
||||
// reset DB updating status
|
||||
return m.versionService.StoreIsUpdating(false)
|
||||
|
||||
@@ -2,13 +2,10 @@ package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateUsersToDBVersion18() error {
|
||||
log.Info().Msg("updating users")
|
||||
|
||||
migrateLog.Info("- updating users")
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -43,8 +40,7 @@ func (m *Migrator) updateUsersToDBVersion18() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointsToDBVersion18() error {
|
||||
log.Info().Msg("updating endpoints")
|
||||
|
||||
migrateLog.Info("- updating endpoints")
|
||||
legacyEndpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -75,8 +71,7 @@ func (m *Migrator) updateEndpointsToDBVersion18() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
log.Info().Msg("updating endpoint groups")
|
||||
|
||||
migrateLog.Info("- updating endpoint groups")
|
||||
legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -107,8 +102,7 @@ func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateRegistriesToDBVersion18() error {
|
||||
log.Info().Msg("updating registries")
|
||||
|
||||
migrateLog.Info("- updating registries")
|
||||
legacyRegistries, err := m.registryService.Registries()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
func (m *Migrator) updateSettingsToDBVersion19() error {
|
||||
log.Info().Msg("updating settings")
|
||||
|
||||
migrateLog.Info("- updating settings")
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,15 +2,12 @@ package migrator
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const scheduleScriptExecutionJobType = 1
|
||||
|
||||
func (m *Migrator) updateUsersToDBVersion20() error {
|
||||
log.Info().Msg("updating user authentication")
|
||||
|
||||
migrateLog.Info("- updating user authentication")
|
||||
return m.authorizationService.UpdateUsersAuthorizations()
|
||||
}
|
||||
|
||||
@@ -26,8 +23,7 @@ func (m *Migrator) updateSettingsToDBVersion20() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateSchedulesToDBVersion20() error {
|
||||
log.Info().Msg("updating schedules")
|
||||
|
||||
migrateLog.Info("- updating schedules")
|
||||
legacySchedules, err := m.scheduleService.Schedules()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,13 +3,10 @@ package migrator
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
log.Info().Msg("updating resource controls")
|
||||
|
||||
migrateLog.Info("- updating resource controls")
|
||||
legacyResourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -28,8 +25,7 @@ func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
log.Info().Msg("updating users and roles")
|
||||
|
||||
migrateLog.Info("- updating users and roles")
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
log.Info().Msg("updating tags")
|
||||
|
||||
migrateLog.Info("- Updating tags")
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -26,8 +21,7 @@ func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
log.Info().Msg("updating endpoints and endpoint groups")
|
||||
|
||||
migrateLog.Info("- updating endpoints and endpoint groups")
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -60,7 +54,7 @@ func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: endpoint.ID,
|
||||
EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
EdgeStacks: map[portainer.EdgeStackID]portainer.EdgeStackStatus{},
|
||||
}
|
||||
|
||||
err = m.endpointRelationService.Create(relation)
|
||||
@@ -96,6 +90,5 @@ func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
func (m *Migrator) updateSettingsToDB24() error {
|
||||
log.Info().Msg("updating Settings")
|
||||
migrateLog.Info("- updating Settings")
|
||||
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
@@ -22,8 +18,7 @@ func (m *Migrator) updateSettingsToDB24() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateStacksToDB24() error {
|
||||
log.Info().Msg("updating stacks")
|
||||
|
||||
migrateLog.Info("- updating stacks")
|
||||
stacks, err := m.stackService.Stacks()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -2,12 +2,10 @@ package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateSettingsToDB25() error {
|
||||
log.Info().Msg("updating settings")
|
||||
migrateLog.Info("- updating settings")
|
||||
|
||||
legacySettings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
|
||||
@@ -2,13 +2,10 @@ package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateEndpointSettingsToDB25() error {
|
||||
log.Info().Msg("updating endpoint settings")
|
||||
|
||||
migrateLog.Info("- updating endpoint settings")
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,13 +4,10 @@ import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/internal/stackutils"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) updateStackResourceControlToDB27() error {
|
||||
log.Info().Msg("updating stack resource controls")
|
||||
|
||||
migrateLog.Info("- updating stack resource controls")
|
||||
resourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package migrator
|
||||
|
||||
import "github.com/rs/zerolog/log"
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB30() error {
|
||||
log.Info().Msg("updating legacy settings")
|
||||
migrateLog.Info("- updating legacy settings")
|
||||
if err := m.MigrateSettingsToDB30(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.MigrateSettingsToDB30()
|
||||
return nil
|
||||
}
|
||||
|
||||
// so setting to false and "", is what would happen without this code
|
||||
|
||||
@@ -2,14 +2,13 @@ package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
snapshotutils "github.com/portainer/portainer/api/internal/snapshot"
|
||||
|
||||
"github.com/docker/docker/api/types/volume"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB32() error {
|
||||
@@ -39,8 +38,7 @@ func (m *Migrator) migrateDBVersionToDB32() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateRegistriesToDB32() error {
|
||||
log.Info().Msg("updating registries")
|
||||
|
||||
migrateLog.Info("- updating registries")
|
||||
registries, err := m.registryService.Registries()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -83,8 +81,7 @@ func (m *Migrator) updateRegistriesToDB32() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateDockerhubToDB32() error {
|
||||
log.Info().Msg("updating dockerhub")
|
||||
|
||||
migrateLog.Info("- updating dockerhub")
|
||||
dockerhub, err := m.dockerhubService.DockerHub()
|
||||
if err == errors.ErrObjectNotFound {
|
||||
return nil
|
||||
@@ -173,8 +170,7 @@ func (m *Migrator) updateDockerhubToDB32() error {
|
||||
}
|
||||
|
||||
func (m *Migrator) updateVolumeResourceControlToDB32() error {
|
||||
log.Info().Msg("updating resource controls")
|
||||
|
||||
migrateLog.Info("- updating resource controls")
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed fetching environments: %w", err)
|
||||
@@ -202,7 +198,7 @@ func (m *Migrator) updateVolumeResourceControlToDB32() error {
|
||||
|
||||
totalSnapshots := len(endpoint.Snapshots)
|
||||
if totalSnapshots == 0 {
|
||||
log.Debug().Msg("no snapshot found")
|
||||
log.Println("[DEBUG] [volume migration] [message: no snapshot found]")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -210,46 +206,52 @@ func (m *Migrator) updateVolumeResourceControlToDB32() error {
|
||||
|
||||
endpointDockerID, err := snapshotutils.FetchDockerID(snapshot)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed fetching environment docker id")
|
||||
log.Printf("[WARN] [database,migrator,v31] [message: failed fetching environment docker id] [err: %s]", err)
|
||||
continue
|
||||
}
|
||||
|
||||
volumesData := snapshot.SnapshotRaw.Volumes
|
||||
if volumesData.Volumes == nil {
|
||||
log.Debug().Msg("no volume data found")
|
||||
continue
|
||||
if volumesData, done := snapshot.SnapshotRaw.Volumes.(map[string]interface{}); done {
|
||||
if volumesData["Volumes"] == nil {
|
||||
log.Println("[DEBUG] [volume migration] [message: no volume data found]")
|
||||
continue
|
||||
}
|
||||
|
||||
findResourcesToUpdateForDB32(endpointDockerID, volumesData, toUpdate, volumeResourceControls)
|
||||
}
|
||||
|
||||
findResourcesToUpdateForDB32(endpointDockerID, volumesData, toUpdate, volumeResourceControls)
|
||||
|
||||
}
|
||||
|
||||
for _, resourceControl := range volumeResourceControls {
|
||||
if newResourceID, ok := toUpdate[resourceControl.ID]; ok {
|
||||
resourceControl.ResourceID = newResourceID
|
||||
|
||||
err := m.resourceControlService.UpdateResourceControl(resourceControl.ID, resourceControl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed updating resource control %d: %w", resourceControl.ID, err)
|
||||
}
|
||||
|
||||
} else {
|
||||
err := m.resourceControlService.DeleteResourceControl(resourceControl.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed deleting resource control %d: %w", resourceControl.ID, err)
|
||||
}
|
||||
|
||||
log.Debug().Str("resource_id", resourceControl.ResourceID).Msg("legacy resource control has been deleted")
|
||||
log.Printf("[DEBUG] [volume migration] [message: legacy resource control(%s) has been deleted]", resourceControl.ResourceID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findResourcesToUpdateForDB32(dockerID string, volumesData volume.VolumeListOKBody, toUpdate map[portainer.ResourceControlID]string, volumeResourceControls map[string]*portainer.ResourceControl) {
|
||||
volumes := volumesData.Volumes
|
||||
for _, volume := range volumes {
|
||||
volumeName := volume.Name
|
||||
createTime := volume.CreatedAt
|
||||
func findResourcesToUpdateForDB32(dockerID string, volumesData map[string]interface{}, toUpdate map[portainer.ResourceControlID]string, volumeResourceControls map[string]*portainer.ResourceControl) {
|
||||
volumes := volumesData["Volumes"].([]interface{})
|
||||
for _, volumeMeta := range volumes {
|
||||
volume := volumeMeta.(map[string]interface{})
|
||||
volumeName, nameExist := volume["Name"].(string)
|
||||
if !nameExist {
|
||||
continue
|
||||
}
|
||||
createTime, createTimeExist := volume["CreatedAt"].(string)
|
||||
if !createTimeExist {
|
||||
continue
|
||||
}
|
||||
|
||||
oldResourceID := fmt.Sprintf("%s%s", volumeName, createTime)
|
||||
resourceControl, ok := volumeResourceControls[oldResourceID]
|
||||
@@ -261,25 +263,21 @@ func findResourcesToUpdateForDB32(dockerID string, volumesData volume.VolumeList
|
||||
}
|
||||
|
||||
func (m *Migrator) kubeconfigExpiryToDB32() error {
|
||||
log.Info().Msg("updating kubeconfig expiry")
|
||||
|
||||
migrateLog.Info("- updating kubeconfig expiry")
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.KubeconfigExpiry = portainer.DefaultKubeconfigExpiry
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
|
||||
func (m *Migrator) helmRepositoryURLToDB32() error {
|
||||
log.Info().Msg("setting default helm repository URL")
|
||||
|
||||
migrateLog.Info("- setting default helm repository URL")
|
||||
settings, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.HelmRepositoryURL = portainer.DefaultHelmRepositoryURL
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB33() error {
|
||||
log.Info().Msg("updating settings")
|
||||
migrateLog.Info("- updating settings")
|
||||
if err := m.migrateSettingsToDB33(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.migrateSettingsToDB33()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) migrateSettingsToDB33() error {
|
||||
@@ -18,8 +19,7 @@ func (m *Migrator) migrateSettingsToDB33() error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Msg("setting default kubectl shell image")
|
||||
migrateLog.Info("- setting default kubectl shell image")
|
||||
settings.KubectlShellImage = portainer.DefaultKubectlShellImage
|
||||
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,16 @@ package migrator
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB34() error {
|
||||
log.Info().Msg("updating stacks")
|
||||
migrateLog.Info("- updating stacks")
|
||||
err := MigrateStackEntryPoint(m.stackService)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return MigrateStackEntryPoint(m.stackService)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MigrateStackEntryPoint exported for testing (blah.)
|
||||
@@ -18,18 +20,15 @@ func MigrateStackEntryPoint(stackService dataservices.StackService) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range stacks {
|
||||
stack := &stacks[i]
|
||||
if stack.GitConfig == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
stack.GitConfig.ConfigFilePath = stack.EntryPoint
|
||||
if err := stackService.UpdateStack(stack.ID, stack); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package migrator
|
||||
|
||||
import "github.com/rs/zerolog/log"
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB35() error {
|
||||
// These should have been migrated already, but due to an earlier bug and a bunch of duplicates,
|
||||
// calling it again will now fix the issue as the function has been repaired.
|
||||
|
||||
log.Info().Msg("updating dockerhub registries")
|
||||
|
||||
return m.updateDockerhubToDB32()
|
||||
migrateLog.Info("- updating dockerhub registries")
|
||||
err := m.updateDockerhubToDB32()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,14 +3,15 @@ package migrator
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB36() error {
|
||||
log.Info().Msg("updating user authorizations")
|
||||
migrateLog.Info("Updating user authorizations")
|
||||
if err := m.migrateUsersToDB36(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.migrateUsersToDB36()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) migrateUsersToDB36() error {
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
import "github.com/portainer/portainer/api/internal/endpointutils"
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB40() error {
|
||||
return m.trustCurrentEdgeEndpointsDB40()
|
||||
if err := m.trustCurrentEdgeEndpointsDB40(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) trustCurrentEdgeEndpointsDB40() error {
|
||||
log.Info().Msg("trusting current edge endpoints")
|
||||
|
||||
migrateLog.Info("- trusting current edge endpoints")
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB50() error {
|
||||
return m.migratePasswordLengthSettings()
|
||||
}
|
||||
|
||||
func (m *Migrator) migratePasswordLengthSettings() error {
|
||||
log.Info().Msg("updating required password length")
|
||||
|
||||
s, err := m.settingsService.Settings()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to retrieve settings")
|
||||
}
|
||||
|
||||
s.InternalAuthSettings.RequiredPasswordLength = 12
|
||||
return m.settingsService.UpdateSettings(s)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB60() error {
|
||||
return m.addGpuInputFieldDB60()
|
||||
}
|
||||
|
||||
func (m *Migrator) addGpuInputFieldDB60() error {
|
||||
log.Info().Msg("add gpu input field")
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint.Gpus = []portainer.Pair{}
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB70() error {
|
||||
log.Info().Msg("- add IngressAvailabilityPerNamespace field")
|
||||
if err := m.updateIngressFieldsForEnvDB70(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
// copy snapshots to new object
|
||||
log.Info().Msg("moving snapshots from endpoint to new object")
|
||||
snapshot := portainer.Snapshot{EndpointID: endpoint.ID}
|
||||
|
||||
if len(endpoint.Snapshots) > 0 {
|
||||
snapshot.Docker = &endpoint.Snapshots[len(endpoint.Snapshots)-1]
|
||||
}
|
||||
|
||||
if len(endpoint.Kubernetes.Snapshots) > 0 {
|
||||
snapshot.Kubernetes = &endpoint.Kubernetes.Snapshots[len(endpoint.Kubernetes.Snapshots)-1]
|
||||
}
|
||||
|
||||
// save new object
|
||||
err = m.snapshotService.Create(&snapshot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// set to nil old fields
|
||||
log.Info().Msg("deleting snapshot from endpoint")
|
||||
endpoint.Snapshots = []portainer.DockerSnapshot{}
|
||||
endpoint.Kubernetes.Snapshots = []portainer.KubernetesSnapshot{}
|
||||
|
||||
// update endpoint
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) updateIngressFieldsForEnvDB70() error {
|
||||
endpoints, err := m.endpointService.Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
endpoint.Kubernetes.Configuration.IngressAvailabilityPerNamespace = true
|
||||
endpoint.Kubernetes.Configuration.AllowNoneIngressClass = false
|
||||
endpoint.PostInitMigrations.MigrateIngresses = true
|
||||
|
||||
err = m.endpointService.UpdateEndpoint(endpoint.ID, &endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func (m *Migrator) migrateDBVersionToDB71() error {
|
||||
log.Info().Msg("removing orphaned snapshots")
|
||||
|
||||
snapshots, err := m.snapshotService.Snapshots()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range snapshots {
|
||||
_, err := m.endpointService.Endpoint(s.EndpointID)
|
||||
if err == nil {
|
||||
log.Debug().Int("endpoint_id", int(s.EndpointID)).Msg("keeping snapshot")
|
||||
continue
|
||||
} else if err != errors.ErrObjectNotFound {
|
||||
log.Debug().Int("endpoint_id", int(s.EndpointID)).Err(err).Msg("database error")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug().Int("endpoint_id", int(s.EndpointID)).Msg("removing snapshot")
|
||||
|
||||
err = m.snapshotService.DeleteSnapshot(s.EndpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -13,15 +13,17 @@ import (
|
||||
"github.com/portainer/portainer/api/dataservices/role"
|
||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||
"github.com/portainer/portainer/api/dataservices/settings"
|
||||
"github.com/portainer/portainer/api/dataservices/snapshot"
|
||||
"github.com/portainer/portainer/api/dataservices/stack"
|
||||
"github.com/portainer/portainer/api/dataservices/tag"
|
||||
"github.com/portainer/portainer/api/dataservices/teammembership"
|
||||
"github.com/portainer/portainer/api/dataservices/user"
|
||||
"github.com/portainer/portainer/api/dataservices/version"
|
||||
plog "github.com/portainer/portainer/api/datastore/log"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
)
|
||||
|
||||
var migrateLog = plog.NewScopedLog("database, migrate")
|
||||
|
||||
type (
|
||||
// Migrator defines a service to migrate data after a Portainer version update.
|
||||
Migrator struct {
|
||||
@@ -36,7 +38,6 @@ type (
|
||||
roleService *role.Service
|
||||
scheduleService *schedule.Service
|
||||
settingsService *settings.Service
|
||||
snapshotService *snapshot.Service
|
||||
stackService *stack.Service
|
||||
tagService *tag.Service
|
||||
teamMembershipService *teammembership.Service
|
||||
@@ -60,7 +61,6 @@ type (
|
||||
RoleService *role.Service
|
||||
ScheduleService *schedule.Service
|
||||
SettingsService *settings.Service
|
||||
SnapshotService *snapshot.Service
|
||||
StackService *stack.Service
|
||||
TagService *tag.Service
|
||||
TeamMembershipService *teammembership.Service
|
||||
@@ -86,7 +86,6 @@ func NewMigrator(parameters *MigratorParameters) *Migrator {
|
||||
roleService: parameters.RoleService,
|
||||
scheduleService: parameters.ScheduleService,
|
||||
settingsService: parameters.SettingsService,
|
||||
snapshotService: parameters.SnapshotService,
|
||||
tagService: parameters.TagService,
|
||||
teamMembershipService: parameters.TeamMembershipService,
|
||||
stackService: parameters.StackService,
|
||||
|
||||
@@ -2,7 +2,6 @@ package datastore
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
@@ -14,7 +13,6 @@ import (
|
||||
"github.com/portainer/portainer/api/dataservices/edgegroup"
|
||||
"github.com/portainer/portainer/api/dataservices/edgejob"
|
||||
"github.com/portainer/portainer/api/dataservices/edgestack"
|
||||
"github.com/portainer/portainer/api/dataservices/edgeupdateschedule"
|
||||
"github.com/portainer/portainer/api/dataservices/endpoint"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointgroup"
|
||||
"github.com/portainer/portainer/api/dataservices/endpointrelation"
|
||||
@@ -26,7 +24,6 @@ import (
|
||||
"github.com/portainer/portainer/api/dataservices/role"
|
||||
"github.com/portainer/portainer/api/dataservices/schedule"
|
||||
"github.com/portainer/portainer/api/dataservices/settings"
|
||||
"github.com/portainer/portainer/api/dataservices/snapshot"
|
||||
"github.com/portainer/portainer/api/dataservices/ssl"
|
||||
"github.com/portainer/portainer/api/dataservices/stack"
|
||||
"github.com/portainer/portainer/api/dataservices/tag"
|
||||
@@ -36,8 +33,7 @@ import (
|
||||
"github.com/portainer/portainer/api/dataservices/user"
|
||||
"github.com/portainer/portainer/api/dataservices/version"
|
||||
"github.com/portainer/portainer/api/dataservices/webhook"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Store defines the implementation of portainer.DataStore using
|
||||
@@ -50,7 +46,6 @@ type Store struct {
|
||||
DockerHubService *dockerhub.Service
|
||||
EdgeGroupService *edgegroup.Service
|
||||
EdgeJobService *edgejob.Service
|
||||
EdgeUpdateScheduleService *edgeupdateschedule.Service
|
||||
EdgeStackService *edgestack.Service
|
||||
EndpointGroupService *endpointgroup.Service
|
||||
EndpointService *endpoint.Service
|
||||
@@ -64,7 +59,6 @@ type Store struct {
|
||||
APIKeyRepositoryService *apikeyrepository.Service
|
||||
ScheduleService *schedule.Service
|
||||
SettingsService *settings.Service
|
||||
SnapshotService *snapshot.Service
|
||||
SSLSettingsService *ssl.Service
|
||||
StackService *stack.Service
|
||||
TagService *tag.Service
|
||||
@@ -95,12 +89,6 @@ func (store *Store) initServices() error {
|
||||
}
|
||||
store.DockerHubService = dockerhubService
|
||||
|
||||
edgeUpdateScheduleService, err := edgeupdateschedule.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.EdgeUpdateScheduleService = edgeUpdateScheduleService
|
||||
|
||||
edgeStackService, err := edgestack.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -173,12 +161,6 @@ func (store *Store) initServices() error {
|
||||
}
|
||||
store.SettingsService = settingsService
|
||||
|
||||
snapshotService, err := snapshot.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
store.SnapshotService = snapshotService
|
||||
|
||||
sslSettingsService, err := ssl.NewService(store.connection)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -263,11 +245,6 @@ func (store *Store) EdgeJob() dataservices.EdgeJobService {
|
||||
return store.EdgeJobService
|
||||
}
|
||||
|
||||
// EdgeUpdateSchedule gives access to the EdgeUpdateSchedule data management layer
|
||||
func (store *Store) EdgeUpdateSchedule() dataservices.EdgeUpdateScheduleService {
|
||||
return store.EdgeUpdateScheduleService
|
||||
}
|
||||
|
||||
// EdgeStack gives access to the EdgeStack data management layer
|
||||
func (store *Store) EdgeStack() dataservices.EdgeStackService {
|
||||
return store.EdgeStackService
|
||||
@@ -323,10 +300,6 @@ func (store *Store) Settings() dataservices.SettingsService {
|
||||
return store.SettingsService
|
||||
}
|
||||
|
||||
func (store *Store) Snapshot() dataservices.SnapshotService {
|
||||
return store.SnapshotService
|
||||
}
|
||||
|
||||
// SSLSettings gives access to the SSL Settings data management layer
|
||||
func (store *Store) SSLSettings() dataservices.SSLSettingsService {
|
||||
return store.SSLSettingsService
|
||||
@@ -387,7 +360,6 @@ type storeExport struct {
|
||||
Role []portainer.Role `json:"roles,omitempty"`
|
||||
Schedules []portainer.Schedule `json:"schedules,omitempty"`
|
||||
Settings portainer.Settings `json:"settings,omitempty"`
|
||||
Snapshot []portainer.Snapshot `json:"snapshots,omitempty"`
|
||||
SSLSettings portainer.SSLSettings `json:"ssl,omitempty"`
|
||||
Stack []portainer.Stack `json:"stacks,omitempty"`
|
||||
Tag []portainer.Tag `json:"tags,omitempty"`
|
||||
@@ -406,7 +378,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if c, err := store.CustomTemplate().CustomTemplates(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Custom Templates")
|
||||
logrus.WithError(err).Errorf("Exporting Custom Templates")
|
||||
}
|
||||
} else {
|
||||
backup.CustomTemplate = c
|
||||
@@ -414,7 +386,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if e, err := store.EdgeGroup().EdgeGroups(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Groups")
|
||||
logrus.WithError(err).Errorf("Exporting Edge Groups")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeGroup = e
|
||||
@@ -422,7 +394,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if e, err := store.EdgeJob().EdgeJobs(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Jobs")
|
||||
logrus.WithError(err).Errorf("Exporting Edge Jobs")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeJob = e
|
||||
@@ -430,7 +402,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if e, err := store.EdgeStack().EdgeStacks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Edge Stacks")
|
||||
logrus.WithError(err).Errorf("Exporting Edge Stacks")
|
||||
}
|
||||
} else {
|
||||
backup.EdgeStack = e
|
||||
@@ -438,7 +410,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if e, err := store.Endpoint().Endpoints(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoints")
|
||||
logrus.WithError(err).Errorf("Exporting Endpoints")
|
||||
}
|
||||
} else {
|
||||
backup.Endpoint = e
|
||||
@@ -446,7 +418,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if e, err := store.EndpointGroup().EndpointGroups(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoint Groups")
|
||||
logrus.WithError(err).Errorf("Exporting Endpoint Groups")
|
||||
}
|
||||
} else {
|
||||
backup.EndpointGroup = e
|
||||
@@ -454,7 +426,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if r, err := store.EndpointRelation().EndpointRelations(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Endpoint Relations")
|
||||
logrus.WithError(err).Errorf("Exporting Endpoint Relations")
|
||||
}
|
||||
} else {
|
||||
backup.EndpointRelation = r
|
||||
@@ -462,7 +434,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if r, err := store.ExtensionService.Extensions(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Extensions")
|
||||
logrus.WithError(err).Errorf("Exporting Extensions")
|
||||
}
|
||||
} else {
|
||||
backup.Extensions = r
|
||||
@@ -470,7 +442,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if r, err := store.HelmUserRepository().HelmUserRepositories(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Helm User Repositories")
|
||||
logrus.WithError(err).Errorf("Exporting Helm User Repositories")
|
||||
}
|
||||
} else {
|
||||
backup.HelmUserRepository = r
|
||||
@@ -478,7 +450,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if r, err := store.Registry().Registries(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Registries")
|
||||
logrus.WithError(err).Errorf("Exporting Registries")
|
||||
}
|
||||
} else {
|
||||
backup.Registry = r
|
||||
@@ -486,7 +458,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if c, err := store.ResourceControl().ResourceControls(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Resource Controls")
|
||||
logrus.WithError(err).Errorf("Exporting Resource Controls")
|
||||
}
|
||||
} else {
|
||||
backup.ResourceControl = c
|
||||
@@ -494,7 +466,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if role, err := store.Role().Roles(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Roles")
|
||||
logrus.WithError(err).Errorf("Exporting Roles")
|
||||
}
|
||||
} else {
|
||||
backup.Role = role
|
||||
@@ -502,7 +474,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if r, err := store.ScheduleService.Schedules(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Schedules")
|
||||
logrus.WithError(err).Errorf("Exporting Schedules")
|
||||
}
|
||||
} else {
|
||||
backup.Schedules = r
|
||||
@@ -510,23 +482,15 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if settings, err := store.Settings().Settings(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Settings")
|
||||
logrus.WithError(err).Errorf("Exporting Settings")
|
||||
}
|
||||
} else {
|
||||
backup.Settings = *settings
|
||||
}
|
||||
|
||||
if snapshot, err := store.Snapshot().Snapshots(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Err(err).Msg("Exporting Snapshots")
|
||||
}
|
||||
} else {
|
||||
backup.Snapshot = snapshot
|
||||
}
|
||||
|
||||
if settings, err := store.SSLSettings().Settings(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting SSL Settings")
|
||||
logrus.WithError(err).Errorf("Exporting SSL Settings")
|
||||
}
|
||||
} else {
|
||||
backup.SSLSettings = *settings
|
||||
@@ -534,7 +498,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if t, err := store.Stack().Stacks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Stacks")
|
||||
logrus.WithError(err).Errorf("Exporting Stacks")
|
||||
}
|
||||
} else {
|
||||
backup.Stack = t
|
||||
@@ -542,7 +506,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if t, err := store.Tag().Tags(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Tags")
|
||||
logrus.WithError(err).Errorf("Exporting Tags")
|
||||
}
|
||||
} else {
|
||||
backup.Tag = t
|
||||
@@ -550,7 +514,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if t, err := store.TeamMembership().TeamMemberships(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Team Memberships")
|
||||
logrus.WithError(err).Errorf("Exporting Team Memberships")
|
||||
}
|
||||
} else {
|
||||
backup.TeamMembership = t
|
||||
@@ -558,7 +522,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if t, err := store.Team().Teams(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Teams")
|
||||
logrus.WithError(err).Errorf("Exporting Teams")
|
||||
}
|
||||
} else {
|
||||
backup.Team = t
|
||||
@@ -566,7 +530,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if info, err := store.TunnelServer().Info(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Tunnel Server")
|
||||
logrus.WithError(err).Errorf("Exporting Tunnel Server")
|
||||
}
|
||||
} else {
|
||||
backup.TunnelServer = *info
|
||||
@@ -574,7 +538,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if users, err := store.User().Users(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Users")
|
||||
logrus.WithError(err).Errorf("Exporting Users")
|
||||
}
|
||||
} else {
|
||||
backup.User = users
|
||||
@@ -582,7 +546,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
if webhooks, err := store.Webhook().Webhooks(); err != nil {
|
||||
if !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting Webhooks")
|
||||
logrus.WithError(err).Errorf("Exporting Webhooks")
|
||||
}
|
||||
} else {
|
||||
backup.Webhook = webhooks
|
||||
@@ -590,7 +554,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
v, err := store.Version().DBVersion()
|
||||
if err != nil && !store.IsErrObjectNotFound(err) {
|
||||
log.Error().Err(err).Msg("exporting DB version")
|
||||
logrus.WithError(err).Errorf("Exporting DB version")
|
||||
}
|
||||
instance, _ := store.Version().InstanceID()
|
||||
backup.Version = map[string]string{
|
||||
@@ -600,7 +564,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
|
||||
backup.Metadata, err = store.connection.BackupMetadata()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("exporting Metadata")
|
||||
logrus.WithError(err).Errorf("Exporting Metadata")
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(backup, "", " ")
|
||||
@@ -611,6 +575,7 @@ func (store *Store) Export(filename string) (err error) {
|
||||
}
|
||||
|
||||
func (store *Store) Import(filename string) (err error) {
|
||||
|
||||
backup := storeExport{}
|
||||
|
||||
s, err := ioutil.ReadFile(filename)
|
||||
@@ -626,13 +591,13 @@ func (store *Store) Import(filename string) (err error) {
|
||||
if dbversion, ok := backup.Version["DB_VERSION"]; ok {
|
||||
if v, err := strconv.Atoi(dbversion); err == nil {
|
||||
if err := store.Version().StoreDBVersion(v); err != nil {
|
||||
log.Error().Err(err).Msg("DB_VERSION import issue")
|
||||
logrus.WithError(err).Errorf("DB_VERSION import issue")
|
||||
}
|
||||
}
|
||||
}
|
||||
if instanceID, ok := backup.Version["INSTANCE_ID"]; ok {
|
||||
if err := store.Version().StoreInstanceID(instanceID); err != nil {
|
||||
log.Error().Err(err).Msg("INSTANCE_ID import issue")
|
||||
logrus.WithError(err).Errorf("INSTANCE_ID import issue")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,10 +648,6 @@ func (store *Store) Import(filename string) (err error) {
|
||||
store.Settings().UpdateSettings(&backup.Settings)
|
||||
store.SSLSettings().UpdateSettings(&backup.SSLSettings)
|
||||
|
||||
for _, v := range backup.Snapshot {
|
||||
store.Snapshot().UpdateSnapshot(&v)
|
||||
}
|
||||
|
||||
for _, v := range backup.Stack {
|
||||
store.Stack().UpdateStack(v.ID, &v)
|
||||
}
|
||||
@@ -707,7 +668,7 @@ func (store *Store) Import(filename string) (err error) {
|
||||
|
||||
for _, user := range backup.User {
|
||||
if err := store.User().UpdateUser(user.ID, &user); err != nil {
|
||||
log.Debug().Str("user", fmt.Sprintf("%+v", user)).Err(err).Msg("user: failed to Update Database")
|
||||
logrus.WithField("user", user).WithError(err).Errorf("User: Failed to Update Database")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@
|
||||
],
|
||||
"endpoints": [
|
||||
{
|
||||
"Agent": {
|
||||
"Version": ""
|
||||
},
|
||||
"AuthorizedTeams": null,
|
||||
"AuthorizedUsers": null,
|
||||
"AzureCredentials": {
|
||||
@@ -38,37 +35,23 @@
|
||||
"TenantID": ""
|
||||
},
|
||||
"ComposeSyntaxMaxVersion": "",
|
||||
"Edge": {
|
||||
"AsyncMode": false,
|
||||
"CommandInterval": 0,
|
||||
"PingInterval": 0,
|
||||
"SnapshotInterval": 0
|
||||
},
|
||||
"EdgeCheckinInterval": 0,
|
||||
"EdgeKey": "",
|
||||
"Gpus": [],
|
||||
"GroupId": 1,
|
||||
"Id": 1,
|
||||
"IsEdgeDevice": false,
|
||||
"Kubernetes": {
|
||||
"Configuration": {
|
||||
"AllowNoneIngressClass": false,
|
||||
"EnableResourceOverCommit": false,
|
||||
"IngressAvailabilityPerNamespace": true,
|
||||
"IngressClasses": null,
|
||||
"ResourceOverCommitPercentage": 0,
|
||||
"RestrictDefaultNamespace": false,
|
||||
"StorageClasses": null,
|
||||
"UseLoadBalancer": false,
|
||||
"UseServerMetrics": false
|
||||
},
|
||||
"Snapshots": []
|
||||
"Snapshots": null
|
||||
},
|
||||
"LastCheckInDate": 0,
|
||||
"Name": "local",
|
||||
"PostInitMigrations": {
|
||||
"MigrateIngresses": true
|
||||
},
|
||||
"PublicURL": "",
|
||||
"QueryDate": 0,
|
||||
"SecuritySettings": {
|
||||
@@ -82,7 +65,32 @@
|
||||
"allowVolumeBrowserForRegularUsers": false,
|
||||
"enableHostManagementFeatures": false
|
||||
},
|
||||
"Snapshots": [],
|
||||
"Snapshots": [
|
||||
{
|
||||
"DockerSnapshotRaw": {
|
||||
"Containers": null,
|
||||
"Images": null,
|
||||
"Info": null,
|
||||
"Networks": null,
|
||||
"Version": null,
|
||||
"Volumes": null
|
||||
},
|
||||
"DockerVersion": "20.10.13",
|
||||
"HealthyContainerCount": 0,
|
||||
"ImageCount": 9,
|
||||
"NodeCount": 0,
|
||||
"RunningContainerCount": 5,
|
||||
"ServiceCount": 0,
|
||||
"StackCount": 2,
|
||||
"StoppedContainerCount": 0,
|
||||
"Swarm": false,
|
||||
"Time": 1648610112,
|
||||
"TotalCPU": 8,
|
||||
"TotalMemory": 25098706944,
|
||||
"UnhealthyContainerCount": 0,
|
||||
"VolumeCount": 10
|
||||
}
|
||||
],
|
||||
"Status": 1,
|
||||
"TLSConfig": {
|
||||
"TLS": false,
|
||||
@@ -581,12 +589,6 @@
|
||||
"BlackListedLabels": [],
|
||||
"DisplayDonationHeader": false,
|
||||
"DisplayExternalContributors": false,
|
||||
"Edge": {
|
||||
"AsyncMode": false,
|
||||
"CommandInterval": 0,
|
||||
"PingInterval": 0,
|
||||
"SnapshotInterval": 0
|
||||
},
|
||||
"EdgeAgentCheckinInterval": 5,
|
||||
"EdgePortainerUrl": "",
|
||||
"EnableEdgeComputeFeatures": false,
|
||||
@@ -595,9 +597,6 @@
|
||||
"EnforceEdgeID": false,
|
||||
"FeatureFlagSettings": null,
|
||||
"HelmRepositoryURL": "https://charts.bitnami.com/bitnami",
|
||||
"InternalAuthSettings": {
|
||||
"RequiredPasswordLength": 12
|
||||
},
|
||||
"KubeconfigExpiry": "0",
|
||||
"KubectlShellImage": "portainer/kubectl-shell",
|
||||
"LDAPSettings": {
|
||||
@@ -662,131 +661,6 @@
|
||||
"mpsUser": ""
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"Docker": {
|
||||
"DockerSnapshotRaw": {
|
||||
"Containers": null,
|
||||
"Images": null,
|
||||
"Info": {
|
||||
"Architecture": "",
|
||||
"BridgeNfIp6tables": false,
|
||||
"BridgeNfIptables": false,
|
||||
"CPUSet": false,
|
||||
"CPUShares": false,
|
||||
"CgroupDriver": "",
|
||||
"ContainerdCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Containers": 0,
|
||||
"ContainersPaused": 0,
|
||||
"ContainersRunning": 0,
|
||||
"ContainersStopped": 0,
|
||||
"CpuCfsPeriod": false,
|
||||
"CpuCfsQuota": false,
|
||||
"Debug": false,
|
||||
"DefaultRuntime": "",
|
||||
"DockerRootDir": "",
|
||||
"Driver": "",
|
||||
"DriverStatus": null,
|
||||
"ExperimentalBuild": false,
|
||||
"GenericResources": null,
|
||||
"HttpProxy": "",
|
||||
"HttpsProxy": "",
|
||||
"ID": "",
|
||||
"IPv4Forwarding": false,
|
||||
"Images": 0,
|
||||
"IndexServerAddress": "",
|
||||
"InitBinary": "",
|
||||
"InitCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Isolation": "",
|
||||
"KernelMemory": false,
|
||||
"KernelMemoryTCP": false,
|
||||
"KernelVersion": "",
|
||||
"Labels": null,
|
||||
"LiveRestoreEnabled": false,
|
||||
"LoggingDriver": "",
|
||||
"MemTotal": 0,
|
||||
"MemoryLimit": false,
|
||||
"NCPU": 0,
|
||||
"NEventsListener": 0,
|
||||
"NFd": 0,
|
||||
"NGoroutines": 0,
|
||||
"Name": "",
|
||||
"NoProxy": "",
|
||||
"OSType": "",
|
||||
"OSVersion": "",
|
||||
"OomKillDisable": false,
|
||||
"OperatingSystem": "",
|
||||
"PidsLimit": false,
|
||||
"Plugins": {
|
||||
"Authorization": null,
|
||||
"Log": null,
|
||||
"Network": null,
|
||||
"Volume": null
|
||||
},
|
||||
"RegistryConfig": null,
|
||||
"RuncCommit": {
|
||||
"Expected": "",
|
||||
"ID": ""
|
||||
},
|
||||
"Runtimes": null,
|
||||
"SecurityOptions": null,
|
||||
"ServerVersion": "",
|
||||
"SwapLimit": false,
|
||||
"Swarm": {
|
||||
"ControlAvailable": false,
|
||||
"Error": "",
|
||||
"LocalNodeState": "",
|
||||
"NodeAddr": "",
|
||||
"NodeID": "",
|
||||
"RemoteManagers": null
|
||||
},
|
||||
"SystemTime": "",
|
||||
"Warnings": null
|
||||
},
|
||||
"Networks": null,
|
||||
"Version": {
|
||||
"ApiVersion": "",
|
||||
"Arch": "",
|
||||
"GitCommit": "",
|
||||
"GoVersion": "",
|
||||
"Os": "",
|
||||
"Platform": {
|
||||
"Name": ""
|
||||
},
|
||||
"Version": ""
|
||||
},
|
||||
"Volumes": {
|
||||
"Volumes": null,
|
||||
"Warnings": null
|
||||
}
|
||||
},
|
||||
"DockerVersion": "20.10.13",
|
||||
"GpuUseAll": false,
|
||||
"GpuUseList": null,
|
||||
"HealthyContainerCount": 0,
|
||||
"ImageCount": 9,
|
||||
"NodeCount": 0,
|
||||
"RunningContainerCount": 5,
|
||||
"ServiceCount": 0,
|
||||
"StackCount": 2,
|
||||
"StoppedContainerCount": 0,
|
||||
"Swarm": false,
|
||||
"Time": 1648610112,
|
||||
"TotalCPU": 8,
|
||||
"TotalMemory": 25098706944,
|
||||
"UnhealthyContainerCount": 0,
|
||||
"VolumeCount": 10
|
||||
},
|
||||
"EndpointId": 1,
|
||||
"Kubernetes": null
|
||||
}
|
||||
],
|
||||
"ssl": {
|
||||
"certPath": "",
|
||||
"httpEnabled": true,
|
||||
@@ -808,7 +682,6 @@
|
||||
"IsComposeFormat": false,
|
||||
"Name": "alpine",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/2",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
@@ -831,7 +704,6 @@
|
||||
"IsComposeFormat": false,
|
||||
"Name": "redis",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/5",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
@@ -854,7 +726,6 @@
|
||||
"IsComposeFormat": false,
|
||||
"Name": "nginx",
|
||||
"Namespace": "",
|
||||
"Option": null,
|
||||
"ProjectPath": "/home/prabhat/portainer/data/ce1.25/compose/6",
|
||||
"ResourceControl": null,
|
||||
"Status": 1,
|
||||
@@ -931,7 +802,7 @@
|
||||
],
|
||||
"version": {
|
||||
"DB_UPDATING": "false",
|
||||
"DB_VERSION": "72",
|
||||
"DB_VERSION": "35",
|
||||
"INSTANCE_ID": "null"
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
)
|
||||
|
||||
var errTempDir = errors.New("can't create a temp dir")
|
||||
@@ -17,22 +18,25 @@ func (store *Store) GetConnection() portainer.Connection {
|
||||
return store.connection
|
||||
}
|
||||
|
||||
func MustNewTestStore(t *testing.T, init, secure bool) (bool, *Store, func()) {
|
||||
newStore, store, teardown, err := NewTestStore(t, init, secure)
|
||||
func MustNewTestStore(init, secure bool) (bool, *Store, func()) {
|
||||
newStore, store, teardown, err := NewTestStore(init, secure)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errTempDir) {
|
||||
teardown()
|
||||
}
|
||||
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
return newStore, store, teardown
|
||||
}
|
||||
|
||||
func NewTestStore(t *testing.T, init, secure bool) (bool, *Store, func(), error) {
|
||||
func NewTestStore(init, secure bool) (bool, *Store, func(), error) {
|
||||
// Creates unique temp directory in a concurrency friendly manner.
|
||||
storePath := t.TempDir()
|
||||
storePath, err := ioutil.TempDir("", "test-store")
|
||||
if err != nil {
|
||||
return false, nil, nil, errors.Wrap(errTempDir, err.Error())
|
||||
}
|
||||
|
||||
fileService, err := filesystem.NewService(storePath, "")
|
||||
if err != nil {
|
||||
return false, nil, nil, err
|
||||
@@ -47,15 +51,12 @@ func NewTestStore(t *testing.T, init, secure bool) (bool, *Store, func(), error)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
store := NewStore(storePath, fileService, connection)
|
||||
newStore, err := store.Open()
|
||||
if err != nil {
|
||||
return newStore, nil, nil, err
|
||||
}
|
||||
|
||||
log.Debug().Msg("opened")
|
||||
|
||||
if init {
|
||||
err = store.Init()
|
||||
if err != nil {
|
||||
@@ -63,8 +64,6 @@ func NewTestStore(t *testing.T, init, secure bool) (bool, *Store, func(), error)
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug().Msg("initialised")
|
||||
|
||||
if newStore {
|
||||
// from MigrateData
|
||||
store.VersionService.StoreDBVersion(portainer.DBVersion)
|
||||
@@ -74,15 +73,20 @@ func NewTestStore(t *testing.T, init, secure bool) (bool, *Store, func(), error)
|
||||
}
|
||||
|
||||
teardown := func() {
|
||||
teardown(store)
|
||||
teardown(store, storePath)
|
||||
}
|
||||
|
||||
return newStore, store, teardown, nil
|
||||
}
|
||||
|
||||
func teardown(store *Store) {
|
||||
func teardown(store *Store, storePath string) {
|
||||
err := store.Close()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(storePath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
118
api/demo/demo.go
118
api/demo/demo.go
@@ -1,118 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type EnvironmentDetails struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Users []portainer.UserID `json:"users"`
|
||||
Environments []portainer.EndpointID `json:"environments"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
details EnvironmentDetails
|
||||
}
|
||||
|
||||
func NewService() *Service {
|
||||
return &Service{}
|
||||
}
|
||||
|
||||
func (service *Service) Details() EnvironmentDetails {
|
||||
return service.details
|
||||
}
|
||||
|
||||
func (service *Service) Init(store dataservices.DataStore, cryptoService portainer.CryptoService) error {
|
||||
log.Info().Msg("starting demo environment")
|
||||
|
||||
isClean, err := isCleanStore(store)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed checking if store is clean")
|
||||
}
|
||||
|
||||
if !isClean {
|
||||
return errors.New(" Demo environment can only be initialized on a clean database")
|
||||
}
|
||||
|
||||
id, err := initDemoUser(store, cryptoService)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed creating demo user")
|
||||
}
|
||||
|
||||
endpointIds, err := initDemoEndpoints(store)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed creating demo endpoint")
|
||||
}
|
||||
|
||||
err = initDemoSettings(store)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed updating demo settings")
|
||||
}
|
||||
|
||||
service.details = EnvironmentDetails{
|
||||
Enabled: true,
|
||||
Users: []portainer.UserID{id},
|
||||
// endpoints 2,3 are created after deployment of portainer
|
||||
Environments: endpointIds,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isCleanStore(store dataservices.DataStore) (bool, error) {
|
||||
endpoints, err := store.Endpoint().Endpoints()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(endpoints) > 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
users, err := store.User().Users()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(users) > 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (service *Service) IsDemo() bool {
|
||||
return service.details.Enabled
|
||||
}
|
||||
|
||||
func (service *Service) IsDemoEnvironment(environmentID portainer.EndpointID) bool {
|
||||
if !service.IsDemo() {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, demoEndpointID := range service.details.Environments {
|
||||
if environmentID == demoEndpointID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (service *Service) IsDemoUser(userID portainer.UserID) bool {
|
||||
if !service.IsDemo() {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, demoUserID := range service.details.Users {
|
||||
if userID == demoUserID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package demo
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
func initDemoUser(
|
||||
store dataservices.DataStore,
|
||||
cryptoService portainer.CryptoService,
|
||||
) (portainer.UserID, error) {
|
||||
|
||||
password, err := cryptoService.Hash("tryportainer")
|
||||
if err != nil {
|
||||
return 0, errors.WithMessage(err, "failed creating password hash")
|
||||
}
|
||||
|
||||
admin := &portainer.User{
|
||||
Username: "admin",
|
||||
Password: password,
|
||||
Role: portainer.AdministratorRole,
|
||||
}
|
||||
|
||||
err = store.User().Create(admin)
|
||||
return admin.ID, errors.WithMessage(err, "failed creating user")
|
||||
}
|
||||
|
||||
func initDemoEndpoints(store dataservices.DataStore) ([]portainer.EndpointID, error) {
|
||||
localEndpointId, err := initDemoLocalEndpoint(store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// second and third endpoints are going to be created with docker-compose as a part of the demo environment set up.
|
||||
// ref: https://github.com/portainer/portainer-demo/blob/master/docker-compose.yml
|
||||
return []portainer.EndpointID{localEndpointId, localEndpointId + 1, localEndpointId + 2}, nil
|
||||
}
|
||||
|
||||
func initDemoLocalEndpoint(store dataservices.DataStore) (portainer.EndpointID, error) {
|
||||
id := portainer.EndpointID(store.Endpoint().GetNextIdentifier())
|
||||
localEndpoint := &portainer.Endpoint{
|
||||
ID: id,
|
||||
Name: "local",
|
||||
URL: "unix:///var/run/docker.sock",
|
||||
PublicURL: "demo.portainer.io",
|
||||
Type: portainer.DockerEnvironment,
|
||||
GroupID: portainer.EndpointGroupID(1),
|
||||
TLSConfig: portainer.TLSConfiguration{
|
||||
TLS: false,
|
||||
},
|
||||
AuthorizedUsers: []portainer.UserID{},
|
||||
AuthorizedTeams: []portainer.TeamID{},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
err := store.Endpoint().Create(localEndpoint)
|
||||
if err != nil {
|
||||
return id, errors.WithMessage(err, "failed creating local endpoint")
|
||||
}
|
||||
|
||||
err = store.Snapshot().Create(&portainer.Snapshot{EndpointID: id})
|
||||
if err != nil {
|
||||
return id, errors.WithMessage(err, "failed creating snapshot")
|
||||
}
|
||||
|
||||
return id, errors.WithMessage(err, "failed creating local endpoint")
|
||||
}
|
||||
|
||||
func initDemoSettings(
|
||||
store dataservices.DataStore,
|
||||
) error {
|
||||
settings, err := store.Settings().Settings()
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed fetching settings")
|
||||
}
|
||||
|
||||
settings.EnableTelemetry = false
|
||||
settings.LogoURL = ""
|
||||
|
||||
err = store.Settings().UpdateSettings(settings)
|
||||
return errors.WithMessage(err, "failed updating settings")
|
||||
}
|
||||
@@ -2,16 +2,14 @@ package docker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
_container "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
// Snapshotter represents a service used to create environment(endpoint) snapshots
|
||||
@@ -49,44 +47,44 @@ func snapshot(cli *client.Client, endpoint *portainer.Endpoint) (*portainer.Dock
|
||||
|
||||
err = snapshotInfo(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot engine information")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot engine information] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
if snapshot.Swarm {
|
||||
err = snapshotSwarmServices(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot Swarm services")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot Swarm services] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
err = snapshotNodes(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot Swarm nodes")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot Swarm nodes] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = snapshotContainers(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot containers")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot containers] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
err = snapshotImages(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot images")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot images] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
err = snapshotVolumes(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot volumes")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot volumes] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
err = snapshotNetworks(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot networks")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot networks] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
err = snapshotVersion(snapshot, cli)
|
||||
if err != nil {
|
||||
log.Warn().Str("environment", endpoint.Name).Err(err).Msg("unable to snapshot engine version")
|
||||
log.Printf("[WARN] [docker,snapshot] [message: unable to snapshot engine version] [environment: %s] [err: %s]", endpoint.Name, err)
|
||||
}
|
||||
|
||||
snapshot.Time = time.Now().Unix()
|
||||
@@ -156,35 +154,11 @@ func snapshotContainers(snapshot *portainer.DockerSnapshot, cli *client.Client)
|
||||
healthyContainers := 0
|
||||
unhealthyContainers := 0
|
||||
stacks := make(map[string]struct{})
|
||||
gpuUseSet := make(map[string]struct{})
|
||||
gpuUseAll := false
|
||||
for _, container := range containers {
|
||||
if container.State == "exited" {
|
||||
stoppedContainers++
|
||||
} else if container.State == "running" {
|
||||
runningContainers++
|
||||
|
||||
// snapshot GPUs
|
||||
response, err := cli.ContainerInspect(context.Background(), container.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var gpuOptions *_container.DeviceRequest = nil
|
||||
for _, deviceRequest := range response.HostConfig.Resources.DeviceRequests {
|
||||
if deviceRequest.Driver == "nvidia" || deviceRequest.Capabilities[0][0] == "gpu" {
|
||||
gpuOptions = &deviceRequest
|
||||
}
|
||||
}
|
||||
|
||||
if gpuOptions != nil {
|
||||
if gpuOptions.Count == -1 {
|
||||
gpuUseAll = true
|
||||
}
|
||||
for _, id := range gpuOptions.DeviceIDs {
|
||||
gpuUseSet[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.Contains(container.Status, "(healthy)") {
|
||||
@@ -200,14 +174,6 @@ func snapshotContainers(snapshot *portainer.DockerSnapshot, cli *client.Client)
|
||||
}
|
||||
}
|
||||
|
||||
gpuUseList := make([]string, 0, len(gpuUseSet))
|
||||
for gpuUse := range gpuUseSet {
|
||||
gpuUseList = append(gpuUseList, gpuUse)
|
||||
}
|
||||
|
||||
snapshot.GpuUseAll = gpuUseAll
|
||||
snapshot.GpuUseList = gpuUseList
|
||||
|
||||
snapshot.RunningContainerCount = runningContainers
|
||||
snapshot.StoppedContainerCount = stoppedContainers
|
||||
snapshot.HealthyContainerCount = healthyContainers
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
package edgetypes
|
||||
|
||||
import portainer "github.com/portainer/portainer/api"
|
||||
|
||||
const (
|
||||
// PortainerAgentUpdateScheduleIDHeader represents the name of the header containing the update schedule id
|
||||
PortainerAgentUpdateScheduleIDHeader = "X-Portainer-Update-Schedule-ID"
|
||||
// PortainerAgentUpdateStatusHeader is the name of the header that will have the update status
|
||||
PortainerAgentUpdateStatusHeader = "X-Portainer-Update-Status"
|
||||
// PortainerAgentUpdateErrorHeader is the name of the header that will have the update error
|
||||
PortainerAgentUpdateErrorHeader = "X-Portainer-Update-Error"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
// UpdateScheduleID represents an Edge schedule identifier
|
||||
UpdateScheduleID int
|
||||
|
||||
// UpdateSchedule represents a schedule for update/rollback of edge devices
|
||||
UpdateSchedule struct {
|
||||
// EdgeUpdateSchedule Identifier
|
||||
ID UpdateScheduleID `json:"id" example:"1"`
|
||||
// Name of the schedule
|
||||
Name string `json:"name" example:"Update Schedule"`
|
||||
// Type of the schedule
|
||||
Time int64 `json:"time" example:"1564897200"`
|
||||
// EdgeGroups to be updated
|
||||
GroupIDs []portainer.EdgeGroupID `json:"groupIds" example:"1"`
|
||||
// Type of the update (1 - update, 2 - rollback)
|
||||
Type UpdateScheduleType `json:"type" example:"1" enums:"1,2"`
|
||||
// Status of the schedule, grouped by environment id
|
||||
Status map[portainer.EndpointID]UpdateScheduleStatus `json:"status"`
|
||||
// Created timestamp
|
||||
Created int64 `json:"created" example:"1564897200"`
|
||||
// Created by user id
|
||||
CreatedBy portainer.UserID `json:"createdBy" example:"1"`
|
||||
}
|
||||
|
||||
// UpdateScheduleType represents type of an Edge update schedule
|
||||
UpdateScheduleType int
|
||||
|
||||
// UpdateScheduleStatus represents status of an Edge update schedule
|
||||
UpdateScheduleStatus struct {
|
||||
// Status of the schedule (0 - pending, 1 - failed, 2 - success)
|
||||
Status UpdateScheduleStatusType `json:"status" example:"1" enums:"1,2,3"`
|
||||
// Error message if status is failed
|
||||
Error string `json:"error" example:""`
|
||||
// Target version of the edge agent
|
||||
TargetVersion string `json:"targetVersion" example:"1"`
|
||||
// Current version of the edge agent
|
||||
CurrentVersion string `json:"currentVersion" example:"1"`
|
||||
}
|
||||
|
||||
// UpdateScheduleStatusType represents status type of an Edge update schedule
|
||||
UpdateScheduleStatusType int
|
||||
|
||||
VersionUpdateRequest struct {
|
||||
// Target version
|
||||
Version string
|
||||
// Scheduled time
|
||||
ScheduledTime int64
|
||||
// If need to update
|
||||
Active bool
|
||||
// Update schedule ID
|
||||
ScheduleID UpdateScheduleID
|
||||
}
|
||||
|
||||
// VersionUpdateStatus represents the status of an agent version update
|
||||
VersionUpdateStatus struct {
|
||||
Status UpdateScheduleStatusType
|
||||
ScheduleID UpdateScheduleID
|
||||
Error string
|
||||
}
|
||||
|
||||
// EndpointUpdateScheduleRelation represents the relation between an environment(endpoint) and an update schedule
|
||||
EndpointUpdateScheduleRelation struct {
|
||||
EnvironmentID portainer.EndpointID `json:"environmentId"`
|
||||
ScheduleID UpdateScheduleID `json:"scheduleId"`
|
||||
TargetVersion string `json:"targetVersion"`
|
||||
Status UpdateScheduleStatusType `json:"status"`
|
||||
Error string `json:"error"`
|
||||
Type UpdateScheduleType `json:"type"`
|
||||
ScheduledTime int64 `json:"scheduledTime"`
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
_ UpdateScheduleType = iota
|
||||
// UpdateScheduleUpdate represents an edge device scheduled for an update
|
||||
UpdateScheduleUpdate
|
||||
// UpdateScheduleRollback represents an edge device scheduled for a rollback
|
||||
UpdateScheduleRollback
|
||||
)
|
||||
|
||||
const (
|
||||
// UpdateScheduleStatusPending represents a pending edge update schedule
|
||||
UpdateScheduleStatusPending UpdateScheduleStatusType = iota
|
||||
// UpdateScheduleStatusError represents a failed edge update schedule
|
||||
UpdateScheduleStatusError
|
||||
// UpdateScheduleStatusSuccess represents a successful edge update schedule
|
||||
UpdateScheduleStatusSuccess
|
||||
)
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
libstack "github.com/portainer/docker-compose-wrapper"
|
||||
"github.com/portainer/docker-compose-wrapper/compose"
|
||||
|
||||
"github.com/docker/cli/cli/compose/loader"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
"github.com/portainer/portainer/api/http/proxy/factory"
|
||||
@@ -54,13 +56,13 @@ func (manager *ComposeStackManager) Up(ctx context.Context, stack *portainer.Sta
|
||||
defer proxy.Close()
|
||||
}
|
||||
|
||||
envFile, err := createEnvFile(stack)
|
||||
envFilePath, err := createEnvFile(stack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
err = manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile, forceRereate)
|
||||
err = manager.deployer.Deploy(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFilePath, forceRereate)
|
||||
return errors.Wrap(err, "failed to deploy a stack")
|
||||
}
|
||||
|
||||
@@ -74,36 +76,13 @@ func (manager *ComposeStackManager) Down(ctx context.Context, stack *portainer.S
|
||||
defer proxy.Close()
|
||||
}
|
||||
|
||||
envFile, err := createEnvFile(stack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
|
||||
err = manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile)
|
||||
return errors.Wrap(err, "failed to remove a stack")
|
||||
}
|
||||
|
||||
// Pull an image associated with a service defined in a docker-compose.yml or docker-stack.yml file,
|
||||
// but does not start containers based on those images.
|
||||
func (manager *ComposeStackManager) Pull(ctx context.Context, stack *portainer.Stack, endpoint *portainer.Endpoint) error {
|
||||
url, proxy, err := manager.fetchEndpointProxy(endpoint)
|
||||
if err != nil {
|
||||
if err := updateNetworkEnvFile(stack); err != nil {
|
||||
return err
|
||||
}
|
||||
if proxy != nil {
|
||||
defer proxy.Close()
|
||||
}
|
||||
|
||||
envFile, err := createEnvFile(stack)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create env file")
|
||||
}
|
||||
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
err = manager.deployer.Pull(ctx, stack.ProjectPath, url, stack.Name, filePaths, envFile)
|
||||
return errors.Wrap(err, "failed to pull images of the stack")
|
||||
err = manager.deployer.Remove(ctx, stack.ProjectPath, url, stack.Name, filePaths)
|
||||
return errors.Wrap(err, "failed to remove a stack")
|
||||
}
|
||||
|
||||
// NormalizeStackName returns a new stack name with unsupported characters replaced
|
||||
@@ -124,42 +103,200 @@ func (manager *ComposeStackManager) fetchEndpointProxy(endpoint *portainer.Endpo
|
||||
return fmt.Sprintf("tcp://127.0.0.1:%d", proxy.Port), proxy, nil
|
||||
}
|
||||
|
||||
// createEnvFile creates a file that would hold both "in-place" and default environment variables.
|
||||
// It will return the name of the file if the stack has "in-place" env vars, otherwise empty string.
|
||||
func createEnvFile(stack *portainer.Stack) (string, error) {
|
||||
// workaround for EE-1862. It will have to be removed when
|
||||
// docker/compose upgraded to v2.x.
|
||||
if err := createNetworkEnvFile(stack); err != nil {
|
||||
return "", errors.Wrap(err, "failed to create network env file")
|
||||
}
|
||||
|
||||
if stack.Env == nil || len(stack.Env) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
envFilePath := path.Join(stack.ProjectPath, "stack.env")
|
||||
|
||||
envfile, err := os.OpenFile(envFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer envfile.Close()
|
||||
|
||||
copyDefaultEnvFile(stack, envfile)
|
||||
|
||||
for _, v := range stack.Env {
|
||||
envfile.WriteString(fmt.Sprintf("%s=%s\n", v.Name, v.Value))
|
||||
}
|
||||
envfile.Close()
|
||||
|
||||
return "stack.env", nil
|
||||
}
|
||||
|
||||
// copyDefaultEnvFile copies the default .env file if it exists to the provided writer
|
||||
func copyDefaultEnvFile(stack *portainer.Stack, w io.Writer) {
|
||||
defaultEnvFile, err := os.Open(path.Join(path.Join(stack.ProjectPath, path.Dir(stack.EntryPoint)), ".env"))
|
||||
if err != nil {
|
||||
// If cannot open a default file, then don't need to copy it.
|
||||
// We could as well stat it and check if it exists, but this is more efficient.
|
||||
return
|
||||
func fileNotExist(filePath string) bool {
|
||||
if _, err := os.Stat(filePath); errors.Is(err, os.ErrNotExist) {
|
||||
return true
|
||||
}
|
||||
|
||||
defer defaultEnvFile.Close()
|
||||
|
||||
if _, err = io.Copy(w, defaultEnvFile); err == nil {
|
||||
io.WriteString(w, "\n")
|
||||
}
|
||||
// If couldn't copy the .env file, then ignore the error and try to continue
|
||||
return false
|
||||
}
|
||||
|
||||
func updateNetworkEnvFile(stack *portainer.Stack) error {
|
||||
envFilePath := path.Join(stack.ProjectPath, ".env")
|
||||
stackFilePath := path.Join(stack.ProjectPath, "stack.env")
|
||||
if fileNotExist(envFilePath) {
|
||||
if fileNotExist(stackFilePath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
flags := os.O_WRONLY | os.O_SYNC | os.O_CREATE
|
||||
envFile, err := os.OpenFile(envFilePath, flags, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer envFile.Close()
|
||||
|
||||
stackFile, err := os.Open(stackFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer stackFile.Close()
|
||||
|
||||
_, err = io.Copy(envFile, stackFile)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createNetworkEnvFile(stack *portainer.Stack) error {
|
||||
networkNameSet := NewStringSet()
|
||||
|
||||
for _, filePath := range stackutils.GetStackFilePaths(stack) {
|
||||
networkNames, err := extractNetworkNames(filePath)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to extract network name")
|
||||
}
|
||||
|
||||
if networkNames == nil || networkNames.Len() == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
networkNameSet.Union(networkNames)
|
||||
}
|
||||
|
||||
for _, s := range networkNameSet.List() {
|
||||
if _, ok := os.LookupEnv(s); ok {
|
||||
networkNameSet.Remove(s)
|
||||
}
|
||||
}
|
||||
|
||||
if networkNameSet.Len() == 0 && stack.Env == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
envfile, err := os.OpenFile(path.Join(stack.ProjectPath, ".env"),
|
||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to open env file")
|
||||
}
|
||||
|
||||
defer envfile.Close()
|
||||
|
||||
var scanEnvSettingFunc = func(name string) (string, bool) {
|
||||
if stack.Env != nil {
|
||||
for _, v := range stack.Env {
|
||||
if name == v.Name {
|
||||
return v.Value, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
for _, s := range networkNameSet.List() {
|
||||
if _, ok := scanEnvSettingFunc(s); !ok {
|
||||
stack.Env = append(stack.Env, portainer.Pair{
|
||||
Name: s,
|
||||
Value: "None",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if stack.Env != nil {
|
||||
for _, v := range stack.Env {
|
||||
envfile.WriteString(
|
||||
fmt.Sprintf("%s=%s\n", v.Name, v.Value))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func extractNetworkNames(filePath string) (StringSet, error) {
|
||||
if info, err := os.Stat(filePath); errors.Is(err,
|
||||
os.ErrNotExist) || info.IsDir() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
stackFileContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to open yaml file")
|
||||
}
|
||||
|
||||
config, err := loader.ParseYAML(stackFileContent)
|
||||
if err != nil {
|
||||
// invalid stack file
|
||||
return nil, errors.Wrap(err, "invalid stack file")
|
||||
}
|
||||
|
||||
var version string
|
||||
if _, ok := config["version"]; ok {
|
||||
version, _ = config["version"].(string)
|
||||
}
|
||||
|
||||
var networks map[string]interface{}
|
||||
if value, ok := config["networks"]; ok {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if networks, ok = value.(map[string]interface{}); !ok {
|
||||
return nil, nil
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
networkContent, err := loader.LoadNetworks(networks, version)
|
||||
if err != nil {
|
||||
return nil, nil // skip the error
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^\$\{?([^\}]+)\}?$`)
|
||||
networkNames := NewStringSet()
|
||||
|
||||
for _, v := range networkContent {
|
||||
matched := re.FindAllStringSubmatch(v.Name, -1)
|
||||
if matched != nil && matched[0] != nil {
|
||||
if strings.Contains(matched[0][1], ":-") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(matched[0][1], "?") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(matched[0][1], "-") {
|
||||
continue
|
||||
}
|
||||
|
||||
networkNames.Add(matched[0][1])
|
||||
}
|
||||
}
|
||||
|
||||
if networkNames.Len() == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return networkNames, nil
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package exec
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -10,9 +11,6 @@ import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const composeFile = `version: "3.9"
|
||||
@@ -43,8 +41,6 @@ func setup(t *testing.T) (*portainer.Stack, *portainer.Endpoint) {
|
||||
|
||||
func Test_UpAndDown(t *testing.T) {
|
||||
|
||||
testhelpers.IntegrationTest(t)
|
||||
|
||||
stack, endpoint := setup(t)
|
||||
|
||||
w, err := NewComposeStackManager("", "", nil)
|
||||
@@ -78,7 +74,7 @@ func containerExists(containerName string) bool {
|
||||
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed to list containers")
|
||||
log.Fatalf("failed to list containers: %s", err)
|
||||
}
|
||||
|
||||
return strings.Contains(string(out), containerName)
|
||||
|
||||
@@ -65,22 +65,56 @@ func Test_createEnvFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_createEnvFile_mergesDefultAndInplaceEnvVars(t *testing.T) {
|
||||
func Test_createNetworkEnvFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
os.WriteFile(path.Join(dir, ".env"), []byte("VAR1=VAL1\nVAR2=VAL2\n"), 0600)
|
||||
stack := &portainer.Stack{
|
||||
buf := []byte(`
|
||||
version: '3.6'
|
||||
services:
|
||||
nginx-example:
|
||||
image: nginx:latest
|
||||
networks:
|
||||
default:
|
||||
name: ${test}
|
||||
driver: bridge
|
||||
`)
|
||||
if err := ioutil.WriteFile(path.Join(dir,
|
||||
"docker-compose.yml"), buf, 0644); err != nil {
|
||||
t.Fatalf("Failed to create yaml file: %s", err)
|
||||
}
|
||||
|
||||
stackWithoutEnv := &portainer.Stack{
|
||||
ProjectPath: dir,
|
||||
EntryPoint: "docker-compose.yml",
|
||||
Env: []portainer.Pair{},
|
||||
}
|
||||
|
||||
if err := createNetworkEnvFile(stackWithoutEnv); err != nil {
|
||||
t.Fatalf("Failed to create network env file: %s", err)
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(path.Join(dir, ".env"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read network env file: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "test=None\n", string(content))
|
||||
|
||||
stackWithEnv := &portainer.Stack{
|
||||
ProjectPath: dir,
|
||||
EntryPoint: "docker-compose.yml",
|
||||
Env: []portainer.Pair{
|
||||
{Name: "VAR1", Value: "NEW_VAL1"},
|
||||
{Name: "VAR3", Value: "VAL3"},
|
||||
{Name: "test", Value: "test-value"},
|
||||
},
|
||||
}
|
||||
result, err := createEnvFile(stack)
|
||||
assert.Equal(t, "stack.env", result)
|
||||
assert.NoError(t, err)
|
||||
assert.FileExists(t, path.Join(dir, "stack.env"))
|
||||
f, _ := os.Open(path.Join(dir, "stack.env"))
|
||||
content, _ := ioutil.ReadAll(f)
|
||||
|
||||
assert.Equal(t, []byte("VAR1=VAL1\nVAR2=VAL2\n\nVAR1=NEW_VAL1\nVAR3=VAL3\n"), content)
|
||||
if err := createNetworkEnvFile(stackWithEnv); err != nil {
|
||||
t.Fatalf("Failed to create network env file: %s", err)
|
||||
}
|
||||
|
||||
content, err = ioutil.ReadFile(path.Join(dir, ".env"))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read network env file: %s", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "test=test-value\n", string(content))
|
||||
}
|
||||
|
||||
@@ -89,7 +89,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, pullImage bool, endpoint *portainer.Endpoint) error {
|
||||
func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, endpoint *portainer.Endpoint) error {
|
||||
filePaths := stackutils.GetStackFilePaths(stack)
|
||||
command, args, err := manager.prepareDockerCommandAndArgs(manager.binaryPath, manager.configPath, endpoint)
|
||||
if err != nil {
|
||||
@@ -101,9 +101,6 @@ func (manager *SwarmStackManager) Deploy(stack *portainer.Stack, prune bool, pul
|
||||
} else {
|
||||
args = append(args, "stack", "deploy", "--with-registry-auth")
|
||||
}
|
||||
if !pullImage {
|
||||
args = append(args, "--resolve-image=never")
|
||||
}
|
||||
|
||||
args = configureFilePaths(args, filePaths)
|
||||
args = append(args, stack.Name)
|
||||
|
||||
@@ -11,13 +11,17 @@ import (
|
||||
)
|
||||
|
||||
func Test_copyFile_returnsError_whenSourceDoesNotExist(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutil.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
err := copyFile("does-not-exist", tmpdir)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_copyFile_shouldMakeAbackup(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutil.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
content := []byte("content")
|
||||
ioutil.WriteFile(path.Join(tmpdir, "origin"), content, 0600)
|
||||
|
||||
@@ -29,7 +33,8 @@ func Test_copyFile_shouldMakeAbackup(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_CopyDir_shouldCopyAllFilesAndDirectories(t *testing.T) {
|
||||
destination := t.TempDir()
|
||||
destination, _ := ioutil.TempDir("", "destination")
|
||||
defer os.RemoveAll(destination)
|
||||
err := CopyDir("./testdata/copy_test", destination, true)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -39,7 +44,8 @@ func Test_CopyDir_shouldCopyAllFilesAndDirectories(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_CopyDir_shouldCopyOnlyDirContents(t *testing.T) {
|
||||
destination := t.TempDir()
|
||||
destination, _ := ioutil.TempDir("", "destination")
|
||||
defer os.RemoveAll(destination)
|
||||
err := CopyDir("./testdata/copy_test", destination, false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -49,7 +55,9 @@ func Test_CopyDir_shouldCopyOnlyDirContents(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_CopyPath_shouldSkipWhenNotExist(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutil.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
err := CopyPath("does-not-exists", tmpdir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
@@ -57,7 +65,9 @@ func Test_CopyPath_shouldSkipWhenNotExist(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_CopyPath_shouldCopyFile(t *testing.T) {
|
||||
tmpdir := t.TempDir()
|
||||
tmpdir, _ := ioutil.TempDir("", "backup")
|
||||
defer os.RemoveAll(tmpdir)
|
||||
|
||||
content := []byte("content")
|
||||
ioutil.WriteFile(path.Join(tmpdir, "file"), content, 0600)
|
||||
|
||||
@@ -71,7 +81,8 @@ func Test_CopyPath_shouldCopyFile(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_CopyPath_shouldCopyDir(t *testing.T) {
|
||||
destination := t.TempDir()
|
||||
destination, _ := ioutil.TempDir("", "destination")
|
||||
defer os.RemoveAll(destination)
|
||||
err := CopyPath("./testdata/copy_test", destination)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
||||
@@ -234,58 +234,6 @@ func (service *Service) StoreStackFileFromBytes(stackIdentifier, fileName string
|
||||
return service.wrapFileStore(stackStorePath), nil
|
||||
}
|
||||
|
||||
// UpdateStoreStackFileFromBytes makes stack file backup and updates a new file from bytes.
|
||||
// It returns the path to the folder where the file is stored.
|
||||
func (service *Service) UpdateStoreStackFileFromBytes(stackIdentifier, fileName string, data []byte) (string, error) {
|
||||
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)
|
||||
composeFilePath := JoinPaths(stackStorePath, fileName)
|
||||
err := service.createBackupFileInStore(composeFilePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
r := bytes.NewReader(data)
|
||||
err = service.createFileInStore(composeFilePath, r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return service.wrapFileStore(stackStorePath), nil
|
||||
}
|
||||
|
||||
// RemoveStackFileBackup removes the stack file backup in the ComposeStorePath.
|
||||
func (service *Service) RemoveStackFileBackup(stackIdentifier, fileName string) error {
|
||||
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)
|
||||
composeFilePath := JoinPaths(stackStorePath, fileName)
|
||||
|
||||
return service.removeBackupFileInStore(composeFilePath)
|
||||
}
|
||||
|
||||
// RollbackStackFile rollbacks the stack file backup in the ComposeStorePath.
|
||||
func (service *Service) RollbackStackFile(stackIdentifier, fileName string) error {
|
||||
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)
|
||||
composeFilePath := JoinPaths(stackStorePath, fileName)
|
||||
path := service.wrapFileStore(composeFilePath)
|
||||
backupPath := fmt.Sprintf("%s.bak", path)
|
||||
|
||||
exists, err := service.FileExists(backupPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
// keep the updated/failed stack file
|
||||
return nil
|
||||
}
|
||||
|
||||
err = service.Copy(backupPath, path, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(backupPath)
|
||||
}
|
||||
|
||||
// GetEdgeStackProjectPath returns the absolute path on the FS for a edge stack based
|
||||
// on its identifier.
|
||||
func (service *Service) GetEdgeStackProjectPath(edgeStackIdentifier string) string {
|
||||
@@ -499,31 +447,6 @@ func (service *Service) createFileInStore(filePath string, r io.Reader) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// createBackupFileInStore makes a copy in the file store.
|
||||
func (service *Service) createBackupFileInStore(filePath string) error {
|
||||
path := service.wrapFileStore(filePath)
|
||||
backupPath := fmt.Sprintf("%s.bak", path)
|
||||
|
||||
return service.Copy(path, backupPath, true)
|
||||
}
|
||||
|
||||
// removeBackupFileInStore removes the copy in the file store.
|
||||
func (service *Service) removeBackupFileInStore(filePath string) error {
|
||||
path := service.wrapFileStore(filePath)
|
||||
backupPath := fmt.Sprintf("%s.bak", path)
|
||||
|
||||
exists, err := service.FileExists(backupPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if exists {
|
||||
return os.Remove(backupPath)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) createPEMFileInStore(content []byte, fileType, filePath string) error {
|
||||
path := service.wrapFileStore(filePath)
|
||||
block := &pem.Block{Type: fileType, Bytes: content}
|
||||
|
||||
@@ -43,7 +43,7 @@ func testHelperFileExists_fileExists(t *testing.T, checker func(path string) (bo
|
||||
}
|
||||
|
||||
func testHelperFileExists_fileNotExists(t *testing.T, checker func(path string) (bool, error)) {
|
||||
filePath := path.Join(t.TempDir(), fmt.Sprintf("%s%d", t.Name(), rand.Int()))
|
||||
filePath := path.Join(os.TempDir(), fmt.Sprintf("%s%d", t.Name(), rand.Int()))
|
||||
|
||||
err := os.RemoveAll(filePath)
|
||||
assert.NoError(t, err, "RemoveAll should not fail")
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func createService(t *testing.T) *Service {
|
||||
dataStorePath := path.Join(t.TempDir(), t.Name())
|
||||
dataStorePath := path.Join(os.TempDir(), t.Name())
|
||||
|
||||
service, err := NewService(dataStorePath, "")
|
||||
assert.NoError(t, err, "NewService should not fail")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user