Path: blob/main/install/installer/pkg/cluster/validation.go
2501 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package cluster56import (7"context"8"encoding/json"9"fmt"1011corev1 "k8s.io/api/core/v1"12metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"13"k8s.io/apimachinery/pkg/version"14"k8s.io/client-go/kubernetes"15_ "k8s.io/client-go/plugin/pkg/client/auth" // https://github.com/kubernetes/client-go/issues/24216"k8s.io/client-go/rest"17)1819type ValidationStatus string2021const (22ValidationStatusOk ValidationStatus = "OK"23ValidationStatusError ValidationStatus = "ERROR"24ValidationStatusWarning ValidationStatus = "WARNING"25)2627type ValidationError struct {28Message string `json:"message"`29Type ValidationStatus `json:"type"`30}3132type ValidationCheck struct {33Name string `json:"name"`34Description string `json:"description"`35Check ValidationCheckFunc `json:"-"`36}3738type ValidationCheckFunc func(ctx context.Context, config *rest.Config, namespace string) ([]ValidationError, error)3940type ValidationItem struct {41ValidationCheck42Status ValidationStatus `json:"status"`43Errors []ValidationError `json:"errors,omitempty"` // Only populated if present44}4546type ValidationResult struct {47Status ValidationStatus `json:"status"`48Items []ValidationItem `json:"items"`49}5051// ClusterChecks are checks against for a cluster52var ClusterChecks = ValidationChecks{53{54Name: "Linux kernel version",55Description: "all cluster nodes run Linux " + kernelVersionConstraint,56Check: checkKernelVersion,57},58{59Name: "containerd enabled",60Check: checkContainerDRuntime,61Description: "all cluster nodes run containerd",62},63{64Name: "Kubernetes version",65Description: "all cluster nodes run kubernetes version " + kubernetesVersionConstraint,66Check: checkKubernetesVersion,67},68{69Name: "cert-manager installed",70Check: checkCertManagerInstalled,71Description: "cert-manager is installed and has available issuer",72},73{74Name: "Namespace exists",75Description: "ensure that the target namespace exists",76Check: checkNamespaceExists,77},78}7980// ValidationChecks are a group of validations81type ValidationChecks []ValidationCheck8283func (v ValidationChecks) Len() int { return len(v) }8485// Validate runs the checks86func (checks ValidationChecks) Validate(ctx context.Context, config *rest.Config, namespace string) (*ValidationResult, error) {87results := &ValidationResult{88Status: ValidationStatusOk,89Items: []ValidationItem{},90}9192client, err := kubernetes.NewForConfig(config)93if err != nil {94return nil, err95}96ctx = context.WithValue(ctx, keyClientset, client)9798list, err := ListNodesFromContext(ctx, config)99if err != nil {100return nil, err101}102ctx = context.WithValue(ctx, keyNodeList, list)103104for _, check := range checks {105result := ValidationItem{106ValidationCheck: check,107Status: ValidationStatusOk,108Errors: []ValidationError{},109}110111res, err := check.Check(ctx, config, namespace)112if err != nil {113return nil, err114}115for _, resultErr := range res {116switch resultErr.Type {117case ValidationStatusError:118// Any error always changes status119result.Status = ValidationStatusError120results.Status = ValidationStatusError121case ValidationStatusWarning:122// Only put to warning if status is ok123if result.Status == ValidationStatusOk {124result.Status = ValidationStatusWarning125}126if results.Status == ValidationStatusOk {127results.Status = ValidationStatusWarning128}129}130131result.Errors = append(result.Errors, resultErr)132}133134results.Items = append(results.Items, result)135}136137return results, nil138}139140const (141keyNodeList = "nodeListKey"142keyClientset = "clientset"143)144145func ListNodesFromContext(ctx context.Context, config *rest.Config) ([]corev1.Node, error) {146client, err := clientsetFromContext(ctx, config)147if err != nil {148return nil, err149}150151val := ctx.Value(keyNodeList)152if res, ok := val.([]corev1.Node); ok && res != nil {153return res, nil154}155156nodes, err := client.CoreV1().Nodes().List(ctx, metav1.ListOptions{})157if err != nil {158return nil, err159}160return nodes.Items, nil161}162163func clientsetFromContext(ctx context.Context, config *rest.Config) (kubernetes.Interface, error) {164val := ctx.Value(keyClientset)165if res, ok := val.(kubernetes.Interface); ok && res != nil {166return res, nil167}168return kubernetes.NewForConfig(config)169}170171func serverVersion(ctx context.Context, config *rest.Config) (*version.Info, error) {172client, err := clientsetFromContext(ctx, config)173if err != nil {174return nil, err175}176body, err := client.CoreV1().RESTClient().Get().AbsPath("/version").Do(ctx).Raw()177if err != nil {178return nil, err179}180var info version.Info181err = json.Unmarshal(body, &info)182if err != nil {183return nil, fmt.Errorf("unable to parse the server version: %v", err)184}185return &info, nil186}187188189