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() {
switch args[0] {
case "templates":
path := fmt.Sprintf("registries/%s/types", *templateRegistry)
if *regexString != "" {
path += fmt.Sprintf("?%s", url.QueryEscape(*regexString))
}
callService(path, "GET", "list templates", nil)
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":
template := loadTemplate(args)
callService("expand", "POST", "expand configuration", marshalTemplate(template))
......@@ -206,26 +216,19 @@ func execute() {
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])
callService(path, "GET", action, nil)
case "manifest":
msg := "Must specify manifest in the form <deployment>/<manifest> or <deployment> to list."
if len(args) < 2 {
msg := "Must specify manifest in the form <deployment> <manifest> or just <deployment> to list."
if len(args) < 2 || len(args) > 3 {
fmt.Fprintln(os.Stderr, msg)
os.Exit(1)
}
s := strings.Split(args[1], "/")
ls := len(s)
if ls < 1 || ls > 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])
path := fmt.Sprintf("deployments/%s/manifests", url.QueryEscape(args[1]))
if len(args) > 2 {
path = path + fmt.Sprintf("/%s", url.QueryEscape(args[2]))
}
action := fmt.Sprintf("get manifest %s", args[1])
......@@ -236,12 +239,12 @@ func execute() {
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])
callService(path, "DELETE", action, nil)
case "update":
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)
callService(path, "PUT", action, marshalTemplate(template))
case "deployed-types":
......@@ -252,15 +255,7 @@ func execute() {
os.Exit(1)
}
tUrls := getDownloadURLs(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]
}
tURL := args[1]
path := fmt.Sprintf("types/%s/instances", url.QueryEscape(tURL))
action := fmt.Sprintf("list deployed instances of type %s", tURL)
callService(path, "GET", action, nil)
......@@ -274,9 +269,14 @@ func execute() {
}
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{}
if err := json.Unmarshal([]byte(resp), &j); err != nil {
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 {
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 {
var template *common.Template
var err error
......@@ -433,24 +397,12 @@ func buildTemplateFromType(t string) *common.Template {
}
// Name the deployment after the type name.
name := t
config := common.Configuration{Resources: []*common.Resource{&common.Resource{
Name: name,
Type: getDownloadURLs(t)[0],
Properties: props,
}}}
y, err := yaml.Marshal(config)
template, err := expander.NewTemplateFromType(t, t, props)
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{
Name: name,
Content: string(y),
// No imports, as this is a single type from repository.
}
return template
}
func marshalTemplate(template *common.Template) io.ReadCloser {
......
......@@ -6,7 +6,7 @@ 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.
......@@ -46,6 +46,30 @@ func NewExpander(binary string) Expander {
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
// and imported files are read from the supplied archive.
func NewTemplateFromArchive(name string, r io.Reader, importFileNames []string) (*common.Template, error) {
......
......@@ -6,7 +6,7 @@ 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.
......@@ -28,6 +28,7 @@ import (
"strings"
"testing"
"github.com/ghodss/yaml"
"github.com/kubernetes/deployment-manager/common"
)
......@@ -107,6 +108,33 @@ func testExpandTemplateFromFile(t *testing.T, fileName, baseName string, importF
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) {
r := bytes.NewReader([]byte{})
if _, err := NewTemplateFromReader("test", r, nil); err == nil {
......
......@@ -53,6 +53,8 @@ var deployments = []Route{
{"Expand", "/expand", "POST", expandHandlerFunc, ""},
{"ListTypes", "/types", "GET", listTypesHandlerFunc, ""},
{"ListTypeInstances", "/types/{type}/instances", "GET", listTypeInstancesHandlerFunc, ""},
{"GetRegistryForType", "/types/{type}/registry", "GET", getRegistryForTypeHandlerFunc, ""},
{"GetMetadataForType", "/types/{type}/metadata", "GET", getMetadataForTypeHandlerFunc, ""},
{"ListRegistries", "/registries", "GET", listRegistriesHandlerFunc, ""},
{"GetRegistry", "/registries/{registry}", "GET", getRegistryHandlerFunc, ""},
{"CreateRegistry", "/registries/{registry}", "POST", createRegistryHandlerFunc, "JSON"},
......@@ -378,6 +380,40 @@ func listTypeInstancesHandlerFunc(w http.ResponseWriter, r *http.Request) {
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
// currently owns its own Manager backend and doesn't like to share.
func listRegistriesHandlerFunc(w http.ResponseWriter, r *http.Request) {
......@@ -459,12 +495,18 @@ func listRegistryTypesHandlerFunc(w http.ResponseWriter, r *http.Request) {
return
}
values, err := url.ParseQuery(r.URL.RawQuery)
if err != nil {
util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return
}
var regex *regexp.Regexp
regexString, err := getPathVariable(w, r, "regex", handler)
if err == nil {
regexString := values.Get("regex")
if regexString != "" {
regex, err = regexp.Compile(regexString)
if err != nil {
util.LogAndReturnError(handler, http.StatusInternalServerError, err, w)
util.LogAndReturnError(handler, http.StatusBadRequest, err, w)
return
}
}
......
......@@ -21,6 +21,7 @@ import (
"log"
"net/url"
"regexp"
"strings"
"time"
"github.com/kubernetes/deployment-manager/common"
......@@ -46,6 +47,8 @@ type Manager interface {
// Types
ListTypes() ([]string, error)
ListInstances(typeName string) ([]*common.TypeInstance, error)
GetRegistryForType(typeName string) (string, error)
GetMetadataForType(typeName string) (string, error)
// Registries
ListRegistries() ([]*common.Registry, error)
......@@ -334,6 +337,42 @@ func (m *manager) ListInstances(typeName string) ([]*common.TypeInstance, error)
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
func (m *manager) ListRegistries() ([]*common.Registry, error) {
return m.service.List()
......@@ -403,11 +442,16 @@ func (m *manager) GetFile(registryName string, url string) (string, error) {
return "", err
}
return getFileFromRegistry(url, r)
}
func getFileFromRegistry(url string, r registry.Registry) (string, error) {
getter := util.NewHTTPClient(3, r, util.NewSleeper())
body, _, err := getter.Get(url)
if err != nil {
return "", err
}
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