<commit_before>package docker

import (
	"bufio"
	"bytes"
	"context"
	"github.com/ViBiOh/docker-deploy/jsonHttp"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"io"
	"log"
	"net/http"
	"os"
	"regexp"
	"strings"
)

const host = `DOCKER_HOST`
const version = `DOCKER_VERSION`
const configurationFile = `./users`

const carriageReturn = []byte(`\n`)[0]

var commaByte = []byte(`,`)
var listRequest = regexp.MustCompile(`/containers/?$`)
var containerRequest = regexp.MustCompile(`/containers/([^/]+)/?$`)
var startRequest = regexp.MustCompile(`/containers/([^/]+)/start`)
var stopRequest = regexp.MustCompile(`/containers/([^/]+)/stop`)
var restartRequest = regexp.MustCompile(`/containers/([^/]+)/restart`)
var logRequest = regexp.MustCompile(`/containers/([^/]+)/logs`)

type results struct {
	Results interface{} `json:"results"`
}

type user struct {
	username string
	password string
}

var docker *client.Client
var users map[string]*user

func handleError(w http.ResponseWriter, err error) {
	log.Print(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func readConfiguration(path string) map[string]*user {
	configFile, err := os.Open(path)
	defer configFile.Close()

	if err != nil {
		log.Print(err)
		return nil
	}

	users := make(map[string]*user)

	scanner := bufio.NewScanner(configFile)
	for scanner.Scan() {
		parts := bytes.Split(scanner.Bytes(), commaByte)
		user := user{string(parts[0]), string(parts[1])}

		users[strings.ToLower(user.username)] = &user
	}

	return users
}

func init() {
	users = readConfiguration(configurationFile)

	client, err := client.NewClient(os.Getenv(host), os.Getenv(version), nil, nil)
	if err != nil {
		log.Fatal(err)
	} else {
		docker = client
	}
}

func inspectContainer(w http.ResponseWriter, containerID []byte) {
	if container, err := docker.ContainerInspect(context.Background(), string(containerID)); err != nil {
		handleError(w, err)
	} else {
		jsonHttp.ResponseJSON(w, container)
	}
}

func startContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerStart(context.Background(), string(containerID), types.ContainerStartOptions{}); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func stopContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerStop(context.Background(), string(containerID), nil); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func restartContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerRestart(context.Background(), string(containerID), nil); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func logContainer(w http.ResponseWriter, containerID []byte) {
	logs, err := docker.ContainerLogs(context.Background(), string(containerID), types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: false})
	if err != nil {
		handleError(w, err)
		return
	}

	logReader := bufio.NewReader(logs)
	logLines := make([][]byte)
	logLine, err := logReader.ReadBytes(carriageReturn)
	for err != nil {
		logLines = append(logLines, logLine[8:])
		logLine, err = logReader.ReadBytes(carriageReturn)
	}

	w.Write(logLines)
}

func listContainers(w http.ResponseWriter) {
	if containers, err := docker.ContainerList(context.Background(), types.ContainerListOptions{}); err != nil {
		handleError(w, err)
	} else {
		jsonHttp.ResponseJSON(w, results{containers})
	}
}

func isAuthenticated(r *http.Request) bool {
	username, password, ok := r.BasicAuth()

	if ok {
		user, ok := users[strings.ToLower(username)]

		if ok && user.password == password {
			return true
		}
	}

	return false
}

func unauthorized(w http.ResponseWriter) {
	http.Error(w, `Authentication required`, http.StatusUnauthorized)
}

// Handler for Hello request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type, Authorization`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if listRequest.Match(urlPath) && r.Method == http.MethodGet {
		listContainers(w)
	} else if isAuthenticated(r) {
		if containerRequest.Match(urlPath) && r.Method == http.MethodGet {
			inspectContainer(w, containerRequest.FindSubmatch(urlPath)[1])
		} else if startRequest.Match(urlPath) && r.Method == http.MethodPost {
			startContainer(w, startRequest.FindSubmatch(urlPath)[1])
		} else if stopRequest.Match(urlPath) && r.Method == http.MethodPost {
			stopContainer(w, stopRequest.FindSubmatch(urlPath)[1])
		} else if restartRequest.Match(urlPath) && r.Method == http.MethodPost {
			restartContainer(w, restartRequest.FindSubmatch(urlPath)[1])
		} else if logRequest.Match(urlPath) && r.Method == http.MethodGet {
			logContainer(w, logRequest.FindSubmatch(urlPath)[1])
		}
	} else {
		unauthorized(w)
	}
}
<commit_msg>Update docker.go<commit_after>package docker

import (
	"bufio"
	"bytes"
	"context"
	"github.com/ViBiOh/docker-deploy/jsonHttp"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"log"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strings"
)

const host = `DOCKER_HOST`
const version = `DOCKER_VERSION`
const configurationFile = `./users`

var commaByte = []byte(`,`)
var splitLogs = regexp.MustCompile(`.(.*?\n)`)

var listRequest = regexp.MustCompile(`/containers/?$`)
var containerRequest = regexp.MustCompile(`/containers/([^/]+)/?$`)
var startRequest = regexp.MustCompile(`/containers/([^/]+)/start`)
var stopRequest = regexp.MustCompile(`/containers/([^/]+)/stop`)
var restartRequest = regexp.MustCompile(`/containers/([^/]+)/restart`)
var logRequest = regexp.MustCompile(`/containers/([^/]+)/logs`)

type results struct {
	Results interface{} `json:"results"`
}

type user struct {
	username string
	password string
}

var docker *client.Client
var users map[string]*user

func handleError(w http.ResponseWriter, err error) {
	log.Print(err)
	http.Error(w, err.Error(), http.StatusInternalServerError)
}

func readConfiguration(path string) map[string]*user {
	configFile, err := os.Open(path)
	defer configFile.Close()

	if err != nil {
		log.Print(err)
		return nil
	}

	users := make(map[string]*user)

	scanner := bufio.NewScanner(configFile)
	for scanner.Scan() {
		parts := bytes.Split(scanner.Bytes(), commaByte)
		user := user{string(parts[0]), string(parts[1])}

		users[strings.ToLower(user.username)] = &user
	}

	return users
}

func init() {
	users = readConfiguration(configurationFile)

	client, err := client.NewClient(os.Getenv(host), os.Getenv(version), nil, nil)
	if err != nil {
		log.Fatal(err)
	} else {
		docker = client
	}
}

func inspectContainer(w http.ResponseWriter, containerID []byte) {
	if container, err := docker.ContainerInspect(context.Background(), string(containerID)); err != nil {
		handleError(w, err)
	} else {
		jsonHttp.ResponseJSON(w, container)
	}
}

func startContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerStart(context.Background(), string(containerID), types.ContainerStartOptions{}); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func stopContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerStop(context.Background(), string(containerID), nil); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func restartContainer(w http.ResponseWriter, containerID []byte) {
	if err := docker.ContainerRestart(context.Background(), string(containerID), nil); err != nil {
		handleError(w, err)
	} else {
		w.Write(nil)
	}
}

func logContainer(w http.ResponseWriter, containerID []byte) {
	logs, err := docker.ContainerLogs(context.Background(), string(containerID), types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: false})
	if err != nil {
		handleError(w, err)
		return
	}

	defer logs.Close()
	logsLines, err := ioutil.ReadAll(logs)
	if err != nil {
		handleError(w, err)
	} else {
		matches := splitLogs.FindAllSubmatch(logsLine, -1)
		cleanLogs := make([]byte, 0, len(matches))
		for _, match := range matches {
			cleanLogs = append(cleanLogs, match[1])
		}

		w.Write(cleanLogs)
	}
}

func listContainers(w http.ResponseWriter) {
	if containers, err := docker.ContainerList(context.Background(), types.ContainerListOptions{}); err != nil {
		handleError(w, err)
	} else {
		jsonHttp.ResponseJSON(w, results{containers})
	}
}

func isAuthenticated(r *http.Request) bool {
	username, password, ok := r.BasicAuth()

	if ok {
		user, ok := users[strings.ToLower(username)]

		if ok && user.password == password {
			return true
		}
	}

	return false
}

func unauthorized(w http.ResponseWriter) {
	http.Error(w, `Authentication required`, http.StatusUnauthorized)
}

// Handler for Hello request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type, Authorization`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if listRequest.Match(urlPath) && r.Method == http.MethodGet {
		listContainers(w)
	} else if isAuthenticated(r) {
		if containerRequest.Match(urlPath) && r.Method == http.MethodGet {
			inspectContainer(w, containerRequest.FindSubmatch(urlPath)[1])
		} else if startRequest.Match(urlPath) && r.Method == http.MethodPost {
			startContainer(w, startRequest.FindSubmatch(urlPath)[1])
		} else if stopRequest.Match(urlPath) && r.Method == http.MethodPost {
			stopContainer(w, stopRequest.FindSubmatch(urlPath)[1])
		} else if restartRequest.Match(urlPath) && r.Method == http.MethodPost {
			restartContainer(w, restartRequest.FindSubmatch(urlPath)[1])
		} else if logRequest.Match(urlPath) && r.Method == http.MethodGet {
			logContainer(w, logRequest.FindSubmatch(urlPath)[1])
		}
	} else {
		unauthorized(w)
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strings"

	bosonFunc "github.com/boson-project/func"
)

// Pusher of images from local to remote registry.
type Pusher struct {
	// Verbose logging.
	Verbose bool
}

// NewPusher creates an instance of a docker-based image pusher.
func NewPusher() *Pusher {
	return &Pusher{}
}

// Push the image of the Function.
func (n *Pusher) Push(f bosonFunc.Function) (digest string, err error) {
	// Check for the docker binary explicitly so that we can return
	// an extra-friendly error message.
	_, err = exec.LookPath("docker")
	if err != nil {
		err = errors.New("please install 'docker'")
		return
	}

	if f.Image == "" {
		return "", errors.New("Function has no associated image.  Has it been built?")
	}

	// set up the command, specifying a sanitized project name and connecting
	// standard output and error.
	cmd := exec.Command("docker", "push", f.Image)

	// Capture the command output in the buffer
	var output bytes.Buffer

	// If verbose logging is enabled, echo chatty stdout.
	if n.Verbose {
		fmt.Println(cmd)
		cmd.Stdout = io.MultiWriter(&output, os.Stdout)
	} else {
		cmd.Stdout = &output
	}

	// Capture stderr for echoing on failure.
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Run the command, echoing captured stderr as well ass the cmd internal error.
	err = cmd.Run()
	if err != nil {
		// TODO: sanitize stderr from docker?
		err = fmt.Errorf("%v. %v", stderr.String(), err.Error())
	}

	digest = parseDigest(output.String())

	return
}

// parseDigest tries to parse the last line from the output, which holds the pushed image digest
// The last line should look like this:
// latest: digest: sha256:a278a91112d17f8bde6b5f802a3317c7c752cf88078dae6f4b5a0784deb81782 size: 2613
func parseDigest(output string) string {

	// get last line from the output
	lines := strings.Split(output, "\n")
	lastline := lines[len(lines)-2]

	// find the start index of the "digest" section
	shaIndex := strings.Index(lastline, "sha256")
	if shaIndex == -1 {
		return ""
	}
	subStr := lastline[shaIndex:]

	// find the end index of the "digest" section
	endIndex := strings.Index(subStr, " ")
	if endIndex == -1 {
		return ""
	}
	return subStr[:endIndex]
}
<commit_msg>Improve digest parsing<commit_after>package docker

import (
	"bytes"
	"errors"
	"fmt"
	bosonFunc "github.com/boson-project/func"
	"io"
	"os"
	"os/exec"
	"regexp"
)

// Pusher of images from local to remote registry.
type Pusher struct {
	// Verbose logging.
	Verbose bool
}

