<commit_before>/*
 * --------------------------------------------------------------------
 * Shamir Secret Sharing Scheme:
 * --------------------------------------------------------------------
 * Split a secret number into 'n' shares where any 'k' combined shares
 * yield the original secret number. The underlying prime field needs a
 * generator that is larger than the secret to be shared.
 *
 * (c) 2011-2012 Bernd Fix   >Y<
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package crypto

///////////////////////////////////////////////////////////////////////
// import external declarations

import (
	"big"
	"gospel/math"
)

///////////////////////////////////////////////////////////////////////
/*
 * A Share is a partial secret.
 */
type Share struct {
	X,Y,P		*big.Int
}

///////////////////////////////////////////////////////////////////////
// Public methods

/*
 * Split a 'secret' into 'n' shares, where a 'k' shares
 * are sufficient to reconstruct 'secret'.
 * @param secret *big.Int - secret value to be shared
 * @param p *big.Int - generator of underlaying prime field
 * @param n int - total number of shares to be generated
 * @param k int - number of shares needed to reconstruct secret
 * @return []Share - generated list of shares
 */
func Split (secret, p *big.Int, n, k int) []Share {

	f := &math.FieldP { p }
	// coefficients for a k-1 polynominal
	a := make ([]*big.Int, k)
	a[0] = secret
	// generate remaining coefficients
	for i := 1; i < k; i++ {
		a[i] = f.Random()
	}

	// construct shares
	shares := make ([]Share, n)
	for i,_ := range shares {
		x := f.Random()
		y := a[0]
		xi := x
		for j := 1; j < k; j++ {
			yi := f.Mul (a[j], xi)
			y = f.Add (y, yi)
			xi = f.Mul (xi, x)
		}
		shares[i] = Share { x, y, f.P }
	}
	return shares
}

//---------------------------------------------------------------------
/*
 * Reconstruct secrets from number of shares: if not sufficient shares
 * are available, the resulting secret is "random"
 * @param shares []Share - (sufficient) number of shares to reconstruct secret
 * @return *big.Int - reconstructed secret
 */
func Reconstruct (shares []Share) *big.Int {

	// compute value of Lagrangian polynominal at 0
	k := len (shares)
	y := big.NewInt (0)
	f := &math.FieldP { shares[0].P }
	for i,s := range shares {
		if s.P.Cmp (f.P) != 0 {
			return nil
		}
		li := big.NewInt (1)
		for j := 0; j < k; j++ {
			if j == i {
				continue
			}
			a := f.Neg (shares[j].X)
			b := f.Sub (s.X, shares[j].X)
			li = f.Mul (li, f.Div (a, b))
		}
		y = f.Add (y, f.Mul (s.Y, li))
	}
	return y
}

///////////////////////////////////////////////////////////////////////
//	Revision history:
///////////////////////////////////////////////////////////////////////
//
//	Revision 2.0  2012-01-11 03:02:41  brf
//  First release as free software (GPL3+)
//
//	Revision 1.2  2011-12-21 23:33:19  brf
//	Comments added. Code clean-up.
//
<commit_msg>Use prime field type from crypto package.<commit_after>/*
 * --------------------------------------------------------------------
 * Shamir Secret Sharing Scheme:
 * --------------------------------------------------------------------
 * Split a secret number into 'n' shares where any 'k' combined shares
 * yield the original secret number. The underlying prime field needs a
 * generator that is larger than the secret to be shared.
 *
 * (c) 2011-2012 Bernd Fix   >Y<
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package crypto

///////////////////////////////////////////////////////////////////////
// import external declarations

import (
	"big"
)

///////////////////////////////////////////////////////////////////////
/*
 * A Share is a partial secret.
 */
type Share struct {
	X,Y,P		*big.Int
}

///////////////////////////////////////////////////////////////////////
// Public methods

/*
 * Split a 'secret' into 'n' shares, where a 'k' shares
 * are sufficient to reconstruct 'secret'.
 * @param secret *big.Int - secret value to be shared
 * @param p *big.Int - generator of underlaying prime field
 * @param n int - total number of shares to be generated
 * @param k int - number of shares needed to reconstruct secret
 * @return []Share - generated list of shares
 */
func Split (secret, p *big.Int, n, k int) []Share {

	f := &FieldP { p }
	// coefficients for a k-1 polynominal
	a := make ([]*big.Int, k)
	a[0] = secret
	// generate remaining coefficients
	for i := 1; i < k; i++ {
		a[i] = f.Random()
	}

	// construct shares
	shares := make ([]Share, n)
	for i,_ := range shares {
		x := f.Random()
		y := a[0]
		xi := x
		for j := 1; j < k; j++ {
			yi := f.Mul (a[j], xi)
			y = f.Add (y, yi)
			xi = f.Mul (xi, x)
		}
		shares[i] = Share { x, y, f.P }
	}
	return shares
}

//---------------------------------------------------------------------
/*
 * Reconstruct secrets from number of shares: if not sufficient shares
 * are available, the resulting secret is "random"
 * @param shares []Share - (sufficient) number of shares to reconstruct secret
 * @return *big.Int - reconstructed secret
 */
func Reconstruct (shares []Share) *big.Int {

	// compute value of Lagrangian polynominal at 0
	k := len (shares)
	y := big.NewInt (0)
	f := &FieldP { shares[0].P }
	for i,s := range shares {
		if s.P.Cmp (f.P) != 0 {
			return nil
		}
		li := big.NewInt (1)
		for j := 0; j < k; j++ {
			if j == i {
				continue
			}
			a := f.Neg (shares[j].X)
			b := f.Sub (s.X, shares[j].X)
			li = f.Mul (li, f.Div (a, b))
		}
		y = f.Add (y, f.Mul (s.Y, li))
	}
	return y
}

///////////////////////////////////////////////////////////////////////
//	Revision history:
///////////////////////////////////////////////////////////////////////
//
//	Revision 2.0  2012-01-11 03:02:41  brf
//  First release as free software (GPL3+)
//
//	Revision 1.2  2011-12-21 23:33:19  brf
//	Comments added. Code clean-up.
//
<|endoftext|>


