diff --git a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json index e70b80cea8..cf31b49124 100755 --- a/pkg/apis/eksctl.io/v1alpha5/assets/schema.json +++ b/pkg/apis/eksctl.io/v1alpha5/assets/schema.json @@ -1947,10 +1947,17 @@ "description": "enables creation of a fully-private cluster", "x-intellij-html-description": "enables creation of a fully-private cluster", "default": "false" + }, + "skipEndpointCreation": { + "type": "boolean", + "description": "skips the creation process for endpoints completely. This is only used in case of an already provided VPC and if the user decided to set it to true.", + "x-intellij-html-description": "skips the creation process for endpoints completely. This is only used in case of an already provided VPC and if the user decided to set it to true.", + "default": "false" } }, "preferredOrder": [ "enabled", + "skipEndpointCreation", "additionalEndpointServices" ], "additionalProperties": false, diff --git a/pkg/apis/eksctl.io/v1alpha5/types.go b/pkg/apis/eksctl.io/v1alpha5/types.go index 99bf3ba3f1..12a8733cf9 100644 --- a/pkg/apis/eksctl.io/v1alpha5/types.go +++ b/pkg/apis/eksctl.io/v1alpha5/types.go @@ -1578,6 +1578,10 @@ type PrivateCluster struct { // Enabled enables creation of a fully-private cluster Enabled bool `json:"enabled"` + // SkipEndpointCreation skips the creation process for endpoints completely. This is only used in case of an already + // provided VPC and if the user decided to set it to true. + SkipEndpointCreation bool `json:"skipEndpointCreation"` + // AdditionalEndpointServices specifies additional endpoint services that // must be enabled for private access. // Valid entries are `AdditionalEndpointServices` constants diff --git a/pkg/apis/eksctl.io/v1alpha5/validation.go b/pkg/apis/eksctl.io/v1alpha5/validation.go index bc39962e4a..26a8688455 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation.go @@ -201,7 +201,9 @@ func (c *ClusterConfig) ValidatePrivateCluster() error { if c.VPC != nil && c.VPC.ID != "" && len(c.VPC.Subnets.Private) == 0 { return errors.New("vpc.subnets.private must be specified in a fully-private cluster when a pre-existing VPC is supplied") } - if additionalEndpoints := c.PrivateCluster.AdditionalEndpointServices; len(additionalEndpoints) > 0 { + if additionalEndpoints := c.PrivateCluster.AdditionalEndpointServices; len(additionalEndpoints) > 0 && c.PrivateCluster.SkipEndpointCreation { + return fmt.Errorf("additionalEndpoints cannot be defined together with skipEndpointCreation set to true") + } else if len(additionalEndpoints) > 0 { if err := ValidateAdditionalEndpointServices(additionalEndpoints); err != nil { return errors.Wrap(err, "invalid value in privateCluster.additionalEndpointServices") } diff --git a/pkg/apis/eksctl.io/v1alpha5/validation_test.go b/pkg/apis/eksctl.io/v1alpha5/validation_test.go index 7f02e021ba..2ac9ae6216 100644 --- a/pkg/apis/eksctl.io/v1alpha5/validation_test.go +++ b/pkg/apis/eksctl.io/v1alpha5/validation_test.go @@ -563,6 +563,65 @@ var _ = Describe("ClusterConfig validation", func() { }) }) + Describe("ValidatePrivateCluster", func() { + var ( + cfg *api.ClusterConfig + vpc *api.ClusterVPC + ) + + BeforeEach(func() { + cfg = api.NewClusterConfig() + vpc = api.NewClusterVPC() + cfg.VPC = vpc + cfg.PrivateCluster = &api.PrivateCluster{ + Enabled: true, + } + }) + When("private cluster is enabled", func() { + It("validates the config", func() { + err := cfg.ValidatePrivateCluster() + Expect(err).NotTo(HaveOccurred()) + }) + }) + When("vpc is provided but no private subnets", func() { + It("fails the validation", func() { + cfg.VPC.Subnets = &api.ClusterSubnets{} + cfg.VPC.ID = "id" + err := cfg.ValidatePrivateCluster() + Expect(err).To(MatchError(ContainSubstring("vpc.subnets.private must be specified in a fully-private cluster when a pre-existing VPC is supplied"))) + }) + }) + When("additional endpoints are defined with skip endpoints", func() { + It("fails the validation", func() { + cfg.PrivateCluster.AdditionalEndpointServices = []string{api.EndpointServiceCloudFormation} + cfg.PrivateCluster.SkipEndpointCreation = true + err := cfg.ValidatePrivateCluster() + Expect(err).To(MatchError(ContainSubstring("additionalEndpoints cannot be defined together with skipEndpointCreation set to true"))) + }) + }) + When("additional endpoints are defined", func() { + It("validates the endpoint configuration", func() { + cfg.PrivateCluster.AdditionalEndpointServices = []string{api.EndpointServiceCloudFormation} + err := cfg.ValidatePrivateCluster() + Expect(err).NotTo(HaveOccurred()) + }) + }) + When("additional endpoints are defined incorrectly", func() { + It("fails the endpoint validation", func() { + cfg.PrivateCluster.AdditionalEndpointServices = []string{"unknown"} + err := cfg.ValidatePrivateCluster() + Expect(err).To(MatchError(ContainSubstring("invalid value in privateCluster.additionalEndpointServices"))) + }) + }) + When("private cluster is enabled with skip endpoints", func() { + It("does not fail the validation", func() { + cfg.PrivateCluster.SkipEndpointCreation = true + err := cfg.ValidatePrivateCluster() + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) + Describe("cpuCredits", func() { var ng *api.NodeGroup BeforeEach(func() { diff --git a/pkg/cfn/builder/cluster.go b/pkg/cfn/builder/cluster.go index e8b9a4d045..20d16c9bfd 100644 --- a/pkg/cfn/builder/cluster.go +++ b/pkg/cfn/builder/cluster.go @@ -58,7 +58,7 @@ func (c *ClusterResourceSet) AddAllResources() error { c.vpcResourceSet.AddOutputs() clusterSG := c.addResourcesForSecurityGroups(vpcResource) - if privateCluster := c.spec.PrivateCluster; privateCluster.Enabled { + if privateCluster := c.spec.PrivateCluster; privateCluster.Enabled && !privateCluster.SkipEndpointCreation { vpcEndpointResourceSet := NewVPCEndpointResourceSet(c.ec2API, c.region, c.rs, c.spec, vpcResource.VPC, vpcResource.SubnetDetails.Private, clusterSG.ClusterSharedNode) if err := vpcEndpointResourceSet.AddResources(); err != nil { diff --git a/pkg/cfn/builder/cluster_test.go b/pkg/cfn/builder/cluster_test.go index 9955fa42a4..397a816e65 100644 --- a/pkg/cfn/builder/cluster_test.go +++ b/pkg/cfn/builder/cluster_test.go @@ -310,6 +310,17 @@ var _ = Describe("Cluster Template Builder", func() { Expect(clusterTemplate.Resources).NotTo(HaveKey("PublicSubnetRoute")) Expect(clusterTemplate.Resources).To(HaveKey(ContainSubstring("PrivateRouteTable"))) }) + When("skip endpoint creation is set", func() { + BeforeEach(func() { + cfg.PrivateCluster = &api.PrivateCluster{ + Enabled: true, + SkipEndpointCreation: true, + } + }) + It("will skip creating all of the endpoints", func() { + Expect(clusterTemplate.Resources).NotTo(HaveKey(ContainSubstring("VPCEndpoint"))) + }) + }) }) Context("when adding vpc endpoint resources fails", func() { diff --git a/pkg/ctl/cmdutils/filter/nodegroup_filter_test.go b/pkg/ctl/cmdutils/filter/nodegroup_filter_test.go index 100744ed38..35fcc64fab 100644 --- a/pkg/ctl/cmdutils/filter/nodegroup_filter_test.go +++ b/pkg/ctl/cmdutils/filter/nodegroup_filter_test.go @@ -357,7 +357,8 @@ const expected = ` "clusterLogging": {} }, "privateCluster": { - "enabled": false + "enabled": false, + "skipEndpointCreation": false }, "nodeGroups": [ { diff --git a/pkg/windows/ipam_test.go b/pkg/windows/ipam_test.go index 97ced2e57c..6041d52a53 100644 --- a/pkg/windows/ipam_test.go +++ b/pkg/windows/ipam_test.go @@ -42,10 +42,10 @@ var _ = DescribeTable("Windows IPAM", func(e ipamEntry) { } ctx := context.Background() err := ipam.Enable(ctx) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) cm, err := clientset.CoreV1().ConfigMaps("kube-system").Get(ctx, "amazon-vpc-cni", metav1.GetOptions{}) - Expect(err).ToNot(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) Expect(cm.Data).To(Equal(e.expectedConfigMapData)) }, diff --git a/userdocs/src/usage/eks-private-cluster.md b/userdocs/src/usage/eks-private-cluster.md index fb31a990f6..77b67a0df7 100644 --- a/userdocs/src/usage/eks-private-cluster.md +++ b/userdocs/src/usage/eks-private-cluster.md @@ -60,6 +60,21 @@ privateCluster: The endpoints supported in `additionalEndpointServices` are `autoscaling`, `cloudformation` and `logs`. +### Skipping endpoint creations + +If a VPC has already been created with the necessary AWS endpoints set up and linked to the subnets described in the EKS documentation, +`eksctl` can skip creating them by providing the option `skipEndpointCreation` like this: + +```yaml +privateCluster: + enabled: true + skipEndpointCreation: true +``` + +_Note_: this setting cannot be used together with `additionalEndpointServices`. It will skip all endpoint creation. Also, this setting is +only recommended if the endpoint <-> subnet topology is correctly set up. I.e.: subnet ids are correct, `vpce` routing is set up with prefix addresses, +all the necessary EKS endpoints are created and linked to the provided VPC. `eksctl` will not alter any of these resources. + ## Nodegroups Only private nodegroups (both managed and self-managed) are supported in a fully-private cluster because the cluster's VPC is created without any public subnets. The `privateNetworking` field (`nodeGroup[*].privateNetworking` and `managedNodeGroup[*].privateNetworking`) must be