diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index a3faf6f3cbb..d23078279b5 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -3099,27 +3099,27 @@ }, { "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/kube-openapi/pkg/generators", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87" + "Rev": "868f2f29720b192240e18284659231b440f9cda5" }, { "ImportPath": "k8s.io/utils/exec", diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go index 15f0830f5c6..aceaa463560 100644 --- a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go @@ -154,8 +154,28 @@ func (s *referenceWalker) Start() { } } -// FilterSpecByPaths remove unnecessary paths and unused definitions. +// usedDefinitionForSpec returns a map with all used definition in the provided spec as keys and true as values. +func usedDefinitionForSpec(sp *spec.Swagger) map[string]bool { + usedDefinitions := map[string]bool{} + walkOnAllReferences(func(ref spec.Ref) spec.Ref { + if refStr := ref.String(); refStr != "" && strings.HasPrefix(refStr, definitionPrefix) { + usedDefinitions[refStr[len(definitionPrefix):]] = true + } + return ref + }, sp) + return usedDefinitions +} + +// FilterSpecByPaths removes unnecessary paths and definitions used by those paths. +// i.e. if a Path removed by this function, all definition used by it and not used +// anywhere else will also be removed. func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) { + // Walk all references to find all used definitions. This function + // want to only deal with unused definitions resulted from filtering paths. + // Thus a definition will be removed only if it has been used before but + // it is unused because of a path prune. + initialUsedDefinitions := usedDefinitionForSpec(sp) + // First remove unwanted paths prefixes := util.NewTrie(keepPathPrefixes) orgPaths := sp.Paths @@ -174,34 +194,24 @@ func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) { } // Walk all references to find all definition references. - usedDefinitions := map[string]bool{} - - walkOnAllReferences(func(ref spec.Ref) spec.Ref { - if ref.String() != "" { - refStr := ref.String() - if strings.HasPrefix(refStr, definitionPrefix) { - usedDefinitions[refStr[len(definitionPrefix):]] = true - } - } - return ref - }, sp) + usedDefinitions := usedDefinitionForSpec(sp) // Remove unused definitions orgDefinitions := sp.Definitions sp.Definitions = spec.Definitions{} for k, v := range orgDefinitions { - if usedDefinitions[k] { + if usedDefinitions[k] || !initialUsedDefinitions[k] { sp.Definitions[k] = v } } } func renameDefinition(s *spec.Swagger, old, new string) { - old_ref := definitionPrefix + old - new_ref := definitionPrefix + new + oldRef := definitionPrefix + old + newRef := definitionPrefix + new walkOnAllReferences(func(ref spec.Ref) spec.Ref { - if ref.String() == old_ref { - return spec.MustCreateRef(new_ref) + if ref.String() == oldRef { + return spec.MustCreateRef(newRef) } return ref }, s) @@ -209,58 +219,117 @@ func renameDefinition(s *spec.Swagger, old, new string) { delete(s.Definitions, old) } -// Copy paths and definitions from source to dest, rename definitions if needed. -// dest will be mutated, and source will not be changed. +// MergeSpecsIgnorePathConflict is the same as MergeSpecs except it will ignore any path +// conflicts by keeping the paths of destination. It will rename definition conflicts. +func MergeSpecsIgnorePathConflict(dest, source *spec.Swagger) error { + return mergeSpecs(dest, source, true, true) +} + +// MergeSpecsFailOnDefinitionConflict is differ from MergeSpecs as it fails if there is +// a definition conflict. +func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error { + return mergeSpecs(dest, source, false, false) +} + +// MergeSpecs copies paths and definitions from source to dest, rename definitions if needed. +// dest will be mutated, and source will not be changed. It will fail on path conflicts. func MergeSpecs(dest, source *spec.Swagger) error { - sourceCopy, err := CloneSpec(source) - if err != nil { - return err + return mergeSpecs(dest, source, true, false) +} + +func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConflicts bool) (err error) { + specCloned := false + if ignorePathConflicts { + keepPaths := []string{} + hasConflictingPath := false + for k := range source.Paths.Paths { + if _, found := dest.Paths.Paths[k]; !found { + keepPaths = append(keepPaths, k) + } else { + hasConflictingPath = true + } + } + if len(keepPaths) == 0 { + // There is nothing to merge. All paths are conflicting. + return nil + } + if hasConflictingPath { + source, err = CloneSpec(source) + if err != nil { + return err + } + specCloned = true + FilterSpecByPaths(source, keepPaths) + } } - for k, v := range sourceCopy.Paths.Paths { + // Check for model conflicts + conflicts := false + for k, v := range source.Definitions { + v2, found := dest.Definitions[k] + if found && !reflect.DeepEqual(v, v2) { + if !renameModelConflicts { + return fmt.Errorf("model name conflict in merging OpenAPI spec: %s", k) + } + conflicts = true + break + } + } + + if conflicts { + if !specCloned { + source, err = CloneSpec(source) + if err != nil { + return err + } + } + specCloned = true + usedNames := map[string]bool{} + for k := range dest.Definitions { + usedNames[k] = true + } + type Rename struct { + from, to string + } + renames := []Rename{} + for k, v := range source.Definitions { + if usedNames[k] { + v2, found := dest.Definitions[k] + // Reuse model iff they are exactly the same. + if found && reflect.DeepEqual(v, v2) { + continue + } + i := 2 + newName := fmt.Sprintf("%s_v%d", k, i) + _, foundInSource := source.Definitions[newName] + for usedNames[newName] || foundInSource { + i++ + newName = fmt.Sprintf("%s_v%d", k, i) + _, foundInSource = source.Definitions[newName] + } + renames = append(renames, Rename{from: k, to: newName}) + usedNames[newName] = true + } + } + for _, r := range renames { + renameDefinition(source, r.from, r.to) + } + } + for k, v := range source.Definitions { + if _, found := dest.Definitions[k]; !found { + dest.Definitions[k] = v + } + } + // Check for path conflicts + for k, v := range source.Paths.Paths { if _, found := dest.Paths.Paths[k]; found { return fmt.Errorf("unable to merge: duplicated path %s", k) } dest.Paths.Paths[k] = v } - usedNames := map[string]bool{} - for k := range dest.Definitions { - usedNames[k] = true - } - type Rename struct { - from, to string - } - renames := []Rename{} - for k, v := range sourceCopy.Definitions { - if usedNames[k] { - v2, found := dest.Definitions[k] - // Reuse model iff they are exactly the same. - if found && reflect.DeepEqual(v, v2) { - continue - } - i := 2 - newName := fmt.Sprintf("%s_v%d", k, i) - _, foundInSource := sourceCopy.Definitions[newName] - for usedNames[newName] || foundInSource { - i += 1 - newName = fmt.Sprintf("%s_v%d", k, i) - _, foundInSource = sourceCopy.Definitions[newName] - } - renames = append(renames, Rename{from: k, to: newName}) - usedNames[newName] = true - } - } - for _, r := range renames { - renameDefinition(sourceCopy, r.from, r.to) - } - for k, v := range sourceCopy.Definitions { - if _, found := dest.Definitions[k]; !found { - dest.Definitions[k] = v - } - } return nil } -// Clone OpenAPI spec +// CloneSpec clones OpenAPI spec func CloneSpec(source *spec.Swagger) (*spec.Swagger, error) { // TODO(mehdy): Find a faster way to clone an spec bytes, err := json.Marshal(source) diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go index 0e8e6c7d5cf..c9f05fd29bf 100644 --- a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go +++ b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go @@ -328,6 +328,7 @@ func (g openAPITypeWriter) generateMembers(t *types.Type, required []string) ([] required = append(required, name) } if err = g.generateProperty(&m, t); err != nil { + glog.Errorf("Error when generating: %v, %v\n", name, m) return required, err } }