refactor services to v1beta3

This commit is contained in:
markturansky
2014-10-30 09:29:11 -04:00
parent 5a649f2b93
commit bd7643c033
26 changed files with 466 additions and 340 deletions

View File

@@ -479,6 +479,38 @@ type ServiceList struct {
Items []Service `json:"items" yaml:"items"`
}
// ServiceStatus represents the current status of a service
type ServiceStatus struct{}
// ServiceSpec describes the attributes that a user creates on a service
type ServiceSpec struct {
// Port is the TCP or UDP port that will be made available to each pod for connecting to the pods
// proxied by this service.
Port int `json:"port" yaml:"port"`
// Optional: Supports "TCP" and "UDP". Defaults to "TCP".
Protocol Protocol `json:"protocol,omitempty" yaml:"protocol,omitempty"`
// This service will route traffic to pods having labels matching this selector.
Selector map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
// PortalIP is usually assigned by the master. If specified by the user
// we will try to respect it or else fail the request. This field can
// not be changed by updates.
PortalIP string `json:"portalIP,omitempty" yaml:"portalIP,omitempty"`
// ProxyPort is assigned by the master. If 0, the proxy will choose an ephemeral port.
// TODO: This is awkward - if we had a BoundService, it would be better factored.
ProxyPort int `json:"proxyPort,omitempty" yaml:"proxyPort,omitempty"`
// CreateExternalLoadBalancer indicates whether a load balancer should be created for this service.
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`
// ContainerPort is the name of the port on the container to direct traffic to.
// Optional, if unspecified use the first port on the container.
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
}
// Service is a named abstraction of software service (for example, mysql) consisting of local port
// (for example 3306) that the proxy listens on, and the selector that determines which pods
// will answer requests sent through the proxy.
@@ -486,27 +518,11 @@ type Service struct {
TypeMeta `json:",inline" yaml:",inline"`
ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
// Required.
Port int `json:"port" yaml:"port"`
// Optional: Defaults to "TCP".
Protocol Protocol `yaml:"protocol,omitempty" json:"protocol,omitempty"`
// Spec defines the behavior of a service.
Spec ServiceSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
// This service will route traffic to pods having labels matching this selector.
Selector map[string]string `json:"selector,omitempty" yaml:"selector,omitempty"`
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`
// ContainerPort is the name of the port on the container to direct traffic to.
// Optional, if unspecified use the first port on the container.
ContainerPort util.IntOrString `json:"containerPort,omitempty" yaml:"containerPort,omitempty"`
// PortalIP is usually assigned by the master. If specified by the user
// we will try to respect it or else fail the request. This field can
// not be changed by updates.
PortalIP string `json:"portalIP,omitempty" yaml:"portalIP,omitempty"`
// ProxyPort is assigned by the master. If specified by the user it will be ignored.
// TODO: This is awkward - if we had a BoundService, it would be better factored.
ProxyPort int `json:"proxyPort,omitempty" yaml:"proxyPort,omitempty"`
// Status represents the current status of a service.
Status ServiceStatus `json:"status,omitempty" yaml:"status,omitempty"`
}
// Endpoints is a collection of endpoints that implement the actual service, for example:

View File

@@ -247,6 +247,7 @@ func init() {
},
func(in *newer.Service, out *Service, s conversion.Scope) error {
if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil {
return err
}
@@ -257,16 +258,15 @@ func init() {
return err
}
out.Port = in.Port
out.Protocol = Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Selector, 0); err != nil {
out.Port = in.Spec.Port
out.Protocol = Protocol(in.Spec.Protocol)
if err := s.Convert(&in.Spec.Selector, &out.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.ContainerPort = in.ContainerPort
out.PortalIP = in.PortalIP
out.ProxyPort = in.ProxyPort
out.CreateExternalLoadBalancer = in.Spec.CreateExternalLoadBalancer
out.ContainerPort = in.Spec.ContainerPort
out.PortalIP = in.Spec.PortalIP
out.ProxyPort = in.Spec.ProxyPort
return nil
},
func(in *Service, out *newer.Service, s conversion.Scope) error {
@@ -280,16 +280,15 @@ func init() {
return err
}
out.Port = in.Port
out.Protocol = newer.Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Selector, 0); err != nil {
out.Spec.Port = in.Port
out.Spec.Protocol = newer.Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Spec.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.ContainerPort = in.ContainerPort
out.PortalIP = in.PortalIP
out.ProxyPort = in.ProxyPort
out.Spec.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Spec.ContainerPort = in.ContainerPort
out.Spec.PortalIP = in.PortalIP
out.Spec.ProxyPort = in.ProxyPort
return nil
},

View File

@@ -188,15 +188,15 @@ func init() {
return err
}
out.Port = in.Port
out.Protocol = Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Selector, 0); err != nil {
out.Port = in.Spec.Port
out.Protocol = Protocol(in.Spec.Protocol)
if err := s.Convert(&in.Spec.Selector, &out.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.ContainerPort = in.ContainerPort
out.PortalIP = in.PortalIP
out.ProxyPort = in.ProxyPort
out.CreateExternalLoadBalancer = in.Spec.CreateExternalLoadBalancer
out.ContainerPort = in.Spec.ContainerPort
out.PortalIP = in.Spec.PortalIP
out.ProxyPort = in.Spec.ProxyPort
return nil
},
@@ -211,15 +211,15 @@ func init() {
return err
}
out.Port = in.Port
out.Protocol = newer.Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Selector, 0); err != nil {
out.Spec.Port = in.Port
out.Spec.Protocol = newer.Protocol(in.Protocol)
if err := s.Convert(&in.Selector, &out.Spec.Selector, 0); err != nil {
return err
}
out.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.ContainerPort = in.ContainerPort
out.PortalIP = in.PortalIP
out.ProxyPort = in.ProxyPort
out.Spec.CreateExternalLoadBalancer = in.CreateExternalLoadBalancer
out.Spec.ContainerPort = in.ContainerPort
out.Spec.PortalIP = in.PortalIP
out.Spec.ProxyPort = in.ProxyPort
return nil
},

