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

nodePortAccess, experimental spec override flag #3336

Merged
merged 1 commit into from
Sep 8, 2017
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
30 changes: 30 additions & 0 deletions cmd/kops/create_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ type CreateClusterOptions struct {
MasterSecurityGroups []string
AssociatePublicIP *bool

// Overrides allows settings values direct in the spec
Overrides []string

// Channel is the location of the api.Channel to use for our defaults
Channel string

Expand Down Expand Up @@ -284,6 +287,10 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {

cmd.Flags().StringVar(&options.APILoadBalancerType, "api-loadbalancer-type", options.APILoadBalancerType, "Sets the API loadbalancer type to either 'public' or 'internal'")

if featureflag.SpecOverrideFlag.Enabled() {
cmd.Flags().StringSliceVar(&options.Overrides, "override", options.Overrides, "Directly configure values in the spec")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit pick. Are these overrides? Plural?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea is that you pass the flag repeatedly. So there are many overrides, but only one per flag.

}

if featureflag.VSphereCloudProvider.Enabled() {
// vSphere flags
cmd.Flags().StringVar(&options.VSphereServer, "vsphere-server", options.VSphereServer, "vsphere-server is required for vSphere. Set vCenter URL Ex: 10.192.10.30 or myvcenter.io (without https://)")
Expand Down Expand Up @@ -860,6 +867,10 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
cluster.Spec.SSHAccess = c.SSHAccess
}

if err := setOverrides(c.Overrides, cluster, instanceGroups); err != nil {
return err
}

err = cloudup.PerformAssignments(cluster)
if err != nil {
return fmt.Errorf("error populating configuration: %v", err)
Expand Down Expand Up @@ -1039,3 +1050,22 @@ func parseCloudLabels(s string) (map[string]string, error) {
}
return m, nil
}

// setOverrides sets override values in the spec
func setOverrides(overrides []string, cluster *api.Cluster, instanceGroups []*api.InstanceGroup) error {
for _, override := range overrides {
kv := strings.SplitN(override, "=", 2)
if len(kv) != 2 {
return fmt.Errorf("unhandled override: %q", override)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unhandled override

Is this actually override is in the wrong format? How about

--override must be in key=value format. For example cluster.spec.nodePortAccess=0.0.0.0/0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TBH I'm not yet sure what the overrides should look like. They could be JSON, for example.

I figured I would guard it behind a feature flag, we can start using it to get test coverage, and we can see what "feels" right.

}

// For now we have hard-code the values we want to support; we'll get test coverage and then do this properly...
switch kv[0] {
case "cluster.spec.nodePortAccess":
cluster.Spec.NodePortAccess = append(cluster.Spec.NodePortAccess, kv[1])
default:
return fmt.Errorf("unhandled override: %q", override)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably not understand this error.

How about:

Only cluster.Spec.NodePortAccess is supported, and %s was set More support is planned for the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - hopefully this won't be a permanent state of affairs - and I certainly don't intend to hard-code every field :-)

}
}
return nil
}
5 changes: 5 additions & 0 deletions cmd/kops/create_cluster_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func TestCreateClusterMinimal(t *testing.T) {
runCreateClusterIntegrationTest(t, "../../tests/integration/create_cluster/minimal", "v1alpha2")
}

// TestCreateClusterOverride tests the override flag
func TestCreateClusterOverride(t *testing.T) {
runCreateClusterIntegrationTest(t, "../../tests/integration/create_cluster/overrides", "v1alpha2")
}

// TestCreateClusterComplex runs kops create cluster, with a grab-bag of edge cases
func TestCreateClusterComplex(t *testing.T) {
runCreateClusterIntegrationTest(t, "../../tests/integration/create_cluster/complex", "v1alpha2")
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ type ClusterSpec struct {
NonMasqueradeCIDR string `json:"nonMasqueradeCIDR,omitempty"`
// SSHAccess is a list of the CIDRs that can access SSH.
SSHAccess []string `json:"sshAccess,omitempty"`
// NodePortAccess is a list of the CIDRs that can access the node ports range (30000-32767).
NodePortAccess []string `json:"nodePortAccess,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit pick, but the name does not make sense to me ... It is an API value so we need to have it named well. @blakebarnett / @alrs / @KashifSaadat thoughts? NodePortAccess sounds like a true / false. Not a list of CIDRS

NodePortSecGroupRanges?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is supposed to be a parallel to kubernetesApiAccess and sshAccess, but open to better ideas.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense.

// HTTPProxy defines connection information to support use of a private cluster behind an forward HTTP Proxy
EgressProxy *EgressProxySpec `json:"egressProxy,omitempty"`

Expand Down
3 changes: 3 additions & 0 deletions pkg/apis/kops/v1alpha1/zz_generated.conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,7 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec,
out.ServiceClusterIPRange = in.ServiceClusterIPRange
out.NonMasqueradeCIDR = in.NonMasqueradeCIDR
// WARNING: in.SSHAccess requires manual conversion: does not exist in peer-type
// WARNING: in.NodePortAccess requires manual conversion: does not exist in peer-type
if in.EgressProxy != nil {
in, out := &in.EgressProxy, &out.EgressProxy
*out = new(EgressProxySpec)
Expand Down Expand Up @@ -1169,6 +1170,7 @@ func autoConvert_v1alpha1_ExternalDNSConfig_To_kops_ExternalDNSConfig(in *Extern
return nil
}

// Convert_v1alpha1_ExternalDNSConfig_To_kops_ExternalDNSConfig is an autogenerated conversion function.
func Convert_v1alpha1_ExternalDNSConfig_To_kops_ExternalDNSConfig(in *ExternalDNSConfig, out *kops.ExternalDNSConfig, s conversion.Scope) error {
return autoConvert_v1alpha1_ExternalDNSConfig_To_kops_ExternalDNSConfig(in, out, s)
}
Expand All @@ -1178,6 +1180,7 @@ func autoConvert_kops_ExternalDNSConfig_To_v1alpha1_ExternalDNSConfig(in *kops.E
return nil
}

// Convert_kops_ExternalDNSConfig_To_v1alpha1_ExternalDNSConfig is an autogenerated conversion function.
func Convert_kops_ExternalDNSConfig_To_v1alpha1_ExternalDNSConfig(in *kops.ExternalDNSConfig, out *ExternalDNSConfig, s conversion.Scope) error {
return autoConvert_kops_ExternalDNSConfig_To_v1alpha1_ExternalDNSConfig(in, out, s)
}
Expand Down
4 changes: 3 additions & 1 deletion pkg/apis/kops/v1alpha2/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,11 @@ type ClusterSpec struct {
NonMasqueradeCIDR string `json:"nonMasqueradeCIDR,omitempty"`

// SSHAccess determines the permitted access to SSH
// Currently only a single CIDR is supported (though a richer grammar could be added in future)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It this changing in this PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I spotted that this comment was wrong and I figured it wasn't entirely unrelated ;-)

SSHAccess []string `json:"sshAccess,omitempty"`

// NodePortAccess is a list of the CIDRs that can access the node ports range (30000-32767).
NodePortAccess []string `json:"nodePortAccess,omitempty"`

// HTTPProxy defines connection information to support use of a private cluster behind an forward HTTP Proxy
EgressProxy *EgressProxySpec `json:"egressProxy,omitempty"`

Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/kops/v1alpha2/zz_generated.conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,7 @@ func autoConvert_v1alpha2_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *
out.ServiceClusterIPRange = in.ServiceClusterIPRange
out.NonMasqueradeCIDR = in.NonMasqueradeCIDR
out.SSHAccess = in.SSHAccess
out.NodePortAccess = in.NodePortAccess
if in.EgressProxy != nil {
in, out := &in.EgressProxy, &out.EgressProxy
*out = new(kops.EgressProxySpec)
Expand Down Expand Up @@ -814,6 +815,7 @@ func autoConvert_kops_ClusterSpec_To_v1alpha2_ClusterSpec(in *kops.ClusterSpec,
out.ServiceClusterIPRange = in.ServiceClusterIPRange
out.NonMasqueradeCIDR = in.NonMasqueradeCIDR
out.SSHAccess = in.SSHAccess
out.NodePortAccess = in.NodePortAccess
if in.EgressProxy != nil {
in, out := &in.EgressProxy, &out.EgressProxy
*out = new(EgressProxySpec)
Expand Down Expand Up @@ -1267,6 +1269,7 @@ func autoConvert_v1alpha2_ExternalDNSConfig_To_kops_ExternalDNSConfig(in *Extern
return nil
}

// Convert_v1alpha2_ExternalDNSConfig_To_kops_ExternalDNSConfig is an autogenerated conversion function.
func Convert_v1alpha2_ExternalDNSConfig_To_kops_ExternalDNSConfig(in *ExternalDNSConfig, out *kops.ExternalDNSConfig, s conversion.Scope) error {
return autoConvert_v1alpha2_ExternalDNSConfig_To_kops_ExternalDNSConfig(in, out, s)
}
Expand All @@ -1276,6 +1279,7 @@ func autoConvert_kops_ExternalDNSConfig_To_v1alpha2_ExternalDNSConfig(in *kops.E
return nil
}

// Convert_kops_ExternalDNSConfig_To_v1alpha2_ExternalDNSConfig is an autogenerated conversion function.
func Convert_kops_ExternalDNSConfig_To_v1alpha2_ExternalDNSConfig(in *kops.ExternalDNSConfig, out *ExternalDNSConfig, s conversion.Scope) error {
return autoConvert_kops_ExternalDNSConfig_To_v1alpha2_ExternalDNSConfig(in, out, s)
}
Expand Down
7 changes: 6 additions & 1 deletion pkg/apis/kops/validation/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ func validateClusterSpec(spec *kops.ClusterSpec, fieldPath *field.Path) field.Er
allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Child("sshAccess").Index(i))...)
}

// AdminAccess
// KubernetesAPIAccess
for i, cidr := range spec.KubernetesAPIAccess {
allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Child("kubernetesAPIAccess").Index(i))...)
}

// NodePortAccess
for i, cidr := range spec.NodePortAccess {
allErrs = append(allErrs, validateCIDR(cidr, fieldPath.Child("nodePortAccess").Index(i))...)
}

for i := range spec.Hooks {
allErrs = append(allErrs, validateHookSpec(&spec.Hooks[i], fieldPath.Child("hooks").Index(i))...)
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/featureflag/featureflag.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ var VSphereCloudProvider = New("VSphereCloudProvider", Bool(false))

var EnableExternalDNS = New("EnableExternalDNS", Bool(false))

// SpecOverrideFlag allows setting spec values on create
var SpecOverrideFlag = New("SpecOverrideFlag", Bool(false))

var flags = make(map[string]*FeatureFlag)
var flagsMutex sync.Mutex

Expand Down
21 changes: 21 additions & 0 deletions pkg/model/external_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,27 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.ModelBuilderContext) error {
}
}

for _, nodePortAccess := range b.Cluster.Spec.NodePortAccess {
c.AddTask(&awstasks.SecurityGroupRule{
Name: s("nodeport-tcp-external-to-node-" + nodePortAccess),
Lifecycle: b.Lifecycle,
SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
Protocol: s("tcp"),
FromPort: i64(30000),
ToPort: i64(32767),
CIDR: s(nodePortAccess),
})
c.AddTask(&awstasks.SecurityGroupRule{
Name: s("nodeport-udp-external-to-node-" + nodePortAccess),
Lifecycle: b.Lifecycle,
SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
Protocol: s("udp"),
FromPort: i64(30000),
ToPort: i64(32767),
CIDR: s(nodePortAccess),
})
}

if !b.UseLoadBalancerForAPI() {
// Configuration for the master, when not using a Loadbalancer (ELB)
// We expect that either the IP address is published, or DNS is set up to point to the IPs
Expand Down
9 changes: 9 additions & 0 deletions pkg/model/gcemodel/external_access.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.ModelBuilderContext) error {
})
}

c.AddTask(&gcetasks.FirewallRule{
Name: s(b.SafeObjectName("nodeport-external-to-node")),
Lifecycle: b.Lifecycle,
TargetTags: []string{b.GCETagForRole(kops.InstanceGroupRoleNode)},
Allowed: []string{"tcp:30000-32767,udp:30000-32767"},
SourceRanges: b.Cluster.Spec.NodePortAccess,
Network: b.LinkToNetwork(),
})

if !b.UseLoadBalancerForAPI() {
// Configuration for the master, when not using a Loadbalancer (ELB)
// We expect that either the IP address is published, or DNS is set up to point to the IPs
Expand Down
5 changes: 4 additions & 1 deletion tests/integration/complex/in-v1alpha2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ spec:
networkCIDR: 172.20.0.0/16
networking:
kubenet: {}
nodePortAccess:
- 1.2.3.4/32
- 10.20.30.0/24
nonMasqueradeCIDR: 100.64.0.0/10
sshAccess:
- 0.0.0.0/0
- 0.0.0.0/0
topology:
masters: public
nodes: public
Expand Down
36 changes: 36 additions & 0 deletions tests/integration/complex/kubernetes.tf
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,42 @@ resource "aws_security_group_rule" "node-to-master-udp-1-65535" {
protocol = "udp"
}

resource "aws_security_group_rule" "nodeport-tcp-external-to-node-1-2-3-4--32" {
type = "ingress"
security_group_id = "${aws_security_group.nodes-complex-example-com.id}"
from_port = 30000
to_port = 32767
protocol = "tcp"
cidr_blocks = ["1.2.3.4/32"]
}

resource "aws_security_group_rule" "nodeport-tcp-external-to-node-10-20-30-0--24" {
type = "ingress"
security_group_id = "${aws_security_group.nodes-complex-example-com.id}"
from_port = 30000
to_port = 32767
protocol = "tcp"
cidr_blocks = ["10.20.30.0/24"]
}

resource "aws_security_group_rule" "nodeport-udp-external-to-node-1-2-3-4--32" {
type = "ingress"
security_group_id = "${aws_security_group.nodes-complex-example-com.id}"
from_port = 30000
to_port = 32767
protocol = "udp"
cidr_blocks = ["1.2.3.4/32"]
}

resource "aws_security_group_rule" "nodeport-udp-external-to-node-10-20-30-0--24" {
type = "ingress"
security_group_id = "${aws_security_group.nodes-complex-example-com.id}"
from_port = 30000
to_port = 32767
protocol = "udp"
cidr_blocks = ["10.20.30.0/24"]
}

resource "aws_security_group_rule" "ssh-external-to-master-0-0-0-0--0" {
type = "ingress"
security_group_id = "${aws_security_group.masters-complex-example-com.id}"
Expand Down
83 changes: 83 additions & 0 deletions tests/integration/create_cluster/overrides/expected-v1alpha2.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
apiVersion: kops/v1alpha2
kind: Cluster
metadata:
creationTimestamp: 2017-01-01T00:00:00Z
name: overrides.example.com
spec:
api:
dns: {}
authorization:
alwaysAllow: {}
channel: stable
cloudProvider: aws
configBase: memfs://tests/overrides.example.com
etcdClusters:
- etcdMembers:
- instanceGroup: master-us-test-1a
name: a
name: main
- etcdMembers:
- instanceGroup: master-us-test-1a
name: a
name: events
iam:
legacy: false
kubernetesApiAccess:
- 0.0.0.0/0
kubernetesVersion: v1.7.5
masterPublicName: api.overrides.example.com
networkCIDR: 172.20.0.0/16
networking:
kubenet: {}
nodePortAccess:
- 1.2.3.4/32
- 10.20.30.0/24
nonMasqueradeCIDR: 100.64.0.0/10
sshAccess:
- 0.0.0.0/0
subnets:
- cidr: 172.20.32.0/19
name: us-test-1a
type: Public
zone: us-test-1a
topology:
dns:
type: Public
masters: public
nodes: public

---

apiVersion: kops/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: 2017-01-01T00:00:00Z
labels:
kops.k8s.io/cluster: overrides.example.com
name: master-us-test-1a
spec:
image: kope.io/k8s-1.7-debian-jessie-amd64-hvm-ebs-2017-07-28
machineType: m3.medium
maxSize: 1
minSize: 1
role: Master
subnets:
- us-test-1a

---

apiVersion: kops/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: 2017-01-01T00:00:00Z
labels:
kops.k8s.io/cluster: overrides.example.com
name: nodes
spec:
image: kope.io/k8s-1.7-debian-jessie-amd64-hvm-ebs-2017-07-28
machineType: t2.medium
maxSize: 2
minSize: 2
role: Node
subnets:
- us-test-1a
8 changes: 8 additions & 0 deletions tests/integration/create_cluster/overrides/options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ClusterName: overrides.example.com
Zones:
- us-test-1a
Cloud: aws
KubernetesVersion: v1.7.5
Overrides:
- cluster.spec.nodePortAccess=1.2.3.4/32
- cluster.spec.nodePortAccess=10.20.30.0/24
2 changes: 1 addition & 1 deletion upup/pkg/fi/dryrun_target.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func getTaskName(t Task) string {
return s
}

// asString returns a human-readable string representation of the passed value
// ValueAsString returns a human-readable string representation of the passed value
func ValueAsString(value reflect.Value) string {
b := &bytes.Buffer{}

Expand Down