Skip to content

Commit

Permalink
add clustercreator and kubeconfig writer
Browse files Browse the repository at this point in the history
  • Loading branch information
tatlat committed Jan 17, 2024
1 parent a99d4da commit 7016b3b
Show file tree
Hide file tree
Showing 22 changed files with 735 additions and 12 deletions.
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 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er
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 @@ func (cc *createClusterOptions) createCluster(cmd *cobra.Command, _ []string) er
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
}
75 changes: 75 additions & 0 deletions pkg/clustermanager/cluster_creator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package clustermanager_test

import (
"context"
"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"
"github.com/aws/eks-anywhere/pkg/kubeconfig"
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) (*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(),
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(clusterName, path string, w io.WriteCloser) {
cct.writer.EXPECT().Create(kubeconfig.FormatWorkloadClusterKubeconfigFilename(clusterName), gomock.AssignableToTypeOf([]filewriter.FileOptionsFunc{})).Return(w, path, nil)
}

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

func (cct *clusterCreatorTest) expectApplierRun(ctx context.Context, spec *cluster.Spec, managementCluster types.Cluster) {
cct.applier.EXPECT().Run(ctx, spec, managementCluster).Return(nil)
}

func TestClusterCreatorCreateSync(t *testing.T) {
clusCreator, tt := newClusterCreator(t)
clusterName := tt.spec.Cluster.Name
path := "testpath"
writer := os.NewFile(uintptr(*pointer.Uint(0)), "test")
tt.expectApplierRun(tt.ctx, tt.spec, *tt.mgmtCluster)
tt.expectWriteKubeconfig(tt.ctx, clusterName, tt.mgmtCluster.KubeconfigFile, writer)
tt.expectFileCreate(clusterName, 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.

38 changes: 38 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,42 @@ 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()
f.buildSteps = append(f.buildSteps, func(ctx context.Context) error {
if f.dependencies.KubeconfigWriter != nil {
return nil
}
writer := kubeconfig.NewClusterAPIKubeconfigSecretWriter(f.dependencies.UnAuthKubeClient)
switch clusterConfig.Spec.DatacenterRef.Kind {
case v1alpha1.DockerDatacenterKind:
f.WithDocker()
f.dependencies.KubeconfigWriter = docker.NewKubeconfigWriter(f.dependencies.DockerClient, writer)
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)
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
65 changes: 65 additions & 0 deletions pkg/dependencies/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type factoryTest struct {
type provider string

const (
docker provider = "docker"
vsphere provider = "vsphere"
tinkerbell provider = "tinkerbell"
nutanix provider = "nutanix"
Expand All @@ -60,6 +61,8 @@ func newTest(t *testing.T, p provider) *factoryTest {
}

switch p {
case docker:
f.clusterConfigFile = "testdata/cluster_docker.yaml"
case vsphere:
f.clusterConfigFile = "testdata/cluster_vsphere.yaml"
case tinkerbell:
Expand All @@ -86,6 +89,68 @@ func newTest(t *testing.T, p provider) *factoryTest {
return f
}

func TestFactoryBuildWithKubeconfigReader(t *testing.T) {
tests := []struct {
provider provider
}{
{
provider: docker,
},
{
provider: vsphere,
},
}

for _, tc := range tests {
tt := newTest(t, tc.provider)
deps, err := dependencies.NewFactory().
WithLocalExecutables().
WithKubeconfigWriter(tt.clusterSpec.Cluster).
Build(context.Background())
tt.Expect(err).To(BeNil())
tt.Expect(deps.KubeconfigWriter).NotTo(BeNil())
}
}

func TestFactoryBuildWithKubeconfigReaderAlreadyExists(t *testing.T) {
tt := newTest(t, docker)
factory := dependencies.NewFactory()
deps, err := factory.WithLocalExecutables().WithKubeconfigWriter(tt.clusterSpec.Cluster).Build(context.Background())
tt.Expect(err).To(BeNil())
tt.Expect(deps.KubeconfigWriter).ToNot(BeNil())
deps, err = factory.WithKubeconfigWriter(tt.clusterSpec.Cluster).Build(context.Background())
tt.Expect(err).To(BeNil())
tt.Expect(deps.KubeconfigWriter).NotTo(BeNil())
}

func TestFactoryBuildWithClusterCreator(t *testing.T) {
tt := newTest(t, docker)
deps, err := dependencies.NewFactory().
WithLocalExecutables().
WithKubeconfigWriter(tt.clusterSpec.Cluster).
WithClusterCreator(tt.clusterSpec.Cluster).
Build(context.Background())
tt.Expect(err).To(BeNil())
tt.Expect(deps.ClusterCreator).NotTo(BeNil())
}

func TestFactoryBuildWithClusterCreatorAlreadyExists(t *testing.T) {
tt := newTest(t, docker)
factory := dependencies.NewFactory()
deps, _ := factory.
WithLocalExecutables().
WithKubeconfigWriter(tt.clusterSpec.Cluster).
WithClusterCreator(tt.clusterSpec.Cluster).
Build(context.Background())
tt.Expect(deps.ClusterCreator).NotTo(BeNil())
deps, err := factory.
WithKubeconfigWriter(tt.clusterSpec.Cluster).
WithClusterCreator(tt.clusterSpec.Cluster).
Build(context.Background())
tt.Expect(err).To(BeNil())
tt.Expect(deps.ClusterCreator).NotTo(BeNil())
}

func TestFactoryBuildWithProvidervSphere(t *testing.T) {
tt := newTest(t, vsphere)
deps, err := dependencies.NewFactory().
Expand Down
Loading

0 comments on commit 7016b3b

Please sign in to comment.