View File

@@ -123,36 +123,37 @@ const (
NamespaceAll string = ""
)
// ContainerManifest corresponds to the Container Manifest format, documented at:
// https://developers.google.com/compute/docs/containers/container_vms#container_manifest
// This is used as the representation of Kubernetes workloads.
// DEPRECATED: Exists to allow backwards compatible storage for clients accessing etcd
// directly.
type ContainerManifest struct {
// Required: This must be a supported version string, such as "v1beta1".
Version string `json:"version" yaml:"version"`
// Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `json:"id" yaml:"id"`
// TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactoring. It is required for now to determine the instance
// of a Pod.
UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"`
Volumes []Volume `json:"volumes" yaml:"volumes"`
Containers []Container `json:"containers" yaml:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"`
}
// ContainerManifestList is used to communicate container manifests to kubelet.
// DEPRECATED: Exists to allow backwards compatible storage for clients accessing etcd
// directly.
type ContainerManifestList struct {
TypeMeta `json:",inline" yaml:",inline"`
// ID is the legacy field representing Name
ID string `json:"id,omitempty" yaml:"id,omitempty"`
Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
}
//
//// ContainerManifest corresponds to the Container Manifest format, documented at:
//// https://developers.google.com/compute/docs/containers/container_vms#container_manifest
//// This is used as the representation of Kubernetes workloads.
//// DEPRECATED: Exists to allow backwards compatible storage for clients accessing etcd
//// directly.
//type ContainerManifest struct {
// // Required: This must be a supported version string, such as "v1beta1".
// Version string `json:"version" yaml:"version"`
// // Required: This must be a DNS_SUBDOMAIN.
// // TODO: ID on Manifest is deprecated and will be removed in the future.
// ID string `json:"id" yaml:"id"`
// // TODO: UUID on Manifest is deprecated in the future once we are done
// // with the API refactoring. It is required for now to determine the instance
// // of a Pod.
// UUID string `json:"uuid,omitempty" yaml:"uuid,omitempty"`
// Volumes []Volume `json:"volumes" yaml:"volumes"`
// Containers []Container `json:"containers" yaml:"containers"`
// RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"`
//}
//
//// ContainerManifestList is used to communicate container manifests to kubelet.
//// DEPRECATED: Exists to allow backwards compatible storage for clients accessing etcd
//// directly.
//type ContainerManifestList struct {
// TypeMeta `json:",inline" yaml:",inline"`
// // ID is the legacy field representing Name
// ID string `json:"id,omitempty" yaml:"id,omitempty"`
//
// Items []ContainerManifest `json:"items,omitempty" yaml:"items,omitempty"`
//}
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
type Volume struct {
@@ -583,10 +584,7 @@ type ReplicationControllerList struct {
}
// ServiceStatus represents the current status of a service
type ServiceStatus struct {
// ProxyPort is assigned by the master. If 0, the proxy will choose an ephemeral port.
ProxyPort int `json:"proxyPort,omitempty" yaml:"proxyPort,omitempty"`
}
type ServiceStatus struct{}
// ServiceSpec describes the attributes that a user creates on a service
type ServiceSpec struct {
@@ -605,6 +603,9 @@ type ServiceSpec struct {
// not be changed by updates.
PortalIP string `json:"portalIP,omitempty" yaml:"portalIP,omitempty"`
// ProxyPort is assigned by the master. If 0, the proxy will choose an ephemeral port.
ProxyPort int `json:"proxyPort,omitempty" yaml:"proxyPort,omitempty"`
// CreateExternalLoadBalancer indicates whether a load balancer should be created for this service.
CreateExternalLoadBalancer bool `json:"createExternalLoadBalancer,omitempty" yaml:"createExternalLoadBalancer,omitempty"`

View File

@@ -392,19 +392,19 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
if !util.IsDNSSubdomain(service.Namespace) {
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", service.Namespace))
}
if !util.IsValidPortNum(service.Port) {
allErrs = append(allErrs, errs.NewFieldInvalid("port", service.Port))
if !util.IsValidPortNum(service.Spec.Port) {
allErrs = append(allErrs, errs.NewFieldInvalid("spec.port", service.Spec.Port))
}
if len(service.Protocol) == 0 {
service.Protocol = "TCP"
} else if !supportedPortProtocols.Has(strings.ToUpper(string(service.Protocol))) {
allErrs = append(allErrs, errs.NewFieldNotSupported("protocol", service.Protocol))
if len(service.Spec.Protocol) == 0 {
service.Spec.Protocol = "TCP"
} else if !supportedPortProtocols.Has(strings.ToUpper(string(service.Spec.Protocol))) {
allErrs = append(allErrs, errs.NewFieldNotSupported("spec.protocol", service.Spec.Protocol))
}
if labels.Set(service.Selector).AsSelector().Empty() {
allErrs = append(allErrs, errs.NewFieldRequired("selector", service.Selector))
if labels.Set(service.Spec.Selector).AsSelector().Empty() {
allErrs = append(allErrs, errs.NewFieldRequired("spec.selector", service.Spec.Selector))
}
allErrs = append(allErrs, validateLabels(service.Labels)...)
allErrs = append(allErrs, validateLabels(service.Selector)...)
allErrs = append(allErrs, validateLabels(service.Spec.Selector)...)
return allErrs
}

View File

@@ -633,8 +633,10 @@ func TestValidateService(t *testing.T) {
name: "missing id",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault},
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
},
},
// Should fail because the ID is missing.
numErrs: 1,
@@ -643,8 +645,10 @@ func TestValidateService(t *testing.T) {
name: "missing namespace",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
},
},
// Should fail because the Namespace is missing.
numErrs: 1,
@@ -653,8 +657,10 @@ func TestValidateService(t *testing.T) {
name: "invalid id",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "123abc", Namespace: api.NamespaceDefault},
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
},
},
// Should fail because the ID is invalid.
numErrs: 1,
@@ -663,7 +669,9 @@ func TestValidateService(t *testing.T) {
name: "missing port",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Selector: map[string]string{"foo": "bar"},
},
},
// Should fail because the port number is missing/invalid.
numErrs: 1,
@@ -672,8 +680,10 @@ func TestValidateService(t *testing.T) {
name: "invalid port",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Port: 65536,
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 66536,
Selector: map[string]string{"foo": "bar"},
},
},
// Should fail because the port number is invalid.
numErrs: 1,
@@ -682,9 +692,11 @@ func TestValidateService(t *testing.T) {
name: "invalid protocol",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Port: 8675,
Protocol: "INVALID",
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Protocol: "INVALID",
},
},
// Should fail because the protocol is invalid.
numErrs: 1,
@@ -693,7 +705,9 @@ func TestValidateService(t *testing.T) {
name: "missing selector",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
Port: 8675,
Spec: api.ServiceSpec{
Port: 8675,
},
},
// Should fail because the selector is missing.
numErrs: 1,
@@ -702,9 +716,11 @@ func TestValidateService(t *testing.T) {
name: "valid 1",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Port: 1,
Protocol: "TCP",
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Protocol: "TCP",
},
},
numErrs: 0,
},
@@ -712,9 +728,11 @@ func TestValidateService(t *testing.T) {
name: "valid 2",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Port: 65535,
Protocol: "UDP",
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
Protocol: "UDP",
},
},
numErrs: 0,
},
@@ -722,8 +740,10 @@ func TestValidateService(t *testing.T) {
name: "valid 3",
svc: api.Service{
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
Port: 80,
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
},
},
numErrs: 0,
},
@@ -737,8 +757,10 @@ func TestValidateService(t *testing.T) {
"NoUppercaseOrSpecialCharsLike=Equals": "bar",
},
},
Port: 80,
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
},
},
numErrs: 2,
},
@@ -752,15 +774,17 @@ func TestValidateService(t *testing.T) {
}
svc := api.Service{
Port: 6502,
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
Selector: map[string]string{"foo": "bar"},
Spec: api.ServiceSpec{
Port: 8675,
Selector: map[string]string{"foo": "bar"},
},
}
errs := ValidateService(&svc)
if len(errs) != 0 {
t.Errorf("Unexpected non-zero error list: %#v", errs)
}
if svc.Protocol != "TCP" {
if svc.Spec.Protocol != "TCP" {
t.Errorf("Expected default protocol of 'TCP': %#v", errs)
}
}