<commit_before>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
)

const (
	defaultBaseURL = "https://api.buildkite.com/"
	userAgent      = "go-buildkite/" + Version
)

// A Client manages communication with the buildkite API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.  Defaults to the public buildkite API. BaseURL should
	// always be specified with a trailing slash.
	BaseURL *url.URL

	// User agent used when communicating with the buildkite API.
	UserAgent string

	// Services used for talking to different parts of the buildkite API.
	Agents        *AgentsService
	Builds        *BuildsService
	Organizations *OrganizationsService
	Projects      *ProjectsService
	User          *UserService
}

// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
	// For paginated result sets, page of results to retrieve.
	Page int `url:"page,omitempty"`

	// For paginated result sets, the number of results to include per page.
	PerPage int `url:"per_page,omitempty"`
}

// NewClient returns a new buildkite API client. As API calls require authentication
// you MUST supply a client which provides the required API key.
func NewClient(httpClient *http.Client) *Client {

	baseURL, _ := url.Parse(defaultBaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: userAgent,
	}

	c.Agents = &AgentsService{c}
	c.Builds = &BuildsService{c}
	c.Organizations = &OrganizationsService{c}
	c.Projects = &ProjectsService{c}
	c.User = &UserService{c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash.  If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	var buf io.ReadWriter
	if body != nil {
		buf = new(bytes.Buffer)
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	if c.UserAgent != "" {
		req.Header.Add("User-Agent", c.UserAgent)
	}
	return req, nil
}

// Response is a buildkite API response.  This wraps the standard http.Response
// returned from buildkite and provides convenient access to things like
// pagination links.
type Response struct {
	*http.Response

	// These fields provide the page values for paginating through a set of
	// results.  Any or all of these may be set to the zero value for
	// responses that are not part of a paginated set, or for which there
	// are no additional pages.

	NextPage  int
	PrevPage  int
	FirstPage int
	LastPage  int
}

// newResponse creats a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	response.populatePageValues()
	return response
}

// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Reponse.
func (r *Response) populatePageValues() {
	if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
		for _, link := range strings.Split(links[0], ",") {
			segments := strings.Split(strings.TrimSpace(link), ";")

			// link must at least have href and rel
			if len(segments) < 2 {
				continue
			}

			// ensure href is properly formatted
			if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
				continue
			}

			// try to pull out page parameter
			url, err := url.Parse(segments[0][1 : len(segments[0])-1])
			if err != nil {
				continue
			}
			page := url.Query().Get("page")
			if page == "" {
				continue
			}

			for _, segment := range segments[1:] {
				switch strings.TrimSpace(segment) {
				case `rel="next"`:
					r.NextPage, _ = strconv.Atoi(page)
				case `rel="prev"`:
					r.PrevPage, _ = strconv.Atoi(page)
				case `rel="first"`:
					r.FirstPage, _ = strconv.Atoi(page)
				case `rel="last"`:
					r.LastPage, _ = strconv.Atoi(page)
				}

			}
		}
	}
}

// Do sends an API request and returns the API response.  The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred.  If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	err = checkResponse(resp)
	if err != nil {
		// even though there was an error, we still return the response
		// in case the caller wants to inspect it further
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
		}
	}
	return response, err
}

// ErrorResponse provides a message.
type ErrorResponse struct {
	Response *http.Response // HTTP response that caused this error
	Message  string         `json:"message"` // error message
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Message)
}

func checkResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}
	return errorResponse
}

// addOptions adds the parameters in opt as URL query parameters to s.  opt
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return s, nil
	}

	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}

	qs, err := query.Values(opt)
	if err != nil {
		return s, err
	}

	u.RawQuery = qs.Encode()
	return u.String(), nil
}

// Int is a helper routine that allocates a new int value
// to store v and returns a pointer to it, but unlike Int
// its argument value is an int.
func Int(v int) *int {
	p := new(int)
	*p = v
	return p
}

// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
	p := new(string)
	*p = v
	return p
}
<commit_msg>Tweak retrieval code.<commit_after>// Copyright 2014 Mark Wolfe. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildkite

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"

	"github.com/google/go-querystring/query"
)

const (
	defaultBaseURL = "https://api.buildkite.com/"
	userAgent      = "go-buildkite/" + Version
)

// A Client manages communication with the buildkite API.
type Client struct {
	// HTTP client used to communicate with the API.
	client *http.Client

	// Base URL for API requests.  Defaults to the public buildkite API. BaseURL should
	// always be specified with a trailing slash.
	BaseURL *url.URL

	// User agent used when communicating with the buildkite API.
	UserAgent string

	// Services used for talking to different parts of the buildkite API.
	Agents        *AgentsService
	Builds        *BuildsService
	Organizations *OrganizationsService
	Projects      *ProjectsService
	User          *UserService
}

// ListOptions specifies the optional parameters to various List methods that
// support pagination.
type ListOptions struct {
	// For paginated result sets, page of results to retrieve.
	Page int `url:"page,omitempty"`

	// For paginated result sets, the number of results to include per page.
	PerPage int `url:"per_page,omitempty"`
}

// NewClient returns a new buildkite API client. As API calls require authentication
// you MUST supply a client which provides the required API key.
func NewClient(httpClient *http.Client) *Client {

	baseURL, _ := url.Parse(defaultBaseURL)

	c := &Client{
		client:    httpClient,
		BaseURL:   baseURL,
		UserAgent: userAgent,
	}

	c.Agents = &AgentsService{c}
	c.Builds = &BuildsService{c}
	c.Organizations = &OrganizationsService{c}
	c.Projects = &ProjectsService{c}
	c.User = &UserService{c}

	return c
}

// NewRequest creates an API request. A relative URL can be provided in urlStr,
// in which case it is resolved relative to the BaseURL of the Client.
// Relative URLs should always be specified without a preceding slash.  If
// specified, the value pointed to by body is JSON encoded and included as the
// request body.
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(urlStr)
	if err != nil {
		return nil, err
	}

	u := c.BaseURL.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}

	if c.UserAgent != "" {
		req.Header.Add("User-Agent", c.UserAgent)
	}

	return req, nil
}

// Response is a buildkite API response.  This wraps the standard http.Response
// returned from buildkite and provides convenient access to things like
// pagination links.
type Response struct {
	*http.Response

	// These fields provide the page values for paginating through a set of
	// results.  Any or all of these may be set to the zero value for
	// responses that are not part of a paginated set, or for which there
	// are no additional pages.

	NextPage  int
	PrevPage  int
	FirstPage int
	LastPage  int
}

// newResponse creats a new Response for the provided http.Response.
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	response.populatePageValues()
	return response
}

// populatePageValues parses the HTTP Link response headers and populates the
// various pagination link values in the Reponse.
func (r *Response) populatePageValues() {
	if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
		for _, link := range strings.Split(links[0], ",") {
			segments := strings.Split(strings.TrimSpace(link), ";")

			// link must at least have href and rel
			if len(segments) < 2 {
				continue
			}

			// ensure href is properly formatted
			if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
				continue
			}

			// try to pull out page parameter
			url, err := url.Parse(segments[0][1 : len(segments[0])-1])
			if err != nil {
				continue
			}
			page := url.Query().Get("page")
			if page == "" {
				continue
			}

			for _, segment := range segments[1:] {
				switch strings.TrimSpace(segment) {
				case `rel="next"`:
					r.NextPage, _ = strconv.Atoi(page)
				case `rel="prev"`:
					r.PrevPage, _ = strconv.Atoi(page)
				case `rel="first"`:
					r.FirstPage, _ = strconv.Atoi(page)
				case `rel="last"`:
					r.LastPage, _ = strconv.Atoi(page)
				}

			}
		}
	}
}

// Do sends an API request and returns the API response.  The API response is
// JSON decoded and stored in the value pointed to by v, or returned as an
// error if an API error has occurred.  If v implements the io.Writer
// interface, the raw response body will be written to v, without attempting to
// first decode it.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	response := newResponse(resp)

	err = checkResponse(resp)
	if err != nil {
		// even though there was an error, we still return the response
		// in case the caller wants to inspect it further
		return response, err
	}

	if v != nil {
		if w, ok := v.(io.Writer); ok {
			io.Copy(w, resp.Body)
		} else {
			err = json.NewDecoder(resp.Body).Decode(v)
		}
	}
	return response, err
}

// ErrorResponse provides a message.
type ErrorResponse struct {
	Response *http.Response // HTTP response that caused this error
	Message  string         `json:"message"` // error message
}

func (r *ErrorResponse) Error() string {
	return fmt.Sprintf("%v %v: %d %v",
		r.Response.Request.Method, r.Response.Request.URL,
		r.Response.StatusCode, r.Message)
}

func checkResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	errorResponse := &ErrorResponse{Response: r}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, errorResponse)
	}
	return errorResponse
}

// addOptions adds the parameters in opt as URL query parameters to s.  opt
// must be a struct whose fields may contain "url" tags.
func addOptions(s string, opt interface{}) (string, error) {
	v := reflect.ValueOf(opt)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return s, nil
	}

	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}

	qs, err := query.Values(opt)
	if err != nil {
		return s, err
	}

	u.RawQuery = qs.Encode()
	return u.String(), nil
}

// Int is a helper routine that allocates a new int value
// to store v and returns a pointer to it, but unlike Int
// its argument value is an int.
func Int(v int) *int {
	p := new(int)
	*p = v
	return p
}

// String is a helper routine that allocates a new string value
// to store v and returns a pointer to it.
func String(v string) *string {
	p := new(string)
	*p = v
	return p
}
<|endoftext|>


<commit_before>// +build !windows

package caddy

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"syscall"
)

