2014-06-06 23:40:48 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
2014-06-18 23:01:49 +00:00
|
|
|
|
2014-08-14 19:46:08 +00:00
|
|
|
package service
|
2014-06-06 23:40:48 +00:00
|
|
|
|
|
|
|
import (
|
2014-07-12 07:15:30 +00:00
|
|
|
"fmt"
|
2014-06-26 22:00:55 +00:00
|
|
|
"net"
|
|
|
|
"strconv"
|
2014-06-06 23:40:48 +00:00
|
|
|
|
2014-06-12 20:17:34 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
2014-10-03 22:08:16 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
2014-07-18 20:22:26 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
2014-06-17 02:10:43 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
2014-07-12 07:15:30 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
2014-08-11 07:34:59 +00:00
|
|
|
|
2014-06-25 03:51:57 +00:00
|
|
|
"github.com/golang/glog"
|
2014-06-06 23:40:48 +00:00
|
|
|
)
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// EndpointController manages service endpoints.
|
2014-06-06 23:40:48 +00:00
|
|
|
type EndpointController struct {
|
2014-10-03 22:27:22 +00:00
|
|
|
client *client.Client
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
|
|
|
|
2014-08-11 07:34:59 +00:00
|
|
|
// NewEndpointController returns a new *EndpointController.
|
2014-10-03 22:27:22 +00:00
|
|
|
func NewEndpointController(client *client.Client) *EndpointController {
|
2014-08-11 07:34:59 +00:00
|
|
|
return &EndpointController{
|
2014-10-03 22:27:22 +00:00
|
|
|
client: client,
|
2014-07-12 07:15:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-11 07:34:59 +00:00
|
|
|
// SyncServiceEndpoints syncs service endpoints.
|
2014-06-06 23:40:48 +00:00
|
|
|
func (e *EndpointController) SyncServiceEndpoints() error {
|
2014-10-02 16:51:36 +00:00
|
|
|
ctx := api.NewContext()
|
|
|
|
services, err := e.client.ListServices(ctx, labels.Everything())
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
2014-08-28 04:32:52 +00:00
|
|
|
glog.Errorf("Failed to list services: %v", err)
|
2014-06-06 23:40:48 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
var resultErr error
|
|
|
|
for _, service := range services.Items {
|
2014-10-02 16:51:36 +00:00
|
|
|
nsCtx := api.WithNamespace(ctx, service.Namespace)
|
|
|
|
pods, err := e.client.ListPods(nsCtx, labels.Set(service.Selector).AsSelector())
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
2014-06-25 03:51:57 +00:00
|
|
|
glog.Errorf("Error syncing service: %#v, skipping.", service)
|
2014-06-06 23:40:48 +00:00
|
|
|
resultErr = err
|
|
|
|
continue
|
|
|
|
}
|
2014-10-03 03:24:30 +00:00
|
|
|
endpoints := []string{}
|
|
|
|
for _, pod := range pods.Items {
|
2014-07-12 07:15:30 +00:00
|
|
|
port, err := findPort(&pod.DesiredState.Manifest, service.ContainerPort)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Failed to find port for service: %v, %v", service, err)
|
|
|
|
continue
|
|
|
|
}
|
2014-07-18 16:29:17 +00:00
|
|
|
if len(pod.CurrentState.PodIP) == 0 {
|
|
|
|
glog.Errorf("Failed to find an IP for pod: %v", pod)
|
|
|
|
continue
|
|
|
|
}
|
2014-10-03 03:24:30 +00:00
|
|
|
endpoints = append(endpoints, net.JoinHostPort(pod.CurrentState.PodIP, strconv.Itoa(port)))
|
2014-06-06 23:40:48 +00:00
|
|
|
}
|
2014-10-02 16:51:36 +00:00
|
|
|
currentEndpoints, err := e.client.GetEndpoints(nsCtx, service.ID)
|
2014-09-26 20:34:55 +00:00
|
|
|
if err != nil {
|
|
|
|
// TODO this is brittle as all get out, refactor the client libraries to return a structured error.
|
2014-10-03 22:08:16 +00:00
|
|
|
if errors.IsNotFound(err) {
|
2014-09-26 20:34:55 +00:00
|
|
|
currentEndpoints = &api.Endpoints{
|
2014-10-07 15:12:16 +00:00
|
|
|
TypeMeta: api.TypeMeta{
|
2014-09-26 20:34:55 +00:00
|
|
|
ID: service.ID,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
glog.Errorf("Error getting endpoints: %#v", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
newEndpoints := &api.Endpoints{}
|
|
|
|
*newEndpoints = *currentEndpoints
|
|
|
|
newEndpoints.Endpoints = endpoints
|
|
|
|
|
2014-10-07 20:51:28 +00:00
|
|
|
if len(currentEndpoints.ResourceVersion) == 0 {
|
2014-09-26 20:34:55 +00:00
|
|
|
// No previous endpoints, create them
|
2014-10-02 16:51:36 +00:00
|
|
|
_, err = e.client.CreateEndpoints(nsCtx, newEndpoints)
|
2014-09-26 20:34:55 +00:00
|
|
|
} else {
|
|
|
|
// Pre-existing
|
|
|
|
if endpointsEqual(currentEndpoints, endpoints) {
|
|
|
|
glog.V(2).Infof("endpoints are equal for %s, skipping update", service.ID)
|
|
|
|
continue
|
|
|
|
}
|
2014-10-02 16:51:36 +00:00
|
|
|
_, err = e.client.UpdateEndpoints(nsCtx, newEndpoints)
|
2014-09-26 20:34:55 +00:00
|
|
|
}
|
2014-06-06 23:40:48 +00:00
|
|
|
if err != nil {
|
2014-06-25 03:51:57 +00:00
|
|
|
glog.Errorf("Error updating endpoints: %#v", err)
|
2014-06-06 23:40:48 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return resultErr
|
|
|
|
}
|
2014-08-11 07:34:59 +00:00
|
|
|
|
2014-09-26 20:34:55 +00:00
|
|
|
func containsEndpoint(endpoints *api.Endpoints, endpoint string) bool {
|
|
|
|
if endpoints == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for ix := range endpoints.Endpoints {
|
|
|
|
if endpoints.Endpoints[ix] == endpoint {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func endpointsEqual(e *api.Endpoints, endpoints []string) bool {
|
|
|
|
if len(e.Endpoints) != len(endpoints) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for _, endpoint := range endpoints {
|
|
|
|
if !containsEndpoint(e, endpoint) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-08-11 07:34:59 +00:00
|
|
|
// findPort locates the container port for the given manifest and portName.
|
|
|
|
func findPort(manifest *api.ContainerManifest, portName util.IntOrString) (int, error) {
|
|
|
|
if ((portName.Kind == util.IntstrString && len(portName.StrVal) == 0) ||
|
|
|
|
(portName.Kind == util.IntstrInt && portName.IntVal == 0)) &&
|
|
|
|
len(manifest.Containers[0].Ports) > 0 {
|
|
|
|
return manifest.Containers[0].Ports[0].ContainerPort, nil
|
|
|
|
}
|
|
|
|
if portName.Kind == util.IntstrInt {
|
|
|
|
return portName.IntVal, nil
|
|
|
|
}
|
|
|
|
name := portName.StrVal
|
|
|
|
for _, container := range manifest.Containers {
|
|
|
|
for _, port := range container.Ports {
|
|
|
|
if port.Name == name {
|
|
|
|
return port.ContainerPort, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1, fmt.Errorf("no suitable port for manifest: %s", manifest.ID)
|
|
|
|
}
|