Revert "Revert "LimitRange updates for Resource Requirements Requests""

This commit is contained in:
Prashanth B
2015-08-28 09:26:36 -07:00
parent 2321d9ee26
commit 52f7833cd3
18 changed files with 780 additions and 180 deletions

View File

@@ -626,6 +626,30 @@ func deepCopy_api_LimitRangeItem(in LimitRangeItem, out *LimitRangeItem, c *conv
} else {
out.Default = nil
}
if in.DefaultRequest != nil {
out.DefaultRequest = make(ResourceList)
for key, val := range in.DefaultRequest {
newVal := new(resource.Quantity)
if err := deepCopy_resource_Quantity(val, newVal, c); err != nil {
return err
}
out.DefaultRequest[key] = *newVal
}
} else {
out.DefaultRequest = nil
}
if in.MaxLimitRequestRatio != nil {
out.MaxLimitRequestRatio = make(ResourceList)
for key, val := range in.MaxLimitRequestRatio {
newVal := new(resource.Quantity)
if err := deepCopy_resource_Quantity(val, newVal, c); err != nil {
return err
}
out.MaxLimitRequestRatio[key] = *newVal
}
} else {
out.MaxLimitRequestRatio = nil
}
return nil
}

View File

@@ -184,6 +184,28 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
q.Limits[api.ResourceStorage] = *storageLimit.Copy()
q.Requests[api.ResourceStorage] = *storageLimit.Copy()
},
func(q *api.LimitRangeItem, c fuzz.Continue) {
randomQuantity := func() resource.Quantity {
return *resource.NewQuantity(c.Int63n(1000), resource.DecimalExponent)
}
cpuLimit := randomQuantity()
q.Type = api.LimitTypeContainer
q.Default = make(api.ResourceList)
q.Default[api.ResourceCPU] = *(cpuLimit.Copy())
q.DefaultRequest = make(api.ResourceList)
q.DefaultRequest[api.ResourceCPU] = *(cpuLimit.Copy())
q.Max = make(api.ResourceList)
q.Max[api.ResourceCPU] = *(cpuLimit.Copy())
q.Min = make(api.ResourceList)
q.Min[api.ResourceCPU] = *(cpuLimit.Copy())
q.MaxLimitRequestRatio = make(api.ResourceList)
q.MaxLimitRequestRatio[api.ResourceCPU] = resource.MustParse("10")
},
func(p *api.PullPolicy, c fuzz.Continue) {
policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent}
*p = policies[c.Rand.Intn(len(policies))]

View File

@@ -1894,8 +1894,12 @@ type LimitRangeItem struct {
Max ResourceList `json:"max,omitempty"`
// Min usage constraints on this kind by resource name
Min ResourceList `json:"min,omitempty"`
// Default usage constraints on this kind by resource name
// Default resource requirement limit value by resource name.
Default ResourceList `json:"default,omitempty"`
// DefaultRequest resource requirement request value by resource name.
DefaultRequest ResourceList `json:"defaultRequest,omitempty"`
// MaxLimitRequestRatio represents the max burst value for the named resource
MaxLimitRequestRatio ResourceList `json:"maxLimitRequestRatio,omitempty"`
}
// LimitRangeSpec defines a min/max usage limit for resources that match on kind

View File

@@ -730,6 +730,30 @@ func convert_api_LimitRangeItem_To_v1_LimitRangeItem(in *api.LimitRangeItem, out
} else {
out.Default = nil
}
if in.DefaultRequest != nil {
out.DefaultRequest = make(ResourceList)
for key, val := range in.DefaultRequest {
newVal := resource.Quantity{}
if err := s.Convert(&val, &newVal, 0); err != nil {
return err
}
out.DefaultRequest[ResourceName(key)] = newVal
}
} else {
out.DefaultRequest = nil
}
if in.MaxLimitRequestRatio != nil {
out.MaxLimitRequestRatio = make(ResourceList)
for key, val := range in.MaxLimitRequestRatio {
newVal := resource.Quantity{}
if err := s.Convert(&val, &newVal, 0); err != nil {
return err
}
out.MaxLimitRequestRatio[ResourceName(key)] = newVal
}
} else {
out.MaxLimitRequestRatio = nil
}
return nil
}
@@ -3020,6 +3044,30 @@ func convert_v1_LimitRangeItem_To_api_LimitRangeItem(in *LimitRangeItem, out *ap
} else {
out.Default = nil
}
if in.DefaultRequest != nil {
out.DefaultRequest = make(api.ResourceList)
for key, val := range in.DefaultRequest {
newVal := resource.Quantity{}
if err := s.Convert(&val, &newVal, 0); err != nil {
return err
}
out.DefaultRequest[api.ResourceName(key)] = newVal
}
} else {
out.DefaultRequest = nil
}
if in.MaxLimitRequestRatio != nil {
out.MaxLimitRequestRatio = make(api.ResourceList)
for key, val := range in.MaxLimitRequestRatio {
newVal := resource.Quantity{}
if err := s.Convert(&val, &newVal, 0); err != nil {
return err
}
out.MaxLimitRequestRatio[api.ResourceName(key)] = newVal
}
} else {
out.MaxLimitRequestRatio = nil
}
return nil
}

