<commit_before>/*
Copyright 2016 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 distros

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
)

// FindDistribution identifies the distribution on which we are running
// We will likely remove this when everything is containerized
func FindDistribution(rootfs string) (Distribution, error) {
	// Ubuntu has /etc/lsb-release (and /etc/debian_version)
	lsbRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/lsb-release"))
	if err == nil {
		for _, line := range strings.Split(string(lsbRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "DISTRIB_CODENAME=xenial" {
				return DistributionXenial, nil
			}
		}
		glog.Infof("could not determine OS from lsb-release info %q", string(lsbRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/lsb-release: %v", err)
	}

	// Debian has /etc/debian_version
	debianVersionBytes, err := ioutil.ReadFile(path.Join(rootfs, "etc/debian_version"))
	if err == nil {
		debianVersion := strings.TrimSpace(string(debianVersionBytes))
		if strings.HasPrefix(debianVersion, "8.") {
			return DistributionJessie, nil
		} else if strings.HasPrefix(debianVersion, "9.") {
			return DistributionDebian9, nil
		} else {
			return "", fmt.Errorf("unhandled debian version %q", debianVersion)
		}
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/debian_version: %v", err)
	}

	// Redhat has /etc/redhat-release
	// Centos has /etc/centos-release
	redhatRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/redhat-release"))
	if err == nil {
		for _, line := range strings.Split(string(redhatRelease), "\n") {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "Red Hat Enterprise Linux Server release 7.") {
				return DistributionRhel7, nil
			}
			if strings.HasPrefix(line, "CentOS Linux release 7.") {
				return DistributionCentos7, nil
			}
		}
		glog.Warningf("unhandled redhat-release info %q", string(lsbRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/redhat-release: %v", err)
	}

	// CoreOS uses /usr/lib/os-release
	osRelease, err := ioutil.ReadFile(path.Join(rootfs, "usr/lib/os-release"))
	if err == nil {
		for _, line := range strings.Split(string(osRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "ID=coreos" {
				return DistributionCoreOS, nil
			}
		}
		glog.Warningf("unhandled os-release info %q", string(osRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /usr/lib/os-release: %v", err)
	}

	// ContainerOS uses /etc/os-release
	{
		osRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/os-release"))
		if err == nil {
			for _, line := range strings.Split(string(osRelease), "\n") {
				line = strings.TrimSpace(line)
				if line == "ID=cos" {
					return DistributionContainerOS, nil
				}
			}
			glog.Warningf("unhandled /etc/os-release info %q", string(osRelease))
		} else if !os.IsNotExist(err) {
			glog.Warningf("error reading /etc/os-release: %v", err)
		}
	}

	return "", fmt.Errorf("cannot identify distro")
}
<commit_msg>nodeup: don't warn during distro detection<commit_after>/*
Copyright 2016 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 distros

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
)

// FindDistribution identifies the distribution on which we are running
// We will likely remove this when everything is containerized
func FindDistribution(rootfs string) (Distribution, error) {
	// Ubuntu has /etc/lsb-release (and /etc/debian_version)
	lsbRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/lsb-release"))
	if err == nil {
		for _, line := range strings.Split(string(lsbRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "DISTRIB_CODENAME=xenial" {
				return DistributionXenial, nil
			}
		}
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/lsb-release: %v", err)
	}

	// Debian has /etc/debian_version
	debianVersionBytes, err := ioutil.ReadFile(path.Join(rootfs, "etc/debian_version"))
	if err == nil {
		debianVersion := strings.TrimSpace(string(debianVersionBytes))
		if strings.HasPrefix(debianVersion, "8.") {
			return DistributionJessie, nil
		} else if strings.HasPrefix(debianVersion, "9.") {
			return DistributionDebian9, nil
		} else {
			return "", fmt.Errorf("unhandled debian version %q", debianVersion)
		}
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/debian_version: %v", err)
	}

	// Redhat has /etc/redhat-release
	// Centos has /etc/centos-release
	redhatRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/redhat-release"))
	if err == nil {
		for _, line := range strings.Split(string(redhatRelease), "\n") {
			line = strings.TrimSpace(line)
			if strings.HasPrefix(line, "Red Hat Enterprise Linux Server release 7.") {
				return DistributionRhel7, nil
			}
			if strings.HasPrefix(line, "CentOS Linux release 7.") {
				return DistributionCentos7, nil
			}
		}
		glog.Warningf("unhandled redhat-release info %q", string(lsbRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/redhat-release: %v", err)
	}

	// CoreOS uses /usr/lib/os-release
	usrLibOsRelease, err := ioutil.ReadFile(path.Join(rootfs, "usr/lib/os-release"))
	if err == nil {
		for _, line := range strings.Split(string(usrLibOsRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "ID=coreos" {
				return DistributionCoreOS, nil
			}
		}
		glog.Warningf("unhandled os-release info %q", string(usrLibOsRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /usr/lib/os-release: %v", err)
	}

	// ContainerOS uses /etc/os-release
	osRelease, err := ioutil.ReadFile(path.Join(rootfs, "etc/os-release"))
	if err == nil {
		for _, line := range strings.Split(string(osRelease), "\n") {
			line = strings.TrimSpace(line)
			if line == "ID=cos" {
				return DistributionContainerOS, nil
			}
		}
		glog.Warningf("unhandled /etc/os-release info %q", string(osRelease))
	} else if !os.IsNotExist(err) {
		glog.Warningf("error reading /etc/os-release: %v", err)
	}

	glog.Warningf("could not determine distro")
	glog.Warningf("  /etc/lsb-release: %q", string(lsbRelease))
	glog.Warningf("  /etc/debian_version: %q", string(debianVersionBytes))
	glog.Warningf("  /etc/redhat-release: %q", string(redhatRelease))
	glog.Warningf("  /usr/lib/os-release: %q", string(usrLibOsRelease))
	glog.Warningf("  /etc/os-release: %q", string(osRelease))

	return "", fmt.Errorf("cannot identify distro")
}
<|endoftext|>


<commit_before>/*
Copyright 2011-2017 Frederic Langlet
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 main

import (
	"flag"
	"fmt"
	"kanzi"
	"kanzi/transform"
	"math/rand"
	"os"
	"time"
)

func main() {
	var input = flag.String("input", "", "input string")

	// Parse
	flag.Parse()

	if len(*input) > 0 {
		buf1 := []byte(*input)
		size := uint(len(buf1))
		buf2 := make([]byte, size)
		bwt, _ := transform.NewBWT()
		bwt.Forward(buf1, buf2)
		fmt.Printf("BWT:  %s (%v)\n", buf2, bwt.PrimaryIndex(0))
		bwts, _ := transform.NewBWTS()
		bwts.Forward(buf1, buf2)
		fmt.Printf("BWTS: %s\n", buf2)
		os.Exit(0)
	}

	fmt.Printf("TestBWT and TestBWTS")
	TestCorrectness(true)
	TestCorrectness(false)
	TestSpeed(true)
	TestSpeed(false)
}

func TestCorrectness(isBWT bool) {
	if isBWT {
		fmt.Printf("\n\nBWT Correctness test")
	} else {
		fmt.Printf("\n\nBWTS Correctness test")
	}

	// Test behavior
	for ii := 1; ii <= 20; ii++ {
		fmt.Printf("\nTest %v\n", ii)
		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

		size := uint(0)
		var buf1 []byte
		var buf2 []byte
		var buf3 []byte

		if ii == 1 {
			buf1 = []byte("mississippi")
		} else if ii == 2 {
			buf1 = []byte("3.14159265358979323846264338327950288419716939937510")
		} else if ii == 3 {
			buf1 = []byte("SIX.MIXED.PIXIES.SIFT.SIXTY.PIXIE.DUST.BOXES")
		} else {
			size = 128
			buf1 = make([]byte, size)

			for i := 0; i < len(buf1); i++ {
				buf1[i] = byte(65 + rnd.Intn(4*ii))
			}
		}

		buf2 = make([]byte, len(buf1))
		buf3 = make([]byte, len(buf1))
		var bwt kanzi.ByteTransform

		if isBWT {
			bwt, _ = transform.NewBWT()
		} else {
			bwt, _ = transform.NewBWTS()
		}

		str1 := string(buf1)
		fmt.Printf("Input:   %s\n", str1)
		_, _, err1 := bwt.Forward(buf1, buf2)

		if err1 != nil {
			fmt.Printf("Error: %v\n", err1)
			os.Exit(1)
		}

		str2 := string(buf2)
		fmt.Printf("Encoded: %s", str2)

		if isBWT {
			primaryIndex := bwt.(*transform.BWT).PrimaryIndex(0)
			fmt.Printf("  (Primary index=%v)\n", primaryIndex)
		} else {
			println()
		}

		_, _, err2 := bwt.Inverse(buf2, buf3)

		if err2 != nil {
			fmt.Printf("Error: %v\n", err2)
			os.Exit(1)
		}

		str3 := string(buf3)
		fmt.Printf("Output:  %s\n", str3)

		if str1 == str3 {
			fmt.Printf("Identical\n")
		} else {
			fmt.Printf("Different\n")
			os.Exit(1)
		}
	}
}

func TestSpeed(isBWT bool) {
	if isBWT {
		fmt.Printf("\n\nBWT Speed test")
	} else {
		fmt.Printf("\n\nBWTS Speed test")
	}

	iter := 2000
	size := 256 * 1024
	buf1 := make([]byte, size)
	buf2 := make([]byte, size)
	buf3 := make([]byte, size)
	fmt.Printf("\nIterations: %v", iter)
	fmt.Printf("\nTransform size: %v\n", size)

	for jj := 0; jj < 3; jj++ {
		delta1 := int64(0)
		delta2 := int64(0)
		var bwt kanzi.ByteTransform

		if isBWT {
			bwt, _ = transform.NewBWT()
		} else {
			bwt, _ = transform.NewBWTS()
		}

		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

		for i := 0; i < iter; i++ {
			for i := range buf1 {
				buf1[i] = byte(rnd.Intn(255) + 1)
			}

			before := time.Now()
			bwt.Forward(buf1, buf2)
			after := time.Now()
			delta1 += after.Sub(before).Nanoseconds()
			before = time.Now()
			bwt.Inverse(buf2, buf3)
			after = time.Now()
			delta2 += after.Sub(before).Nanoseconds()

			// Sanity check
			for i := range buf1 {
				if buf1[i] != buf3[i] {
					fmt.Printf("Error at index %v: %v<->%v\n", i, buf1[i], buf3[i])
					os.Exit(1)
				}
			}
		}

		prod := int64(iter) * int64(size)
		fmt.Printf("Forward transform [ms] : %v\n", delta1/1000000)
		fmt.Printf("Throughput [KB/s]      : %d\n", prod*1000000/delta1*1000/1024)
		fmt.Printf("Inverse transform [ms] : %v\n", delta2/1000000)
		fmt.Printf("Throughput [KB/s]      : %d\n", prod*1000000/delta2*1000/1024)
	}

	println()
}
<commit_msg>Fix build error.<commit_after>/*
Copyright 2011-2017 Frederic Langlet
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 main

import (
	"flag"
	"fmt"
	kanzi "github.com/flanglet/kanzi"
	"github.com/flanglet/kanzi/transform"
	"math/rand"
	"os"
	"time"
)

func main() {
	var input = flag.String("input", "", "input string")

	// Parse
	flag.Parse()

	if len(*input) > 0 {
		buf1 := []byte(*input)
		size := uint(len(buf1))
		buf2 := make([]byte, size)
		bwt, _ := transform.NewBWT()
		bwt.Forward(buf1, buf2)
		fmt.Printf("BWT:  %s (%v)\n", buf2, bwt.PrimaryIndex(0))
		bwts, _ := transform.NewBWTS()
		bwts.Forward(buf1, buf2)
		fmt.Printf("BWTS: %s\n", buf2)
		os.Exit(0)
	}

	fmt.Printf("TestBWT and TestBWTS")
	TestCorrectness(true)
	TestCorrectness(false)
	TestSpeed(true)
	TestSpeed(false)
}

func TestCorrectness(isBWT bool) {
	if isBWT {
		fmt.Printf("\n\nBWT Correctness test")
	} else {
		fmt.Printf("\n\nBWTS Correctness test")
	}

	// Test behavior
	for ii := 1; ii <= 20; ii++ {
		fmt.Printf("\nTest %v\n", ii)
		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

		size := uint(0)
		var buf1 []byte
		var buf2 []byte
		var buf3 []byte

		if ii == 1 {
			buf1 = []byte("mississippi")
		} else if ii == 2 {
			buf1 = []byte("3.14159265358979323846264338327950288419716939937510")
		} else if ii == 3 {
			buf1 = []byte("SIX.MIXED.PIXIES.SIFT.SIXTY.PIXIE.DUST.BOXES")
		} else {
			size = 128
			buf1 = make([]byte, size)

			for i := 0; i < len(buf1); i++ {
				buf1[i] = byte(65 + rnd.Intn(4*ii))
			}
		}

		buf2 = make([]byte, len(buf1))
		buf3 = make([]byte, len(buf1))
		var bwt kanzi.ByteTransform

		if isBWT {
			bwt, _ = transform.NewBWT()
		} else {
			bwt, _ = transform.NewBWTS()
		}

		str1 := string(buf1)
		fmt.Printf("Input:   %s\n", str1)
		_, _, err1 := bwt.Forward(buf1, buf2)

		if err1 != nil {
			fmt.Printf("Error: %v\n", err1)
			os.Exit(1)
		}

		str2 := string(buf2)
		fmt.Printf("Encoded: %s", str2)

		if isBWT {
			primaryIndex := bwt.(*transform.BWT).PrimaryIndex(0)
			fmt.Printf("  (Primary index=%v)\n", primaryIndex)
		} else {
			println()
		}

		_, _, err2 := bwt.Inverse(buf2, buf3)

		if err2 != nil {
			fmt.Printf("Error: %v\n", err2)
			os.Exit(1)
		}

		str3 := string(buf3)
		fmt.Printf("Output:  %s\n", str3)

		if str1 == str3 {
			fmt.Printf("Identical\n")
		} else {
			fmt.Printf("Different\n")
			os.Exit(1)
		}
	}
}

func TestSpeed(isBWT bool) {
	if isBWT {
		fmt.Printf("\n\nBWT Speed test")
	} else {
		fmt.Printf("\n\nBWTS Speed test")
	}

	iter := 2000
	size := 256 * 1024
	buf1 := make([]byte, size)
	buf2 := make([]byte, size)
	buf3 := make([]byte, size)
	fmt.Printf("\nIterations: %v", iter)
	fmt.Printf("\nTransform size: %v\n", size)

	for jj := 0; jj < 3; jj++ {
		delta1 := int64(0)
		delta2 := int64(0)
		var bwt kanzi.ByteTransform

		if isBWT {
			bwt, _ = transform.NewBWT()
		} else {
			bwt, _ = transform.NewBWTS()
		}

		rnd := rand.New(rand.NewSource(time.Now().UnixNano()))

		for i := 0; i < iter; i++ {
			for i := range buf1 {
				buf1[i] = byte(rnd.Intn(255) + 1)
			}

			before := time.Now()
			bwt.Forward(buf1, buf2)
			after := time.Now()
			delta1 += after.Sub(before).Nanoseconds()
			before = time.Now()
			bwt.Inverse(buf2, buf3)
			after = time.Now()
			delta2 += after.Sub(before).Nanoseconds()

			// Sanity check
			for i := range buf1 {
				if buf1[i] != buf3[i] {
					fmt.Printf("Error at index %v: %v<->%v\n", i, buf1[i], buf3[i])
					os.Exit(1)
				}
			}
		}

		prod := int64(iter) * int64(size)
		fmt.Printf("Forward transform [ms] : %v\n", delta1/1000000)
		fmt.Printf("Throughput [KB/s]      : %d\n", prod*1000000/delta1*1000/1024)
		fmt.Printf("Inverse transform [ms] : %v\n", delta2/1000000)
		fmt.Printf("Throughput [KB/s]      : %d\n", prod*1000000/delta2*1000/1024)
	}

	println()
}
<|endoftext|>


<commit_before>package eval

import (
	"fmt"
	"time"

	"github.com/abrander/gansoi/database"
	"github.com/abrander/gansoi/node"
	"github.com/hashicorp/raft"
)

type (
	// Evaluator will evaluate check results from all nodes on the leader node.
	Evaluator struct {
		node  *node.Node
		peers raft.PeerStore
	}
)

// NewEvaluator will instantiate a new Evaluator listening to cluster changes,
// and evaluating results as they arrive.
func NewEvaluator(n *node.Node, peers raft.PeerStore) *Evaluator {
	e := &Evaluator{
		node:  n,
		peers: peers,
	}

	n.RegisterListener(e)

	return e
}

// evaluate1 will evalute a check result from a single node.
func (e *Evaluator) evaluate1(checkResult *database.CheckResult) {
	pe := PartialEvaluation{}
	pe.ID = checkResult.CheckID + ":::" + checkResult.Node

	state := StateDown

	// Evaluate if the check went well for a single node. For now we simply
	// checks for empty error string and assume everything is good if its
	// empty :)
	if checkResult.Error == "" {
		state = StateUp
	}

	err := e.node.One("ID", pe.ID, &pe)
	if err != nil {
		// None was found. Fill out new.
		pe.CheckID = checkResult.CheckID
		pe.NodeID = checkResult.Node
		pe.Start = checkResult.TimeStamp
		pe.End = checkResult.TimeStamp
		pe.State = state
	} else {
		// Check if the state changed.
		if pe.State == state {
			// If the state is the same, just update end time.
			pe.End = checkResult.TimeStamp
		} else {
			// If the state changed, reset both start and end time.
			pe.Start = checkResult.TimeStamp
			pe.End = checkResult.TimeStamp
		}

		pe.State = state
	}

	err = e.node.Save(&pe)
	if err != nil {
		// FIXME: It seems wrong to panic here.
		panic(err.Error())
	}
}

// evaluate2 will evalute if a given check should be considered up or down when
// evaluating the result from all nodes.
func (e *Evaluator) evaluate2(n *PartialEvaluation) {
	var eval Evaluation

	// FIXME: Locking.

	err := e.node.One("CheckID", n.CheckID, &eval)
	if err != nil {
		// Evaluation is unknown. Start new.
		eval.CheckID = n.CheckID
		eval.Start = n.Start
		eval.End = n.End
	}

	state := StateUp
	states := make(map[State]int)

	nodes, _ := e.peers.Peers()
	for _, nodeID := range nodes {
		ID := n.CheckID + ":::" + nodeID

		var pe PartialEvaluation
		err = e.node.One("ID", ID, &pe)
		if err != nil {
			// Not all nodes have reported yet. Could be StateDegraded instead?
			state = StateUnknown
			break
		}

		// FIXME: Deal with old checks somehow. Maybe we should simply discard
		//        them? I'm not sure it makes much sense to evalute a period
		//        where gansoi were not running for example.

		states[pe.State]++
	}

	if state == StateUp {
		if states[StateUp] == len(nodes) {
			state = StateUp
		} else if states[StateDown] == len(nodes) {
			state = StateDown
		} else {
			state = StateDegraded
		}
	}

	if eval.State == state {
		eval.End = time.Now()
	} else {
		eval.Start = time.Now()
		eval.End = eval.Start
	}

	eval.State = state

	err = e.node.Save(&eval)
	if err != nil {
		// FIXME: It seems wrong to panic here.
		panic(err.Error())
	}
}

// PostClusterApply implements node.Listener.
func (e *Evaluator) PostClusterApply(leader bool, command database.Command, data interface{}, err error) {
	// If we're not the leader, we abort. Only the leader should evaluate
	// check results.
	if !leader {
		return
	}

	// We're only interested in saves for now.
	if command != database.CommandSave {
		return
	}

	switch data.(type) {
	case *database.CheckResult:
		e.evaluate1(data.(*database.CheckResult))
	case *PartialEvaluation:
		e.evaluate2(data.(*PartialEvaluation))
	case *Evaluation:
		eval := data.(*Evaluation)
		fmt.Printf("%s: %s (%s)\n", eval.CheckID, eval.State.ColorString(), eval.End.Sub(eval.Start).String())
	}
}
<commit_msg>More comments.<commit_after>package eval

import (
	"fmt"
	"time"

	"github.com/abrander/gansoi/database"
	"github.com/abrander/gansoi/node"
	"github.com/hashicorp/raft"
)

type (
	// Evaluator will evaluate check results from all nodes on the leader node.
	Evaluator struct {
		node  *node.Node
		peers raft.PeerStore
	}
)

// NewEvaluator will instantiate a new Evaluator listening to cluster changes,
// and evaluating results as they arrive.
func NewEvaluator(n *node.Node, peers raft.PeerStore) *Evaluator {
	e := &Evaluator{
		node:  n,
		peers: peers,
	}

	n.RegisterListener(e)

	return e
}

// evaluate1 will evalute a check result from a single node.
func (e *Evaluator) evaluate1(checkResult *database.CheckResult) {
	pe := PartialEvaluation{}
	pe.ID = checkResult.CheckID + ":::" + checkResult.Node

	state := StateDown

	// Evaluate if the check went well for a single node. For now we simply
	// checks for empty error string and assume everything is good if its
	// empty :)
	if checkResult.Error == "" {
		state = StateUp
	}

	err := e.node.One("ID", pe.ID, &pe)
	if err != nil {
		// None was found. Fill out new.
		pe.CheckID = checkResult.CheckID
		pe.NodeID = checkResult.Node
		pe.Start = checkResult.TimeStamp
		pe.End = checkResult.TimeStamp
		pe.State = state
	} else {
		// Check if the state changed.
		if pe.State == state {
			// If the state is the same, just update end time.
			pe.End = checkResult.TimeStamp
		} else {
			// If the state changed, reset both start and end time.
			pe.Start = checkResult.TimeStamp
			pe.End = checkResult.TimeStamp
		}

		pe.State = state
	}

	err = e.node.Save(&pe)
	if err != nil {
		// FIXME: It seems wrong to panic here.
		panic(err.Error())
	}
}

// evaluate2 will evalute if a given check should be considered up or down when
// evaluating the result from all nodes.
// This should only be done on the leader.
func (e *Evaluator) evaluate2(n *PartialEvaluation) {
	var eval Evaluation

	// FIXME: Locking.

	err := e.node.One("CheckID", n.CheckID, &eval)
	if err != nil {
		// Evaluation is unknown. Start new.
		eval.CheckID = n.CheckID
		eval.Start = n.Start
		eval.End = n.End
	}

	state := StateUp
	states := make(map[State]int)

	nodes, _ := e.peers.Peers()
	for _, nodeID := range nodes {
		ID := n.CheckID + ":::" + nodeID

		var pe PartialEvaluation
		err = e.node.One("ID", ID, &pe)
		if err != nil {
			// Not all nodes have reported yet. Could be StateDegraded instead?
			state = StateUnknown
			break
		}

		// FIXME: Deal with old checks somehow. Maybe we should simply discard
		//        them? I'm not sure it makes much sense to evalute a period
		//        where gansoi were not running for example.

		states[pe.State]++
	}

	if state == StateUp {
		if states[StateUp] == len(nodes) {
			state = StateUp
		} else if states[StateDown] == len(nodes) {
			state = StateDown
		} else {
			state = StateDegraded
		}
	}

	if eval.State == state {
		// There is no change in state. Keep current start time, and update end
		// time.
		eval.End = time.Now()
	} else {
		// We have a new state. Update both start and end time.
		eval.Start = time.Now()
		eval.End = eval.Start
	}

	eval.State = state

	err = e.node.Save(&eval)
	if err != nil {
		// FIXME: It seems wrong to panic here.
		panic(err.Error())
	}
}

// PostClusterApply implements node.Listener.
func (e *Evaluator) PostClusterApply(leader bool, command database.Command, data interface{}, err error) {
	// If we're not the leader, we abort. Only the leader should evaluate
	// check results.
	if !leader {
		return
	}

	// We're only interested in saves for now.
	if command != database.CommandSave {
		return
	}

	switch data.(type) {
	case *database.CheckResult:
		e.evaluate1(data.(*database.CheckResult))
	case *PartialEvaluation:
		e.evaluate2(data.(*PartialEvaluation))
	case *Evaluation:
		eval := data.(*Evaluation)
		fmt.Printf("%s: %s (%s)\n", eval.CheckID, eval.State.ColorString(), eval.End.Sub(eval.Start).String())
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 etcdhttp

import (
	"encoding/json"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
	"github.com/coreos/etcd/etcdserver/security"
)

type securityHandler struct {
	sec         *security.Store
	clusterInfo etcdserver.ClusterInfo
}

func hasWriteRootAccess(sec *security.Store, r *http.Request) bool {
	if r.Method == "GET" || r.Method == "HEAD" {
		return true
	}
	return hasRootAccess(sec, r)
}

func hasRootAccess(sec *security.Store, r *http.Request) bool {
	if sec == nil {
		// No store means no security avaliable, eg, tests.
		return true
	}
	if !sec.SecurityEnabled() {
		return true
	}
	username, password, ok := r.BasicAuth()
	if !ok {
		return false
	}
	if username != "root" {
		log.Printf("security: Attempting to use user %s for resource that requires root.", username)
		return false
	}
	root, err := sec.GetUser("root")
	if err != nil {
		return false
	}
	ok = root.CheckPassword(password)
	if !ok {
		log.Printf("security: Wrong password for user %s", username)
	}
	return ok
}

func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string) bool {
	if sec == nil {
		// No store means no security avaliable, eg, tests.
		return true
	}
	if !sec.SecurityEnabled() {
		return true
	}
	username, password, ok := r.BasicAuth()
	if !ok {
		return false
	}
	user, err := sec.GetUser(username)
	if err != nil {
		log.Printf("security: No such user: %s.", username)
		return false
	}
	authAsUser := user.CheckPassword(password)
	if !authAsUser {
		log.Printf("security: Incorrect password for user: %s.", username)
		return false
	}
	if user.User == "root" {
		return true
	}
	writeAccess := r.Method != "GET" && r.Method != "HEAD"
	for _, roleName := range user.Roles {
		role, err := sec.GetRole(roleName)
		if err != nil {
			continue
		}
		if role.HasKeyAccess(key, writeAccess) {
			return true
		}
	}
	log.Printf("security: Invalid access for user %s on key %s.", username, key)
	return false
}

func writeNoAuth(w http.ResponseWriter) {
	herr := httptypes.NewHTTPError(http.StatusUnauthorized, "Insufficient credentials")
	herr.WriteTo(w)
}

func handleSecurity(mux *http.ServeMux, sh *securityHandler) {
	mux.HandleFunc(securityPrefix+"/roles", sh.baseRoles)
	mux.HandleFunc(securityPrefix+"/roles/", sh.handleRoles)
	mux.HandleFunc(securityPrefix+"/users", sh.baseUsers)
	mux.HandleFunc(securityPrefix+"/users/", sh.handleUsers)
	mux.HandleFunc(securityPrefix+"/enable", sh.enableDisable)
}

func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	var rolesCollections struct {
		Roles []string `json:"roles"`
	}
	roles, err := sh.sec.AllRoles()
	if err != nil {
		writeError(w, err)
		return
	}

	rolesCollections.Roles = roles
	err = json.NewEncoder(w).Encode(rolesCollections)
	if err != nil {
		log.Println("etcdhttp: baseRoles error encoding on", r.URL)
	}
}

func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
	subpath := path.Clean(r.URL.Path[len(securityPrefix):])
	// Split "/roles/rolename/command".
	// First item is an empty string, second is "roles"
	pieces := strings.Split(subpath, "/")
	if len(pieces) != 3 {
	}
	sh.forRole(w, r, pieces[2])
}

func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())

	switch r.Method {
	case "GET":
		w.Header().Set("Content-Type", "application/json")
		data, err := sh.sec.GetRole(role)
		if err != nil {
			writeError(w, err)
			return
		}
		err = json.NewEncoder(w).Encode(data)
		if err != nil {
			log.Println("etcdhttp: forRole error encoding on", r.URL)
			return
		}
		return
	case "PUT":
		var in security.Role
		err := json.NewDecoder(r.Body).Decode(&in)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if in.Role != role {
			writeError(w, httptypes.NewHTTPError(400, "Role JSON name does not match the name in the URL"))
			return
		}
		newrole, err := sh.sec.CreateOrUpdateRole(in)
		if err != nil {
			writeError(w, err)
			return
		}
		w.WriteHeader(http.StatusCreated)
		err = json.NewEncoder(w).Encode(newrole)
		if err != nil {
			log.Println("etcdhttp: forRole error encoding on", r.URL)
			return
		}
		return
	case "DELETE":
		err := sh.sec.DeleteRole(role)
		if err != nil {
			writeError(w, err)
			return
		}
	}
}

func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	var usersCollections struct {
		Users []string `json:"users"`
	}
	users, err := sh.sec.AllUsers()
	if err != nil {
		writeError(w, err)
		return
	}

	usersCollections.Users = users
	err = json.NewEncoder(w).Encode(usersCollections)
	if err != nil {
		log.Println("etcdhttp: baseUsers error encoding on", r.URL)
	}
}

func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
	subpath := path.Clean(r.URL.Path[len(securityPrefix):])
	// Split "/users/username/command".
	// First item is an empty string, second is "users"
	pieces := strings.Split(subpath, "/")
	if len(pieces) != 3 {
		writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path"))
		return
	}
	sh.forUser(w, r, pieces[2])
}

func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())

	switch r.Method {
	case "GET":
		w.Header().Set("Content-Type", "application/json")
		u, err := sh.sec.GetUser(user)
		if err != nil {
			writeError(w, err)
			return
		}
		u.Password = ""
		err = json.NewEncoder(w).Encode(u)
		if err != nil {
			log.Println("etcdhttp: forUser error encoding on", r.URL)
			return
		}
		return
	case "PUT":
		var u security.User
		err := json.NewDecoder(r.Body).Decode(&u)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if u.User != user {
			writeError(w, httptypes.NewHTTPError(400, "User JSON name does not match the name in the URL"))
			return
		}
		newuser, err := sh.sec.CreateOrUpdateUser(u)
		if err != nil {
			writeError(w, err)
			return
		}
		w.WriteHeader(http.StatusCreated)
		err = json.NewEncoder(w).Encode(newuser)
		if err != nil {
			log.Println("etcdhttp: forUser error encoding on", r.URL)
			return
		}
		return
	case "DELETE":
		err := sh.sec.DeleteUser(user)
		if err != nil {
			writeError(w, err)
			return
		}
	}
}

type enabled struct {
	Enabled bool `json:"enabled"`
}

func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasWriteRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	isEnabled := sh.sec.SecurityEnabled()
	switch r.Method {
	case "GET":
		jsonDict := enabled{isEnabled}
		err := json.NewEncoder(w).Encode(jsonDict)
		if err != nil {
			log.Println("etcdhttp: error encoding security state on", r.URL)
		}
	case "PUT":
		var in security.User
		err := json.NewDecoder(r.Body).Decode(&in)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if in.User != "root" {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Need to create root user"))
			return
		}
		err = sh.sec.EnableSecurity(in)
		if err != nil {
			writeError(w, err)
			return
		}
	case "DELETE":
		err := sh.sec.DisableSecurity()
		if err != nil {
			writeError(w, err)
			return
		}
	}
}
<commit_msg>etcdserver/etcdhttp: do not return back the password of a user<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 etcdhttp

import (
	"encoding/json"
	"log"
	"net/http"
	"path"
	"strings"

	"github.com/coreos/etcd/etcdserver"
	"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
	"github.com/coreos/etcd/etcdserver/security"
)

type securityHandler struct {
	sec         *security.Store
	clusterInfo etcdserver.ClusterInfo
}

func hasWriteRootAccess(sec *security.Store, r *http.Request) bool {
	if r.Method == "GET" || r.Method == "HEAD" {
		return true
	}
	return hasRootAccess(sec, r)
}

func hasRootAccess(sec *security.Store, r *http.Request) bool {
	if sec == nil {
		// No store means no security avaliable, eg, tests.
		return true
	}
	if !sec.SecurityEnabled() {
		return true
	}
	username, password, ok := r.BasicAuth()
	if !ok {
		return false
	}
	if username != "root" {
		log.Printf("security: Attempting to use user %s for resource that requires root.", username)
		return false
	}
	root, err := sec.GetUser("root")
	if err != nil {
		return false
	}
	ok = root.CheckPassword(password)
	if !ok {
		log.Printf("security: Wrong password for user %s", username)
	}
	return ok
}

func hasKeyPrefixAccess(sec *security.Store, r *http.Request, key string) bool {
	if sec == nil {
		// No store means no security avaliable, eg, tests.
		return true
	}
	if !sec.SecurityEnabled() {
		return true
	}
	username, password, ok := r.BasicAuth()
	if !ok {
		return false
	}
	user, err := sec.GetUser(username)
	if err != nil {
		log.Printf("security: No such user: %s.", username)
		return false
	}
	authAsUser := user.CheckPassword(password)
	if !authAsUser {
		log.Printf("security: Incorrect password for user: %s.", username)
		return false
	}
	if user.User == "root" {
		return true
	}
	writeAccess := r.Method != "GET" && r.Method != "HEAD"
	for _, roleName := range user.Roles {
		role, err := sec.GetRole(roleName)
		if err != nil {
			continue
		}
		if role.HasKeyAccess(key, writeAccess) {
			return true
		}
	}
	log.Printf("security: Invalid access for user %s on key %s.", username, key)
	return false
}

func writeNoAuth(w http.ResponseWriter) {
	herr := httptypes.NewHTTPError(http.StatusUnauthorized, "Insufficient credentials")
	herr.WriteTo(w)
}

func handleSecurity(mux *http.ServeMux, sh *securityHandler) {
	mux.HandleFunc(securityPrefix+"/roles", sh.baseRoles)
	mux.HandleFunc(securityPrefix+"/roles/", sh.handleRoles)
	mux.HandleFunc(securityPrefix+"/users", sh.baseUsers)
	mux.HandleFunc(securityPrefix+"/users/", sh.handleUsers)
	mux.HandleFunc(securityPrefix+"/enable", sh.enableDisable)
}

func (sh *securityHandler) baseRoles(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	var rolesCollections struct {
		Roles []string `json:"roles"`
	}
	roles, err := sh.sec.AllRoles()
	if err != nil {
		writeError(w, err)
		return
	}

	rolesCollections.Roles = roles
	err = json.NewEncoder(w).Encode(rolesCollections)
	if err != nil {
		log.Println("etcdhttp: baseRoles error encoding on", r.URL)
	}
}

func (sh *securityHandler) handleRoles(w http.ResponseWriter, r *http.Request) {
	subpath := path.Clean(r.URL.Path[len(securityPrefix):])
	// Split "/roles/rolename/command".
	// First item is an empty string, second is "roles"
	pieces := strings.Split(subpath, "/")
	if len(pieces) != 3 {
	}
	sh.forRole(w, r, pieces[2])
}

func (sh *securityHandler) forRole(w http.ResponseWriter, r *http.Request, role string) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())

	switch r.Method {
	case "GET":
		w.Header().Set("Content-Type", "application/json")
		data, err := sh.sec.GetRole(role)
		if err != nil {
			writeError(w, err)
			return
		}
		err = json.NewEncoder(w).Encode(data)
		if err != nil {
			log.Println("etcdhttp: forRole error encoding on", r.URL)
			return
		}
		return
	case "PUT":
		var in security.Role
		err := json.NewDecoder(r.Body).Decode(&in)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if in.Role != role {
			writeError(w, httptypes.NewHTTPError(400, "Role JSON name does not match the name in the URL"))
			return
		}
		newrole, err := sh.sec.CreateOrUpdateRole(in)
		if err != nil {
			writeError(w, err)
			return
		}
		w.WriteHeader(http.StatusCreated)
		err = json.NewEncoder(w).Encode(newrole)
		if err != nil {
			log.Println("etcdhttp: forRole error encoding on", r.URL)
			return
		}
		return
	case "DELETE":
		err := sh.sec.DeleteRole(role)
		if err != nil {
			writeError(w, err)
			return
		}
	}
}

func (sh *securityHandler) baseUsers(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	var usersCollections struct {
		Users []string `json:"users"`
	}
	users, err := sh.sec.AllUsers()
	if err != nil {
		writeError(w, err)
		return
	}

	usersCollections.Users = users
	err = json.NewEncoder(w).Encode(usersCollections)
	if err != nil {
		log.Println("etcdhttp: baseUsers error encoding on", r.URL)
	}
}

func (sh *securityHandler) handleUsers(w http.ResponseWriter, r *http.Request) {
	subpath := path.Clean(r.URL.Path[len(securityPrefix):])
	// Split "/users/username/command".
	// First item is an empty string, second is "users"
	pieces := strings.Split(subpath, "/")
	if len(pieces) != 3 {
		writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid path"))
		return
	}
	sh.forUser(w, r, pieces[2])
}

func (sh *securityHandler) forUser(w http.ResponseWriter, r *http.Request, user string) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())

	switch r.Method {
	case "GET":
		w.Header().Set("Content-Type", "application/json")
		u, err := sh.sec.GetUser(user)
		if err != nil {
			writeError(w, err)
			return
		}
		u.Password = ""

		err = json.NewEncoder(w).Encode(u)
		if err != nil {
			log.Println("etcdhttp: forUser error encoding on", r.URL)
			return
		}
		return
	case "PUT":
		var u security.User
		err := json.NewDecoder(r.Body).Decode(&u)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if u.User != user {
			writeError(w, httptypes.NewHTTPError(400, "User JSON name does not match the name in the URL"))
			return
		}
		newuser, err := sh.sec.CreateOrUpdateUser(u)
		if err != nil {
			writeError(w, err)
			return
		}
		newuser.Password = ""

		w.WriteHeader(http.StatusCreated)
		err = json.NewEncoder(w).Encode(newuser)
		if err != nil {
			log.Println("etcdhttp: forUser error encoding on", r.URL)
			return
		}
		return
	case "DELETE":
		err := sh.sec.DeleteUser(user)
		if err != nil {
			writeError(w, err)
			return
		}
	}
}

type enabled struct {
	Enabled bool `json:"enabled"`
}

func (sh *securityHandler) enableDisable(w http.ResponseWriter, r *http.Request) {
	if !allowMethod(w, r.Method, "GET", "PUT", "DELETE") {
		return
	}
	if !hasWriteRootAccess(sh.sec, r) {
		writeNoAuth(w)
		return
	}
	w.Header().Set("X-Etcd-Cluster-ID", sh.clusterInfo.ID().String())
	w.Header().Set("Content-Type", "application/json")
	isEnabled := sh.sec.SecurityEnabled()
	switch r.Method {
	case "GET":
		jsonDict := enabled{isEnabled}
		err := json.NewEncoder(w).Encode(jsonDict)
		if err != nil {
			log.Println("etcdhttp: error encoding security state on", r.URL)
		}
	case "PUT":
		var in security.User
		err := json.NewDecoder(r.Body).Decode(&in)
		if err != nil {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Invalid JSON in request body."))
			return
		}
		if in.User != "root" {
			writeError(w, httptypes.NewHTTPError(http.StatusBadRequest, "Need to create root user"))
			return
		}
		err = sh.sec.EnableSecurity(in)
		if err != nil {
			writeError(w, err)
			return
		}
	case "DELETE":
		err := sh.sec.DisableSecurity()
		if err != nil {
			writeError(w, err)
			return
		}
	}
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"time"

	"github.com/koding/bongo"
)

type MessageReply struct {
	// unique identifier of the MessageReply
	Id int64 `json:"id"`

	// Id of the interacted message
	MessageId int64 `json:"messageId"             sql:"NOT NULL"`

	// Id of the reply
	ReplyId int64 `json:"replyId"                 sql:"NOT NULL"`

	// Creation of the MessageReply
	CreatedAt time.Time `json:"createdAt"         sql:"NOT NULL"`
}

func (m MessageReply) GetId() int64 {
	return m.Id
}

func (m MessageReply) TableName() string {
	return "api.message_reply"
}

func NewMessageReply() *MessageReply {
	return &MessageReply{}
}

func (m *MessageReply) AfterCreate() {
	bongo.B.AfterCreate(m)
}

func (m *MessageReply) AfterUpdate() {
	bongo.B.AfterUpdate(m)
}

func (m MessageReply) AfterDelete() {
	bongo.B.AfterDelete(m)
}

func (m *MessageReply) ById(id int64) error {
	return bongo.B.ById(m, id)
}

func (m *MessageReply) Create() error {
	return bongo.B.Create(m)
}

func (m *MessageReply) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(m, data, q)
}

func (m *MessageReply) Delete() error {
	if err := bongo.B.DB.
		Where("message_id = ? and reply_id = ?", m.MessageId, m.ReplyId).
		Delete(m).Error; err != nil {
		return err
	}
	return nil
}

func (m *MessageReply) DeleteByOrQuery(messageId int64) error {
	if err := bongo.B.DB.
		Where("message_id = ? or reply_id = ?", messageId, messageId).
		Delete(m).Error; err != nil {
		return err
	}
	return nil
}

func (m *MessageReply) List(query *Query) ([]ChannelMessage, error) {
	return m.fetchMessages(query)
}

func (m *MessageReply) ListAll() ([]ChannelMessage, error) {
	query := NewQuery()
	query.Limit = 0
	query.Skip = 0
	return m.fetchMessages(query)
}

func (m *MessageReply) fetchMessages(query *Query) ([]ChannelMessage, error) {
	var replies []int64

	if m.MessageId == 0 {
		return nil, errors.New("MessageId is not set")
	}

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": m.MessageId,
		},
		Pluck: "reply_id",
		Skip:  query.Skip,
		Limit: query.Limit,
		Sort:  map[string]string{"created_at": "DESC"},
	}

	if err := m.Some(&replies, q); err != nil {
		return nil, err
	}

	parent := NewChannelMessage()
	channelMessageReplies, err := parent.FetchByIds(replies)
	if err != nil {
		return nil, err
	}

	return channelMessageReplies, nil
}

func (m *MessageReply) Count() (int, error) {
	if m.MessageId == 0 {
		return 0, errors.New("MessageId is not set")
	}

	return bongo.B.Count(m,
		"message_id = ?",
		m.MessageId,
	)
}
<commit_msg>Social: do not use delete with selectors, fetch them all, then delete one by one<commit_after>package models

import (
	"errors"
	"time"

	"github.com/koding/bongo"
)

type MessageReply struct {
	// unique identifier of the MessageReply
	Id int64 `json:"id"`

	// Id of the interacted message
	MessageId int64 `json:"messageId"             sql:"NOT NULL"`

	// Id of the reply
	ReplyId int64 `json:"replyId"                 sql:"NOT NULL"`

	// Creation of the MessageReply
	CreatedAt time.Time `json:"createdAt"         sql:"NOT NULL"`
}

func (m MessageReply) GetId() int64 {
	return m.Id
}

func (m MessageReply) TableName() string {
	return "api.message_reply"
}

func NewMessageReply() *MessageReply {
	return &MessageReply{}
}

func (m *MessageReply) AfterCreate() {
	bongo.B.AfterCreate(m)
}

func (m *MessageReply) AfterUpdate() {
	bongo.B.AfterUpdate(m)
}

func (m MessageReply) AfterDelete() {
	bongo.B.AfterDelete(m)
}

func (m *MessageReply) ById(id int64) error {
	return bongo.B.ById(m, id)
}

func (m *MessageReply) Create() error {
	return bongo.B.Create(m)
}

func (m *MessageReply) Some(data interface{}, q *bongo.Query) error {
	return bongo.B.Some(m, data, q)
}

func (m *MessageReply) One(q *bongo.Query) error {
	return bongo.B.One(m, m, q)
}

func (m *MessageReply) Delete() error {
	selector := map[string]interface{}{
		"message_id": m.MessageId,
		"reply_id":   m.ReplyId,
	}

	if err := m.One(bongo.NewQS(selector)); err != nil {
		return err
	}

	err := bongo.B.Delete(m)
	if err != nil {
		return err
	}

	return err
}

func (m *MessageReply) DeleteByOrQuery(messageId int64) error {
	var messageReplies []MessageReply
	query := bongo.B.DB.Table(m.TableName())
	query = query.Where("message_id = ? or reply_id = ?", messageId, messageId)

	if err := query.Find(&messageReplies).Error; err != nil {
		return err
	}

	if messageReplies == nil {
		return nil
	}

	if len(messageReplies) == 0 {
		return nil
	}

	for _, messageReply := range messageReplies {
		err := bongo.B.Delete(messageReply)
		if err != nil {
			return err
		}
	}

	return nil
}

func (m *MessageReply) List(query *Query) ([]ChannelMessage, error) {
	return m.fetchMessages(query)
}

func (m *MessageReply) ListAll() ([]ChannelMessage, error) {
	query := NewQuery()
	query.Limit = 0
	query.Skip = 0
	return m.fetchMessages(query)
}

func (m *MessageReply) fetchMessages(query *Query) ([]ChannelMessage, error) {
	var replies []int64

	if m.MessageId == 0 {
		return nil, errors.New("MessageId is not set")
	}

	q := &bongo.Query{
		Selector: map[string]interface{}{
			"message_id": m.MessageId,
		},
		Pluck: "reply_id",
		Skip:  query.Skip,
		Limit: query.Limit,
		Sort:  map[string]string{"created_at": "DESC"},
	}

	if err := m.Some(&replies, q); err != nil {
		return nil, err
	}

	parent := NewChannelMessage()
	channelMessageReplies, err := parent.FetchByIds(replies)
	if err != nil {
		return nil, err
	}

	return channelMessageReplies, nil
}

func (m *MessageReply) Count() (int, error) {
	if m.MessageId == 0 {
		return 0, errors.New("MessageId is not set")
	}

	return bongo.B.Count(m,
		"message_id = ?",
		m.MessageId,
	)
}
<|endoftext|>


<commit_before>package storage

import (
	"github.com/pkg/errors"

	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

var lxdEarlyPatches = map[string]func(b *lxdBackend) error{}

var lxdLatePatches = map[string]func(b *lxdBackend) error{
	"storage_create_vm":                        lxdPatchStorageCreateVM,
	"storage_create_vm_again":                  lxdPatchStorageCreateVM,
	"storage_rename_custom_volume_add_project": lxdPatchStorageRenameCustomVolumeAddProject,
}

// Patches start here.
func lxdPatchStorageCreateVM(b *lxdBackend) error {
	return b.createStorageStructure(drivers.GetPoolMountPath(b.name))
}

// lxdPatchStorageRenameCustomVolumeAddProject renames all custom volumes in the default project (which is all of
// the custom volumes right now) to have the project prefix added to the storage device volume name.
// This is so we can added project support to custom volumes and avoid any name collisions.
func lxdPatchStorageRenameCustomVolumeAddProject(b *lxdBackend) error {
	// Get all custom volumes in default project on this node.
	// At this time, all custom volumes are in the default project.
	volumes, err := b.state.Cluster.StoragePoolNodeVolumesGet(project.Default, b.ID(), []int{db.StoragePoolVolumeTypeCustom})
	if err != nil && err != db.ErrNoSuchObject {
		return errors.Wrapf(err, "Failed getting custom volumes for default project")
	}

	revert := revert.New()
	defer revert.Fail()

	for _, v := range volumes {
		// Run inside temporary function to ensure revert has correct volume scope.
		err = func(curVol *api.StorageVolume) error {
			// There's no need to pass the config as it's not needed when renaming a volume.
			oldVol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, curVol.Name, nil)

			// Add default project prefix to current volume name.
			newVolStorageName := project.StorageVolume(project.Default, curVol.Name)
			newVol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, newVolStorageName, nil)

			// Check if volume has already been renamed.
			if b.driver.HasVolume(newVol) {
				logger.Infof("Skipping already renamed custom volume %q in pool %q", newVol.Name(), b.Name())
				return nil
			}

			// Check if volume is currently mounted.
			oldMntPath := drivers.GetVolumeMountPath(b.Name(), drivers.VolumeTypeCustom, curVol.Name)

			// If the volume is mounted we need to be careful how we rename it to avoid interrupting a
			// running instance's attached volumes.
			ourUnmount := false
			if shared.IsMountPoint(oldMntPath) {
				logger.Infof("Lazy unmount custom volume %q in pool %q", curVol.Name, b.Name())
				err = unix.Unmount(oldMntPath, unix.MNT_DETACH)
				if err != nil {
					return err
				}
				ourUnmount = true
			}

			logger.Infof("Renaming custom volume %q in pool %q to %q", curVol.Name, b.Name(), newVolStorageName)
			err = b.driver.RenameVolume(oldVol, newVolStorageName, nil)
			if err != nil {
				return err
			}

			// Ensure we don't use the wrong volume for revert by using a temporary function.
			revert.Add(func() {
				logger.Infof("Reverting rename of custom volume %q in pool %q to %q", newVol.Name(), b.Name(), curVol.Name)
				b.driver.RenameVolume(newVol, curVol.Name, nil)
			})

			if ourUnmount {
				logger.Infof("Mount custom volume %q in pool %q", newVolStorageName, b.Name())
				_, err = b.driver.MountVolume(newVol, nil)
				if err != nil {
					return err
				}
			}

			return nil
		}(v)
		if err != nil {
			return err
		}
	}

	revert.Success()
	return nil
}
<commit_msg>lxd/storage/backend/lxd/patches: Adds daemon storage symlink update to lxdPatchStorageRenameCustomVolumeAddProject<commit_after>package storage

import (
	"os"

	"github.com/pkg/errors"
	"golang.org/x/sys/unix"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/storage/drivers"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

var lxdEarlyPatches = map[string]func(b *lxdBackend) error{}

var lxdLatePatches = map[string]func(b *lxdBackend) error{
	"storage_create_vm":                        lxdPatchStorageCreateVM,
	"storage_create_vm_again":                  lxdPatchStorageCreateVM,
	"storage_rename_custom_volume_add_project": lxdPatchStorageRenameCustomVolumeAddProject,
}

// Patches start here.
func lxdPatchStorageCreateVM(b *lxdBackend) error {
	return b.createStorageStructure(drivers.GetPoolMountPath(b.name))
}

// lxdPatchStorageRenameCustomVolumeAddProject renames all custom volumes in the default project (which is all of
// the custom volumes right now) to have the project prefix added to the storage device volume name.
// This is so we can added project support to custom volumes and avoid any name collisions.
func lxdPatchStorageRenameCustomVolumeAddProject(b *lxdBackend) error {
	// Get all custom volumes in default project on this node.
	// At this time, all custom volumes are in the default project.
	volumes, err := b.state.Cluster.StoragePoolNodeVolumesGet(project.Default, b.ID(), []int{db.StoragePoolVolumeTypeCustom})
	if err != nil && err != db.ErrNoSuchObject {
		return errors.Wrapf(err, "Failed getting custom volumes for default project")
	}

	revert := revert.New()
	defer revert.Fail()

	for _, v := range volumes {
		// Run inside temporary function to ensure revert has correct volume scope.
		err = func(curVol *api.StorageVolume) error {
			// There's no need to pass the config as it's not needed when renaming a volume.
			oldVol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, curVol.Name, nil)

			// Add default project prefix to current volume name.
			newVolStorageName := project.StorageVolume(project.Default, curVol.Name)
			newVol := b.newVolume(drivers.VolumeTypeCustom, drivers.ContentTypeFS, newVolStorageName, nil)

			// Check if volume has already been renamed.
			if b.driver.HasVolume(newVol) {
				logger.Infof("Skipping already renamed custom volume %q in pool %q", newVol.Name(), b.Name())
				return nil
			}

			// Check if volume is currently mounted.
			oldMntPath := drivers.GetVolumeMountPath(b.Name(), drivers.VolumeTypeCustom, curVol.Name)

			// If the volume is mounted we need to be careful how we rename it to avoid interrupting a
			// running instance's attached volumes.
			ourUnmount := false
			if shared.IsMountPoint(oldMntPath) {
				logger.Infof("Lazy unmount custom volume %q in pool %q", curVol.Name, b.Name())
				err = unix.Unmount(oldMntPath, unix.MNT_DETACH)
				if err != nil {
					return err
				}
				ourUnmount = true
			}

			logger.Infof("Renaming custom volume %q in pool %q to %q", curVol.Name, b.Name(), newVolStorageName)
			err = b.driver.RenameVolume(oldVol, newVolStorageName, nil)
			if err != nil {
				return err
			}

			// Ensure we don't use the wrong volume for revert by using a temporary function.
			revert.Add(func() {
				logger.Infof("Reverting rename of custom volume %q in pool %q to %q", newVol.Name(), b.Name(), curVol.Name)
				b.driver.RenameVolume(newVol, curVol.Name, nil)
			})

			// Check if volume is being used by daemon storage and needs its symlink updating.
			used, err := VolumeUsedByDaemon(b.state, b.Name(), curVol.Name)
			if err != nil {
				return err
			}

			if used {
				logger.Infof("Updating daemon storage symlinks for volume %q in pool %q", curVol.Name, b.Name())
				for _, storageType := range []string{"images", "backups"} {
					err = func(storageType string) error {
						symlinkPath := shared.VarPath(storageType)
						destPath, err := os.Readlink(symlinkPath)

						// Check if storage type path is a symlink and points to volume.
						if err == nil && destPath == oldVol.MountPath() {
							newDestPath := newVol.MountPath()
							logger.Infof("Updating daemon storage symlink at %q to %q", symlinkPath, newDestPath)
							os.Remove(symlinkPath)
							err = os.Symlink(newDestPath, symlinkPath)
							if err != nil {
								return errors.Wrapf(err, "Failed to create the new symlink at %q to %q", symlinkPath, newDestPath)
							}

							revert.Add(func() {
								logger.Infof("Reverting daemon storage symlink at %q to %q", symlinkPath, destPath)
								os.Remove(symlinkPath)
								os.Symlink(destPath, symlinkPath)
							})
						}

						return nil
					}(storageType)
					if err != nil {
						return err
					}
				}
			}

			if ourUnmount {
				logger.Infof("Mount custom volume %q in pool %q", newVolStorageName, b.Name())
				_, err = b.driver.MountVolume(newVol, nil)
				if err != nil {
					return err
				}
			}

			return nil
		}(v)
		if err != nil {
			return err
		}
	}

	revert.Success()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

type line struct {
	label     int
	statement string
}

var (
	usageDescription = makeUsageDescription()
	linePattern      = regexp.MustCompile(`^\s*(\d+)?\s*(.*?)\s*$`)
)

func main() {
	filename := processArguments()
	code := readFile(filename)
	rawLines := splitLines(code)
	parsedLines := parseLines(rawLines)

	fmt.Println(parsedLines)
}

func makeUsageDescription() string {
	_, scriptPath, _, ok := runtime.Caller(0)
	if !ok {
		return ""
	}

	return fmt.Sprintf(
		"Usage:\n"+
			"\tgo run %s [options] <filename>\n"+
			"\n"+
			"Options:\n"+
			"\t-h, --help - show help.\n",
		filepath.Base(scriptPath),
	)
}

func processArguments() string {
	testArguments()

	firstArgument := os.Args[1]
	processHelpOption(firstArgument)

	return firstArgument
}

func testArguments() {
	numberOfArguments := len(os.Args)
	if numberOfArguments < 2 {
		fmt.Print(
			"Error: filename not specified.\n" +
				"\n" +
				usageDescription,
		)

		os.Exit(1)
	}
}

func processHelpOption(firstArgument string) {
	if firstArgument == "-h" || firstArgument == "--help" {
		fmt.Print(usageDescription)
		os.Exit(0)
	}
}

func readFile(filename string) string {
	code, error := ioutil.ReadFile(filename)
	if error != nil {
		fmt.Printf(
			"Error: unable to read file \"%s\" (%v).\n",
			filename,
			error,
		)

		os.Exit(1)
	}

	return string(code)
}

func splitLines(code string) []string {
	return strings.Split(code, "\n")
}

func parseLines(lines []string) []line {
	var parsedLines []line
	for index, content := range lines {
		parsedLine := parseLine(index, content)
		parsedLines = append(parsedLines, parsedLine)
	}

	return parsedLines
}

func parseLine(index int, content string) line {
	lineParts := linePattern.FindStringSubmatch(content)
	if len(lineParts) != 3 {
		fmt.Printf("Warning: invalid line #%d.\n", index+1)
		return line{}
	}

	label := parseLabel(index, lineParts[1])
	return line{label, lineParts[2]}
}

func parseLabel(index int, stringLabel string) int {
	integralLabel, error := strconv.Atoi(stringLabel)
	if error != nil && len(stringLabel) != 0 {
		fmt.Printf(
			"Warning: invalid label \"%s\" on line #%d.\n",
			stringLabel,
			index+1,
		)
	}

	return integralLabel
}
<commit_msg>Fixes #6: add making label map.<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"strings"
)

type line struct {
	label     int
	statement string
}

var (
	usageDescription = makeUsageDescription()
	linePattern      = regexp.MustCompile(`^\s*(\d+)?\s*(.*?)\s*$`)
)

func main() {
	filename := processArguments()
	code := readFile(filename)
	rawLines := splitLines(code)
	parsedLines := parseLines(rawLines)
	fmt.Println(parsedLines)

	labelMap := makeLabelMap(parsedLines)
	fmt.Println(labelMap)
}

func makeUsageDescription() string {
	_, scriptPath, _, ok := runtime.Caller(0)
	if !ok {
		return ""
	}

	return fmt.Sprintf(
		"Usage:\n"+
			"\tgo run %s [options] <filename>\n"+
			"\n"+
			"Options:\n"+
			"\t-h, --help - show help.\n",
		filepath.Base(scriptPath),
	)
}

func processArguments() string {
	testArguments()

	firstArgument := os.Args[1]
	processHelpOption(firstArgument)

	return firstArgument
}

func testArguments() {
	numberOfArguments := len(os.Args)
	if numberOfArguments < 2 {
		fmt.Print(
			"Error: filename not specified.\n" +
				"\n" +
				usageDescription,
		)

		os.Exit(1)
	}
}

func processHelpOption(firstArgument string) {
	if firstArgument == "-h" || firstArgument == "--help" {
		fmt.Print(usageDescription)
		os.Exit(0)
	}
}

func readFile(filename string) string {
	code, error := ioutil.ReadFile(filename)
	if error != nil {
		fmt.Printf(
			"Error: unable to read file \"%s\" (%v).\n",
			filename,
			error,
		)

		os.Exit(1)
	}

	return string(code)
}

func splitLines(code string) []string {
	return strings.Split(code, "\n")
}

func parseLines(lines []string) []line {
	var parsedLines []line
	for index, content := range lines {
		parsedLine := parseLine(index, content)
		parsedLines = append(parsedLines, parsedLine)
	}

	return parsedLines
}

func parseLine(index int, content string) line {
	lineParts := linePattern.FindStringSubmatch(content)
	if len(lineParts) != 3 {
		fmt.Printf("Warning: invalid line #%d.\n", index+1)
		return line{}
	}

	label := parseLabel(index, lineParts[1])
	return line{label, lineParts[2]}
}

func parseLabel(index int, stringLabel string) int {
	integralLabel, error := strconv.Atoi(stringLabel)
	if error != nil && len(stringLabel) != 0 {
		fmt.Printf(
			"Warning: invalid label \"%s\" on line #%d.\n",
			stringLabel,
			index+1,
		)
	}

	return integralLabel
}

func makeLabelMap(lines []line) map[int]int {
	labelMap := make(map[int]int)
	for index, parsedLine := range lines {
		labelMap[parsedLine.label] = (index + 1) * 10
	}

	return labelMap
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/shifr/imgwizard/cache"

	"github.com/gorilla/mux"
	"github.com/shifr/vips"
)

type Context struct {
	Path      string
	CachePath string
	Storage   string
	Width     int
	Height    int
}

type Settings struct {
	ListenAddr    string
	CacheDir      string
	Scheme        string
	Local404Thumb string
	AllowedSizes  []string
	AllowedMedia  []string
	UrlTemplate   string

	Context Context
	Options vips.Options
}

const DEFAULT_CACHE_DIR = "/tmp/imgwizard"

var (
	settings     Settings
	listenAddr   = flag.String("l", "", "Address to listen on")
	allowedMedia = flag.String("m", "", "comma separated list of allowed media")
	allowedSizes = flag.String("s", "", "comma separated list of allowed sizes")
	cacheDir     = flag.String("c", "", "directory for cached files")
	quality      = flag.Int("q", 0, "image quality after resize")
)

// loadSettings loads settings from settings.json
// and from command-line
func (s *Settings) loadSettings() {

	s.ListenAddr = ":8070"
	s.CacheDir = DEFAULT_CACHE_DIR
	s.Scheme = "http"
	s.Local404Thumb = "/tmp/404.jpg"
	s.AllowedSizes = nil
	s.AllowedMedia = nil

	//defaults for vips
	s.Options.Crop = true
	s.Options.Enlarge = true
	s.Options.Quality = 80
	s.Options.Extend = vips.EXTEND_WHITE
	s.Options.Interpolator = vips.BILINEAR
	s.Options.Gravity = vips.CENTRE

	var sizes = "[0-9]*x[0-9]*"
	var medias = ""

	if *listenAddr != "" {
		s.ListenAddr = *listenAddr
	}

	if *allowedMedia != "" {
		s.AllowedMedia = strings.Split(*allowedMedia, ",")
	}

	if *allowedSizes != "" {
		s.AllowedSizes = strings.Split(*allowedSizes, ",")
	}

	if *cacheDir != "" {
		s.CacheDir = *cacheDir
	}

	if *quality != 0 {
		s.Options.Quality = *quality
	}

	if len(s.AllowedSizes) > 0 {
		sizes = strings.Join(s.AllowedSizes, "|")
	}

	if len(s.AllowedMedia) > 0 {
		medias = strings.Join(s.AllowedMedia, "|")
	}

	s.UrlTemplate = fmt.Sprintf(
		"/images/{storage:loc|rem}/{size:%s}/{path:%s.+}", sizes, medias)
}

// makeCachePath generates cache path from resized image
func (s *Settings) makeCachePath() {
	var subPath string

	pathParts := strings.Split(s.Context.Path, "/")
	lastIndex := len(pathParts) - 1
	imageData := strings.Split(pathParts[lastIndex], ".")
	imageName, imageFormat := imageData[0], imageData[1]
	cacheImageName := fmt.Sprintf(
		"%s_%dx%d.%s", imageName, s.Options.Width, s.Options.Height, imageFormat)

	switch s.Context.Storage {
	case "loc":
		subPath = strings.Join(pathParts[:lastIndex], "/")
	case "rem":
		subPath = strings.Join(pathParts[1:lastIndex], "/")
	}

	s.Context.CachePath = fmt.Sprintf(
		"%s/%s/%s", s.CacheDir, subPath, cacheImageName)
}

// getLocalImage fetches original image from file system
func getLocalImage(s *Settings) ([]byte, error) {
	var image []byte

	file, err := os.Open(path.Join("/", s.Context.Path))
	if err != nil {

		file, err = os.Open(s.Local404Thumb)
		if err != nil {
			return image, err
		}
	}

	info, _ := file.Stat()
	image = make([]byte, info.Size())

	_, err = file.Read(image)
	if err != nil {
		return image, err
	}

	return image, nil
}

// getRemoteImage fetches original image by http url
func getRemoteImage(url string) ([]byte, error) {
	var image []byte

	resp, err := http.Get(url)
	if err != nil {
		return image, err
	}
	defer resp.Body.Close()

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

	return image, nil
}

// getOrCreateImage check cache path for requested image
// if image doesn't exist - creates it
func getOrCreateImage() []byte {
	sett := settings
	sett.makeCachePath()

	var c *cache.Cache
	var image []byte
	var err error

	if image, err = c.Get(sett.Context.CachePath); err == nil {
		return image
	}

	switch sett.Context.Storage {
	case "loc":
		image, err = getLocalImage(&sett)
		if err != nil {
			log.Println("Can't get orig local file, reason - ", err)
		}

	case "rem":
		imgUrl := fmt.Sprintf("%s://%s", sett.Scheme, sett.Context.Path)
		image, err = getRemoteImage(imgUrl)
		if err != nil {
			log.Println("Can't get orig remote file, reason - ", err)
		}
	}

	buf, err := vips.Resize(image, sett.Options)
	if err != nil {
		log.Println("Can't resize image, reason - ", err)
	}

	err = c.Set(sett.Context.CachePath, buf)
	if err != nil {
		log.Println("Can't set cache, reason - ", err)
	}

	return buf
}

func fetchImage(rw http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	sizes := strings.Split(params["size"], "x")

	settings.Context.Storage = params["storage"]
	settings.Context.Path = params["path"]
	settings.Options.Width, _ = strconv.Atoi(sizes[0])
	settings.Options.Height, _ = strconv.Atoi(sizes[1])

	resultImage := getOrCreateImage()

	rw.Write(resultImage)
}

func init() {
	flag.Parse()
	settings.loadSettings()
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc(settings.UrlTemplate, fetchImage).Methods("GET")

	log.Println("ImgWizard started...")
	http.ListenAndServe(settings.ListenAddr, r)
}
<commit_msg>Dynamic proxy mark<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/shifr/imgwizard/cache"

	"github.com/gorilla/mux"
	"github.com/shifr/vips"
)

type Context struct {
	Path      string
	CachePath string
	Storage   string
	Width     int
	Height    int
}

type Settings struct {
	ListenAddr    string
	CacheDir      string
	Scheme        string
	Local404Thumb string
	AllowedSizes  []string
	AllowedMedia  []string
	UrlTemplate   string

	Context Context
	Options vips.Options
}

const DEFAULT_CACHE_DIR = "/tmp/imgwizard"

var (
	settings     Settings
	listenAddr   = flag.String("l", ":8070", "Address to listen on")
	allowedMedia = flag.String("m", "", "comma separated list of allowed media")
	allowedSizes = flag.String("s", "", "comma separated list of allowed sizes")
	cacheDir     = flag.String("c", "", "directory for cached files")
	mark         = flag.String("mark", "images", "Mark for nginx")
	quality      = flag.Int("q", 0, "image quality after resize")
)

// loadSettings loads settings from settings.json
// and from command-line
func (s *Settings) loadSettings() {

	s.CacheDir = DEFAULT_CACHE_DIR
	s.Scheme = "http"
	s.Local404Thumb = "/tmp/404.jpg"
	s.AllowedSizes = nil
	s.AllowedMedia = nil

	//defaults for vips
	s.Options.Crop = true
	s.Options.Enlarge = true
	s.Options.Quality = 80
	s.Options.Extend = vips.EXTEND_WHITE
	s.Options.Interpolator = vips.BILINEAR
	s.Options.Gravity = vips.CENTRE

	var sizes = "[0-9]*x[0-9]*"
	var medias = ""
	var proxyMark = *mark

	s.ListenAddr = *listenAddr

	if *allowedMedia != "" {
		s.AllowedMedia = strings.Split(*allowedMedia, ",")
	}

	if *allowedSizes != "" {
		s.AllowedSizes = strings.Split(*allowedSizes, ",")
	}

	if *cacheDir != "" {
		s.CacheDir = *cacheDir
	}

	if *quality != 0 {
		s.Options.Quality = *quality
	}

	if len(s.AllowedSizes) > 0 {
		sizes = strings.Join(s.AllowedSizes, "|")
	}

	if len(s.AllowedMedia) > 0 {
		medias = strings.Join(s.AllowedMedia, "|")
	}

	s.UrlTemplate = fmt.Sprintf(
		"/{mark:%s}/{storage:loc|rem}/{size:%s}/{path:%s.+}", proxyMark, sizes, medias)
}

// makeCachePath generates cache path from resized image
func (s *Settings) makeCachePath() {
	var subPath string

	pathParts := strings.Split(s.Context.Path, "/")
	lastIndex := len(pathParts) - 1
	imageData := strings.Split(pathParts[lastIndex], ".")
	imageName, imageFormat := imageData[0], imageData[1]
	cacheImageName := fmt.Sprintf(
		"%s_%dx%d.%s", imageName, s.Options.Width, s.Options.Height, imageFormat)

	switch s.Context.Storage {
	case "loc":
		subPath = strings.Join(pathParts[:lastIndex], "/")
	case "rem":
		subPath = strings.Join(pathParts[1:lastIndex], "/")
	}

	s.Context.CachePath = fmt.Sprintf(
		"%s/%s/%s", s.CacheDir, subPath, cacheImageName)
}

// getLocalImage fetches original image from file system
func getLocalImage(s *Settings) ([]byte, error) {
	var image []byte

	file, err := os.Open(path.Join("/", s.Context.Path))
	if err != nil {

		file, err = os.Open(s.Local404Thumb)
		if err != nil {
			return image, err
		}
	}

	info, _ := file.Stat()
	image = make([]byte, info.Size())

	_, err = file.Read(image)
	if err != nil {
		return image, err
	}

	return image, nil
}

// getRemoteImage fetches original image by http url
func getRemoteImage(url string) ([]byte, error) {
	var image []byte

	resp, err := http.Get(url)
	if err != nil {
		return image, err
	}
	defer resp.Body.Close()

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

	return image, nil
}

// getOrCreateImage check cache path for requested image
// if image doesn't exist - creates it
func getOrCreateImage() []byte {
	sett := settings
	sett.makeCachePath()

	var c *cache.Cache
	var image []byte
	var err error

	if image, err = c.Get(sett.Context.CachePath); err == nil {
		return image
	}

	switch sett.Context.Storage {
	case "loc":
		image, err = getLocalImage(&sett)
		if err != nil {
			log.Println("Can't get orig local file, reason - ", err)
		}

	case "rem":
		imgUrl := fmt.Sprintf("%s://%s", sett.Scheme, sett.Context.Path)
		image, err = getRemoteImage(imgUrl)
		if err != nil {
			log.Println("Can't get orig remote file, reason - ", err)
		}
	}

	buf, err := vips.Resize(image, sett.Options)
	if err != nil {
		log.Println("Can't resize image, reason - ", err)
	}

	err = c.Set(sett.Context.CachePath, buf)
	if err != nil {
		log.Println("Can't set cache, reason - ", err)
	}

	return buf
}

func fetchImage(rw http.ResponseWriter, req *http.Request) {
	params := mux.Vars(req)
	sizes := strings.Split(params["size"], "x")

	settings.Context.Storage = params["storage"]
	settings.Context.Path = params["path"]
	settings.Options.Width, _ = strconv.Atoi(sizes[0])
	settings.Options.Height, _ = strconv.Atoi(sizes[1])

	resultImage := getOrCreateImage()

	rw.Write(resultImage)
}

func init() {
	flag.Parse()
	settings.loadSettings()
}

func main() {
	r := mux.NewRouter()
	r.HandleFunc(settings.UrlTemplate, fetchImage).Methods("GET")

	log.Println("ImgWizard started...")
	http.ListenAndServe(settings.ListenAddr, r)
}
<|endoftext|>


<commit_before>package revok

import (
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/metrics"
	"cred-alert/scanners"
	"cred-alert/scanners/diffscanner"
	"cred-alert/sniff"
	"strings"

	"code.cloudfoundry.org/lager"
	git "github.com/libgit2/git2go"
)

//go:generate counterfeiter . Scanner

type Scanner interface {
	Scan(lager.Logger, string, string, string) error
}

type scanner struct {
	gitClient            gitclient.Client
	repositoryRepository db.RepositoryRepository
	scanRepository       db.ScanRepository
	sniffer              sniff.Sniffer
}

func NewScanner(
	gitClient gitclient.Client,
	repositoryRepository db.RepositoryRepository,
	scanRepository db.ScanRepository,
	sniffer sniff.Sniffer,
	emitter metrics.Emitter,
) Scanner {
	return &scanner{
		gitClient:            gitClient,
		repositoryRepository: repositoryRepository,
		scanRepository:       scanRepository,
		sniffer:              sniffer,
	}
}

func (s *scanner) Scan(
	logger lager.Logger,
	owner string,
	repository string,
	startSHA string,
) error {

	dbRepository, err := s.repositoryRepository.Find(owner, repository)
	if err != nil {
		logger.Error("failed-to-find-db-repo", err)
		return err
	}

	repo, err := git.OpenRepository(dbRepository.Path)
	if err != nil {
		logger.Error("failed-to-open-repo", err)
		return err
	}

	oid, err := git.NewOid(startSHA)
	if err != nil {
		logger.Error("failed-to-create-oid", err)
		return err
	}

	scannedOids := map[git.Oid]struct{}{}
	err = s.scanAncestors(kolsch.NewLogger(), logger, repo, dbRepository, oid, scannedOids)
	if err != nil {
		logger.Error("failed-to-scan", err)
	}

	return nil
}

func (s *scanner) scanAncestors(
	quietLogger lager.Logger,
	logger lager.Logger,
	repo *git.Repository,
	dbRepository db.Repository,
	child *git.Oid,
	scannedOids map[git.Oid]struct{},
) error {
	parents, err := s.gitClient.GetParents(repo, child)
	if err != nil {
		return err
	}

	if len(parents) == 0 {
		return s.scan(quietLogger, logger, dbRepository, child, scannedOids)
	}

	for _, parent := range parents {
		if _, found := scannedOids[*parent]; found {
			continue
		}

		err = s.scan(quietLogger, logger, dbRepository, child, scannedOids, parent)
		if err != nil {
			return err
		}

		return s.scanAncestors(quietLogger, logger, repo, dbRepository, parent, scannedOids)
	}

	return nil
}

func (s *scanner) scan(
	quietLogger lager.Logger,
	logger lager.Logger,
	dbRepository db.Repository,
	child *git.Oid,
	scannedOids map[git.Oid]struct{},
	parents ...*git.Oid,
) error {
	var parent *git.Oid
	if len(parents) == 1 {
		parent = parents[0]
	}

	diff, err := s.gitClient.Diff(dbRepository.Path, parent, child)
	if err != nil {
		return err
	}

	scan := s.scanRepository.Start(quietLogger, "diff-scan", &dbRepository, nil)
	s.sniffer.Sniff(
		quietLogger,
		diffscanner.NewDiffScanner(strings.NewReader(diff)),
		func(logger lager.Logger, violation scanners.Violation) error {
			line := violation.Line
			scan.RecordCredential(db.NewCredential(
				dbRepository.Owner,
				dbRepository.Name,
				"",
				line.Path,
				line.LineNumber,
				violation.Start,
				violation.End,
			))
			return nil
		},
	)

	scannedOids[*child] = struct{}{}

	err = scan.Finish()
	if err != nil {
		logger.Error("failed-to-finish-scan", err)
		return err
	}

	return nil
}
<commit_msg>Whitespace<commit_after>package revok

import (
	"cred-alert/db"
	"cred-alert/gitclient"
	"cred-alert/kolsch"
	"cred-alert/metrics"
	"cred-alert/scanners"
	"cred-alert/scanners/diffscanner"
	"cred-alert/sniff"
	"strings"

	"code.cloudfoundry.org/lager"
	git "github.com/libgit2/git2go"
)

//go:generate counterfeiter . Scanner

type Scanner interface {
	Scan(lager.Logger, string, string, string) error
}

type scanner struct {
	gitClient            gitclient.Client
	repositoryRepository db.RepositoryRepository
	scanRepository       db.ScanRepository
	sniffer              sniff.Sniffer
}

func NewScanner(
	gitClient gitclient.Client,
	repositoryRepository db.RepositoryRepository,
	scanRepository db.ScanRepository,
	sniffer sniff.Sniffer,
	emitter metrics.Emitter,
) Scanner {
	return &scanner{
		gitClient:            gitClient,
		repositoryRepository: repositoryRepository,
		scanRepository:       scanRepository,
		sniffer:              sniffer,
	}
}

func (s *scanner) Scan(
	logger lager.Logger,
	owner string,
	repository string,
	startSHA string,
) error {
	dbRepository, err := s.repositoryRepository.Find(owner, repository)
	if err != nil {
		logger.Error("failed-to-find-db-repo", err)
		return err
	}

	repo, err := git.OpenRepository(dbRepository.Path)
	if err != nil {
		logger.Error("failed-to-open-repo", err)
		return err
	}

	oid, err := git.NewOid(startSHA)
	if err != nil {
		logger.Error("failed-to-create-oid", err)
		return err
	}

	scannedOids := map[git.Oid]struct{}{}
	err = s.scanAncestors(kolsch.NewLogger(), logger, repo, dbRepository, oid, scannedOids)
	if err != nil {
		logger.Error("failed-to-scan", err)
	}

	return nil
}

func (s *scanner) scanAncestors(
	quietLogger lager.Logger,
	logger lager.Logger,
	repo *git.Repository,
	dbRepository db.Repository,
	child *git.Oid,
	scannedOids map[git.Oid]struct{},
) error {
	parents, err := s.gitClient.GetParents(repo, child)
	if err != nil {
		return err
	}

	if len(parents) == 0 {
		return s.scan(quietLogger, logger, dbRepository, child, scannedOids)
	}

	for _, parent := range parents {
		if _, found := scannedOids[*parent]; found {
			continue
		}

		err = s.scan(quietLogger, logger, dbRepository, child, scannedOids, parent)
		if err != nil {
			return err
		}

		return s.scanAncestors(quietLogger, logger, repo, dbRepository, parent, scannedOids)
	}

	return nil
}

func (s *scanner) scan(
	quietLogger lager.Logger,
	logger lager.Logger,
	dbRepository db.Repository,
	child *git.Oid,
	scannedOids map[git.Oid]struct{},
	parents ...*git.Oid,
) error {
	var parent *git.Oid
	if len(parents) == 1 {
		parent = parents[0]
	}

	diff, err := s.gitClient.Diff(dbRepository.Path, parent, child)
	if err != nil {
		return err
	}

	scan := s.scanRepository.Start(quietLogger, "diff-scan", &dbRepository, nil)
	s.sniffer.Sniff(
		quietLogger,
		diffscanner.NewDiffScanner(strings.NewReader(diff)),
		func(logger lager.Logger, violation scanners.Violation) error {
			line := violation.Line
			scan.RecordCredential(db.NewCredential(
				dbRepository.Owner,
				dbRepository.Name,
				"",
				line.Path,
				line.LineNumber,
				violation.Start,
				violation.End,
			))
			return nil
		},
	)

	scannedOids[*child] = struct{}{}

	err = scan.Finish()
	if err != nil {
		logger.Error("failed-to-finish-scan", err)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>//
// Copyright 2021, Arkbriar
//
// 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 gitlab

import (
	"fmt"
	"net/http"
)

// IssueLinksService handles communication with the issue relations related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLinksService struct {
	client *Client
}

// IssueLink represents a two-way relation between two issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLink struct {
	SourceIssue *Issue `json:"source_issue"`
	TargetIssue *Issue `json:"target_issue"`
	LinkType    string `json:"link_type"`
}

// ListIssueRelations gets a list of related issues of a given issue,
// sorted by the relationship creation datetime (ascending).
//
// Issues will be filtered according to the user authorizations.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
func (s *IssueLinksService) ListIssueRelations(pid interface{}, issueIID int, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issueIID)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*Issue
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, err
}

// GetIssueLink gets a specific issue link.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#get-an-issue-link
func (s *IssueLinksService) GetIssueLink(pid interface{}, issueIID int, issueLinkID int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d", PathEscape(project), issueIID, issueLinkID)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	issueLink := new(IssueLink)
	resp, err := s.client.Do(req, issueLink)
	if err != nil {
		return nil, resp, err
	}

	return issueLink, resp, err
}

// CreateIssueLinkOptions represents the available CreateIssueLink() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type CreateIssueLinkOptions struct {
	TargetProjectID *string `json:"target_project_id"`
	TargetIssueIID  *string `json:"target_issue_iid"`
	LinkType        *string `json:"link_type"`
}

// CreateIssueLink creates a two-way relation between two issues.
// User must be allowed to update both issues in order to succeed.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
func (s *IssueLinksService) CreateIssueLink(pid interface{}, issueIID int, opt *CreateIssueLinkOptions, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issueIID)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(IssueLink)
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, err
}

// DeleteIssueLink deletes an issue link, thus removes the two-way relationship.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issueIID, issueLinkID int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d",
		PathEscape(project),
		issueIID,
		issueLinkID)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(IssueLink)
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, err
}
<commit_msg>Make variable names consistent<commit_after>//
// Copyright 2021, Arkbriar
//
// 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 gitlab

import (
	"fmt"
	"net/http"
)

// IssueLinksService handles communication with the issue relations related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLinksService struct {
	client *Client
}

// IssueLink represents a two-way relation between two issues.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type IssueLink struct {
	SourceIssue *Issue `json:"source_issue"`
	TargetIssue *Issue `json:"target_issue"`
	LinkType    string `json:"link_type"`
}

// ListIssueRelations gets a list of related issues of a given issue,
// sorted by the relationship creation datetime (ascending).
//
// Issues will be filtered according to the user authorizations.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#list-issue-relations
func (s *IssueLinksService) ListIssueRelations(pid interface{}, issue int, options ...RequestOptionFunc) ([]*Issue, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	var is []*Issue
	resp, err := s.client.Do(req, &is)
	if err != nil {
		return nil, resp, err
	}

	return is, resp, err
}

// GetIssueLink gets a specific issue link.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#get-an-issue-link
func (s *IssueLinksService) GetIssueLink(pid interface{}, issue, issueLink int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d", PathEscape(project), issue, issueLink)

	req, err := s.client.NewRequest(http.MethodGet, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	il := new(IssueLink)
	resp, err := s.client.Do(req, il)
	if err != nil {
		return nil, resp, err
	}

	return il, resp, err
}

// CreateIssueLinkOptions represents the available CreateIssueLink() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/issue_links.html
type CreateIssueLinkOptions struct {
	TargetProjectID *string `json:"target_project_id"`
	TargetIssueIID  *string `json:"target_issue_iid"`
	LinkType        *string `json:"link_type"`
}

// CreateIssueLink creates a two-way relation between two issues.
// User must be allowed to update both issues in order to succeed.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#create-an-issue-link
func (s *IssueLinksService) CreateIssueLink(pid interface{}, issue int, opt *CreateIssueLinkOptions, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links", PathEscape(project), issue)

	req, err := s.client.NewRequest(http.MethodPost, u, opt, options)
	if err != nil {
		return nil, nil, err
	}

	i := new(IssueLink)
	resp, err := s.client.Do(req, &i)
	if err != nil {
		return nil, resp, err
	}

	return i, resp, err
}

// DeleteIssueLink deletes an issue link, thus removes the two-way relationship.
//
// GitLab API docs:
// https://docs.gitlab.com/ee/api/issue_links.html#delete-an-issue-link
func (s *IssueLinksService) DeleteIssueLink(pid interface{}, issue, issueLink int, options ...RequestOptionFunc) (*IssueLink, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/issues/%d/links/%d",
		PathEscape(project),
		issue,
		issueLink)

	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	il := new(IssueLink)
	resp, err := s.client.Do(req, &il)
	if err != nil {
		return nil, resp, err
	}

	return il, resp, err
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 auth

import (
	"sort"
	"strings"

	"github.com/coreos/etcd/auth/authpb"
	"github.com/coreos/etcd/mvcc/backend"
)

func isSubset(a, b *rangePerm) bool {
	// return true if a is a subset of b
	return 0 <= strings.Compare(a.begin, b.begin) && strings.Compare(a.end, b.end) <= 0
}

// removeSubsetRangePerms removes any rangePerms that are subsets of other rangePerms.
func removeSubsetRangePerms(perms []*rangePerm) []*rangePerm {
	// TODO(mitake): currently it is O(n^2), we need a better algorithm
	newp := make([]*rangePerm, 0)

	for i := range perms {
		subset := false

		for j := range perms {
			if i != j && isSubset(perms[i], perms[j]) {
				subset = true
				break
			}
		}

		if subset {
			continue
		}

		newp = append(newp, perms[i])
	}

	return newp
}

// mergeRangePerms merges adjacent rangePerms.
func mergeRangePerms(perms []*rangePerm) []*rangePerm {
	merged := make([]*rangePerm, 0)
	perms = removeSubsetRangePerms(perms)
	sort.Sort(RangePermSliceByBegin(perms))

	i := 0
	for i < len(perms) {
		begin, next := i, i
		for next+1 < len(perms) && perms[next].end >= perms[next+1].begin {
			next++
		}

		merged = append(merged, &rangePerm{begin: perms[begin].begin, end: perms[next].end})

		i = next + 1
	}

	return merged
}

func (as *authStore) makeUnifiedPerms(tx backend.BatchTx, userName string) *unifiedRangePermissions {
	user := getUser(tx, userName)
	if user == nil {
		plog.Errorf("invalid user name %s", userName)
		return nil
	}

	var readPerms, writePerms []*rangePerm

	for _, roleName := range user.Roles {
		role := getRole(tx, roleName)
		if role == nil {
			continue
		}

		for _, perm := range role.KeyPermission {
			if len(perm.RangeEnd) == 0 {
				continue
			}

			if perm.PermType == authpb.READWRITE || perm.PermType == authpb.READ {
				readPerms = append(readPerms, &rangePerm{begin: string(perm.Key), end: string(perm.RangeEnd)})
			}

			if perm.PermType == authpb.READWRITE || perm.PermType == authpb.WRITE {
				writePerms = append(writePerms, &rangePerm{begin: string(perm.Key), end: string(perm.RangeEnd)})
			}
		}
	}

	return &unifiedRangePermissions{readPerms: mergeRangePerms(readPerms), writePerms: mergeRangePerms(writePerms)}
}

func checkCachedPerm(cachedPerms *unifiedRangePermissions, userName string, key, rangeEnd string, write, read bool) bool {
	var perms []*rangePerm

	if write {
		perms = cachedPerms.writePerms
	} else {
		perms = cachedPerms.readPerms
	}

	for _, perm := range perms {
		if strings.Compare(rangeEnd, "") != 0 {
			if strings.Compare(perm.begin, key) <= 0 && strings.Compare(rangeEnd, perm.end) <= 0 {
				return true
			}
		} else {
			if strings.Compare(perm.begin, key) <= 0 && strings.Compare(key, perm.end) <= 0 {
				return true
			}
		}
	}

	return false
}

func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd string, write, read bool) bool {
	// assumption: tx is Lock()ed
	_, ok := as.rangePermCache[userName]
	if ok {
		return checkCachedPerm(as.rangePermCache[userName], userName, key, rangeEnd, write, read)
	}

	perms := as.makeUnifiedPerms(tx, userName)
	if perms == nil {
		plog.Errorf("failed to create a unified permission of user %s", userName)
		return false
	}
	as.rangePermCache[userName] = perms

	return checkCachedPerm(as.rangePermCache[userName], userName, key, rangeEnd, write, read)

}

func (as *authStore) clearCachedPerm() {
	as.rangePermCache = make(map[string]*unifiedRangePermissions)
}

func (as *authStore) invalidateCachedPerm(userName string) {
	delete(as.rangePermCache, userName)
}

type unifiedRangePermissions struct {
	// readPerms[i] and readPerms[j] (i != j) don't overlap
	readPerms []*rangePerm
	// writePerms[i] and writePerms[j] (i != j) don't overlap, too
	writePerms []*rangePerm
}

type rangePerm struct {
	begin, end string
}

type RangePermSliceByBegin []*rangePerm

func (slice RangePermSliceByBegin) Len() int {
	return len(slice)
}

func (slice RangePermSliceByBegin) Less(i, j int) bool {
	if slice[i].begin == slice[j].begin {
		return slice[i].end < slice[j].end
	}
	return slice[i].begin < slice[j].begin
}

func (slice RangePermSliceByBegin) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}
<commit_msg>auth: cleanup get perm func<commit_after>// Copyright 2016 The etcd 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 auth

import (
	"sort"
	"strings"

	"github.com/coreos/etcd/auth/authpb"
	"github.com/coreos/etcd/mvcc/backend"
)

func isSubset(a, b *rangePerm) bool {
	// return true if a is a subset of b
	return 0 <= strings.Compare(a.begin, b.begin) && strings.Compare(a.end, b.end) <= 0
}

// removeSubsetRangePerms removes any rangePerms that are subsets of other rangePerms.
func removeSubsetRangePerms(perms []*rangePerm) []*rangePerm {
	// TODO(mitake): currently it is O(n^2), we need a better algorithm
	newp := make([]*rangePerm, 0)

	for i := range perms {
		subset := false

		for j := range perms {
			if i != j && isSubset(perms[i], perms[j]) {
				subset = true
				break
			}
		}

		if subset {
			continue
		}

		newp = append(newp, perms[i])
	}

	return newp
}

// mergeRangePerms merges adjacent rangePerms.
func mergeRangePerms(perms []*rangePerm) []*rangePerm {
	merged := make([]*rangePerm, 0)
	perms = removeSubsetRangePerms(perms)
	sort.Sort(RangePermSliceByBegin(perms))

	i := 0
	for i < len(perms) {
		begin, next := i, i
		for next+1 < len(perms) && perms[next].end >= perms[next+1].begin {
			next++
		}

		merged = append(merged, &rangePerm{begin: perms[begin].begin, end: perms[next].end})

		i = next + 1
	}

	return merged
}

func getMergedPerms(tx backend.BatchTx, userName string) *unifiedRangePermissions {
	user := getUser(tx, userName)
	if user == nil {
		plog.Errorf("invalid user name %s", userName)
		return nil
	}

	var readPerms, writePerms []*rangePerm

	for _, roleName := range user.Roles {
		role := getRole(tx, roleName)
		if role == nil {
			continue
		}

		for _, perm := range role.KeyPermission {
			if len(perm.RangeEnd) == 0 {
				continue
			}
			rp := &rangePerm{begin: string(perm.Key), end: string(perm.RangeEnd)}

			switch perm.PermType {
			case authpb.READWRITE:
				readPerms = append(readPerms, rp)
				writePerms = append(writePerms, rp)

			case authpb.READ:
				readPerms = append(readPerms, rp)

			case authpb.WRITE:
				writePerms = append(writePerms, rp)
			}
		}
	}

	return &unifiedRangePermissions{
		readPerms:  mergeRangePerms(readPerms),
		writePerms: mergeRangePerms(writePerms),
	}
}

func checkCachedPerm(cachedPerms *unifiedRangePermissions, userName string, key, rangeEnd string, write, read bool) bool {
	var perms []*rangePerm

	if write {
		perms = cachedPerms.writePerms
	} else {
		perms = cachedPerms.readPerms
	}

	for _, perm := range perms {
		if strings.Compare(rangeEnd, "") != 0 {
			if strings.Compare(perm.begin, key) <= 0 && strings.Compare(rangeEnd, perm.end) <= 0 {
				return true
			}
		} else {
			if strings.Compare(perm.begin, key) <= 0 && strings.Compare(key, perm.end) <= 0 {
				return true
			}
		}
	}

	return false
}

func (as *authStore) isRangeOpPermitted(tx backend.BatchTx, userName string, key, rangeEnd string, write, read bool) bool {
	// assumption: tx is Lock()ed
	_, ok := as.rangePermCache[userName]
	if ok {
		return checkCachedPerm(as.rangePermCache[userName], userName, key, rangeEnd, write, read)
	}

	perms := getMergedPerms(tx, userName)
	if perms == nil {
		plog.Errorf("failed to create a unified permission of user %s", userName)
		return false
	}
	as.rangePermCache[userName] = perms

	return checkCachedPerm(as.rangePermCache[userName], userName, key, rangeEnd, write, read)

}

func (as *authStore) clearCachedPerm() {
	as.rangePermCache = make(map[string]*unifiedRangePermissions)
}

func (as *authStore) invalidateCachedPerm(userName string) {
	delete(as.rangePermCache, userName)
}

type unifiedRangePermissions struct {
	// readPerms[i] and readPerms[j] (i != j) don't overlap
	readPerms []*rangePerm
	// writePerms[i] and writePerms[j] (i != j) don't overlap, too
	writePerms []*rangePerm
}

type rangePerm struct {
	begin, end string
}

type RangePermSliceByBegin []*rangePerm

func (slice RangePermSliceByBegin) Len() int {
	return len(slice)
}

func (slice RangePermSliceByBegin) Less(i, j int) bool {
	if slice[i].begin == slice[j].begin {
		return slice[i].end < slice[j].end
	}
	return slice[i].begin < slice[j].begin
}

func (slice RangePermSliceByBegin) Swap(i, j int) {
	slice[i], slice[j] = slice[j], slice[i]
}
<|endoftext|>