Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate kubeconfig operations from cluster manager #7267

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -528,15 +528,15 @@ mocks: ## Generate mocks
${MOCKGEN} -destination=controllers/mocks/snow_machineconfig_controller.go -package=mocks -source "controllers/snow_machineconfig_controller.go"
${MOCKGEN} -destination=pkg/providers/mocks/providers.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers" Provider,DatacenterConfig,MachineConfig
${MOCKGEN} -destination=pkg/executables/mocks/executables.go -package=mocks "github.com/aws/eks-anywhere/pkg/executables" Executable,DockerClient,DockerContainer
${MOCKGEN} -destination=pkg/providers/docker/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/docker" ProviderClient,ProviderKubectlClient
${MOCKGEN} -destination=pkg/providers/docker/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/docker" ProviderClient,ProviderKubectlClient,KubeconfigReader
${MOCKGEN} -destination=pkg/providers/tinkerbell/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/tinkerbell" ProviderKubectlClient,SSHAuthKeyGenerator
${MOCKGEN} -destination=pkg/providers/cloudstack/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/cloudstack" ProviderCmkClient,ProviderKubectlClient
${MOCKGEN} -destination=pkg/providers/cloudstack/validator_mocks.go -package=cloudstack "github.com/aws/eks-anywhere/pkg/providers/cloudstack" ProviderValidator,ValidatorRegistry
${MOCKGEN} -destination=pkg/providers/vsphere/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/vsphere" ProviderGovcClient,ProviderKubectlClient,IPValidator,VSphereClientBuilder
${MOCKGEN} -destination=pkg/providers/vsphere/setupuser/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/providers/vsphere/setupuser" GovcClient
${MOCKGEN} -destination=pkg/govmomi/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/govmomi" VSphereClient,VMOMIAuthorizationManager,VMOMIFinder,VMOMISessionBuilder,VMOMIFinderBuilder,VMOMIAuthorizationManagerBuilder
${MOCKGEN} -destination=pkg/filewriter/mocks/filewriter.go -package=mocks "github.com/aws/eks-anywhere/pkg/filewriter" FileWriter
${MOCKGEN} -destination=pkg/clustermanager/mocks/client_and_networking.go -package=mocks "github.com/aws/eks-anywhere/pkg/clustermanager" ClusterClient,Networking,AwsIamAuth,EKSAComponents,KubernetesClient,ClientFactory
${MOCKGEN} -destination=pkg/clustermanager/mocks/client_and_networking.go -package=mocks "github.com/aws/eks-anywhere/pkg/clustermanager" ClusterClient,Networking,AwsIamAuth,EKSAComponents,KubernetesClient,ClientFactory,ClusterApplier
${MOCKGEN} -destination=pkg/gitops/flux/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/gitops/flux" FluxClient,KubeClient,GitOpsFluxClient,GitClient,Templater
${MOCKGEN} -destination=pkg/task/mocks/task.go -package=mocks "github.com/aws/eks-anywhere/pkg/task" Task
${MOCKGEN} -destination=pkg/bootstrapper/mocks/client.go -package=mocks "github.com/aws/eks-anywhere/pkg/bootstrapper" KindClient,KubernetesClient
Expand Down Expand Up @@ -613,6 +613,7 @@ mocks: ## Generate mocks
${MOCKGEN} -destination=pkg/registry/mocks/storage.go -package=mocks -source "pkg/registry/storage.go" StorageClient
${MOCKGEN} -destination=pkg/registry/mocks/repository.go -package=mocks oras.land/oras-go/v2/registry Repository
${MOCKGEN} -destination=controllers/mocks/nodeupgrade_controller.go -package=mocks -source "controllers/nodeupgrade_controller.go" RemoteClientRegistry
${MOCKGEN} -destination=pkg/kubeconfig/mocks/writer.go -package=mocks -source "pkg/kubeconfig/kubeconfig.go" Writer

.PHONY: verify-mocks
verify-mocks: mocks ## Verify if mocks need to be updated
Expand Down
6 changes: 4 additions & 2 deletions cmd/eksctl-anywhere/cmd/createcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@
WithPackageInstaller(clusterSpec, cc.installPackages, cc.managementKubeconfig).
WithValidatorClients().
WithCreateClusterDefaulter(createCLIConfig).
WithClusterApplier()
WithClusterApplier().
WithKubeconfigWriter(clusterSpec.Cluster).
WithClusterCreator(clusterSpec.Cluster)

Check warning on line 193 in cmd/eksctl-anywhere/cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/createcluster.go#L191-L193

Added lines #L191 - L193 were not covered by tests

if cc.timeoutOptions.noTimeouts {
factory.WithNoTimeouts()
Expand Down Expand Up @@ -263,9 +265,9 @@
deps.ClusterManager,
deps.GitOpsFlux,
deps.Writer,
deps.ClusterApplier,
deps.EksdInstaller,
deps.PackageInstaller,
deps.ClusterCreator,

Check warning on line 270 in cmd/eksctl-anywhere/cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/eksctl-anywhere/cmd/createcluster.go#L270

Added line #L270 was not covered by tests
)
err = createWorkloadCluster.Run(ctx, clusterSpec, createValidations)

Expand Down
5 changes: 5 additions & 0 deletions pkg/clusterapi/name.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,8 @@ func EnsureNewNameIfChanged[M Object[M]](ctx context.Context,
func ClusterCASecretName(clusterName string) string {
return fmt.Sprintf("%s-ca", clusterName)
}

// ClusterKubeconfigSecretName returns the name of the kubeconfig secret for the cluster.
func ClusterKubeconfigSecretName(clusterName string) string {
return fmt.Sprintf("%s-kubeconfig", clusterName)
}
5 changes: 5 additions & 0 deletions pkg/clusterapi/name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,11 @@ func TestClusterCASecretName(t *testing.T) {
g.Expect(clusterapi.ClusterCASecretName("my-cluster")).To(Equal("my-cluster-ca"))
}

func TestClusterKubeconfigSecretName(t *testing.T) {
g := NewWithT(t)
g.Expect(clusterapi.ClusterKubeconfigSecretName("my-cluster")).To(Equal("my-cluster-kubeconfig"))
}

func TestInitialTemplateNamesForWorkers(t *testing.T) {
tests := []struct {
name string
Expand Down
66 changes: 66 additions & 0 deletions pkg/clustermanager/cluster_creator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package clustermanager

import (
"context"

"github.com/aws/eks-anywhere/pkg/cluster"
"github.com/aws/eks-anywhere/pkg/filewriter"
"github.com/aws/eks-anywhere/pkg/kubeconfig"
"github.com/aws/eks-anywhere/pkg/types"
)

// ClusterApplier is responsible for applying the cluster spec to the cluster.
type ClusterApplier interface {
Run(ctx context.Context, spec *cluster.Spec, managementCluster types.Cluster) error
}

// ClusterCreator is responsible for applying the cluster config and writing the kubeconfig file.
type ClusterCreator struct {
ClusterApplier
kubeconfigWriter kubeconfig.Writer
fs filewriter.FileWriter
}

// NewClusterCreator creates a ClusterCreator.
func NewClusterCreator(applier ClusterApplier, kubeconfigWriter kubeconfig.Writer, fs filewriter.FileWriter) *ClusterCreator {
return &ClusterCreator{
ClusterApplier: applier,
kubeconfigWriter: kubeconfigWriter,
fs: fs,
}
}

// CreateSync creates a workload cluster using the EKS-A controller and returns the types.Cluster object for that cluster.
func (cc ClusterCreator) CreateSync(ctx context.Context, spec *cluster.Spec, managementCluster *types.Cluster) (*types.Cluster, error) {
if err := cc.Run(ctx, spec, *managementCluster); err != nil {
return nil, err
}

Check warning on line 37 in pkg/clustermanager/cluster_creator.go

View check run for this annotation

Codecov / codecov/patch

pkg/clustermanager/cluster_creator.go#L36-L37

Added lines #L36 - L37 were not covered by tests

return cc.buildClusterAccess(ctx, spec.Cluster.Name, managementCluster)
}

func (cc ClusterCreator) buildClusterAccess(ctx context.Context, clusterName string, management *types.Cluster) (*types.Cluster, error) {
cluster := &types.Cluster{
Name: clusterName,
}

fsOptions := []filewriter.FileOptionsFunc{filewriter.PersistentFile, filewriter.Permission0600}
fh, path, err := cc.fs.Create(
kubeconfig.FormatWorkloadClusterKubeconfigFilename(clusterName),
fsOptions...,
)
if err != nil {
return nil, err
}

Check warning on line 54 in pkg/clustermanager/cluster_creator.go

View check run for this annotation

Codecov / codecov/patch

pkg/clustermanager/cluster_creator.go#L53-L54

Added lines #L53 - L54 were not covered by tests

defer fh.Close()

err = cc.kubeconfigWriter.WriteKubeconfig(ctx, clusterName, management.KubeconfigFile, fh)
if err != nil {
return nil, err
}

Check warning on line 61 in pkg/clustermanager/cluster_creator.go

View check run for this annotation

Codecov / codecov/patch

pkg/clustermanager/cluster_creator.go#L60-L61

Added lines #L60 - L61 were not covered by tests

cluster.KubeconfigFile = path

return cluster, nil
}
78 changes: 78 additions & 0 deletions pkg/clustermanager/cluster_creator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package clustermanager_test

import (
"context"
"fmt"
"io"
"os"
"testing"

"github.com/golang/mock/gomock"
. "github.com/onsi/gomega"
"k8s.io/utils/pointer"

"github.com/aws/eks-anywhere/internal/test"
"github.com/aws/eks-anywhere/pkg/cluster"
"github.com/aws/eks-anywhere/pkg/clustermanager"
"github.com/aws/eks-anywhere/pkg/clustermanager/mocks"
"github.com/aws/eks-anywhere/pkg/filewriter"
mockswriter "github.com/aws/eks-anywhere/pkg/filewriter/mocks"
mockskubeconfig "github.com/aws/eks-anywhere/pkg/kubeconfig/mocks"
"github.com/aws/eks-anywhere/pkg/types"
)

type clusterCreatorTest struct {
*WithT
ctx context.Context
spec *cluster.Spec
mgmtCluster *types.Cluster
applier *mocks.MockClusterApplier
writer *mockswriter.MockFileWriter
kubeconfigWriter *mockskubeconfig.MockWriter
}

func newClusterCreator(t *testing.T, clusterName string) (*clustermanager.ClusterCreator, *clusterCreatorTest) {
ctrl := gomock.NewController(t)
cct := &clusterCreatorTest{
WithT: NewWithT(t),
applier: mocks.NewMockClusterApplier(ctrl),
writer: mockswriter.NewMockFileWriter(ctrl),
kubeconfigWriter: mockskubeconfig.NewMockWriter(ctrl),
spec: test.NewClusterSpec(func(s *cluster.Spec) {
s.Cluster.Name = clusterName
}),
ctx: context.Background(),
mgmtCluster: &types.Cluster{
KubeconfigFile: "my-config",
},
}

cc := clustermanager.NewClusterCreator(cct.applier, cct.kubeconfigWriter, cct.writer)

return cc, cct
}

func (cct *clusterCreatorTest) expectFileCreate(fileName, path string, w io.WriteCloser) {
cct.writer.EXPECT().Create(fileName, gomock.AssignableToTypeOf([]filewriter.FileOptionsFunc{})).Return(w, path, nil)
}

func (cct *clusterCreatorTest) expectWriteKubeconfig(clusterName string, w io.Writer) {
cct.kubeconfigWriter.EXPECT().WriteKubeconfig(cct.ctx, clusterName, cct.mgmtCluster.KubeconfigFile, w).Return(nil)
}

func (cct *clusterCreatorTest) expectApplierRun() {
cct.applier.EXPECT().Run(cct.ctx, cct.spec, *cct.mgmtCluster).Return(nil)
}

func TestClusterCreatorCreateSync(t *testing.T) {
clusterName := "testCluster"
clusCreator, tt := newClusterCreator(t, clusterName)
path := "testpath"
writer := os.NewFile(uintptr(*pointer.Uint(0)), "test")
tt.expectApplierRun()
tt.expectWriteKubeconfig(clusterName, writer)
fileName := fmt.Sprintf("%s-eks-a-cluster.kubeconfig", clusterName)
tt.expectFileCreate(fileName, path, writer)
_, err := clusCreator.CreateSync(tt.ctx, tt.spec, tt.mgmtCluster)
tt.Expect(err).To(BeNil())
}
39 changes: 38 additions & 1 deletion pkg/clustermanager/mocks/client_and_networking.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions pkg/dependencies/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ type Dependencies struct {
ExecutableBuilder *executables.ExecutablesBuilder
CreateClusterDefaulter cli.CreateClusterDefaulter
UpgradeClusterDefaulter cli.UpgradeClusterDefaulter
KubeconfigWriter kubeconfig.Writer
ClusterCreator *clustermanager.ClusterCreator
}

// KubeClients defines super struct that exposes all behavior.
Expand Down Expand Up @@ -580,6 +582,45 @@ func (f *Factory) WithProvider(clusterConfigFile string, clusterConfig *v1alpha1
return f
}

// WithKubeconfigWriter adds the KubeconfigReader dependency depending on the provider.
func (f *Factory) WithKubeconfigWriter(clusterConfig *v1alpha1.Cluster) *Factory {
f.WithUnAuthKubeClient()
if clusterConfig.Spec.DatacenterRef.Kind == v1alpha1.DockerDatacenterKind {
f.WithDocker()
}

f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.dependencies.KubeconfigWriter != nil {
return nil
}
writer := kubeconfig.NewClusterAPIKubeconfigSecretWriter(f.dependencies.UnAuthKubeClient)
tatlat marked this conversation as resolved.
Show resolved Hide resolved
switch clusterConfig.Spec.DatacenterRef.Kind {
case v1alpha1.DockerDatacenterKind:
f.dependencies.KubeconfigWriter = docker.NewKubeconfigWriter(f.dependencies.DockerClient, writer)
tatlat marked this conversation as resolved.
Show resolved Hide resolved
default:
f.dependencies.KubeconfigWriter = writer
}
return nil
})

return f
}

// WithClusterCreator adds the ClusterCreator dependency.
func (f *Factory) WithClusterCreator(clusterConfig *v1alpha1.Cluster) *Factory {
f.WithClusterApplier().WithWriter().WithKubeconfigWriter(clusterConfig)
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.dependencies.ClusterCreator != nil {
return nil
}

f.dependencies.ClusterCreator = clustermanager.NewClusterCreator(f.dependencies.ClusterApplier, f.dependencies.KubeconfigWriter, f.dependencies.Writer)
tatlat marked this conversation as resolved.
Show resolved Hide resolved
return nil
})

return f
}

func (f *Factory) WithDocker() *Factory {
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.dependencies.DockerClient != nil {
Expand Down
Loading