Skip to content

Commit

Permalink
Use kubectl for kube-proxy upgrader calls (#5631)
Browse files Browse the repository at this point in the history
Before the kube-proxy upgrader
(#5345) all API calls to the
management cluster came either from kubectl or clusterctl, which both
happened to be run in a docker container in the admin machine. This was
the first piece of code that introduced the use of Kubernetes Go client
directly from the CLI binary. This means that if a user was relying on
this internal implementation (explicit interface vs implicit interface),
their system could break if it wasn't setup to give the CLI network
connectivity to the kind cluster.

This PR "reverts" the addition of that new paradigm byt changing the
underlying client implementation to use kubectl commands.

Co-authored-by: Guillermo Gaston <gaslor@amazon.com>
  • Loading branch information
eks-distro-pr-bot and g-gaston authored Apr 13, 2023
1 parent e52fb30 commit 21ee8c7
Show file tree
Hide file tree
Showing 30 changed files with 2,042 additions and 268 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,8 @@ mocks: ## Generate mocks
${MOCKGEN} -destination=pkg/curatedpackages/mocks/packageinstaller.go -package=mocks -source "pkg/curatedpackages/packageinstaller.go" PackageController PackageHandler
${MOCKGEN} -destination=pkg/curatedpackages/mocks/reader.go -package=mocks -source "pkg/curatedpackages/bundle.go" Reader BundleRegistry
${MOCKGEN} -destination=pkg/curatedpackages/mocks/bundlemanager.go -package=mocks -source "pkg/curatedpackages/bundlemanager.go" Manager
${MOCKGEN} -destination=pkg/clients/kubernetes/mocks/kubectl.go -package=mocks -source "pkg/clients/kubernetes/unauth.go"
${MOCKGEN} -destination=pkg/clients/kubernetes/mocks/client.go -package=mocks -source "pkg/clients/kubernetes/client.go"
${MOCKGEN} -destination=pkg/clients/kubernetes/mocks/kubectl.go -package=mocks -source "pkg/clients/kubernetes/kubectl.go"
${MOCKGEN} -destination=pkg/clients/kubernetes/mocks/kubeconfig.go -package=mocks -source "pkg/clients/kubernetes/kubeconfig.go"
${MOCKGEN} -destination=pkg/curatedpackages/mocks/installer.go -package=mocks -source "pkg/curatedpackages/packagecontrollerclient.go" ChartManager ClientBuilder
${MOCKGEN} -destination=pkg/curatedpackages/mocks/kube_client.go -package=mocks -mock_names Client=MockKubeClient sigs.k8s.io/controller-runtime/pkg/client Client
Expand Down
28 changes: 8 additions & 20 deletions internal/test/kubernetes.go
Original file line number Diff line number Diff line change
@@ -1,41 +1,29 @@
package test

import (
"context"

"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"

_ "github.com/aws/eks-anywhere/internal/test/envtest"
"github.com/aws/eks-anywhere/pkg/clients/kubernetes"
"github.com/aws/eks-anywhere/pkg/controller/clientutil"
)

// KubeClient implements kubernetes.Client by using client.Client.
type KubeClient struct {
client client.Client
}

func NewKubeClient(client client.Client) *KubeClient {
return &KubeClient{
client: client,
}
}

// Get retrieves an obj for the given name and namespace from the Kubernetes Cluster.
func (c *KubeClient) Get(ctx context.Context, name, namespace string, obj kubernetes.Object) error {
return c.client.Get(ctx, client.ObjectKey{Name: name, Namespace: namespace}, obj)
// NewKubeClient builds a new kubernetes.Client by using client.Client.
func NewKubeClient(client client.Client) kubernetes.Client {
return clientutil.NewKubeClient(client)
}

// NewFakeKubeClient returns a KubeClient that uses a fake client.Client under the hood.
func NewFakeKubeClient(objs ...client.Object) *KubeClient {
// NewFakeKubeClient returns a kubernetes.Client that uses a fake client.Client under the hood.
func NewFakeKubeClient(objs ...client.Object) kubernetes.Client {
return NewKubeClient(fake.NewClientBuilder().WithObjects(objs...).Build())
}

// NewFakeKubeClientAlwaysError returns a KubeClient that will always fail in any operation
// NewFakeKubeClientAlwaysError returns a kubernetes.Client that will always fail in any operation
// This is achieved by injecting an empty Scheme, which will make the underlying client.Client
// incapable of determining the resource type for a particular client.Object.
func NewFakeKubeClientAlwaysError(objs ...client.Object) *KubeClient {
func NewFakeKubeClientAlwaysError(objs ...client.Object) kubernetes.Client {
return NewKubeClient(
fake.NewClientBuilder().WithScheme(runtime.NewScheme()).WithObjects(objs...).Build(),
)
Expand Down
73 changes: 73 additions & 0 deletions pkg/clients/kubernetes/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package kubernetes

import (
"context"

"sigs.k8s.io/controller-runtime/pkg/client"
)

// Object is a Kubernetes object.
type Object client.Object

// ObjectList is a Kubernetes object list.
type ObjectList client.ObjectList

// Client is Kubernetes API client.
type Client interface {
Reader
Writer
}

// Reader knows how to read and list Kubernetes objects.
type Reader interface {
// Get retrieves an obj for the given name and namespace from the Kubernetes Cluster.
Get(ctx context.Context, name, namespace string, obj Object) error

// List retrieves list of objects. On a successful call, Items field
// in the list will be populated with the result returned from the server.
List(ctx context.Context, list ObjectList) error
}

// Writer knows how to create, delete, and update Kubernetes objects.
type Writer interface {
// Create saves the object obj in the Kubernetes cluster.
Create(ctx context.Context, obj Object) error

// Update updates the given obj in the Kubernetes cluster.
Update(ctx context.Context, obj Object) error

// Delete deletes the given obj from Kubernetes cluster.
Delete(ctx context.Context, obj Object) error

// DeleteAllOf deletes all objects of the given type matching the given options.
DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error
}

// DeleteAllOfOption is some configuration that modifies options for a delete request.
type DeleteAllOfOption interface {
// ApplyToDeleteAllOf applies this configuration to the given deletecollection options.
ApplyToDeleteAllOf(*DeleteAllOfOptions)
}

// DeleteAllOfOptions contains options for deletecollection (deleteallof) requests.
type DeleteAllOfOptions struct {
// HasLabels filters results by label and value. The requirement is an AND match
// for all labels.
HasLabels map[string]string

// Namespace represents the namespace to list for, or empty for
// non-namespaced objects, or to list across all namespaces.
Namespace string
}

var _ DeleteAllOfOption = &DeleteAllOfOptions{}

// ApplyToDeleteAllOf implements DeleteAllOfOption.
func (o *DeleteAllOfOptions) ApplyToDeleteAllOf(do *DeleteAllOfOptions) {
if o.HasLabels != nil {
do.HasLabels = o.HasLabels
}
if o.Namespace != "" {
do.Namespace = o.Namespace
}
}
95 changes: 95 additions & 0 deletions pkg/clients/kubernetes/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package kubernetes_test

import (
"testing"

. "github.com/onsi/gomega"

"github.com/aws/eks-anywhere/pkg/clients/kubernetes"
)

func TestDeleteAllOfOptionsApplyToDeleteAllOf(t *testing.T) {
tests := []struct {
name string
option, in, want *kubernetes.DeleteAllOfOptions
}{
{
name: "empty",
option: &kubernetes.DeleteAllOfOptions{},
in: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "ns",
},
want: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "ns",
},
},
{
name: "only Namespace",
option: &kubernetes.DeleteAllOfOptions{
Namespace: "other-ns",
},
in: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "ns",
},
want: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "other-ns",
},
},
{
name: "Namespace and labels",
option: &kubernetes.DeleteAllOfOptions{
Namespace: "other-ns",
HasLabels: map[string]string{
"label2": "value2",
},
},
in: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "ns",
},
want: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label2": "value2",
},
Namespace: "other-ns",
},
},
{
name: "empty not nil labels",
option: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{},
},
in: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{
"label": "value",
},
Namespace: "ns",
},
want: &kubernetes.DeleteAllOfOptions{
HasLabels: map[string]string{},
Namespace: "ns",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
tt.option.ApplyToDeleteAllOf(tt.in)
g.Expect(tt.in).To(BeComparableTo(tt.want))
})
}
}
36 changes: 26 additions & 10 deletions pkg/clients/kubernetes/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,8 @@ package kubernetes

