Skip to content

Commit

Permalink
add base changes for cluster endpoint access
Browse files Browse the repository at this point in the history
  • Loading branch information
D3nn committed Sep 17, 2019
1 parent 0c87ef5 commit d508d86
Show file tree
Hide file tree
Showing 10 changed files with 328 additions and 6 deletions.
8 changes: 8 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,11 @@ func DefaultClusterNAT() *ClusterNAT {
Gateway: &single,
}
}

// ClusterEndpointAccessDefaults returns a ClusterEndpoints pointer with default values set.
func ClusterEndpointAccessDefaults() *ClusterEndpoints {
return &ClusterEndpoints{
PrivateAccess: &False,
PublicAccess: &True,
}
}
6 changes: 5 additions & 1 deletion pkg/apis/eksctl.io/v1alpha5/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,11 @@ func IsEnabled(v *bool) bool { return v != nil && *v }
// IsDisabled will only return true if v is not nil and false
func IsDisabled(v *bool) bool { return v != nil && !*v }

// IsSet will only return true if v is not nil
func IsSet(v *string) bool { return v != nil }

// IsSetAndNonEmptyString will only return true if s is not nil and not empty
func IsSetAndNonEmptyString(s *string) bool { return s != nil && *s != "" }
func IsSetAndNonEmptyString(s *string) bool { return IsSet(s) && *s != "" }

// SupportedRegions are the regions where EKS is available
func SupportedRegions() []string {
Expand Down Expand Up @@ -390,6 +393,7 @@ func NewClusterVPC() *ClusterVPC {
},
NAT: DefaultClusterNAT(),
AutoAllocateIPv6: Disabled(),
ClusterEndpoints: ClusterEndpointAccessDefaults(),
}
}

Expand Down
53 changes: 53 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/validation.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package v1alpha5

import (
"errors"
"fmt"
"strings"

Expand Down Expand Up @@ -63,9 +64,61 @@ func ValidateClusterConfig(cfg *ClusterConfig) error {
}
}

if !cfg.HasClusterEndpointAccess() {
return fmt.Errorf("Cluster endpoint access invalid, at least one of Public, Private " +
"needs to be enabled")
}
return nil
}
// NoAccessMsg function returns a message inidicating that the config leaves no API endpoint access
func NoAccessMsg(endpts *ClusterEndpoints) string {
return fmt.Sprintf("Cluster API access must have one of public or private endpointAccess " +
"enabled, current values are publicAccess=%v and privateAccess=%v, aborting",
endpts.PublicAccess, endpts.PrivateAccess)
}

// PrivateOnlyUseUtilsMsg returns a message that indicates that the operation must be done using
// eksctl utils update-cluster-api-access
func PrivateOnlyUseUtilsMsg() string {
return "eksctl cannot join worker nodes to the EKS cluster when public access isn't allowed. " +
"use 'eksctl utils update-cluster-api-access ...' after creating cluster with default access"
}

// PrivateOnlyAwsChangesNeededMsg returns a message that warns that not having pulbic access
// requires changes to AWS resource configuration in order to effectively use clients in the VPC
func PrivateOnlyAwsChangesNeededMsg() string {
return "warning, having public access disallowed will subsequently interfere with some " +
"features of eksctl. This will require running subsequent eksctl (and kubernetes) " +
"commands from within the VPC. Running such commands in the VPC requires making " +
"updates to some AWS resources. See: " +
"https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html#private-access " +
"for more details"
}

//ValidateClusterEndpointConfig checks the endpoint configuration for potential issues
func (c *ClusterConfig) ValidateClusterEndpointConfig() error {
if c.HasClusterEndpointAccess() {
endpts := c.VPC.ClusterEndpoints
if NoAccess(endpts) {
return errors.New(NoAccessMsg(endpts))
}
if PrivateOnly(endpts) {
return errors.New(PrivateOnlyUseUtilsMsg())
}
}
return nil
}

//NoAccess returns true if neither public are private cluster endpoint access is enabled and false otherise
func NoAccess(ces *ClusterEndpoints) bool {
return !(*ces.PublicAccess || *ces.PrivateAccess)
}

//PrivateOnly returns true if public cluster endpoint access is disabled and public cluster endpoint access is enabled, and false otherwise
func PrivateOnly(ces *ClusterEndpoints) bool {
return !*ces.PublicAccess && *ces.PrivateAccess
}