// NewPusher creates an instance of a docker-based image pusher.
func NewPusher() *Pusher {
	return &Pusher{}
}

// Push the image of the Function.
func (n *Pusher) Push(f bosonFunc.Function) (digest string, err error) {
	// Check for the docker binary explicitly so that we can return
	// an extra-friendly error message.
	_, err = exec.LookPath("docker")
	if err != nil {
		err = errors.New("please install 'docker'")
		return
	}

	if f.Image == "" {
		return "", errors.New("Function has no associated image.  Has it been built?")
	}

	// set up the command, specifying a sanitized project name and connecting
	// standard output and error.
	cmd := exec.Command("docker", "push", f.Image)

	// Capture the command output in the buffer
	var output bytes.Buffer

	// If verbose logging is enabled, echo chatty stdout.
	if n.Verbose {
		fmt.Println(cmd)
		cmd.Stdout = io.MultiWriter(&output, os.Stdout)
	} else {
		cmd.Stdout = &output
	}

	// Capture stderr for echoing on failure.
	var stderr bytes.Buffer
	cmd.Stderr = &stderr

	// Run the command, echoing captured stderr as well ass the cmd internal error.
	err = cmd.Run()
	if err != nil {
		// TODO: sanitize stderr from docker?
		err = fmt.Errorf("%v. %v", stderr.String(), err.Error())
	}

	digest = parseDigest(output.String())

	return
}

var digestRE = regexp.MustCompile(`digest:\s+sha256:(?P<Digest>\w{64})`)

// parseDigest tries to parse the last line from the output, which holds the pushed image digest
// The output should contain line like this:
// latest: digest: sha256:a278a91112d17f8bde6b5f802a3317c7c752cf88078dae6f4b5a0784deb81782 size: 2613
func parseDigest(output string) string {
	match := digestRE.FindStringSubmatch(output)
	if len(match) >= 2 {
		return match[1]
	}
	return ""
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

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

	"strings"

	"k8s.io/api/admission/v1beta1"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1beta2"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
	"k8s.io/klog"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	recommendationProvider RecommendationProvider
	podPreProcessor        PodPreProcessor
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(recommendationProvider RecommendationProvider, podPreProcessor PodPreProcessor) *AdmissionServer {
	return &AdmissionServer{recommendationProvider, podPreProcessor}
}

type patchRecord struct {
	Op    string      `json:"op,inline"`
	Path  string      `json:"path,inline"`
	Value interface{} `json:"value"`
}

func (s *AdmissionServer) getPatchesForPodResourceRequest(raw []byte, namespace string) ([]patchRecord, error) {
	pod := v1.Pod{}
	if err := json.Unmarshal(raw, &pod); err != nil {
		return nil, err
	}
	if len(pod.Name) == 0 {
		pod.Name = pod.GenerateName + "%"
		pod.Namespace = namespace
	}
	klog.V(4).Infof("Admitting pod %v", pod.ObjectMeta)
	containersResources, annotationsPerContainer, vpaName, err := s.recommendationProvider.GetContainersResourcesForPod(&pod)
	if err != nil {
		return nil, err
	}
	pod, err = s.podPreProcessor.Process(pod)
	if err != nil {
		return nil, err
	}
	if annotationsPerContainer == nil {
		annotationsPerContainer = vpa_api_util.ContainerToAnnotationsMap{}
	}
	patches := []patchRecord{}
	updatesAnnotation := []string{}
	for i, containerResources := range containersResources {

		// Add resources empty object if missing
		if pod.Spec.Containers[i].Resources.Limits == nil &&
			pod.Spec.Containers[i].Resources.Requests == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources", i),
				Value: v1.ResourceRequirements{},
			})
		}

		// Add request empty map if missing
		if pod.Spec.Containers[i].Resources.Requests == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources/requests", i),
				Value: v1.ResourceList{}})
		}

		annotations, found := annotationsPerContainer[pod.Spec.Containers[i].Name]
		if !found {
			annotations = make([]string, 0)
		}
		for resource, request := range containerResources.Requests {
			// Set request
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources/requests/%s", i, resource),
				Value: request.String()})
			annotations = append(annotations, fmt.Sprintf("%s request", resource))
		}

		updatesAnnotation = append(updatesAnnotation, fmt.Sprintf("container %d: ", i)+strings.Join(annotations, ", "))
	}
	if len(updatesAnnotation) > 0 {
		vpaAnnotationValue := fmt.Sprintf("Pod resources updated by %s: ", vpaName) + strings.Join(updatesAnnotation, "; ")
		if pod.Annotations == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  "/metadata/annotations",
				Value: map[string]string{"vpaUpdates": vpaAnnotationValue}})
		} else {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  "/metadata/annotations/vpaUpdates",
				Value: vpaAnnotationValue})
		}
	}
	return patches, nil
}

func parseVPA(raw []byte) (*vpa_types.VerticalPodAutoscaler, error) {
	vpa := vpa_types.VerticalPodAutoscaler{}
	if err := json.Unmarshal(raw, &vpa); err != nil {
		return nil, err
	}
	return &vpa, nil
}

var (
	possibleUpdateModes = map[vpa_types.UpdateMode]interface{}{
		vpa_types.UpdateModeOff:      struct{}{},
		vpa_types.UpdateModeInitial:  struct{}{},
		vpa_types.UpdateModeRecreate: struct{}{},
		vpa_types.UpdateModeAuto:     struct{}{},
	}

	possibleScalingModes = map[vpa_types.ContainerScalingMode]interface{}{
		vpa_types.ContainerScalingModeAuto: struct{}{},
		vpa_types.ContainerScalingModeOff:  struct{}{},
	}
)

func validateVPA(vpa *vpa_types.VerticalPodAutoscaler) error {
	if vpa.Spec.UpdatePolicy != nil {
		mode := vpa.Spec.UpdatePolicy.UpdateMode
		if mode == nil {
			return fmt.Errorf("UpdateMode is required if UpdatePolicy is used")
		}
		if _, found := possibleUpdateModes[*mode]; !found {
			return fmt.Errorf("unexpected UpdateMode value %s", *mode)
		}
	}

	if vpa.Spec.ResourcePolicy != nil {
		for _, policy := range vpa.Spec.ResourcePolicy.ContainerPolicies {
			if policy.ContainerName == "" {
				return fmt.Errorf("ContainerPolicies.ContainerName is required")
			}
			mode := policy.Mode
			if mode != nil {
				if _, found := possibleScalingModes[*mode]; !found {
					return fmt.Errorf("unexpected Mode value %s", *mode)
				}
			}
			for resource, min := range policy.MinAllowed {
				max, found := policy.MaxAllowed[resource]
				if found && max.Cmp(min) < 0 {
					return fmt.Errorf("max resource for %v is lower than min", resource)
				}
			}
		}
	}

	return nil
}

func getPatchesForVPADefaults(raw []byte) ([]patchRecord, error) {
	vpa, err := parseVPA(raw)
	if err != nil {
		return nil, err
	}

	err = validateVPA(vpa)
	if err != nil {
		return nil, err
	}

	klog.V(4).Infof("Processing vpa: %v", vpa)
	patches := []patchRecord{}
	if vpa.Spec.UpdatePolicy == nil {
		// Sets the default updatePolicy.
		defaultUpdateMode := vpa_types.UpdateModeAuto
		patches = append(patches, patchRecord{
			Op:    "add",
			Path:  "/spec/updatePolicy",
			Value: vpa_types.PodUpdatePolicy{UpdateMode: &defaultUpdateMode}})
	}
	return patches, nil
}

func (s *AdmissionServer) admit(data []byte) (*v1beta1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1beta1.AdmissionResponse{}
	response.Allowed = true

	ar := v1beta1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}
	// The externalAdmissionHookConfiguration registered via selfRegistration
	// asks the kube-apiserver only to send admission requests regarding pods & VPA objects.
	podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
	vpaResource := metav1.GroupVersionResource{Group: "autoscaling.k8s.io", Version: "v1beta1", Resource: "verticalpodautoscalers"}
	var patches []patchRecord
	var err error
	resource := metrics_admission.Unknown

	switch ar.Request.Resource {
	case podResource:
		patches, err = s.getPatchesForPodResourceRequest(ar.Request.Object.Raw, ar.Request.Namespace)
		resource = metrics_admission.Pod
	case vpaResource:
		patches, err = getPatchesForVPADefaults(ar.Request.Object.Raw)
		resource = metrics_admission.Vpa
		// we don't let in problematic VPA objects - late validation
		if err != nil {
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	default:
		patches, err = nil, fmt.Errorf("expected the resource to be %v or %v", podResource, vpaResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1beta1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1beta1.AdmissionReview{
		Response: reviewResponse,
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<commit_msg>Forbid creating VPA objects without targetRef.<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package logic

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

	"strings"

	"k8s.io/api/admission/v1beta1"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1beta2"
	metrics_admission "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/metrics/admission"
	vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
	"k8s.io/klog"
)

// AdmissionServer is an admission webhook server that modifies pod resources request based on VPA recommendation
type AdmissionServer struct {
	recommendationProvider RecommendationProvider
	podPreProcessor        PodPreProcessor
}

// NewAdmissionServer constructs new AdmissionServer
func NewAdmissionServer(recommendationProvider RecommendationProvider, podPreProcessor PodPreProcessor) *AdmissionServer {
	return &AdmissionServer{recommendationProvider, podPreProcessor}
}

type patchRecord struct {
	Op    string      `json:"op,inline"`
	Path  string      `json:"path,inline"`
	Value interface{} `json:"value"`
}

func (s *AdmissionServer) getPatchesForPodResourceRequest(raw []byte, namespace string) ([]patchRecord, error) {
	pod := v1.Pod{}
	if err := json.Unmarshal(raw, &pod); err != nil {
		return nil, err
	}
	if len(pod.Name) == 0 {
		pod.Name = pod.GenerateName + "%"
		pod.Namespace = namespace
	}
	klog.V(4).Infof("Admitting pod %v", pod.ObjectMeta)
	containersResources, annotationsPerContainer, vpaName, err := s.recommendationProvider.GetContainersResourcesForPod(&pod)
	if err != nil {
		return nil, err
	}
	pod, err = s.podPreProcessor.Process(pod)
	if err != nil {
		return nil, err
	}
	if annotationsPerContainer == nil {
		annotationsPerContainer = vpa_api_util.ContainerToAnnotationsMap{}
	}
	patches := []patchRecord{}
	updatesAnnotation := []string{}
	for i, containerResources := range containersResources {

		// Add resources empty object if missing
		if pod.Spec.Containers[i].Resources.Limits == nil &&
			pod.Spec.Containers[i].Resources.Requests == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources", i),
				Value: v1.ResourceRequirements{},
			})
		}

		// Add request empty map if missing
		if pod.Spec.Containers[i].Resources.Requests == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources/requests", i),
				Value: v1.ResourceList{}})
		}

		annotations, found := annotationsPerContainer[pod.Spec.Containers[i].Name]
		if !found {
			annotations = make([]string, 0)
		}
		for resource, request := range containerResources.Requests {
			// Set request
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  fmt.Sprintf("/spec/containers/%d/resources/requests/%s", i, resource),
				Value: request.String()})
			annotations = append(annotations, fmt.Sprintf("%s request", resource))
		}

		updatesAnnotation = append(updatesAnnotation, fmt.Sprintf("container %d: ", i)+strings.Join(annotations, ", "))
	}
	if len(updatesAnnotation) > 0 {
		vpaAnnotationValue := fmt.Sprintf("Pod resources updated by %s: ", vpaName) + strings.Join(updatesAnnotation, "; ")
		if pod.Annotations == nil {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  "/metadata/annotations",
				Value: map[string]string{"vpaUpdates": vpaAnnotationValue}})
		} else {
			patches = append(patches, patchRecord{
				Op:    "add",
				Path:  "/metadata/annotations/vpaUpdates",
				Value: vpaAnnotationValue})
		}
	}
	return patches, nil
}