import (
"context"

"sigs.k8s.io/controller-runtime/pkg/client"
)

type Client interface {
Get(ctx context.Context, name, namespace string, obj Object) error
}

type Object client.Object

type ObjectList client.ObjectList

// KubeconfigClient is an authenticated kubernetes API client
// it authenticates using the credentials of a kubeconfig file.
type KubeconfigClient struct {
Expand All @@ -33,3 +23,29 @@ func NewKubeconfigClient(client *UnAuthClient, kubeconfig string) *KubeconfigCli
func (c *KubeconfigClient) Get(ctx context.Context, name, namespace string, obj Object) error {
return c.client.Get(ctx, name, namespace, c.kubeconfig, obj)
}

// List retrieves list of objects. On a successful call, Items field
// in the list will be populated with the result returned from the server.
func (c *KubeconfigClient) List(ctx context.Context, list ObjectList) error {
return c.client.List(ctx, c.kubeconfig, list)
}

// Create saves the object obj in the Kubernetes cluster.
func (c *KubeconfigClient) Create(ctx context.Context, obj Object) error {
return c.client.Create(ctx, c.kubeconfig, obj)
}

// Update updates the given obj in the Kubernetes cluster.
func (c *KubeconfigClient) Update(ctx context.Context, obj Object) error {
return c.client.Update(ctx, c.kubeconfig, obj)
}

// Delete deletes the given obj from Kubernetes cluster.
func (c *KubeconfigClient) Delete(ctx context.Context, obj Object) error {
return c.client.Delete(ctx, c.kubeconfig, obj)
}

// DeleteAllOf deletes all objects of the given type matching the given options.
func (c *KubeconfigClient) DeleteAllOf(ctx context.Context, obj Object, opts ...DeleteAllOfOption) error {
return c.client.DeleteAllOf(ctx, c.kubeconfig, obj, opts...)
}
Loading

0 comments on commit 21ee8c7

Please sign in to comment.