mirror of
https://github.com/portainer/portainer.git
synced 2025-08-10 16:25:22 +02:00
fix(linters): upgrade golangci-lint to v2.3.1 BE-12136 (#997)
This commit is contained in:
parent
85b7e881eb
commit
64ed988169
69 changed files with 582 additions and 318 deletions
|
@ -1,50 +1,59 @@
|
|||
version: "2"
|
||||
linters:
|
||||
# Disable all linters, the defaults don't pass on our code yet
|
||||
disable-all: true
|
||||
|
||||
# Enable these for now
|
||||
default: none
|
||||
enable:
|
||||
- unused
|
||||
- depguard
|
||||
- gosimple
|
||||
- govet
|
||||
- errorlint
|
||||
- bodyclose
|
||||
- copyloopvar
|
||||
- depguard
|
||||
- errorlint
|
||||
- forbidigo
|
||||
- govet
|
||||
- ineffassign
|
||||
- intrange
|
||||
- perfsprint
|
||||
- ineffassign
|
||||
- bodyclose
|
||||
- forbidigo
|
||||
|
||||
linters-settings:
|
||||
forbidigo:
|
||||
analyze-types: true
|
||||
forbid:
|
||||
- p: ^tls\.Config$
|
||||
msg: 'Use crypto.CreateTLSConfiguration() instead'
|
||||
- p: ^tls\.Config\.(InsecureSkipVerify|MinVersion|MaxVersion|CipherSuites|CurvePreferences)$
|
||||
msg: 'Do not set this field directly, use crypto.CreateTLSConfiguration() instead'
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: 'encoding/json'
|
||||
desc: 'use github.com/segmentio/encoding/json'
|
||||
- pkg: 'golang.org/x/exp'
|
||||
desc: 'exp is not allowed'
|
||||
- pkg: 'github.com/portainer/libcrypto'
|
||||
desc: 'use github.com/portainer/portainer/pkg/libcrypto'
|
||||
- pkg: 'github.com/portainer/libhttp'
|
||||
desc: 'use github.com/portainer/portainer/pkg/libhttp'
|
||||
files:
|
||||
- '!**/*_test.go'
|
||||
- '!**/base.go'
|
||||
- '!**/base_tx.go'
|
||||
|
||||
# errorlint is causing a typecheck error for some reason. The go compiler will report these
|
||||
# anyway, so ignore them from the linter
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: ./
|
||||
linters:
|
||||
- typecheck
|
||||
- staticcheck
|
||||
- unused
|
||||
settings:
|
||||
staticcheck:
|
||||
checks: ["all", "-ST1003", "-ST1005", "-ST1016", "-SA1019", "-QF1003"]
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
files:
|
||||
- '!**/*_test.go'
|
||||
- '!**/base.go'
|
||||
- '!**/base_tx.go'
|
||||
deny:
|
||||
- pkg: encoding/json
|
||||
desc: use github.com/segmentio/encoding/json
|
||||
- pkg: golang.org/x/exp
|
||||
desc: exp is not allowed
|
||||
- pkg: github.com/portainer/libcrypto
|
||||
desc: use github.com/portainer/portainer/pkg/libcrypto
|
||||
- pkg: github.com/portainer/libhttp
|
||||
desc: use github.com/portainer/portainer/pkg/libhttp
|
||||
forbidigo:
|
||||
forbid:
|
||||
- pattern: ^tls\.Config$
|
||||
msg: Use crypto.CreateTLSConfiguration() instead
|
||||
- pattern: ^tls\.Config\.(InsecureSkipVerify|MinVersion|MaxVersion|CipherSuites|CurvePreferences)$
|
||||
msg: Do not set this field directly, use crypto.CreateTLSConfiguration() instead
|
||||
analyze-types: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
|
@ -54,8 +54,8 @@ func ecdsaGenerateKey(c elliptic.Curve, rand io.Reader) (*ecdsa.PrivateKey, erro
|
|||
}
|
||||
|
||||
priv := new(ecdsa.PrivateKey)
|
||||
priv.PublicKey.Curve = c
|
||||
priv.Curve = c
|
||||
priv.D = k
|
||||
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
|
||||
priv.X, priv.Y = c.ScalarBaseMult(k.Bytes())
|
||||
return priv, nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/portainer/portainer/pkg/fips"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
|
@ -56,8 +57,7 @@ func AesEncrypt(input io.Reader, output io.Writer, passphrase []byte) error {
|
|||
|
||||
// AesDecrypt reads from input, decrypts with AES-256 and returns the reader to read the decrypted content from
|
||||
func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) {
|
||||
fipsMode := fips.FIPSMode()
|
||||
return aesDecrypt(input, passphrase, fipsMode)
|
||||
return aesDecrypt(input, passphrase, fips.FIPSMode())
|
||||
}
|
||||
|
||||
func aesDecrypt(input io.Reader, passphrase []byte, fipsMode bool) (io.Reader, error) {
|
||||
|
@ -152,15 +152,14 @@ func aesEncryptGCM(input io.Reader, output io.Writer, passphrase []byte) error {
|
|||
break // end of plaintext input
|
||||
}
|
||||
|
||||
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
|
||||
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Seal encrypts the plaintext using the nonce returning the updated slice.
|
||||
ciphertext = aesgcm.Seal(ciphertext[:0], nonce.Value(), buf[:n], nil)
|
||||
|
||||
_, err = output.Write(ciphertext)
|
||||
if err != nil {
|
||||
if _, err := output.Write(ciphertext); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -221,7 +220,7 @@ func aesDecryptGCM(input io.Reader, passphrase []byte) (io.Reader, error) {
|
|||
break // end of ciphertext
|
||||
}
|
||||
|
||||
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
|
||||
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -282,15 +281,14 @@ func aesEncryptGCMFIPS(input io.Reader, output io.Writer, passphrase []byte) err
|
|||
break // end of plaintext input
|
||||
}
|
||||
|
||||
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
|
||||
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Seal encrypts the plaintext
|
||||
ciphertext := aesgcm.Seal(nil, nil, buf[:n], nil)
|
||||
|
||||
_, err = output.Write(ciphertext)
|
||||
if err != nil {
|
||||
if _, err := output.Write(ciphertext); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -343,7 +341,7 @@ func aesDecryptGCMFIPS(input io.Reader, passphrase []byte) (io.Reader, error) {
|
|||
break // end of ciphertext
|
||||
}
|
||||
|
||||
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
|
||||
if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -365,11 +363,9 @@ func aesDecryptGCMFIPS(input io.Reader, passphrase []byte) (io.Reader, error) {
|
|||
// passphrase is used to generate an encryption key.
|
||||
// note: This function used to decrypt files that were encrypted without a header i.e. old archives
|
||||
func aesDecryptOFB(input io.Reader, passphrase []byte) (io.Reader, error) {
|
||||
var emptySalt []byte = make([]byte, 0)
|
||||
|
||||
// making a 32 bytes key that would correspond to AES-256
|
||||
// don't necessarily need a salt, so just kept in empty
|
||||
key, err := scrypt.Key(passphrase, emptySalt, 32768, 8, 1, 32)
|
||||
key, err := scrypt.Key(passphrase, nil, 32768, 8, 1, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
@ -8,8 +10,10 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/fips"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/crypto/scrypt"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -23,6 +27,7 @@ func randBytes(n int) []byte {
|
|||
for i := range b {
|
||||
b[i] = letterBytes[rand.Intn(len(letterBytes))]
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
|
@ -123,6 +128,10 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
|
|||
|
||||
testFunc(t, aesEncryptGCMFIPS, decrypt, true)
|
||||
})
|
||||
|
||||
t.Run("legacy", func(t *testing.T) {
|
||||
testFunc(t, legacyAesEncrypt, aesDecryptOFB, true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_encryptAndDecrypt_withStrongPassphrase(t *testing.T) {
|
||||
|
@ -319,3 +328,25 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T)
|
|||
testFunc(t, aesEncryptGCM, aesDecryptGCM)
|
||||
})
|
||||
}
|
||||
|
||||
func legacyAesEncrypt(input io.Reader, output io.Writer, passphrase []byte) error {
|
||||
key, err := scrypt.Key(passphrase, nil, 32768, 8, 1, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var iv [aes.BlockSize]byte
|
||||
stream := cipher.NewOFB(block, iv[:])
|
||||
|
||||
writer := &cipher.StreamWriter{S: stream, W: output}
|
||||
if _, err := io.Copy(writer, input); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -6,12 +6,11 @@ import (
|
|||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const (
|
||||
BucketName = "pending_actions"
|
||||
)
|
||||
const BucketName = "pending_actions"
|
||||
|
||||
type Service struct {
|
||||
dataservices.BaseDataService[portainer.PendingAction, portainer.PendingActionID]
|
||||
|
@ -78,15 +77,14 @@ func (s ServiceTx) Update(ID portainer.PendingActionID, config *portainer.Pendin
|
|||
|
||||
func (s ServiceTx) DeleteByEndpointID(ID portainer.EndpointID) error {
|
||||
log.Debug().Int("endpointId", int(ID)).Msg("deleting pending actions for endpoint")
|
||||
pendingActions, err := s.BaseDataServiceTx.ReadAll()
|
||||
pendingActions, err := s.ReadAll()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve pending-actions for endpoint (%d): %w", ID, err)
|
||||
}
|
||||
|
||||
for _, pendingAction := range pendingActions {
|
||||
if pendingAction.EndpointID == ID {
|
||||
err := s.BaseDataServiceTx.Delete(pendingAction.ID)
|
||||
if err != nil {
|
||||
if err := s.Delete(pendingAction.ID); err != nil {
|
||||
log.Debug().Int("endpointId", int(ID)).Msgf("failed to delete pending action: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
32
api/dataservices/pendingactions/pendingactions_test.go
Normal file
32
api/dataservices/pendingactions/pendingactions_test.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package pendingactions_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDeleteByEndpoint(t *testing.T) {
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
// Create Endpoint 1
|
||||
err := store.PendingActions().Create(&portainer.PendingAction{EndpointID: 1})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create Endpoint 2
|
||||
err = store.PendingActions().Create(&portainer.PendingAction{EndpointID: 2})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Delete Endpoint 1
|
||||
err = store.PendingActions().DeleteByEndpointID(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Check that only Endpoint 2 remains
|
||||
pendingActions, err := store.PendingActions().ReadAll()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pendingActions, 1)
|
||||
require.Equal(t, portainer.EndpointID(2), pendingActions[0].EndpointID)
|
||||
}
|
|
@ -149,7 +149,7 @@ func (migrator *PostInitMigrator) MigrateGPUs(e portainer.Endpoint, dockerClient
|
|||
return migrator.dataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
environment, err := tx.Endpoint().Endpoint(e.ID)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("Error getting environment %d", environment.ID)
|
||||
log.Error().Err(err).Msgf("Error getting environment %d", e.ID)
|
||||
return err
|
||||
}
|
||||
// Early exit if we do not need to migrate!
|
||||
|
@ -175,10 +175,11 @@ func (migrator *PostInitMigrator) MigrateGPUs(e portainer.Endpoint, dockerClient
|
|||
continue
|
||||
}
|
||||
|
||||
deviceRequests := containerDetails.HostConfig.Resources.DeviceRequests
|
||||
deviceRequests := containerDetails.HostConfig.DeviceRequests
|
||||
for _, deviceRequest := range deviceRequests {
|
||||
if deviceRequest.Driver == "nvidia" {
|
||||
environment.EnableGPUManagement = true
|
||||
|
||||
break containersLoop
|
||||
}
|
||||
}
|
||||
|
@ -186,8 +187,7 @@ func (migrator *PostInitMigrator) MigrateGPUs(e portainer.Endpoint, dockerClient
|
|||
|
||||
// set the MigrateGPUs flag to false so we don't run this again
|
||||
environment.PostInitMigrations.MigrateGPUs = false
|
||||
err = tx.Endpoint().UpdateEndpoint(environment.ID, environment)
|
||||
if err != nil {
|
||||
if err := tx.Endpoint().UpdateEndpoint(environment.ID, environment); err != nil {
|
||||
log.Error().Err(err).Msgf("Error updating EnableGPUManagement flag for environment %d", environment.ID)
|
||||
return err
|
||||
}
|
||||
|
|
75
api/datastore/postinit/migrate_post_init_test.go
Normal file
75
api/datastore/postinit/migrate_post_init_test.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package postinit
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/segmentio/encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrateGPUs(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasSuffix(r.URL.Path, "/containers/json") {
|
||||
containerSummary := []container.Summary{{ID: "container1"}}
|
||||
|
||||
err := json.NewEncoder(w).Encode(containerSummary)
|
||||
require.NoError(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
container := container.InspectResponse{
|
||||
ContainerJSONBase: &container.ContainerJSONBase{
|
||||
ID: "container1",
|
||||
HostConfig: &container.HostConfig{
|
||||
Resources: container.Resources{
|
||||
DeviceRequests: []container.DeviceRequest{
|
||||
{Driver: "nvidia"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := json.NewEncoder(w).Encode(container)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
_, store := datastore.MustNewTestStore(t, true, false)
|
||||
|
||||
migrator := &PostInitMigrator{dataStore: store}
|
||||
|
||||
dockerCli, err := client.NewClientWithOpts(client.WithHost(srv.URL), client.WithHTTPClient(http.DefaultClient))
|
||||
require.NoError(t, err)
|
||||
|
||||
// Nonexistent endpoint
|
||||
|
||||
err = migrator.MigrateGPUs(portainer.Endpoint{}, dockerCli)
|
||||
require.Error(t, err)
|
||||
|
||||
// Valid endpoint
|
||||
|
||||
endpoint := portainer.Endpoint{ID: 1, PostInitMigrations: portainer.EndpointPostInitMigrations{MigrateGPUs: true}}
|
||||
|
||||
err = store.Endpoint().Create(&endpoint)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = migrator.MigrateGPUs(endpoint, dockerCli)
|
||||
require.NoError(t, err)
|
||||
|
||||
migratedEndpoint, err := store.Endpoint().Endpoint(endpoint.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, endpoint.ID, migratedEndpoint.ID)
|
||||
require.False(t, migratedEndpoint.PostInitMigrations.MigrateGPUs)
|
||||
require.True(t, migratedEndpoint.EnableGPUManagement)
|
||||
}
|
|
@ -24,6 +24,8 @@ const (
|
|||
dockerClientVersion = "1.37"
|
||||
)
|
||||
|
||||
type NodeNamesCtxKey struct{}
|
||||
|
||||
// ClientFactory is used to create Docker clients
|
||||
type ClientFactory struct {
|
||||
signatureService portainer.DigitalSignatureService
|
||||
|
@ -162,7 +164,7 @@ func (t *NodeNameTransport) RoundTrip(req *http.Request) (*http.Response, error)
|
|||
return resp, nil
|
||||
}
|
||||
|
||||
nodeNames, ok := req.Context().Value("nodeNames").(map[string]string)
|
||||
nodeNames, ok := req.Context().Value(NodeNamesCtxKey{}).(map[string]string)
|
||||
if ok {
|
||||
for idx, r := range rs {
|
||||
// as there is no way to differentiate the same image available in multiple nodes only by their ID
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
"github.com/containers/image/v5/docker"
|
||||
imagetypes "github.com/containers/image/v5/types"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
@ -85,7 +85,7 @@ func (c *DigestClient) RemoteDigest(image Image) (digest.Digest, error) {
|
|||
return rmDigest, nil
|
||||
}
|
||||
|
||||
func ParseLocalImage(inspect types.ImageInspect) (*Image, error) {
|
||||
func ParseLocalImage(inspect image.InspectResponse) (*Image, error) {
|
||||
if IsLocalImage(inspect) || IsDanglingImage(inspect) {
|
||||
return nil, errors.New("the image is not regular")
|
||||
}
|
||||
|
|
33
api/docker/images/digest_test.go
Normal file
33
api/docker/images/digest_test.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package images
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestParseLocalImage(t *testing.T) {
|
||||
// Test with a regular image
|
||||
|
||||
img, err := ParseLocalImage(image.InspectResponse{
|
||||
ID: "sha256:9234e8fb04c47cfe0f49931e4ac7eb76fa904e33b7f8576aec0501c085f02516",
|
||||
RepoTags: []string{"myimage:latest"},
|
||||
RepoDigests: []string{"myimage@sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, img)
|
||||
require.Equal(t, "library/myimage", img.Path)
|
||||
require.Equal(t, "latest", img.Tag)
|
||||
require.Equal(t, "sha256:4bcff63911fcb4448bd4fdacec207030997caf25e9bea4045fa6c8c44de311d1", img.Digest.String())
|
||||
|
||||
// Test with a dangling image
|
||||
|
||||
img, err = ParseLocalImage(image.InspectResponse{
|
||||
ID: "sha256:abcdef1234567890",
|
||||
RepoTags: []string{"<none>:<none>"},
|
||||
RepoDigests: []string{"<none>@<none>"},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, img)
|
||||
}
|
|
@ -7,9 +7,9 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
|
||||
"github.com/containers/image/v5/docker/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -179,11 +179,11 @@ func IsLocalImage(image types.ImageInspect) bool {
|
|||
// IsDanglingImage returns whether the given image is "dangling" which means
|
||||
// that there are no repository references to the given image and it has no
|
||||
// child images
|
||||
func IsDanglingImage(image types.ImageInspect) bool {
|
||||
func IsDanglingImage(image image.InspectResponse) bool {
|
||||
return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
|
||||
}
|
||||
|
||||
// IsNoTagImage returns whether the given image is damaged, has no tags
|
||||
func IsNoTagImage(image types.ImageInspect) bool {
|
||||
func IsNoTagImage(image image.InspectResponse) bool {
|
||||
return len(image.RepoTags) == 0
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func Test_ClonePublicRepository_Shallow(t *testing.T) {
|
|||
t.Logf("Cloning into %s", dir)
|
||||
err := service.CloneRepository(dir, repositoryURL, referenceName, "", "", gittypes.GitCredentialAuthType_Basic, false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, getCommitHistoryLength(t, err, dir), "cloned repo has incorrect depth")
|
||||
assert.Equal(t, 1, getCommitHistoryLength(t, dir), "cloned repo has incorrect depth")
|
||||
}
|
||||
|
||||
func Test_ClonePublicRepository_NoGitDirectory(t *testing.T) {
|
||||
|
@ -75,7 +75,7 @@ func Test_cloneRepository(t *testing.T) {
|
|||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, getCommitHistoryLength(t, err, dir), "cloned repo has incorrect depth")
|
||||
assert.Equal(t, 4, getCommitHistoryLength(t, dir), "cloned repo has incorrect depth")
|
||||
}
|
||||
|
||||
func Test_latestCommitID(t *testing.T) {
|
||||
|
@ -123,7 +123,7 @@ func Test_ListFiles(t *testing.T) {
|
|||
assert.Equal(t, []string{"docker-compose.yml"}, fs)
|
||||
}
|
||||
|
||||
func getCommitHistoryLength(t *testing.T, err error, dir string) int {
|
||||
func getCommitHistoryLength(t *testing.T, dir string) int {
|
||||
repo, err := git.PlainOpen(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("can't open a git repo at %s with error %v", dir, err)
|
||||
|
@ -135,11 +135,10 @@ func getCommitHistoryLength(t *testing.T, err error, dir string) int {
|
|||
}
|
||||
|
||||
count := 0
|
||||
err = iter.ForEach(func(_ *object.Commit) error {
|
||||
if err := iter.ForEach(func(_ *object.Commit) error {
|
||||
count++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
t.Fatalf("can't iterate over the commit history with error %v", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -141,10 +141,7 @@ func pingOperation(client *http.Client, target string) (bool, error) {
|
|||
io.Copy(io.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
agentOnDockerEnvironment := false
|
||||
if resp.Header.Get(portainer.PortainerAgentHeader) != "" {
|
||||
agentOnDockerEnvironment = true
|
||||
}
|
||||
agentOnDockerEnvironment := resp.Header.Get(portainer.PortainerAgentHeader) != ""
|
||||
|
||||
return agentOnDockerEnvironment, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
@ -29,3 +31,14 @@ func TestExecutePingOperationFailure(t *testing.T) {
|
|||
require.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestPingOperation(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add(portainer.PortainerAgentHeader, "1")
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
agentOnDockerEnv, err := pingOperation(http.DefaultClient, srv.URL)
|
||||
require.NoError(t, err)
|
||||
require.True(t, agentOnDockerEnv)
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/http/handler/docker/utils"
|
||||
"github.com/portainer/portainer/api/set"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
|
@ -50,7 +51,7 @@ func (handler *Handler) imagesList(w http.ResponseWriter, r *http.Request) *http
|
|||
nodeNames := make(map[string]string)
|
||||
|
||||
// Pass the node names map to the context so the custom NodeNameTransport can use it
|
||||
ctx := context.WithValue(r.Context(), "nodeNames", nodeNames)
|
||||
ctx := context.WithValue(r.Context(), client.NodeNamesCtxKey{}, nodeNames)
|
||||
|
||||
images, err := cli.ImageList(ctx, image.ListOptions{})
|
||||
if err != nil {
|
||||
|
|
|
@ -6,14 +6,13 @@ import (
|
|||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
portaineree "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
dockerconsts "github.com/portainer/portainer/api/docker/consts"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
)
|
||||
|
||||
type StackViewModel struct {
|
||||
InternalStack *portaineree.Stack
|
||||
InternalStack *portainer.Stack
|
||||
|
||||
ID portainer.StackID
|
||||
Name string
|
||||
|
@ -23,7 +22,6 @@ type StackViewModel struct {
|
|||
|
||||
// GetDockerStacks retrieves all the stacks associated to a specific environment filtered by the user's access.
|
||||
func GetDockerStacks(tx dataservices.DataStoreTx, securityContext *security.RestrictedRequestContext, environmentID portainer.EndpointID, containers []types.Container, services []swarm.Service) ([]StackViewModel, error) {
|
||||
|
||||
stacks, err := tx.Stack().ReadAll()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to retrieve stacks: %w", err)
|
||||
|
|
|
@ -6,15 +6,15 @@ import (
|
|||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
portaineree "github.com/portainer/portainer/api"
|
||||
dockerconsts "github.com/portainer/portainer/api/docker/consts"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHandler_getDockerStacks(t *testing.T) {
|
||||
environment := &portaineree.Endpoint{
|
||||
environment := &portainer.Endpoint{
|
||||
ID: 1,
|
||||
SecuritySettings: portainer.EndpointSecuritySettings{
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
|
@ -46,7 +46,7 @@ func TestHandler_getDockerStacks(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
stack1 := portaineree.Stack{
|
||||
stack1 := portainer.Stack{
|
||||
ID: 1,
|
||||
Name: "stack1",
|
||||
EndpointID: 1,
|
||||
|
@ -54,8 +54,8 @@ func TestHandler_getDockerStacks(t *testing.T) {
|
|||
}
|
||||
|
||||
datastore := testhelpers.NewDatastore(
|
||||
testhelpers.WithEndpoints([]portaineree.Endpoint{*environment}),
|
||||
testhelpers.WithStacks([]portaineree.Stack{
|
||||
testhelpers.WithEndpoints([]portainer.Endpoint{*environment}),
|
||||
testhelpers.WithStacks([]portainer.Stack{
|
||||
stack1,
|
||||
{
|
||||
ID: 2,
|
||||
|
|
|
@ -6,11 +6,11 @@ import (
|
|||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
)
|
||||
|
||||
func TestEmptyGlobalKey(t *testing.T) {
|
||||
handler := NewHandler(helper.NewTestRequestBouncer())
|
||||
handler := NewHandler(testhelpers.NewTestRequestBouncer())
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "https://portainer.io:9443/endpoints/global-key", nil)
|
||||
if err != nil {
|
||||
|
|
|
@ -723,27 +723,22 @@ func getEdgeStackStatusParam(r *http.Request) (*portainer.EdgeStackStatusType, e
|
|||
}
|
||||
|
||||
func getShortestAsyncInterval(endpoint *portainer.Endpoint, settings *portainer.Settings) int {
|
||||
var edgeIntervalUseDefault int = -1
|
||||
const edgeIntervalUseDefault = -1
|
||||
|
||||
pingInterval := endpoint.Edge.PingInterval
|
||||
if pingInterval == edgeIntervalUseDefault {
|
||||
pingInterval = settings.Edge.PingInterval
|
||||
}
|
||||
shortestAsyncInterval := pingInterval
|
||||
|
||||
snapshotInterval := endpoint.Edge.SnapshotInterval
|
||||
if snapshotInterval == edgeIntervalUseDefault {
|
||||
snapshotInterval = settings.Edge.SnapshotInterval
|
||||
}
|
||||
if shortestAsyncInterval > snapshotInterval {
|
||||
shortestAsyncInterval = snapshotInterval
|
||||
}
|
||||
|
||||
commandInterval := endpoint.Edge.CommandInterval
|
||||
if commandInterval == edgeIntervalUseDefault {
|
||||
commandInterval = settings.Edge.CommandInterval
|
||||
}
|
||||
if shortestAsyncInterval > commandInterval {
|
||||
shortestAsyncInterval = commandInterval
|
||||
}
|
||||
return shortestAsyncInterval
|
||||
|
||||
return min(pingInterval, snapshotInterval, commandInterval)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ type filterTest struct {
|
|||
}
|
||||
|
||||
func Test_Filter_AgentVersion(t *testing.T) {
|
||||
|
||||
version1Endpoint := portainer.Endpoint{ID: 1, GroupID: 1,
|
||||
Type: portainer.AgentOnDockerEnvironment,
|
||||
Agent: struct {
|
||||
|
@ -78,7 +77,6 @@ func Test_Filter_AgentVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func Test_Filter_edgeFilter(t *testing.T) {
|
||||
|
||||
trustedEdgeAsync := portainer.Endpoint{ID: 1, UserTrusted: true, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: true}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
untrustedEdgeAsync := portainer.Endpoint{ID: 2, UserTrusted: false, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: true}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
regularUntrustedEdgeStandard := portainer.Endpoint{ID: 3, UserTrusted: false, Edge: portainer.EnvironmentEdgeSettings{AsyncMode: false}, GroupID: 1, Type: portainer.EdgeAgentOnDockerEnvironment}
|
||||
|
@ -280,7 +278,6 @@ func runTest(t *testing.T, test filterTest, handler *Handler, endpoints []portai
|
|||
}
|
||||
|
||||
is.ElementsMatch(test.expected, respIds)
|
||||
|
||||
}
|
||||
|
||||
func setupFilterTest(t *testing.T, endpoints []portainer.Endpoint) *Handler {
|
||||
|
@ -426,3 +423,25 @@ func TestFilterEndpointsByExcludeEdgeGroupIDs(t *testing.T) {
|
|||
require.Len(t, egs, 1)
|
||||
require.Equal(t, egs[0].ID, portainer.EdgeGroupID(2))
|
||||
}
|
||||
|
||||
func TestGetShortestAsyncInterval(t *testing.T) {
|
||||
endpoint := &portainer.Endpoint{
|
||||
ID: 1,
|
||||
Name: "Test Endpoint",
|
||||
Edge: portainer.EnvironmentEdgeSettings{
|
||||
PingInterval: -1,
|
||||
SnapshotInterval: -1,
|
||||
CommandInterval: -1,
|
||||
},
|
||||
}
|
||||
|
||||
settings := &portainer.Settings{
|
||||
Edge: portainer.Edge{
|
||||
PingInterval: 10,
|
||||
SnapshotInterval: 20,
|
||||
CommandInterval: 30,
|
||||
},
|
||||
}
|
||||
|
||||
require.Equal(t, 10, getShortestAsyncInterval(endpoint, settings))
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import (
|
|||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
@ -36,7 +35,7 @@ func Test_helmDelete(t *testing.T) {
|
|||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
@ -39,7 +38,7 @@ func Test_helmGet(t *testing.T) {
|
|||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
@ -39,7 +38,7 @@ func Test_helmGetHistory(t *testing.T) {
|
|||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
@ -40,7 +39,7 @@ func Test_helmInstall(t *testing.T) {
|
|||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"github.com/portainer/portainer/api/exec/exectest"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
@ -39,7 +38,7 @@ func Test_helmList(t *testing.T) {
|
|||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
// Install a single chart. We expect to get these values back
|
||||
options := options.InstallOptions{Name: "nginx-1", Chart: "nginx", Namespace: "default"}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ func Test_helmRepoSearch(t *testing.T) {
|
|||
is := assert.New(t)
|
||||
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
h := NewTemplateHandler(helper.NewTestRequestBouncer(), helmPackageManager)
|
||||
h := NewTemplateHandler(testhelpers.NewTestRequestBouncer(), helmPackageManager)
|
||||
|
||||
assert.NotNil(t, h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"net/url"
|
||||
"testing"
|
||||
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ func Test_helmShow(t *testing.T) {
|
|||
is := assert.New(t)
|
||||
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
h := NewTemplateHandler(helper.NewTestRequestBouncer(), helmPackageManager)
|
||||
h := NewTemplateHandler(testhelpers.NewTestRequestBouncer(), helmPackageManager)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
|
|
|
@ -29,9 +29,7 @@ func Test_RegistryAccess_RequiresAuthentication(t *testing.T) {
|
|||
req := httptest.NewRequest(http.MethodGet, "/registries/1", nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"id": "1"})
|
||||
rr := httptest.NewRecorder()
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
bouncer := handler.RegistryAccess(testHandler)
|
||||
bouncer.ServeHTTP(rr, req)
|
||||
assert.Equal(t, http.StatusUnauthorized, rr.Code)
|
||||
|
@ -53,9 +51,7 @@ func Test_RegistryAccess_InvalidRegistryID(t *testing.T) {
|
|||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
bouncer := handler.RegistryAccess(testHandler)
|
||||
bouncer.ServeHTTP(rr, req)
|
||||
|
@ -79,9 +75,7 @@ func Test_RegistryAccess_RegistryNotFound(t *testing.T) {
|
|||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
bouncer := handler.RegistryAccess(testHandler)
|
||||
bouncer.ServeHTTP(rr, req)
|
||||
|
|
|
@ -30,21 +30,21 @@ func TestHandler_webhookInvoke(t *testing.T) {
|
|||
t.Run("invalid uuid results in http.StatusBadRequest", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := newRequest("notuuid")
|
||||
h.Router.ServeHTTP(w, req)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusBadRequest, w.Code)
|
||||
})
|
||||
|
||||
t.Run("registered webhook ID in http.StatusNoContent", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := newRequest(webhookID)
|
||||
h.Router.ServeHTTP(w, req)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
})
|
||||
|
||||
t.Run("unregistered webhook ID in http.StatusNotFound", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := newRequest(newGuidString(t))
|
||||
h.Router.ServeHTTP(w, req)
|
||||
h.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -58,8 +58,7 @@ func (handler *Handler) teamMembershipUpdate(w http.ResponseWriter, r *http.Requ
|
|||
}
|
||||
|
||||
var payload teamMembershipUpdatePayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
}
|
||||
|
||||
|
@ -77,7 +76,7 @@ func (handler *Handler) teamMembershipUpdate(w http.ResponseWriter, r *http.Requ
|
|||
|
||||
isLeadingBothTeam := security.AuthorizedTeamManagement(portainer.TeamID(payload.TeamID), securityContext) &&
|
||||
security.AuthorizedTeamManagement(membership.TeamID, securityContext)
|
||||
if !(securityContext.IsAdmin || isLeadingBothTeam) {
|
||||
if !securityContext.IsAdmin && !isLeadingBothTeam {
|
||||
return httperror.Forbidden("Permission denied to update the membership", httperrors.ErrResourceAccessDenied)
|
||||
}
|
||||
|
||||
|
@ -85,8 +84,7 @@ func (handler *Handler) teamMembershipUpdate(w http.ResponseWriter, r *http.Requ
|
|||
membership.TeamID = portainer.TeamID(payload.TeamID)
|
||||
membership.Role = portainer.MembershipRole(payload.Role)
|
||||
|
||||
err = handler.DataStore.TeamMembership().Update(membership.ID, membership)
|
||||
if err != nil {
|
||||
if err := handler.DataStore.TeamMembership().Update(membership.ID, membership); err != nil {
|
||||
return httperror.InternalServerError("Unable to persist membership changes inside the database", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package teammemberships
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/datastore"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/testhelpers"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
_, store := datastore.MustNewTestStore(t, false, false)
|
||||
|
||||
err := store.TeamMembershipService.Create(&portainer.TeamMembership{
|
||||
UserID: 3,
|
||||
TeamID: 3,
|
||||
Role: 1,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
h := NewHandler(testhelpers.NewTestRequestBouncer())
|
||||
h.DataStore = store
|
||||
|
||||
payload := `{"UserID": 3, "TeamID": 2, "Role": 1}`
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
r := httptest.NewRequest(http.MethodPut, "/team_memberships/1", strings.NewReader(payload))
|
||||
|
||||
restrictedCtx := &security.RestrictedRequestContext{IsAdmin: true}
|
||||
r = r.WithContext(security.StoreRestrictedRequestContext(r, restrictedCtx))
|
||||
|
||||
h.ServeHTTP(rr, r)
|
||||
|
||||
require.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
var updatedMembership portainer.TeamMembership
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &updatedMembership)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, portainer.UserID(3), updatedMembership.UserID)
|
||||
require.Equal(t, portainer.TeamID(2), updatedMembership.TeamID)
|
||||
require.Equal(t, portainer.MembershipRole(1), updatedMembership.Role)
|
||||
}
|
|
@ -12,14 +12,10 @@ import (
|
|||
)
|
||||
|
||||
func TestInitDial(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer srv.Close()
|
||||
|
||||
tlsSrv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
tlsSrv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer tlsSrv.Close()
|
||||
|
||||
f := func(srvURL string) {
|
||||
|
|
|
@ -9,5 +9,5 @@ import (
|
|||
func TestNewLocalTransport(t *testing.T) {
|
||||
transport, err := NewLocalTransport(nil, nil, nil, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.True(t, transport.baseTransport.httpTransport.TLSClientConfig.InsecureSkipVerify) //nolint:forbidigo
|
||||
require.True(t, transport.httpTransport.TLSClientConfig.InsecureSkipVerify) //nolint:forbidigo
|
||||
}
|
||||
|
|
|
@ -277,7 +277,6 @@ func TestBaseTransport_AddTokenForExec_Integration(t *testing.T) {
|
|||
testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
capturedRequest = r
|
||||
capturedHeaders = r.Header.Clone()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("success"))
|
||||
}))
|
||||
defer testServer.Close()
|
||||
|
|
|
@ -19,9 +19,7 @@ import (
|
|||
)
|
||||
|
||||
// testHandler200 is a simple handler which returns HTTP status 200 OK
|
||||
var testHandler200 = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
var testHandler200 = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
func tokenLookupSucceed(dataStore dataservices.DataStore, jwtService portainer.JWTService) tokenLookup {
|
||||
return func(r *http.Request) (*portainer.TokenData, error) {
|
||||
|
|
|
@ -9,9 +9,7 @@ import (
|
|||
)
|
||||
|
||||
func TestLimitAccess(t *testing.T) {
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
t.Run("Request below the limit", func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
|
|
|
@ -181,15 +181,15 @@ func TestExtractListModifiersQueryParams(t *testing.T) {
|
|||
result := ExtractListModifiersQueryParams(req)
|
||||
|
||||
// Assertions
|
||||
require.Equal(t, tt.expectedResult.SearchQueryParams.search, result.SearchQueryParams.search,
|
||||
require.Equal(t, tt.expectedResult.search, result.search,
|
||||
"Search parameter should match expected value")
|
||||
require.Equal(t, tt.expectedResult.SortQueryParams.sort, result.SortQueryParams.sort,
|
||||
require.Equal(t, tt.expectedResult.sort, result.sort,
|
||||
"Sort parameter should match expected value")
|
||||
require.Equal(t, tt.expectedResult.SortQueryParams.order, result.SortQueryParams.order,
|
||||
require.Equal(t, tt.expectedResult.order, result.order,
|
||||
"Order parameter should match expected value")
|
||||
require.Equal(t, tt.expectedResult.PaginationQueryParams.start, result.PaginationQueryParams.start,
|
||||
require.Equal(t, tt.expectedResult.start, result.start,
|
||||
"Start parameter should match expected value")
|
||||
require.Equal(t, tt.expectedResult.PaginationQueryParams.limit, result.PaginationQueryParams.limit,
|
||||
require.Equal(t, tt.expectedResult.limit, result.limit,
|
||||
"Limit parameter should match expected value")
|
||||
|
||||
// Verify the complete struct
|
||||
|
|
|
@ -133,7 +133,7 @@ func (service *Service) ParseAndVerifyToken(token string) (*portainer.TokenData,
|
|||
}
|
||||
|
||||
user, err := service.dataStore.User().Read(portainer.UserID(cl.UserID))
|
||||
if err != nil || user.TokenIssueAt > cl.RegisteredClaims.IssuedAt.Unix() {
|
||||
if err != nil || user.TokenIssueAt > cl.IssuedAt.Unix() {
|
||||
return nil, "", time.Time{}, errInvalidJWTToken
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt
|
|||
|
||||
// If expiresAt is set to a zero value, the token should never expire
|
||||
if expiresAt.IsZero() {
|
||||
cl.RegisteredClaims.ExpiresAt = nil
|
||||
cl.ExpiresAt = nil
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, cl)
|
||||
|
|
|
@ -73,7 +73,7 @@ func parseEvent(event *corev1.Event) models.K8sEvent {
|
|||
EventTime: event.EventTime.UTC(),
|
||||
Kind: event.Kind,
|
||||
Count: event.Count,
|
||||
UID: string(event.ObjectMeta.GetUID()),
|
||||
UID: string(event.GetUID()),
|
||||
InvolvedObjectKind: models.K8sEventInvolvedObject{
|
||||
Kind: event.InvolvedObject.Kind,
|
||||
UID: string(event.InvolvedObject.UID),
|
||||
|
|
|
@ -119,14 +119,17 @@ func parseJobTimes(job batchv1.Job) jobTimes {
|
|||
duration: "N/A",
|
||||
}
|
||||
|
||||
if st := job.Status.StartTime; st != nil {
|
||||
times.start = st.Time.Format(time.RFC3339)
|
||||
times.duration = time.Since(st.Time).Truncate(time.Minute).String()
|
||||
st := job.Status.StartTime
|
||||
if st == nil {
|
||||
return times
|
||||
}
|
||||
|
||||
if ct := job.Status.CompletionTime; ct != nil {
|
||||
times.finish = ct.Time.Format(time.RFC3339)
|
||||
times.duration = ct.Time.Sub(st.Time).String()
|
||||
}
|
||||
times.start = st.Format(time.RFC3339)
|
||||
times.duration = time.Since(st.Time).Truncate(time.Minute).String()
|
||||
|
||||
if ct := job.Status.CompletionTime; ct != nil {
|
||||
times.finish = ct.Format(time.RFC3339)
|
||||
times.duration = ct.Time.Sub(st.Time).String()
|
||||
}
|
||||
|
||||
return times
|
||||
|
|
|
@ -3,8 +3,11 @@ package cli
|
|||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kfake "k8s.io/client-go/kubernetes/fake"
|
||||
|
@ -62,3 +65,27 @@ func (kcl *KubeClient) TestFetchJobs(t *testing.T) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseJobTimes(t *testing.T) {
|
||||
// Empty job
|
||||
jobTimes := parseJobTimes(batchv1.Job{})
|
||||
|
||||
require.Equal(t, "N/A", jobTimes.duration)
|
||||
require.Equal(t, "N/A", jobTimes.start)
|
||||
require.Equal(t, "N/A", jobTimes.finish)
|
||||
|
||||
// Full job
|
||||
now := time.Now()
|
||||
completionTime := now.Add(10 * time.Minute)
|
||||
|
||||
jobTimes = parseJobTimes(batchv1.Job{
|
||||
Status: batchv1.JobStatus{
|
||||
StartTime: &metav1.Time{Time: now},
|
||||
CompletionTime: &metav1.Time{Time: completionTime},
|
||||
},
|
||||
})
|
||||
|
||||
require.Equal(t, "10m0s", jobTimes.duration)
|
||||
require.Equal(t, now.Format(time.RFC3339), jobTimes.start)
|
||||
require.Equal(t, completionTime.Format(time.RFC3339), jobTimes.finish)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (kcl *KubeClient) GetNodesLimits() (portainer.K8sNodesLimits, error) {
|
|||
cpu := item.Status.Allocatable.Cpu().MilliValue()
|
||||
memory := item.Status.Allocatable.Memory().Value() // bytes
|
||||
|
||||
nodesLimits[item.ObjectMeta.Name] = &portainer.K8sNodeLimits{
|
||||
nodesLimits[item.Name] = &portainer.K8sNodeLimits{
|
||||
CPU: cpu,
|
||||
Memory: memory,
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ func (kcl *KubeClient) UpdateNamespacesWithResourceQuotas(namespaces map[string]
|
|||
// GetResourceQuotaFromNamespace gets the resource quota in a specific namespace where the resource quota's name is prefixed with "portainer-rq-".
|
||||
func (kcl *KubeClient) GetResourceQuotaFromNamespace(namespace portainer.K8sNamespaceInfo, resourceQuotas []corev1.ResourceQuota) *corev1.ResourceQuota {
|
||||
for _, resourceQuota := range resourceQuotas {
|
||||
if resourceQuota.ObjectMeta.Namespace == namespace.Name && resourceQuota.ObjectMeta.Name == "portainer-rq-"+namespace.Name {
|
||||
if resourceQuota.Namespace == namespace.Name && resourceQuota.Name == "portainer-rq-"+namespace.Name {
|
||||
return &resourceQuota
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,11 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientV1 "k8s.io/client-go/tools/clientcmd/api/v1"
|
||||
)
|
||||
|
@ -91,3 +96,35 @@ func Test_GenerateYAML(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetResourceQuotaFromNamespace(t *testing.T) {
|
||||
kcl := &KubeClient{}
|
||||
|
||||
namespace := portainer.K8sNamespaceInfo{Name: "my-namespace"}
|
||||
resourceQuotas := []v1.ResourceQuota{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "portainer-rq-" + namespace.Name + "-1",
|
||||
Namespace: namespace.Name,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "portainer-rq-" + namespace.Name,
|
||||
Namespace: namespace.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
rq := kcl.GetResourceQuotaFromNamespace(namespace, resourceQuotas)
|
||||
require.NotNil(t, rq)
|
||||
require.Equal(t, namespace.Name, rq.Namespace)
|
||||
|
||||
// Empty cases
|
||||
rq = kcl.GetResourceQuotaFromNamespace(namespace, nil)
|
||||
require.Nil(t, rq)
|
||||
|
||||
namespace.Name = "another-namespace"
|
||||
rq = kcl.GetResourceQuotaFromNamespace(namespace, resourceQuotas)
|
||||
require.Nil(t, rq)
|
||||
}
|
||||
|
|
|
@ -263,7 +263,7 @@ func (kcl *KubeClient) updateVolumesWithOwningApplications(volumes *[]models.K8s
|
|||
for _, pod := range pods.Items {
|
||||
if pod.Spec.Volumes != nil {
|
||||
for _, podVolume := range pod.Spec.Volumes {
|
||||
if podVolume.VolumeSource.PersistentVolumeClaim != nil && podVolume.VolumeSource.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
||||
if podVolume.PersistentVolumeClaim != nil && podVolume.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
||||
application, err := kcl.ConvertPodToApplication(pod, PortainerApplicationResources{
|
||||
ReplicaSets: replicaSetItems,
|
||||
Deployments: deploymentItems,
|
||||
|
|
|
@ -12,14 +12,10 @@ import (
|
|||
)
|
||||
|
||||
func TestCreateConnectionForURL(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer srv.Close()
|
||||
|
||||
tlsSrv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
tlsSrv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
|
||||
defer tlsSrv.Close()
|
||||
|
||||
srvURL, err := url.Parse(tlsSrv.URL)
|
||||
|
|
|
@ -104,5 +104,5 @@ func (b *K8sStackFileContentBuilder) Deploy(payload *StackPayload, endpoint *por
|
|||
}
|
||||
|
||||
func (b *K8sStackFileContentBuilder) GetResponse() string {
|
||||
return b.FileContentMethodStackBuilder.deploymentConfiger.GetResponse()
|
||||
return b.deploymentConfiger.GetResponse()
|
||||
}
|
||||
|
|
28
api/stacks/stackbuilders/k8s_file_content_builder_test.go
Normal file
28
api/stacks/stackbuilders/k8s_file_content_builder_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package stackbuilders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/api/stacks/deployments"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type mockDeploymentConfiger struct {
|
||||
deployments.StackDeploymentConfiger
|
||||
}
|
||||
|
||||
func (m mockDeploymentConfiger) GetResponse() string {
|
||||
return "mock response"
|
||||
}
|
||||
|
||||
func TestGetResponse(t *testing.T) {
|
||||
c := &K8sStackFileContentBuilder{
|
||||
FileContentMethodStackBuilder: FileContentMethodStackBuilder{
|
||||
StackBuilder: StackBuilder{
|
||||
deploymentConfiger: mockDeploymentConfiger{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
require.Equal(t, "mock response", c.GetResponse())
|
||||
}
|
|
@ -40,12 +40,6 @@ func CreateKubernetesStackGitBuilder(dataStore dataservices.DataStore,
|
|||
}
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) SetGeneralInfo(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetGeneralInfo(payload, endpoint)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
@ -60,12 +54,6 @@ func (b *KubernetesStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMeth
|
|||
return b
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetGitRepository(payload)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
@ -91,13 +79,3 @@ func (b *KubernetesStackGitBuilder) Deploy(payload *StackPayload, endpoint *port
|
|||
|
||||
return b.GitMethodStackBuilder.Deploy(payload, endpoint)
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetAutoUpdate(payload)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *KubernetesStackGitBuilder) GetResponse() string {
|
||||
return b.GitMethodStackBuilder.deploymentConfiger.GetResponse()
|
||||
}
|
||||
|
|
|
@ -38,12 +38,6 @@ func CreateKubernetesStackUrlBuilder(dataStore dataservices.DataStore,
|
|||
}
|
||||
}
|
||||
|
||||
func (b *KubernetesStackUrlBuilder) SetGeneralInfo(payload *StackPayload, endpoint *portainer.Endpoint) UrlMethodStackBuildProcess {
|
||||
b.UrlMethodStackBuilder.SetGeneralInfo(payload, endpoint)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *KubernetesStackUrlBuilder) SetUniqueInfo(payload *StackPayload) UrlMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
@ -106,9 +100,9 @@ func (b *KubernetesStackUrlBuilder) Deploy(payload *StackPayload, endpoint *port
|
|||
|
||||
b.deploymentConfiger = k8sDeploymentConfig
|
||||
|
||||
return b.UrlMethodStackBuilder.Deploy(payload, endpoint)
|
||||
return b.Deploy(payload, endpoint)
|
||||
}
|
||||
|
||||
func (b *KubernetesStackUrlBuilder) GetResponse() string {
|
||||
return b.UrlMethodStackBuilder.deploymentConfiger.GetResponse()
|
||||
return b.deploymentConfiger.GetResponse()
|
||||
}
|
||||
|
|
19
api/stacks/stackbuilders/k8s_url_builder_test.go
Normal file
19
api/stacks/stackbuilders/k8s_url_builder_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package stackbuilders
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestK8SUrlBuilderGetResponse(t *testing.T) {
|
||||
c := &KubernetesStackUrlBuilder{
|
||||
UrlMethodStackBuilder: UrlMethodStackBuilder{
|
||||
StackBuilder: StackBuilder{
|
||||
deploymentConfiger: mockDeploymentConfiger{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
require.Equal(t, "mock response", c.GetResponse())
|
||||
}
|
|
@ -61,8 +61,8 @@ func (b *GitMethodStackBuilder) SetGitRepository(payload *StackPayload) GitMetho
|
|||
var repoConfig gittypes.RepoConfig
|
||||
if payload.Authentication {
|
||||
repoConfig.Authentication = &gittypes.GitAuthentication{
|
||||
Username: payload.RepositoryConfigPayload.Username,
|
||||
Password: payload.RepositoryConfigPayload.Password,
|
||||
Username: payload.Username,
|
||||
Password: payload.Password,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,6 @@ func (b *UrlMethodStackBuilder) SetGeneralInfo(payload *StackPayload, endpoint *
|
|||
}
|
||||
|
||||
func (b *UrlMethodStackBuilder) SetUniqueInfo(payload *StackPayload) UrlMethodStackBuildProcess {
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
|
|
|
@ -28,12 +28,6 @@ func CreateSwarmStackFileUploadBuilder(securityContext *security.RestrictedReque
|
|||
}
|
||||
}
|
||||
|
||||
func (b *SwarmStackFileUploadBuilder) SetGeneralInfo(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess {
|
||||
b.FileUploadMethodStackBuilder.SetGeneralInfo(payload, endpoint)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SwarmStackFileUploadBuilder) SetUniqueInfo(payload *StackPayload) FileUploadMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
@ -48,16 +42,6 @@ func (b *SwarmStackFileUploadBuilder) SetUniqueInfo(payload *StackPayload) FileU
|
|||
return b
|
||||
}
|
||||
|
||||
func (b *SwarmStackFileUploadBuilder) SetUploadedFile(payload *StackPayload) FileUploadMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
}
|
||||
|
||||
b.FileUploadMethodStackBuilder.SetUploadedFile(payload)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SwarmStackFileUploadBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) FileUploadMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
|
|
@ -32,11 +32,6 @@ func CreateSwarmStackGitBuilder(securityContext *security.RestrictedRequestConte
|
|||
}
|
||||
}
|
||||
|
||||
func (b *SwarmStackGitBuilder) SetGeneralInfo(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetGeneralInfo(payload, endpoint)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *SwarmStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
return b
|
||||
|
@ -50,11 +45,6 @@ func (b *SwarmStackGitBuilder) SetUniqueInfo(payload *StackPayload) GitMethodSta
|
|||
return b
|
||||
}
|
||||
|
||||
func (b *SwarmStackGitBuilder) SetGitRepository(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetGitRepository(payload)
|
||||
return b
|
||||
}
|
||||
|
||||
// Deploy creates deployment configuration for swarm stack
|
||||
func (b *SwarmStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer.Endpoint) GitMethodStackBuildProcess {
|
||||
if b.hasError() {
|
||||
|
@ -72,8 +62,3 @@ func (b *SwarmStackGitBuilder) Deploy(payload *StackPayload, endpoint *portainer
|
|||
|
||||
return b.GitMethodStackBuilder.Deploy(payload, endpoint)
|
||||
}
|
||||
|
||||
func (b *SwarmStackGitBuilder) SetAutoUpdate(payload *StackPayload) GitMethodStackBuildProcess {
|
||||
b.GitMethodStackBuilder.SetAutoUpdate(payload)
|
||||
return b
|
||||
}
|
||||
|
|
|
@ -57,9 +57,10 @@ func IsValidEdgeStackName(name string) bool {
|
|||
}
|
||||
|
||||
for _, r := range name {
|
||||
if !(unicode.IsLower(r) || unicode.IsDigit(r) || r == '-' || r == '_') {
|
||||
if !unicode.IsLower(r) && !unicode.IsDigit(r) && r != '-' && r != '_' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
|
@ -16,7 +17,7 @@ func Test_InitActionConfig(t *testing.T) {
|
|||
|
||||
t.Run("with nil k8sAccess should use default kubeconfig", func(t *testing.T) {
|
||||
actionConfig := new(action.Configuration)
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", nil)
|
||||
err := hspm.initActionConfig(actionConfig, "default", nil)
|
||||
|
||||
// The function should not fail by design, even when not running in a k8s environment
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
|
@ -30,7 +31,7 @@ func Test_InitActionConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
// The function should not fail by design
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", k8sAccess)
|
||||
err := hspm.initActionConfig(actionConfig, "default", k8sAccess)
|
||||
is.NoError(err, "should not return error when using in-memory config")
|
||||
})
|
||||
|
||||
|
@ -43,7 +44,7 @@ func Test_InitActionConfig(t *testing.T) {
|
|||
}
|
||||
|
||||
// The function should not fail by design
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", k8sAccess)
|
||||
err := hspm.initActionConfig(actionConfig, "default", k8sAccess)
|
||||
is.NoError(err, "should not return error when using in-memory config with CA")
|
||||
})
|
||||
}
|
||||
|
@ -117,7 +118,7 @@ resources:
|
|||
cpu: 100m
|
||||
memory: 128Mi
|
||||
`)
|
||||
values, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
values, err := hspm.parseValues(yamlData)
|
||||
is.NoError(err, "should parse valid YAML without error")
|
||||
is.NotNil(values, "should return non-nil values")
|
||||
|
||||
|
@ -142,13 +143,13 @@ service:
|
|||
port: 80
|
||||
invalid yaml
|
||||
`)
|
||||
_, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
_, err := hspm.parseValues(yamlData)
|
||||
is.Error(err, "should return error for invalid YAML")
|
||||
})
|
||||
|
||||
t.Run("should handle empty YAML", func(t *testing.T) {
|
||||
yamlData := []byte(``)
|
||||
values, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
values, err := hspm.parseValues(yamlData)
|
||||
is.NoError(err, "should not return error for empty YAML")
|
||||
is.NotNil(values, "should return non-nil values for empty YAML")
|
||||
is.Len(values, 0, "should return empty map for empty YAML")
|
||||
|
|
|
@ -6,12 +6,13 @@ import (
|
|||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Install(t *testing.T) {
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
is := require.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
@ -29,14 +30,12 @@ func Test_Install(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal("test-nginx", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
|
@ -60,14 +59,12 @@ func Test_Install(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal("test-nginx-2", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
|
@ -86,14 +83,12 @@ func Test_Install(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal("portainer-test", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
|
@ -118,14 +113,12 @@ func Test_Install(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal("test-nginx-3", release.Name, "release name should match")
|
||||
})
|
||||
|
||||
|
@ -143,14 +136,12 @@ func Test_Install(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal("test-nginx-4", release.Name, "release name should match")
|
||||
is.Equal("default", release.Namespace, "release namespace should match")
|
||||
})
|
||||
|
|
|
@ -3,7 +3,6 @@ package sdk
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
)
|
||||
|
||||
|
@ -14,7 +13,7 @@ type HelmSDKPackageManager struct {
|
|||
}
|
||||
|
||||
// NewHelmSDKPackageManager initializes a new HelmPackageManager service using the Helm SDK
|
||||
func NewHelmSDKPackageManager() types.HelmPackageManager {
|
||||
func NewHelmSDKPackageManager() *HelmSDKPackageManager {
|
||||
settings := cli.New()
|
||||
return &HelmSDKPackageManager{
|
||||
settings: settings,
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -15,14 +16,9 @@ func Test_NewHelmSDKPackageManager(t *testing.T) {
|
|||
manager := NewHelmSDKPackageManager()
|
||||
is.NotNil(manager, "should return non-nil HelmPackageManager")
|
||||
|
||||
// Test that the returned manager is of the correct type
|
||||
_, ok := manager.(*HelmSDKPackageManager)
|
||||
is.True(ok, "should return a *HelmSDKPackageManager")
|
||||
|
||||
// Test that the manager has the expected fields
|
||||
sdkManager := manager.(*HelmSDKPackageManager)
|
||||
is.NotNil(sdkManager.settings, "should have non-nil settings")
|
||||
is.Equal(300*time.Second, sdkManager.timeout, "should have 5 minute timeout")
|
||||
is.NotNil(manager.settings, "should have non-nil settings")
|
||||
is.Equal(300*time.Second, manager.timeout, "should have 5 minute timeout")
|
||||
|
||||
// Test that the manager implements the HelmPackageManager interface
|
||||
var _ types.HelmPackageManager = manager
|
||||
|
|
|
@ -52,7 +52,7 @@ func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, e
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse chart reference: %w", err)
|
||||
}
|
||||
chartPath, err := showClient.ChartPathOptions.LocateChart(chartRef, hspm.settings)
|
||||
chartPath, err := showClient.LocateChart(chartRef, hspm.settings)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
|
|
|
@ -6,12 +6,12 @@ import (
|
|||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUpgrade(t *testing.T) {
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
is := require.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
@ -31,14 +31,12 @@ func TestUpgrade(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(upgradeOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: upgradeOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release via upgrade")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: upgradeOpts.Name,
|
||||
})
|
||||
|
||||
is.Equal(upgradeOpts.Name, release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1 for new install")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
|
@ -64,11 +62,11 @@ func TestUpgrade(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
|
||||
// Upgrade the release with the same options
|
||||
upgradedRelease, err := hspm.Upgrade(installOpts)
|
||||
|
@ -95,11 +93,11 @@ func TestUpgrade(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts) // Cleanup
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
|
||||
// Create values file
|
||||
values, err := test.CreateValuesFile("service:\n port: 8083")
|
||||
|
@ -139,11 +137,11 @@ func TestUpgrade(t *testing.T) {
|
|||
})
|
||||
|
||||
release, err := hspm.Upgrade(installOpts)
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
|
||||
// Create invalid values file
|
||||
values, err := test.CreateValuesFile("this is not valid yaml")
|
||||
|
|
|
@ -59,18 +59,19 @@ func newMockRelease(re *release.ReleaseElement) *release.Release {
|
|||
|
||||
// Install a helm chart (not thread safe)
|
||||
func (hpm helmMockPackageManager) Install(installOpts options.InstallOptions) (*release.Release, error) {
|
||||
|
||||
releaseElement := newMockReleaseElement(installOpts)
|
||||
|
||||
// Enforce only one chart with the same name per namespace
|
||||
for i, rel := range mockCharts {
|
||||
if rel.Name == installOpts.Name && rel.Namespace == installOpts.Namespace {
|
||||
mockCharts[i] = *releaseElement
|
||||
|
||||
return newMockRelease(releaseElement), nil
|
||||
}
|
||||
}
|
||||
|
||||
mockCharts = append(mockCharts, *releaseElement)
|
||||
|
||||
return newMockRelease(releaseElement), nil
|
||||
}
|
||||
|
||||
|
@ -81,7 +82,7 @@ func (hpm helmMockPackageManager) Upgrade(upgradeOpts options.InstallOptions) (*
|
|||
|
||||
// Rollback a helm chart (not thread safe)
|
||||
func (hpm helmMockPackageManager) Rollback(rollbackOpts options.RollbackOptions) (*release.Release, error) {
|
||||
return hpm.Rollback(rollbackOpts)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Show values/readme/chart etc
|
||||
|
@ -94,6 +95,7 @@ func (hpm helmMockPackageManager) Show(showOpts options.ShowOptions) ([]byte, er
|
|||
case options.ShowValues:
|
||||
return []byte(MockDataValues), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -104,6 +106,7 @@ func (hpm helmMockPackageManager) Uninstall(uninstallOpts options.UninstallOptio
|
|||
mockCharts = slices.Delete(mockCharts, i, i+1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -117,6 +120,7 @@ func (hpm helmMockPackageManager) Get(getOpts options.GetOptions) (*release.Rele
|
|||
index := slices.IndexFunc(mockCharts, func(re release.ReleaseElement) bool {
|
||||
return re.Name == getOpts.Name && re.Namespace == getOpts.Namespace
|
||||
})
|
||||
|
||||
return newMockRelease(&mockCharts[index]), nil
|
||||
}
|
||||
|
||||
|
@ -159,8 +163,7 @@ func (hbpm helmMockPackageManager) SearchRepo(searchRepoOpts options.SearchRepoO
|
|||
reader := strings.NewReader(mockPortainerIndex)
|
||||
|
||||
var file release.File
|
||||
err := yaml.NewDecoder(reader).Decode(&file)
|
||||
if err != nil {
|
||||
if err := yaml.NewDecoder(reader).Decode(&file); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode index file")
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ func Now() Time {
|
|||
}
|
||||
|
||||
func (t Time) MarshalJSON() ([]byte, error) {
|
||||
if t.Time.IsZero() {
|
||||
if t.IsZero() {
|
||||
return []byte(emptyString), nil
|
||||
}
|
||||
|
||||
|
|
|
@ -62,8 +62,6 @@ func TestValidateHelmRepositoryURL(t *testing.T) {
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
|
|
|
@ -42,11 +42,11 @@ func init() {
|
|||
}
|
||||
|
||||
func withCli(
|
||||
ctx context.Context,
|
||||
ctx context.Context, //nolint:staticcheck
|
||||
options libstack.Options,
|
||||
cliFn func(context.Context, *command.DockerCli) error,
|
||||
) error {
|
||||
ctx = context.Background()
|
||||
ctx = context.Background() //nolint:staticcheck
|
||||
|
||||
cli, err := command.NewDockerCli(command.WithCombinedStreams(log.Logger))
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
|
@ -162,7 +163,7 @@ func TestKubernetesSnapshotNodesWithAPIError(t *testing.T) {
|
|||
fakeClient := kfake.NewClientset()
|
||||
|
||||
// Add a reactor to simulate API error
|
||||
fakeClient.Fake.PrependReactor("list", "nodes", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
fakeClient.PrependReactor("list", "nodes", func(action ktesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, nil, errors.New("simulated API error")
|
||||
})
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ func init() {
|
|||
func IntegrationTest(t *testing.T) {
|
||||
_, enabled := os.LookupEnv("INTEGRATION_TEST")
|
||||
|
||||
if !(integration || enabled) {
|
||||
if !integration && !enabled {
|
||||
t.Skip("Skipping integration test")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue