mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 09:37:21 +00:00 
			
		
		
		
	- the image upload uses the cloud API - currently auth and image creation need the `gcloud` CLI tool. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
		
			
				
	
	
		
			249 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2013 Joshua Tacoma. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| // Package uritemplates is a level 3 implementation of RFC 6570 (URI
 | |
| // Template, http://tools.ietf.org/html/rfc6570).
 | |
| // uritemplates does not support composite values (in Go: slices or maps)
 | |
| // and so does not qualify as a level 4 implementation.
 | |
| package uritemplates
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	unreserved = regexp.MustCompile("[^A-Za-z0-9\\-._~]")
 | |
| 	reserved   = regexp.MustCompile("[^A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=]")
 | |
| 	validname  = regexp.MustCompile("^([A-Za-z0-9_\\.]|%[0-9A-Fa-f][0-9A-Fa-f])+$")
 | |
| 	hex        = []byte("0123456789ABCDEF")
 | |
| )
 | |
| 
 | |
| func pctEncode(src []byte) []byte {
 | |
| 	dst := make([]byte, len(src)*3)
 | |
| 	for i, b := range src {
 | |
| 		buf := dst[i*3 : i*3+3]
 | |
| 		buf[0] = 0x25
 | |
| 		buf[1] = hex[b/16]
 | |
| 		buf[2] = hex[b%16]
 | |
| 	}
 | |
| 	return dst
 | |
| }
 | |
| 
 | |
| // pairWriter is a convenience struct which allows escaped and unescaped
 | |
| // versions of the template to be written in parallel.
 | |
| type pairWriter struct {
 | |
| 	escaped, unescaped bytes.Buffer
 | |
| }
 | |
| 
 | |
| // Write writes the provided string directly without any escaping.
 | |
| func (w *pairWriter) Write(s string) {
 | |
| 	w.escaped.WriteString(s)
 | |
| 	w.unescaped.WriteString(s)
 | |
| }
 | |
| 
 | |
| // Escape writes the provided string, escaping the string for the
 | |
| // escaped output.
 | |
| func (w *pairWriter) Escape(s string, allowReserved bool) {
 | |
| 	w.unescaped.WriteString(s)
 | |
| 	if allowReserved {
 | |
| 		w.escaped.Write(reserved.ReplaceAllFunc([]byte(s), pctEncode))
 | |
| 	} else {
 | |
| 		w.escaped.Write(unreserved.ReplaceAllFunc([]byte(s), pctEncode))
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Escaped returns the escaped string.
 | |
| func (w *pairWriter) Escaped() string {
 | |
| 	return w.escaped.String()
 | |
| }
 | |
| 
 | |
| // Unescaped returns the unescaped string.
 | |
| func (w *pairWriter) Unescaped() string {
 | |
| 	return w.unescaped.String()
 | |
| }
 | |
| 
 | |
| // A uriTemplate is a parsed representation of a URI template.
 | |
| type uriTemplate struct {
 | |
| 	raw   string
 | |
| 	parts []templatePart
 | |
| }
 | |
| 
 | |
| // parse parses a URI template string into a uriTemplate object.
 | |
| func parse(rawTemplate string) (*uriTemplate, error) {
 | |
| 	split := strings.Split(rawTemplate, "{")
 | |
| 	parts := make([]templatePart, len(split)*2-1)
 | |
| 	for i, s := range split {
 | |
| 		if i == 0 {
 | |
| 			if strings.Contains(s, "}") {
 | |
| 				return nil, errors.New("unexpected }")
 | |
| 			}
 | |
| 			parts[i].raw = s
 | |
| 			continue
 | |
| 		}
 | |
| 		subsplit := strings.Split(s, "}")
 | |
| 		if len(subsplit) != 2 {
 | |
| 			return nil, errors.New("malformed template")
 | |
| 		}
 | |
| 		expression := subsplit[0]
 | |
| 		var err error
 | |
| 		parts[i*2-1], err = parseExpression(expression)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		parts[i*2].raw = subsplit[1]
 | |
| 	}
 | |
| 	return &uriTemplate{
 | |
| 		raw:   rawTemplate,
 | |
| 		parts: parts,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| type templatePart struct {
 | |
| 	raw           string
 | |
| 	terms         []templateTerm
 | |
| 	first         string
 | |
| 	sep           string
 | |
| 	named         bool
 | |
| 	ifemp         string
 | |
| 	allowReserved bool
 | |
| }
 | |
| 
 | |
| type templateTerm struct {
 | |
| 	name     string
 | |
| 	explode  bool
 | |
| 	truncate int
 | |
| }
 | |
| 
 | |
| func parseExpression(expression string) (result templatePart, err error) {
 | |
| 	switch expression[0] {
 | |
| 	case '+':
 | |
| 		result.sep = ","
 | |
| 		result.allowReserved = true
 | |
| 		expression = expression[1:]
 | |
| 	case '.':
 | |
| 		result.first = "."
 | |
| 		result.sep = "."
 | |
| 		expression = expression[1:]
 | |
| 	case '/':
 | |
| 		result.first = "/"
 | |
| 		result.sep = "/"
 | |
| 		expression = expression[1:]
 | |
| 	case ';':
 | |
| 		result.first = ";"
 | |
| 		result.sep = ";"
 | |
| 		result.named = true
 | |
| 		expression = expression[1:]
 | |
| 	case '?':
 | |
| 		result.first = "?"
 | |
| 		result.sep = "&"
 | |
| 		result.named = true
 | |
| 		result.ifemp = "="
 | |
| 		expression = expression[1:]
 | |
| 	case '&':
 | |
| 		result.first = "&"
 | |
| 		result.sep = "&"
 | |
| 		result.named = true
 | |
| 		result.ifemp = "="
 | |
| 		expression = expression[1:]
 | |
| 	case '#':
 | |
| 		result.first = "#"
 | |
| 		result.sep = ","
 | |
| 		result.allowReserved = true
 | |
| 		expression = expression[1:]
 | |
| 	default:
 | |
| 		result.sep = ","
 | |
| 	}
 | |
| 	rawterms := strings.Split(expression, ",")
 | |
| 	result.terms = make([]templateTerm, len(rawterms))
 | |
| 	for i, raw := range rawterms {
 | |
| 		result.terms[i], err = parseTerm(raw)
 | |
| 		if err != nil {
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	return result, err
 | |
| }
 | |
| 
 | |
| func parseTerm(term string) (result templateTerm, err error) {
 | |
| 	// TODO(djd): Remove "*" suffix parsing once we check that no APIs have
 | |
| 	// mistakenly used that attribute.
 | |
| 	if strings.HasSuffix(term, "*") {
 | |
| 		result.explode = true
 | |
| 		term = term[:len(term)-1]
 | |
| 	}
 | |
| 	split := strings.Split(term, ":")
 | |
| 	if len(split) == 1 {
 | |
| 		result.name = term
 | |
| 	} else if len(split) == 2 {
 | |
| 		result.name = split[0]
 | |
| 		var parsed int64
 | |
| 		parsed, err = strconv.ParseInt(split[1], 10, 0)
 | |
| 		result.truncate = int(parsed)
 | |
| 	} else {
 | |
| 		err = errors.New("multiple colons in same term")
 | |
| 	}
 | |
| 	if !validname.MatchString(result.name) {
 | |
| 		err = errors.New("not a valid name: " + result.name)
 | |
| 	}
 | |
| 	if result.explode && result.truncate > 0 {
 | |
| 		err = errors.New("both explode and prefix modifers on same term")
 | |
| 	}
 | |
| 	return result, err
 | |
| }
 | |
| 
 | |
| // Expand expands a URI template with a set of values to produce the
 | |
| // resultant URI. Two forms of the result are returned: one with all the
 | |
| // elements escaped, and one with the elements unescaped.
 | |
| func (t *uriTemplate) Expand(values map[string]string) (escaped, unescaped string) {
 | |
| 	var w pairWriter
 | |
| 	for _, p := range t.parts {
 | |
| 		p.expand(&w, values)
 | |
| 	}
 | |
| 	return w.Escaped(), w.Unescaped()
 | |
| }
 | |
| 
 | |
| func (tp *templatePart) expand(w *pairWriter, values map[string]string) {
 | |
| 	if len(tp.raw) > 0 {
 | |
| 		w.Write(tp.raw)
 | |
| 		return
 | |
| 	}
 | |
| 	var first = true
 | |
| 	for _, term := range tp.terms {
 | |
| 		value, exists := values[term.name]
 | |
| 		if !exists {
 | |
| 			continue
 | |
| 		}
 | |
| 		if first {
 | |
| 			w.Write(tp.first)
 | |
| 			first = false
 | |
| 		} else {
 | |
| 			w.Write(tp.sep)
 | |
| 		}
 | |
| 		tp.expandString(w, term, value)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (tp *templatePart) expandName(w *pairWriter, name string, empty bool) {
 | |
| 	if tp.named {
 | |
| 		w.Write(name)
 | |
| 		if empty {
 | |
| 			w.Write(tp.ifemp)
 | |
| 		} else {
 | |
| 			w.Write("=")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (tp *templatePart) expandString(w *pairWriter, t templateTerm, s string) {
 | |
| 	if len(s) > t.truncate && t.truncate > 0 {
 | |
| 		s = s[:t.truncate]
 | |
| 	}
 | |
| 	tp.expandName(w, t.name, len(s) == 0)
 | |
| 	w.Escape(s, tp.allowReserved)
 | |
| }
 |