<commit_before>package libcarina

import (
	"archive/zip"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/rackspace"
)

// BetaEndpoint reflects the default endpoint for this library
const BetaEndpoint = "https://app.getcarina.com"
const mimetypeJSON = "application/json"
const authHeaderKey = "X-Auth-Token"
const userAgent = "getcarina/libcarina"

// ZipURLResponse is the response that comes back from the zip endpoint
type ZipURLResponse struct {
	URL string `json:"zip_url"`
}

// ClusterClient accesses Carina directly
type ClusterClient struct {
	Client   *http.Client
	Username string
	Token    string
	Endpoint string
}

// ErrorResponse is the JSON formatted error response from Carina
type ErrorResponse struct {
	Error string `json:"error"`
}

// Cluster is a cluster of Docker nodes
type Cluster struct {
	// ID of the cluster
	ID string `json:"id"`

	// Name of the cluster
	Name string `json:"name"`

	// COE (container orchestration engine) used by the cluster
	COE string `json:"coe"`

	// Underlying type of the host nodes, such as lxc or vm
	HostType string `json:"host_type"`

	// Nodes in the cluster
	Nodes int `json:"node_count,omitempty"`

	// Status of the cluster
	Status    string `json:"status,omitempty"`
}

// Credentials holds the keys to the kingdom
type Credentials struct {
	README     []byte
	Cert       []byte
	Key        []byte
	CA         []byte
	CAKey      []byte
	DockerEnv  []byte
	DockerCmd  []byte
	DockerPS1  []byte
	DockerHost string
	Files      map[string][]byte
	DockerFish []byte
}

type Quotas struct {
	MaxClusters        int `json:"max_clusters"`
	MaxNodesPerCluster int `json:"max_nodes_per_cluster"`
}

func newClusterClient(endpoint string, ao gophercloud.AuthOptions) (*ClusterClient, error) {
	provider, err := rackspace.AuthenticatedClient(ao)
	if err != nil {
		return nil, err
	}

	return &ClusterClient{
		Client:   &http.Client{},
		Username: ao.Username,
		Token:    provider.TokenID,
		Endpoint: endpoint,
	}, nil
}

// NewClusterClient create a new clusterclient by API Key
func NewClusterClient(endpoint, username, apikey string) (*ClusterClient, error) {
	ao := gophercloud.AuthOptions{
		Username:         username,
		APIKey:           apikey,
		IdentityEndpoint: rackspace.RackspaceUSIdentity,
	}

	return newClusterClient(endpoint, ao)
}

// NewRequest handles a request using auth used by Carina
func (c *ClusterClient) NewRequest(method string, uri string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, c.Endpoint+uri, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Add("Content-Type", mimetypeJSON)
	req.Header.Add("Accept", mimetypeJSON)
	req.Header.Add(authHeaderKey, c.Token)
	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		if resp.Body == nil {
			return nil, errors.New(resp.Status)
		}
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.New(resp.Status)
		}
		return nil, errors.New(string(b))
	}

	return resp, nil
}

// List the current clusters
func (c *ClusterClient) List() ([]Cluster, error) {
	resp, err := c.NewRequest("GET", "/clusters", nil)
	if err != nil {
		return nil, err
	}

	var result struct {
		Clusters []Cluster `json:"clusters"`
	}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		return nil, err
	}
	return result.Clusters, nil
}

func clusterFromResponse(resp *http.Response, err error) (*Cluster, error) {
	if err != nil {
		return nil, err
	}
	cluster := new(Cluster)
	err = json.NewDecoder(resp.Body).Decode(&cluster)
	if err != nil {
		return nil, err
	}
	return cluster, nil
}

// Get a cluster by cluster name
func (c *ClusterClient) Get(clusterID string) (*Cluster, error) {
	uri := path.Join("/bays", clusterID)
	resp, err := c.NewRequest("GET", uri, nil)
	return clusterFromResponse(resp, err)
}

// Create a new cluster with cluster options
func (c *ClusterClient) Create(clusterOpts Cluster) (*Cluster, error) {
	clusterOptsJSON, err := json.Marshal(clusterOpts)
	if err != nil {
		return nil, err
	}

	body := bytes.NewReader(clusterOptsJSON)
	resp, err := c.NewRequest("POST", "/clusters", body)
	return clusterFromResponse(resp, err)
}

// GetZipURL returns the URL for downloading credentials
func (c *ClusterClient) GetZipURL(clusterName string) (string, error) {
	uri := path.Join("/clusters", c.Username, clusterName, "zip")
	resp, err := c.NewRequest("GET", uri, nil)
	if err != nil {
		return "", err
	}

	var zipURLResp ZipURLResponse

	err = json.NewDecoder(resp.Body).Decode(&zipURLResp)

	if err != nil {
		return "", err
	}

	return zipURLResp.URL, nil
}

// GetCredentials returns a Credentials struct for the given cluster name
func (c *ClusterClient) GetCredentials(clusterName string) (*Credentials, error) {
	url, err := c.GetZipURL(clusterName)
	if err != nil {
		return nil, err
	}
	zr, err := fetchZip(c.Client, url)
	if err != nil || len(zr.File) < 6 {
		return nil, err
	}

	// fetch the contents for each credential/note
	creds := new(Credentials)
	creds.Files = make(map[string][]byte)
	for _, zf := range zr.File {
		_, fname := path.Split(zf.Name)
		fi := zf.FileInfo()

		if fi.IsDir() {
			// Explicitly skip past directories (the UUID directory from a previous release)
			continue
		}

		rc, err := zf.Open()
		if err != nil {
			return nil, err
		}

		b, err := ioutil.ReadAll(rc)
		if err != nil {
			return nil, err
		}
		creds.Files[fname] = b

		switch fname {
		case "ca.pem":
			creds.CA = b
		case "README.md":
			creds.README = b
		case "ca-key.pem":
			creds.CAKey = b
		case "docker.env":
			creds.DockerEnv = b
		case "cert.pem":
			creds.Cert = b
		case "key.pem":
			creds.Key = b
		case "docker.ps1":
			creds.DockerPS1 = b
		case "docker.cmd":
			creds.DockerCmd = b
		case "docker.fish":
			creds.DockerFish = b
		}

	}

	sourceLines := strings.Split(string(creds.DockerEnv), "\n")
	for _, line := range sourceLines {
		if strings.Index(line, "export ") == 0 {
			varDecl := strings.TrimRight(line[7:], "\n")
			eqLocation := strings.Index(varDecl, "=")

			varName := varDecl[:eqLocation]
			varValue := varDecl[eqLocation+1:]

			switch varName {
			case "DOCKER_HOST":
				creds.DockerHost = varValue
			}

		}
	}

	return creds, nil
}

// GetDockerConfig returns the hostname and tls.Config for a given clustername
func (c *ClusterClient) GetDockerConfig(clusterName string) (hostname string, tlsConfig *tls.Config, err error) {
	creds, err := c.GetCredentials(clusterName)
	if err != nil {
		return "", nil, err
	}
	tlsConfig, err = creds.GetTLSConfig()
	return creds.DockerHost, tlsConfig, err
}

// GetTLSConfig returns a tls.Config for a credential set
func (creds *Credentials) GetTLSConfig() (*tls.Config, error) {
	// TLS config
	var tlsConfig tls.Config
	tlsConfig.InsecureSkipVerify = true
	certPool := x509.NewCertPool()

	certPool.AppendCertsFromPEM(creds.CA)
	tlsConfig.RootCAs = certPool
	keypair, err := tls.X509KeyPair(creds.Cert, creds.Key)
	if err != nil {
		return &tlsConfig, err
	}
	tlsConfig.Certificates = []tls.Certificate{keypair}

	return &tlsConfig, nil
}

func fetchZip(client *http.Client, zipurl string) (*zip.Reader, error) {
	req, err := http.NewRequest("GET", zipurl, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", userAgent)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.New(resp.Status)
		}
		return nil, errors.New(string(b))
	}

	buf := &bytes.Buffer{}

	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	b := bytes.NewReader(buf.Bytes())
	return zip.NewReader(b, int64(b.Len()))
}

// Grow increases a cluster by the provided number of nodes
func (c *ClusterClient) Grow(clusterName string, nodes int) (*Cluster, error) {
	incr := map[string]int{
		"nodes": nodes,
	}

	growthRequest, err := json.Marshal(incr)
	if err != nil {
		return nil, err
	}
	r := bytes.NewReader(growthRequest)

	uri := path.Join("/clusters", c.Username, clusterName, "grow")
	resp, err := c.NewRequest("POST", uri, r)
	return clusterFromResponse(resp, err)
}

// SetAutoScale enables or disables autoscale on an already running cluster
func (c *ClusterClient) SetAutoScale(clusterName string, autoscale bool) (*Cluster, error) {
	setAutoscale := "false"
	if autoscale {
		setAutoscale = "true"
	}
	uri := path.Join("/clusters", c.Username, clusterName, "autoscale", setAutoscale)
	resp, err := c.NewRequest("PUT", uri, nil)
	return clusterFromResponse(resp, err)
}

const rebuildSwarmAction = "rebuild-swarm"

type actionRequest struct {
	Action string `json:"action"`
}

func (c *ClusterClient) doAction(clusterName, action string) (*Cluster, error) {
	actionReq, err := json.Marshal(actionRequest{Action: action})
	if err != nil {
		return nil, err
	}
	r := bytes.NewReader(actionReq)
	uri := path.Join("/clusters", c.Username, clusterName, "action")
	resp, err := c.NewRequest("POST", uri, r)
	return clusterFromResponse(resp, err)
}

// Rebuild creates a wholly new Swarm cluster
func (c *ClusterClient) Rebuild(clusterName string) (*Cluster, error) {
	return c.doAction(clusterName, rebuildSwarmAction)
}

// Delete nukes a cluster out of existence
func (c *ClusterClient) Delete(clusterName string) (*Cluster, error) {
	uri := path.Join("/clusters", c.Username, clusterName)
	resp, err := c.NewRequest("DELETE", uri, nil)
	return clusterFromResponse(resp, err)
}

func quotasFromResponse(resp *http.Response) (*Quotas, error) {
	quotas := new(Quotas)
	err := json.NewDecoder(resp.Body).Decode(&quotas)
	if err != nil {
		return nil, err
	}
	return quotas, nil
}

func (c *ClusterClient) GetQuotas() (*Quotas, error) {
	uri := path.Join("/quotas", c.Username)
	resp, err := c.NewRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	return quotasFromResponse(resp)
}
<commit_msg>Update get to use /clusters, retrieving by id or name<commit_after>package libcarina

import (
	"archive/zip"
	"bytes"
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"path"
	"strings"

	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/rackspace"
	"regexp"
	"fmt"
)

// BetaEndpoint reflects the default endpoint for this library
const BetaEndpoint = "https://app.getcarina.com"
const mimetypeJSON = "application/json"
const authHeaderKey = "X-Auth-Token"
const userAgent = "getcarina/libcarina"

// ZipURLResponse is the response that comes back from the zip endpoint
type ZipURLResponse struct {
	URL string `json:"zip_url"`
}

// ClusterClient accesses Carina directly
type ClusterClient struct {
	Client   *http.Client
	Username string
	Token    string
	Endpoint string
}

// ErrorResponse is the JSON formatted error response from Carina
type ErrorResponse struct {
	Error string `json:"error"`
}

// Cluster is a cluster of Docker nodes
type Cluster struct {
	// ID of the cluster
	ID string `json:"id"`

	// Name of the cluster
	Name string `json:"name"`

	// COE (container orchestration engine) used by the cluster
	COE string `json:"coe"`

	// Underlying type of the host nodes, such as lxc or vm
	HostType string `json:"host_type"`

	// Nodes in the cluster
	Nodes int `json:"node_count,omitempty"`

	// Status of the cluster
	Status    string `json:"status,omitempty"`
}

// Credentials holds the keys to the kingdom
type Credentials struct {
	README     []byte
	Cert       []byte
	Key        []byte
	CA         []byte
	CAKey      []byte
	DockerEnv  []byte
	DockerCmd  []byte
	DockerPS1  []byte
	DockerHost string
	Files      map[string][]byte
	DockerFish []byte
}

type Quotas struct {
	MaxClusters        int `json:"max_clusters"`
	MaxNodesPerCluster int `json:"max_nodes_per_cluster"`
}

func newClusterClient(endpoint string, ao gophercloud.AuthOptions) (*ClusterClient, error) {
	provider, err := rackspace.AuthenticatedClient(ao)
	if err != nil {
		return nil, err
	}

	return &ClusterClient{
		Client:   &http.Client{},
		Username: ao.Username,
		Token:    provider.TokenID,
		Endpoint: endpoint,
	}, nil
}

// NewClusterClient create a new clusterclient by API Key
func NewClusterClient(endpoint, username, apikey string) (*ClusterClient, error) {
	ao := gophercloud.AuthOptions{
		Username:         username,
		APIKey:           apikey,
		IdentityEndpoint: rackspace.RackspaceUSIdentity,
	}

	return newClusterClient(endpoint, ao)
}

// NewRequest handles a request using auth used by Carina
func (c *ClusterClient) NewRequest(method string, uri string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, c.Endpoint+uri, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("User-Agent", userAgent)
	req.Header.Add("Content-Type", mimetypeJSON)
	req.Header.Add("Accept", mimetypeJSON)
	req.Header.Add(authHeaderKey, c.Token)
	resp, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 400 {
		if resp.Body == nil {
			return nil, errors.New(resp.Status)
		}
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.New(resp.Status)
		}
		return nil, errors.New(string(b))
	}

	return resp, nil
}

// List the current clusters
func (c *ClusterClient) List() ([]Cluster, error) {
	resp, err := c.NewRequest("GET", "/clusters", nil)
	if err != nil {
		return nil, err
	}

	var result struct {
		Clusters []Cluster `json:"clusters"`
	}
	err = json.NewDecoder(resp.Body).Decode(&result)
	if err != nil {
		return nil, err
	}
	return result.Clusters, nil
}

func clusterFromResponse(resp *http.Response, err error) (*Cluster, error) {
	if err != nil {
		return nil, err
	}
	cluster := new(Cluster)
	err = json.NewDecoder(resp.Body).Decode(&cluster)
	if err != nil {
		return nil, err
	}
	return cluster, nil
}

func isClusterID(token string) bool {
	r := regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[8|9|aA|bB][a-f0-9]{3}-[a-f0-9]{12}$")
	return r.MatchString(token)
}

func (c *ClusterClient) lookupClusterID(token string) (string, error) {
	if isClusterID(token) {
		return token, nil
	}

	clusters, err := c.List()
	if err != nil {
		return "", err
	}

	for _, cluster := range clusters {
		if strings.ToLower(cluster.Name) == strings.ToLower(token) {
			return cluster.ID, nil
		}
	}

	return "", fmt.Errorf("Could not find cluster named: %s", token)
}

// Get a cluster by cluster by its name or id
func (c *ClusterClient) Get(token string) (*Cluster, error) {
	id, err := c.lookupClusterID(token)
	if err != nil {
		return nil, err
	}

	uri := path.Join("/clusters", id)
	resp, err := c.NewRequest("GET", uri, nil)
	return clusterFromResponse(resp, err)
}

// Create a new cluster with cluster options
func (c *ClusterClient) Create(clusterOpts Cluster) (*Cluster, error) {
	clusterOptsJSON, err := json.Marshal(clusterOpts)
	if err != nil {
		return nil, err
	}

	body := bytes.NewReader(clusterOptsJSON)
	resp, err := c.NewRequest("POST", "/clusters", body)
	return clusterFromResponse(resp, err)
}

// GetZipURL returns the URL for downloading credentials
func (c *ClusterClient) GetZipURL(clusterName string) (string, error) {
	uri := path.Join("/clusters", c.Username, clusterName, "zip")
	resp, err := c.NewRequest("GET", uri, nil)
	if err != nil {
		return "", err
	}

	var zipURLResp ZipURLResponse

	err = json.NewDecoder(resp.Body).Decode(&zipURLResp)

	if err != nil {
		return "", err
	}

	return zipURLResp.URL, nil
}

// GetCredentials returns a Credentials struct for the given cluster name
func (c *ClusterClient) GetCredentials(clusterName string) (*Credentials, error) {
	url, err := c.GetZipURL(clusterName)
	if err != nil {
		return nil, err
	}
	zr, err := fetchZip(c.Client, url)
	if err != nil || len(zr.File) < 6 {
		return nil, err
	}

	// fetch the contents for each credential/note
	creds := new(Credentials)
	creds.Files = make(map[string][]byte)
	for _, zf := range zr.File {
		_, fname := path.Split(zf.Name)
		fi := zf.FileInfo()

		if fi.IsDir() {
			// Explicitly skip past directories (the UUID directory from a previous release)
			continue
		}

		rc, err := zf.Open()
		if err != nil {
			return nil, err
		}

		b, err := ioutil.ReadAll(rc)
		if err != nil {
			return nil, err
		}
		creds.Files[fname] = b

		switch fname {
		case "ca.pem":
			creds.CA = b
		case "README.md":
			creds.README = b
		case "ca-key.pem":
			creds.CAKey = b
		case "docker.env":
			creds.DockerEnv = b
		case "cert.pem":
			creds.Cert = b
		case "key.pem":
			creds.Key = b
		case "docker.ps1":
			creds.DockerPS1 = b
		case "docker.cmd":
			creds.DockerCmd = b
		case "docker.fish":
			creds.DockerFish = b
		}

	}

	sourceLines := strings.Split(string(creds.DockerEnv), "\n")
	for _, line := range sourceLines {
		if strings.Index(line, "export ") == 0 {
			varDecl := strings.TrimRight(line[7:], "\n")
			eqLocation := strings.Index(varDecl, "=")

			varName := varDecl[:eqLocation]
			varValue := varDecl[eqLocation+1:]

			switch varName {
			case "DOCKER_HOST":
				creds.DockerHost = varValue
			}

		}
	}

	return creds, nil
}

// GetDockerConfig returns the hostname and tls.Config for a given clustername
func (c *ClusterClient) GetDockerConfig(clusterName string) (hostname string, tlsConfig *tls.Config, err error) {
	creds, err := c.GetCredentials(clusterName)
	if err != nil {
		return "", nil, err
	}
	tlsConfig, err = creds.GetTLSConfig()
	return creds.DockerHost, tlsConfig, err
}

// GetTLSConfig returns a tls.Config for a credential set
func (creds *Credentials) GetTLSConfig() (*tls.Config, error) {
	// TLS config
	var tlsConfig tls.Config
	tlsConfig.InsecureSkipVerify = true
	certPool := x509.NewCertPool()

	certPool.AppendCertsFromPEM(creds.CA)
	tlsConfig.RootCAs = certPool
	keypair, err := tls.X509KeyPair(creds.Cert, creds.Key)
	if err != nil {
		return &tlsConfig, err
	}
	tlsConfig.Certificates = []tls.Certificate{keypair}

	return &tlsConfig, nil
}

func fetchZip(client *http.Client, zipurl string) (*zip.Reader, error) {
	req, err := http.NewRequest("GET", zipurl, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", userAgent)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		b, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return nil, errors.New(resp.Status)
		}
		return nil, errors.New(string(b))
	}

	buf := &bytes.Buffer{}

	_, err = io.Copy(buf, resp.Body)
	if err != nil {
		return nil, err
	}

	b := bytes.NewReader(buf.Bytes())
	return zip.NewReader(b, int64(b.Len()))
}

// Grow increases a cluster by the provided number of nodes
func (c *ClusterClient) Grow(clusterName string, nodes int) (*Cluster, error) {
	incr := map[string]int{
		"nodes": nodes,
	}

	growthRequest, err := json.Marshal(incr)
	if err != nil {
		return nil, err
	}
	r := bytes.NewReader(growthRequest)

	uri := path.Join("/clusters", c.Username, clusterName, "grow")
	resp, err := c.NewRequest("POST", uri, r)
	return clusterFromResponse(resp, err)
}

// SetAutoScale enables or disables autoscale on an already running cluster
func (c *ClusterClient) SetAutoScale(clusterName string, autoscale bool) (*Cluster, error) {
	setAutoscale := "false"
	if autoscale {
		setAutoscale = "true"
	}
	uri := path.Join("/clusters", c.Username, clusterName, "autoscale", setAutoscale)
	resp, err := c.NewRequest("PUT", uri, nil)
	return clusterFromResponse(resp, err)
}

const rebuildSwarmAction = "rebuild-swarm"

type actionRequest struct {
	Action string `json:"action"`
}

func (c *ClusterClient) doAction(clusterName, action string) (*Cluster, error) {
	actionReq, err := json.Marshal(actionRequest{Action: action})
	if err != nil {
		return nil, err
	}
	r := bytes.NewReader(actionReq)
	uri := path.Join("/clusters", c.Username, clusterName, "action")
	resp, err := c.NewRequest("POST", uri, r)
	return clusterFromResponse(resp, err)
}

// Rebuild creates a wholly new Swarm cluster
func (c *ClusterClient) Rebuild(clusterName string) (*Cluster, error) {
	return c.doAction(clusterName, rebuildSwarmAction)
}

// Delete nukes a cluster out of existence
func (c *ClusterClient) Delete(clusterName string) (*Cluster, error) {
	uri := path.Join("/clusters", c.Username, clusterName)
	resp, err := c.NewRequest("DELETE", uri, nil)
	return clusterFromResponse(resp, err)
}

func quotasFromResponse(resp *http.Response) (*Quotas, error) {
	quotas := new(Quotas)
	err := json.NewDecoder(resp.Body).Decode(&quotas)
	if err != nil {
		return nil, err
	}
	return quotas, nil
}

func (c *ClusterClient) GetQuotas() (*Quotas, error) {
	uri := path.Join("/quotas", c.Username)
	resp, err := c.NewRequest("GET", uri, nil)
	if err != nil {
		return nil, err
	}
	return quotasFromResponse(resp)
}
<|endoftext|>


<commit_before>package libfs

