diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index c85d9179d9..177dda7773 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -80650,7 +80650,7 @@
"type": "string"
},
"protocol": {
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\".",
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
"type": "string"
}
}
@@ -80897,7 +80897,7 @@
"format": "int32"
},
"protocol": {
- "description": "The IP protocol for this port. Must be UDP or TCP. Default is TCP.",
+ "description": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.",
"type": "string"
}
}
@@ -84386,7 +84386,7 @@
"format": "int32"
},
"protocol": {
- "description": "The IP protocol for this port. Supports \"TCP\" and \"UDP\". Default is TCP.",
+ "description": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.",
"type": "string"
},
"targetPort": {
@@ -85882,7 +85882,7 @@
"$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString"
},
"protocol": {
- "description": "Optional. The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.",
+ "description": "Optional. The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.",
"type": "string"
}
}
@@ -86553,7 +86553,7 @@
"$ref": "#/definitions/io.k8s.apimachinery.pkg.util.intstr.IntOrString"
},
"protocol": {
- "description": "The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.",
+ "description": "The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.",
"type": "string"
}
}
diff --git a/api/swagger-spec/apps_v1.json b/api/swagger-spec/apps_v1.json
index 4f3699ef7b..802dbf0e9e 100644
--- a/api/swagger-spec/apps_v1.json
+++ b/api/swagger-spec/apps_v1.json
@@ -8018,7 +8018,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/apps_v1beta1.json b/api/swagger-spec/apps_v1beta1.json
index 60f03d4233..8ab8e0fb47 100644
--- a/api/swagger-spec/apps_v1beta1.json
+++ b/api/swagger-spec/apps_v1beta1.json
@@ -5626,7 +5626,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/apps_v1beta2.json b/api/swagger-spec/apps_v1beta2.json
index 963cda983e..4ab9729312 100644
--- a/api/swagger-spec/apps_v1beta2.json
+++ b/api/swagger-spec/apps_v1beta2.json
@@ -8018,7 +8018,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/batch_v1.json b/api/swagger-spec/batch_v1.json
index 3ad07b21fc..f431fe6d0e 100644
--- a/api/swagger-spec/batch_v1.json
+++ b/api/swagger-spec/batch_v1.json
@@ -2933,7 +2933,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/batch_v1beta1.json b/api/swagger-spec/batch_v1beta1.json
index 35d5615134..396bdf3505 100644
--- a/api/swagger-spec/batch_v1beta1.json
+++ b/api/swagger-spec/batch_v1beta1.json
@@ -2988,7 +2988,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/batch_v2alpha1.json b/api/swagger-spec/batch_v2alpha1.json
index 7457726565..2859b0b3e3 100644
--- a/api/swagger-spec/batch_v2alpha1.json
+++ b/api/swagger-spec/batch_v2alpha1.json
@@ -2988,7 +2988,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
diff --git a/api/swagger-spec/extensions_v1beta1.json b/api/swagger-spec/extensions_v1beta1.json
index dc263d21de..0497ac5382 100644
--- a/api/swagger-spec/extensions_v1beta1.json
+++ b/api/swagger-spec/extensions_v1beta1.json
@@ -8666,7 +8666,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
@@ -10280,7 +10280,7 @@
"properties": {
"protocol": {
"$ref": "v1.Protocol",
- "description": "Optional. The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP."
+ "description": "Optional. The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP."
},
"port": {
"type": "string",
diff --git a/api/swagger-spec/networking.k8s.io_v1.json b/api/swagger-spec/networking.k8s.io_v1.json
index be43b96b7c..65f326637f 100644
--- a/api/swagger-spec/networking.k8s.io_v1.json
+++ b/api/swagger-spec/networking.k8s.io_v1.json
@@ -1426,7 +1426,7 @@
"properties": {
"protocol": {
"$ref": "v1.Protocol",
- "description": "The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP."
+ "description": "The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP."
},
"port": {
"type": "string",
diff --git a/api/swagger-spec/v1.json b/api/swagger-spec/v1.json
index 4873b0841a..d3bf5da50a 100644
--- a/api/swagger-spec/v1.json
+++ b/api/swagger-spec/v1.json
@@ -18480,7 +18480,7 @@
},
"protocol": {
"type": "string",
- "description": "The IP protocol for this port. Must be UDP or TCP. Default is TCP."
+ "description": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP."
}
}
},
@@ -21466,7 +21466,7 @@
},
"protocol": {
"type": "string",
- "description": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\"."
+ "description": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\"."
},
"hostIP": {
"type": "string",
@@ -23091,7 +23091,7 @@
},
"protocol": {
"type": "string",
- "description": "The IP protocol for this port. Supports \"TCP\" and \"UDP\". Default is TCP."
+ "description": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP."
},
"port": {
"type": "integer",
diff --git a/cluster/gce/gci/configure-helper.sh b/cluster/gce/gci/configure-helper.sh
index dc00dcafb6..b6b5205c12 100644
--- a/cluster/gce/gci/configure-helper.sh
+++ b/cluster/gce/gci/configure-helper.sh
@@ -51,18 +51,20 @@ function config-ip-firewall {
sysctl -w net.ipv4.conf.all.route_localnet=1
# The GCI image has host firewall which drop most inbound/forwarded packets.
- # We need to add rules to accept all TCP/UDP/ICMP packets.
+ # We need to add rules to accept all TCP/UDP/ICMP/SCTP packets.
if iptables -w -L INPUT | grep "Chain INPUT (policy DROP)" > /dev/null; then
echo "Add rules to accept all inbound TCP/UDP/ICMP packets"
iptables -A INPUT -w -p TCP -j ACCEPT
iptables -A INPUT -w -p UDP -j ACCEPT
iptables -A INPUT -w -p ICMP -j ACCEPT
+ iptables -A INPUT -w -p SCTP -j ACCEPT
fi
if iptables -w -L FORWARD | grep "Chain FORWARD (policy DROP)" > /dev/null; then
- echo "Add rules to accept all forwarded TCP/UDP/ICMP packets"
+ echo "Add rules to accept all forwarded TCP/UDP/ICMP/SCTP packets"
iptables -A FORWARD -w -p TCP -j ACCEPT
iptables -A FORWARD -w -p UDP -j ACCEPT
iptables -A FORWARD -w -p ICMP -j ACCEPT
+ iptables -A FORWARD -w -p SCTP -j ACCEPT
fi
# Flush iptables nat table
diff --git a/cmd/kube-proxy/app/server.go b/cmd/kube-proxy/app/server.go
index 2ecc8a2b14..c403b52a9d 100644
--- a/cmd/kube-proxy/app/server.go
+++ b/cmd/kube-proxy/app/server.go
@@ -339,7 +339,7 @@ func NewProxyCommand() *cobra.Command {
Use: "kube-proxy",
Long: `The Kubernetes network proxy runs on each node. This
reflects services as defined in the Kubernetes API on each node and can do simple
-TCP and UDP stream forwarding or round robin TCP and UDP forwarding across a set of backends.
+TCP, UDP, and SCTP stream forwarding or round robin TCP, UDP, and SCTP forwarding across a set of backends.
Service cluster IPs and ports are currently found through Docker-links-compatible
environment variables specifying ports opened by the service proxy. There is an optional
addon that provides cluster DNS for these cluster IPs. The user must create a service
diff --git a/docs/api-reference/apps/v1/definitions.html b/docs/api-reference/apps/v1/definitions.html
index 7f8660199a..be4d20f851 100755
--- a/docs/api-reference/apps/v1/definitions.html
+++ b/docs/api-reference/apps/v1/definitions.html
@@ -4085,7 +4085,7 @@ When an object is created, the system will populate this list with the current s
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/apps/v1beta1/definitions.html b/docs/api-reference/apps/v1beta1/definitions.html
index aa421060d4..e05a98e520 100755
--- a/docs/api-reference/apps/v1beta1/definitions.html
+++ b/docs/api-reference/apps/v1beta1/definitions.html
@@ -4138,7 +4138,7 @@ The StatefulSet guarantees that a given network identity will always map to the
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/apps/v1beta2/definitions.html b/docs/api-reference/apps/v1beta2/definitions.html
index a38472bd9c..0e2dbd5472 100755
--- a/docs/api-reference/apps/v1beta2/definitions.html
+++ b/docs/api-reference/apps/v1beta2/definitions.html
@@ -4754,7 +4754,7 @@ The StatefulSet guarantees that a given network identity will always map to the
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/batch/v1/definitions.html b/docs/api-reference/batch/v1/definitions.html
index ba884857ef..607e8a2cee 100755
--- a/docs/api-reference/batch/v1/definitions.html
+++ b/docs/api-reference/batch/v1/definitions.html
@@ -3418,7 +3418,7 @@ When an object is created, the system will populate this list with the current s
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/batch/v1beta1/definitions.html b/docs/api-reference/batch/v1beta1/definitions.html
index b597490ce5..7769cfc01b 100755
--- a/docs/api-reference/batch/v1beta1/definitions.html
+++ b/docs/api-reference/batch/v1beta1/definitions.html
@@ -3452,7 +3452,7 @@ When an object is created, the system will populate this list with the current s
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/batch/v2alpha1/definitions.html b/docs/api-reference/batch/v2alpha1/definitions.html
index 14e34837f9..c2332d3f0f 100755
--- a/docs/api-reference/batch/v2alpha1/definitions.html
+++ b/docs/api-reference/batch/v2alpha1/definitions.html
@@ -3425,7 +3425,7 @@ When an object is created, the system will populate this list with the current s
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
diff --git a/docs/api-reference/extensions/v1beta1/definitions.html b/docs/api-reference/extensions/v1beta1/definitions.html
index 252677d9bf..26b93b3388 100755
--- a/docs/api-reference/extensions/v1beta1/definitions.html
+++ b/docs/api-reference/extensions/v1beta1/definitions.html
@@ -4784,7 +4784,7 @@ When an object is created, the system will populate this list with the current s
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
@@ -8058,7 +8058,7 @@ If PodSelector is also set, then the NetworkPolicyPeer as a whole selects the Po
protocol |
-Optional. The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP. |
+Optional. The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP. |
false |
v1.Protocol |
|
diff --git a/docs/api-reference/networking.k8s.io/v1/definitions.html b/docs/api-reference/networking.k8s.io/v1/definitions.html
index c8a84f0b38..f01c8b0cb8 100755
--- a/docs/api-reference/networking.k8s.io/v1/definitions.html
+++ b/docs/api-reference/networking.k8s.io/v1/definitions.html
@@ -676,7 +676,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
protocol |
-The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP. |
+The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP. |
false |
v1.Protocol |
|
diff --git a/docs/api-reference/v1/definitions.html b/docs/api-reference/v1/definitions.html
index 800d0f0202..c47c47d521 100755
--- a/docs/api-reference/v1/definitions.html
+++ b/docs/api-reference/v1/definitions.html
@@ -2780,7 +2780,7 @@ The resulting set of endpoints can be viewed as:
protocol |
-Protocol for port. Must be UDP or TCP. Defaults to "TCP". |
+Protocol for port. Must be UDP, TCP, or SCTP. Defaults to "TCP". |
false |
string |
|
@@ -3770,7 +3770,7 @@ Examples:
protocol |
-The IP protocol for this port. Must be UDP or TCP. Default is TCP. |
+The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP. |
false |
string |
|
@@ -4405,7 +4405,7 @@ Examples:
protocol |
-The IP protocol for this port. Supports "TCP" and "UDP". Default is TCP. |
+The IP protocol for this port. Supports "TCP", "UDP", and "SCTP". Default is TCP. |
false |
string |
|
diff --git a/pkg/apis/core/fuzzer/fuzzer.go b/pkg/apis/core/fuzzer/fuzzer.go
index dede9ceb69..7a2c9b7daf 100644
--- a/pkg/apis/core/fuzzer/fuzzer.go
+++ b/pkg/apis/core/fuzzer/fuzzer.go
@@ -263,7 +263,7 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
*d = policies[c.Rand.Intn(len(policies))]
},
func(p *core.Protocol, c fuzz.Continue) {
- protocols := []core.Protocol{core.ProtocolTCP, core.ProtocolUDP}
+ protocols := []core.Protocol{core.ProtocolTCP, core.ProtocolUDP, core.ProtocolSCTP}
*p = protocols[c.Rand.Intn(len(protocols))]
},
func(p *core.ServiceAffinity, c fuzz.Continue) {
diff --git a/pkg/apis/core/types.go b/pkg/apis/core/types.go
index 15d7746e6d..0dddca02f3 100644
--- a/pkg/apis/core/types.go
+++ b/pkg/apis/core/types.go
@@ -564,6 +564,8 @@ const (
ProtocolTCP Protocol = "TCP"
// ProtocolUDP is the UDP protocol.
ProtocolUDP Protocol = "UDP"
+ // ProtocolSCTP is the SCTP protocol.
+ ProtocolSCTP Protocol = "SCTP"
)
// Represents a Persistent Disk resource in Google Compute Engine.
@@ -1570,7 +1572,7 @@ type ContainerPort struct {
HostPort int32
// Required: This must be a valid port number, 0 < x < 65536.
ContainerPort int32
- // Required: Supports "TCP" and "UDP".
+ // Required: Supports "TCP", "UDP" and "SCTP"
// +optional
Protocol Protocol
// Optional: What host IP to bind the external port to.
@@ -3175,7 +3177,7 @@ type ServicePort struct {
// the 'Name' field in EndpointPort objects.
Name string
- // The IP protocol for this port. Supports "TCP" and "UDP".
+ // The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
Protocol Protocol
// The port that will be exposed on the service.
diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go
index d1af9e9fab..cf5584dd9d 100644
--- a/pkg/apis/core/validation/validation.go
+++ b/pkg/apis/core/validation/validation.go
@@ -1918,7 +1918,7 @@ func ValidatePersistentVolumeClaimStatusUpdate(newPvc, oldPvc *core.PersistentVo
return allErrs
}
-var supportedPortProtocols = sets.NewString(string(core.ProtocolTCP), string(core.ProtocolUDP))
+var supportedPortProtocols = sets.NewString(string(core.ProtocolTCP), string(core.ProtocolUDP), string(core.ProtocolSCTP))
func validateContainerPorts(ports []core.ContainerPort, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@@ -1951,6 +1951,8 @@ func validateContainerPorts(ports []core.ContainerPort, fldPath *field.Path) fie
}
if len(port.Protocol) == 0 {
allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), ""))
+ } else if !utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) && port.Protocol == core.ProtocolSCTP {
+ allErrs = append(allErrs, field.NotSupported(idxPath.Child("protocol"), port.Protocol, []string{string(core.ProtocolTCP), string(core.ProtocolUDP)}))
} else if !supportedPortProtocols.Has(string(port.Protocol)) {
allErrs = append(allErrs, field.NotSupported(idxPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
}
@@ -3751,8 +3753,10 @@ func ValidateService(service *core.Service) field.ErrorList {
includeProtocols := sets.NewString()
for i := range service.Spec.Ports {
portPath := portsPath.Index(i)
- if !supportedPortProtocols.Has(string(service.Spec.Ports[i].Protocol)) {
- allErrs = append(allErrs, field.Invalid(portPath.Child("protocol"), service.Spec.Ports[i].Protocol, "cannot create an external load balancer with non-TCP/UDP ports"))
+ if !utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) && service.Spec.Ports[i].Protocol == core.ProtocolSCTP {
+ allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), service.Spec.Ports[i].Protocol, []string{string(core.ProtocolTCP), string(core.ProtocolUDP)}))
+ } else if !supportedPortProtocols.Has(string(service.Spec.Ports[i].Protocol)) {
+ allErrs = append(allErrs, field.Invalid(portPath.Child("protocol"), service.Spec.Ports[i].Protocol, "cannot create an external load balancer with non-TCP/UDP/SCTP ports"))
} else {
includeProtocols.Insert(string(service.Spec.Ports[i].Protocol))
}
@@ -3850,6 +3854,8 @@ func validateServicePort(sp *core.ServicePort, requireName, isHeadlessService bo
if len(sp.Protocol) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
+ } else if !utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) && sp.Protocol == core.ProtocolSCTP {
+ allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, []string{string(core.ProtocolTCP), string(core.ProtocolUDP)}))
} else if !supportedPortProtocols.Has(string(sp.Protocol)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, supportedPortProtocols.List()))
}
@@ -5219,6 +5225,8 @@ func validateEndpointPort(port *core.EndpointPort, requireName bool, fldPath *fi
}
if len(port.Protocol) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
+ } else if !utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) && port.Protocol == core.ProtocolSCTP {
+ allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, []string{string(core.ProtocolTCP), string(core.ProtocolUDP)}))
} else if !supportedPortProtocols.Has(string(port.Protocol)) {
allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), port.Protocol, supportedPortProtocols.List()))
}
diff --git a/pkg/apis/core/validation/validation_test.go b/pkg/apis/core/validation/validation_test.go
index 91c1c2a624..4456729349 100644
--- a/pkg/apis/core/validation/validation_test.go
+++ b/pkg/apis/core/validation/validation_test.go
@@ -4060,6 +4060,7 @@ func TestValidateResourceQuotaWithAlphaLocalStorageCapacityIsolation(t *testing.
}
func TestValidatePorts(t *testing.T) {
+ defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
successCase := []core.ContainerPort{
{Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
{Name: "easy", ContainerPort: 82, Protocol: "TCP"},
@@ -4125,12 +4126,12 @@ func TestValidatePorts(t *testing.T) {
"invalid protocol case": {
[]core.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
field.ErrorTypeNotSupported,
- "protocol", `supported values: "TCP", "UDP"`,
+ "protocol", `supported values: "SCTP", "TCP", "UDP"`,
},
"invalid protocol": {
[]core.ContainerPort{{ContainerPort: 80, Protocol: "ICMP"}},
field.ErrorTypeNotSupported,
- "protocol", `supported values: "TCP", "UDP"`,
+ "protocol", `supported values: "SCTP", "TCP", "UDP"`,
},
"protocol required": {
[]core.ContainerPort{{Name: "abc", ContainerPort: 80}},
@@ -8590,6 +8591,8 @@ func makeValidService() core.Service {
}
func TestValidateService(t *testing.T) {
+ defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
+
testCases := []struct {
name string
tweakSvc func(svc *core.Service) // given a basic valid service, each test case can customize it
@@ -8947,6 +8950,7 @@ func TestValidateService(t *testing.T) {
s.Spec.Type = core.ServiceTypeNodePort
s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 1, Protocol: "TCP", NodePort: 1, TargetPort: intstr.FromInt(1)})
s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 2, Protocol: "UDP", NodePort: 1, TargetPort: intstr.FromInt(2)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 3, Protocol: "SCTP", NodePort: 1, TargetPort: intstr.FromInt(3)})
},
numErrs: 0,
},
@@ -8965,6 +8969,7 @@ func TestValidateService(t *testing.T) {
s.Spec.Type = core.ServiceTypeClusterIP
s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "q", Port: 12345, Protocol: "TCP", TargetPort: intstr.FromInt(8080)})
s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "r", Port: 12345, Protocol: "UDP", TargetPort: intstr.FromInt(80)})
+ s.Spec.Ports = append(s.Spec.Ports, core.ServicePort{Name: "s", Port: 12345, Protocol: "SCTP", TargetPort: intstr.FromInt(8088)})
},
numErrs: 0,
},
diff --git a/pkg/apis/networking/types.go b/pkg/apis/networking/types.go
index b92e03295b..541b884b58 100644
--- a/pkg/apis/networking/types.go
+++ b/pkg/apis/networking/types.go
@@ -134,7 +134,7 @@ type NetworkPolicyEgressRule struct {
// NetworkPolicyPort describes a port to allow traffic on
type NetworkPolicyPort struct {
- // The protocol (TCP or UDP) which traffic must match. If not specified, this
+ // The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this
// field defaults to TCP.
// +optional
Protocol *api.Protocol
diff --git a/pkg/apis/networking/validation/BUILD b/pkg/apis/networking/validation/BUILD
index c21e356632..42021b70ac 100644
--- a/pkg/apis/networking/validation/BUILD
+++ b/pkg/apis/networking/validation/BUILD
@@ -13,8 +13,11 @@ go_test(
deps = [
"//pkg/apis/core:go_default_library",
"//pkg/apis/networking:go_default_library",
+ "//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
+ "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
+ "//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
],
)
@@ -26,12 +29,14 @@ go_library(
"//pkg/apis/core:go_default_library",
"//pkg/apis/core/validation:go_default_library",
"//pkg/apis/networking:go_default_library",
+ "//pkg/features:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
+ "//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
],
)
diff --git a/pkg/apis/networking/validation/validation.go b/pkg/apis/networking/validation/validation.go
index c64d085d60..0e012f3664 100644
--- a/pkg/apis/networking/validation/validation.go
+++ b/pkg/apis/networking/validation/validation.go
@@ -23,9 +23,11 @@ import (
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
api "k8s.io/kubernetes/pkg/apis/core"
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/networking"
+ "k8s.io/kubernetes/pkg/features"
)
// ValidateNetworkPolicyName can be used to check whether the given networkpolicy
@@ -37,8 +39,11 @@ func ValidateNetworkPolicyName(name string, prefix bool) []string {
// ValidateNetworkPolicyPort validates a NetworkPolicyPort
func ValidateNetworkPolicyPort(port *networking.NetworkPolicyPort, portPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
-
- if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP {
+ if utilfeature.DefaultFeatureGate.Enabled(features.SCTPSupport) {
+ if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP && *port.Protocol != api.ProtocolSCTP {
+ allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP), string(api.ProtocolSCTP)}))
+ }
+ } else if port.Protocol != nil && *port.Protocol != api.ProtocolTCP && *port.Protocol != api.ProtocolUDP {
allErrs = append(allErrs, field.NotSupported(portPath.Child("protocol"), *port.Protocol, []string{string(api.ProtocolTCP), string(api.ProtocolUDP)}))
}
if port.Port != nil {
diff --git a/pkg/apis/networking/validation/validation_test.go b/pkg/apis/networking/validation/validation_test.go
index 67ebc24f91..bacffbfa38 100644
--- a/pkg/apis/networking/validation/validation_test.go
+++ b/pkg/apis/networking/validation/validation_test.go
@@ -21,14 +21,20 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
+ utilfeature "k8s.io/apiserver/pkg/util/feature"
+ utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/networking"
+ "k8s.io/kubernetes/pkg/features"
)
func TestValidateNetworkPolicy(t *testing.T) {
protocolTCP := api.ProtocolTCP
protocolUDP := api.ProtocolUDP
protocolICMP := api.Protocol("ICMP")
+ protocolSCTP := api.ProtocolSCTP
+
+ defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SCTPSupport, true)()
successCases := []networking.NetworkPolicy{
{
@@ -79,6 +85,10 @@ func TestValidateNetworkPolicy(t *testing.T) {
Protocol: &protocolUDP,
Port: &intstr.IntOrString{Type: intstr.String, StrVal: "dns"},
},
+ {
+ Protocol: &protocolSCTP,
+ Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 7777},
+ },
},
},
},
@@ -262,6 +272,10 @@ func TestValidateNetworkPolicy(t *testing.T) {
Protocol: &protocolUDP,
Port: &intstr.IntOrString{Type: intstr.String, StrVal: "dns"},
},
+ {
+ Protocol: &protocolSCTP,
+ Port: &intstr.IntOrString{Type: intstr.Int, IntVal: 7777},
+ },
},
},
},
@@ -270,6 +284,7 @@ func TestValidateNetworkPolicy(t *testing.T) {
}
// Success cases are expected to pass validation.
+
for k, v := range successCases {
if errs := ValidateNetworkPolicy(&v); len(errs) != 0 {
t.Errorf("Expected success for %d, got %v", k, errs)
diff --git a/pkg/cloudprovider/providers/azure/azure_loadbalancer.go b/pkg/cloudprovider/providers/azure/azure_loadbalancer.go
index ffe66a32bc..904c8c3067 100644
--- a/pkg/cloudprovider/providers/azure/azure_loadbalancer.go
+++ b/pkg/cloudprovider/providers/azure/azure_loadbalancer.go
@@ -737,6 +737,13 @@ func (az *Cloud) reconcileLoadBalancer(clusterName string, service *v1.Service,
return nil, fmt.Errorf("services requiring health checks are incompatible with UDP ports")
}
+ if port.Protocol == v1.ProtocolSCTP {
+ // ERROR: this isn't supported
+ // health check (aka source ip preservation) is not
+ // compatible with SCTP (it uses an HTTP check)
+ return nil, fmt.Errorf("services requiring health checks are incompatible with SCTP ports")
+ }
+
podPresencePath, podPresencePort := serviceapi.GetServiceHealthCheckPathPort(service)
expectedProbes = append(expectedProbes, network.Probe{
@@ -749,7 +756,7 @@ func (az *Cloud) reconcileLoadBalancer(clusterName string, service *v1.Service,
NumberOfProbes: to.Int32Ptr(2),
},
})
- } else if port.Protocol != v1.ProtocolUDP {
+ } else if port.Protocol != v1.ProtocolUDP && port.Protocol != v1.ProtocolSCTP {
// we only add the expected probe if we're doing TCP
expectedProbes = append(expectedProbes, network.Probe{
Name: &lbRuleName,
@@ -787,8 +794,8 @@ func (az *Cloud) reconcileLoadBalancer(clusterName string, service *v1.Service,
expectedRule.LoadBalancingRulePropertiesFormat.IdleTimeoutInMinutes = lbIdleTimeout
}
- // we didn't construct the probe objects for UDP because they're not used/needed/allowed
- if port.Protocol != v1.ProtocolUDP {
+ // we didn't construct the probe objects for UDP or SCTP because they're not used/needed/allowed
+ if port.Protocol != v1.ProtocolUDP && port.Protocol != v1.ProtocolSCTP {
expectedRule.Probe = &network.SubResource{
ID: to.StringPtr(az.getLoadBalancerProbeID(lbName, lbRuleName)),
}
diff --git a/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go b/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go
index e4401b21ab..4542fa1232 100644
--- a/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go
+++ b/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go
@@ -38,6 +38,9 @@ const (
func (gce *GCECloud) ensureInternalLoadBalancer(clusterName, clusterID string, svc *v1.Service, existingFwdRule *compute.ForwardingRule, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) {
nm := types.NamespacedName{Name: svc.Name, Namespace: svc.Namespace}
ports, protocol := getPortsAndProtocol(svc.Spec.Ports)
+ if protocol != v1.ProtocolTCP && protocol != v1.ProtocolUDP {
+ return nil, fmt.Errorf("Invalid protocol %s, only TCP and UDP are supported", string(protocol))
+ }
scheme := cloud.SchemeInternal
loadBalancerName := gce.GetLoadBalancerName(context.TODO(), clusterName, svc)
sharedBackend := shareBackendService(svc)
diff --git a/pkg/cloudprovider/providers/gce/gce_util_test.go b/pkg/cloudprovider/providers/gce/gce_util_test.go
index c2d1dda1f8..bd21e26f6b 100644
--- a/pkg/cloudprovider/providers/gce/gce_util_test.go
+++ b/pkg/cloudprovider/providers/gce/gce_util_test.go
@@ -103,11 +103,15 @@ func TestFirewallToGcloudArgs(t *testing.T) {
IPProtocol: "tcp",
Ports: []string{"321", "123-456", "123"},
},
+ {
+ IPProtocol: "sctp",
+ Ports: []string{"321", "123-456", "123"},
+ },
},
}
got := firewallToGcloudArgs(&firewall, "my-project")
- var e = `--description "Last Line of Defense" --allow tcp:123,tcp:123-456,tcp:321,udp:123,udp:123-456,udp:321 --source-ranges 1.1.1.1/20,2.2.2.2/20,3.3.3.3/20 --target-tags band-nodes,jock-nodes --project my-project`
+ var e = `--description "Last Line of Defense" --allow sctp:123,sctp:123-456,sctp:321,tcp:123,tcp:123-456,tcp:321,udp:123,udp:123-456,udp:321 --source-ranges 1.1.1.1/20,2.2.2.2/20,3.3.3.3/20 --target-tags band-nodes,jock-nodes --project my-project`
if got != e {
t.Errorf("%q does not equal %q", got, e)
}
diff --git a/pkg/controller/endpoint/endpoints_controller_test.go b/pkg/controller/endpoint/endpoints_controller_test.go
index 24446913b8..3625d233b2 100644
--- a/pkg/controller/endpoint/endpoints_controller_test.go
+++ b/pkg/controller/endpoint/endpoints_controller_test.go
@@ -344,6 +344,47 @@ func TestSyncEndpointsProtocolUDP(t *testing.T) {
endpointsHandler.ValidateRequest(t, testapi.Default.ResourcePath("endpoints", ns, "foo"), "PUT", &data)
}
+func TestSyncEndpointsProtocolSCTP(t *testing.T) {
+ ns := "other"
+ testServer, endpointsHandler := makeTestServer(t, ns)
+ defer testServer.Close()
+ endpoints := newController(testServer.URL)
+ endpoints.endpointsStore.Add(&v1.Endpoints{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ Namespace: ns,
+ ResourceVersion: "1",
+ },
+ Subsets: []v1.EndpointSubset{{
+ Addresses: []v1.EndpointAddress{{IP: "6.7.8.9", NodeName: &emptyNodeName}},
+ Ports: []v1.EndpointPort{{Port: 1000, Protocol: "SCTP"}},
+ }},
+ })
+ addPods(endpoints.podStore, ns, 1, 1, 0)
+ endpoints.serviceStore.Add(&v1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: ns},
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{},
+ Ports: []v1.ServicePort{{Port: 80, TargetPort: intstr.FromInt(8080), Protocol: "SCTP"}},
+ },
+ })
+ endpoints.syncService(ns + "/foo")
+
+ endpointsHandler.ValidateRequestCount(t, 1)
+ data := runtime.EncodeOrDie(testapi.Default.Codec(), &v1.Endpoints{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ Namespace: ns,
+ ResourceVersion: "1",
+ },
+ Subsets: []v1.EndpointSubset{{
+ Addresses: []v1.EndpointAddress{{IP: "1.2.3.4", NodeName: &emptyNodeName, TargetRef: &v1.ObjectReference{Kind: "Pod", Name: "pod0", Namespace: ns}}},
+ Ports: []v1.EndpointPort{{Port: 8080, Protocol: "SCTP"}},
+ }},
+ })
+ endpointsHandler.ValidateRequest(t, testapi.Default.ResourcePath("endpoints", ns, "foo"), "PUT", &data)
+}
+
func TestSyncEndpointsItemsEmptySelectorSelectsAll(t *testing.T) {
ns := "other"
testServer, endpointsHandler := makeTestServer(t, ns)
diff --git a/pkg/controller/service/service_controller_test.go b/pkg/controller/service/service_controller_test.go
index 266929c281..962fe62b1b 100644
--- a/pkg/controller/service/service_controller_test.go
+++ b/pkg/controller/service/service_controller_test.go
@@ -124,6 +124,24 @@ func TestCreateExternalLoadBalancer(t *testing.T) {
expectErr: false,
expectCreateAttempt: true,
},
+ {
+ service: &v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "sctp-service",
+ Namespace: "default",
+ SelfLink: testapi.Default.SelfLink("services", "sctp-service"),
+ },
+ Spec: v1.ServiceSpec{
+ Ports: []v1.ServicePort{{
+ Port: 80,
+ Protocol: v1.ProtocolSCTP,
+ }},
+ Type: v1.ServiceTypeLoadBalancer,
+ },
+ },
+ expectErr: false,
+ expectCreateAttempt: true,
+ },
}
for _, item := range table {
diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go
index 3cc49dfeae..19da3fc7c9 100644
--- a/pkg/features/kube_features.go
+++ b/pkg/features/kube_features.go
@@ -357,6 +357,12 @@ const (
// Kubelet uses the new Lease API to report node heartbeats,
// (Kube) Node Lifecycle Controller uses these heartbeats as a node health signal.
NodeLease utilfeature.Feature = "NodeLease"
+
+ // owner: @janosi
+ // alpha: v1.12
+ //
+ // Enables SCTP as new protocol for Service ports, NetworkPolicy, and ContainerPort in Pod/Containers definition
+ SCTPSupport utilfeature.Feature = "SCTPSupport"
)
func init() {
@@ -417,6 +423,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
CSIBlockVolume: {Default: false, PreRelease: utilfeature.Alpha},
RuntimeClass: {Default: false, PreRelease: utilfeature.Alpha},
NodeLease: {Default: false, PreRelease: utilfeature.Alpha},
+ SCTPSupport: {Default: false, PreRelease: utilfeature.Alpha},
// inherited features from generic apiserver, relisted here to get a conflict if it is changed
// unintentionally on either side:
diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go
index b671662aa7..6a9222838f 100644
--- a/pkg/kubectl/cmd/expose.go
+++ b/pkg/kubectl/cmd/expose.go
@@ -126,7 +126,7 @@ func NewCmdExposeService(f cmdutil.Factory, streams genericclioptions.IOStreams)
}
cmd := &cobra.Command{
- Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]",
+ Use: "expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP|SCTP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]",
DisableFlagsInUseLine: true,
Short: i18n.T("Take a replication controller, service, deployment or pod and expose it as a new Kubernetes Service"),
Long: exposeLong,
diff --git a/pkg/kubectl/cmd/expose_test.go b/pkg/kubectl/cmd/expose_test.go
index ec5a76ca94..c968facd9f 100644
--- a/pkg/kubectl/cmd/expose_test.go
+++ b/pkg/kubectl/cmd/expose_test.go
@@ -425,6 +425,11 @@ func TestRunExposeService(t *testing.T) {
Port: 8081,
TargetPort: intstr.FromInt(8081),
},
+ {
+ Protocol: corev1.ProtocolSCTP,
+ Port: 8082,
+ TargetPort: intstr.FromInt(8082),
+ },
},
},
},
@@ -451,12 +456,144 @@ func TestRunExposeService(t *testing.T) {
Port: 8081,
TargetPort: intstr.FromInt(8081),
},
+ {
+ Name: "port-4",
+ Protocol: corev1.ProtocolSCTP,
+ Port: 8082,
+ TargetPort: intstr.FromInt(8082),
+ },
},
Selector: map[string]string{"svc": "fromfoo"},
},
},
status: 200,
},
+ {
+ name: "expose-service-from-service-no-selector-defined-sctp",
+ args: []string{"service", "baz"},
+ ns: "test",
+ calls: map[string]string{
+ "GET": "/namespaces/test/services/baz",
+ "POST": "/namespaces/test/services",
+ },
+ input: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
+ Spec: corev1.ServiceSpec{
+ Selector: map[string]string{"app": "go"},
+ },
+ },
+ flags: map[string]string{"protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test"},
+ output: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
+ Spec: corev1.ServiceSpec{
+ Ports: []corev1.ServicePort{
+ {
+ Protocol: corev1.ProtocolSCTP,
+ Port: 14,
+ TargetPort: intstr.FromInt(14),
+ },
+ },
+ Selector: map[string]string{"app": "go"},
+ },
+ },
+ expected: "service/foo exposed",
+ status: 200,
+ },
+ {
+ name: "expose-service-from-service-sctp",
+ args: []string{"service", "baz"},
+ ns: "test",
+ calls: map[string]string{
+ "GET": "/namespaces/test/services/baz",
+ "POST": "/namespaces/test/services",
+ },
+ input: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
+ Spec: corev1.ServiceSpec{
+ Selector: map[string]string{"app": "go"},
+ },
+ },
+ flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test"},
+ output: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
+ Spec: corev1.ServiceSpec{
+ Ports: []corev1.ServicePort{
+ {
+ Protocol: corev1.ProtocolSCTP,
+ Port: 14,
+ TargetPort: intstr.FromInt(14),
+ },
+ },
+ Selector: map[string]string{"func": "stream"},
+ },
+ },
+ expected: "service/foo exposed",
+ status: 200,
+ },
+ {
+ name: "expose-service-cluster-ip-sctp",
+ args: []string{"service", "baz"},
+ ns: "test",
+ calls: map[string]string{
+ "GET": "/namespaces/test/services/baz",
+ "POST": "/namespaces/test/services",
+ },
+ input: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
+ Spec: corev1.ServiceSpec{
+ Selector: map[string]string{"app": "go"},
+ },
+ },
+ flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "10.10.10.10", "dry-run": "true"},
+ output: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
+ Spec: corev1.ServiceSpec{
+ Ports: []corev1.ServicePort{
+ {
+ Protocol: corev1.ProtocolSCTP,
+ Port: 14,
+ TargetPort: intstr.FromInt(14),
+ },
+ },
+ Selector: map[string]string{"func": "stream"},
+ ClusterIP: "10.10.10.10",
+ },
+ },
+ expected: "service /foo exposed",
+ status: 200,
+ },
+ {
+ name: "expose-headless-service-sctp",
+ args: []string{"service", "baz"},
+ ns: "test",
+ calls: map[string]string{
+ "GET": "/namespaces/test/services/baz",
+ "POST": "/namespaces/test/services",
+ },
+ input: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
+ Spec: corev1.ServiceSpec{
+ Selector: map[string]string{"app": "go"},
+ },
+ },
+ flags: map[string]string{"selector": "func=stream", "protocol": "SCTP", "port": "14", "name": "foo", "labels": "svc=test", "cluster-ip": "None", "dry-run": "true"},
+ output: &corev1.Service{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "", Labels: map[string]string{"svc": "test"}},
+ Spec: corev1.ServiceSpec{
+ Ports: []corev1.ServicePort{
+ {
+ Protocol: corev1.ProtocolSCTP,
+ Port: 14,
+ TargetPort: intstr.FromInt(14),
+ },
+ },
+ Selector: map[string]string{"func": "stream"},
+ ClusterIP: corev1.ClusterIPNone,
+ },
+ },
+ expected: "service/foo exposed",
+ status: 200,
+ },
}
for _, test := range tests {
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-create-list-error/3.response b/pkg/kubectl/cmd/testdata/edit/testcase-create-list-error/3.response
index 156f2ea131..d47aa684fa 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-create-list-error/3.response
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-create-list-error/3.response
@@ -3,7 +3,7 @@
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
- "message": "Service \"svc2\" is invalid: [spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP, spec.clusterIP: Invalid value: \"10.0.0.182.1\": must be empty, 'None', or a valid IP address]",
+ "message": "Service \"svc2\" is invalid: [spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP, spec.clusterIP: Invalid value: \"10.0.0.182.1\": must be empty, 'None', or a valid IP address]",
"reason": "Invalid",
"details": {
"name": "svc2",
@@ -11,7 +11,7 @@
"causes": [
{
"reason": "FieldValueNotSupported",
- "message": "Unsupported value: \"VHF\": supported values: TCP, UDP",
+ "message": "Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP",
"field": "spec.ports[0].protocol"
},
{
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/3.response b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/3.response
index b40462575f..887b0b5fe2 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/3.response
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/3.response
@@ -3,7 +3,7 @@
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
- "message": "Service \"svc1\" is invalid: [spec.clusterIP: Invalid value: \"10.0.0.10\": field is immutable, spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP]",
+ "message": "Service \"svc1\" is invalid: [spec.clusterIP: Invalid value: \"10.0.0.10\": field is immutable, spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP]",
"reason": "Invalid",
"details": {
"name": "svc1",
@@ -16,7 +16,7 @@
},
{
"reason": "FieldValueNotSupported",
- "message": "Unsupported value: \"VHF\": supported values: TCP, UDP",
+ "message": "Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP",
"field": "spec.ports[0].protocol"
}
]
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.edited b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.edited
index 3aa682b445..09b5aee489 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.edited
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.edited
@@ -4,7 +4,7 @@
#
# services "svc1" was not valid:
# * spec.clusterIP: Invalid value: "10.0.0.10": field is immutable
-# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP
+# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP, SCTP
#
apiVersion: v1
items:
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.original b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.original
index 7913d211ac..b56a73ca4e 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.original
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/5.original
@@ -4,7 +4,7 @@
#
# services "svc1" was not valid:
# * spec.clusterIP: Invalid value: "10.0.0.10": field is immutable
-# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP
+# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP, SCTP
#
apiVersion: v1
items:
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/6.response b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/6.response
index 726f0fd487..eba9193950 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/6.response
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/6.response
@@ -3,7 +3,7 @@
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
- "message": "Service \"svc1\" is invalid: spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP",
+ "message": "Service \"svc1\" is invalid: spec.ports[0].protocol: Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP",
"reason": "Invalid",
"details": {
"name": "svc1",
@@ -11,7 +11,7 @@
"causes": [
{
"reason": "FieldValueNotSupported",
- "message": "Unsupported value: \"VHF\": supported values: TCP, UDP",
+ "message": "Unsupported value: \"VHF\": supported values: TCP, UDP, SCTP",
"field": "spec.ports[0].protocol"
}
]
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.edited b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.edited
index 7ad39a82a4..6c0168cd63 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.edited
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.edited
@@ -3,7 +3,7 @@
# reopened with the relevant failures.
#
# services "svc1" was not valid:
-# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP
+# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP, SCTP
#
apiVersion: v1
items:
diff --git a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.original b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.original
index 4d82b22b20..eee7f0d508 100755
--- a/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.original
+++ b/pkg/kubectl/cmd/testdata/edit/testcase-list-errors/8.original
@@ -3,7 +3,7 @@
# reopened with the relevant failures.
#
# services "svc1" was not valid:
-# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP
+# * spec.ports[0].protocol: Unsupported value: "VHF": supported values: TCP, UDP, SCTP
#
apiVersion: v1
items:
diff --git a/pkg/kubectl/generate_test.go b/pkg/kubectl/generate_test.go
index 6bb174add1..d62d79469d 100644
--- a/pkg/kubectl/generate_test.go
+++ b/pkg/kubectl/generate_test.go
@@ -366,10 +366,12 @@ func TestMakeParseProtocols(t *testing.T) {
protocols: map[string]string{
"102": "UDP",
"101": "TCP",
+ "103": "SCTP",
},
expected: map[string]string{
"102": "UDP",
"101": "TCP",
+ "103": "SCTP",
},
},
}
diff --git a/pkg/kubectl/service_test.go b/pkg/kubectl/service_test.go
index ceaba9b463..420ed8e712 100644
--- a/pkg/kubectl/service_test.go
+++ b/pkg/kubectl/service_test.go
@@ -598,6 +598,355 @@ func TestGenerateService(t *testing.T) {
},
},
},
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "labels": "key1=value1,key2=value2",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Labels: map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ },
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV1{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "default",
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV1{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ "session-affinity": "ClientIP",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "default",
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ SessionAffinity: v1.ServiceAffinityClientIP,
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ "cluster-ip": "10.10.10.10",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ ClusterIP: "10.10.10.10",
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "port": "80",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ "cluster-ip": "None",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Port: 80,
+ Protocol: "SCTP",
+ TargetPort: intstr.FromInt(1234),
+ },
+ },
+ ClusterIP: v1.ClusterIPNone,
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV1{},
+ params: map[string]interface{}{
+ "selector": "foo=bar",
+ "name": "test",
+ "ports": "80,443",
+ "protocol": "SCTP",
+ "container-port": "foobar",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "port-1",
+ Port: 80,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromString("foobar"),
+ },
+ {
+ Name: "port-2",
+ Port: 443,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromString("foobar"),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar",
+ "name": "test",
+ "ports": "80,443",
+ "protocol": "SCTP",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "port-1",
+ Port: 80,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromInt(80),
+ },
+ {
+ Name: "port-2",
+ Port: 443,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromInt(443),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar",
+ "name": "test",
+ "ports": "80,8080",
+ "protocols": "8080/SCTP",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "port-1",
+ Port: 80,
+ Protocol: v1.ProtocolTCP,
+ TargetPort: intstr.FromInt(80),
+ },
+ {
+ Name: "port-2",
+ Port: 8080,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromInt(8080),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar",
+ "name": "test",
+ "ports": "80,8080,8081,8082",
+ "protocols": "8080/UDP,8081/TCP,8082/SCTP",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ },
+ Ports: []v1.ServicePort{
+ {
+ Name: "port-1",
+ Port: 80,
+ Protocol: v1.ProtocolTCP,
+ TargetPort: intstr.FromInt(80),
+ },
+ {
+ Name: "port-2",
+ Port: 8080,
+ Protocol: v1.ProtocolUDP,
+ TargetPort: intstr.FromInt(8080),
+ },
+ {
+ Name: "port-3",
+ Port: 8081,
+ Protocol: v1.ProtocolTCP,
+ TargetPort: intstr.FromInt(8081),
+ },
+ {
+ Name: "port-4",
+ Port: 8082,
+ Protocol: v1.ProtocolSCTP,
+ TargetPort: intstr.FromInt(8082),
+ },
+ },
+ },
+ },
+ },
+ {
+ generator: ServiceGeneratorV2{},
+ params: map[string]interface{}{
+ "selector": "foo=bar,baz=blah",
+ "name": "test",
+ "protocol": "SCTP",
+ "container-port": "1234",
+ "cluster-ip": "None",
+ },
+ expected: v1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ },
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{
+ "foo": "bar",
+ "baz": "blah",
+ },
+ Ports: []v1.ServicePort{},
+ ClusterIP: v1.ClusterIPNone,
+ },
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
diff --git a/pkg/kubelet/apis/cri/runtime/v1alpha2/api.pb.go b/pkg/kubelet/apis/cri/runtime/v1alpha2/api.pb.go
index 6b496711e1..76e8e456ce 100644
--- a/pkg/kubelet/apis/cri/runtime/v1alpha2/api.pb.go
+++ b/pkg/kubelet/apis/cri/runtime/v1alpha2/api.pb.go
@@ -160,17 +160,20 @@ const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
type Protocol int32
const (
- Protocol_TCP Protocol = 0
- Protocol_UDP Protocol = 1
+ Protocol_TCP Protocol = 0
+ Protocol_UDP Protocol = 1
+ Protocol_SCTP Protocol = 2
)
var Protocol_name = map[int32]string{
0: "TCP",
1: "UDP",
+ 2: "SCTP",
}
var Protocol_value = map[string]int32{
- "TCP": 0,
- "UDP": 1,
+ "TCP": 0,
+ "UDP": 1,
+ "SCTP": 2,
}
func (x Protocol) String() string {
diff --git a/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto b/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto
index bd6cacc148..bb00c3842c 100644
--- a/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto
+++ b/pkg/kubelet/apis/cri/runtime/v1alpha2/api.proto
@@ -143,6 +143,7 @@ message DNSConfig {
enum Protocol {
TCP = 0;
UDP = 1;
+ SCTP = 2;
}
// PortMapping specifies the port mapping configurations of a sandbox.
diff --git a/pkg/kubelet/dockershim/docker_checkpoint.go b/pkg/kubelet/dockershim/docker_checkpoint.go
index 8bfa1a7782..e44f0bd62f 100644
--- a/pkg/kubelet/dockershim/docker_checkpoint.go
+++ b/pkg/kubelet/dockershim/docker_checkpoint.go
@@ -28,6 +28,7 @@ const (
sandboxCheckpointDir = "sandbox"
protocolTCP = Protocol("tcp")
protocolUDP = Protocol("udp")
+ protocolSCTP = Protocol("sctp")
schemaVersion = "v1"
)
diff --git a/pkg/kubelet/dockershim/docker_sandbox.go b/pkg/kubelet/dockershim/docker_sandbox.go
index 13e9c42366..c5cf216698 100644
--- a/pkg/kubelet/dockershim/docker_sandbox.go
+++ b/pkg/kubelet/dockershim/docker_sandbox.go
@@ -672,6 +672,8 @@ func toCheckpointProtocol(protocol runtimeapi.Protocol) Protocol {
return protocolTCP
case runtimeapi.Protocol_UDP:
return protocolUDP
+ case runtimeapi.Protocol_SCTP:
+ return protocolSCTP
}
glog.Warningf("Unknown protocol %q: defaulting to TCP", protocol)
return protocolTCP
diff --git a/pkg/kubelet/dockershim/docker_service.go b/pkg/kubelet/dockershim/docker_service.go
index d7fcc9232d..a0ca35e0b5 100644
--- a/pkg/kubelet/dockershim/docker_service.go
+++ b/pkg/kubelet/dockershim/docker_service.go
@@ -515,6 +515,8 @@ func toAPIProtocol(protocol Protocol) v1.Protocol {
return v1.ProtocolTCP
case protocolUDP:
return v1.ProtocolUDP
+ case protocolSCTP:
+ return v1.ProtocolSCTP
}
glog.Warningf("Unknown protocol %q: defaulting to TCP", protocol)
return v1.ProtocolTCP
diff --git a/pkg/kubelet/dockershim/helpers.go b/pkg/kubelet/dockershim/helpers.go
index 3caa2441da..05d23c75b2 100644
--- a/pkg/kubelet/dockershim/helpers.go
+++ b/pkg/kubelet/dockershim/helpers.go
@@ -172,6 +172,8 @@ func makePortsAndBindings(pm []*runtimeapi.PortMapping) (dockernat.PortSet, map[
protocol = "/udp"
case runtimeapi.Protocol_TCP:
protocol = "/tcp"
+ case runtimeapi.Protocol_SCTP:
+ protocol = "/sctp"
default:
glog.Warningf("Unknown protocol %q: defaulting to TCP", port.Protocol)
protocol = "/tcp"
diff --git a/pkg/kubelet/dockershim/network/hostport/hostport_manager.go b/pkg/kubelet/dockershim/network/hostport/hostport_manager.go
index 67ef0e5aea..70bfd16dab 100644
--- a/pkg/kubelet/dockershim/network/hostport/hostport_manager.go
+++ b/pkg/kubelet/dockershim/network/hostport/hostport_manager.go
@@ -264,6 +264,12 @@ func (hm *hostportManager) openHostports(podPortMapping *PodPortMapping) (map[ho
if pm.HostPort <= 0 {
continue
}
+
+ // We do not open host ports for SCTP ports, as we agreed in the Support of SCTP KEP
+ if pm.Protocol == v1.ProtocolSCTP {
+ continue
+ }
+
hp := portMappingToHostport(pm)
socket, err := hm.portOpener(&hp)
if err != nil {
diff --git a/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go b/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go
index 1b3b460cb5..e8b2c070bf 100644
--- a/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go
+++ b/pkg/kubelet/dockershim/network/hostport/hostport_manager_test.go
@@ -83,6 +83,16 @@ func TestOpenCloseHostports(t *testing.T) {
},
false,
},
+ {
+ &PodPortMapping{
+ Namespace: "ns1",
+ Name: "n4",
+ PortMappings: []*PortMapping{
+ {HostPort: 7777, Protocol: v1.Protocol("STCP")},
+ },
+ },
+ false,
+ },
}
iptables := NewFakeIPTables()
@@ -142,6 +152,11 @@ func TestOpenCloseHostports(t *testing.T) {
{HostPort: 7070, Protocol: v1.Protocol("TCP")},
},
},
+ {
+ portMappings: []*PortMapping{
+ {HostPort: 7777, Protocol: v1.Protocol("SCTP")},
+ },
+ },
}
for _, tc := range closePortCases {
@@ -187,6 +202,11 @@ func TestHostportManager(t *testing.T) {
ContainerPort: 81,
Protocol: v1.ProtocolUDP,
},
+ {
+ HostPort: 8083,
+ ContainerPort: 83,
+ Protocol: v1.ProtocolSCTP,
+ },
},
},
expectError: false,
@@ -208,6 +228,11 @@ func TestHostportManager(t *testing.T) {
ContainerPort: 81,
Protocol: v1.ProtocolUDP,
},
+ {
+ HostPort: 8083,
+ ContainerPort: 83,
+ Protocol: v1.ProtocolSCTP,
+ },
},
},
expectError: true,
@@ -262,25 +287,29 @@ func TestHostportManager(t *testing.T) {
lines := strings.Split(string(raw.Bytes()), "\n")
expectedLines := map[string]bool{
`*nat`: true,
- `:KUBE-HOSTPORTS - [0:0]`: true,
- `:OUTPUT - [0:0]`: true,
- `:PREROUTING - [0:0]`: true,
- `:POSTROUTING - [0:0]`: true,
- `:KUBE-HP-IJHALPHTORMHHPPK - [0:0]`: true,
- `:KUBE-HP-63UPIDJXVRSZGSUZ - [0:0]`: true,
- `:KUBE-HP-WFBOALXEP42XEMJK - [0:0]`: true,
- "-A KUBE-HOSTPORTS -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j KUBE-HP-WFBOALXEP42XEMJK": true,
- "-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp --dport 8081 -j KUBE-HP-63UPIDJXVRSZGSUZ": true,
- "-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp --dport 8080 -j KUBE-HP-IJHALPHTORMHHPPK": true,
- "-A OUTPUT -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
- "-A PREROUTING -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
- "-A POSTROUTING -m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE": true,
- "-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
- "-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80": true,
- "-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
- "-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81": true,
- "-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -s 10.1.1.4/32 -j KUBE-MARK-MASQ": true,
- "-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.4:443": true,
+ `:KUBE-HOSTPORTS - [0:0]`: true,
+ `:OUTPUT - [0:0]`: true,
+ `:PREROUTING - [0:0]`: true,
+ `:POSTROUTING - [0:0]`: true,
+ `:KUBE-HP-IJHALPHTORMHHPPK - [0:0]`: true,
+ `:KUBE-HP-63UPIDJXVRSZGSUZ - [0:0]`: true,
+ `:KUBE-HP-WFBOALXEP42XEMJK - [0:0]`: true,
+ `:KUBE-HP-XU6AWMMJYOZOFTFZ - [0:0]`: true,
+ "-A KUBE-HOSTPORTS -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp --dport 8443 -j KUBE-HP-WFBOALXEP42XEMJK": true,
+ "-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp --dport 8081 -j KUBE-HP-63UPIDJXVRSZGSUZ": true,
+ "-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp --dport 8080 -j KUBE-HP-IJHALPHTORMHHPPK": true,
+ "-A KUBE-HOSTPORTS -m comment --comment \"pod1_ns1 hostport 8083\" -m sctp -p sctp --dport 8083 -j KUBE-HP-XU6AWMMJYOZOFTFZ": true,
+ "-A OUTPUT -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
+ "-A PREROUTING -m comment --comment \"kube hostport portals\" -m addrtype --dst-type LOCAL -j KUBE-HOSTPORTS": true,
+ "-A POSTROUTING -m comment --comment \"SNAT for localhost access to hostports\" -o cbr0 -s 127.0.0.0/8 -j MASQUERADE": true,
+ "-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
+ "-A KUBE-HP-IJHALPHTORMHHPPK -m comment --comment \"pod1_ns1 hostport 8080\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.2:80": true,
+ "-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
+ "-A KUBE-HP-63UPIDJXVRSZGSUZ -m comment --comment \"pod1_ns1 hostport 8081\" -m udp -p udp -j DNAT --to-destination 10.1.1.2:81": true,
+ "-A KUBE-HP-XU6AWMMJYOZOFTFZ -m comment --comment \"pod1_ns1 hostport 8083\" -s 10.1.1.2/32 -j KUBE-MARK-MASQ": true,
+ "-A KUBE-HP-XU6AWMMJYOZOFTFZ -m comment --comment \"pod1_ns1 hostport 8083\" -m sctp -p sctp -j DNAT --to-destination 10.1.1.2:83": true,
+ "-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -s 10.1.1.4/32 -j KUBE-MARK-MASQ": true,
+ "-A KUBE-HP-WFBOALXEP42XEMJK -m comment --comment \"pod3_ns1 hostport 8443\" -m tcp -p tcp -j DNAT --to-destination 10.1.1.4:443": true,
`COMMIT`: true,
}
for _, line := range lines {
diff --git a/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go b/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go
index 43c3c52ecb..1f9df7e9b9 100644
--- a/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go
+++ b/pkg/kubelet/dockershim/network/hostport/hostport_syncer.go
@@ -27,6 +27,7 @@ import (
"github.com/golang/glog"
+ "k8s.io/api/core/v1"
iptablesproxy "k8s.io/kubernetes/pkg/proxy/iptables"
utiliptables "k8s.io/kubernetes/pkg/util/iptables"
)
@@ -74,6 +75,12 @@ func (h *hostportSyncer) openHostports(podHostportMapping *PodPortMapping) error
// Assume hostport is not specified in this portmapping. So skip
continue
}
+
+ // We do not open host ports for SCTP ports, as we agreed in the Support of SCTP KEP
+ if port.Protocol == v1.ProtocolSCTP {
+ continue
+ }
+
hp := hostport{
port: port.HostPort,
protocol: strings.ToLower(string(port.Protocol)),
diff --git a/pkg/kubelet/envvars/envvars_test.go b/pkg/kubelet/envvars/envvars_test.go
index 7d0cd84dcc..b99dd31bf9 100644
--- a/pkg/kubelet/envvars/envvars_test.go
+++ b/pkg/kubelet/envvars/envvars_test.go
@@ -90,6 +90,16 @@ func TestFromServices(t *testing.T) {
},
},
},
+ {
+ ObjectMeta: metav1.ObjectMeta{Name: "sctp-1"},
+ Spec: v1.ServiceSpec{
+ Selector: map[string]string{"bar": "sctp-sel"},
+ ClusterIP: "1.2.3.4",
+ Ports: []v1.ServicePort{
+ {Port: 777, Protocol: "SCTP"},
+ },
+ },
+ },
}
vars := envvars.FromServices(sl)
expected := []v1.EnvVar{
@@ -138,6 +148,13 @@ func TestFromServices(t *testing.T) {
{Name: "SUPER_IPV6_PORT_8084_TCP_PROTO", Value: "tcp"},
{Name: "SUPER_IPV6_PORT_8084_TCP_PORT", Value: "8084"},
{Name: "SUPER_IPV6_PORT_8084_TCP_ADDR", Value: "2001:DB8::"},
+ {Name: "SCTP_1_SERVICE_HOST", Value: "1.2.3.4"},
+ {Name: "SCTP_1_SERVICE_PORT", Value: "777"},
+ {Name: "SCTP_1_PORT", Value: "sctp://1.2.3.4:777"},
+ {Name: "SCTP_1_PORT_777_SCTP", Value: "sctp://1.2.3.4:777"},
+ {Name: "SCTP_1_PORT_777_SCTP_PROTO", Value: "sctp"},
+ {Name: "SCTP_1_PORT_777_SCTP_PORT", Value: "777"},
+ {Name: "SCTP_1_PORT_777_SCTP_ADDR", Value: "1.2.3.4"},
}
if len(vars) != len(expected) {
t.Errorf("Expected %d env vars, got: %+v", len(expected), vars)
diff --git a/pkg/kubelet/kuberuntime/helpers.go b/pkg/kubelet/kuberuntime/helpers.go
index d52faa0640..fd977d5a16 100644
--- a/pkg/kubelet/kuberuntime/helpers.go
+++ b/pkg/kubelet/kuberuntime/helpers.go
@@ -79,6 +79,8 @@ func toRuntimeProtocol(protocol v1.Protocol) runtimeapi.Protocol {
return runtimeapi.Protocol_TCP
case v1.ProtocolUDP:
return runtimeapi.Protocol_UDP
+ case v1.ProtocolSCTP:
+ return runtimeapi.Protocol_SCTP
}
glog.Warningf("Unknown protocol %q: defaulting to TCP", protocol)
diff --git a/pkg/master/controller_test.go b/pkg/master/controller_test.go
index a20d82cd69..8e31a71ccf 100644
--- a/pkg/master/controller_test.go
+++ b/pkg/master/controller_test.go
@@ -372,6 +372,20 @@ func TestReconcileEndpoints(t *testing.T) {
}},
},
},
+ {
+ testName: "no existing sctp endpoints",
+ serviceName: "boo",
+ ip: "1.2.3.4",
+ endpointPorts: []api.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}},
+ endpoints: nil,
+ expectCreate: &api.Endpoints{
+ ObjectMeta: om("boo"),
+ Subsets: []api.EndpointSubset{{
+ Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
+ Ports: []api.EndpointPort{{Name: "boo", Port: 7777, Protocol: "SCTP"}},
+ }},
+ },
+ },
}
for _, test := range reconcile_tests {
fakeClient := fake.NewSimpleClientset()
diff --git a/pkg/printers/internalversion/printers_test.go b/pkg/printers/internalversion/printers_test.go
index 817a451699..f8dbf24bfc 100644
--- a/pkg/printers/internalversion/printers_test.go
+++ b/pkg/printers/internalversion/printers_test.go
@@ -1152,6 +1152,10 @@ func TestPrintHumanReadableService(t *testing.T) {
Port: 8000,
Protocol: "TCP",
},
+ {
+ Port: 7777,
+ Protocol: "SCTP",
+ },
},
},
},
diff --git a/pkg/proxy/iptables/proxier.go b/pkg/proxy/iptables/proxier.go
index 470916ce70..34f7c699d9 100644
--- a/pkg/proxy/iptables/proxier.go
+++ b/pkg/proxy/iptables/proxier.go
@@ -847,7 +847,7 @@ func (proxier *Proxier) syncProxyRules() {
// (because the socket might open but it would never work).
if local, err := utilproxy.IsLocalIP(externalIP); err != nil {
glog.Errorf("can't determine if IP is local, assuming not: %v", err)
- } else if local {
+ } else if local && (svcInfo.GetProtocol() != v1.ProtocolSCTP) {
lp := utilproxy.LocalPort{
Description: "externalIP for " + svcNameString,
IP: externalIP,
@@ -1016,7 +1016,7 @@ func (proxier *Proxier) syncProxyRules() {
if proxier.portsMap[lp] != nil {
glog.V(4).Infof("Port %s was open before and is still needed", lp.String())
replacementPortsMap[lp] = proxier.portsMap[lp]
- } else {
+ } else if svcInfo.GetProtocol() != v1.ProtocolSCTP {
socket, err := proxier.portMapper.OpenLocalPort(&lp)
if err != nil {
glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err)
diff --git a/pkg/proxy/iptables/proxier_test.go b/pkg/proxy/iptables/proxier_test.go
index c11cde9f0b..fd711552f6 100644
--- a/pkg/proxy/iptables/proxier_test.go
+++ b/pkg/proxy/iptables/proxier_test.go
@@ -161,8 +161,9 @@ func newFakeServiceInfo(service proxy.ServicePortName, ip net.IP, port int, prot
func TestDeleteEndpointConnections(t *testing.T) {
const (
- UDP = v1.ProtocolUDP
- TCP = v1.ProtocolTCP
+ UDP = v1.ProtocolUDP
+ TCP = v1.ProtocolTCP
+ SCTP = v1.ProtocolSCTP
)
testCases := []struct {
description string
@@ -188,6 +189,13 @@ func TestDeleteEndpointConnections(t *testing.T) {
svcPort: 80,
protocol: TCP,
endpoint: "10.240.0.4:80",
+ }, {
+ description: "V4 SCTP",
+ svcName: "v4-sctp",
+ svcIP: "10.96.3.3",
+ svcPort: 80,
+ protocol: SCTP,
+ endpoint: "10.240.0.5:80",
}, {
description: "V4 UDP, nothing to delete, benign error",
svcName: "v4-udp-nothing-to-delete",
@@ -218,6 +226,13 @@ func TestDeleteEndpointConnections(t *testing.T) {
svcPort: 80,
protocol: TCP,
endpoint: "[2001:db8::3]:80",
+ }, {
+ description: "V6 SCTP",
+ svcName: "v6-sctp",
+ svcIP: "fd00:1234::40",
+ svcPort: 80,
+ protocol: SCTP,
+ endpoint: "[2001:db8::4]:80",
},
}
@@ -1061,12 +1076,14 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpport", "SCTP", 1236, 6321, 0)
}),
makeTestService("somewhere-else", "node-port", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeNodePort
svc.Spec.ClusterIP = "172.16.55.10"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blahblah", "UDP", 345, 678, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "moreblahblah", "TCP", 344, 677, 0)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "muchmoreblah", "SCTP", 343, 676, 0)
}),
makeTestService("somewhere", "load-balancer", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeLoadBalancer
@@ -1100,8 +1117,8 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
fp.OnServiceAdd(services[i])
}
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
- if len(fp.serviceMap) != 8 {
- t.Errorf("expected service map length 8, got %v", fp.serviceMap)
+ if len(fp.serviceMap) != 10 {
+ t.Errorf("expected service map length 10, got %v", fp.serviceMap)
}
// The only-local-loadbalancer ones get added
diff --git a/pkg/proxy/ipvs/ipset.go b/pkg/proxy/ipvs/ipset.go
index 5da72749b4..99bdc6d154 100644
--- a/pkg/proxy/ipvs/ipset.go
+++ b/pkg/proxy/ipvs/ipset.go
@@ -64,6 +64,12 @@ const (
kubeNodePortLocalSetUDPComment = "Kubernetes nodeport UDP port with externalTrafficPolicy=local"
kubeNodePortLocalSetUDP = "KUBE-NODE-PORT-LOCAL-UDP"
+
+ kubeNodePortSetSCTPComment = "Kubernetes nodeport SCTP port for masquerade purpose"
+ kubeNodePortSetSCTP = "KUBE-NODE-PORT-SCTP"
+
+ kubeNodePortLocalSetSCTPComment = "Kubernetes nodeport SCTP port with externalTrafficPolicy=local"
+ kubeNodePortLocalSetSCTP = "KUBE-NODE-PORT-LOCAL-SCTP"
)
// IPSetVersioner can query the current ipset version.
diff --git a/pkg/proxy/ipvs/ipset_test.go b/pkg/proxy/ipvs/ipset_test.go
index 2e6f667de5..f90dca706e 100644
--- a/pkg/proxy/ipvs/ipset_test.go
+++ b/pkg/proxy/ipvs/ipset_test.go
@@ -179,6 +179,26 @@ func TestSyncIPSetEntries(t *testing.T) {
currentEntries: []string{"80", "9090", "8081", "8082"},
expectedEntries: []string{"8080"},
},
+ { // case 12
+ set: &utilipset.IPSet{
+ Name: "sctp-1",
+ },
+ setType: utilipset.HashIPPort,
+ ipv6: false,
+ activeEntries: []string{"172.17.0.4,sctp:80"},
+ currentEntries: nil,
+ expectedEntries: []string{"172.17.0.4,sctp:80"},
+ },
+ { // case 1
+ set: &utilipset.IPSet{
+ Name: "sctp-2",
+ },
+ setType: utilipset.HashIPPort,
+ ipv6: true,
+ activeEntries: []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
+ currentEntries: []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
+ expectedEntries: []string{"FE80::0202:B3FF:FE1E:8329,sctp:80"},
+ },
}
for i := range testCases {
diff --git a/pkg/proxy/ipvs/proxier.go b/pkg/proxy/ipvs/proxier.go
index 35a6510181..d1f90874fb 100644
--- a/pkg/proxy/ipvs/proxier.go
+++ b/pkg/proxy/ipvs/proxier.go
@@ -128,6 +128,8 @@ var ipsetInfo = []struct {
{kubeNodePortLocalSetTCP, utilipset.BitmapPort, false, kubeNodePortLocalSetTCPComment},
{kubeNodePortSetUDP, utilipset.BitmapPort, false, kubeNodePortSetUDPComment},
{kubeNodePortLocalSetUDP, utilipset.BitmapPort, false, kubeNodePortLocalSetUDPComment},
+ {kubeNodePortSetSCTP, utilipset.BitmapPort, false, kubeNodePortSetSCTPComment},
+ {kubeNodePortLocalSetSCTP, utilipset.BitmapPort, false, kubeNodePortLocalSetSCTPComment},
}
// ipsetWithIptablesChain is the ipsets list with iptables source chain and the chain jump to
@@ -152,6 +154,8 @@ var ipsetWithIptablesChain = []struct {
{kubeNodePortSetTCP, string(KubeNodePortChain), string(KubeMarkMasqChain), "dst", "tcp"},
{kubeNodePortLocalSetUDP, string(KubeNodePortChain), "RETURN", "dst", "udp"},
{kubeNodePortSetUDP, string(KubeNodePortChain), string(KubeMarkMasqChain), "dst", "udp"},
+ {kubeNodePortSetSCTP, string(kubeServicesChain), string(KubeNodePortChain), "dst", "sctp"},
+ {kubeNodePortLocalSetSCTP, string(KubeNodePortChain), "RETURN", "dst", "sctp"},
}
var ipvsModules = []string{
@@ -805,7 +809,9 @@ func (proxier *Proxier) syncProxyRules() {
for _, externalIP := range svcInfo.ExternalIPs {
if local, err := utilproxy.IsLocalIP(externalIP); err != nil {
glog.Errorf("can't determine if IP is local, assuming not: %v", err)
- } else if local {
+ // We do not start listening on SCTP ports, according to our agreement in the
+ // SCTP support KEP
+ } else if local && (svcInfo.GetProtocol() != v1.ProtocolSCTP) {
lp := utilproxy.LocalPort{
Description: "externalIP for " + svcNameString,
IP: externalIP,
@@ -1004,7 +1010,9 @@ func (proxier *Proxier) syncProxyRules() {
if proxier.portsMap[lp] != nil {
glog.V(4).Infof("Port %s was open before and is still needed", lp.String())
replacementPortsMap[lp] = proxier.portsMap[lp]
- } else {
+ // We do not start listening on SCTP ports, according to our agreement in the
+ // SCTP support KEP
+ } else if svcInfo.GetProtocol() != v1.ProtocolSCTP {
socket, err := proxier.portMapper.OpenLocalPort(&lp)
if err != nil {
glog.Errorf("can't open %s, skipping this nodePort: %v", lp.String(), err)
@@ -1032,6 +1040,8 @@ func (proxier *Proxier) syncProxyRules() {
nodePortSet = proxier.ipsetList[kubeNodePortSetTCP]
case "udp":
nodePortSet = proxier.ipsetList[kubeNodePortSetUDP]
+ case "sctp":
+ nodePortSet = proxier.ipsetList[kubeNodePortSetSCTP]
default:
// It should never hit
glog.Errorf("Unsupported protocol type: %s", protocol)
@@ -1052,6 +1062,8 @@ func (proxier *Proxier) syncProxyRules() {
nodePortLocalSet = proxier.ipsetList[kubeNodePortLocalSetTCP]
case "udp":
nodePortLocalSet = proxier.ipsetList[kubeNodePortLocalSetUDP]
+ case "sctp":
+ nodePortLocalSet = proxier.ipsetList[kubeNodePortLocalSetSCTP]
default:
// It should never hit
glog.Errorf("Unsupported protocol type: %s", protocol)
diff --git a/pkg/proxy/ipvs/proxier_test.go b/pkg/proxy/ipvs/proxier_test.go
index 4f2ea198b7..61288ce1dc 100644
--- a/pkg/proxy/ipvs/proxier_test.go
+++ b/pkg/proxy/ipvs/proxier_test.go
@@ -1345,12 +1345,14 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
svc.Spec.ClusterIP = "172.16.55.4"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "something", "UDP", 1234, 4321, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somethingelse", "UDP", 1235, 5321, 0)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "somesctp", "SCTP", 1236, 6321, 0)
}),
makeTestService("somewhere-else", "node-port", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeNodePort
svc.Spec.ClusterIP = "172.16.55.10"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "blahblah", "UDP", 345, 678, 0)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "moreblahblah", "TCP", 344, 677, 0)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpblah", "SCTP", 343, 676, 0)
}),
makeTestService("somewhere", "load-balancer", func(svc *v1.Service) {
svc.Spec.Type = v1.ServiceTypeLoadBalancer
@@ -1358,6 +1360,7 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
svc.Spec.LoadBalancerIP = "5.6.7.8"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar", "UDP", 8675, 30061, 7000)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8676, 30062, 7001)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpfoo", "SCTP", 8677, 30063, 7002)
svc.Status.LoadBalancer = v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "10.1.2.4"},
@@ -1370,6 +1373,7 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
svc.Spec.LoadBalancerIP = "5.6.7.8"
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "foobar2", "UDP", 8677, 30063, 7002)
svc.Spec.Ports = addTestPort(svc.Spec.Ports, "baz", "UDP", 8678, 30064, 7003)
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sctpbaz", "SCTP", 8679, 30065, 7004)
svc.Status.LoadBalancer = v1.LoadBalancerStatus{
Ingress: []v1.LoadBalancerIngress{
{IP: "10.1.2.3"},
@@ -1384,8 +1388,8 @@ func TestBuildServiceMapAddRemove(t *testing.T) {
fp.OnServiceAdd(services[i])
}
result := proxy.UpdateServiceMap(fp.serviceMap, fp.serviceChanges)
- if len(fp.serviceMap) != 8 {
- t.Errorf("expected service map length 8, got %v", fp.serviceMap)
+ if len(fp.serviceMap) != 12 {
+ t.Errorf("expected service map length 12, got %v", fp.serviceMap)
}
// The only-local-loadbalancer ones get added
@@ -1455,6 +1459,11 @@ func TestBuildServiceMapServiceHeadless(t *testing.T) {
svc.Spec.Type = v1.ServiceTypeClusterIP
svc.Spec.ClusterIP = v1.ClusterIPNone
}),
+ makeTestService("somewhere-else", "headless-sctp", func(svc *v1.Service) {
+ svc.Spec.Type = v1.ServiceTypeClusterIP
+ svc.Spec.ClusterIP = v1.ClusterIPNone
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sip", "SCTP", 1235, 0, 0)
+ }),
)
// Headless service should be ignored
@@ -2620,6 +2629,76 @@ func Test_syncService(t *testing.T) {
},
bindAddr: true,
},
+ {
+ // case 4, SCTP, old virtual server is same as new virtual server
+ oldVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 80,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagHashed,
+ },
+ svcName: "foo",
+ newVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 80,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagHashed,
+ },
+ bindAddr: false,
+ },
+ {
+ // case 5, old virtual server is different from new virtual server
+ oldVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 8080,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagHashed,
+ },
+ svcName: "bar",
+ newVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 8080,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagPersistent,
+ },
+ bindAddr: false,
+ },
+ {
+ // case 6, old virtual server is different from new virtual server
+ oldVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 8080,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagHashed,
+ },
+ svcName: "bar",
+ newVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 8080,
+ Scheduler: "wlc",
+ Flags: utilipvs.FlagHashed,
+ },
+ bindAddr: false,
+ },
+ {
+ // case 7, old virtual server is nil, and create new virtual server
+ oldVirtualServer: nil,
+ svcName: "baz",
+ newVirtualServer: &utilipvs.VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: string(v1.ProtocolSCTP),
+ Port: 53,
+ Scheduler: "rr",
+ Flags: utilipvs.FlagHashed,
+ },
+ bindAddr: true,
+ },
}
for i := range testCases {
diff --git a/pkg/proxy/service_test.go b/pkg/proxy/service_test.go
index 61bfa1ee42..e929013ecc 100644
--- a/pkg/proxy/service_test.go
+++ b/pkg/proxy/service_test.go
@@ -114,6 +114,15 @@ func TestServiceToServiceMap(t *testing.T) {
}),
expected: map[ServicePortName]*BaseServiceInfo{},
},
+ {
+ desc: "headless sctp service",
+ service: makeTestService("ns2", "headless", func(svc *v1.Service) {
+ svc.Spec.Type = v1.ServiceTypeClusterIP
+ svc.Spec.ClusterIP = v1.ClusterIPNone
+ svc.Spec.Ports = addTestPort(svc.Spec.Ports, "sip", "SCTP", 7777, 0, 0)
+ }),
+ expected: map[ServicePortName]*BaseServiceInfo{},
+ },
{
desc: "headless service without port",
service: makeTestService("ns2", "headless-without-port", func(svc *v1.Service) {
diff --git a/pkg/proxy/userspace/proxysocket.go b/pkg/proxy/userspace/proxysocket.go
index d16b002ac9..098f68c15a 100644
--- a/pkg/proxy/userspace/proxysocket.go
+++ b/pkg/proxy/userspace/proxysocket.go
@@ -68,6 +68,8 @@ func newProxySocket(protocol v1.Protocol, ip net.IP, port int) (ProxySocket, err
return nil, err
}
return &udpProxySocket{UDPConn: conn, port: port}, nil
+ case "SCTP":
+ return nil, fmt.Errorf("SCTP is not supported for user space proxy")
}
return nil, fmt.Errorf("unknown protocol %q", protocol)
}
diff --git a/pkg/proxy/util/port_test.go b/pkg/proxy/util/port_test.go
index f6a4fccb7f..7295fd5000 100644
--- a/pkg/proxy/util/port_test.go
+++ b/pkg/proxy/util/port_test.go
@@ -38,6 +38,8 @@ func TestLocalPortString(t *testing.T) {
{"IPv4 UDP", "1.2.3.4", 9999, "udp", "\"IPv4 UDP\" (1.2.3.4:9999/udp)"},
{"IPv4 TCP", "5.6.7.8", 1053, "tcp", "\"IPv4 TCP\" (5.6.7.8:1053/tcp)"},
{"IPv6 TCP", "2001:db8::1", 80, "tcp", "\"IPv6 TCP\" ([2001:db8::1]:80/tcp)"},
+ {"IPv4 SCTP", "9.10.11.12", 7777, "sctp", "\"IPv4 SCTP\" (9.10.11.12:7777/sctp)"},
+ {"IPv6 SCTP", "2001:db8::2", 80, "sctp", "\"IPv6 SCTP\" ([2001:db8::2]:80/sctp)"},
}
for _, tc := range testCases {
diff --git a/pkg/proxy/winkernel/proxier.go b/pkg/proxy/winkernel/proxier.go
index d87aec75a0..e9d7f72edb 100644
--- a/pkg/proxy/winkernel/proxier.go
+++ b/pkg/proxy/winkernel/proxier.go
@@ -428,6 +428,9 @@ func Enum(p v1.Protocol) uint16 {
if p == v1.ProtocolUDP {
return 17
}
+ if p == v1.ProtocolSCTP {
+ return 132
+ }
return 0
}
diff --git a/pkg/proxy/winuserspace/proxysocket.go b/pkg/proxy/winuserspace/proxysocket.go
index a000f27f5e..df7d334d94 100644
--- a/pkg/proxy/winuserspace/proxysocket.go
+++ b/pkg/proxy/winuserspace/proxysocket.go
@@ -101,6 +101,8 @@ func newProxySocket(protocol v1.Protocol, ip net.IP, port int) (proxySocket, err
return nil, err
}
return &udpProxySocket{UDPConn: conn, port: port}, nil
+ case "SCTP":
+ return nil, fmt.Errorf("SCTP is not supported for user space proxy")
}
return nil, fmt.Errorf("unknown protocol %q", protocol)
}
diff --git a/pkg/util/ipset/ipset.go b/pkg/util/ipset/ipset.go
index 270987badf..4bc4a739d3 100644
--- a/pkg/util/ipset/ipset.go
+++ b/pkg/util/ipset/ipset.go
@@ -153,7 +153,7 @@ type Entry struct {
// Port is the entry's Port.
Port int
// Protocol is the entry's Protocol. The protocols of entries in the same ip set are all
- // the same. The accepted protocols are TCP and UDP.
+ // the same. The accepted protocols are TCP, UDP and SCTP.
Protocol string
// Net is the entry's IP network address. Network address with zero prefix size can NOT
// be stored.
@@ -482,10 +482,10 @@ func IsNotFoundError(err error) bool {
// checks if given protocol is supported in entry
func validateProtocol(protocol string) bool {
- if protocol == ProtocolTCP || protocol == ProtocolUDP {
+ if protocol == ProtocolTCP || protocol == ProtocolUDP || protocol == ProtocolSCTP {
return true
}
- glog.Errorf("Invalid entry's protocol: %s, supported protocols are [%s, %s]", protocol, ProtocolTCP, ProtocolUDP)
+ glog.Errorf("Invalid entry's protocol: %s, supported protocols are [%s, %s]", protocol, ProtocolTCP, ProtocolUDP, ProtocolSCTP)
return false
}
diff --git a/pkg/util/ipset/ipset_test.go b/pkg/util/ipset/ipset_test.go
index 1e0ac8fe83..f5b95da0d2 100644
--- a/pkg/util/ipset/ipset_test.go
+++ b/pkg/util/ipset/ipset_test.go
@@ -346,6 +346,22 @@ var testCases = []struct {
},
delCombinedOutputLog: []string{"ipset", "del", "SIX", "80"},
},
+ { // case 7
+ entry: &Entry{
+ IP: "192.168.1.2",
+ Port: 80,
+ Protocol: ProtocolSCTP,
+ SetType: HashIPPort,
+ },
+ set: &IPSet{
+ Name: "SETTE",
+ },
+ addCombinedOutputLog: [][]string{
+ {"ipset", "add", "SETTE", "192.168.1.2,sctp:80"},
+ {"ipset", "add", "SETTE", "192.168.1.2,sctp:80", "-exist"},
+ },
+ delCombinedOutputLog: []string{"ipset", "del", "SETTE", "192.168.1.2,sctp:80"},
+ },
}
func TestAddEntry(t *testing.T) {
@@ -755,6 +771,10 @@ func Test_validateFamily(t *testing.T) {
family: "",
valid: false,
},
+ { // case[8]
+ family: "sctp",
+ valid: false,
+ },
}
for i := range testCases {
valid := validateHashFamily(testCases[i].family)
@@ -804,6 +824,10 @@ func Test_validateProtocol(t *testing.T) {
protocol: "",
valid: false,
},
+ { // case[8]
+ protocol: ProtocolSCTP,
+ valid: true,
+ },
}
for i := range testCases {
valid := validateProtocol(testCases[i].protocol)
@@ -1401,16 +1425,16 @@ func TestValidateEntry(t *testing.T) {
},
{ // case[19]
entry: &Entry{
- SetType: HashIPPortIP,
- IP: "10.20.30.40",
- Protocol: "SCTP ",
- Port: 8090,
- IP2: "10.20.30.41",
+ SetType: HashIPPortIP,
+ IP: "10.20.30.40",
+ Protocol: ProtocolSCTP,
+ Port: 8090,
+ IP2: "10.20.30.41",
},
set: &IPSet{
- Name: "unsupported-protocol",
+ Name: "sctp",
},
- valid: false,
+ valid: true,
},
{ // case[20]
entry: &Entry{
diff --git a/pkg/util/ipset/types.go b/pkg/util/ipset/types.go
index cacfd6f8d8..0c2d16daac 100644
--- a/pkg/util/ipset/types.go
+++ b/pkg/util/ipset/types.go
@@ -49,6 +49,8 @@ const (
ProtocolTCP = "tcp"
// ProtocolUDP represents UDP protocol.
ProtocolUDP = "udp"
+ // ProtocolSCTP represents SCTP protocol.
+ ProtocolSCTP = "sctp"
)
// ValidIPSetTypes defines the supported ip set type.
diff --git a/pkg/util/ipvs/ipvs_linux.go b/pkg/util/ipvs/ipvs_linux.go
index 86c42730d2..47c640c183 100644
--- a/pkg/util/ipvs/ipvs_linux.go
+++ b/pkg/util/ipvs/ipvs_linux.go
@@ -252,6 +252,8 @@ func stringToProtocol(protocol string) uint16 {
return uint16(syscall.IPPROTO_TCP)
case "udp":
return uint16(syscall.IPPROTO_UDP)
+ case "sctp":
+ return uint16(syscall.IPPROTO_SCTP)
}
return uint16(0)
}
@@ -263,6 +265,8 @@ func protocolToString(proto Protocol) string {
return "TCP"
case syscall.IPPROTO_UDP:
return "UDP"
+ case syscall.IPPROTO_SCTP:
+ return "SCTP"
}
return ""
}
diff --git a/pkg/util/ipvs/ipvs_linux_test.go b/pkg/util/ipvs/ipvs_linux_test.go
index 9a6de6c0b5..706073870d 100644
--- a/pkg/util/ipvs/ipvs_linux_test.go
+++ b/pkg/util/ipvs/ipvs_linux_test.go
@@ -147,6 +147,30 @@ func Test_toVirtualServer(t *testing.T) {
false,
"",
},
+ {
+ libipvs.Service{
+ Protocol: syscall.IPPROTO_SCTP,
+ Port: 80,
+ FWMark: 0,
+ SchedName: "",
+ Flags: uint32(FlagPersistent + FlagHashed),
+ Timeout: 0,
+ Netmask: 0xffffffff,
+ AddressFamily: syscall.AF_INET,
+ Address: nil,
+ PEName: "",
+ },
+ VirtualServer{
+ Address: net.ParseIP("0.0.0.0"),
+ Protocol: "SCTP",
+ Port: 80,
+ Scheduler: "",
+ Flags: ServiceFlags(FlagPersistent),
+ Timeout: 0,
+ },
+ false,
+ "",
+ },
}
for i := range Tests {
@@ -359,10 +383,10 @@ func Test_toIPVSDestination(t *testing.T) {
func Test_stringToProtocol(t *testing.T) {
tests := []string{
- "TCP", "UDP", "ICMP",
+ "TCP", "UDP", "ICMP", "SCTP",
}
expected := []uint16{
- uint16(syscall.IPPROTO_TCP), uint16(syscall.IPPROTO_UDP), uint16(0),
+ uint16(syscall.IPPROTO_TCP), uint16(syscall.IPPROTO_UDP), uint16(0), uint16(syscall.IPPROTO_SCTP),
}
for i := range tests {
got := stringToProtocol(tests[i])
@@ -375,10 +399,10 @@ func Test_stringToProtocol(t *testing.T) {
func Test_protocolToString(t *testing.T) {
tests := []Protocol{
- syscall.IPPROTO_TCP, syscall.IPPROTO_UDP, Protocol(0),
+ syscall.IPPROTO_TCP, syscall.IPPROTO_UDP, Protocol(0), syscall.IPPROTO_SCTP,
}
expected := []string{
- "TCP", "UDP", "",
+ "TCP", "UDP", "", "SCTP",
}
for i := range tests {
got := protocolToString(tests[i])
diff --git a/pkg/util/ipvs/ipvs_test.go b/pkg/util/ipvs/ipvs_test.go
index ae92f02bf8..52e6b010d8 100644
--- a/pkg/util/ipvs/ipvs_test.go
+++ b/pkg/util/ipvs/ipvs_test.go
@@ -188,6 +188,46 @@ func TestVirtualServerEqual(t *testing.T) {
equal: true,
reason: "All fields equal",
},
+ {
+ svcA: &VirtualServer{
+ Address: net.ParseIP("2012::beef"),
+ Protocol: "TCP",
+ Port: 0,
+ Scheduler: "wrr",
+ Flags: 0,
+ Timeout: 0,
+ },
+ svcB: &VirtualServer{
+ Address: net.ParseIP("2012::beeef"),
+ Protocol: "SCTP",
+ Port: 0,
+ Scheduler: "wrr",
+ Flags: 0,
+ Timeout: 0,
+ },
+ equal: false,
+ reason: "Protocol not equal",
+ },
+ {
+ svcA: &VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: "SCTP",
+ Port: 80,
+ Scheduler: "rr",
+ Flags: 0x1,
+ Timeout: 10800,
+ },
+ svcB: &VirtualServer{
+ Address: net.ParseIP("1.2.3.4"),
+ Protocol: "SCTP",
+ Port: 80,
+ Scheduler: "rr",
+ Flags: 0x1,
+ Timeout: 10800,
+ },
+ equal: true,
+ reason: "All fields equal",
+ },
}
for i := range Tests {
diff --git a/pkg/util/ipvs/testing/fake_test.go b/pkg/util/ipvs/testing/fake_test.go
index c66e8930f2..c07a2617f6 100644
--- a/pkg/util/ipvs/testing/fake_test.go
+++ b/pkg/util/ipvs/testing/fake_test.go
@@ -71,12 +71,22 @@ func TestVirtualServer(t *testing.T) {
if err != nil {
t.Errorf("Unexpected error when add virtual server, error: %v", err)
}
+ // Add another virtual server
+ vs3 := &utilipvs.VirtualServer{
+ Address: net.ParseIP("10::40"),
+ Port: uint16(7777),
+ Protocol: string("SCTP"),
+ }
+ err = fake.AddVirtualServer(vs3)
+ if err != nil {
+ t.Errorf("Unexpected error when add virtual server, error: %v", err)
+ }
// List all virtual servers
list, err := fake.GetVirtualServers()
if err != nil {
t.Errorf("Fail to list virtual servers, error: %v", err)
}
- if len(list) != 2 {
+ if len(list) != 3 {
t.Errorf("Expect 2 virtual servers, got: %d", len(list))
}
// Delete a virtual server
diff --git a/staging/src/k8s.io/api/core/v1/generated.proto b/staging/src/k8s.io/api/core/v1/generated.proto
index f89b7d858b..ff47e1c827 100644
--- a/staging/src/k8s.io/api/core/v1/generated.proto
+++ b/staging/src/k8s.io/api/core/v1/generated.proto
@@ -749,7 +749,7 @@ message ContainerPort {
// This must be a valid port number, 0 < x < 65536.
optional int32 containerPort = 3;
- // Protocol for port. Must be UDP or TCP.
+ // Protocol for port. Must be UDP, TCP, or SCTP.
// Defaults to "TCP".
// +optional
optional string protocol = 4;
@@ -968,7 +968,7 @@ message EndpointPort {
optional int32 port = 2;
// The IP protocol for this port.
- // Must be UDP or TCP.
+ // Must be UDP, TCP, or SCTP.
// Default is TCP.
// +optional
optional string protocol = 3;
@@ -4148,7 +4148,7 @@ message ServicePort {
// +optional
optional string name = 1;
- // The IP protocol for this port. Supports "TCP" and "UDP".
+ // The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
// Default is TCP.
// +optional
optional string protocol = 2;
diff --git a/staging/src/k8s.io/api/core/v1/types.go b/staging/src/k8s.io/api/core/v1/types.go
index 96e7b30a3d..543d980e4f 100644
--- a/staging/src/k8s.io/api/core/v1/types.go
+++ b/staging/src/k8s.io/api/core/v1/types.go
@@ -861,6 +861,8 @@ const (
ProtocolTCP Protocol = "TCP"
// ProtocolUDP is the UDP protocol.
ProtocolUDP Protocol = "UDP"
+ // ProtocolSCTP is the SCTP protocol.
+ ProtocolSCTP Protocol = "SCTP"
)
// Represents a Persistent Disk resource in Google Compute Engine.
@@ -1662,7 +1664,7 @@ type ContainerPort struct {
// Number of port to expose on the pod's IP address.
// This must be a valid port number, 0 < x < 65536.
ContainerPort int32 `json:"containerPort" protobuf:"varint,3,opt,name=containerPort"`
- // Protocol for port. Must be UDP or TCP.
+ // Protocol for port. Must be UDP, TCP, or SCTP.
// Defaults to "TCP".
// +optional
Protocol Protocol `json:"protocol,omitempty" protobuf:"bytes,4,opt,name=protocol,casttype=Protocol"`
@@ -3515,7 +3517,7 @@ type ServicePort struct {
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
- // The IP protocol for this port. Supports "TCP" and "UDP".
+ // The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
// Default is TCP.
// +optional
Protocol Protocol `json:"protocol,omitempty" protobuf:"bytes,2,opt,name=protocol,casttype=Protocol"`
@@ -3729,7 +3731,7 @@ type EndpointPort struct {
Port int32 `json:"port" protobuf:"varint,2,opt,name=port"`
// The IP protocol for this port.
- // Must be UDP or TCP.
+ // Must be UDP, TCP, or SCTP.
// Default is TCP.
// +optional
Protocol Protocol `json:"protocol,omitempty" protobuf:"bytes,3,opt,name=protocol,casttype=Protocol"`
diff --git a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go
index 13d5c78980..e83641754c 100644
--- a/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go
+++ b/staging/src/k8s.io/api/core/v1/types_swagger_doc_generated.go
@@ -353,7 +353,7 @@ var map_ContainerPort = map[string]string{
"name": "If specified, this must be an IANA_SVC_NAME and unique within the pod. Each named port in a pod must have a unique name. Name for the port that can be referred to by services.",
"hostPort": "Number of port to expose on the host. If specified, this must be a valid port number, 0 < x < 65536. If HostNetwork is specified, this must match ContainerPort. Most containers do not need this.",
"containerPort": "Number of port to expose on the pod's IP address. This must be a valid port number, 0 < x < 65536.",
- "protocol": "Protocol for port. Must be UDP or TCP. Defaults to \"TCP\".",
+ "protocol": "Protocol for port. Must be UDP, TCP, or SCTP. Defaults to \"TCP\".",
"hostIP": "What host IP to bind the external port to.",
}
@@ -488,7 +488,7 @@ var map_EndpointPort = map[string]string{
"": "EndpointPort is a tuple that describes a single port.",
"name": "The name of this port (corresponds to ServicePort.Name). Must be a DNS_LABEL. Optional only if one port is defined.",
"port": "The port number of the endpoint.",
- "protocol": "The IP protocol for this port. Must be UDP or TCP. Default is TCP.",
+ "protocol": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.",
}
func (EndpointPort) SwaggerDoc() map[string]string {
@@ -2058,7 +2058,7 @@ func (ServiceList) SwaggerDoc() map[string]string {
var map_ServicePort = map[string]string{
"": "ServicePort contains information on service's port.",
"name": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. This maps to the 'Name' field in EndpointPort objects. Optional if only one ServicePort is defined on this service.",
- "protocol": "The IP protocol for this port. Supports \"TCP\" and \"UDP\". Default is TCP.",
+ "protocol": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.",
"port": "The port that will be exposed by this service.",
"targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service",
"nodePort": "The port on each node on which this service is exposed when type=NodePort or LoadBalancer. Usually assigned by the system. If specified, it will be allocated to the service if unused or else creation of the service will fail. Default is to auto-allocate a port if the ServiceType of this Service requires one. More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport",
diff --git a/staging/src/k8s.io/api/extensions/v1beta1/generated.proto b/staging/src/k8s.io/api/extensions/v1beta1/generated.proto
index 1d40fd6c7e..1cba30a17c 100644
--- a/staging/src/k8s.io/api/extensions/v1beta1/generated.proto
+++ b/staging/src/k8s.io/api/extensions/v1beta1/generated.proto
@@ -712,7 +712,7 @@ message NetworkPolicyPeer {
// DEPRECATED 1.9 - This group version of NetworkPolicyPort is deprecated by networking/v1/NetworkPolicyPort.
message NetworkPolicyPort {
- // Optional. The protocol (TCP or UDP) which traffic must match.
+ // Optional. The protocol (TCP, UDP, or SCTP) which traffic must match.
// If not specified, this field defaults to TCP.
// +optional
optional string protocol = 1;
diff --git a/staging/src/k8s.io/api/extensions/v1beta1/types.go b/staging/src/k8s.io/api/extensions/v1beta1/types.go
index a2b17822c2..b2780368d9 100644
--- a/staging/src/k8s.io/api/extensions/v1beta1/types.go
+++ b/staging/src/k8s.io/api/extensions/v1beta1/types.go
@@ -1275,7 +1275,7 @@ type NetworkPolicyEgressRule struct {
// DEPRECATED 1.9 - This group version of NetworkPolicyPort is deprecated by networking/v1/NetworkPolicyPort.
type NetworkPolicyPort struct {
- // Optional. The protocol (TCP or UDP) which traffic must match.
+ // Optional. The protocol (TCP, UDP, or SCTP) which traffic must match.
// If not specified, this field defaults to TCP.
// +optional
Protocol *v1.Protocol `json:"protocol,omitempty" protobuf:"bytes,1,opt,name=protocol,casttype=k8s.io/api/core/v1.Protocol"`
diff --git a/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go
index c9ffadec1e..268d2c4fcc 100644
--- a/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go
+++ b/staging/src/k8s.io/api/extensions/v1beta1/types_swagger_doc_generated.go
@@ -419,7 +419,7 @@ func (NetworkPolicyPeer) SwaggerDoc() map[string]string {
var map_NetworkPolicyPort = map[string]string{
"": "DEPRECATED 1.9 - This group version of NetworkPolicyPort is deprecated by networking/v1/NetworkPolicyPort.",
- "protocol": "Optional. The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.",
+ "protocol": "Optional. The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.",
"port": "If specified, the port on the given protocol. This can either be a numerical or named port on a pod. If this field is not provided, this matches all port names and numbers. If present, only traffic on the specified protocol AND port will be matched.",
}
diff --git a/staging/src/k8s.io/api/networking/v1/generated.proto b/staging/src/k8s.io/api/networking/v1/generated.proto
index eacf0ed90e..4e068d08f0 100644
--- a/staging/src/k8s.io/api/networking/v1/generated.proto
+++ b/staging/src/k8s.io/api/networking/v1/generated.proto
@@ -138,7 +138,7 @@ message NetworkPolicyPeer {
// NetworkPolicyPort describes a port to allow traffic on
message NetworkPolicyPort {
- // The protocol (TCP or UDP) which traffic must match. If not specified, this
+ // The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this
// field defaults to TCP.
// +optional
optional string protocol = 1;
diff --git a/staging/src/k8s.io/api/networking/v1/types.go b/staging/src/k8s.io/api/networking/v1/types.go
index e1b81fdc7c..ce70448d32 100644
--- a/staging/src/k8s.io/api/networking/v1/types.go
+++ b/staging/src/k8s.io/api/networking/v1/types.go
@@ -136,7 +136,7 @@ type NetworkPolicyEgressRule struct {
// NetworkPolicyPort describes a port to allow traffic on
type NetworkPolicyPort struct {
- // The protocol (TCP or UDP) which traffic must match. If not specified, this
+ // The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this
// field defaults to TCP.
// +optional
Protocol *v1.Protocol `json:"protocol,omitempty" protobuf:"bytes,1,opt,name=protocol,casttype=k8s.io/api/core/v1.Protocol"`
diff --git a/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go
index af2553a9df..f4363bc09e 100644
--- a/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go
+++ b/staging/src/k8s.io/api/networking/v1/types_swagger_doc_generated.go
@@ -90,7 +90,7 @@ func (NetworkPolicyPeer) SwaggerDoc() map[string]string {
var map_NetworkPolicyPort = map[string]string{
"": "NetworkPolicyPort describes a port to allow traffic on",
- "protocol": "The protocol (TCP or UDP) which traffic must match. If not specified, this field defaults to TCP.",
+ "protocol": "The protocol (TCP, UDP, or SCTP) which traffic must match. If not specified, this field defaults to TCP.",
"port": "The port on the given protocol. This can either be a numerical or named port on a pod. If this field is not provided, this matches all port names and numbers.",
}