Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
b82c028f
Commit
b82c028f
authored
Aug 15, 2016
by
Girish Kalele
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
GCE Cloud provider changes for ESIPP
Add feature gate (ExternalTrafficLocalOnly) for alpha feature
parent
4b554925
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
418 additions
and
16 deletions
+418
-16
annotations.go
pkg/api/service/annotations.go
+61
-0
gce.go
pkg/cloudprovider/providers/gce/gce.go
+107
-8
proxier.go
pkg/proxy/iptables/proxier.go
+0
-0
rest.go
pkg/registry/service/rest.go
+40
-0
rest_test.go
pkg/registry/service/rest_test.go
+183
-2
feature_gate.go
pkg/util/config/feature_gate.go
+13
-2
Makefile
test/images/netexec/Makefile
+1
-1
netexec.go
test/images/netexec/netexec.go
+13
-3
No files found.
pkg/api/service/annotations.go
View file @
b82c028f
...
@@ -16,6 +16,13 @@ limitations under the License.
...
@@ -16,6 +16,13 @@ limitations under the License.
package
service
package
service
import
(
"strconv"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
)
const
(
const
(
// AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers
// AnnotationLoadBalancerSourceRangesKey is the key of the annotation on a service to set allowed ingress ranges on their LoadBalancers
//
//
...
@@ -25,4 +32,58 @@ const (
...
@@ -25,4 +32,58 @@ const (
//
//
// Not all cloud providers support this annotation, though AWS & GCE do.
// Not all cloud providers support this annotation, though AWS & GCE do.
AnnotationLoadBalancerSourceRangesKey
=
"service.beta.kubernetes.io/load-balancer-source-ranges"
AnnotationLoadBalancerSourceRangesKey
=
"service.beta.kubernetes.io/load-balancer-source-ranges"
// AnnotationExternalTraffic An annotation that denotes if this Service desires to route external traffic to local
// endpoints only. This preserves Source IP and avoids a second hop.
AnnotationExternalTraffic
=
"service.alpha.kubernetes.io/external-traffic"
// AnnotationValueExternalTrafficLocal Value of annotation to specify local endpoints behaviour
AnnotationValueExternalTrafficLocal
=
"OnlyLocal"
// AnnotationValueExternalTrafficGlobal Value of annotation to specify global (legacy) behaviour
AnnotationValueExternalTrafficGlobal
=
"Global"
// AnnotationHealthCheckNodePort Annotation specifying the healthcheck nodePort for the service
// If not specified, annotation is created by the service api backend with the allocated nodePort
// Will use user-specified nodePort value if specified by the client
AnnotationHealthCheckNodePort
=
"service.alpha.kubernetes.io/healthcheck-nodeport"
)
)
// NeedsHealthCheck Check service for health check annotations
func
NeedsHealthCheck
(
service
*
api
.
Service
)
bool
{
if
l
,
ok
:=
service
.
Annotations
[
AnnotationExternalTraffic
];
ok
{
if
l
==
AnnotationValueExternalTrafficLocal
{
return
true
}
else
if
l
==
AnnotationValueExternalTrafficGlobal
{
return
false
}
else
{
glog
.
Errorf
(
"Invalid value for annotation %v"
,
AnnotationExternalTraffic
)
return
false
}
}
return
false
}
// GetServiceHealthCheckNodePort Return health check node port annotation for service, if one exists
func
GetServiceHealthCheckNodePort
(
service
*
api
.
Service
)
int32
{
if
NeedsHealthCheck
(
service
)
{
if
l
,
ok
:=
service
.
Annotations
[
AnnotationHealthCheckNodePort
];
ok
{
p
,
err
:=
strconv
.
Atoi
(
l
)
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to parse annotation %v: %v"
,
AnnotationHealthCheckNodePort
,
err
)
return
0
}
return
int32
(
p
)
}
}
return
0
}
// GetServiceHealthCheckPathPort Return the path and nodePort programmed into the Cloud LB Health Check
func
GetServiceHealthCheckPathPort
(
service
*
api
.
Service
)
(
string
,
int32
)
{
if
!
NeedsHealthCheck
(
service
)
{
return
""
,
0
}
port
:=
GetServiceHealthCheckNodePort
(
service
)
if
port
==
0
{
return
""
,
0
}
return
"/healthz"
,
port
}
pkg/cloudprovider/providers/gce/gce.go
View file @
b82c028f
...
@@ -31,7 +31,7 @@ import (
...
@@ -31,7 +31,7 @@ import (
"gopkg.in/gcfg.v1"
"gopkg.in/gcfg.v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/service"
apiservice
"k8s.io/kubernetes/pkg/api/service"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/cloudprovider"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/types"
...
@@ -70,8 +70,16 @@ const (
...
@@ -70,8 +70,16 @@ const (
// were to continuously return a nextPageToken.
// were to continuously return a nextPageToken.
maxPages
=
25
maxPages
=
25
// Target Pool creation is limited to 200 instances.
maxTargetPoolCreateInstances
=
200
maxTargetPoolCreateInstances
=
200
// HTTP Load Balancer parameters
// Configure 2 second period for external health checks.
gceHcCheckIntervalSeconds
=
int64
(
2
)
gceHcTimeoutSeconds
=
int64
(
1
)
// Start sending requests as soon as a pod is found on the node.
gceHcHealthyThreshold
=
int64
(
1
)
// Defaults to 5 * 2 = 10 seconds before the LB will steer traffic away
gceHcUnhealthyThreshold
=
int64
(
5
)
)
)
// GCECloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine.
// GCECloud is an implementation of Interface, LoadBalancer and Instances for Google Compute Engine.
...
@@ -665,7 +673,7 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
...
@@ -665,7 +673,7 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
// is because the forwarding rule is used as the indicator that the load
// is because the forwarding rule is used as the indicator that the load
// balancer is fully created - it's what getLoadBalancer checks for.
// balancer is fully created - it's what getLoadBalancer checks for.
// Check if user specified the allow source range
// Check if user specified the allow source range
sourceRanges
,
err
:=
service
.
GetLoadBalancerSourceRanges
(
apiService
)
sourceRanges
,
err
:=
api
service
.
GetLoadBalancerSourceRanges
(
apiService
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
err
return
nil
,
err
}
}
...
@@ -720,7 +728,18 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
...
@@ -720,7 +728,18 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
glog
.
Infof
(
"EnsureLoadBalancer(%v(%v)): deleted forwarding rule"
,
loadBalancerName
,
serviceName
)
glog
.
Infof
(
"EnsureLoadBalancer(%v(%v)): deleted forwarding rule"
,
loadBalancerName
,
serviceName
)
}
}
if
tpExists
&&
tpNeedsUpdate
{
if
tpExists
&&
tpNeedsUpdate
{
if
err
:=
gce
.
deleteTargetPool
(
loadBalancerName
,
gce
.
region
);
err
!=
nil
{
// Generate the list of health checks for this target pool to pass to deleteTargetPool
var
hc
*
compute
.
HttpHealthCheck
if
path
,
_
:=
apiservice
.
GetServiceHealthCheckPathPort
(
apiService
);
path
!=
""
{
var
err
error
hc
,
err
=
gce
.
GetHttpHealthCheck
(
loadBalancerName
)
if
err
!=
nil
&&
!
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
glog
.
Infof
(
"Failed to retrieve health check %v:%v"
,
loadBalancerName
,
err
)
}
}
// Pass healthchecks to deleteTargetPool to cleanup health checks prior to cleaning up the target pool itself.
if
err
:=
gce
.
deleteTargetPool
(
loadBalancerName
,
gce
.
region
,
hc
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to delete existing target pool %s for load balancer update: %v"
,
loadBalancerName
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to delete existing target pool %s for load balancer update: %v"
,
loadBalancerName
,
err
)
}
}
glog
.
Infof
(
"EnsureLoadBalancer(%v(%v)): deleted target pool"
,
loadBalancerName
,
serviceName
)
glog
.
Infof
(
"EnsureLoadBalancer(%v(%v)): deleted target pool"
,
loadBalancerName
,
serviceName
)
...
@@ -734,7 +753,18 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
...
@@ -734,7 +753,18 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
createInstances
=
createInstances
[
:
maxTargetPoolCreateInstances
]
createInstances
=
createInstances
[
:
maxTargetPoolCreateInstances
]
}
}
if
err
:=
gce
.
createTargetPool
(
loadBalancerName
,
serviceName
.
String
(),
gce
.
region
,
createInstances
,
affinityType
);
err
!=
nil
{
// Create health checks for this target pool to pass to createTargetPool for health check links
var
hc
*
compute
.
HttpHealthCheck
if
path
,
healthCheckNodePort
:=
apiservice
.
GetServiceHealthCheckPathPort
(
apiService
);
path
!=
""
{
glog
.
Infof
(
"service %v needs health checks on :%d/%s)"
,
apiService
.
Name
,
healthCheckNodePort
,
path
)
var
err
error
hc
,
err
=
gce
.
ensureHttpHealthCheck
(
loadBalancerName
,
path
,
healthCheckNodePort
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Failed to create health check for localized service %v on node port %v: %v"
,
loadBalancerName
,
healthCheckNodePort
,
err
)
}
}
// Pass healthchecks to createTargetPool which needs them as health check links in the target pool
if
err
:=
gce
.
createTargetPool
(
loadBalancerName
,
serviceName
.
String
(),
gce
.
region
,
createInstances
,
affinityType
,
hc
);
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"failed to create target pool %s: %v"
,
loadBalancerName
,
err
)
return
nil
,
fmt
.
Errorf
(
"failed to create target pool %s: %v"
,
loadBalancerName
,
err
)
}
}
if
len
(
hosts
)
<=
maxTargetPoolCreateInstances
{
if
len
(
hosts
)
<=
maxTargetPoolCreateInstances
{
...
@@ -767,9 +797,53 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
...
@@ -767,9 +797,53 @@ func (gce *GCECloud) EnsureLoadBalancer(clusterName string, apiService *api.Serv
status
:=
&
api
.
LoadBalancerStatus
{}
status
:=
&
api
.
LoadBalancerStatus
{}
status
.
Ingress
=
[]
api
.
LoadBalancerIngress
{{
IP
:
ipAddress
}}
status
.
Ingress
=
[]
api
.
LoadBalancerIngress
{{
IP
:
ipAddress
}}
return
status
,
nil
return
status
,
nil
}
}
func
makeHealthCheckDescription
(
serviceName
string
)
string
{
return
fmt
.
Sprintf
(
`{"kubernetes.io/service-name":"%s"}`
,
serviceName
)
}
func
(
gce
*
GCECloud
)
ensureHttpHealthCheck
(
name
,
path
string
,
port
int32
)
(
hc
*
compute
.
HttpHealthCheck
,
err
error
)
{
newHC
:=
&
compute
.
HttpHealthCheck
{
Name
:
name
,
Port
:
int64
(
port
),
RequestPath
:
path
,
Host
:
""
,
Description
:
makeHealthCheckDescription
(
name
),
CheckIntervalSec
:
gceHcCheckIntervalSeconds
,
TimeoutSec
:
gceHcTimeoutSeconds
,
HealthyThreshold
:
gceHcHealthyThreshold
,
UnhealthyThreshold
:
gceHcUnhealthyThreshold
,
}
hc
,
err
=
gce
.
GetHttpHealthCheck
(
name
)
if
hc
==
nil
||
err
!=
nil
&&
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
glog
.
Infof
(
"Did not find health check %v, creating port %v path %v"
,
name
,
port
,
path
)
if
err
=
gce
.
CreateHttpHealthCheck
(
newHC
);
err
!=
nil
{
return
nil
,
err
}
hc
,
err
=
gce
.
GetHttpHealthCheck
(
name
)
if
err
!=
nil
{
glog
.
Errorf
(
"Failed to get http health check %v"
,
err
)
return
nil
,
err
}
return
hc
,
nil
}
// Validate health check fields
drift
:=
hc
.
Port
!=
int64
(
port
)
||
hc
.
RequestPath
!=
path
||
hc
.
Description
!=
makeHealthCheckDescription
(
name
)
drift
=
drift
||
hc
.
CheckIntervalSec
!=
gceHcCheckIntervalSeconds
||
hc
.
TimeoutSec
!=
gceHcTimeoutSeconds
drift
=
drift
||
hc
.
UnhealthyThreshold
!=
gceHcUnhealthyThreshold
||
hc
.
HealthyThreshold
!=
gceHcHealthyThreshold
if
drift
{
glog
.
Infof
(
"Health check %v exists but parameters have drifted - updating"
,
name
)
if
err
:=
gce
.
UpdateHttpHealthCheck
(
newHC
);
err
!=
nil
{
return
nil
,
err
}
}
return
hc
,
nil
}
// Passing nil for requested IP is perfectly fine - it just means that no specific
// Passing nil for requested IP is perfectly fine - it just means that no specific
// IP is being requested.
// IP is being requested.
// Returns whether the forwarding rule exists, whether it needs to be updated,
// Returns whether the forwarding rule exists, whether it needs to be updated,
...
@@ -962,16 +1036,24 @@ func (gce *GCECloud) createForwardingRule(name, serviceName, region, ipAddress s
...
@@ -962,16 +1036,24 @@ func (gce *GCECloud) createForwardingRule(name, serviceName, region, ipAddress s
return
nil
return
nil
}
}
func
(
gce
*
GCECloud
)
createTargetPool
(
name
,
serviceName
,
region
string
,
hosts
[]
*
gceInstance
,
affinityType
api
.
ServiceAffinity
)
error
{
func
(
gce
*
GCECloud
)
createTargetPool
(
name
,
serviceName
,
region
string
,
hosts
[]
*
gceInstance
,
affinityType
api
.
ServiceAffinity
,
hc
*
compute
.
HttpHealthCheck
)
error
{
var
instances
[]
string
var
instances
[]
string
for
_
,
host
:=
range
hosts
{
for
_
,
host
:=
range
hosts
{
instances
=
append
(
instances
,
makeHostURL
(
gce
.
projectID
,
host
.
Zone
,
host
.
Name
))
instances
=
append
(
instances
,
makeHostURL
(
gce
.
projectID
,
host
.
Zone
,
host
.
Name
))
}
}
hcLinks
:=
[]
string
{}
if
hc
!=
nil
{
hcLinks
=
append
(
hcLinks
,
hc
.
SelfLink
)
}
if
len
(
hcLinks
)
>
0
{
glog
.
Infof
(
"Creating targetpool %v with healthchecking"
,
name
)
}
pool
:=
&
compute
.
TargetPool
{
pool
:=
&
compute
.
TargetPool
{
Name
:
name
,
Name
:
name
,
Description
:
fmt
.
Sprintf
(
`{"kubernetes.io/service-name":"%s"}`
,
serviceName
),
Description
:
fmt
.
Sprintf
(
`{"kubernetes.io/service-name":"%s"}`
,
serviceName
),
Instances
:
instances
,
Instances
:
instances
,
SessionAffinity
:
translateAffinityType
(
affinityType
),
SessionAffinity
:
translateAffinityType
(
affinityType
),
HealthChecks
:
hcLinks
,
}
}
op
,
err
:=
gce
.
service
.
TargetPools
.
Insert
(
gce
.
projectID
,
region
,
pool
)
.
Do
()
op
,
err
:=
gce
.
service
.
TargetPools
.
Insert
(
gce
.
projectID
,
region
,
pool
)
.
Do
()
if
err
!=
nil
&&
!
isHTTPErrorCode
(
err
,
http
.
StatusConflict
)
{
if
err
!=
nil
&&
!
isHTTPErrorCode
(
err
,
http
.
StatusConflict
)
{
...
@@ -1279,6 +1361,16 @@ func (gce *GCECloud) EnsureLoadBalancerDeleted(clusterName string, service *api.
...
@@ -1279,6 +1361,16 @@ func (gce *GCECloud) EnsureLoadBalancerDeleted(clusterName string, service *api.
glog
.
V
(
2
)
.
Infof
(
"EnsureLoadBalancerDeleted(%v, %v, %v, %v, %v)"
,
clusterName
,
service
.
Namespace
,
service
.
Name
,
loadBalancerName
,
glog
.
V
(
2
)
.
Infof
(
"EnsureLoadBalancerDeleted(%v, %v, %v, %v, %v)"
,
clusterName
,
service
.
Namespace
,
service
.
Name
,
loadBalancerName
,
gce
.
region
)
gce
.
region
)
var
hc
*
compute
.
HttpHealthCheck
if
path
,
_
:=
apiservice
.
GetServiceHealthCheckPathPort
(
service
);
path
!=
""
{
var
err
error
hc
,
err
=
gce
.
GetHttpHealthCheck
(
loadBalancerName
)
if
err
!=
nil
&&
!
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
glog
.
Infof
(
"Failed to retrieve health check %v:%v"
,
loadBalancerName
,
err
)
return
err
}
}
errs
:=
utilerrors
.
AggregateGoroutines
(
errs
:=
utilerrors
.
AggregateGoroutines
(
func
()
error
{
return
gce
.
deleteFirewall
(
loadBalancerName
,
gce
.
region
)
},
func
()
error
{
return
gce
.
deleteFirewall
(
loadBalancerName
,
gce
.
region
)
},
// Even though we don't hold on to static IPs for load balancers, it's
// Even though we don't hold on to static IPs for load balancers, it's
...
@@ -1291,7 +1383,7 @@ func (gce *GCECloud) EnsureLoadBalancerDeleted(clusterName string, service *api.
...
@@ -1291,7 +1383,7 @@ func (gce *GCECloud) EnsureLoadBalancerDeleted(clusterName string, service *api.
if
err
:=
gce
.
deleteForwardingRule
(
loadBalancerName
,
gce
.
region
);
err
!=
nil
{
if
err
:=
gce
.
deleteForwardingRule
(
loadBalancerName
,
gce
.
region
);
err
!=
nil
{
return
err
return
err
}
}
if
err
:=
gce
.
deleteTargetPool
(
loadBalancerName
,
gce
.
region
);
err
!=
nil
{
if
err
:=
gce
.
deleteTargetPool
(
loadBalancerName
,
gce
.
region
,
hc
);
err
!=
nil
{
return
err
return
err
}
}
return
nil
return
nil
...
@@ -1319,7 +1411,14 @@ func (gce *GCECloud) deleteForwardingRule(name, region string) error {
...
@@ -1319,7 +1411,14 @@ func (gce *GCECloud) deleteForwardingRule(name, region string) error {
return
nil
return
nil
}
}
func
(
gce
*
GCECloud
)
deleteTargetPool
(
name
,
region
string
)
error
{
func
(
gce
*
GCECloud
)
deleteTargetPool
(
name
,
region
string
,
hc
*
compute
.
HttpHealthCheck
)
error
{
if
hc
!=
nil
{
glog
.
Infof
(
"Deleting health check %v"
,
hc
.
Name
)
if
err
:=
gce
.
DeleteHttpHealthCheck
(
hc
.
Name
);
err
!=
nil
{
glog
.
Warningf
(
"Failed to delete health check %v: %v"
,
hc
,
err
)
return
err
}
}
op
,
err
:=
gce
.
service
.
TargetPools
.
Delete
(
gce
.
projectID
,
region
,
name
)
.
Do
()
op
,
err
:=
gce
.
service
.
TargetPools
.
Delete
(
gce
.
projectID
,
region
,
name
)
.
Do
()
if
err
!=
nil
&&
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
if
err
!=
nil
&&
isHTTPErrorCode
(
err
,
http
.
StatusNotFound
)
{
glog
.
Infof
(
"Target pool %s already deleted. Continuing to delete other resources."
,
name
)
glog
.
Infof
(
"Target pool %s already deleted. Continuing to delete other resources."
,
name
)
...
...
pkg/proxy/iptables/proxier.go
View file @
b82c028f
This diff is collapsed.
Click to expand it.
pkg/registry/service/rest.go
View file @
b82c028f
...
@@ -28,12 +28,14 @@ import (
...
@@ -28,12 +28,14 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/rest"
apiservice
"k8s.io/kubernetes/pkg/api/service"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/registry/endpoint"
"k8s.io/kubernetes/pkg/registry/endpoint"
"k8s.io/kubernetes/pkg/registry/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime"
featuregate
"k8s.io/kubernetes/pkg/util/config"
utilnet
"k8s.io/kubernetes/pkg/util/net"
utilnet
"k8s.io/kubernetes/pkg/util/net"
"k8s.io/kubernetes/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/watch"
"k8s.io/kubernetes/pkg/watch"
...
@@ -133,6 +135,35 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err
...
@@ -133,6 +135,35 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, err
}
}
}
}
if
featuregate
.
DefaultFeatureGate
.
ExternalTrafficLocalOnly
()
&&
shouldCheckOrAssignHealthCheckNodePort
(
service
)
{
var
healthCheckNodePort
int
var
err
error
if
l
,
ok
:=
service
.
Annotations
[
apiservice
.
AnnotationHealthCheckNodePort
];
ok
{
healthCheckNodePort
,
err
=
strconv
.
Atoi
(
l
)
if
err
!=
nil
||
healthCheckNodePort
<=
0
{
return
nil
,
errors
.
NewInternalError
(
fmt
.
Errorf
(
"Failed to parse annotation %v: %v"
,
apiservice
.
AnnotationHealthCheckNodePort
,
err
))
}
}
if
healthCheckNodePort
>
0
{
// If the request has a health check nodePort in mind, attempt to reserve it
err
:=
nodePortOp
.
Allocate
(
int
(
healthCheckNodePort
))
if
err
!=
nil
{
return
nil
,
errors
.
NewInternalError
(
fmt
.
Errorf
(
"Failed to allocate requested HealthCheck nodePort %v: %v"
,
healthCheckNodePort
,
err
))
}
}
else
{
// If the request has no health check nodePort specified, allocate any
healthCheckNodePort
,
err
=
nodePortOp
.
AllocateNext
()
if
err
!=
nil
{
// TODO: what error should be returned here? It's not a
// field-level validation failure (the field is valid), and it's
// not really an internal error.
return
nil
,
errors
.
NewInternalError
(
fmt
.
Errorf
(
"failed to allocate a nodePort: %v"
,
err
))
}
// Insert the newly allocated health check port as an annotation (plan of record for Alpha)
service
.
Annotations
[
apiservice
.
AnnotationHealthCheckNodePort
]
=
fmt
.
Sprintf
(
"%d"
,
healthCheckNodePort
)
}
}
out
,
err
:=
rs
.
registry
.
CreateService
(
ctx
,
service
)
out
,
err
:=
rs
.
registry
.
CreateService
(
ctx
,
service
)
if
err
!=
nil
{
if
err
!=
nil
{
err
=
rest
.
CheckGeneratedNameError
(
Strategy
,
err
,
service
)
err
=
rest
.
CheckGeneratedNameError
(
Strategy
,
err
,
service
)
...
@@ -398,3 +429,12 @@ func shouldAssignNodePorts(service *api.Service) bool {
...
@@ -398,3 +429,12 @@ func shouldAssignNodePorts(service *api.Service) bool {
return
false
return
false
}
}
}
}
func
shouldCheckOrAssignHealthCheckNodePort
(
service
*
api
.
Service
)
bool
{
if
service
.
Spec
.
Type
==
api
.
ServiceTypeLoadBalancer
{
// True if Service-type == LoadBalancer AND annotation AnnotationExternalTraffic present
return
(
featuregate
.
DefaultFeatureGate
.
ExternalTrafficLocalOnly
()
&&
apiservice
.
NeedsHealthCheck
(
service
))
}
glog
.
V
(
4
)
.
Infof
(
"Service type: %v does not need health check node port"
,
service
.
Spec
.
Type
)
return
false
}
pkg/registry/service/rest_test.go
View file @
b82c028f
...
@@ -24,14 +24,19 @@ import (
...
@@ -24,14 +24,19 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/api/service"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/registry/registrytest"
"k8s.io/kubernetes/pkg/registry/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/service/ipallocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
"k8s.io/kubernetes/pkg/registry/service/portallocator"
utilnet
"k8s.io/kubernetes/pkg/util/net"
featuregate
"k8s.io/kubernetes/pkg/util/config"
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/util/intstr"
utilnet
"k8s.io/kubernetes/pkg/util/net"
)
)
func
init
()
{
featuregate
.
DefaultFeatureGate
.
Set
(
"AllowExtTrafficLocalEndpoints=true"
)
}
// TODO(wojtek-t): Cleanup this file.
// TODO(wojtek-t): Cleanup this file.
// It is now testing mostly the same things as other resources but
// It is now testing mostly the same things as other resources but
// in a completely different way. We should unify it.
// in a completely different way. We should unify it.
...
@@ -821,3 +826,179 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) {
...
@@ -821,3 +826,179 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) {
t
.
Errorf
(
"Expected 'Service.Namespace does not match the provided context' error, got '%s'"
,
err
.
Error
())
t
.
Errorf
(
"Expected 'Service.Namespace does not match the provided context' error, got '%s'"
,
err
.
Error
())
}
}
}
}
// Validate allocation of a nodePort when the externalTraffic=OnlyLocal annotation is set
// and type is LoadBalancer
func
TestServiceRegistryExternalTrafficAnnotationHealthCheckNodePortAllocation
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
NewTestREST
(
t
,
nil
)
svc
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"external-lb-esipp"
,
Annotations
:
map
[
string
]
string
{
service
.
AnnotationExternalTraffic
:
service
.
AnnotationValueExternalTrafficLocal
,
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
SessionAffinity
:
api
.
ServiceAffinityNone
,
Type
:
api
.
ServiceTypeLoadBalancer
,
Ports
:
[]
api
.
ServicePort
{{
Port
:
6502
,
Protocol
:
api
.
ProtocolTCP
,
TargetPort
:
intstr
.
FromInt
(
6502
),
}},
},
}
created_svc
,
err
:=
storage
.
Create
(
ctx
,
svc
)
if
created_svc
==
nil
||
err
!=
nil
{
t
.
Errorf
(
"Unexpected failure creating service %v"
,
err
)
}
created_service
:=
created_svc
.
(
*
api
.
Service
)
if
!
service
.
NeedsHealthCheck
(
created_service
)
{
t
.
Errorf
(
"Unexpected missing annotation %s"
,
service
.
AnnotationExternalTraffic
)
}
port
:=
service
.
GetServiceHealthCheckNodePort
(
created_service
)
if
port
==
0
{
t
.
Errorf
(
"Failed to allocate and create the health check node port annotation %s"
,
service
.
AnnotationHealthCheckNodePort
)
}
}
// Validate using the user specified nodePort when the externalTraffic=OnlyLocal annotation is set
// and type is LoadBalancer
func
TestServiceRegistryExternalTrafficAnnotationHealthCheckNodePortUserAllocation
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
NewTestREST
(
t
,
nil
)
svc
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"external-lb-esipp"
,
Annotations
:
map
[
string
]
string
{
service
.
AnnotationExternalTraffic
:
service
.
AnnotationValueExternalTrafficLocal
,
service
.
AnnotationHealthCheckNodePort
:
"30200"
,
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
SessionAffinity
:
api
.
ServiceAffinityNone
,
Type
:
api
.
ServiceTypeLoadBalancer
,
Ports
:
[]
api
.
ServicePort
{{
Port
:
6502
,
Protocol
:
api
.
ProtocolTCP
,
TargetPort
:
intstr
.
FromInt
(
6502
),
}},
},
}
created_svc
,
err
:=
storage
.
Create
(
ctx
,
svc
)
if
created_svc
==
nil
||
err
!=
nil
{
t
.
Errorf
(
"Unexpected failure creating service %v"
,
err
)
}
created_service
:=
created_svc
.
(
*
api
.
Service
)
if
!
service
.
NeedsHealthCheck
(
created_service
)
{
t
.
Errorf
(
"Unexpected missing annotation %s"
,
service
.
AnnotationExternalTraffic
)
}
port
:=
service
.
GetServiceHealthCheckNodePort
(
created_service
)
if
port
==
0
{
t
.
Errorf
(
"Failed to allocate and create the health check node port annotation %s"
,
service
.
AnnotationHealthCheckNodePort
)
}
if
port
!=
30200
{
t
.
Errorf
(
"Failed to allocate requested nodePort expected 30200, got %d"
,
port
)
}
}
// Validate that the service creation fails when the requested port number is -1
func
TestServiceRegistryExternalTrafficAnnotationNegative
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
NewTestREST
(
t
,
nil
)
svc
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"external-lb-esipp"
,
Annotations
:
map
[
string
]
string
{
service
.
AnnotationExternalTraffic
:
service
.
AnnotationValueExternalTrafficLocal
,
service
.
AnnotationHealthCheckNodePort
:
"-1"
,
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
SessionAffinity
:
api
.
ServiceAffinityNone
,
Type
:
api
.
ServiceTypeLoadBalancer
,
Ports
:
[]
api
.
ServicePort
{{
Port
:
6502
,
Protocol
:
api
.
ProtocolTCP
,
TargetPort
:
intstr
.
FromInt
(
6502
),
}},
},
}
created_svc
,
err
:=
storage
.
Create
(
ctx
,
svc
)
if
created_svc
==
nil
||
err
!=
nil
{
return
}
t
.
Errorf
(
"Unexpected creation of service with invalid healthCheckNodePort specified"
)
}
// Validate that the health check nodePort is not allocated when the externalTraffic annotation is !"OnlyLocal"
func
TestServiceRegistryExternalTrafficAnnotationGlobal
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
NewTestREST
(
t
,
nil
)
svc
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"external-lb-esipp"
,
Annotations
:
map
[
string
]
string
{
service
.
AnnotationExternalTraffic
:
service
.
AnnotationValueExternalTrafficGlobal
,
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
SessionAffinity
:
api
.
ServiceAffinityNone
,
Type
:
api
.
ServiceTypeLoadBalancer
,
Ports
:
[]
api
.
ServicePort
{{
Port
:
6502
,
Protocol
:
api
.
ProtocolTCP
,
TargetPort
:
intstr
.
FromInt
(
6502
),
}},
},
}
created_svc
,
err
:=
storage
.
Create
(
ctx
,
svc
)
if
created_svc
==
nil
||
err
!=
nil
{
t
.
Errorf
(
"Unexpected failure creating service %v"
,
err
)
}
created_service
:=
created_svc
.
(
*
api
.
Service
)
// Make sure the service does not have the annotation
if
service
.
NeedsHealthCheck
(
created_service
)
{
t
.
Errorf
(
"Unexpected value for annotation %s"
,
service
.
AnnotationExternalTraffic
)
}
// Make sure the service does not have the health check node port allocated
port
:=
service
.
GetServiceHealthCheckNodePort
(
created_service
)
if
port
!=
0
{
t
.
Errorf
(
"Unexpected allocation of health check node port annotation %s"
,
service
.
AnnotationHealthCheckNodePort
)
}
}
// Validate that the health check nodePort is not allocated when service type is ClusterIP
func
TestServiceRegistryExternalTrafficAnnotationClusterIP
(
t
*
testing
.
T
)
{
ctx
:=
api
.
NewDefaultContext
()
storage
,
_
:=
NewTestREST
(
t
,
nil
)
svc
:=
&
api
.
Service
{
ObjectMeta
:
api
.
ObjectMeta
{
Name
:
"external-lb-esipp"
,
Annotations
:
map
[
string
]
string
{
service
.
AnnotationExternalTraffic
:
service
.
AnnotationValueExternalTrafficGlobal
,
},
},
Spec
:
api
.
ServiceSpec
{
Selector
:
map
[
string
]
string
{
"bar"
:
"baz"
},
SessionAffinity
:
api
.
ServiceAffinityNone
,
Type
:
api
.
ServiceTypeClusterIP
,
Ports
:
[]
api
.
ServicePort
{{
Port
:
6502
,
Protocol
:
api
.
ProtocolTCP
,
TargetPort
:
intstr
.
FromInt
(
6502
),
}},
},
}
created_svc
,
err
:=
storage
.
Create
(
ctx
,
svc
)
if
created_svc
==
nil
||
err
!=
nil
{
t
.
Errorf
(
"Unexpected failure creating service %v"
,
err
)
}
created_service
:=
created_svc
.
(
*
api
.
Service
)
// Make sure that ClusterIP services do not have the health check node port allocated
port
:=
service
.
GetServiceHealthCheckNodePort
(
created_service
)
if
port
!=
0
{
t
.
Errorf
(
"Unexpected allocation of health check node port annotation %s"
,
service
.
AnnotationHealthCheckNodePort
)
}
}
pkg/util/config/feature_gate.go
View file @
b82c028f
...
@@ -38,14 +38,16 @@ const (
...
@@ -38,14 +38,16 @@ const (
// specification of gates. Examples:
// specification of gates. Examples:
// AllAlpha=false,NewFeature=true will result in newFeature=true
// AllAlpha=false,NewFeature=true will result in newFeature=true
// AllAlpha=true,NewFeature=false will result in newFeature=false
// AllAlpha=true,NewFeature=false will result in newFeature=false
allAlphaGate
=
"AllAlpha"
allAlphaGate
=
"AllAlpha"
externalTrafficLocalOnly
=
"AllowExtTrafficLocalEndpoints"
)
)
var
(
var
(
// Default values for recorded features. Every new feature gate should be
// Default values for recorded features. Every new feature gate should be
// represented here.
// represented here.
knownFeatures
=
map
[
string
]
featureSpec
{
knownFeatures
=
map
[
string
]
featureSpec
{
allAlphaGate
:
{
false
,
alpha
},
allAlphaGate
:
{
false
,
alpha
},
externalTrafficLocalOnly
:
{
false
,
alpha
},
}
}
// Special handling for a few gates.
// Special handling for a few gates.
...
@@ -85,6 +87,10 @@ type FeatureGate interface {
...
@@ -85,6 +87,10 @@ type FeatureGate interface {
// // alpha: v1.4
// // alpha: v1.4
// MyFeature() bool
// MyFeature() bool
// owner: @girishkalele
// alpha: v1.4
ExternalTrafficLocalOnly
()
bool
// TODO: Define accessors for each non-API alpha feature.
// TODO: Define accessors for each non-API alpha feature.
}
}
...
@@ -154,6 +160,11 @@ func (f *featureGate) Type() string {
...
@@ -154,6 +160,11 @@ func (f *featureGate) Type() string {
return
"mapStringBool"
return
"mapStringBool"
}
}
// ExternalTrafficLocalOnly returns value for AllowExtTrafficLocalEndpoints
func
(
f
*
featureGate
)
ExternalTrafficLocalOnly
()
bool
{
return
f
.
lookup
(
externalTrafficLocalOnly
)
}
func
(
f
*
featureGate
)
lookup
(
key
string
)
bool
{
func
(
f
*
featureGate
)
lookup
(
key
string
)
bool
{
defaultValue
:=
f
.
known
[
key
]
.
enabled
defaultValue
:=
f
.
known
[
key
]
.
enabled
if
f
.
enabled
!=
nil
{
if
f
.
enabled
!=
nil
{
...
...
test/images/netexec/Makefile
View file @
b82c028f
...
@@ -14,7 +14,7 @@
...
@@ -14,7 +14,7 @@
.PHONY
:
all netexec image push clean
.PHONY
:
all netexec image push clean
TAG
=
1.
5
TAG
=
1.
6
PREFIX
=
gcr.io/google_containers
PREFIX
=
gcr.io/google_containers
...
...
test/images/netexec/netexec.go
View file @
b82c028f
...
@@ -80,6 +80,7 @@ func main() {
...
@@ -80,6 +80,7 @@ func main() {
func
startHTTPServer
(
httpPort
int
)
{
func
startHTTPServer
(
httpPort
int
)
{
http
.
HandleFunc
(
"/"
,
rootHandler
)
http
.
HandleFunc
(
"/"
,
rootHandler
)
http
.
HandleFunc
(
"/clientip"
,
clientIpHandler
)
http
.
HandleFunc
(
"/echo"
,
echoHandler
)
http
.
HandleFunc
(
"/echo"
,
echoHandler
)
http
.
HandleFunc
(
"/exit"
,
exitHandler
)
http
.
HandleFunc
(
"/exit"
,
exitHandler
)
http
.
HandleFunc
(
"/hostname"
,
hostnameHandler
)
http
.
HandleFunc
(
"/hostname"
,
hostnameHandler
)
...
@@ -102,6 +103,11 @@ func echoHandler(w http.ResponseWriter, r *http.Request) {
...
@@ -102,6 +103,11 @@ func echoHandler(w http.ResponseWriter, r *http.Request) {
fmt
.
Fprintf
(
w
,
"%s"
,
r
.
FormValue
(
"msg"
))
fmt
.
Fprintf
(
w
,
"%s"
,
r
.
FormValue
(
"msg"
))
}
}
func
clientIpHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
log
.
Printf
(
"GET /clientip"
)
fmt
.
Fprintf
(
w
,
r
.
RemoteAddr
)
}
func
exitHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
func
exitHandler
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
log
.
Printf
(
"GET /exit?code=%s"
,
r
.
FormValue
(
"code"
))
log
.
Printf
(
"GET /exit?code=%s"
,
r
.
FormValue
(
"code"
))
code
,
err
:=
strconv
.
Atoi
(
r
.
FormValue
(
"code"
))
code
,
err
:=
strconv
.
Atoi
(
r
.
FormValue
(
"code"
))
...
@@ -345,7 +351,7 @@ func hostNameHandler(w http.ResponseWriter, r *http.Request) {
...
@@ -345,7 +351,7 @@ func hostNameHandler(w http.ResponseWriter, r *http.Request) {
fmt
.
Fprintf
(
w
,
getHostName
())
fmt
.
Fprintf
(
w
,
getHostName
())
}
}
// udp server
only supports the hostName command
.
// udp server
supports the hostName, echo and clientIP commands
.
func
startUDPServer
(
udpPort
int
)
{
func
startUDPServer
(
udpPort
int
)
{
serverAddress
,
err
:=
net
.
ResolveUDPAddr
(
"udp"
,
fmt
.
Sprintf
(
":%d"
,
udpPort
))
serverAddress
,
err
:=
net
.
ResolveUDPAddr
(
"udp"
,
fmt
.
Sprintf
(
":%d"
,
udpPort
))
assertNoError
(
err
)
assertNoError
(
err
)
...
@@ -363,8 +369,8 @@ func startUDPServer(udpPort int) {
...
@@ -363,8 +369,8 @@ func startUDPServer(udpPort int) {
for
{
for
{
n
,
clientAddress
,
err
:=
serverConn
.
ReadFromUDP
(
buf
)
n
,
clientAddress
,
err
:=
serverConn
.
ReadFromUDP
(
buf
)
assertNoError
(
err
)
assertNoError
(
err
)
receivedText
:=
strings
.
T
rimSpace
(
string
(
buf
[
0
:
n
]
))
receivedText
:=
strings
.
T
oLower
(
strings
.
TrimSpace
(
string
(
buf
[
0
:
n
])
))
if
receivedText
==
"host
Name"
||
receivedText
==
"host
name"
{
if
receivedText
==
"hostname"
{
log
.
Println
(
"Sending udp hostName response"
)
log
.
Println
(
"Sending udp hostName response"
)
_
,
err
=
serverConn
.
WriteToUDP
([]
byte
(
getHostName
()),
clientAddress
)
_
,
err
=
serverConn
.
WriteToUDP
([]
byte
(
getHostName
()),
clientAddress
)
assertNoError
(
err
)
assertNoError
(
err
)
...
@@ -377,6 +383,10 @@ func startUDPServer(udpPort int) {
...
@@ -377,6 +383,10 @@ func startUDPServer(udpPort int) {
log
.
Printf
(
"Echoing %v
\n
"
,
resp
)
log
.
Printf
(
"Echoing %v
\n
"
,
resp
)
_
,
err
=
serverConn
.
WriteToUDP
([]
byte
(
resp
),
clientAddress
)
_
,
err
=
serverConn
.
WriteToUDP
([]
byte
(
resp
),
clientAddress
)
assertNoError
(
err
)
assertNoError
(
err
)
}
else
if
receivedText
==
"clientip"
{
log
.
Printf
(
"Sending back clientip to %s"
,
clientAddress
.
String
())
_
,
err
=
serverConn
.
WriteToUDP
([]
byte
(
clientAddress
.
String
()),
clientAddress
)
assertNoError
(
err
)
}
else
if
len
(
receivedText
)
>
0
{
}
else
if
len
(
receivedText
)
>
0
{
log
.
Printf
(
"Unknown udp command received: %v
\n
"
,
receivedText
)
log
.
Printf
(
"Unknown udp command received: %v
\n
"
,
receivedText
)
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment