feat(kubernetes): cluster setup reasonable defaults EE-4518 (#8082)

pull/8218/head
Dakota Walsh 2022-12-16 16:03:40 +13:00 committed by GitHub
parent 0436be7bc4
commit 046738c967
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 3 deletions

View File

@ -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)

View File

@ -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"`
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -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 {