import (
	"github.com/keybase/client/go/logger"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

func FilterTLFEarlyExitError(ctx context.Context, err error, log logger.Logger, name libkbfs.CanonicalTlfName) (
	exitEarly bool, retErr error) {
	switch err := err.(type) {
	case nil:
		// No error.
		return false, nil

	case libkbfs.WriteAccessError:
		// No permission to create TLF, so pretend it's still
		// empty.
		//
		// In theory, we need to invalidate this once the TLF
		// is created, but in practice, the Linux kernel
		// doesn't cache readdir results, and probably not
		// OSXFUSE either.
		log.CDebugf(ctx,
			"No permission to write to %s, so pretending it's empty",
			name)
		return true, nil

	case libkbfs.MDServerErrorWriteAccess:
		// Same as above; cannot fallthrough in type switch
		log.CDebugf(ctx,
			"No permission to write to %s, so pretending it's empty",
			name)
		return true, nil

	default:
		// Some other error.
		return true, err
	}
}
<commit_msg>libfs: fix lint<commit_after>package libfs

import (
	"github.com/keybase/client/go/logger"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

// FilterTLFEarlyExitError decides whether an error received while
// trying to create a TLF should result in showing the user an empty
// folder (exitEarly == true), or not.
func FilterTLFEarlyExitError(ctx context.Context, err error, log logger.Logger, name libkbfs.CanonicalTlfName) (
	exitEarly bool, retErr error) {
	switch err := err.(type) {
	case nil:
		// No error.
		return false, nil

	case libkbfs.WriteAccessError:
		// No permission to create TLF, so pretend it's still
		// empty.
		//
		// In theory, we need to invalidate this once the TLF
		// is created, but in practice, the Linux kernel
		// doesn't cache readdir results, and probably not
		// OSXFUSE either.
		log.CDebugf(ctx,
			"No permission to write to %s, so pretending it's empty",
			name)
		return true, nil

	case libkbfs.MDServerErrorWriteAccess:
		// Same as above; cannot fallthrough in type switch
		log.CDebugf(ctx,
			"No permission to write to %s, so pretending it's empty",
			name)
		return true, nil

	default:
		// Some other error.
		return true, err
	}
}
<|endoftext|>


<commit_before>package cc_messages

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/models"
)

type HealthCheckType string

const UnspecifiedHealthCheckType HealthCheckType = "" // backwards-compatibility
const PortHealthCheckType HealthCheckType = "port"
const NoneHealthCheckType HealthCheckType = "none"

const CC_HTTP_ROUTES = "http_routes"

const CC_TCP_ROUTES = "tcp_routes"

const (
	TaskStatePending   = "PENDING"
	TaskStateRunning   = "RUNNING"
	TaskStateCanceling = "CANCELING"
	TaskStateSucceeded = "SUCCEEDED"
)

type DesireAppRequestFromCC struct {
	ProcessGuid                 string                        `json:"process_guid"`
	DropletUri                  string                        `json:"droplet_uri"`
	DockerImageUrl              string                        `json:"docker_image"`
	DockerLoginServer           string                        `json:"docker_login_server,omitempty"`
	DockerUser                  string                        `json:"docker_user,omitempty"`
	DockerPassword              string                        `json:"docker_password,omitempty"`
	DockerEmail                 string                        `json:"docker_email,omitempty"`
	Stack                       string                        `json:"stack"`
	StartCommand                string                        `json:"start_command"`
	ExecutionMetadata           string                        `json:"execution_metadata"`
	Environment                 []*models.EnvironmentVariable `json:"environment"`
	MemoryMB                    int                           `json:"memory_mb"`
	DiskMB                      int                           `json:"disk_mb"`
	FileDescriptors             uint64                        `json:"file_descriptors"`
	NumInstances                int                           `json:"num_instances"`
	RoutingInfo                 CCRouteInfo                   `json:"routing_info"`
	AllowSSH                    bool                          `json:"allow_ssh"`
	LogGuid                     string                        `json:"log_guid"`
	HealthCheckType             HealthCheckType               `json:"health_check_type"`
	HealthCheckTimeoutInSeconds uint                          `json:"health_check_timeout_in_seconds"`
	EgressRules                 []*models.SecurityGroupRule   `json:"egress_rules,omitempty"`
	ETag                        string                        `json:"etag"`
	Ports                       []uint32                      `json:"ports,omitempty"`
	LogSource                   string                        `json:"log_source,omitempty"`
	Network                     *models.Network               `json:"network,omitempty"`
}

type CCRouteInfo map[string]*json.RawMessage

type CCHTTPRoutes []CCHTTPRoute

func (r CCHTTPRoutes) CCRouteInfo() (CCRouteInfo, error) {
	routesJson, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	routesPayload := json.RawMessage(routesJson)
	routingInfo := make(map[string]*json.RawMessage)
	routingInfo[CC_HTTP_ROUTES] = &routesPayload
	return routingInfo, nil
}

type CCHTTPRoute struct {
	Hostname        string `json:"hostname"`
	RouteServiceUrl string `json:"route_service_url,omitempty"`
	Port            uint32 `json:"port,omitempty"`
}

type CCTCPRoutes []CCTCPRoute

type CCTCPRoute struct {
	RouterGroupGuid string `json:"router_group_guid"`
	ExternalPort    uint32 `json:"external_port,omitempty"`
	ContainerPort   uint32 `json:"container_port,omitempty"`
}

func (r CCTCPRoutes) CCRouteInfo() (CCRouteInfo, error) {
	routesJson, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	routesPayload := json.RawMessage(routesJson)
	routingInfo := make(map[string]*json.RawMessage)
	routingInfo[CC_TCP_ROUTES] = &routesPayload
	return routingInfo, nil
}

type CCDesiredStateServerResponse struct {
	Apps        []DesireAppRequestFromCC `json:"apps"`
	CCBulkToken *json.RawMessage         `json:"token"`
}

type CCDesiredAppFingerprint struct {
	ProcessGuid string `json:"process_guid"`
	ETag        string `json:"etag"`
}

type CCTaskState struct {
	TaskGuid              string `json:"task_guid"`
	State                 string `json:"state"`
	CompletionCallbackUrl string `json:"completion_callback"`
}

type CCDesiredStateFingerprintResponse struct {
	Fingerprints []CCDesiredAppFingerprint `json:"fingerprints"`
	CCBulkToken  *json.RawMessage          `json:"token"`
}

type CCTaskStatesResponse struct {
	TaskStates  []CCTaskState    `json:"task_states"`
	CCBulkToken *json.RawMessage `json:"token"`
}

type CCBulkToken struct {
	Id int `json:"id"`
}

type TaskErrorID string

type TaskRequestFromCC struct {
	TaskGuid              string                        `json:"task_guid"`
	LogGuid               string                        `json:"log_guid"`
	MemoryMb              int                           `json:"memory_mb"`
	DiskMb                int                           `json:"disk_mb"`
	Lifecycle             string                        `json:"lifecycle"`
	EnvironmentVariables  []*models.EnvironmentVariable `json:"environment"`
	EgressRules           []*models.SecurityGroupRule   `json:"egress_rules,omitempty"`
	DropletUri            string                        `json:"droplet_uri"`
	DockerPath            string                        `json:"docker_path"`
	RootFs                string                        `json:"rootfs"`
	CompletionCallbackUrl string                        `json:"completion_callback"`
	Command               string                        `json:"command"`
	LogSource             string                        `json:"log_source,omit_empty"`
}

type TaskFailResponseForCC struct {
	TaskGuid      string `json:"task_guid"`
	Failed        bool   `json:"failed"`
	FailureReason string `json:"failure_reason"`
}

type TaskError struct {
	Id      TaskErrorID `json:"id"`
	Message string      `json:"message"`
}
<commit_msg>Add VolumeMounts to cc desire app message<commit_after>package cc_messages

import (
	"encoding/json"

	"github.com/cloudfoundry-incubator/bbs/models"
)

type HealthCheckType string

const UnspecifiedHealthCheckType HealthCheckType = "" // backwards-compatibility
const PortHealthCheckType HealthCheckType = "port"
const NoneHealthCheckType HealthCheckType = "none"

const CC_HTTP_ROUTES = "http_routes"

const CC_TCP_ROUTES = "tcp_routes"

const (
	TaskStatePending   = "PENDING"
	TaskStateRunning   = "RUNNING"
	TaskStateCanceling = "CANCELING"
	TaskStateSucceeded = "SUCCEEDED"
)

type DesireAppRequestFromCC struct {
	ProcessGuid                 string                        `json:"process_guid"`
	DropletUri                  string                        `json:"droplet_uri"`
	DockerImageUrl              string                        `json:"docker_image"`
	DockerLoginServer           string                        `json:"docker_login_server,omitempty"`
	DockerUser                  string                        `json:"docker_user,omitempty"`
	DockerPassword              string                        `json:"docker_password,omitempty"`
	DockerEmail                 string                        `json:"docker_email,omitempty"`
	Stack                       string                        `json:"stack"`
	StartCommand                string                        `json:"start_command"`
	ExecutionMetadata           string                        `json:"execution_metadata"`
	Environment                 []*models.EnvironmentVariable `json:"environment"`
	MemoryMB                    int                           `json:"memory_mb"`
	DiskMB                      int                           `json:"disk_mb"`
	FileDescriptors             uint64                        `json:"file_descriptors"`
	NumInstances                int                           `json:"num_instances"`
	RoutingInfo                 CCRouteInfo                   `json:"routing_info"`
	AllowSSH                    bool                          `json:"allow_ssh"`
	LogGuid                     string                        `json:"log_guid"`
	HealthCheckType             HealthCheckType               `json:"health_check_type"`
	HealthCheckTimeoutInSeconds uint                          `json:"health_check_timeout_in_seconds"`
	EgressRules                 []*models.SecurityGroupRule   `json:"egress_rules,omitempty"`
	ETag                        string                        `json:"etag"`
	Ports                       []uint32                      `json:"ports,omitempty"`
	LogSource                   string                        `json:"log_source,omitempty"`
	Network                     *models.Network               `json:"network,omitempty"`
	VolumeMounts                []*models.VolumeMount         `json:"volume_mounts"`
}

type CCRouteInfo map[string]*json.RawMessage

type CCHTTPRoutes []CCHTTPRoute

func (r CCHTTPRoutes) CCRouteInfo() (CCRouteInfo, error) {
	routesJson, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	routesPayload := json.RawMessage(routesJson)
	routingInfo := make(map[string]*json.RawMessage)
	routingInfo[CC_HTTP_ROUTES] = &routesPayload
	return routingInfo, nil
}

type CCHTTPRoute struct {
	Hostname        string `json:"hostname"`
	RouteServiceUrl string `json:"route_service_url,omitempty"`
	Port            uint32 `json:"port,omitempty"`
}

type CCTCPRoutes []CCTCPRoute

type CCTCPRoute struct {
	RouterGroupGuid string `json:"router_group_guid"`
	ExternalPort    uint32 `json:"external_port,omitempty"`
	ContainerPort   uint32 `json:"container_port,omitempty"`
}

func (r CCTCPRoutes) CCRouteInfo() (CCRouteInfo, error) {
	routesJson, err := json.Marshal(r)
	if err != nil {
		return nil, err
	}

	routesPayload := json.RawMessage(routesJson)
	routingInfo := make(map[string]*json.RawMessage)
	routingInfo[CC_TCP_ROUTES] = &routesPayload
	return routingInfo, nil
}

type CCDesiredStateServerResponse struct {
	Apps        []DesireAppRequestFromCC `json:"apps"`
	CCBulkToken *json.RawMessage         `json:"token"`
}

type CCDesiredAppFingerprint struct {
	ProcessGuid string `json:"process_guid"`
	ETag        string `json:"etag"`
}

type CCTaskState struct {
	TaskGuid              string `json:"task_guid"`
	State                 string `json:"state"`
	CompletionCallbackUrl string `json:"completion_callback"`
}

type CCDesiredStateFingerprintResponse struct {
	Fingerprints []CCDesiredAppFingerprint `json:"fingerprints"`
	CCBulkToken  *json.RawMessage          `json:"token"`
}

type CCTaskStatesResponse struct {
	TaskStates  []CCTaskState    `json:"task_states"`
	CCBulkToken *json.RawMessage `json:"token"`
}

type CCBulkToken struct {
	Id int `json:"id"`
}

type TaskErrorID string

type TaskRequestFromCC struct {
	TaskGuid              string                        `json:"task_guid"`
	LogGuid               string                        `json:"log_guid"`
	MemoryMb              int                           `json:"memory_mb"`
	DiskMb                int                           `json:"disk_mb"`
	Lifecycle             string                        `json:"lifecycle"`
	EnvironmentVariables  []*models.EnvironmentVariable `json:"environment"`
	EgressRules           []*models.SecurityGroupRule   `json:"egress_rules,omitempty"`
	DropletUri            string                        `json:"droplet_uri"`
	DockerPath            string                        `json:"docker_path"`
	RootFs                string                        `json:"rootfs"`
	CompletionCallbackUrl string                        `json:"completion_callback"`
	Command               string                        `json:"command"`
	LogSource             string                        `json:"log_source,omit_empty"`
}

type TaskFailResponseForCC struct {
	TaskGuid      string `json:"task_guid"`
	Failed        bool   `json:"failed"`
	FailureReason string `json:"failure_reason"`
}

type TaskError struct {
	Id      TaskErrorID `json:"id"`
	Message string      `json:"message"`
}
<|endoftext|>


<commit_before>package chain_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"

	"github.com/codemodus/chain"
)

