Skip to content

Commit

Permalink
show cluster instance name and version in flux check and flux version
Browse files Browse the repository at this point in the history
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
  • Loading branch information
somtochiama committed Dec 6, 2023
1 parent 62ac960 commit 909f181
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 49 deletions.
77 changes: 42 additions & 35 deletions cmd/flux/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package main

import (
"context"
"fmt"
"os"
"time"

Expand All @@ -26,6 +27,7 @@ import (
v1 "k8s.io/api/apps/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"

"github.com/fluxcd/pkg/version"
Expand Down Expand Up @@ -80,7 +82,20 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {

fluxCheck()

if !kubernetesCheck(kubernetesConstraints) {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
return fmt.Errorf("Kubernetes client initialization failed: %s", err.Error())
}

kubeClient, err := client.New(cfg, client.Options{Scheme: utils.NewScheme()})
if err != nil {
return err
}

if !kubernetesCheck(cfg, kubernetesConstraints) {
checkFailed = true
}

Expand All @@ -92,13 +107,18 @@ func runCheckCmd(cmd *cobra.Command, args []string) error {
return nil
}

logger.Actionf("checking version in cluster")
if !fluxClusterVersionCheck(ctx, kubeClient) {
checkFailed = true
}

logger.Actionf("checking controllers")
if !componentsCheck() {
if !componentsCheck(ctx, kubeClient) {
checkFailed = true
}

logger.Actionf("checking crds")
if !crdsCheck() {
if !crdsCheck(ctx, kubeClient) {
checkFailed = true
}

Expand Down Expand Up @@ -129,17 +149,11 @@ func fluxCheck() {
return
}
if latestSv.GreaterThan(curSv) {
logger.Failuref("flux %s <%s (new version is available, please upgrade)", curSv, latestSv)
logger.Failuref("flux %s <%s (new CLI version is available, please upgrade)", curSv, latestSv)
}
}

func kubernetesCheck(constraints []string) bool {
cfg, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
return false
}

func kubernetesCheck(cfg *rest.Config, constraints []string) bool {
clientSet, err := kubernetes.NewForConfig(cfg)
if err != nil {
logger.Failuref("Kubernetes client initialization failed: %s", err.Error())
Expand Down Expand Up @@ -178,21 +192,8 @@ func kubernetesCheck(constraints []string) bool {
return true
}

func componentsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

kubeConfig, err := utils.KubeConfig(kubeconfigArgs, kubeclientOptions)
if err != nil {
return false
}

statusChecker, err := status.NewStatusChecker(kubeConfig, checkArgs.pollInterval, rootArgs.timeout, logger)
if err != nil {
return false
}

kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
func componentsCheck(ctx context.Context, kubeClient client.Client) bool {
statusChecker, err := status.NewStatusCheckerWithClient(kubeClient, checkArgs.pollInterval, rootArgs.timeout, logger)
if err != nil {
return false
}
Expand Down Expand Up @@ -222,15 +223,7 @@ func componentsCheck() bool {
return ok
}

func crdsCheck() bool {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return false
}

func crdsCheck(ctx context.Context, kubeClient client.Client) bool {
ok := true
selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
var list apiextensionsv1.CustomResourceDefinitionList
Expand All @@ -253,3 +246,17 @@ func crdsCheck() bool {
}
return ok
}

func fluxClusterVersionCheck(ctx context.Context, kubeClient client.Client) bool {
clusterInfo, err := getFluxClusterInfo(ctx, kubeClient)
if err != nil {
logger.Failuref("checking failed: %s", err.Error())
return false
}

if clusterInfo.distribution() != "" {
logger.Successf("distribution: %s", clusterInfo.distribution())
}
logger.Successf("bootstrapped: %t", clusterInfo.bootstrapped)
return true
}
20 changes: 18 additions & 2 deletions cmd/flux/cluster_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import (

kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
sourcev1 "github.com/fluxcd/source-controller/api/v1"

"github.com/fluxcd/flux2/v2/pkg/manifestgen"
)

// bootstrapLabels are labels put on a resource by kustomize-controller. These labels on the CRD indicates
Expand All @@ -42,6 +44,8 @@ type fluxClusterInfo struct {
bootstrapped bool
// managedBy is the name of the tool being used to manage the installation of Flux.
managedBy string
// partOf indicates which distribution the instance is a part of.
partOf string
// version is the Flux version number in semver format.
version string
}
Expand All @@ -68,7 +72,7 @@ func getFluxClusterInfo(ctx context.Context, c client.Client) (fluxClusterInfo,
return info, err
}

info.version = crdMetadata.Labels["app.kubernetes.io/version"]
info.version = crdMetadata.Labels[manifestgen.VersionLabelKey]

var present bool
for _, l := range bootstrapLabels {
Expand All @@ -78,11 +82,15 @@ func getFluxClusterInfo(ctx context.Context, c client.Client) (fluxClusterInfo,
info.bootstrapped = true
}

// the `app.kubernetes.io` label is not set by flux but might be set by other
// the `app.kubernetes.io/managed-by` label is not set by flux but might be set by other
// tools used to install Flux e.g Helm.
if manager, ok := crdMetadata.Labels["app.kubernetes.io/managed-by"]; ok {
info.managedBy = manager
}

if partOf, ok := crdMetadata.Labels[manifestgen.PartOfLabelKey]; ok {
info.partOf = partOf
}
return info, nil
}

Expand All @@ -105,6 +113,14 @@ func confirmFluxInstallOverride(info fluxClusterInfo) error {
return err
}

func (info fluxClusterInfo) distribution() string {
distribution := info.version
if info.partOf != "" {
distribution = fmt.Sprintf("%s-%s", info.partOf, info.version)
}
return distribution
}

func installManagedByFlux(manager string) bool {
return manager == "" || manager == "flux"
}
11 changes: 11 additions & 0 deletions cmd/flux/cluster_info_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ func Test_getFluxClusterInfo(t *testing.T) {
version: "v2.1.0",
},
},
{
name: "CRD with version and part-of labels",
labels: map[string]string{
"app.kubernetes.io/version": "v2.1.0",
"app.kubernetes.io/part-of": "flux",
},
wantInfo: fluxClusterInfo{
version: "v2.1.0",
partOf: "flux",
},
},
}

for _, tt := range tests {
Expand Down
43 changes: 38 additions & 5 deletions cmd/flux/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ import (
"github.com/google/go-containerregistry/pkg/name"
"github.com/spf13/cobra"
v1 "k8s.io/api/apps/v1"
"k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
"sigs.k8s.io/yaml/goyaml.v2"

"github.com/fluxcd/flux2/v2/internal/utils"
"github.com/fluxcd/flux2/v2/pkg/manifestgen"
Expand Down Expand Up @@ -55,6 +56,12 @@ type versionFlags struct {

var versionArgs versionFlags

type versionInfo struct {
Flux string `yaml:"flux"`
Distribution string `yaml:"distribution,omitempty"`
Controller map[string]string `yaml:"controller,inline"`
}

func init() {
versionCmd.Flags().BoolVar(&versionArgs.client, "client", false,
"print only client version")
Expand All @@ -71,15 +78,27 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

info := map[string]string{}
info["flux"] = rootArgs.defaults.Version
// VersionInfo struct and goyaml is used because we care about the order.
// Without this `distribution` is printed before `flux` when the struct is marshalled.
info := &versionInfo{
Controller: map[string]string{},
}
info.Flux = rootArgs.defaults.Version

if !versionArgs.client {
kubeClient, err := utils.KubeClient(kubeconfigArgs, kubeclientOptions)
if err != nil {
return err
}

clusterInfo, err := getFluxClusterInfo(ctx, kubeClient)
if err != nil && !errors.IsNotFound(err) {
return err
}
if clusterInfo.distribution() != "" {
info.Distribution = clusterInfo.distribution()
}

selector := client.MatchingLabels{manifestgen.PartOfLabelKey: manifestgen.PartOfLabelValue}
var list v1.DeploymentList
if err := kubeClient.List(ctx, &list, client.InNamespace(*kubeconfigArgs.Namespace), selector); err != nil {
Expand All @@ -96,7 +115,7 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
info[name] = tag
info.Controller[name] = tag
}
}
}
Expand All @@ -105,7 +124,7 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
var err error

if versionArgs.output == "json" {
marshalled, err = json.MarshalIndent(&info, "", " ")
marshalled, err = info.toJSON()
marshalled = append(marshalled, "\n"...)
} else {
marshalled, err = yaml.Marshal(&info)
Expand All @@ -119,6 +138,20 @@ func versionCmdRun(cmd *cobra.Command, args []string) error {
return nil
}

func (info versionInfo) toJSON() ([]byte, error) {
mapInfo := map[string]string{
"flux": info.Flux,
}

if info.Distribution != "" {
mapInfo["distribution"] = info.Distribution
}
for k, v := range info.Controller {
mapInfo[k] = v
}
return json.MarshalIndent(&mapInfo, "", " ")
}

func splitImageStr(image string) (string, string, error) {
ref, err := name.ParseReference(image)
if err != nil {
Expand Down
18 changes: 11 additions & 7 deletions pkg/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ type StatusChecker struct {
logger log.Logger
}

func NewStatusCheckerWithClient(c client.Client, pollInterval time.Duration, timeout time.Duration, log log.Logger) (*StatusChecker, error) {
return &StatusChecker{
pollInterval: pollInterval,
timeout: timeout,
client: c,
statusPoller: polling.NewStatusPoller(c, c.RESTMapper(), polling.Options{}),
logger: log,
}, nil
}

func NewStatusChecker(kubeConfig *rest.Config, pollInterval time.Duration, timeout time.Duration, log log.Logger) (*StatusChecker, error) {
restMapper, err := runtimeclient.NewDynamicRESTMapper(kubeConfig)
if err != nil {
Expand All @@ -55,13 +65,7 @@ func NewStatusChecker(kubeConfig *rest.Config, pollInterval time.Duration, timeo
return nil, err
}

return &StatusChecker{
pollInterval: pollInterval,
timeout: timeout,
client: c,
statusPoller: polling.NewStatusPoller(c, restMapper, polling.Options{}),
logger: log,
}, nil
return NewStatusCheckerWithClient(c, pollInterval, timeout, log)
}

func (sc *StatusChecker) Assess(identifiers ...object.ObjMetadata) error {
Expand Down

0 comments on commit 909f181

Please sign in to comment.