Skip to content

Commit

Permalink
feat: added get kubeconfig sub command (#513)
Browse files Browse the repository at this point in the history
* feat: added get config sub command

* chore: added success message
  • Loading branch information
Al-Pragliola committed May 3, 2024
1 parent 81f9789 commit 379b22c
Show file tree
Hide file tree
Showing 11 changed files with 703 additions and 5 deletions.
23 changes: 23 additions & 0 deletions cmd/get.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cmd

import (
"github.com/spf13/cobra"

"github.com/sighupio/furyctl/cmd/get"
"github.com/sighupio/furyctl/internal/analytics"
)

func NewGetCommand(tracker *analytics.Tracker) *cobra.Command {
getCmd := &cobra.Command{
Use: "get",
Short: "Get a resource (e.g. kubeconfig) from a cluster",
}

getCmd.AddCommand(get.NewKubeconfigCmd(tracker))

return getCmd
}
245 changes: 245 additions & 0 deletions cmd/get/kubeconfig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package get

import (
"errors"
"fmt"
"os"
"path"

"github.com/sirupsen/logrus"
"github.com/spf13/cobra"

"github.com/sighupio/furyctl/internal/analytics"
"github.com/sighupio/furyctl/internal/cluster"
"github.com/sighupio/furyctl/internal/cmd/cmdutil"
"github.com/sighupio/furyctl/internal/config"
"github.com/sighupio/furyctl/internal/dependencies"
"github.com/sighupio/furyctl/internal/distribution"
"github.com/sighupio/furyctl/internal/git"
cobrax "github.com/sighupio/furyctl/internal/x/cobra"
execx "github.com/sighupio/furyctl/internal/x/exec"
netx "github.com/sighupio/furyctl/internal/x/net"
)

var (
ErrParsingFlag = errors.New("error while parsing flag")
ErrDownloadDependenciesFailed = errors.New("dependencies download failed")
)

func NewKubeconfigCmd(tracker *analytics.Tracker) *cobra.Command {
var cmdEvent analytics.Event

cmd := &cobra.Command{
Use: "kubeconfig",
Short: "Get kubeconfig from a cluster",
PreRun: func(cmd *cobra.Command, _ []string) {
cmdEvent = analytics.NewCommandEvent(cobrax.GetFullname(cmd))
},
RunE: func(cmd *cobra.Command, _ []string) error {
// Get flags.
debug, err := cmdutil.BoolFlag(cmd, "debug", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: debug", ErrParsingFlag)
}

binPath := cmdutil.StringFlagOptional(cmd, "bin-path")

furyctlPath, err := cmdutil.StringFlag(cmd, "config", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: config", ErrParsingFlag)
}

outDir, err := cmdutil.StringFlag(cmd, "outdir", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: outdir", ErrParsingFlag)
}

distroLocation, err := cmdutil.StringFlag(cmd, "distro-location", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: distro-location", ErrParsingFlag)
}

gitProtocol, err := cmdutil.StringFlag(cmd, "git-protocol", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: git-protocol", ErrParsingFlag)
}

skipDepsDownload, err := cmdutil.BoolFlag(cmd, "skip-deps-download", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: skip-deps-download", ErrParsingFlag)
}

skipDepsValidation, err := cmdutil.BoolFlag(cmd, "skip-deps-validation", tracker, cmdEvent)
if err != nil {
return fmt.Errorf("%w: skip-deps-validation", ErrParsingFlag)
}

// Get Current dir.
logrus.Debug("Getting current directory path...")

currentDir, err := os.Getwd()
if err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while getting current directory: %w", err)
}

// Get home dir.
logrus.Debug("Getting Home directory path...")
homeDir, err := os.UserHomeDir()
if err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while getting user home directory: %w", err)
}

if binPath == "" {
binPath = path.Join(homeDir, ".furyctl", "bin")
}

parsedGitProtocol := (git.Protocol)(gitProtocol)

if outDir == "" {
outDir = currentDir
}

// Init packages.
execx.Debug = debug

executor := execx.NewStdExecutor()

distrodl := &distribution.Downloader{}
depsvl := dependencies.NewValidator(executor, binPath, furyctlPath, false)

// Init first half of collaborators.
client := netx.NewGoGetterClient()

if distroLocation == "" {
distrodl = distribution.NewCachingDownloader(client, outDir, parsedGitProtocol, "")
} else {
distrodl = distribution.NewDownloader(client, parsedGitProtocol, "")
}

// Validate base requirements.
if err := depsvl.ValidateBaseReqs(); err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while validating requirements: %w", err)
}

// Download the distribution.
logrus.Info("Downloading distribution...")

res, err := distrodl.Download(distroLocation, furyctlPath)
if err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while downloading distribution: %w", err)
}

basePath := path.Join(outDir, ".furyctl", res.MinimalConf.Metadata.Name)

// Init second half of collaborators.
depsdl := dependencies.NewCachingDownloader(client, homeDir, basePath, binPath, parsedGitProtocol)

// Validate the furyctl.yaml file.
logrus.Info("Validating configuration file...")
if err := config.Validate(furyctlPath, res.RepoPath); err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while validating configuration file: %w", err)
}

// Download the dependencies.
if !skipDepsDownload {
logrus.Info("Downloading dependencies...")
if _, err := depsdl.DownloadTools(res.DistroManifest); err != nil {
cmdEvent.AddErrorMessage(ErrDownloadDependenciesFailed)
tracker.Track(cmdEvent)

return fmt.Errorf("%w: %v", ErrDownloadDependenciesFailed, err)
}
}

// Validate the dependencies, unless explicitly told to skip it.
if !skipDepsValidation {
logrus.Info("Validating dependencies...")
if err := depsvl.Validate(res); err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while validating dependencies: %w", err)
}
}

getter, err := cluster.NewKubeconfigGetter(res.MinimalConf, res.DistroManifest, res.RepoPath, furyctlPath, outDir)
if err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while creating the kubeconfig getter: %w", err)
}

if err := getter.Get(); err != nil {
cmdEvent.AddErrorMessage(err)
tracker.Track(cmdEvent)

return fmt.Errorf("error while getting the kubeconfig, please check that the cluster is up and running and is reachable: %w", err)
}

logrus.Infof("Kubeconfig successfully retrieved, you can find it at: %s", path.Join(outDir, "kubeconfig"))

cmdEvent.AddSuccessMessage("kubeconfig successfully retrieved")
tracker.Track(cmdEvent)

return nil
},
}

cmd.Flags().StringP(
"bin-path",
"b",
"",
"Path to the folder where all the dependencies' binaries are installed",
)

cmd.Flags().StringP(
"config",
"c",
"furyctl.yaml",
"Path to the configuration file",
)

cmd.Flags().StringP(
"distro-location",
"",
"",
"Location where to download schemas, defaults and the distribution manifests from. "+
"It can either be a local path (eg: /path/to/fury/distribution) or "+
"a remote URL (eg: git::git@github.com:sighupio/fury-distribution?depth=1&ref=BRANCH_NAME). "+
cmdutil.AnyGoGetterFormatStr,
)

cmd.Flags().Bool(
"skip-deps-download",
false,
"Skip downloading the binaries",
)

cmd.Flags().Bool(
"skip-deps-validation",
false,
"Skip validating dependencies",
)

return cmd
}
1 change: 1 addition & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ furyctl is a command line interface tool to manage the full lifecycle of a Kuber
rootCmd.AddCommand(NewConnectCommand(tracker))
rootCmd.AddCommand(NewApplyCommand(tracker))
rootCmd.AddCommand(NewDiffCommand(tracker))
rootCmd.AddCommand(NewGetCommand(tracker))

return rootCmd
}
Expand Down
6 changes: 6 additions & 0 deletions internal/apis/kfd/v1alpha2/ekscluster/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ func init() {
"EKSCluster",
cluster.NewDeleterFactory[*ClusterDeleter, private.EksclusterKfdV1Alpha2](&ClusterDeleter{}),
)

cluster.RegisterKubeconfigFactory(
"kfd.sighup.io/v1alpha2",
"EKSCluster",
cluster.NewKubeconfigFactory[*KubeconfigGetter, private.EksclusterKfdV1Alpha2](&KubeconfigGetter{}),
)
}
80 changes: 80 additions & 0 deletions internal/apis/kfd/v1alpha2/ekscluster/kubeconfig_getter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) 2017-present SIGHUP s.r.l All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ekscluster

import (
"fmt"
"path"
"strings"

"github.com/sirupsen/logrus"

"github.com/sighupio/fury-distribution/pkg/apis/ekscluster/v1alpha2/private"
"github.com/sighupio/furyctl/internal/cluster"
"github.com/sighupio/furyctl/internal/tool/awscli"
execx "github.com/sighupio/furyctl/internal/x/exec"
)

type KubeconfigGetter struct {
furyctlConf private.EksclusterKfdV1Alpha2
configPath string
outDir string
}

func (k *KubeconfigGetter) SetProperties(props []cluster.KubeconfigProperty) {
for _, prop := range props {
k.SetProperty(prop.Name, prop.Value)
}
}

func (k *KubeconfigGetter) SetProperty(name string, value any) {
lcName := strings.ToLower(name)

switch lcName {
case cluster.KubeconfigPropertyFuryctlConf:
if s, ok := value.(private.EksclusterKfdV1Alpha2); ok {
k.furyctlConf = s
}

case cluster.KubeconfigPropertyConfigPath:
if s, ok := value.(string); ok {
k.configPath = s
}

case cluster.KubeconfigPropertyOutdir:
if s, ok := value.(string); ok {
k.outDir = s
}
}
}

func (k *KubeconfigGetter) Get() error {
logrus.Info("Getting kubeconfig...")

kubeconfigPath := path.Join(k.outDir, "kubeconfig")

awsRunner := awscli.NewRunner(
execx.NewStdExecutor(),
awscli.Paths{
Awscli: "aws",
WorkDir: k.outDir,
},
)

if _, err := awsRunner.Eks(
false,
"update-kubeconfig",
"--name",
k.furyctlConf.Metadata.Name,
"--kubeconfig",
kubeconfigPath,
"--region",
string(k.furyctlConf.Spec.Region),
); err != nil {
return fmt.Errorf("error getting kubeconfig: %w", err)
}

return nil
}
6 changes: 6 additions & 0 deletions internal/apis/kfd/v1alpha2/kfddistribution/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ func init() {
"KFDDistribution",
cluster.NewDeleterFactory[*ClusterDeleter, public.KfddistributionKfdV1Alpha2](&ClusterDeleter{}),
)

cluster.RegisterKubeconfigFactory(
"kfd.sighup.io/v1alpha2",
"KFDDistribution",
cluster.NewKubeconfigFactory[*KubeconfigGetter, public.KfddistributionKfdV1Alpha2](&KubeconfigGetter{}),
)
}
Loading

0 comments on commit 379b22c

Please sign in to comment.