func init() {
	// Trap POSIX-only signals
	go func() {
		reload := make(chan os.Signal, 1)
		signal.Notify(reload, syscall.SIGUSR1) // reload configuration

		for {
			<-reload

			var updatedCaddyfile Input

			caddyfileMu.Lock()
			if caddyfile.IsFile() {
				body, err := ioutil.ReadFile(caddyfile.Path())
				if err == nil {
					caddyfile = CaddyfileInput{
						Filepath: caddyfile.Path(),
						Contents: body,
						RealFile: true,
					}
				}
			}
			caddyfileMu.Unlock()

			err := Restart(updatedCaddyfile)
			if err != nil {
				log.Println("error at restart:", err)
			}
		}
	}()
}
<commit_msg>Prevent panic in case of hung loading sequence (fixes #315)<commit_after>// +build !windows

package caddy

import (
	"io/ioutil"
	"log"
	"os"
	"os/signal"
	"syscall"
)

func init() {
	// Trap POSIX-only signals
	go func() {
		reload := make(chan os.Signal, 1)
		signal.Notify(reload, syscall.SIGUSR1) // reload configuration

		for {
			<-reload

			var updatedCaddyfile Input

			caddyfileMu.Lock()
			if caddyfile == nil {
				// Hmm, did spawing process forget to close stdin? Anyhow, this is unusual.
				log.Println("[ERROR] SIGUSR1: no caddyfile to reload")
				caddyfileMu.Unlock()
				continue
			}
			if caddyfile.IsFile() {
				body, err := ioutil.ReadFile(caddyfile.Path())
				if err == nil {
					caddyfile = CaddyfileInput{
						Filepath: caddyfile.Path(),
						Contents: body,
						RealFile: true,
					}
				}
			}
			caddyfileMu.Unlock()

			err := Restart(updatedCaddyfile)
			if err != nil {
				log.Println("error at restart:", err)
			}
		}
	}()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 api

import (
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/runtime"
)

type FakeAPIObject struct{}

func (obj *FakeAPIObject) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }

type ExtensionAPIObject struct {
	unversioned.TypeMeta
	ObjectMeta
}

func (obj *ExtensionAPIObject) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

func TestGetReference(t *testing.T) {
	table := map[string]struct {
		obj       runtime.Object
		ref       *ObjectReference
		fieldPath string
		shouldErr bool
	}{
		"pod": {
			obj: &Pod{
				ObjectMeta: ObjectMeta{
					Name:            "foo",
					UID:             "bar",
					ResourceVersion: "42",
					SelfLink:        "/api/version1/pods/foo",
				},
			},
			fieldPath: ".desiredState.containers[0]",
			ref: &ObjectReference{
				Kind:            "Pod",
				APIVersion:      "version1",
				Name:            "foo",
				UID:             "bar",
				ResourceVersion: "42",
				FieldPath:       ".desiredState.containers[0]",
			},
		},
		"serviceList": {
			obj: &ServiceList{
				ListMeta: unversioned.ListMeta{
					ResourceVersion: "42",
					SelfLink:        "/api/version2/services",
				},
			},
			ref: &ObjectReference{
				Kind:            "ServiceList",
				APIVersion:      "version2",
				ResourceVersion: "42",
			},
		},
		"extensionAPIObject": {
			obj: &ExtensionAPIObject{
				TypeMeta: unversioned.TypeMeta{
					Kind: "ExtensionAPIObject",
				},
				ObjectMeta: ObjectMeta{
					Name:            "foo",
					UID:             "bar",
					ResourceVersion: "42",
					SelfLink:        "/custom_prefix/version1/extensions/foo",
				},
			},
			ref: &ObjectReference{
				Kind:            "ExtensionAPIObject",
				APIVersion:      "version1",
				Name:            "foo",
				UID:             "bar",
				ResourceVersion: "42",
			},
		},
		"badSelfLink": {
			obj: &ServiceList{
				ListMeta: unversioned.ListMeta{
					ResourceVersion: "42",
					SelfLink:        "version2/services",
				},
			},
			shouldErr: true,
		},
		"error": {
			obj:       &FakeAPIObject{},
			ref:       nil,
			shouldErr: true,
		},
		"errorNil": {
			obj:       nil,
			ref:       nil,
			shouldErr: true,
		},
	}

	for name, item := range table {
		ref, err := GetPartialReference(item.obj, item.fieldPath)
		if e, a := item.shouldErr, (err != nil); e != a {
			t.Errorf("%v: expected %v, got %v, err %v", name, e, a, err)
			continue
		}
		if e, a := item.ref, ref; !reflect.DeepEqual(e, a) {
			t.Errorf("%v: expected %#v, got %#v", name, e, a)
		}
	}
}
<commit_msg>register internal types with scheme for reference unit test<commit_after>/*
Copyright 2014 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 api

import (
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/runtime"
)

type FakeAPIObject struct{}

func (obj *FakeAPIObject) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }

type ExtensionAPIObject struct {
	unversioned.TypeMeta
	ObjectMeta
}

func (obj *ExtensionAPIObject) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }

func TestGetReference(t *testing.T) {

	// when vendoring kube, if you don't force the set of registered versions (like this hack/test-go.sh does)
	// then you run into trouble because the types aren't registered in the scheme by anything.  This does the
	// register manually to allow unit test execution
	if _, err := Scheme.ObjectKind(&Pod{}); err != nil {
		AddToScheme(Scheme)
	}

	table := map[string]struct {
		obj       runtime.Object
		ref       *ObjectReference
		fieldPath string
		shouldErr bool
	}{
		"pod": {
			obj: &Pod{
				ObjectMeta: ObjectMeta{
					Name:            "foo",
					UID:             "bar",
					ResourceVersion: "42",
					SelfLink:        "/api/version1/pods/foo",
				},
			},
			fieldPath: ".desiredState.containers[0]",
			ref: &ObjectReference{
				Kind:            "Pod",
				APIVersion:      "version1",
				Name:            "foo",
				UID:             "bar",
				ResourceVersion: "42",
				FieldPath:       ".desiredState.containers[0]",
			},
		},
		"serviceList": {
			obj: &ServiceList{
				ListMeta: unversioned.ListMeta{
					ResourceVersion: "42",
					SelfLink:        "/api/version2/services",
				},
			},
			ref: &ObjectReference{
				Kind:            "ServiceList",
				APIVersion:      "version2",
				ResourceVersion: "42",
			},
		},
		"extensionAPIObject": {
			obj: &ExtensionAPIObject{
				TypeMeta: unversioned.TypeMeta{
					Kind: "ExtensionAPIObject",
				},
				ObjectMeta: ObjectMeta{
					Name:            "foo",
					UID:             "bar",
					ResourceVersion: "42",
					SelfLink:        "/custom_prefix/version1/extensions/foo",
				},
			},
			ref: &ObjectReference{
				Kind:            "ExtensionAPIObject",
				APIVersion:      "version1",
				Name:            "foo",
				UID:             "bar",
				ResourceVersion: "42",
			},
		},
		"badSelfLink": {
			obj: &ServiceList{
				ListMeta: unversioned.ListMeta{
					ResourceVersion: "42",
					SelfLink:        "version2/services",
				},
			},
			shouldErr: true,
		},
		"error": {
			obj:       &FakeAPIObject{},
			ref:       nil,
			shouldErr: true,
		},
		"errorNil": {
			obj:       nil,
			ref:       nil,
			shouldErr: true,
		},
	}

	for name, item := range table {
		ref, err := GetPartialReference(item.obj, item.fieldPath)
		if e, a := item.shouldErr, (err != nil); e != a {
			t.Errorf("%v: expected %v, got %v, err %v", name, e, a, err)
			continue
		}
		if e, a := item.ref, ref; !reflect.DeepEqual(e, a) {
			t.Errorf("%v: expected %#v, got %#v", name, e, a)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2017-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package curl implements routines to fetch files given a URL.
//
// curl currently supports HTTP, TFTP, and local files.
package curl

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cenkalti/backoff/v4"
	"github.com/u-root/u-root/pkg/uio"
	"pack.ag/tftp"
)

var (
	// ErrNoSuchScheme is returned by Schemes.Fetch and
	// Schemes.LazyFetch if there is no registered FileScheme
	// implementation for the given URL scheme.
	ErrNoSuchScheme = errors.New("no such scheme")
)

// File is a reference to a file fetched through this library.
type File interface {
	io.ReaderAt

	// URL is the file's original URL.
	URL() *url.URL
}

// FileScheme represents the implementation of a URL scheme and gives access to
// fetching files of that scheme.
//
// For example, an http FileScheme implementation would fetch files using
// the HTTP protocol.
type FileScheme interface {
	// Fetch returns a reader that gives the contents of `u`.
	//
	// It may do so by fetching `u` and placing it in a buffer, or by
	// returning an io.ReaderAt that fetchs the file.
	Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error)
}

var (
	// DefaultHTTPClient is the default HTTP FileScheme.
	//
	// It is not recommended to use this for HTTPS. We recommend creating an
	// http.Client that accepts only a private pool of certificates.
	DefaultHTTPClient = NewHTTPClient(http.DefaultClient)

	// DefaultTFTPClient is the default TFTP FileScheme.
	DefaultTFTPClient = NewTFTPClient(tftp.ClientMode(tftp.ModeOctet), tftp.ClientBlocksize(1450), tftp.ClientWindowsize(65535))

	// DefaultSchemes are the schemes supported by default.
	DefaultSchemes = Schemes{
		"tftp": DefaultTFTPClient,
		"http": DefaultHTTPClient,
		"file": &LocalFileClient{},
	}
)

// URLError is an error involving URLs.
type URLError struct {
	URL *url.URL
	Err error
}

// Error implements error.Error.
func (s *URLError) Error() string {
	return fmt.Sprintf("encountered error %v with %q", s.Err, s.URL)
}

// Unwrap unwraps the underlying error.
func (s *URLError) Unwrap() error {
	return s.Err
}

// IsURLError returns true iff err is a URLError.
func IsURLError(err error) bool {
	_, ok := err.(*URLError)
	return ok
}

// Schemes is a map of URL scheme identifier -> implementation that can
// fetch a file for that scheme.
type Schemes map[string]FileScheme

// RegisterScheme calls DefaultSchemes.Register.
func RegisterScheme(scheme string, fs FileScheme) {
	DefaultSchemes.Register(scheme, fs)
}

// Register registers a scheme identified by `scheme` to be `fs`.
func (s Schemes) Register(scheme string, fs FileScheme) {
	s[scheme] = fs
}

// Fetch fetchs a file via DefaultSchemes.
func Fetch(ctx context.Context, u *url.URL) (File, error) {
	return DefaultSchemes.Fetch(ctx, u)
}

// file is an io.ReaderAt with a nice Stringer.
type file struct {
	io.ReaderAt

	url *url.URL
}

// URL returns the file URL.
func (f file) URL() *url.URL {
	return f.url
}

// String implements fmt.Stringer.
func (f file) String() string {
	return f.url.String()
}

// Fetch fetchs the file with the given `u`. `u.Scheme` is used to
// select the FileScheme via `s`.
//
// If `s` does not contain a FileScheme for `u.Scheme`, ErrNoSuchScheme is
// returned.
func (s Schemes) Fetch(ctx context.Context, u *url.URL) (File, error) {
	fg, ok := s[u.Scheme]
	if !ok {
		return nil, &URLError{URL: u, Err: ErrNoSuchScheme}
	}
	r, err := fg.Fetch(ctx, u)
	if err != nil {
		return nil, &URLError{URL: u, Err: err}
	}
	return &file{ReaderAt: r, url: u}, nil
}

// LazyFetch calls LazyFetch on DefaultSchemes.
func LazyFetch(u *url.URL) (File, error) {
	return DefaultSchemes.LazyFetch(u)
}

// LazyFetch returns a reader that will Fetch the file given by `u` when
// Read is called, based on `u`s scheme. See Schemes.Fetch for more
// details.
func (s Schemes) LazyFetch(u *url.URL) (File, error) {
	fg, ok := s[u.Scheme]
	if !ok {
		return nil, &URLError{URL: u, Err: ErrNoSuchScheme}
	}

	return &file{
		url: u,
		ReaderAt: uio.NewLazyOpenerAt(u.String(), func() (io.ReaderAt, error) {
			// TODO
			r, err := fg.Fetch(context.TODO(), u)
			if err != nil {
				return nil, &URLError{URL: u, Err: err}
			}
			return r, nil
		}),
	}, nil
}

// TFTPClient implements FileScheme for TFTP files.
type TFTPClient struct {
	opts []tftp.ClientOpt
}

// NewTFTPClient returns a new TFTP client based on the given tftp.ClientOpt.
func NewTFTPClient(opts ...tftp.ClientOpt) FileScheme {
	return &TFTPClient{
		opts: opts,
	}
}

// Fetch implements FileScheme.Fetch.
func (t *TFTPClient) Fetch(_ context.Context, u *url.URL) (io.ReaderAt, error) {
	// TODO(hugelgupf): These clients are basically stateless, except for
	// the options. Figure out whether you actually have to re-establish
	// this connection every time. Audit the TFTP library.
	c, err := tftp.NewClient(t.opts...)
	if err != nil {
		return nil, err
	}

	r, err := c.Get(u.String())
	if err != nil {
		return nil, err
	}
	return uio.NewCachingReader(r), nil
}

// RetryTFTP retries downloads if the error does not contain FILE_NOT_FOUND.
//
// pack.ag/tftp does not export the necessary structs to get the
// code out of the error message cleanly, but it does embed FILE_NOT_FOUND in
// the error string.
func RetryTFTP(u *url.URL, err error) bool {
	return !strings.Contains(err.Error(), "FILE_NOT_FOUND")
}

// DoRetry returns true if the Fetch request for the URL should be
// retried. err is the error that Fetch previously returned.
//
// DoRetry lets a FileScheme filter for errors returned by Fetch
// which are worth retrying. If this interface is not implemented, the
// default for SchemeWithRetries is to always retry. DoRetry
// returns true to indicate a request should be retried.
type DoRetry func(u *url.URL, err error) bool

// SchemeWithRetries wraps a FileScheme and automatically retries (with
// backoff) when Fetch returns a non-nil err.
type SchemeWithRetries struct {
	Scheme FileScheme

	// DoRetry should return true to indicate the Fetch shall be retried.
	// Even if DoRetry returns true, BackOff can still determine whether to
	// stop.
	//
	// If DoRetry is nil, it will be retried if the BackOff agrees.
	DoRetry DoRetry

	// BackOff determines how often to retry and how long to wait between
	// each retry.
	BackOff backoff.BackOff
}

// Fetch implements FileScheme.Fetch.
func (s *SchemeWithRetries) Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error) {
	var err error
	s.BackOff.Reset()
	back := backoff.WithContext(s.BackOff, ctx)
	for d := time.Duration(0); d != backoff.Stop; d = back.NextBackOff() {
		if d > 0 {
			time.Sleep(d)
		}

		var r io.ReaderAt
		// Note: err uses the scope outside the for loop.
		r, err = s.Scheme.Fetch(ctx, u)
		if err == nil {
			return r, nil
		}

		log.Printf("Error: Getting %v: %v", u, err)
		if s.DoRetry != nil && !s.DoRetry(u, err) {
			return r, err
		}
		log.Printf("Retrying %v", u)
	}

	log.Printf("Error: Too many retries to get file %v", u)
	return nil, err
}

// HTTPClientCodeError is returned by HTTPClient.Fetch when the server replies
// with a non-200 code.
type HTTPClientCodeError struct {
	Err      error
	HTTPCode int
}

// Error implements error for HTTPClientCodeError.
func (h *HTTPClientCodeError) Error() string {
	return fmt.Sprintf("HTTP server responded with error code %d, want 200: response %v", h.HTTPCode, h.Err)
}

// Unwrap implements errors.Unwrap.
func (h *HTTPClientCodeError) Unwrap() error {
	return h.Err
}

// HTTPClient implements FileScheme for HTTP files.
type HTTPClient struct {
	c *http.Client
}

// NewHTTPClient returns a new HTTP FileScheme based on the given http.Client.
func NewHTTPClient(c *http.Client) *HTTPClient {
	return &HTTPClient{
		c: c,
	}
}

// Fetch implements FileScheme.Fetch.
func (h HTTPClient) Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error) {
	req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := h.c.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, &HTTPClientCodeError{err, resp.StatusCode}
	}
	return uio.NewCachingReader(resp.Body), nil
}

// RetryOr returns a DoRetry function that returns true if any one of fn return
// true.
func RetryOr(fn ...DoRetry) DoRetry {
	return func(u *url.URL, err error) bool {
		for _, f := range fn {
			if f(u, err) {
				return true
			}
		}
		return false
	}
}

// RetryConnectErrors retries only connect(2) errors.
func RetryConnectErrors(u *url.URL, err error) bool {
	var serr *os.SyscallError
	if errors.As(err, &serr) && serr.Syscall == "connect" {
		return true
	}
	return false
}

// RetryTemporaryNetworkErrors only retries temporary network errors.
//
// This relies on Go's net.Error.Temporary definition of temporary network
// errors, which does not include network configuration errors. The latter are
// relevant for users of DHCP, for example.
func RetryTemporaryNetworkErrors(u *url.URL, err error) bool {
	var nerr net.Error
	if errors.As(err, &nerr) {
		return nerr.Temporary()
	}
	return false
}

// RetryHTTP implements DoRetry for HTTP error codes where it makes sense.
func RetryHTTP(u *url.URL, err error) bool {
	var e *HTTPClientCodeError
	if !errors.As(err, &e) {
		return false
	}
	switch c := e.HTTPCode; {
	case c == 200:
		return false

	case c == 408, c == 409, c == 425, c == 429:
		// Retry for codes "Request Timeout(408), Conflict(409), Too Early(425), and Too Many Requests(429)"
		return true

	case c >= 400 && c < 500:
		// We don't retry all other 400 codes, since the situation won't be improved with a retry.
		return false

	default:
		return true
	}
}

// LocalFileClient implements FileScheme for files on disk.
type LocalFileClient struct{}

// Fetch implements FileScheme.Fetch.
func (lfs LocalFileClient) Fetch(_ context.Context, u *url.URL) (io.ReaderAt, error) {
	return os.Open(filepath.Clean(u.Path))
}
<commit_msg>Change tftp windowsize to 64<commit_after>// Copyright 2017-2020 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package curl implements routines to fetch files given a URL.
//
// curl currently supports HTTP, TFTP, and local files.
package curl

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cenkalti/backoff/v4"
	"github.com/u-root/u-root/pkg/uio"
	"pack.ag/tftp"
)

var (
	// ErrNoSuchScheme is returned by Schemes.Fetch and
	// Schemes.LazyFetch if there is no registered FileScheme
	// implementation for the given URL scheme.
	ErrNoSuchScheme = errors.New("no such scheme")
)

// File is a reference to a file fetched through this library.
type File interface {
	io.ReaderAt

	// URL is the file's original URL.
	URL() *url.URL
}

// FileScheme represents the implementation of a URL scheme and gives access to
// fetching files of that scheme.
//
// For example, an http FileScheme implementation would fetch files using
// the HTTP protocol.
type FileScheme interface {
	// Fetch returns a reader that gives the contents of `u`.
	//
	// It may do so by fetching `u` and placing it in a buffer, or by
	// returning an io.ReaderAt that fetchs the file.
	Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error)
}

var (
	// DefaultHTTPClient is the default HTTP FileScheme.
	//
	// It is not recommended to use this for HTTPS. We recommend creating an
	// http.Client that accepts only a private pool of certificates.
	DefaultHTTPClient = NewHTTPClient(http.DefaultClient)

	// DefaultTFTPClient is the default TFTP FileScheme.
	DefaultTFTPClient = NewTFTPClient(tftp.ClientMode(tftp.ModeOctet), tftp.ClientBlocksize(1450), tftp.ClientWindowsize(64))

	// DefaultSchemes are the schemes supported by default.
	DefaultSchemes = Schemes{
		"tftp": DefaultTFTPClient,
		"http": DefaultHTTPClient,
		"file": &LocalFileClient{},
	}
)

// URLError is an error involving URLs.
type URLError struct {
	URL *url.URL
	Err error
}

// Error implements error.Error.
func (s *URLError) Error() string {
	return fmt.Sprintf("encountered error %v with %q", s.Err, s.URL)
}

// Unwrap unwraps the underlying error.
func (s *URLError) Unwrap() error {
	return s.Err
}

// IsURLError returns true iff err is a URLError.
func IsURLError(err error) bool {
	_, ok := err.(*URLError)
	return ok
}

// Schemes is a map of URL scheme identifier -> implementation that can
// fetch a file for that scheme.
type Schemes map[string]FileScheme

// RegisterScheme calls DefaultSchemes.Register.
func RegisterScheme(scheme string, fs FileScheme) {
	DefaultSchemes.Register(scheme, fs)
}

// Register registers a scheme identified by `scheme` to be `fs`.
func (s Schemes) Register(scheme string, fs FileScheme) {
	s[scheme] = fs
}

// Fetch fetchs a file via DefaultSchemes.
func Fetch(ctx context.Context, u *url.URL) (File, error) {
	return DefaultSchemes.Fetch(ctx, u)
}

// file is an io.ReaderAt with a nice Stringer.
type file struct {
	io.ReaderAt

	url *url.URL
}

// URL returns the file URL.
func (f file) URL() *url.URL {
	return f.url
}

// String implements fmt.Stringer.
func (f file) String() string {
	return f.url.String()
}

// Fetch fetchs the file with the given `u`. `u.Scheme` is used to
// select the FileScheme via `s`.
//
// If `s` does not contain a FileScheme for `u.Scheme`, ErrNoSuchScheme is
// returned.
func (s Schemes) Fetch(ctx context.Context, u *url.URL) (File, error) {
	fg, ok := s[u.Scheme]
	if !ok {
		return nil, &URLError{URL: u, Err: ErrNoSuchScheme}
	}
	r, err := fg.Fetch(ctx, u)
	if err != nil {
		return nil, &URLError{URL: u, Err: err}
	}
	return &file{ReaderAt: r, url: u}, nil
}

// LazyFetch calls LazyFetch on DefaultSchemes.
func LazyFetch(u *url.URL) (File, error) {
	return DefaultSchemes.LazyFetch(u)
}

// LazyFetch returns a reader that will Fetch the file given by `u` when
// Read is called, based on `u`s scheme. See Schemes.Fetch for more
// details.
func (s Schemes) LazyFetch(u *url.URL) (File, error) {
	fg, ok := s[u.Scheme]
	if !ok {
		return nil, &URLError{URL: u, Err: ErrNoSuchScheme}
	}

	return &file{
		url: u,
		ReaderAt: uio.NewLazyOpenerAt(u.String(), func() (io.ReaderAt, error) {
			// TODO
			r, err := fg.Fetch(context.TODO(), u)
			if err != nil {
				return nil, &URLError{URL: u, Err: err}
			}
			return r, nil
		}),
	}, nil
}

// TFTPClient implements FileScheme for TFTP files.
type TFTPClient struct {
	opts []tftp.ClientOpt
}

// NewTFTPClient returns a new TFTP client based on the given tftp.ClientOpt.
func NewTFTPClient(opts ...tftp.ClientOpt) FileScheme {
	return &TFTPClient{
		opts: opts,
	}
}

// Fetch implements FileScheme.Fetch.
func (t *TFTPClient) Fetch(_ context.Context, u *url.URL) (io.ReaderAt, error) {
	// TODO(hugelgupf): These clients are basically stateless, except for
	// the options. Figure out whether you actually have to re-establish
	// this connection every time. Audit the TFTP library.
	c, err := tftp.NewClient(t.opts...)
	if err != nil {
		return nil, err
	}

	r, err := c.Get(u.String())
	if err != nil {
		return nil, err
	}
	return uio.NewCachingReader(r), nil
}

// RetryTFTP retries downloads if the error does not contain FILE_NOT_FOUND.
//
// pack.ag/tftp does not export the necessary structs to get the
// code out of the error message cleanly, but it does embed FILE_NOT_FOUND in
// the error string.
func RetryTFTP(u *url.URL, err error) bool {
	return !strings.Contains(err.Error(), "FILE_NOT_FOUND")
}

// DoRetry returns true if the Fetch request for the URL should be
// retried. err is the error that Fetch previously returned.
//
// DoRetry lets a FileScheme filter for errors returned by Fetch
// which are worth retrying. If this interface is not implemented, the
// default for SchemeWithRetries is to always retry. DoRetry
// returns true to indicate a request should be retried.
type DoRetry func(u *url.URL, err error) bool

// SchemeWithRetries wraps a FileScheme and automatically retries (with
// backoff) when Fetch returns a non-nil err.
type SchemeWithRetries struct {
	Scheme FileScheme

	// DoRetry should return true to indicate the Fetch shall be retried.
	// Even if DoRetry returns true, BackOff can still determine whether to
	// stop.
	//
	// If DoRetry is nil, it will be retried if the BackOff agrees.
	DoRetry DoRetry

	// BackOff determines how often to retry and how long to wait between
	// each retry.
	BackOff backoff.BackOff
}

// Fetch implements FileScheme.Fetch.
func (s *SchemeWithRetries) Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error) {
	var err error
	s.BackOff.Reset()
	back := backoff.WithContext(s.BackOff, ctx)
	for d := time.Duration(0); d != backoff.Stop; d = back.NextBackOff() {
		if d > 0 {
			time.Sleep(d)
		}

		var r io.ReaderAt
		// Note: err uses the scope outside the for loop.
		r, err = s.Scheme.Fetch(ctx, u)
		if err == nil {
			return r, nil
		}

		log.Printf("Error: Getting %v: %v", u, err)
		if s.DoRetry != nil && !s.DoRetry(u, err) {
			return r, err
		}
		log.Printf("Retrying %v", u)
	}

	log.Printf("Error: Too many retries to get file %v", u)
	return nil, err
}

// HTTPClientCodeError is returned by HTTPClient.Fetch when the server replies
// with a non-200 code.
type HTTPClientCodeError struct {
	Err      error
	HTTPCode int
}

// Error implements error for HTTPClientCodeError.
func (h *HTTPClientCodeError) Error() string {
	return fmt.Sprintf("HTTP server responded with error code %d, want 200: response %v", h.HTTPCode, h.Err)
}

// Unwrap implements errors.Unwrap.
func (h *HTTPClientCodeError) Unwrap() error {
	return h.Err
}

// HTTPClient implements FileScheme for HTTP files.
type HTTPClient struct {
	c *http.Client
}

// NewHTTPClient returns a new HTTP FileScheme based on the given http.Client.
func NewHTTPClient(c *http.Client) *HTTPClient {
	return &HTTPClient{
		c: c,
	}
}

// Fetch implements FileScheme.Fetch.
func (h HTTPClient) Fetch(ctx context.Context, u *url.URL) (io.ReaderAt, error) {
	req, err := http.NewRequestWithContext(ctx, "GET", u.String(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := h.c.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != 200 {
		return nil, &HTTPClientCodeError{err, resp.StatusCode}
	}
	return uio.NewCachingReader(resp.Body), nil
}

// RetryOr returns a DoRetry function that returns true if any one of fn return
// true.
func RetryOr(fn ...DoRetry) DoRetry {
	return func(u *url.URL, err error) bool {
		for _, f := range fn {
			if f(u, err) {
				return true
			}
		}
		return false
	}
}

// RetryConnectErrors retries only connect(2) errors.
func RetryConnectErrors(u *url.URL, err error) bool {
	var serr *os.SyscallError
	if errors.As(err, &serr) && serr.Syscall == "connect" {
		return true
	}
	return false
}

// RetryTemporaryNetworkErrors only retries temporary network errors.
//
// This relies on Go's net.Error.Temporary definition of temporary network
// errors, which does not include network configuration errors. The latter are
// relevant for users of DHCP, for example.
func RetryTemporaryNetworkErrors(u *url.URL, err error) bool {
	var nerr net.Error
	if errors.As(err, &nerr) {
		return nerr.Temporary()
	}
	return false
}

// RetryHTTP implements DoRetry for HTTP error codes where it makes sense.
func RetryHTTP(u *url.URL, err error) bool {
	var e *HTTPClientCodeError
	if !errors.As(err, &e) {
		return false
	}
	switch c := e.HTTPCode; {
	case c == 200:
		return false

	case c == 408, c == 409, c == 425, c == 429:
		// Retry for codes "Request Timeout(408), Conflict(409), Too Early(425), and Too Many Requests(429)"
		return true

	case c >= 400 && c < 500:
		// We don't retry all other 400 codes, since the situation won't be improved with a retry.
		return false

	default:
		return true
	}
}

// LocalFileClient implements FileScheme for files on disk.
type LocalFileClient struct{}

// Fetch implements FileScheme.Fetch.
func (lfs LocalFileClient) Fetch(_ context.Context, u *url.URL) (io.ReaderAt, error) {
	return os.Open(filepath.Clean(u.Path))
}
<|endoftext|>


<commit_before>package domain

import (
	"encoding/json"
	"time"

	"github.com/google/uuid"
)

// RawEvent represents raw event that it is aware of its type
type RawEvent interface {
	GetType() string
}

// Event contains id, payload and metadata
type Event struct {
	ID       uuid.UUID       `json:"id"`
	Metadata EventMetaData   `json:"metadata"`
	Payload  json.RawMessage `json:"payload"`
}

// EventMetaData for Event
type EventMetaData struct {
	Type          string    `json:"type"`
	StreamID      uuid.UUID `json:"stream_id"`
	StreamName    string    `json:"stream_name"`
	StreamVersion int       `json:"stream_version"`
	OccurredAt    time.Time `json:"occurred_at"`
}

// NewEvent create new event
func NewEvent(streamID uuid.UUID, streamName string, streamVersion int, data RawEvent) (*Event, error) {
	meta := EventMetaData{
		Type:          data.GetType(),
		StreamID:      streamID,
		StreamName:    streamName,
		StreamVersion: streamVersion,
		OccurredAt:    time.Now(),
	}

	payload, err := json.Marshal(data)
	if err != nil {
		return nil, err
	}

	id, err := uuid.NewRandom()

	return &Event{id, meta, payload}, err
}

// MakeEvent makes a event object from metadata and payload
func MakeEvent(meta EventMetaData, payload json.RawMessage) (*Event, error) {
	id, err := uuid.NewRandom()

	return &Event{id, meta, payload}, err
}
<commit_msg>Rename argument<commit_after>package domain

import (
	"encoding/json"
	"time"

	"github.com/google/uuid"
)

// RawEvent represents raw event that it is aware of its type
type RawEvent interface {
	GetType() string
}

// Event contains id, payload and metadata
type Event struct {
	ID       uuid.UUID       `json:"id"`
	Metadata EventMetaData   `json:"metadata"`
	Payload  json.RawMessage `json:"payload"`
}

// EventMetaData for Event
type EventMetaData struct {
	Type          string    `json:"type"`
	StreamID      uuid.UUID `json:"stream_id"`
	StreamName    string    `json:"stream_name"`
	StreamVersion int       `json:"stream_version"`
	OccurredAt    time.Time `json:"occurred_at"`
}

// NewEvent create new event
func NewEvent(streamID uuid.UUID, streamName string, streamVersion int, rawEvent RawEvent) (*Event, error) {
	meta := EventMetaData{
		Type:          rawEvent.GetType(),
		StreamID:      streamID,
		StreamName:    streamName,
		StreamVersion: streamVersion,
		OccurredAt:    time.Now(),
	}

	payload, err := json.Marshal(rawEvent)
	if err != nil {
		return nil, err
	}

	id, err := uuid.NewRandom()

	return &Event{id, meta, payload}, err
}

// MakeEvent makes a event object from metadata and payload
func MakeEvent(meta EventMetaData, payload json.RawMessage) (*Event, error) {
	id, err := uuid.NewRandom()

	return &Event{id, meta, payload}, err
}
<|endoftext|>


<commit_before>package install

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"

	"github.com/datawire/ambassador/pkg/kates"
)

func GetPodTemplateFromObject(obj kates.Object) (*kates.PodTemplateSpec, error) {
	var tplSpec *kates.PodTemplateSpec
	kind := obj.GetObjectKind().GroupVersionKind().Kind
	switch kind {
	case "ReplicaSet":
		rs := obj.(*kates.ReplicaSet)
		tplSpec = &rs.Spec.Template
	case "Deployment":
		dep := obj.(*kates.Deployment)
		tplSpec = &dep.Spec.Template
	case "StatefulSet":
		statefulSet := obj.(*kates.StatefulSet)
		tplSpec = &statefulSet.Spec.Template
	default:
		return nil, ObjErrorf(obj, "unsupported workload kind %q", kind)
	}

	return tplSpec, nil
}

// GetPort finds a port with the given name and returns it.
func GetPort(cn *corev1.Container, portName string) (*corev1.ContainerPort, error) {
	ports := cn.Ports
	for pn := range ports {
		p := &ports[pn]
		if p.Name == portName {
			return p, nil
		}
	}
	return nil, fmt.Errorf("unable to locate port %q in container %q", portName, cn.Name)
}

func ObjErrorf(obj kates.Object, format string, args ...interface{}) error {
	return fmt.Errorf("%s name=%q namespace=%q: %w",
		obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace(),
		fmt.Errorf(format, args...))
}
<commit_msg>Simplify GetPodTemplateFromObject().<commit_after>package install

import (
	"fmt"

	corev1 "k8s.io/api/core/v1"

	"github.com/datawire/ambassador/pkg/kates"
)

func GetPodTemplateFromObject(obj kates.Object) (*kates.PodTemplateSpec, error) {
	var tplSpec *kates.PodTemplateSpec
	switch obj := obj.(type) {
	case *kates.ReplicaSet:
		tplSpec = &obj.Spec.Template
	case *kates.Deployment:
		tplSpec = &obj.Spec.Template
	case *kates.StatefulSet:
		tplSpec = &obj.Spec.Template
	default:
		return nil, ObjErrorf(obj, "unsupported workload kind %q", obj.GetObjectKind().GroupVersionKind().Kind)
	}
	return tplSpec, nil
}

// GetPort finds a port with the given name and returns it.
func GetPort(cn *corev1.Container, portName string) (*corev1.ContainerPort, error) {
	ports := cn.Ports
	for pn := range ports {
		p := &ports[pn]
		if p.Name == portName {
			return p, nil
		}
	}
	return nil, fmt.Errorf("unable to locate port %q in container %q", portName, cn.Name)
}

func ObjErrorf(obj kates.Object, format string, args ...interface{}) error {
	return fmt.Errorf("%s name=%q namespace=%q: %w",
		obj.GetObjectKind().GroupVersionKind().Kind, obj.GetName(), obj.GetNamespace(),
		fmt.Errorf(format, args...))
}
<|endoftext|>


<commit_before>package matching

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/models"
	"github.com/SpectoLabs/hoverfly/core/views"
	"reflect"
)

type RequestTemplateStore []RequestTemplateResponsePair

type RequestTemplateResponsePair struct {
	RequestTemplate RequestTemplate        `json:"requestTemplate"`
	Response        models.ResponseDetails `json:"response"`
}

type RequestTemplateResponsePairView struct {
	RequestTemplate RequestTemplate           `json:"requestTemplate"`
	Response        views.ResponseDetailsView `json:"response"`
}

type RequestTemplateResponsePairPayload struct {
	Data *[]RequestTemplateResponsePairView `json:"data"`
}

type RequestTemplate struct {
	Path        *string             `json:"path"`
	Method      *string             `json:"method"`
	Destination *string             `json:"destination"`
	Scheme      *string             `json:"scheme"`
	Query       *string             `json:"query"`
	Body        *string             `json:"body"`
	Headers     map[string][]string `json:"headers"`
}

func (this *RequestTemplateStore) GetResponse(req models.RequestDetails, webserver bool) (*models.ResponseDetails, error) {
	// iterate through the request templates, looking for template to match request
	for _, entry := range *this {
		// TODO: not matching by default on URL and body - need to enable this
		// TODO: need to enable regex matches
		// TODO: enable matching on scheme

		if entry.RequestTemplate.Body != nil && *entry.RequestTemplate.Body == req.Body {
			continue
		}
		if !webserver {
			if entry.RequestTemplate.Destination != nil && *entry.RequestTemplate.Destination != req.Destination {
				continue
			}
		}
		if entry.RequestTemplate.Path != nil && *entry.RequestTemplate.Path != req.Path {
			continue
		}
		if entry.RequestTemplate.Query != nil && *entry.RequestTemplate.Query != req.Query {
			continue
		}
		if !headerMatch(entry.RequestTemplate.Headers, req.Headers) {
			continue
		}
		if entry.RequestTemplate.Method != nil && *entry.RequestTemplate.Method != req.Method {
			continue
		}

		// return the first template to match
		return &entry.Response, nil
	}
	return nil, errors.New("No match found")
}

// ImportPayloads - a function to save given payloads into the database.
func (this *RequestTemplateStore) ImportPayloads(pairPayload RequestTemplateResponsePairPayload) error {
	if len(*pairPayload.Data) > 0 {
		// Convert PayloadView back to Payload for internal storage
		templateStore := pairPayload.ConvertToRequestTemplateStore()
		for _, pl := range templateStore {

			//TODO: add hooks for concsistency with request import
			// note that importing hoverfly is a disallowed circular import

			*this = append(*this, pl)
		}
		log.WithFields(log.Fields{
			"total": len(*this),
		}).Info("payloads imported")
		return nil
	}
	return fmt.Errorf("Bad request. Nothing to import!")
}

func (this *RequestTemplateStore) Wipe() {
	// don't change the pointer here!
	*this = RequestTemplateStore{}
}

/**
Check keys and corresponding values in template headers are also present in request headers
*/
func headerMatch(tmplHeaders, reqHeaders map[string][]string) bool {

	for headerName, headerVal := range tmplHeaders {
		// TODO: case insensitive lookup
		// TODO: is order of values in slice really important?

		reqHeaderVal, ok := reqHeaders[headerName]
		if ok && reflect.DeepEqual(headerVal, reqHeaderVal) {
			continue
		} else {
			return false
		}
	}
	return true
}

func (this *RequestTemplateStore) GetPayload() RequestTemplateResponsePairPayload {
	var pairsPayload []RequestTemplateResponsePairView
	for _, pair := range *this {
		pairsPayload = append(pairsPayload, pair.ConvertToRequestTemplateResponsePairView())
	}
	return RequestTemplateResponsePairPayload{
		Data: &pairsPayload,
	}
}

func (this *RequestTemplateResponsePair) ConvertToRequestTemplateResponsePairView() RequestTemplateResponsePairView {
	return RequestTemplateResponsePairView{
		RequestTemplate: this.RequestTemplate,
		Response:        this.Response.ConvertToResponseDetailsView(),
	}
}

func (this *RequestTemplateResponsePairPayload) ConvertToRequestTemplateStore() RequestTemplateStore {
	var requestTemplateStore RequestTemplateStore
	for _, pair := range *this.Data {
		requestTemplateStore = append(requestTemplateStore, pair.ConvertToRequestTemplateResponsePair())
	}
	return requestTemplateStore
}

func (this *RequestTemplateResponsePairView) ConvertToRequestTemplateResponsePair() RequestTemplateResponsePair {
	return RequestTemplateResponsePair{
		RequestTemplate: this.RequestTemplate,
		Response:        models.NewResponseDetialsFromResponseDetailsView(this.Response),
	}
}<commit_msg>gofmt<commit_after>package matching

import (
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/SpectoLabs/hoverfly/core/models"
	"github.com/SpectoLabs/hoverfly/core/views"
	"reflect"
)

type RequestTemplateStore []RequestTemplateResponsePair

type RequestTemplateResponsePair struct {
	RequestTemplate RequestTemplate        `json:"requestTemplate"`
	Response        models.ResponseDetails `json:"response"`
}

type RequestTemplateResponsePairView struct {
	RequestTemplate RequestTemplate           `json:"requestTemplate"`
	Response        views.ResponseDetailsView `json:"response"`
}

type RequestTemplateResponsePairPayload struct {
	Data *[]RequestTemplateResponsePairView `json:"data"`
}

type RequestTemplate struct {
	Path        *string             `json:"path"`
	Method      *string             `json:"method"`
	Destination *string             `json:"destination"`
	Scheme      *string             `json:"scheme"`
	Query       *string             `json:"query"`
	Body        *string             `json:"body"`
	Headers     map[string][]string `json:"headers"`
}

func (this *RequestTemplateStore) GetResponse(req models.RequestDetails, webserver bool) (*models.ResponseDetails, error) {
	// iterate through the request templates, looking for template to match request
	for _, entry := range *this {
		// TODO: not matching by default on URL and body - need to enable this
		// TODO: need to enable regex matches
		// TODO: enable matching on scheme

		if entry.RequestTemplate.Body != nil && *entry.RequestTemplate.Body == req.Body {
			continue
		}
		if !webserver {
			if entry.RequestTemplate.Destination != nil && *entry.RequestTemplate.Destination != req.Destination {
				continue
			}
		}
		if entry.RequestTemplate.Path != nil && *entry.RequestTemplate.Path != req.Path {
			continue
		}
		if entry.RequestTemplate.Query != nil && *entry.RequestTemplate.Query != req.Query {
			continue
		}
		if !headerMatch(entry.RequestTemplate.Headers, req.Headers) {
			continue
		}
		if entry.RequestTemplate.Method != nil && *entry.RequestTemplate.Method != req.Method {
			continue
		}

		// return the first template to match
		return &entry.Response, nil
	}
	return nil, errors.New("No match found")
}

// ImportPayloads - a function to save given payloads into the database.
func (this *RequestTemplateStore) ImportPayloads(pairPayload RequestTemplateResponsePairPayload) error {
	if len(*pairPayload.Data) > 0 {
		// Convert PayloadView back to Payload for internal storage
		templateStore := pairPayload.ConvertToRequestTemplateStore()
		for _, pl := range templateStore {

			//TODO: add hooks for concsistency with request import
			// note that importing hoverfly is a disallowed circular import

			*this = append(*this, pl)
		}
		log.WithFields(log.Fields{
			"total": len(*this),
		}).Info("payloads imported")
		return nil
	}
	return fmt.Errorf("Bad request. Nothing to import!")
}

func (this *RequestTemplateStore) Wipe() {
	// don't change the pointer here!
	*this = RequestTemplateStore{}
}

/**
Check keys and corresponding values in template headers are also present in request headers
*/
func headerMatch(tmplHeaders, reqHeaders map[string][]string) bool {

	for headerName, headerVal := range tmplHeaders {
		// TODO: case insensitive lookup
		// TODO: is order of values in slice really important?

		reqHeaderVal, ok := reqHeaders[headerName]
		if ok && reflect.DeepEqual(headerVal, reqHeaderVal) {
			continue
		} else {
			return false
		}
	}
	return true
}

func (this *RequestTemplateStore) GetPayload() RequestTemplateResponsePairPayload {
	var pairsPayload []RequestTemplateResponsePairView
	for _, pair := range *this {
		pairsPayload = append(pairsPayload, pair.ConvertToRequestTemplateResponsePairView())
	}
	return RequestTemplateResponsePairPayload{
		Data: &pairsPayload,
	}
}

func (this *RequestTemplateResponsePair) ConvertToRequestTemplateResponsePairView() RequestTemplateResponsePairView {
	return RequestTemplateResponsePairView{
		RequestTemplate: this.RequestTemplate,
		Response:        this.Response.ConvertToResponseDetailsView(),
	}
}

func (this *RequestTemplateResponsePairPayload) ConvertToRequestTemplateStore() RequestTemplateStore {
	var requestTemplateStore RequestTemplateStore
	for _, pair := range *this.Data {
		requestTemplateStore = append(requestTemplateStore, pair.ConvertToRequestTemplateResponsePair())
	}
	return requestTemplateStore
}

func (this *RequestTemplateResponsePairView) ConvertToRequestTemplateResponsePair() RequestTemplateResponsePair {
	return RequestTemplateResponsePair{
		RequestTemplate: this.RequestTemplate,
		Response:        models.NewResponseDetialsFromResponseDetailsView(this.Response),
	}
}
<|endoftext|>


<commit_before>package proxy

import (
	"github.com/buger/jsonparser"
	"github.com/fagongzi/log"
	"github.com/valyala/fasthttp"
)

var (
	emptyString = []byte{'"', '"'}
	emptyObject = []byte("{}")
	emptyArray  = []byte("[]")
)

type render struct {
	multi        bool
	multiContext []byte
	api          *apiRuntime
	nodes        []*dispathNode
	doRender     func(*fasthttp.RequestCtx)
	allocBytes   [][]byte
}

func (rd *render) init(api *apiRuntime, nodes []*dispathNode) {
	rd.nodes = nodes
	rd.api = api
	rd.doRender = rd.renderSingle

	if len(nodes) > 1 {
		rd.doRender = rd.renderMulti
	} else if len(nodes) == 0 {
		rd.doRender = rd.renderDefault
	}
}

func (rd *render) reset() {
	for _, buf := range rd.allocBytes {
		bytesPool.Free(buf)
	}
	*rd = emptyRender
}

func (rd *render) render(ctx *fasthttp.RequestCtx, multiCtx *multiContext) {
	ctx.Response.Header.SetContentType(MultiResultsContentType)
	ctx.SetStatusCode(fasthttp.StatusOK)
	rd.multiContext = multiCtx.data
	rd.doRender(ctx)
}

func (rd *render) renderSingle(ctx *fasthttp.RequestCtx) {
	dn := rd.nodes[0]

	if dn.err != nil ||
		dn.code >= fasthttp.StatusBadRequest {
		log.Errorf("render: render failed, code=<%d>, errors:\n%+v",
			dn.code,
			dn.err)

		if rd.api.meta.DefaultValue != nil {
			rd.renderDefault(ctx)
			dn.release()
			return
		}

		ctx.SetStatusCode(dn.code)
		dn.release()
		return
	}

	if !rd.api.hasRenderTemplate() {
		rd.renderRaw(ctx, dn)
		return
	}

	src := dn.getResponseBody()
	dn.release()

	rd.renderTemplate(ctx, src)
}

func (rd *render) renderMulti(ctx *fasthttp.RequestCtx) {
	var err error
	var hasError bool
	code := fasthttp.StatusInternalServerError
	hasTemplate := rd.api.hasRenderTemplate()

	for _, dn := range rd.nodes {
		if hasError {
			dn.release()
			continue
		}

		if dn.hasError() &&
			!dn.hasDefaultValue() {
			hasError = true
			code = dn.code
			err = dn.err
			dn.release()
			continue
		}

		dn.copyHeaderTo(ctx)
		dn.release()
	}

	if hasError {
		log.Errorf("render: render failed, code=<%d>, errors:\n%+v",
			code,
			err)

		if rd.api.hasDefaultValue() {
			rd.renderDefault(ctx)
			return
		}

		ctx.SetStatusCode(code)
		return
	}

	if !hasTemplate {
		ctx.Write(rd.multiContext)
		return
	}

	rd.renderTemplate(ctx, rd.multiContext)
}

func (rd *render) renderRaw(ctx *fasthttp.RequestCtx, dn *dispathNode) {
	ctx.Response.Header.SetContentTypeBytes(dn.getResponseContentType())
	ctx.Write(dn.getResponseBody())
	dn.release()
}

func (rd *render) renderDefault(ctx *fasthttp.RequestCtx) {
	if !rd.api.hasDefaultValue() {
		return
	}

	header := &ctx.Response.Header

	for _, h := range rd.api.meta.DefaultValue.Headers {
		if h.Name == "Content-Type" {
			header.SetContentType(h.Value)
		} else {
			header.Add(h.Name, h.Value)
		}
	}

	for _, ck := range rd.api.defaultCookies {
		header.SetCookie(ck)
	}

	ctx.SetStatusCode(fasthttp.StatusOK)
	ctx.Write(rd.api.meta.DefaultValue.Body)
}

func (rd *render) renderTemplate(ctx *fasthttp.RequestCtx, context []byte) {
	data, err := rd.extract(context)
	if err != nil {
		log.Errorf("render: render failed, errors:\n%+v",
			err)
		ctx.SetStatusCode(fasthttp.StatusInternalServerError)
		return
	}

	ctx.Write(data)
}

func (rd *render) extract(src []byte) ([]byte, error) {
	var err error
	data := emptyObject
	for _, obj := range rd.api.parsedRenderObjects {
		isFlat := obj.meta.FlatAttrs
		tmp := emptyObject

		for _, attr := range obj.attrs {
			value, err := rd.extractValue(attr, src)
			if err != nil {
				return nil, err
			}

			// if is flat attr, add to data
			// otherwise, add to tmp object, and add tmp obj to data
			if isFlat {
				data, err = jsonparser.Set(data, value, attr.meta.Name)
				if err != nil {
					return nil, err
				}

				continue
			}

			tmp, err = jsonparser.Set(tmp, value, attr.meta.Name)
			if err != nil {
				return nil, err
			}
		}

		if !isFlat {
			data, err = jsonparser.Set(data, tmp, obj.meta.Name)
			if err != nil {
				return nil, err
			}
		}
	}

	return data, nil
}

func (rd *render) extractValue(attr *renderAttr, src []byte) ([]byte, error) {
	if len(attr.extracts) == 1 {
		return rd.extractAttrValue(src, attr.extracts[0]...)
	}

	obj := emptyObject
	for _, exp := range attr.extracts {
		data, err := rd.extractAttrValue(src, exp...)
		if err != nil {
			return nil, err
		}

		obj, err = jsonparser.Set(obj, data, exp[len(exp)-1])
		if err != nil {
			return nil, err
		}
	}
	return obj, nil
}

func (rd *render) extractAttrValue(src []byte, paths ...string) ([]byte, error) {
	value, vt, _, err := jsonparser.Get(src, paths...)
	if err != nil {
		return nil, err
	}

	size := len(value)
	if vt == jsonparser.String && size > 0 {
		stringValue := bytesPool.Alloc(size + 2)
		rd.allocBytes = append(rd.allocBytes, stringValue)
		stringValue[0] = '"'
		copy(stringValue[1:], value)
		stringValue[size+1] = '"'
		return stringValue, nil
	} else if vt == jsonparser.String && size == 0 {
		return emptyString, nil
	} else if vt == jsonparser.Array && size == 0 {
		return emptyArray, nil
	} else if vt == jsonparser.Unknown {
		return emptyString, nil
	} else if vt == jsonparser.NotExist {
		return emptyString, nil
	} else if vt == jsonparser.Null {
		return emptyString, nil
	}

	return value, nil
}
<commit_msg>fix: render bug<commit_after>package proxy

import (
	"github.com/buger/jsonparser"
	"github.com/fagongzi/log"
	"github.com/valyala/fasthttp"
)

var (
	emptyString = []byte{'"', '"'}
	emptyObject = []byte("{}")
	emptyArray  = []byte("[]")
)

type render struct {
	multi        bool
	multiContext []byte
	api          *apiRuntime
	nodes        []*dispathNode
	doRender     func(*fasthttp.RequestCtx)
	allocBytes   [][]byte
}

func (rd *render) init(api *apiRuntime, nodes []*dispathNode) {
	rd.nodes = nodes
	rd.api = api
	rd.doRender = rd.renderSingle

	if len(nodes) > 1 {
		rd.doRender = rd.renderMulti
	} else if len(nodes) == 0 {
		rd.doRender = rd.renderDefault
	}
}

func (rd *render) reset() {
	for _, buf := range rd.allocBytes {
		bytesPool.Free(buf)
	}
	*rd = emptyRender
}

func (rd *render) render(ctx *fasthttp.RequestCtx, multiCtx *multiContext) {
	ctx.Response.Header.SetContentType(MultiResultsContentType)
	ctx.SetStatusCode(fasthttp.StatusOK)
	if multiCtx != nil {
		rd.multiContext = multiCtx.data
	}
	rd.doRender(ctx)
}

func (rd *render) renderSingle(ctx *fasthttp.RequestCtx) {
	dn := rd.nodes[0]

	if dn.err != nil ||
		dn.code >= fasthttp.StatusBadRequest {
		log.Errorf("render: render failed, code=<%d>, errors:\n%+v",
			dn.code,
			dn.err)

		if rd.api.meta.DefaultValue != nil {
			rd.renderDefault(ctx)
			dn.release()
			return
		}

		ctx.SetStatusCode(dn.code)
		dn.release()
		return
	}

	if !rd.api.hasRenderTemplate() {
		rd.renderRaw(ctx, dn)
		return
	}

	src := dn.getResponseBody()
	dn.release()

	rd.renderTemplate(ctx, src)
}

func (rd *render) renderMulti(ctx *fasthttp.RequestCtx) {
	var err error
	var hasError bool
	code := fasthttp.StatusInternalServerError
	hasTemplate := rd.api.hasRenderTemplate()

	for _, dn := range rd.nodes {
		if hasError {
			dn.release()
			continue
		}

		if dn.hasError() &&
			!dn.hasDefaultValue() {
			hasError = true
			code = dn.code
			err = dn.err
			dn.release()
			continue
		}

		dn.copyHeaderTo(ctx)
		dn.release()
	}

	if hasError {
		log.Errorf("render: render failed, code=<%d>, errors:\n%+v",
			code,
			err)

		if rd.api.hasDefaultValue() {
			rd.renderDefault(ctx)
			return
		}

		ctx.SetStatusCode(code)
		return
	}

	if !hasTemplate {
		ctx.Write(rd.multiContext)
		return
	}

	rd.renderTemplate(ctx, rd.multiContext)
}

func (rd *render) renderRaw(ctx *fasthttp.RequestCtx, dn *dispathNode) {
	ctx.Response.Header.SetContentTypeBytes(dn.getResponseContentType())
	ctx.Write(dn.getResponseBody())
	dn.release()
}

func (rd *render) renderDefault(ctx *fasthttp.RequestCtx) {
	if !rd.api.hasDefaultValue() {
		return
	}

	header := &ctx.Response.Header

	for _, h := range rd.api.meta.DefaultValue.Headers {
		if h.Name == "Content-Type" {
			header.SetContentType(h.Value)
		} else {
			header.Add(h.Name, h.Value)
		}
	}

	for _, ck := range rd.api.defaultCookies {
		header.SetCookie(ck)
	}

	ctx.SetStatusCode(fasthttp.StatusOK)
	ctx.Write(rd.api.meta.DefaultValue.Body)
}

func (rd *render) renderTemplate(ctx *fasthttp.RequestCtx, context []byte) {
	data, err := rd.extract(context)
	if err != nil {
		log.Errorf("render: render failed, errors:\n%+v",
			err)
		ctx.SetStatusCode(fasthttp.StatusInternalServerError)
		return
	}

	ctx.Write(data)
}

func (rd *render) extract(src []byte) ([]byte, error) {
	var err error
	data := emptyObject
	for _, obj := range rd.api.parsedRenderObjects {
		isFlat := obj.meta.FlatAttrs
		tmp := emptyObject

		for _, attr := range obj.attrs {
			value, err := rd.extractValue(attr, src)
			if err != nil {
				return nil, err
			}

			// if is flat attr, add to data
			// otherwise, add to tmp object, and add tmp obj to data
			if isFlat {
				data, err = jsonparser.Set(data, value, attr.meta.Name)
				if err != nil {
					return nil, err
				}

				continue
			}

			tmp, err = jsonparser.Set(tmp, value, attr.meta.Name)
			if err != nil {
				return nil, err
			}
		}

		if !isFlat {
			data, err = jsonparser.Set(data, tmp, obj.meta.Name)
			if err != nil {
				return nil, err
			}
		}
	}

	return data, nil
}

func (rd *render) extractValue(attr *renderAttr, src []byte) ([]byte, error) {
	if len(attr.extracts) == 1 {
		return rd.extractAttrValue(src, attr.extracts[0]...)
	}

	obj := emptyObject
	for _, exp := range attr.extracts {
		data, err := rd.extractAttrValue(src, exp...)
		if err != nil {
			return nil, err
		}

		obj, err = jsonparser.Set(obj, data, exp[len(exp)-1])
		if err != nil {
			return nil, err
		}
	}
	return obj, nil
}

func (rd *render) extractAttrValue(src []byte, paths ...string) ([]byte, error) {
	value, vt, _, err := jsonparser.Get(src, paths...)
	if err != nil {
		return nil, err
	}

	size := len(value)
	if vt == jsonparser.String && size > 0 {
		stringValue := bytesPool.Alloc(size + 2)
		rd.allocBytes = append(rd.allocBytes, stringValue)
		stringValue[0] = '"'
		copy(stringValue[1:], value)
		stringValue[size+1] = '"'
		return stringValue, nil
	} else if vt == jsonparser.String && size == 0 {
		return emptyString, nil
	} else if vt == jsonparser.Array && size == 0 {
		return emptyArray, nil
	} else if vt == jsonparser.Unknown {
		return emptyString, nil
	} else if vt == jsonparser.NotExist {
		return emptyString, nil
	} else if vt == jsonparser.Null {
		return emptyString, nil
	}

	return value, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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 version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "" // major version, always numeric
	gitMinor string = "" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v0.0.0-master+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<commit_msg>Kubernetes version v1.4.0-beta.0<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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 version

// Base version information.
//
// This is the fallback data used when version information from git is not
// provided via go ldflags. It provides an approximation of the Kubernetes
// version for ad-hoc builds (e.g. `go build`) that cannot get the version
// information from git.
//
// If you are looking at these fields in the git tree, they look
// strange. They are modified on the fly by the build process. The
// in-tree values are dummy values used for "git archive", which also
// works for GitHub tar downloads.
//
// When releasing a new Kubernetes version, this file is updated by
// build/mark_new_version.sh to reflect the new version, and then a
// git annotated tag (using format vX.Y where X == Major version and Y
// == Minor version) is created to point to the commit that updates
// pkg/version/base.go
var (
	// TODO: Deprecate gitMajor and gitMinor, use only gitVersion
	// instead. First step in deprecation, keep the fields but make
	// them irrelevant. (Next we'll take it out, which may muck with
	// scripts consuming the kubectl version output - but most of
	// these should be looking at gitVersion already anyways.)
	gitMajor string = "1"  // major version, always numeric
	gitMinor string = "4+" // minor version, numeric possibly followed by "+"

	// semantic version, derived by build scripts (see
	// https://github.com/kubernetes/kubernetes/blob/master/docs/design/versioning.md
	// for a detailed discussion of this field)
	//
	// TODO: This field is still called "gitVersion" for legacy
	// reasons. For prerelease versions, the build metadata on the
	// semantic version is a git hash, but the version itself is no
	// longer the direct output of "git describe", but a slight
	// translation to be semver compliant.
	gitVersion   string = "v1.4.0-beta.0+$Format:%h$"
	gitCommit    string = "$Format:%H$"    // sha1 from git, output of $(git rev-parse HEAD)
	gitTreeState string = "not a git tree" // state of git tree, either "clean" or "dirty"

	buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
)
<|endoftext|>


<commit_before>// Copyright 2015, Truveris Inc. All Rights Reserved.
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
)

var (
	// These are the known domains to check for, where special formatting of
	// the passed URL is required so connected minions can most effectively
	// embed and manipulate the desired content.
	imgurHostNames = []string{
		"i.imgur.com",
		"www.imgur.com",
		"imgur.com",
	}
	youtubeHostNames = []string{
		"www.youtube.com",
		"www.youtu.be",
		"youtube.com",
		"youtu.be",
	}

	supportedFormatsAndTypes = map[string][]string{
		"img": {
			"image/bmp",
			"image/cis-cod",
			"image/gif",
			"image/ief",
			"image/jpeg",
			"image/webp",
			"image/pict",
			"image/pipeg",
			"image/png",
			"image/svg+xml",
			"image/tiff",
			"image/vnd.microsoft.icon",
			"image/x-cmu-raster",
			"image/x-cmx",
			"image/x-icon",
			"image/x-portable-anymap",
			"image/x-portable-bitmap",
			"image/x-portable-graymap",
			"image/x-portable-pixmap",
			"image/x-rgb",
			"image/x-xbitmap",
			"image/x-xpixmap",
			"image/x-xwindowdump",
		},
		"audio": {
			"audio/aac",
			"audio/aiff",
			"audio/amr",
			"audio/basic",
			"audio/midi",
			"audio/mp3",
			"audio/mp4",
			"audio/mpeg",
			"audio/mpeg3",
			"audio/ogg",
			"audio/vorbis",
			"audio/wav",
			"audio/webm",
			"audio/x-m4a",
			"audio/x-ms-wma",
			"audio/vnd.rn-realaudio",
			"audio/vnd.wave",
		},
		"video": {
			"video/avi",
			"video/divx",
			"video/flc",
			"video/mp4",
			"video/mpeg",
			"video/ogg",
			"video/quicktime",
			"video/sd-video",
			"video/webm",
			"video/x-dv",
			"video/x-m4v",
			"video/x-mpeg",
			"video/x-ms-asf",
			"video/x-ms-wmv",
		},
		"web": {
			"text/",
		},
	}

	// ygor should fallback to checking the file extensions for potential
	// matches if the content-type doesn't appear to be supported. The server
	// may simply be providing the wrong content-type in the header.
	supportedFormatsAndExtensions = map[string][]string{
		"img": {
			".apng",
			".bmp",
			".dib",
			".gif",
			".jfi",
			".jfif",
			".jif",
			".jpe",
			".jpeg",
			".jpg",
			".png",
			".webp",
		},
		"audio": {
			".mp3",
			".wav",
			".wave",
		},
		"video": {
			".m4a",
			".m4b",
			".m4p",
			".m4r",
			".m4v",
			".mp4",
			".oga",
			".ogg",
			".ogm",
			".ogv",
			".ogx",
			".opus",
			".spx",
			".webm",
		},
	}

	reYTVideoID = regexp.MustCompile(
		`^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*`)
)

// MediaObj represents the relevant data that will eventually be passed to
// the connected minions. It is used to generate the information that connected
// minions would use to properly embed the desired content.
//
// It also provides several functions that can be used to more easily work with
// the data, so that command modules aren't filled with a lot of excessive
// code.
type MediaObj struct {
	// 'Src' is formatted over time and is what will eventually be passed to
	// the connected minions.
	Src  string `json:"src"`
	url  string
	host string
	// 'Format' tells the connected minions how to embed the desired content
	// using 'Src'.
	Format      string `json:"format"`
	mediaType   string
	// End represents where in the desired content's timeline to stop playing.
	End string `json:"end"`
	// Muted represents whether or not the desired content should be muted.
	Muted             bool `json:"muted"`
	Loop              bool `json:"loop"`
	track             string
	acceptableFormats []string
}

// SetAcceptableFormats takes in a string array of acceptable media types,
// which will be checked against during SetSrc. If the determined media type is
// not acceptable, the url will be rejected.
func (mObj *MediaObj) SetAcceptableFormats(formats []string) {
	mObj.acceptableFormats = formats
}

// checkFormatIsAcceptable checks to make sure that the determined media
// type is acceptable. If the MediaObj's acceptableFormats attribute is not
// set, it is assumed that the media type is acceptable.
func (mObj *MediaObj) checkFormatIsAcceptable() error {
	if len(mObj.acceptableFormats) == 0 {
		// if acceptableFormats is not set, all media types are acceptable
		return nil
	}

	for _, acceptableFormat := range mObj.acceptableFormats {
		if mObj.Format == acceptableFormat {
			// The determined media type is acceptable.
			return nil
		}
	}

	// If it made it here, the determined media type must not be acceptable.
	errMsg := "error: content-type (" + mObj.mediaType + ") not supported " +
		"by this command"
	return errors.New(errMsg)
}

// SetSrc takes in a string that represents a URL. This function determines if
// the URL is a valid URL, formats imgur links to use .webm instead of .gif(v),
// and determines the Format that the URL represents.
//
// The MediaObj's 'Src' attribute will either be set to the passed URL, or the
// formatted imgur URL (if it was an imgur link).
//
// The MediaObj's 'Src' attribute can be retrieved using the MediaObj's
// 'GetSrc()' function.
//
// The URL that was originally passed, is saved as the MediaObj's 'url'
// attribute, and can be retrieved with the MediaObj's 'GetURL()' function.
func (mObj *MediaObj) SetSrc(link string) error {
	uri, linkErr := url.ParseRequestURI(link)
	if linkErr != nil {
		errorMsg := "error: not a valid URL"
		return errors.New(errorMsg)
	}
	// Strip any query or fragment attached to the URL
	mObj.Src = uri.String()
	mObj.url = link
	mObj.host = uri.Host

	// Check that the URL returns a status code of 200.
	res, err := http.Head(mObj.Src)
	if err != nil {
		errMsg := "error: " + err.Error()
		return errors.New(errMsg)
	}
	statusCode := strconv.Itoa(res.StatusCode)
	if statusCode != "200" {
		errMsg := "error: response status code is " + statusCode
		return errors.New(errMsg)
	}

	headErr := mObj.setFormat(res.Header)
	if headErr != nil {
		return headErr
	}

	// If it's an imgur link, and the content-type contains "image/gif", modify
	// the MediaObj so minions embed the far more efficient webm version.
	if mObj.isImgur() {
		isGIF := strings.Contains(strings.ToLower(mObj.mediaType), "image/gif")
		hasGIFVExt := mObj.GetExt() == ".gifv"
		if isGIF || hasGIFVExt {
			mObj.replaceSrcExt(".webm")
			mObj.Format = "video"
			mObj.mediaType = "video/webm"
		}
	}

	merr := mObj.checkFormatIsAcceptable()
	if merr != nil {
		return merr
	}

	return nil
}

// GetSrc returns the MediaObj's 'Src' attribute (this is what should get
// passed to the connected minions).
func (mObj *MediaObj) GetSrc() string {
	return mObj.Src
}

// GetURL returns the URL that was originally passed to the 'SetSrc()'
// function.
func (mObj *MediaObj) GetURL() string {
	return mObj.url
}

// setFormat sets the 'Format' attribute of the MediaObj. This tells the
// connected minions what kind of content they should be trying to embed.
func (mObj *MediaObj) setFormat(header map[string][]string) error {
	// If it's a YouTube link, check if there's a video ID we can grab.
	if mObj.isYouTube() {
		match := reYTVideoID.FindAllStringSubmatch(mObj.Src, -1)
		if len(match) > 0 {
			mObj.Src = match[0][2]
			mObj.Format = "youtube"
			mObj.mediaType = "youtube"
			return nil
		}
	}

	// Is the media type in the contentType an image|audio|video type that
	// Chromium supports?
	if contentType, ok := header["Content-Type"]; ok {
		// Check for standard, supported media types.
		for format, formatMediaTypes := range supportedFormatsAndTypes {
			for _, mediaType := range formatMediaTypes {
				for _, cType := range contentType {
					if strings.Contains(cType, mediaType) {
						mObj.Format = format
						mObj.mediaType = mediaType
						return nil
					}
				}
			}
		}

		// Fallback to known supported file extensions if content-type isn't
		// recognized as supported.
		ext := mObj.GetExt()
		for format, formatExtensions := range supportedFormatsAndExtensions {
			for _, extension := range formatExtensions {
				if extension == ext {
					mObj.Format = format
					mObj.mediaType = ext
					return nil
				}
			}
		}

		// If the media type isn't supported, return an error.
		errMsg := "error: unsupported content-type " +
			"(" + strings.Join(contentType, ", ") + ")"
		return errors.New(errMsg)
	}

	// It will only get here if it didn't have a content-type in the header.
	errMsg := "error: no content-type found"
	return errors.New(errMsg)
}

// GetFormat returnes the MediaObj's 'Format' attribute. The 'Format'
// tells the connected minions what kind of content they should be trying to
// embed when using the MediaObj's 'Src' attribute.
func (mObj *MediaObj) GetFormat() string {
	return mObj.Format
}

// IsOfFormat determines if the MediaObj's Format is contained in the
// passed string array.
func (mObj *MediaObj) IsOfFormat(formats []string) bool {
	format := mObj.GetFormat()
	for _, mt := range formats {
		if format == mt {
			return true
		}
	}
	return false
}

// GetExt is a convenience function to get the extension of theMediaObj's
// current Src.
func (mObj *MediaObj) GetExt() string {
	return strings.ToLower(path.Ext(mObj.Src))
}

// isImgur attempts to determine if the desired content is hosted on imgur.
func (mObj *MediaObj) isImgur() bool {
	for _, d := range imgurHostNames {
		if mObj.host == d {
			return true
		}
	}
	return false
}

// isYouTube attempts to determine if the desired content is a video hosted on
// YouTube
func (mObj *MediaObj) isYouTube() bool {
	for _, d := range youtubeHostNames {
		if mObj.host == d {
			return true
		}
	}
	return false
}

// replaceSrcExt is a convenience function to replace the extension of the
// MediaObj's current Src.
func (mObj *MediaObj) replaceSrcExt(newExt string) {
	mObj.Src = mObj.Src[0:len(mObj.Src)-len(mObj.GetExt())] + newExt
}

// Serialize generates and returns the JSON string out of the MediaObj. This
// JSON string is what should be sent to the connected minions.
func (mObj *MediaObj) Serialize() string {
	serializedJSON, _ := json.Marshal(struct {
		MediaObj *MediaObj `json:"mediaObj"`
		Status   string    `json:"status"`
		Track    string    `json:"track"`
	}{
		Status:   "media",
		Track:    mObj.track,
		MediaObj: mObj,
	})
	return string(serializedJSON)
}

// NewMediaObj is a convenience function meant to clean up the code of modules.
// It builds the MediaObj.
func NewMediaObj(mediaItem map[string]string, track string, muted bool, loop bool, acceptableFormats []string) (*MediaObj, error) {
	// Parse the mediaItem map into a MediaObj.
	mObj := new(MediaObj)
	mObj.End = mediaItem["end"]
	mObj.Muted = muted
	mObj.Loop = loop
	mObj.track = track
	mObj.SetAcceptableFormats(acceptableFormats)

	setSrcErr := mObj.SetSrc(mediaItem["url"])
	if setSrcErr != nil {
		return nil, setSrcErr
	}

	return mObj, nil
}
<commit_msg>now grabs vimeo video id from url if present, and instructs minions to embed a vimeo video with that id<commit_after>// Copyright 2015, Truveris Inc. All Rights Reserved.
// Use of this source code is governed by the ISC license in the LICENSE file.

package main

import (
	"encoding/json"
	"errors"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strconv"
	"strings"
)

var (
	// These are the known domains to check for, where special formatting of
	// the passed URL is required so connected minions can most effectively
	// embed and manipulate the desired content.
	imgurHostNames = []string{
		"i.imgur.com",
		"www.imgur.com",
		"imgur.com",
	}
	youtubeHostNames = []string{
		"www.youtube.com",
		"www.youtu.be",
		"youtube.com",
		"youtu.be",
	}
	vimeoHostNames = []string{
		"vimeo.com",
		"www.vimeo.com",
		"player.vimeo.com",
		"www.player.vimeo.com",
	}

	supportedFormatsAndTypes = map[string][]string{
		"img": {
			"image/bmp",
			"image/cis-cod",
			"image/gif",
			"image/ief",
			"image/jpeg",
			"image/webp",
			"image/pict",
			"image/pipeg",
			"image/png",
			"image/svg+xml",
			"image/tiff",
			"image/vnd.microsoft.icon",
			"image/x-cmu-raster",
			"image/x-cmx",
			"image/x-icon",
			"image/x-portable-anymap",
			"image/x-portable-bitmap",
			"image/x-portable-graymap",
			"image/x-portable-pixmap",
			"image/x-rgb",
			"image/x-xbitmap",
			"image/x-xpixmap",
			"image/x-xwindowdump",
		},
		"audio": {
			"audio/aac",
			"audio/aiff",
			"audio/amr",
			"audio/basic",
			"audio/midi",
			"audio/mp3",
			"audio/mp4",
			"audio/mpeg",
			"audio/mpeg3",
			"audio/ogg",
			"audio/vorbis",
			"audio/wav",
			"audio/webm",
			"audio/x-m4a",
			"audio/x-ms-wma",
			"audio/vnd.rn-realaudio",
			"audio/vnd.wave",
		},
		"video": {
			"video/avi",
			"video/divx",
			"video/flc",
			"video/mp4",
			"video/mpeg",
			"video/ogg",
			"video/quicktime",
			"video/sd-video",
			"video/webm",
			"video/x-dv",
			"video/x-m4v",
			"video/x-mpeg",
			"video/x-ms-asf",
			"video/x-ms-wmv",
		},
		"web": {
			"text/",
		},
	}

	// ygor should fallback to checking the file extensions for potential
	// matches if the content-type doesn't appear to be supported. The server
	// may simply be providing the wrong content-type in the header.
	supportedFormatsAndExtensions = map[string][]string{
		"img": {
			".apng",
			".bmp",
			".dib",
			".gif",
			".jfi",
			".jfif",
			".jif",
			".jpe",
			".jpeg",
			".jpg",
			".png",
			".webp",
		},
		"audio": {
			".mp3",
			".wav",
			".wave",
		},
		"video": {
			".m4a",
			".m4b",
			".m4p",
			".m4r",
			".m4v",
			".mp4",
			".oga",
			".ogg",
			".ogm",
			".ogv",
			".ogx",
			".opus",
			".spx",
			".webm",
		},
	}

	reYTVideoID = regexp.MustCompile(
		`^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*`)
)

// MediaObj represents the relevant data that will eventually be passed to
// the connected minions. It is used to generate the information that connected
// minions would use to properly embed the desired content.
//
// It also provides several functions that can be used to more easily work with
// the data, so that command modules aren't filled with a lot of excessive
// code.
type MediaObj struct {
	// 'Src' is formatted over time and is what will eventually be passed to
	// the connected minions.
	Src  string `json:"src"`
	url  string
	host string
	// 'Format' tells the connected minions how to embed the desired content
	// using 'Src'.
	Format      string `json:"format"`
	mediaType   string
	// End represents where in the desired content's timeline to stop playing.
	End string `json:"end"`
	// Muted represents whether or not the desired content should be muted.
	Muted             bool `json:"muted"`
	Loop              bool `json:"loop"`
	track             string
	acceptableFormats []string
}

// SetAcceptableFormats takes in a string array of acceptable media types,
// which will be checked against during SetSrc. If the determined media type is
// not acceptable, the url will be rejected.
func (mObj *MediaObj) SetAcceptableFormats(formats []string) {
	mObj.acceptableFormats = formats
}

// checkFormatIsAcceptable checks to make sure that the determined media
// type is acceptable. If the MediaObj's acceptableFormats attribute is not
// set, it is assumed that the media type is acceptable.
func (mObj *MediaObj) checkFormatIsAcceptable() error {
	if len(mObj.acceptableFormats) == 0 {
		// if acceptableFormats is not set, all media types are acceptable
		return nil
	}

	for _, acceptableFormat := range mObj.acceptableFormats {
		if mObj.Format == acceptableFormat {
			// The determined media type is acceptable.
			return nil
		}
	}

	// If it made it here, the determined media type must not be acceptable.
	errMsg := "error: content-type (" + mObj.mediaType + ") not supported " +
		"by this command"
	return errors.New(errMsg)
}

// SetSrc takes in a string that represents a URL. This function determines if
// the URL is a valid URL, formats imgur links to use .webm instead of .gif(v),
// and determines the Format that the URL represents.
//
// The MediaObj's 'Src' attribute will either be set to the passed URL, or the
// formatted imgur URL (if it was an imgur link).
//
// The MediaObj's 'Src' attribute can be retrieved using the MediaObj's
// 'GetSrc()' function.
//
// The URL that was originally passed, is saved as the MediaObj's 'url'
// attribute, and can be retrieved with the MediaObj's 'GetURL()' function.
func (mObj *MediaObj) SetSrc(link string) error {
	uri, linkErr := url.ParseRequestURI(link)
	if linkErr != nil {
		errorMsg := "error: not a valid URL"
		return errors.New(errorMsg)
	}
	// Strip any query or fragment attached to the URL
	mObj.Src = uri.String()
	mObj.url = link
	mObj.host = uri.Host

	// Check that the URL returns a status code of 200.
	res, err := http.Head(mObj.Src)
	if err != nil {
		errMsg := "error: " + err.Error()
		return errors.New(errMsg)
	}
	statusCode := strconv.Itoa(res.StatusCode)
	if statusCode != "200" {
		errMsg := "error: response status code is " + statusCode
		return errors.New(errMsg)
	}

	headErr := mObj.setFormat(res.Header)
	if headErr != nil {
		return headErr
	}

	// If it's an imgur link, and the content-type contains "image/gif", modify
	// the MediaObj so minions embed the far more efficient webm version.
	if mObj.isImgur() {
		isGIF := strings.Contains(strings.ToLower(mObj.mediaType), "image/gif")
		hasGIFVExt := mObj.GetExt() == ".gifv"
		if isGIF || hasGIFVExt {
			mObj.replaceSrcExt(".webm")
			mObj.Format = "video"
			mObj.mediaType = "video/webm"
		}
	}

	merr := mObj.checkFormatIsAcceptable()
	if merr != nil {
		return merr
	}

	return nil
}

// GetSrc returns the MediaObj's 'Src' attribute (this is what should get
// passed to the connected minions).
func (mObj *MediaObj) GetSrc() string {
	return mObj.Src
}

// GetURL returns the URL that was originally passed to the 'SetSrc()'
// function.
func (mObj *MediaObj) GetURL() string {
	return mObj.url
}

// setFormat sets the 'Format' attribute of the MediaObj. This tells the
// connected minions what kind of content they should be trying to embed.
func (mObj *MediaObj) setFormat(header map[string][]string) error {
	// If it's a YouTube link, check if there's a video ID we can grab.
	if mObj.isYouTube() {
		match := reYTVideoID.FindAllStringSubmatch(mObj.Src, -1)
		if len(match) > 0 {
			mObj.Src = match[0][2]
			mObj.Format = "youtube"
			mObj.mediaType = "youtube"
			return nil
		}
	}

	// If it's a Vimeo link, check if there's a video ID we can grab.
	if mObj.isVimeo() {
		// Vimeo video IDs are the last element in the URL (represented as an
		// integer between 6 and 11 digits long) before the query string and/or
		// fragment. mObj.Src has the query string and fragment stripped off,
		// so if this is a link to a Vimeo video, potentialVideoID should be an
		// integer between 6 and 11 digits long.
		potentialVideoID := path.Base(mObj.Src)
		// Check to see if it is between 6 and 11 characters long.
		if 6 <= len(potentialVideoID) && len(potentialVideoID) <= 11 {
			// Check to make sure it is a number.
			if _, err := strconv.Atoi(potentialVideoID); err == nil {
				// It is a number
				mObj.Src = potentialVideoID
				mObj.Format = "vimeo"
				mObj.mediaType = "vimeo"
				return nil
			}
		}
	}

	// Is the media type in the contentType an image|audio|video type that
	// Chromium supports?
	if contentType, ok := header["Content-Type"]; ok {
		// Check for standard, supported media types.
		for format, formatMediaTypes := range supportedFormatsAndTypes {
			for _, mediaType := range formatMediaTypes {
				for _, cType := range contentType {
					if strings.Contains(cType, mediaType) {
						mObj.Format = format
						mObj.mediaType = mediaType
						return nil
					}
				}
			}
		}

		// Fallback to known supported file extensions if content-type isn't
		// recognized as supported.
		ext := mObj.GetExt()
		for format, formatExtensions := range supportedFormatsAndExtensions {
			for _, extension := range formatExtensions {
				if extension == ext {
					mObj.Format = format
					mObj.mediaType = ext
					return nil
				}
			}
		}

		// If the media type isn't supported, return an error.
		errMsg := "error: unsupported content-type " +
			"(" + strings.Join(contentType, ", ") + ")"
		return errors.New(errMsg)
	}

	// It will only get here if it didn't have a content-type in the header.
	errMsg := "error: no content-type found"
	return errors.New(errMsg)
}

// GetFormat returnes the MediaObj's 'Format' attribute. The 'Format'
// tells the connected minions what kind of content they should be trying to
// embed when using the MediaObj's 'Src' attribute.
func (mObj *MediaObj) GetFormat() string {
	return mObj.Format
}

// IsOfFormat determines if the MediaObj's Format is contained in the
// passed string array.
func (mObj *MediaObj) IsOfFormat(formats []string) bool {
	format := mObj.GetFormat()
	for _, mt := range formats {
		if format == mt {
			return true
		}
	}
	return false
}

// GetExt is a convenience function to get the extension of theMediaObj's
// current Src.
func (mObj *MediaObj) GetExt() string {
	return strings.ToLower(path.Ext(mObj.Src))
}

// isImgur attempts to determine if the desired content is hosted on imgur.
func (mObj *MediaObj) isImgur() bool {
	for _, d := range imgurHostNames {
		if mObj.host == d {
			return true
		}
	}
	return false
}

// isYouTube attempts to determine if the desired content is a video hosted on
// YouTube
func (mObj *MediaObj) isYouTube() bool {
	for _, d := range youtubeHostNames {
		if mObj.host == d {
			return true
		}
	}
	return false
}

// isVimeo attempts to determine if the desired content is a video hosted on
// Vimeo
func (mObj *MediaObj) isVimeo() bool {
	for _, d := range vimeoHostNames {
		if mObj.host == d {
			return true
		}
	}
	return false
}

// replaceSrcExt is a convenience function to replace the extension of the
// MediaObj's current Src.
func (mObj *MediaObj) replaceSrcExt(newExt string) {
	mObj.Src = mObj.Src[0:len(mObj.Src)-len(mObj.GetExt())] + newExt
}

// Serialize generates and returns the JSON string out of the MediaObj. This
// JSON string is what should be sent to the connected minions.
func (mObj *MediaObj) Serialize() string {
	serializedJSON, _ := json.Marshal(struct {
		MediaObj *MediaObj `json:"mediaObj"`
		Status   string    `json:"status"`
		Track    string    `json:"track"`
	}{
		Status:   "media",
		Track:    mObj.track,
		MediaObj: mObj,
	})
	return string(serializedJSON)
}

// NewMediaObj is a convenience function meant to clean up the code of modules.
// It builds the MediaObj.
func NewMediaObj(mediaItem map[string]string, track string, muted bool, loop bool, acceptableFormats []string) (*MediaObj, error) {
	// Parse the mediaItem map into a MediaObj.
	mObj := new(MediaObj)
	mObj.End = mediaItem["end"]
	mObj.Muted = muted
	mObj.Loop = loop
	mObj.track = track
	mObj.SetAcceptableFormats(acceptableFormats)

	setSrcErr := mObj.SetSrc(mediaItem["url"])
	if setSrcErr != nil {
		return nil, setSrcErr
	}

	return mObj, nil
}
<|endoftext|>