chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:08:39 +08:00
commit a0df89c693
5252 changed files with 523444 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
package platform
import (
"os"
)
const (
PodmanMode = "PODMAN"
KubernetesServiceHost = "KUBERNETES_SERVICE_HOST"
)
// ContainerPlatform represent the platform on which the container is running (Docker, Kubernetes)
type ContainerPlatform string
const (
// PlatformDocker represent the Docker platform (Unknown)
PlatformDocker = ContainerPlatform("Docker")
// PlatformDockerStandalone represent the Docker platform (Standalone)
PlatformDockerStandalone = ContainerPlatform("Docker Standalone")
// PlatformDockerSwarm represent the Docker platform (Swarm)
PlatformDockerSwarm = ContainerPlatform("Docker Swarm")
// PlatformKubernetes represent the Kubernetes platform
PlatformKubernetes = ContainerPlatform("Kubernetes")
// PlatformPodman represent the Podman platform (Standalone)
PlatformPodman = ContainerPlatform("Podman")
)
// DetermineContainerPlatform will check for the existence of the PODMAN_MODE
// or KUBERNETES_SERVICE_HOST environment variable to determine if
// the container is running on Podman or inside the Kubernetes platform.
// Defaults to Docker otherwise.
func DetermineContainerPlatform() ContainerPlatform {
podmanModeEnvVar := os.Getenv(PodmanMode)
if podmanModeEnvVar == "1" {
return PlatformPodman
}
serviceHostKubernetesEnvVar := os.Getenv(KubernetesServiceHost)
if serviceHostKubernetesEnvVar != "" {
return PlatformKubernetes
}
if !isRunningInContainer() {
return ""
}
return PlatformDocker
}
// isRunningInContainer returns true if the process is running inside a container
// this code is taken from https://github.com/moby/libnetwork/blob/master/drivers/bridge/setup_bridgenetfiltering.go
func isRunningInContainer() bool {
_, err := os.Stat("/.dockerenv")
return !os.IsNotExist(err)
}
+155
View File
@@ -0,0 +1,155 @@
package platform
import (
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/testhelpers"
"github.com/stretchr/testify/require"
)
func TestDetermineContainerPlatform_Podman(t *testing.T) {
t.Setenv(PodmanMode, "1")
require.Equal(t, PlatformPodman, DetermineContainerPlatform())
}
func TestDetermineContainerPlatform_Kubernetes(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
require.Equal(t, PlatformKubernetes, DetermineContainerPlatform())
}
func TestDetermineContainerPlatform_PodmanTakesPrecedenceOverKubernetes(t *testing.T) {
t.Setenv(PodmanMode, "1")
t.Setenv(KubernetesServiceHost, "10.96.0.1")
require.Equal(t, PlatformPodman, DetermineContainerPlatform())
}
func TestCheckDockerEnvTypeForUpgrade_UnixSocket(t *testing.T) {
t.Parallel()
endpoint := &portainer.Endpoint{URL: "unix:///var/run/docker.sock"}
require.Equal(t, PlatformDockerStandalone, checkDockerEnvTypeForUpgrade(endpoint))
}
func TestCheckDockerEnvTypeForUpgrade_Npipe(t *testing.T) {
t.Parallel()
endpoint := &portainer.Endpoint{URL: "npipe:////./pipe/docker_engine", Type: portainer.DockerEnvironment}
require.Equal(t, PlatformDockerStandalone, checkDockerEnvTypeForUpgrade(endpoint))
}
func TestCheckDockerEnvTypeForUpgrade_Swarm(t *testing.T) {
t.Parallel()
endpoint := &portainer.Endpoint{URL: "tcp://tasks.portainer_agent:9001"}
require.Equal(t, PlatformDockerSwarm, checkDockerEnvTypeForUpgrade(endpoint))
}
func TestCheckDockerEnvTypeForUpgrade_RemoteTCP(t *testing.T) {
t.Parallel()
endpoint := &portainer.Endpoint{URL: "tcp://192.168.1.100:2376"}
require.Equal(t, ContainerPlatform(""), checkDockerEnvTypeForUpgrade(endpoint))
}
func TestDetectLocalEnvironment_UnsupportedPlatform(t *testing.T) {
t.Setenv(PodmanMode, "1")
t.Setenv(KubernetesServiceHost, "")
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{
{ID: 1, Type: portainer.DockerEnvironment},
}))
endpoint, platform, err := detectLocalEnvironment(ds)
require.NoError(t, err)
require.Nil(t, endpoint)
require.Empty(t, platform)
}
func TestDetectLocalEnvironment_NoEndpoints(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{}))
endpoint, platform, err := detectLocalEnvironment(ds)
require.NoError(t, err)
require.Nil(t, endpoint)
require.Empty(t, platform)
}
func TestDetectLocalEnvironment_KubernetesEndpointFound(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
kube := portainer.Endpoint{ID: 1, Name: "local-k8s", Type: portainer.KubernetesLocalEnvironment}
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{kube}))
endpoint, platform, err := detectLocalEnvironment(ds)
require.NoError(t, err)
require.NotNil(t, endpoint)
require.Equal(t, portainer.EndpointID(1), endpoint.ID)
require.Equal(t, PlatformKubernetes, platform)
}
func TestDetectLocalEnvironment_NoMatchingEndpointType(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
docker := portainer.Endpoint{ID: 1, Type: portainer.DockerEnvironment}
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{docker}))
_, _, err := detectLocalEnvironment(ds)
require.ErrorIs(t, err, ErrNoLocalEnvironment)
}
func TestService_GetPlatform(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
kube := portainer.Endpoint{ID: 1, Type: portainer.KubernetesLocalEnvironment}
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{kube}))
svc := NewService(ds)
platform, err := svc.GetPlatform()
require.NoError(t, err)
require.Equal(t, PlatformKubernetes, platform)
}
func TestService_GetLocalEnvironment(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
kube := portainer.Endpoint{ID: 1, Type: portainer.KubernetesLocalEnvironment}
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{kube}))
svc := NewService(ds)
env, err := svc.GetLocalEnvironment()
require.NoError(t, err)
require.NotNil(t, env)
require.Equal(t, portainer.EndpointID(1), env.ID)
}
func TestService_CachesLoadedEnvironment(t *testing.T) {
t.Setenv(KubernetesServiceHost, "10.96.0.1")
t.Setenv(PodmanMode, "")
kube := portainer.Endpoint{ID: 1, Type: portainer.KubernetesLocalEnvironment}
ds := testhelpers.NewDatastore(testhelpers.WithEndpoints([]portainer.Endpoint{kube}))
svc := NewService(ds)
env1, err := svc.GetLocalEnvironment()
require.NoError(t, err)
env2, err := svc.GetLocalEnvironment()
require.NoError(t, err)
require.Same(t, env1, env2)
}
+133
View File
@@ -0,0 +1,133 @@
package platform
import (
"errors"
"fmt"
"slices"
"strings"
"sync"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/rs/zerolog/log"
)
var (
ErrNoLocalEnvironment = errors.New("No local environment was detected")
)
type Service interface {
GetLocalEnvironment() (*portainer.Endpoint, error)
GetPlatform() (ContainerPlatform, error)
}
type service struct {
dataStore dataservices.DataStore
environment *portainer.Endpoint
platform ContainerPlatform
mu sync.Mutex
}
func NewService(dataStore dataservices.DataStore) *service {
return &service{dataStore: dataStore}
}
func (service *service) loadEnvAndPlatform() error {
if service.environment != nil {
return nil
}
environment, platform, err := detectLocalEnvironment(service.dataStore)
if err != nil {
return err
}
service.environment = environment
service.platform = platform
return nil
}
func (service *service) GetLocalEnvironment() (*portainer.Endpoint, error) {
service.mu.Lock()
defer service.mu.Unlock()
if err := service.loadEnvAndPlatform(); err != nil {
return nil, err
}
return service.environment, nil
}
func (service *service) GetPlatform() (ContainerPlatform, error) {
service.mu.Lock()
defer service.mu.Unlock()
if err := service.loadEnvAndPlatform(); err != nil {
return "", err
}
return service.platform, nil
}
var platformToEndpointType = map[ContainerPlatform][]portainer.EndpointType{
PlatformDocker: {portainer.AgentOnDockerEnvironment, portainer.DockerEnvironment},
PlatformKubernetes: {portainer.KubernetesLocalEnvironment},
}
func detectLocalEnvironment(dataStore dataservices.DataStore) (*portainer.Endpoint, ContainerPlatform, error) {
platform := DetermineContainerPlatform()
if !slices.Contains([]ContainerPlatform{PlatformDocker, PlatformKubernetes}, platform) {
log.Debug().
Str("platform", string(platform)).
Msg("environment not supported for upgrade")
return nil, "", nil
}
endpoints, err := dataStore.Endpoint().Endpoints()
if err != nil {
return nil, "", fmt.Errorf("failed to retrieve environments: %w", err)
}
// skip guessing when there is no endpoints registered in DB
if len(endpoints) == 0 {
return nil, "", nil
}
endpointTypes, ok := platformToEndpointType[platform]
if !ok {
return nil, "", errors.New("failed to determine environment type")
}
for _, endpoint := range endpoints {
if !slices.Contains(endpointTypes, endpoint.Type) {
continue
}
if platform != PlatformDocker {
return &endpoint, platform, nil
}
if dockerPlatform := checkDockerEnvTypeForUpgrade(&endpoint); dockerPlatform != "" {
return &endpoint, dockerPlatform, nil
}
}
return nil, "", ErrNoLocalEnvironment
}
func checkDockerEnvTypeForUpgrade(environment *portainer.Endpoint) ContainerPlatform {
if endpointutils.IsLocalEndpoint(environment) { // standalone
return PlatformDockerStandalone
}
if strings.HasPrefix(environment.URL, "tcp://tasks.") { // swarm
return PlatformDockerSwarm
}
return ""
}