-
Notifications
You must be signed in to change notification settings - Fork 288
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Wrapping around the the kubectl interface to make the client API agnostic from the authentication mechanism. Also adding support for an internal schema that avoids having to indicate the resource type because it's infered from the concrete CRD type. This allows to provide a common interface that can be implemented by both a kubectl binary wrapper and a controller runtime client. With this, we will be be able to build logic that interacts with the api server and that can be shared between the CLI and the controller.
- Loading branch information
Showing
12 changed files
with
344 additions
and
12 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
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,27 @@ | ||
package kubernetes | ||
|
||
import ( | ||
"context" | ||
|
||
"k8s.io/apimachinery/pkg/runtime" | ||
) | ||
|
||
// KubeconfigClient is an authenticated kubernetes API client | ||
// it authenticates using the credentials of a kubeconfig file | ||
type KubeconfigClient struct { | ||
client *UnAuthClient | ||
kubeconfig string | ||
} | ||
|
||
func NewKubeconfigClient(client *UnAuthClient, kubeconfig string) *KubeconfigClient { | ||
return &KubeconfigClient{ | ||
client: client, | ||
kubeconfig: kubeconfig, | ||
} | ||
} | ||
|
||
// Get performas a GET call to the kube API server | ||
// and unmarshalls the response into the provdied Object | ||
func (c *KubeconfigClient) Get(ctx context.Context, name, namespace string, obj runtime.Object) error { | ||
return c.client.Get(ctx, name, namespace, c.kubeconfig, obj) | ||
} |
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,34 @@ | ||
package kubernetes_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
. "github.com/onsi/gomega" | ||
|
||
anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" | ||
"github.com/aws/eks-anywhere/pkg/clients/kubernetes" | ||
"github.com/aws/eks-anywhere/pkg/clients/kubernetes/mocks" | ||
) | ||
|
||
func TestKubeconfigClientGet(t *testing.T) { | ||
g := NewWithT(t) | ||
ctx := context.Background() | ||
ctrl := gomock.NewController(t) | ||
kubectl := mocks.NewMockKubectlGetter(ctrl) | ||
kubeconfig := "k.kubeconfig" | ||
|
||
name := "eksa cluster" | ||
namespace := "eksa-system" | ||
obj := &anywherev1.Cluster{} | ||
wantResourceType := "Cluster.v1alpha1.anywhere.eks.amazonaws.com" | ||
|
||
kubectl.EXPECT().GetObject(ctx, wantResourceType, name, namespace, kubeconfig, obj) | ||
|
||
c := kubernetes.NewUnAuthClient(kubectl) | ||
g.Expect(c.Init()).To(Succeed()) | ||
kc := c.KubeconfigClient(kubeconfig) | ||
|
||
g.Expect(kc.Get(ctx, name, namespace, obj)).To(Succeed()) | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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,25 @@ | ||
package kubernetes | ||
|
||
import ( | ||
"k8s.io/apimachinery/pkg/runtime" | ||
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" | ||
|
||
anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" | ||
) | ||
|
||
type schemeAdder func(s *runtime.Scheme) error | ||
|
||
var schemeAdders = []schemeAdder{ | ||
clusterv1.AddToScheme, | ||
anywherev1.AddToScheme, | ||
} | ||
|
||
func addToScheme(scheme *runtime.Scheme, schemeAdder ...schemeAdder) error { | ||
for _, adder := range schemeAdders { | ||
if err := adder(scheme); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} |
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,56 @@ | ||
package kubernetes | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/schema" | ||
"sigs.k8s.io/controller-runtime/pkg/client/apiutil" | ||
) | ||
|
||
type KubectlGetter interface { | ||
GetObject(ctx context.Context, resourceType, name, namespace, kubeconfig string, obj runtime.Object) error | ||
} | ||
|
||
// UnAuthClient is a generic kubernetes API client that takes a kubeconfig | ||
// file on every call in order to authenticate | ||
type UnAuthClient struct { | ||
kubectl KubectlGetter | ||
scheme *runtime.Scheme | ||
} | ||
|
||
func NewUnAuthClient(kubectl KubectlGetter) *UnAuthClient { | ||
return &UnAuthClient{ | ||
kubectl: kubectl, | ||
scheme: runtime.NewScheme(), | ||
} | ||
} | ||
|
||
// Init initializes the client internal API scheme | ||
// It has always be invoked at least once before making any API call | ||
// It is not thread safe | ||
func (c *UnAuthClient) Init() error { | ||
return addToScheme(c.scheme, schemeAdders...) | ||
} | ||
|
||
// Get performas a GET call to the kube API server authenticating with a kubeconfig file | ||
// and unmarshalls the response into the provdied Object | ||
func (c *UnAuthClient) Get(ctx context.Context, name, namespace, kubeconfig string, obj runtime.Object) error { | ||
groupVersionKind, err := apiutil.GVKForObject(obj, c.scheme) | ||
if err != nil { | ||
return fmt.Errorf("getting kubernetes resource: %v", err) | ||
} | ||
|
||
resourceType := groupVersionToKubectlResourceType(groupVersionKind) | ||
return c.kubectl.GetObject(ctx, resourceType, name, namespace, kubeconfig, obj) | ||
} | ||
|
||
// KubeconfigClient returns an equivalent authenticated client | ||
func (c *UnAuthClient) KubeconfigClient(kubeconfig string) *KubeconfigClient { | ||
return NewKubeconfigClient(c, kubeconfig) | ||
} | ||
|
||
func groupVersionToKubectlResourceType(g schema.GroupVersionKind) string { | ||
return fmt.Sprintf("%s.%s.%s", g.Kind, g.Version, g.Group) | ||
} |
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,66 @@ | ||
package kubernetes_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/golang/mock/gomock" | ||
. "github.com/onsi/gomega" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
clusterapiv1 "sigs.k8s.io/cluster-api/api/v1beta1" | ||
|
||
anywherev1 "github.com/aws/eks-anywhere/pkg/api/v1alpha1" | ||
"github.com/aws/eks-anywhere/pkg/clients/kubernetes" | ||
"github.com/aws/eks-anywhere/pkg/clients/kubernetes/mocks" | ||
releasev1 "github.com/aws/eks-anywhere/release/api/v1alpha1" | ||
) | ||
|
||
func TestUnAuthClientGetSuccess(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
namespace string | ||
obj runtime.Object | ||
wantResourceType string | ||
}{ | ||
{ | ||
name: "eksa cluster", | ||
namespace: "eksa-system", | ||
obj: &anywherev1.Cluster{}, | ||
wantResourceType: "Cluster.v1alpha1.anywhere.eks.amazonaws.com", | ||
}, | ||
{ | ||
name: "capi cluster", | ||
namespace: "eksa-system", | ||
obj: &clusterapiv1.Cluster{}, | ||
wantResourceType: "Cluster.v1beta1.cluster.x-k8s.io", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
g := NewWithT(t) | ||
ctx := context.Background() | ||
ctrl := gomock.NewController(t) | ||
kubectl := mocks.NewMockKubectlGetter(ctrl) | ||
kubeconfig := "k.kubeconfig" | ||
|
||
kubectl.EXPECT().GetObject(ctx, tt.wantResourceType, tt.name, tt.namespace, kubeconfig, tt.obj) | ||
|
||
c := kubernetes.NewUnAuthClient(kubectl) | ||
g.Expect(c.Init()).To(Succeed()) | ||
|
||
g.Expect(c.Get(ctx, tt.name, tt.namespace, kubeconfig, tt.obj)).To(Succeed()) | ||
}) | ||
} | ||
} | ||
|
||
func TestUnAuthClientGetUnknownObjType(t *testing.T) { | ||
g := NewWithT(t) | ||
ctx := context.Background() | ||
ctrl := gomock.NewController(t) | ||
kubectl := mocks.NewMockKubectlGetter(ctrl) | ||
|
||
c := kubernetes.NewUnAuthClient(kubectl) | ||
g.Expect(c.Init()).To(Succeed()) | ||
|
||
g.Expect(c.Get(ctx, "name", "namespace", "kubeconfig", &releasev1.Release{})).Error() | ||
} |
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
Oops, something went wrong.