View File

@@ -641,6 +641,30 @@ func deepCopy_v1_LimitRangeItem(in LimitRangeItem, out *LimitRangeItem, c *conve
} else {
out.Default = nil
}
if in.DefaultRequest != nil {
out.DefaultRequest = make(ResourceList)
for key, val := range in.DefaultRequest {
newVal := new(resource.Quantity)
if err := deepCopy_resource_Quantity(val, newVal, c); err != nil {
return err
}
out.DefaultRequest[key] = *newVal
}
} else {
out.DefaultRequest = nil
}
if in.MaxLimitRequestRatio != nil {
out.MaxLimitRequestRatio = make(ResourceList)
for key, val := range in.MaxLimitRequestRatio {
newVal := new(resource.Quantity)
if err := deepCopy_resource_Quantity(val, newVal, c); err != nil {
return err
}
out.MaxLimitRequestRatio[key] = *newVal
}
} else {
out.MaxLimitRequestRatio = nil
}
return nil
}

View File

@@ -173,6 +173,37 @@ func addDefaultingFuncs() {
}
}
},
func(obj *LimitRangeItem) {
// for container limits, we apply default values
if obj.Type == LimitTypeContainer {
if obj.Default == nil {
obj.Default = make(ResourceList)
}
if obj.DefaultRequest == nil {
obj.DefaultRequest = make(ResourceList)
}
// If a default limit is unspecified, but the max is specified, default the limit to the max
for key, value := range obj.Max {
if _, exists := obj.Default[key]; !exists {
obj.Default[key] = *(value.Copy())
}
}
// If a default limit is specified, but the default request is not, default request to limit
for key, value := range obj.Default {
if _, exists := obj.DefaultRequest[key]; !exists {
obj.DefaultRequest[key] = *(value.Copy())
}
}
// If a default request is not specified, but the min is provided, default request to the min
for key, value := range obj.Min {
if _, exists := obj.DefaultRequest[key]; !exists {
obj.DefaultRequest[key] = *(value.Copy())
}
}
}
},
)
}

View File

@@ -21,6 +21,7 @@ import (
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
versioned "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
@@ -431,3 +432,45 @@ func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) {
t.Errorf("Expected default APIVersion v1, got: %v", apiVersion)
}
}
func TestSetDefaultLimitRangeItem(t *testing.T) {
limitRange := &versioned.LimitRange{
ObjectMeta: versioned.ObjectMeta{
Name: "test-defaults",
},
Spec: versioned.LimitRangeSpec{
Limits: []versioned.LimitRangeItem{{
Type: versioned.LimitTypeContainer,
Max: versioned.ResourceList{
versioned.ResourceCPU: resource.MustParse("100m"),
},
Min: versioned.ResourceList{
versioned.ResourceMemory: resource.MustParse("100Mi"),
},
Default: versioned.ResourceList{},
DefaultRequest: versioned.ResourceList{},
}},
},
}
output := roundTrip(t, runtime.Object(limitRange))
limitRange2 := output.(*versioned.LimitRange)
defaultLimit := limitRange2.Spec.Limits[0].Default
defaultRequest := limitRange2.Spec.Limits[0].DefaultRequest
// verify that default cpu was set to the max
defaultValue := defaultLimit[versioned.ResourceCPU]
if defaultValue.String() != "100m" {
t.Errorf("Expected default cpu: %s, got: %s", "100m", defaultValue.String())
}
// verify that default request was set to the limit
requestValue := defaultRequest[versioned.ResourceCPU]
if requestValue.String() != "100m" {
t.Errorf("Expected request cpu: %s, got: %s", "100m", requestValue.String())
}
// verify that if a min is provided, it will be the default if no limit is specified
requestMinValue := defaultRequest[versioned.ResourceMemory]
if requestMinValue.String() != "100Mi" {
t.Errorf("Expected request memory: %s, got: %s", "100Mi", requestMinValue.String())
}
}