func Example() {
	// Nested handlers write either "0" or "1" to the response body before
	// and after ServeHTTP() is called.
	//
	// endHandler writes "_END_" to the response body.

	ch00 := chain.New(nestedHandler0, nestedHandler0)
	ch001 := ch00.Append(nestedHandler1)

	ch1 := chain.New(nestedHandler1)
	ch1001 := ch1.Merge(ch001)

	mux := http.NewServeMux()
	mux.Handle("/00_End", ch00.EndFn(endHandler))
	mux.Handle("/001_End", ch001.EndFn(endHandler))
	mux.Handle("/1001_End", ch1001.EndFn(endHandler))

	server := httptest.NewServer(mux)

	resp00, err := respBody(server.URL + "/00_End")
	if err != nil {
		fmt.Println(err)
	}

	resp001, err := respBody(server.URL + "/001_End")
	if err != nil {
		fmt.Println(err)
	}

	resp1001, err := respBody(server.URL + "/1001_End")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Chain 00 Resp:", resp00)
	fmt.Println("Chain 001 Resp:", resp001)
	fmt.Println("Chain 1001 Resp:", resp1001)

	// Output:
	// Chain 00 Resp: 00_END_00
	// Chain 001 Resp: 001_END_100
	// Chain 1001 Resp: 1001_END_1001
}

func respBody(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	_ = resp.Body.Close()

	return string(body), nil
}

func nestedHandler0(n http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(b0))
		n.ServeHTTP(w, r)
		_, _ = w.Write([]byte(b0))
	})
}

func nestedHandler1(n http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(b1))
		n.ServeHTTP(w, r)
		_, _ = w.Write([]byte(b1))
	})
}
<commit_msg>Rename example as advanced and add simpler package example.<commit_after>package chain_test

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"

	"github.com/codemodus/chain"
)

func Example() {
	// Nested handlers write either "0" or "1" to the response body before
	// and after ServeHTTP() is called.
	//
	// endHandler writes "_END_" to the response body.

	ch := chain.New(nestedHandler0, nestedHandler1)
	ch = ch.Append(nestedHandler0)

	mux := http.NewServeMux()
	mux.Handle("/010_End", ch.EndFn(endHandler))

	server := httptest.NewServer(mux)

	resp, err := respBody(server.URL + "/010_End")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Chain 010 Resp:", resp)

	// Output:
	// Chain 010 Resp: 010_END_010
}

func Example_advanced() {
	// Nested handlers write either "0" or "1" to the response body before
	// and after ServeHTTP() is called.
	//
	// endHandler writes "_END_" to the response body.

	ch00 := chain.New(nestedHandler0, nestedHandler0)
	ch001 := ch00.Append(nestedHandler1)

	ch1 := chain.New(nestedHandler1)
	ch1001 := ch1.Merge(ch001)

	mux := http.NewServeMux()
	mux.Handle("/00_End", ch00.EndFn(endHandler))
	mux.Handle("/001_End", ch001.EndFn(endHandler))
	mux.Handle("/1001_End", ch1001.EndFn(endHandler))

	server := httptest.NewServer(mux)

	resp00, err := respBody(server.URL + "/00_End")
	if err != nil {
		fmt.Println(err)
	}

	resp001, err := respBody(server.URL + "/001_End")
	if err != nil {
		fmt.Println(err)
	}

	resp1001, err := respBody(server.URL + "/1001_End")
	if err != nil {
		fmt.Println(err)
	}

	fmt.Println("Chain 00 Resp:", resp00)
	fmt.Println("Chain 001 Resp:", resp001)
	fmt.Println("Chain 1001 Resp:", resp1001)

	// Output:
	// Chain 00 Resp: 00_END_00
	// Chain 001 Resp: 001_END_100
	// Chain 1001 Resp: 1001_END_1001
}

func respBody(url string) (string, error) {
	resp, err := http.Get(url)
	if err != nil {
		return "", err
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	_ = resp.Body.Close()

	return string(body), nil
}

func nestedHandler0(n http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(b0))
		n.ServeHTTP(w, r)
		_, _ = w.Write([]byte(b0))
	})
}

func nestedHandler1(n http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		_, _ = w.Write([]byte(b1))
		n.ServeHTTP(w, r)
		_, _ = w.Write([]byte(b1))
	})
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
)

func contentEncodingHandler(handleFunc serviceHandleFunc) serviceHandleFunc {
	return func(w http.ResponseWriter, r *gitRequest) {
		var body io.ReadCloser
		var err error

		// The client request body may have been gzipped.
		contentEncoding := r.Header.Get("Content-Encoding")
		switch contentEncoding {
		case "":
			body = r.Body
		case "gzip":
			body, err = gzip.NewReader(r.Body)
			defer body.Close()
		default:
			err = fmt.Errorf("unsupported content encoding: %s", contentEncoding)
		}

		if err != nil {
			fail500(w, fmt.Errorf("contentEncodingHandler: %v", err))
			return
		}

		r.Body = body
		r.Header.Del("Content-Encoding")

		handleFunc(w, r)
	}
}
<commit_msg>That change does not belong here<commit_after>package main

import (
	"compress/gzip"
	"fmt"
	"io"
	"net/http"
)

func contentEncodingHandler(handleFunc serviceHandleFunc) serviceHandleFunc {
	return func(w http.ResponseWriter, r *gitRequest) {
		var body io.ReadCloser
		var err error

		// The client request body may have been gzipped.
		contentEncoding := r.Header.Get("Content-Encoding")
		switch contentEncoding {
		case "":
			body = r.Body
		case "gzip":
			body, err = gzip.NewReader(r.Body)
		default:
			err = fmt.Errorf("unsupported content encoding: %s", contentEncoding)
		}

		if err != nil {
			fail500(w, fmt.Errorf("contentEncodingHandler: %v", err))
			return
		}
		defer body.Close()

		r.Body = body
		r.Header.Del("Content-Encoding")

		handleFunc(w, r)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/zcalusic/restic-server/fs"
)

// Context contains repository meta-data.
type Context struct {
	path string
}

// AuthHandler wraps h with a http.HandlerFunc that performs basic authentication against the user/passwords pairs
// stored in f and returns the http.HandlerFunc.
func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		username, password, ok := r.BasicAuth()
		if !ok {
			http.Error(w, "401 unauthorized", 401)
			return
		}
		if !f.Validate(username, password) {
			http.Error(w, "401 unauthorized", 401)
			return
		}

		h.ServeHTTP(w, r)
	}
}

// CheckConfig returns a http.HandlerFunc that checks whether a configuration exists.
func CheckConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		st, err := os.Stat(config)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
	}
}

// GetConfig returns a http.HandlerFunc that allows for a config to be retrieved.
func GetConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		bytes, err := ioutil.ReadFile(config)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Write(bytes)
	}
}

// SaveConfig returns a http.HandlerFunc that allows for a config to be saved.
func SaveConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		bytes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "400 bad request", 400)
			return
		}
		errw := ioutil.WriteFile(config, bytes, 0600)
		if errw != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write([]byte("200 ok"))
	}
}

// ListBlobs returns a http.HandlerFunc that lists all blobs of a given type in an arbitrary order.
func ListBlobs(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		path := filepath.Join(c.path, dir)

		items, err := ioutil.ReadDir(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		var names []string
		for _, i := range items {
			if dir == "data" {
				subpath := filepath.Join(path, i.Name())
				subitems, err := ioutil.ReadDir(subpath)
				if err != nil {
					http.Error(w, "404 not found", 404)
					return
				}
				for _, f := range subitems {
					names = append(names, f.Name())
				}
			} else {
				names = append(names, i.Name())
			}
		}

		data, err := json.Marshal(names)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write(data)
	}
}

// CheckBlob returns a http.HandlerFunc that tests whether a blob exists and returns 200, if it does, or 404 otherwise.
func CheckBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		st, err := os.Stat(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
	}
}

// GetBlob returns a http.HandlerFunc that retrieves a blob from the repository.
func GetBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		file, err := fs.Open(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}
		defer file.Close()

		http.ServeContent(w, r, "", time.Unix(0, 0), file)
	}
}

// SaveBlob returns a http.HandlerFunc that saves a blob to the repository.
func SaveBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			os.MkdirAll(filepath.Join(c.path, dir, name[:2]), 0700)
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		tmp := path + ".tmp"
		tf, err := fs.OpenFile(tmp, os.O_CREATE|os.O_WRONLY, 0600)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}
		if _, err := io.Copy(tf, r.Body); err != nil {
			http.Error(w, "400 bad request", 400)
			tf.Close()
			os.Remove(tmp)
			return
		}
		if err := tf.Close(); err != nil {
			http.Error(w, "500 internal server error", 500)
		}
		if err := os.Rename(tmp, path); err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write([]byte("200 ok"))
	}
}

// DeleteBlob returns a http.HandlerFunc that deletes a blob from the repository.
func DeleteBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		err := os.Remove(path)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write([]byte("200 ok"))
	}
}
<commit_msg>Use temporary folder for temporary files<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/zcalusic/restic-server/fs"
)

// Context contains repository meta-data.
type Context struct {
	path string
}

// AuthHandler wraps h with a http.HandlerFunc that performs basic authentication against the user/passwords pairs
// stored in f and returns the http.HandlerFunc.
func AuthHandler(f *HtpasswdFile, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		username, password, ok := r.BasicAuth()
		if !ok {
			http.Error(w, "401 unauthorized", 401)
			return
		}
		if !f.Validate(username, password) {
			http.Error(w, "401 unauthorized", 401)
			return
		}

		h.ServeHTTP(w, r)
	}
}

// CheckConfig returns a http.HandlerFunc that checks whether a configuration exists.
func CheckConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		st, err := os.Stat(config)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
	}
}

// GetConfig returns a http.HandlerFunc that allows for a config to be retrieved.
func GetConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		bytes, err := ioutil.ReadFile(config)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Write(bytes)
	}
}

// SaveConfig returns a http.HandlerFunc that allows for a config to be saved.
func SaveConfig(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		config := filepath.Join(c.path, "config")
		bytes, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "400 bad request", 400)
			return
		}
		errw := ioutil.WriteFile(config, bytes, 0600)
		if errw != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write([]byte("200 ok"))
	}
}

// ListBlobs returns a http.HandlerFunc that lists all blobs of a given type in an arbitrary order.
func ListBlobs(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		path := filepath.Join(c.path, dir)

		items, err := ioutil.ReadDir(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		var names []string
		for _, i := range items {
			if dir == "data" {
				subpath := filepath.Join(path, i.Name())
				subitems, err := ioutil.ReadDir(subpath)
				if err != nil {
					http.Error(w, "404 not found", 404)
					return
				}
				for _, f := range subitems {
					names = append(names, f.Name())
				}
			} else {
				names = append(names, i.Name())
			}
		}

		data, err := json.Marshal(names)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write(data)
	}
}

// CheckBlob returns a http.HandlerFunc that tests whether a blob exists and returns 200, if it does, or 404 otherwise.
func CheckBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		st, err := os.Stat(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		w.Header().Add("Content-Length", fmt.Sprint(st.Size()))
	}
}

// GetBlob returns a http.HandlerFunc that retrieves a blob from the repository.
func GetBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		file, err := fs.Open(path)
		if err != nil {
			http.Error(w, "404 not found", 404)
			return
		}

		http.ServeContent(w, r, "", time.Unix(0, 0), file)
		file.Close()
	}
}

// SaveBlob returns a http.HandlerFunc that saves a blob to the repository.
func SaveBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		tmp := filepath.Join(c.path, "tmp", name)

		tf, err := fs.OpenFile(tmp, os.O_CREATE|os.O_WRONLY, 0600)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		if _, err := io.Copy(tf, r.Body); err != nil {
			http.Error(w, "400 bad request", 400)
			tf.Close()
			os.Remove(tmp)
			return
		}
		if err := tf.Close(); err != nil {
			http.Error(w, "500 internal server error", 500)
			os.Remove(tmp)
			return
		}

		if dir == "data" {
			os.MkdirAll(filepath.Join(c.path, dir, name[:2]), 0700)
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		if err := os.Rename(tmp, path); err != nil {
			http.Error(w, "500 internal server error", 500)
			os.Remove(tmp)
			return
		}

		w.Write([]byte("200 ok"))
	}
}

// DeleteBlob returns a http.HandlerFunc that deletes a blob from the repository.
func DeleteBlob(c *Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := strings.Split(r.RequestURI, "/")
		dir := vars[1]
		name := vars[2]

		if dir == "data" {
			name = filepath.Join(name[:2], name)
		}
		path := filepath.Join(c.path, dir, name)

		err := os.Remove(path)
		if err != nil {
			http.Error(w, "500 internal server error", 500)
			return
		}

		w.Write([]byte("200 ok"))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
	"github.com/jfrazelle/leeroy/github"
	"github.com/jfrazelle/leeroy/jenkins"
)

func pingHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "pong")
	return
}

func jenkinsHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var j jenkins.JenkinsResponse
	if err := decoder.Decode(&j); err != nil {
		log.Errorf("decoding the jenkins request as json failed: %v", err)
		return
	}

	log.Infof("Received Jenkins notification for %s %d (%s): %s", j.Name, j.Build.Number, j.Build.Url, j.Build.Phase)

	// if the phase is not started or completed
	// we don't care
	if j.Build.Phase != "STARTED" && j.Build.Phase != "COMPLETED" {
		return
	}

	// get the status for github
	// and create a status description
	desc := fmt.Sprintf("Jenkins build %s %d", j.Name, j.Build.Number)
	var state string
	if j.Build.Phase == "STARTED" {
		state = "pending"
		desc += " is running"
	} else {

		switch j.Build.Status {
		case "SUCCESS":
			state = "success"
			desc += " has succeeded"
		case "FAILURE":
			state = "failure"
			desc += " has failed"
		case "UNSTABLE":
			state = "failure"
			desc += " was unstable"
		case "ABORTED":
			state = "error"
			desc += " has encountered an error"
		default:
			log.Errorf("Did not understand %q build status. Aborting.", j.Build.Status)
			return
		}
	}
	// get the build
	build, err := config.getBuildByJob(j.Name)
	if err != nil {
		log.Error(err)
		return
	}

	// update the github status
	if err := config.updateGithubStatus(j.Build.Parameters.GitBaseRepo, build.Context, j.Build.Parameters.GitSha, state, desc, j.Build.Url+"console"); err != nil {
		log.Error(err)
	}

	return
}

func githubHandler(w http.ResponseWriter, r *http.Request) {
	event := r.Header.Get("X-GitHub-Event")

	switch event {
	case "":
		log.Error("Got GitHub notification without a type")
	case "ping":
		w.WriteHeader(200)
	case "issues", "issue_comment":
		handleIssue(w, r)
	case "pull_request":
		handlePullRequest(w, r)
	default:
		fmt.Errorf("Got unknown GitHub notification event type: %s", event)
	}
}

func handleIssue(w http.ResponseWriter, r *http.Request) {
	log.Debugf("Got an issue hook")

	// parse the issue
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("Error reading github issue handler body: %v", err)
		w.WriteHeader(500)
		return
	}

	issueHook, err := octokat.ParseIssueHook(body)
	if err != nil {
		log.Errorf("Error parsing issue hook: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	baseRepo := fmt.Sprintf("%s/%s", issueHook.Repo.Owner.Login, issueHook.Repo.Name)
	log.Debugf("Issue is for repo: %s", baseRepo)
	build, err := config.getBuildByContextAndRepo("janky", baseRepo)
	if err != nil {
		log.Warnf("could not find build for repo %s for issue handler, skipping: %v", baseRepo, err)
		return
	}

	// if we do not handle issues for this build just return
	if !build.HandleIssues {
		log.Warnf("Not configured to handle issues for %s", baseRepo)
		return
	}

	g := github.GitHub{
		AuthToken: config.GHToken,
		User:      config.GHUser,
	}

	log.Infof("Received GitHub issue notification for %s %d (%s): %s", baseRepo, issueHook.Issue.Number, issueHook.Issue.URL, issueHook.Action)

	// if it is not a comment or an opened issue
	// return becuase we dont care
	if !issueHook.IsComment() && !issueHook.IsOpened() {
		log.Debugf("Ignoring issue hook action %q", issueHook.Action)
		return
	}

	// if the issue has just been opened
	// parse if ENEEDMOREINFO
	if issueHook.IsOpened() {
		log.Debug("Issue is opened, checking if we have correct info")
		if err := g.IssueInfoCheck(issueHook); err != nil {
			log.Errorf("Error checking if issue opened needs more info: %v", err)
			w.WriteHeader(500)
			return
		}

		w.WriteHeader(200)
		return
	}

	// handle if it is an issue comment
	// apply approproate labels
	if err := g.LabelIssueComment(issueHook); err != nil {
		log.Errorf("Error applying labels to issue comment: %v", err)
		w.WriteHeader(500)
		return
	}

	return
}

func handlePullRequest(w http.ResponseWriter, r *http.Request) {
	log.Debugf("Got a pull request hook")

	// parse the pull request
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("Error reading github pull request handler body: %v", err)
		w.WriteHeader(500)
		return
	}

	prHook, err := octokat.ParsePullRequestHook(body)
	if err != nil {
		log.Errorf("Error parsing pull request hook: %v", err)
		w.WriteHeader(500)
		return
	}

	pr := prHook.PullRequest
	baseRepo := fmt.Sprintf("%s/%s", pr.Base.Repo.Owner.Login, pr.Base.Repo.Name)

	log.Infof("Received GitHub pull request notification for %s %d (%s): %s", baseRepo, pr.Number, pr.URL, prHook.Action)

	// ignore everything we don't care about
	if prHook.Action != "opened" && prHook.Action != "reopened" && prHook.Action != "synchronize" {
		log.Debugf("Ignoring PR hook action %q", prHook.Action)
		return
	}

	g := github.GitHub{
		AuthToken: config.GHToken,
		User:      config.GHUser,
	}
	valid, content, err := g.DcoVerified(prHook)

	if err != nil {
		log.Errorf("Error validating DCO: %v", err)
		w.WriteHeader(500)
		return
	}

	// DCO not valid, we don't start the build
	if !valid {
		log.Errorf("Invalid DCO for %s #%d. Aborting build", baseRepo, pr.Number)
		w.WriteHeader(200)
		return
	}

	mergeable, err := g.IsMergeable(prHook)

	if err != nil {
		log.Errorf("Error checking if PR is mergeable: %v", err)
		w.WriteHeader(500)
		return
	}

	// PR is not mergeable, so don't start the build
	if !mergeable {
		log.Errorf("Unmergeable PR for %s #%d. Aborting build", baseRepo, pr.Number)
		w.WriteHeader(200)
		return
	}

	if content.IsDocsOnly() {
		w.WriteHeader(200)
		return
	}

	// get the builds
	builds, err := config.getBuilds(baseRepo, false)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// schedule the jenkins builds
	for _, build := range builds {
		if err := config.scheduleJenkinsBuild(baseRepo, pr.Number, build); err != nil {
			log.Error(err)
			w.WriteHeader(500)
		}
	}

	return
}

type requestBuild struct {
	Number  int    `json:"number"`
	Repo    string `json:"repo"`
	Context string `json:"context"`
}