<commit_before>/*
Copyright 2017 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 storage

import (
	"context"
	"fmt"
	"strconv"

	"github.com/onsi/ginkgo"
	compute "google.golang.org/api/compute/v1"
	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/uuid"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	"k8s.io/kubernetes/test/e2e/framework/providers/gce"
	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
	"k8s.io/kubernetes/test/e2e/storage/utils"
)

var _ = utils.SIGDescribe("Multi-AZ Cluster Volumes", func() {
	f := framework.NewDefaultFramework("multi-az")
	var zoneCount int
	var err error
	image := framework.ServeHostnameImage
	ginkgo.BeforeEach(func() {
		e2eskipper.SkipUnlessProviderIs("gce", "gke")
		if zoneCount <= 0 {
			zoneCount, err = getZoneCount(f.ClientSet)
			framework.ExpectNoError(err)
		}
		ginkgo.By(fmt.Sprintf("Checking for multi-zone cluster.  Zone count = %d", zoneCount))
		msg := fmt.Sprintf("Zone count is %d, only run for multi-zone clusters, skipping test", zoneCount)
		e2eskipper.SkipUnlessAtLeast(zoneCount, 2, msg)
		// TODO: SkipUnlessDefaultScheduler() // Non-default schedulers might not spread
	})
	ginkgo.It("should schedule pods in the same zones as statically provisioned PVs", func() {
		PodsUseStaticPVsOrFail(f, (2*zoneCount)+1, image)
	})

	ginkgo.It("should only be allowed to provision PDs in zones where nodes exist", func() {
		OnlyAllowNodeZones(f, zoneCount, image)
	})
})

// OnlyAllowNodeZones tests that PDs are only provisioned in zones with nodes.
func OnlyAllowNodeZones(f *framework.Framework, zoneCount int, image string) {
	gceCloud, err := gce.GetGCECloud()
	framework.ExpectNoError(err)

	// Get all the zones that the nodes are in
	expectedZones, err := gceCloud.GetAllZonesFromCloudProvider()
	framework.ExpectNoError(err)
	framework.Logf("Expected zones: %v", expectedZones)

	// Get all the zones in this current region
	region := gceCloud.Region()
	allZonesInRegion, err := gceCloud.ListZonesInRegion(region)
	framework.ExpectNoError(err)

	var extraZone string
	for _, zone := range allZonesInRegion {
		if !expectedZones.Has(zone.Name) {
			extraZone = zone.Name
			break
		}
	}

	if extraZone == "" {
		e2eskipper.Skipf("All zones in region %s have compute instances, no extra zones available", region)
	}

	ginkgo.By(fmt.Sprintf("starting a compute instance in unused zone: %v\n", extraZone))
	project := framework.TestContext.CloudConfig.ProjectID
	zone := extraZone
	myuuid := string(uuid.NewUUID())
	name := "compute-" + myuuid
	imageURL := "https://www.googleapis.com/compute/v1/projects/debian-cloud/global/images/debian-7-wheezy-v20140606"

	rb := &compute.Instance{
		MachineType: "zones/" + zone + "/machineTypes/f1-micro",
		Disks: []*compute.AttachedDisk{
			{
				AutoDelete: true,
				Boot:       true,
				Type:       "PERSISTENT",
				InitializeParams: &compute.AttachedDiskInitializeParams{
					DiskName:    "my-root-pd-" + myuuid,
					SourceImage: imageURL,
				},
			},
		},
		NetworkInterfaces: []*compute.NetworkInterface{
			{
				AccessConfigs: []*compute.AccessConfig{
					{
						Type: "ONE_TO_ONE_NAT",
						Name: "External NAT",
					},
				},
				Network: "/global/networks/default",
			},
		},
		Name: name,
	}

	err = gceCloud.InsertInstance(project, zone, rb)
	framework.ExpectNoError(err)

	defer func() {
		// Teardown of the compute instance
		framework.Logf("Deleting compute resource: %v", name)
		err := gceCloud.DeleteInstance(project, zone, name)
		framework.ExpectNoError(err)
	}()

	ginkgo.By("Creating zoneCount+1 PVCs and making sure PDs are only provisioned in zones with nodes")
	// Create some (zoneCount+1) PVCs with names of form "pvc-x" where x is 1...zoneCount+1
	// This will exploit ChooseZoneForVolume in pkg/volume/util.go to provision them in all the zones it "sees"
	var pvcList []*v1.PersistentVolumeClaim
	c := f.ClientSet
	ns := f.Namespace.Name

	for index := 1; index <= zoneCount+1; index++ {
		pvc := newNamedDefaultClaim(ns, index)
		pvc, err = e2epv.CreatePVC(c, ns, pvc)
		framework.ExpectNoError(err)
		pvcList = append(pvcList, pvc)

		// Defer the cleanup
		defer func() {
			framework.Logf("deleting claim %q/%q", pvc.Namespace, pvc.Name)
			err = c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(context.TODO(), pvc.Name, metav1.DeleteOptions{})
			if err != nil {
				framework.Failf("Error deleting claim %q. Error: %v", pvc.Name, err)
			}
		}()
	}

	// Wait for all claims bound
	for _, claim := range pvcList {
		err = e2epv.WaitForPersistentVolumeClaimPhase(v1.ClaimBound, c, claim.Namespace, claim.Name, framework.Poll, framework.ClaimProvisionTimeout)
		framework.ExpectNoError(err)
	}

	pvZones := sets.NewString()
	ginkgo.By("Checking that PDs have been provisioned in only the expected zones")
	for _, claim := range pvcList {
		// Get a new copy of the claim to have all fields populated
		claim, err = c.CoreV1().PersistentVolumeClaims(claim.Namespace).Get(context.TODO(), claim.Name, metav1.GetOptions{})
		framework.ExpectNoError(err)

		// Get the related PV
		pv, err := c.CoreV1().PersistentVolumes().Get(context.TODO(), claim.Spec.VolumeName, metav1.GetOptions{})
		framework.ExpectNoError(err)

		pvZone, ok := pv.ObjectMeta.Labels[v1.LabelFailureDomainBetaZone]
		framework.ExpectEqual(ok, true, "PV has no LabelZone to be found")
		pvZones.Insert(pvZone)
	}
	framework.ExpectEqual(pvZones.Equal(expectedZones), true, fmt.Sprintf("PDs provisioned in unwanted zones. We want zones: %v, got: %v", expectedZones, pvZones))
}

// Return the number of zones in which we have nodes in this cluster.
func getZoneCount(c clientset.Interface) (int, error) {
	zoneNames, err := e2enode.GetClusterZones(c)
	if err != nil {
		return -1, err
	}
	return len(zoneNames), nil
}

type staticPVTestConfig struct {
	pvSource *v1.PersistentVolumeSource
	pv       *v1.PersistentVolume
	pvc      *v1.PersistentVolumeClaim
	pod      *v1.Pod
}

// PodsUseStaticPVsOrFail Check that the pods using statically
// created PVs get scheduled to the same zone that the PV is in.
func PodsUseStaticPVsOrFail(f *framework.Framework, podCount int, image string) {
	var err error
	c := f.ClientSet
	ns := f.Namespace.Name

	zones, err := e2enode.GetClusterZones(c)
	framework.ExpectNoError(err)
	zonelist := zones.List()
	ginkgo.By("Creating static PVs across zones")
	configs := make([]*staticPVTestConfig, podCount)
	for i := range configs {
		configs[i] = &staticPVTestConfig{}
	}

	defer func() {
		ginkgo.By("Cleaning up pods and PVs")
		for _, config := range configs {
			e2epod.DeletePodOrFail(c, ns, config.pod.Name)
		}
		for _, config := range configs {
			e2epod.WaitForPodNoLongerRunningInNamespace(c, config.pod.Name, ns)
			e2epv.PVPVCCleanup(c, ns, config.pv, config.pvc)
			err = e2epv.DeletePVSource(config.pvSource)
			framework.ExpectNoError(err)
		}
	}()

	for i, config := range configs {
		zone := zonelist[i%len(zones)]
		config.pvSource, err = e2epv.CreatePVSource(zone)
		framework.ExpectNoError(err)

		pvConfig := e2epv.PersistentVolumeConfig{
			NamePrefix: "multizone-pv",
			PVSource:   *config.pvSource,
			Prebind:    nil,
		}
		className := ""
		pvcConfig := e2epv.PersistentVolumeClaimConfig{StorageClassName: &className}

		config.pv, config.pvc, err = e2epv.CreatePVPVC(c, pvConfig, pvcConfig, ns, true)
		framework.ExpectNoError(err)
	}

	ginkgo.By("Waiting for all PVCs to be bound")
	for _, config := range configs {
		e2epv.WaitOnPVandPVC(c, f.Timeouts, ns, config.pv, config.pvc)
	}

	ginkgo.By("Creating pods for each static PV")
	for _, config := range configs {
		podConfig := e2epod.MakePod(ns, nil, []*v1.PersistentVolumeClaim{config.pvc}, false, "")
		config.pod, err = c.CoreV1().Pods(ns).Create(context.TODO(), podConfig, metav1.CreateOptions{})
		framework.ExpectNoError(err)
	}

	ginkgo.By("Waiting for all pods to be running")
	for _, config := range configs {
		err = e2epod.WaitForPodRunningInNamespace(c, config.pod)
		framework.ExpectNoError(err)
	}
}

func newNamedDefaultClaim(ns string, index int) *v1.PersistentVolumeClaim {
	claim := v1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "pvc-" + strconv.Itoa(index),
			Namespace: ns,
		},
		Spec: v1.PersistentVolumeClaimSpec{
			AccessModes: []v1.PersistentVolumeAccessMode{
				v1.ReadWriteOnce,
			},
			Resources: v1.ResourceRequirements{
				Requests: v1.ResourceList{
					v1.ResourceName(v1.ResourceStorage): resource.MustParse("1Gi"),
				},
			},
		},
	}

	return &claim
}
<commit_msg>[storage] [test] Remove extra zone test<commit_after>/*
Copyright 2017 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 storage

import (
	"context"
	"fmt"
	"github.com/onsi/ginkgo"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	e2enode "k8s.io/kubernetes/test/e2e/framework/node"
	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
	e2epv "k8s.io/kubernetes/test/e2e/framework/pv"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
	"k8s.io/kubernetes/test/e2e/storage/utils"
)

var _ = utils.SIGDescribe("Multi-AZ Cluster Volumes", func() {
	f := framework.NewDefaultFramework("multi-az")
	var zoneCount int
	var err error
	image := framework.ServeHostnameImage
	ginkgo.BeforeEach(func() {
		e2eskipper.SkipUnlessProviderIs("gce", "gke")
		if zoneCount <= 0 {
			zoneCount, err = getZoneCount(f.ClientSet)
			framework.ExpectNoError(err)
		}
		ginkgo.By(fmt.Sprintf("Checking for multi-zone cluster.  Zone count = %d", zoneCount))
		msg := fmt.Sprintf("Zone count is %d, only run for multi-zone clusters, skipping test", zoneCount)
		e2eskipper.SkipUnlessAtLeast(zoneCount, 2, msg)
		// TODO: SkipUnlessDefaultScheduler() // Non-default schedulers might not spread
	})
	ginkgo.It("should schedule pods in the same zones as statically provisioned PVs", func() {
		PodsUseStaticPVsOrFail(f, (2*zoneCount)+1, image)
	})
})

// Return the number of zones in which we have nodes in this cluster.
func getZoneCount(c clientset.Interface) (int, error) {
	zoneNames, err := e2enode.GetClusterZones(c)
	if err != nil {
		return -1, err
	}
	return len(zoneNames), nil
}

type staticPVTestConfig struct {
	pvSource *v1.PersistentVolumeSource
	pv       *v1.PersistentVolume
	pvc      *v1.PersistentVolumeClaim
	pod      *v1.Pod
}

// PodsUseStaticPVsOrFail Check that the pods using statically
// created PVs get scheduled to the same zone that the PV is in.
func PodsUseStaticPVsOrFail(f *framework.Framework, podCount int, image string) {
	var err error
	c := f.ClientSet
	ns := f.Namespace.Name

	zones, err := e2enode.GetClusterZones(c)
	framework.ExpectNoError(err)
	zonelist := zones.List()
	ginkgo.By("Creating static PVs across zones")
	configs := make([]*staticPVTestConfig, podCount)
	for i := range configs {
		configs[i] = &staticPVTestConfig{}
	}

	defer func() {
		ginkgo.By("Cleaning up pods and PVs")
		for _, config := range configs {
			e2epod.DeletePodOrFail(c, ns, config.pod.Name)
		}
		for _, config := range configs {
			e2epod.WaitForPodNoLongerRunningInNamespace(c, config.pod.Name, ns)
			e2epv.PVPVCCleanup(c, ns, config.pv, config.pvc)
			err = e2epv.DeletePVSource(config.pvSource)
			framework.ExpectNoError(err)
		}
	}()

	for i, config := range configs {
		zone := zonelist[i%len(zones)]
		config.pvSource, err = e2epv.CreatePVSource(zone)
		framework.ExpectNoError(err)

		pvConfig := e2epv.PersistentVolumeConfig{
			NamePrefix: "multizone-pv",
			PVSource:   *config.pvSource,
			Prebind:    nil,
		}
		className := ""
		pvcConfig := e2epv.PersistentVolumeClaimConfig{StorageClassName: &className}

		config.pv, config.pvc, err = e2epv.CreatePVPVC(c, pvConfig, pvcConfig, ns, true)
		framework.ExpectNoError(err)
	}

	ginkgo.By("Waiting for all PVCs to be bound")
	for _, config := range configs {
		e2epv.WaitOnPVandPVC(c, f.Timeouts, ns, config.pv, config.pvc)
	}

	ginkgo.By("Creating pods for each static PV")
	for _, config := range configs {
		podConfig := e2epod.MakePod(ns, nil, []*v1.PersistentVolumeClaim{config.pvc}, false, "")
		config.pod, err = c.CoreV1().Pods(ns).Create(context.TODO(), podConfig, metav1.CreateOptions{})
		framework.ExpectNoError(err)
	}

	ginkgo.By("Waiting for all pods to be running")
	for _, config := range configs {
		err = e2epod.WaitForPodRunningInNamespace(c, config.pod)
		framework.ExpectNoError(err)
	}
}
<|endoftext|>


<commit_before>package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/connections"
)

const URLRouteGetGameByID = "/games/{id}"

const MethodGetGame = http.MethodGet

type responseGetGameHandler struct {
	ID     int `json:"id"`
	Limit  int `json:"limit"`
	Count  int `json:"count"`
	Width  int `json:"width"`
	Height int `json:"height"`
}

type getGameHandler struct {
	logger       logrus.FieldLogger
	groupManager *connections.ConnectionGroupManager
}

type ErrGetGameHandler string

func (e ErrGetGameHandler) Error() string {
	return "get game handler error: " + string(e)
}

func NewGetGameHandler(logger logrus.FieldLogger, groupManager *connections.ConnectionGroupManager) http.Handler {
	return &getGameHandler{
		logger:       logger,
		groupManager: groupManager,
	}
}

func (h *getGameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		h.logger.Error(ErrGetGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
		return
	}

	h.logger.Infoln("group id to get:", id)

	group, err := h.groupManager.Get(id)
	if err != nil {
		h.logger.Errorln("cannot get group:", err.Error())

		switch err {
		case connections.ErrNotFoundGroup:
			http.NotFound(w, r)
		default:
			http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		}
		return
	}

	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	err = json.NewEncoder(w).Encode(responseGetGameHandler{
		ID:     id,
		Limit:  group.GetLimit(),
		Count:  group.GetCount(),
		Width:  int(group.GetWorldWidth()),
		Height: int(group.GetWorldHeight()),
	})
	if err != nil {
		h.logger.Error(ErrGetGameHandler(err.Error()))
		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
		return
	}
}
<commit_msg>Create JSON error response in getGameHandler<commit_after>package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/sirupsen/logrus"

	"github.com/ivan1993spb/snake-server/connections"
)

const URLRouteGetGameByID = "/games/{id}"

const MethodGetGame = http.MethodGet

type responseGetGameHandler struct {
	ID     int `json:"id"`
	Limit  int `json:"limit"`
	Count  int `json:"count"`
	Width  int `json:"width"`
	Height int `json:"height"`
}

type responseGetGameHandlerError struct {
	Code int    `json:"code"`
	Text string `json:"text"`
	ID   int    `json:"id"`
}

type getGameHandler struct {
	logger       logrus.FieldLogger
	groupManager *connections.ConnectionGroupManager
}

type ErrGetGameHandler string

func (e ErrGetGameHandler) Error() string {
	return "get game handler error: " + string(e)
}

func NewGetGameHandler(logger logrus.FieldLogger, groupManager *connections.ConnectionGroupManager) http.Handler {
	return &getGameHandler{
		logger:       logger,
		groupManager: groupManager,
	}
}

func (h *getGameHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	id, err := strconv.Atoi(vars["id"])
	if err != nil {
		h.logger.Error(ErrGetGameHandler(err.Error()))
		h.writeResponseJSON(w, http.StatusBadRequest, &responseGetGameHandlerError{
			Code: http.StatusBadRequest,
			Text: "invalid game id",
			ID:   id,
		})
		return
	}

	h.logger.Infoln("group id to get:", id)

	group, err := h.groupManager.Get(id)
	if err != nil {
		h.logger.Errorln("cannot get group:", err.Error())

		switch err {
		case connections.ErrNotFoundGroup:
			h.writeResponseJSON(w, http.StatusNotFound, &responseGetGameHandlerError{
				Code: http.StatusNotFound,
				Text: "game not found",
				ID:   id,
			})
		default:
			h.writeResponseJSON(w, http.StatusInternalServerError, &responseGetGameHandlerError{
				Code: http.StatusInternalServerError,
				Text: "unknown error",
				ID:   id,
			})
		}
		return
	}

	h.writeResponseJSON(w, http.StatusOK, &responseGetGameHandler{
		ID:     id,
		Limit:  group.GetLimit(),
		Count:  group.GetCount(),
		Width:  int(group.GetWorldWidth()),
		Height: int(group.GetWorldHeight()),
	})
}

func (h *getGameHandler) writeResponseJSON(w http.ResponseWriter, statusCode int, response interface{}) {
	w.WriteHeader(statusCode)
	w.Header().Set("Content-Type", "application/json; charset=utf-8")
	w.Header().Set("X-Content-Type-Options", "nosniff")

	if err := json.NewEncoder(w).Encode(response); err != nil {
		h.logger.Error(ErrGetGameHandler(err.Error()))
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package fix

import (
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
	"sigs.k8s.io/kustomize/kyaml/filesys"
)

func TestFix(t *testing.T) {
	fSys := filesys.MakeFsInMemory()
	testutils_test.WriteTestKustomizationWith(fSys, []byte(`nameprefix: some-prefix-`))

	cmd := NewCmdFix(fSys, os.Stdout)
	assert.NoError(t, cmd.RunE(cmd, nil))

	content, err := testutils_test.ReadTestKustomization(fSys)
	assert.NoError(t, err)

	assert.Contains(t, string(content), "apiVersion: ")
	assert.Contains(t, string(content), "kind: Kustomization")
}

func TestFixCommand(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		files    map[string]string
		expected string
	}{
		{
			name: "FixOutdatedPatchesFieldTitle",
			input: `
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Service
- path: patch2.yaml
  target:
    group: apps
    kind: Deployment
    version: v1
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
  target:
    kind: Service
- path: patch2.yaml
  target:
    group: apps
    kind: Deployment
    version: v1
`,
		},
		{
			name: "TestRenameAndKeepOutdatedPatchesField",
			input: `
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Deployment
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- path: patch3.yaml
  target:
    kind: Service
`,
			expected: `
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- path: patch3.yaml
  target:
    kind: Service
- path: patch1.yaml
  target:
    kind: Deployment
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeFieldTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            image: nignx:latest
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              image: nignx:latest
`,
		},
		{
			name: "TestFixAndMergeOutdatedPatchesStrategicMergeFieldTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            image: nignx:latest
patches:
- path: patch2.yaml
  target:
    kind: Deployment
`,
			expected: `
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              image: nignx:latest
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeToPathFieldTitle",
			input: `
patchesStrategicMerge:
- deploy.yaml
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
        - name: nginx
          env:
            - name: CONFIG_FILE_PATH
              value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yaml
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeToPathFieldYMLTitle",
			input: `
patchesStrategicMerge:
- deploy.yml
`,
			files: map[string]string{
				"deploy.yml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
  - name: nginx
    env:
      - name: CONFIG_FILE_PATH
        value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yml
`,
		},
		{
			name: "Test fix outdated patchesStrategicMerge from a file and one string literal",
			input: `
patchesStrategicMerge:
- deploy.yaml
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            env:
              - name: CONFIG_FILE_PATH
                value: home.yaml
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
env:
- name: CONFIG_FILE_PATH
  value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yaml
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              env:
                - name: CONFIG_FILE_PATH
                  value: home.yaml
`,
		},
		{
			name: "Test fix outdated patchesStrategicMerge and patchesJson6902",
			input: `
patchesStrategicMerge:
- deploy.yaml
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Deployment
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
template:
spec:
containers:
- name: nginx
env:
- name: CONFIG_FILE_PATH
  value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
  target:
    kind: Deployment
- path: deploy.yaml
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeFieldPatchEndOfYamlTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            env:
              - name: CONFIG_FILE_PATH
                value: home.yaml
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              env:
                - name: CONFIG_FILE_PATH
                  value: home.yaml
`,
		},
		{
			name: "TestFixOutdatedCommonLabels",
			input: `
commonLabels:
  foo: bar
labels:
- pairs:
    a: b
`,
			expected: `
labels:
- pairs:
    a: b
- includeSelectors: true
  pairs:
    foo: bar
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
	}
	for _, test := range tests {
		fSys := filesys.MakeFsInMemory()
		testutils_test.WriteTestKustomizationWith(fSys, []byte(test.input))
		for filename, content := range test.files {
			require.NoError(t, fSys.WriteFile(filename, []byte(content)))
		}
		cmd := NewCmdFix(fSys, os.Stdout)
		require.NoError(t, cmd.RunE(cmd, nil))

		content, err := testutils_test.ReadTestKustomization(fSys)
		require.NoError(t, err)
		require.Contains(t, string(content), "apiVersion: ")
		require.Contains(t, string(content), "kind: Kustomization")

		if diff := cmp.Diff([]byte(test.expected), content); diff != "" {
			t.Errorf("%s: Mismatch (-expected, +actual):\n%s", test.name, diff)
		}
	}
}

func TestFixOutdatedCommonLabelsDuplicate(t *testing.T) {
	kustomizationContentWithOutdatedCommonLabels := []byte(`
commonLabels:
  foo: bar
labels:
- pairs:
    foo: baz
    a: b
`)

	fSys := filesys.MakeFsInMemory()
	testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedCommonLabels)
	cmd := NewCmdFix(fSys, os.Stdout)
	err := cmd.RunE(cmd, nil)
	assert.Error(t, err)
	assert.Equal(t, err.Error(), "label name 'foo' exists in both commonLabels and labels")
}
<commit_msg>fix yaml in test<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package fix

import (
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
	"sigs.k8s.io/kustomize/kyaml/filesys"
)

func TestFix(t *testing.T) {
	fSys := filesys.MakeFsInMemory()
	testutils_test.WriteTestKustomizationWith(fSys, []byte(`nameprefix: some-prefix-`))

	cmd := NewCmdFix(fSys, os.Stdout)
	assert.NoError(t, cmd.RunE(cmd, nil))

	content, err := testutils_test.ReadTestKustomization(fSys)
	assert.NoError(t, err)

	assert.Contains(t, string(content), "apiVersion: ")
	assert.Contains(t, string(content), "kind: Kustomization")
}

func TestFixCommand(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		files    map[string]string
		expected string
	}{
		{
			name: "FixOutdatedPatchesFieldTitle",
			input: `
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Service
- path: patch2.yaml
  target:
    group: apps
    kind: Deployment
    version: v1
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
  target:
    kind: Service
- path: patch2.yaml
  target:
    group: apps
    kind: Deployment
    version: v1
`,
		},
		{
			name: "TestRenameAndKeepOutdatedPatchesField",
			input: `
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Deployment
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- path: patch3.yaml
  target:
    kind: Service
`,
			expected: `
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- path: patch3.yaml
  target:
    kind: Service
- path: patch1.yaml
  target:
    kind: Deployment
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeFieldTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            image: nignx:latest
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              image: nignx:latest
`,
		},
		{
			name: "TestFixAndMergeOutdatedPatchesStrategicMergeFieldTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            image: nignx:latest
patches:
- path: patch2.yaml
  target:
    kind: Deployment
`,
			expected: `
patches:
- path: patch2.yaml
  target:
    kind: Deployment
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              image: nignx:latest
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeToPathFieldTitle",
			input: `
patchesStrategicMerge:
- deploy.yaml
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
        - name: nginx
          env:
            - name: CONFIG_FILE_PATH
              value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yaml
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeToPathFieldYMLTitle",
			input: `
patchesStrategicMerge:
- deploy.yml
`,
			files: map[string]string{
				"deploy.yml": `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
      - name: nginx
        env:
        - name: CONFIG_FILE_PATH
          value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yml
`,
		},
		{
			name: "Test fix outdated patchesStrategicMerge from a file and one string literal",
			input: `
patchesStrategicMerge:
- deploy.yaml
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            env:
              - name: CONFIG_FILE_PATH
                value: home.yaml
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
      - name: nginx
        env:
        - name: CONFIG_FILE_PATH
          value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: deploy.yaml
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              env:
                - name: CONFIG_FILE_PATH
                  value: home.yaml
`,
		},
		{
			name: "Test fix outdated patchesStrategicMerge and patchesJson6902",
			input: `
patchesStrategicMerge:
- deploy.yaml
patchesJson6902:
- path: patch1.yaml
  target:
    kind: Deployment
`,
			files: map[string]string{
				"deploy.yaml": `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    spec:
      containers:
      - name: nginx
        env:
        - name: CONFIG_FILE_PATH
          value: home.yaml`,
			},
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- path: patch1.yaml
  target:
    kind: Deployment
- path: deploy.yaml
`,
		},
		{
			name: "TestFixOutdatedPatchesStrategicMergeFieldPatchEndOfYamlTitle",
			input: `
patchesStrategicMerge:
- |-
  apiVersion: apps/v1
  kind: Deployment
  metadata:
    name: nginx
  spec:
    template:
      spec:
        containers:
          - name: nginx
            env:
              - name: CONFIG_FILE_PATH
                value: home.yaml
`,
			expected: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
patches:
- patch: |-
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nginx
    spec:
      template:
        spec:
          containers:
            - name: nginx
              env:
                - name: CONFIG_FILE_PATH
                  value: home.yaml
`,
		},
		{
			name: "TestFixOutdatedCommonLabels",
			input: `
commonLabels:
  foo: bar
labels:
- pairs:
    a: b
`,
			expected: `
labels:
- pairs:
    a: b
- includeSelectors: true
  pairs:
    foo: bar
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
`,
		},
	}
	for _, test := range tests {
		fSys := filesys.MakeFsInMemory()
		testutils_test.WriteTestKustomizationWith(fSys, []byte(test.input))
		for filename, content := range test.files {
			require.NoError(t, fSys.WriteFile(filename, []byte(content)))
		}
		cmd := NewCmdFix(fSys, os.Stdout)
		require.NoError(t, cmd.RunE(cmd, nil))

		content, err := testutils_test.ReadTestKustomization(fSys)
		require.NoError(t, err)
		require.Contains(t, string(content), "apiVersion: ")
		require.Contains(t, string(content), "kind: Kustomization")

		if diff := cmp.Diff([]byte(test.expected), content); diff != "" {
			t.Errorf("%s: Mismatch (-expected, +actual):\n%s", test.name, diff)
		}
	}
}

func TestFixOutdatedCommonLabelsDuplicate(t *testing.T) {
	kustomizationContentWithOutdatedCommonLabels := []byte(`
commonLabels:
  foo: bar
labels:
- pairs:
    foo: baz
    a: b
`)

	fSys := filesys.MakeFsInMemory()
	testutils_test.WriteTestKustomizationWith(fSys, kustomizationContentWithOutdatedCommonLabels)
	cmd := NewCmdFix(fSys, os.Stdout)
	err := cmd.RunE(cmd, nil)
	assert.Error(t, err)
	assert.Equal(t, err.Error(), "label name 'foo' exists in both commonLabels and labels")
}
<|endoftext|>


<commit_before>package providers

import (
	"os"
	"strings"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	conplicity "github.com/camptocamp/conplicity/lib"
	"github.com/docker/engine-api/types"
)

// A Provider is an interface for providers
type Provider interface {
	GetName() string
	GetPrepareCommand(*types.MountPoint) []string
	GetHandler() *conplicity.Conplicity
	GetVolume() *types.Volume
	GetBackupDir() string
	BackupVolume(*types.Volume) error
}

// BaseProvider is a struct implementing the Provider interface
type BaseProvider struct {
	handler   *conplicity.Conplicity
	vol       *types.Volume
	backupDir string
}

// GetProvider detects which provider suits the passed volume and returns it
func GetProvider(c *conplicity.Conplicity, v *types.Volume) Provider {
	log.WithFields(log.Fields{
		"volume": v.Name,
	}).Info("Detecting provider")
	p := &BaseProvider{
		handler: c,
		vol:     v,
	}
	if f, err := os.Stat(v.Mountpoint + "/PG_VERSION"); err == nil && f.Mode().IsRegular() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("PG_VERSION file found, this should be a PostgreSQL datadir")
		return &PostgreSQLProvider{
			BaseProvider: p,
		}
	} else if f, err := os.Stat(v.Mountpoint + "/mysql"); err == nil && f.Mode().IsDir() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("mysql directory found, this should be MySQL datadir")
		return &MySQLProvider{
			BaseProvider: p,
		}
	} else if f, err := os.Stat(v.Mountpoint + "/DB_CONFIG"); err == nil && f.Mode().IsRegular() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("DB_CONFIG file found, this should be and OpenLDAP datadir")
		return &OpenLDAPProvider{
			BaseProvider: p,
		}
	}

	return &DefaultProvider{
		BaseProvider: p,
	}
}

// PrepareBackup sets up the data before backup
func PrepareBackup(p Provider) (err error) {
	c := p.GetHandler()
	vol := p.GetVolume()
	containers, err := c.ContainerList(context.Background(), types.ContainerListOptions{})
	conplicity.CheckErr(err, "Failed to list containers: %v", "fatal")
	for _, container := range containers {
		container, err := c.ContainerInspect(context.Background(), container.ID)
		conplicity.CheckErr(err, "Failed to inspect container "+container.ID+": %v", "fatal")
		for _, mount := range container.Mounts {
			if mount.Name == vol.Name {
				log.WithFields(log.Fields{
					"volume":    vol.Name,
					"container": container.ID,
				}).Debug("Container found using volume")

				cmd := p.GetPrepareCommand(&mount)
				if cmd != nil {
					exec, err := c.ContainerExecCreate(context.Background(), container.ID, types.ExecConfig{
						Cmd: p.GetPrepareCommand(&mount),
					},
					)

					conplicity.CheckErr(err, "Failed to create exec: %v", "fatal")

					err = c.ContainerExecStart(context.Background(), exec.ID, types.ExecStartCheck{})

					conplicity.CheckErr(err, "Failed to start exec: %v", "fatal")
				} else {
					log.WithFields(log.Fields{
						"volume":    vol.Name,
						"container": container.ID,
					}).Info("No prepare command to execute in container")
				}
			}
		}
	}
	return
}

// BackupVolume performs the backup of the passed volume
func (p *BaseProvider) BackupVolume(vol *types.Volume) (err error) {
	log.WithFields(log.Fields{
		"volume":     vol.Name,
		"driver":     vol.Driver,
		"mountpoint": vol.Mountpoint,
	}).Info("Creating duplicity container")

	c := p.GetHandler()

	fullIfOlderThan, _ := conplicity.GetVolumeLabel(vol, ".full_if_older_than")
	if fullIfOlderThan == "" {
		fullIfOlderThan = c.Config.Duplicity.FullIfOlderThan
	}

	removeOlderThan, _ := conplicity.GetVolumeLabel(vol, ".remove_older_than")
	if removeOlderThan == "" {
		removeOlderThan = c.Config.Duplicity.RemoveOlderThan
	}

	pathSeparator := "/"
	if strings.HasPrefix(c.Config.Duplicity.TargetURL, "swift://") {
		// Looks like I'm not the one to fall on this issue: http://stackoverflow.com/questions/27991960/upload-to-swift-pseudo-folders-using-duplicity
		pathSeparator = "_"
	}

	backupDir := p.GetBackupDir()
	fullTarget := c.Config.Duplicity.TargetURL + pathSeparator + c.Hostname + pathSeparator + vol.Name
	fullBackupDir := vol.Mountpoint + "/" + backupDir
	roMount := vol.Name + ":" + vol.Mountpoint + ":ro"

	volume := &conplicity.Volume{
		Name:            vol.Name,
		Target:          fullTarget,
		BackupDir:       fullBackupDir,
		Mount:           roMount,
		FullIfOlderThan: fullIfOlderThan,
		RemoveOlderThan: removeOlderThan,
		Client:          c,
	}

	var newMetrics []string

	newMetrics, err = volume.Backup()
	conplicity.CheckErr(err, "Failed to backup volume "+vol.Name+" : %v", "fatal")
	c.Metrics = append(c.Metrics, newMetrics...)

	_, err = volume.RemoveOld()
	conplicity.CheckErr(err, "Failed to remove old backups for volume "+vol.Name+" : %v", "fatal")

	_, err = volume.Cleanup()
	conplicity.CheckErr(err, "Failed to cleanup extraneous duplicity files for volume "+vol.Name+" : %v", "fatal")

	noVerifyLbl, _ := conplicity.GetVolumeLabel(vol, ".no_verify")
	noVerify := c.Config.NoVerify || (noVerifyLbl == "true")
	if noVerify {
		log.WithFields(log.Fields{
			"volume": vol.Name,
		}).Info("Skipping verification")
	} else {
		newMetrics, err = volume.Verify()
		conplicity.CheckErr(err, "Failed to verify backup for volume "+vol.Name+" : %v", "fatal")
		c.Metrics = append(c.Metrics, newMetrics...)
	}

	newMetrics, err = volume.Status()
	conplicity.CheckErr(err, "Failed to retrieve last backup info for volume "+vol.Name+" : %v", "fatal")
	c.Metrics = append(c.Metrics, newMetrics...)

	return
}

// GetHandler returns the handler associated with the provider
func (p *BaseProvider) GetHandler() *conplicity.Conplicity {
	return p.handler
}

// GetVolume returns the volume associated with the provider
func (p *BaseProvider) GetVolume() *types.Volume {
	return p.vol
}

// GetBackupDir returns the backup directory used by the provider
func (p *BaseProvider) GetBackupDir() string {
	return p.backupDir
}
<commit_msg>Create new Docker client for PrepareBackup (fix #70)<commit_after>package providers

import (
	"os"
	"strings"

	"golang.org/x/net/context"

	log "github.com/Sirupsen/logrus"
	conplicity "github.com/camptocamp/conplicity/lib"
	"github.com/docker/engine-api/types"
	"github.com/fsouza/go-dockerclient"
)

// A Provider is an interface for providers
type Provider interface {
	GetName() string
	GetPrepareCommand(*types.MountPoint) []string
	GetHandler() *conplicity.Conplicity
	GetVolume() *types.Volume
	GetBackupDir() string
	BackupVolume(*types.Volume) error
}

// BaseProvider is a struct implementing the Provider interface
type BaseProvider struct {
	handler   *conplicity.Conplicity
	vol       *types.Volume
	backupDir string
}

// GetProvider detects which provider suits the passed volume and returns it
func GetProvider(c *conplicity.Conplicity, v *types.Volume) Provider {
	log.WithFields(log.Fields{
		"volume": v.Name,
	}).Info("Detecting provider")
	p := &BaseProvider{
		handler: c,
		vol:     v,
	}
	if f, err := os.Stat(v.Mountpoint + "/PG_VERSION"); err == nil && f.Mode().IsRegular() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("PG_VERSION file found, this should be a PostgreSQL datadir")
		return &PostgreSQLProvider{
			BaseProvider: p,
		}
	} else if f, err := os.Stat(v.Mountpoint + "/mysql"); err == nil && f.Mode().IsDir() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("mysql directory found, this should be MySQL datadir")
		return &MySQLProvider{
			BaseProvider: p,
		}
	} else if f, err := os.Stat(v.Mountpoint + "/DB_CONFIG"); err == nil && f.Mode().IsRegular() {
		log.WithFields(log.Fields{
			"volume": v.Name,
		}).Debug("DB_CONFIG file found, this should be and OpenLDAP datadir")
		return &OpenLDAPProvider{
			BaseProvider: p,
		}
	}

	return &DefaultProvider{
		BaseProvider: p,
	}
}

// PrepareBackup sets up the data before backup
func PrepareBackup(p Provider) (err error) {
	c := p.GetHandler()
	vol := p.GetVolume()
	containers, err := c.ContainerList(context.Background(), types.ContainerListOptions{})
	conplicity.CheckErr(err, "Failed to list containers: %v", "fatal")

	// Work around https://github.com/docker/engine-api/issues/303
	client, err := docker.NewClient(c.Config.Docker.Endpoint, "", nil, nil)
	CheckErr(err, "Failed to create new Docker client: %v", "fatal")

	for _, container := range containers {
		container, err := client.ContainerInspect(context.Background(), container.ID)
		conplicity.CheckErr(err, "Failed to inspect container "+container.ID+": %v", "fatal")
		for _, mount := range container.Mounts {
			if mount.Name == vol.Name {
				log.WithFields(log.Fields{
					"volume":    vol.Name,
					"container": container.ID,
				}).Debug("Container found using volume")

				cmd := p.GetPrepareCommand(&mount)
				if cmd != nil {
					exec, err := client.ContainerExecCreate(context.Background(), container.ID, types.ExecConfig{
						Cmd: p.GetPrepareCommand(&mount),
					},
					)

					conplicity.CheckErr(err, "Failed to create exec: %v", "fatal")

					err = client.ContainerExecStart(context.Background(), exec.ID, types.ExecStartCheck{})

					conplicity.CheckErr(err, "Failed to start exec: %v", "fatal")
				} else {
					log.WithFields(log.Fields{
						"volume":    vol.Name,
						"container": container.ID,
					}).Info("No prepare command to execute in container")
				}
			}
		}
	}
	return
}

// BackupVolume performs the backup of the passed volume
func (p *BaseProvider) BackupVolume(vol *types.Volume) (err error) {
	log.WithFields(log.Fields{
		"volume":     vol.Name,
		"driver":     vol.Driver,
		"mountpoint": vol.Mountpoint,
	}).Info("Creating duplicity container")

	c := p.GetHandler()

	fullIfOlderThan, _ := conplicity.GetVolumeLabel(vol, ".full_if_older_than")
	if fullIfOlderThan == "" {
		fullIfOlderThan = c.Config.Duplicity.FullIfOlderThan
	}

	removeOlderThan, _ := conplicity.GetVolumeLabel(vol, ".remove_older_than")
	if removeOlderThan == "" {
		removeOlderThan = c.Config.Duplicity.RemoveOlderThan
	}

	pathSeparator := "/"
	if strings.HasPrefix(c.Config.Duplicity.TargetURL, "swift://") {
		// Looks like I'm not the one to fall on this issue: http://stackoverflow.com/questions/27991960/upload-to-swift-pseudo-folders-using-duplicity
		pathSeparator = "_"
	}

	backupDir := p.GetBackupDir()
	fullTarget := c.Config.Duplicity.TargetURL + pathSeparator + c.Hostname + pathSeparator + vol.Name
	fullBackupDir := vol.Mountpoint + "/" + backupDir
	roMount := vol.Name + ":" + vol.Mountpoint + ":ro"

	volume := &conplicity.Volume{
		Name:            vol.Name,
		Target:          fullTarget,
		BackupDir:       fullBackupDir,
		Mount:           roMount,
		FullIfOlderThan: fullIfOlderThan,
		RemoveOlderThan: removeOlderThan,
		Client:          c,
	}

	var newMetrics []string

	newMetrics, err = volume.Backup()
	conplicity.CheckErr(err, "Failed to backup volume "+vol.Name+" : %v", "fatal")
	c.Metrics = append(c.Metrics, newMetrics...)

	_, err = volume.RemoveOld()
	conplicity.CheckErr(err, "Failed to remove old backups for volume "+vol.Name+" : %v", "fatal")

	_, err = volume.Cleanup()
	conplicity.CheckErr(err, "Failed to cleanup extraneous duplicity files for volume "+vol.Name+" : %v", "fatal")

	noVerifyLbl, _ := conplicity.GetVolumeLabel(vol, ".no_verify")
	noVerify := c.Config.NoVerify || (noVerifyLbl == "true")
	if noVerify {
		log.WithFields(log.Fields{
			"volume": vol.Name,
		}).Info("Skipping verification")
	} else {
		newMetrics, err = volume.Verify()
		conplicity.CheckErr(err, "Failed to verify backup for volume "+vol.Name+" : %v", "fatal")
		c.Metrics = append(c.Metrics, newMetrics...)
	}

	newMetrics, err = volume.Status()
	conplicity.CheckErr(err, "Failed to retrieve last backup info for volume "+vol.Name+" : %v", "fatal")
	c.Metrics = append(c.Metrics, newMetrics...)

	return
}

// GetHandler returns the handler associated with the provider
func (p *BaseProvider) GetHandler() *conplicity.Conplicity {
	return p.handler
}

// GetVolume returns the volume associated with the provider
func (p *BaseProvider) GetVolume() *types.Volume {
	return p.vol
}

// GetBackupDir returns the backup directory used by the provider
func (p *BaseProvider) GetBackupDir() string {
	return p.backupDir
}
<|endoftext|>


<commit_before>package tcp

import (
	"errors"
	"fmt"
	"sync"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
)

// Global src, dst port and ip registry for TCP binding
type portManagerType struct {
	tcpReader ipv4.Reader
	incoming  map[uint16](map[uint16](map[ipv4.Hash](chan *packet))) // dst, src port, remote ip
	lock      *sync.RWMutex
}

func (m *portManagerType) GetUnusedPort() (uint16, error) {
	for i := minPort; i <= maxPort; i++ {
		if _, exists := m.incoming[i]; !exists {
			return i, nil
		}
	}
	return uint16(0), errors.New("No ports available to bind to")
}

var portManager = func() *portManagerType { // TODO use an init function
	ipr, err := ipv4.NewReader(ipv4.IPAll, ipv4.IPProtoTCP)
	if err != nil {
		logs.Error.Println(err)
		return nil
	}

	m := &portManagerType{
		tcpReader: ipr,
		incoming:  make(map[uint16](map[uint16](map[ipv4.Hash](chan *packet)))),
		lock:      &sync.RWMutex{},
	}
	go m.readAll()
	return m
}()

func (m *portManagerType) bind(rport, lport uint16, ip *ipv4.Address) (chan *packet, error) {
	// race prevention
	m.lock.Lock()
	defer m.lock.Unlock()

	// lport is the local one here, rport is the remote
	/*logs*/ logs.Info.Println("Attempting to bind to rport", rport, "lport", lport, "ip", ip.Hash())
	if _, ok := m.incoming[lport]; !ok {
		m.incoming[lport] = make(map[uint16](map[ipv4.Hash](chan *packet)))
	}

	// TODO add an option (for servers) for all srcports
	if _, ok := m.incoming[lport][rport]; !ok {
		m.incoming[lport][rport] = make(map[ipv4.Hash](chan *packet))
	}

	if _, ok := m.incoming[lport][rport][ip.Hash()]; ok {
		return nil, fmt.Errorf("Ports (lport: %d, rport %d) and IP (%v) already binded to", lport, rport, ip)
	}

	ans := make(chan *packet, incomingBufferSize)
	m.incoming[lport][rport][ip.Hash()] = ans
	return ans, nil
}

func (m *portManagerType) unbind(rport, lport uint16, ip *ipv4.Address) error {
	// race prevention
	m.lock.Lock()
	defer m.lock.Unlock()

	// TODO verify that it actually won't crash
	close(m.incoming[lport][rport][ip.Hash()])
	/*logs*/ logs.Trace.Println("Closing the packetdealer channel")
	delete(m.incoming[lport][rport], ip.Hash())
	return nil
}

func (m *portManagerType) readAll() {
	for {
		header, err := m.tcpReader.ReadFrom()
		if err != nil {
			logs.Error.Println("TCP readAll error", err)
			continue
		}

		err = m.readDeal(header.Rip, header.Lip, header.Payload)
		if err != nil {
			logs.Error.Println(err)
			continue
		}
	}
}

func (m *portManagerType) readDeal(rip, lip *ipv4.Address, payload []byte) error {
	p, err := extractPacket(payload, rip, lip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	rport := p.header.srcport
	lport := p.header.dstport

	var output chan *packet

	m.lock.RLock()
	defer m.lock.RUnlock()
	///*logs*/logs.Trace.Printf("readAll tcp packet manager dealing with packet or rport: %d and lport %d", rport, lport)
	if _, ok := m.incoming[lport]; ok {
		///*logs*/logs.Trace.Printf("readAll: promising packet rport: %d and lport %d", rport, lport)
		if p, ok := m.incoming[lport][rport]; ok {
			///*logs*/logs.Trace.Println("readAll: exact port number match")
			if x, ok := p[rip.Hash()]; ok {
				output = x
			} else if x, ok := p[ipv4.IPAllHash]; ok {
				output = x
			}
		} else if p, ok := m.incoming[lport][0]; ok {
			///*logs*/logs.Trace.Println("readAll: forwarding to a listening server")
			if x, ok := p[ipv4.IPAllHash]; ok {
				output = x
			} else if x, ok := p[rip.Hash()]; ok {
				output = x
			}
		}
	}

	if output != nil {
		select {
		case output <- p:
		default:
			logs.Warn.Println("Dropping TCP packet: no space in buffer")
		}
	} else {
		// TODO send a rst to sender if nothing is binded to the dst port, src port, and remote ip
		//fmt.Println(errors.New("Dst/Src port + ip not binded to"))
		logs.Warn.Println(fmt.Errorf("Dropping TCP packet (lport: %d, rport %d); nothing listening for it", lport, rport))
	}

	return nil
}
<commit_msg>Fixed a data race in tcp<commit_after>package tcp

import (
	"errors"
	"fmt"
	"sync"

	"github.com/hsheth2/gonet/ipv4"

	"github.com/hsheth2/logs"
)

// Global src, dst port and ip registry for TCP binding
type portManagerType struct {
	tcpReader ipv4.Reader
	incoming  map[uint16](map[uint16](map[ipv4.Hash](chan *packet))) // dst, src port, remote ip
	lock      *sync.RWMutex
}

func (m *portManagerType) GetUnusedPort() (uint16, error) {
	// race prevention
	m.lock.Lock()
	defer m.lock.Unlock()

	for i := minPort; i <= maxPort; i++ {
		if _, exists := m.incoming[i]; !exists {
			return i, nil
		}
	}
	return uint16(0), errors.New("No ports available to bind to")
}

var portManager = func() *portManagerType { // TODO use an init function
	ipr, err := ipv4.NewReader(ipv4.IPAll, ipv4.IPProtoTCP)
	if err != nil {
		logs.Error.Println(err)
		return nil
	}

	m := &portManagerType{
		tcpReader: ipr,
		incoming:  make(map[uint16](map[uint16](map[ipv4.Hash](chan *packet)))),
		lock:      &sync.RWMutex{},
	}
	go m.readAll()
	return m
}()

func (m *portManagerType) bind(rport, lport uint16, ip *ipv4.Address) (chan *packet, error) {
	// race prevention
	m.lock.Lock()
	defer m.lock.Unlock()

	// lport is the local one here, rport is the remote
	/*logs*/ logs.Info.Println("Attempting to bind to rport", rport, "lport", lport, "ip", ip.Hash())
	if _, ok := m.incoming[lport]; !ok {
		m.incoming[lport] = make(map[uint16](map[ipv4.Hash](chan *packet)))
	}

	// TODO add an option (for servers) for all srcports
	if _, ok := m.incoming[lport][rport]; !ok {
		m.incoming[lport][rport] = make(map[ipv4.Hash](chan *packet))
	}

	if _, ok := m.incoming[lport][rport][ip.Hash()]; ok {
		return nil, fmt.Errorf("Ports (lport: %d, rport %d) and IP (%v) already binded to", lport, rport, ip)
	}

	ans := make(chan *packet, incomingBufferSize)
	m.incoming[lport][rport][ip.Hash()] = ans
	return ans, nil
}

func (m *portManagerType) unbind(rport, lport uint16, ip *ipv4.Address) error {
	// race prevention
	m.lock.Lock()
	defer m.lock.Unlock()

	// TODO verify that it actually won't crash
	close(m.incoming[lport][rport][ip.Hash()])
	/*logs*/ logs.Trace.Println("Closing the packetdealer channel")
	delete(m.incoming[lport][rport], ip.Hash())
	return nil
}

func (m *portManagerType) readAll() {
	for {
		header, err := m.tcpReader.ReadFrom()
		if err != nil {
			logs.Error.Println("TCP readAll error", err)
			continue
		}

		err = m.readDeal(header.Rip, header.Lip, header.Payload)
		if err != nil {
			logs.Error.Println(err)
			continue
		}
	}
}

func (m *portManagerType) readDeal(rip, lip *ipv4.Address, payload []byte) error {
	p, err := extractPacket(payload, rip, lip)
	if err != nil {
		logs.Error.Println(err)
		return err
	}

	rport := p.header.srcport
	lport := p.header.dstport

	var output chan *packet

	m.lock.RLock()
	defer m.lock.RUnlock()
	///*logs*/logs.Trace.Printf("readAll tcp packet manager dealing with packet or rport: %d and lport %d", rport, lport)
	if _, ok := m.incoming[lport]; ok {
		///*logs*/logs.Trace.Printf("readAll: promising packet rport: %d and lport %d", rport, lport)
		if p, ok := m.incoming[lport][rport]; ok {
			///*logs*/logs.Trace.Println("readAll: exact port number match")
			if x, ok := p[rip.Hash()]; ok {
				output = x
			} else if x, ok := p[ipv4.IPAllHash]; ok {
				output = x
			}
		} else if p, ok := m.incoming[lport][0]; ok {
			///*logs*/logs.Trace.Println("readAll: forwarding to a listening server")
			if x, ok := p[ipv4.IPAllHash]; ok {
				output = x
			} else if x, ok := p[rip.Hash()]; ok {
				output = x
			}
		}
	}

	if output != nil {
		select {
		case output <- p:
		default:
			logs.Warn.Println("Dropping TCP packet: no space in buffer")
		}
	} else {
		// TODO send a rst to sender if nothing is binded to the dst port, src port, and remote ip
		//fmt.Println(errors.New("Dst/Src port + ip not binded to"))
		logs.Warn.Println(fmt.Errorf("Dropping TCP packet (lport: %d, rport %d); nothing listening for it", lport, rport))
	}

	return nil
}
<|endoftext|>


<commit_before>package random

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"

	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/helper/xor"
	"github.com/hashicorp/vault/sdk/logical"
)