View File

@@ -2266,9 +2266,12 @@ type LimitRangeItem struct {
Max ResourceList `json:"max,omitempty"`
// Min usage constraints on this kind by resource name.
Min ResourceList `json:"min,omitempty"`
// Default usage constraints on this kind by resource name.
// Default values on this kind by resource name if omitted.
// Default resource requirement limit value by resource name if resource limit is omitted.
Default ResourceList `json:"default,omitempty"`
// DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.
DefaultRequest ResourceList `json:"defaultRequest,omitempty"`
// MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.
MaxLimitRequestRatio ResourceList `json:"maxLimitRequestRatio,omitempty"`
}
// LimitRangeSpec defines a min/max usage limit for resources that match on kind.

View File

@@ -432,11 +432,13 @@ func (LimitRange) SwaggerDoc() map[string]string {
}
var map_LimitRangeItem = map[string]string{
"": "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.",
"type": "Type of resource that this limit applies to.",
"max": "Max usage constraints on this kind by resource name.",
"min": "Min usage constraints on this kind by resource name.",
"default": "Default usage constraints on this kind by resource name. Default values on this kind by resource name if omitted.",
"": "LimitRangeItem defines a min/max usage limit for any resource that matches on kind.",
"type": "Type of resource that this limit applies to.",
"max": "Max usage constraints on this kind by resource name.",
"min": "Min usage constraints on this kind by resource name.",
"default": "Default resource requirement limit value by resource name if resource limit is omitted.",
"defaultRequest": "DefaultRequest is the default resource requirement request value by resource name if resource request is omitted.",
"maxLimitRequestRatio": "MaxLimitRequestRatio if specified, the named resource must have a request and limit that are both non-zero where limit divided by request is less than or equal to the enumerated value; this represents the max burst for the named resource.",
}
func (LimitRangeItem) SwaggerDoc() map[string]string {

View File

@@ -1352,6 +1352,7 @@ func ValidateLimitRange(limitRange *api.LimitRange) errs.ValidationErrorList {
min := map[string]int64{}
max := map[string]int64{}
defaults := map[string]int64{}
defaultRequests := map[string]int64{}
for k := range limit.Max {
allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].max[%s]", i, k))...)
@@ -1371,28 +1372,56 @@ func ValidateLimitRange(limitRange *api.LimitRange) errs.ValidationErrorList {
q := limit.Default[k]
defaults[string(k)] = q.Value()
}
for k := range limit.DefaultRequest {
allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k))...)
keys.Insert(string(k))
q := limit.DefaultRequest[k]
defaultRequests[string(k)] = q.Value()
}
for k := range limit.MaxLimitRequestRatio {
allErrs = append(allErrs, validateResourceName(string(k), fmt.Sprintf("spec.limits[%d].maxLimitRequestRatio[%s]", i, k))...)
}
for k := range keys {
minValue, minValueFound := min[k]
maxValue, maxValueFound := max[k]
defaultValue, defaultValueFound := defaults[k]
defaultRequestValue, defaultRequestValueFound := defaultRequests[k]
if minValueFound && maxValueFound && minValue > maxValue {
minQuantity := limit.Min[api.ResourceName(k)]
maxQuantity := limit.Max[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].max[%s]", i, k), minValue, fmt.Sprintf("min value %s is greater than max value %s", minQuantity.String(), maxQuantity.String())))
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].min[%s]", i, k), minValue, fmt.Sprintf("min value %s is greater than max value %s", minQuantity.String(), maxQuantity.String())))
}
if defaultRequestValueFound && minValueFound && minValue > defaultRequestValue {
minQuantity := limit.Min[api.ResourceName(k)]
defaultRequestQuantity := limit.DefaultRequest[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestValue, fmt.Sprintf("min value %s is greater than default request value %s", minQuantity.String(), defaultRequestQuantity.String())))
}
if defaultRequestValueFound && maxValueFound && defaultRequestValue > maxValue {
maxQuantity := limit.Max[api.ResourceName(k)]
defaultRequestQuantity := limit.DefaultRequest[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestValue, fmt.Sprintf("default request value %s is greater than max value %s", defaultRequestQuantity.String(), maxQuantity.String())))
}
if defaultRequestValueFound && defaultValueFound && defaultRequestValue > defaultValue {
defaultQuantity := limit.Default[api.ResourceName(k)]
defaultRequestQuantity := limit.DefaultRequest[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].defaultRequest[%s]", i, k), defaultRequestValue, fmt.Sprintf("default request value %s is greater than default limit value %s", defaultRequestQuantity.String(), defaultQuantity.String())))
}
if defaultValueFound && minValueFound && minValue > defaultValue {
minQuantity := limit.Min[api.ResourceName(k)]
defaultQuantity := limit.Default[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].max[%s]", i, k), minValue, fmt.Sprintf("min value %s is greater than default value %s", minQuantity.String(), defaultQuantity.String())))
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].default[%s]", i, k), minValue, fmt.Sprintf("min value %s is greater than default value %s", minQuantity.String(), defaultQuantity.String())))
}
if defaultValueFound && maxValueFound && defaultValue > maxValue {
maxQuantity := limit.Max[api.ResourceName(k)]
defaultQuantity := limit.Default[api.ResourceName(k)]
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].max[%s]", i, k), minValue, fmt.Sprintf("default value %s is greater than max value %s", defaultQuantity.String(), maxQuantity.String())))
allErrs = append(allErrs, errs.NewFieldInvalid(fmt.Sprintf("spec.limits[%d].default[%s]", i, k), maxValue, fmt.Sprintf("default value %s is greater than max value %s", defaultQuantity.String(), maxQuantity.String())))
}
}
}

