mirror of
				https://github.com/fluxcd/flux2.git
				synced 2025-11-01 01:25:53 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			216 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"os"
 | |
| 	"os/exec"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/blang/semver"
 | |
| 	"github.com/spf13/cobra"
 | |
| 	"k8s.io/client-go/kubernetes"
 | |
| 	"k8s.io/client-go/tools/clientcmd"
 | |
| )
 | |
| 
 | |
| var checkCmd = &cobra.Command{
 | |
| 	Use:   "check",
 | |
| 	Short: "Check requirements and installation",
 | |
| 	Long: `
 | |
| The check command will perform a series of checks to validate that
 | |
| the local environment is configured correctly and if the installed components are healthy.`,
 | |
| 	Example: `  # Run pre-installation checks
 | |
|   check --pre
 | |
| 
 | |
|   # Run installation checks
 | |
|   check
 | |
| `,
 | |
| 	RunE: runCheckCmd,
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	checkPre bool
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	checkCmd.Flags().BoolVarP(&checkPre, "pre", "", false,
 | |
| 		"only run pre-installation checks")
 | |
| 
 | |
| 	rootCmd.AddCommand(checkCmd)
 | |
| }
 | |
| 
 | |
| func runCheckCmd(cmd *cobra.Command, args []string) error {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	logAction("checking prerequisites")
 | |
| 	checkFailed := false
 | |
| 	if !sshCheck() {
 | |
| 		checkFailed = true
 | |
| 	}
 | |
| 
 | |
| 	if !kubectlCheck(ctx, ">=1.18.0") {
 | |
| 		checkFailed = true
 | |
| 	}
 | |
| 
 | |
| 	if !kustomizeCheck(ctx, ">=3.5.0") {
 | |
| 		checkFailed = true
 | |
| 	}
 | |
| 
 | |
| 	if !kubernetesCheck(">=1.14.0") {
 | |
| 		checkFailed = true
 | |
| 	}
 | |
| 
 | |
| 	if checkPre {
 | |
| 		if checkFailed {
 | |
| 			os.Exit(1)
 | |
| 		}
 | |
| 		logSuccess("prerequisites checks passed")
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if !componentsCheck() {
 | |
| 		checkFailed = true
 | |
| 	}
 | |
| 	if checkFailed {
 | |
| 		os.Exit(1)
 | |
| 	}
 | |
| 	logSuccess("all checks passed")
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func sshCheck() bool {
 | |
| 	ok := true
 | |
| 	for _, cmd := range []string{"ssh-keygen", "ssh-keyscan"} {
 | |
| 		_, err := exec.LookPath(cmd)
 | |
| 		if err != nil {
 | |
| 			logFailure("%s not found", cmd)
 | |
| 			ok = false
 | |
| 		} else {
 | |
| 			logSuccess("%s found", cmd)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ok
 | |
| }
 | |
| 
 | |
| func kubectlCheck(ctx context.Context, version string) bool {
 | |
| 	_, err := exec.LookPath("kubectl")
 | |
| 	if err != nil {
 | |
| 		logFailure("kubectl not found")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	command := "kubectl version --client --short | awk '{ print $3 }'"
 | |
| 	output, err := utils.execCommand(ctx, ModeCapture, command)
 | |
| 	if err != nil {
 | |
| 		logFailure("kubectl version can't be determined")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	v, err := semver.ParseTolerant(output)
 | |
| 	if err != nil {
 | |
| 		logFailure("kubectl version can't be parsed")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	rng, _ := semver.ParseRange(version)
 | |
| 	if !rng(v) {
 | |
| 		logFailure("kubectl version must be %s", version)
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	logSuccess("kubectl %s %s", v.String(), version)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func kustomizeCheck(ctx context.Context, version string) bool {
 | |
| 	_, err := exec.LookPath("kustomize")
 | |
| 	if err != nil {
 | |
| 		logFailure("kustomize not found")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	command := "kustomize version --short | awk '{ print $1 }' | cut -c2-"
 | |
| 	output, err := utils.execCommand(ctx, ModeCapture, command)
 | |
| 	if err != nil {
 | |
| 		logFailure("kustomize version can't be determined")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	if strings.Contains(output, "kustomize/") {
 | |
| 		command = "kustomize version --short | awk '{ print $1 }' | cut -c12-"
 | |
| 		output, err = utils.execCommand(ctx, ModeCapture, command)
 | |
| 		if err != nil {
 | |
| 			logFailure("kustomize version can't be determined")
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	v, err := semver.ParseTolerant(output)
 | |
| 	if err != nil {
 | |
| 		logFailure("kustomize version can't be parsed")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	rng, _ := semver.ParseRange(version)
 | |
| 	if !rng(v) {
 | |
| 		logFailure("kustomize version must be %s", version)
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	logSuccess("kustomize %s %s", v.String(), version)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func kubernetesCheck(version string) bool {
 | |
| 	cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
 | |
| 	if err != nil {
 | |
| 		logFailure("kubernetes client initialization failed: %s", err.Error())
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	client, err := kubernetes.NewForConfig(cfg)
 | |
| 	if err != nil {
 | |
| 		logFailure("kubernetes client initialization failed: %s", err.Error())
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	ver, err := client.Discovery().ServerVersion()
 | |
| 	if err != nil {
 | |
| 		logFailure("kubernetes API call failed %s", err.Error())
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	v, err := semver.ParseTolerant(ver.String())
 | |
| 	if err != nil {
 | |
| 		logFailure("kubernetes version can't be determined")
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	rng, _ := semver.ParseRange(version)
 | |
| 	if !rng(v) {
 | |
| 		logFailure("kubernetes version must be %s", version)
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	logSuccess("kubernetes %s %s", v.String(), version)
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| func componentsCheck() bool {
 | |
| 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 | |
| 	defer cancel()
 | |
| 
 | |
| 	for _, deployment := range components {
 | |
| 		command := fmt.Sprintf("kubectl -n %s rollout status deployment %s --timeout=%s",
 | |
| 			namespace, deployment, timeout.String())
 | |
| 		if output, err := utils.execCommand(ctx, ModeCapture, command); err != nil {
 | |
| 			logFailure("%s: %s", deployment, strings.TrimSuffix(output, "\n"))
 | |
| 		} else {
 | |
| 			logSuccess("%s is healthy", deployment)
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | 
