Skip to content

Commit

Permalink
fix: Add cluster reachability check before executing commands
Browse files Browse the repository at this point in the history
Signed-off-by: Justin Toh <tohjustin@hotmail.com>
  • Loading branch information
tohjustin committed Oct 10, 2021
1 parent 580de98 commit 0e6a7b0
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 35 deletions.
7 changes: 7 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type ListOptions struct {
}

type Interface interface {
IsReachable() error
ResolveAPIResource(s string) (*APIResource, error)
Get(ctx context.Context, name string, opts GetOptions) (*unstructuredv1.Unstructured, error)
List(ctx context.Context, opts ListOptions) (*unstructuredv1.UnstructuredList, error)
Expand Down Expand Up @@ -80,6 +81,12 @@ func (f *Flags) ToClient() (Interface, error) {
return cli, nil
}

// IsReachable tests connectivity to the cluster.
func (c *client) IsReachable() error {
_, err := c.discoveryClient.ServerVersion()
return err
}

func (c *client) ResolveAPIResource(s string) (*APIResource, error) {
var gvr schema.GroupVersionResource
var gvk schema.GroupVersionKind
Expand Down
23 changes: 13 additions & 10 deletions pkg/cmd/helm/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var (

// CmdOptions contains all the options for running the helm command.
type CmdOptions struct {
// RequestRelease represents the requested release
// RequestRelease represents the requested Helm release.
RequestRelease string

HelmDriver string
Expand Down Expand Up @@ -109,10 +109,9 @@ func NewCmd(streams genericclioptions.IOStreams, name, parentCmdPath string) *co
// Complete completes all the required options for command.
func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {
var err error
var rlsName string
switch len(args) {
case 1:
rlsName = args[0]
o.RequestRelease = args[0]
default:
return fmt.Errorf("release name must be specified\nSee '%s -h' for help and examples", cmdPath)
}
Expand All @@ -133,9 +132,6 @@ func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {
return err
}

// Resolve command arguments
o.RequestRelease = rlsName

// Setup printer
o.ToPrinter = func(withGroup bool, withNamespace bool) (printers.ResourcePrinterFunc, error) {
printFlags := o.PrintFlags.Copy()
Expand Down Expand Up @@ -166,6 +162,10 @@ func (o *CmdOptions) Validate() error {
return fmt.Errorf("release NAME must be specified")
}

if o.Client == nil {
return fmt.Errorf("client must be provided")
}

klog.V(4).Infof("Namespace: %s", o.Namespace)
klog.V(4).Infof("RequestRelease: %v", o.RequestRelease)
klog.V(4).Infof("Flags.AllNamespaces: %t", *o.Flags.AllNamespaces)
Expand All @@ -176,17 +176,20 @@ func (o *CmdOptions) Validate() error {
klog.V(4).Infof("PrintFlags.NoHeaders: %t", *o.PrintFlags.HumanReadableFlags.NoHeaders)
klog.V(4).Infof("PrintFlags.ShowGroup: %t", *o.PrintFlags.HumanReadableFlags.ShowGroup)
klog.V(4).Infof("PrintFlags.ShowLabels: %t", *o.PrintFlags.HumanReadableFlags.ShowLabels)
klog.V(4).Infof("PrintFlags.WithNamespace: %t", o.PrintFlags.HumanReadableFlags.WithNamespace)

return nil
}

// Run implements all the necessary functionality for command.
//nolint:funlen
func (o *CmdOptions) Run() error {
var err error
ctx := context.Background()

// First check if Kubernetes cluster is reachable
if err := o.Client.IsReachable(); err != nil {
return err
}

// Fetch the release to ensure it exists before proceeding
helmClient := action.NewGet(o.ActionConfig)
rls, err := helmClient.Run(o.RequestRelease)
Expand All @@ -197,7 +200,7 @@ func (o *CmdOptions) Run() error {

// Fetch all Helm release objects (i.e. resources found in the helm release
// manifests) from the cluster
rlsObjs, err := o.getManifestObjects(rls)
rlsObjs, err := o.getManifestObjects(ctx, rls)
if err != nil {
return err
}
Expand Down Expand Up @@ -264,7 +267,7 @@ func (o *CmdOptions) Run() error {

// getManifestObjects fetches all objects found in the manifest of the provided
// Helm release.
func (o *CmdOptions) getManifestObjects(rls *release.Release) ([]unstructuredv1.Unstructured, error) {
func (o *CmdOptions) getManifestObjects(_ context.Context, rls *release.Release) ([]unstructuredv1.Unstructured, error) {
var objs []unstructuredv1.Unstructured
name, ns := rls.Name, rls.Namespace
r := strings.NewReader(rls.Manifest)
Expand Down
53 changes: 28 additions & 25 deletions pkg/cmd/lineage/lineage.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ var (

// CmdOptions contains all the options for running the lineage command.
type CmdOptions struct {
// RequestObject represents the requested object.
RequestObject client.ObjectMeta
// RequestType represents the type of the requested object.
RequestType string
// RequestName represents the name of the requested object.
RequestName string

Flags *Flags
Client client.Interface
Expand Down Expand Up @@ -100,18 +102,17 @@ func NewCmd(streams genericclioptions.IOStreams, name, parentCmdPath string) *co
// Complete completes all the required options for command.
func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {
var err error
var resourceType, resourceName string
switch len(args) {
case 1:
resourceTokens := strings.SplitN(args[0], "/", 2)
if len(resourceTokens) != 2 {
return fmt.Errorf("arguments in <resource>/<name> form must have a single resource and name\nSee '%s -h' for help and examples", cmdPath)
}
resourceType = resourceTokens[0]
resourceName = resourceTokens[1]
o.RequestType = resourceTokens[0]
o.RequestName = resourceTokens[1]
case 2:
resourceType = args[0]
resourceName = args[1]
o.RequestType = args[0]
o.RequestName = args[1]
default:
return fmt.Errorf("resource must be specified as <resource> <name> or <resource>/<name>\nSee '%s -h' for help and examples", cmdPath)
}
Expand All @@ -126,17 +127,6 @@ func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {
return err
}

// Resolve command arguments
api, err := o.Client.ResolveAPIResource(resourceType)
if err != nil {
return err
}
o.RequestObject = client.ObjectMeta{
APIResource: *api,
Name: resourceName,
Namespace: o.Namespace,
}

// Setup printer
o.ToPrinter = func(withGroup bool, withNamespace bool) (printers.ResourcePrinterFunc, error) {
printFlags := o.PrintFlags.Copy()
Expand All @@ -163,18 +153,17 @@ func (o *CmdOptions) Complete(cmd *cobra.Command, args []string) error {

// Validate validates all the required options for the lineage command.
func (o *CmdOptions) Validate() error {
if len(o.RequestObject.Name) == 0 ||
o.RequestObject.GroupVersionResource().Empty() ||
o.RequestObject.GroupVersionKind().Empty() {
return fmt.Errorf("resource TYPE/NAME must be specified")
if len(o.RequestType) == 0 || len(o.RequestName) == 0 {
return fmt.Errorf("resource TYPE & NAME must be specified")
}

if o.Client == nil {
return fmt.Errorf("client must be provided")
}

klog.V(4).Infof("Namespace: %s", o.Namespace)
klog.V(4).Infof("RequestObject: %v", o.RequestObject)
klog.V(4).Infof("RequestType: %v", o.RequestType)
klog.V(4).Infof("RequestName: %v", o.RequestName)
klog.V(4).Infof("Flags.AllNamespaces: %t", *o.Flags.AllNamespaces)
klog.V(4).Infof("Flags.Scopes: %v", *o.Flags.Scopes)
klog.V(4).Infof("ClientFlags.Context: %s", *o.ClientFlags.Context)
Expand All @@ -191,9 +180,23 @@ func (o *CmdOptions) Validate() error {
func (o *CmdOptions) Run() error {
ctx := context.Background()

// First check if Kubernetes cluster is reachable
if err := o.Client.IsReachable(); err != nil {
return err
}

// Fetch the provided object to ensure it exists before proceeding
root, err := o.Client.Get(ctx, o.RequestObject.Name, client.GetOptions{
APIResource: o.RequestObject.APIResource,
api, err := o.Client.ResolveAPIResource(o.RequestType)
if err != nil {
return err
}
obj := client.ObjectMeta{
APIResource: *api,
Name: o.RequestName,
Namespace: o.Namespace,
}
root, err := o.Client.Get(ctx, obj.Name, client.GetOptions{
APIResource: obj.APIResource,
Namespace: o.Namespace,
})
if err != nil {
Expand Down

0 comments on commit 0e6a7b0

Please sign in to comment.