// ValidateNodeGroup checks compatible fields of a given nodegroup
func ValidateNodeGroup(i int, ng *NodeGroup) error {
path := fmt.Sprintf("nodeGroups[%d]", i)
Expand Down
32 changes: 32 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type (
AutoAllocateIPv6 *bool `json:"autoAllocateIPv6,omitempty"`
// +optional
NAT *ClusterNAT `json:"nat,omitempty"`
// +optional
ClusterEndpoints *ClusterEndpoints `json:"clusterEndpoints,omitempty"`
}
// ClusterSubnets holds private and public subnets
ClusterSubnets struct {
Expand All @@ -47,6 +49,12 @@ type (
ClusterNAT struct {
Gateway *string `json:"gateway,omitempty"`
}

// ClusterEndpoints holds cluster api server endpoint access information
ClusterEndpoints struct {
PrivateAccess *bool `json:"privateAccess,omitempty,false"`
PublicAccess *bool `json:"publicAccess,omitempty,true"`
}
)

const (
Expand All @@ -58,6 +66,14 @@ const (
SubnetTopologyPrivate SubnetTopology = "Private"
// SubnetTopologyPublic represents publicly-routed subnets
SubnetTopologyPublic SubnetTopology = "Public"

)

var (
// True holds true value and can have it's address taken
True = true
// False holds false value and can have it's address taken
False = false
)

// SubnetTopologies returns a list of topologies
Expand All @@ -68,6 +84,7 @@ func SubnetTopologies() []SubnetTopology {
}
}


// DefaultCIDR returns default global CIDR for VPC
func DefaultCIDR() ipnet.IPNet {
return ipnet.IPNet{
Expand Down Expand Up @@ -186,3 +203,18 @@ func (c *ClusterConfig) HasSufficientSubnets() error {

return nil
}

//HasClusterEndpointAccess determines if endpoint access was configured in config file or not
func (c *ClusterConfig) HasClusterEndpointAccess() bool {
hasAccess := false
if !(c.VPC == nil || c.VPC.ClusterEndpoints == nil) {
hasPublicAccess := &c.VPC.ClusterEndpoints.PublicAccess != nil && *c.VPC.ClusterEndpoints.PublicAccess
hasPrivateAccess := &c.VPC.ClusterEndpoints.PrivateAccess != nil && *c.VPC.ClusterEndpoints.PrivateAccess

hasAccess = hasPublicAccess || hasPrivateAccess
} else {
// an empty VPC or ClusterEndpoints config defaults to public true/private false
hasAccess = true
}
return hasAccess
}
84 changes: 84 additions & 0 deletions pkg/apis/eksctl.io/v1alpha5/vpc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package v1alpha5

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
)

type EndpointAccessCases struct {
vpc *ClusterVPC
endpoints *ClusterEndpoints
Public *bool
Private *bool
}

var _ = Describe("VPC Configuration", func() {
DescribeTable("Can determine if VPC config in config file has cluster endpoints",
func(e EndpointAccessCases) {
cc := &ClusterConfig{}
Expect(cc.HasClusterEndpointAccess()).Should(BeTrue())
cc.VPC = e.vpc
Expect(cc.HasClusterEndpointAccess()).Should(BeTrue())
if cc.VPC != nil {
cc.VPC.ClusterEndpoints = e.endpoints
}
if e.Public != nil && cc.VPC.ClusterEndpoints != nil {
cc.VPC.ClusterEndpoints.PublicAccess = e.Public
}
if e.Private != nil && cc.VPC.ClusterEndpoints != nil {
cc.VPC.ClusterEndpoints.PrivateAccess = e.Private
}
if e.Public != nil && e.Private != nil {
if *e.Public == true || *e.Private == true {
Expect(cc.HasClusterEndpointAccess()).Should(BeTrue())
}
if *e.Public == true && *e.Private == false {
Expect(cc.HasClusterEndpointAccess()).Should(BeTrue())
}
if *e.Public == false && *e.Private == true {
Expect(cc.HasClusterEndpointAccess()).Should(BeTrue())
}
if *e.Public == false && *e.Private == false {
Expect(cc.HasClusterEndpointAccess()).Should(BeFalse())
}
}
},
Entry("No VPC", EndpointAccessCases{
vpc: nil,
endpoints: nil,
Public: nil,
Private: nil,
}),
Entry("Has Empty VPC", EndpointAccessCases{
vpc: &ClusterVPC{},
endpoints: nil,
Public: nil,
Private: nil,
}),
Entry("Public=True, Private=true", EndpointAccessCases{
vpc: &ClusterVPC{},
endpoints: &ClusterEndpoints{},
Public: &True,
Private: &True,
}),
Entry("Public=true, Private=false", EndpointAccessCases{
vpc: &ClusterVPC{},
endpoints: &ClusterEndpoints{},
Public: &True,
Private: &False,
}),
Entry("Public=false, Private=true", EndpointAccessCases{
vpc: &ClusterVPC{},
endpoints: &ClusterEndpoints{},
Public: nil,
Private: nil,
}),
Entry("Public=false, Private=false", EndpointAccessCases{
vpc: &ClusterVPC{},
endpoints: &ClusterEndpoints{},
Public: &False,
Private: &False,
}),
)
})
4 changes: 4 additions & 0 deletions pkg/cfn/builder/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ func testVPC() *api.ClusterVPC {
},
},
},
ClusterEndpoints: &api.ClusterEndpoints {
PrivateAccess: api.Disabled(),
PublicAccess: api.Enabled(),
},
}
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/ctl/cmdutils/nodegroup_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,11 @@ const expected = `
"autoAllocateIPv6": false,
"nat": {
"gateway": "Single"
}
},
"clusterEndpoints": {
"privateAccess": false,
"publicAccess": true
}
},
"cloudWatch": {
"clusterLogging": {}
Expand Down
33 changes: 29 additions & 4 deletions pkg/vpc/vpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
cfn "github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/ec2"
awseks "github.com/aws/aws-sdk-go/service/eks"

api "github.com/weaveworks/eksctl/pkg/apis/eksctl.io/v1alpha5"
"github.com/weaveworks/eksctl/pkg/cfn/outputs"
Expand Down Expand Up @@ -73,11 +74,11 @@ func describeSubnets(provider api.ClusterProvider, subnetIDs ...string) ([]*ec2.
return output.Subnets, nil
}

func describe(povider api.ClusterProvider, vpcID string) (*ec2.Vpc, error) {
func describe(provider api.ClusterProvider, vpcID string) (*ec2.Vpc, error) {
input := &ec2.DescribeVpcsInput{
VpcIds: []*string{aws.String(vpcID)},
}
output, err := povider.EC2().DescribeVpcs(input)
output, err := provider.EC2().DescribeVpcs(input)
if err != nil {
return nil, err
}
Expand All @@ -96,6 +97,12 @@ func UseFromCluster(provider api.ClusterProvider, stack *cfn.Stack, spec *api.Cl
// CIDR, as it can only be set to anything due to defaulting
spec.VPC.CIDR = nil

// Cluster Endpoint Access isn't part of the EKS CloudFormation Cluster stack at this point
// Retrieve the current confiugration via the SDK
if err := UseEndpointAccessFromCluster(provider, spec); err != nil {
return err
}

requiredCollectors := map[string]outputs.Collector{
outputs.ClusterVPC: func(v string) error {
spec.VPC.ID = v
Expand Down Expand Up @@ -173,7 +180,7 @@ func ImportSubnets(provider api.ClusterProvider, spec *api.ClusterConfig, topolo
Gateway: &disable,
}

// ensure VPC gets imported and validated firt, if it's already set
// ensure VPC gets imported and validated first, if it's already set
if err := Import(provider, spec, spec.VPC.ID); err != nil {
return err
}
Expand Down Expand Up @@ -218,7 +225,7 @@ func ImportSubnetsFromList(provider api.ClusterProvider, spec *api.ClusterConfig
// there is a mismatch of local vs remote states
func ImportAllSubnets(provider api.ClusterProvider, spec *api.ClusterConfig) error {
if spec.VPC.ID != "" {
// ensure VPC gets imported and validated firt, if it's already set
// ensure VPC gets imported and validated first, if it's already set
if err := Import(provider, spec, spec.VPC.ID); err != nil {
return err
}
Expand All @@ -232,3 +239,21 @@ func ImportAllSubnets(provider api.ClusterProvider, spec *api.ClusterConfig) err

return nil
}

//UseEndpointAccessFromCluster retrieves the Cluster's endpoint access configuration via the SDK
// as the CloudFormation Stack doesn't support that configuration currently
func UseEndpointAccessFromCluster(provider api.ClusterProvider, spec *api.ClusterConfig) error {
input := &awseks.DescribeClusterInput{
Name: aws.String(spec.Metadata.Name),
}
output, err := provider.EKS().DescribeCluster(input)
if err != nil {
return err
}
if spec.VPC.ClusterEndpoints == nil {
spec.VPC.ClusterEndpoints = &api.ClusterEndpoints{}
}
spec.VPC.ClusterEndpoints.PublicAccess = output.Cluster.ResourcesVpcConfig.EndpointPublicAccess
spec.VPC.ClusterEndpoints.PrivateAccess = output.Cluster.ResourcesVpcConfig.EndpointPrivateAccess
return nil
}
11 changes: 11 additions & 0 deletions pkg/vpc/vpc_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package vpc

import (
"testing"

"github.com/weaveworks/eksctl/pkg/testutils"
)

func TestSuite(t *testing.T) {
testutils.RegisterAndRun(t)
}
Loading

0 comments on commit d508d86

Please sign in to comment.