|
|
@ -21,14 +21,19 @@ import (
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/json"
|
|
|
|
"os"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/blang/semver/v4"
|
|
|
|
"github.com/Masterminds/semver/v3"
|
|
|
|
"github.com/fluxcd/flux2/internal/utils"
|
|
|
|
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
|
|
|
|
v1 "k8s.io/api/apps/v1"
|
|
|
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
|
|
|
apimachineryversion "k8s.io/apimachinery/pkg/version"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
"k8s.io/client-go/kubernetes"
|
|
|
|
|
|
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/fluxcd/pkg/version"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/fluxcd/flux2/internal/utils"
|
|
|
|
|
|
|
|
"github.com/fluxcd/flux2/pkg/manifestgen/install"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
var checkCmd = &cobra.Command{
|
|
|
|
var checkCmd = &cobra.Command{
|
|
|
@ -74,6 +79,8 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|
|
|
logger.Actionf("checking prerequisites")
|
|
|
|
logger.Actionf("checking prerequisites")
|
|
|
|
checkFailed := false
|
|
|
|
checkFailed := false
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fluxCheck()
|
|
|
|
|
|
|
|
|
|
|
|
if !kubectlCheck(ctx, ">=1.18.0") {
|
|
|
|
if !kubectlCheck(ctx, ">=1.18.0") {
|
|
|
|
checkFailed = true
|
|
|
|
checkFailed = true
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -101,7 +108,29 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func kubectlCheck(ctx context.Context, version string) bool {
|
|
|
|
func fluxCheck() {
|
|
|
|
|
|
|
|
curSv, err := version.ParseVersion(VERSION)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exclude development builds.
|
|
|
|
|
|
|
|
if curSv.Prerelease() != "" {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
latest, err := install.GetLatestVersion()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
latestSv, err := version.ParseVersion(latest)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if latestSv.GreaterThan(curSv) {
|
|
|
|
|
|
|
|
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func kubectlCheck(ctx context.Context, constraint string) bool {
|
|
|
|
_, err := exec.LookPath("kubectl")
|
|
|
|
_, err := exec.LookPath("kubectl")
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("kubectl not found")
|
|
|
|
logger.Failuref("kubectl not found")
|
|
|
@ -117,58 +146,58 @@ func kubectlCheck(ctx context.Context, version string) bool {
|
|
|
|
|
|
|
|
|
|
|
|
kv := &kubectlVersion{}
|
|
|
|
kv := &kubectlVersion{}
|
|
|
|
if err = json.Unmarshal([]byte(output), kv); err != nil {
|
|
|
|
if err = json.Unmarshal([]byte(output), kv); err != nil {
|
|
|
|
logger.Failuref("kubectl version output can't be unmarshaled")
|
|
|
|
logger.Failuref("kubectl version output can't be unmarshalled")
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
v, err := semver.ParseTolerant(kv.ClientVersion.GitVersion)
|
|
|
|
v, err := version.ParseVersion(kv.ClientVersion.GitVersion)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("kubectl version can't be parsed")
|
|
|
|
logger.Failuref("kubectl version can't be parsed")
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rng, _ := semver.ParseRange(version)
|
|
|
|
c, _ := semver.NewConstraint(constraint)
|
|
|
|
if !rng(v) {
|
|
|
|
if !c.Check(v) {
|
|
|
|
logger.Failuref("kubectl version must be %s", version)
|
|
|
|
logger.Failuref("kubectl version must be %s", constraint)
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Successf("kubectl %s %s", v.String(), version)
|
|
|
|
logger.Successf("kubectl %s %s", v.String(), constraint)
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func kubernetesCheck(version string) bool {
|
|
|
|
func kubernetesCheck(constraint string) bool {
|
|
|
|
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
|
|
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
|
|
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
client, err := kubernetes.NewForConfig(cfg)
|
|
|
|
clientSet, err := kubernetes.NewForConfig(cfg)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
|
|
|
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ver, err := client.Discovery().ServerVersion()
|
|
|
|
kv, err := clientSet.Discovery().ServerVersion()
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("Kubernetes API call failed: %s", err.Error())
|
|
|
|
logger.Failuref("Kubernetes API call failed: %s", err.Error())
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
v, err := semver.ParseTolerant(ver.String())
|
|
|
|
v, err := version.ParseVersion(kv.String())
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
logger.Failuref("Kubernetes version can't be determined")
|
|
|
|
logger.Failuref("Kubernetes version can't be determined")
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rng, _ := semver.ParseRange(version)
|
|
|
|
c, _ := semver.NewConstraint(constraint)
|
|
|
|
if !rng(v) {
|
|
|
|
if !c.Check(v) {
|
|
|
|
logger.Failuref("Kubernetes version must be %s", version)
|
|
|
|
logger.Failuref("Kubernetes version must be %s", constraint)
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
logger.Successf("Kubernetes %s %s", v.String(), version)
|
|
|
|
logger.Successf("Kubernetes %s %s", v.String(), constraint)
|
|
|
|
return true
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -176,23 +205,29 @@ func componentsCheck() bool {
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
|
|
|
defer cancel()
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
|
|
statusChecker, err := NewStatusChecker(time.Second, 30*time.Second)
|
|
|
|
kubeClient, err := utils.KubeClient(rootArgs.kubeconfig, rootArgs.kubecontext)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ok := true
|
|
|
|
statusChecker, err := NewStatusChecker(time.Second, rootArgs.timeout)
|
|
|
|
deployments := append(checkArgs.components, checkArgs.extraComponents...)
|
|
|
|
if err != nil {
|
|
|
|
for _, deployment := range deployments {
|
|
|
|
return false
|
|
|
|
if err := statusChecker.Assess(deployment); err != nil {
|
|
|
|
}
|
|
|
|
ok = false
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
logger.Successf("%s: healthy", deployment)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kubectlArgs := []string{"-n", rootArgs.namespace, "get", "deployment", deployment, "-o", "jsonpath=\"{..image}\""}
|
|
|
|
ok := true
|
|
|
|
if output, err := utils.ExecKubectlCommand(ctx, utils.ModeCapture, rootArgs.kubeconfig, rootArgs.kubecontext, kubectlArgs...); err == nil {
|
|
|
|
selector := client.MatchingLabels{"app.kubernetes.io/instance": rootArgs.namespace}
|
|
|
|
logger.Actionf(strings.TrimPrefix(strings.TrimSuffix(output, "\""), "\""))
|
|
|
|
var list v1.DeploymentList
|
|
|
|
|
|
|
|
if err := kubeClient.List(ctx, &list, client.InNamespace(rootArgs.namespace), selector); err == nil {
|
|
|
|
|
|
|
|
for _, d := range list.Items {
|
|
|
|
|
|
|
|
if err := statusChecker.Assess(d.Name); err != nil {
|
|
|
|
|
|
|
|
ok = false
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
logger.Successf("%s: healthy", d.Name)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, c := range d.Spec.Template.Spec.Containers {
|
|
|
|
|
|
|
|
logger.Actionf(c.Image)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok
|
|
|
|
return ok
|
|
|
|