-
Notifications
You must be signed in to change notification settings - Fork 431
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1e5ccb5
commit 72df660
Showing
4 changed files
with
344 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package cmd | ||
|
||
import ( | ||
"cmp" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/loft-sh/log" | ||
"github.com/loft-sh/vcluster/pkg/cli" | ||
"github.com/loft-sh/vcluster/pkg/cli/config" | ||
"github.com/loft-sh/vcluster/pkg/cli/flags" | ||
pdefaults "github.com/loft-sh/vcluster/pkg/platform/defaults" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
// DescribeCmd holds the login cmd flags | ||
type DescribeCmd struct { | ||
*flags.GlobalFlags | ||
|
||
output string | ||
log log.Logger | ||
project string | ||
} | ||
|
||
// NewDescribeCmd creates a new command | ||
func NewDescribeCmd(globalFlags *flags.GlobalFlags, defaults *pdefaults.Defaults) *cobra.Command { | ||
cmd := &DescribeCmd{ | ||
GlobalFlags: globalFlags, | ||
log: log.GetInstance(), | ||
} | ||
driver := "" | ||
|
||
cobraCmd := &cobra.Command{ | ||
Use: "describe", | ||
Short: "Describes a virtual cluster", | ||
Long: `####################################################### | ||
################## vcluster describe ################## | ||
####################################################### | ||
describes a virtual cluster | ||
Example: | ||
vcluster describe test | ||
vcluster describe -o json test | ||
####################################################### | ||
`, | ||
Args: cobra.ExactArgs(1), | ||
RunE: func(cobraCmd *cobra.Command, args []string) error { | ||
return cmd.Run(cobraCmd, driver, args[0]) | ||
}, | ||
} | ||
p, _ := defaults.Get(pdefaults.KeyProject, "") | ||
|
||
cobraCmd.Flags().StringVar(&driver, "driver", "", "The driver to use for managing the virtual cluster, can be either helm or platform.") | ||
cobraCmd.Flags().StringVarP(&cmd.output, "output", "o", "", "The format to use to display the information, can either be json or yaml") | ||
cobraCmd.Flags().StringVarP(&cmd.project, "project", "p", p, "The project to use") | ||
|
||
return cobraCmd | ||
} | ||
|
||
// Run executes the functionality | ||
func (cmd *DescribeCmd) Run(cobraCmd *cobra.Command, driver, name string) error { | ||
cfg := cmd.LoadedConfig(cmd.log) | ||
|
||
// If driver has been passed as flag use it, otherwise read it from the config file | ||
driverType, err := config.ParseDriverType(cmp.Or(driver, string(cfg.Driver.Type))) | ||
if err != nil { | ||
return fmt.Errorf("parse driver type: %w", err) | ||
} | ||
if driverType == config.PlatformDriver { | ||
return cli.DescribePlatform(cobraCmd.Context(), cmd.GlobalFlags, os.Stdout, cmd.log, name, cmd.project, cmd.output) | ||
} | ||
|
||
return cli.DescribeHelm(cobraCmd.Context(), cmd.GlobalFlags, os.Stdout, name, cmd.output) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
package cli | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"strings" | ||
|
||
"github.com/ghodss/yaml" | ||
"github.com/loft-sh/log" | ||
"github.com/loft-sh/vcluster/config" | ||
"github.com/loft-sh/vcluster/pkg/cli/find" | ||
"github.com/loft-sh/vcluster/pkg/cli/flags" | ||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/client-go/kubernetes" | ||
"k8s.io/client-go/tools/clientcmd" | ||
) | ||
|
||
type DescribeOutput struct { | ||
Distro string `json:"Distro"` | ||
Version string `json:"Version"` | ||
BackingStore string `json:"Backing Store"` | ||
ImageTags ImageTag `json:"Image tags"` | ||
} | ||
|
||
type ImageTag struct { | ||
APIServer string `json:"apiServer,omitempty"` | ||
Syncer string `json:"syncer,omitempty"` | ||
Scheduler string `json:"scheduler,omitempty"` | ||
ControllerManager string `json:"controllerManger,omitempty"` | ||
} | ||
|
||
func DescribeHelm(ctx context.Context, flags *flags.GlobalFlags, output io.Writer, name, format string) error { | ||
namespace := "vcluster-" + name | ||
if flags.Namespace != "" { | ||
namespace = flags.Namespace | ||
} | ||
|
||
secretName := "vc-config-" + name | ||
|
||
kConf := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}) | ||
rawConfig, err := kConf.RawConfig() | ||
if err != nil { | ||
return err | ||
} | ||
clientConfig, err := kConf.ClientConfig() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
clientset, err := kubernetes.NewForConfig(clientConfig) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
secret, err := clientset.CoreV1().Secrets(namespace).Get(ctx, secretName, v1.GetOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
configBytes, ok := secret.Data["config.yaml"] | ||
if !ok { | ||
return fmt.Errorf("secret %s in namespace %s does not contain the expected 'config.yaml' field", secretName, namespace) | ||
} | ||
|
||
switch format { | ||
case "yaml": | ||
_, err = output.Write(configBytes) | ||
return err | ||
case "json": | ||
b, err := yaml.YAMLToJSON(configBytes) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = output.Write(b) | ||
return err | ||
} | ||
fVcluster, err := find.GetVCluster(ctx, rawConfig.CurrentContext, name, namespace, log.Discard) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
describeOutput := &DescribeOutput{} | ||
err = extractFromValues(describeOutput, configBytes, format, fVcluster.Version, output) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
describeOutput.Version = fVcluster.Version | ||
|
||
b, err := yaml.Marshal(describeOutput) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = output.Write(b) | ||
|
||
return err | ||
} | ||
|
||
func extractFromValues(d *DescribeOutput, configBytes []byte, format, version string, output io.Writer) error { | ||
conf := &config.Config{} | ||
err := yaml.Unmarshal(configBytes, conf) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
switch format { | ||
case "yaml": | ||
_, err := output.Write(configBytes) | ||
return err | ||
case "json": | ||
err := json.NewEncoder(output).Encode(conf) | ||
if err != nil { | ||
return err | ||
} | ||
default: | ||
d.Distro = conf.Distro() | ||
d.BackingStore = string(conf.BackingStoreType()) | ||
syncer, api, scheduler, controllerManager := getImageTags(conf, version) | ||
d.ImageTags = ImageTag{ | ||
Syncer: syncer, | ||
APIServer: api, | ||
Scheduler: scheduler, | ||
ControllerManager: controllerManager, | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func valueOrDefaultRegistry(value, def string) string { | ||
if value != "" { | ||
return value | ||
} | ||
if def != "" { | ||
return def | ||
} | ||
return "ghcr.io/loft-sh" | ||
} | ||
|
||
func valueOrDefaultSyncerImage(value string) string { | ||
if value != "" { | ||
return value | ||
} | ||
return "vcluster-pro" | ||
} | ||
|
||
func getImageTags(c *config.Config, version string) (syncer, api, scheduler, controllerManager string) { | ||
syncerConfig := c.ControlPlane.StatefulSet.Image | ||
defaultRegistry := c.ControlPlane.Advanced.DefaultImageRegistry | ||
|
||
syncer = valueOrDefaultRegistry(syncerConfig.Registry, defaultRegistry) + "/" + valueOrDefaultSyncerImage(syncerConfig.Repository) + ":" + syncerConfig.Tag | ||
if syncerConfig.Tag == "" { | ||
// the chart uses the chart version for the syncer tag, so the tag isn't set by default | ||
syncer += version | ||
} | ||
|
||
switch c.Distro() { | ||
case config.K8SDistro: | ||
k8s := c.ControlPlane.Distro.K8S | ||
|
||
api = valueOrDefaultRegistry(k8s.APIServer.Image.Registry, defaultRegistry) + "/" + k8s.APIServer.Image.Repository + ":" + k8s.APIServer.Image.Tag | ||
// this is the case where the repository is not set | ||
if strings.HasPrefix(api, valueOrDefaultRegistry(k8s.APIServer.Image.Registry, defaultRegistry)+"/:") { | ||
// with the platform driver if only the registry is set we won't be able to display complete info | ||
api = "" | ||
} | ||
|
||
scheduler = valueOrDefaultRegistry(k8s.Scheduler.Image.Registry, defaultRegistry) + "/" + k8s.Scheduler.Image.Repository + ":" + k8s.Scheduler.Image.Tag | ||
if strings.HasPrefix(scheduler, valueOrDefaultRegistry(k8s.Scheduler.Image.Registry, defaultRegistry)+"/:") { | ||
// with the platform driver if only the registry is set we won't be able to display complete info | ||
scheduler = "" | ||
} | ||
|
||
controllerManager = valueOrDefaultRegistry(k8s.ControllerManager.Image.Registry, defaultRegistry) + "/" + k8s.ControllerManager.Image.Repository + ":" + k8s.ControllerManager.Image.Tag | ||
if strings.HasPrefix(controllerManager, valueOrDefaultRegistry(k8s.ControllerManager.Image.Registry, defaultRegistry)+"/:") { | ||
// with the platform driver if only the registry is set we won't be able to display complete info | ||
controllerManager = "" | ||
} | ||
|
||
case config.K3SDistro: | ||
k3s := c.ControlPlane.Distro.K3S | ||
|
||
api = valueOrDefaultRegistry(k3s.Image.Registry, defaultRegistry) + "/" + k3s.Image.Repository + ":" + k3s.Image.Tag | ||
if strings.HasPrefix(api, valueOrDefaultRegistry(k3s.Image.Registry, defaultRegistry)+"/:") { | ||
// with the platform driver if only the registry is set we won't be able to display complete info | ||
api = "" | ||
} | ||
case config.K0SDistro: | ||
k0s := c.ControlPlane.Distro.K0S | ||
|
||
api = valueOrDefaultRegistry(k0s.Image.Registry, defaultRegistry) + "/" + k0s.Image.Repository + ":" + k0s.Image.Tag | ||
|
||
if strings.HasPrefix(api, valueOrDefaultRegistry(k0s.Image.Registry, defaultRegistry)+"/:") { | ||
// with the platform driver if only the registry is set we won't be able to display complete info | ||
api = "" | ||
} | ||
} | ||
|
||
syncer = strings.TrimSuffix(syncer, ":") | ||
api = strings.TrimSuffix(api, ":") | ||
scheduler = strings.TrimSuffix(scheduler, ":") | ||
controllerManager = strings.TrimSuffix(controllerManager, ":") | ||
|
||
return syncer, api, scheduler, controllerManager | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package cli | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
|
||
"github.com/ghodss/yaml" | ||
"github.com/loft-sh/log" | ||
"github.com/loft-sh/vcluster/pkg/cli/flags" | ||
"github.com/loft-sh/vcluster/pkg/platform" | ||
) | ||
|
||
func DescribePlatform(ctx context.Context, globalFlags *flags.GlobalFlags, output io.Writer, l log.Logger, name, projectName, format string) error { | ||
platformClient, err := platform.InitClientFromConfig(ctx, globalFlags.LoadedConfig(l)) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
proVClusters, err := platform.ListVClusters(ctx, platformClient, name, projectName) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// provclusters should be len(1), because 0 exits beforehand, and there's only 1 | ||
// vcluster with a name in a project | ||
values := proVClusters[0].VirtualCluster.Status.VirtualCluster.HelmRelease.Values | ||
version := proVClusters[0].VirtualCluster.Status.VirtualCluster.HelmRelease.Chart.Version | ||
|
||
switch format { | ||
case "yaml": | ||
_, err = output.Write([]byte(values)) | ||
return err | ||
case "json": | ||
b, err := yaml.YAMLToJSON([]byte(values)) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = output.Write(b) | ||
return err | ||
} | ||
describeOutput := &DescribeOutput{} | ||
|
||
describeOutput.Version = version | ||
|
||
err = extractFromValues(describeOutput, []byte(values), format, version, output) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if describeOutput.ImageTags.Syncer == "" { | ||
describeOutput.ImageTags.Syncer = fmt.Sprintf("ghcr.io/loft-sh/vcluster-pro:%s", version) | ||
} | ||
|
||
b, err := yaml.Marshal(describeOutput) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = output.Write(b) | ||
|
||
return err | ||
} |