func customBuildHandler(w http.ResponseWriter, r *http.Request) {
	// setup auth
	user, pass, ok := r.BasicAuth()
	if !ok {
		w.WriteHeader(401)
		return
	}
	if user != config.User && pass != config.Pass {
		w.WriteHeader(401)
		return
	}

	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var b requestBuild
	if err := decoder.Decode(&b); err != nil {
		log.Errorf("decoding the retry request as json failed: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	build, err := config.getBuildByContextAndRepo(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// schedule the jenkins build
	if err := config.scheduleJenkinsBuild(b.Repo, b.Number, build); err != nil {
		w.WriteHeader(500)
		log.Error(err)
		return
	}

	w.WriteHeader(204)
	return
}

func cronBuildHandler(w http.ResponseWriter, r *http.Request) {
	// setup auth
	user, pass, ok := r.BasicAuth()
	if !ok {
		w.WriteHeader(401)
		return
	}
	if user != config.User && pass != config.Pass {
		w.WriteHeader(401)
		return
	}

	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var b requestBuild
	if err := decoder.Decode(&b); err != nil {
		log.Errorf("decoding the retry request as json failed: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	build, err := config.getBuildByContextAndRepo(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// get PRs that have failed for the context
	nums, err := config.getFailedPRs(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	for _, prNum := range nums {
		// schedule the jenkins build
		if err := config.scheduleJenkinsBuild(b.Repo, prNum, build); err != nil {
			log.Error(err)
		}
	}

	w.WriteHeader(204)
	return
}
<commit_msg>Content can be nil.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	log "github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
	"github.com/jfrazelle/leeroy/github"
	"github.com/jfrazelle/leeroy/jenkins"
)

func pingHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "pong")
	return
}

func jenkinsHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var j jenkins.JenkinsResponse
	if err := decoder.Decode(&j); err != nil {
		log.Errorf("decoding the jenkins request as json failed: %v", err)
		return
	}

	log.Infof("Received Jenkins notification for %s %d (%s): %s", j.Name, j.Build.Number, j.Build.Url, j.Build.Phase)

	// if the phase is not started or completed
	// we don't care
	if j.Build.Phase != "STARTED" && j.Build.Phase != "COMPLETED" {
		return
	}

	// get the status for github
	// and create a status description
	desc := fmt.Sprintf("Jenkins build %s %d", j.Name, j.Build.Number)
	var state string
	if j.Build.Phase == "STARTED" {
		state = "pending"
		desc += " is running"
	} else {

		switch j.Build.Status {
		case "SUCCESS":
			state = "success"
			desc += " has succeeded"
		case "FAILURE":
			state = "failure"
			desc += " has failed"
		case "UNSTABLE":
			state = "failure"
			desc += " was unstable"
		case "ABORTED":
			state = "error"
			desc += " has encountered an error"
		default:
			log.Errorf("Did not understand %q build status. Aborting.", j.Build.Status)
			return
		}
	}
	// get the build
	build, err := config.getBuildByJob(j.Name)
	if err != nil {
		log.Error(err)
		return
	}

	// update the github status
	if err := config.updateGithubStatus(j.Build.Parameters.GitBaseRepo, build.Context, j.Build.Parameters.GitSha, state, desc, j.Build.Url+"console"); err != nil {
		log.Error(err)
	}

	return
}

func githubHandler(w http.ResponseWriter, r *http.Request) {
	event := r.Header.Get("X-GitHub-Event")

	switch event {
	case "":
		log.Error("Got GitHub notification without a type")
	case "ping":
		w.WriteHeader(200)
	case "issues", "issue_comment":
		handleIssue(w, r)
	case "pull_request":
		handlePullRequest(w, r)
	default:
		fmt.Errorf("Got unknown GitHub notification event type: %s", event)
	}
}

func handleIssue(w http.ResponseWriter, r *http.Request) {
	log.Debugf("Got an issue hook")

	// parse the issue
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("Error reading github issue handler body: %v", err)
		w.WriteHeader(500)
		return
	}

	issueHook, err := octokat.ParseIssueHook(body)
	if err != nil {
		log.Errorf("Error parsing issue hook: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	baseRepo := fmt.Sprintf("%s/%s", issueHook.Repo.Owner.Login, issueHook.Repo.Name)
	log.Debugf("Issue is for repo: %s", baseRepo)
	build, err := config.getBuildByContextAndRepo("janky", baseRepo)
	if err != nil {
		log.Warnf("could not find build for repo %s for issue handler, skipping: %v", baseRepo, err)
		return
	}

	// if we do not handle issues for this build just return
	if !build.HandleIssues {
		log.Warnf("Not configured to handle issues for %s", baseRepo)
		return
	}

	g := github.GitHub{
		AuthToken: config.GHToken,
		User:      config.GHUser,
	}

	log.Infof("Received GitHub issue notification for %s %d (%s): %s", baseRepo, issueHook.Issue.Number, issueHook.Issue.URL, issueHook.Action)

	// if it is not a comment or an opened issue
	// return becuase we dont care
	if !issueHook.IsComment() && !issueHook.IsOpened() {
		log.Debugf("Ignoring issue hook action %q", issueHook.Action)
		return
	}

	// if the issue has just been opened
	// parse if ENEEDMOREINFO
	if issueHook.IsOpened() {
		log.Debug("Issue is opened, checking if we have correct info")
		if err := g.IssueInfoCheck(issueHook); err != nil {
			log.Errorf("Error checking if issue opened needs more info: %v", err)
			w.WriteHeader(500)
			return
		}

		w.WriteHeader(200)
		return
	}

	// handle if it is an issue comment
	// apply approproate labels
	if err := g.LabelIssueComment(issueHook); err != nil {
		log.Errorf("Error applying labels to issue comment: %v", err)
		w.WriteHeader(500)
		return
	}

	return
}

func handlePullRequest(w http.ResponseWriter, r *http.Request) {
	log.Debugf("Got a pull request hook")

	// parse the pull request
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Errorf("Error reading github pull request handler body: %v", err)
		w.WriteHeader(500)
		return
	}

	prHook, err := octokat.ParsePullRequestHook(body)
	if err != nil {
		log.Errorf("Error parsing pull request hook: %v", err)
		w.WriteHeader(500)
		return
	}

	pr := prHook.PullRequest
	baseRepo := fmt.Sprintf("%s/%s", pr.Base.Repo.Owner.Login, pr.Base.Repo.Name)

	log.Infof("Received GitHub pull request notification for %s %d (%s): %s", baseRepo, pr.Number, pr.URL, prHook.Action)

	// ignore everything we don't care about
	if prHook.Action != "opened" && prHook.Action != "reopened" && prHook.Action != "synchronize" {
		log.Debugf("Ignoring PR hook action %q", prHook.Action)
		return
	}

	g := github.GitHub{
		AuthToken: config.GHToken,
		User:      config.GHUser,
	}
	valid, content, err := g.DcoVerified(prHook)

	if err != nil {
		log.Errorf("Error validating DCO: %v", err)
		w.WriteHeader(500)
		return
	}

	// DCO not valid, we don't start the build
	if !valid {
		log.Errorf("Invalid DCO for %s #%d. Aborting build", baseRepo, pr.Number)
		w.WriteHeader(200)
		return
	}

	mergeable, err := g.IsMergeable(prHook)

	if err != nil {
		log.Errorf("Error checking if PR is mergeable: %v", err)
		w.WriteHeader(500)
		return
	}

	// PR is not mergeable, so don't start the build
	if !mergeable {
		log.Errorf("Unmergeable PR for %s #%d. Aborting build", baseRepo, pr.Number)
		w.WriteHeader(200)
		return
	}

	if content != nil && content.IsDocsOnly() {
		w.WriteHeader(200)
		return
	}

	// get the builds
	builds, err := config.getBuilds(baseRepo, false)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// schedule the jenkins builds
	for _, build := range builds {
		if err := config.scheduleJenkinsBuild(baseRepo, pr.Number, build); err != nil {
			log.Error(err)
			w.WriteHeader(500)
		}
	}

	return
}

type requestBuild struct {
	Number  int    `json:"number"`
	Repo    string `json:"repo"`
	Context string `json:"context"`
}

func customBuildHandler(w http.ResponseWriter, r *http.Request) {
	// setup auth
	user, pass, ok := r.BasicAuth()
	if !ok {
		w.WriteHeader(401)
		return
	}
	if user != config.User && pass != config.Pass {
		w.WriteHeader(401)
		return
	}

	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var b requestBuild
	if err := decoder.Decode(&b); err != nil {
		log.Errorf("decoding the retry request as json failed: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	build, err := config.getBuildByContextAndRepo(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// schedule the jenkins build
	if err := config.scheduleJenkinsBuild(b.Repo, b.Number, build); err != nil {
		w.WriteHeader(500)
		log.Error(err)
		return
	}

	w.WriteHeader(204)
	return
}

func cronBuildHandler(w http.ResponseWriter, r *http.Request) {
	// setup auth
	user, pass, ok := r.BasicAuth()
	if !ok {
		w.WriteHeader(401)
		return
	}
	if user != config.User && pass != config.Pass {
		w.WriteHeader(401)
		return
	}

	if r.Method != "POST" {
		fmt.Errorf("%q is not a valid method", r.Method)
		w.WriteHeader(405)
		return
	}

	// decode the body
	decoder := json.NewDecoder(r.Body)
	var b requestBuild
	if err := decoder.Decode(&b); err != nil {
		log.Errorf("decoding the retry request as json failed: %v", err)
		w.WriteHeader(500)
		return
	}

	// get the build
	build, err := config.getBuildByContextAndRepo(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	// get PRs that have failed for the context
	nums, err := config.getFailedPRs(b.Context, b.Repo)
	if err != nil {
		log.Error(err)
		w.WriteHeader(500)
		return
	}

	for _, prNum := range nums {
		// schedule the jenkins build
		if err := config.scheduleJenkinsBuild(b.Repo, prNum, build); err != nil {
			log.Error(err)
		}
	}

	w.WriteHeader(204)
	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lakeformation"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsLakeFormationDataLakeSettings() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLakeFormationDataLakeSettingsPut,
		Update: resourceAwsLakeFormationDataLakeSettingsPut,
		Read:   resourceAwsLakeFormationDataLakeSettingsRead,
		Delete: resourceAwsLakeFormationDataLakeSettingsReset,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"catalog_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
				Computed: true,
			},
			"admins": {
				Type:     schema.TypeList,
				Required: true,
				MinItems: 0,
				MaxItems: 10,
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validation.NoZeroValues,
				},
			},
		},
	}
}

func resourceAwsLakeFormationDataLakeSettingsPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid)

	input := &lakeformation.PutDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
		DataLakeSettings: &lakeformation.DataLakeSettings{
			DataLakeAdmins: expandAdmins(d),
		},
	}

	_, err := conn.PutDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error updating DataLakeSettings: %s", err)
	}

	awsRegion := meta.(*AWSClient).region
	d.SetId(fmt.Sprintf("lakeformation:%s:%s", awsRegion, catalogId))
	d.Set("catalog_id", catalogId)

	return resourceAwsLakeFormationDataLakeSettingsRead(d, meta)
}

func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := d.Get("catalog_id").(string)

	input := &lakeformation.GetDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
	}

	out, err := conn.GetDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error reading DataLakeSettings: %s", err)
	}

	d.Set("catalog_id", catalogId)
	if err := d.Set("admins", flattenAdmins(out.DataLakeSettings.DataLakeAdmins)); err != nil {
		return fmt.Errorf("Error setting admins from DataLakeSettings: %s", err)
	}
	// TODO: Add CreateDatabaseDefaultPermissions and CreateTableDefaultPermissions

	return nil
}

func resourceAwsLakeFormationDataLakeSettingsReset(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := d.Get("catalog_id").(string)

	input := &lakeformation.PutDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
		DataLakeSettings: &lakeformation.DataLakeSettings{
			DataLakeAdmins: make([]*lakeformation.DataLakePrincipal, 0),
		},
	}

	_, err := conn.PutDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error reseting DataLakeSettings: %s", err)
	}

	return nil
}

func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) {
	if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok {
		catalogId = inputCatalogId.(string)
	} else {
		catalogId = accountId
	}
	return
}

func expandAdmins(d *schema.ResourceData) []*lakeformation.DataLakePrincipal {
	xs := d.Get("admins")
	ys := make([]*lakeformation.DataLakePrincipal, len(xs.([]interface{})))

	for i, x := range xs.([]interface{}) {
		ys[i] = &lakeformation.DataLakePrincipal{
			DataLakePrincipalIdentifier: aws.String(x.(string)),
		}
	}

	return ys
}

func flattenAdmins(xs []*lakeformation.DataLakePrincipal) []string {
	admins := make([]string, len(xs))
	for i, x := range xs {
		admins[i] = aws.StringValue(x.DataLakePrincipalIdentifier)
	}

	return admins
}
<commit_msg>Rename expand and flatten functions for admins list<commit_after>package aws

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/lakeformation"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceAwsLakeFormationDataLakeSettings() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsLakeFormationDataLakeSettingsPut,
		Update: resourceAwsLakeFormationDataLakeSettingsPut,
		Read:   resourceAwsLakeFormationDataLakeSettingsRead,
		Delete: resourceAwsLakeFormationDataLakeSettingsReset,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"catalog_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
				Computed: true,
			},
			"admins": {
				Type:     schema.TypeList,
				Required: true,
				MinItems: 0,
				MaxItems: 10,
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validation.NoZeroValues,
				},
			},
		},
	}
}

func resourceAwsLakeFormationDataLakeSettingsPut(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid)

	input := &lakeformation.PutDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
		DataLakeSettings: &lakeformation.DataLakeSettings{
			DataLakeAdmins: expandLakeFormationDataLakePrincipal(d),
		},
	}

	_, err := conn.PutDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error updating DataLakeSettings: %s", err)
	}

	awsRegion := meta.(*AWSClient).region
	d.SetId(fmt.Sprintf("lakeformation:%s:%s", awsRegion, catalogId))
	d.Set("catalog_id", catalogId)

	return resourceAwsLakeFormationDataLakeSettingsRead(d, meta)
}

func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := d.Get("catalog_id").(string)

	input := &lakeformation.GetDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
	}

	out, err := conn.GetDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error reading DataLakeSettings: %s", err)
	}

	d.Set("catalog_id", catalogId)
	if err := d.Set("admins", flattenLakeFormationDataLakePrincipal(out.DataLakeSettings.DataLakeAdmins)); err != nil {
		return fmt.Errorf("Error setting admins from DataLakeSettings: %s", err)
	}
	// TODO: Add CreateDatabaseDefaultPermissions and CreateTableDefaultPermissions

	return nil
}

func resourceAwsLakeFormationDataLakeSettingsReset(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).lakeformationconn
	catalogId := d.Get("catalog_id").(string)

	input := &lakeformation.PutDataLakeSettingsInput{
		CatalogId: aws.String(catalogId),
		DataLakeSettings: &lakeformation.DataLakeSettings{
			DataLakeAdmins: make([]*lakeformation.DataLakePrincipal, 0),
		},
	}

	_, err := conn.PutDataLakeSettings(input)
	if err != nil {
		return fmt.Errorf("Error reseting DataLakeSettings: %s", err)
	}

	return nil
}

func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) {
	if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok {
		catalogId = inputCatalogId.(string)
	} else {
		catalogId = accountId
	}
	return
}

func expandLakeFormationDataLakePrincipal(d *schema.ResourceData) []*lakeformation.DataLakePrincipal {
	xs := d.Get("admins")
	ys := make([]*lakeformation.DataLakePrincipal, len(xs.([]interface{})))

	for i, x := range xs.([]interface{}) {
		ys[i] = &lakeformation.DataLakePrincipal{
			DataLakePrincipalIdentifier: aws.String(x.(string)),
		}
	}

	return ys
}

func flattenLakeFormationDataLakePrincipal(xs []*lakeformation.DataLakePrincipal) []string {
	admins := make([]string, len(xs))
	for i, x := range xs {
		admins[i] = aws.StringValue(x.DataLakePrincipalIdentifier)
	}

	return admins
}
<|endoftext|>


<commit_before>package executor

import (
	log "github.com/sirupsen/logrus"
	pb "github.com/virtru/cork/protocol"
	"github.com/virtru/cork/server/definition"
	"github.com/virtru/cork/server/streamer"
)

func init() {
	RegisterHandler("export", ExportStepHandler)
	RegisterRunner("export", ExportStepRunnerFactory)
}

type ExportStepRunner struct {
	Params StepRunnerParams
}

func ExportStepRunnerFactory(params StepRunnerParams) (StepRunner, error) {
	runner := &ExportStepRunner{}

	err := runner.Initialize(params)
	if err != nil {
		return nil, err
	}
	return runner, nil
}

func (e *ExportStepRunner) Initialize(params StepRunnerParams) error {
	e.Params = params
	return nil
}

func (e *ExportStepRunner) Run() {
	export := e.Params.Args.Export

	fmt.Printf("E PARAMS: %+v\n", *e.Params.Args)

	exportEvent := pb.ExecuteOutputEvent{
		Type: "export",
		Body: &pb.ExecuteOutputEvent_Export{
			Export: &pb.ExportEvent{
				Name:  export.Name,
				Value: export.Value,
			},
		},
	}

	e.Params.Stream.Send(&exportEvent)

	e.Params.DoneChan <- true
	return
}

func (e *ExportStepRunner) HandleInput(bytes []byte) error {
	return nil
}

func (e *ExportStepRunner) HandleSignal(signal int32) error {
	return nil
}

// ExportStepHandler - Handles exporting variables from a stage execution
func ExportStepHandler(corkDir string, executor *StepsExecutor, stream streamer.StepStream, step *definition.Step) (map[string]string, error) {
	log.Debugf("Running export step %s", step.Name)
	args, err := step.Args.ResolveArgs(executor.Renderer)
	if err != nil {
		log.Debugf("Error resolving arguments: %v", err)
		return nil, err
	}

	err = stream.Send(&pb.ExecuteOutputEvent{
		Type: "export",
		Body: &pb.ExecuteOutputEvent_Export{
			Export: &pb.ExportEvent{
				Name:  args.Export.Name,
				Value: args.Export.Value,
			},
		},
	})

	if err != nil {
		return nil, err
	}

	return nil, nil
}
<commit_msg>Fix logging on export step<commit_after>package executor

import (
	log "github.com/sirupsen/logrus"
	pb "github.com/virtru/cork/protocol"
	"github.com/virtru/cork/server/definition"
	"github.com/virtru/cork/server/streamer"
)

func init() {
	RegisterHandler("export", ExportStepHandler)
	RegisterRunner("export", ExportStepRunnerFactory)
}

type ExportStepRunner struct {
	Params StepRunnerParams
}

func ExportStepRunnerFactory(params StepRunnerParams) (StepRunner, error) {
	log.Debugf("Creating ExportStepRunner with %+v", params)
	runner := &ExportStepRunner{}

	err := runner.Initialize(params)
	if err != nil {
		return nil, err
	}
	return runner, nil
}

func (e *ExportStepRunner) Initialize(params StepRunnerParams) error {
	e.Params = params
	return nil
}

func (e *ExportStepRunner) Run() {
	export := e.Params.Args.Export

	log.Debugf("Export params: %+v\n", *e.Params.Args)

	exportEvent := pb.ExecuteOutputEvent{
		Type: "export",
		Body: &pb.ExecuteOutputEvent_Export{
			Export: &pb.ExportEvent{
				Name:  export.Name,
				Value: export.Value,
			},
		},
	}

	e.Params.Stream.Send(&exportEvent)

	e.Params.DoneChan <- true
	return
}

func (e *ExportStepRunner) HandleInput(bytes []byte) error {
	return nil
}

func (e *ExportStepRunner) HandleSignal(signal int32) error {
	return nil
}

// ExportStepHandler - Handles exporting variables from a stage execution
func ExportStepHandler(corkDir string, executor *StepsExecutor, stream streamer.StepStream, step *definition.Step) (map[string]string, error) {
	log.Debugf("Running export step %s", step.Name)
	args, err := step.Args.ResolveArgs(executor.Renderer)
	if err != nil {
		log.Debugf("Error resolving arguments: %v", err)
		return nil, err
	}

	err = stream.Send(&pb.ExecuteOutputEvent{
		Type: "export",
		Body: &pb.ExecuteOutputEvent_Export{
			Export: &pb.ExportEvent{
				Name:  args.Export.Name,
				Value: args.Export.Value,
			},
		},
	})

	if err != nil {
		return nil, err
	}

	return nil, nil
}
<|endoftext|>


<commit_before>/*
 * jQuery File Upload Plugin GAE Go Example 3.0
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

package app

import (
	"appengine"
	"appengine/blobstore"
	"appengine/image"
	"appengine/taskqueue"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"
)

const (
	WEBSITE           = "http://blueimp.github.com/jQuery-File-Upload/"
	MIN_FILE_SIZE     = 1       // bytes
	MAX_FILE_SIZE     = 5000000 // bytes
	IMAGE_TYPES       = "image/(gif|p?jpeg|(x-)?png)"
	ACCEPT_FILE_TYPES = IMAGE_TYPES
	EXPIRATION_TIME   = 300 // seconds
	THUMBNAIL_PARAM   = "=s80"
)

var (
	imageTypes      = regexp.MustCompile(IMAGE_TYPES)
	acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
)

type FileInfo struct {
	Key          appengine.BlobKey `json:"-"`
	Url          string            `json:"url,omitempty"`
	ThumbnailUrl string            `json:"thumbnail_url,omitempty"`
	Name         string            `json:"name"`
	Type         string            `json:"type"`
	Size         int64             `json:"size"`
	Error        string            `json:"error,omitempty"`
	DeleteUrl    string            `json:"delete_url,omitempty"`
	DeleteType   string            `json:"delete_type,omitempty"`
}

func (fi *FileInfo) ValidateType() (valid bool) {
	if acceptFileTypes.MatchString(fi.Type) {
		return true
	}
	fi.Error = "Filetype not allowed"
	return false
}

func (fi *FileInfo) ValidateSize() (valid bool) {
	if fi.Size < MIN_FILE_SIZE {
		fi.Error = "File is too small"
	} else if fi.Size > MAX_FILE_SIZE {
		fi.Error = "File is too big"
	} else {
		return true
	}
	return false
}

func (fi *FileInfo) CreateUrls(r *http.Request, c appengine.Context) {
	u := &url.URL{
		Scheme: r.URL.Scheme,
		Host:   appengine.DefaultVersionHostname(c),
		Path:   "/",
	}
	uString := u.String()
	fi.Url = uString + escape(string(fi.Key)) + "/" +
		escape(string(fi.Name))
	fi.DeleteUrl = fi.Url + "?delete=true"
	fi.DeleteType = "DELETE"
	if imageTypes.MatchString(fi.Type) {
		servingUrl, err := image.ServingURL(
			c,
			fi.Key,
			&image.ServingURLOptions{
				Secure: strings.HasSuffix(u.Scheme, "s"),
				Size:   0,
				Crop:   false,
			},
		)
		check(err)
		fi.ThumbnailUrl = servingUrl.String() + THUMBNAIL_PARAM
	}
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func escape(s string) string {
	return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
}

func delayedDelete(c appengine.Context, fi *FileInfo) {
	if key := string(fi.Key); key != "" {
		task := &taskqueue.Task{
			Path:   "/" + escape(key) + "/-",
			Method: "DELETE",
			Delay:  time.Duration(EXPIRATION_TIME) * time.Second,
		}
		taskqueue.Add(c, task, "")
	}
}

func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
	fi = &FileInfo{
		Name: p.FileName(),
		Type: p.Header.Get("Content-Type"),
	}
	if !fi.ValidateType() {
		return
	}
	defer func() {
		if rec := recover(); rec != nil {
			log.Println(rec)
			fi.Error = rec.(error).Error()
		}
	}()
	lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
	context := appengine.NewContext(r)
	w, err := blobstore.Create(context, fi.Type)
	defer func() {
		w.Close()
		fi.Size = MAX_FILE_SIZE + 1 - lr.N
		fi.Key, err = w.Key()
		check(err)
		if !fi.ValidateSize() {
			err := blobstore.Delete(context, fi.Key)
			check(err)
			return
		}
		delayedDelete(context, fi)
		fi.CreateUrls(r, context)
	}()
	check(err)
	_, err = io.Copy(w, lr)
	return
}

func getFormValue(p *multipart.Part) string {
	var b bytes.Buffer
	io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
	return b.String()
}

func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
	fileInfos = make([]*FileInfo, 0)
	mr, err := r.MultipartReader()
	check(err)
	r.Form, err = url.ParseQuery(r.URL.RawQuery)
	check(err)
	part, err := mr.NextPart()
	for err == nil {
		if name := part.FormName(); name != "" {
			if part.FileName() != "" {
				fileInfos = append(fileInfos, handleUpload(r, part))
			} else {
				r.Form[name] = append(r.Form[name], getFormValue(part))
			}
		}
		part, err = mr.NextPart()
	}
	return
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		http.Redirect(w, r, WEBSITE, http.StatusFound)
		return
	}
	parts := strings.Split(r.URL.Path, "/")
	if len(parts) == 3 {
		if key := parts[1]; key != "" {
			blobKey := appengine.BlobKey(key)
			bi, err := blobstore.Stat(appengine.NewContext(r), blobKey)
			if err == nil {
				w.Header().Add(
					"Cache-Control",
					fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
				)
				if imageTypes.MatchString(bi.ContentType) {
					w.Header().Add("X-Content-Type-Options", "nosniff")
				} else {
					w.Header().Add("Content-Type", "application/octet-stream")
					w.Header().Add(
						"Content-Disposition:",
						fmt.Sprintf("attachment; filename=%s;", parts[2]),
					)
				}
				blobstore.Send(w, blobKey)
				return
			}
		}
	}
	http.Error(w, "404 Not Found", http.StatusNotFound)
}

func post(w http.ResponseWriter, r *http.Request) {
    result := make(map[string][]*FileInfo, 1)
    result["files"] = handleUploads(r)
	b, err := json.Marshal(result)
	check(err)
	if redirect := r.FormValue("redirect"); redirect != "" {
	    if strings.Contains(redirect, "%s") {
	        redirect = fmt.Sprintf(
    			redirect,
    			escape(string(b)),
    		)
	    }
		http.Redirect(w, r, redirect, http.StatusFound)
		return
	}
	w.Header().Set("Cache-Control", "no-cache")
	jsonType := "application/json"
	if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
		w.Header().Set("Content-Type", jsonType)
	}
	fmt.Fprintln(w, string(b))
}

func delete(w http.ResponseWriter, r *http.Request) {
	parts := strings.Split(r.URL.Path, "/")
	if len(parts) != 3 {
		return
	}
	if key := parts[1]; key != "" {
		c := appengine.NewContext(r)
		blobKey := appengine.BlobKey(key)
		err := blobstore.Delete(c, blobKey)
		check(err)
		err = image.DeleteServingURL(c, blobKey)
		check(err)
	}
}

func handle(w http.ResponseWriter, r *http.Request) {
	params, err := url.ParseQuery(r.URL.RawQuery)
	check(err)
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add(
		"Access-Control-Allow-Methods",
		"OPTIONS, HEAD, GET, POST, PUT, DELETE",
	)
	switch r.Method {
	case "OPTIONS":
	case "HEAD":
	case "GET":
		get(w, r)
	case "POST":
		if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
			delete(w, r)
		} else {
			post(w, r)
		}
	case "DELETE":
		delete(w, r)
	default:
		http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
	}
}

func init() {
	http.HandleFunc("/", handle)
}
<commit_msg>Allow Content-headers for cross domain requests.<commit_after>/*
 * jQuery File Upload Plugin GAE Go Example 3.0.1
 * https://github.com/blueimp/jQuery-File-Upload
 *
 * Copyright 2011, Sebastian Tschan
 * https://blueimp.net
 *
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/MIT
 */

package app

import (
	"appengine"
	"appengine/blobstore"
	"appengine/image"
	"appengine/taskqueue"
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"regexp"
	"strings"
	"time"
)

const (
	WEBSITE           = "http://blueimp.github.com/jQuery-File-Upload/"
	MIN_FILE_SIZE     = 1       // bytes
	MAX_FILE_SIZE     = 5000000 // bytes
	IMAGE_TYPES       = "image/(gif|p?jpeg|(x-)?png)"
	ACCEPT_FILE_TYPES = IMAGE_TYPES
	EXPIRATION_TIME   = 300 // seconds
	THUMBNAIL_PARAM   = "=s80"
)

var (
	imageTypes      = regexp.MustCompile(IMAGE_TYPES)
	acceptFileTypes = regexp.MustCompile(ACCEPT_FILE_TYPES)
)

type FileInfo struct {
	Key          appengine.BlobKey `json:"-"`
	Url          string            `json:"url,omitempty"`
	ThumbnailUrl string            `json:"thumbnail_url,omitempty"`
	Name         string            `json:"name"`
	Type         string            `json:"type"`
	Size         int64             `json:"size"`
	Error        string            `json:"error,omitempty"`
	DeleteUrl    string            `json:"delete_url,omitempty"`
	DeleteType   string            `json:"delete_type,omitempty"`
}

func (fi *FileInfo) ValidateType() (valid bool) {
	if acceptFileTypes.MatchString(fi.Type) {
		return true
	}
	fi.Error = "Filetype not allowed"
	return false
}

func (fi *FileInfo) ValidateSize() (valid bool) {
	if fi.Size < MIN_FILE_SIZE {
		fi.Error = "File is too small"
	} else if fi.Size > MAX_FILE_SIZE {
		fi.Error = "File is too big"
	} else {
		return true
	}
	return false
}

func (fi *FileInfo) CreateUrls(r *http.Request, c appengine.Context) {
	u := &url.URL{
		Scheme: r.URL.Scheme,
		Host:   appengine.DefaultVersionHostname(c),
		Path:   "/",
	}
	uString := u.String()
	fi.Url = uString + escape(string(fi.Key)) + "/" +
		escape(string(fi.Name))
	fi.DeleteUrl = fi.Url + "?delete=true"
	fi.DeleteType = "DELETE"
	if imageTypes.MatchString(fi.Type) {
		servingUrl, err := image.ServingURL(
			c,
			fi.Key,
			&image.ServingURLOptions{
				Secure: strings.HasSuffix(u.Scheme, "s"),
				Size:   0,
				Crop:   false,
			},
		)
		check(err)
		fi.ThumbnailUrl = servingUrl.String() + THUMBNAIL_PARAM
	}
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

func escape(s string) string {
	return strings.Replace(url.QueryEscape(s), "+", "%20", -1)
}

func delayedDelete(c appengine.Context, fi *FileInfo) {
	if key := string(fi.Key); key != "" {
		task := &taskqueue.Task{
			Path:   "/" + escape(key) + "/-",
			Method: "DELETE",
			Delay:  time.Duration(EXPIRATION_TIME) * time.Second,
		}
		taskqueue.Add(c, task, "")
	}
}

func handleUpload(r *http.Request, p *multipart.Part) (fi *FileInfo) {
	fi = &FileInfo{
		Name: p.FileName(),
		Type: p.Header.Get("Content-Type"),
	}
	if !fi.ValidateType() {
		return
	}
	defer func() {
		if rec := recover(); rec != nil {
			log.Println(rec)
			fi.Error = rec.(error).Error()
		}
	}()
	lr := &io.LimitedReader{R: p, N: MAX_FILE_SIZE + 1}
	context := appengine.NewContext(r)
	w, err := blobstore.Create(context, fi.Type)
	defer func() {
		w.Close()
		fi.Size = MAX_FILE_SIZE + 1 - lr.N
		fi.Key, err = w.Key()
		check(err)
		if !fi.ValidateSize() {
			err := blobstore.Delete(context, fi.Key)
			check(err)
			return
		}
		delayedDelete(context, fi)
		fi.CreateUrls(r, context)
	}()
	check(err)
	_, err = io.Copy(w, lr)
	return
}

func getFormValue(p *multipart.Part) string {
	var b bytes.Buffer
	io.CopyN(&b, p, int64(1<<20)) // Copy max: 1 MiB
	return b.String()
}

func handleUploads(r *http.Request) (fileInfos []*FileInfo) {
	fileInfos = make([]*FileInfo, 0)
	mr, err := r.MultipartReader()
	check(err)
	r.Form, err = url.ParseQuery(r.URL.RawQuery)
	check(err)
	part, err := mr.NextPart()
	for err == nil {
		if name := part.FormName(); name != "" {
			if part.FileName() != "" {
				fileInfos = append(fileInfos, handleUpload(r, part))
			} else {
				r.Form[name] = append(r.Form[name], getFormValue(part))
			}
		}
		part, err = mr.NextPart()
	}
	return
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		http.Redirect(w, r, WEBSITE, http.StatusFound)
		return
	}
	parts := strings.Split(r.URL.Path, "/")
	if len(parts) == 3 {
		if key := parts[1]; key != "" {
			blobKey := appengine.BlobKey(key)
			bi, err := blobstore.Stat(appengine.NewContext(r), blobKey)
			if err == nil {
				w.Header().Add(
					"Cache-Control",
					fmt.Sprintf("public,max-age=%d", EXPIRATION_TIME),
				)
				if imageTypes.MatchString(bi.ContentType) {
					w.Header().Add("X-Content-Type-Options", "nosniff")
				} else {
					w.Header().Add("Content-Type", "application/octet-stream")
					w.Header().Add(
						"Content-Disposition:",
						fmt.Sprintf("attachment; filename=%s;", parts[2]),
					)
				}
				blobstore.Send(w, blobKey)
				return
			}
		}
	}
	http.Error(w, "404 Not Found", http.StatusNotFound)
}

func post(w http.ResponseWriter, r *http.Request) {
    result := make(map[string][]*FileInfo, 1)
    result["files"] = handleUploads(r)
	b, err := json.Marshal(result)
	check(err)
	if redirect := r.FormValue("redirect"); redirect != "" {
	    if strings.Contains(redirect, "%s") {
	        redirect = fmt.Sprintf(
    			redirect,
    			escape(string(b)),
    		)
	    }
		http.Redirect(w, r, redirect, http.StatusFound)
		return
	}
	w.Header().Set("Cache-Control", "no-cache")
	jsonType := "application/json"
	if strings.Index(r.Header.Get("Accept"), jsonType) != -1 {
		w.Header().Set("Content-Type", jsonType)
	}
	fmt.Fprintln(w, string(b))
}

func delete(w http.ResponseWriter, r *http.Request) {
	parts := strings.Split(r.URL.Path, "/")
	if len(parts) != 3 {
		return
	}
	if key := parts[1]; key != "" {
		c := appengine.NewContext(r)
		blobKey := appengine.BlobKey(key)
		err := blobstore.Delete(c, blobKey)
		check(err)
		err = image.DeleteServingURL(c, blobKey)
		check(err)
	}
}

func handle(w http.ResponseWriter, r *http.Request) {
	params, err := url.ParseQuery(r.URL.RawQuery)
	check(err)
	w.Header().Add("Access-Control-Allow-Origin", "*")
	w.Header().Add(
		"Access-Control-Allow-Methods",
		"OPTIONS, HEAD, GET, POST, PUT, DELETE",
	)
	w.Header().Add(
		"Access-Control-Allow-Headers",
		"Content-Type, Content-Range, Content-Disposition",
	)
	switch r.Method {
	case "OPTIONS":
	case "HEAD":
	case "GET":
		get(w, r)
	case "POST":
		if len(params["_method"]) > 0 && params["_method"][0] == "DELETE" {
			delete(w, r)
		} else {
			post(w, r)
		}
	case "DELETE":
		delete(w, r)
	default:
		http.Error(w, "501 Not Implemented", http.StatusNotImplemented)
	}
}

func init() {
	http.HandleFunc("/", handle)
}
<|endoftext|>