const APIMaxBytes = 128 * 1024

func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logical.Response, error) {
	bytes := 0
	// Parsing is convoluted here, but allows operators to ACL both source and byte count
	maybeUrlBytes := d.Raw["urlbytes"]
	maybeSource := d.Raw["source"]
	source := "platform"
	var err error
	if maybeSource == "" {
		bytes = d.Get("bytes").(int)
	} else if maybeUrlBytes == "" && isValidSource(maybeSource.(string)) {
		source = maybeSource.(string)
		bytes = d.Get("bytes").(int)
	} else if maybeUrlBytes == "" {
		bytes, err = strconv.Atoi(maybeSource.(string))
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
		}
	} else {
		source = maybeSource.(string)
		bytes, err = strconv.Atoi(maybeUrlBytes.(string))
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
		}
	}
	format := d.Get("format").(string)

	if bytes < 1 {
		return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil
	}

	if bytes > APIMaxBytes {
		return logical.ErrorResponse(`"bytes" should be less than %d`, APIMaxBytes), nil
	}

	switch format {
	case "hex":
	case "base64":
	default:
		return logical.ErrorResponse("unsupported encoding format %q; must be \"hex\" or \"base64\"", format), nil
	}

	var randBytes []byte
	var warning string
	switch source {
	case "", "platform":
		randBytes, err = uuid.GenerateRandomBytes(bytes)
		if err != nil {
			return nil, err
		}
	case "seal":
		if rand.Reader == additionalSource {
			warning = "no seal/entropy augmentation available, using platform entropy source"
		}
		randBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
	case "all":
		var sealBytes []byte
		sealBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
		if err == nil {
			randBytes, err = uuid.GenerateRandomBytes(bytes)
			if err == nil {
				randBytes, err = xor.XORBytes(sealBytes, randBytes)
			}
		}
	default:
		return logical.ErrorResponse("unsupported entropy source %q; must be \"platform\" or \"seal\", or \"all\"", source), nil
	}
	if err != nil {
		return nil, err
	}

	var retStr string
	switch format {
	case "hex":
		retStr = hex.EncodeToString(randBytes)
	case "base64":
		retStr = base64.StdEncoding.EncodeToString(randBytes)
	}

	// Generate the response
	resp := &logical.Response{
		Data: map[string]interface{}{
			"random_bytes": retStr,
		},
	}
	if warning != "" {
		resp.Warnings = []string{warning}
	}
	return resp, nil
}

