chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 ""
|
||||
}
|
||||
Reference in New Issue
Block a user