chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
package endpointutils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/kubernetes/cli"
|
||||
"github.com/portainer/portainer/pkg/endpoints"
|
||||
|
||||
log "github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
IsLocalEndpoint = endpoints.IsLocalEndpoint
|
||||
IsKubernetesEndpoint = endpoints.IsKubernetesEndpoint
|
||||
IsDockerEndpoint = endpoints.IsDockerEndpoint
|
||||
IsEdgeEndpoint = endpoints.IsEdgeEndpoint
|
||||
IsAgentEndpoint = endpoints.IsAgentEndpoint
|
||||
EndpointSet = endpoints.EndpointSet
|
||||
)
|
||||
|
||||
// EndpointPlatformType returns the type of the endpoint based on the environment and container engine
|
||||
func EndpointPlatformType(endpoint *portainer.Endpoint) portainer.PlatformType {
|
||||
switch endpoint.Type {
|
||||
case portainer.DockerEnvironment, portainer.AgentOnDockerEnvironment, portainer.EdgeAgentOnDockerEnvironment:
|
||||
if endpoint.ContainerEngine == portainer.ContainerEnginePodman {
|
||||
return portainer.PodmanPlatformType
|
||||
}
|
||||
return portainer.DockerPlatformType
|
||||
case portainer.KubernetesLocalEnvironment, portainer.AgentOnKubernetesEnvironment, portainer.EdgeAgentOnKubernetesEnvironment:
|
||||
return portainer.KubernetesPlatformType
|
||||
case portainer.AzureEnvironment:
|
||||
return portainer.AzurePlatformType
|
||||
}
|
||||
return portainer.UnknownPlatformType
|
||||
}
|
||||
|
||||
// FilterByExcludeIDs receives an environment(endpoint) array and returns a filtered array using an excludeIds param
|
||||
func FilterByExcludeIDs(endpoints []portainer.Endpoint, excludeIds []portainer.EndpointID) []portainer.Endpoint {
|
||||
if len(excludeIds) == 0 {
|
||||
return endpoints
|
||||
}
|
||||
|
||||
filteredEndpoints := make([]portainer.Endpoint, 0)
|
||||
|
||||
idsSet := make(map[portainer.EndpointID]bool)
|
||||
for _, id := range excludeIds {
|
||||
idsSet[id] = true
|
||||
}
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
if !idsSet[endpoint.ID] {
|
||||
filteredEndpoints = append(filteredEndpoints, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
return filteredEndpoints
|
||||
}
|
||||
|
||||
func InitialIngressClassDetection(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, factory *cli.ClientFactory) {
|
||||
if endpoint.Kubernetes.Flags.IsServerIngressClassDetected {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
endpoint.Kubernetes.Flags.IsServerIngressClassDetected = true
|
||||
|
||||
if err := tx.Endpoint().UpdateEndpoint(endpoint.ID, endpoint); err != nil {
|
||||
log.Debug().Err(err).Msg("unable to store found IngressClasses inside the database")
|
||||
}
|
||||
}()
|
||||
|
||||
cli, err := factory.GetPrivilegedKubeClient(endpoint)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to create kubernetes client for ingress class detection")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
controllers, err := cli.GetIngressControllers()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("failed to fetch ingressclasses")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var updatedClasses []portainer.KubernetesIngressClassConfig
|
||||
for i := range controllers {
|
||||
var updatedClass portainer.KubernetesIngressClassConfig
|
||||
updatedClass.Name = controllers[i].ClassName
|
||||
updatedClass.Type = controllers[i].Type
|
||||
updatedClasses = append(updatedClasses, updatedClass)
|
||||
}
|
||||
|
||||
endpoint.Kubernetes.Configuration.IngressClasses = updatedClasses
|
||||
}
|
||||
|
||||
func InitialMetricsDetection(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, factory *cli.ClientFactory) {
|
||||
if endpoint.Kubernetes.Flags.IsServerMetricsDetected {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
endpoint.Kubernetes.Flags.IsServerMetricsDetected = true
|
||||
if err := tx.Endpoint().UpdateEndpoint(endpoint.ID, endpoint); err != nil {
|
||||
log.Debug().Err(err).Msg("unable to enable UseServerMetrics inside the database")
|
||||
}
|
||||
}()
|
||||
|
||||
cli, err := factory.GetPrivilegedKubeClient(endpoint)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to create kubernetes client for initial metrics detection")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := cli.GetMetrics(); err != nil {
|
||||
log.Debug().Err(err).Msg("unable to fetch metrics: leaving metrics collection disabled.")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
endpoint.Kubernetes.Configuration.UseServerMetrics = true
|
||||
}
|
||||
|
||||
func storageDetect(tx dataservices.DataStoreTx, endpoint *portainer.Endpoint, factory *cli.ClientFactory) error {
|
||||
if endpoint.Kubernetes.Flags.IsServerStorageDetected {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
endpoint.Kubernetes.Flags.IsServerStorageDetected = true
|
||||
if err := tx.Endpoint().UpdateEndpoint(endpoint.ID, endpoint); err != nil {
|
||||
log.Info().Err(err).Msg("unable to enable storage class inside the database")
|
||||
}
|
||||
}()
|
||||
|
||||
cli, err := factory.GetPrivilegedKubeClient(endpoint)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to create Kubernetes client for initial storage detection")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
storage, err := cli.GetStorage()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to fetch storage classes: leaving storage classes disabled")
|
||||
|
||||
return err
|
||||
} else if len(storage) == 0 {
|
||||
log.Info().Err(err).Msg("zero storage classes found: they may be still building, retrying in 30 seconds")
|
||||
|
||||
return errors.New("zero storage classes found: they may be still building, retrying in 30 seconds")
|
||||
}
|
||||
|
||||
endpoint.Kubernetes.Configuration.StorageClasses = storage
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func InitialStorageDetection(tx dataservices.DataStoreTx, dataStore dataservices.DataStore, endpoint *portainer.Endpoint, factory *cli.ClientFactory) {
|
||||
log.Info().Msg("attempting to detect storage classes in the cluster")
|
||||
err := storageDetect(tx, endpoint, factory)
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
log.Err(err).Msg("error while detecting storage classes")
|
||||
|
||||
endpointID := endpoint.ID
|
||||
go func() {
|
||||
// Retry after 30 seconds if the initial detection failed.
|
||||
log.Info().Msg("retrying storage detection in 30 seconds")
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
err := dataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
|
||||
endpoint, err := tx.Endpoint().Endpoint(endpointID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return storageDetect(tx, endpoint, factory)
|
||||
})
|
||||
log.Err(err).Msg("final error while detecting storage classes")
|
||||
}()
|
||||
}
|
||||
|
||||
func UpdateEdgeEndpointHeartbeat(endpoint *portainer.Endpoint, settings *portainer.Settings) {
|
||||
if !IsEdgeEndpoint(endpoint) {
|
||||
return
|
||||
}
|
||||
|
||||
endpoint.Heartbeat = GetHeartbeatStatus(endpoint, settings)
|
||||
}
|
||||
|
||||
func GetHeartbeatStatus(endpoint *portainer.Endpoint, settings *portainer.Settings) bool {
|
||||
checkInInterval := getEndpointCheckinInterval(endpoint, settings)
|
||||
return time.Now().Unix()-endpoint.LastCheckInDate <= int64(checkInInterval*2+20)
|
||||
}
|
||||
|
||||
func getEndpointCheckinInterval(endpoint *portainer.Endpoint, settings *portainer.Settings) int {
|
||||
if !endpoint.Edge.AsyncMode {
|
||||
if endpoint.EdgeCheckinInterval > 0 {
|
||||
return endpoint.EdgeCheckinInterval
|
||||
}
|
||||
|
||||
return settings.EdgeAgentCheckinInterval
|
||||
}
|
||||
|
||||
defaultInterval := 60
|
||||
intervals := [][]int{
|
||||
{endpoint.Edge.PingInterval, settings.Edge.PingInterval},
|
||||
{endpoint.Edge.CommandInterval, settings.Edge.CommandInterval},
|
||||
{endpoint.Edge.SnapshotInterval, settings.Edge.SnapshotInterval},
|
||||
}
|
||||
|
||||
for i := range intervals {
|
||||
effectiveInterval := intervals[i][0]
|
||||
if effectiveInterval <= 0 {
|
||||
effectiveInterval = intervals[i][1]
|
||||
}
|
||||
|
||||
if effectiveInterval > 0 && effectiveInterval < defaultInterval {
|
||||
defaultInterval = effectiveInterval
|
||||
}
|
||||
}
|
||||
|
||||
return defaultInterval
|
||||
}
|
||||
|
||||
func InitializeEdgeEndpointRelation(endpoint *portainer.Endpoint, tx dataservices.DataStoreTx) error {
|
||||
if !IsEdgeEndpoint(endpoint) {
|
||||
return nil
|
||||
}
|
||||
|
||||
relation := &portainer.EndpointRelation{
|
||||
EndpointID: endpoint.ID,
|
||||
EdgeStacks: make(map[portainer.EdgeStackID]bool),
|
||||
}
|
||||
|
||||
return tx.EndpointRelation().Create(relation)
|
||||
}
|
||||
Reference in New Issue
Block a user