Merge pull request #13372 from thockin/strict-compat-external-ip

easier auto-conversion, strict compat with deprecatedPublicIPs
This commit is contained in:
Brian Grant 2015-09-25 11:10:28 -07:00
commit 51a3b80b99
8 changed files with 2790 additions and 770 deletions

View File

@ -13818,7 +13818,14 @@
"items": {
"type": "string"
},
"description": "ExternalIPs are used by external load balancers, or can be set by users to handle external traffic that arrives at a node. Externally visible IPs (e.g. load balancers) that should be proxied to this service."
"description": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system. A previous form of this functionality exists as the deprecatedPublicIPs field. When using this field, callers should also clear the deprecatedPublicIPs field."
},
"deprecatedPublicIPs": {
"type": "array",
"items": {
"type": "string"
},
"description": "deprecatedPublicIPs is deprecated and replaced by the externalIPs field with almost the exact same semantics. This field is retained in the v1 API for compatibility until at least 8/20/2016. It will be removed from any new API revisions. If both deprecatedPublicIPs *and* externalIPs are set, deprecatedPublicIPs is used."
},
"sessionAffinity": {
"type": "string",

View File

@ -29,8 +29,10 @@ func addConversionFuncs() {
err := api.Scheme.AddConversionFuncs(
convert_api_PodSpec_To_v1_PodSpec,
convert_api_ReplicationControllerSpec_To_v1_ReplicationControllerSpec,
convert_api_ServiceSpec_To_v1_ServiceSpec,
convert_v1_PodSpec_To_api_PodSpec,
convert_v1_ReplicationControllerSpec_To_api_ReplicationControllerSpec,
convert_v1_ServiceSpec_To_api_ServiceSpec,
)
if err != nil {
// If one of the conversion functions is malformed, detect it immediately.
@ -365,3 +367,28 @@ func convert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conversi
}
return nil
}
func convert_api_ServiceSpec_To_v1_ServiceSpec(in *api.ServiceSpec, out *ServiceSpec, s conversion.Scope) error {
if err := autoconvert_api_ServiceSpec_To_v1_ServiceSpec(in, out, s); err != nil {
return err
}
// Publish both externalIPs and deprecatedPublicIPs fields in v1.
for _, ip := range in.ExternalIPs {
out.DeprecatedPublicIPs = append(out.DeprecatedPublicIPs, ip)
}
return nil
}
func convert_v1_ServiceSpec_To_api_ServiceSpec(in *ServiceSpec, out *api.ServiceSpec, s conversion.Scope) error {
if err := autoconvert_v1_ServiceSpec_To_api_ServiceSpec(in, out, s); err != nil {
return err
}
// Prefer the legacy deprecatedPublicIPs field, if provided.
if len(in.DeprecatedPublicIPs) > 0 {
out.ExternalIPs = nil
for _, ip := range in.DeprecatedPublicIPs {
out.ExternalIPs = append(out.ExternalIPs, ip)
}
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -2098,6 +2098,14 @@ func deepCopy_v1_ServiceSpec(in ServiceSpec, out *ServiceSpec, c *conversion.Clo
} else {
out.ExternalIPs = nil
}
if in.DeprecatedPublicIPs != nil {
out.DeprecatedPublicIPs = make([]string, len(in.DeprecatedPublicIPs))
for i := range in.DeprecatedPublicIPs {
out.DeprecatedPublicIPs[i] = in.DeprecatedPublicIPs[i]
}
} else {
out.DeprecatedPublicIPs = nil
}
out.SessionAffinity = in.SessionAffinity
out.LoadBalancerIP = in.LoadBalancerIP
return nil

View File

@ -1496,11 +1496,22 @@ type ServiceSpec struct {
// More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services
Type ServiceType `json:"type,omitempty"`
// ExternalIPs are used by external load balancers, or can be set by
// users to handle external traffic that arrives at a node.
// Externally visible IPs (e.g. load balancers) that should be proxied to this service.
// externalIPs is a list of IP addresses for which nodes in the cluster
// will also accept traffic for this service. These IPs are not managed by
// Kubernetes. The user is responsible for ensuring that traffic arrives
// at a node with this IP. A common example is external load-balancers
// that are not part of the Kubernetes system. A previous form of this
// functionality exists as the deprecatedPublicIPs field. When using this
// field, callers should also clear the deprecatedPublicIPs field.
ExternalIPs []string `json:"externalIPs,omitempty"`
// deprecatedPublicIPs is deprecated and replaced by the externalIPs field
// with almost the exact same semantics. This field is retained in the v1
// API for compatibility until at least 8/20/2016. It will be removed from
// any new API revisions. If both deprecatedPublicIPs *and* externalIPs are
// set, deprecatedPublicIPs is used.
DeprecatedPublicIPs []string `json:"deprecatedPublicIPs,omitempty"`
// Supports "ClientIP" and "None". Used to maintain session affinity.
// Enable client IP based session affinity.
// Must be ClientIP or None.

View File

@ -1290,14 +1290,15 @@ func (ServicePort) SwaggerDoc() map[string]string {
}
var map_ServiceSpec = map[string]string{
"": "ServiceSpec describes the attributes that a user creates on a service.",
"ports": "The list of ports that are exposed by this service. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"selector": "This service will route traffic to pods having labels matching this selector. Label keys and values that must match in order to receive traffic for this service. If empty, all pods are selected, if not specified, endpoints must be manually specified. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#overview",
"clusterIP": "ClusterIP is usually assigned by the master and is the IP address of the service. If specified, it will be allocated to the service if it is unused or else creation of the service will fail. Valid values are None, empty string (\"\"), or a valid IP address. 'None' can be specified for a headless service when proxying is not required. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"type": "Type of exposed service. Must be ClusterIP, NodePort, or LoadBalancer. Defaults to ClusterIP. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services",
"externalIPs": "ExternalIPs are used by external load balancers, or can be set by users to handle external traffic that arrives at a node. Externally visible IPs (e.g. load balancers) that should be proxied to this service.",
"sessionAffinity": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"loadBalancerIP": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.",
"": "ServiceSpec describes the attributes that a user creates on a service.",
"ports": "The list of ports that are exposed by this service. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"selector": "This service will route traffic to pods having labels matching this selector. Label keys and values that must match in order to receive traffic for this service. If empty, all pods are selected, if not specified, endpoints must be manually specified. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#overview",
"clusterIP": "ClusterIP is usually assigned by the master and is the IP address of the service. If specified, it will be allocated to the service if it is unused or else creation of the service will fail. Valid values are None, empty string (\"\"), or a valid IP address. 'None' can be specified for a headless service when proxying is not required. Cannot be updated. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"type": "Type of exposed service. Must be ClusterIP, NodePort, or LoadBalancer. Defaults to ClusterIP. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#external-services",
"externalIPs": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system. A previous form of this functionality exists as the deprecatedPublicIPs field. When using this field, callers should also clear the deprecatedPublicIPs field.",
"deprecatedPublicIPs": "deprecatedPublicIPs is deprecated and replaced by the externalIPs field with almost the exact same semantics. This field is retained in the v1 API for compatibility until at least 8/20/2016. It will be removed from any new API revisions. If both deprecatedPublicIPs *and* externalIPs are set, deprecatedPublicIPs is used.",
"sessionAffinity": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: http://releases.k8s.io/HEAD/docs/user-guide/services.md#virtual-ips-and-service-proxies",
"loadBalancerIP": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.",
}
func (ServiceSpec) SwaggerDoc() map[string]string {

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,7 @@ func NewConversionGenerator(scheme *conversion.Scheme, targetPkg string) Convers
scheme: scheme,
targetPkg: targetPkg,
convertibles: make(map[reflect.Type]reflect.Type),
overridden: make(map[reflect.Type]bool),
pkgOverwrites: make(map[string]string),
imports: make(map[string]string),
shortImports: make(map[string]string),
@ -60,6 +61,7 @@ type conversionGenerator struct {
scheme *conversion.Scheme
targetPkg string
convertibles map[reflect.Type]reflect.Type
overridden map[reflect.Type]bool
// If pkgOverwrites is set for a given package name, that package name
// will be replaced while writing conversion function. If empty, package
// name will be omitted.
@ -165,9 +167,10 @@ func (g *conversionGenerator) generateConversionsBetween(inType, outType reflect
if !existingConversion && (inErr != nil || outErr != nil) {
return inErr
}
if !existingConversion {
g.convertibles[inType] = outType
if existingConversion {
g.overridden[inType] = true
}
g.convertibles[inType] = outType
return nil
default:
// All simple types should be handled correctly with default conversion.
@ -368,7 +371,7 @@ func (g *conversionGenerator) RegisterConversionFunctions(w io.Writer, pkg strin
// Write conversion function names alphabetically ordered.
var names []string
for inType, outType := range g.convertibles {
names = append(names, g.conversionFunctionName(inType, outType))
names = append(names, g.generatedFunctionName(inType, outType))
}
sort.Strings(names)
@ -482,6 +485,10 @@ func (g *conversionGenerator) conversionFunctionName(inType, outType reflect.Typ
return funcName
}
func (g *conversionGenerator) generatedFunctionName(inType, outType reflect.Type) string {
return "auto" + g.conversionFunctionName(inType, outType)
}
func (g *conversionGenerator) writeHeader(b *buffer, name, inType, outType string, indent int) {
format := "func %s(in *%s, out *%s, s conversion.Scope) error {\n"
stmt := fmt.Sprintf(format, name, inType, outType)
@ -654,10 +661,28 @@ func (g *conversionGenerator) writeConversionForPtr(b *buffer, inField, outField
return nil
}
func (g *conversionGenerator) canTryConversion(b *buffer, inType reflect.Type, inField, outField reflect.StructField, indent int) (bool, error) {
if inField.Type.Kind() != outField.Type.Kind() {
if !g.overridden[inType] {
return false, fmt.Errorf("input %s.%s (%s) does not match output (%s) and conversion is not overridden", inType, inField.Name, inField.Type.Kind(), outField.Type.Kind())
}
b.addLine(fmt.Sprintf("// in.%s has no peer in out\n", inField.Name), indent)
return false, nil
}
return true, nil
}
func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outType reflect.Type, indent int) error {
for i := 0; i < inType.NumField(); i++ {
inField := inType.Field(i)
outField, _ := outType.FieldByName(inField.Name)
outField, found := outType.FieldByName(inField.Name)
if !found {
if !g.overridden[inType] {
return fmt.Errorf("input %s.%s has no peer in output %s and conversion is not overridden", inType, inField.Name, outType)
}
b.addLine(fmt.Sprintf("// in.%s has no peer in out\n", inField.Name), indent)
continue
}
existsConversion := g.scheme.Converter().HasConversionFunc(inField.Type, outField.Type)
if existsConversion && !g.existsDedicatedConversionFunction(inField.Type, outField.Type) {
@ -672,16 +697,31 @@ func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outTyp
switch inField.Type.Kind() {
case reflect.Map:
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
return err
} else if !try {
continue
}
if err := g.writeConversionForMap(b, inField, outField, indent); err != nil {
return err
}
continue
case reflect.Ptr:
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
return err
} else if !try {
continue
}
if err := g.writeConversionForPtr(b, inField, outField, indent); err != nil {
return err
}
continue
case reflect.Slice:
if try, err := g.canTryConversion(b, inType, inField, outField, indent); err != nil {
return err
} else if !try {
continue
}
if err := g.writeConversionForSlice(b, inField, outField, indent); err != nil {
return err
}
@ -721,8 +761,9 @@ func (g *conversionGenerator) writeConversionForStruct(b *buffer, inType, outTyp
}
func (g *conversionGenerator) writeConversionForType(b *buffer, inType, outType reflect.Type, indent int) error {
funcName := g.conversionFunctionName(inType, outType)
g.writeHeader(b, funcName, g.typeName(inType), g.typeName(outType), indent)
// Always emit the auto-generated name.
autoFuncName := g.generatedFunctionName(inType, outType)
g.writeHeader(b, autoFuncName, g.typeName(inType), g.typeName(outType), indent)
if err := g.writeDefaultingFunc(b, inType, indent+1); err != nil {
return err
}
@ -736,6 +777,15 @@ func (g *conversionGenerator) writeConversionForType(b *buffer, inType, outType
}
g.writeFooter(b, indent)
b.addLine("\n", 0)
if !g.overridden[inType] {
// Also emit the "user-facing" name.
userFuncName := g.conversionFunctionName(inType, outType)
g.writeHeader(b, userFuncName, g.typeName(inType), g.typeName(outType), indent)
b.addLine(fmt.Sprintf("return %s(in, out, s)\n", autoFuncName), indent+1)
b.addLine("}\n\n", 0)
}
return nil
}