func isValidSource(s string) bool {
	switch s {
	case "", "platform", "seal", "all":
		return true
	}
	return false
}
<commit_msg>Only use additional entropy source when configured within transit and sys random (#15734)<commit_after>package random

import (
	"crypto/rand"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"

	"github.com/hashicorp/go-uuid"
	"github.com/hashicorp/vault/sdk/framework"
	"github.com/hashicorp/vault/sdk/helper/xor"
	"github.com/hashicorp/vault/sdk/logical"
)

const APIMaxBytes = 128 * 1024

func HandleRandomAPI(d *framework.FieldData, additionalSource io.Reader) (*logical.Response, error) {
	bytes := 0
	// Parsing is convoluted here, but allows operators to ACL both source and byte count
	maybeUrlBytes := d.Raw["urlbytes"]
	maybeSource := d.Raw["source"]
	source := "platform"
	var err error
	if maybeSource == "" {
		bytes = d.Get("bytes").(int)
	} else if maybeUrlBytes == "" && isValidSource(maybeSource.(string)) {
		source = maybeSource.(string)
		bytes = d.Get("bytes").(int)
	} else if maybeUrlBytes == "" {
		bytes, err = strconv.Atoi(maybeSource.(string))
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
		}
	} else {
		source = maybeSource.(string)
		bytes, err = strconv.Atoi(maybeUrlBytes.(string))
		if err != nil {
			return logical.ErrorResponse(fmt.Sprintf("error parsing url-set byte count: %s", err)), nil
		}
	}
	format := d.Get("format").(string)

	if bytes < 1 {
		return logical.ErrorResponse(`"bytes" cannot be less than 1`), nil
	}

	if bytes > APIMaxBytes {
		return logical.ErrorResponse(`"bytes" should be less than %d`, APIMaxBytes), nil
	}

	switch format {
	case "hex":
	case "base64":
	default:
		return logical.ErrorResponse("unsupported encoding format %q; must be \"hex\" or \"base64\"", format), nil
	}

	var randBytes []byte
	var warning string
	switch source {
	case "", "platform":
		randBytes, err = uuid.GenerateRandomBytes(bytes)
		if err != nil {
			return nil, err
		}
	case "seal":
		if rand.Reader == additionalSource {
			warning = "no seal/entropy augmentation available, using platform entropy source"
		}
		randBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
	case "all":
		randBytes, err = uuid.GenerateRandomBytes(bytes)
		if err == nil && rand.Reader != additionalSource {
			var sealBytes []byte
			sealBytes, err = uuid.GenerateRandomBytesWithReader(bytes, additionalSource)
			if err == nil {
				randBytes, err = xor.XORBytes(sealBytes, randBytes)
			}
		}
	default:
		return logical.ErrorResponse("unsupported entropy source %q; must be \"platform\" or \"seal\", or \"all\"", source), nil
	}
	if err != nil {
		return nil, err
	}

	var retStr string
	switch format {
	case "hex":
		retStr = hex.EncodeToString(randBytes)
	case "base64":
		retStr = base64.StdEncoding.EncodeToString(randBytes)
	}

	// Generate the response
	resp := &logical.Response{
		Data: map[string]interface{}{
			"random_bytes": retStr,
		},
	}
	if warning != "" {
		resp.Warnings = []string{warning}
	}
	return resp, nil
}

