diff --git a/dm/dm.go b/dm/dm.go index 9ecd0f1c0..6f1ee833b 100644 --- a/dm/dm.go +++ b/dm/dm.go @@ -19,6 +19,7 @@ import ( "github.com/kubernetes/deployment-manager/expandybird/expander" "github.com/kubernetes/deployment-manager/manager/manager" "github.com/kubernetes/deployment-manager/registry" + "github.com/kubernetes/deployment-manager/util" "bytes" "encoding/json" @@ -215,7 +216,7 @@ func describeType(args []string) { } func getTypeUrl(tName string) string { - if isHttp(tName) { + if util.IsHttpUrl(tName) { // User can pass raw URL to template. return tName } diff --git a/manager/manager/expander.go b/manager/manager/expander.go index 3456f8571..0e3e33422 100644 --- a/manager/manager/expander.go +++ b/manager/manager/expander.go @@ -21,6 +21,7 @@ import ( "net/http" "github.com/ghodss/yaml" + "github.com/kubernetes/deployment-manager/util" ) const ( @@ -96,7 +97,7 @@ func walkLayout(l *Layout, toReplace map[string]*LayoutResource) map[string]*Lay for len(toVisit) > 0 { lr := toVisit[0] nodeKey := lr.Resource.Name + layoutNodeKeySeparator + lr.Resource.Type - if len(lr.Layout.Resources) == 0 && Primitives[lr.Resource.Type] == false { + if len(lr.Layout.Resources) == 0 && util.IsTemplate(lr.Resource.Type) { ret[nodeKey] = lr } else if toReplace[nodeKey] != nil { toReplace[nodeKey].Resources = lr.Resources diff --git a/manager/manager/expander_test.go b/manager/manager/expander_test.go index a14116f84..0bf80a179 100644 --- a/manager/manager/expander_test.go +++ b/manager/manager/expander_test.go @@ -138,7 +138,7 @@ var roundTripContent = ` config: resources: - name: test - type: test + type: test.py properties: test: test ` @@ -146,7 +146,7 @@ config: var roundTripExpanded = ` resources: - name: test2 - type: test2 + type: test2.py properties: test: test ` @@ -154,12 +154,12 @@ resources: var roundTripLayout = ` resources: - name: test - type: test + type: test.py properties: test: test resources: - name: test2 - type: test2 + type: test2.py properties: test: test ` @@ -175,7 +175,7 @@ resources: var roundTripLayout2 = ` resources: - name: test2 - type: test2 + type: test2.py properties: test: test resources: @@ -195,12 +195,12 @@ config: layout: resources: - name: test - type: test + type: test.py properties: test: test resources: - name: test2 - type: test2 + type: test2.py properties: test: test resources: @@ -251,7 +251,7 @@ func TestExpandTemplate(t *testing.T) { roundTripHandler, &mockResolver{[][]*ImportFile{ {}, - {&ImportFile{Name: "test"}}, + {&ImportFile{Name: "test.py"}}, }, t}, roundTripResponse, }, diff --git a/manager/manager/typeresolver.go b/manager/manager/typeresolver.go index 785b3f42a..2696401a2 100644 --- a/manager/manager/typeresolver.go +++ b/manager/manager/typeresolver.go @@ -87,11 +87,13 @@ func (tr *typeResolver) ResolveTypes(config *Configuration, imports []*ImportFil fetched := map[string][]*ImportFile{} toFetch := make([]string, 0, tr.maxUrls) for _, r := range config.Resources { - if !Primitives[r.Type] && !existing[r.Type] { + // Only fetch HTTP URLs that we haven't already imported. + if util.IsHttpUrl(r.Type) && !existing[r.Type] { toFetch = append(toFetch, r.Type) fetched[r.Type] = append(fetched[r.Type], &ImportFile{Name: r.Type}) } } + count := 0 for len(toFetch) > 0 { //1. Fetch import URL. Exit if no URLs left diff --git a/manager/manager/typeresolver_test.go b/manager/manager/typeresolver_test.go index ff87d488f..a9d80269b 100644 --- a/manager/manager/typeresolver_test.go +++ b/manager/manager/typeresolver_test.go @@ -117,15 +117,15 @@ func TestIncludedImport(t *testing.T) { var templateSingleURL = ` resources: - name: foo - type: my-fake-url + type: http://my-fake-url ` func TestSingleUrl(t *testing.T) { - finalImports := []*ImportFile{&ImportFile{Name: "my-fake-url", Content: "my-content"}} + finalImports := []*ImportFile{&ImportFile{Name: "http://my-fake-url", Content: "my-content"}} responses := map[string]responseAndError{ - "my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url.schema": responseAndError{nil, http.StatusNotFound, ""}, } test := resolverTestCase{ @@ -139,7 +139,7 @@ func TestSingleUrl(t *testing.T) { func TestSingleUrlWith500(t *testing.T) { responses := map[string]responseAndError{ - "my-fake-url": responseAndError{nil, http.StatusInternalServerError, "my-content"}, + "http://my-fake-url": responseAndError{nil, http.StatusInternalServerError, "my-content"}, } test := resolverTestCase{ @@ -159,16 +159,16 @@ imports: func TestSingleUrlWithSchema(t *testing.T) { finalImports := []*ImportFile{ - &ImportFile{Name: "my-fake-url", Content: "my-content"}, + &ImportFile{Name: "http://my-fake-url", Content: "my-content"}, &ImportFile{Name: "schema-import", Content: "schema-import"}, - &ImportFile{Name: "my-fake-url.schema", Content: schema1}, + &ImportFile{Name: "http://my-fake-url.schema", Content: schema1}, } responses := map[string]responseAndError{ - "my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url.schema": responseAndError{nil, http.StatusOK, schema1}, - "my-next-url": responseAndError{nil, http.StatusOK, "schema-import"}, - "my-next-url.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url.schema": responseAndError{nil, http.StatusOK, schema1}, + "my-next-url": responseAndError{nil, http.StatusOK, "schema-import"}, + "my-next-url.schema": responseAndError{nil, http.StatusNotFound, ""}, } test := resolverTestCase{ @@ -183,33 +183,33 @@ func TestSingleUrlWithSchema(t *testing.T) { var templateExceedsMax = ` resources: - name: foo - type: my-fake-url + type: http://my-fake-url - name: foo1 - type: my-fake-url1 + type: http://my-fake-url1 - name: foo2 - type: my-fake-url2 + type: http://my-fake-url2 - name: foo3 - type: my-fake-url3 + type: http://my-fake-url3 - name: foo4 - type: my-fake-url4 + type: http://my-fake-url4 - name: foo5 - type: my-fake-url5 + type: http://my-fake-url5 ` func TestTooManyImports(t *testing.T) { responses := map[string]responseAndError{ - "my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url.schema": responseAndError{nil, http.StatusNotFound, ""}, - "my-fake-url1": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url1.schema": responseAndError{nil, http.StatusNotFound, ""}, - "my-fake-url2": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url2.schema": responseAndError{nil, http.StatusNotFound, ""}, - "my-fake-url3": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url3.schema": responseAndError{nil, http.StatusNotFound, ""}, - "my-fake-url4": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url4.schema": responseAndError{nil, http.StatusNotFound, ""}, - "my-fake-url5": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url5.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url1": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url1.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url2": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url2.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url3": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url3.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url4": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url4.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url5": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url5.schema": responseAndError{nil, http.StatusNotFound, ""}, } test := resolverTestCase{ @@ -224,9 +224,9 @@ func TestTooManyImports(t *testing.T) { var templateSharesImport = ` resources: - name: foo - type: my-fake-url + type: http://my-fake-url - name: foo1 - type: my-fake-url1 + type: http://my-fake-url1 ` var schema2 = ` @@ -237,21 +237,21 @@ imports: func TestSharedImport(t *testing.T) { finalImports := []*ImportFile{ - &ImportFile{Name: "my-fake-url", Content: "my-content"}, - &ImportFile{Name: "my-fake-url1", Content: "my-content-1"}, + &ImportFile{Name: "http://my-fake-url", Content: "my-content"}, + &ImportFile{Name: "http://my-fake-url1", Content: "my-content-1"}, &ImportFile{Name: "schema-import", Content: "schema-import"}, &ImportFile{Name: "schema-import-1", Content: "schema-import"}, - &ImportFile{Name: "my-fake-url.schema", Content: schema1}, - &ImportFile{Name: "my-fake-url1.schema", Content: schema2}, + &ImportFile{Name: "http://my-fake-url.schema", Content: schema1}, + &ImportFile{Name: "http://my-fake-url1.schema", Content: schema2}, } responses := map[string]responseAndError{ - "my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, - "my-fake-url.schema": responseAndError{nil, http.StatusOK, schema1}, - "my-fake-url1": responseAndError{nil, http.StatusOK, "my-content-1"}, - "my-fake-url1.schema": responseAndError{nil, http.StatusOK, schema2}, - "my-next-url": responseAndError{nil, http.StatusOK, "schema-import"}, - "my-next-url.schema": responseAndError{nil, http.StatusNotFound, ""}, + "http://my-fake-url": responseAndError{nil, http.StatusOK, "my-content"}, + "http://my-fake-url.schema": responseAndError{nil, http.StatusOK, schema1}, + "http://my-fake-url1": responseAndError{nil, http.StatusOK, "my-content-1"}, + "http://my-fake-url1.schema": responseAndError{nil, http.StatusOK, schema2}, + "my-next-url": responseAndError{nil, http.StatusOK, "schema-import"}, + "my-next-url.schema": responseAndError{nil, http.StatusNotFound, ""}, } test := resolverTestCase{ diff --git a/manager/manager/types.go b/manager/manager/types.go index d324ef1fb..cf7944296 100644 --- a/manager/manager/types.go +++ b/manager/manager/types.go @@ -17,19 +17,6 @@ import ( "time" ) -// This map defines the primitives that DM knows how to handle implicitly. -// TODO (iantw): Make these come from the resourcifier(?). Add more as appropriate... -var Primitives = map[string]bool{ - "Pod": true, - "ReplicationController": true, - "Service": true, - "Namespace": true, - "Volume": true, - "Endpoints": true, - "PersistentVolumeClaim": true, - "PersistentVolume": true, -} - // SchemaImport represents an import as declared in a schema file. type SchemaImport struct { Path string `json:"path"` diff --git a/util/httputil.go b/util/httputil.go index d921e3dac..d1d9105b0 100644 --- a/util/httputil.go +++ b/util/httputil.go @@ -200,3 +200,13 @@ func ToJSONOrError(v interface{}) string { return string(j) } + +// IsHttpURL returns whether a string is an HTTP URL. +func IsHttpUrl(s string) bool { + u, err := url.Parse(s) + if err != nil { + return false + } + + return u.Scheme == "http" || u.Scheme == "https" +} diff --git a/util/templateutil.go b/util/templateutil.go new file mode 100644 index 000000000..efc5e8fcc --- /dev/null +++ b/util/templateutil.go @@ -0,0 +1,23 @@ +/* +Copyright 2015 The Kubernetes Authors 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 util + +import ( + "strings" +) + +// IsTemplate returns whether a given type is a template. +func IsTemplate(t string) bool { + return strings.HasSuffix(t, ".py") || strings.HasSuffix(t, ".jinja") +}