func parseVPA(raw []byte) (*vpa_types.VerticalPodAutoscaler, error) {
	vpa := vpa_types.VerticalPodAutoscaler{}
	if err := json.Unmarshal(raw, &vpa); err != nil {
		return nil, err
	}
	return &vpa, nil
}

var (
	possibleUpdateModes = map[vpa_types.UpdateMode]interface{}{
		vpa_types.UpdateModeOff:      struct{}{},
		vpa_types.UpdateModeInitial:  struct{}{},
		vpa_types.UpdateModeRecreate: struct{}{},
		vpa_types.UpdateModeAuto:     struct{}{},
	}

	possibleScalingModes = map[vpa_types.ContainerScalingMode]interface{}{
		vpa_types.ContainerScalingModeAuto: struct{}{},
		vpa_types.ContainerScalingModeOff:  struct{}{},
	}
)

func validateVPA(vpa *vpa_types.VerticalPodAutoscaler, isCreate bool) error {
	if vpa.Spec.UpdatePolicy != nil {
		mode := vpa.Spec.UpdatePolicy.UpdateMode
		if mode == nil {
			return fmt.Errorf("UpdateMode is required if UpdatePolicy is used")
		}
		if _, found := possibleUpdateModes[*mode]; !found {
			return fmt.Errorf("unexpected UpdateMode value %s", *mode)
		}
	}

	if vpa.Spec.ResourcePolicy != nil {
		for _, policy := range vpa.Spec.ResourcePolicy.ContainerPolicies {
			if policy.ContainerName == "" {
				return fmt.Errorf("ContainerPolicies.ContainerName is required")
			}
			mode := policy.Mode
			if mode != nil {
				if _, found := possibleScalingModes[*mode]; !found {
					return fmt.Errorf("unexpected Mode value %s", *mode)
				}
			}
			for resource, min := range policy.MinAllowed {
				max, found := policy.MaxAllowed[resource]
				if found && max.Cmp(min) < 0 {
					return fmt.Errorf("max resource for %v is lower than min", resource)
				}
			}
		}
	}

	if isCreate && vpa.Spec.TargetRef == nil {
		return fmt.Errorf("TargetRef is required. If you're using v1beta1 version of the API, please migrate to v1beta2.")
	}

	return nil
}

func getPatchesForVPADefaults(raw []byte, isCreate bool) ([]patchRecord, error) {
	vpa, err := parseVPA(raw)
	if err != nil {
		return nil, err
	}

	err = validateVPA(vpa, isCreate)
	if err != nil {
		return nil, err
	}

	klog.V(4).Infof("Processing vpa: %v", vpa)
	patches := []patchRecord{}
	if vpa.Spec.UpdatePolicy == nil {
		// Sets the default updatePolicy.
		defaultUpdateMode := vpa_types.UpdateModeAuto
		patches = append(patches, patchRecord{
			Op:    "add",
			Path:  "/spec/updatePolicy",
			Value: vpa_types.PodUpdatePolicy{UpdateMode: &defaultUpdateMode}})
	}
	return patches, nil
}

func (s *AdmissionServer) admit(data []byte) (*v1beta1.AdmissionResponse, metrics_admission.AdmissionStatus, metrics_admission.AdmissionResource) {
	// we don't block the admission by default, even on unparsable JSON
	response := v1beta1.AdmissionResponse{}
	response.Allowed = true

	ar := v1beta1.AdmissionReview{}
	if err := json.Unmarshal(data, &ar); err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, metrics_admission.Unknown
	}
	// The externalAdmissionHookConfiguration registered via selfRegistration
	// asks the kube-apiserver only to send admission requests regarding pods & VPA objects.
	podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
	vpaResource := metav1.GroupVersionResource{Group: "autoscaling.k8s.io", Version: "v1beta1", Resource: "verticalpodautoscalers"}
	var patches []patchRecord
	var err error
	resource := metrics_admission.Unknown

	switch ar.Request.Resource {
	case podResource:
		patches, err = s.getPatchesForPodResourceRequest(ar.Request.Object.Raw, ar.Request.Namespace)
		resource = metrics_admission.Pod
	case vpaResource:
		patches, err = getPatchesForVPADefaults(ar.Request.Object.Raw, ar.Request.Operation == v1beta1.Create)
		resource = metrics_admission.Vpa
		// we don't let in problematic VPA objects - late validation
		if err != nil {
			status := metav1.Status{}
			status.Status = "Failure"
			status.Message = err.Error()
			response.Result = &status
			response.Allowed = false
		}
	default:
		patches, err = nil, fmt.Errorf("expected the resource to be %v or %v", podResource, vpaResource)
	}

	if err != nil {
		klog.Error(err)
		return &response, metrics_admission.Error, resource
	}

	if len(patches) > 0 {
		patch, err := json.Marshal(patches)
		if err != nil {
			klog.Errorf("Cannot marshal the patch %v: %v", patches, err)
			return &response, metrics_admission.Error, resource
		}
		patchType := v1beta1.PatchTypeJSONPatch
		response.PatchType = &patchType
		response.Patch = patch
		klog.V(4).Infof("Sending patches: %v", patches)
	}

	var status metrics_admission.AdmissionStatus
	if len(patches) > 0 {
		status = metrics_admission.Applied
	} else {
		status = metrics_admission.Skipped
	}
	if resource == metrics_admission.Pod {
		metrics_admission.OnAdmittedPod(status == metrics_admission.Applied)
	}

	return &response, status, resource
}

// Serve is a handler function of AdmissionServer
func (s *AdmissionServer) Serve(w http.ResponseWriter, r *http.Request) {
	timer := metrics_admission.NewAdmissionLatency()

	var body []byte
	if r.Body != nil {
		if data, err := ioutil.ReadAll(r.Body); err == nil {
			body = data
		}
	}

	// verify the content type is accurate
	contentType := r.Header.Get("Content-Type")
	if contentType != "application/json" {
		klog.Errorf("contentType=%s, expect application/json", contentType)
		timer.Observe(metrics_admission.Error, metrics_admission.Unknown)
		return
	}

	reviewResponse, status, resource := s.admit(body)
	ar := v1beta1.AdmissionReview{
		Response: reviewResponse,
	}

	resp, err := json.Marshal(ar)
	if err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	if _, err := w.Write(resp); err != nil {
		klog.Error(err)
		timer.Observe(metrics_admission.Error, resource)
		return
	}

	timer.Observe(status, resource)
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"io"
	"os"
	"strconv"
	"sync"
)

type Sender struct {
	beam.Sender
}

func NewSender(s beam.Sender) *Sender {
	return &Sender{s}
}

func (s *Sender) Install(eng *Engine) error {
	// FIXME: this doesn't exist yet.
	eng.RegisterCatchall(s.Handle)
	return nil
}

func (s *Sender) Handle(job *Job) Status {
	msg := data.Empty().Set("cmd", append([]string{job.Name}, job.Args...)...)
	peer, err := beam.SendConn(s, msg.Bytes())
	if err != nil {
		return job.Errorf("beamsend: %v", err)
	}
	defer peer.Close()
	var tasks sync.WaitGroup
	defer tasks.Wait()
	r := beam.NewRouter(nil)
	r.NewRoute().KeyStartsWith("cmd", "log", "stdout").HasAttachment().Handler(func(p []byte, stdout *os.File) error {
		tasks.Add(1)
		io.Copy(job.Stdout, stdout)
		tasks.Done()
		return nil
	})
	r.NewRoute().KeyStartsWith("cmd", "log", "stderr").HasAttachment().Handler(func(p []byte, stderr *os.File) error {
		tasks.Add(1)
		io.Copy(job.Stderr, stderr)
		tasks.Done()
		return nil
	})
	var status int
	r.NewRoute().KeyStartsWith("cmd", "status").Handler(func(p []byte, f *os.File) error {
		cmd := data.Message(p).Get("cmd")
		if len(cmd) != 3 {
			return fmt.Errorf("usage: %s <0-127>", cmd[0])
		}
		s, err := strconv.ParseUint(cmd[2], 10, 8)
		if err != nil {
			return fmt.Errorf("usage: %s <0-127>", cmd[0])
		}
		status = int(s)
		return nil

	})
	if _, err := beam.Copy(r, peer); err != nil {
		return job.Errorf("%v", err)
	}
	return Status(status)
}

type Receiver struct {
	*Engine
	peer beam.Receiver
}

func NewReceiver(peer beam.Receiver) *Receiver {
	return &Receiver{Engine: New(), peer: peer}
}

func (rcv *Receiver) Run() error {
	r := beam.NewRouter(nil)
	r.NewRoute().KeyExists("cmd").Handler(func(p []byte, f *os.File) error {
		// Use the attachment as a beam return channel
		peer, err := beam.FileConn(f)
		if err != nil {
			f.Close()
			return err
		}
		cmd := data.Message(p).Get("cmd")
		job := rcv.Engine.Job(cmd[0], cmd[1:]...)
		stdout, err := beam.SendPipe(peer, data.Empty().Set("cmd", "log", "stdout").Bytes())
		if err != nil {
			return err
		}
		job.Stdout.Add(stdout)
		stderr, err := beam.SendPipe(peer, data.Empty().Set("cmd", "log", "stderr").Bytes())
		if err != nil {
			return err
		}
		job.Stderr.Add(stderr)
		// ignore error because we pass the raw status
		job.Run()
		err = peer.Send(data.Empty().Set("cmd", "status", fmt.Sprintf("%d", job.status)).Bytes(), nil)
		if err != nil {
			return err
		}
		return nil
	})
	_, err := beam.Copy(r, rcv.peer)
	return err
}
<commit_msg>engine.Sender and engine.Receiver support stdin<commit_after>package engine

import (
	"fmt"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"io"
	"os"
	"strconv"
	"sync"
)

type Sender struct {
	beam.Sender
}

func NewSender(s beam.Sender) *Sender {
	return &Sender{s}
}

func (s *Sender) Install(eng *Engine) error {
	// FIXME: this doesn't exist yet.
	eng.RegisterCatchall(s.Handle)
	return nil
}

func (s *Sender) Handle(job *Job) Status {
	msg := data.Empty().Set("cmd", append([]string{job.Name}, job.Args...)...)
	peer, err := beam.SendConn(s, msg.Bytes())
	if err != nil {
		return job.Errorf("beamsend: %v", err)
	}
	defer peer.Close()
	var tasks sync.WaitGroup
	defer tasks.Wait()
	r := beam.NewRouter(nil)
	r.NewRoute().KeyStartsWith("cmd", "log", "stdout").HasAttachment().Handler(func(p []byte, stdout *os.File) error {
		tasks.Add(1)
		io.Copy(job.Stdout, stdout)
		tasks.Done()
		return nil
	})
	r.NewRoute().KeyStartsWith("cmd", "log", "stderr").HasAttachment().Handler(func(p []byte, stderr *os.File) error {
		tasks.Add(1)
		io.Copy(job.Stderr, stderr)
		tasks.Done()
		return nil
	})
	r.NewRoute().KeyStartsWith("cmd", "log", "stdin").HasAttachment().Handler(func(p []byte, stdin *os.File) error {
		tasks.Add(1)
		io.Copy(stdin, job.Stdin)
		tasks.Done()
		return nil
	})
	var status int
	r.NewRoute().KeyStartsWith("cmd", "status").Handler(func(p []byte, f *os.File) error {
		cmd := data.Message(p).Get("cmd")
		if len(cmd) != 3 {
			return fmt.Errorf("usage: %s <0-127>", cmd[0])
		}
		s, err := strconv.ParseUint(cmd[2], 10, 8)
		if err != nil {
			return fmt.Errorf("usage: %s <0-127>", cmd[0])
		}
		status = int(s)
		return nil

	})
	if _, err := beam.Copy(r, peer); err != nil {
		return job.Errorf("%v", err)
	}
	return Status(status)
}

type Receiver struct {
	*Engine
	peer beam.Receiver
}

func NewReceiver(peer beam.Receiver) *Receiver {
	return &Receiver{Engine: New(), peer: peer}
}

func (rcv *Receiver) Run() error {
	r := beam.NewRouter(nil)
	r.NewRoute().KeyExists("cmd").Handler(func(p []byte, f *os.File) error {
		// Use the attachment as a beam return channel
		peer, err := beam.FileConn(f)
		if err != nil {
			f.Close()
			return err
		}
		cmd := data.Message(p).Get("cmd")
		job := rcv.Engine.Job(cmd[0], cmd[1:]...)
		stdout, err := beam.SendPipe(peer, data.Empty().Set("cmd", "log", "stdout").Bytes())
		if err != nil {
			return err
		}
		job.Stdout.Add(stdout)
		stderr, err := beam.SendPipe(peer, data.Empty().Set("cmd", "log", "stderr").Bytes())
		if err != nil {
			return err
		}
		job.Stderr.Add(stderr)
		stdin, err := beam.SendPipe(peer, data.Empty().Set("cmd", "log", "stdin").Bytes())
		if err != nil {
			return err
		}
		job.Stdin.Add(stdin)
		// ignore error because we pass the raw status
		job.Run()
		err = peer.Send(data.Empty().Set("cmd", "status", fmt.Sprintf("%d", job.status)).Bytes(), nil)
		if err != nil {
			return err
		}
		return nil
	})
	_, err := beam.Copy(r, rcv.peer)
	return err
}
<|endoftext|>


<commit_before>package slack

import (
	"encoding/json"
	"errors"
)

// slack user type
type User struct {
	Id       string `json:"id"`
	Name     string `json:"name"`
	Deleted  bool   `json:"deleted"`
	Color    string `json:"color"`
	Profile  *ProfileInfo
	IsAdmin  bool `json:"is_admin"`
	IsOwner  bool `json:"is_owner"`
	Has2fa   bool `json:"has_2fa"`
	HasFiles bool `json:"has_files"`
}

// slack user profile type
type ProfileInfo struct {
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	RealName  string `json:"real_name"`
	Email     string `json:"email"`
	Skype     string `json:"skype"`
	Phone     string `json:"phone"`
	Image24   string `json:"image_24"`
	Image32   string `json:"image_32"`
	Image48   string `json:"image_48"`
	Image72   string `json:"image_72"`
	Image192  string `json:"image_192"`
}