func isValidSource(s string) bool {
	switch s {
	case "", "platform", "seal", "all":
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package helpers_test

import (
	"crypto/x509"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/diego-ssh/helpers"
	"code.cloudfoundry.org/inigo/helpers/certauthority"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("NewHTTPSClient", func() {
	var (
		caCertFiles        []string
		insecureSkipVerify bool
		timeout            time.Duration
	)

	BeforeEach(func() {
		caCertFiles = []string{}
	})

	It("sets InsecureSkipVerify on the TLS config", func() {
		client, err := helpers.NewHTTPSClient(true, caCertFiles, timeout)
		Expect(err).NotTo(HaveOccurred())
		httpTrans, ok := client.Transport.(*http.Transport)
		Expect(ok).To(BeTrue())
		Expect(httpTrans.TLSClientConfig.InsecureSkipVerify).To(BeTrue())
	})

	It("sets the client timeout", func() {
		client, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, 5*time.Second)
		Expect(err).NotTo(HaveOccurred())
		Expect(client.Timeout).To(Equal(5 * time.Second))
	})

	Context("when a list of ca Cert files is provided", func() {
		var (
			certDepotDir string
			ca           certauthority.CertAuthority
		)

		BeforeEach(func() {
			var err error
			certDepotDir, err = ioutil.TempDir("", "")
			Expect(err).NotTo(HaveOccurred())

			ca, err = certauthority.NewCertAuthority(certDepotDir, "one")
			Expect(err).NotTo(HaveOccurred())

			_, cert := ca.CAAndKey()
			caCertFiles = []string{cert}
		})

		AfterEach(func() {
			Expect(os.RemoveAll(certDepotDir)).To(Succeed())
		})

		It("sets the RootCAs with a pool consisting of those CAs", func() {
			expectedPool := x509.NewCertPool()
			for _, caCert := range caCertFiles {
				certBytes, err := ioutil.ReadFile(caCert)
				Expect(err).NotTo(HaveOccurred())

				Expect(expectedPool.AppendCertsFromPEM(certBytes)).To(BeTrue())
			}

			client, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
			Expect(err).NotTo(HaveOccurred())
			httpTrans, ok := client.Transport.(*http.Transport)
			Expect(ok).To(BeTrue())

			caPool := httpTrans.TLSClientConfig.RootCAs

			Expect(expectedPool).To(Equal(caPool))
		})

		Context("when an invalid tls cert is provided", func() {
			var invalidCertPath string

			BeforeEach(func() {
				invalidCert, err := ioutil.TempFile("", "")
				Expect(err).NotTo(HaveOccurred())

				invalidCertPath = invalidCert.Name()

				Expect(invalidCert.Close()).To(Succeed())

				Expect(ioutil.WriteFile(invalidCertPath, []byte("not valid pem"), 0644)).To(Succeed())

				caCertFiles = append(caCertFiles, invalidCertPath)
			})

			AfterEach(func() {
				Expect(os.Remove(invalidCertPath)).To(Succeed())
			})

			It("returns an error", func() {
				_, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("Unable to load caCert"))
			})
		})

		Context("when the UAA tls cert does not exist", func() {
			BeforeEach(func() {
				caCertFiles = append(caCertFiles, "doesntexist")
			})

			It("returns an error", func() {
				_, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("failed to read ca cert file"))
			})
		})
	})
})
<commit_msg>as of Go1.16 CertPools can't be compared<commit_after>package helpers_test

