mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
refactor(docker): migrate dashboard to react [EE-2191] (#11574)
Some checks are pending
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
Some checks are pending
ci / build_images (map[arch:ppc64le platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:s390x platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
ci / build_images (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
ci / build_images (map[arch:arm platform:linux version:]) (push) Waiting to run
ci / build_images (map[arch:arm64 platform:linux version:]) (push) Waiting to run
ci / build_manifests (push) Blocked by required conditions
/ triage (push) Waiting to run
Lint / Run linters (push) Waiting to run
Test / test-client (push) Waiting to run
Test / test-server (map[arch:amd64 platform:linux]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:1809]) (push) Waiting to run
Test / test-server (map[arch:amd64 platform:windows version:ltsc2022]) (push) Waiting to run
Test / test-server (map[arch:arm64 platform:linux]) (push) Waiting to run
This commit is contained in:
parent
2669a44d79
commit
014a590704
54 changed files with 1297 additions and 507 deletions
36
api/http/handler/docker/utils/filter_by_uac.go
Normal file
36
api/http/handler/docker/utils/filter_by_uac.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/internal/authorization"
|
||||
"github.com/portainer/portainer/api/internal/slices"
|
||||
)
|
||||
|
||||
// filterByResourceControl filters a list of items based on the user's role and the resource control associated to the item.
|
||||
func FilterByResourceControl[T any](tx dataservices.DataStoreTx, items []T, rcType portainer.ResourceControlType, securityContext *security.RestrictedRequestContext, idGetter func(T) string) ([]T, error) {
|
||||
if securityContext.IsAdmin {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
userTeamIDs := slices.Map(securityContext.UserMemberships, func(membership portainer.TeamMembership) portainer.TeamID {
|
||||
return membership.TeamID
|
||||
})
|
||||
|
||||
filteredItems := make([]T, 0)
|
||||
for _, item := range items {
|
||||
resourceControl, err := tx.ResourceControl().ResourceControlByResourceIDAndType(idGetter(item), portainer.ContainerResourceControl)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to retrieve resource control: %w", err)
|
||||
}
|
||||
|
||||
if resourceControl == nil || authorization.UserCanAccessResource(securityContext.UserID, userTeamIDs, resourceControl) {
|
||||
filteredItems = append(filteredItems, item)
|
||||
}
|
||||
|
||||
}
|
||||
return filteredItems, nil
|
||||
}
|
83
api/http/handler/docker/utils/get_stacks.go
Normal file
83
api/http/handler/docker/utils/get_stacks.go
Normal file
|
@ -0,0 +1,83 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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
|
||||
|
||||
ID portainer.StackID
|
||||
Name string
|
||||
IsExternal bool
|
||||
Type portainer.StackType
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
stacksNameSet := map[string]*StackViewModel{}
|
||||
|
||||
for i := range stacks {
|
||||
stack := stacks[i]
|
||||
if stack.EndpointID == environmentID {
|
||||
stacksNameSet[stack.Name] = &StackViewModel{
|
||||
InternalStack: &stack,
|
||||
ID: stack.ID,
|
||||
Name: stack.Name,
|
||||
IsExternal: false,
|
||||
Type: stack.Type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
name := container.Labels[dockerconsts.ComposeStackNameLabel]
|
||||
|
||||
if name != "" && stacksNameSet[name] == nil && !isHiddenStack(container.Labels) {
|
||||
stacksNameSet[name] = &StackViewModel{
|
||||
Name: name,
|
||||
IsExternal: true,
|
||||
Type: portainer.DockerComposeStack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, service := range services {
|
||||
name := service.Spec.Labels[dockerconsts.SwarmStackNameLabel]
|
||||
|
||||
if name != "" && stacksNameSet[name] == nil && !isHiddenStack(service.Spec.Labels) {
|
||||
stacksNameSet[name] = &StackViewModel{
|
||||
Name: name,
|
||||
IsExternal: true,
|
||||
Type: portainer.DockerSwarmStack,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stacksList := make([]StackViewModel, 0)
|
||||
for _, stack := range stacksNameSet {
|
||||
stacksList = append(stacksList, *stack)
|
||||
}
|
||||
|
||||
return FilterByResourceControl(tx, stacksList, portainer.StackResourceControl, securityContext, func(c StackViewModel) string {
|
||||
return c.Name
|
||||
})
|
||||
}
|
||||
|
||||
func isHiddenStack(labels map[string]string) bool {
|
||||
return labels[dockerconsts.HideStackLabel] != ""
|
||||
}
|
96
api/http/handler/docker/utils/get_stacks_test.go
Normal file
96
api/http/handler/docker/utils/get_stacks_test.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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{
|
||||
ID: 1,
|
||||
SecuritySettings: portainer.EndpointSecuritySettings{
|
||||
AllowStackManagementForRegularUsers: true,
|
||||
},
|
||||
}
|
||||
|
||||
containers := []types.Container{
|
||||
{
|
||||
Labels: map[string]string{
|
||||
dockerconsts.ComposeStackNameLabel: "stack1",
|
||||
},
|
||||
},
|
||||
{
|
||||
Labels: map[string]string{
|
||||
dockerconsts.ComposeStackNameLabel: "stack2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
services := []swarm.Service{
|
||||
{
|
||||
Spec: swarm.ServiceSpec{
|
||||
Annotations: swarm.Annotations{
|
||||
Labels: map[string]string{
|
||||
dockerconsts.SwarmStackNameLabel: "stack3",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
stack1 := portaineree.Stack{
|
||||
ID: 1,
|
||||
Name: "stack1",
|
||||
EndpointID: 1,
|
||||
Type: portainer.DockerComposeStack,
|
||||
}
|
||||
|
||||
datastore := testhelpers.NewDatastore(
|
||||
testhelpers.WithEndpoints([]portaineree.Endpoint{*environment}),
|
||||
testhelpers.WithStacks([]portaineree.Stack{
|
||||
stack1,
|
||||
{
|
||||
ID: 2,
|
||||
Name: "stack2",
|
||||
EndpointID: 2,
|
||||
Type: portainer.DockerSwarmStack,
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
stacksList, err := GetDockerStacks(datastore, &security.RestrictedRequestContext{
|
||||
IsAdmin: true,
|
||||
}, environment.ID, containers, services)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, stacksList, 3)
|
||||
|
||||
expectedStacks := []StackViewModel{
|
||||
{
|
||||
InternalStack: &stack1,
|
||||
ID: 1,
|
||||
Name: "stack1",
|
||||
IsExternal: false,
|
||||
Type: portainer.DockerComposeStack,
|
||||
},
|
||||
{
|
||||
Name: "stack2",
|
||||
IsExternal: true,
|
||||
Type: portainer.DockerComposeStack,
|
||||
},
|
||||
{
|
||||
Name: "stack3",
|
||||
IsExternal: true,
|
||||
Type: portainer.DockerSwarmStack,
|
||||
},
|
||||
}
|
||||
|
||||
assert.ElementsMatch(t, expectedStacks, stacksList)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue