Compare commits
6 Commits
test-versi
...
vault/deve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b14438fd99 | ||
|
|
ba96d8a5fb | ||
|
|
db4b1dd024 | ||
|
|
469a4e94c2 | ||
|
|
44d6c0885e | ||
|
|
9ce4ac9c9e |
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -94,6 +94,7 @@ body:
|
||||
multiple: false
|
||||
options:
|
||||
- '2.22.0'
|
||||
- '2.21.3'
|
||||
- '2.21.2'
|
||||
- '2.21.1'
|
||||
- '2.21.0'
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/Masterminds/semver"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/boltdb"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package migrator
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/Masterminds/semver"
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/database/models"
|
||||
"github.com/portainer/portainer/api/dataservices/dockerhub"
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
dockerclient "github.com/portainer/portainer/api/docker/client"
|
||||
"github.com/portainer/portainer/api/docker/images"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/Masterminds/semver"
|
||||
"github.com/docker/docker/api/types"
|
||||
dockercontainer "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
|
||||
@@ -2,6 +2,7 @@ package edgestacks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
@@ -63,7 +64,7 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
|
||||
|
||||
var payload updateStatusPayload
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
return httperror.BadRequest("Invalid request payload", fmt.Errorf("edge polling error: %w. Environment ID: %d", err, payload.EndpointID))
|
||||
}
|
||||
|
||||
var stack *portainer.EdgeStack
|
||||
@@ -95,16 +96,16 @@ func (handler *Handler) updateEdgeStackStatus(tx dataservices.DataStoreTx, r *ht
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to retrieve Edge stack from the database: %w. Environment ID: %d", err, payload.EndpointID)
|
||||
}
|
||||
|
||||
endpoint, err := tx.Endpoint().Endpoint(payload.EndpointID)
|
||||
if err != nil {
|
||||
return nil, handler.handlerDBErr(err, "Unable to find an environment with the specified identifier inside the database")
|
||||
return nil, handler.handlerDBErr(fmt.Errorf("unable to find the environment from the database: %w. Environment ID: %d", err, payload.EndpointID), "unable to find the environment")
|
||||
}
|
||||
|
||||
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
|
||||
return nil, httperror.Forbidden("Permission denied to access environment", err)
|
||||
return nil, httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
status := *payload.Status
|
||||
@@ -123,7 +124,7 @@ func (handler *Handler) updateEdgeStackStatus(tx dataservices.DataStoreTx, r *ht
|
||||
updateEnvStatus(payload.EndpointID, stack, deploymentStatus)
|
||||
|
||||
if err := tx.EdgeStack().UpdateEdgeStack(stackID, stack); err != nil {
|
||||
return nil, handler.handlerDBErr(err, "Unable to persist the stack changes inside the database")
|
||||
return nil, handler.handlerDBErr(fmt.Errorf("unable to update Edge stack to the database: %w. Environment name: %s", err, endpoint.Name), "unable to update Edge stack")
|
||||
}
|
||||
|
||||
return stack, nil
|
||||
|
||||
@@ -2,6 +2,7 @@ package endpointedge
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -39,32 +40,30 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ
|
||||
return httperror.BadRequest("Unable to find an environment on request context", err)
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
edgeJobID, err := request.RetrieveNumericRouteVariableValue(r, "jobID")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid edge job identifier route variable", err)
|
||||
return httperror.BadRequest("Invalid edge job identifier route variable", fmt.Errorf("invalid Edge job route variable: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
var payload logsPayload
|
||||
err = request.DecodeAndValidateJSONPayload(r, &payload)
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", err)
|
||||
if err := request.DecodeAndValidateJSONPayload(r, &payload); err != nil {
|
||||
return httperror.BadRequest("Invalid request payload", fmt.Errorf("invalid Edge job request payload: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
if err := handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
return handler.getEdgeJobLobs(tx, endpoint.ID, portainer.EdgeJobID(edgeJobID), payload)
|
||||
})
|
||||
if err != nil {
|
||||
}); err != nil {
|
||||
var httpErr *httperror.HandlerError
|
||||
if errors.As(err, &httpErr) {
|
||||
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name)
|
||||
return httpErr
|
||||
}
|
||||
|
||||
return httperror.InternalServerError("Unexpected error", err)
|
||||
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
return response.JSON(w, nil)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package endpointedge
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
@@ -33,27 +33,26 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
|
||||
return httperror.BadRequest("Unable to find an environment on request context", err)
|
||||
}
|
||||
|
||||
err = handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint)
|
||||
if err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", fmt.Errorf("unauthorized edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
edgeStackID, err := request.RetrieveNumericRouteVariableValue(r, "stackId")
|
||||
if err != nil {
|
||||
return httperror.BadRequest("Invalid edge stack identifier route variable", err)
|
||||
return httperror.BadRequest("Invalid edge stack identifier route variable", fmt.Errorf("invalid Edge stack route variable: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
edgeStack, err := handler.DataStore.EdgeStack().EdgeStack(portainer.EdgeStackID(edgeStackID))
|
||||
if handler.DataStore.IsErrObjectNotFound(err) {
|
||||
return httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", err)
|
||||
return httperror.NotFound("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("unable to find the Edge stack from database: %w. Environment name: %s", err, endpoint.Name))
|
||||
} else if err != nil {
|
||||
return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", err)
|
||||
return httperror.InternalServerError("Unable to find an edge stack with the specified identifier inside the database", fmt.Errorf("failed to find the Edge stack from database: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
fileName := edgeStack.EntryPoint
|
||||
if endpointutils.IsDockerEndpoint(endpoint) {
|
||||
if fileName == "" {
|
||||
return httperror.BadRequest("Docker is not supported by this stack", errors.New("Docker is not supported by this stack"))
|
||||
return httperror.BadRequest("Docker is not supported by this stack", fmt.Errorf("no filename is provided for the Docker endpoint. Environment name: %s", endpoint.Name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,18 +65,18 @@ func (handler *Handler) endpointEdgeStackInspect(w http.ResponseWriter, r *http.
|
||||
fileName = edgeStack.ManifestPath
|
||||
|
||||
if fileName == "" {
|
||||
return httperror.BadRequest("Kubernetes is not supported by this stack", errors.New("Kubernetes is not supported by this stack"))
|
||||
return httperror.BadRequest("Kubernetes is not supported by this stack", fmt.Errorf("no filename is provided for the Kubernetes endpoint. Environment name: %s", endpoint.Name))
|
||||
}
|
||||
}
|
||||
|
||||
dirEntries, err := filesystem.LoadDir(edgeStack.ProjectPath)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("Unable to load repository", err)
|
||||
return httperror.InternalServerError("Unable to load repository", fmt.Errorf("failed to load project directory: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
fileContent, err := filesystem.FilterDirForCompatibility(dirEntries, fileName, endpoint.Agent.Version)
|
||||
if err != nil {
|
||||
return httperror.InternalServerError("File not found", err)
|
||||
return httperror.InternalServerError("File not found", fmt.Errorf("unable to find file: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
dirEntries = filesystem.FilterDirForEntryFile(dirEntries, fileName)
|
||||
|
||||
@@ -85,25 +85,25 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
|
||||
|
||||
if _, ok := handler.DataStore.Endpoint().Heartbeat(portainer.EndpointID(endpointID)); !ok {
|
||||
// EE-5190
|
||||
return httperror.Forbidden("Permission denied to access environment", errors.New("the device has not been trusted yet"))
|
||||
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unable to retrieve endpoint heartbeat. Environment ID: %d", endpointID))
|
||||
}
|
||||
|
||||
endpoint, err := handler.DataStore.Endpoint().Endpoint(portainer.EndpointID(endpointID))
|
||||
if err != nil {
|
||||
// EE-5190
|
||||
return httperror.Forbidden("Permission denied to access environment", errors.New("the device has not been trusted yet"))
|
||||
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unable to retrieve endpoint from database: %w. Environment ID: %d", err, endpointID))
|
||||
}
|
||||
|
||||
firstConn := endpoint.LastCheckInDate == 0
|
||||
|
||||
if err := handler.requestBouncer.AuthorizedEdgeEndpointOperation(r, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("unauthorized Edge endpoint operation: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
handler.DataStore.Endpoint().UpdateHeartbeat(endpoint.ID)
|
||||
|
||||
if err := handler.requestBouncer.TrustedEdgeEnvironmentAccess(handler.DataStore, endpoint); err != nil {
|
||||
return httperror.Forbidden("Permission denied to access environment", err)
|
||||
return httperror.Forbidden("Permission denied to access environment. The device has not been trusted yet", fmt.Errorf("untrusted Edge environment access: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
var statusResponse *endpointEdgeStatusInspectResponse
|
||||
@@ -113,10 +113,11 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
|
||||
}); err != nil {
|
||||
var httpErr *httperror.HandlerError
|
||||
if errors.As(err, &httpErr) {
|
||||
httpErr.Err = fmt.Errorf("edge polling error: %w. Environment name: %s", httpErr.Err, endpoint.Name)
|
||||
return httpErr
|
||||
}
|
||||
|
||||
return httperror.InternalServerError("Unexpected error", err)
|
||||
return httperror.InternalServerError("Unexpected error", fmt.Errorf("edge polling error: %w. Environment name: %s", err, endpoint.Name))
|
||||
}
|
||||
|
||||
return cacheResponse(w, endpoint.ID, *statusResponse)
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
"github.com/portainer/portainer/pkg/libhttp/response"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/coreos/go-semver/semver"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/segmentio/encoding/json"
|
||||
)
|
||||
@@ -119,7 +119,7 @@ func HasNewerVersion(currentVersion, latestVersion string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return currentVersionSemver.LessThan(latestVersionSemver)
|
||||
return currentVersionSemver.LessThan(*latestVersionSemver)
|
||||
}
|
||||
|
||||
// @id Version
|
||||
|
||||
@@ -18,60 +18,6 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestHasNewerVersion(t *testing.T) {
|
||||
// Test cases
|
||||
tests := []struct {
|
||||
name string
|
||||
currentVersion string
|
||||
latestVersion string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "current version is less than latest version",
|
||||
currentVersion: "2.22.0",
|
||||
latestVersion: "v2.22.1",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "current version is equal to latest version",
|
||||
currentVersion: "2.22.0",
|
||||
latestVersion: "v2.22.0",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "current version is greater than latest version",
|
||||
currentVersion: "v2.22.2",
|
||||
latestVersion: "v2.22.1",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "invalid current version",
|
||||
currentVersion: "invalid",
|
||||
latestVersion: "v2.22.0",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "invalid latest version",
|
||||
currentVersion: "2.22.0",
|
||||
latestVersion: "invalid",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "both versions are invalid",
|
||||
currentVersion: "invalid",
|
||||
latestVersion: "invalid",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := HasNewerVersion(tt.currentVersion, tt.latestVersion)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getSystemVersion(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
|
||||
@@ -70,8 +70,8 @@ type TLSInfo struct {
|
||||
|
||||
// Existing types
|
||||
type K8sApplicationResource struct {
|
||||
CPURequest int64 `json:"CpuRequest"`
|
||||
CPULimit int64 `json:"CpuLimit"`
|
||||
MemoryRequest int64 `json:"MemoryRequest"`
|
||||
MemoryLimit int64 `json:"MemoryLimit"`
|
||||
CPURequest float64 `json:"CpuRequest"`
|
||||
CPULimit float64 `json:"CpuLimit"`
|
||||
MemoryRequest int64 `json:"MemoryRequest"`
|
||||
MemoryLimit int64 `json:"MemoryLimit"`
|
||||
}
|
||||
|
||||
@@ -135,8 +135,8 @@ func (kcl *KubeClient) GetApplicationsResource(namespace, node string) (models.K
|
||||
|
||||
for _, pod := range pods.Items {
|
||||
for _, container := range pod.Spec.Containers {
|
||||
resource.CPURequest += container.Resources.Requests.Cpu().MilliValue()
|
||||
resource.CPULimit += container.Resources.Limits.Cpu().MilliValue()
|
||||
resource.CPURequest += float64(container.Resources.Requests.Cpu().MilliValue())
|
||||
resource.CPULimit += float64(container.Resources.Limits.Cpu().MilliValue())
|
||||
resource.MemoryRequest += container.Resources.Requests.Memory().Value()
|
||||
resource.MemoryLimit += container.Resources.Limits.Memory().Value()
|
||||
}
|
||||
@@ -356,8 +356,8 @@ func updateApplicationWithService(application models.K8sApplication, services []
|
||||
func calculateResourceUsage(pod corev1.Pod) models.K8sApplicationResource {
|
||||
resource := models.K8sApplicationResource{}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
resource.CPURequest += container.Resources.Requests.Cpu().MilliValue()
|
||||
resource.CPULimit += container.Resources.Limits.Cpu().MilliValue()
|
||||
resource.CPURequest += float64(container.Resources.Requests.Cpu().MilliValue())
|
||||
resource.CPULimit += float64(container.Resources.Limits.Cpu().MilliValue())
|
||||
resource.MemoryRequest += container.Resources.Requests.Memory().Value()
|
||||
resource.MemoryLimit += container.Resources.Limits.Memory().Value()
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ func (kcl *KubeClient) CombineVolumesWithApplications(volumes *[]models.K8sVolum
|
||||
|
||||
hasReplicaSetOwnerReference := containsReplicaSetOwnerReference(pods)
|
||||
replicaSetItems := make([]appsv1.ReplicaSet, 0)
|
||||
deploymentItems := make([]appsv1.Deployment, 0)
|
||||
if hasReplicaSetOwnerReference {
|
||||
replicaSets, err := kcl.cli.AppsV1().ReplicaSets("").List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
@@ -221,19 +222,48 @@ func (kcl *KubeClient) CombineVolumesWithApplications(volumes *[]models.K8sVolum
|
||||
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list replica sets across the cluster. Error: %w", err)
|
||||
}
|
||||
replicaSetItems = replicaSets.Items
|
||||
|
||||
deployments, err := kcl.cli.AppsV1().Deployments("").List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to list deployments across the cluster")
|
||||
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list deployments across the cluster. Error: %w", err)
|
||||
}
|
||||
deploymentItems = deployments.Items
|
||||
}
|
||||
|
||||
return kcl.updateVolumesWithOwningApplications(volumes, pods, replicaSetItems)
|
||||
hasStatefulSetOwnerReference := containsStatefulSetOwnerReference(pods)
|
||||
statefulSetItems := make([]appsv1.StatefulSet, 0)
|
||||
if hasStatefulSetOwnerReference {
|
||||
statefulSets, err := kcl.cli.AppsV1().StatefulSets("").List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to list stateful sets across the cluster")
|
||||
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list stateful sets across the cluster. Error: %w", err)
|
||||
}
|
||||
statefulSetItems = statefulSets.Items
|
||||
}
|
||||
|
||||
hasDaemonSetOwnerReference := containsDaemonSetOwnerReference(pods)
|
||||
daemonSetItems := make([]appsv1.DaemonSet, 0)
|
||||
if hasDaemonSetOwnerReference {
|
||||
daemonSets, err := kcl.cli.AppsV1().DaemonSets("").List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to list daemon sets across the cluster")
|
||||
return nil, fmt.Errorf("an error occurred during the CombineVolumesWithApplications operation, unable to list daemon sets across the cluster. Error: %w", err)
|
||||
}
|
||||
daemonSetItems = daemonSets.Items
|
||||
}
|
||||
|
||||
return kcl.updateVolumesWithOwningApplications(volumes, pods, deploymentItems, replicaSetItems, statefulSetItems, daemonSetItems)
|
||||
}
|
||||
|
||||
// updateVolumesWithOwningApplications updates the volumes with the applications that use them.
|
||||
func (kcl *KubeClient) updateVolumesWithOwningApplications(volumes *[]models.K8sVolumeInfo, pods *corev1.PodList, replicaSetItems []appsv1.ReplicaSet) (*[]models.K8sVolumeInfo, error) {
|
||||
func (kcl *KubeClient) updateVolumesWithOwningApplications(volumes *[]models.K8sVolumeInfo, pods *corev1.PodList, deploymentItems []appsv1.Deployment, replicaSetItems []appsv1.ReplicaSet, statefulSetItems []appsv1.StatefulSet, daemonSetItems []appsv1.DaemonSet) (*[]models.K8sVolumeInfo, error) {
|
||||
for i, volume := range *volumes {
|
||||
for _, pod := range pods.Items {
|
||||
if pod.Spec.Volumes != nil {
|
||||
for _, podVolume := range pod.Spec.Volumes {
|
||||
if podVolume.PersistentVolumeClaim != nil && podVolume.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
||||
application, err := kcl.ConvertPodToApplication(pod, replicaSetItems, []appsv1.Deployment{}, []appsv1.StatefulSet{}, []appsv1.DaemonSet{}, []corev1.Service{}, false)
|
||||
if podVolume.VolumeSource.PersistentVolumeClaim != nil && podVolume.VolumeSource.PersistentVolumeClaim.ClaimName == volume.PersistentVolumeClaim.Name && pod.Namespace == volume.PersistentVolumeClaim.Namespace {
|
||||
application, err := kcl.ConvertPodToApplication(pod, replicaSetItems, deploymentItems, statefulSetItems, daemonSetItems, []corev1.Service{}, false)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to convert pod to application")
|
||||
return nil, fmt.Errorf("an error occurred during the CombineServicesWithApplications operation, unable to convert pod to application. Error: %w", err)
|
||||
|
||||
@@ -31,19 +31,19 @@ func NewService() *Service {
|
||||
func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings) (string, error) {
|
||||
token, err := getOAuthToken(code, configuration)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed retrieving oauth token")
|
||||
log.Error().Err(err).Msg("failed retrieving oauth token")
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
idToken, err := getIdToken(token)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed parsing id_token")
|
||||
log.Error().Err(err).Msg("failed parsing id_token")
|
||||
}
|
||||
|
||||
resource, err := getResource(token.AccessToken, configuration)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed retrieving resource")
|
||||
log.Error().Err(err).Msg("failed retrieving resource")
|
||||
|
||||
return "", err
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings
|
||||
|
||||
username, err := getUsername(resource, configuration)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed retrieving username")
|
||||
log.Error().Err(err).Msg("failed retrieving username")
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
KubernetesApplicationVolumePersistentPayload,
|
||||
KubernetesApplicationVolumeSecretPayload,
|
||||
} from 'Kubernetes/models/application/payloads';
|
||||
import KubernetesVolumeHelper from 'Kubernetes/helpers/volumeHelper';
|
||||
import { generatedApplicationConfigVolumeName } from '@/react/kubernetes/volumes/utils';
|
||||
import { HelmApplication } from 'Kubernetes/models/application/models';
|
||||
import { KubernetesApplicationDeploymentTypes, KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
||||
import { KubernetesPodAffinity, KubernetesPodNodeAffinityNodeSelectorRequirementOperators } from 'Kubernetes/pod/models';
|
||||
@@ -239,7 +239,7 @@ class KubernetesApplicationHelper {
|
||||
const volKeys = _.filter(config.overridenKeys, (item) => item.type === 'FILESYSTEM');
|
||||
const groupedVolKeys = _.groupBy(volKeys, 'path');
|
||||
_.forEach(groupedVolKeys, (items, path) => {
|
||||
const volumeName = KubernetesVolumeHelper.generatedApplicationConfigVolumeName(app.Name);
|
||||
const volumeName = generatedApplicationConfigVolumeName(app.Name);
|
||||
const configurationName = config.selectedConfiguration.metadata.name;
|
||||
const itemsMap = _.map(items, (item) => {
|
||||
const entry = new KubernetesApplicationVolumeEntryPayload();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import _ from 'lodash-es';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models/appConstants';
|
||||
|
||||
class KubernetesVolumeHelper {
|
||||
@@ -20,18 +19,6 @@ class KubernetesVolumeHelper {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
static isUsed(item) {
|
||||
return item.Applications.length !== 0;
|
||||
}
|
||||
|
||||
static generatedApplicationConfigVolumeName(name) {
|
||||
return 'config-' + name + '-' + uuidv4();
|
||||
}
|
||||
|
||||
static isExternalVolume(volume) {
|
||||
return !volume.PersistentVolumeClaim.ApplicationOwner;
|
||||
}
|
||||
}
|
||||
|
||||
export default KubernetesVolumeHelper;
|
||||
|
||||
@@ -29,6 +29,7 @@ import { confirm, confirmUpdate, confirmWebEditorDiscard } from '@@/modals/confi
|
||||
import { buildConfirmButton } from '@@/modals/utils';
|
||||
import { ModalType } from '@@/modals';
|
||||
import { KUBE_STACK_NAME_VALIDATION_REGEX } from '@/react/kubernetes/DeployView/StackName/constants';
|
||||
import { isVolumeUsed } from '@/react/kubernetes/volumes/utils';
|
||||
|
||||
class KubernetesCreateApplicationController {
|
||||
/* #region CONSTRUCTOR */
|
||||
@@ -785,7 +786,7 @@ class KubernetesCreateApplicationController {
|
||||
});
|
||||
this.volumes = volumes;
|
||||
const filteredVolumes = _.filter(this.volumes, (volume) => {
|
||||
const isUnused = !KubernetesVolumeHelper.isUsed(volume);
|
||||
const isUnused = !isVolumeUsed(volume);
|
||||
const isRWX = volume.PersistentVolumeClaim.storageClass && _.includes(volume.PersistentVolumeClaim.storageClass.AccessModes, 'RWX');
|
||||
return isUnused || isRWX;
|
||||
});
|
||||
|
||||
@@ -310,7 +310,9 @@ class KubernetesNodeController {
|
||||
}
|
||||
|
||||
getNodeUsage() {
|
||||
return this.$async(this.getNodeUsageAsync);
|
||||
if (this.hasResourceUsageAccess()) {
|
||||
return this.$async(this.getNodeUsageAsync);
|
||||
}
|
||||
}
|
||||
|
||||
hasEventWarnings() {
|
||||
@@ -361,9 +363,7 @@ class KubernetesNodeController {
|
||||
useServerMetrics: this.endpoint.Kubernetes.Configuration.UseServerMetrics,
|
||||
};
|
||||
|
||||
await this.getNodes();
|
||||
await this.getEvents();
|
||||
await this.getEndpoints();
|
||||
await Promise.allSettled([this.getNodes(), this.getEvents(), this.getEndpoints(), this.getNodeUsage()]);
|
||||
|
||||
this.availableEffects = _.values(KubernetesNodeTaintEffects);
|
||||
this.formValues = KubernetesNodeConverter.nodeToFormValues(this.node);
|
||||
|
||||
@@ -6,6 +6,7 @@ import KubernetesEventHelper from 'Kubernetes/helpers/eventHelper';
|
||||
import { KubernetesStorageClassAccessPolicies } from 'Kubernetes/models/storage-class/models';
|
||||
import KubernetesNamespaceHelper from 'Kubernetes/helpers/namespaceHelper';
|
||||
import { confirmRedeploy } from '@/react/kubernetes/volumes/ItemView/ConfirmRedeployModal';
|
||||
import { isVolumeUsed, isVolumeExternal } from '@/react/kubernetes/volumes/utils';
|
||||
|
||||
class KubernetesVolumeController {
|
||||
/* @ngInject */
|
||||
@@ -49,7 +50,7 @@ class KubernetesVolumeController {
|
||||
}
|
||||
|
||||
isExternalVolume() {
|
||||
return KubernetesVolumeHelper.isExternalVolume(this.volume);
|
||||
return isVolumeExternal(this.volume);
|
||||
}
|
||||
|
||||
isSystemNamespace() {
|
||||
@@ -57,7 +58,7 @@ class KubernetesVolumeController {
|
||||
}
|
||||
|
||||
isUsed() {
|
||||
return KubernetesVolumeHelper.isUsed(this.volume);
|
||||
return isVolumeUsed(this.volume);
|
||||
}
|
||||
|
||||
onChangeSize() {
|
||||
@@ -102,7 +103,7 @@ class KubernetesVolumeController {
|
||||
}
|
||||
|
||||
updateVolume() {
|
||||
if (KubernetesVolumeHelper.isUsed(this.volume)) {
|
||||
if (isVolumeUsed(this.volume)) {
|
||||
confirmRedeploy().then((redeploy) => {
|
||||
return this.$async(this.updateVolumeAsync, redeploy);
|
||||
});
|
||||
|
||||
@@ -139,7 +139,7 @@ function ErrorCell({
|
||||
</div>
|
||||
<div
|
||||
className={clsx('overflow-hidden whitespace-normal', {
|
||||
'h-[1.5em]': isExpanded,
|
||||
'h-[1.5em]': !isExpanded,
|
||||
})}
|
||||
>
|
||||
{value}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Database } from 'lucide-react';
|
||||
|
||||
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||
import KubernetesVolumeHelper from '@/kubernetes/helpers/volumeHelper';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { refreshableSettings } from '@@/datatables/types';
|
||||
@@ -20,6 +19,7 @@ import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery'
|
||||
import { useAllVolumesQuery } from '../queries/useVolumesQuery';
|
||||
import { isSystemNamespace } from '../../namespaces/queries/useIsSystemNamespace';
|
||||
import { useDeleteVolumes } from '../queries/useDeleteVolumes';
|
||||
import { isVolumeUsed } from '../utils';
|
||||
|
||||
import { columns } from './columns';
|
||||
|
||||
@@ -68,7 +68,7 @@ export function VolumesDatatable() {
|
||||
disableSelect={!hasWriteAuth}
|
||||
isRowSelectable={({ original: volume }) =>
|
||||
!isSystemNamespace(volume.ResourcePool.Namespace.Name, namespaces) &&
|
||||
!KubernetesVolumeHelper.isUsed(volume)
|
||||
!isVolumeUsed(volume)
|
||||
}
|
||||
renderTableActions={(selectedItems) => (
|
||||
<Authorized authorizations="K8sVolumesW">
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { CellContext } from '@tanstack/react-table';
|
||||
|
||||
import KubernetesVolumeHelper from '@/kubernetes/helpers/volumeHelper';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { Link } from '@@/Link';
|
||||
@@ -9,6 +8,7 @@ import { ExternalBadge } from '@@/Badge/ExternalBadge';
|
||||
import { UnusedBadge } from '@@/Badge/UnusedBadge';
|
||||
|
||||
import { useNamespacesQuery } from '../../namespaces/queries/useNamespacesQuery';
|
||||
import { isVolumeExternal, isVolumeUsed } from '../utils';
|
||||
|
||||
import { VolumeViewModel } from './types';
|
||||
import { helper } from './columns.helper';
|
||||
@@ -44,8 +44,8 @@ export function NameCell({
|
||||
<SystemBadge />
|
||||
) : (
|
||||
<>
|
||||
{KubernetesVolumeHelper.isExternalVolume(item) && <ExternalBadge />}
|
||||
{!KubernetesVolumeHelper.isUsed(item) && <UnusedBadge />}
|
||||
{isVolumeExternal(item) && <ExternalBadge />}
|
||||
{!isVolumeUsed(item) && <UnusedBadge />}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
15
app/react/kubernetes/volumes/utils.ts
Normal file
15
app/react/kubernetes/volumes/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import uuidv4 from 'uuid/v4';
|
||||
|
||||
import { VolumeViewModel } from './ListView/types';
|
||||
|
||||
export function isVolumeUsed(volume: VolumeViewModel) {
|
||||
return volume.Applications.length !== 0;
|
||||
}
|
||||
|
||||
export function isVolumeExternal(volume: VolumeViewModel) {
|
||||
return !volume.PersistentVolumeClaim.ApplicationOwner;
|
||||
}
|
||||
|
||||
export function generatedApplicationConfigVolumeName(applicationName: string) {
|
||||
return `config-${applicationName}-${uuidv4()}`;
|
||||
}
|
||||
3
go.mod
3
go.mod
@@ -3,7 +3,7 @@ module github.com/portainer/portainer
|
||||
go 1.22.7
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver/v3 v3.3.0
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/Microsoft/go-winio v0.6.1
|
||||
github.com/VictoriaMetrics/fastcache v1.12.0
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
@@ -12,6 +12,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.14.0
|
||||
github.com/cbroglie/mustache v1.4.0
|
||||
github.com/containers/image/v5 v5.30.1
|
||||
github.com/coreos/go-semver v0.3.0
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5
|
||||
github.com/docker/cli v26.0.1+incompatible
|
||||
github.com/docker/docker v26.1.5+incompatible
|
||||
|
||||
6
go.sum
6
go.sum
@@ -8,8 +8,8 @@ github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzS
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
|
||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
@@ -77,6 +77,8 @@ github.com/containers/ocicrypt v1.1.9 h1:2Csfba4jse85Raxk5HIyEk8OwZNjRvfkhEGijOj
|
||||
github.com/containers/ocicrypt v1.1.9/go.mod h1:dTKx1918d8TDkxXvarscpNVY+lyPakPNFN4jwA9GBys=
|
||||
github.com/containers/storage v1.53.0 h1:VSES3C/u1pxjTJIXvLrSmyP7OBtDky04oGu07UvdTEA=
|
||||
github.com/containers/storage v1.53.0/go.mod h1:pujcoOSc+upx15Jirdkebhtd8uJiLwbSd/mYT6zDJK8=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||
|
||||
Reference in New Issue
Block a user