cli: refactor kubernetes package (#2232)

* Clean up CLI kubernetes package

* Rename CLI kubernetes pkg to kubecmd

* Unify kubernetes clients

* Refactor attestation config upgrade

* Update CODEOWNERS file

* Remove outdated GetMeasurementSalt

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-08-21 16:15:32 +02:00 committed by GitHub
parent 3bf316e28f
commit afa7fd0edb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1024 additions and 1160 deletions

View file

@ -13,20 +13,16 @@ import (
"strings"
"github.com/edgelesssys/constellation/v2/cli/internal/helm"
"github.com/edgelesssys/constellation/v2/cli/internal/kubernetes"
"github.com/edgelesssys/constellation/v2/cli/internal/kubecmd"
"github.com/edgelesssys/constellation/v2/internal/api/attestationconfigapi"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/edgelesssys/constellation/v2/internal/kubernetes/kubectl"
"github.com/edgelesssys/constellation/v2/operators/constellation-node-operator/v2/api/v1alpha1"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"gopkg.in/yaml.v3"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/tools/clientcmd"
)
// NewStatusCmd returns a new cobra.Command for the statuus command.
@ -50,38 +46,17 @@ func runStatus(cmd *cobra.Command, _ []string) error {
}
defer log.Sync()
kubeClient := kubectl.New()
flags, err := parseStatusFlags(cmd)
if err != nil {
return fmt.Errorf("parsing flags: %w", err)
}
fileHandler := file.NewHandler(afero.NewOsFs())
kubeConfig, err := fileHandler.Read(constants.AdminConfFilename)
if err != nil {
return fmt.Errorf("reading admin.conf: %w", err)
}
// need kubectl client to fetch nodes.
if err := kubeClient.Initialize(kubeConfig); err != nil {
return err
}
restConfig, err := clientcmd.RESTConfigFromKubeConfig(kubeConfig)
if err != nil {
return fmt.Errorf("creating k8s client config from kubeconfig: %w", err)
}
// need unstructed client to fetch NodeVersion CRD.
unstructuredClient, err := dynamic.NewForConfig(restConfig)
if err != nil {
return fmt.Errorf("setting up custom resource client: %w", err)
}
// need helm client to fetch service versions.
// The client used here, doesn't need to know the current workspace.
// It may be refactored in the future for easier usage.
helmClient, err := helm.NewUpgradeClient(kubectl.New(), constants.UpgradeDir, constants.AdminConfFilename, constants.HelmNamespace, log)
helmClient, err := helm.NewUpgradeClient(kubectl.NewUninitialized(), constants.UpgradeDir, constants.AdminConfFilename, constants.HelmNamespace, log)
if err != nil {
return fmt.Errorf("setting up helm client: %w", err)
}
@ -97,11 +72,12 @@ func runStatus(cmd *cobra.Command, _ []string) error {
}
variant := conf.GetAttestationConfig().GetVariant()
stableClient, err := kubernetes.NewStableClient(constants.AdminConfFilename)
kubeClient, err := kubecmd.New(cmd.OutOrStdout(), constants.AdminConfFilename, log)
if err != nil {
return fmt.Errorf("setting up stable client: %w", err)
return fmt.Errorf("setting up kubernetes client: %w", err)
}
output, err := status(cmd.Context(), kubeClient, stableClient, helmVersionGetter, kubernetes.NewNodeVersionClient(unstructuredClient), variant)
output, err := status(cmd.Context(), helmVersionGetter, kubeClient, variant)
if err != nil {
return fmt.Errorf("getting status: %w", err)
}
@ -111,19 +87,14 @@ func runStatus(cmd *cobra.Command, _ []string) error {
}
// status queries the cluster for the relevant status information and returns the output string.
func status(
ctx context.Context, kubeClient kubeClient, cmClient configMapClient, getHelmVersions func() (fmt.Stringer, error),
dynamicInterface kubernetes.DynamicInterface, attestVariant variant.Variant,
func status(ctx context.Context, getHelmVersions func() (fmt.Stringer, error), kubeClient kubeCmd, attestVariant variant.Variant,
) (string, error) {
nodeVersion, err := kubernetes.GetConstellationVersion(ctx, dynamicInterface)
nodeVersion, err := kubeClient.GetConstellationVersion(ctx)
if err != nil {
return "", fmt.Errorf("getting constellation version: %w", err)
}
if len(nodeVersion.Status.Conditions) != 1 {
return "", fmt.Errorf("expected exactly one condition, got %d", len(nodeVersion.Status.Conditions))
}
attestationConfig, err := getAttestationConfig(ctx, cmClient, attestVariant)
attestationConfig, err := kubeClient.GetClusterAttestationConfig(ctx, attestVariant)
if err != nil {
return "", fmt.Errorf("getting attestation config: %w", err)
}
@ -132,51 +103,30 @@ func status(
return "", fmt.Errorf("marshalling attestation config: %w", err)
}
targetVersions, err := kubernetes.NewTargetVersions(nodeVersion)
if err != nil {
return "", fmt.Errorf("getting configured versions: %w", err)
}
serviceVersions, err := getHelmVersions()
if err != nil {
return "", fmt.Errorf("getting service versions: %w", err)
}
status, err := kubernetes.ClusterStatus(ctx, kubeClient)
status, err := kubeClient.ClusterStatus(ctx)
if err != nil {
return "", fmt.Errorf("getting cluster status: %w", err)
}
return statusOutput(targetVersions, serviceVersions, status, nodeVersion, string(prettyYAML)), nil
}
func getAttestationConfig(ctx context.Context, cmClient configMapClient, attestVariant variant.Variant) (config.AttestationCfg, error) {
joinConfig, err := cmClient.GetConfigMap(ctx, constants.JoinConfigMap)
if err != nil {
return nil, fmt.Errorf("getting current config map: %w", err)
}
rawAttestationConfig, ok := joinConfig.Data[constants.AttestationConfigFilename]
if !ok {
return nil, fmt.Errorf("attestationConfig not found in %s", constants.JoinConfigMap)
}
attestationConfig, err := config.UnmarshalAttestationConfig([]byte(rawAttestationConfig), attestVariant)
if err != nil {
return nil, fmt.Errorf("unmarshalling attestation config: %w", err)
}
return attestationConfig, nil
return statusOutput(nodeVersion, serviceVersions, status, string(prettyYAML)), nil
}
// statusOutput creates the status cmd output string by formatting the received information.
func statusOutput(
targetVersions kubernetes.TargetVersions, serviceVersions fmt.Stringer,
status map[string]kubernetes.NodeStatus, nodeVersion v1alpha1.NodeVersion, rawAttestationConfig string,
nodeVersion kubecmd.NodeVersion, serviceVersions fmt.Stringer,
status map[string]kubecmd.NodeStatus, rawAttestationConfig string,
) string {
builder := strings.Builder{}
builder.WriteString(targetVersionsString(targetVersions))
builder.WriteString(targetVersionsString(nodeVersion))
builder.WriteString(serviceVersions.String())
builder.WriteString(fmt.Sprintf("Cluster status: %s\n", nodeVersion.Status.Conditions[0].Message))
builder.WriteString(nodeStatusString(status, targetVersions))
builder.WriteString(fmt.Sprintf("Cluster status: %s\n", nodeVersion.ClusterStatus()))
builder.WriteString(nodeStatusString(status, nodeVersion))
builder.WriteString(fmt.Sprintf("Attestation config:\n%s", indentEntireStringWithTab(rawAttestationConfig)))
return builder.String()
}
@ -190,14 +140,14 @@ func indentEntireStringWithTab(input string) string {
}
// nodeStatusString creates the node status part of the output string.
func nodeStatusString(status map[string]kubernetes.NodeStatus, targetVersions kubernetes.TargetVersions) string {
func nodeStatusString(status map[string]kubecmd.NodeStatus, targetVersions kubecmd.NodeVersion) string {
var upToDateImages int
var upToDateK8s int
for _, node := range status {
if node.KubeletVersion() == targetVersions.Kubernetes() {
if node.KubeletVersion() == targetVersions.KubernetesVersion() {
upToDateK8s++
}
if node.ImageVersion() == targetVersions.ImagePath() {
if node.ImageVersion() == targetVersions.ImageReference() {
upToDateImages++
}
}
@ -212,11 +162,11 @@ func nodeStatusString(status map[string]kubernetes.NodeStatus, targetVersions ku
}
// targetVersionsString creates the target versions part of the output string.
func targetVersionsString(target kubernetes.TargetVersions) string {
func targetVersionsString(target kubecmd.NodeVersion) string {
builder := strings.Builder{}
builder.WriteString("Target versions:\n")
builder.WriteString(fmt.Sprintf("\tImage: %s\n", target.Image()))
builder.WriteString(fmt.Sprintf("\tKubernetes: %s\n", target.Kubernetes()))
builder.WriteString(fmt.Sprintf("\tImage: %s\n", target.ImageVersion()))
builder.WriteString(fmt.Sprintf("\tKubernetes: %s\n", target.KubernetesVersion()))
return builder.String()
}
@ -241,10 +191,8 @@ func parseStatusFlags(cmd *cobra.Command) (statusFlags, error) {
}, nil
}
type kubeClient interface {
GetNodes(ctx context.Context) ([]corev1.Node, error)
}
type configMapClient interface {
GetConfigMap(ctx context.Context, name string) (*corev1.ConfigMap, error)
type kubeCmd interface {
ClusterStatus(ctx context.Context) (map[string]kubecmd.NodeStatus, error)
GetConstellationVersion(ctx context.Context) (kubecmd.NodeVersion, error)
GetClusterAttestationConfig(ctx context.Context, variant variant.Variant) (config.AttestationCfg, error)
}