Core support for ip-per-service
This commit is contained in:
@@ -19,6 +19,7 @@ package service
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -32,21 +33,49 @@ import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// REST adapts a service registry into apiserver's RESTStorage model.
|
||||
type REST struct {
|
||||
registry Registry
|
||||
cloud cloudprovider.Interface
|
||||
machines minion.Registry
|
||||
registry Registry
|
||||
cloud cloudprovider.Interface
|
||||
machines minion.Registry
|
||||
portalMgr *ipAllocator
|
||||
}
|
||||
|
||||
// NewREST returns a new REST.
|
||||
func NewREST(registry Registry, cloud cloudprovider.Interface, machines minion.Registry) *REST {
|
||||
func NewREST(registry Registry, cloud cloudprovider.Interface, machines minion.Registry, portalNet *net.IPNet) *REST {
|
||||
// TODO: Before we can replicate masters, this has to be synced (e.g. lives in etcd)
|
||||
ipa := newIPAllocator(portalNet)
|
||||
reloadIPsFromStorage(ipa, registry)
|
||||
|
||||
return &REST{
|
||||
registry: registry,
|
||||
cloud: cloud,
|
||||
machines: machines,
|
||||
registry: registry,
|
||||
cloud: cloud,
|
||||
machines: machines,
|
||||
portalMgr: ipa,
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: mark all previously allocated IPs in the allocator.
|
||||
func reloadIPsFromStorage(ipa *ipAllocator, registry Registry) {
|
||||
services, err := registry.ListServices(api.NewContext())
|
||||
if err != nil {
|
||||
// This is really bad.
|
||||
glog.Errorf("can't list services to init service REST: %s", err)
|
||||
return
|
||||
}
|
||||
for i := range services.Items {
|
||||
s := &services.Items[i]
|
||||
if s.PortalIP == "" {
|
||||
glog.Warningf("service %q has no PortalIP", s.ID)
|
||||
continue
|
||||
}
|
||||
if err := ipa.Allocate(net.ParseIP(s.PortalIP)); err != nil {
|
||||
// This is really bad.
|
||||
glog.Errorf("service %q PortalIP %s could not be allocated: %s", s.ID, s.PortalIP, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +90,16 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan runtime.Obje
|
||||
|
||||
srv.CreationTimestamp = util.Now()
|
||||
|
||||
if ip, err := rs.portalMgr.AllocateNext(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
srv.PortalIP = ip.String()
|
||||
}
|
||||
|
||||
return apiserver.MakeAsync(func() (runtime.Object, error) {
|
||||
// TODO: Consider moving this to a rectification loop, so that we make/remove external load balancers
|
||||
// correctly no matter what http operations happen.
|
||||
srv.ProxyPort = 0
|
||||
if srv.CreateExternalLoadBalancer {
|
||||
if rs.cloud == nil {
|
||||
return nil, fmt.Errorf("requested an external service, but no cloud provider supplied.")
|
||||
@@ -88,6 +124,9 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan runtime.Obje
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// External load-balancers require a known port for the service proxy.
|
||||
// TODO: If we end up brokering HostPorts between Pods and Services, this can be any port.
|
||||
srv.ProxyPort = srv.Port
|
||||
}
|
||||
err := rs.registry.CreateService(ctx, srv)
|
||||
if err != nil {
|
||||
@@ -110,6 +149,7 @@ func (rs *REST) Delete(ctx api.Context, id string) (<-chan runtime.Object, error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rs.portalMgr.Release(net.ParseIP(service.PortalIP))
|
||||
return apiserver.MakeAsync(func() (runtime.Object, error) {
|
||||
rs.deleteExternalLoadBalancer(service)
|
||||
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteService(ctx, id)
|
||||
@@ -161,16 +201,13 @@ func GetServiceEnvironmentVariables(ctx api.Context, registry Registry, machine
|
||||
for _, service := range services.Items {
|
||||
// Host
|
||||
name := makeEnvVariableName(service.ID) + "_SERVICE_HOST"
|
||||
result = append(result, api.EnvVar{Name: name, Value: machine})
|
||||
result = append(result, api.EnvVar{Name: name, Value: service.PortalIP})
|
||||
// Port
|
||||
name = makeEnvVariableName(service.ID) + "_SERVICE_PORT"
|
||||
result = append(result, api.EnvVar{Name: name, Value: strconv.Itoa(service.Port)})
|
||||
// Docker-compatible vars.
|
||||
result = append(result, makeLinkVariables(service, machine)...)
|
||||
result = append(result, makeLinkVariables(service)...)
|
||||
}
|
||||
// The 'SERVICE_HOST' variable is deprecated.
|
||||
// TODO(thockin): get rid of it once ip-per-service is in and "deployed".
|
||||
result = append(result, api.EnvVar{Name: "SERVICE_HOST", Value: machine})
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -183,8 +220,15 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan runtime.Obje
|
||||
return nil, errors.NewInvalid("service", srv.ID, errs)
|
||||
}
|
||||
return apiserver.MakeAsync(func() (runtime.Object, error) {
|
||||
cur, err := rs.registry.GetService(ctx, srv.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Copy over non-user fields.
|
||||
srv.PortalIP = cur.PortalIP
|
||||
srv.ProxyPort = cur.ProxyPort
|
||||
// TODO: check to see if external load balancer status changed
|
||||
err := rs.registry.UpdateService(ctx, srv)
|
||||
err = rs.registry.UpdateService(ctx, srv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -234,7 +278,7 @@ func makeEnvVariableName(str string) string {
|
||||
return strings.ToUpper(strings.Replace(str, "-", "_", -1))
|
||||
}
|
||||
|
||||
func makeLinkVariables(service api.Service, machine string) []api.EnvVar {
|
||||
func makeLinkVariables(service api.Service) []api.EnvVar {
|
||||
prefix := makeEnvVariableName(service.ID)
|
||||
protocol := string(api.ProtocolTCP)
|
||||
if service.Protocol != "" {
|
||||
@@ -244,11 +288,11 @@ func makeLinkVariables(service api.Service, machine string) []api.EnvVar {
|
||||
return []api.EnvVar{
|
||||
{
|
||||
Name: prefix + "_PORT",
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), machine, service.Port),
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.PortalIP, service.Port),
|
||||
},
|
||||
{
|
||||
Name: portPrefix,
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), machine, service.Port),
|
||||
Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.PortalIP, service.Port),
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_PROTO",
|
||||
@@ -260,7 +304,7 @@ func makeLinkVariables(service api.Service, machine string) []api.EnvVar {
|
||||
},
|
||||
{
|
||||
Name: portPrefix + "_ADDR",
|
||||
Value: machine,
|
||||
Value: service.PortalIP,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user