Compare commits
26 Commits
fix/EE-637
...
feat/EE-45
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf5bec7f2d | ||
|
|
5ce938123d | ||
|
|
8d7fcde4cb | ||
|
|
b5a05a2e12 | ||
|
|
5acb8edbd0 | ||
|
|
ee32f513e1 | ||
|
|
e819a6ad74 | ||
|
|
7647feb395 | ||
|
|
d60b187d6c | ||
|
|
1e9250eccf | ||
|
|
1f38623b60 | ||
|
|
4205d871de | ||
|
|
357cb66fbf | ||
|
|
3396f1ca91 | ||
|
|
4ff7135b73 | ||
|
|
479842d5ef | ||
|
|
6ab6ffa68e | ||
|
|
e5af8cb58a | ||
|
|
cc1b8c05d4 | ||
|
|
74615bff91 | ||
|
|
22d960e51b | ||
|
|
8fa1875891 | ||
|
|
3d313be013 | ||
|
|
52142c3151 | ||
|
|
8fd67d8320 | ||
|
|
525efdc035 |
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Portainer Business Edition - Get 3 nodes free
|
||||
url: https://www.portainer.io/take-3
|
||||
about: Portainer Business Edition has more features, more support and you can now get 3 nodes free for as long as you want.
|
||||
- name: Portainer Business Edition - Get 5 nodes free
|
||||
url: https://portainer.io/pricing/take5
|
||||
about: Portainer Business Edition has more features, more support and you can now get 5 nodes free for as long as you want.
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -41,6 +41,6 @@ jobs:
|
||||
- name: GolangCI-Lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
with:
|
||||
version: v1.54.1
|
||||
version: latest
|
||||
working-directory: api
|
||||
args: --timeout=10m -c .golangci.yaml
|
||||
|
||||
22
.github/workflows/test.yaml
vendored
22
.github/workflows/test.yaml
vendored
@@ -14,12 +14,16 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
run: yarn jest --maxWorkers=2
|
||||
test-server:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19.5
|
||||
- name: Run tests
|
||||
run: make test-server
|
||||
# test-server:
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# GOPRIVATE: "github.com/portainer"
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: actions/setup-go@v3
|
||||
# with:
|
||||
# go-version: '1.18'
|
||||
# - name: Run tests
|
||||
# run: |
|
||||
# cd api
|
||||
# go test ./...
|
||||
|
||||
2
.github/workflows/validate-openapi-spec.yaml
vendored
2
.github/workflows/validate-openapi-spec.yaml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
run: cd ./api && go get -t -v -d ./...
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18'
|
||||
node-version: '14'
|
||||
cache: 'yarn'
|
||||
- run: yarn --frozen-lockfile
|
||||
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,7 +11,7 @@ storybook-static
|
||||
*.DS_Store
|
||||
|
||||
.eslintcache
|
||||
__debug_bin*
|
||||
__debug_bin
|
||||
|
||||
api/docs
|
||||
.idea
|
||||
|
||||
58
.storybook/main.js
Normal file
58
.storybook/main.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
stories: ['../app/**/*.stories.mdx', '../app/**/*.stories.@(ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
{
|
||||
name: '@storybook/addon-postcss',
|
||||
options: {
|
||||
cssLoaderOptions: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
localIdentName: '[path][name]__[local]',
|
||||
auto: true,
|
||||
exportLocalsConvention: 'camelCaseOnly',
|
||||
},
|
||||
},
|
||||
postcssLoaderOptions: {
|
||||
implementation: require('postcss'),
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
webpackFinal: (config) => {
|
||||
config.resolve.plugins = [
|
||||
...(config.resolve.plugins || []),
|
||||
new TsconfigPathsPlugin({
|
||||
extensions: config.resolve.extensions,
|
||||
}),
|
||||
];
|
||||
|
||||
const svgRule = config.module.rules.find((rule) => rule.test && typeof rule.test.test === 'function' && rule.test.test('.svg'));
|
||||
svgRule.test = new RegExp(svgRule.test.source.replace('svg|', ''));
|
||||
|
||||
config.module.rules.unshift({
|
||||
test: /\.svg$/i,
|
||||
type: 'asset',
|
||||
resourceQuery: { not: [/c/] }, // exclude react component if *.svg?url
|
||||
});
|
||||
|
||||
config.module.rules.unshift({
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.(js|ts)(x)?$/,
|
||||
resourceQuery: /c/, // *.svg?c
|
||||
use: [{ loader: '@svgr/webpack', options: { icon: true } }],
|
||||
});
|
||||
|
||||
return config;
|
||||
},
|
||||
core: {
|
||||
builder: 'webpack5',
|
||||
},
|
||||
staticDirs: ['./public'],
|
||||
typescript: {
|
||||
reactDocgen: 'react-docgen-typescript-plugin',
|
||||
},
|
||||
};
|
||||
@@ -1,95 +0,0 @@
|
||||
import { StorybookConfig } from '@storybook/react-webpack5';
|
||||
|
||||
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||
import { Configuration } from 'webpack';
|
||||
import postcss from 'postcss';
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../app/**/*.stories.@(ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
{
|
||||
name: '@storybook/addon-styling',
|
||||
options: {
|
||||
cssLoaderOptions: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
localIdentName: '[path][name]__[local]',
|
||||
auto: true,
|
||||
exportLocalsConvention: 'camelCaseOnly',
|
||||
},
|
||||
},
|
||||
postCss: {
|
||||
implementation: postcss,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
webpackFinal: (config) => {
|
||||
const rules = config?.module?.rules || [];
|
||||
|
||||
const imageRule = rules.find((rule) => {
|
||||
const test = (rule as { test: RegExp }).test;
|
||||
|
||||
if (!test) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return test.test('.svg');
|
||||
}) as { [key: string]: any };
|
||||
|
||||
imageRule.exclude = /\.svg$/;
|
||||
|
||||
rules.unshift({
|
||||
test: /\.svg$/i,
|
||||
type: 'asset',
|
||||
resourceQuery: {
|
||||
not: [/c/],
|
||||
}, // exclude react component if *.svg?url
|
||||
});
|
||||
|
||||
rules.unshift({
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.(js|ts)(x)?$/,
|
||||
resourceQuery: /c/,
|
||||
// *.svg?c
|
||||
use: [
|
||||
{
|
||||
loader: '@svgr/webpack',
|
||||
options: {
|
||||
icon: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
return {
|
||||
...config,
|
||||
resolve: {
|
||||
...config.resolve,
|
||||
plugins: [
|
||||
...(config.resolve?.plugins || []),
|
||||
new TsconfigPathsPlugin({
|
||||
extensions: config.resolve?.extensions,
|
||||
}),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
...config.module,
|
||||
rules,
|
||||
},
|
||||
} satisfies Configuration;
|
||||
},
|
||||
staticDirs: ['./public'],
|
||||
typescript: {
|
||||
reactDocgen: 'react-docgen-typescript',
|
||||
},
|
||||
framework: {
|
||||
name: '@storybook/react-webpack5',
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: true,
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
15
README.md
15
README.md
@@ -9,7 +9,7 @@ Portainer consists of a single container that can run on any cluster. It can be
|
||||
**Portainer Business Edition** builds on the open-source base and includes a range of advanced features and functions (like RBAC and Support) that are specific to the needs of business users.
|
||||
|
||||
- [Compare Portainer CE and Compare Portainer BE](https://portainer.io/products)
|
||||
- [Take3 – get 3 free nodes of Portainer Business for as long as you want them](https://www.portainer.io/take-3)
|
||||
- [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)
|
||||
|
||||
## Latest Version
|
||||
@@ -21,8 +21,8 @@ Portainer CE is updated regularly. We aim to do an update release every couple o
|
||||
## Getting started
|
||||
|
||||
- [Deploy Portainer](https://docs.portainer.io/start/install)
|
||||
- [Documentation](https://docs.portainer.io)
|
||||
- [Contribute to the project](https://docs.portainer.io/contribute/contribute)
|
||||
- [Documentation](https://documentation.portainer.io)
|
||||
- [Contribute to the project](https://documentation.portainer.io/contributing/instructions/)
|
||||
|
||||
## Features & Functions
|
||||
|
||||
@@ -30,22 +30,23 @@ View [this](https://www.portainer.io/products) table to see all of the Portainer
|
||||
|
||||
- [Portainer CE for Docker / Docker Swarm](https://www.portainer.io/solutions/docker)
|
||||
- [Portainer CE for Kubernetes](https://www.portainer.io/solutions/kubernetes-ui)
|
||||
- [Portainer CE for Azure ACI](https://www.portainer.io/solutions/serverless-containers)
|
||||
|
||||
## Getting help
|
||||
|
||||
Portainer CE is an open source project and is supported by the community. You can buy a supported version of Portainer at portainer.io
|
||||
|
||||
Learn more about Portainer's community support channels [here.](https://www.portainer.io/get-support-for-portainer)
|
||||
Learn more about Portainer's community support channels [here.](https://www.portainer.io/community_help)
|
||||
|
||||
- Issues: https://github.com/portainer/portainer/issues
|
||||
- Slack (chat): [https://portainer.io/slack](https://portainer.io/slack)
|
||||
|
||||
You can join the Portainer Community by visiting [https://www.portainer.io/join-our-community](https://www.portainer.io/join-our-community). This will give you advance notice of events, content and other related Portainer content.
|
||||
You can join the Portainer Community by visiting community.portainer.io. This will give you advance notice of events, content and other related Portainer content.
|
||||
|
||||
## Reporting bugs and contributing
|
||||
|
||||
- Want to report a bug or request a feature? Please open [an issue](https://github.com/portainer/portainer/issues/new).
|
||||
- Want to help us build **_portainer_**? Follow our [contribution guidelines](https://docs.portainer.io/contribute/contribute) to build it locally and make a pull request.
|
||||
- Want to help us build **_portainer_**? Follow our [contribution guidelines](https://documentation.portainer.io/contributing/instructions/) to build it locally and make a pull request.
|
||||
|
||||
## Security
|
||||
|
||||
@@ -59,7 +60,7 @@ If you are a developer, and our code in this repo makes sense to you, we would l
|
||||
|
||||
**To make sure we focus our development effort in the right places we need to know which features get used most often. To give us this information we use [Matomo Analytics](https://matomo.org/), which is hosted in Germany and is fully GDPR compliant.**
|
||||
|
||||
When Portainer first starts, you are given the option to DISABLE analytics. If you **don't** choose to disable it, we collect anonymous usage as per [our privacy policy](https://www.portainer.io/privacy-policy). **Please note**, there is no personally identifiable information sent or stored at any time and we only use the data to help us improve Portainer.
|
||||
When Portainer first starts, you are given the option to DISABLE analytics. If you **don't** choose to disable it, we collect anonymous usage as per [our privacy policy](https://www.portainer.io/documentation/in-app-analytics-and-privacy-policy/). **Please note**, there is no personally identifiable information sent or stored at any time and we only use the data to help us improve Portainer.
|
||||
|
||||
## Limitations
|
||||
|
||||
|
||||
@@ -10,17 +10,15 @@ linters:
|
||||
- exportloopref
|
||||
linters-settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: 'github.com/sirupsen/logrus'
|
||||
desc: 'logging is allowed only by github.com/rs/zerolog'
|
||||
- pkg: 'golang.org/x/exp'
|
||||
desc: 'exp is not allowed'
|
||||
files:
|
||||
- '!**/*_test.go'
|
||||
- '!**/base.go'
|
||||
- '!**/base_tx.go'
|
||||
list-type: denylist
|
||||
include-go-root: true
|
||||
packages:
|
||||
- github.com/sirupsen/logrus
|
||||
- golang.org/x/exp
|
||||
packages-with-error-message:
|
||||
- github.com/sirupsen/logrus: 'logging is allowed only by github.com/rs/zerolog'
|
||||
ignore-file-rules:
|
||||
- '**/*_test.go'
|
||||
|
||||
# errorlint is causing a typecheck error for some reason. The go compiler will report these
|
||||
# anyway, so ignore them from the linter
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package apikey
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"io"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
@@ -15,3 +18,13 @@ type APIKeyService interface {
|
||||
DeleteAPIKey(apiKeyID portainer.APIKeyID) error
|
||||
InvalidateUserKeyCache(userId portainer.UserID) bool
|
||||
}
|
||||
|
||||
// generateRandomKey generates a random key of specified length
|
||||
// source: https://github.com/gorilla/securecookie/blob/master/securecookie.go#L515
|
||||
func generateRandomKey(length int) []byte {
|
||||
k := make([]byte, length)
|
||||
if _, err := io.ReadFull(rand.Reader, k); err != nil {
|
||||
return nil
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package apikey
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/api/internal/securecookie"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -34,7 +33,7 @@ func Test_generateRandomKey(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := securecookie.GenerateRandomKey(tt.wantLenth)
|
||||
got := generateRandomKey(tt.wantLenth)
|
||||
is.Equal(tt.wantLenth, len(got))
|
||||
})
|
||||
}
|
||||
@@ -42,7 +41,7 @@ func Test_generateRandomKey(t *testing.T) {
|
||||
t.Run("Generated keys are unique", func(t *testing.T) {
|
||||
keys := make(map[string]bool)
|
||||
for i := 0; i < 100; i++ {
|
||||
key := securecookie.GenerateRandomKey(8)
|
||||
key := generateRandomKey(8)
|
||||
_, ok := keys[string(key)]
|
||||
is.False(ok)
|
||||
keys[string(key)] = true
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/securecookie"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@@ -40,7 +39,7 @@ func (a *apiKeyService) HashRaw(rawKey string) []byte {
|
||||
// GenerateApiKey generates a raw API key for a user (for one-time display).
|
||||
// The generated API key is stored in the cache and database.
|
||||
func (a *apiKeyService) GenerateApiKey(user portainer.User, description string) (string, *portainer.APIKey, error) {
|
||||
randKey := securecookie.GenerateRandomKey(32)
|
||||
randKey := generateRandomKey(32)
|
||||
encodedRawAPIKey := base64.StdEncoding.EncodeToString(randKey)
|
||||
prefixedAPIKey := portainerAPIKeyPrefix + encodedRawAPIKey
|
||||
|
||||
@@ -54,7 +53,7 @@ func (a *apiKeyService) GenerateApiKey(user portainer.User, description string)
|
||||
Digest: hashDigest,
|
||||
}
|
||||
|
||||
err := a.apiKeyRepository.Create(apiKey)
|
||||
err := a.apiKeyRepository.CreateAPIKey(apiKey)
|
||||
if err != nil {
|
||||
return "", nil, errors.Wrap(err, "Unable to create API key")
|
||||
}
|
||||
@@ -67,7 +66,7 @@ func (a *apiKeyService) GenerateApiKey(user portainer.User, description string)
|
||||
|
||||
// GetAPIKey returns an API key by its ID.
|
||||
func (a *apiKeyService) GetAPIKey(apiKeyID portainer.APIKeyID) (*portainer.APIKey, error) {
|
||||
return a.apiKeyRepository.Read(apiKeyID)
|
||||
return a.apiKeyRepository.GetAPIKey(apiKeyID)
|
||||
}
|
||||
|
||||
// GetAPIKeys returns all the API keys associated to a user.
|
||||
@@ -89,7 +88,7 @@ func (a *apiKeyService) GetDigestUserAndKey(digest []byte) (portainer.User, port
|
||||
return portainer.User{}, portainer.APIKey{}, errors.Wrap(err, "Unable to retrieve API key")
|
||||
}
|
||||
|
||||
user, err := a.userRepository.Read(apiKey.UserID)
|
||||
user, err := a.userRepository.User(apiKey.UserID)
|
||||
if err != nil {
|
||||
return portainer.User{}, portainer.APIKey{}, errors.Wrap(err, "Unable to retrieve digest user")
|
||||
}
|
||||
@@ -107,20 +106,20 @@ func (a *apiKeyService) UpdateAPIKey(apiKey *portainer.APIKey) error {
|
||||
return errors.Wrap(err, "Unable to retrieve API key")
|
||||
}
|
||||
a.cache.Set(apiKey.Digest, user, *apiKey)
|
||||
return a.apiKeyRepository.Update(apiKey.ID, apiKey)
|
||||
return a.apiKeyRepository.UpdateAPIKey(apiKey)
|
||||
}
|
||||
|
||||
// DeleteAPIKey deletes an API key and removes the digest/api-key entry from the cache.
|
||||
func (a *apiKeyService) DeleteAPIKey(apiKeyID portainer.APIKeyID) error {
|
||||
// get api-key digest to remove from cache
|
||||
apiKey, err := a.apiKeyRepository.Read(apiKeyID)
|
||||
apiKey, err := a.apiKeyRepository.GetAPIKey(apiKeyID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("Unable to retrieve API key: %d", apiKeyID))
|
||||
}
|
||||
|
||||
// delete the user/api-key from cache
|
||||
a.cache.Delete(apiKey.Digest)
|
||||
return a.apiKeyRepository.Delete(apiKeyID)
|
||||
return a.apiKeyRepository.DeleteAPIKey(apiKeyID)
|
||||
}
|
||||
|
||||
func (a *apiKeyService) InvalidateUserKeyCache(userId portainer.UserID) bool {
|
||||
|
||||
@@ -30,7 +30,6 @@ var filesToBackup = []string{
|
||||
"portainer.key",
|
||||
"portainer.pub",
|
||||
"tls",
|
||||
"chisel",
|
||||
}
|
||||
|
||||
// Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
|
||||
@@ -86,9 +85,9 @@ func backupDb(backupDirPath string, datastore dataservices.DataStore) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = datastore.BackupTo(backupWriter); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err = datastore.BackupTo(backupWriter); err != nil {
|
||||
// return err
|
||||
// }
|
||||
return backupWriter.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/api/archive"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/filesystem"
|
||||
"github.com/portainer/portainer/api/http/offlinegate"
|
||||
@@ -98,16 +97,17 @@ func restoreFiles(srcDir string, destinationDir string) error {
|
||||
// TODO: This is very boltdb module specific once again due to the filename. Move to bolt module? Refactor for another day
|
||||
|
||||
// Prevent the possibility of having both databases. Remove any default new instance
|
||||
os.Remove(filepath.Join(destinationDir, boltdb.DatabaseFileName))
|
||||
os.Remove(filepath.Join(destinationDir, boltdb.EncryptedDatabaseFileName))
|
||||
// os.Remove(filepath.Join(destinationDir, boltdb.DatabaseFileName))
|
||||
// os.Remove(filepath.Join(destinationDir, boltdb.EncryptedDatabaseFileName))
|
||||
|
||||
// Now copy the database. It'll be either portainer.db or portainer.edb
|
||||
// // Now copy the database. It'll be either portainer.db or portainer.edb
|
||||
|
||||
// Note: CopyPath does not return an error if the source file doesn't exist
|
||||
err := filesystem.CopyPath(filepath.Join(srcDir, boltdb.EncryptedDatabaseFileName), destinationDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Note: CopyPath does not return an error if the source file doesn't exist
|
||||
// err := filesystem.CopyPath(filepath.Join(srcDir, boltdb.EncryptedDatabaseFileName), destinationDir)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
return filesystem.CopyPath(filepath.Join(srcDir, boltdb.DatabaseFileName), destinationDir)
|
||||
// return filesystem.CopyPath(filepath.Join(srcDir, boltdb.DatabaseFileName), destinationDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
chshare "github.com/jpillora/chisel/share"
|
||||
)
|
||||
|
||||
var one = new(big.Int).SetInt64(1)
|
||||
|
||||
// GenerateGo119CompatibleKey This function is basically copied from chshare.GenerateKey.
|
||||
func GenerateGo119CompatibleKey(seed string) ([]byte, error) {
|
||||
r := chshare.NewDetermRand([]byte(seed))
|
||||
priv, err := ecdsaGenerateKey(elliptic.P256(), r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b, err := x509.MarshalECPrivateKey(priv)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to marshal ECDSA private key: %w", err)
|
||||
}
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}), nil
|
||||
}
|
||||
|
||||
// This function is copied from Go1.19
|
||||
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
|
||||
params := c.Params()
|
||||
// Note that for P-521 this will actually be 63 bits more than the order, as
|
||||
// division rounds down, but the extra bit is inconsequential.
|
||||
b := make([]byte, params.N.BitLen()/8+8)
|
||||
_, err = io.ReadFull(rand, b)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
k = new(big.Int).SetBytes(b)
|
||||
n := new(big.Int).Sub(params.N, one)
|
||||
k.Mod(k, n)
|
||||
k.Add(k, one)
|
||||
return
|
||||
}
|
||||
|
||||
// This function is copied from Go1.19
|
||||
func ecdsaGenerateKey(c elliptic.Curve, rand io.Reader) (*ecdsa.PrivateKey, error) {
|
||||
k, err := randFieldElement(c, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv := new(ecdsa.PrivateKey)
|
||||
priv.PublicKey.Curve = c
|
||||
priv.D = k
|
||||
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||
return priv, nil
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateGo119CompatibleKey(t *testing.T) {
|
||||
type args struct {
|
||||
seed string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []byte
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Generate Go 1.19 compatible private key with a given seed",
|
||||
args: args{seed: "94qh17MCIk8BOkiI"},
|
||||
want: []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIHeohwk0Gy3RHVVViaHz7pz/HOiqA7fkv1FTM3mGgfT3oAoGCCqGSM49\nAwEHoUQDQgAEN7riX06xDsLNPuUmOvYFluNEakcFwZZRVvOcIYk/9VYnanDzW0Km\n8/BUUiKyJDuuGdS4fj9SlQ4iL8yBK01uKg==\n-----END EC PRIVATE KEY-----\n"),
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := GenerateGo119CompatibleKey(tt.args.seed)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("GenerateGo119CompatibleKey() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("GenerateGo119CompatibleKey()\ngot: Z %v\nwant: %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ func (service *Service) AddEdgeJob(endpoint *portainer.Endpoint, edgeJob *portai
|
||||
for idx, existingJob := range tunnel.Jobs {
|
||||
if existingJob.ID == edgeJob.ID {
|
||||
existingJobIndex = idx
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
"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/jpillora/chisel/share/ccrypto"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -36,16 +36,14 @@ type Service struct {
|
||||
shutdownCtx context.Context
|
||||
ProxyManager *proxy.Manager
|
||||
mu sync.Mutex
|
||||
fileService portainer.FileService
|
||||
}
|
||||
|
||||
// NewService returns a pointer to a new instance of Service
|
||||
func NewService(dataStore dataservices.DataStore, shutdownCtx context.Context, fileService portainer.FileService) *Service {
|
||||
func NewService(dataStore dataservices.DataStore, shutdownCtx context.Context) *Service {
|
||||
return &Service{
|
||||
tunnelDetailsMap: make(map[portainer.EndpointID]*portainer.TunnelDetails),
|
||||
dataStore: dataStore,
|
||||
shutdownCtx: shutdownCtx,
|
||||
fileService: fileService,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,11 +73,10 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Float64("max_alive_minutes", maxAlive.Minutes()).
|
||||
Msg("KeepTunnelAlive: start")
|
||||
Msg("start")
|
||||
|
||||
maxAliveTicker := time.NewTicker(maxAlive)
|
||||
defer maxAliveTicker.Stop()
|
||||
|
||||
pingTicker := time.NewTicker(tunnelCleanupInterval)
|
||||
defer pingTicker.Stop()
|
||||
|
||||
@@ -92,13 +89,13 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Err(err).
|
||||
Msg("KeepTunnelAlive: ping agent")
|
||||
Msg("ping agent")
|
||||
}
|
||||
case <-maxAliveTicker.C:
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Float64("timeout_minutes", maxAlive.Minutes()).
|
||||
Msg("KeepTunnelAlive: tunnel keep alive timeout")
|
||||
Msg("tunnel keep alive timeout")
|
||||
|
||||
return
|
||||
case <-ctx.Done():
|
||||
@@ -106,7 +103,7 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
|
||||
log.Debug().
|
||||
Int("endpoint_id", int(endpointID)).
|
||||
Err(err).
|
||||
Msg("KeepTunnelAlive: tunnel stop")
|
||||
Msg("tunnel stop")
|
||||
|
||||
return
|
||||
}
|
||||
@@ -120,15 +117,14 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
|
||||
// It starts the tunnel status verification process in the background.
|
||||
// The snapshotter is used in the tunnel status verification process.
|
||||
func (service *Service) StartTunnelServer(addr, port string, snapshotService portainer.SnapshotService) error {
|
||||
privateKeyFile, err := service.retrievePrivateKeyFile()
|
||||
|
||||
keySeed, err := service.retrievePrivateKeySeed()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := &chserver.Config{
|
||||
Reverse: true,
|
||||
PrivateKeyFile: privateKeyFile,
|
||||
Reverse: true,
|
||||
KeySeed: keySeed,
|
||||
}
|
||||
|
||||
chiselServer, err := chserver.NewServer(config)
|
||||
@@ -164,41 +160,26 @@ func (service *Service) StopTunnelServer() error {
|
||||
return service.chiselServer.Close()
|
||||
}
|
||||
|
||||
func (service *Service) retrievePrivateKeyFile() (string, error) {
|
||||
privateKeyFile := service.fileService.GetDefaultChiselPrivateKeyPath()
|
||||
func (service *Service) retrievePrivateKeySeed() (string, error) {
|
||||
var serverInfo *portainer.TunnelServerInfo
|
||||
|
||||
exist, _ := service.fileService.FileExists(privateKeyFile)
|
||||
if !exist {
|
||||
log.Debug().
|
||||
Str("private-key", privateKeyFile).
|
||||
Msg("Chisel private key file does not exist")
|
||||
serverInfo, err := service.dataStore.TunnelServer().Info()
|
||||
if service.dataStore.IsErrObjectNotFound(err) {
|
||||
keySeed := uniuri.NewLen(16)
|
||||
|
||||
privateKey, err := ccrypto.GenerateKey("")
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to generate chisel private key")
|
||||
return "", err
|
||||
serverInfo = &portainer.TunnelServerInfo{
|
||||
PrivateKeySeed: keySeed,
|
||||
}
|
||||
|
||||
err = service.fileService.StoreChiselPrivateKey(privateKey)
|
||||
err := service.dataStore.TunnelServer().UpdateInfo(serverInfo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to save Chisel private key to disk")
|
||||
return "", err
|
||||
} else {
|
||||
log.Info().
|
||||
Str("private-key", privateKeyFile).
|
||||
Msg("Generated a new Chisel private key file")
|
||||
}
|
||||
} else {
|
||||
log.Info().
|
||||
Str("private-key", privateKeyFile).
|
||||
Msg("Found Chisel private key file on disk")
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return privateKeyFile, nil
|
||||
return serverInfo.PrivateKeySeed, nil
|
||||
}
|
||||
|
||||
func (service *Service) startTunnelVerificationLoop() {
|
||||
@@ -290,7 +271,14 @@ func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tun
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint.URL = fmt.Sprintf("tcp://127.0.0.1:%d", tunnelPort)
|
||||
endpointURL := endpoint.URL
|
||||
|
||||
return service.snapshotService.SnapshotEndpoint(endpoint)
|
||||
endpoint.URL = fmt.Sprintf("tcp://127.0.0.1:%d", tunnelPort)
|
||||
err = service.snapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
endpoint.URL = endpointURL
|
||||
return service.dataStore.Endpoint().UpdateEndpoint(endpoint.ID, endpoint)
|
||||
}
|
||||
|
||||
@@ -126,10 +126,7 @@ func (service *Service) SetTunnelStatusToIdle(endpointID portainer.EndpointID) {
|
||||
credentials := tunnel.Credentials
|
||||
if credentials != "" {
|
||||
tunnel.Credentials = ""
|
||||
|
||||
if service.chiselServer != nil {
|
||||
service.chiselServer.DeleteUser(strings.Split(credentials, ":")[0])
|
||||
}
|
||||
service.chiselServer.DeleteUser(strings.Split(credentials, ":")[0])
|
||||
}
|
||||
|
||||
service.ProxyManager.DeleteEndpointProxy(endpointID)
|
||||
@@ -164,12 +161,9 @@ func (service *Service) SetTunnelStatusToRequired(endpointID portainer.EndpointI
|
||||
|
||||
username, password := generateRandomCredentials()
|
||||
authorizedRemote := fmt.Sprintf("^R:0.0.0.0:%d$", tunnel.Port)
|
||||
|
||||
if service.chiselServer != nil {
|
||||
err = service.chiselServer.AddUser(username, password, authorizedRemote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = service.chiselServer.AddUser(username, password, authorizedRemote)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
credentials, err := encryptCredentials(username, password, endpoint.EdgeID)
|
||||
|
||||
@@ -49,7 +49,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
|
||||
SSL: kingpin.Flag("ssl", "Secure Portainer instance using SSL (deprecated)").Default(defaultSSL).Bool(),
|
||||
SSLCert: kingpin.Flag("sslcert", "Path to the SSL certificate used to secure the Portainer instance").String(),
|
||||
SSLKey: kingpin.Flag("sslkey", "Path to the SSL key used to secure the Portainer instance").String(),
|
||||
Rollback: kingpin.Flag("rollback", "Rollback the database to the previous backup").Bool(),
|
||||
Rollback: kingpin.Flag("rollback", "Rollback the database store to the previous version").Bool(),
|
||||
SnapshotInterval: kingpin.Flag("snapshot-interval", "Duration between each environment snapshot job").String(),
|
||||
AdminPassword: kingpin.Flag("admin-password", "Set admin password with provided hash").String(),
|
||||
AdminPasswordFile: kingpin.Flag("admin-password-file", "Path to the file containing the password for the admin user").String(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
// Confirm starts a rollback db cli application
|
||||
func Confirm(message string) (bool, error) {
|
||||
fmt.Printf("%s [y/N] ", message)
|
||||
fmt.Printf("%s [y/N]", message)
|
||||
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
type pairList []portainer.Pair
|
||||
type pairList portainer.MultiPair
|
||||
|
||||
// Set implementation for a list of portainer.Pair
|
||||
func (l *pairList) Set(value string) error {
|
||||
@@ -19,7 +19,7 @@ func (l *pairList) Set(value string) error {
|
||||
}
|
||||
p := new(portainer.Pair)
|
||||
p.Name = parts[0]
|
||||
p.Value = parts[1]
|
||||
p.Value1 = parts[1]
|
||||
*l = append(*l, *p)
|
||||
return nil
|
||||
}
|
||||
@@ -34,8 +34,8 @@ func (l *pairList) IsCumulative() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func pairs(s kingpin.Settings) (target *[]portainer.Pair) {
|
||||
target = new([]portainer.Pair)
|
||||
func pairs(s kingpin.Settings) (target *portainer.MultiPair) {
|
||||
target = new(portainer.MultiPair)
|
||||
s.SetValue((*pairList)(target))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
type pairListBool []portainer.Pair
|
||||
type pairListBool portainer.MultiPair
|
||||
|
||||
// Set implementation for a list of portainer.Pair
|
||||
func (l *pairListBool) Set(value string) error {
|
||||
@@ -18,10 +18,10 @@ func (l *pairListBool) Set(value string) error {
|
||||
parts := strings.SplitN(value, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
p.Name = parts[0]
|
||||
p.Value = "true"
|
||||
p.Value1 = "true"
|
||||
} else {
|
||||
p.Name = parts[0]
|
||||
p.Value = parts[1]
|
||||
p.Value1 = parts[1]
|
||||
}
|
||||
|
||||
*l = append(*l, *p)
|
||||
@@ -38,8 +38,8 @@ func (l *pairListBool) IsCumulative() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func BoolPairs(s kingpin.Settings) (target *[]portainer.Pair) {
|
||||
target = new([]portainer.Pair)
|
||||
func BoolPairs(s kingpin.Settings) (target *portainer.MultiPair) {
|
||||
target = new(portainer.MultiPair)
|
||||
s.SetValue((*pairListBool)(target))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
libstack "github.com/portainer/docker-compose-wrapper"
|
||||
"github.com/portainer/docker-compose-wrapper/compose"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/apikey"
|
||||
"github.com/portainer/portainer/api/build"
|
||||
@@ -16,11 +18,9 @@ import (
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/database"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/demo"
|
||||
"github.com/portainer/portainer/api/docker"
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/portainer/portainer/api/git"
|
||||
"github.com/portainer/portainer/api/hostmanagement/openamt"
|
||||
"github.com/portainer/portainer/api/http"
|
||||
"github.com/portainer/portainer/api/http/client"
|
||||
"github.com/portainer/portainer/api/http/proxy"
|
||||
kubeproxy "github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
@@ -47,8 +48,6 @@ import (
|
||||
"github.com/portainer/portainer/api/stacks/deployments"
|
||||
"github.com/portainer/portainer/pkg/featureflags"
|
||||
"github.com/portainer/portainer/pkg/libhelm"
|
||||
"github.com/portainer/portainer/pkg/libstack"
|
||||
"github.com/portainer/portainer/pkg/libstack/compose"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -79,19 +78,11 @@ func initFileService(dataStorePath string) portainer.FileService {
|
||||
}
|
||||
|
||||
func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService portainer.FileService, shutdownCtx context.Context) dataservices.DataStore {
|
||||
connection, err := database.NewDatabase("boltdb", *flags.Data, secretKey)
|
||||
connection, err := database.NewDatabase("sqlite", *flags.Data, secretKey)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating database connection")
|
||||
}
|
||||
|
||||
if bconn, ok := connection.(*boltdb.DbConnection); ok {
|
||||
bconn.MaxBatchSize = *flags.MaxBatchSize
|
||||
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")
|
||||
}
|
||||
|
||||
store := datastore.NewStore(*flags.Data, fileService, connection)
|
||||
isNew, err := store.Open()
|
||||
if err != nil {
|
||||
@@ -120,26 +111,30 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
log.Fatal().Err(err).Msg("failed generating instance id")
|
||||
}
|
||||
|
||||
migratorInstance := migrator.NewMigrator(&migrator.MigratorParameters{})
|
||||
migratorCount := migratorInstance.GetMigratorCountOfCurrentAPIVersion()
|
||||
|
||||
// from MigrateData
|
||||
v := models.Version{
|
||||
SchemaVersion: portainer.APIVersion,
|
||||
Edition: int(portainer.PortainerCE),
|
||||
InstanceID: instanceId.String(),
|
||||
MigratorCount: migratorCount,
|
||||
v := map[string]interface{}{
|
||||
models.SchemaVersionKey: portainer.APIVersion,
|
||||
models.EditionKey: int(portainer.PortainerCE),
|
||||
models.InstanceKey: instanceId.String(),
|
||||
}
|
||||
err = store.VersionService.UpdateAll(v)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed inserting version bucket")
|
||||
}
|
||||
store.VersionService.UpdateVersion(&v)
|
||||
|
||||
err = updateSettingsFromFlags(store, flags)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed updating settings from flags")
|
||||
}
|
||||
} else {
|
||||
err = store.MigrateData()
|
||||
// err = store.MigrateData()
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("failed migration")
|
||||
// }
|
||||
|
||||
err := store.PostInit()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed migration")
|
||||
log.Fatal().Err(err).Msg("postinit failed")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,17 +152,7 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
|
||||
return store
|
||||
}
|
||||
|
||||
// checkDBSchemaServerVersionMatch checks if the server version matches the db scehma version
|
||||
func checkDBSchemaServerVersionMatch(dbStore dataservices.DataStore, serverVersion string, serverEdition int) bool {
|
||||
v, err := dbStore.Version().Version()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return v.SchemaVersion == serverVersion && v.Edition == serverEdition
|
||||
}
|
||||
|
||||
func initComposeStackManager(composeDeployer libstack.Deployer, proxyManager *proxy.Manager) portainer.ComposeStackManager {
|
||||
func initComposeStackManager(composeDeployer libstack.Deployer, reverseTunnelService portainer.ReverseTunnelService, proxyManager *proxy.Manager) portainer.ComposeStackManager {
|
||||
composeWrapper, err := exec.NewComposeStackManager(composeDeployer, proxyManager)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed creating compose manager")
|
||||
@@ -362,6 +347,143 @@ func initKeyPair(fileService portainer.FileService, signatureService portainer.D
|
||||
return generateAndStoreKeyPair(fileService, signatureService)
|
||||
}
|
||||
|
||||
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
|
||||
tlsConfiguration := portainer.TLSConfiguration{
|
||||
TLS: *flags.TLS,
|
||||
TLSSkipVerify: *flags.TLSSkipVerify,
|
||||
}
|
||||
|
||||
if *flags.TLS {
|
||||
tlsConfiguration.TLSCACertPath = *flags.TLSCacert
|
||||
tlsConfiguration.TLSCertPath = *flags.TLSCert
|
||||
tlsConfiguration.TLSKeyPath = *flags.TLSKey
|
||||
} else if !*flags.TLS && *flags.TLSSkipVerify {
|
||||
tlsConfiguration.TLS = true
|
||||
}
|
||||
|
||||
endpoint := &portainer.Endpoint{
|
||||
Name: "primary",
|
||||
URL: *flags.EndpointURL,
|
||||
GroupID: portainer.EndpointGroupID(1),
|
||||
Type: portainer.DockerEnvironment,
|
||||
TLSConfig: tlsConfiguration,
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
Kubernetes: portainer.KubernetesDefault(),
|
||||
|
||||
SecuritySettings: portainer.EndpointSecuritySettings{
|
||||
AllowVolumeBrowserForRegularUsers: false,
|
||||
EnableHostManagementFeatures: false,
|
||||
|
||||
AllowSysctlSettingForRegularUsers: true,
|
||||
AllowBindMountsForRegularUsers: true,
|
||||
AllowPrivilegedModeForRegularUsers: true,
|
||||
AllowHostNamespaceForRegularUsers: true,
|
||||
AllowContainerCapabilitiesForRegularUsers: true,
|
||||
AllowDeviceMappingForRegularUsers: true,
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
},
|
||||
}
|
||||
|
||||
if strings.HasPrefix(endpoint.URL, "tcp://") {
|
||||
tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(tlsConfiguration.TLSCACertPath, tlsConfiguration.TLSCertPath, tlsConfiguration.TLSKeyPath, tlsConfiguration.TLSSkipVerify)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
agentOnDockerEnvironment, err := client.ExecutePingOperation(endpoint.URL, tlsConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if agentOnDockerEnvironment {
|
||||
endpoint.Type = portainer.AgentOnDockerEnvironment
|
||||
}
|
||||
}
|
||||
|
||||
err := snapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("endpoint", endpoint.Name).
|
||||
Str("URL", endpoint.URL).
|
||||
Err(err).
|
||||
Msg("environment snapshot error")
|
||||
}
|
||||
|
||||
return dataStore.Endpoint().Create(endpoint)
|
||||
}
|
||||
|
||||
func createUnsecuredEndpoint(endpointURL string, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
|
||||
if strings.HasPrefix(endpointURL, "tcp://") {
|
||||
_, err := client.ExecutePingOperation(endpointURL, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
endpoint := &portainer.Endpoint{
|
||||
Name: "primary",
|
||||
URL: endpointURL,
|
||||
GroupID: portainer.EndpointGroupID(1),
|
||||
Type: portainer.DockerEnvironment,
|
||||
TLSConfig: portainer.TLSConfiguration{},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
Kubernetes: portainer.KubernetesDefault(),
|
||||
|
||||
SecuritySettings: portainer.EndpointSecuritySettings{
|
||||
AllowVolumeBrowserForRegularUsers: false,
|
||||
EnableHostManagementFeatures: false,
|
||||
|
||||
AllowSysctlSettingForRegularUsers: true,
|
||||
AllowBindMountsForRegularUsers: true,
|
||||
AllowPrivilegedModeForRegularUsers: true,
|
||||
AllowHostNamespaceForRegularUsers: true,
|
||||
AllowContainerCapabilitiesForRegularUsers: true,
|
||||
AllowDeviceMappingForRegularUsers: true,
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
},
|
||||
}
|
||||
|
||||
err := snapshotService.SnapshotEndpoint(endpoint)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("endpoint", endpoint.Name).
|
||||
Str("URL", endpoint.URL).Err(err).
|
||||
Msg("environment snapshot error")
|
||||
}
|
||||
|
||||
return dataStore.Endpoint().Create(endpoint)
|
||||
}
|
||||
|
||||
func initEndpoint(flags *portainer.CLIFlags, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
|
||||
if *flags.EndpointURL == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
endpoints, err := dataStore.Endpoint().Endpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(endpoints) > 0 {
|
||||
log.Info().Msg("instance already has defined environments, skipping the environment defined via CLI")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if *flags.TLS || *flags.TLSSkipVerify {
|
||||
return createTLSSecuredEndpoint(flags, dataStore, snapshotService)
|
||||
}
|
||||
return createUnsecuredEndpoint(*flags.EndpointURL, dataStore, snapshotService)
|
||||
}
|
||||
|
||||
func loadEncryptionSecretKey(keyfilename string) []byte {
|
||||
content, err := os.ReadFile(path.Join("/run/secrets", keyfilename))
|
||||
if err != nil {
|
||||
@@ -398,11 +520,6 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
// check if the db schema version matches with server version
|
||||
if !checkDBSchemaServerVersionMatch(dataStore, portainer.APIVersion, int(portainer.Edition)) {
|
||||
log.Fatal().Msg("The database schema version does not align with the server version. Please consider reverting to the previous server version or addressing the database migration issue.")
|
||||
}
|
||||
|
||||
instanceID, err := dataStore.Version().InstanceID()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed getting instance id")
|
||||
@@ -449,7 +566,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
log.Fatal().Err(err).Msg("failed initializing key pair")
|
||||
}
|
||||
|
||||
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx, fileService)
|
||||
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx)
|
||||
|
||||
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
|
||||
kubernetesClientFactory, err := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, dataStore, instanceID, *flags.AddrHTTPS, settings.UserSessionTimeout)
|
||||
@@ -478,7 +595,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
||||
log.Fatal().Err(err).Msg("failed initializing compose deployer")
|
||||
}
|
||||
|
||||
composeStackManager := initComposeStackManager(composeDeployer, proxyManager)
|
||||
composeStackManager := initComposeStackManager(composeDeployer, reverseTunnelService, proxyManager)
|
||||
|
||||
swarmStackManager, err := initSwarmStackManager(*flags.Assets, dockerConfigPath, digitalSignatureService, fileService, reverseTunnelService, dataStore)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package portainer
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
import "gorm.io/gorm"
|
||||
|
||||
type ReadTransaction interface {
|
||||
GetObject(bucketName string, key []byte, object interface{}) error
|
||||
@@ -25,19 +23,15 @@ type Transaction interface {
|
||||
}
|
||||
|
||||
type Connection interface {
|
||||
Transaction
|
||||
|
||||
Open() error
|
||||
Close() error
|
||||
Init() error
|
||||
GetDB() *gorm.DB
|
||||
|
||||
UpdateTx(fn func(Transaction) error) error
|
||||
ViewTx(fn func(Transaction) error) error
|
||||
|
||||
// write the db contents to filename as json (the schema needs defining)
|
||||
ExportRaw(filename string) error
|
||||
GetByID(ID int, obj interface{}) error
|
||||
DeleteByID(ID int, obj interface{}) error
|
||||
|
||||
// TODO: this one is very database specific atm
|
||||
BackupTo(w io.Writer) error
|
||||
GetDatabaseFileName() string
|
||||
GetDatabaseFilePath() string
|
||||
GetStorePath() string
|
||||
@@ -45,10 +39,4 @@ type Connection interface {
|
||||
IsEncryptedStore() bool
|
||||
NeedsEncryptionMigration() (bool, error)
|
||||
SetEncrypted(encrypted bool)
|
||||
|
||||
BackupMetadata() (map[string]interface{}, error)
|
||||
RestoreMetadata(s map[string]interface{}) error
|
||||
|
||||
UpdateObjectFunc(bucketName string, key []byte, object any, updateFn func()) error
|
||||
ConvertToKey(v int) []byte
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ func (tx *DbTransaction) GetObject(bucketName string, key []byte, object interfa
|
||||
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
|
||||
}
|
||||
|
||||
return tx.conn.UnmarshalObjectWithJsoniter(value, object)
|
||||
data := make([]byte, len(value))
|
||||
copy(data, value)
|
||||
|
||||
return tx.conn.UnmarshalObjectWithJsoniter(data, object)
|
||||
}
|
||||
|
||||
func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object interface{}) error {
|
||||
@@ -46,9 +49,7 @@ func (tx *DbTransaction) DeleteObject(bucketName string, key []byte) error {
|
||||
return bucket.Delete(key)
|
||||
}
|
||||
|
||||
func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, matchingFn func(o interface{}) (id int, ok bool)) error {
|
||||
var ids []int
|
||||
|
||||
func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, matching func(o interface{}) (id int, ok bool)) error {
|
||||
bucket := tx.tx.Bucket([]byte(bucketName))
|
||||
|
||||
cursor := bucket.Cursor()
|
||||
@@ -58,14 +59,11 @@ func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj interface{}, ma
|
||||
return err
|
||||
}
|
||||
|
||||
if id, ok := matchingFn(obj); ok {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
if err := bucket.Delete(tx.conn.ConvertToKey(id)); err != nil {
|
||||
return err
|
||||
if id, ok := matching(obj); ok {
|
||||
err := bucket.Delete(tx.conn.ConvertToKey(id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,33 +115,45 @@ func (tx *DbTransaction) CreateObjectWithStringId(bucketName string, id []byte,
|
||||
return bucket.Put(id, data)
|
||||
}
|
||||
|
||||
func (tx *DbTransaction) GetAll(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
||||
func (tx *DbTransaction) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
bucket := tx.tx.Bucket([]byte(bucketName))
|
||||
|
||||
return bucket.ForEach(func(k []byte, v []byte) error {
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
err := tx.conn.UnmarshalObject(v, obj)
|
||||
if err == nil {
|
||||
obj, err = appendFn(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
obj, err = append(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
||||
func (tx *DbTransaction) GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
bucket := tx.tx.Bucket([]byte(bucketName))
|
||||
|
||||
return bucket.ForEach(func(k []byte, v []byte) error {
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
err := tx.conn.UnmarshalObjectWithJsoniter(v, obj)
|
||||
if err == nil {
|
||||
obj, err = appendFn(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
obj, err = append(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, appendFn func(o interface{}) (interface{}, error)) error {
|
||||
func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj interface{}, append func(o interface{}) (interface{}, error)) error {
|
||||
cursor := tx.tx.Bucket([]byte(bucketName)).Cursor()
|
||||
|
||||
for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); k, v = cursor.Next() {
|
||||
@@ -152,7 +162,7 @@ func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte
|
||||
return err
|
||||
}
|
||||
|
||||
obj, err = appendFn(obj)
|
||||
obj, err = append(obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func backupMetadata(connection *bolt.DB) (map[string]interface{}, error) {
|
||||
buckets := map[string]interface{}{}
|
||||
|
||||
err := connection.View(func(tx *bolt.Tx) error {
|
||||
err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
|
||||
bucketName := string(name)
|
||||
seqId := bucket.Sequence()
|
||||
buckets[bucketName] = int(seqId)
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return buckets, err
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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")
|
||||
|
||||
connection, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true})
|
||||
if err != nil {
|
||||
return []byte("{}"), err
|
||||
}
|
||||
defer connection.Close()
|
||||
|
||||
backup := make(map[string]interface{})
|
||||
if metadata {
|
||||
meta, err := backupMetadata(connection)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed exporting metadata")
|
||||
}
|
||||
|
||||
backup["__metadata"] = meta
|
||||
}
|
||||
|
||||
err = connection.View(func(tx *bolt.Tx) error {
|
||||
err = tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
|
||||
bucketName := string(name)
|
||||
var list []interface{}
|
||||
version := make(map[string]string)
|
||||
cursor := bucket.Cursor()
|
||||
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
|
||||
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")
|
||||
|
||||
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" ||
|
||||
bucketName == "tunnel_server" {
|
||||
backup[bucketName] = nil
|
||||
if len(list) > 0 {
|
||||
backup[bucketName] = list[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
backup[bucketName] = list
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return []byte("{}"), err
|
||||
}
|
||||
|
||||
return json.MarshalIndent(backup, "", " ")
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var errEncryptedStringTooShort = fmt.Errorf("encrypted string too short")
|
||||
|
||||
// MarshalObject encodes an object to binary format
|
||||
func (connection *DbConnection) MarshalObject(object interface{}) (data []byte, err error) {
|
||||
// Special case for the VERSION bucket. Here we're not using json
|
||||
if v, ok := object.(string); ok {
|
||||
data = []byte(v)
|
||||
} else {
|
||||
data, err = json.Marshal(object)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
}
|
||||
if connection.getEncryptionKey() == nil {
|
||||
return data, nil
|
||||
}
|
||||
return encrypt(data, connection.getEncryptionKey())
|
||||
}
|
||||
|
||||
// UnmarshalObject decodes an object from binary data
|
||||
func (connection *DbConnection) UnmarshalObject(data []byte, object interface{}) error {
|
||||
var err error
|
||||
if connection.getEncryptionKey() != nil {
|
||||
data, err = decrypt(data, connection.getEncryptionKey())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Failed decrypting object")
|
||||
}
|
||||
}
|
||||
e := json.Unmarshal(data, object)
|
||||
if e != nil {
|
||||
// Special case for the VERSION bucket. Here we're not using json
|
||||
// So we need to return it as a string
|
||||
s, ok := object.(*string)
|
||||
if !ok {
|
||||
return errors.Wrap(err, e.Error())
|
||||
}
|
||||
|
||||
*s = string(data)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalObjectWithJsoniter decodes an object from binary data
|
||||
// using the jsoniter library. It is mainly used to accelerate environment(endpoint)
|
||||
// decoding at the moment.
|
||||
func (connection *DbConnection) UnmarshalObjectWithJsoniter(data []byte, object interface{}) error {
|
||||
if connection.getEncryptionKey() != nil {
|
||||
var err error
|
||||
data, err = decrypt(data, connection.getEncryptionKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var jsoni = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||
err := jsoni.Unmarshal(data, &object)
|
||||
if err != nil {
|
||||
if s, ok := object.(*string); ok {
|
||||
*s = string(data)
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mmm, don't have a KMS .... aes GCM seems the most likely from
|
||||
// https://gist.github.com/atoponce/07d8d4c833873be2f68c34f9afc5a78a#symmetric-encryption
|
||||
|
||||
func encrypt(plaintext []byte, passphrase []byte) (encrypted []byte, err error) {
|
||||
block, _ := aes.NewCipher(passphrase)
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return encrypted, err
|
||||
}
|
||||
ciphertextByte := gcm.Seal(
|
||||
nonce,
|
||||
nonce,
|
||||
plaintext,
|
||||
nil)
|
||||
return ciphertextByte, nil
|
||||
}
|
||||
|
||||
func decrypt(encrypted []byte, passphrase []byte) (plaintextByte []byte, err error) {
|
||||
if string(encrypted) == "false" {
|
||||
return []byte("false"), nil
|
||||
}
|
||||
block, err := aes.NewCipher(passphrase)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error creating cypher block")
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error creating GCM")
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(encrypted) < nonceSize {
|
||||
return encrypted, errEncryptedStringTooShort
|
||||
}
|
||||
|
||||
nonce, ciphertextByteClean := encrypted[:nonceSize], encrypted[nonceSize:]
|
||||
plaintextByte, err = gcm.Open(
|
||||
nil,
|
||||
nonce,
|
||||
ciphertextByteClean,
|
||||
nil)
|
||||
if err != nil {
|
||||
return encrypted, errors.Wrap(err, "Error decrypting text")
|
||||
}
|
||||
|
||||
return plaintextByte, err
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
package boltdb
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
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}`
|
||||
passphrase = "my secret key"
|
||||
)
|
||||
|
||||
func secretToEncryptionKey(passphrase string) []byte {
|
||||
hash := sha256.Sum256([]byte(passphrase))
|
||||
return hash[:]
|
||||
}
|
||||
|
||||
func Test_MarshalObjectUnencrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
uuid := uuid.Must(uuid.NewV4())
|
||||
|
||||
tests := []struct {
|
||||
object interface{}
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: nil,
|
||||
expected: `null`,
|
||||
},
|
||||
{
|
||||
object: true,
|
||||
expected: `true`,
|
||||
},
|
||||
{
|
||||
object: false,
|
||||
expected: `false`,
|
||||
},
|
||||
{
|
||||
object: 123,
|
||||
expected: `123`,
|
||||
},
|
||||
{
|
||||
object: "456",
|
||||
expected: "456",
|
||||
},
|
||||
{
|
||||
object: uuid,
|
||||
expected: "\"" + uuid.String() + "\"",
|
||||
},
|
||||
{
|
||||
object: uuid.String(),
|
||||
expected: uuid.String(),
|
||||
},
|
||||
{
|
||||
object: map[string]interface{}{"key": "value"},
|
||||
expected: `{"key":"value"}`,
|
||||
},
|
||||
{
|
||||
object: []bool{true, false},
|
||||
expected: `[true,false]`,
|
||||
},
|
||||
{
|
||||
object: []int{1, 2, 3},
|
||||
expected: `[1,2,3]`,
|
||||
},
|
||||
{
|
||||
object: []string{"1", "2", "3"},
|
||||
expected: `["1","2","3"]`,
|
||||
},
|
||||
{
|
||||
object: []map[string]interface{}{{"key1": "value1"}, {"key2": "value2"}},
|
||||
expected: `[{"key1":"value1"},{"key2":"value2"}]`,
|
||||
},
|
||||
{
|
||||
object: []interface{}{1, "2", false, map[string]interface{}{"key1": "value1"}},
|
||||
expected: `[1,"2",false,{"key1":"value1"}]`,
|
||||
},
|
||||
}
|
||||
|
||||
conn := DbConnection{}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
data, err := conn.MarshalObject(test.object)
|
||||
is.NoError(err)
|
||||
is.Equal(test.expected, string(data))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_UnMarshalObjectUnencrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Based on actual data entering and what we expect out of the function
|
||||
|
||||
tests := []struct {
|
||||
object []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: []byte(""),
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
object: []byte("35"),
|
||||
expected: "35",
|
||||
},
|
||||
{
|
||||
// An unmarshalled byte string should return the same without error
|
||||
object: []byte("9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6"),
|
||||
expected: "9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6",
|
||||
},
|
||||
{
|
||||
// An un-marshalled json object string should return the same as a string without error also
|
||||
object: []byte(jsonobject),
|
||||
expected: jsonobject,
|
||||
},
|
||||
}
|
||||
|
||||
conn := DbConnection{}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
var object string
|
||||
err := conn.UnmarshalObject(test.object, &object)
|
||||
is.NoError(err)
|
||||
is.Equal(test.expected, object)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ObjectMarshallingEncrypted(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Based on actual data entering and what we expect out of the function
|
||||
|
||||
tests := []struct {
|
||||
object []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
object: []byte(""),
|
||||
},
|
||||
{
|
||||
object: []byte("35"),
|
||||
},
|
||||
{
|
||||
// An unmarshalled byte string should return the same without error
|
||||
object: []byte("9ca4a1dd-a439-4593-b386-a7dfdc2e9fc6"),
|
||||
},
|
||||
{
|
||||
// An un-marshalled json object string should return the same as a string without error also
|
||||
object: []byte(jsonobject),
|
||||
},
|
||||
}
|
||||
|
||||
key := secretToEncryptionKey(passphrase)
|
||||
conn := DbConnection{EncryptionKey: key}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s -> %s", test.object, test.expected), func(t *testing.T) {
|
||||
|
||||
data, err := conn.MarshalObject(test.object)
|
||||
is.NoError(err)
|
||||
|
||||
var object []byte
|
||||
err = conn.UnmarshalObject(data, &object)
|
||||
|
||||
is.NoError(err)
|
||||
is.Equal(test.object, object)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,13 +4,13 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/database/sqlite"
|
||||
)
|
||||
|
||||
// NewDatabase should use config options to return a connection to the requested database
|
||||
func NewDatabase(storeType, storePath string, encryptionKey []byte) (connection portainer.Connection, err error) {
|
||||
if storeType == "boltdb" {
|
||||
return &boltdb.DbConnection{
|
||||
if storeType == "sqlite" {
|
||||
return &sqlite.DbConnection{
|
||||
Path: storePath,
|
||||
EncryptionKey: encryptionKey,
|
||||
}, nil
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
VersionKey string = "DB_VERSION"
|
||||
InstanceKey string = "INSTANCE_ID"
|
||||
EditionKey string = "EDITION"
|
||||
UpdatingKey string = "DB_UPDATING"
|
||||
MigratorCountKey string = "MIGRATOR_COUNT"
|
||||
SchemaVersionKey string = "SCHEMA_VERSION"
|
||||
)
|
||||
|
||||
type Version struct {
|
||||
SchemaVersion string
|
||||
MigratorCount int
|
||||
Edition int
|
||||
InstanceID string
|
||||
Key string `json:"Key" gorm:"unique,primaryKey"`
|
||||
Value string `json:"Value"`
|
||||
}
|
||||
|
||||
func (r *Version) Validate(request *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
291
api/database/sqlite/db.go
Normal file
291
api/database/sqlite/db.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
DatabaseFileName = "portainer.db"
|
||||
EncryptedDatabaseFileName = "portainer.edb"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrHaveEncryptedAndUnencrypted = errors.New("Portainer has detected both an encrypted and un-encrypted database and cannot start. Only one database should exist")
|
||||
ErrHaveEncryptedWithNoKey = errors.New("The portainer database is encrypted, but no secret was loaded")
|
||||
)
|
||||
|
||||
type DbConnection struct {
|
||||
Path string
|
||||
EncryptionKey []byte
|
||||
isEncrypted bool
|
||||
|
||||
*gorm.DB
|
||||
}
|
||||
|
||||
func (connection *DbConnection) GetDB() *gorm.DB {
|
||||
if connection.DB == nil {
|
||||
err := connection.Open()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return connection.DB
|
||||
}
|
||||
|
||||
// GetDatabaseFileName get the database filename
|
||||
func (connection *DbConnection) GetDatabaseFileName() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return EncryptedDatabaseFileName
|
||||
}
|
||||
|
||||
return DatabaseFileName
|
||||
}
|
||||
|
||||
// GetDataseFilePath get the path + filename for the database file
|
||||
func (connection *DbConnection) GetDatabaseFilePath() string {
|
||||
if connection.IsEncryptedStore() {
|
||||
return path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
}
|
||||
|
||||
return path.Join(connection.Path, DatabaseFileName)
|
||||
}
|
||||
|
||||
// GetStorePath get the filename and path for the database file
|
||||
func (connection *DbConnection) GetStorePath() string {
|
||||
return connection.Path
|
||||
}
|
||||
|
||||
// Return true if the database is encrypted
|
||||
func (connection *DbConnection) IsEncryptedStore() bool {
|
||||
return connection.getEncryptionKey() != nil
|
||||
}
|
||||
|
||||
func (connection *DbConnection) SetEncrypted(encrypted bool) {
|
||||
connection.isEncrypted = encrypted
|
||||
}
|
||||
|
||||
// NeedsEncryptionMigration returns true if database encryption is enabled and
|
||||
// we have an un-encrypted DB that requires migration to an encrypted DB
|
||||
func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
|
||||
|
||||
// Cases: Note, we need to check both portainer.db and portainer.edb
|
||||
// to determine if it's a new store. We only need to differentiate between cases 2,3 and 5
|
||||
|
||||
// 1) portainer.edb + key => False
|
||||
// 2) portainer.edb + no key => ERROR Fatal!
|
||||
// 3) portainer.db + key => True (needs migration)
|
||||
// 4) portainer.db + no key => False
|
||||
// 5) NoDB (new) + key => False
|
||||
// 6) NoDB (new) + no key => False
|
||||
// 7) portainer.db & portainer.edb => ERROR Fatal!
|
||||
|
||||
// If we have a loaded encryption key, always set encrypted
|
||||
if connection.EncryptionKey != nil {
|
||||
connection.SetEncrypted(true)
|
||||
}
|
||||
|
||||
// Check for portainer.db
|
||||
dbFile := path.Join(connection.Path, DatabaseFileName)
|
||||
_, err := os.Stat(dbFile)
|
||||
haveDbFile := err == nil
|
||||
|
||||
// Check for portainer.edb
|
||||
edbFile := path.Join(connection.Path, EncryptedDatabaseFileName)
|
||||
_, err = os.Stat(edbFile)
|
||||
haveEdbFile := err == nil
|
||||
|
||||
if haveDbFile && haveEdbFile {
|
||||
// 7 - encrypted and unencrypted db?
|
||||
return false, ErrHaveEncryptedAndUnencrypted
|
||||
}
|
||||
|
||||
if haveDbFile && connection.EncryptionKey != nil {
|
||||
// 3 - needs migration
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if haveEdbFile && connection.EncryptionKey == nil {
|
||||
// 2 - encrypted db, but no key?
|
||||
return false, ErrHaveEncryptedWithNoKey
|
||||
}
|
||||
|
||||
// 1, 4, 5, 6
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Open opens and initializes the BoltDB database.
|
||||
func (connection *DbConnection) Open() error {
|
||||
|
||||
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
|
||||
|
||||
// Now we open the db
|
||||
databasePath := connection.GetDatabaseFilePath()
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(databasePath), &gorm.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(5)
|
||||
sqlDB.SetMaxOpenConns(10)
|
||||
connection.DB = db
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (connection *DbConnection) Close() error {
|
||||
sqlDB, err := connection.DB.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
connection.DB = nil
|
||||
return sqlDB.Close()
|
||||
}
|
||||
|
||||
func (connection *DbConnection) getEncryptionKey() []byte {
|
||||
if !connection.isEncrypted {
|
||||
return nil
|
||||
}
|
||||
|
||||
return connection.EncryptionKey
|
||||
}
|
||||
|
||||
func (connection *DbConnection) GetByID(ID int, obj interface{}) error {
|
||||
tx := connection.DB.First(obj, `id = ?`, ID)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (connection *DbConnection) DeleteByID(ID int, obj interface{}) error {
|
||||
tx := connection.DB.Model(obj).Delete(`id = ?`, ID)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (connection *DbConnection) Init() error {
|
||||
err := connection.DB.AutoMigrate(&models.Version{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Version")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Settings{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Settings")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.APIKey{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate APIKey")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.User{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate User")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.CustomTemplate{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate CustomTemplate")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.DockerHub{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate DockerHub")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.EdgeGroup{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate EdgeGroup")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.EdgeJob{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate EdgeJob")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.EdgeStack{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate EdgeStack")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Endpoint{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Endpoint")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.EndpointGroup{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate EndpointGroup")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.EndpointRelation{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate EndpointRelation")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Extension{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Extension")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.FDOProfile{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate FDOProfile")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.HelmUserRepository{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate HelmUserRepository")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Registry{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Registry")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.ResourceControl{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate ResourceControl")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Role{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Role")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Schedule{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Schedule")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Snapshot{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Snapshot")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.SSLSettings{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate SSLSettings")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Stack{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Stack")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Tag{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Tag")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Team{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Team")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.TeamMembership{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate TeamMembership")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.TunnelServerInfo{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate TunnelServerInfo")
|
||||
}
|
||||
err = connection.DB.AutoMigrate(&portainer.Webhook{})
|
||||
if err != nil {
|
||||
log.Err(err).Msgf("failed to auto migrate Webhook")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,37 +1,28 @@
|
||||
package apikeyrepository
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "api_key"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "api_key"
|
||||
)
|
||||
|
||||
// Service represents a service for managing api-key data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.APIKey, portainer.APIKeyID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.APIKey, portainer.APIKeyID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -39,67 +30,91 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error) {
|
||||
var result = make([]portainer.APIKey, 0)
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.APIKey{},
|
||||
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")
|
||||
return nil, fmt.Errorf("failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.APIKey{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
|
||||
// }
|
||||
|
||||
if record.UserID == userID {
|
||||
result = append(result, *record)
|
||||
}
|
||||
// if record.UserID == userID {
|
||||
// result = append(result, *record)
|
||||
// }
|
||||
|
||||
return &portainer.APIKey{}, nil
|
||||
})
|
||||
// return &portainer.APIKey{}, nil
|
||||
// })
|
||||
|
||||
return result, err
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetAPIKeyByDigest returns the API key for the associated digest.
|
||||
// Note: there is a 1-to-1 mapping of api-key and digest
|
||||
func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error) {
|
||||
var k *portainer.APIKey
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.APIKey{},
|
||||
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")
|
||||
return nil, fmt.Errorf("failed to convert to APIKey object: %s", obj)
|
||||
}
|
||||
if bytes.Equal(key.Digest, digest) {
|
||||
k = key
|
||||
return nil, stop
|
||||
}
|
||||
// var k *portainer.APIKey
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.APIKey{},
|
||||
// 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")
|
||||
// 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
|
||||
})
|
||||
// return &portainer.APIKey{}, nil
|
||||
// })
|
||||
|
||||
if errors.Is(err, stop) {
|
||||
return k, nil
|
||||
}
|
||||
// if errors.Is(err, stop) {
|
||||
// return k, nil
|
||||
// }
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
// if err == nil {
|
||||
// return nil, dserrors.ErrObjectNotFound
|
||||
// }
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create creates a new APIKey object.
|
||||
func (service *Service) Create(record *portainer.APIKey) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
record.ID = portainer.APIKeyID(id)
|
||||
// CreateAPIKey creates a new APIKey object.
|
||||
func (service *Service) CreateAPIKey(record *portainer.APIKey) error {
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// record.ID = portainer.APIKeyID(id)
|
||||
|
||||
return int(record.ID), record
|
||||
},
|
||||
)
|
||||
// return int(record.ID), record
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAPIKey retrieves an existing APIKey object by api key ID.
|
||||
func (service *Service) GetAPIKey(keyID portainer.APIKeyID) (*portainer.APIKey, error) {
|
||||
var key portainer.APIKey
|
||||
// identifier := service.connection.ConvertToKey(int(keyID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &key)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &key, nil
|
||||
}
|
||||
|
||||
func (service *Service) UpdateAPIKey(key *portainer.APIKey) error {
|
||||
// identifier := service.connection.ConvertToKey(int(key.ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, key)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) DeleteAPIKey(ID portainer.APIKeyID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package dataservices
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type BaseCRUD[T any, I constraints.Integer] interface {
|
||||
Create(element *T) error
|
||||
Read(ID I) (*T, error)
|
||||
ReadAll() ([]T, error)
|
||||
Update(ID I, element *T) error
|
||||
Delete(ID I) error
|
||||
}
|
||||
|
||||
type BaseDataService[T any, I constraints.Integer] struct {
|
||||
Bucket string
|
||||
Connection portainer.Connection
|
||||
}
|
||||
|
||||
func (s *BaseDataService[T, I]) BucketName() string {
|
||||
return s.Bucket
|
||||
}
|
||||
|
||||
func (service *BaseDataService[T, I]) Tx(tx portainer.Transaction) BaseDataServiceTx[T, I] {
|
||||
return BaseDataServiceTx[T, I]{
|
||||
Bucket: service.Bucket,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
func (service BaseDataService[T, I]) Read(ID I) (*T, error) {
|
||||
var element *T
|
||||
|
||||
return element, service.Connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
var err error
|
||||
element, err = service.Tx(tx).Read(ID)
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (service BaseDataService[T, I]) ReadAll() ([]T, error) {
|
||||
var collection = make([]T, 0)
|
||||
|
||||
return collection, service.Connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
var err error
|
||||
collection, err = service.Tx(tx).ReadAll()
|
||||
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
func (service BaseDataService[T, I]) Update(ID I, element *T) error {
|
||||
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).Update(ID, element)
|
||||
})
|
||||
}
|
||||
|
||||
func (service BaseDataService[T, I]) Delete(ID I) error {
|
||||
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).Delete(ID)
|
||||
})
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package dataservices
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
type BaseDataServiceTx[T any, I constraints.Integer] struct {
|
||||
Bucket string
|
||||
Connection portainer.Connection
|
||||
Tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service BaseDataServiceTx[T, I]) BucketName() string {
|
||||
return service.Bucket
|
||||
}
|
||||
|
||||
func (service BaseDataServiceTx[T, I]) Read(ID I) (*T, error) {
|
||||
var element T
|
||||
identifier := service.Connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.Tx.GetObject(service.Bucket, identifier, &element)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &element, nil
|
||||
}
|
||||
|
||||
func (service BaseDataServiceTx[T, I]) ReadAll() ([]T, error) {
|
||||
var collection = make([]T, 0)
|
||||
|
||||
return collection, service.Tx.GetAllWithJsoniter(
|
||||
service.Bucket,
|
||||
new(T),
|
||||
AppendFn(&collection),
|
||||
)
|
||||
}
|
||||
|
||||
func (service BaseDataServiceTx[T, I]) Update(ID I, element *T) error {
|
||||
identifier := service.Connection.ConvertToKey(int(ID))
|
||||
return service.Tx.UpdateObject(service.Bucket, identifier, element)
|
||||
}
|
||||
|
||||
func (service BaseDataServiceTx[T, I]) Delete(ID I) error {
|
||||
identifier := service.Connection.ConvertToKey(int(ID))
|
||||
return service.Tx.DeleteObject(service.Bucket, identifier)
|
||||
}
|
||||
@@ -2,39 +2,81 @@ package customtemplate
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "customtemplates"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "customtemplates"
|
||||
)
|
||||
|
||||
// Service represents a service for managing custom template data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.CustomTemplate, portainer.CustomTemplateID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.CustomTemplate, portainer.CustomTemplateID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CustomTemplates return an array containing all the custom templates.
|
||||
func (service *Service) CustomTemplates() ([]portainer.CustomTemplate, error) {
|
||||
var customTemplates = make([]portainer.CustomTemplate, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.CustomTemplate{},
|
||||
// func(obj interface{}) (interface{}, 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj)
|
||||
// }
|
||||
// customTemplates = append(customTemplates, *customTemplate)
|
||||
|
||||
// return &portainer.CustomTemplate{}, nil
|
||||
// })
|
||||
|
||||
return customTemplates, nil
|
||||
}
|
||||
|
||||
// CustomTemplate returns an custom template by ID.
|
||||
func (service *Service) CustomTemplate(ID portainer.CustomTemplateID) (*portainer.CustomTemplate, error) {
|
||||
var customTemplate portainer.CustomTemplate
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &customTemplate)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &customTemplate, nil
|
||||
}
|
||||
|
||||
// UpdateCustomTemplate updates an custom template.
|
||||
func (service *Service) UpdateCustomTemplate(ID portainer.CustomTemplateID, customTemplate *portainer.CustomTemplate) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteCustomTemplate deletes an custom template.
|
||||
func (service *Service) DeleteCustomTemplate(ID portainer.CustomTemplateID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateCustomTemplate uses the existing id and saves it.
|
||||
// TODO: where does the ID come from, and is it safe?
|
||||
func (service *Service) Create(customTemplate *portainer.CustomTemplate) error {
|
||||
return service.Connection.CreateObjectWithId(BucketName, int(customTemplate.ID), customTemplate)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a custom template.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.Connection.GetNextIdentifier(BucketName)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -35,15 +30,10 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) DockerHub() (*portainer.DockerHub, error) {
|
||||
var dockerhub portainer.DockerHub
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(dockerHubKey), &dockerhub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dockerhub, nil
|
||||
}
|
||||
|
||||
// UpdateDockerHub updates a DockerHub object.
|
||||
func (service *Service) UpdateDockerHub(dockerhub *portainer.DockerHub) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(dockerHubKey), dockerhub)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package edgegroup
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
@@ -10,7 +9,7 @@ const BucketName = "edgegroups"
|
||||
|
||||
// Service represents a service for managing Edge group data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.EdgeGroup, portainer.EdgeGroupID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
@@ -19,42 +18,60 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.EdgeGroup, portainer.EdgeGroupID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EdgeGroup, portainer.EdgeGroupID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// EdgeGroups return a slice containing all the Edge groups.
|
||||
func (service *Service) EdgeGroups() ([]portainer.EdgeGroup, error) {
|
||||
var groups []portainer.EdgeGroup
|
||||
var err error
|
||||
|
||||
// err = service.connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
// groups, err = service.Tx(tx).EdgeGroups()
|
||||
// return err
|
||||
// })
|
||||
|
||||
return groups, err
|
||||
}
|
||||
|
||||
// EdgeGroup returns an Edge group by ID.
|
||||
func (service *Service) EdgeGroup(ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
|
||||
var group *portainer.EdgeGroup
|
||||
var err error
|
||||
|
||||
// err = service.connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
// group, err = service.Tx(tx).EdgeGroup(ID)
|
||||
// return err
|
||||
// })
|
||||
|
||||
return group, err
|
||||
}
|
||||
|
||||
// UpdateEdgeGroup updates an edge group.
|
||||
func (service *Service) UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: UpdateEdgeGroupFunc updates an edge group inside a transaction avoiding data races.
|
||||
func (service *Service) UpdateEdgeGroupFunc(ID portainer.EdgeGroupID, updateFunc func(edgeGroup *portainer.EdgeGroup)) error {
|
||||
id := service.Connection.ConvertToKey(int(ID))
|
||||
edgeGroup := &portainer.EdgeGroup{}
|
||||
return nil
|
||||
}
|
||||
|
||||
return service.Connection.UpdateObjectFunc(BucketName, id, edgeGroup, func() {
|
||||
updateFunc(edgeGroup)
|
||||
})
|
||||
// DeleteEdgeGroup deletes an Edge group.
|
||||
func (service *Service) DeleteEdgeGroup(ID portainer.EdgeGroupID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateEdgeGroup assign an ID to a new Edge group and saves it.
|
||||
func (service *Service) Create(group *portainer.EdgeGroup) error {
|
||||
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).Create(group)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,48 @@ import (
|
||||
"errors"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.EdgeGroup, portainer.EdgeGroupID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// EdgeGroups return a slice containing all the Edge groups.
|
||||
func (service ServiceTx) EdgeGroups() ([]portainer.EdgeGroup, error) {
|
||||
var groups = make([]portainer.EdgeGroup, 0)
|
||||
|
||||
// err := service.tx.GetAllWithJsoniter(
|
||||
// BucketName,
|
||||
// &portainer.EdgeGroup{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj)
|
||||
// }
|
||||
// groups = append(groups, *group)
|
||||
|
||||
// return &portainer.EdgeGroup{}, nil
|
||||
// })
|
||||
|
||||
return groups, nil
|
||||
}
|
||||
|
||||
// EdgeGroup returns an Edge group by ID.
|
||||
func (service ServiceTx) EdgeGroup(ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error) {
|
||||
var group portainer.EdgeGroup
|
||||
|
||||
return &group, nil
|
||||
}
|
||||
|
||||
// UpdateEdgeGroup updates an edge group.
|
||||
func (service ServiceTx) UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeGroupFunc is a no-op inside a transaction.
|
||||
@@ -16,12 +53,11 @@ func (service ServiceTx) UpdateEdgeGroupFunc(ID portainer.EdgeGroupID, updateFun
|
||||
return errors.New("cannot be called inside a transaction")
|
||||
}
|
||||
|
||||
func (service ServiceTx) Create(group *portainer.EdgeGroup) error {
|
||||
return service.Tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
group.ID = portainer.EdgeGroupID(id)
|
||||
return int(group.ID), group
|
||||
},
|
||||
)
|
||||
// DeleteEdgeGroup deletes an Edge group.
|
||||
func (service ServiceTx) DeleteEdgeGroup(ID portainer.EdgeGroupID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) Create(group *portainer.EdgeGroup) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package edgejob
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
@@ -10,61 +9,56 @@ const BucketName = "edgejobs"
|
||||
|
||||
// Service represents a service for managing edge jobs data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.EdgeJob, portainer.EdgeJobID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.EdgeJob, portainer.EdgeJobID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EdgeJob, portainer.EdgeJobID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// Create creates a new EdgeJob
|
||||
func (service *Service) Create(edgeJob *portainer.EdgeJob) error {
|
||||
return service.CreateWithID(portainer.EdgeJobID(service.GetNextIdentifier()), edgeJob)
|
||||
// EdgeJobs returns a list of Edge jobs
|
||||
func (service *Service) EdgeJobs() ([]portainer.EdgeJob, error) {
|
||||
var edgeJobs = make([]portainer.EdgeJob, 0)
|
||||
return edgeJobs, nil
|
||||
}
|
||||
|
||||
// CreateWithID creates a new EdgeJob
|
||||
func (service *Service) CreateWithID(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
edgeJob.ID = ID
|
||||
// EdgeJob returns an Edge job by ID
|
||||
func (service *Service) EdgeJob(ID portainer.EdgeJobID) (*portainer.EdgeJob, error) {
|
||||
var edgeJob portainer.EdgeJob
|
||||
|
||||
return service.Connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(edgeJob.ID),
|
||||
edgeJob,
|
||||
)
|
||||
return &edgeJob, nil
|
||||
}
|
||||
|
||||
// Create creates a new EdgeJob
|
||||
func (service *Service) Create(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: use UpdateEdgeJobFunc instead
|
||||
func (service *Service) UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeJobFunc updates an edge job inside a transaction avoiding data races.
|
||||
func (service *Service) UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFunc func(edgeJob *portainer.EdgeJob)) error {
|
||||
id := service.Connection.ConvertToKey(int(ID))
|
||||
edgeJob := &portainer.EdgeJob{}
|
||||
|
||||
return service.Connection.UpdateObjectFunc(BucketName, id, edgeJob, func() {
|
||||
updateFunc(edgeJob)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.Connection.GetNextIdentifier(BucketName)
|
||||
// DeleteEdgeJob deletes an Edge job
|
||||
func (service *Service) DeleteEdgeJob(ID portainer.EdgeJobID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,23 +4,41 @@ import (
|
||||
"errors"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.EdgeJob, portainer.EdgeJobID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// EdgeJobs returns a list of Edge jobs
|
||||
func (service ServiceTx) EdgeJobs() ([]portainer.EdgeJob, error) {
|
||||
var edgeJobs = make([]portainer.EdgeJob, 0)
|
||||
|
||||
return edgeJobs, nil
|
||||
}
|
||||
|
||||
// EdgeJob returns an Edge job by ID
|
||||
func (service ServiceTx) EdgeJob(ID portainer.EdgeJobID) (*portainer.EdgeJob, error) {
|
||||
var edgeJob portainer.EdgeJob
|
||||
|
||||
return &edgeJob, nil
|
||||
}
|
||||
|
||||
// Create creates a new EdgeJob
|
||||
func (service ServiceTx) Create(edgeJob *portainer.EdgeJob) error {
|
||||
return service.CreateWithID(portainer.EdgeJobID(service.GetNextIdentifier()), edgeJob)
|
||||
}
|
||||
|
||||
// CreateWithID creates a new EdgeJob
|
||||
func (service ServiceTx) CreateWithID(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
func (service ServiceTx) Create(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
edgeJob.ID = ID
|
||||
|
||||
return service.Tx.CreateObjectWithId(BucketName, int(edgeJob.ID), edgeJob)
|
||||
return service.tx.CreateObjectWithId(BucketName, int(edgeJob.ID), edgeJob)
|
||||
}
|
||||
|
||||
// UpdateEdgeJob updates an edge job
|
||||
func (service ServiceTx) UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeJobFunc is a no-op inside a transaction.
|
||||
@@ -28,7 +46,7 @@ func (service ServiceTx) UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFunc fu
|
||||
return errors.New("cannot be called inside a transaction")
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service ServiceTx) GetNextIdentifier() int {
|
||||
return service.Tx.GetNextIdentifier(BucketName)
|
||||
// DeleteEdgeJob deletes an Edge job
|
||||
func (service ServiceTx) DeleteEdgeJob(ID portainer.EdgeJobID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"sync"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
@@ -24,11 +23,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection, cacheInvalidationFn func(portainer.EdgeStackID)) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
connection: connection,
|
||||
idxVersion: make(map[portainer.EdgeStackID]int),
|
||||
@@ -61,24 +55,12 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
// EdgeStacks returns an array containing all edge stacks
|
||||
func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) {
|
||||
var stacks = make([]portainer.EdgeStack, 0)
|
||||
|
||||
return stacks, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EdgeStack{},
|
||||
dataservices.AppendFn(&stacks),
|
||||
)
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// EdgeStack returns an Edge stack by ID.
|
||||
func (service *Service) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error) {
|
||||
var stack portainer.EdgeStack
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stack, nil
|
||||
}
|
||||
|
||||
@@ -95,15 +77,6 @@ func (service *Service) EdgeStackVersion(ID portainer.EdgeStackID) (int, bool) {
|
||||
func (service *Service) Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
|
||||
edgeStack.ID = id
|
||||
|
||||
err := service.connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(edgeStack.ID),
|
||||
edgeStack,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.mu.Lock()
|
||||
service.idxVersion[id] = edgeStack.Version
|
||||
service.cacheInvalidationFn(id)
|
||||
@@ -117,13 +90,6 @@ func (service *Service) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *por
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.UpdateObject(BucketName, identifier, edgeStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.idxVersion[ID] = edgeStack.Version
|
||||
service.cacheInvalidationFn(ID)
|
||||
|
||||
@@ -132,18 +98,7 @@ func (service *Service) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *por
|
||||
|
||||
// UpdateEdgeStackFunc updates an Edge stack inside a transaction avoiding data races.
|
||||
func (service *Service) UpdateEdgeStackFunc(ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error {
|
||||
id := service.connection.ConvertToKey(int(ID))
|
||||
edgeStack := &portainer.EdgeStack{}
|
||||
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
return service.connection.UpdateObjectFunc(BucketName, id, edgeStack, func() {
|
||||
updateFunc(edgeStack)
|
||||
|
||||
service.idxVersion[ID] = edgeStack.Version
|
||||
service.cacheInvalidationFn(ID)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEdgeStackFuncTx is a helper function used to call UpdateEdgeStackFunc inside a transaction.
|
||||
@@ -156,21 +111,7 @@ func (service *Service) DeleteEdgeStack(ID portainer.EdgeStackID) error {
|
||||
service.mu.Lock()
|
||||
defer service.mu.Unlock()
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.DeleteObject(BucketName, identifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(service.idxVersion, ID)
|
||||
|
||||
service.cacheInvalidationFn(ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package edgestack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
@@ -21,33 +17,12 @@ func (service ServiceTx) BucketName() string {
|
||||
func (service ServiceTx) EdgeStacks() ([]portainer.EdgeStack, error) {
|
||||
var stacks = make([]portainer.EdgeStack, 0)
|
||||
|
||||
err := service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.EdgeStack{},
|
||||
func(obj interface{}) (interface{}, error) {
|
||||
stack, ok := obj.(*portainer.EdgeStack)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("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
|
||||
})
|
||||
|
||||
return stacks, err
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// EdgeStack returns an Edge stack by ID.
|
||||
func (service ServiceTx) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error) {
|
||||
var stack portainer.EdgeStack
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.GetObject(BucketName, identifier, &stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stack, nil
|
||||
}
|
||||
@@ -65,15 +40,6 @@ func (service ServiceTx) EdgeStackVersion(ID portainer.EdgeStackID) (int, bool)
|
||||
func (service ServiceTx) Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
|
||||
edgeStack.ID = id
|
||||
|
||||
err := service.tx.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(edgeStack.ID),
|
||||
edgeStack,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.service.mu.Lock()
|
||||
service.service.idxVersion[id] = edgeStack.Version
|
||||
service.service.cacheInvalidationFn(id)
|
||||
@@ -87,13 +53,6 @@ func (service ServiceTx) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *po
|
||||
service.service.mu.Lock()
|
||||
defer service.service.mu.Unlock()
|
||||
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.UpdateObject(BucketName, identifier, edgeStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
service.service.idxVersion[ID] = edgeStack.Version
|
||||
service.service.cacheInvalidationFn(ID)
|
||||
|
||||
@@ -116,22 +75,5 @@ func (service ServiceTx) UpdateEdgeStackFunc(ID portainer.EdgeStackID, updateFun
|
||||
func (service ServiceTx) DeleteEdgeStack(ID portainer.EdgeStackID) error {
|
||||
service.service.mu.Lock()
|
||||
defer service.service.mu.Unlock()
|
||||
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.DeleteObject(BucketName, identifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(service.service.idxVersion, ID)
|
||||
|
||||
service.service.cacheInvalidationFn(ID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service ServiceTx) GetNextIdentifier() int {
|
||||
return service.tx.GetNextIdentifier(BucketName)
|
||||
}
|
||||
|
||||
@@ -5,12 +5,8 @@ import (
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "endpoints"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
@@ -19,36 +15,31 @@ type Service struct {
|
||||
heartbeats sync.Map
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
s := &Service{
|
||||
connection: connection,
|
||||
idxEdgeID: make(map[string]portainer.EndpointID),
|
||||
}
|
||||
|
||||
es, err := s.endpoints()
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (service *Service) Init() error {
|
||||
es, err := service.endpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
for _, e := range es {
|
||||
if len(e.EdgeID) > 0 {
|
||||
s.idxEdgeID[e.EdgeID] = e.ID
|
||||
service.idxEdgeID[e.EdgeID] = e.ID
|
||||
}
|
||||
|
||||
s.heartbeats.Store(e.ID, e.LastCheckInDate)
|
||||
service.heartbeats.Store(e.ID, e.LastCheckInDate)
|
||||
}
|
||||
|
||||
return s, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
@@ -60,44 +51,48 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
|
||||
// Endpoint returns an environment(endpoint) by ID.
|
||||
func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
|
||||
var endpoint *portainer.Endpoint
|
||||
var err error
|
||||
var obj portainer.Endpoint
|
||||
|
||||
err = service.connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
endpoint, err = service.Tx(tx).Endpoint(ID)
|
||||
return err
|
||||
})
|
||||
err := service.connection.GetByID(int(ID), &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint.LastCheckInDate, _ = service.Heartbeat(ID)
|
||||
|
||||
return endpoint, nil
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// UpdateEndpoint updates an environment(endpoint).
|
||||
func (service *Service) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
|
||||
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).UpdateEndpoint(ID, endpoint)
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
endpoint.ID = ID
|
||||
tx := db.Save(&endpoint)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpoint deletes an environment(endpoint).
|
||||
func (service *Service) DeleteEndpoint(ID portainer.EndpointID) error {
|
||||
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).DeleteEndpoint(ID)
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&portainer.Endpoint{}).Delete("id = ?", ID)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) endpoints() ([]portainer.Endpoint, error) {
|
||||
var endpoints []portainer.Endpoint
|
||||
var err error
|
||||
|
||||
err = service.connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
endpoints, err = service.Tx(tx).Endpoints()
|
||||
return err
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&endpoints)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return endpoints, err
|
||||
}
|
||||
@@ -140,37 +135,10 @@ func (service *Service) UpdateHeartbeat(endpointID portainer.EndpointID) {
|
||||
|
||||
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
|
||||
func (service *Service) Create(endpoint *portainer.Endpoint) error {
|
||||
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return service.Tx(tx).Create(endpoint)
|
||||
})
|
||||
}
|
||||
|
||||
func (service *Service) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.Endpoint, error) {
|
||||
var endpoints = make([]portainer.Endpoint, 0)
|
||||
|
||||
return endpoints, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Endpoint{},
|
||||
dataservices.FilterFn(&endpoints, func(e portainer.Endpoint) bool {
|
||||
for t := range e.TeamAccessPolicies {
|
||||
if t == teamID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
var identifier int
|
||||
|
||||
service.connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
identifier = service.Tx(tx).GetNextIdentifier()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return identifier
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&portainer.Endpoint{}).Create(&endpoint)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package endpoint
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge/cache"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -13,33 +12,27 @@ type ServiceTx struct {
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// Endpoint returns an environment(endpoint) by ID.
|
||||
func (service ServiceTx) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
|
||||
var endpoint portainer.Endpoint
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.GetObject(BucketName, identifier, &endpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
endpoint.LastCheckInDate, _ = service.service.Heartbeat(ID)
|
||||
// err := service.tx.GetObject(BucketName, identifier, &endpoint)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpoint, nil
|
||||
}
|
||||
|
||||
// UpdateEndpoint updates an environment(endpoint).
|
||||
func (service ServiceTx) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.UpdateObject(BucketName, identifier, endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// err := service.tx.UpdateObject(BucketName, identifier, endpoint)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.service.mu.Lock()
|
||||
if len(endpoint.EdgeID) > 0 {
|
||||
@@ -55,18 +48,17 @@ func (service ServiceTx) UpdateEndpoint(ID portainer.EndpointID, endpoint *porta
|
||||
|
||||
// DeleteEndpoint deletes an environment(endpoint).
|
||||
func (service ServiceTx) DeleteEndpoint(ID portainer.EndpointID) error {
|
||||
identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.tx.DeleteObject(BucketName, identifier)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// err := service.tx.DeleteObject(BucketName, identifier)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.service.mu.Lock()
|
||||
for edgeID, endpointID := range service.service.idxEdgeID {
|
||||
if endpointID == ID {
|
||||
delete(service.service.idxEdgeID, edgeID)
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -82,11 +74,22 @@ func (service ServiceTx) DeleteEndpoint(ID portainer.EndpointID) error {
|
||||
func (service ServiceTx) Endpoints() ([]portainer.Endpoint, error) {
|
||||
var endpoints = make([]portainer.Endpoint, 0)
|
||||
|
||||
return endpoints, service.tx.GetAllWithJsoniter(
|
||||
BucketName,
|
||||
&portainer.Endpoint{},
|
||||
dataservices.AppendFn(&endpoints),
|
||||
)
|
||||
// err := service.tx.GetAllWithJsoniter(
|
||||
// BucketName,
|
||||
// &portainer.Endpoint{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj)
|
||||
// }
|
||||
|
||||
// endpoints = append(endpoints, *endpoint)
|
||||
|
||||
// return &portainer.Endpoint{}, nil
|
||||
// })
|
||||
|
||||
return endpoints, nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) EndpointIDByEdgeID(edgeID string) (portainer.EndpointID, bool) {
|
||||
@@ -107,39 +110,17 @@ func (service ServiceTx) UpdateHeartbeat(endpointID portainer.EndpointID) {
|
||||
|
||||
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
|
||||
func (service ServiceTx) Create(endpoint *portainer.Endpoint) error {
|
||||
err := service.tx.CreateObjectWithId(BucketName, int(endpoint.ID), endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// err := service.tx.CreateObjectWithId(BucketName, int(endpoint.ID), endpoint)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.service.mu.Lock()
|
||||
if len(endpoint.EdgeID) > 0 {
|
||||
service.service.idxEdgeID[endpoint.EdgeID] = endpoint.ID
|
||||
}
|
||||
service.service.heartbeats.Store(endpoint.ID, endpoint.LastCheckInDate)
|
||||
service.service.mu.Unlock()
|
||||
// service.service.mu.Lock()
|
||||
// if len(endpoint.EdgeID) > 0 {
|
||||
// service.service.idxEdgeID[endpoint.EdgeID] = endpoint.ID
|
||||
// }
|
||||
// service.service.heartbeats.Store(endpoint.ID, endpoint.LastCheckInDate)
|
||||
// service.service.mu.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.Endpoint, error) {
|
||||
var endpoints = make([]portainer.Endpoint, 0)
|
||||
|
||||
return endpoints, service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Endpoint{},
|
||||
dataservices.FilterFn(&endpoints, func(e portainer.Endpoint) bool {
|
||||
for t := range e.TeamAccessPolicies {
|
||||
if t == teamID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for an environment(endpoint).
|
||||
func (service ServiceTx) GetNextIdentifier() int {
|
||||
return service.tx.GetNextIdentifier(BucketName)
|
||||
}
|
||||
|
||||
@@ -2,51 +2,62 @@ package endpointgroup
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "endpoint_groups"
|
||||
)
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.EndpointGroup, portainer.EndpointGroupID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.EndpointGroup, portainer.EndpointGroupID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EndpointGroup, portainer.EndpointGroupID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// EndpointGroup returns an environment(endpoint) group by ID.
|
||||
func (service *Service) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.EndpointGroup, error) {
|
||||
var endpointGroup portainer.EndpointGroup
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &endpointGroup)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpointGroup, nil
|
||||
}
|
||||
|
||||
// UpdateEndpointGroup updates an environment(endpoint) group.
|
||||
func (service *Service) UpdateEndpointGroup(ID portainer.EndpointGroupID, endpointGroup *portainer.EndpointGroup) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, endpointGroup)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpointGroup deletes an environment(endpoint) group.
|
||||
func (service *Service) DeleteEndpointGroup(ID portainer.EndpointGroupID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EndpointGroups return an array containing all the environment(endpoint) groups.
|
||||
func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
|
||||
var endpointGroups = make([]portainer.EndpointGroup, 0)
|
||||
|
||||
return endpointGroups, nil
|
||||
}
|
||||
|
||||
// CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it.
|
||||
func (service *Service) Create(endpointGroup *portainer.EndpointGroup) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
endpointGroup.ID = portainer.EndpointGroupID(id)
|
||||
return int(endpointGroup.ID), endpointGroup
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,20 +2,48 @@ package endpointgroup
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.EndpointGroup, portainer.EndpointGroupID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
// EndpointGroup returns an environment(endpoint) group by ID.
|
||||
func (service ServiceTx) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.EndpointGroup, error) {
|
||||
var endpointGroup portainer.EndpointGroup
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &endpointGroup)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpointGroup, nil
|
||||
}
|
||||
|
||||
// UpdateEndpointGroup updates an environment(endpoint) group.
|
||||
func (service ServiceTx) UpdateEndpointGroup(ID portainer.EndpointGroupID, endpointGroup *portainer.EndpointGroup) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, endpointGroup)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteEndpointGroup deletes an environment(endpoint) group.
|
||||
func (service ServiceTx) DeleteEndpointGroup(ID portainer.EndpointGroupID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EndpointGroups return an array containing all the environment(endpoint) groups.
|
||||
func (service ServiceTx) EndpointGroups() ([]portainer.EndpointGroup, error) {
|
||||
var endpointGroups = make([]portainer.EndpointGroup, 0)
|
||||
|
||||
return endpointGroups, nil
|
||||
}
|
||||
|
||||
// CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it.
|
||||
func (service ServiceTx) Create(endpointGroup *portainer.EndpointGroup) error {
|
||||
return service.Tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
endpointGroup.ID = portainer.EndpointGroupID(id)
|
||||
return int(endpointGroup.ID), endpointGroup
|
||||
},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,15 +2,11 @@ package endpointrelation
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge/cache"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "endpoint_relations"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) relation data.
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
@@ -18,10 +14,6 @@ type Service struct {
|
||||
updateStackFnTx func(tx portainer.Transaction, ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error
|
||||
}
|
||||
|
||||
func (service *Service) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
func (service *Service) RegisterUpdateStackFunction(
|
||||
updateFunc func(portainer.EdgeStackID, func(*portainer.EdgeStack)) error,
|
||||
updateFuncTx func(portainer.Transaction, portainer.EdgeStackID, func(*portainer.EdgeStack)) error,
|
||||
@@ -32,10 +24,6 @@ func (service *Service) RegisterUpdateStackFunction(
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -53,44 +41,32 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
func (service *Service) EndpointRelations() ([]portainer.EndpointRelation, error) {
|
||||
var all = make([]portainer.EndpointRelation, 0)
|
||||
|
||||
return all, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.EndpointRelation{},
|
||||
dataservices.AppendFn(&all),
|
||||
)
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// EndpointRelation returns a Environment(Endpoint) relation object by EndpointID
|
||||
func (service *Service) EndpointRelation(endpointID portainer.EndpointID) (*portainer.EndpointRelation, error) {
|
||||
var endpointRelation portainer.EndpointRelation
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &endpointRelation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &endpointRelation, nil
|
||||
}
|
||||
|
||||
// CreateEndpointRelation saves endpointRelation
|
||||
func (service *Service) Create(endpointRelation *portainer.EndpointRelation) error {
|
||||
err := service.connection.CreateObjectWithId(BucketName, int(endpointRelation.EndpointID), endpointRelation)
|
||||
cache.Del(endpointRelation.EndpointID)
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEndpointRelation updates an Environment(Endpoint) relation object
|
||||
func (service *Service) UpdateEndpointRelation(endpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error {
|
||||
previousRelationState, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
err := service.connection.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
cache.Del(endpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
// err := service.connection.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
// cache.Del(endpointID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
updatedRelationState, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
@@ -103,12 +79,12 @@ func (service *Service) UpdateEndpointRelation(endpointID portainer.EndpointID,
|
||||
func (service *Service) DeleteEndpointRelation(endpointID portainer.EndpointID) error {
|
||||
deletedRelation, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
err := service.connection.DeleteObject(BucketName, identifier)
|
||||
cache.Del(endpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
// err := service.connection.DeleteObject(BucketName, identifier)
|
||||
// cache.Del(endpointID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.updateEdgeStacksAfterRelationChange(deletedRelation, nil)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package endpointrelation
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/internal/edge/cache"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -13,52 +12,59 @@ type ServiceTx struct {
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// EndpointRelations returns an array of all EndpointRelations
|
||||
func (service ServiceTx) EndpointRelations() ([]portainer.EndpointRelation, error) {
|
||||
var all = make([]portainer.EndpointRelation, 0)
|
||||
|
||||
return all, service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.EndpointRelation{},
|
||||
dataservices.AppendFn(&all),
|
||||
)
|
||||
// err := service.tx.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.EndpointRelation{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("failed to convert to EndpointRelation object: %s", obj)
|
||||
// }
|
||||
|
||||
// all = append(all, *r)
|
||||
|
||||
// return &portainer.EndpointRelation{}, nil
|
||||
// })
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// EndpointRelation returns an Environment(Endpoint) relation object by EndpointID
|
||||
func (service ServiceTx) EndpointRelation(endpointID portainer.EndpointID) (*portainer.EndpointRelation, error) {
|
||||
var endpointRelation portainer.EndpointRelation
|
||||
identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
// identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
err := service.tx.GetObject(BucketName, identifier, &endpointRelation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.tx.GetObject(BucketName, identifier, &endpointRelation)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &endpointRelation, nil
|
||||
}
|
||||
|
||||
// CreateEndpointRelation saves endpointRelation
|
||||
func (service ServiceTx) Create(endpointRelation *portainer.EndpointRelation) error {
|
||||
err := service.tx.CreateObjectWithId(BucketName, int(endpointRelation.EndpointID), endpointRelation)
|
||||
cache.Del(endpointRelation.EndpointID)
|
||||
// err := service.tx.CreateObjectWithId(BucketName, int(endpointRelation.EndpointID), endpointRelation)
|
||||
// cache.Del(endpointRelation.EndpointID)
|
||||
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateEndpointRelation updates an Environment(Endpoint) relation object
|
||||
func (service ServiceTx) UpdateEndpointRelation(endpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error {
|
||||
previousRelationState, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
err := service.tx.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
cache.Del(endpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
// err := service.tx.UpdateObject(BucketName, identifier, endpointRelation)
|
||||
// cache.Del(endpointID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
updatedRelationState, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
@@ -71,12 +77,12 @@ func (service ServiceTx) UpdateEndpointRelation(endpointID portainer.EndpointID,
|
||||
func (service ServiceTx) DeleteEndpointRelation(endpointID portainer.EndpointID) error {
|
||||
deletedRelation, _ := service.EndpointRelation(endpointID)
|
||||
|
||||
identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
err := service.tx.DeleteObject(BucketName, identifier)
|
||||
cache.Del(endpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
// err := service.tx.DeleteObject(BucketName, identifier)
|
||||
// cache.Del(endpointID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
service.updateEdgeStacksAfterRelationChange(deletedRelation, nil)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoSuchTable = errors.New("no such table")
|
||||
ErrObjectNotFound = errors.New("object not found inside the database")
|
||||
ErrWrongDBEdition = errors.New("the Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
|
||||
ErrDBImportFailed = errors.New("importing backup failed")
|
||||
|
||||
@@ -2,11 +2,12 @@ package extension
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "extension"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "extension"
|
||||
)
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
@@ -19,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// 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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -32,12 +33,12 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Extension returns a extension by ID
|
||||
func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extension, error) {
|
||||
var extension portainer.Extension
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &extension)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &extension)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &extension, nil
|
||||
}
|
||||
@@ -46,21 +47,33 @@ func (service *Service) Extension(ID portainer.ExtensionID) (*portainer.Extensio
|
||||
func (service *Service) Extensions() ([]portainer.Extension, error) {
|
||||
var extensions = make([]portainer.Extension, 0)
|
||||
|
||||
return extensions, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Extension{},
|
||||
dataservices.AppendFn(&extensions),
|
||||
)
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Extension{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Extension object: %s", obj)
|
||||
// }
|
||||
|
||||
// extensions = append(extensions, *extension)
|
||||
|
||||
// return &portainer.Extension{}, nil
|
||||
// })
|
||||
|
||||
return extensions, nil
|
||||
}
|
||||
|
||||
// Persist persists a extension inside the database.
|
||||
func (service *Service) Persist(extension *portainer.Extension) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(extension.ID), extension)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(extension.ID), extension)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteExtension deletes a Extension.
|
||||
func (service *Service) DeleteExtension(ID portainer.ExtensionID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,42 +2,88 @@ package fdoprofile
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "fdo_profiles"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "fdo_profiles"
|
||||
)
|
||||
|
||||
// Service represents a service for managingFDO Profiles data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.FDOProfile, portainer.FDOProfileID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FDOProfiles return an array containing all the FDO Profiles.
|
||||
func (service *Service) FDOProfiles() ([]portainer.FDOProfile, error) {
|
||||
var fdoProfiles = make([]portainer.FDOProfile, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.FDOProfile{},
|
||||
// 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)
|
||||
// }
|
||||
// fdoProfiles = append(fdoProfiles, *fdoProfile)
|
||||
// return &portainer.FDOProfile{}, nil
|
||||
// })
|
||||
|
||||
return fdoProfiles, nil
|
||||
}
|
||||
|
||||
// FDOProfile returns an FDO Profile by ID.
|
||||
func (service *Service) FDOProfile(ID portainer.FDOProfileID) (*portainer.FDOProfile, error) {
|
||||
var FDOProfile portainer.FDOProfile
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &FDOProfile)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &FDOProfile, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new FDO Profile and saves it.
|
||||
func (service *Service) Create(FDOProfile *portainer.FDOProfile) error {
|
||||
return service.Connection.CreateObjectWithId(
|
||||
BucketName,
|
||||
int(FDOProfile.ID),
|
||||
FDOProfile,
|
||||
)
|
||||
// return service.connection.CreateObjectWithId(
|
||||
// BucketName,
|
||||
// int(FDOProfile.ID),
|
||||
// FDOProfile,
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a FDO Profile.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.Connection.GetNextIdentifier(BucketName)
|
||||
// Update updates an FDO Profile.
|
||||
func (service *Service) Update(ID portainer.FDOProfileID, FDOProfile *portainer.FDOProfile) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, FDOProfile)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes an FDO Profile.
|
||||
func (service *Service) Delete(ID portainer.FDOProfileID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,52 +2,102 @@ package helmuserrepository
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "helm_user_repository"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "helm_user_repository"
|
||||
)
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.HelmUserRepository, portainer.HelmUserRepositoryID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.HelmUserRepository, portainer.HelmUserRepositoryID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// HelmUserRepository returns an array of all HelmUserRepository
|
||||
func (service *Service) HelmUserRepositories() ([]portainer.HelmUserRepository, error) {
|
||||
var repos = make([]portainer.HelmUserRepository, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &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")
|
||||
// return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
// }
|
||||
|
||||
// repos = append(repos, *r)
|
||||
|
||||
// return &portainer.HelmUserRepository{}, nil
|
||||
// })
|
||||
|
||||
return repos, nil
|
||||
}
|
||||
|
||||
// HelmUserRepositoryByUserID return an array containing all the HelmUserRepository objects where the specified userID is present.
|
||||
func (service *Service) HelmUserRepositoryByUserID(userID portainer.UserID) ([]portainer.HelmUserRepository, error) {
|
||||
var result = make([]portainer.HelmUserRepository, 0)
|
||||
|
||||
return result, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.HelmUserRepository{},
|
||||
dataservices.FilterFn(&result, func(e portainer.HelmUserRepository) bool {
|
||||
return e.UserID == userID
|
||||
}),
|
||||
)
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.HelmUserRepository{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to HelmUserRepository object: %s", obj)
|
||||
// }
|
||||
|
||||
// if record.UserID == userID {
|
||||
// result = append(result, *record)
|
||||
// }
|
||||
|
||||
// return &portainer.HelmUserRepository{}, nil
|
||||
// })
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateHelmUserRepository creates a new HelmUserRepository object.
|
||||
func (service *Service) Create(record *portainer.HelmUserRepository) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
record.ID = portainer.HelmUserRepositoryID(id)
|
||||
return int(record.ID), record
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// record.ID = portainer.HelmUserRepositoryID(id)
|
||||
// return int(record.ID), record
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateHelmUserRepostory updates an registry.
|
||||
func (service *Service) UpdateHelmUserRepository(ID portainer.HelmUserRepositoryID, registry *portainer.HelmUserRepository) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteHelmUserRepository deletes an registry.
|
||||
func (service *Service) DeleteHelmUserRepository(ID portainer.HelmUserRepositoryID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package dataservices
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
perrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// ErrStop signals the stop of computation when filtering results
|
||||
var ErrStop = errors.New("stop")
|
||||
|
||||
func IsErrObjectNotFound(e error) bool {
|
||||
return errors.Is(e, perrors.ErrObjectNotFound)
|
||||
}
|
||||
|
||||
// AppendFn appends elements to the given collection slice
|
||||
func AppendFn[T any](collection *[]T) func(obj interface{}) (interface{}, error) {
|
||||
return func(obj interface{}) (interface{}, error) {
|
||||
element, ok := obj.(*T)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed")
|
||||
return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj)
|
||||
}
|
||||
|
||||
*collection = append(*collection, *element)
|
||||
|
||||
return new(T), nil
|
||||
}
|
||||
}
|
||||
|
||||
// FilterFn appends elements to the given collection when the predicate is true
|
||||
func FilterFn[T any](collection *[]T, predicate func(T) bool) func(obj interface{}) (interface{}, error) {
|
||||
return func(obj interface{}) (interface{}, error) {
|
||||
element, ok := obj.(*T)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed")
|
||||
return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj)
|
||||
}
|
||||
|
||||
if predicate(*element) {
|
||||
*collection = append(*collection, *element)
|
||||
}
|
||||
|
||||
return new(T), nil
|
||||
}
|
||||
}
|
||||
|
||||
// FirstFn sets the element to the first one that satisfies the predicate and stops the computation, returns ErrStop on
|
||||
// success
|
||||
func FirstFn[T any](element *T, predicate func(T) bool) func(obj interface{}) (interface{}, error) {
|
||||
return func(obj interface{}) (interface{}, error) {
|
||||
e, ok := obj.(*T)
|
||||
if !ok {
|
||||
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("type assertion failed")
|
||||
return nil, fmt.Errorf("failed to convert to %T object: %#v", new(T), obj)
|
||||
}
|
||||
|
||||
if predicate(*e) {
|
||||
*element = *e
|
||||
return new(T), ErrStop
|
||||
}
|
||||
|
||||
return new(T), nil
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package dataservices
|
||||
|
||||
import (
|
||||
"io"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -37,16 +39,13 @@ type (
|
||||
Webhook() WebhookService
|
||||
}
|
||||
|
||||
// DataStore defines the interface to manage the data
|
||||
DataStore interface {
|
||||
Open() (newStore bool, err error)
|
||||
Init() error
|
||||
Close() error
|
||||
UpdateTx(func(DataStoreTx) error) error
|
||||
ViewTx(func(DataStoreTx) error) error
|
||||
MigrateData() error
|
||||
Rollback(force bool) error
|
||||
CheckCurrentEdition() error
|
||||
BackupTo(w io.Writer) error
|
||||
Export(filename string) (err error)
|
||||
|
||||
DataStoreTx
|
||||
@@ -54,22 +53,31 @@ type (
|
||||
|
||||
// CustomTemplateService represents a service to manage custom templates
|
||||
CustomTemplateService interface {
|
||||
BaseCRUD[portainer.CustomTemplate, portainer.CustomTemplateID]
|
||||
GetNextIdentifier() int
|
||||
CustomTemplates() ([]portainer.CustomTemplate, error)
|
||||
CustomTemplate(ID portainer.CustomTemplateID) (*portainer.CustomTemplate, error)
|
||||
Create(customTemplate *portainer.CustomTemplate) error
|
||||
UpdateCustomTemplate(ID portainer.CustomTemplateID, customTemplate *portainer.CustomTemplate) error
|
||||
DeleteCustomTemplate(ID portainer.CustomTemplateID) error
|
||||
}
|
||||
|
||||
// EdgeGroupService represents a service to manage Edge groups
|
||||
EdgeGroupService interface {
|
||||
BaseCRUD[portainer.EdgeGroup, portainer.EdgeGroupID]
|
||||
EdgeGroups() ([]portainer.EdgeGroup, error)
|
||||
EdgeGroup(ID portainer.EdgeGroupID) (*portainer.EdgeGroup, error)
|
||||
Create(group *portainer.EdgeGroup) error
|
||||
UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error
|
||||
UpdateEdgeGroupFunc(ID portainer.EdgeGroupID, updateFunc func(group *portainer.EdgeGroup)) error
|
||||
DeleteEdgeGroup(ID portainer.EdgeGroupID) error
|
||||
}
|
||||
|
||||
// EdgeJobService represents a service to manage Edge jobs
|
||||
EdgeJobService interface {
|
||||
BaseCRUD[portainer.EdgeJob, portainer.EdgeJobID]
|
||||
CreateWithID(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error
|
||||
EdgeJobs() ([]portainer.EdgeJob, error)
|
||||
EdgeJob(ID portainer.EdgeJobID) (*portainer.EdgeJob, error)
|
||||
Create(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error
|
||||
UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error
|
||||
UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFunc func(edgeJob *portainer.EdgeJob)) error
|
||||
GetNextIdentifier() int
|
||||
DeleteEdgeJob(ID portainer.EdgeJobID) error
|
||||
}
|
||||
|
||||
// EdgeStackService represents a service to manage Edge stacks
|
||||
@@ -78,31 +86,31 @@ type (
|
||||
EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error)
|
||||
EdgeStackVersion(ID portainer.EdgeStackID) (int, bool)
|
||||
Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error
|
||||
// Deprecated: Use UpdateEdgeStackFunc instead.
|
||||
UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error
|
||||
UpdateEdgeStackFunc(ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error
|
||||
DeleteEdgeStack(ID portainer.EdgeStackID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EndpointService represents a service for managing environment(endpoint) data
|
||||
EndpointService interface {
|
||||
Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error)
|
||||
EndpointIDByEdgeID(edgeID string) (portainer.EndpointID, bool)
|
||||
EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.Endpoint, error)
|
||||
Heartbeat(endpointID portainer.EndpointID) (int64, bool)
|
||||
UpdateHeartbeat(endpointID portainer.EndpointID)
|
||||
Endpoints() ([]portainer.Endpoint, error)
|
||||
Create(endpoint *portainer.Endpoint) error
|
||||
UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error
|
||||
DeleteEndpoint(ID portainer.EndpointID) error
|
||||
GetNextIdentifier() int
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// EndpointGroupService represents a service for managing environment(endpoint) group data
|
||||
EndpointGroupService interface {
|
||||
BaseCRUD[portainer.EndpointGroup, portainer.EndpointGroupID]
|
||||
EndpointGroup(ID portainer.EndpointGroupID) (*portainer.EndpointGroup, error)
|
||||
EndpointGroups() ([]portainer.EndpointGroup, error)
|
||||
Create(group *portainer.EndpointGroup) error
|
||||
UpdateEndpointGroup(ID portainer.EndpointGroupID, group *portainer.EndpointGroup) error
|
||||
DeleteEndpointGroup(ID portainer.EndpointGroupID) error
|
||||
}
|
||||
|
||||
// EndpointRelationService represents a service for managing environment(endpoint) relations data
|
||||
@@ -112,19 +120,24 @@ type (
|
||||
Create(endpointRelation *portainer.EndpointRelation) error
|
||||
UpdateEndpointRelation(EndpointID portainer.EndpointID, endpointRelation *portainer.EndpointRelation) error
|
||||
DeleteEndpointRelation(EndpointID portainer.EndpointID) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// FDOProfileService represents a service to manage FDO Profiles
|
||||
FDOProfileService interface {
|
||||
BaseCRUD[portainer.FDOProfile, portainer.FDOProfileID]
|
||||
GetNextIdentifier() int
|
||||
FDOProfiles() ([]portainer.FDOProfile, error)
|
||||
FDOProfile(ID portainer.FDOProfileID) (*portainer.FDOProfile, error)
|
||||
Create(FDOProfile *portainer.FDOProfile) error
|
||||
Update(ID portainer.FDOProfileID, FDOProfile *portainer.FDOProfile) error
|
||||
Delete(ID portainer.FDOProfileID) error
|
||||
}
|
||||
|
||||
// HelmUserRepositoryService represents a service to manage HelmUserRepositories
|
||||
HelmUserRepositoryService interface {
|
||||
BaseCRUD[portainer.HelmUserRepository, portainer.HelmUserRepositoryID]
|
||||
HelmUserRepositories() ([]portainer.HelmUserRepository, error)
|
||||
HelmUserRepositoryByUserID(userID portainer.UserID) ([]portainer.HelmUserRepository, error)
|
||||
Create(record *portainer.HelmUserRepository) error
|
||||
UpdateHelmUserRepository(ID portainer.HelmUserRepositoryID, repository *portainer.HelmUserRepository) error
|
||||
DeleteHelmUserRepository(ID portainer.HelmUserRepositoryID) error
|
||||
}
|
||||
|
||||
// JWTService represents a service for managing JWT tokens
|
||||
@@ -138,23 +151,37 @@ type (
|
||||
|
||||
// RegistryService represents a service for managing registry data
|
||||
RegistryService interface {
|
||||
BaseCRUD[portainer.Registry, portainer.RegistryID]
|
||||
Registry(ID portainer.RegistryID) (*portainer.Registry, error)
|
||||
Registries() ([]portainer.Registry, error)
|
||||
Create(registry *portainer.Registry) error
|
||||
UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error
|
||||
DeleteRegistry(ID portainer.RegistryID) error
|
||||
}
|
||||
|
||||
// ResourceControlService represents a service for managing resource control data
|
||||
ResourceControlService interface {
|
||||
BaseCRUD[portainer.ResourceControl, portainer.ResourceControlID]
|
||||
ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error)
|
||||
ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error)
|
||||
ResourceControls() ([]portainer.ResourceControl, error)
|
||||
Create(rc *portainer.ResourceControl) error
|
||||
UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error
|
||||
DeleteResourceControl(ID portainer.ResourceControlID) error
|
||||
}
|
||||
|
||||
// RoleService represents a service for managing user roles
|
||||
RoleService interface {
|
||||
BaseCRUD[portainer.Role, portainer.RoleID]
|
||||
Role(ID portainer.RoleID) (*portainer.Role, error)
|
||||
Roles() ([]portainer.Role, error)
|
||||
Create(role *portainer.Role) error
|
||||
UpdateRole(ID portainer.RoleID, role *portainer.Role) error
|
||||
}
|
||||
|
||||
// APIKeyRepositoryService
|
||||
APIKeyRepository interface {
|
||||
BaseCRUD[portainer.APIKey, portainer.APIKeyID]
|
||||
CreateAPIKey(key *portainer.APIKey) error
|
||||
GetAPIKey(keyID portainer.APIKeyID) (*portainer.APIKey, error)
|
||||
UpdateAPIKey(key *portainer.APIKey) error
|
||||
DeleteAPIKey(ID portainer.APIKeyID) error
|
||||
GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error)
|
||||
GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error)
|
||||
}
|
||||
@@ -163,47 +190,64 @@ type (
|
||||
SettingsService interface {
|
||||
Settings() (*portainer.Settings, error)
|
||||
UpdateSettings(settings *portainer.Settings) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
SnapshotService interface {
|
||||
BaseCRUD[portainer.Snapshot, portainer.EndpointID]
|
||||
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
|
||||
}
|
||||
|
||||
// SSLSettingsService represents a service for managing application settings
|
||||
SSLSettingsService interface {
|
||||
Settings() (*portainer.SSLSettings, error)
|
||||
UpdateSettings(settings *portainer.SSLSettings) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// StackService represents a service for managing stack data
|
||||
StackService interface {
|
||||
BaseCRUD[portainer.Stack, portainer.StackID]
|
||||
Stack(ID portainer.StackID) (*portainer.Stack, error)
|
||||
StackByName(name string) (*portainer.Stack, error)
|
||||
StacksByName(name string) ([]portainer.Stack, error)
|
||||
GetNextIdentifier() int
|
||||
Stacks() ([]portainer.Stack, error)
|
||||
Create(stack *portainer.Stack) error
|
||||
UpdateStack(ID portainer.StackID, stack *portainer.Stack) error
|
||||
DeleteStack(ID portainer.StackID) error
|
||||
StackByWebhookID(ID string) (*portainer.Stack, error)
|
||||
RefreshableStacks() ([]portainer.Stack, error)
|
||||
}
|
||||
|
||||
// TagService represents a service for managing tag data
|
||||
TagService interface {
|
||||
BaseCRUD[portainer.Tag, portainer.TagID]
|
||||
Tags() ([]portainer.Tag, error)
|
||||
Tag(ID portainer.TagID) (*portainer.Tag, error)
|
||||
Create(tag *portainer.Tag) error
|
||||
UpdateTag(ID portainer.TagID, tag *portainer.Tag) error
|
||||
UpdateTagFunc(ID portainer.TagID, updateFunc func(tag *portainer.Tag)) error
|
||||
DeleteTag(ID portainer.TagID) error
|
||||
}
|
||||
|
||||
// TeamService represents a service for managing user data
|
||||
TeamService interface {
|
||||
BaseCRUD[portainer.Team, portainer.TeamID]
|
||||
Team(ID portainer.TeamID) (*portainer.Team, error)
|
||||
TeamByName(name string) (*portainer.Team, error)
|
||||
Teams() ([]portainer.Team, error)
|
||||
Create(team *portainer.Team) error
|
||||
UpdateTeam(ID portainer.TeamID, team *portainer.Team) error
|
||||
DeleteTeam(ID portainer.TeamID) error
|
||||
}
|
||||
|
||||
// TeamMembershipService represents a service for managing team membership data
|
||||
TeamMembershipService interface {
|
||||
BaseCRUD[portainer.TeamMembership, portainer.TeamMembershipID]
|
||||
TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error)
|
||||
TeamMemberships() ([]portainer.TeamMembership, error)
|
||||
TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error)
|
||||
TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error)
|
||||
Create(membership *portainer.TeamMembership) error
|
||||
UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error
|
||||
DeleteTeamMembership(ID portainer.TeamMembershipID) error
|
||||
DeleteTeamMembershipByUserID(userID portainer.UserID) error
|
||||
DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error
|
||||
DeleteTeamMembershipByTeamIDAndUserID(teamID portainer.TeamID, userID portainer.UserID) error
|
||||
@@ -213,29 +257,48 @@ type (
|
||||
TunnelServerService interface {
|
||||
Info() (*portainer.TunnelServerInfo, error)
|
||||
UpdateInfo(info *portainer.TunnelServerInfo) error
|
||||
BucketName() string
|
||||
}
|
||||
|
||||
// UserService represents a service for managing user data
|
||||
UserService interface {
|
||||
BaseCRUD[portainer.User, portainer.UserID]
|
||||
User(ID portainer.UserID) (*portainer.User, error)
|
||||
UserByUsername(username string) (*portainer.User, error)
|
||||
Users() ([]portainer.User, error)
|
||||
UsersByRole(role portainer.UserRole) ([]portainer.User, error)
|
||||
Create(user *portainer.User) error
|
||||
UpdateUser(ID portainer.UserID, user *portainer.User) error
|
||||
DeleteUser(ID portainer.UserID) error
|
||||
}
|
||||
|
||||
// VersionService represents a service for managing version data
|
||||
VersionService interface {
|
||||
InstanceID() (string, error)
|
||||
UpdateInstanceID(ID string) error
|
||||
Edition() (portainer.SoftwareEdition, error)
|
||||
InstanceID() (string, error)
|
||||
// UpdateInstanceID(ID string) error
|
||||
Version() (*models.Version, error)
|
||||
UpdateVersion(*models.Version) error
|
||||
GetAll() (map[string]interface{}, error)
|
||||
UpdateAll(map[string]interface{}) error
|
||||
}
|
||||
|
||||
// WebhookService represents a service for managing webhook data.
|
||||
WebhookService interface {
|
||||
BaseCRUD[portainer.Webhook, portainer.WebhookID]
|
||||
Webhooks() ([]portainer.Webhook, error)
|
||||
Webhook(ID portainer.WebhookID) (*portainer.Webhook, error)
|
||||
Create(portainer *portainer.Webhook) error
|
||||
UpdateWebhook(ID portainer.WebhookID, webhook *portainer.Webhook) error
|
||||
WebhookByResourceID(resourceID string) (*portainer.Webhook, error)
|
||||
WebhookByToken(token string) (*portainer.Webhook, error)
|
||||
DeleteWebhook(ID portainer.WebhookID) error
|
||||
}
|
||||
)
|
||||
|
||||
func IsErrObjectNotFound(e error) bool {
|
||||
var sqliteErr sqlite3.Error
|
||||
errNotFound := false
|
||||
if errors.As(e, &sqliteErr) {
|
||||
errNotFound = sqliteErr.Code == sqlite3.ErrError
|
||||
}
|
||||
|
||||
return errNotFound || errors.Is(e, gorm.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package registry
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
@@ -10,41 +9,89 @@ const BucketName = "registries"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Registry, portainer.RegistryID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Registry, portainer.RegistryID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.Registry, portainer.RegistryID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// Registry returns a registry by ID.
|
||||
func (service *Service) Registry(ID portainer.RegistryID) (*portainer.Registry, error) {
|
||||
var registry portainer.Registry
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, ®istry)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return ®istry, nil
|
||||
}
|
||||
|
||||
// Registries returns an array containing all the registries.
|
||||
func (service *Service) Registries() ([]portainer.Registry, error) {
|
||||
var registries = make([]portainer.Registry, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Registry{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj)
|
||||
// }
|
||||
|
||||
// registries = append(registries, *registry)
|
||||
|
||||
// return &portainer.Registry{}, nil
|
||||
// })
|
||||
|
||||
return registries, nil
|
||||
}
|
||||
|
||||
// Create creates a new registry.
|
||||
func (service *Service) Create(registry *portainer.Registry) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
registry.ID = portainer.RegistryID(id)
|
||||
return int(registry.ID), registry
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// registry.ID = portainer.RegistryID(id)
|
||||
// return int(registry.ID), registry
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRegistry updates a registry.
|
||||
func (service *Service) UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, registry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRegistry deletes a registry.
|
||||
func (service *Service) DeleteRegistry(ID portainer.RegistryID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,17 +1,59 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.Registry, portainer.RegistryID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// Registry returns a registry by ID.
|
||||
func (service ServiceTx) Registry(ID portainer.RegistryID) (*portainer.Registry, error) {
|
||||
var registry portainer.Registry
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, ®istry)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return ®istry, nil
|
||||
}
|
||||
|
||||
// Registries returns an array containing all the registries.
|
||||
func (service ServiceTx) Registries() ([]portainer.Registry, error) {
|
||||
var registries = make([]portainer.Registry, 0)
|
||||
|
||||
err := service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Registry{},
|
||||
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")
|
||||
return nil, fmt.Errorf("Failed to convert to Registry object: %s", obj)
|
||||
}
|
||||
|
||||
registries = append(registries, *registry)
|
||||
|
||||
return &portainer.Registry{}, nil
|
||||
})
|
||||
|
||||
return registries, err
|
||||
}
|
||||
|
||||
// Create creates a new registry.
|
||||
func (service ServiceTx) Create(registry *portainer.Registry) error {
|
||||
return service.Tx.CreateObject(
|
||||
return service.tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
registry.ID = portainer.RegistryID(id)
|
||||
@@ -19,3 +61,17 @@ func (service ServiceTx) Create(registry *portainer.Registry) error {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateRegistry updates a registry.
|
||||
func (service ServiceTx) UpdateRegistry(ID portainer.RegistryID, registry *portainer.Registry) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, registry)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRegistry deletes a registry.
|
||||
func (service ServiceTx) DeleteRegistry(ID portainer.RegistryID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
package resourcecontrol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
@@ -15,78 +9,126 @@ const BucketName = "resource_control"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.ResourceControl, portainer.ResourceControlID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.ResourceControl, portainer.ResourceControlID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.ResourceControl, portainer.ResourceControlID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// ResourceControl returns a ResourceControl object by ID
|
||||
func (service *Service) ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error) {
|
||||
var resourceControl portainer.ResourceControl
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &resourceControl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &resourceControl, nil
|
||||
}
|
||||
|
||||
// ResourceControlByResourceIDAndType returns a ResourceControl object by checking if the resourceID is equal
|
||||
// to the main ResourceID or in SubResourceIDs. It also performs a check on the resource type. Return nil
|
||||
// if no ResourceControl was found.
|
||||
func (service *Service) ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) {
|
||||
var resourceControl *portainer.ResourceControl
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.ResourceControl{},
|
||||
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")
|
||||
return nil, fmt.Errorf("failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
// var resourceControl *portainer.ResourceControl
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.ResourceControl{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
// }
|
||||
|
||||
if rc.ResourceID == resourceID && rc.Type == resourceType {
|
||||
resourceControl = rc
|
||||
return nil, stop
|
||||
}
|
||||
// if rc.ResourceID == resourceID && rc.Type == resourceType {
|
||||
// resourceControl = rc
|
||||
// return nil, stop
|
||||
// }
|
||||
|
||||
for _, subResourceID := range rc.SubResourceIDs {
|
||||
if subResourceID == resourceID {
|
||||
resourceControl = rc
|
||||
return nil, stop
|
||||
}
|
||||
}
|
||||
// for _, subResourceID := range rc.SubResourceIDs {
|
||||
// if subResourceID == resourceID {
|
||||
// resourceControl = rc
|
||||
// return nil, stop
|
||||
// }
|
||||
// }
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
if errors.Is(err, stop) {
|
||||
return resourceControl, nil
|
||||
}
|
||||
// return &portainer.ResourceControl{}, nil
|
||||
// })
|
||||
// if errors.Is(err, stop) {
|
||||
// return resourceControl, nil
|
||||
// }
|
||||
|
||||
return nil, err
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ResourceControls returns all the ResourceControl objects
|
||||
func (service *Service) ResourceControls() ([]portainer.ResourceControl, error) {
|
||||
var rcs = make([]portainer.ResourceControl, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.ResourceControl{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
// }
|
||||
|
||||
// rcs = append(rcs, *rc)
|
||||
|
||||
// return &portainer.ResourceControl{}, nil
|
||||
// })
|
||||
|
||||
return rcs, nil
|
||||
}
|
||||
|
||||
// CreateResourceControl creates a new ResourceControl object
|
||||
func (service *Service) Create(resourceControl *portainer.ResourceControl) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
resourceControl.ID = portainer.ResourceControlID(id)
|
||||
return int(resourceControl.ID), resourceControl
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// resourceControl.ID = portainer.ResourceControlID(id)
|
||||
// return int(resourceControl.ID), resourceControl
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateResourceControl saves a ResourceControl object.
|
||||
func (service *Service) UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, resourceControl)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteResourceControl deletes a ResourceControl object by ID
|
||||
func (service *Service) DeleteResourceControl(ID portainer.ResourceControlID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5,13 +5,30 @@ import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.ResourceControl, portainer.ResourceControlID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// ResourceControl returns a ResourceControl object by ID
|
||||
func (service ServiceTx) ResourceControl(ID portainer.ResourceControlID) (*portainer.ResourceControl, error) {
|
||||
var resourceControl portainer.ResourceControl
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &resourceControl)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &resourceControl, nil
|
||||
}
|
||||
|
||||
// ResourceControlByResourceIDAndType returns a ResourceControl object by checking if the resourceID is equal
|
||||
@@ -20,14 +37,14 @@ type ServiceTx struct {
|
||||
func (service ServiceTx) ResourceControlByResourceIDAndType(resourceID string, resourceType portainer.ResourceControlType) (*portainer.ResourceControl, error) {
|
||||
var resourceControl *portainer.ResourceControl
|
||||
stop := fmt.Errorf("ok")
|
||||
err := service.Tx.GetAll(
|
||||
err := service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.ResourceControl{},
|
||||
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")
|
||||
return nil, fmt.Errorf("failed to convert to ResourceControl object: %s", obj)
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
|
||||
if rc.ResourceID == resourceID && rc.Type == resourceType {
|
||||
@@ -51,9 +68,31 @@ func (service ServiceTx) ResourceControlByResourceIDAndType(resourceID string, r
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ResourceControls returns all the ResourceControl objects
|
||||
func (service ServiceTx) ResourceControls() ([]portainer.ResourceControl, error) {
|
||||
var rcs = make([]portainer.ResourceControl, 0)
|
||||
|
||||
err := service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.ResourceControl{},
|
||||
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")
|
||||
return nil, fmt.Errorf("Failed to convert to ResourceControl object: %s", obj)
|
||||
}
|
||||
|
||||
rcs = append(rcs, *rc)
|
||||
|
||||
return &portainer.ResourceControl{}, nil
|
||||
})
|
||||
|
||||
return rcs, err
|
||||
}
|
||||
|
||||
// CreateResourceControl creates a new ResourceControl object
|
||||
func (service ServiceTx) Create(resourceControl *portainer.ResourceControl) error {
|
||||
return service.Tx.CreateObject(
|
||||
return service.tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
resourceControl.ID = portainer.ResourceControlID(id)
|
||||
@@ -61,3 +100,17 @@ func (service ServiceTx) Create(resourceControl *portainer.ResourceControl) erro
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateResourceControl saves a ResourceControl object.
|
||||
func (service ServiceTx) UpdateResourceControl(ID portainer.ResourceControlID, resourceControl *portainer.ResourceControl) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, resourceControl)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteResourceControl deletes a ResourceControl object by ID
|
||||
func (service ServiceTx) DeleteResourceControl(ID portainer.ResourceControlID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,49 +2,70 @@ package role
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "roles"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Role, portainer.RoleID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Role, portainer.RoleID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.Role, portainer.RoleID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// Role returns a Role by ID
|
||||
func (service *Service) Role(ID portainer.RoleID) (*portainer.Role, error) {
|
||||
var set portainer.Role
|
||||
db := service.connection.GetDB()
|
||||
|
||||
tx := db.First(&set, `id = ?`, ID)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return &set, nil
|
||||
}
|
||||
|
||||
// Roles returns an array containing all the sets.
|
||||
func (service *Service) Roles() ([]portainer.Role, error) {
|
||||
var sets = make([]portainer.Role, 0)
|
||||
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&sets)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
// CreateRole creates a new Role.
|
||||
func (service *Service) Create(role *portainer.Role) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
role.ID = portainer.RoleID(id)
|
||||
return int(role.ID), role
|
||||
},
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Create(&role)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRole updates a role.
|
||||
func (service *Service) UpdateRole(ID portainer.RoleID, role *portainer.Role) error {
|
||||
db := service.connection.GetDB()
|
||||
role.ID = ID
|
||||
tx := db.Save(&role)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,20 +2,64 @@ package role
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.Role, portainer.RoleID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
// Role returns a Role by ID
|
||||
func (service ServiceTx) Role(ID portainer.RoleID) (*portainer.Role, error) {
|
||||
var set portainer.Role
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &set)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &set, nil
|
||||
}
|
||||
|
||||
// Roles returns an array containing all the sets.
|
||||
func (service ServiceTx) Roles() ([]portainer.Role, error) {
|
||||
var sets = make([]portainer.Role, 0)
|
||||
|
||||
// err := service.tx.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Role{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("failed to convert to Role object: %s", obj)
|
||||
// }
|
||||
|
||||
// sets = append(sets, *set)
|
||||
|
||||
// return &portainer.Role{}, nil
|
||||
// })
|
||||
|
||||
return sets, nil
|
||||
}
|
||||
|
||||
// CreateRole creates a new Role.
|
||||
func (service ServiceTx) Create(role *portainer.Role) error {
|
||||
return service.Tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
role.ID = portainer.RoleID(id)
|
||||
return int(role.ID), role
|
||||
},
|
||||
)
|
||||
// return service.tx.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// role.ID = portainer.RoleID(id)
|
||||
// return int(role.ID), role
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateRole updates a role.
|
||||
func (service ServiceTx) UpdateRole(ID portainer.RoleID, role *portainer.Role) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, role)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@ package schedule
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "schedules"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "schedules"
|
||||
)
|
||||
|
||||
// Service represents a service for managing schedule data.
|
||||
type Service struct {
|
||||
@@ -19,10 +20,10 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// 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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
@@ -32,37 +33,50 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
// Schedule returns a schedule by ID.
|
||||
func (service *Service) Schedule(ID portainer.ScheduleID) (*portainer.Schedule, error) {
|
||||
var schedule portainer.Schedule
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
err := service.connection.GetObject(BucketName, identifier, &schedule)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, identifier, &schedule)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &schedule, nil
|
||||
}
|
||||
|
||||
// UpdateSchedule updates a schedule.
|
||||
func (service *Service) UpdateSchedule(ID portainer.ScheduleID, schedule *portainer.Schedule) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.UpdateObject(BucketName, identifier, schedule)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, schedule)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSchedule deletes a schedule.
|
||||
func (service *Service) DeleteSchedule(ID portainer.ScheduleID) error {
|
||||
identifier := service.connection.ConvertToKey(int(ID))
|
||||
return service.connection.DeleteObject(BucketName, identifier)
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Schedules return a array containing all the schedules.
|
||||
func (service *Service) Schedules() ([]portainer.Schedule, error) {
|
||||
var schedules = make([]portainer.Schedule, 0)
|
||||
|
||||
return schedules, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Schedule{},
|
||||
dataservices.AppendFn(&schedules),
|
||||
)
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Schedule{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
// }
|
||||
|
||||
// schedules = append(schedules, *schedule)
|
||||
|
||||
// return &portainer.Schedule{}, nil
|
||||
// })
|
||||
|
||||
return schedules, nil
|
||||
}
|
||||
|
||||
// SchedulesByJobType return a array containing all the schedules
|
||||
@@ -70,21 +84,28 @@ func (service *Service) Schedules() ([]portainer.Schedule, error) {
|
||||
func (service *Service) SchedulesByJobType(jobType portainer.JobType) ([]portainer.Schedule, error) {
|
||||
var schedules = make([]portainer.Schedule, 0)
|
||||
|
||||
return schedules, service.connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Schedule{},
|
||||
dataservices.FilterFn(&schedules, func(e portainer.Schedule) bool {
|
||||
return e.JobType == jobType
|
||||
}),
|
||||
)
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Schedule{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Schedule object: %s", obj)
|
||||
// }
|
||||
|
||||
// if schedule.JobType == jobType {
|
||||
// schedules = append(schedules, *schedule)
|
||||
// }
|
||||
|
||||
// return &portainer.Schedule{}, nil
|
||||
// })
|
||||
|
||||
return schedules, nil
|
||||
}
|
||||
|
||||
// Create assign an ID to a new schedule and saves it.
|
||||
func (service *Service) CreateSchedule(schedule *portainer.Schedule) error {
|
||||
return service.connection.CreateObjectWithId(BucketName, int(schedule.ID), schedule)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a schedule.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.connection.GetNextIdentifier(BucketName)
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(schedule.ID), schedule)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -15,17 +15,8 @@ type Service struct {
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -40,9 +31,9 @@ func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
|
||||
// Settings retrieve the settings object.
|
||||
func (service *Service) Settings() (*portainer.Settings, error) {
|
||||
var settings portainer.Settings
|
||||
settings := portainer.Settings{ID: 1}
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(settingsKey), &settings)
|
||||
err := service.connection.GetByID(1, &settings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -52,5 +43,10 @@ func (service *Service) Settings() (*portainer.Settings, error) {
|
||||
|
||||
// UpdateSettings persists a Settings object.
|
||||
func (service *Service) UpdateSettings(settings *portainer.Settings) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(settingsKey), settings)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&portainer.Settings{}).Where(portainer.Settings{ID: 1}).Save(settings)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package snapshot
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -10,33 +9,51 @@ const (
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Snapshot, portainer.EndpointID]
|
||||
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{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Snapshot, portainer.EndpointID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.Snapshot, portainer.EndpointID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||
return service.Connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
func (service *Service) Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||
var snapshot portainer.Snapshot
|
||||
|
||||
return &snapshot, nil
|
||||
}
|
||||
|
||||
func (service *Service) Snapshots() ([]portainer.Snapshot, error) {
|
||||
var snapshots = make([]portainer.Snapshot, 0)
|
||||
|
||||
return snapshots, nil
|
||||
}
|
||||
|
||||
func (service *Service) UpdateSnapshot(snapshot *portainer.Snapshot) error {
|
||||
// identifier := service.connection.ConvertToKey(int(snapshot.EndpointID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) DeleteSnapshot(endpointID portainer.EndpointID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(endpointID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service *Service) Create(snapshot *portainer.Snapshot) error {
|
||||
// return service.connection.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,13 +2,61 @@ package snapshot
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.Snapshot, portainer.EndpointID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
func (service ServiceTx) Snapshot(endpointID portainer.EndpointID) (*portainer.Snapshot, error) {
|
||||
var snapshot portainer.Snapshot
|
||||
// identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &snapshot)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &snapshot, nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) Snapshots() ([]portainer.Snapshot, error) {
|
||||
var snapshots = make([]portainer.Snapshot, 0)
|
||||
|
||||
// err := service.tx.GetAllWithJsoniter(
|
||||
// BucketName,
|
||||
// &portainer.Snapshot{},
|
||||
// func(obj interface{}) (interface{}, error) {
|
||||
// snapshot, ok := obj.(*portainer.Snapshot)
|
||||
// if !ok {
|
||||
// log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("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, nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) UpdateSnapshot(snapshot *portainer.Snapshot) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(snapshot.EndpointID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) DeleteSnapshot(endpointID portainer.EndpointID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(endpointID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) Create(snapshot *portainer.Snapshot) error {
|
||||
return service.Tx.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
// return service.tx.CreateObjectWithId(BucketName, int(snapshot.EndpointID), snapshot)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,28 +4,13 @@ import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "ssl"
|
||||
key = "SSL"
|
||||
)
|
||||
|
||||
// Service represents a service for managing ssl data.
|
||||
type Service struct {
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -33,17 +18,22 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
|
||||
// Settings retrieve the ssl settings object.
|
||||
func (service *Service) Settings() (*portainer.SSLSettings, error) {
|
||||
var settings portainer.SSLSettings
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(key), &settings)
|
||||
var obj portainer.SSLSettings
|
||||
err := service.connection.GetByID(1, &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &settings, nil
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// UpdateSettings persists a SSLSettings object.
|
||||
func (service *Service) UpdateSettings(settings *portainer.SSLSettings) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(key), settings)
|
||||
db := service.connection.GetDB()
|
||||
settings.ID = 1
|
||||
tx := db.Save(settings)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,123 +1,136 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "stacks"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Stack, portainer.StackID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Stack returns a stack object by ID.
|
||||
func (service *Service) Stack(ID portainer.StackID) (*portainer.Stack, error) {
|
||||
var obj portainer.Stack
|
||||
|
||||
err := service.connection.GetByID(int(ID), &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Stack, portainer.StackID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: service.BaseDataService.Tx(tx),
|
||||
}
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// StackByName returns a stack object by name.
|
||||
func (service *Service) StackByName(name string) (*portainer.Stack, error) {
|
||||
var s portainer.Stack
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FirstFn(&s, func(e portainer.Stack) bool {
|
||||
return e.Name == name
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.First(&s, `name = ?`, name)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &s, nil
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Stacks returns an array containing all the stacks with same name
|
||||
func (service *Service) StacksByName(name string) ([]portainer.Stack, error) {
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
|
||||
return stacks, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FilterFn(&stacks, func(e portainer.Stack) bool {
|
||||
return e.Name == name
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&stacks, `name = ?`, name)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a stack.
|
||||
func (service *Service) GetNextIdentifier() int {
|
||||
return service.Connection.GetNextIdentifier(BucketName)
|
||||
// Stacks returns an array containing all the stacks.
|
||||
func (service *Service) Stacks() ([]portainer.Stack, error) {
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&stacks)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return stacks, nil
|
||||
}
|
||||
|
||||
// CreateStack creates a new stack.
|
||||
func (service *Service) Create(stack *portainer.Stack) error {
|
||||
return service.Connection.CreateObjectWithId(BucketName, int(stack.ID), stack)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Create(&stack)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateStack updates a stack.
|
||||
func (service *Service) UpdateStack(ID portainer.StackID, stack *portainer.Stack) error {
|
||||
db := service.connection.GetDB()
|
||||
stack.ID = ID
|
||||
tx := db.Save(&stack)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteStack deletes a stack.
|
||||
func (service *Service) DeleteStack(ID portainer.StackID) error {
|
||||
return service.connection.DeleteByID(int(ID), &portainer.Stack{})
|
||||
}
|
||||
|
||||
// StackByWebhookID returns a pointer to a stack object by webhook ID.
|
||||
// It returns nil, errors.ErrObjectNotFound if there's no stack associated with the webhook ID.
|
||||
func (service *Service) StackByWebhookID(id string) (*portainer.Stack, error) {
|
||||
var s portainer.Stack
|
||||
db := service.connection.GetDB()
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FirstFn(&s, func(e portainer.Stack) bool {
|
||||
return e.AutoUpdate != nil && strings.EqualFold(e.AutoUpdate.Webhook, id)
|
||||
}),
|
||||
)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &s, nil
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
tx := db.Find(&stacks)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
for _, stack := range stacks {
|
||||
if stack.AutoUpdate != nil && stack.AutoUpdate.Webhook != "" && stack.AutoUpdate.Webhook == id {
|
||||
return &stack, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// RefreshableStacks returns stacks that are configured for a periodic update
|
||||
func (service *Service) RefreshableStacks() ([]portainer.Stack, error) {
|
||||
stacks := make([]portainer.Stack, 0)
|
||||
|
||||
return stacks, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FilterFn(&stacks, func(e portainer.Stack) bool {
|
||||
return e.AutoUpdate != nil && e.AutoUpdate.Interval != ""
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
var stacksRes = make([]portainer.Stack, 0)
|
||||
tx := db.Find(&stacks)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
for _, stack := range stacks {
|
||||
if stack.AutoUpdate != nil && stack.AutoUpdate.Interval != "" {
|
||||
stacksRes = append(stacksRes, stack)
|
||||
}
|
||||
}
|
||||
|
||||
return stacksRes, nil
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func (b *stackBuilder) createNewStack(webhookID string) portainer.Stack {
|
||||
Type: portainer.DockerComposeStack,
|
||||
EndpointID: 2,
|
||||
EntryPoint: filesystem.ComposeFileDefaultName,
|
||||
Env: []portainer.Pair{{Name: "Name1", Value: "Value1"}},
|
||||
Env: portainer.MultiPair{{Name: "Name1", Value1: "Value1"}},
|
||||
Status: portainer.StackStatusActive,
|
||||
CreationDate: time.Now().Unix(),
|
||||
ProjectPath: "/tmp/project",
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package stack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.Stack, portainer.StackID]
|
||||
}
|
||||
|
||||
// StackByName returns a stack object by name.
|
||||
func (service ServiceTx) StackByName(name string) (*portainer.Stack, error) {
|
||||
var s portainer.Stack
|
||||
|
||||
err := service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FirstFn(&s, func(e portainer.Stack) bool {
|
||||
return e.Name == name
|
||||
}),
|
||||
)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Stacks returns an array containing all the stacks with same name
|
||||
func (service ServiceTx) StacksByName(name string) ([]portainer.Stack, error) {
|
||||
var stacks = make([]portainer.Stack, 0)
|
||||
|
||||
return stacks, service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FilterFn(&stacks, func(e portainer.Stack) bool {
|
||||
return e.Name == name
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// GetNextIdentifier returns the next identifier for a stack.
|
||||
func (service ServiceTx) GetNextIdentifier() int {
|
||||
return service.Tx.GetNextIdentifier(BucketName)
|
||||
}
|
||||
|
||||
// CreateStack creates a new stack.
|
||||
func (service ServiceTx) Create(stack *portainer.Stack) error {
|
||||
return service.Tx.CreateObjectWithId(BucketName, int(stack.ID), stack)
|
||||
}
|
||||
|
||||
// StackByWebhookID returns a pointer to a stack object by webhook ID.
|
||||
// It returns nil, errors.ErrObjectNotFound if there's no stack associated with the webhook ID.
|
||||
func (service ServiceTx) StackByWebhookID(id string) (*portainer.Stack, error) {
|
||||
var s portainer.Stack
|
||||
|
||||
err := service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FirstFn(&s, func(e portainer.Stack) bool {
|
||||
return e.AutoUpdate != nil && strings.EqualFold(e.AutoUpdate.Webhook, id)
|
||||
}),
|
||||
)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
// RefreshableStacks returns stacks that are configured for a periodic update
|
||||
func (service ServiceTx) RefreshableStacks() ([]portainer.Stack, error) {
|
||||
stacks := make([]portainer.Stack, 0)
|
||||
|
||||
return stacks, service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Stack{},
|
||||
dataservices.FilterFn(&stacks, func(e portainer.Stack) bool {
|
||||
return e.AutoUpdate != nil && e.AutoUpdate.Interval != ""
|
||||
}),
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ package tag
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,51 +11,96 @@ const (
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Tag, portainer.TagID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Tag, portainer.TagID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.Tag, portainer.TagID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// Tags return an array containing all the tags.
|
||||
func (service *Service) Tags() ([]portainer.Tag, error) {
|
||||
var tags = make([]portainer.Tag, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Tag{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Tag object: %s", obj)
|
||||
// }
|
||||
|
||||
// tags = append(tags, *tag)
|
||||
|
||||
// return &portainer.Tag{}, nil
|
||||
// })
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
// Tag returns a tag by ID.
|
||||
func (service *Service) Tag(ID portainer.TagID) (*portainer.Tag, error) {
|
||||
var tag portainer.Tag
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &tag)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
// CreateTag creates a new tag.
|
||||
func (service *Service) Create(tag *portainer.Tag) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
tag.ID = portainer.TagID(id)
|
||||
return int(tag.ID), tag
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// tag.ID = portainer.TagID(id)
|
||||
// return int(tag.ID), tag
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use UpdateTagFunc instead.
|
||||
func (service *Service) UpdateTag(ID portainer.TagID, tag *portainer.Tag) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTagFunc updates a tag inside a transaction avoiding data races.
|
||||
func (service *Service) UpdateTagFunc(ID portainer.TagID, updateFunc func(tag *portainer.Tag)) error {
|
||||
id := service.Connection.ConvertToKey(int(ID))
|
||||
tag := &portainer.Tag{}
|
||||
// id := service.connection.ConvertToKey(int(ID))
|
||||
// tag := &portainer.Tag{}
|
||||
|
||||
return service.Connection.UpdateObjectFunc(BucketName, id, tag, func() {
|
||||
updateFunc(tag)
|
||||
})
|
||||
// return service.connection.UpdateObjectFunc(BucketName, id, tag, func() {
|
||||
// updateFunc(tag)
|
||||
// })
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTag deletes a tag.
|
||||
func (service *Service) DeleteTag(ID portainer.TagID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,18 +2,60 @@ package tag
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.Tag, portainer.TagID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
func (service ServiceTx) BucketName() string {
|
||||
return BucketName
|
||||
}
|
||||
|
||||
// Tags return an array containing all the tags.
|
||||
func (service ServiceTx) Tags() ([]portainer.Tag, error) {
|
||||
var tags = make([]portainer.Tag, 0)
|
||||
|
||||
err := service.tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.Tag{},
|
||||
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")
|
||||
return nil, fmt.Errorf("failed to convert to Tag object: %s", obj)
|
||||
}
|
||||
|
||||
tags = append(tags, *tag)
|
||||
|
||||
return &portainer.Tag{}, nil
|
||||
})
|
||||
|
||||
return tags, err
|
||||
}
|
||||
|
||||
// Tag returns a tag by ID.
|
||||
func (service ServiceTx) Tag(ID portainer.TagID) (*portainer.Tag, error) {
|
||||
var tag portainer.Tag
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &tag)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &tag, nil
|
||||
}
|
||||
|
||||
// CreateTag creates a new tag.
|
||||
func (service ServiceTx) Create(tag *portainer.Tag) error {
|
||||
return service.Tx.CreateObject(
|
||||
return service.tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
tag.ID = portainer.TagID(id)
|
||||
@@ -22,7 +64,21 @@ func (service ServiceTx) Create(tag *portainer.Tag) error {
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateTag updates a tag
|
||||
func (service ServiceTx) UpdateTag(ID portainer.TagID, tag *portainer.Tag) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, tag)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTagFunc is a no-op inside a transaction
|
||||
func (service ServiceTx) UpdateTagFunc(ID portainer.TagID, updateFunc func(tag *portainer.Tag)) error {
|
||||
return errors.New("cannot be called inside a transaction")
|
||||
}
|
||||
|
||||
// DeleteTag deletes a tag.
|
||||
func (service ServiceTx) DeleteTag(ID portainer.TagID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,67 +1,91 @@
|
||||
package team
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "teams"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Team, portainer.TeamID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Team returns a Team by ID
|
||||
func (service *Service) Team(ID portainer.TeamID) (*portainer.Team, error) {
|
||||
var obj portainer.Team
|
||||
|
||||
err := service.connection.GetByID(int(ID), &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Team, portainer.TeamID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
}, nil
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// TeamByName returns a team by name.
|
||||
func (service *Service) TeamByName(name string) (*portainer.Team, error) {
|
||||
var t portainer.Team
|
||||
var team portainer.Team
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Team{},
|
||||
dataservices.FirstFn(&t, func(e portainer.Team) bool {
|
||||
return strings.EqualFold(e.Name, name)
|
||||
}),
|
||||
)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &t, nil
|
||||
db := service.connection.GetDB()
|
||||
tx := db.First(&team, `name = ?`, name)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
return &team, nil
|
||||
}
|
||||
|
||||
// Teams return an array containing all the teams.
|
||||
func (service *Service) Teams() ([]portainer.Team, error) {
|
||||
var teams = make([]portainer.Team, 0)
|
||||
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&teams)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return teams, nil
|
||||
}
|
||||
|
||||
// UpdateTeam saves a Team.
|
||||
func (service *Service) UpdateTeam(ID portainer.TeamID, team *portainer.Team) error {
|
||||
db := service.connection.GetDB()
|
||||
team.ID = ID
|
||||
tx := db.Save(&team)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeam creates a new Team.
|
||||
func (service *Service) Create(team *portainer.Team) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
team.ID = portainer.TeamID(id)
|
||||
return int(team.ID), team
|
||||
},
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
|
||||
tx := db.Create(&team)
|
||||
if tx.Error != nil {
|
||||
fmt.Println(tx.Error)
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeam deletes a Team.
|
||||
func (service *Service) DeleteTeam(ID portainer.TeamID) error {
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&portainer.Team{}).Delete("id = ?", ID)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,142 +1,120 @@
|
||||
package teammembership
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "team_membership"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.TeamMembership, portainer.TeamMembershipID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.TeamMembership, portainer.TeamMembershipID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.TeamMembership, portainer.TeamMembershipID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
service: service,
|
||||
tx: tx,
|
||||
}
|
||||
}
|
||||
|
||||
// TeamMembership returns a TeamMembership object by ID
|
||||
func (service *Service) TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error) {
|
||||
var obj portainer.TeamMembership
|
||||
|
||||
err := service.connection.GetByID(int(ID), &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// TeamMemberships return an array containing all the TeamMembership objects.
|
||||
func (service *Service) TeamMemberships() ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&memberships)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByUserID return an array containing all the TeamMembership objects where the specified userID is present.
|
||||
func (service *Service) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
return memberships, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool {
|
||||
return e.UserID == userID
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&memberships, `user_id = ?`, userID)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByTeamID return an array containing all the TeamMembership objects where the specified teamID is present.
|
||||
func (service *Service) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
return memberships, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool {
|
||||
return e.TeamID == teamID
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&memberships, `team_id = ?`, teamID)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// UpdateTeamMembership saves a TeamMembership object.
|
||||
func (service *Service) UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error {
|
||||
db := service.connection.GetDB()
|
||||
|
||||
membership.ID = ID
|
||||
tx := db.Save(&membership)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeamMembership creates a new TeamMembership object.
|
||||
func (service *Service) Create(membership *portainer.TeamMembership) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
membership.ID = portainer.TeamMembershipID(id)
|
||||
return int(membership.ID), membership
|
||||
},
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Create(&membership)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembership deletes a TeamMembership object.
|
||||
func (service *Service) DeleteTeamMembership(ID portainer.TeamMembershipID) error {
|
||||
return service.connection.DeleteByID(int(ID), &portainer.TeamMembership{})
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByUserID deletes all the TeamMembership object associated to a UserID.
|
||||
func (service *Service) DeleteTeamMembershipByUserID(userID portainer.UserID) error {
|
||||
return service.Connection.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//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
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
return db.Model(&portainer.TeamMembership{}).Delete("user_id = ?", userID).Error
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByTeamID deletes all the TeamMembership object associated to a TeamID.
|
||||
func (service *Service) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error {
|
||||
return service.Connection.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//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
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
return db.Model(&portainer.TeamMembership{}).Delete("team_id = ?", teamID).Error
|
||||
}
|
||||
|
||||
func (service *Service) DeleteTeamMembershipByTeamIDAndUserID(teamID portainer.TeamID, userID portainer.UserID) error {
|
||||
return service.Connection.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
|
||||
if membership.TeamID == teamID && membership.UserID == userID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
|
||||
return -1, false
|
||||
})
|
||||
db := service.connection.GetDB()
|
||||
return db.Model(&portainer.TeamMembership{}).Delete("team_id = ? AND user_id = ?", teamID, userID).Error
|
||||
}
|
||||
|
||||
@@ -1,113 +1,184 @@
|
||||
package teammembership
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.TeamMembership, portainer.TeamMembershipID]
|
||||
service *Service
|
||||
tx portainer.Transaction
|
||||
}
|
||||
|
||||
// TeamMembership returns a TeamMembership object by ID
|
||||
func (service ServiceTx) TeamMembership(ID portainer.TeamMembershipID) (*portainer.TeamMembership, error) {
|
||||
var membership portainer.TeamMembership
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.tx.GetObject(BucketName, identifier, &membership)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &membership, nil
|
||||
}
|
||||
|
||||
// TeamMemberships return an array containing all the TeamMembership objects.
|
||||
func (service ServiceTx) TeamMemberships() ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
// err := service.tx.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// }
|
||||
|
||||
// memberships = append(memberships, *membership)
|
||||
|
||||
// return &portainer.TeamMembership{}, nil
|
||||
// })
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByUserID return an array containing all the TeamMembership objects where the specified userID is present.
|
||||
func (service ServiceTx) TeamMembershipsByUserID(userID portainer.UserID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
return memberships, service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool {
|
||||
return e.UserID == userID
|
||||
}),
|
||||
)
|
||||
// err := service.tx.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// }
|
||||
|
||||
// if membership.UserID == userID {
|
||||
// memberships = append(memberships, *membership)
|
||||
// }
|
||||
|
||||
// return &portainer.TeamMembership{}, nil
|
||||
// })
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// TeamMembershipsByTeamID return an array containing all the TeamMembership objects where the specified teamID is present.
|
||||
func (service ServiceTx) TeamMembershipsByTeamID(teamID portainer.TeamID) ([]portainer.TeamMembership, error) {
|
||||
var memberships = make([]portainer.TeamMembership, 0)
|
||||
|
||||
return memberships, service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
dataservices.FilterFn(&memberships, func(e portainer.TeamMembership) bool {
|
||||
return e.TeamID == teamID
|
||||
}),
|
||||
)
|
||||
// err := service.tx.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// }
|
||||
|
||||
// if membership.TeamID == teamID {
|
||||
// memberships = append(memberships, *membership)
|
||||
// }
|
||||
|
||||
// return &portainer.TeamMembership{}, nil
|
||||
// })
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
// UpdateTeamMembership saves a TeamMembership object.
|
||||
func (service ServiceTx) UpdateTeamMembership(ID portainer.TeamMembershipID, membership *portainer.TeamMembership) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.UpdateObject(BucketName, identifier, membership)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateTeamMembership creates a new TeamMembership object.
|
||||
func (service ServiceTx) Create(membership *portainer.TeamMembership) error {
|
||||
return service.Tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
membership.ID = portainer.TeamMembershipID(id)
|
||||
return int(membership.ID), membership
|
||||
},
|
||||
)
|
||||
// return service.tx.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// membership.ID = portainer.TeamMembershipID(id)
|
||||
// return int(membership.ID), membership
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembership deletes a TeamMembership object.
|
||||
func (service ServiceTx) DeleteTeamMembership(ID portainer.TeamMembershipID) error {
|
||||
// identifier := service.service.connection.ConvertToKey(int(ID))
|
||||
// return service.tx.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByUserID deletes all the TeamMembership object associated to a UserID.
|
||||
func (service ServiceTx) DeleteTeamMembershipByUserID(userID portainer.UserID) error {
|
||||
return service.Tx.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
// return service.tx.DeleteAllObjects(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// return -1, false
|
||||
// }
|
||||
|
||||
if membership.UserID == userID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
// if membership.UserID == userID {
|
||||
// return int(membership.ID), true
|
||||
// }
|
||||
|
||||
return -1, false
|
||||
})
|
||||
// return -1, false
|
||||
// })
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTeamMembershipByTeamID deletes all the TeamMembership object associated to a TeamID.
|
||||
func (service ServiceTx) DeleteTeamMembershipByTeamID(teamID portainer.TeamID) error {
|
||||
return service.Tx.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
// return service.tx.DeleteAllObjects(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// return -1, false
|
||||
// }
|
||||
|
||||
if membership.TeamID == teamID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
// if membership.TeamID == teamID {
|
||||
// return int(membership.ID), true
|
||||
// }
|
||||
|
||||
return -1, false
|
||||
})
|
||||
// return -1, false
|
||||
// })
|
||||
return nil
|
||||
}
|
||||
|
||||
func (service ServiceTx) DeleteTeamMembershipByTeamIDAndUserID(teamID portainer.TeamID, userID portainer.UserID) error {
|
||||
return service.Tx.DeleteAllObjects(
|
||||
BucketName,
|
||||
&portainer.TeamMembership{},
|
||||
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")
|
||||
//return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
return -1, false
|
||||
}
|
||||
// return service.tx.DeleteAllObjects(
|
||||
// BucketName,
|
||||
// &portainer.TeamMembership{},
|
||||
// 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")
|
||||
// //return fmt.Errorf("Failed to convert to TeamMembership object: %s", obj)
|
||||
// return -1, false
|
||||
// }
|
||||
|
||||
if membership.TeamID == teamID && membership.UserID == userID {
|
||||
return int(membership.ID), true
|
||||
}
|
||||
// if membership.TeamID == teamID && membership.UserID == userID {
|
||||
// return int(membership.ID), true
|
||||
// }
|
||||
|
||||
return -1, false
|
||||
})
|
||||
// return -1, false
|
||||
// })
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,11 +21,6 @@ func (service *Service) BucketName() string {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
@@ -35,15 +30,16 @@ func NewService(connection portainer.Connection) (*Service, error) {
|
||||
func (service *Service) Info() (*portainer.TunnelServerInfo, error) {
|
||||
var info portainer.TunnelServerInfo
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(infoKey), &info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// err := service.connection.GetObject(BucketName, []byte(infoKey), &info)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
// UpdateInfo persists a TunnelServerInfo object.
|
||||
func (service *Service) UpdateInfo(settings *portainer.TunnelServerInfo) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(infoKey), settings)
|
||||
// return service.connection.UpdateObject(BucketName, []byte(infoKey), settings)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
type ServiceTx struct {
|
||||
dataservices.BaseDataServiceTx[portainer.User, portainer.UserID]
|
||||
}
|
||||
|
||||
// UserByUsername returns a user by username.
|
||||
func (service ServiceTx) UserByUsername(username string) (*portainer.User, error) {
|
||||
var u portainer.User
|
||||
|
||||
err := service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
dataservices.FirstFn(&u, func(e portainer.User) bool {
|
||||
return strings.EqualFold(e.Username, username)
|
||||
}),
|
||||
)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// UsersByRole return an array containing all the users with the specified role.
|
||||
func (service ServiceTx) UsersByRole(role portainer.UserRole) ([]portainer.User, error) {
|
||||
var users = make([]portainer.User, 0)
|
||||
|
||||
return users, service.Tx.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
dataservices.FilterFn(&users, func(e portainer.User) bool {
|
||||
return e.Role == role
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (service ServiceTx) Create(user *portainer.User) error {
|
||||
return service.Tx.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
user.ID = portainer.UserID(id)
|
||||
user.Username = strings.ToLower(user.Username)
|
||||
|
||||
return int(user.ID), user
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1,92 +1,96 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "users"
|
||||
|
||||
// Service represents a service for managing environment(endpoint) data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.User, portainer.UserID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
// NewService creates a new instance of a service.
|
||||
func NewService(connection portainer.Connection) (*Service, error) {
|
||||
err := connection.SetServiceName(BucketName)
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// User returns a user by ID
|
||||
func (service *Service) User(ID portainer.UserID) (*portainer.User, error) {
|
||||
var obj portainer.User
|
||||
|
||||
err := service.connection.GetByID(int(ID), &obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.User, portainer.UserID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
|
||||
return ServiceTx{
|
||||
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.User, portainer.UserID]{
|
||||
Bucket: BucketName,
|
||||
Connection: service.Connection,
|
||||
Tx: tx,
|
||||
},
|
||||
}
|
||||
return &obj, nil
|
||||
}
|
||||
|
||||
// UserByUsername returns a user by username.
|
||||
func (service *Service) UserByUsername(username string) (*portainer.User, error) {
|
||||
var u portainer.User
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
dataservices.FirstFn(&u, func(e portainer.User) bool {
|
||||
return strings.EqualFold(e.Username, username)
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.First(&u, `username = ?`, username)
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &u, nil
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
// Users return an array containing all the users.
|
||||
func (service *Service) Users() ([]portainer.User, error) {
|
||||
var users = make([]portainer.User, 0)
|
||||
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&users)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// UsersByRole return an array containing all the users with the specified role.
|
||||
func (service *Service) UsersByRole(role portainer.UserRole) ([]portainer.User, error) {
|
||||
var users = make([]portainer.User, 0)
|
||||
|
||||
return users, service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.User{},
|
||||
dataservices.FilterFn(&users, func(e portainer.User) bool {
|
||||
return e.Role == role
|
||||
}),
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Find(&users, `role = ?`, role)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// UpdateUser saves a user.
|
||||
func (service *Service) UpdateUser(ID portainer.UserID, user *portainer.User) error {
|
||||
db := service.connection.GetDB()
|
||||
user.ID = ID
|
||||
tx := db.Save(&user)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateUser creates a new user.
|
||||
func (service *Service) Create(user *portainer.User) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
user.ID = portainer.UserID(id)
|
||||
user.Username = strings.ToLower(user.Username)
|
||||
|
||||
return int(user.ID), user
|
||||
},
|
||||
)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Create(&user)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user.
|
||||
func (service *Service) DeleteUser(ID portainer.UserID) error {
|
||||
return service.connection.DeleteByID(int(ID), &portainer.User{})
|
||||
}
|
||||
|
||||
@@ -1,19 +1,12 @@
|
||||
package version
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "version"
|
||||
versionKey = "VERSION"
|
||||
updatingKey = "DB_UPDATING"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Service represents a service to manage stored versions.
|
||||
@@ -23,97 +16,136 @@ type Service struct {
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return &Service{
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (service *Service) SchemaVersion() (string, error) {
|
||||
v, err := service.Version()
|
||||
if err != nil {
|
||||
return "", err
|
||||
// DBVersion retrieves the stored database version.
|
||||
func (service *Service) DBVersion() (int, error) {
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.VersionKey)
|
||||
if tx.Error != nil {
|
||||
return 0, tx.Error
|
||||
}
|
||||
|
||||
return v.SchemaVersion, nil
|
||||
}
|
||||
|
||||
func (service *Service) UpdateSchemaVersion(version string) error {
|
||||
v, err := service.Version()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.SchemaVersion = version
|
||||
return service.UpdateVersion(v)
|
||||
return strconv.Atoi(version.Value)
|
||||
}
|
||||
|
||||
// Edition retrieves the stored portainer edition.
|
||||
func (service *Service) Edition() (portainer.SoftwareEdition, error) {
|
||||
v, err := service.Version()
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.EditionKey)
|
||||
if tx.Error != nil {
|
||||
return 0, tx.Error
|
||||
}
|
||||
e, err := strconv.Atoi(version.Value)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fmt.Println(portainer.SoftwareEdition(e))
|
||||
return portainer.SoftwareEdition(e), nil
|
||||
}
|
||||
|
||||
return portainer.SoftwareEdition(v.Edition), nil
|
||||
// StoreDBVersion store the database version.
|
||||
func (service *Service) StoreDBVersion(v int) error {
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&models.Version{}).Where("key = ?", models.VersionKey).Update("value", strconv.FormatInt(int64(v), 10)).Limit(1)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsUpdating retrieves the database updating status.
|
||||
func (service *Service) IsUpdating() (bool, error) {
|
||||
var isUpdating bool
|
||||
err := service.connection.GetObject(BucketName, []byte(updatingKey), &isUpdating)
|
||||
if err != nil && errors.Is(err, dserrors.ErrObjectNotFound) {
|
||||
return false, nil
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.UpdatingKey)
|
||||
if tx.Error != nil {
|
||||
return false, tx.Error
|
||||
}
|
||||
return isUpdating, err
|
||||
return version.Value == "true", nil
|
||||
}
|
||||
|
||||
// StoreIsUpdating store the database updating status.
|
||||
func (service *Service) StoreIsUpdating(isUpdating bool) error {
|
||||
return service.connection.DeleteObject(BucketName, []byte(updatingKey))
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&models.Version{}).Where("key = ?", models.UpdatingKey).Update("value", strconv.FormatBool(isUpdating)).Limit(1)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstanceID retrieves the stored instance ID.
|
||||
func (service *Service) InstanceID() (string, error) {
|
||||
v, err := service.Version()
|
||||
if err != nil {
|
||||
return "", err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.InstanceKey)
|
||||
if tx.Error != nil {
|
||||
return "", tx.Error
|
||||
}
|
||||
|
||||
return v.InstanceID, nil
|
||||
return version.Value, nil
|
||||
}
|
||||
|
||||
// StoreInstanceID store the instance ID.
|
||||
func (service *Service) UpdateInstanceID(id string) error {
|
||||
v, err := service.Version()
|
||||
if err != nil {
|
||||
if !dataservices.IsErrObjectNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
v = &models.Version{}
|
||||
func (service *Service) StoreInstanceID(ID string) error {
|
||||
db := service.connection.GetDB()
|
||||
tx := db.FirstOrCreate(&models.Version{Key: models.InstanceKey, Value: ID})
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
|
||||
v.InstanceID = id
|
||||
return service.UpdateVersion(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Version retrieve the version object.
|
||||
func (service *Service) Version() (*models.Version, error) {
|
||||
var v models.Version
|
||||
|
||||
err := service.connection.GetObject(BucketName, []byte(versionKey), &v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
db := service.connection.GetDB()
|
||||
var version models.Version
|
||||
tx := db.First(&version, `key = ?`, models.VersionKey)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
|
||||
return &v, nil
|
||||
return &version, nil
|
||||
}
|
||||
|
||||
// UpdateVersion persists a Version object.
|
||||
func (service *Service) UpdateVersion(version *models.Version) error {
|
||||
return service.connection.UpdateObject(BucketName, []byte(versionKey), version)
|
||||
db := service.connection.GetDB()
|
||||
tx := db.Model(&models.Version{}).Where("key = ?", models.VersionKey).Update("value", version).Limit(1)
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Version retrieve the version object.
|
||||
func (service *Service) GetAll() (map[string]interface{}, error) {
|
||||
db := service.connection.GetDB()
|
||||
var all map[string]interface{}
|
||||
tx := db.Find(&all)
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// Version retrieve the version object.
|
||||
func (service *Service) UpdateAll(all map[string]interface{}) error {
|
||||
db := service.connection.GetDB()
|
||||
db.Transaction(func(tx *gorm.DB) error {
|
||||
for k, v := range all {
|
||||
tx := db.Model(&models.Version{}).Where(models.Version{Key: k}).FirstOrCreate(&models.Version{Key: k, Value: fmt.Sprintf("%v", v)})
|
||||
if tx.Error != nil {
|
||||
tx.Rollback()
|
||||
return tx.Error
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,89 +1,160 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
)
|
||||
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
const BucketName = "webhooks"
|
||||
const (
|
||||
// BucketName represents the name of the bucket where this service stores data.
|
||||
BucketName = "webhooks"
|
||||
)
|
||||
|
||||
// Service represents a service for managing webhook data.
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.Webhook, portainer.WebhookID]
|
||||
connection portainer.Connection
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// err := connection.SetServiceName(BucketName)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &Service{
|
||||
BaseDataService: dataservices.BaseDataService[portainer.Webhook, portainer.WebhookID]{
|
||||
Bucket: BucketName,
|
||||
Connection: connection,
|
||||
},
|
||||
connection: connection,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Webhooks returns an array of all webhooks
|
||||
func (service *Service) Webhooks() ([]portainer.Webhook, error) {
|
||||
var webhooks = make([]portainer.Webhook, 0)
|
||||
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Webhook{},
|
||||
// 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")
|
||||
// return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
// }
|
||||
|
||||
// webhooks = append(webhooks, *webhook)
|
||||
|
||||
// return &portainer.Webhook{}, nil
|
||||
// })
|
||||
|
||||
return webhooks, nil
|
||||
}
|
||||
|
||||
// Webhook returns a webhook by ID.
|
||||
func (service *Service) Webhook(ID portainer.WebhookID) (*portainer.Webhook, error) {
|
||||
var webhook portainer.Webhook
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
|
||||
// err := service.connection.GetObject(BucketName, identifier, &webhook)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &webhook, nil
|
||||
}
|
||||
|
||||
// WebhookByResourceID returns a webhook by the ResourceID it is associated with.
|
||||
func (service *Service) WebhookByResourceID(ID string) (*portainer.Webhook, error) {
|
||||
var w portainer.Webhook
|
||||
// var w *portainer.Webhook
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Webhook{},
|
||||
// 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")
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Webhook{},
|
||||
dataservices.FirstFn(&w, func(e portainer.Webhook) bool {
|
||||
return e.ResourceID == ID
|
||||
}),
|
||||
)
|
||||
// return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
// }
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &w, nil
|
||||
}
|
||||
// if webhook.ResourceID == ID {
|
||||
// w = webhook
|
||||
// return nil, stop
|
||||
// }
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
// return &portainer.Webhook{}, nil
|
||||
// })
|
||||
|
||||
return nil, err
|
||||
// if errors.Is(err, stop) {
|
||||
// return w, nil
|
||||
// }
|
||||
|
||||
// if err == nil {
|
||||
// return nil, dserrors.ErrObjectNotFound
|
||||
// }
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// WebhookByToken returns a webhook by the random token it is associated with.
|
||||
func (service *Service) WebhookByToken(token string) (*portainer.Webhook, error) {
|
||||
var w portainer.Webhook
|
||||
// var w *portainer.Webhook
|
||||
// stop := fmt.Errorf("ok")
|
||||
// err := service.connection.GetAll(
|
||||
// BucketName,
|
||||
// &portainer.Webhook{},
|
||||
// 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")
|
||||
|
||||
err := service.Connection.GetAll(
|
||||
BucketName,
|
||||
&portainer.Webhook{},
|
||||
dataservices.FirstFn(&w, func(e portainer.Webhook) bool {
|
||||
return e.Token == token
|
||||
}),
|
||||
)
|
||||
// return nil, fmt.Errorf("Failed to convert to Webhook object: %s", obj)
|
||||
// }
|
||||
|
||||
if errors.Is(err, dataservices.ErrStop) {
|
||||
return &w, nil
|
||||
}
|
||||
// if webhook.Token == token {
|
||||
// w = webhook
|
||||
// return nil, stop
|
||||
// }
|
||||
|
||||
if err == nil {
|
||||
return nil, dserrors.ErrObjectNotFound
|
||||
}
|
||||
// return &portainer.Webhook{}, nil
|
||||
// })
|
||||
|
||||
return nil, err
|
||||
// if errors.Is(err, stop) {
|
||||
// return w, nil
|
||||
// }
|
||||
|
||||
// if err == nil {
|
||||
// return nil, dserrors.ErrObjectNotFound
|
||||
// }
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DeleteWebhook deletes a webhook.
|
||||
func (service *Service) DeleteWebhook(ID portainer.WebhookID) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.DeleteObject(BucketName, identifier)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateWebhook assign an ID to a new webhook and saves it.
|
||||
func (service *Service) Create(webhook *portainer.Webhook) error {
|
||||
return service.Connection.CreateObject(
|
||||
BucketName,
|
||||
func(id uint64) (int, interface{}) {
|
||||
webhook.ID = portainer.WebhookID(id)
|
||||
return int(webhook.ID), webhook
|
||||
},
|
||||
)
|
||||
// return service.connection.CreateObject(
|
||||
// BucketName,
|
||||
// func(id uint64) (int, interface{}) {
|
||||
// webhook.ID = portainer.WebhookID(id)
|
||||
// return int(webhook.ID), webhook
|
||||
// },
|
||||
// )
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateWebhook update a webhook.
|
||||
func (service *Service) UpdateWebhook(ID portainer.WebhookID, webhook *portainer.Webhook) error {
|
||||
// identifier := service.connection.ConvertToKey(int(ID))
|
||||
// return service.connection.UpdateObject(BucketName, identifier, webhook)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,77 +1,189 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path"
|
||||
// "time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
// "github.com/portainer/portainer/api/database/models"
|
||||
// "github.com/rs/zerolog/log"
|
||||
// )
|
||||
|
||||
func (store *Store) Backup() (string, error) {
|
||||
if err := store.createBackupPath(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
// var backupDefaults = struct {
|
||||
// backupDir string
|
||||
// commonDir string
|
||||
// }{
|
||||
// "backups",
|
||||
// "common",
|
||||
// }
|
||||
|
||||
backupFilename := store.backupFilename()
|
||||
log.Info().Str("from", store.connection.GetDatabaseFilePath()).Str("to", backupFilename).Msgf("Backing up database")
|
||||
err := store.fileService.Copy(store.connection.GetDatabaseFilePath(), backupFilename, true)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("failed to create backup file")
|
||||
return "", err
|
||||
}
|
||||
// //
|
||||
// // Backup Helpers
|
||||
// //
|
||||
|
||||
return backupFilename, nil
|
||||
}
|
||||
// // createBackupFolders create initial folders for backups
|
||||
// func (store *Store) createBackupFolders() {
|
||||
// // create common dir
|
||||
// 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")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
func (store *Store) Restore() error {
|
||||
backupFilename := store.backupFilename()
|
||||
return store.RestoreFromFile(backupFilename)
|
||||
}
|
||||
// func (store *Store) databasePath() string {
|
||||
// return store.connection.GetDatabaseFilePath()
|
||||
// }
|
||||
|
||||
func (store *Store) RestoreFromFile(backupFilename string) error {
|
||||
if exists, _ := store.fileService.FileExists(backupFilename); !exists {
|
||||
log.Error().Str("backupFilename", backupFilename).Msg("backup file does not exist")
|
||||
return os.ErrNotExist
|
||||
}
|
||||
// func (store *Store) commonBackupDir() string {
|
||||
// return path.Join(store.connection.GetStorePath(), backupDefaults.backupDir, backupDefaults.commonDir)
|
||||
// }
|
||||
|
||||
if err := store.fileService.Copy(backupFilename, store.connection.GetDatabaseFilePath(), true); err != nil {
|
||||
log.Error().Err(err).Msg("error while restoring backup.")
|
||||
return err
|
||||
}
|
||||
// func (store *Store) copyDBFile(from string, to string) error {
|
||||
// log.Info().Str("from", from).Str("to", to).Msg("copying DB file")
|
||||
|
||||
log.Info().Str("from", store.connection.GetDatabaseFilePath()).Str("to", backupFilename).Msgf("database restored")
|
||||
// err := store.fileService.Copy(from, to, true)
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msg("failed")
|
||||
// }
|
||||
|
||||
// determine the db version
|
||||
store.Open()
|
||||
version, err := store.VersionService.Version()
|
||||
// return err
|
||||
// }
|
||||
|
||||
edition := "CE"
|
||||
if version.Edition == 2 {
|
||||
edition = "EE"
|
||||
}
|
||||
// // BackupOptions provide a helper to inject backup options
|
||||
// type BackupOptions struct {
|
||||
// Version string
|
||||
// BackupDir string
|
||||
// BackupFileName string
|
||||
// BackupPath string
|
||||
// }
|
||||
|
||||
if err == nil {
|
||||
log.Info().Str("version", version.SchemaVersion).Msgf("Restored database version: Portainer %s %s", edition, version.SchemaVersion)
|
||||
}
|
||||
// // getBackupRestoreOptions returns options to store db at common backup dir location; used by:
|
||||
// // - db backup prior to version upgrade
|
||||
// // - db rollback
|
||||
// func getBackupRestoreOptions(backupDir string) *BackupOptions {
|
||||
// return &BackupOptions{
|
||||
// BackupDir: backupDir, //connection.commonBackupDir(),
|
||||
// BackupFileName: beforePortainerVersionUpgradeBackup,
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// // Backup current database with default options
|
||||
// func (store *Store) Backup(version *models.Version) (string, error) {
|
||||
// if version == nil {
|
||||
// return store.backupWithOptions(nil)
|
||||
// }
|
||||
|
||||
func (store *Store) createBackupPath() error {
|
||||
backupDir := path.Join(store.connection.GetStorePath(), "backups")
|
||||
if exists, _ := store.fileService.FileExists(backupDir); !exists {
|
||||
if err := os.MkdirAll(backupDir, 0700); err != nil {
|
||||
log.Error().Err(err).Msg("error while creating backup folder")
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// return store.backupWithOptions(&BackupOptions{
|
||||
// Version: version.SchemaVersion,
|
||||
// })
|
||||
// }
|
||||
|
||||
func (store *Store) backupFilename() string {
|
||||
return path.Join(store.connection.GetStorePath(), "backups", store.connection.GetDatabaseFileName()+".bak")
|
||||
}
|
||||
// func (store *Store) setupOptions(options *BackupOptions) *BackupOptions {
|
||||
// if options == nil {
|
||||
// options = &BackupOptions{}
|
||||
// }
|
||||
// if options.Version == "" {
|
||||
// v, err := store.VersionService.Version()
|
||||
// if err != nil {
|
||||
// options.Version = ""
|
||||
// }
|
||||
// options.Version = v.SchemaVersion
|
||||
// }
|
||||
// if options.BackupDir == "" {
|
||||
// options.BackupDir = store.commonBackupDir()
|
||||
// }
|
||||
// if options.BackupFileName == "" {
|
||||
// options.BackupFileName = fmt.Sprintf("%s.%s.%s", store.connection.GetDatabaseFileName(), options.Version, time.Now().Format("20060102150405"))
|
||||
// }
|
||||
// if options.BackupPath == "" {
|
||||
// options.BackupPath = path.Join(options.BackupDir, options.BackupFileName)
|
||||
// }
|
||||
// return options
|
||||
// }
|
||||
|
||||
func (store *Store) databasePath() string {
|
||||
return store.connection.GetDatabaseFilePath()
|
||||
}
|
||||
// // BackupWithOptions backup current database with options
|
||||
// func (store *Store) backupWithOptions(options *BackupOptions) (string, error) {
|
||||
// log.Info().Msg("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: %w",
|
||||
// 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: %w",
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
|
||||
// return options.BackupPath, nil
|
||||
// }
|
||||
|
||||
// // RestoreWithOptions previously saved backup for the current Edition with options
|
||||
// // Restore strategies:
|
||||
// // - default: restore latest from current edition
|
||||
// // - restore a specific
|
||||
// func (store *Store) restoreWithOptions(options *BackupOptions) error {
|
||||
// options = store.setupOptions(options)
|
||||
|
||||
// // 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")
|
||||
|
||||
// return err
|
||||
// }
|
||||
|
||||
// err = store.Close()
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msg("error while closing store before restore")
|
||||
|
||||
// return err
|
||||
// }
|
||||
|
||||
// log.Info().Msg("restoring DB backup")
|
||||
// err = store.copyDBFile(options.BackupPath, store.databasePath())
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// _, err = store.Open()
|
||||
// return err
|
||||
// }
|
||||
|
||||
// // RemoveWithOptions removes backup database based on supplied options
|
||||
// func (store *Store) removeWithOptions(options *BackupOptions) error {
|
||||
// log.Info().Msg("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")
|
||||
// return err
|
||||
// }
|
||||
|
||||
// log.Info().Str("path", options.BackupPath).Msg("removing DB file")
|
||||
// err = os.Remove(options.BackupPath)
|
||||
// if err != nil {
|
||||
// log.Error().Err(err).Msg("failed")
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
@@ -1,80 +1,107 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "os"
|
||||
// "path"
|
||||
// "testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
// portainer "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/database/models"
|
||||
// )
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
// func TestCreateBackupFolders(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, true, true)
|
||||
|
||||
func TestStoreCreation(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, true)
|
||||
if store == nil {
|
||||
t.Fatal("Expect to create a store")
|
||||
}
|
||||
// connection := store.GetConnection()
|
||||
// backupPath := path.Join(connection.GetStorePath(), backupDefaults.backupDir)
|
||||
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
// if isFileExist(backupPath) {
|
||||
// t.Error("Expect backups folder to not exist")
|
||||
// }
|
||||
|
||||
if portainer.SoftwareEdition(v.Edition) != portainer.PortainerCE {
|
||||
t.Error("Expect to get CE Edition")
|
||||
}
|
||||
// store.createBackupFolders()
|
||||
// if !isFileExist(backupPath) {
|
||||
// t.Error("Expect backups folder to exist")
|
||||
// }
|
||||
// }
|
||||
|
||||
if v.SchemaVersion != portainer.APIVersion {
|
||||
t.Error("Expect to get APIVersion")
|
||||
}
|
||||
}
|
||||
// func TestStoreCreation(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, true, true)
|
||||
// if store == nil {
|
||||
// t.Error("Expect to create a store")
|
||||
// }
|
||||
|
||||
func TestBackup(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, true)
|
||||
backupFileName := store.backupFilename()
|
||||
t.Run(fmt.Sprintf("Backup should create %s", backupFileName), func(t *testing.T) {
|
||||
v := models.Version{
|
||||
Edition: int(portainer.PortainerCE),
|
||||
SchemaVersion: portainer.APIVersion,
|
||||
}
|
||||
store.VersionService.UpdateVersion(&v)
|
||||
store.Backup()
|
||||
// if store.CheckCurrentEdition() != nil {
|
||||
// t.Error("Expect to get CE Edition")
|
||||
// }
|
||||
// }
|
||||
|
||||
if !isFileExist(backupFileName) {
|
||||
t.Errorf("Expect backup file to be created %s", backupFileName)
|
||||
}
|
||||
})
|
||||
}
|
||||
// func TestBackup(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, true, true)
|
||||
// connection := store.GetConnection()
|
||||
|
||||
func TestRestore(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
// t.Run("Backup should create default db backup", func(t *testing.T) {
|
||||
// v := models.Version{
|
||||
// SchemaVersion: portainer.APIVersion,
|
||||
// }
|
||||
// store.VersionService.UpdateVersion(&v)
|
||||
// store.backupWithOptions(nil)
|
||||
|
||||
t.Run(fmt.Sprintf("Basic Restore"), func(t *testing.T) {
|
||||
// override and set initial db version and edition
|
||||
updateEdition(store, portainer.PortainerCE)
|
||||
updateVersion(store, "2.4")
|
||||
// backupFileName := path.Join(connection.GetStorePath(), "backups", "common", fmt.Sprintf("portainer.edb.%s.*", portainer.APIVersion))
|
||||
// if !isFileExist(backupFileName) {
|
||||
// t.Errorf("Expect backup file to be created %s", backupFileName)
|
||||
// }
|
||||
// })
|
||||
|
||||
store.Backup()
|
||||
updateVersion(store, "2.16")
|
||||
testVersion(store, "2.16", t)
|
||||
store.Restore()
|
||||
// t.Run("BackupWithOption should create a name specific backup at common path", func(t *testing.T) {
|
||||
// store.backupWithOptions(&BackupOptions{
|
||||
// BackupFileName: beforePortainerVersionUpgradeBackup,
|
||||
// BackupDir: store.commonBackupDir(),
|
||||
// })
|
||||
// backupFileName := path.Join(connection.GetStorePath(), "backups", "common", beforePortainerVersionUpgradeBackup)
|
||||
// if !isFileExist(backupFileName) {
|
||||
// t.Errorf("Expect backup file to be created %s", backupFileName)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
// check if the restore is successful and the version is correct
|
||||
testVersion(store, "2.4", t)
|
||||
})
|
||||
// func TestRemoveWithOptions(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, true, true)
|
||||
|
||||
t.Run(fmt.Sprintf("Basic Restore After Multiple Backups"), func(t *testing.T) {
|
||||
// override and set initial db version and edition
|
||||
updateEdition(store, portainer.PortainerCE)
|
||||
updateVersion(store, "2.4")
|
||||
store.Backup()
|
||||
updateVersion(store, "2.14")
|
||||
updateVersion(store, "2.16")
|
||||
testVersion(store, "2.16", t)
|
||||
store.Restore()
|
||||
// t.Run("successfully removes file if existent", func(t *testing.T) {
|
||||
// store.createBackupFolders()
|
||||
// options := &BackupOptions{
|
||||
// BackupDir: store.commonBackupDir(),
|
||||
// BackupFileName: "test.txt",
|
||||
// }
|
||||
|
||||
// check if the restore is successful and the version is correct
|
||||
testVersion(store, "2.4", t)
|
||||
})
|
||||
}
|
||||
// filePath := path.Join(options.BackupDir, options.BackupFileName)
|
||||
// f, err := os.Create(filePath)
|
||||
// if err != nil {
|
||||
// t.Fatalf("file should be created; err=%s", err)
|
||||
// }
|
||||
// f.Close()
|
||||
|
||||
// err = store.removeWithOptions(options)
|
||||
// if err != nil {
|
||||
// t.Errorf("RemoveWithOptions should successfully remove file; err=%v", err)
|
||||
// }
|
||||
|
||||
// if isFileExist(f.Name()) {
|
||||
// t.Errorf("RemoveWithOptions should successfully remove file; file=%s", f.Name())
|
||||
// }
|
||||
// })
|
||||
|
||||
// t.Run("fails to removes file if non-existent", func(t *testing.T) {
|
||||
// options := &BackupOptions{
|
||||
// BackupDir: store.commonBackupDir(),
|
||||
// BackupFileName: "test.txt",
|
||||
// }
|
||||
|
||||
// err := store.removeWithOptions(options)
|
||||
// if err == nil {
|
||||
// t.Error("RemoveWithOptions should fail for non-existent file")
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
@@ -3,14 +3,14 @@ package datastore
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/mattn/go-sqlite3"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
portainerErrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
@@ -25,26 +25,25 @@ func NewStore(storePath string, fileService portainer.FileService, connection po
|
||||
|
||||
// Open opens and initializes the BoltDB database.
|
||||
func (store *Store) Open() (newStore bool, err error) {
|
||||
|
||||
encryptionReq, err := store.connection.NeedsEncryptionMigration()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if encryptionReq {
|
||||
backupFilename, err := store.Backup()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to backup database prior to encrypting: %w", err)
|
||||
}
|
||||
|
||||
err = store.encryptDB()
|
||||
if err != nil {
|
||||
store.RestoreFromFile(backupFilename) // restore from backup if encryption fails
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
err = store.connection.Open()
|
||||
if err != nil {
|
||||
fmt.Printf("connection open returned error: %s\n", err.Error())
|
||||
if store.IsErrObjectNotFound(err) || store.IsErrNoSuchTable(err) {
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -56,7 +55,7 @@ func (store *Store) Open() (newStore bool, err error) {
|
||||
// If no settings object exists then assume we have a new store
|
||||
_, err = store.SettingsService.Settings()
|
||||
if err != nil {
|
||||
if store.IsErrObjectNotFound(err) {
|
||||
if store.IsErrObjectNotFound(err) || store.IsErrNoSuchTable(err) {
|
||||
return true, nil
|
||||
}
|
||||
return false, err
|
||||
@@ -69,32 +68,9 @@ func (store *Store) Close() error {
|
||||
return store.connection.Close()
|
||||
}
|
||||
|
||||
func (store *Store) UpdateTx(fn func(dataservices.DataStoreTx) error) error {
|
||||
return store.connection.UpdateTx(func(tx portainer.Transaction) error {
|
||||
return fn(&StoreTx{
|
||||
store: store,
|
||||
tx: tx,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (store *Store) ViewTx(fn func(dataservices.DataStoreTx) error) error {
|
||||
return store.connection.ViewTx(func(tx portainer.Transaction) error {
|
||||
return fn(&StoreTx{
|
||||
store: store,
|
||||
tx: tx,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// BackupTo backs up db to a provided writer.
|
||||
// It does hot backup and doesn't block other database reads and writes
|
||||
func (store *Store) BackupTo(w io.Writer) error {
|
||||
return store.connection.BackupTo(w)
|
||||
}
|
||||
|
||||
// CheckCurrentEdition checks if current edition is community edition
|
||||
func (store *Store) CheckCurrentEdition() error {
|
||||
fmt.Println("Edition:", store.edition(), portainer.Edition)
|
||||
if store.edition() != portainer.Edition {
|
||||
return portainerErrors.ErrWrongDBEdition
|
||||
}
|
||||
@@ -109,9 +85,19 @@ func (store *Store) edition() portainer.SoftwareEdition {
|
||||
return edition
|
||||
}
|
||||
|
||||
func (store *Store) IsErrNoSuchTable(e error) bool {
|
||||
return errors.Is(e, portainerErrors.ErrNoSuchTable)
|
||||
}
|
||||
|
||||
// TODO: move the use of this to dataservices.IsErrObjectNotFound()?
|
||||
func (store *Store) IsErrObjectNotFound(e error) bool {
|
||||
return errors.Is(e, portainerErrors.ErrObjectNotFound)
|
||||
var sqliteErr sqlite3.Error
|
||||
errNotFound := false
|
||||
if errors.As(e, &sqliteErr) {
|
||||
errNotFound = sqliteErr.Code == sqlite3.ErrError
|
||||
}
|
||||
|
||||
return errNotFound || errors.Is(e, gorm.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
func (store *Store) Connection() portainer.Connection {
|
||||
@@ -119,7 +105,7 @@ func (store *Store) Connection() portainer.Connection {
|
||||
}
|
||||
|
||||
func (store *Store) Rollback(force bool) error {
|
||||
return store.connectionRollback(force)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) encryptDB() error {
|
||||
@@ -139,7 +125,7 @@ func (store *Store) encryptDB() error {
|
||||
log.Info().Msg("encrypting database")
|
||||
|
||||
// export file path for backup
|
||||
exportFilename := path.Join(store.databasePath() + "." + fmt.Sprintf("backup-%d.json", time.Now().Unix()))
|
||||
exportFilename := path.Join(store.connection.GetDatabaseFilePath() + "." + fmt.Sprintf("backup-%d.json", time.Now().Unix()))
|
||||
|
||||
log.Info().Str("filename", exportFilename).Msg("exporting database backup")
|
||||
|
||||
|
||||
@@ -1,416 +1,416 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/dchest/uniuri"
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/chisel"
|
||||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
adminUsername = "admin"
|
||||
adminPassword = "password"
|
||||
standardUsername = "standard"
|
||||
standardPassword = "password"
|
||||
agentOnDockerEnvironmentUrl = "tcp://192.168.167.207:30775"
|
||||
edgeAgentOnKubernetesEnvironmentUrl = "tcp://192.168.167.207"
|
||||
kubernetesLocalEnvironmentUrl = "https://kubernetes.default.svc"
|
||||
)
|
||||
|
||||
// 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 := MustNewTestStore(t, true, true)
|
||||
|
||||
testCases := map[string]func(t *testing.T){
|
||||
"User Accounts": func(t *testing.T) {
|
||||
store.testUserAccounts(t)
|
||||
},
|
||||
"Environments": func(t *testing.T) {
|
||||
store.testEnvironments(t)
|
||||
},
|
||||
"Settings": func(t *testing.T) {
|
||||
store.testSettings(t)
|
||||
},
|
||||
"SSL Settings": func(t *testing.T) {
|
||||
store.testSSLSettings(t)
|
||||
},
|
||||
"Tunnel Server": func(t *testing.T) {
|
||||
store.testTunnelServer(t)
|
||||
},
|
||||
"Custom Templates": func(t *testing.T) {
|
||||
store.testCustomTemplates(t)
|
||||
},
|
||||
"Registries": func(t *testing.T) {
|
||||
store.testRegistries(t)
|
||||
},
|
||||
"Resource Control": func(t *testing.T) {
|
||||
store.testResourceControl(t)
|
||||
},
|
||||
"Schedules": func(t *testing.T) {
|
||||
store.testSchedules(t)
|
||||
},
|
||||
"Tags": func(t *testing.T) {
|
||||
store.testTags(t)
|
||||
},
|
||||
|
||||
// "Test Title": func(t *testing.T) {
|
||||
// },
|
||||
}
|
||||
|
||||
for name, test := range testCases {
|
||||
t.Run(name, test)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (store *Store) testEnvironments(t *testing.T) {
|
||||
id := store.CreateEndpoint(t, "local", portainer.KubernetesLocalEnvironment, "", true)
|
||||
store.CreateEndpointRelation(id)
|
||||
|
||||
id = store.CreateEndpoint(t, "agent", portainer.AgentOnDockerEnvironment, agentOnDockerEnvironmentUrl, true)
|
||||
store.CreateEndpointRelation(id)
|
||||
|
||||
id = store.CreateEndpoint(t, "edge", portainer.EdgeAgentOnKubernetesEnvironment, edgeAgentOnKubernetesEnvironmentUrl, true)
|
||||
store.CreateEndpointRelation(id)
|
||||
}
|
||||
|
||||
func newEndpoint(endpointType portainer.EndpointType, id portainer.EndpointID, name, URL string, TLS bool) *portainer.Endpoint {
|
||||
endpoint := &portainer.Endpoint{
|
||||
ID: id,
|
||||
Name: name,
|
||||
URL: URL,
|
||||
Type: endpointType,
|
||||
GroupID: portainer.EndpointGroupID(1),
|
||||
PublicURL: "",
|
||||
TLSConfig: portainer.TLSConfiguration{
|
||||
TLS: false,
|
||||
},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
Status: portainer.EndpointStatusUp,
|
||||
Snapshots: []portainer.DockerSnapshot{},
|
||||
Kubernetes: portainer.KubernetesDefault(),
|
||||
}
|
||||
|
||||
if TLS {
|
||||
endpoint.TLSConfig = portainer.TLSConfiguration{
|
||||
TLS: true,
|
||||
TLSSkipVerify: true,
|
||||
}
|
||||
}
|
||||
|
||||
return endpoint
|
||||
}
|
||||
|
||||
func setEndpointAuthorizations(endpoint *portainer.Endpoint) {
|
||||
endpoint.SecuritySettings = portainer.EndpointSecuritySettings{
|
||||
AllowVolumeBrowserForRegularUsers: false,
|
||||
EnableHostManagementFeatures: false,
|
||||
|
||||
AllowSysctlSettingForRegularUsers: true,
|
||||
AllowBindMountsForRegularUsers: true,
|
||||
AllowPrivilegedModeForRegularUsers: true,
|
||||
AllowHostNamespaceForRegularUsers: true,
|
||||
AllowContainerCapabilitiesForRegularUsers: true,
|
||||
AllowDeviceMappingForRegularUsers: true,
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) CreateEndpoint(t *testing.T, name string, endpointType portainer.EndpointType, URL string, tls bool) portainer.EndpointID {
|
||||
is := assert.New(t)
|
||||
|
||||
var expectedEndpoint *portainer.Endpoint
|
||||
id := portainer.EndpointID(store.Endpoint().GetNextIdentifier())
|
||||
|
||||
switch endpointType {
|
||||
case portainer.DockerEnvironment:
|
||||
if URL == "" {
|
||||
URL = "unix:///var/run/docker.sock"
|
||||
if runtime.GOOS == "windows" {
|
||||
URL = "npipe:////./pipe/docker_engine"
|
||||
}
|
||||
}
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.AgentOnDockerEnvironment:
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.AgentOnKubernetesEnvironment:
|
||||
URL = strings.TrimPrefix(URL, "tcp://")
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
case portainer.EdgeAgentOnKubernetesEnvironment:
|
||||
cs := chisel.NewService(store, nil, nil)
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
edgeKey := cs.GenerateEdgeKey(URL, "", int(id))
|
||||
expectedEndpoint.EdgeKey = edgeKey
|
||||
store.testTunnelServer(t)
|
||||
|
||||
case portainer.KubernetesLocalEnvironment:
|
||||
if URL == "" {
|
||||
URL = kubernetesLocalEnvironmentUrl
|
||||
}
|
||||
expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
}
|
||||
|
||||
setEndpointAuthorizations(expectedEndpoint)
|
||||
store.Endpoint().Create(expectedEndpoint)
|
||||
|
||||
endpoint, err := store.Endpoint().Endpoint(id)
|
||||
is.NoError(err, "Endpoint() should not return an error")
|
||||
is.Equal(expectedEndpoint, endpoint, "endpoint should be the same")
|
||||
|
||||
return endpoint.ID
|
||||
}
|
||||
|
||||
func (store *Store) CreateEndpointRelation(id portainer.EndpointID) {
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: id,
|
||||
EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
}
|
||||
|
||||
store.EndpointRelation().Create(relation)
|
||||
}
|
||||
|
||||
func (store *Store) testSSLSettings(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
ssl := &portainer.SSLSettings{
|
||||
CertPath: "/data/certs/cert.pem",
|
||||
HTTPEnabled: true,
|
||||
KeyPath: "/data/certs/key.pem",
|
||||
SelfSigned: true,
|
||||
}
|
||||
|
||||
store.SSLSettings().UpdateSettings(ssl)
|
||||
|
||||
settings, err := store.SSLSettings().Settings()
|
||||
is.NoError(err, "Get sslsettings should succeed")
|
||||
is.Equal(ssl, settings, "Stored SSLSettings should be the same as what is read out")
|
||||
}
|
||||
|
||||
func (store *Store) testTunnelServer(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
expectPrivateKeySeed := uniuri.NewLen(16)
|
||||
|
||||
err := store.TunnelServer().UpdateInfo(&portainer.TunnelServerInfo{PrivateKeySeed: expectPrivateKeySeed})
|
||||
is.NoError(err, "UpdateInfo should have succeeded")
|
||||
|
||||
serverInfo, err := store.TunnelServer().Info()
|
||||
is.NoError(err, "Info should have succeeded")
|
||||
|
||||
is.Equal(expectPrivateKeySeed, serverInfo.PrivateKeySeed, "hashed passwords should not differ")
|
||||
}
|
||||
|
||||
// add users, read them back and check the details are unchanged
|
||||
func (store *Store) testUserAccounts(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
err := store.createAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
is.NoError(err, "CreateAccount should succeed")
|
||||
store.checkAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
is.NoError(err, "Account failure")
|
||||
|
||||
err = store.createAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
is.NoError(err, "CreateAccount should succeed")
|
||||
store.checkAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
is.NoError(err, "Account failure")
|
||||
}
|
||||
|
||||
// create an account with the provided details
|
||||
func (store *Store) createAccount(username, password string, role portainer.UserRole) error {
|
||||
var err error
|
||||
user := &portainer.User{Username: username, Role: role}
|
||||
|
||||
// encrypt the password
|
||||
cs := &crypto.Service{}
|
||||
user.Password, err = cs.Hash(password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = store.User().Create(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) checkAccount(username, expectPassword string, expectRole portainer.UserRole) error {
|
||||
// Read the account for username. Check password and role is what we expect
|
||||
|
||||
user, err := store.User().UserByUsername(username)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to find user")
|
||||
}
|
||||
|
||||
if user.Username != username || user.Role != expectRole {
|
||||
return fmt.Errorf("%s user details do not match", user.Username)
|
||||
}
|
||||
|
||||
// Check the password
|
||||
cs := &crypto.Service{}
|
||||
expectPasswordHash, err := cs.Hash(expectPassword)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "hash failed")
|
||||
}
|
||||
|
||||
if user.Password != expectPasswordHash {
|
||||
return fmt.Errorf("%s user password hash failure", user.Username)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) testSettings(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// since many settings are default and basically nil, I'm going to update some and read them back
|
||||
expectedSettings, err := store.Settings().Settings()
|
||||
is.NoError(err, "Settings() should not return an error")
|
||||
expectedSettings.TemplatesURL = "http://portainer.io/application-templates"
|
||||
expectedSettings.HelmRepositoryURL = "http://portainer.io/helm-repository"
|
||||
expectedSettings.EdgeAgentCheckinInterval = 60
|
||||
expectedSettings.AuthenticationMethod = portainer.AuthenticationLDAP
|
||||
expectedSettings.LDAPSettings = portainer.LDAPSettings{
|
||||
AnonymousMode: true,
|
||||
StartTLS: true,
|
||||
AutoCreateUsers: true,
|
||||
Password: "random",
|
||||
}
|
||||
expectedSettings.SnapshotInterval = "10m"
|
||||
|
||||
err = store.Settings().UpdateSettings(expectedSettings)
|
||||
is.NoError(err, "UpdateSettings() should succeed")
|
||||
|
||||
settings, err := store.Settings().Settings()
|
||||
is.NoError(err, "Settings() should not return an error")
|
||||
is.Equal(expectedSettings, settings, "stored settings should match")
|
||||
}
|
||||
|
||||
func (store *Store) testCustomTemplates(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
customTemplate := store.CustomTemplate()
|
||||
is.NotNil(customTemplate, "customTemplate Service shouldn't be nil")
|
||||
|
||||
expectedTemplate := &portainer.CustomTemplate{
|
||||
ID: portainer.CustomTemplateID(customTemplate.GetNextIdentifier()),
|
||||
Title: "Custom Title",
|
||||
Description: "Custom Template Description",
|
||||
ProjectPath: "/data/custom_template/1",
|
||||
Note: "A note about this custom template",
|
||||
EntryPoint: "docker-compose.yaml",
|
||||
CreatedByUserID: 10,
|
||||
}
|
||||
|
||||
customTemplate.Create(expectedTemplate)
|
||||
|
||||
actualTemplate, err := customTemplate.Read(expectedTemplate.ID)
|
||||
is.NoError(err, "CustomTemplate should not return an error")
|
||||
is.Equal(expectedTemplate, actualTemplate, "expected and actual template do not match")
|
||||
}
|
||||
|
||||
func (store *Store) testRegistries(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
regService := store.RegistryService
|
||||
is.NotNil(regService, "RegistryService shouldn't be nil")
|
||||
|
||||
reg1 := &portainer.Registry{
|
||||
ID: 1,
|
||||
Type: portainer.DockerHubRegistry,
|
||||
Name: "Dockerhub Registry Test",
|
||||
}
|
||||
|
||||
reg2 := &portainer.Registry{
|
||||
ID: 2,
|
||||
Type: portainer.GitlabRegistry,
|
||||
Name: "Gitlab Registry Test",
|
||||
Gitlab: portainer.GitlabRegistryData{
|
||||
ProjectID: 12345,
|
||||
InstanceURL: "http://gitlab.com/12345",
|
||||
ProjectPath: "mytestproject",
|
||||
},
|
||||
}
|
||||
|
||||
err := regService.Create(reg1)
|
||||
is.NoError(err)
|
||||
|
||||
err = regService.Create(reg2)
|
||||
is.NoError(err)
|
||||
|
||||
actualReg1, err := regService.Read(reg1.ID)
|
||||
is.NoError(err)
|
||||
is.Equal(reg1, actualReg1, "registries differ")
|
||||
|
||||
actualReg2, err := regService.Read(reg2.ID)
|
||||
is.NoError(err)
|
||||
is.Equal(reg2, actualReg2, "registries differ")
|
||||
}
|
||||
|
||||
func (store *Store) testResourceControl(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// resControl := store.ResourceControl()
|
||||
// ctrl := &portainer.ResourceControl{
|
||||
// }
|
||||
// resControl().Create()
|
||||
}
|
||||
|
||||
func (store *Store) testSchedules(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
schedule := store.ScheduleService
|
||||
s := &portainer.Schedule{
|
||||
ID: portainer.ScheduleID(schedule.GetNextIdentifier()),
|
||||
Name: "My Custom Schedule 1",
|
||||
CronExpression: "*/5 * * * * portainer /bin/sh -c echo 'hello world'",
|
||||
}
|
||||
|
||||
err := schedule.CreateSchedule(s)
|
||||
is.NoError(err, "CreateSchedule should succeed")
|
||||
|
||||
actual, err := schedule.Schedule(s.ID)
|
||||
is.NoError(err, "schedule should be found")
|
||||
is.Equal(s, actual, "schedules differ")
|
||||
}
|
||||
|
||||
func (store *Store) testTags(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
tags := store.TagService
|
||||
|
||||
tag1 := &portainer.Tag{
|
||||
ID: 1,
|
||||
Name: "Tag 1",
|
||||
}
|
||||
|
||||
tag2 := &portainer.Tag{
|
||||
ID: 2,
|
||||
Name: "Tag 1",
|
||||
}
|
||||
|
||||
err := tags.Create(tag1)
|
||||
is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
err = tags.Create(tag2)
|
||||
is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
actual, err := tags.Read(tag1.ID)
|
||||
is.NoError(err, "tag1 should be found")
|
||||
is.Equal(tag1, actual, "tags differ")
|
||||
|
||||
actual, err = tags.Read(tag2.ID)
|
||||
is.NoError(err, "tag2 should be found")
|
||||
is.Equal(tag2, actual, "tags differ")
|
||||
}
|
||||
// import (
|
||||
// "fmt"
|
||||
// "runtime"
|
||||
// "strings"
|
||||
// "testing"
|
||||
|
||||
// "github.com/dchest/uniuri"
|
||||
// "github.com/pkg/errors"
|
||||
// portainer "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/chisel"
|
||||
// "github.com/portainer/portainer/api/crypto"
|
||||
// "github.com/stretchr/testify/assert"
|
||||
// )
|
||||
|
||||
// const (
|
||||
// adminUsername = "admin"
|
||||
// adminPassword = "password"
|
||||
// standardUsername = "standard"
|
||||
// standardPassword = "password"
|
||||
// agentOnDockerEnvironmentUrl = "tcp://192.168.167.207:30775"
|
||||
// edgeAgentOnKubernetesEnvironmentUrl = "tcp://192.168.167.207"
|
||||
// kubernetesLocalEnvironmentUrl = "https://kubernetes.default.svc"
|
||||
// )
|
||||
|
||||
// // 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 := MustNewTestStore(t, true, true)
|
||||
|
||||
// testCases := map[string]func(t *testing.T){
|
||||
// "User Accounts": func(t *testing.T) {
|
||||
// store.testUserAccounts(t)
|
||||
// },
|
||||
// "Environments": func(t *testing.T) {
|
||||
// store.testEnvironments(t)
|
||||
// },
|
||||
// "Settings": func(t *testing.T) {
|
||||
// store.testSettings(t)
|
||||
// },
|
||||
// "SSL Settings": func(t *testing.T) {
|
||||
// store.testSSLSettings(t)
|
||||
// },
|
||||
// "Tunnel Server": func(t *testing.T) {
|
||||
// store.testTunnelServer(t)
|
||||
// },
|
||||
// "Custom Templates": func(t *testing.T) {
|
||||
// store.testCustomTemplates(t)
|
||||
// },
|
||||
// "Registries": func(t *testing.T) {
|
||||
// store.testRegistries(t)
|
||||
// },
|
||||
// "Resource Control": func(t *testing.T) {
|
||||
// store.testResourceControl(t)
|
||||
// },
|
||||
// "Schedules": func(t *testing.T) {
|
||||
// store.testSchedules(t)
|
||||
// },
|
||||
// "Tags": func(t *testing.T) {
|
||||
// store.testTags(t)
|
||||
// },
|
||||
|
||||
// // "Test Title": func(t *testing.T) {
|
||||
// // },
|
||||
// }
|
||||
|
||||
// for name, test := range testCases {
|
||||
// t.Run(name, test)
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// func (store *Store) testEnvironments(t *testing.T) {
|
||||
// id := store.CreateEndpoint(t, "local", portainer.KubernetesLocalEnvironment, "", true)
|
||||
// store.CreateEndpointRelation(id)
|
||||
|
||||
// id = store.CreateEndpoint(t, "agent", portainer.AgentOnDockerEnvironment, agentOnDockerEnvironmentUrl, true)
|
||||
// store.CreateEndpointRelation(id)
|
||||
|
||||
// id = store.CreateEndpoint(t, "edge", portainer.EdgeAgentOnKubernetesEnvironment, edgeAgentOnKubernetesEnvironmentUrl, true)
|
||||
// store.CreateEndpointRelation(id)
|
||||
// }
|
||||
|
||||
// func newEndpoint(endpointType portainer.EndpointType, id portainer.EndpointID, name, URL string, TLS bool) *portainer.Endpoint {
|
||||
// endpoint := &portainer.Endpoint{
|
||||
// ID: id,
|
||||
// Name: name,
|
||||
// URL: URL,
|
||||
// Type: endpointType,
|
||||
// GroupID: portainer.EndpointGroupID(1),
|
||||
// PublicURL: "",
|
||||
// TLSConfig: portainer.TLSConfiguration{
|
||||
// TLS: false,
|
||||
// },
|
||||
// UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
// TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
// TagIDs: []portainer.TagID{},
|
||||
// Status: portainer.EndpointStatusUp,
|
||||
// Snapshots: []portainer.DockerSnapshot{},
|
||||
// Kubernetes: portainer.KubernetesDefault(),
|
||||
// }
|
||||
|
||||
// if TLS {
|
||||
// endpoint.TLSConfig = portainer.TLSConfiguration{
|
||||
// TLS: true,
|
||||
// TLSSkipVerify: true,
|
||||
// }
|
||||
// }
|
||||
|
||||
// return endpoint
|
||||
// }
|
||||
|
||||
// func setEndpointAuthorizations(endpoint *portainer.Endpoint) {
|
||||
// endpoint.SecuritySettings = portainer.EndpointSecuritySettings{
|
||||
// AllowVolumeBrowserForRegularUsers: false,
|
||||
// EnableHostManagementFeatures: false,
|
||||
|
||||
// AllowSysctlSettingForRegularUsers: true,
|
||||
// AllowBindMountsForRegularUsers: true,
|
||||
// AllowPrivilegedModeForRegularUsers: true,
|
||||
// AllowHostNamespaceForRegularUsers: true,
|
||||
// AllowContainerCapabilitiesForRegularUsers: true,
|
||||
// AllowDeviceMappingForRegularUsers: true,
|
||||
// AllowStackManagementForRegularUsers: true,
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (store *Store) CreateEndpoint(t *testing.T, name string, endpointType portainer.EndpointType, URL string, tls bool) portainer.EndpointID {
|
||||
// is := assert.New(t)
|
||||
|
||||
// var expectedEndpoint *portainer.Endpoint
|
||||
// id := portainer.EndpointID(store.Endpoint().GetNextIdentifier())
|
||||
|
||||
// switch endpointType {
|
||||
// case portainer.DockerEnvironment:
|
||||
// if URL == "" {
|
||||
// URL = "unix:///var/run/docker.sock"
|
||||
// if runtime.GOOS == "windows" {
|
||||
// URL = "npipe:////./pipe/docker_engine"
|
||||
// }
|
||||
// }
|
||||
// expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
// case portainer.AgentOnDockerEnvironment:
|
||||
// expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
// case portainer.AgentOnKubernetesEnvironment:
|
||||
// URL = strings.TrimPrefix(URL, "tcp://")
|
||||
// expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
|
||||
// case portainer.EdgeAgentOnKubernetesEnvironment:
|
||||
// cs := chisel.NewService(store, nil)
|
||||
// expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
// edgeKey := cs.GenerateEdgeKey(URL, "", int(id))
|
||||
// expectedEndpoint.EdgeKey = edgeKey
|
||||
// store.testTunnelServer(t)
|
||||
|
||||
// case portainer.KubernetesLocalEnvironment:
|
||||
// if URL == "" {
|
||||
// URL = kubernetesLocalEnvironmentUrl
|
||||
// }
|
||||
// expectedEndpoint = newEndpoint(endpointType, id, name, URL, tls)
|
||||
// }
|
||||
|
||||
// setEndpointAuthorizations(expectedEndpoint)
|
||||
// store.Endpoint().Create(expectedEndpoint)
|
||||
|
||||
// endpoint, err := store.Endpoint().Endpoint(id)
|
||||
// is.NoError(err, "Endpoint() should not return an error")
|
||||
// is.Equal(expectedEndpoint, endpoint, "endpoint should be the same")
|
||||
|
||||
// return endpoint.ID
|
||||
// }
|
||||
|
||||
// func (store *Store) CreateEndpointRelation(id portainer.EndpointID) {
|
||||
// relation := &portainer.EndpointRelation{
|
||||
// EndpointID: id,
|
||||
// EdgeStacks: map[portainer.EdgeStackID]bool{},
|
||||
// }
|
||||
|
||||
// store.EndpointRelation().Create(relation)
|
||||
// }
|
||||
|
||||
// func (store *Store) testSSLSettings(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// ssl := &portainer.SSLSettings{
|
||||
// CertPath: "/data/certs/cert.pem",
|
||||
// HTTPEnabled: true,
|
||||
// KeyPath: "/data/certs/key.pem",
|
||||
// SelfSigned: true,
|
||||
// }
|
||||
|
||||
// store.SSLSettings().UpdateSettings(ssl)
|
||||
|
||||
// settings, err := store.SSLSettings().Settings()
|
||||
// is.NoError(err, "Get sslsettings should succeed")
|
||||
// is.Equal(ssl, settings, "Stored SSLSettings should be the same as what is read out")
|
||||
// }
|
||||
|
||||
// func (store *Store) testTunnelServer(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
// expectPrivateKeySeed := uniuri.NewLen(16)
|
||||
|
||||
// err := store.TunnelServer().UpdateInfo(&portainer.TunnelServerInfo{PrivateKeySeed: expectPrivateKeySeed})
|
||||
// is.NoError(err, "UpdateInfo should have succeeded")
|
||||
|
||||
// serverInfo, err := store.TunnelServer().Info()
|
||||
// is.NoError(err, "Info should have succeeded")
|
||||
|
||||
// is.Equal(expectPrivateKeySeed, serverInfo.PrivateKeySeed, "hashed passwords should not differ")
|
||||
// }
|
||||
|
||||
// // add users, read them back and check the details are unchanged
|
||||
// func (store *Store) testUserAccounts(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// err := store.createAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
// is.NoError(err, "CreateAccount should succeed")
|
||||
// store.checkAccount(adminUsername, adminPassword, portainer.AdministratorRole)
|
||||
// is.NoError(err, "Account failure")
|
||||
|
||||
// err = store.createAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
// is.NoError(err, "CreateAccount should succeed")
|
||||
// store.checkAccount(standardUsername, standardPassword, portainer.StandardUserRole)
|
||||
// is.NoError(err, "Account failure")
|
||||
// }
|
||||
|
||||
// // create an account with the provided details
|
||||
// func (store *Store) createAccount(username, password string, role portainer.UserRole) error {
|
||||
// var err error
|
||||
// user := &portainer.User{Username: username, Role: role}
|
||||
|
||||
// // encrypt the password
|
||||
// cs := &crypto.Service{}
|
||||
// user.Password, err = cs.Hash(password)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// err = store.User().Create(user)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (store *Store) checkAccount(username, expectPassword string, expectRole portainer.UserRole) error {
|
||||
// // Read the account for username. Check password and role is what we expect
|
||||
|
||||
// user, err := store.User().UserByUsername(username)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "failed to find user")
|
||||
// }
|
||||
|
||||
// if user.Username != username || user.Role != expectRole {
|
||||
// return fmt.Errorf("%s user details do not match", user.Username)
|
||||
// }
|
||||
|
||||
// // Check the password
|
||||
// cs := &crypto.Service{}
|
||||
// expectPasswordHash, err := cs.Hash(expectPassword)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "hash failed")
|
||||
// }
|
||||
|
||||
// if user.Password != expectPasswordHash {
|
||||
// return fmt.Errorf("%s user password hash failure", user.Username)
|
||||
// }
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (store *Store) testSettings(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// // since many settings are default and basically nil, I'm going to update some and read them back
|
||||
// expectedSettings, err := store.Settings().Settings()
|
||||
// is.NoError(err, "Settings() should not return an error")
|
||||
// expectedSettings.TemplatesURL = "http://portainer.io/application-templates"
|
||||
// expectedSettings.HelmRepositoryURL = "http://portainer.io/helm-repository"
|
||||
// expectedSettings.EdgeAgentCheckinInterval = 60
|
||||
// expectedSettings.AuthenticationMethod = portainer.AuthenticationLDAP
|
||||
// expectedSettings.LDAPSettings = portainer.LDAPSettings{
|
||||
// AnonymousMode: true,
|
||||
// StartTLS: true,
|
||||
// AutoCreateUsers: true,
|
||||
// Password: "random",
|
||||
// }
|
||||
// expectedSettings.SnapshotInterval = "10m"
|
||||
|
||||
// err = store.Settings().UpdateSettings(expectedSettings)
|
||||
// is.NoError(err, "UpdateSettings() should succeed")
|
||||
|
||||
// settings, err := store.Settings().Settings()
|
||||
// is.NoError(err, "Settings() should not return an error")
|
||||
// is.Equal(expectedSettings, settings, "stored settings should match")
|
||||
// }
|
||||
|
||||
// func (store *Store) testCustomTemplates(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// customTemplate := store.CustomTemplate()
|
||||
// is.NotNil(customTemplate, "customTemplate Service shouldn't be nil")
|
||||
|
||||
// expectedTemplate := &portainer.CustomTemplate{
|
||||
// ID: portainer.CustomTemplateID(customTemplate.GetNextIdentifier()),
|
||||
// Title: "Custom Title",
|
||||
// Description: "Custom Template Description",
|
||||
// ProjectPath: "/data/custom_template/1",
|
||||
// Note: "A note about this custom template",
|
||||
// EntryPoint: "docker-compose.yaml",
|
||||
// CreatedByUserID: 10,
|
||||
// }
|
||||
|
||||
// customTemplate.Create(expectedTemplate)
|
||||
|
||||
// actualTemplate, err := customTemplate.CustomTemplate(expectedTemplate.ID)
|
||||
// is.NoError(err, "CustomTemplate should not return an error")
|
||||
// is.Equal(expectedTemplate, actualTemplate, "expected and actual template do not match")
|
||||
// }
|
||||
|
||||
// func (store *Store) testRegistries(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// regService := store.RegistryService
|
||||
// is.NotNil(regService, "RegistryService shouldn't be nil")
|
||||
|
||||
// reg1 := &portainer.Registry{
|
||||
// ID: 1,
|
||||
// Type: portainer.DockerHubRegistry,
|
||||
// Name: "Dockerhub Registry Test",
|
||||
// }
|
||||
|
||||
// reg2 := &portainer.Registry{
|
||||
// ID: 2,
|
||||
// Type: portainer.GitlabRegistry,
|
||||
// Name: "Gitlab Registry Test",
|
||||
// Gitlab: portainer.GitlabRegistryData{
|
||||
// ProjectID: 12345,
|
||||
// InstanceURL: "http://gitlab.com/12345",
|
||||
// ProjectPath: "mytestproject",
|
||||
// },
|
||||
// }
|
||||
|
||||
// err := regService.Create(reg1)
|
||||
// is.NoError(err)
|
||||
|
||||
// err = regService.Create(reg2)
|
||||
// is.NoError(err)
|
||||
|
||||
// actualReg1, err := regService.Registry(reg1.ID)
|
||||
// is.NoError(err)
|
||||
// is.Equal(reg1, actualReg1, "registries differ")
|
||||
|
||||
// actualReg2, err := regService.Registry(reg2.ID)
|
||||
// is.NoError(err)
|
||||
// is.Equal(reg2, actualReg2, "registries differ")
|
||||
// }
|
||||
|
||||
// func (store *Store) testResourceControl(t *testing.T) {
|
||||
// // is := assert.New(t)
|
||||
// // resControl := store.ResourceControl()
|
||||
// // ctrl := &portainer.ResourceControl{
|
||||
// // }
|
||||
// // resControl().Create()
|
||||
// }
|
||||
|
||||
// func (store *Store) testSchedules(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// schedule := store.ScheduleService
|
||||
// s := &portainer.Schedule{
|
||||
// ID: portainer.ScheduleID(schedule.GetNextIdentifier()),
|
||||
// Name: "My Custom Schedule 1",
|
||||
// CronExpression: "*/5 * * * * portainer /bin/sh -c echo 'hello world'",
|
||||
// }
|
||||
|
||||
// err := schedule.CreateSchedule(s)
|
||||
// is.NoError(err, "CreateSchedule should succeed")
|
||||
|
||||
// actual, err := schedule.Schedule(s.ID)
|
||||
// is.NoError(err, "schedule should be found")
|
||||
// is.Equal(s, actual, "schedules differ")
|
||||
// }
|
||||
|
||||
// func (store *Store) testTags(t *testing.T) {
|
||||
// is := assert.New(t)
|
||||
|
||||
// tags := store.TagService
|
||||
|
||||
// tag1 := &portainer.Tag{
|
||||
// ID: 1,
|
||||
// Name: "Tag 1",
|
||||
// }
|
||||
|
||||
// tag2 := &portainer.Tag{
|
||||
// ID: 2,
|
||||
// Name: "Tag 1",
|
||||
// }
|
||||
|
||||
// err := tags.Create(tag1)
|
||||
// is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
// err = tags.Create(tag2)
|
||||
// is.NoError(err, "Tags.Create should succeed")
|
||||
|
||||
// actual, err := tags.Tag(tag1.ID)
|
||||
// is.NoError(err, "tag1 should be found")
|
||||
// is.Equal(tag1, actual, "tags differ")
|
||||
|
||||
// actual, err = tags.Tag(tag2.ID)
|
||||
// is.NoError(err, "tag2 should be found")
|
||||
// is.Equal(tag2, actual, "tags differ")
|
||||
// }
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// isFileExist is helper function to check for file existence
|
||||
func isFileExist(path string) bool {
|
||||
matches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(matches) > 0
|
||||
}
|
||||
|
||||
func updateVersion(store *Store, v string) {
|
||||
version, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
version.SchemaVersion = v
|
||||
|
||||
err = store.VersionService.UpdateVersion(version)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
}
|
||||
|
||||
func updateEdition(store *Store, edition portainer.SoftwareEdition) {
|
||||
version, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
version.Edition = int(edition)
|
||||
|
||||
err = store.VersionService.UpdateVersion(version)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
}
|
||||
|
||||
// testVersion is a helper which tests current store version against wanted version
|
||||
func testVersion(store *Store, versionWant string, t *testing.T) {
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
if v.SchemaVersion != versionWant {
|
||||
t.Errorf("Expect store version to be %s but was %s", versionWant, v.SchemaVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func testEdition(store *Store, editionWant portainer.SoftwareEdition, t *testing.T) {
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
if portainer.SoftwareEdition(v.Edition) != editionWant {
|
||||
t.Errorf("Expect store edition to be %s but was %s", editionWant.GetEditionLabel(), portainer.SoftwareEdition(v.Edition).GetEditionLabel())
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
@@ -8,6 +9,11 @@ import (
|
||||
|
||||
// Init creates the default data set.
|
||||
func (store *Store) Init() error {
|
||||
|
||||
if err := store.connection.Init(); err != nil {
|
||||
fmt.Errorf("connection init returned error: %s\n", err.Error())
|
||||
}
|
||||
|
||||
err := store.checkOrCreateDefaultSettings()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -21,7 +27,13 @@ func (store *Store) Init() error {
|
||||
return store.checkOrCreateDefaultData()
|
||||
}
|
||||
|
||||
func (store *Store) PostInit() error {
|
||||
store.EndpointService.Init()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
|
||||
isDDExtention := false
|
||||
if _, ok := os.LookupEnv("DOCKER_EXTENSION"); ok {
|
||||
isDDExtention = true
|
||||
@@ -33,7 +45,7 @@ func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
defaultSettings := &portainer.Settings{
|
||||
EnableTelemetry: false,
|
||||
AuthenticationMethod: portainer.AuthenticationInternal,
|
||||
BlackListedLabels: make([]portainer.Pair, 0),
|
||||
BlackListedLabels: make(portainer.MultiPair, 0),
|
||||
InternalAuthSettings: portainer.InternalAuthSettings{
|
||||
RequiredPasswordLength: 12,
|
||||
},
|
||||
@@ -72,7 +84,6 @@ func (store *Store) checkOrCreateDefaultSettings() error {
|
||||
settings.UserSessionTimeout = portainer.DefaultUserSessionTimeout
|
||||
return store.Settings().UpdateSettings(settings)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,7 +101,7 @@ func (store *Store) checkOrCreateDefaultSSLSettings() error {
|
||||
}
|
||||
|
||||
func (store *Store) checkOrCreateDefaultData() error {
|
||||
groups, err := store.EndpointGroupService.ReadAll()
|
||||
groups, err := store.EndpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -99,7 +110,7 @@ func (store *Store) checkOrCreateDefaultData() error {
|
||||
unassignedGroup := &portainer.EndpointGroup{
|
||||
Name: "Unassigned",
|
||||
Description: "Unassigned environments",
|
||||
Labels: []portainer.Pair{},
|
||||
Labels: portainer.MultiPair{},
|
||||
UserAccessPolicies: portainer.UserAccessPolicies{},
|
||||
TeamAccessPolicies: portainer.TeamAccessPolicies{},
|
||||
TagIDs: []portainer.TagID{},
|
||||
@@ -110,6 +121,5 @@ func (store *Store) checkOrCreateDefaultData() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,148 +1,145 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
// import (
|
||||
// "fmt"
|
||||
// "runtime/debug"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/cli"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
// portainer "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/cli"
|
||||
// "github.com/portainer/portainer/api/database/models"
|
||||
// dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||
// "github.com/portainer/portainer/api/datastore/migrator"
|
||||
// "github.com/portainer/portainer/api/internal/authorization"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
// "github.com/pkg/errors"
|
||||
// "github.com/rs/zerolog/log"
|
||||
// )
|
||||
|
||||
func (store *Store) MigrateData() error {
|
||||
updating, err := store.VersionService.IsUpdating()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while checking if the store is updating")
|
||||
}
|
||||
// const beforePortainerVersionUpgradeBackup = "portainer.db.bak"
|
||||
|
||||
if updating {
|
||||
return dserrors.ErrDatabaseIsUpdating
|
||||
}
|
||||
// func (store *Store) MigrateData() error {
|
||||
// updating, err := store.VersionService.IsUpdating()
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "while checking if the store is updating")
|
||||
// }
|
||||
|
||||
// migrate new version bucket if required (doesn't write anything to db yet)
|
||||
version, err := store.getOrMigrateLegacyVersion()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while migrating legacy version")
|
||||
}
|
||||
// if updating {
|
||||
// return dserrors.ErrDatabaseIsUpdating
|
||||
// }
|
||||
|
||||
migratorParams := store.newMigratorParameters(version)
|
||||
migrator := migrator.NewMigrator(migratorParams)
|
||||
// // migrate new version bucket if required (doesn't write anything to db yet)
|
||||
// version, err := store.getOrMigrateLegacyVersion()
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "while migrating legacy version")
|
||||
// }
|
||||
|
||||
if !migrator.NeedsMigration() {
|
||||
return nil
|
||||
}
|
||||
// migratorParams := store.newMigratorParameters(version)
|
||||
// migrator := migrator.NewMigrator(migratorParams)
|
||||
|
||||
// before we alter anything in the DB, create a backup
|
||||
_, err = store.Backup()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while backing up database")
|
||||
}
|
||||
// if !migrator.NeedsMigration() {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
err = store.FailSafeMigrate(migrator, version)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "failed to migrate database")
|
||||
// // before we alter anything in the DB, create a backup
|
||||
// backupPath, err := store.Backup(version)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "while backing up database")
|
||||
// }
|
||||
|
||||
log.Warn().Err(err).Msg("migration failed, restoring database to previous version")
|
||||
restoreErr := store.Restore()
|
||||
if restoreErr != nil {
|
||||
return errors.Wrap(restoreErr, "failed to restore database")
|
||||
}
|
||||
// err = store.FailSafeMigrate(migrator, version)
|
||||
// if err != nil {
|
||||
// err = errors.Wrap(err, "failed to migrate database")
|
||||
|
||||
log.Info().Msg("database restored to previous version")
|
||||
return err
|
||||
}
|
||||
// log.Warn().Msg("migration failed, restoring database to previous version")
|
||||
// err = store.restoreWithOptions(&BackupOptions{BackupPath: backupPath})
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "failed to restore database")
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// log.Info().Msg("database restored to previous version")
|
||||
// return err
|
||||
// }
|
||||
|
||||
func (store *Store) newMigratorParameters(version *models.Version) *migrator.MigratorParameters {
|
||||
return &migrator.MigratorParameters{
|
||||
CurrentDBVersion: version,
|
||||
EndpointGroupService: store.EndpointGroupService,
|
||||
EndpointService: store.EndpointService,
|
||||
EndpointRelationService: store.EndpointRelationService,
|
||||
ExtensionService: store.ExtensionService,
|
||||
RegistryService: store.RegistryService,
|
||||
ResourceControlService: store.ResourceControlService,
|
||||
RoleService: store.RoleService,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
SnapshotService: store.SnapshotService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
UserService: store.UserService,
|
||||
VersionService: store.VersionService,
|
||||
FileService: store.fileService,
|
||||
DockerhubService: store.DockerHubService,
|
||||
AuthorizationService: authorization.NewService(store),
|
||||
EdgeStackService: store.EdgeStackService,
|
||||
EdgeJobService: store.EdgeJobService,
|
||||
TunnelServerService: store.TunnelServerService,
|
||||
}
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// FailSafeMigrate backup and restore DB if migration fail
|
||||
func (store *Store) FailSafeMigrate(migrator *migrator.Migrator, version *models.Version) (err error) {
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
// return error with cause and stacktrace (recover() doesn't include a stacktrace)
|
||||
err = fmt.Errorf("%v %s", e, string(debug.Stack()))
|
||||
}
|
||||
}()
|
||||
// func (store *Store) newMigratorParameters(version *models.Version) *migrator.MigratorParameters {
|
||||
// return &migrator.MigratorParameters{
|
||||
// CurrentDBVersion: version,
|
||||
// EndpointGroupService: store.EndpointGroupService,
|
||||
// EndpointService: store.EndpointService,
|
||||
// EndpointRelationService: store.EndpointRelationService,
|
||||
// ExtensionService: store.ExtensionService,
|
||||
// RegistryService: store.RegistryService,
|
||||
// ResourceControlService: store.ResourceControlService,
|
||||
// RoleService: store.RoleService,
|
||||
// ScheduleService: store.ScheduleService,
|
||||
// SettingsService: store.SettingsService,
|
||||
// SnapshotService: store.SnapshotService,
|
||||
// StackService: store.StackService,
|
||||
// TagService: store.TagService,
|
||||
// TeamMembershipService: store.TeamMembershipService,
|
||||
// UserService: store.UserService,
|
||||
// VersionService: store.VersionService,
|
||||
// FileService: store.fileService,
|
||||
// DockerhubService: store.DockerHubService,
|
||||
// AuthorizationService: authorization.NewService(store),
|
||||
// EdgeStackService: store.EdgeStackService,
|
||||
// EdgeJobService: store.EdgeJobService,
|
||||
// }
|
||||
// }
|
||||
|
||||
err = store.VersionService.StoreIsUpdating(true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while updating the store")
|
||||
}
|
||||
// // FailSafeMigrate backup and restore DB if migration fail
|
||||
// func (store *Store) FailSafeMigrate(migrator *migrator.Migrator, version *models.Version) (err error) {
|
||||
// defer func() {
|
||||
// if e := recover(); e != nil {
|
||||
// // return error with cause and stacktrace (recover() doesn't include a stacktrace)
|
||||
// err = fmt.Errorf("%v %s", e, string(debug.Stack()))
|
||||
// }
|
||||
// }()
|
||||
|
||||
// now update the version to the new struct (if required)
|
||||
err = store.finishMigrateLegacyVersion(version)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "while updating version")
|
||||
}
|
||||
// err = store.VersionService.StoreIsUpdating(true)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "while updating the store")
|
||||
// }
|
||||
|
||||
log.Info().Msg("migrating database from version " + version.SchemaVersion + " to " + portainer.APIVersion)
|
||||
// // now update the version to the new struct (if required)
|
||||
// err = store.finishMigrateLegacyVersion(version)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "while updating version")
|
||||
// }
|
||||
|
||||
err = migrator.Migrate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// log.Info().Msg("migrating database from version " + version.SchemaVersion + " to " + portainer.APIVersion)
|
||||
|
||||
// Special test code to simulate a failure (used by migrate_data_test.go). Do not remove...
|
||||
if os.Getenv("PORTAINER_TEST_MIGRATE_FAIL") == "FAIL" {
|
||||
panic("test migration failure")
|
||||
}
|
||||
// err = migrator.Migrate()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
err = store.VersionService.StoreIsUpdating(false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to update the store")
|
||||
}
|
||||
// err = store.VersionService.StoreIsUpdating(false)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "failed to update the store")
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// Rollback to a pre-upgrade backup copy/snapshot of portainer.db
|
||||
func (store *Store) connectionRollback(force bool) error {
|
||||
// // Rollback to a pre-upgrade backup copy/snapshot of portainer.db
|
||||
// func (store *Store) connectionRollback(force bool) error {
|
||||
|
||||
if !force {
|
||||
confirmed, err := cli.Confirm("Are you sure you want to rollback your database to the previous backup?")
|
||||
if err != nil || !confirmed {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// if !force {
|
||||
// confirmed, err := cli.Confirm("Are you sure you want to rollback your database to the previous backup?")
|
||||
// if err != nil || !confirmed {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
err := store.Restore()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// options := getBackupRestoreOptions(store.commonBackupDir())
|
||||
|
||||
return store.connection.Close()
|
||||
}
|
||||
// err := store.restoreWithOptions(options)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// return store.connection.Close()
|
||||
// }
|
||||
|
||||
@@ -1,514 +1,507 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
// testVersion is a helper which tests current store version against wanted version
|
||||
func testVersion(store *Store, versionWant string, t *testing.T) {
|
||||
// v, err := store.VersionService.Version()
|
||||
// if err != nil {
|
||||
// t.Errorf("Expect store version to be %s but was %s with error: %s", versionWant, v.SchemaVersion, err)
|
||||
// }
|
||||
// if v.SchemaVersion != versionWant {
|
||||
// t.Errorf("Expect store version to be %s but was %s", versionWant, v.SchemaVersion)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestMigrateData(t *testing.T) {
|
||||
tests := []struct {
|
||||
testName string
|
||||
srcPath string
|
||||
wantPath string
|
||||
overrideInstanceId bool
|
||||
}{
|
||||
{
|
||||
testName: "migrate version 24 to latest",
|
||||
srcPath: "test_data/input_24.json",
|
||||
wantPath: "test_data/output_24_to_latest.json",
|
||||
overrideInstanceId: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.testName, func(t *testing.T) {
|
||||
err := migrateDBTestHelper(t, test.srcPath, test.wantPath, test.overrideInstanceId)
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"Failed migrating mock database %v: %v",
|
||||
test.srcPath,
|
||||
err,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
// snapshotTests := []struct {
|
||||
// testName string
|
||||
// srcPath string
|
||||
// wantPath string
|
||||
// overrideInstanceId bool
|
||||
// }{
|
||||
// {
|
||||
// testName: "migrate version 24 to latest",
|
||||
// srcPath: "test_data/input_24.json",
|
||||
// wantPath: "test_data/output_24_to_latest.json",
|
||||
// overrideInstanceId: true,
|
||||
// },
|
||||
// }
|
||||
// for _, test := range snapshotTests {
|
||||
// t.Run(test.testName, func(t *testing.T) {
|
||||
// err := migrateDBTestHelper(t, test.srcPath, test.wantPath, test.overrideInstanceId)
|
||||
// if err != nil {
|
||||
// t.Errorf(
|
||||
// "Failed migrating mock database %v: %v",
|
||||
// test.srcPath,
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
t.Run("MigrateData for New Store & Re-Open Check", func(t *testing.T) {
|
||||
newStore, store := MustNewTestStore(t, true, false)
|
||||
if !newStore {
|
||||
t.Error("Expect a new DB")
|
||||
}
|
||||
// t.Run("MigrateData for New Store & Re-Open Check", func(t *testing.T) {
|
||||
// newStore, store, teardown := MustNewTestStore(t, true, false)
|
||||
// defer teardown()
|
||||
|
||||
testVersion(store, portainer.APIVersion, t)
|
||||
store.Close()
|
||||
// if !newStore {
|
||||
// t.Error("Expect a new DB")
|
||||
// }
|
||||
|
||||
newStore, _ = store.Open()
|
||||
if newStore {
|
||||
t.Error("Expect store to NOT be new DB")
|
||||
}
|
||||
})
|
||||
// testVersion(store, portainer.APIVersion, t)
|
||||
// store.Close()
|
||||
|
||||
t.Run("MigrateData should create backup file upon update", func(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
store.VersionService.UpdateVersion(&models.Version{SchemaVersion: "1.0", Edition: int(portainer.PortainerCE)})
|
||||
store.MigrateData()
|
||||
// newStore, _ = store.Open()
|
||||
// if newStore {
|
||||
// t.Error("Expect store to NOT be new DB")
|
||||
// }
|
||||
// })
|
||||
|
||||
backupfilename := store.backupFilename()
|
||||
if exists, _ := store.fileService.FileExists(backupfilename); !exists {
|
||||
t.Errorf("Expect backup file to be created %s", backupfilename)
|
||||
}
|
||||
})
|
||||
// tests := []struct {
|
||||
// version string
|
||||
// expectedVersion string
|
||||
// }{
|
||||
// {version: "1.24.1", expectedVersion: portainer.APIVersion},
|
||||
// {version: "2.0.0", expectedVersion: portainer.APIVersion},
|
||||
// }
|
||||
// for _, tc := range tests {
|
||||
// _, store, teardown := MustNewTestStore(t, true, true)
|
||||
// defer teardown()
|
||||
|
||||
t.Run("MigrateData should recover and restore backup during migration critical failure", func(t *testing.T) {
|
||||
os.Setenv("PORTAINER_TEST_MIGRATE_FAIL", "FAIL")
|
||||
// // Setup data
|
||||
// v := models.Version{SchemaVersion: tc.version}
|
||||
// store.VersionService.UpdateVersion(&v)
|
||||
|
||||
version := "2.15"
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
store.VersionService.UpdateVersion(&models.Version{SchemaVersion: version, Edition: int(portainer.PortainerCE)})
|
||||
store.MigrateData()
|
||||
// // Required roles by migrations 22.2
|
||||
// store.RoleService.Create(&portainer.Role{ID: 1})
|
||||
// store.RoleService.Create(&portainer.Role{ID: 2})
|
||||
// store.RoleService.Create(&portainer.Role{ID: 3})
|
||||
// store.RoleService.Create(&portainer.Role{ID: 4})
|
||||
|
||||
store.Open()
|
||||
testVersion(store, version, t)
|
||||
})
|
||||
// t.Run(fmt.Sprintf("MigrateData for version %s", tc.version), func(t *testing.T) {
|
||||
// store.MigrateData()
|
||||
// testVersion(store, tc.expectedVersion, t)
|
||||
// })
|
||||
|
||||
t.Run("MigrateData should fail to create backup if database file is set to updating", func(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
store.VersionService.StoreIsUpdating(true)
|
||||
store.MigrateData()
|
||||
// t.Run(fmt.Sprintf("Restoring DB after migrateData for version %s", tc.version), func(t *testing.T) {
|
||||
// store.Rollback(true)
|
||||
// store.Open()
|
||||
// testVersion(store, tc.version, t)
|
||||
// })
|
||||
// }
|
||||
|
||||
// If you get an error, it usually means that the backup folder doesn't exist (no backups). Expected!
|
||||
// If the backup file is not blank, then it means a backup was created. We don't want that because we
|
||||
// only create a backup when the version changes.
|
||||
backupfilename := store.backupFilename()
|
||||
if exists, _ := store.fileService.FileExists(backupfilename); exists {
|
||||
t.Errorf("Backup file should not exist for dirty database")
|
||||
}
|
||||
})
|
||||
// t.Run("Error in MigrateData should restore backup before MigrateData", func(t *testing.T) {
|
||||
// _, store, teardown := MustNewTestStore(t, false, true)
|
||||
// defer teardown()
|
||||
|
||||
t.Run("MigrateData should not create backup on startup if portainer version matches db", func(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
// v := models.Version{SchemaVersion: "1.24.1"}
|
||||
// store.VersionService.UpdateVersion(&v)
|
||||
|
||||
// Set migrator the count to match our migrations array (simulate no changes).
|
||||
// Should not create a backup
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read version from db: %s", err)
|
||||
t.FailNow()
|
||||
}
|
||||
// store.MigrateData()
|
||||
|
||||
migratorParams := store.newMigratorParameters(v)
|
||||
m := migrator.NewMigrator(migratorParams)
|
||||
latestMigrations := m.LatestMigrations()
|
||||
// testVersion(store, v.SchemaVersion, t)
|
||||
// })
|
||||
|
||||
if latestMigrations.Version.Equal(semver.MustParse(portainer.APIVersion)) {
|
||||
v.MigratorCount = len(latestMigrations.MigrationFuncs)
|
||||
store.VersionService.UpdateVersion(v)
|
||||
}
|
||||
// t.Run("MigrateData should create backup file upon update", func(t *testing.T) {
|
||||
// _, store, teardown := MustNewTestStore(t, false, true)
|
||||
// defer teardown()
|
||||
|
||||
store.MigrateData()
|
||||
// v := models.Version{SchemaVersion: "0.0.0"}
|
||||
// store.VersionService.UpdateVersion(&v)
|
||||
|
||||
// If you get an error, it usually means that the backup folder doesn't exist (no backups). Expected!
|
||||
// If the backup file is not blank, then it means a backup was created. We don't want that because we
|
||||
// only create a backup when the version changes.
|
||||
backupfilename := store.backupFilename()
|
||||
if exists, _ := store.fileService.FileExists(backupfilename); exists {
|
||||
t.Errorf("Backup file should not exist for dirty database")
|
||||
}
|
||||
})
|
||||
// store.MigrateData()
|
||||
|
||||
t.Run("MigrateData should create backup on startup if portainer version matches db and migrationFuncs counts differ", func(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
// options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
// Set migrator count very large to simulate changes
|
||||
// Should not create a backup
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
t.Errorf("Unable to read version from db: %s", err)
|
||||
t.FailNow()
|
||||
}
|
||||
// if !isFileExist(options.BackupPath) {
|
||||
// t.Errorf("Backup file should exist; file=%s", options.BackupPath)
|
||||
// }
|
||||
// })
|
||||
|
||||
v.MigratorCount = 1000
|
||||
store.VersionService.UpdateVersion(v)
|
||||
store.MigrateData()
|
||||
// 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)
|
||||
// defer teardown()
|
||||
|
||||
// If you get an error, it usually means that the backup folder doesn't exist (no backups). Expected!
|
||||
// If the backup file is not blank, then it means a backup was created. We don't want that because we
|
||||
// only create a backup when the version changes.
|
||||
backupfilename := store.backupFilename()
|
||||
if exists, _ := store.fileService.FileExists(backupfilename); !exists {
|
||||
t.Errorf("DB backup should exist and there should be no error")
|
||||
}
|
||||
})
|
||||
// store.VersionService.StoreIsUpdating(true)
|
||||
|
||||
// store.MigrateData()
|
||||
|
||||
// options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
// if isFileExist(options.BackupPath) {
|
||||
// t.Errorf("Backup file should not exist for dirty database; file=%s", options.BackupPath)
|
||||
// }
|
||||
// })
|
||||
|
||||
// t.Run("MigrateData should not create backup on startup if portainer version matches db", func(t *testing.T) {
|
||||
// _, store, teardown := MustNewTestStore(t, false, true)
|
||||
// defer teardown()
|
||||
|
||||
// store.MigrateData()
|
||||
|
||||
// options := store.setupOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
|
||||
// if isFileExist(options.BackupPath) {
|
||||
// t.Errorf("Backup file should not exist for dirty database; file=%s", options.BackupPath)
|
||||
// }
|
||||
// })
|
||||
}
|
||||
|
||||
func Test_getBackupRestoreOptions(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, false, true)
|
||||
|
||||
// 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")
|
||||
// }
|
||||
|
||||
// wantFilename := "portainer.db.bak"
|
||||
// if options.BackupFileName != wantFilename {
|
||||
// log.Fatal().Str("got", options.BackupFileName).Str("want", wantFilename).Msg("incorrect backup file")
|
||||
// }
|
||||
}
|
||||
|
||||
func TestRollback(t *testing.T) {
|
||||
t.Run("Rollback should restore upgrade after backup", func(t *testing.T) {
|
||||
version := "2.11"
|
||||
// t.Run("Rollback should restore upgrade after backup", func(t *testing.T) {
|
||||
// version := models.Version{SchemaVersion: "2.4.0"}
|
||||
// _, store := MustNewTestStore(t, true, false)
|
||||
|
||||
v := models.Version{
|
||||
SchemaVersion: version,
|
||||
}
|
||||
// err := store.VersionService.UpdateVersion(&version)
|
||||
// if err != nil {
|
||||
// t.Errorf("Failed updating version: %v", err)
|
||||
// }
|
||||
|
||||
_, store := MustNewTestStore(t, false, false)
|
||||
store.VersionService.UpdateVersion(&v)
|
||||
// _, err = store.backupWithOptions(getBackupRestoreOptions(store.commonBackupDir()))
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("")
|
||||
// }
|
||||
|
||||
_, err := store.Backup()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
// // Change the current version
|
||||
// version2 := models.Version{SchemaVersion: "2.6.0"}
|
||||
// err = store.VersionService.UpdateVersion(&version2)
|
||||
// if err != nil {
|
||||
// log.Fatal().Err(err).Msg("")
|
||||
// }
|
||||
|
||||
v.SchemaVersion = "2.14"
|
||||
// Change the current edition
|
||||
err = store.VersionService.UpdateVersion(&v)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
// err = store.Rollback(true)
|
||||
// if err != nil {
|
||||
// t.Logf("Rollback failed: %s", err)
|
||||
// t.Fail()
|
||||
// return
|
||||
// }
|
||||
|
||||
err = store.Rollback(true)
|
||||
if err != nil {
|
||||
t.Logf("Rollback failed: %s", err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
// _, err = store.Open()
|
||||
// if err != nil {
|
||||
// t.Logf("Open failed: %s", err)
|
||||
// t.Fail()
|
||||
// return
|
||||
// }
|
||||
|
||||
store.Open()
|
||||
testVersion(store, version, t)
|
||||
})
|
||||
// testVersion(store, version.SchemaVersion, t)
|
||||
// })
|
||||
}
|
||||
|
||||
t.Run("Rollback should restore upgrade after backup", func(t *testing.T) {
|
||||
version := "2.15"
|
||||
|
||||
v := models.Version{
|
||||
SchemaVersion: version,
|
||||
Edition: int(portainer.PortainerCE),
|
||||
}
|
||||
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
store.VersionService.UpdateVersion(&v)
|
||||
|
||||
_, err := store.Backup()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
v.SchemaVersion = "2.14"
|
||||
// Change the current edition
|
||||
err = store.VersionService.UpdateVersion(&v)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("")
|
||||
}
|
||||
|
||||
err = store.Rollback(true)
|
||||
if err != nil {
|
||||
t.Logf("Rollback failed: %s", err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
store.Open()
|
||||
testVersion(store, version, t)
|
||||
})
|
||||
// isFileExist is helper function to check for file existence
|
||||
func isFileExist(path string) bool {
|
||||
matches, err := filepath.Glob(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(matches) > 0
|
||||
}
|
||||
|
||||
// migrateDBTestHelper loads a json representation of a bolt database from srcPath,
|
||||
// parses it into a database, runs a migration on that database, and then
|
||||
// compares it with an expected output database.
|
||||
func migrateDBTestHelper(t *testing.T, srcPath, wantPath string, overrideInstanceId bool) error {
|
||||
srcJSON, err := os.ReadFile(srcPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading source JSON file %v: %v", srcPath, err)
|
||||
}
|
||||
// srcJSON, err := os.ReadFile(srcPath)
|
||||
// if err != nil {
|
||||
// t.Fatalf("failed loading source JSON file %v: %v", srcPath, err)
|
||||
// }
|
||||
|
||||
// Parse source json to db.
|
||||
// When we create a new test store, it sets its version field automatically to latest.
|
||||
_, store := MustNewTestStore(t, true, false)
|
||||
// // Parse source json to db.
|
||||
// // When we create a new test store, it sets its version field automatically to latest.
|
||||
// _, store := MustNewTestStore(t, true, false)
|
||||
|
||||
fmt.Println("store.path=", store.GetConnection().GetDatabaseFilePath())
|
||||
store.connection.DeleteObject("version", []byte("VERSION"))
|
||||
// fmt.Println("store.path=", store.GetConnection().GetDatabaseFilePath())
|
||||
// store.connection.DeleteObject("version", []byte("VERSION"))
|
||||
|
||||
// defer teardown()
|
||||
err = importJSON(t, bytes.NewReader(srcJSON), store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // defer teardown()
|
||||
// err = importJSON(t, bytes.NewReader(srcJSON), store)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Run the actual migrations on our input database.
|
||||
err = store.MigrateData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Run the actual migrations on our input database.
|
||||
// err = store.MigrateData()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if overrideInstanceId {
|
||||
// old versions of portainer did not have instance-id. Because this gets generated
|
||||
// we need to override the expected output to match the expected value to pass the test
|
||||
v, err := store.VersionService.Version()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// if overrideInstanceId {
|
||||
// // old versions of portainer did not have instance-id. Because this gets generated
|
||||
// // we need to override the expected output to match the expected value to pass the test
|
||||
// v, err := store.VersionService.Version()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
v.InstanceID = "463d5c47-0ea5-4aca-85b1-405ceefee254"
|
||||
err = store.VersionService.UpdateVersion(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// v.InstanceID = "463d5c47-0ea5-4aca-85b1-405ceefee254"
|
||||
// err = store.VersionService.UpdateVersion(v)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
|
||||
// Assert that our database connection is using bolt so we can call
|
||||
// exportJson rather than ExportRaw. The exportJson function allows us to
|
||||
// strip out the metadata which we don't want for our tests.
|
||||
// TODO: update connection interface in CE to allow us to use ExportRaw and pass meta false
|
||||
err = store.connection.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("err closing bolt connection: %v", err)
|
||||
}
|
||||
con, ok := store.connection.(*boltdb.DbConnection)
|
||||
if !ok {
|
||||
t.Fatalf("backing database is not using boltdb, but the migrations test requires it")
|
||||
}
|
||||
// // Assert that our database connection is using bolt so we can call
|
||||
// // exportJson rather than ExportRaw. The exportJson function allows us to
|
||||
// // strip out the metadata which we don't want for our tests.
|
||||
// // TODO: update connection interface in CE to allow us to use ExportRaw and pass meta false
|
||||
// err = store.connection.Close()
|
||||
// if err != nil {
|
||||
// t.Fatalf("err closing bolt connection: %v", err)
|
||||
// }
|
||||
// con, ok := store.connection.(*boltdb.DbConnection)
|
||||
// if !ok {
|
||||
// t.Fatalf("backing database is not using boltdb, but the migrations test requires it")
|
||||
// }
|
||||
|
||||
// Convert database back to json.
|
||||
databasePath := con.GetDatabaseFilePath()
|
||||
if _, err := os.Stat(databasePath); err != nil {
|
||||
return fmt.Errorf("stat on %s failed: %w", databasePath, err)
|
||||
}
|
||||
// // Convert database back to json.
|
||||
// databasePath := con.GetDatabaseFilePath()
|
||||
// if _, err := os.Stat(databasePath); err != nil {
|
||||
// return fmt.Errorf("stat on %s failed: %w", databasePath, err)
|
||||
// }
|
||||
|
||||
gotJSON, err := con.ExportJSON(databasePath, false)
|
||||
if err != nil {
|
||||
t.Logf(
|
||||
"failed re-exporting database %s to JSON: %v",
|
||||
databasePath,
|
||||
err,
|
||||
)
|
||||
}
|
||||
// gotJSON, err := con.ExportJSON(databasePath, false)
|
||||
// if err != nil {
|
||||
// t.Logf(
|
||||
// "failed re-exporting database %s to JSON: %v",
|
||||
// databasePath,
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
|
||||
wantJSON, err := os.ReadFile(wantPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed loading want JSON file %v: %v", wantPath, err)
|
||||
}
|
||||
// wantJSON, err := os.ReadFile(wantPath)
|
||||
// if err != nil {
|
||||
// t.Fatalf("failed loading want JSON file %v: %v", wantPath, err)
|
||||
// }
|
||||
|
||||
// Compare the result we got with the one we wanted.
|
||||
if diff := cmp.Diff(wantJSON, gotJSON); diff != "" {
|
||||
gotPath := filepath.Join(os.TempDir(), "portainer-migrator-test-fail.json")
|
||||
os.WriteFile(
|
||||
gotPath,
|
||||
gotJSON,
|
||||
0600,
|
||||
)
|
||||
t.Errorf(
|
||||
"migrate data from %s to %s failed\nwrote migrated input to %s\nmismatch (-want +got):\n%s",
|
||||
srcPath,
|
||||
wantPath,
|
||||
gotPath,
|
||||
diff,
|
||||
)
|
||||
}
|
||||
// // Compare the result we got with the one we wanted.
|
||||
// if diff := cmp.Diff(wantJSON, gotJSON); diff != "" {
|
||||
// gotPath := filepath.Join(os.TempDir(), "portainer-migrator-test-fail.json")
|
||||
// os.WriteFile(
|
||||
// gotPath,
|
||||
// gotJSON,
|
||||
// 0600,
|
||||
// )
|
||||
// t.Errorf(
|
||||
// "migrate data from %s to %s failed\nwrote migrated input to %s\nmismatch (-want +got):\n%s",
|
||||
// srcPath,
|
||||
// wantPath,
|
||||
// gotPath,
|
||||
// diff,
|
||||
// )
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
|
||||
// importJSON reads input JSON and commits it to a portainer datastore.Store.
|
||||
// Errors are logged with the testing package.
|
||||
func importJSON(t *testing.T, r io.Reader, store *Store) error {
|
||||
objects := make(map[string]interface{})
|
||||
// objects := make(map[string]interface{})
|
||||
|
||||
// Parse json into map of objects.
|
||||
d := json.NewDecoder(r)
|
||||
d.UseNumber()
|
||||
err := d.Decode(&objects)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// // Parse json into map of objects.
|
||||
// d := json.NewDecoder(r)
|
||||
// d.UseNumber()
|
||||
// err := d.Decode(&objects)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
// Get database connection from store.
|
||||
con := store.connection
|
||||
// // Get database connection from store.
|
||||
// con := store.connection
|
||||
|
||||
for k, v := range objects {
|
||||
switch k {
|
||||
case "version":
|
||||
versions, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed casting %s to map[string]interface{}", k)
|
||||
}
|
||||
// for k, v := range objects {
|
||||
// switch k {
|
||||
// case "version":
|
||||
// versions, ok := v.(map[string]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed casting %s to map[string]interface{}", k)
|
||||
// }
|
||||
|
||||
// New format db
|
||||
version, ok := versions["VERSION"]
|
||||
if ok {
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("VERSION"),
|
||||
version,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing VERSION in %s: %v", k, err)
|
||||
}
|
||||
}
|
||||
// // New format db
|
||||
// version, ok := versions["VERSION"]
|
||||
// if ok {
|
||||
// err := con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("VERSION"),
|
||||
// version,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing VERSION in %s: %v", k, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// old format db
|
||||
// // old format db
|
||||
|
||||
dbVersion, ok := versions["DB_VERSION"]
|
||||
if ok {
|
||||
numDBVersion, ok := dbVersion.(json.Number)
|
||||
if !ok {
|
||||
t.Logf("failed parsing DB_VERSION as json number from %s", k)
|
||||
}
|
||||
// dbVersion, ok := versions["DB_VERSION"]
|
||||
// if ok {
|
||||
// numDBVersion, ok := dbVersion.(json.Number)
|
||||
// if !ok {
|
||||
// t.Logf("failed parsing DB_VERSION as json number from %s", k)
|
||||
// }
|
||||
|
||||
intDBVersion, err := numDBVersion.Int64()
|
||||
if err != nil {
|
||||
t.Logf("failed casting %v to int: %v", numDBVersion, intDBVersion)
|
||||
}
|
||||
// intDBVersion, err := numDBVersion.Int64()
|
||||
// if err != nil {
|
||||
// t.Logf("failed casting %v to int: %v", numDBVersion, intDBVersion)
|
||||
// }
|
||||
|
||||
err = con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("DB_VERSION"),
|
||||
int(intDBVersion),
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing DB_VERSION in %s: %v", k, err)
|
||||
}
|
||||
}
|
||||
// err = con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("DB_VERSION"),
|
||||
// int(intDBVersion),
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing DB_VERSION in %s: %v", k, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
instanceID, ok := versions["INSTANCE_ID"]
|
||||
if ok {
|
||||
err = con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("INSTANCE_ID"),
|
||||
instanceID,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing INSTANCE_ID in %s: %v", k, err)
|
||||
}
|
||||
}
|
||||
// instanceID, ok := versions["INSTANCE_ID"]
|
||||
// if ok {
|
||||
// err = con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("INSTANCE_ID"),
|
||||
// instanceID,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing INSTANCE_ID in %s: %v", k, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
edition, ok := versions["EDITION"]
|
||||
if ok {
|
||||
err = con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("EDITION"),
|
||||
edition,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing EDITION in %s: %v", k, err)
|
||||
}
|
||||
}
|
||||
// edition, ok := versions["EDITION"]
|
||||
// if ok {
|
||||
// err = con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("EDITION"),
|
||||
// edition,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing EDITION in %s: %v", k, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
case "dockerhub":
|
||||
obj, ok := v.([]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %s to []interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("DOCKERHUB"),
|
||||
obj[0],
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing DOCKERHUB in %s: %v", k, err)
|
||||
}
|
||||
// case "dockerhub":
|
||||
// obj, ok := v.([]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to cast %s to []interface{}", k)
|
||||
// }
|
||||
// err := con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("DOCKERHUB"),
|
||||
// obj[0],
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing DOCKERHUB in %s: %v", k, err)
|
||||
// }
|
||||
|
||||
case "ssl":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("SSL"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing SSL in %s: %v", k, err)
|
||||
}
|
||||
// case "ssl":
|
||||
// obj, ok := v.(map[string]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
// }
|
||||
// err := con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("SSL"),
|
||||
// obj,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing SSL in %s: %v", k, err)
|
||||
// }
|
||||
|
||||
case "settings":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("SETTINGS"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing SETTINGS in %s: %v", k, err)
|
||||
}
|
||||
// case "settings":
|
||||
// obj, ok := v.(map[string]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
// }
|
||||
// err := con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("SETTINGS"),
|
||||
// obj,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing SETTINGS in %s: %v", k, err)
|
||||
// }
|
||||
|
||||
case "tunnel_server":
|
||||
obj, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
}
|
||||
err := con.CreateObjectWithStringId(
|
||||
k,
|
||||
[]byte("INFO"),
|
||||
obj,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing INFO in %s: %v", k, err)
|
||||
}
|
||||
case "templates":
|
||||
continue
|
||||
// case "tunnel_server":
|
||||
// obj, ok := v.(map[string]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to case %s to map[string]interface{}", k)
|
||||
// }
|
||||
// err := con.CreateObjectWithStringId(
|
||||
// k,
|
||||
// []byte("INFO"),
|
||||
// obj,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing INFO in %s: %v", k, err)
|
||||
// }
|
||||
// case "templates":
|
||||
// continue
|
||||
|
||||
default:
|
||||
objlist, ok := v.([]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %s to []interface{}", k)
|
||||
}
|
||||
// default:
|
||||
// objlist, ok := v.([]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to cast %s to []interface{}", k)
|
||||
// }
|
||||
|
||||
for _, obj := range objlist {
|
||||
value, ok := obj.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Logf("failed to cast %v to map[string]interface{}", obj)
|
||||
} else {
|
||||
var ok bool
|
||||
var id interface{}
|
||||
switch k {
|
||||
case "endpoint_relations":
|
||||
// TODO: need to make into an int, then do that weird
|
||||
// stringification
|
||||
id, ok = value["EndpointID"]
|
||||
default:
|
||||
id, ok = value["Id"]
|
||||
}
|
||||
if !ok {
|
||||
// endpoint_relations: EndpointID
|
||||
t.Logf("missing Id field: %s", k)
|
||||
id = "error"
|
||||
}
|
||||
n, ok := id.(json.Number)
|
||||
if !ok {
|
||||
t.Logf("failed to cast %v to json.Number in %s", id, k)
|
||||
} else {
|
||||
key, err := n.Int64()
|
||||
if err != nil {
|
||||
t.Logf("failed to cast %v to int in %s", n, k)
|
||||
} else {
|
||||
err := con.CreateObjectWithId(
|
||||
k,
|
||||
int(key),
|
||||
value,
|
||||
)
|
||||
if err != nil {
|
||||
t.Logf("failed writing %v in %s: %v", key, k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// for _, obj := range objlist {
|
||||
// value, ok := obj.(map[string]interface{})
|
||||
// if !ok {
|
||||
// t.Logf("failed to cast %v to map[string]interface{}", obj)
|
||||
// } else {
|
||||
// var ok bool
|
||||
// var id interface{}
|
||||
// switch k {
|
||||
// case "endpoint_relations":
|
||||
// // TODO: need to make into an int, then do that weird
|
||||
// // stringification
|
||||
// id, ok = value["EndpointID"]
|
||||
// default:
|
||||
// id, ok = value["Id"]
|
||||
// }
|
||||
// if !ok {
|
||||
// // endpoint_relations: EndpointID
|
||||
// t.Logf("missing Id field: %s", k)
|
||||
// id = "error"
|
||||
// }
|
||||
// n, ok := id.(json.Number)
|
||||
// if !ok {
|
||||
// t.Logf("failed to cast %v to json.Number in %s", id, k)
|
||||
// } else {
|
||||
// key, err := n.Int64()
|
||||
// if err != nil {
|
||||
// t.Logf("failed to cast %v to int in %s", n, k)
|
||||
// } else {
|
||||
// err := con.CreateObjectWithId(
|
||||
// k,
|
||||
// int(key),
|
||||
// value,
|
||||
// )
|
||||
// if err != nil {
|
||||
// t.Logf("failed writing %v in %s: %v", key, k, err)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,89 +1,89 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
// import (
|
||||
// "testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore/migrator"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
)
|
||||
// portainer "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/datastore/migrator"
|
||||
// "github.com/portainer/portainer/api/internal/authorization"
|
||||
// )
|
||||
|
||||
const dummyLogoURL = "example.com"
|
||||
// const dummyLogoURL = "example.com"
|
||||
|
||||
// initTestingDBConn creates a settings service with raw database DB connection
|
||||
// for unit testing usage only since using NewStore will cause cycle import inside migrator pkg
|
||||
func initTestingSettingsService(dbConn portainer.Connection, preSetObj map[string]interface{}) error {
|
||||
//insert a obj
|
||||
return dbConn.UpdateObject("settings", []byte("SETTINGS"), preSetObj)
|
||||
}
|
||||
// func initTestingSettingsService(dbConn portainer.Connection, preSetObj map[string]interface{}) error {
|
||||
// //insert a obj
|
||||
// return dbConn.UpdateObject("settings", []byte("SETTINGS"), preSetObj)
|
||||
// }
|
||||
|
||||
func setup(store *Store) error {
|
||||
dummySettingsObj := map[string]interface{}{
|
||||
"LogoURL": dummyLogoURL,
|
||||
}
|
||||
// func setup(store *Store) error {
|
||||
// dummySettingsObj := map[string]interface{}{
|
||||
// "LogoURL": dummyLogoURL,
|
||||
// }
|
||||
|
||||
return initTestingSettingsService(store.connection, dummySettingsObj)
|
||||
}
|
||||
// return initTestingSettingsService(store.connection, dummySettingsObj)
|
||||
// }
|
||||
|
||||
func TestMigrateSettings(t *testing.T) {
|
||||
_, store := MustNewTestStore(t, false, true)
|
||||
// func TestMigrateSettings(t *testing.T) {
|
||||
// _, store := MustNewTestStore(t, false, true)
|
||||
|
||||
err := setup(store)
|
||||
if err != nil {
|
||||
t.Errorf("failed to complete testing setups, err: %v", err)
|
||||
}
|
||||
// // err := setup(store)
|
||||
// // if err != nil {
|
||||
// // t.Errorf("failed to complete testing setups, err: %v", err)
|
||||
// // }
|
||||
|
||||
updatedSettings, err := store.SettingsService.Settings()
|
||||
// SO -basically, this test _isn't_ testing migration, its testing golang type defaults.
|
||||
if updatedSettings.LogoURL != dummyLogoURL { // ensure a pre-migrate setting isn't unset
|
||||
t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
}
|
||||
// updatedSettings, err := store.SettingsService.Settings()
|
||||
// // SO -basically, this test _isn't_ testing migration, its testing golang type defaults.
|
||||
// if updatedSettings.LogoURL != dummyLogoURL { // ensure a pre-migrate setting isn't unset
|
||||
// t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
// }
|
||||
|
||||
if updatedSettings.OAuthSettings.SSO != false { // I recon golang defaulting will make this false
|
||||
t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
}
|
||||
// if updatedSettings.OAuthSettings.SSO != false { // I recon golang defaulting will make this false
|
||||
// t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
// }
|
||||
|
||||
if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
}
|
||||
// if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
// t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
// }
|
||||
|
||||
m := migrator.NewMigrator(&migrator.MigratorParameters{
|
||||
EndpointGroupService: store.EndpointGroupService,
|
||||
EndpointService: store.EndpointService,
|
||||
EndpointRelationService: store.EndpointRelationService,
|
||||
ExtensionService: store.ExtensionService,
|
||||
RegistryService: store.RegistryService,
|
||||
ResourceControlService: store.ResourceControlService,
|
||||
RoleService: store.RoleService,
|
||||
ScheduleService: store.ScheduleService,
|
||||
SettingsService: store.SettingsService,
|
||||
StackService: store.StackService,
|
||||
TagService: store.TagService,
|
||||
TeamMembershipService: store.TeamMembershipService,
|
||||
UserService: store.UserService,
|
||||
VersionService: store.VersionService,
|
||||
FileService: store.fileService,
|
||||
DockerhubService: store.DockerHubService,
|
||||
AuthorizationService: authorization.NewService(store),
|
||||
})
|
||||
// m := migrator.NewMigrator(&migrator.MigratorParameters{
|
||||
// EndpointGroupService: store.EndpointGroupService,
|
||||
// EndpointService: store.EndpointService,
|
||||
// EndpointRelationService: store.EndpointRelationService,
|
||||
// ExtensionService: store.ExtensionService,
|
||||
// RegistryService: store.RegistryService,
|
||||
// ResourceControlService: store.ResourceControlService,
|
||||
// RoleService: store.RoleService,
|
||||
// ScheduleService: store.ScheduleService,
|
||||
// SettingsService: store.SettingsService,
|
||||
// StackService: store.StackService,
|
||||
// TagService: store.TagService,
|
||||
// TeamMembershipService: store.TeamMembershipService,
|
||||
// UserService: store.UserService,
|
||||
// VersionService: store.VersionService,
|
||||
// FileService: store.fileService,
|
||||
// DockerhubService: store.DockerHubService,
|
||||
// AuthorizationService: authorization.NewService(store),
|
||||
// })
|
||||
|
||||
if err := m.MigrateSettingsToDB30(); err != nil {
|
||||
t.Errorf("failed to update settings: %v", err)
|
||||
}
|
||||
// if err := m.MigrateSettingsToDB30(); err != nil {
|
||||
// t.Errorf("failed to update settings: %v", err)
|
||||
// }
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("failed to retrieve the updated settings: %v", err)
|
||||
}
|
||||
// if err != nil {
|
||||
// t.Errorf("failed to retrieve the updated settings: %v", err)
|
||||
// }
|
||||
|
||||
if updatedSettings.LogoURL != dummyLogoURL {
|
||||
t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
}
|
||||
// if updatedSettings.LogoURL != dummyLogoURL {
|
||||
// t.Errorf("unexpected value changes in the updated settings, want LogoURL value: %s, got LogoURL value: %s", dummyLogoURL, updatedSettings.LogoURL)
|
||||
// }
|
||||
|
||||
if updatedSettings.OAuthSettings.SSO != false {
|
||||
t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
}
|
||||
// if updatedSettings.OAuthSettings.SSO != false {
|
||||
// t.Errorf("unexpected default OAuth SSO setting, want: false, got: %t", updatedSettings.OAuthSettings.SSO)
|
||||
// }
|
||||
|
||||
if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
}
|
||||
}
|
||||
// if updatedSettings.OAuthSettings.LogoutURI != "" {
|
||||
// t.Errorf("unexpected default OAuth HideInternalAuth setting, want:, got: %s", updatedSettings.OAuthSettings.LogoutURI)
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -31,22 +31,22 @@ func TestMigrateStackEntryPoint(t *testing.T) {
|
||||
assert.NoError(t, err, "failed to create stack")
|
||||
}
|
||||
|
||||
s, err := stackService.Read(1)
|
||||
s, err := stackService.Stack(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, s.GitConfig, "first stack should not have git config")
|
||||
|
||||
s, err = stackService.Read(2)
|
||||
s, err = stackService.Stack(2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "", s.GitConfig.ConfigFilePath, "not migrated yet migrated")
|
||||
|
||||
err = migrator.MigrateStackEntryPoint(stackService)
|
||||
assert.NoError(t, err, "failed to migrate entry point to Git ConfigFilePath")
|
||||
|
||||
s, err = stackService.Read(1)
|
||||
s, err = stackService.Stack(1)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, s.GitConfig, "first stack should not have git config")
|
||||
|
||||
s, err = stackService.Read(2)
|
||||
s, err = stackService.Stack(2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "dir/sub/compose.yml", s.GitConfig.ConfigFilePath, "second stack should have config file path migrated")
|
||||
}
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
package datastore
|
||||
|
||||
import (
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
)
|
||||
// import (
|
||||
// portaineree "github.com/portainer/portainer/api"
|
||||
// "github.com/portainer/portainer/api/database/models"
|
||||
// "github.com/portainer/portainer/api/dataservices"
|
||||
// )
|
||||
|
||||
const (
|
||||
bucketName = "version"
|
||||
legacyDBVersionKey = "DB_VERSION"
|
||||
legacyInstanceKey = "INSTANCE_ID"
|
||||
legacyEditionKey = "EDITION"
|
||||
)
|
||||
// const (
|
||||
// bucketName = "version"
|
||||
// legacyDBVersionKey = "DB_VERSION"
|
||||
// legacyInstanceKey = "INSTANCE_ID"
|
||||
// legacyEditionKey = "EDITION"
|
||||
// )
|
||||
|
||||
var dbVerToSemVerMap = map[int]string{
|
||||
18: "1.21",
|
||||
19: "1.22",
|
||||
20: "1.22.1",
|
||||
21: "1.22.2",
|
||||
22: "1.23",
|
||||
23: "1.24",
|
||||
24: "1.24.1",
|
||||
25: "2.0",
|
||||
26: "2.1",
|
||||
27: "2.2",
|
||||
28: "2.4",
|
||||
29: "2.4",
|
||||
30: "2.6",
|
||||
31: "2.7",
|
||||
32: "2.9",
|
||||
33: "2.9.1",
|
||||
34: "2.10",
|
||||
35: "2.9.3",
|
||||
36: "2.11",
|
||||
40: "2.13",
|
||||
50: "2.14",
|
||||
51: "2.14.1",
|
||||
52: "2.14.2",
|
||||
60: "2.15",
|
||||
61: "2.15.1",
|
||||
70: "2.16",
|
||||
80: "2.17",
|
||||
}
|
||||
// var dbVerToSemVerMap = map[int]string{
|
||||
// 18: "1.21",
|
||||
// 19: "1.22",
|
||||
// 20: "1.22.1",
|
||||
// 21: "1.22.2",
|
||||
// 22: "1.23",
|
||||
// 23: "1.24",
|
||||
// 24: "1.24.1",
|
||||
// 25: "2.0",
|
||||
// 26: "2.1",
|
||||
// 27: "2.2",
|
||||
// 28: "2.4",
|
||||
// 29: "2.4",
|
||||
// 30: "2.6",
|
||||
// 31: "2.7",
|
||||
// 32: "2.9",
|
||||
// 33: "2.9.1",
|
||||
// 34: "2.10",
|
||||
// 35: "2.9.3",
|
||||
// 36: "2.11",
|
||||
// 40: "2.13",
|
||||
// 50: "2.14",
|
||||
// 51: "2.14.1",
|
||||
// 52: "2.14.2",
|
||||
// 60: "2.15",
|
||||
// 61: "2.15.1",
|
||||
// 70: "2.16",
|
||||
// 80: "2.17",
|
||||
// }
|
||||
|
||||
func dbVersionToSemanticVersion(dbVersion int) string {
|
||||
if dbVersion < 18 {
|
||||
return "1.0.0"
|
||||
}
|
||||
// func dbVersionToSemanticVersion(dbVersion int) string {
|
||||
// if dbVersion < 18 {
|
||||
// return "1.0.0"
|
||||
// }
|
||||
|
||||
ver, ok := dbVerToSemVerMap[dbVersion]
|
||||
if ok {
|
||||
return ver
|
||||
}
|
||||
// ver, ok := dbVerToSemVerMap[dbVersion]
|
||||
// if ok {
|
||||
// return ver
|
||||
// }
|
||||
|
||||
// We should always return something sensible
|
||||
switch {
|
||||
case dbVersion < 40:
|
||||
return "2.11"
|
||||
case dbVersion < 50:
|
||||
return "2.13"
|
||||
case dbVersion < 60:
|
||||
return "2.14.2"
|
||||
case dbVersion < 70:
|
||||
return "2.15.1"
|
||||
}
|
||||
// // We should always return something sensible
|
||||
// switch {
|
||||
// case dbVersion < 40:
|
||||
// return "2.11"
|
||||
// case dbVersion < 50:
|
||||
// return "2.13"
|
||||
// case dbVersion < 60:
|
||||
// return "2.14.2"
|
||||
// case dbVersion < 70:
|
||||
// return "2.15.1"
|
||||
// }
|
||||
|
||||
return "2.16.0"
|
||||
}
|
||||
// return "2.16.0"
|
||||
// }
|
||||
|
||||
// getOrMigrateLegacyVersion to new Version struct
|
||||
func (store *Store) getOrMigrateLegacyVersion() (*models.Version, error) {
|
||||
// Very old versions of portainer did not have a version bucket, lets set some defaults
|
||||
dbVersion := 24
|
||||
edition := int(portainer.PortainerCE)
|
||||
instanceId := ""
|
||||
// // getOrMigrateLegacyVersion to new Version struct
|
||||
// func (store *Store) getOrMigrateLegacyVersion() (*models.Version, error) {
|
||||
// // Very old versions of portainer did not have a version bucket, lets set some defaults
|
||||
// dbVersion := 24
|
||||
// edition := int(portaineree.PortainerCE)
|
||||
// instanceId := ""
|
||||
|
||||
// If we already have a version key, we don't need to migrate
|
||||
v, err := store.VersionService.Version()
|
||||
if err == nil || !dataservices.IsErrObjectNotFound(err) {
|
||||
return v, err
|
||||
}
|
||||
// // If we already have a version key, we don't need to migrate
|
||||
// v, err := store.VersionService.Version()
|
||||
// if err == nil || !dataservices.IsErrObjectNotFound(err) {
|
||||
// return v, err
|
||||
// }
|
||||
|
||||
err = store.connection.GetObject(bucketName, []byte(legacyDBVersionKey), &dbVersion)
|
||||
if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
// err = store.connection.GetObject(bucketName, []byte(legacyDBVersionKey), &dbVersion)
|
||||
// if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
err = store.connection.GetObject(bucketName, []byte(legacyEditionKey), &edition)
|
||||
if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
// err = store.connection.GetObject(bucketName, []byte(legacyEditionKey), &edition)
|
||||
// if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
err = store.connection.GetObject(bucketName, []byte(legacyInstanceKey), &instanceId)
|
||||
if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
// err = store.connection.GetObject(bucketName, []byte(legacyInstanceKey), &instanceId)
|
||||
// if err != nil && !dataservices.IsErrObjectNotFound(err) {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
return &models.Version{
|
||||
SchemaVersion: dbVersionToSemanticVersion(dbVersion),
|
||||
Edition: edition,
|
||||
InstanceID: string(instanceId),
|
||||
}, nil
|
||||
}
|
||||
// return &models.Version{
|
||||
// SchemaVersion: dbVersionToSemanticVersion(dbVersion),
|
||||
// Edition: edition,
|
||||
// InstanceID: string(instanceId),
|
||||
// }, nil
|
||||
// }
|
||||
|
||||
// finishMigrateLegacyVersion writes the new version to the DB and removes the old version keys from the DB
|
||||
func (store *Store) finishMigrateLegacyVersion(versionToWrite *models.Version) error {
|
||||
err := store.VersionService.UpdateVersion(versionToWrite)
|
||||
// // finishMigrateLegacyVersion writes the new version to the DB and removes the old version keys from the DB
|
||||
// func (store *Store) finishMigrateLegacyVersion(versionToWrite *models.Version) error {
|
||||
// err := store.VersionService.UpdateVersion(versionToWrite)
|
||||
|
||||
// Remove legacy keys if present
|
||||
store.connection.DeleteObject(bucketName, []byte(legacyDBVersionKey))
|
||||
store.connection.DeleteObject(bucketName, []byte(legacyEditionKey))
|
||||
store.connection.DeleteObject(bucketName, []byte(legacyInstanceKey))
|
||||
return err
|
||||
}
|
||||
// // Remove legacy keys if present
|
||||
// store.connection.DeleteObject(bucketName, []byte(legacyDBVersionKey))
|
||||
// store.connection.DeleteObject(bucketName, []byte(legacyEditionKey))
|
||||
// store.connection.DeleteObject(bucketName, []byte(legacyInstanceKey))
|
||||
// return err
|
||||
// }
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package migrator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -21,16 +23,28 @@ func GetFunctionName(i interface{}) string {
|
||||
|
||||
// Migrate checks the database version and migrate the existing data to the most recent data model.
|
||||
func (m *Migrator) Migrate() error {
|
||||
version, err := m.versionService.Version()
|
||||
versions, err := m.versionService.GetAll()
|
||||
if err != nil {
|
||||
return migrationError(err, "get version service")
|
||||
}
|
||||
|
||||
schemaVersion, err := semver.NewVersion(version.SchemaVersion)
|
||||
version, ok := versions[models.VersionKey]
|
||||
if !ok {
|
||||
return migrationError(err, "no version specified in the DB")
|
||||
}
|
||||
|
||||
schemaVersion, err := semver.NewVersion(fmt.Sprintf("%v", version))
|
||||
if err != nil {
|
||||
return migrationError(err, "invalid db schema version")
|
||||
}
|
||||
|
||||
migratorCount, ok := versions[models.MigratorCountKey]
|
||||
if ok {
|
||||
migratorCount = migratorCount.(int)
|
||||
} else {
|
||||
migratorCount = 0
|
||||
}
|
||||
|
||||
newMigratorCount := 0
|
||||
apiVersion := semver.MustParse(portainer.APIVersion)
|
||||
if schemaVersion.Equal(apiVersion) {
|
||||
@@ -38,7 +52,7 @@ func (m *Migrator) Migrate() error {
|
||||
// e.g. development builds
|
||||
latestMigrations := m.LatestMigrations()
|
||||
if latestMigrations.Version.Equal(schemaVersion) &&
|
||||
version.MigratorCount != len(latestMigrations.MigrationFuncs) {
|
||||
migratorCount != len(latestMigrations.MigrationFuncs) {
|
||||
err := runMigrations(latestMigrations.MigrationFuncs)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -68,10 +82,10 @@ func (m *Migrator) Migrate() error {
|
||||
return migrationError(err, "Always migrations returned error")
|
||||
}
|
||||
|
||||
version.SchemaVersion = portainer.APIVersion
|
||||
version.MigratorCount = newMigratorCount
|
||||
versions[models.SchemaVersionKey] = portainer.APIVersion
|
||||
versions[models.MigratorCountKey] = newMigratorCount
|
||||
|
||||
err = m.versionService.UpdateVersion(version)
|
||||
err = m.versionService.UpdateAll(versions)
|
||||
if err != nil {
|
||||
return migrationError(err, "StoreDBVersion")
|
||||
}
|
||||
@@ -91,35 +105,35 @@ func runMigrations(migrationFuncs []func() error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Migrator) NeedsMigration() bool {
|
||||
// we need to migrate if anything changes with the version in the DB vs what our software version is.
|
||||
// If the version matches, then it's all down to the number of migration funcs we have for the current version
|
||||
// i.e. the MigratorCount
|
||||
// func (m *Migrator) NeedsMigration() bool {
|
||||
// // we need to migrate if anything changes with the version in the DB vs what our software version is.
|
||||
// // If the version matches, then it's all down to the number of migration funcs we have for the current version
|
||||
// // i.e. the MigratorCount
|
||||
|
||||
// In this particular instance we should log a fatal error
|
||||
if m.CurrentDBEdition() != portainer.PortainerCE {
|
||||
log.Fatal().Msg("the Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
|
||||
return false
|
||||
}
|
||||
// // In this particular instance we should log a fatal error
|
||||
// if m.CurrentDBEdition() != portainer.PortainerCE {
|
||||
// log.Fatal().Msg("the Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
|
||||
// return false
|
||||
// }
|
||||
|
||||
if m.CurrentSemanticDBVersion().LessThan(semver.MustParse(portainer.APIVersion)) {
|
||||
return true
|
||||
}
|
||||
// if m.CurrentSemanticDBVersion().LessThan(semver.MustParse(portainer.APIVersion)) {
|
||||
// return true
|
||||
// }
|
||||
|
||||
// Check if we have any migrations for the current version
|
||||
latestMigrations := m.LatestMigrations()
|
||||
if latestMigrations.Version.Equal(semver.MustParse(portainer.APIVersion)) {
|
||||
if m.currentDBVersion.MigratorCount != len(latestMigrations.MigrationFuncs) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// One remaining possibility if we get here. If our migrator count > 0 and we have no migration funcs
|
||||
// for the current version (i.e. they were deleted during development). Then we we need to migrate.
|
||||
// This is to reset the migrator count back to 0
|
||||
if m.currentDBVersion.MigratorCount > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// // Check if we have any migrations for the current version
|
||||
// latestMigrations := m.LatestMigrations()
|
||||
// if latestMigrations.Version.Equal(semver.MustParse(portainer.APIVersion)) {
|
||||
// if m.currentDBVersion.MigratorCount != len(latestMigrations.MigrationFuncs) {
|
||||
// return true
|
||||
// }
|
||||
// } else {
|
||||
// // One remaining possibility if we get here. If our migrator count > 0 and we have no migration funcs
|
||||
// // for the current version (i.e. they were deleted during development). Then we we need to migrate.
|
||||
// // This is to reset the migrator count back to 0
|
||||
// if m.currentDBVersion.MigratorCount > 0 {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
// return false
|
||||
// }
|
||||
|
||||
@@ -2,11 +2,7 @@ package migrator
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/chisel/crypto"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -24,133 +20,10 @@ func (m *Migrator) migrateDockerDesktopExtentionSetting() error {
|
||||
}
|
||||
|
||||
settings.IsDockerDesktopExtension = isDDExtension
|
||||
|
||||
return m.settingsService.UpdateSettings(settings)
|
||||
}
|
||||
|
||||
func (m *Migrator) convertSeedToPrivateKeyForDB100() error {
|
||||
var serverInfo *portainer.TunnelServerInfo
|
||||
|
||||
serverInfo, err := m.TunnelServerService.Info()
|
||||
if err != nil {
|
||||
if dataservices.IsErrObjectNotFound(err) {
|
||||
log.Info().Msg("ServerInfo object not found")
|
||||
return nil
|
||||
}
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to read ServerInfo from DB")
|
||||
return err
|
||||
}
|
||||
|
||||
if serverInfo.PrivateKeySeed != "" {
|
||||
key, err := crypto.GenerateGo119CompatibleKey(serverInfo.PrivateKeySeed)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to read ServerInfo from DB")
|
||||
return err
|
||||
}
|
||||
|
||||
err = m.fileService.StoreChiselPrivateKey(key)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to save Chisel private key to disk")
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
log.Info().Msg("PrivateKeySeed is blank")
|
||||
}
|
||||
|
||||
serverInfo.PrivateKeySeed = ""
|
||||
err = m.TunnelServerService.UpdateInfo(serverInfo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Err(err).
|
||||
Msg("Failed to clean private key seed in DB")
|
||||
} else {
|
||||
log.Info().Msg("Success to migrate private key seed to private key file")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Migrator) updateEdgeStackStatusForDB100() error {
|
||||
log.Info().Msg("update edge stack status to have deployment steps")
|
||||
|
||||
edgeStacks, err := m.edgeStackService.EdgeStacks()
|
||||
err = m.settingsService.UpdateSettings(settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, edgeStack := range edgeStacks {
|
||||
|
||||
for environmentID, environmentStatus := range edgeStack.Status {
|
||||
// skip if status is already updated
|
||||
if len(environmentStatus.Status) > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
statusArray := []portainer.EdgeStackDeploymentStatus{}
|
||||
if environmentStatus.Details.Pending {
|
||||
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusPending,
|
||||
Time: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
if environmentStatus.Details.Acknowledged {
|
||||
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusAcknowledged,
|
||||
Time: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
if environmentStatus.Details.Error {
|
||||
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusError,
|
||||
Error: environmentStatus.Error,
|
||||
Time: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
if environmentStatus.Details.Ok {
|
||||
statusArray = append(statusArray,
|
||||
portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusDeploymentReceived,
|
||||
Time: time.Now().Unix(),
|
||||
},
|
||||
portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusRunning,
|
||||
Time: time.Now().Unix(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if environmentStatus.Details.ImagesPulled {
|
||||
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusImagesPulled,
|
||||
Time: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
if environmentStatus.Details.Remove {
|
||||
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
|
||||
Type: portainer.EdgeStackStatusRemoving,
|
||||
Time: time.Now().Unix(),
|
||||
})
|
||||
}
|
||||
|
||||
environmentStatus.Status = statusArray
|
||||
|
||||
edgeStack.Status[environmentID] = environmentStatus
|
||||
}
|
||||
|
||||
err = m.edgeStackService.UpdateEdgeStack(edgeStack.ID, &edgeStack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
func (m *Migrator) updateUsersToDBVersion18() error {
|
||||
log.Info().Msg("updating users")
|
||||
|
||||
legacyUsers, err := m.userService.ReadAll()
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (m *Migrator) updateUsersToDBVersion18() error {
|
||||
portainer.OperationPortainerUserMemberships: true,
|
||||
}
|
||||
|
||||
err = m.userService.Update(user.ID, &user)
|
||||
err = m.userService.UpdateUser(user.ID, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func (m *Migrator) updateEndpointsToDBVersion18() error {
|
||||
func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
log.Info().Msg("updating endpoint groups")
|
||||
|
||||
legacyEndpointGroups, err := m.endpointGroupService.ReadAll()
|
||||
legacyEndpointGroups, err := m.endpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
}
|
||||
}
|
||||
|
||||
err = m.endpointGroupService.Update(endpointGroup.ID, &endpointGroup)
|
||||
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (m *Migrator) updateEndpointGroupsToDBVersion18() error {
|
||||
func (m *Migrator) updateRegistriesToDBVersion18() error {
|
||||
log.Info().Msg("updating registries")
|
||||
|
||||
legacyRegistries, err := m.registryService.ReadAll()
|
||||
legacyRegistries, err := m.registryService.Registries()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +125,7 @@ func (m *Migrator) updateRegistriesToDBVersion18() error {
|
||||
registry.TeamAccessPolicies[teamID] = portainer.AccessPolicy{}
|
||||
}
|
||||
|
||||
err = m.registryService.Update(registry.ID, ®istry)
|
||||
err = m.registryService.UpdateRegistry(registry.ID, ®istry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
log.Info().Msg("updating resource controls")
|
||||
|
||||
legacyResourceControls, err := m.resourceControlService.ReadAll()
|
||||
legacyResourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -18,7 +18,7 @@ func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
for _, resourceControl := range legacyResourceControls {
|
||||
resourceControl.AdministratorsOnly = false
|
||||
|
||||
err := m.resourceControlService.Update(resourceControl.ID, &resourceControl)
|
||||
err := m.resourceControlService.UpdateResourceControl(resourceControl.ID, &resourceControl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func (m *Migrator) updateResourceControlsToDBVersion22() error {
|
||||
func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
log.Info().Msg("updating users and roles")
|
||||
|
||||
legacyUsers, err := m.userService.ReadAll()
|
||||
legacyUsers, err := m.userService.Users()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -42,47 +42,47 @@ func (m *Migrator) updateUsersAndRolesToDBVersion22() error {
|
||||
|
||||
for _, user := range legacyUsers {
|
||||
user.PortainerAuthorizations = authorization.DefaultPortainerAuthorizations()
|
||||
err = m.userService.Update(user.ID, &user)
|
||||
err = m.userService.UpdateUser(user.ID, &user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
endpointAdministratorRole, err := m.roleService.Read(portainer.RoleID(1))
|
||||
endpointAdministratorRole, err := m.roleService.Role(portainer.RoleID(1))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
endpointAdministratorRole.Priority = 1
|
||||
endpointAdministratorRole.Authorizations = authorization.DefaultEndpointAuthorizationsForEndpointAdministratorRole()
|
||||
|
||||
err = m.roleService.Update(endpointAdministratorRole.ID, endpointAdministratorRole)
|
||||
err = m.roleService.UpdateRole(endpointAdministratorRole.ID, endpointAdministratorRole)
|
||||
|
||||
helpDeskRole, err := m.roleService.Read(portainer.RoleID(2))
|
||||
helpDeskRole, err := m.roleService.Role(portainer.RoleID(2))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
helpDeskRole.Priority = 2
|
||||
helpDeskRole.Authorizations = authorization.DefaultEndpointAuthorizationsForHelpDeskRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.Update(helpDeskRole.ID, helpDeskRole)
|
||||
err = m.roleService.UpdateRole(helpDeskRole.ID, helpDeskRole)
|
||||
|
||||
standardUserRole, err := m.roleService.Read(portainer.RoleID(3))
|
||||
standardUserRole, err := m.roleService.Role(portainer.RoleID(3))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
standardUserRole.Priority = 3
|
||||
standardUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForStandardUserRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.Update(standardUserRole.ID, standardUserRole)
|
||||
err = m.roleService.UpdateRole(standardUserRole.ID, standardUserRole)
|
||||
|
||||
readOnlyUserRole, err := m.roleService.Read(portainer.RoleID(4))
|
||||
readOnlyUserRole, err := m.roleService.Role(portainer.RoleID(4))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
readOnlyUserRole.Priority = 4
|
||||
readOnlyUserRole.Authorizations = authorization.DefaultEndpointAuthorizationsForReadOnlyUserRole(settings.AllowVolumeBrowserForRegularUsers)
|
||||
|
||||
err = m.roleService.Update(readOnlyUserRole.ID, readOnlyUserRole)
|
||||
err = m.roleService.UpdateRole(readOnlyUserRole.ID, readOnlyUserRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
log.Info().Msg("updating tags")
|
||||
|
||||
tags, err := m.tagService.ReadAll()
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -17,7 +17,7 @@ func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
for _, tag := range tags {
|
||||
tag.EndpointGroups = make(map[portainer.EndpointGroupID]bool)
|
||||
tag.Endpoints = make(map[portainer.EndpointID]bool)
|
||||
err = m.tagService.Update(tag.ID, &tag)
|
||||
err = m.tagService.UpdateTag(tag.ID, &tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func (m *Migrator) updateTagsToDBVersion23() error {
|
||||
func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
log.Info().Msg("updating endpoints and endpoint groups")
|
||||
|
||||
tags, err := m.tagService.ReadAll()
|
||||
tags, err := m.tagService.Tags()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
}
|
||||
}
|
||||
|
||||
endpointGroups, err := m.endpointGroupService.ReadAll()
|
||||
endpointGroups, err := m.endpointGroupService.EndpointGroups()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -85,14 +85,14 @@ func (m *Migrator) updateEndpointsAndEndpointGroupsToDBVersion23() error {
|
||||
}
|
||||
}
|
||||
endpointGroup.TagIDs = endpointGroupTags
|
||||
err = m.endpointGroupService.Update(endpointGroup.ID, &endpointGroup)
|
||||
err = m.endpointGroupService.UpdateEndpointGroup(endpointGroup.ID, &endpointGroup)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, tag := range tagsNameMap {
|
||||
err = m.tagService.Update(tag.ID, &tag)
|
||||
err = m.tagService.UpdateTag(tag.ID, &tag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func (m *Migrator) updateSettingsToDB24() error {
|
||||
func (m *Migrator) updateStacksToDB24() error {
|
||||
log.Info().Msg("updating stacks")
|
||||
|
||||
stacks, err := m.stackService.ReadAll()
|
||||
stacks, err := m.stackService.Stacks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -32,7 +32,7 @@ func (m *Migrator) updateStacksToDB24() error {
|
||||
for idx := range stacks {
|
||||
stack := &stacks[idx]
|
||||
stack.Status = portainer.StackStatusActive
|
||||
err := m.stackService.Update(stack.ID, stack)
|
||||
err := m.stackService.UpdateStack(stack.ID, stack)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
func (m *Migrator) updateStackResourceControlToDB27() error {
|
||||
log.Info().Msg("updating stack resource controls")
|
||||
|
||||
resourceControls, err := m.resourceControlService.ReadAll()
|
||||
resourceControls, err := m.resourceControlService.ResourceControls()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -34,7 +34,7 @@ func (m *Migrator) updateStackResourceControlToDB27() error {
|
||||
|
||||
resource.ResourceID = stackutils.ResourceControlID(stack.EndpointID, stack.Name)
|
||||
|
||||
err = m.resourceControlService.Update(resource.ID, &resource)
|
||||
err = m.resourceControlService.UpdateResourceControl(resource.ID, &resource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user