mirror of https://github.com/portainer/portainer
feat(kubernetes): cluster setup reasonable defaults EE-4518 (#8082)
parent
0436be7bc4
commit
046738c967
|
@ -17,6 +17,7 @@ import (
|
|||
"github.com/portainer/portainer/api/crypto"
|
||||
"github.com/portainer/portainer/api/http/client"
|
||||
"github.com/portainer/portainer/api/internal/edge"
|
||||
"github.com/portainer/portainer/api/internal/endpointutils"
|
||||
)
|
||||
|
||||
type endpointCreatePayload struct {
|
||||
|
@ -244,6 +245,22 @@ func (handler *Handler) endpointCreate(w http.ResponseWriter, r *http.Request) *
|
|||
for _, stackID := range relatedEdgeStacks {
|
||||
relationObject.EdgeStacks[stackID] = true
|
||||
}
|
||||
} else if endpointutils.IsKubernetesEndpoint(endpoint) {
|
||||
endpointutils.InitialIngressClassDetection(
|
||||
endpoint,
|
||||
handler.DataStore.Endpoint(),
|
||||
handler.K8sClientFactory,
|
||||
)
|
||||
endpointutils.InitialMetricsDetection(
|
||||
endpoint,
|
||||
handler.DataStore.Endpoint(),
|
||||
handler.K8sClientFactory,
|
||||
)
|
||||
endpointutils.InitialStorageDetection(
|
||||
endpoint,
|
||||
handler.DataStore.Endpoint(),
|
||||
handler.K8sClientFactory,
|
||||
)
|
||||
}
|
||||
|
||||
err = handler.DataStore.EndpointRelation().Create(relationObject)
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
package kubernetes
|
||||
|
||||
type K8sMetrics struct {
|
||||
Resources []K8sMetricsResources `json:"resources"`
|
||||
}
|
||||
|
||||
type K8sMetricsResources struct {
|
||||
Kind string `json:"Kind,omitempty"`
|
||||
Name string `json:"Name,omitempty"`
|
||||
Namespaced bool `json:"Namespaced,omitempty"`
|
||||
SingularName string `json:"SingularName,omitempty"`
|
||||
Verbs []string `json:"Verbs,omitempty"`
|
||||
}
|
|
@ -4,6 +4,9 @@ import (
|
|||
"strings"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
"github.com/portainer/portainer/api/dataservices"
|
||||
"github.com/portainer/portainer/api/kubernetes/cli"
|
||||
log "github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// IsLocalEndpoint returns true if this is a local environment(endpoint)
|
||||
|
@ -69,3 +72,79 @@ func EndpointSet(endpointIDs []portainer.EndpointID) map[portainer.EndpointID]bo
|
|||
|
||||
return set
|
||||
}
|
||||
|
||||
func InitialIngressClassDetection(endpoint *portainer.Endpoint, endpointService dataservices.EndpointService, factory *cli.ClientFactory) {
|
||||
cli, err := factory.GetKubeClient(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
|
||||
err = endpointService.UpdateEndpoint(
|
||||
portainer.EndpointID(endpoint.ID),
|
||||
endpoint,
|
||||
)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to store found IngressClasses inside the database")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func InitialMetricsDetection(endpoint *portainer.Endpoint, endpointService dataservices.EndpointService, factory *cli.ClientFactory) {
|
||||
cli, err := factory.GetKubeClient(endpoint)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to create kubernetes client for initial metrics detection")
|
||||
return
|
||||
}
|
||||
_, err = cli.GetMetrics()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to fetch metrics: leaving metrics collection disabled.")
|
||||
return
|
||||
}
|
||||
endpoint.Kubernetes.Configuration.UseServerMetrics = true
|
||||
err = endpointService.UpdateEndpoint(
|
||||
portainer.EndpointID(endpoint.ID),
|
||||
endpoint,
|
||||
)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to enable UseServerMetrics inside the database")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func InitialStorageDetection(endpoint *portainer.Endpoint, endpointService dataservices.EndpointService, factory *cli.ClientFactory) {
|
||||
cli, err := factory.GetKubeClient(endpoint)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to create Kubernetes client for initial storage detection")
|
||||
return
|
||||
}
|
||||
|
||||
storage, err := cli.GetStorage()
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to fetch storage classes: leaving storage classes disabled")
|
||||
return
|
||||
}
|
||||
endpoint.Kubernetes.Configuration.StorageClasses = storage
|
||||
err = endpointService.UpdateEndpoint(
|
||||
portainer.EndpointID(endpoint.ID),
|
||||
endpoint,
|
||||
)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("unable to enable storage class inside the database")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
models "github.com/portainer/portainer/api/http/models/kubernetes"
|
||||
)
|
||||
|
||||
func (kcl *KubeClient) GetMetrics() (models.K8sMetrics, error) {
|
||||
var metrics models.K8sMetrics
|
||||
resp, err := kcl.cli.CoreV1().RESTClient().Get().AbsPath("apis/metrics.k8s.io/v1beta1/nodes").DoRaw(context.Background())
|
||||
if err != nil {
|
||||
return metrics, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(resp, &metrics)
|
||||
return metrics, err
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (kcl *KubeClient) GetStorage() ([]portainer.KubernetesStorageClassConfig, error) {
|
||||
var storages []portainer.KubernetesStorageClassConfig
|
||||
|
||||
storageClient := kcl.cli.StorageV1().StorageClasses()
|
||||
storageList, err := storageClient.List(context.Background(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return storages, err
|
||||
}
|
||||
|
||||
for _, s := range storageList.Items {
|
||||
var storage portainer.KubernetesStorageClassConfig
|
||||
|
||||
v, ok := s.Annotations["storageclass.kubernetes.io/is-default-class"]
|
||||
if !ok || v != "true" {
|
||||
continue
|
||||
}
|
||||
storage.Name = s.Name
|
||||
storage.Provisioner = s.Provisioner
|
||||
storage.AccessModes = []string{"RWO"}
|
||||
if s.AllowVolumeExpansion != nil {
|
||||
storage.AllowVolumeExpansion = *s.AllowVolumeExpansion
|
||||
}
|
||||
|
||||
storages = append(storages, storage)
|
||||
}
|
||||
|
||||
return storages, nil
|
||||
}
|
|
@ -1375,6 +1375,8 @@ type (
|
|||
DeleteNamespace(namespace string) error
|
||||
GetConfigMapsAndSecrets(namespace string) ([]models.K8sConfigMapOrSecret, error)
|
||||
GetIngressControllers() (models.K8sIngressControllers, error)
|
||||
GetMetrics() (models.K8sMetrics, error)
|
||||
GetStorage() ([]KubernetesStorageClassConfig, error)
|
||||
CreateIngress(namespace string, info models.K8sIngressInfo) error
|
||||
UpdateIngress(namespace string, info models.K8sIngressInfo) error
|
||||
GetIngresses(namespace string) ([]models.K8sIngressInfo, error)
|
||||
|
|
|
@ -225,10 +225,8 @@ class KubernetesConfigureController {
|
|||
}
|
||||
});
|
||||
await Promise.all(storagePromises);
|
||||
|
||||
this.$state.reload();
|
||||
this.Notifications.success('Success', 'Configuration successfully applied');
|
||||
|
||||
this.$state.go('portainer.home');
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to apply configuration');
|
||||
} finally {
|
||||
|
|
Loading…
Reference in New Issue