View File

@@ -2814,13 +2814,20 @@ func TestValidateLimitRange(t *testing.T) {
api.ResourceMemory: resource.MustParse("10000"),
},
Min: api.ResourceList{
api.ResourceCPU: resource.MustParse("0"),
api.ResourceCPU: resource.MustParse("5"),
api.ResourceMemory: resource.MustParse("100"),
},
Default: api.ResourceList{
api.ResourceCPU: resource.MustParse("50"),
api.ResourceMemory: resource.MustParse("500"),
},
DefaultRequest: api.ResourceList{
api.ResourceCPU: resource.MustParse("10"),
api.ResourceMemory: resource.MustParse("200"),
},
MaxLimitRequestRatio: api.ResourceList{
api.ResourceCPU: resource.MustParse("20"),
},
},
},
}
@@ -2879,6 +2886,43 @@ func TestValidateLimitRange(t *testing.T) {
},
}
invalidSpecRangeDefaultRequestOutsideRange := api.LimitRangeSpec{
Limits: []api.LimitRangeItem{
{
Type: api.LimitTypePod,
Max: api.ResourceList{
api.ResourceCPU: resource.MustParse("1000"),
},
Min: api.ResourceList{
api.ResourceCPU: resource.MustParse("100"),
},
DefaultRequest: api.ResourceList{
api.ResourceCPU: resource.MustParse("2000"),
},
},
},
}
invalidSpecRangeRequestMoreThanDefaultRange := api.LimitRangeSpec{
Limits: []api.LimitRangeItem{
{
Type: api.LimitTypePod,
Max: api.ResourceList{
api.ResourceCPU: resource.MustParse("1000"),
},
Min: api.ResourceList{
api.ResourceCPU: resource.MustParse("100"),
},
Default: api.ResourceList{
api.ResourceCPU: resource.MustParse("500"),
},
DefaultRequest: api.ResourceList{
api.ResourceCPU: resource.MustParse("800"),
},
},
},
}
successCases := []api.LimitRange{
{
ObjectMeta: api.ObjectMeta{
@@ -2927,6 +2971,14 @@ func TestValidateLimitRange(t *testing.T) {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidSpecRangeDefaultOutsideRange},
"default value 2k is greater than max value 1k",
},
"invalid spec defaultrequest outside range": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidSpecRangeDefaultRequestOutsideRange},
"default request value 2k is greater than max value 1k",
},
"invalid spec defaultrequest more than default": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "foo"}, Spec: invalidSpecRangeRequestMoreThanDefaultRange},
"default request value 800 is greater than default limit value 500",
},
}
for k, v := range errorCases {
errs := ValidateLimitRange(&v.R)

View File

@@ -182,14 +182,16 @@ func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
fmt.Fprint(w, "No resource limits.\n")
return
}
fmt.Fprintf(w, "Resource Limits\n Type\tResource\tMin\tMax\tDefault\n")
fmt.Fprintf(w, " ----\t--------\t---\t---\t---\n")
fmt.Fprintf(w, "Resource Limits\n Type\tResource\tMin\tMax\tRequest\tLimit\tLimit/Request\n")
fmt.Fprintf(w, " ----\t--------\t---\t---\t-------\t-----\t-------------\n")
for _, limitRange := range limitRanges.Items {
for i := range limitRange.Spec.Limits {
item := limitRange.Spec.Limits[i]
maxResources := item.Max
minResources := item.Min
defaultResources := item.Default
defaultLimitResources := item.Default
defaultRequestResources := item.DefaultRequest
ratio := item.MaxLimitRequestRatio
set := map[api.ResourceName]bool{}
for k := range maxResources {
@@ -198,7 +200,13 @@ func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
for k := range minResources {
set[k] = true
}
for k := range defaultResources {
for k := range defaultLimitResources {
set[k] = true
}
for k := range defaultRequestResources {
set[k] = true
}
for k := range ratio {
set[k] = true
}
@@ -206,7 +214,9 @@ func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
defaultValue := "-"
defaultLimitValue := "-"
defaultRequestValue := "-"
ratioValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
@@ -218,13 +228,23 @@ func DescribeLimitRanges(limitRanges *api.LimitRangeList, w io.Writer) {
minValue = minQuantity.String()
}
defaultQuantity, defaultQuantityFound := defaultResources[k]
if defaultQuantityFound {
defaultValue = defaultQuantity.String()
defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
if defaultLimitQuantityFound {
defaultLimitValue = defaultLimitQuantity.String()
}
msg := " %v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(w, msg, item.Type, k, minValue, maxValue, defaultValue)
defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
if defaultRequestQuantityFound {
defaultRequestValue = defaultRequestQuantity.String()
}
ratioQuantity, ratioQuantityFound := ratio[k]
if ratioQuantityFound {
ratioValue = ratioQuantity.String()
}
msg := " %s\t%v\t%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(w, msg, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
}
}
}
@@ -287,13 +307,15 @@ func describeLimitRange(limitRange *api.LimitRange) (string, error) {
return tabbedString(func(out io.Writer) error {
fmt.Fprintf(out, "Name:\t%s\n", limitRange.Name)
fmt.Fprintf(out, "Namespace:\t%s\n", limitRange.Namespace)
fmt.Fprintf(out, "Type\tResource\tMin\tMax\tDefault\n")
fmt.Fprintf(out, "----\t--------\t---\t---\t---\n")
fmt.Fprintf(out, "Type\tResource\tMin\tMax\tRequest\tLimit\tLimit/Request\n")
fmt.Fprintf(out, "----\t--------\t---\t---\t-------\t-----\t-------------\n")
for i := range limitRange.Spec.Limits {
item := limitRange.Spec.Limits[i]
maxResources := item.Max
minResources := item.Min
defaultResources := item.Default
defaultLimitResources := item.Default
defaultRequestResources := item.DefaultRequest
ratio := item.MaxLimitRequestRatio
set := map[api.ResourceName]bool{}
for k := range maxResources {
@@ -302,7 +324,13 @@ func describeLimitRange(limitRange *api.LimitRange) (string, error) {
for k := range minResources {
set[k] = true
}
for k := range defaultResources {
for k := range defaultLimitResources {
set[k] = true
}
for k := range defaultRequestResources {
set[k] = true
}
for k := range ratio {
set[k] = true
}
@@ -310,7 +338,9 @@ func describeLimitRange(limitRange *api.LimitRange) (string, error) {
// if no value is set, we output -
maxValue := "-"
minValue := "-"
defaultValue := "-"
defaultLimitValue := "-"
defaultRequestValue := "-"
ratioValue := "-"
maxQuantity, maxQuantityFound := maxResources[k]
if maxQuantityFound {
@@ -322,13 +352,23 @@ func describeLimitRange(limitRange *api.LimitRange) (string, error) {
minValue = minQuantity.String()
}
defaultQuantity, defaultQuantityFound := defaultResources[k]
if defaultQuantityFound {
defaultValue = defaultQuantity.String()
defaultLimitQuantity, defaultLimitQuantityFound := defaultLimitResources[k]
if defaultLimitQuantityFound {
defaultLimitValue = defaultLimitQuantity.String()
}
msg := "%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(out, msg, item.Type, k, minValue, maxValue, defaultValue)
defaultRequestQuantity, defaultRequestQuantityFound := defaultRequestResources[k]
if defaultRequestQuantityFound {
defaultRequestValue = defaultRequestQuantity.String()
}
ratioQuantity, ratioQuantityFound := ratio[k]
if ratioQuantityFound {
ratioValue = ratioQuantity.String()
}
msg := "%v\t%v\t%v\t%v\t%v\t%v\t%v\n"
fmt.Fprintf(out, msg, item.Type, k, minValue, maxValue, defaultRequestValue, defaultLimitValue, ratioValue)
}
}
return nil