import (
	"crypto/x509"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	"code.cloudfoundry.org/diego-ssh/helpers"
	"code.cloudfoundry.org/inigo/helpers/certauthority"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("NewHTTPSClient", func() {
	var (
		caCertFiles        []string
		insecureSkipVerify bool
		timeout            time.Duration
	)

	BeforeEach(func() {
		caCertFiles = []string{}
	})

	It("sets InsecureSkipVerify on the TLS config", func() {
		client, err := helpers.NewHTTPSClient(true, caCertFiles, timeout)
		Expect(err).NotTo(HaveOccurred())
		httpTrans, ok := client.Transport.(*http.Transport)
		Expect(ok).To(BeTrue())
		Expect(httpTrans.TLSClientConfig.InsecureSkipVerify).To(BeTrue())
	})

	It("sets the client timeout", func() {
		client, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, 5*time.Second)
		Expect(err).NotTo(HaveOccurred())
		Expect(client.Timeout).To(Equal(5 * time.Second))
	})

	Context("when a list of ca Cert files is provided", func() {
		var (
			certDepotDir string
			ca           certauthority.CertAuthority
		)

		BeforeEach(func() {
			var err error
			certDepotDir, err = ioutil.TempDir("", "")
			Expect(err).NotTo(HaveOccurred())

			ca, err = certauthority.NewCertAuthority(certDepotDir, "one")
			Expect(err).NotTo(HaveOccurred())

			_, cert := ca.CAAndKey()
			caCertFiles = []string{cert}
		})

		AfterEach(func() {
			Expect(os.RemoveAll(certDepotDir)).To(Succeed())
		})

		It("sets the RootCAs with a pool consisting of those CAs", func() {
			expectedPool := x509.NewCertPool()
			for _, caCert := range caCertFiles {
				certBytes, err := ioutil.ReadFile(caCert)
				Expect(err).NotTo(HaveOccurred())

				Expect(expectedPool.AppendCertsFromPEM(certBytes)).To(BeTrue())
			}

			client, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
			Expect(err).NotTo(HaveOccurred())
			httpTrans, ok := client.Transport.(*http.Transport)
			Expect(ok).To(BeTrue())

			caPool := httpTrans.TLSClientConfig.RootCAs

			Expect(expectedPool.Subjects()).To(Equal(caPool.Subjects()))
		})

		Context("when an invalid tls cert is provided", func() {
			var invalidCertPath string

			BeforeEach(func() {
				invalidCert, err := ioutil.TempFile("", "")
				Expect(err).NotTo(HaveOccurred())

				invalidCertPath = invalidCert.Name()

				Expect(invalidCert.Close()).To(Succeed())

				Expect(ioutil.WriteFile(invalidCertPath, []byte("not valid pem"), 0644)).To(Succeed())

				caCertFiles = append(caCertFiles, invalidCertPath)
			})

			AfterEach(func() {
				Expect(os.Remove(invalidCertPath)).To(Succeed())
			})

			It("returns an error", func() {
				_, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("Unable to load caCert"))
			})
		})

		Context("when the UAA tls cert does not exist", func() {
			BeforeEach(func() {
				caCertFiles = append(caCertFiles, "doesntexist")
			})

			It("returns an error", func() {
				_, err := helpers.NewHTTPSClient(insecureSkipVerify, caCertFiles, timeout)
				Expect(err).To(HaveOccurred())
				Expect(err.Error()).To(ContainSubstring("failed to read ca cert file"))
			})
		})
	})
})
<|endoftext|>


<commit_before>package main // import "github.com/ONSdigital/go-launch-a-survey"

import (
	"fmt"

	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/ONSdigital/go-launch-a-survey/authentication"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"github.com/ONSdigital/go-launch-a-survey/surveys"
	"github.com/gorilla/mux"
	"gopkg.in/square/go-jose.v2/json"
	"html"
	"github.com/satori/go.uuid"
)

func serveTemplate(templateName string, data interface{}, w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(templateName))

	// Return a 404 if the template doesn't exist or is directory
	info, err := os.Stat(fp)
	if err != nil && (os.IsNotExist(err) || info.IsDir()) {
		log.Println("Cannot find: " + fp)
		http.NotFound(w, r)
		return
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

type page struct {
	Schemas           surveys.LauncherSchemas
	AccountServiceURL string
}

func getLaunchHandler(w http.ResponseWriter, r *http.Request) {
	p := page{
		Schemas:           surveys.GetAvailableSchemas(),
		AccountServiceURL: getAccountURL(r),
	}
	serveTemplate("launch.html", p, w, r)
}

func postLaunchHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("POST. r.ParseForm() err: %v", err), 500)
		return
	}
	redirectURL(w, r)
}

func getMetadataHandler(w http.ResponseWriter, r *http.Request) {
	schema := r.URL.Query().Get("schema")
	launcherSchema := surveys.FindSurveyByName(schema)

	metadata, err := authentication.GetRequiredMetadata(launcherSchema)

	if err != "" {
		http.Error(w, fmt.Sprintf("GetRequiredMetadata err: %v", err), 500)
		return
	}

	metadataJSON, _ := json.Marshal(metadata)

	w.Write([]byte(metadataJSON))

	return
}

func getAccountURL(r *http.Request) string {
	forwardedProtocol := r.Header.Get("X-Forwarded-Proto")

	requestProtocol := "http"

	if forwardedProtocol != "" {
		requestProtocol = forwardedProtocol
	}

	return fmt.Sprintf("%s://%s",
		requestProtocol,
		html.EscapeString(r.Host))
}

func redirectURL(w http.ResponseWriter, r *http.Request) {
	hostURL := settings.Get("SURVEY_RUNNER_URL")

	token, err := authentication.GenerateTokenFromPost(r.PostForm)
	if err != "" {
		http.Error(w, err, 500)
		return
	}

	launchAction := r.PostForm.Get("action_launch")
	flushAction := r.PostForm.Get("action_flush")
	log.Println("Request: " + r.PostForm.Encode())

	if flushAction != "" {
		http.Redirect(w, r, hostURL+"/flush?token="+token, 307)
	} else if launchAction != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 301)
	} else {
		http.Error(w, fmt.Sprintf("Invalid Action"), 500)
	}
}

func quickLauncherHandler(w http.ResponseWriter, r *http.Request) {
	hostURL := settings.Get("SURVEY_RUNNER_URL")
	accountURL := getAccountURL(r)
	urlValues := r.URL.Query()
	surveyURL := urlValues.Get("url")
	log.Println("Quick launch request received", surveyURL)

	urlValues.Add("ru_ref", authentication.GetDefaultValues()["ru_ref"])
	urlValues.Add("collection_exercise_sid", uuid.NewV4().String())

	token, err := authentication.GenerateTokenFromDefaults(surveyURL, accountURL, urlValues)
	if err != "" {
		http.Error(w, err, 500)
		return
	}

	if surveyURL != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 302)
	} else {
		http.Error(w, fmt.Sprintf("Not Found"), 404)
	}
}

func main() {
	r := mux.NewRouter()

	// Launch handlers
	r.HandleFunc("/", getLaunchHandler).Methods("GET")
	r.HandleFunc("/", postLaunchHandler).Methods("POST")
	r.HandleFunc("/metadata", getMetadataHandler).Methods("GET")

	//Author Launcher with passed parameters in Url
	r.HandleFunc("/quick-launch", quickLauncherHandler).Methods("GET")

	// Serve static assets
	staticFs := http.FileServer(http.Dir("static"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticFs))

	// Bind to a port and pass our router in
	hostname := settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_HOST") + ":" + settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_PORT")

	log.Println("Listening on " + hostname)
	log.Fatal(http.ListenAndServe(hostname, r))
}
<commit_msg>Add a status page<commit_after>package main // import "github.com/ONSdigital/go-launch-a-survey"

import (
	"fmt"

	"html/template"
	"log"
	"net/http"
	"os"
	"path/filepath"

	"github.com/ONSdigital/go-launch-a-survey/authentication"
	"github.com/ONSdigital/go-launch-a-survey/settings"
	"github.com/ONSdigital/go-launch-a-survey/surveys"
	"github.com/gorilla/mux"
	"gopkg.in/square/go-jose.v2/json"
	"html"
	"github.com/satori/go.uuid"
)

func serveTemplate(templateName string, data interface{}, w http.ResponseWriter, r *http.Request) {
	lp := filepath.Join("templates", "layout.html")
	fp := filepath.Join("templates", filepath.Clean(templateName))

	// Return a 404 if the template doesn't exist or is directory
	info, err := os.Stat(fp)
	if err != nil && (os.IsNotExist(err) || info.IsDir()) {
		log.Println("Cannot find: " + fp)
		http.NotFound(w, r)
		return
	}

	tmpl, err := template.ParseFiles(lp, fp)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
		return
	}

	if err := tmpl.ExecuteTemplate(w, "layout", data); err != nil {
		log.Println(err.Error())
		http.Error(w, http.StatusText(500), 500)
	}
}

type page struct {
	Schemas           surveys.LauncherSchemas
	AccountServiceURL string
}

func getStatusPage(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("OK"))
}

func getLaunchHandler(w http.ResponseWriter, r *http.Request) {
	p := page{
		Schemas:           surveys.GetAvailableSchemas(),
		AccountServiceURL: getAccountURL(r),
	}
	serveTemplate("launch.html", p, w, r)
}

func postLaunchHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
	if err != nil {
		http.Error(w, fmt.Sprintf("POST. r.ParseForm() err: %v", err), 500)
		return
	}
	redirectURL(w, r)
}

func getMetadataHandler(w http.ResponseWriter, r *http.Request) {
	schema := r.URL.Query().Get("schema")
	launcherSchema := surveys.FindSurveyByName(schema)

	metadata, err := authentication.GetRequiredMetadata(launcherSchema)

	if err != "" {
		http.Error(w, fmt.Sprintf("GetRequiredMetadata err: %v", err), 500)
		return
	}

	metadataJSON, _ := json.Marshal(metadata)

	w.Write([]byte(metadataJSON))

	return
}

func getAccountURL(r *http.Request) string {
	forwardedProtocol := r.Header.Get("X-Forwarded-Proto")

	requestProtocol := "http"

	if forwardedProtocol != "" {
		requestProtocol = forwardedProtocol
	}

	return fmt.Sprintf("%s://%s",
		requestProtocol,
		html.EscapeString(r.Host))
}

func redirectURL(w http.ResponseWriter, r *http.Request) {
	hostURL := settings.Get("SURVEY_RUNNER_URL")

	token, err := authentication.GenerateTokenFromPost(r.PostForm)
	if err != "" {
		http.Error(w, err, 500)
		return
	}

	launchAction := r.PostForm.Get("action_launch")
	flushAction := r.PostForm.Get("action_flush")
	log.Println("Request: " + r.PostForm.Encode())

	if flushAction != "" {
		http.Redirect(w, r, hostURL+"/flush?token="+token, 307)
	} else if launchAction != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 301)
	} else {
		http.Error(w, fmt.Sprintf("Invalid Action"), 500)
	}
}

func quickLauncherHandler(w http.ResponseWriter, r *http.Request) {
	hostURL := settings.Get("SURVEY_RUNNER_URL")
	accountURL := getAccountURL(r)
	urlValues := r.URL.Query()
	surveyURL := urlValues.Get("url")
	log.Println("Quick launch request received", surveyURL)

	urlValues.Add("ru_ref", authentication.GetDefaultValues()["ru_ref"])
	urlValues.Add("collection_exercise_sid", uuid.NewV4().String())

	token, err := authentication.GenerateTokenFromDefaults(surveyURL, accountURL, urlValues)
	if err != "" {
		http.Error(w, err, 500)
		return
	}

	if surveyURL != "" {
		http.Redirect(w, r, hostURL+"/session?token="+token, 302)
	} else {
		http.Error(w, fmt.Sprintf("Not Found"), 404)
	}
}

func main() {
	r := mux.NewRouter()

	// Launch handlers
	r.HandleFunc("/", getLaunchHandler).Methods("GET")
	r.HandleFunc("/", postLaunchHandler).Methods("POST")
	r.HandleFunc("/metadata", getMetadataHandler).Methods("GET")

	//Author Launcher with passed parameters in Url
	r.HandleFunc("/quick-launch", quickLauncherHandler).Methods("GET")

	// Status Page
	r.HandleFunc("/status", getStatusPage).Methods("GET")

	// Serve static assets
	staticFs := http.FileServer(http.Dir("static"))
	r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", staticFs))

	// Bind to a port and pass our router in
	hostname := settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_HOST") + ":" + settings.Get("GO_LAUNCH_A_SURVEY_LISTEN_PORT")

	log.Println("Listening on " + hostname)
	log.Fatal(http.ListenAndServe(hostname, r))
}
<|endoftext|>


<commit_before>package termite

import (
	"fmt"
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var _ = fmt.Println

// TODO - we could remember md5s for files outside the writable root
// so we don't read stuff under /usr for every master startup.

type FsServer struct {
	contentServer *ContentServer
	contentCache  *ContentCache
	Root          string
	excluded      map[string]bool

	hashCacheMutex sync.RWMutex
	// TODO - should use string (immutable) throughout for storing MD5 signatures.
	hashCache map[string][]byte
}

func NewFsServer(root string, cache *ContentCache, excluded []string) *FsServer {
	fs := &FsServer{
		contentCache:  cache,
		contentServer: &ContentServer{Cache: cache},
		Root:          root,
		hashCache:     make(map[string][]byte),
	}

	fs.excluded = make(map[string]bool)
	for _, e := range excluded {
		fs.excluded[e] = true
	}
	return fs
}

type AttrRequest struct {
	Name string
}

type AttrResponse struct {
	Path string // Used in WorkReply
	*os.FileInfo
	fuse.Status
	Hash    []byte
	Link    string
	Content []byte // optional.
}

func (me AttrResponse) String() string {
	id := ""
	if me.Hash != nil {
		id = fmt.Sprintf(" sz %d", me.FileInfo.Size)
	}
	if me.Link != "" {
		id = fmt.Sprintf(" -> %s", me.Link)
	}
	if me.Deletion() {
		id = " (del)"
	}
	return fmt.Sprintf("%s%s", me.Path, id)
}

func (me AttrResponse) Deletion() bool {
	return me.Status == fuse.ENOENT
}

type DirRequest struct {
	Name string
}

type DirResponse struct {
	NameModeMap map[string]uint32
}

func (me *FsServer) path(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func (me *FsServer) ReadDir(req *DirRequest, r *DirResponse) os.Error {
	d, e := ioutil.ReadDir(me.path(req.Name))
	log.Println("ReadDir", req)
	r.NameModeMap = make(map[string]uint32)
	for _, v := range d {
		r.NameModeMap[v.Name] = v.Mode
	}
	return e
}

func (me *FsServer) GetAttr(req *AttrRequest, rep *AttrResponse) os.Error {
	log.Println("GetAttr req", req.Name)
	if me.excluded[req.Name] {
		rep.Status = fuse.ENOENT
		return nil
	}

	fi, err := os.Lstat(me.path(req.Name))
	rep.FileInfo = fi
	rep.Status = fuse.OsErrorToErrno(err)
	if fi == nil {
		return nil
	}

	if fi.IsSymlink() {
		rep.Link, err = os.Readlink(req.Name)
	}
	if fi.IsRegular() {
		rep.Hash, rep.Content = me.getHash(req.Name)
	}
	log.Println("GetAttr", req.Name, rep)
	return nil
}

func (me *FsServer) updateHashes(infos []AttrResponse) {
	me.hashCacheMutex.Lock()
	defer me.hashCacheMutex.Unlock()

	for _, r := range infos {
		name := r.Path
		if !r.Status.Ok() || r.Link != "" {
			me.hashCache[name] = nil, false
		}
		if r.Hash != nil {
			me.hashCache[name] = r.Hash
		}
	}
}

func (me *FsServer) getHash(name string) (hash []byte, content []byte) {
	fullPath := me.path(name)

	me.hashCacheMutex.RLock()
	hash = me.hashCache[name]
	me.hashCacheMutex.RUnlock()

	if hash != nil {
		return []byte(hash), nil
	}

	hash, content = me.contentCache.SavePath(fullPath)

	// This is racy; we assume concurrent runs of this will reach
	// the same conclusion regarding the MD5 of the file.
	me.hashCacheMutex.Lock()
	defer me.hashCacheMutex.Unlock()

	me.hashCache[name] = hash
	return hash, content
}
<commit_msg>Note 2 TODOs.<commit_after>package termite

import (
	"fmt"
	"github.com/hanwen/go-fuse/fuse"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

var _ = fmt.Println

// TODO - we could remember md5s for files outside the writable root
// so we don't read stuff under /usr for every master startup.

type FsServer struct {
	contentServer *ContentServer
	contentCache  *ContentCache
	Root          string
	excluded      map[string]bool

	hashCacheMutex sync.RWMutex
	// TODO - should use string (immutable) throughout for storing MD5 signatures.
	hashCache map[string][]byte
}

func NewFsServer(root string, cache *ContentCache, excluded []string) *FsServer {
	fs := &FsServer{
		contentCache:  cache,
		contentServer: &ContentServer{Cache: cache},
		Root:          root,
		hashCache:     make(map[string][]byte),
	}

	fs.excluded = make(map[string]bool)
	for _, e := range excluded {
		fs.excluded[e] = true
	}
	return fs
}

type AttrRequest struct {
	Name string
}

type AttrResponse struct {
	Path string // Used in WorkReply
	*os.FileInfo
	fuse.Status
	Hash    []byte
	Link    string
	Content []byte // optional.
}

func (me AttrResponse) String() string {
	id := ""
	if me.Hash != nil {
		id = fmt.Sprintf(" sz %d", me.FileInfo.Size)
	}
	if me.Link != "" {
		id = fmt.Sprintf(" -> %s", me.Link)
	}
	if me.Deletion() {
		id = " (del)"
	}
	return fmt.Sprintf("%s%s", me.Path, id)
}

func (me AttrResponse) Deletion() bool {
	return me.Status == fuse.ENOENT
}

type DirRequest struct {
	Name string
}

type DirResponse struct {
	NameModeMap map[string]uint32
}

func (me *FsServer) path(n string) string {
	if me.Root == "" {
		return n
	}
	return filepath.Join(me.Root, strings.TrimLeft(n, "/"))
}

func (me *FsServer) FileContent(req *ContentRequest, rep *ContentResponse) os.Error {
	return me.contentServer.FileContent(req, rep)
}

func (me *FsServer) ReadDir(req *DirRequest, r *DirResponse) os.Error {
	d, e := ioutil.ReadDir(me.path(req.Name))
	log.Println("ReadDir", req)
	r.NameModeMap = make(map[string]uint32)
	for _, v := range d {
		r.NameModeMap[v.Name] = v.Mode
	}
	return e
}

func (me *FsServer) GetAttr(req *AttrRequest, rep *AttrResponse) os.Error {
	log.Println("GetAttr req", req.Name)
	// TODO - this is not a good security measure, as we are not
	// checking the prefix; someone might directly ask for
	// /forbidden/subdir/
	if me.excluded[req.Name] {
		rep.Status = fuse.ENOENT
		return nil
	}

	fi, err := os.Lstat(me.path(req.Name))
	rep.FileInfo = fi
	rep.Status = fuse.OsErrorToErrno(err)
	if fi == nil {
		return nil
	}

	if fi.IsSymlink() {
		rep.Link, err = os.Readlink(req.Name)
	}
	if fi.IsRegular() {
		rep.Hash, rep.Content = me.getHash(req.Name)
	}
	log.Println("GetAttr", req.Name, rep)
	return nil
}

func (me *FsServer) updateHashes(infos []AttrResponse) {
	me.hashCacheMutex.Lock()
	defer me.hashCacheMutex.Unlock()

	for _, r := range infos {
		name := r.Path
		if !r.Status.Ok() || r.Link != "" {
			me.hashCache[name] = nil, false
		}
		if r.Hash != nil {
			me.hashCache[name] = r.Hash
		}
	}
}

func (me *FsServer) getHash(name string) (hash []byte, content []byte) {
	fullPath := me.path(name)

	me.hashCacheMutex.RLock()
	hash = me.hashCache[name]
	me.hashCacheMutex.RUnlock()

	if hash != nil {
		return []byte(hash), nil
	}

	// TODO - stop concurrent runs for the same file, as this will
	// exhaust the master's CPU if all compiles ask for the same
	// files on startup.
	hash, content = me.contentCache.SavePath(fullPath)

	// This is racy; we assume concurrent runs of this will reach
	// the same conclusion regarding the MD5 of the file.
	me.hashCacheMutex.Lock()
	defer me.hashCacheMutex.Unlock()

	me.hashCache[name] = hash
	return hash, content
}
<|endoftext|>