Merge pull request #1047 from lavalamp/fixApiObj

Cleanup: remove deprecated fields the proper way
This commit is contained in:
Clayton Coleman 2014-08-27 11:43:27 -04:00
commit 31dadca04f
8 changed files with 207 additions and 139 deletions

66
pkg/api/conversion.go Normal file
View File

@ -0,0 +1,66 @@
/*
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 api
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
)
func init() {
// TODO: Consider inverting dependency chain-- imagine v1beta1 package
// registering all of these functions. Then, if you want to be able to understand
// v1beta1 objects, you just import that package for its side effects.
AddConversionFuncs(
// EnvVar's Key is deprecated in favor of Name.
func(in *EnvVar, out *v1beta1.EnvVar) error {
out.Value = in.Value
out.Key = in.Name
out.Name = in.Name
return nil
},
func(in *v1beta1.EnvVar, out *EnvVar) error {
out.Value = in.Value
if in.Name != "" {
out.Name = in.Name
} else {
out.Name = in.Key
}
return nil
},
// Path & MountType are deprecated.
func(in *VolumeMount, out *v1beta1.VolumeMount) error {
out.Name = in.Name
out.ReadOnly = in.ReadOnly
out.MountPath = in.MountPath
out.Path = in.MountPath
out.MountType = "" // MountType is ignored.
return nil
},
func(in *v1beta1.VolumeMount, out *VolumeMount) error {
out.Name = in.Name
out.ReadOnly = in.ReadOnly
if in.MountPath == "" {
out.MountPath = in.Path
} else {
out.MountPath = in.MountPath
}
return nil
},
)
}

112
pkg/api/conversion_test.go Normal file
View File

@ -0,0 +1,112 @@
/*
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 api
import (
"reflect"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
)
func TestEnvConversion(t *testing.T) {
nonCanonical := []v1beta1.EnvVar{
{Key: "EV"},
{Key: "EV", Name: "EX"},
}
canonical := []EnvVar{
{Name: "EV"},
{Name: "EX"},
}
for i := range nonCanonical {
var got EnvVar
err := Convert(&nonCanonical[i], &got)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := canonical[i], got; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
}
// Test conversion the other way, too.
for i := range canonical {
var got v1beta1.EnvVar
err := Convert(&canonical[i], &got)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := canonical[i].Name, got.Key; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := canonical[i].Name, got.Name; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
}
func TestVolumeMountConversionToOld(t *testing.T) {
table := []struct {
in VolumeMount
out v1beta1.VolumeMount
}{
{
in: VolumeMount{Name: "foo", MountPath: "/dev/foo", ReadOnly: true},
out: v1beta1.VolumeMount{Name: "foo", MountPath: "/dev/foo", Path: "/dev/foo", ReadOnly: true},
},
}
for _, item := range table {
got := v1beta1.VolumeMount{}
err := Convert(&item.in, &got)
if err != nil {
t.Errorf("Unexpected error: %v", err)
continue
}
if e, a := item.out, got; !reflect.DeepEqual(e, a) {
t.Errorf("Expected: %#v, got %#v", e, a)
}
}
}
func TestVolumeMountConversionToNew(t *testing.T) {
table := []struct {
in v1beta1.VolumeMount
out VolumeMount
}{
{
in: v1beta1.VolumeMount{Name: "foo", MountPath: "/dev/foo", ReadOnly: true},
out: VolumeMount{Name: "foo", MountPath: "/dev/foo", ReadOnly: true},
}, {
in: v1beta1.VolumeMount{Name: "foo", MountPath: "/dev/foo", Path: "/dev/bar", ReadOnly: true},
out: VolumeMount{Name: "foo", MountPath: "/dev/foo", ReadOnly: true},
}, {
in: v1beta1.VolumeMount{Name: "foo", Path: "/dev/bar", ReadOnly: true},
out: VolumeMount{Name: "foo", MountPath: "/dev/bar", ReadOnly: true},
},
}
for _, item := range table {
got := VolumeMount{}
err := Convert(&item.in, &got)
if err != nil {
t.Errorf("Unexpected error: %v", err)
continue
}
if e, a := item.out, got; !reflect.DeepEqual(e, a) {
t.Errorf("Expected: %#v, got %#v", e, a)
}
}
}

View File

@ -43,10 +43,9 @@ type resourceVersioner interface {
var Codec codec var Codec codec
var ResourceVersioner resourceVersioner var ResourceVersioner resourceVersioner
var conversionScheme *conversion.Scheme var conversionScheme = conversion.NewScheme()
func init() { func init() {
conversionScheme = conversion.NewScheme()
conversionScheme.InternalVersion = "" conversionScheme.InternalVersion = ""
conversionScheme.ExternalVersion = "v1beta1" conversionScheme.ExternalVersion = "v1beta1"
conversionScheme.MetaInsertionFactory = metaInsertion{} conversionScheme.MetaInsertionFactory = metaInsertion{}
@ -83,30 +82,6 @@ func init() {
v1beta1.Binding{}, v1beta1.Binding{},
) )
// TODO: when we get more of this stuff, move to its own file. This is not a
// good home for lots of conversion functions.
// TODO: Consider inverting dependency chain-- imagine v1beta1 package
// registering all of these functions. Then, if you want to be able to understand
// v1beta1 objects, you just import that package for its side effects.
AddConversionFuncs(
// EnvVar's Key is deprecated in favor of Name.
func(in *EnvVar, out *v1beta1.EnvVar) error {
out.Value = in.Value
out.Key = in.Name
out.Name = in.Name
return nil
},
func(in *v1beta1.EnvVar, out *EnvVar) error {
out.Value = in.Value
if in.Name != "" {
out.Name = in.Name
} else {
out.Name = in.Key
}
return nil
},
)
Codec = conversionScheme Codec = conversionScheme
ResourceVersioner = NewJSONBaseResourceVersioner() ResourceVersioner = NewJSONBaseResourceVersioner()
} }

View File

@ -116,13 +116,7 @@ type VolumeMount struct {
// Optional: Defaults to false (read-write). // Optional: Defaults to false (read-write).
ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"` ReadOnly bool `yaml:"readOnly,omitempty" json:"readOnly,omitempty"`
// Required. // Required.
// Exactly one of the following must be set. If both are set, prefer MountPath.
// DEPRECATED: Path will be removed in a future version of the API.
MountPath string `yaml:"mountPath,omitempty" json:"mountPath,omitempty"` MountPath string `yaml:"mountPath,omitempty" json:"mountPath,omitempty"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
// One of: "LOCAL" (local volume) or "HOST" (external mount from the host). Default: LOCAL.
// DEPRECATED: MountType will be removed in a future version of the API.
MountType string `yaml:"mountType,omitempty" json:"mountType,omitempty"`
} }
// EnvVar represents an environment variable present in a Container // EnvVar represents an environment variable present in a Container

View File

@ -22,7 +22,6 @@ import (
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/golang/glog"
) )
func validateVolumes(volumes []Volume) (util.StringSet, errs.ErrorList) { func validateVolumes(volumes []Volume) (util.StringSet, errs.ErrorList) {
@ -142,17 +141,7 @@ func validateVolumeMounts(mounts []VolumeMount, volumes util.StringSet) errs.Err
mErrs = append(mErrs, errs.NewNotFound("name", mnt.Name)) mErrs = append(mErrs, errs.NewNotFound("name", mnt.Name))
} }
if len(mnt.MountPath) == 0 { if len(mnt.MountPath) == 0 {
// Backwards compat.
if len(mnt.Path) == 0 {
mErrs = append(mErrs, errs.NewRequired("mountPath", mnt.MountPath)) mErrs = append(mErrs, errs.NewRequired("mountPath", mnt.MountPath))
} else {
glog.Warning("DEPRECATED: VolumeMount.Path has been replaced by VolumeMount.MountPath")
mnt.MountPath = mnt.Path
mnt.Path = ""
}
}
if len(mnt.MountType) != 0 {
glog.Warning("DEPRECATED: VolumeMount.MountType will be removed. The Volume struct will handle types")
} }
allErrs = append(allErrs, mErrs.PrefixIndex(i)...) allErrs = append(allErrs, mErrs.PrefixIndex(i)...)
} }

View File

@ -17,12 +17,10 @@ limitations under the License.
package api package api
import ( import (
"reflect"
"strings" "strings"
"testing" "testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
@ -154,42 +152,6 @@ func TestValidateEnv(t *testing.T) {
} }
} }
func TestEnvConversion(t *testing.T) {
nonCanonical := []v1beta1.EnvVar{
{Key: "EV"},
{Key: "EV", Name: "EX"},
}
canonical := []EnvVar{
{Name: "EV"},
{Name: "EX"},
}
for i := range nonCanonical {
var got EnvVar
err := Convert(&nonCanonical[i], &got)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := canonical[i], got; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
}
// Test conversion the other way, too.
for i := range canonical {
var got v1beta1.EnvVar
err := Convert(&canonical[i], &got)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := canonical[i].Name, got.Key; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := canonical[i].Name, got.Name; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
}
func TestValidateVolumeMounts(t *testing.T) { func TestValidateVolumeMounts(t *testing.T) {
volumes := util.NewStringSet("abc", "123", "abc-123") volumes := util.NewStringSet("abc", "123", "abc-123")
@ -202,16 +164,6 @@ func TestValidateVolumeMounts(t *testing.T) {
t.Errorf("expected success: %v", errs) t.Errorf("expected success: %v", errs)
} }
nonCanonicalCase := []VolumeMount{
{Name: "abc", Path: "/foo"},
}
if errs := validateVolumeMounts(nonCanonicalCase, volumes); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
if nonCanonicalCase[0].MountPath != "/foo" {
t.Errorf("expected canonicalized values: %+v", nonCanonicalCase[0])
}
errorCases := map[string][]VolumeMount{ errorCases := map[string][]VolumeMount{
"empty name": {{Name: "", MountPath: "/foo"}}, "empty name": {{Name: "", MountPath: "/foo"}},
"name not found": {{Name: "", MountPath: "/foo"}}, "name not found": {{Name: "", MountPath: "/foo"}},
@ -293,7 +245,7 @@ func TestValidateManifest(t *testing.T) {
}, },
VolumeMounts: []VolumeMount{ VolumeMounts: []VolumeMount{
{Name: "vol1", MountPath: "/foo"}, {Name: "vol1", MountPath: "/foo"},
{Name: "vol1", Path: "/bar"}, {Name: "vol1", MountPath: "/bar"},
}, },
}, },
}, },

View File

@ -198,29 +198,20 @@ func makeEnvironmentVariables(container *api.Container) []string {
return result return result
} }
func makeVolumesAndBinds(pod *Pod, container *api.Container, podVolumes volumeMap) (map[string]struct{}, []string) { func makeBinds(pod *Pod, container *api.Container, podVolumes volumeMap) []string {
volumes := map[string]struct{}{}
binds := []string{} binds := []string{}
for _, volume := range container.VolumeMounts { for _, mount := range container.VolumeMounts {
var basePath string vol, ok := podVolumes[mount.Name]
if vol, ok := podVolumes[volume.Name]; ok { if !ok {
// Host volumes are not Docker volumes and are directly mounted from the host. continue
basePath = fmt.Sprintf("%s:%s", vol.GetPath(), volume.MountPath)
} else if volume.MountType == "HOST" {
// DEPRECATED: VolumeMount.MountType will be handled by the Volume struct.
basePath = fmt.Sprintf("%s:%s", volume.MountPath, volume.MountPath)
} else {
// TODO(jonesdl) This clause should be deleted and an error should be thrown. The default
// behavior is now supported by the EmptyDirectory type.
volumes[volume.MountPath] = struct{}{}
basePath = fmt.Sprintf("/exports/%s/%s:%s", GetPodFullName(pod), volume.Name, volume.MountPath)
} }
if volume.ReadOnly { b := fmt.Sprintf("%s:%s", vol.GetPath(), mount.MountPath)
basePath += ":ro" if mount.ReadOnly {
b += ":ro"
} }
binds = append(binds, basePath) binds = append(binds, b)
} }
return volumes, binds return binds
} }
func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, map[docker.Port][]docker.PortBinding) { func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, map[docker.Port][]docker.PortBinding) {
@ -294,7 +285,7 @@ func (kl *Kubelet) mountExternalVolumes(manifest *api.ContainerManifest) (volume
// Run a single container from a pod. Returns the docker container ID // Run a single container from a pod. Returns the docker container ID
func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes volumeMap, netMode string) (id DockerID, err error) { func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes volumeMap, netMode string) (id DockerID, err error) {
envVariables := makeEnvironmentVariables(container) envVariables := makeEnvironmentVariables(container)
volumes, binds := makeVolumesAndBinds(pod, container, podVolumes) binds := makeBinds(pod, container, podVolumes)
exposedPorts, portBindings := makePortsAndBindings(container) exposedPorts, portBindings := makePortsAndBindings(container)
opts := docker.CreateContainerOptions{ opts := docker.CreateContainerOptions{
@ -307,7 +298,6 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
Image: container.Image, Image: container.Image,
Memory: int64(container.Memory), Memory: int64(container.Memory),
CpuShares: int64(milliCPUToShares(container.CPU)), CpuShares: int64(milliCPUToShares(container.CPU)),
Volumes: volumes,
WorkingDir: container.WorkingDir, WorkingDir: container.WorkingDir,
}, },
} }

View File

@ -671,17 +671,10 @@ func TestMakeVolumesAndBinds(t *testing.T) {
Name: "disk", Name: "disk",
ReadOnly: false, ReadOnly: false,
}, },
{
MountPath: "/mnt/path2",
Name: "disk2",
ReadOnly: true,
MountType: "LOCAL",
},
{ {
MountPath: "/mnt/path3", MountPath: "/mnt/path3",
Name: "disk3", Name: "disk",
ReadOnly: false, ReadOnly: true,
MountType: "HOST",
}, },
{ {
MountPath: "/mnt/path4", MountPath: "/mnt/path4",
@ -701,24 +694,21 @@ func TestMakeVolumesAndBinds(t *testing.T) {
Namespace: "test", Namespace: "test",
} }
podVolumes := make(volumeMap) podVolumes := volumeMap{
podVolumes["disk4"] = &volume.HostDirectory{"/mnt/host"} "disk": &volume.HostDirectory{"/mnt/disk"},
podVolumes["disk5"] = &volume.EmptyDirectory{"disk5", "podID", "/var/lib/kubelet"} "disk4": &volume.HostDirectory{"/mnt/host"},
"disk5": &volume.EmptyDirectory{"disk5", "podID", "/var/lib/kubelet"},
volumes, binds := makeVolumesAndBinds(&pod, &container, podVolumes)
expectedVolumes := []string{"/mnt/path", "/mnt/path2"}
expectedBinds := []string{"/exports/pod.test/disk:/mnt/path", "/exports/pod.test/disk2:/mnt/path2:ro", "/mnt/path3:/mnt/path3",
"/mnt/host:/mnt/path4", "/var/lib/kubelet/podID/volumes/empty/disk5:/mnt/path5"}
if len(volumes) != len(expectedVolumes) {
t.Errorf("Unexpected volumes. Expected %#v got %#v. Container was: %#v", expectedVolumes, volumes, container)
}
for _, expectedVolume := range expectedVolumes {
if _, ok := volumes[expectedVolume]; !ok {
t.Errorf("Volumes map is missing key: %s. %#v", expectedVolume, volumes)
} }
binds := makeBinds(&pod, &container, podVolumes)
expectedBinds := []string{
"/mnt/disk:/mnt/path",
"/mnt/disk:/mnt/path3:ro",
"/mnt/host:/mnt/path4",
"/var/lib/kubelet/podID/volumes/empty/disk5:/mnt/path5",
} }
if len(binds) != len(expectedBinds) { if len(binds) != len(expectedBinds) {
t.Errorf("Unexpected binds: Expected %#v got %#v. Container was: %#v", expectedBinds, binds, container) t.Errorf("Unexpected binds: Expected %#v got %#v. Container was: %#v", expectedBinds, binds, container)
} }