Commit 7344fed4 authored by Jack Greenfield's avatar Jack Greenfield

Merge pull request #256 from jackgr/url-escapes

Preparation for api cleanup.
parents 6d020901 2cdb34fd
...@@ -166,9 +166,19 @@ func execute() { ...@@ -166,9 +166,19 @@ func execute() {
switch args[0] { switch args[0] {
case "templates": case "templates":
path := fmt.Sprintf("registries/%s/types", *templateRegistry) path := fmt.Sprintf("registries/%s/types", *templateRegistry)
if *regexString != "" {
path += fmt.Sprintf("?%s", url.QueryEscape(*regexString))
}
callService(path, "GET", "list templates", nil) callService(path, "GET", "list templates", nil)
case "describe": case "describe":
describeType(args) if len(args) != 2 {
fmt.Fprintln(os.Stderr, "No type name or URL supplied")
os.Exit(1)
}
path := fmt.Sprintf("types/%s/metadata", url.QueryEscape(args[1]))
callService(path, "GET", "get metadata for type", nil)
case "expand": case "expand":
template := loadTemplate(args) template := loadTemplate(args)
callService("expand", "POST", "expand configuration", marshalTemplate(template)) callService("expand", "POST", "expand configuration", marshalTemplate(template))
...@@ -206,26 +216,19 @@ func execute() { ...@@ -206,26 +216,19 @@ func execute() {
os.Exit(1) os.Exit(1)
} }
path := fmt.Sprintf("deployments/%s", args[1]) path := fmt.Sprintf("deployments/%s", url.QueryEscape(args[1]))
action := fmt.Sprintf("get deployment named %s", args[1]) action := fmt.Sprintf("get deployment named %s", args[1])
callService(path, "GET", action, nil) callService(path, "GET", action, nil)
case "manifest": case "manifest":
msg := "Must specify manifest in the form <deployment>/<manifest> or <deployment> to list." msg := "Must specify manifest in the form <deployment> <manifest> or just <deployment> to list."
if len(args) < 2 { if len(args) < 2 || len(args) > 3 {
fmt.Fprintln(os.Stderr, msg) fmt.Fprintln(os.Stderr, msg)
os.Exit(1) os.Exit(1)
} }
s := strings.Split(args[1], "/") path := fmt.Sprintf("deployments/%s/manifests", url.QueryEscape(args[1]))
ls := len(s) if len(args) > 2 {
if ls < 1 || ls > 2 { path = path + fmt.Sprintf("/%s", url.QueryEscape(args[2]))
fmt.Fprintln(os.Stderr, fmt.Sprintf("Invalid manifest (%s), %s", args[1], msg))
os.Exit(1)
}
path := fmt.Sprintf("deployments/%s/manifests", s[0])
if ls == 2 {
path = path + fmt.Sprintf("/%s", s[1])
} }
action := fmt.Sprintf("get manifest %s", args[1]) action := fmt.Sprintf("get manifest %s", args[1])
...@@ -236,12 +239,12 @@ func execute() { ...@@ -236,12 +239,12 @@ func execute() {
os.Exit(1) os.Exit(1)
} }
path := fmt.Sprintf("deployments/%s", args[1]) path := fmt.Sprintf("deployments/%s", url.QueryEscape(args[1]))
action := fmt.Sprintf("delete deployment named %s", args[1]) action := fmt.Sprintf("delete deployment named %s", args[1])
callService(path, "DELETE", action, nil) callService(path, "DELETE", action, nil)
case "update": case "update":
template := loadTemplate(args) template := loadTemplate(args)
path := fmt.Sprintf("deployments/%s", template.Name) path := fmt.Sprintf("deployments/%s", url.QueryEscape(template.Name))
action := fmt.Sprintf("delete deployment named %s", template.Name) action := fmt.Sprintf("delete deployment named %s", template.Name)
callService(path, "PUT", action, marshalTemplate(template)) callService(path, "PUT", action, marshalTemplate(template))
case "deployed-types": case "deployed-types":
...@@ -252,15 +255,7 @@ func execute() { ...@@ -252,15 +255,7 @@ func execute() {
os.Exit(1) os.Exit(1)
} }
tUrls := getDownloadURLs(args[1]) tURL := args[1]
var tURL = ""
if len(tUrls) == 0 {
// Type is most likely a primitive.
tURL = args[1]
} else {
// TODO(vaikas): Support packages properly.
tURL = tUrls[0]
}
path := fmt.Sprintf("types/%s/instances", url.QueryEscape(tURL)) path := fmt.Sprintf("types/%s/instances", url.QueryEscape(tURL))
action := fmt.Sprintf("list deployed instances of type %s", tURL) action := fmt.Sprintf("list deployed instances of type %s", tURL)
callService(path, "GET", action, nil) callService(path, "GET", action, nil)
...@@ -274,9 +269,14 @@ func execute() { ...@@ -274,9 +269,14 @@ func execute() {
} }
func callService(path, method, action string, reader io.ReadCloser) { func callService(path, method, action string, reader io.ReadCloser) {
u := fmt.Sprintf("%s/%s", *service, path) var URL *url.URL
URL, err := url.Parse(*service)
if err != nil {
panic(fmt.Errorf("cannot parse url (%s): %s\n", path, err))
}
resp := callHTTP(u, method, action, reader) URL.Path += path
resp := callHTTP(URL.String(), method, action, reader)
var j interface{} var j interface{}
if err := json.Unmarshal([]byte(resp), &j); err != nil { if err := json.Unmarshal([]byte(resp), &j); err != nil {
panic(fmt.Errorf("Failed to parse JSON response from service: %s", resp)) panic(fmt.Errorf("Failed to parse JSON response from service: %s", resp))
...@@ -318,42 +318,6 @@ func callHTTP(path, method, action string, reader io.ReadCloser) string { ...@@ -318,42 +318,6 @@ func callHTTP(path, method, action string, reader io.ReadCloser) string {
return string(body) return string(body)
} }
// describeType prints the schema for a type specified by either a
// template URL or a fully qualified registry type name (e.g.,
// <type-name>:<version>)
func describeType(args []string) {
if len(args) != 2 {
fmt.Fprintln(os.Stderr, "No type name or URL supplied")
os.Exit(1)
}
tUrls := getDownloadURLs(url.QueryEscape(args[1]))
if len(tUrls) == 0 {
panic(fmt.Errorf("Invalid type name, must be a template URL or in the form \"<type-name>:<version>\": %s", args[1]))
}
if !strings.Contains(tUrls[0], ".prov") {
// It's not a chart, so grab the schema
path := fmt.Sprintf("registries/%s/download?file=%s.schema", *templateRegistry, url.QueryEscape(tUrls[0]))
callService(path, "GET", "get schema for type ("+tUrls[0]+")", nil)
} else {
// It's a chart, so grab the provenance file
path := fmt.Sprintf("registries/%s/download?file=%s", *templateRegistry, url.QueryEscape(tUrls[0]))
callService(path, "GET", "get file", nil)
}
}
// getDownloadURLs returns URLs for a type in the given registry
func getDownloadURLs(tName string) []string {
path := fmt.Sprintf("%s/registries/%s/types/%s", *service, *templateRegistry, url.QueryEscape(tName))
resp := callHTTP(path, "GET", "get download urls", nil)
u := []string{}
if err := json.Unmarshal([]byte(resp), &u); err != nil {
panic(fmt.Errorf("Failed to parse JSON response from service: %s", resp))
}
return u
}
func loadTemplate(args []string) *common.Template { func loadTemplate(args []string) *common.Template {
var template *common.Template var template *common.Template
var err error var err error
...@@ -433,24 +397,12 @@ func buildTemplateFromType(t string) *common.Template { ...@@ -433,24 +397,12 @@ func buildTemplateFromType(t string) *common.Template {
} }
// Name the deployment after the type name. // Name the deployment after the type name.
name := t template, err := expander.NewTemplateFromType(t, t, props)
config := common.Configuration{Resources: []*common.Resource{&common.Resource{
Name: name,
Type: getDownloadURLs(t)[0],
Properties: props,
}}}
y, err := yaml.Marshal(config)
if err != nil { if err != nil {
panic(fmt.Errorf("error: %s\ncannot create configuration for deployment: %v\n", err, config)) panic(fmt.Errorf("cannot create configuration from type (%s): %s\n", t, err))
} }
return &common.Template{ return template
Name: name,
Content: string(y),
// No imports, as this is a single type from repository.
}
} }
func marshalTemplate(template *common.Template) io.ReadCloser { func marshalTemplate(template *common.Template) io.ReadCloser {
......
...@@ -46,6 +46,30 @@ func NewExpander(binary string) Expander { ...@@ -46,6 +46,30 @@ func NewExpander(binary string) Expander {
return &expander{binary} return &expander{binary}
} }
// NewTemplateFromType creates and returns a new template whose content
// is a YAML marshaled resource assembled from the supplied arguments.
func NewTemplateFromType(name, typeName string, properties map[string]interface{}) (*common.Template, error) {
resource := &common.Resource{
Name: name,
Type: typeName,
Properties: properties,
}
config := common.Configuration{Resources: []*common.Resource{resource}}
content, err := yaml.Marshal(config)
if err != nil {
return nil, fmt.Errorf("error: %s\ncannot marshal configuration: %v\n", err, config)
}
template := &common.Template{
Name: name,
Content: string(content),
Imports: []*common.ImportFile{},
}
return template, nil
}
// NewTemplateFromArchive creates and returns a new template whose content // NewTemplateFromArchive creates and returns a new template whose content
// and imported files are read from the supplied archive. // and imported files are read from the supplied archive.
func NewTemplateFromArchive(name string, r io.Reader, importFileNames []string) (*common.Template, error) { func NewTemplateFromArchive(name string, r io.Reader, importFileNames []string) (*common.Template, error) {
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
) )
...@@ -107,6 +108,33 @@ func testExpandTemplateFromFile(t *testing.T, fileName, baseName string, importF ...@@ -107,6 +108,33 @@ func testExpandTemplateFromFile(t *testing.T, fileName, baseName string, importF
expandAndVerifyOutput(t, actualOutput, description) expandAndVerifyOutput(t, actualOutput, description)
} }
var (
testTemplateName = "expandybird"
testTemplateType = "replicatedservice.py"
testTemplateProperties = `
service_port: 8080
target_port: 8080
container_port: 8080
external_service: true
replicas: 3
image: gcr.io/dm-k8s-testing/expandybird
labels:
app: expandybird
`
)
func TestNewTemplateFromType(t *testing.T) {
var properties map[string]interface{}
if err := yaml.Unmarshal([]byte(testTemplateProperties), &properties); err != nil {
t.Fatalf("cannot unmarshal test data: %s", err)
}
_, err := NewTemplateFromType(testTemplateName, testTemplateType, properties)
if err != nil {
t.Fatalf("cannot create template from type %s: %s", testTemplateType, err)
}
}
func TestNewTemplateFromReader(t *testing.T) { func TestNewTemplateFromReader(t *testing.T) {
r := bytes.NewReader([]byte{}) r := bytes.NewReader([]byte{})
if _, err := NewTemplateFromReader("test", r, nil); err == nil { if _, err := NewTemplateFromReader("test", r, nil); err == nil {
......
...@@ -53,6 +53,8 @@ var deployments = []Route{ ...@@ -53,6 +53,8 @@ var deployments = []Route{
{"Expand", "/expand", "POST", expandHandlerFunc, ""}, {"Expand", "/expand", "POST", expandHandlerFunc, ""},
{"ListTypes", "/types", "GET", listTypesHandlerFunc, ""}, {"ListTypes", "/types", "GET", listTypesHandlerFunc, ""},
{"ListTypeInstances", "/types/{type}/instances", "GET", listTypeInstancesHandlerFunc, ""}, {"ListTypeInstances", "/types/{type}/instances", "GET", listTypeInstancesHandlerFunc, ""},
{"GetRegistryForType", "/types/{type}/registry", "GET", getRegistryForTypeHandlerFunc, ""},
{"GetMetadataForType", "/types/{type}/metadata", "GET", getMetadataForTypeHandlerFunc, ""},
{"ListRegistries", "/registries", "GET", listRegistriesHandlerFunc, ""}, {"ListRegistries", "/registries", "GET", listRegistriesHandlerFunc, ""},
{"GetRegistry", "/registries/{registry}", "GET", getRegistryHandlerFunc, ""}, {"GetRegistry", "/registries/{registry}", "GET", getRegistryHandlerFunc, ""},
{"CreateRegistry", "/registries/{registry}", "POST", createRegistryHandlerFunc, "JSON"}, {"CreateRegistry", "/registries/{registry}", "POST", createRegistryHandlerFunc, "JSON"},
...@@ -378,6 +380,40 @@ func listTypeInstancesHandlerFunc(w http.ResponseWriter, r *http.Request) { ...@@ -378,6 +380,40 @@ func listTypeInstancesHandlerFunc(w http.ResponseWriter, r *http.Request) {
util.LogHandlerExitWithJSON(handler, w, instances, http.StatusOK) util.LogHandlerExitWithJSON(handler, w, instances, http.StatusOK)
} }
func getRegistryForTypeHandlerFunc(w http.ResponseWriter, r *http.Request) {
handler := "manager: get type registry"
util.LogHandlerEntry(handler, r)
typeName, err := getPathVariable(w, r, "type", handler)
if err != nil {
return
}
registry, err := backend.GetRegistryForType(typeName)
if err != nil {
util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return
}
util.LogHandlerExitWithJSON(handler, w, registry, http.StatusOK)
}
func getMetadataForTypeHandlerFunc(w http.ResponseWriter, r *http.Request) {
handler := "manager: get type metadata"
util.LogHandlerEntry(handler, r)
typeName, err := getPathVariable(w, r, "type", handler)
if err != nil {
return
}
metadata, err := backend.GetMetadataForType(typeName)
if err != nil {
util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return
}
util.LogHandlerExitWithJSON(handler, w, metadata, http.StatusOK)
}
// Putting Registry handlers here for now because deployments.go // Putting Registry handlers here for now because deployments.go
// currently owns its own Manager backend and doesn't like to share. // currently owns its own Manager backend and doesn't like to share.
func listRegistriesHandlerFunc(w http.ResponseWriter, r *http.Request) { func listRegistriesHandlerFunc(w http.ResponseWriter, r *http.Request) {
...@@ -459,12 +495,18 @@ func listRegistryTypesHandlerFunc(w http.ResponseWriter, r *http.Request) { ...@@ -459,12 +495,18 @@ func listRegistryTypesHandlerFunc(w http.ResponseWriter, r *http.Request) {
return return
} }
values, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return
}
var regex *regexp.Regexp var regex *regexp.Regexp
regexString, err := getPathVariable(w, r, "regex", handler) regexString := values.Get("regex")
if err == nil { if regexString != "" {
regex, err = regexp.Compile(regexString) regex, err = regexp.Compile(regexString)
if err != nil { if err != nil {
util.LogAndReturnError(handler, http.StatusInternalServerError, err, w) util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return return
} }
} }
......
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"log" "log"
"net/url" "net/url"
"regexp" "regexp"
"strings"
"time" "time"
"github.com/kubernetes/deployment-manager/common" "github.com/kubernetes/deployment-manager/common"
...@@ -46,6 +47,8 @@ type Manager interface { ...@@ -46,6 +47,8 @@ type Manager interface {
// Types // Types
ListTypes() ([]string, error) ListTypes() ([]string, error)
ListInstances(typeName string) ([]*common.TypeInstance, error) ListInstances(typeName string) ([]*common.TypeInstance, error)
GetRegistryForType(typeName string) (string, error)
GetMetadataForType(typeName string) (string, error)
// Registries // Registries
ListRegistries() ([]*common.Registry, error) ListRegistries() ([]*common.Registry, error)
...@@ -334,6 +337,42 @@ func (m *manager) ListInstances(typeName string) ([]*common.TypeInstance, error) ...@@ -334,6 +337,42 @@ func (m *manager) ListInstances(typeName string) ([]*common.TypeInstance, error)
return m.repository.GetTypeInstances(typeName) return m.repository.GetTypeInstances(typeName)
} }
// GetRegistryForType returns the registry where a type resides.
func (m *manager) GetRegistryForType(typeName string) (string, error) {
_, r, err := registry.GetDownloadURLs(m.registryProvider, typeName)
if err != nil {
return "", err
}
return r.GetRegistryName(), nil
}
// GetMetadataForType returns the metadata for type.
func (m *manager) GetMetadataForType(typeName string) (string, error) {
URLs, r, err := registry.GetDownloadURLs(m.registryProvider, typeName)
if err != nil {
return "", err
}
if len(URLs) < 1 {
return "", nil
}
// If it's a chart, we want the provenance file
fPath := URLs[0]
if !strings.Contains(fPath, ".prov") {
// It's not a chart, so we want the schema
fPath += ".schema"
}
metadata, err := getFileFromRegistry(fPath, r)
if err != nil {
return "", fmt.Errorf("cannot get metadata for type (%s): %s", typeName, err)
}
return metadata, nil
}
// ListRegistries returns the list of registries // ListRegistries returns the list of registries
func (m *manager) ListRegistries() ([]*common.Registry, error) { func (m *manager) ListRegistries() ([]*common.Registry, error) {
return m.service.List() return m.service.List()
...@@ -403,11 +442,16 @@ func (m *manager) GetFile(registryName string, url string) (string, error) { ...@@ -403,11 +442,16 @@ func (m *manager) GetFile(registryName string, url string) (string, error) {
return "", err return "", err
} }
return getFileFromRegistry(url, r)
}
func getFileFromRegistry(url string, r registry.Registry) (string, error) {
getter := util.NewHTTPClient(3, r, util.NewSleeper()) getter := util.NewHTTPClient(3, r, util.NewSleeper())
body, _, err := getter.Get(url) body, _, err := getter.Get(url)
if err != nil { if err != nil {
return "", err return "", err
} }
return body, nil return body, nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment