diff --git a/pkg/kubelet/envvars/doc.go b/pkg/kubelet/envvars/doc.go new file mode 100644 index 0000000000..b6985119b7 --- /dev/null +++ b/pkg/kubelet/envvars/doc.go @@ -0,0 +1,19 @@ +/* +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. +*/ + +// Package envvars is the package that build the environment variables that kubernetes provides +// to the containers run by it. +package envvars diff --git a/pkg/kubelet/envvars/envvars.go b/pkg/kubelet/envvars/envvars.go new file mode 100644 index 0000000000..2cf5418496 --- /dev/null +++ b/pkg/kubelet/envvars/envvars.go @@ -0,0 +1,78 @@ +/* +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. +*/ + +package envvars + +import ( + "fmt" + "strconv" + "strings" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// FromServices builds environment variables that a container is started with, +// which tell the container where to find the services it may need, which are +// provided as an argument. +func FromServices(services *api.ServiceList) []api.EnvVar { + var result []api.EnvVar + for _, service := range services.Items { + // Host + name := makeEnvVariableName(service.Name) + "_SERVICE_HOST" + result = append(result, api.EnvVar{Name: name, Value: service.Spec.PortalIP}) + // Port + name = makeEnvVariableName(service.Name) + "_SERVICE_PORT" + result = append(result, api.EnvVar{Name: name, Value: strconv.Itoa(service.Spec.Port)}) + // Docker-compatible vars. + result = append(result, makeLinkVariables(service)...) + } + return result +} + +func makeEnvVariableName(str string) string { + return strings.ToUpper(strings.Replace(str, "-", "_", -1)) +} + +func makeLinkVariables(service api.Service) []api.EnvVar { + prefix := makeEnvVariableName(service.Name) + protocol := string(api.ProtocolTCP) + if service.Spec.Protocol != "" { + protocol = string(service.Spec.Protocol) + } + portPrefix := fmt.Sprintf("%s_PORT_%d_%s", prefix, service.Spec.Port, strings.ToUpper(protocol)) + return []api.EnvVar{ + { + Name: prefix + "_PORT", + Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.PortalIP, service.Spec.Port), + }, + { + Name: portPrefix, + Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.PortalIP, service.Spec.Port), + }, + { + Name: portPrefix + "_PROTO", + Value: strings.ToLower(protocol), + }, + { + Name: portPrefix + "_PORT", + Value: strconv.Itoa(service.Spec.Port), + }, + { + Name: portPrefix + "_ADDR", + Value: service.Spec.PortalIP, + }, + } +} diff --git a/pkg/kubelet/envvars/envvars_test.go b/pkg/kubelet/envvars/envvars_test.go new file mode 100644 index 0000000000..64f1779499 --- /dev/null +++ b/pkg/kubelet/envvars/envvars_test.go @@ -0,0 +1,92 @@ +/* +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. +*/ + +package envvars_test + +import ( + "reflect" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/envvars" +) + +func TestFromServices(t *testing.T) { + sl := api.ServiceList{ + Items: []api.Service{ + { + ObjectMeta: api.ObjectMeta{Name: "foo-bar"}, + Spec: api.ServiceSpec{ + Port: 8080, + Selector: map[string]string{"bar": "baz"}, + Protocol: "TCP", + PortalIP: "1.2.3.4", + }, + }, + { + ObjectMeta: api.ObjectMeta{Name: "abc-123"}, + Spec: api.ServiceSpec{ + Port: 8081, + Selector: map[string]string{"bar": "baz"}, + Protocol: "UDP", + PortalIP: "5.6.7.8", + }, + }, + { + ObjectMeta: api.ObjectMeta{Name: "q-u-u-x"}, + Spec: api.ServiceSpec{ + Port: 8082, + Selector: map[string]string{"bar": "baz"}, + Protocol: "TCP", + PortalIP: "9.8.7.6", + }, + }, + }, + } + vars := envvars.FromServices(&sl) + expected := []api.EnvVar{ + {Name: "FOO_BAR_SERVICE_HOST", Value: "1.2.3.4"}, + {Name: "FOO_BAR_SERVICE_PORT", Value: "8080"}, + {Name: "FOO_BAR_PORT", Value: "tcp://1.2.3.4:8080"}, + {Name: "FOO_BAR_PORT_8080_TCP", Value: "tcp://1.2.3.4:8080"}, + {Name: "FOO_BAR_PORT_8080_TCP_PROTO", Value: "tcp"}, + {Name: "FOO_BAR_PORT_8080_TCP_PORT", Value: "8080"}, + {Name: "FOO_BAR_PORT_8080_TCP_ADDR", Value: "1.2.3.4"}, + {Name: "ABC_123_SERVICE_HOST", Value: "5.6.7.8"}, + {Name: "ABC_123_SERVICE_PORT", Value: "8081"}, + {Name: "ABC_123_PORT", Value: "udp://5.6.7.8:8081"}, + {Name: "ABC_123_PORT_8081_UDP", Value: "udp://5.6.7.8:8081"}, + {Name: "ABC_123_PORT_8081_UDP_PROTO", Value: "udp"}, + {Name: "ABC_123_PORT_8081_UDP_PORT", Value: "8081"}, + {Name: "ABC_123_PORT_8081_UDP_ADDR", Value: "5.6.7.8"}, + {Name: "Q_U_U_X_SERVICE_HOST", Value: "9.8.7.6"}, + {Name: "Q_U_U_X_SERVICE_PORT", Value: "8082"}, + {Name: "Q_U_U_X_PORT", Value: "tcp://9.8.7.6:8082"}, + {Name: "Q_U_U_X_PORT_8082_TCP", Value: "tcp://9.8.7.6:8082"}, + {Name: "Q_U_U_X_PORT_8082_TCP_PROTO", Value: "tcp"}, + {Name: "Q_U_U_X_PORT_8082_TCP_PORT", Value: "8082"}, + {Name: "Q_U_U_X_PORT_8082_TCP_ADDR", Value: "9.8.7.6"}, + } + if len(vars) != len(expected) { + t.Errorf("Expected %d env vars, got: %+v", len(expected), vars) + return + } + for i := range expected { + if !reflect.DeepEqual(vars[i], expected[i]) { + t.Errorf("expected %#v, got %#v", vars[i], expected[i]) + } + } +} diff --git a/pkg/registry/pod/bound_pod_factory.go b/pkg/registry/pod/bound_pod_factory.go index 0cbbd54c5a..5cd915874d 100644 --- a/pkg/registry/pod/bound_pod_factory.go +++ b/pkg/registry/pod/bound_pod_factory.go @@ -18,6 +18,7 @@ package pod import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/envvars" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/service" ) @@ -31,8 +32,19 @@ type BasicBoundPodFactory struct { ServiceRegistry service.Registry } +// getServiceEnvironmentVariables populates a list of environment variables that are use +// in the container environment to get access to services. +func getServiceEnvironmentVariables(ctx api.Context, registry service.Registry, machine string) ([]api.EnvVar, error) { + var result []api.EnvVar + services, err := registry.ListServices(ctx) + if err != nil { + return result, err + } + return envvars.FromServices(services), nil +} + func (b *BasicBoundPodFactory) MakeBoundPod(machine string, pod *api.Pod) (*api.BoundPod, error) { - envVars, err := service.GetServiceEnvironmentVariables(api.NewContext(), b.ServiceRegistry, machine) + envVars, err := getServiceEnvironmentVariables(api.NewContext(), b.ServiceRegistry, machine) if err != nil { return nil, err } @@ -44,8 +56,6 @@ func (b *BasicBoundPodFactory) MakeBoundPod(machine string, pod *api.Pod) (*api. boundPod.Spec.Containers[ix].Env = append(container.Env, envVars...) } // Make a dummy self link so that references to this bound pod will work. - // TODO: When kubelets get boundPods from apiserver instead of etcd, then - // the selflink should be generated there. boundPod.SelfLink = "/api/v1beta1/boundPods/" + boundPod.Name return boundPod, nil } diff --git a/pkg/registry/service/rest.go b/pkg/registry/service/rest.go index 8b518be8e3..64febd65a3 100644 --- a/pkg/registry/service/rest.go +++ b/pkg/registry/service/rest.go @@ -20,8 +20,6 @@ import ( "fmt" "math/rand" "net" - "strconv" - "strings" "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" @@ -211,27 +209,6 @@ func (*REST) New() runtime.Object { return &api.Service{} } -// GetServiceEnvironmentVariables populates a list of environment variables that are use -// in the container environment to get access to services. -func GetServiceEnvironmentVariables(ctx api.Context, registry Registry, machine string) ([]api.EnvVar, error) { - var result []api.EnvVar - services, err := registry.ListServices(ctx) - if err != nil { - return result, err - } - for _, service := range services.Items { - // Host - name := makeEnvVariableName(service.Name) + "_SERVICE_HOST" - result = append(result, api.EnvVar{Name: name, Value: service.Spec.PortalIP}) - // Port - name = makeEnvVariableName(service.Name) + "_SERVICE_PORT" - result = append(result, api.EnvVar{Name: name, Value: strconv.Itoa(service.Spec.Port)}) - // Docker-compatible vars. - result = append(result, makeLinkVariables(service)...) - } - return result, nil -} - func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) { service := obj.(*api.Service) if !api.ValidNamespace(ctx, &service.ObjectMeta) { @@ -300,38 +277,3 @@ func (rs *REST) deleteExternalLoadBalancer(service *api.Service) error { } return nil } - -func makeEnvVariableName(str string) string { - return strings.ToUpper(strings.Replace(str, "-", "_", -1)) -} - -func makeLinkVariables(service api.Service) []api.EnvVar { - prefix := makeEnvVariableName(service.Name) - protocol := string(api.ProtocolTCP) - if service.Spec.Protocol != "" { - protocol = string(service.Spec.Protocol) - } - portPrefix := fmt.Sprintf("%s_PORT_%d_%s", prefix, service.Spec.Port, strings.ToUpper(protocol)) - return []api.EnvVar{ - { - Name: prefix + "_PORT", - Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.PortalIP, service.Spec.Port), - }, - { - Name: portPrefix, - Value: fmt.Sprintf("%s://%s:%d", strings.ToLower(protocol), service.Spec.PortalIP, service.Spec.Port), - }, - { - Name: portPrefix + "_PROTO", - Value: strings.ToLower(protocol), - }, - { - Name: portPrefix + "_PORT", - Value: strconv.Itoa(service.Spec.Port), - }, - { - Name: portPrefix + "_ADDR", - Value: service.Spec.PortalIP, - }, - } -} diff --git a/pkg/registry/service/rest_test.go b/pkg/registry/service/rest_test.go index 9d8430c7d7..299cb599ab 100644 --- a/pkg/registry/service/rest_test.go +++ b/pkg/registry/service/rest_test.go @@ -19,7 +19,6 @@ package service import ( "fmt" "net" - "reflect" "strings" "testing" @@ -288,79 +287,6 @@ func TestServiceRegistryDeleteExternal(t *testing.T) { } } -func TestServiceRegistryMakeLinkVariables(t *testing.T) { - ctx := api.NewDefaultContext() - registry := registrytest.NewServiceRegistry() - registry.List = api.ServiceList{ - Items: []api.Service{ - { - ObjectMeta: api.ObjectMeta{Name: "foo-bar"}, - Spec: api.ServiceSpec{ - Port: 8080, - Selector: map[string]string{"bar": "baz"}, - Protocol: "TCP", - PortalIP: "1.2.3.4", - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "abc-123"}, - Spec: api.ServiceSpec{ - Port: 8081, - Selector: map[string]string{"bar": "baz"}, - Protocol: "UDP", - PortalIP: "5.6.7.8", - }, - }, - { - ObjectMeta: api.ObjectMeta{Name: "q-u-u-x"}, - Spec: api.ServiceSpec{ - Port: 8082, - Selector: map[string]string{"bar": "baz"}, - Protocol: "TCP", - PortalIP: "9.8.7.6", - }, - }, - }, - } - machine := "machine" - vars, err := GetServiceEnvironmentVariables(ctx, registry, machine) - if err != nil { - t.Errorf("Unexpected err: %v", err) - } - expected := []api.EnvVar{ - {Name: "FOO_BAR_SERVICE_HOST", Value: "1.2.3.4"}, - {Name: "FOO_BAR_SERVICE_PORT", Value: "8080"}, - {Name: "FOO_BAR_PORT", Value: "tcp://1.2.3.4:8080"}, - {Name: "FOO_BAR_PORT_8080_TCP", Value: "tcp://1.2.3.4:8080"}, - {Name: "FOO_BAR_PORT_8080_TCP_PROTO", Value: "tcp"}, - {Name: "FOO_BAR_PORT_8080_TCP_PORT", Value: "8080"}, - {Name: "FOO_BAR_PORT_8080_TCP_ADDR", Value: "1.2.3.4"}, - {Name: "ABC_123_SERVICE_HOST", Value: "5.6.7.8"}, - {Name: "ABC_123_SERVICE_PORT", Value: "8081"}, - {Name: "ABC_123_PORT", Value: "udp://5.6.7.8:8081"}, - {Name: "ABC_123_PORT_8081_UDP", Value: "udp://5.6.7.8:8081"}, - {Name: "ABC_123_PORT_8081_UDP_PROTO", Value: "udp"}, - {Name: "ABC_123_PORT_8081_UDP_PORT", Value: "8081"}, - {Name: "ABC_123_PORT_8081_UDP_ADDR", Value: "5.6.7.8"}, - {Name: "Q_U_U_X_SERVICE_HOST", Value: "9.8.7.6"}, - {Name: "Q_U_U_X_SERVICE_PORT", Value: "8082"}, - {Name: "Q_U_U_X_PORT", Value: "tcp://9.8.7.6:8082"}, - {Name: "Q_U_U_X_PORT_8082_TCP", Value: "tcp://9.8.7.6:8082"}, - {Name: "Q_U_U_X_PORT_8082_TCP_PROTO", Value: "tcp"}, - {Name: "Q_U_U_X_PORT_8082_TCP_PORT", Value: "8082"}, - {Name: "Q_U_U_X_PORT_8082_TCP_ADDR", Value: "9.8.7.6"}, - } - if len(vars) != len(expected) { - t.Errorf("Expected %d env vars, got: %+v", len(expected), vars) - return - } - for i := range expected { - if !reflect.DeepEqual(vars[i], expected[i]) { - t.Errorf("expected %#v, got %#v", vars[i], expected[i]) - } - } -} - func TestServiceRegistryGet(t *testing.T) { ctx := api.NewDefaultContext() registry := registrytest.NewServiceRegistry()