// API users.list: Lists all users in a Slack team.
func (sl *Slack) UsersList() ([]*User, error) {
	uv := sl.urlValues()
	body, err := sl.GetRequest(usersListApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(UsersListAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.Members()
}

// response type of `users.list` api
type UsersListAPIResponse struct {
	BaseAPIResponse
	RawMembers json.RawMessage `json:"members"`
}

func (res *UsersListAPIResponse) Members() ([]*User, error) {
	var members []*User
	err := json.Unmarshal(res.RawMembers, &members)
	if err != nil {
		return nil, err
	}
	return members, nil
}

// FindUser returns a user object that satisfy conditions specified.
func (sl *Slack) FindUser(cb func(*User) bool) (*User, error) {
	members, err := sl.UsersList()
	if err != nil {
		return nil, err
	}
	for _, member := range members {
		if cb(member) {
			return member, nil
		}
	}
	return nil, errors.New("No such user.")
}

// response type of `users.info` api
type UsersInfoAPIResponse struct {
	BaseAPIResponse
	User *User `json:"user"`
}

// API users.info: Gets information about a user.
func (sl *Slack) UsersInfo(userId string) (*User, error) {
	uv := sl.urlValues()
	uv.Add("user", userId)

	body, err := sl.GetRequest(usersInfoApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(UsersInfoAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.User, nil
}
<commit_msg>add finduserbyname method<commit_after>package slack

import (
	"encoding/json"
	"errors"
)

// slack user type
type User struct {
	Id       string `json:"id"`
	Name     string `json:"name"`
	Deleted  bool   `json:"deleted"`
	Color    string `json:"color"`
	Profile  *ProfileInfo
	IsAdmin  bool `json:"is_admin"`
	IsOwner  bool `json:"is_owner"`
	Has2fa   bool `json:"has_2fa"`
	HasFiles bool `json:"has_files"`
}

// slack user profile type
type ProfileInfo struct {
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	RealName  string `json:"real_name"`
	Email     string `json:"email"`
	Skype     string `json:"skype"`
	Phone     string `json:"phone"`
	Image24   string `json:"image_24"`
	Image32   string `json:"image_32"`
	Image48   string `json:"image_48"`
	Image72   string `json:"image_72"`
	Image192  string `json:"image_192"`
}

// API users.list: Lists all users in a Slack team.
func (sl *Slack) UsersList() ([]*User, error) {
	uv := sl.urlValues()
	body, err := sl.GetRequest(usersListApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(UsersListAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.Members()
}

// response type of `users.list` api
type UsersListAPIResponse struct {
	BaseAPIResponse
	RawMembers json.RawMessage `json:"members"`
}

func (res *UsersListAPIResponse) Members() ([]*User, error) {
	var members []*User
	err := json.Unmarshal(res.RawMembers, &members)
	if err != nil {
		return nil, err
	}
	return members, nil
}

// FindUser returns a user object that satisfy conditions specified.
func (sl *Slack) FindUser(cb func(*User) bool) (*User, error) {
	members, err := sl.UsersList()
	if err != nil {
		return nil, err
	}
	for _, member := range members {
		if cb(member) {
			return member, nil
		}
	}
	return nil, errors.New("No such user.")
}

// FindUserByName returns a user object that matches name specified.
func (sl *Slack) FindUserByName(name string) (*User, error) {
	return sl.FindUser(func(user *User) bool {
		return user.Name == name
	})
}

// response type of `users.info` api
type UsersInfoAPIResponse struct {
	BaseAPIResponse
	User *User `json:"user"`
}

// API users.info: Gets information about a user.
func (sl *Slack) UsersInfo(userId string) (*User, error) {
	uv := sl.urlValues()
	uv.Add("user", userId)

	body, err := sl.GetRequest(usersInfoApiEndpoint, uv)
	if err != nil {
		return nil, err
	}
	res := new(UsersInfoAPIResponse)
	err = json.Unmarshal(body, res)
	if err != nil {
		return nil, err
	}
	if !res.Ok {
		return nil, errors.New(res.Error)
	}
	return res.User, nil
}
<|endoftext|>


<commit_before>package main

import (
	r "github.com/dancannon/gorethink"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	//"time"
	"errors"
	"log"
)

func RoutesUser(res render.Render, db *r.Session, s sessions.Session) {
	var person Person
	user, err := person.Session(db, s)
	if err != nil {
		res.HTML(500, "error", err)
		return
	}
	res.HTML(200, "user/index", user)
}

func CreateUser(res render.Render, db *r.Session, s sessions.Session, person Person) {
	if !EmailIsUnique(db, person) {
		res.JSON(422, map[string]interface{}{"error": "Email already in use."})
		return
	}
	user, err := person.Insert(db)
	if err != nil {
		log.Println(err)
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	s.Set("user", user.Id)
	res.JSON(200, user)
}

func ReadUser(params martini.Params, res render.Render, db *r.Session) {
	var person Person
	person.Id = params["id"]
	user, err := person.Get(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	res.JSON(200, user)
}

func ReadUsers(res render.Render, db *r.Session) {
	var person Person
	users, err := person.GetAll(db)
	if err != nil {
		res.JSON(500, err)
		return
	}
	res.JSON(200, users)
}

func EmailIsUnique(s *r.Session, person Person) (unique bool) {
	row, err := r.Table("users").Filter(func(user r.RqlTerm) r.RqlTerm {
		return user.Field("email").Eq(person.Email)
	}).RunRow(s)
	if err != nil || !row.IsNil() {
		return false
	}
	return true
}

func (person Person) Login(s *r.Session, password string) (Person, error) {
	row, err := r.Table("users").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("email").Eq(person.Email)
	}).RunRow(s)
	if err != nil {
		return person, err
	}
	if row.IsNil() {
		return person, errors.New("Nothing was found.")
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	if !CompareHash(person.Digest, password) {
		return person, errors.New("Wrong username or password.")
	}
	return person, nil
}

func (person Person) Get(s *r.Session) (Person, error) {
	row, err := r.Table("users").Get(person.Id).Merge(map[string]interface{}{"posts": r.Table("posts").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("author").Eq(person.Id)
	}).CoerceTo("ARRAY").Without("author")}).Without("digest", "email").RunRow(s)
	if err != nil {
		return person, err
	}
	if row.IsNil() {
		return person, errors.New("Nothing was found.")
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

func (person Person) Session(db *r.Session, s sessions.Session) (Person, error) {
	data := s.Get("user")
	id, exists := data.(string)
	if exists {
		var person Person
		person.Id = id
		person, err := person.Get(db)
		if err != nil {
			return person, err
		}
		return person, nil
	}
	return person, errors.New("Session could not be retrieved.")
}

func (person Person) Insert(s *r.Session) (Person, error) {
	person.Digest = GenerateHash(person.Password)
	person.Password = ""
	row, err := r.Table("users").Insert(person).RunRow(s)
	if err != nil {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

func (person Person) GetAll(s *r.Session) ([]Person, error) {
	var persons []Person
	rows, err := r.Table("users").Without("digest", "email").Run(s)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		err := rows.Scan(&person)
		person, err := person.Get(s)
		if err != nil {
			return nil, err
		}
		persons = append(persons, person)
	}
	return persons, nil
}
<commit_msg>users: add delete method and cleanup<commit_after>package main

import (
	r "github.com/dancannon/gorethink"
	"github.com/go-martini/martini"
	"github.com/martini-contrib/render"
	"github.com/martini-contrib/sessions"
	//"time"
	"errors"
	"log"
)

func CreateUser(res render.Render, db *r.Session, s sessions.Session, person Person) {
	if !EmailIsUnique(db, person) {
		res.JSON(422, map[string]interface{}{"error": "Email already in use"})
		return
	}
	user, err := person.Insert(db)
	if err != nil {
		log.Println(err)
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	s.Set("user", user.Id)
	res.JSON(200, user)
}

func DeleteUser(res render.Render, db *r.Session, s sessions.Session, person Person) {
	person, err := person.Login(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	err = person.Delete(db, s)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	res.JSON(200, map[string]interface{}{"status": "User successfully deleted"})
}

func ReadUser(params martini.Params, res render.Render, db *r.Session) {
	var person Person
	person.Id = params["id"]
	user, err := person.Get(db)
	if err != nil {
		res.JSON(500, map[string]interface{}{"error": "Internal server error"})
		return
	}
	res.JSON(200, user)
}

func ReadUsers(res render.Render, db *r.Session) {
	var person Person
	users, err := person.GetAll(db)
	if err != nil {
		res.JSON(500, err)
		return
	}
	res.JSON(200, users)
}

func EmailIsUnique(s *r.Session, person Person) (unique bool) {
	row, err := r.Table("users").Filter(func(user r.RqlTerm) r.RqlTerm {
		return user.Field("email").Eq(person.Email)
	}).RunRow(s)
	if err != nil || !row.IsNil() {
		return false
	}
	return true
}

func (person Person) Login(s *r.Session) (Person, error) {
	row, err := r.Table("users").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("email").Eq(person.Email)
	}).RunRow(s)
	if err != nil || row.IsNil() {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil || !CompareHash(person.Digest, person.Password) {
		return person, err
	}
	return person, nil
}

func (person Person) Get(s *r.Session) (Person, error) {
	row, err := r.Table("users").Get(person.Id).Merge(map[string]interface{}{"posts": r.Table("posts").Filter(func(post r.RqlTerm) r.RqlTerm {
		return post.Field("author").Eq(person.Id)
	}).CoerceTo("ARRAY").Without("author")}).Without("digest", "email").RunRow(s)
	if err != nil {
		return person, err
	}
	if row.IsNil() {
		return person, errors.New("Nothing was found.")
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

func (person Person) Session(db *r.Session, s sessions.Session) (Person, error) {
	data := s.Get("user")
	id, exists := data.(string)
	if exists {
		var person Person
		person.Id = id
		person, err := person.Get(db)
		if err != nil {
			return person, err
		}
		return person, nil
	}
	return person, errors.New("Session could not be retrieved.")
}

func (person Person) Delete(db *r.Session, s sessions.Session) error {
	person, err := person.Session(db, s)
	if err != nil {
		return err
	}
	_, err = r.Table("users").Get(person.Id).Delete().RunRow(db)
	if err != nil {
		return err
	}
	return nil
}

func (person Person) Insert(s *r.Session) (Person, error) {
	person.Digest = GenerateHash(person.Password)
	person.Password = ""
	row, err := r.Table("users").Insert(person).RunRow(s)
	if err != nil {
		return person, err
	}
	err = row.Scan(&person)
	if err != nil {
		return person, err
	}
	return person, err
}

func (person Person) GetAll(s *r.Session) ([]Person, error) {
	var persons []Person
	rows, err := r.Table("users").Without("digest", "email").Run(s)
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		err := rows.Scan(&person)
		person, err := person.Get(s)
		if err != nil {
			return nil, err
		}
		persons = append(persons, person)
	}
	return persons, nil
}
<|endoftext|>


<commit_before>package cart

type Next func()
//normal handler
type Handler func(*Context, Next)
type HandlerFinal func(*Context)
type HandlerCompose func(*Context, Next) Next

func makeCompose(handles ...Handler) HandlerCompose {
	composeHandles := []HandlerCompose{}
	for _, handle := range handles {
		innerHandle := handle
		tempHandle := func(c *Context, next Next) Next {
			return func() {
				innerHandle(c,next)
			}
		}
		composeHandles = append(composeHandles, tempHandle)
	}
	return compose(composeHandles...)
}

/*
compose HandlerCompose

	temp := 0
	A := func(c *Context, next Next) Next {
		return func() {
			temp = temp + 2;
			next()
		}
	}
	B := func(c *Context, next Next) Next {
		return func() {
			temp = temp * 2;
			next()
		}
	}
	composed := compose(A,B,B)(nil, func(){
		//this is the end the temp value is (0+2)*2*2
	})
	composed()
 */
func compose(functions ...HandlerCompose) HandlerCompose {
	if len(functions) == 0 {
		return nil
	}
	if len(functions) == 1 {
		return functions[0]
	}

	return func(c *Context, next Next) Next {
		last := functions[len(functions)-1]
		rest := functions[0:len(functions)-1]
		composed := last(c, next);
		for i, _ := range rest {
			composed = rest[len(rest)-1-i](c, composed)
		}
		return composed
	}

}<commit_msg>add test file<commit_after>package cart

type Next func()
//normal handler
type Handler func(*Context, Next)
type HandlerFinal func(*Context)
type HandlerCompose func(*Context, Next) Next

/*
transfer Handler to HandlerCompose func
 */
func makeCompose(handles ...Handler) HandlerCompose {
	composeHandles := []HandlerCompose{}
	for _, handle := range handles {
		innerHandle := handle
		tempHandle := func(c *Context, next Next) Next {
			return func() {
				innerHandle(c,next)
			}
		}
		composeHandles = append(composeHandles, tempHandle)
	}
	return compose(composeHandles...)
}

/*
compose HandlerCompose

	temp := 0
	A := func(c *Context, next Next) Next {
		return func() {
			temp = temp + 2;
			next()
		}
	}
	B := func(c *Context, next Next) Next {
		return func() {
			temp = temp * 2;
			next()
		}
	}
	composed := compose(A,B,B)(nil, func(){
		//this is the end the temp value is (0+2)*2*2
	})
	composed()
 */
func compose(functions ...HandlerCompose) HandlerCompose {
	if len(functions) == 0 {
		return nil
	}
	if len(functions) == 1 {
		return functions[0]
	}

	return func(c *Context, next Next) Next {
		last := functions[len(functions)-1]
		rest := functions[0:len(functions)-1]
		composed := last(c, next);
		for i, _ := range rest {
			composed = rest[len(rest)-1-i](c, composed)
		}
		return composed
	}

}<|endoftext|>


<commit_before>package flag

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"unicode"

	"github.com/cosiner/gohper/runtime2"
)

func isKindNumber(k reflect.Kind) bool {
	switch k {
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64,
		reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64,
		reflect.Float32,
		reflect.Float64:
		return true
	}
	return false
}

func isKindCompatible(k1, k2 reflect.Kind) bool {
	switch k1 {
	case reflect.Bool, reflect.String:
		return k2 == k1
	}
	return isKindNumber(k1) && isKindNumber(k2)
}

func sliceElemKind(val reflect.Value) reflect.Kind {
	k := val.Kind()
	if k == reflect.Slice {
		return val.Type().Elem().Kind()
	}
	return k
}

func isBoolPtr(ptr interface{}) bool {
	switch ptr.(type) {
	case *bool, *[]bool:
		return true
	}
	return false
}

func isSlicePtr(ptr interface{}) bool {
	refval := reflect.ValueOf(ptr)
	return refval.Kind() == reflect.Ptr && refval.Elem().Kind() == reflect.Slice
}

func parseDefault(val, valsep string, ptr interface{}) (interface{}, error) {
	if val == "" {
		return nil, nil
	}
	refval := reflect.ValueOf(ptr).Elem()
	switch refval.Kind() {
	case reflect.String:
		return val, nil
	case reflect.Bool:
		b, err := parseBool(val)
		return b, err
	default:
		if isKindNumber(refval.Kind()) {
			return strconv.ParseFloat(val, 64)
		}
	case reflect.Slice:
		vals := splitAndTrimSpace(val, valsep)
		switch k := sliceElemKind(refval); k {
		case reflect.String:
			return vals, nil
		case reflect.Bool:
			bs, err := convertToBools(vals)
			return bs, err
		default:
			if isKindNumber(k) {
				fs, err := convertToFloats(vals)
				return fs, err
			}
		}
	}
	return nil, fmt.Errorf("unsupported kind: %s", refval.Kind())
}

func convertNumberSlice(val interface{}) []float64 {
	var fs []float64
	switch vals := val.(type) {
	case []int:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int8:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int16:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int64:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint8:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint16:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint64:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []float32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []float64:
		fs = vals
	}
	return fs
}

func parseSelectsString(val, valsep string, ptr interface{}) (interface{}, error) {
	if val == "" {
		return nil, nil
	}

	refval := reflect.ValueOf(ptr).Elem()
	vals := splitAndTrimSpace(val, valsep)
	k := sliceElemKind(refval)
	switch {
	case k == reflect.String:
		return vals, nil
	case isKindNumber(k):
		return convertToFloats(vals)
	}
	return nil, fmt.Errorf("doesn't support select: %s", k.String())
}

func parseSelectsValue(ptr interface{}, val interface{}) (interface{}, error) {
	if val == nil {
		return nil, nil
	}

	refval := reflect.ValueOf(ptr).Elem()
	k := sliceElemKind(refval)
	if isKindNumber(k) {
		fs := convertNumberSlice(val)
		if len(fs) != 0 {
			return fs, nil
		}
	} else if k == reflect.String {
		if vals, ok := val.([]string); ok && len(vals) != 0 {
			return vals, nil
		}
	}
	return nil, errors.New("invalid selects")
}

func typeName(ptr interface{}) string {
	switch ptr.(type) {
	case *int:
		return "int"
	case *int8:
		return "int8"
	case *int16:
		return "int16"
	case *int32:
		return "int32"
	case *int64:
		return "int64"
	case *[]int:
		return "[]int"
	case *[]int8:
		return "[]int8"
	case *[]int16:
		return "[]int16"
	case *[]int32:
		return "[]int32"
	case *[]int64:
		return "[]int64"
	case *uint:
		return "uint"
	case *uint8:
		return "uint8"
	case *uint16:
		return "uint16"
	case *uint32:
		return "uint32"
	case *uint64:
		return "uint64"
	case *[]uint:
		return "[]uint"
	case *[]uint8:
		return "[]uint8"
	case *[]uint16:
		return "[]uint16"
	case *[]uint32:
		return "[]uint32"
	case *[]uint64:
		return "[]uint64"
	case *float32:
		return "float32"
	case *float64:
		return "float64"
	case *[]float32:
		return "[]float32"
	case *[]float64:
		return "[]float64"
	case *string:
		return "string"
	case *[]string:
		return "[]string"
	case *bool:
		return "bool"
	case *[]bool:
		return "[]bool"
	}
	return ""
}

func checkSelects(k reflect.Kind, selects interface{}, val string, flt float64) bool {
	var valid bool
	switch {
	case isKindNumber(k):
		vals, _ := selects.([]float64)
		for _, v := range vals {
			valid = valid || flt == v
			if valid {
				break
			}
		}
	case k == reflect.String:
		vals, _ := selects.([]string)
		for _, v := range vals {
			valid = valid || v == val
			if valid {
				break
			}
		}
	}
	return valid
}

func applyValToPtr(names string, ptr interface{}, val string, selects interface{}) error {
	var err error
	if isBoolPtr(ptr) {
		val, err = convertBool(val)
		if err != nil {
			return fmt.Errorf("%s: %s", names, err.Error())
		}
	}

	flt, ferr := strconv.ParseFloat(val, 64)
	bl, berr := strconv.ParseBool(val)
	switch v := ptr.(type) {
	case *int:
		*v, err = int(flt), ferr
	case *int8:
		*v, err = int8(flt), ferr
	case *int16:
		*v, err = int16(flt), ferr
	case *int32:
		*v, err = int32(flt), ferr
	case *int64:
		*v, err = int64(flt), ferr
	case *uint:
		*v, err = uint(flt), ferr
	case *uint8:
		*v, err = uint8(flt), ferr
	case *uint16:
		*v, err = uint16(flt), ferr
	case *uint32:
		*v, err = uint32(flt), ferr
	case *uint64:
		*v, err = uint64(flt), ferr
	case *float32:
		*v, err = float32(flt), ferr
	case *float64:
		*v, err = float64(flt), ferr
	case *[]int:
		*v, err = append(*v, int(flt)), ferr
	case *[]int8:
		*v, err = append(*v, int8(flt)), ferr
	case *[]int16:
		*v, err = append(*v, int16(flt)), ferr
	case *[]int32:
		*v, err = append(*v, int32(flt)), ferr
	case *[]int64:
		*v, err = append(*v, int64(flt)), ferr
	case *[]uint:
		*v, err = append(*v, uint(flt)), ferr
	case *[]uint8:
		*v, err = append(*v, uint8(flt)), ferr
	case *[]uint16:
		*v, err = append(*v, uint16(flt)), ferr
	case *[]uint32:
		*v, err = append(*v, uint32(flt)), ferr
	case *[]uint64:
		*v, err = append(*v, uint64(flt)), ferr
	case *[]float32:
		*v, err = append(*v, float32(flt)), ferr
	case *[]float64:
		*v, err = append(*v, float64(flt)), ferr
	case *string:
		*v = val
	case *[]string:
		*v = append(*v, val)
	case *bool:
		*v, err = bl, berr
	case *[]bool:
		*v, err = append(*v, bl), berr
	default:
		err = fmt.Errorf("unsupported type: %t %s", ptr, runtime2.Caller(2))
	}
	if err != nil {
		return fmt.Errorf("%s: %s", names, err.Error())
	}
	if selects != nil {
		refval := reflect.ValueOf(ptr).Elem()
		k := sliceElemKind(refval)
		if !checkSelects(k, selects, val, flt) {
			return fmt.Errorf("%s: invalid value %s of %v", names, val, selects)
		}
	}
	return err
}

func splitAndTrimSpace(s, sep string) []string {
	s = strings.TrimSpace(s)
	if s == "" {
		return nil
	}
	secs := strings.Split(s, sep)
	for i := range secs {
		secs[i] = strings.TrimSpace(secs[i])
	}
	return secs
}

func unexportedName(name string) string {
	for _, r := range name {
		if unicode.IsUpper(r) {
			bs := []rune(name)
			for i, r := range bs {
				if unicode.IsUpper(r) {
					bs[i] = unicode.ToLower(r)
				} else {
					break
				}
			}
			return string(bs)
		}
	}
	return name
}

func convertBool(val string) (string, error) {
	switch strings.ToLower(val) {
	case "true", "t", "yes", "y", "1":
		return "true", nil
	case "false", "f", "no", "n", "0":
		return "false", nil
	}
	return "", fmt.Errorf("illegal boolean value: %s", val)
}

func parseBool(val string) (bool, error) {
	val, err := convertBool(val)
	if err != nil {
		return false, err
	}
	return strconv.ParseBool(val)
}

func convertToFloats(vals []string) ([]float64, error) {
	fs := make([]float64, 0, len(vals))
	for _, v := range vals {
		f, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		fs = append(fs, f)
	}
	return fs, nil
}

func convertToBools(vals []string) ([]bool, error) {
	bs := make([]bool, 0, len(vals))
	for _, v := range vals {
		f, err := parseBool(v)
		if err != nil {
			return nil, err
		}
		bs = append(bs, f)
	}
	return bs, nil
}
<commit_msg>fixed imports.<commit_after>package flag

import (
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"unicode"
)

func isKindNumber(k reflect.Kind) bool {
	switch k {
	case reflect.Int,
		reflect.Int8,
		reflect.Int16,
		reflect.Int32,
		reflect.Int64,
		reflect.Uint,
		reflect.Uint8,
		reflect.Uint16,
		reflect.Uint32,
		reflect.Uint64,
		reflect.Float32,
		reflect.Float64:
		return true
	}
	return false
}

func isKindCompatible(k1, k2 reflect.Kind) bool {
	switch k1 {
	case reflect.Bool, reflect.String:
		return k2 == k1
	}
	return isKindNumber(k1) && isKindNumber(k2)
}

func sliceElemKind(val reflect.Value) reflect.Kind {
	k := val.Kind()
	if k == reflect.Slice {
		return val.Type().Elem().Kind()
	}
	return k
}

func isBoolPtr(ptr interface{}) bool {
	switch ptr.(type) {
	case *bool, *[]bool:
		return true
	}
	return false
}

func isSlicePtr(ptr interface{}) bool {
	refval := reflect.ValueOf(ptr)
	return refval.Kind() == reflect.Ptr && refval.Elem().Kind() == reflect.Slice
}

func parseDefault(val, valsep string, ptr interface{}) (interface{}, error) {
	if val == "" {
		return nil, nil
	}
	refval := reflect.ValueOf(ptr).Elem()
	switch refval.Kind() {
	case reflect.String:
		return val, nil
	case reflect.Bool:
		b, err := parseBool(val)
		return b, err
	default:
		if isKindNumber(refval.Kind()) {
			return strconv.ParseFloat(val, 64)
		}
	case reflect.Slice:
		vals := splitAndTrimSpace(val, valsep)
		switch k := sliceElemKind(refval); k {
		case reflect.String:
			return vals, nil
		case reflect.Bool:
			bs, err := convertToBools(vals)
			return bs, err
		default:
			if isKindNumber(k) {
				fs, err := convertToFloats(vals)
				return fs, err
			}
		}
	}
	return nil, fmt.Errorf("unsupported kind: %s", refval.Kind())
}

func convertNumberSlice(val interface{}) []float64 {
	var fs []float64
	switch vals := val.(type) {
	case []int:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int8:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int16:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []int64:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint8:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint16:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []uint64:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []float32:
		fs = make([]float64, len(vals))
		for i, v := range vals {
			fs[i] = float64(v)
		}
	case []float64:
		fs = vals
	}
	return fs
}

func parseSelectsString(val, valsep string, ptr interface{}) (interface{}, error) {
	if val == "" {
		return nil, nil
	}

	refval := reflect.ValueOf(ptr).Elem()
	vals := splitAndTrimSpace(val, valsep)
	k := sliceElemKind(refval)
	switch {
	case k == reflect.String:
		return vals, nil
	case isKindNumber(k):
		return convertToFloats(vals)
	}
	return nil, fmt.Errorf("doesn't support select: %s", k.String())
}

func parseSelectsValue(ptr interface{}, val interface{}) (interface{}, error) {
	if val == nil {
		return nil, nil
	}

	refval := reflect.ValueOf(ptr).Elem()
	k := sliceElemKind(refval)
	if isKindNumber(k) {
		fs := convertNumberSlice(val)
		if len(fs) != 0 {
			return fs, nil
		}
	} else if k == reflect.String {
		if vals, ok := val.([]string); ok && len(vals) != 0 {
			return vals, nil
		}
	}
	return nil, errors.New("invalid selects")
}

func typeName(ptr interface{}) string {
	switch ptr.(type) {
	case *int:
		return "int"
	case *int8:
		return "int8"
	case *int16:
		return "int16"
	case *int32:
		return "int32"
	case *int64:
		return "int64"
	case *[]int:
		return "[]int"
	case *[]int8:
		return "[]int8"
	case *[]int16:
		return "[]int16"
	case *[]int32:
		return "[]int32"
	case *[]int64:
		return "[]int64"
	case *uint:
		return "uint"
	case *uint8:
		return "uint8"
	case *uint16:
		return "uint16"
	case *uint32:
		return "uint32"
	case *uint64:
		return "uint64"
	case *[]uint:
		return "[]uint"
	case *[]uint8:
		return "[]uint8"
	case *[]uint16:
		return "[]uint16"
	case *[]uint32:
		return "[]uint32"
	case *[]uint64:
		return "[]uint64"
	case *float32:
		return "float32"
	case *float64:
		return "float64"
	case *[]float32:
		return "[]float32"
	case *[]float64:
		return "[]float64"
	case *string:
		return "string"
	case *[]string:
		return "[]string"
	case *bool:
		return "bool"
	case *[]bool:
		return "[]bool"
	}
	return ""
}

func checkSelects(k reflect.Kind, selects interface{}, val string, flt float64) bool {
	var valid bool
	switch {
	case isKindNumber(k):
		vals, _ := selects.([]float64)
		for _, v := range vals {
			valid = valid || flt == v
			if valid {
				break
			}
		}
	case k == reflect.String:
		vals, _ := selects.([]string)
		for _, v := range vals {
			valid = valid || v == val
			if valid {
				break
			}
		}
	}
	return valid
}

func applyValToPtr(names string, ptr interface{}, val string, selects interface{}) error {
	var err error
	if isBoolPtr(ptr) {
		val, err = convertBool(val)
		if err != nil {
			return fmt.Errorf("%s: %s", names, err.Error())
		}
	}

	flt, ferr := strconv.ParseFloat(val, 64)
	bl, berr := strconv.ParseBool(val)
	switch v := ptr.(type) {
	case *int:
		*v, err = int(flt), ferr
	case *int8:
		*v, err = int8(flt), ferr
	case *int16:
		*v, err = int16(flt), ferr
	case *int32:
		*v, err = int32(flt), ferr
	case *int64:
		*v, err = int64(flt), ferr
	case *uint:
		*v, err = uint(flt), ferr
	case *uint8:
		*v, err = uint8(flt), ferr
	case *uint16:
		*v, err = uint16(flt), ferr
	case *uint32:
		*v, err = uint32(flt), ferr
	case *uint64:
		*v, err = uint64(flt), ferr
	case *float32:
		*v, err = float32(flt), ferr
	case *float64:
		*v, err = float64(flt), ferr
	case *[]int:
		*v, err = append(*v, int(flt)), ferr
	case *[]int8:
		*v, err = append(*v, int8(flt)), ferr
	case *[]int16:
		*v, err = append(*v, int16(flt)), ferr
	case *[]int32:
		*v, err = append(*v, int32(flt)), ferr
	case *[]int64:
		*v, err = append(*v, int64(flt)), ferr
	case *[]uint:
		*v, err = append(*v, uint(flt)), ferr
	case *[]uint8:
		*v, err = append(*v, uint8(flt)), ferr
	case *[]uint16:
		*v, err = append(*v, uint16(flt)), ferr
	case *[]uint32:
		*v, err = append(*v, uint32(flt)), ferr
	case *[]uint64:
		*v, err = append(*v, uint64(flt)), ferr
	case *[]float32:
		*v, err = append(*v, float32(flt)), ferr
	case *[]float64:
		*v, err = append(*v, float64(flt)), ferr
	case *string:
		*v = val
	case *[]string:
		*v = append(*v, val)
	case *bool:
		*v, err = bl, berr
	case *[]bool:
		*v, err = append(*v, bl), berr
	default:
		err = fmt.Errorf("unsupported type: %t %s", ptr)
	}
	if err != nil {
		return fmt.Errorf("%s: %s", names, err.Error())
	}
	if selects != nil {
		refval := reflect.ValueOf(ptr).Elem()
		k := sliceElemKind(refval)
		if !checkSelects(k, selects, val, flt) {
			return fmt.Errorf("%s: invalid value %s of %v", names, val, selects)
		}
	}
	return err
}

func splitAndTrimSpace(s, sep string) []string {
	s = strings.TrimSpace(s)
	if s == "" {
		return nil
	}
	secs := strings.Split(s, sep)
	for i := range secs {
		secs[i] = strings.TrimSpace(secs[i])
	}
	return secs
}

func unexportedName(name string) string {
	for _, r := range name {
		if unicode.IsUpper(r) {
			bs := []rune(name)
			for i, r := range bs {
				if unicode.IsUpper(r) {
					bs[i] = unicode.ToLower(r)
				} else {
					break
				}
			}
			return string(bs)
		}
	}
	return name
}

func convertBool(val string) (string, error) {
	switch strings.ToLower(val) {
	case "true", "t", "yes", "y", "1":
		return "true", nil
	case "false", "f", "no", "n", "0":
		return "false", nil
	}
	return "", fmt.Errorf("illegal boolean value: %s", val)
}

func parseBool(val string) (bool, error) {
	val, err := convertBool(val)
	if err != nil {
		return false, err
	}
	return strconv.ParseBool(val)
}

func convertToFloats(vals []string) ([]float64, error) {
	fs := make([]float64, 0, len(vals))
	for _, v := range vals {
		f, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return nil, err
		}
		fs = append(fs, f)
	}
	return fs, nil
}

func convertToBools(vals []string) ([]bool, error) {
	bs := make([]bool, 0, len(vals))
	for _, v := range vals {
		f, err := parseBool(v)
		if err != nil {
			return nil, err
		}
		bs = append(bs, f)
	}
	return bs, nil
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2014 The heketi Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package glusterfs

import (
	"errors"
	"github.com/heketi/heketi/requests"
	//goon "github.com/shurcooL/go-goon"
	"sync"
)

const (
	KB = 1
	MB = KB * 1024
	GB = MB * 1024
	TB = GB * 1024
)

var (

	// :TODO: Need to add what the maximum number of volumes is
	BRICK_MIN_SIZE = uint64(1 * GB)
	BRICK_MAX_SIZE = uint64(4 * TB)
	BRICK_MAX_NUM  = 200

	ErrNoSpace = errors.New("No space")
)

// return size of each brick, error
func (m *GlusterFSPlugin) numBricksNeeded(size uint64) (uint64, error) {
	brick_size := size / 2

	if brick_size < BRICK_MIN_SIZE {
		return 0, errors.New("Minimum brick size limit reached.  Out of space.")
	} else if brick_size > BRICK_MAX_SIZE {
		return m.numBricksNeeded(brick_size)
	}

	return brick_size, nil
}

func (m *GlusterFSPlugin) allocBricks(num_bricks, replicas int, size uint64) ([]*Brick, error) {

	bricks := make([]*Brick, 0)

	// Allocate size for the brick plus the snapshot
	tpsize := uint64(float64(size) * THINP_SNAPSHOT_FACTOR)

	for brick_num := 0; brick_num < num_bricks; brick_num++ {

		var brick *Brick

		brick = NewBrick(size, m.db)
		nodelist, err := m.ring.GetNodes(brick_num, brick.Id)
		if err != nil {
			return nil, err
		}

		var nodeid, deviceid string
		for i := 0; i < replicas; i++ {

			if i > 0 {
				brick = NewBrick(size, m.db)
			}

			// This could be a function
			for enough_space := false; !enough_space; {

				// Could ask for more than just the replicas
				if len(nodelist) < 1 {
					// unable to satisfy request.  Give back the data
					for brick := range bricks {
						bricks[brick].FreeStorage()
					}
					return nil, ErrNoSpace
				}

				var bricknode BrickNode

				// Should check list size
				bricknode, nodelist = nodelist[0], nodelist[1:len(nodelist)]

				m.db.Reader(func() error {
					if m.db.nodes[bricknode.NodeId].Info.Devices[bricknode.DeviceId].Free > tpsize {
						enough_space = true
						nodeid, deviceid = bricknode.NodeId, bricknode.DeviceId
					}
					return nil
				})

			}

			brick.AllocateStorage(nodeid, deviceid)

			// Create a brick object
			bricks = append(bricks, brick)

		}
	}

	return bricks, nil

}

func (m *GlusterFSPlugin) createBricks(bricks []*Brick) error {
	var wg sync.WaitGroup
	for brick := range bricks {
		wg.Add(1)
		go func(b int) {
			defer wg.Done()
			bricks[b].Create()
		}(brick)
	}

	wg.Wait()

	return nil
}

func (m *GlusterFSPlugin) VolumeCreate(v *requests.VolumeCreateRequest) (*requests.VolumeInfoResp, error) {

	// Get the nodes and storage for these bricks
	// and Create the bricks
	replica := v.Replica
	if v.Replica == 0 {
		replica = 2
	}

	var bricklist []*Brick
	size := v.Size
	for {
		// Determine number of bricks needed
		brick_size, err := m.numBricksNeeded(size)
		if err != nil {
			return nil, err
		}
		num_bricks := int(v.Size / brick_size)

		if num_bricks > BRICK_MAX_NUM {
			return nil, errors.New("No space. Maximum number of bricks reached.")
		}

		// Allocate bricks in the cluster
		bricklist, err = m.allocBricks(num_bricks, replica, brick_size)
		if err == ErrNoSpace {
			size /= 2
			continue
		}
		if err != nil {
			return nil, err
		} else {
			break
		}
	}

	// Create bricks
	err := m.createBricks(bricklist)
	if err != nil {
		return nil, err
	}

	// Create volume object
	volume := NewVolumeEntry(v, bricklist, replica, m.db)
	err = volume.CreateGlusterVolume()
	if err != nil {
		return nil, err
	}

	err = m.db.Writer(func() error {
		// Save volume information on the DB
		m.db.volumes[volume.Info.Id] = volume

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

	return &volume.Copy().Info, nil
}

func (m *GlusterFSPlugin) VolumeDelete(id string) error {

	return m.db.Writer(func() error {
		if v, ok := m.db.volumes[id]; ok {

			// :TODO: This can probably be done outside the transaction
			v.Destroy()

			delete(m.db.volumes, id)
		} else {
			return errors.New("Volume id not found")
		}

		return nil
	})

}

func (m *GlusterFSPlugin) VolumeInfo(id string) (*requests.VolumeInfoResp, error) {

	var info *requests.VolumeInfoResp

	err := m.db.Reader(func() error {
		if volume, ok := m.db.volumes[id]; ok {
			info = &volume.Copy().Info
			return nil
		} else {
			return errors.New("Id not found")
		}
	})

	return info, err
}

func (m *GlusterFSPlugin) VolumeResize(id string) (*requests.VolumeInfoResp, error) {
	return nil, errors.New("Not supported yet")
}

func (m *GlusterFSPlugin) VolumeList() (*requests.VolumeListResponse, error) {

	list := &requests.VolumeListResponse{}
	list.Volumes = make([]requests.VolumeInfoResp, 0)

	err := m.db.Reader(func() error {
		for _, volume := range m.db.volumes {
			list.Volumes = append(list.Volumes, volume.Info)
		}

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

	return list, nil
}
<commit_msg>Fix deadlock in delete<commit_after>//
// Copyright (c) 2014 The heketi Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package glusterfs

import (
	"errors"
	"github.com/heketi/heketi/requests"
	//goon "github.com/shurcooL/go-goon"
	"sync"
)

const (
	KB = 1
	MB = KB * 1024
	GB = MB * 1024
	TB = GB * 1024
)

var (

	// :TODO: Need to add what the maximum number of volumes is
	BRICK_MIN_SIZE = uint64(1 * GB)
	BRICK_MAX_SIZE = uint64(4 * TB)
	BRICK_MAX_NUM  = 200

	ErrNoSpace = errors.New("No space")
)

// return size of each brick, error
func (m *GlusterFSPlugin) numBricksNeeded(size uint64) (uint64, error) {
	brick_size := size / 2

	if brick_size < BRICK_MIN_SIZE {
		return 0, errors.New("Minimum brick size limit reached.  Out of space.")
	} else if brick_size > BRICK_MAX_SIZE {
		return m.numBricksNeeded(brick_size)
	}

	return brick_size, nil
}

func (m *GlusterFSPlugin) allocBricks(num_bricks, replicas int, size uint64) ([]*Brick, error) {

	bricks := make([]*Brick, 0)

	// Allocate size for the brick plus the snapshot
	tpsize := uint64(float64(size) * THINP_SNAPSHOT_FACTOR)

	for brick_num := 0; brick_num < num_bricks; brick_num++ {

		var brick *Brick

		brick = NewBrick(size, m.db)
		nodelist, err := m.ring.GetNodes(brick_num, brick.Id)
		if err != nil {
			return nil, err
		}

		var nodeid, deviceid string
		for i := 0; i < replicas; i++ {

			if i > 0 {
				brick = NewBrick(size, m.db)
			}

			// This could be a function
			for enough_space := false; !enough_space; {

				// Could ask for more than just the replicas
				if len(nodelist) < 1 {
					// unable to satisfy request.  Give back the data
					for brick := range bricks {
						bricks[brick].FreeStorage()
					}
					return nil, ErrNoSpace
				}

				var bricknode BrickNode

				// Should check list size
				bricknode, nodelist = nodelist[0], nodelist[1:len(nodelist)]

				m.db.Reader(func() error {
					if m.db.nodes[bricknode.NodeId].Info.Devices[bricknode.DeviceId].Free > tpsize {
						enough_space = true
						nodeid, deviceid = bricknode.NodeId, bricknode.DeviceId
					}
					return nil
				})

			}

			brick.AllocateStorage(nodeid, deviceid)

			// Create a brick object
			bricks = append(bricks, brick)

		}
	}

	return bricks, nil

}

func (m *GlusterFSPlugin) createBricks(bricks []*Brick) error {
	var wg sync.WaitGroup
	for brick := range bricks {
		wg.Add(1)
		go func(b int) {
			defer wg.Done()
			bricks[b].Create()
		}(brick)
	}

	wg.Wait()

	return nil
}

func (m *GlusterFSPlugin) VolumeCreate(v *requests.VolumeCreateRequest) (*requests.VolumeInfoResp, error) {

	// Get the nodes and storage for these bricks
	// and Create the bricks
	replica := v.Replica
	if v.Replica == 0 {
		replica = 2
	}

	var bricklist []*Brick
	size := v.Size
	for {
		// Determine number of bricks needed
		brick_size, err := m.numBricksNeeded(size)
		if err != nil {
			return nil, err
		}
		num_bricks := int(v.Size / brick_size)

		if num_bricks > BRICK_MAX_NUM {
			return nil, errors.New("No space. Maximum number of bricks reached.")
		}

		// Allocate bricks in the cluster
		bricklist, err = m.allocBricks(num_bricks, replica, brick_size)
		if err == ErrNoSpace {
			size /= 2
			continue
		}
		if err != nil {
			return nil, err
		} else {
			break
		}
	}

	// Create bricks
	err := m.createBricks(bricklist)
	if err != nil {
		return nil, err
	}

	// Create volume object
	volume := NewVolumeEntry(v, bricklist, replica, m.db)
	err = volume.CreateGlusterVolume()
	if err != nil {
		return nil, err
	}

	err = m.db.Writer(func() error {
		// Save volume information on the DB
		m.db.volumes[volume.Info.Id] = volume

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

	return &volume.Copy().Info, nil
}

func (m *GlusterFSPlugin) VolumeDelete(id string) error {

	var volume *VolumeEntry

	// Remove from database, then destroy the volume
	err := m.db.Writer(func() error {
		if v, ok := m.db.volumes[id]; ok {
			volume = v
			delete(m.db.volumes, id)
		} else {
			return errors.New("Volume id not found")
		}
		return nil
	})
	if err != nil {
		return err
	}

	// Destroy volume
	err = volume.Destroy()
	if err != nil {
		// :TODO: Add back to database with a Zombie state
		return err
	}

	return nil

}

func (m *GlusterFSPlugin) VolumeInfo(id string) (*requests.VolumeInfoResp, error) {

	var info *requests.VolumeInfoResp

	err := m.db.Reader(func() error {
		if volume, ok := m.db.volumes[id]; ok {
			info = &volume.Copy().Info
			return nil
		} else {
			return errors.New("Id not found")
		}
	})

	return info, err
}

func (m *GlusterFSPlugin) VolumeResize(id string) (*requests.VolumeInfoResp, error) {
	return nil, errors.New("Not supported yet")
}

func (m *GlusterFSPlugin) VolumeList() (*requests.VolumeListResponse, error) {

	list := &requests.VolumeListResponse{}
	list.Volumes = make([]requests.VolumeInfoResp, 0)

	err := m.db.Reader(func() error {
		for _, volume := range m.db.volumes {
			list.Volumes = append(list.Volumes, volume.Info)
		}

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

	return list, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
)

func IsParamSet(r *http.Request, param string) bool {
	return len(r.URL.Query().Get(param)) > 0
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

func GetHost(r *http.Request) (host string, err error) {
	// get remote ip
	host = r.Header.Get("X-Forwarded-For")
	if len(host) > 0 {
		parts := strings.Split(host, ",")
		// apache will append the remote address
		host = strings.TrimSpace(parts[len(parts)-1])
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	}
	return
}

var TBBUserAgents = regexp.MustCompile(`^Mozilla/5\.0 \(Windows NT 6\.1; rv:[\d]+\.0\) Gecko/20100101 Firefox/[\d]+\.0$`)
var OrfoxUserAgents = regexp.MustCompile(`^Mozilla/5\.0 \(Android; Mobile; rv:38.0\) Gecko/38.0 Firefox/38.0$`)

func LikelyTBB(ua string) bool {
	return TBBUserAgents.MatchString(ua) || OrfoxUserAgents.MatchString(ua)
}

var HaveManual = map[string]bool{
	"ar":    true,
	"cs":    true,
	"de":    true,
	"el":    true,
	"en":    true,
	"es":    true,
	"fa":    true,
	"fr":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"nl":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"sv":    true,
	"tr":    true,
	"zh_CN": true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	// and https://en.wikipedia.org/w/api.php?action=sitematrix&format=json
	haveTranslatedNames := map[string]string{
		"af":          "Afrikaans",
		"ar":          "العربية",
		"az":          "Azərbaycanca",
		"bg":          "български",
		"bs":          "Bosanski jezik",
		"ca":          "Català",
		"cs":          "Čeština",
		"cy":          "Cymraeg",
		"da":          "Dansk",
		"de":          "Deutsch",
		"el":          "ελληνικά",
		"en_GB":       "English (United Kingdom)",
		"eo":          "Esperanto",
		"es":          "Español",
		"es_AR":       "Español (Argentina)",
		"es_CO":       "Español (Colombia)",
		"es_MX":       "Español (Mexico)",
		"et":          "Eesti",
		"eu":          "Euskara",
		"fa":          "فارسی",
		"fi":          "Suomi",
		"fr":          "Français",
		"fr_CA":       "Français (Canadien)",
		"gl":          "Galego",
		"he":          "עברית",
		"hi":          "हिन्दी",
		"hr":          "Hrvatski jezik",
		"hr_HR":       "Hrvatski jezik (Croatia)",
		"hu":          "Magyar",
		"id":          "Bahasa Indonesia",
		"it":          "Italiano",
		"ja":          "日本語",
		"km":          "មែរ",
		"kn":          "ಕನ್ನಡ",
		"ko":          "한국어",
		"ko_KR":       "한국어 (South Korea)",
		"lo":          "ລາວ",
		"lv":          "Latviešu valoda",
		"mk":          "македонски јазик",
		"ms_MY":       "Bahasa Melayu",
		"my":          "ဗမာစာ",
		"nb":          "Norsk bokmål",
		"nl":          "Nederlands",
		"nl_BE":       "Vlaams",
		"nn":          "Norsk nynorsk",
		"pa":          "ਪੰਜਾਬੀ",
		"pl":          "Język polski",
		"pl_PL":       "Język polski (Poland)",
		"pt":          "Português",
		"pt_BR":       "Português do Brasil",
		"ro":          "română",
		"ru":          "русский язык",
		"ru@petr1708": "Russian Petrine orthography",
		"si_LK":       "සිංහල",
		"sk":          "Slovenčina",
		"sk_SK":       "Slovenčina (Slovakia)",
		"sl":          "Slovenski jezik",
		"sl_SI":       "Slovenski jezik (Slovenia)",
		"sq":          "shqip",
		"sr":          "српски језик",
		"sv":          "Svenska",
		"ta":          "தமிழ்",
		"te_IN":       "తెలుగు",
		"th":          "ไทย",
		"tr":          "Türkçe",
		"uk":          "українська мова",
		"zh_CN":       "中文简体",
		"zh_TW":       "中文繁體",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			log.Print("No translated name for code: " + code)
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<commit_msg>Add some translations<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/samuel/go-gettext/gettext"
	"html/template"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"path"
	"regexp"
	"strconv"
	"strings"
)

func IsParamSet(r *http.Request, param string) bool {
	return len(r.URL.Query().Get(param)) > 0
}

func Lang(r *http.Request) string {
	lang := r.URL.Query().Get("lang")
	if len(lang) == 0 {
		lang = "en_US"
	}
	return lang
}

func GetQS(q url.Values, param string, deflt int) (num int, str string) {
	str = q.Get(param)
	num, err := strconv.Atoi(str)
	if err != nil {
		num = deflt
		str = ""
	} else {
		str = fmt.Sprintf("&%s=%s", param, str)
	}
	return
}

func GetHost(r *http.Request) (host string, err error) {
	// get remote ip
	host = r.Header.Get("X-Forwarded-For")
	if len(host) > 0 {
		parts := strings.Split(host, ",")
		// apache will append the remote address
		host = strings.TrimSpace(parts[len(parts)-1])
	} else {
		host, _, err = net.SplitHostPort(r.RemoteAddr)
	}
	return
}

var TBBUserAgents = regexp.MustCompile(`^Mozilla/5\.0 \(Windows NT 6\.1; rv:[\d]+\.0\) Gecko/20100101 Firefox/[\d]+\.0$`)
var OrfoxUserAgents = regexp.MustCompile(`^Mozilla/5\.0 \(Android; Mobile; rv:38.0\) Gecko/38.0 Firefox/38.0$`)

func LikelyTBB(ua string) bool {
	return TBBUserAgents.MatchString(ua) || OrfoxUserAgents.MatchString(ua)
}

var HaveManual = map[string]bool{
	"ar":    true,
	"cs":    true,
	"de":    true,
	"el":    true,
	"en":    true,
	"es":    true,
	"fa":    true,
	"fr":    true,
	"hu":    true,
	"it":    true,
	"ja":    true,
	"lv":    true,
	"nb":    true,
	"nl":    true,
	"pl":    true,
	"pt_BR": true,
	"ru":    true,
	"sv":    true,
	"tr":    true,
	"zh_CN": true,
}

func FuncMap(domain *gettext.Domain) template.FuncMap {
	return template.FuncMap{
		"UnEscaped": func(x string) interface{} {
			return template.HTML(x)
		},
		"UnEscapedURL": func(x string) interface{} {
			return template.URL(x)
		},
		"GetText": func(lang string, text string) string {
			return domain.GetText(lang, text)
		},
		"Equal": func(one string, two string) bool {
			return one == two
		},
		"Not": func(b bool) bool {
			return !b
		},
		"And": func(a bool, b bool) bool {
			return a && b
		},
		"UserManual": func(lang string) string {
			if _, ok := HaveManual[lang]; !ok {
				lang = "en"
			}
			return lang
		},
	}
}

var Layout *template.Template

func CompileTemplate(base string, domain *gettext.Domain, templateName string) *template.Template {
	if Layout == nil {
		Layout = template.New("")
		Layout = Layout.Funcs(FuncMap(domain))
		Layout = template.Must(Layout.ParseFiles(
			path.Join(base, "public/base.html"),
			path.Join(base, "public/torbutton.html"),
		))
	}
	l, err := Layout.Clone()
	if err != nil {
		log.Fatal(err)
	}
	return template.Must(l.ParseFiles(path.Join(base, "public/", templateName)))
}

type locale struct {
	Code string
	Name string
}

func GetLocaleList(base string) map[string]string {
	// populated from https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
	// and https://en.wikipedia.org/w/api.php?action=sitematrix&format=json
	haveTranslatedNames := map[string]string{
		"af":          "Afrikaans",
		"ar":          "العربية",
		"az":          "Azərbaycanca",
		"bg":          "български",
		"bs":          "Bosanski jezik",
		"ca":          "Català",
		"cs":          "Čeština",
		"cy":          "Cymraeg",
		"da":          "Dansk",
		"de":          "Deutsch",
		"el":          "ελληνικά",
		"en_GB":       "English (United Kingdom)",
		"eo":          "Esperanto",
		"es":          "Español",
		"es_AR":       "Español (Argentina)",
		"es_CO":       "Español (Colombia)",
		"es_MX":       "Español (Mexico)",
		"et":          "Eesti",
		"eu":          "Euskara",
		"fa":          "فارسی",
		"fi":          "Suomi",
		"fr":          "Français",
		"fr_CA":       "Français (Canadien)",
		"gl":          "Galego",
		"he":          "עברית",
		"hi":          "हिन्दी",
		"hr":          "Hrvatski jezik",
		"hr_HR":       "Hrvatski jezik (Croatia)",
		"hu":          "Magyar",
		"id":          "Bahasa Indonesia",
		"is":          "íslenska",
		"it":          "Italiano",
		"ja":          "日本語",
		"km":          "មែរ",
		"kn":          "ಕನ್ನಡ",
		"ko":          "한국어",
		"ko_KR":       "한국어 (South Korea)",
		"lb":          "Lëtzebuergesch",
		"lo":          "ລາວ",
		"lv":          "Latviešu valoda",
		"mk":          "македонски јазик",
		"ms_MY":       "Bahasa Melayu",
		"my":          "ဗမာစာ",
		"nb":          "Norsk bokmål",
		"nl":          "Nederlands",
		"nl_BE":       "Vlaams",
		"nn":          "Norsk nynorsk",
		"pa":          "ਪੰਜਾਬੀ",
		"pl":          "Język polski",
		"pl_PL":       "Język polski (Poland)",
		"pt":          "Português",
		"pt_BR":       "Português do Brasil",
		"ro":          "română",
		"ru":          "русский язык",
		"ru@petr1708": "Russian Petrine orthography",
		"si_LK":       "සිංහල",
		"sk":          "Slovenčina",
		"sk_SK":       "Slovenčina (Slovakia)",
		"sl":          "Slovenski jezik",
		"sl_SI":       "Slovenski jezik (Slovenia)",
		"sq":          "shqip",
		"sr":          "српски језик",
		"sv":          "Svenska",
		"ta":          "தமிழ்",
		"te_IN":       "తెలుగు",
		"th":          "ไทย",
		"tr":          "Türkçe",
		"uk":          "українська мова",
		"zh_CN":       "中文简体",
		"zh_HK":       "中文繁體",
		"zh_TW":       "中文繁體",
	}

	// for all folders in locale which match a locale from https://www.transifex.com/api/2/languages/
	// use the language name unless we have an override
	webLocales, err := FetchTranslationLocales(base)
	if err != nil {
		log.Printf("Failed to get up to date language list, using fallback.")
		return haveTranslatedNames
	}

	return GetInstalledLocales(base, webLocales, haveTranslatedNames)
}

func FetchTranslationLocales(base string) (map[string]locale, error) {
	file, err := os.Open(path.Join(base, "data/langs"))
	if err != nil {
		return nil, err
	}
	defer file.Close()

	webLocales := make(map[string]locale)
	// Parse the api response into a list of possible locales
	dec := json.NewDecoder(file)
	for {
		var webList []locale
		if err = dec.Decode(&webList); err == io.EOF {
			break
		} else if err != nil {
			log.Fatal(err)
		}

		// The api returns an array, so we need to map it
		for _, l := range webList {
			webLocales[l.Code] = l
		}
	}

	return webLocales, nil
}

// Get a list of all languages installed in our locale folder with translations if available
func GetInstalledLocales(base string, webLocales map[string]locale, nameTranslations map[string]string) map[string]string {
	localFiles, err := ioutil.ReadDir(path.Join(base, "locale"))

	if err != nil {
		log.Print("No locales found in 'locale'. Try running 'make i18n'.")
		log.Fatal(err)
	}

	locales := make(map[string]string, len(localFiles))
	locales["en_US"] = "English"

	for _, f := range localFiles {
		// TODO: Ensure a language has 100% of the template file
		// Currently this is what should be on the torcheck_completed
		// branch on the translations git should be, so we don't really
		// have to check it in theory...
		code := f.Name()

		// Only accept folders which have corresponding locale
		if !f.IsDir() || webLocales[code] == (locale{}) {
			continue
		}

		// If we have a translated name for a given locale, use it
		if transName := nameTranslations[code]; transName != "" {
			locales[code] = transName
		} else {
			log.Print("No translated name for code: " + code)
			locales[code] = webLocales[code].Name
		}
	}

	return locales
}
<|endoftext|>