<commit_before>// Use SSM multicast to discover other processes on the same local network
// See RFC-3569 for SSM description
// See RFC-5771 for IANA IPv4 Multicast address assignments

package multicast

import (
	"log"
	"net"
	"os"
	"strings"
	"time"
)

const (
	// Addresses for SSM multicasting are in the space 232/8
	// See https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-10
	multicastIP = "232.77.77.77"
	multicastPort = "9864"
	multicastAddress = multicastIP + ":" + multicastPort
	maxUDPMsg = 1 << 12
	defaultPeriod = 10
	failedTime = 60 // Seconds until a peer is considered failed

	helloMsgPrefix = "Lantern Hello"
	byeMsgPrefix = "Lantern Bye"
)

type Multicast struct {
        Conn        *net.UDPConn
        Addr        *net.UDPAddr
	Period      int // multicast period (in secs, 10 by default)

	quit        chan bool
	helloTicker *time.Ticker
	peers       map [string]time.Time
}

// Join the Lantern multicast group
func JoinMulticast() *Multicast {
        udpAddr, e := net.ResolveUDPAddr("udp4", multicastAddress)
        if e != nil {
		log.Fatal(e)
                return nil
        }

        c, e := net.ListenMulticastUDP("udp4", nil, udpAddr)
        if e != nil {
                return nil
        }
        return &Multicast{
		Conn: c,
		Addr: udpAddr,
		Period: defaultPeriod,
		quit: make(chan bool, 1),
		helloTicker: nil,
		peers: make(map[string]time.Time),
	}
}

// Initiate multicasting
func (mc *Multicast) StartMulticast() {
	// Periodically announce ourselves to the network
	go mc.sendHellos()

	// Listen multicasts by others
	go mc.listenPeers()
}

// Stop multicasting and leave the group. This should be called by the users of
// this library when the program exits or the discovery service is disabled by
// the end user
func (mc *Multicast) LeaveMulticast() error {
	// Stop sending hello
	if mc.helloTicker != nil {
		mc.helloTicker.Stop()
	}
	// Send bye
	host, _ := os.Hostname()
	addrs, _ := net.LookupIP(host)
	mc.write( byeMessage(addrs) )

	// Leave the listening goroutine as soon as it timeouts
	mc.quit <- true

	return nil
}

func (mc *Multicast) write(b []byte) (int, error) {
        return mc.Conn.WriteTo(b, mc.Addr)
}

func (mc *Multicast) read(b []byte) (int, *net.UDPAddr, error) {
        return mc.Conn.ReadFromUDP(b)
}

func (mc *Multicast) sendHellos () {
	mc.helloTicker = time.NewTicker(time.Duration(mc.Period) * time.Second)
	for range mc.helloTicker.C {
		host, _ := os.Hostname()
		addrs, _ := net.LookupIP(host)
		mc.write( helloMessage(addrs) )
	}
}

func (mc *Multicast) listenPeers() error {
	b := make([]byte, maxUDPMsg)
	// Set a deadline to avoid blocking on a read forever
	for {
		select {
		case <- mc.quit:
			return mc.Conn.Close()
		default:
			mc.Conn.SetReadDeadline(time.Now().Add(time.Duration(mc.Period) * time.Second))
			n, udpAddr, e := mc.read(b)
			udpAddrStr := udpAddr.String()
			if e != nil {
				log.Println(e)
				break
			}

			msg := b[:n]
			if n > 0 {
				if isHello(msg) {
					// Add peer only if its reported IP is the same as the
					// origin IP of the UDP package
					for _, a := range extractMessageAddresses(msg) {
						astr := a.String()
						if udpAddrStr == astr &&
							!isMyIP(strings.TrimSuffix(astr, ":" + multicastPort)) {
							// Use failedTime (the future), so failure detection can be performed directly
							mc.peers[udpAddrStr] = time.Now().Add(time.Second * time.Duration(failedTime))
						}
					}
				} else if isBye(msg) {
					// Remove peer
					delete(mc.peers, udpAddrStr)
				} else {
					log.Fatal("Unrecognized message sent to Lantern multicast SSM address")
				}
			}

			// We are checking here also that no peer is to old.
			// If we don't hear from peers soon enough, we consider
			// them failed.
			for p, pt := range mc.peers {
				if time.Now().After(pt) {
					delete(mc.peers, p)
				}
			}
		}
	}
}

func helloMessage(addrs []net.IP) []byte {
	return []byte(helloMsgPrefix + IPsToString(addrs))
}

func byeMessage(addrs []net.IP) []byte {
	return []byte(byeMsgPrefix + IPsToString(addrs))
}

func IPsToString(addrs []net.IP) string {
	var msg string
	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			msg += "|" + ipv4.String() + ":" + multicastPort
		}
	}
	return msg
}

func isHello(msg []byte) bool {
	return strings.HasPrefix(string(msg), helloMsgPrefix)
}

func isBye(msg []byte) bool {
	return strings.HasPrefix(string(msg), byeMsgPrefix)
}

func extractMessageAddresses(msg []byte) []*net.UDPAddr {
	strMsg := string(msg)
	strMsg = strings.TrimPrefix(strMsg, helloMsgPrefix)
	strs := strings.Split(strMsg[1:], "|")
	addrs := make([]*net.UDPAddr, len(strs))

	for i, str := range strs {
		addr, e := net.ResolveUDPAddr("udp4",str)
		if e != nil {
			continue
		}
		addrs[i] = addr
	}
	return addrs
}

func isMyIP(addr string) bool {
	host, _ := os.Hostname()
	addrs, _ := net.LookupIP(host)
	for _, a := range addrs {
		if addr == a.String() {
			return true
		}
	}
	return false
}
<commit_msg>Multicast: callbacks for add/remove peers<commit_after>// Use SSM multicast to discover other processes on the same local network
// See RFC-3569 for SSM description
// See RFC-5771 for IANA IPv4 Multicast address assignments

package multicast

import (
	"log"
	"net"
	"os"
	"strings"
	"time"
)

const (
	// Addresses for SSM multicasting are in the space 232/8
	// See https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-10
	multicastIP = "232.77.77.77"
	multicastPort = "9864"
	multicastAddress = multicastIP + ":" + multicastPort
	maxUDPMsg = 1 << 12
	defaultPeriod = 10
	failedTime = 60 // Seconds until a peer is considered failed

	helloMsgPrefix = "Lantern Hello"
	byeMsgPrefix = "Lantern Bye"
)

type Multicast struct {
        Conn                 *net.UDPConn
        Addr                 *net.UDPAddr
	Period               int // multicast period (in secs, 10 by default)
	AddPeerCallback     func(string) // Callback called when a peer is added
	RemovePeerCallback  func(string) // Callback called when a peer is removed

	quit        chan bool
	helloTicker *time.Ticker
	peers       map [string]time.Time
}

// Join the Lantern multicast group
func JoinMulticast() *Multicast {
        udpAddr, e := net.ResolveUDPAddr("udp4", multicastAddress)
        if e != nil {
		log.Fatal(e)
                return nil
        }

        c, e := net.ListenMulticastUDP("udp4", nil, udpAddr)
        if e != nil {
                return nil
        }
        return &Multicast{
		Conn: c,
		Addr: udpAddr,
		Period: defaultPeriod,
		quit: make(chan bool, 1),
		peers: make(map[string]time.Time),
	}
}

// Initiate multicasting
func (mc *Multicast) StartMulticast() {
	// Periodically announce ourselves to the network
	go mc.sendHellos()

	// Listen multicasts by others
	go mc.listenPeers()
}

// Stop multicasting and leave the group. This should be called by the users of
// this library when the program exits or the discovery service is disabled by
// the end user
func (mc *Multicast) LeaveMulticast() error {
	// Stop sending hello
	if mc.helloTicker != nil {
		mc.helloTicker.Stop()
	}
	// Send bye
	host, _ := os.Hostname()
	addrs, _ := net.LookupIP(host)
	mc.write( byeMessage(addrs) )

	// Leave the listening goroutine as soon as it timeouts
	mc.quit <- true

	return nil
}

func (mc *Multicast) write(b []byte) (int, error) {
        return mc.Conn.WriteTo(b, mc.Addr)
}

func (mc *Multicast) read(b []byte) (int, *net.UDPAddr, error) {
        return mc.Conn.ReadFromUDP(b)
}

func (mc *Multicast) sendHellos () {
	mc.helloTicker = time.NewTicker(time.Duration(mc.Period) * time.Second)
	for range mc.helloTicker.C {
		host, _ := os.Hostname()
		addrs, _ := net.LookupIP(host)
		mc.write( helloMessage(addrs) )
	}
}

func (mc *Multicast) listenPeers() error {
	b := make([]byte, maxUDPMsg)
	// Set a deadline to avoid blocking on a read forever
	for {
		select {
		case <- mc.quit:
			return mc.Conn.Close()
		default:
			mc.Conn.SetReadDeadline(time.Now().Add(time.Duration(mc.Period) * time.Second))
			n, udpAddr, e := mc.read(b)
			udpAddrStr := udpAddr.String()
			if e != nil {
				log.Println(e)
				break
			}

			msg := b[:n]
			if n > 0 {
				if isHello(msg) {
					// Add peer only if its reported IP is the same as the
					// origin IP of the UDP package
					for _, a := range extractMessageAddresses(msg) {
						astr := a.String()
						if udpAddrStr == astr &&
							!isMyIP(strings.TrimSuffix(astr, ":" + multicastPort)) {
							// Use failedTime (the future), so failure detection can be performed directly
							mc.peers[udpAddrStr] = time.Now().Add(time.Second * time.Duration(failedTime))
							if mc.AddPeerCallback != nil {
								mc.AddPeerCallback(udpAddrStr)
							}
						}
					}
				} else if isBye(msg) {
					// Remove peer
					delete(mc.peers, udpAddrStr)
					if mc.RemovePeerCallback != nil {
						mc.RemovePeerCallback(udpAddrStr)
					}
				} else {
					log.Fatal("Unrecognized message sent to Lantern multicast SSM address")
				}
			}

			// We are checking here also that no peer is to old.
			// If we don't hear from peers soon enough, we consider
			// them failed.
			for p, pt := range mc.peers {
				if time.Now().After(pt) {
					delete(mc.peers, p)
				}
			}
		}
	}
}

func helloMessage(addrs []net.IP) []byte {
	return []byte(helloMsgPrefix + IPsToString(addrs))
}

func byeMessage(addrs []net.IP) []byte {
	return []byte(byeMsgPrefix + IPsToString(addrs))
}

func IPsToString(addrs []net.IP) string {
	var msg string
	for _, addr := range addrs {
		if ipv4 := addr.To4(); ipv4 != nil {
			msg += "|" + ipv4.String() + ":" + multicastPort
		}
	}
	return msg
}

func isHello(msg []byte) bool {
	return strings.HasPrefix(string(msg), helloMsgPrefix)
}

func isBye(msg []byte) bool {
	return strings.HasPrefix(string(msg), byeMsgPrefix)
}

func extractMessageAddresses(msg []byte) []*net.UDPAddr {
	strMsg := string(msg)
	strMsg = strings.TrimPrefix(strMsg, helloMsgPrefix)
	strs := strings.Split(strMsg[1:], "|")
	addrs := make([]*net.UDPAddr, len(strs))

	for i, str := range strs {
		addr, e := net.ResolveUDPAddr("udp4",str)
		if e != nil {
			continue
		}
		addrs[i] = addr
	}
	return addrs
}

func isMyIP(addr string) bool {
	host, _ := os.Hostname()
	addrs, _ := net.LookupIP(host)
	for _, a := range addrs {
		if addr == a.String() {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/http/httptest"
	"net/url"
	"os"
	"path/filepath"
	"time"

	"github.com/blang/semver"
	"github.com/cloudfoundry/libbuildpack/cutlass"
	"github.com/cloudfoundry/libbuildpack/packager"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.Parse()
}

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		localVersion := fmt.Sprintf("%s.%s", buildpackVersion, cutlass.RandStringRunes(10))
		bpFile, err := packager.Package(bpDir, packager.CacheDir, localVersion, cutlass.Cached)
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, err := cutlass.InternetTraffic(
			bpDir,
			filepath.Join("fixtures", fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		Expect(traffic).To(BeEmpty())
	})
}

func ApiHasTask() bool {
	apiVersionString, err := cutlass.ApiVersion()
	Expect(err).To(BeNil())
	apiVersion, err := semver.Make(apiVersionString)
	Expect(err).To(BeNil())
	apiHasTask, err := semver.ParseRange(">= 2.75.0")
	Expect(err).To(BeNil())
	return apiHasTask(apiVersion)
}

var _ = SynchronizedBeforeSuite(func() []byte {
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack()
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) func() {
	return func() {
		Context("with an uncached buildpack", func() {
			var bpFile string
			var proxy *httptest.Server
			BeforeEach(func() {
				var err error
				if cutlass.Cached {
					Skip("Running cached tests")
				}

				localVersion := fmt.Sprintf("%s.%s", buildpackVersion, time.Now().Format("20060102150405"))
				bpFile, err = packager.Package(bpDir, packager.CacheDir, localVersion, cutlass.Cached)
				Expect(err).To(BeNil())

				proxy, err = cutlass.NewProxy()
				Expect(err).To(BeNil())
			})
			AfterEach(func() {
				os.Remove(bpFile)
				proxy.Close()
			})

			It("uses a proxy during staging if present", func() {
				traffic, err := cutlass.InternetTraffic(
					bpDir,
					filepath.Join("fixtures", fixtureName),
					bpFile,
					[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
				)
				Expect(err).To(BeNil())

				destUrl, err := url.Parse(proxy.URL)
				Expect(err).To(BeNil())

				Expect(cutlass.UniqueDestination(
					traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
				)).To(BeNil())
			})
		})
	}
}
<commit_msg>Change from machete -> cutlass<commit_after>package integration_test

import (
	"encoding/json"
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"github.com/blang/semver"
	"github.com/cloudfoundry/libbuildpack/cutlass"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var bpDir string
var buildpackVersion string
var packagedBuildpack cutlass.VersionedBuildpackPackage

func init() {
	flag.StringVar(&buildpackVersion, "version", "", "version to use (builds if empty)")
	flag.BoolVar(&cutlass.Cached, "cached", true, "cached buildpack")
	flag.StringVar(&cutlass.DefaultMemory, "memory", "128M", "default memory for pushed apps")
	flag.StringVar(&cutlass.DefaultDisk, "disk", "384M", "default disk for pushed apps")
	flag.Parse()
}

var _ = SynchronizedBeforeSuite(func() []byte {
	// Run once
	if buildpackVersion == "" {
		packagedBuildpack, err := cutlass.PackageUniquelyVersionedBuildpack()
		Expect(err).NotTo(HaveOccurred())

		data, err := json.Marshal(packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		return data
	}

	return []byte{}
}, func(data []byte) {
	// Run on all nodes
	var err error
	if len(data) > 0 {
		err = json.Unmarshal(data, &packagedBuildpack)
		Expect(err).NotTo(HaveOccurred())
		buildpackVersion = packagedBuildpack.Version
	}

	bpDir, err = cutlass.FindRoot()
	Expect(err).NotTo(HaveOccurred())

	cutlass.SeedRandom()
	cutlass.DefaultStdoutStderr = GinkgoWriter
})

var _ = SynchronizedAfterSuite(func() {
	// Run on all nodes
}, func() {
	// Run once
	Expect(cutlass.RemovePackagedBuildpack(packagedBuildpack)).To(Succeed())
	Expect(cutlass.DeleteOrphanedRoutes()).To(Succeed())
})

func TestIntegration(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Integration Suite")
}

func PushAppAndConfirm(app *cutlass.App) {
	Expect(app.Push()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
	Expect(app.ConfirmBuildpack(buildpackVersion)).To(Succeed())
}

func Restart(app *cutlass.App) {
	Expect(app.Restart()).To(Succeed())
	Eventually(func() ([]string, error) { return app.InstanceStates() }, 20*time.Second).Should(Equal([]string{"RUNNING"}))
}

func ApiHasTask() bool {
	apiVersionString, err := cutlass.ApiVersion()
	Expect(err).To(BeNil())
	apiVersion, err := semver.Make(apiVersionString)
	Expect(err).To(BeNil())
	apiHasTask, err := semver.ParseRange(">= 2.75.0")
	Expect(err).To(BeNil())
	return apiHasTask(apiVersion)
}

func AssertUsesProxyDuringStagingIfPresent(fixtureName string) {
	Context("with an uncached buildpack", func() {
		BeforeEach(func() {
			if cutlass.Cached {
				Skip("Running cached tests")
			}
		})

		It("uses a proxy during staging if present", func() {
			proxy, err := cutlass.NewProxy()
			Expect(err).To(BeNil())
			defer proxy.Close()

			bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
			cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
			err = cmd.Run()
			Expect(err).To(BeNil())
			defer os.Remove(bpFile)

			traffic, err := cutlass.InternetTraffic(
				bpDir,
				filepath.Join("fixtures", fixtureName),
				bpFile,
				[]string{"HTTP_PROXY=" + proxy.URL, "HTTPS_PROXY=" + proxy.URL},
			)
			Expect(err).To(BeNil())

			destUrl, err := url.Parse(proxy.URL)
			Expect(err).To(BeNil())

			Expect(cutlass.UniqueDestination(
				traffic, fmt.Sprintf("%s.%s", destUrl.Hostname(), destUrl.Port()),
			)).To(BeNil())
		})
	})
}

func AssertNoInternetTraffic(fixtureName string) {
	It("has no traffic", func() {
		if !cutlass.Cached {
			Skip("Running uncached tests")
		}

		bpFile := filepath.Join(bpDir, buildpackVersion+"tmp")
		cmd := exec.Command("cp", packagedBuildpack.File, bpFile)
		err := cmd.Run()
		Expect(err).To(BeNil())
		defer os.Remove(bpFile)

		traffic, err := cutlass.InternetTraffic(
			bpDir,
			filepath.Join("fixtures", fixtureName),
			bpFile,
			[]string{},
		)
		Expect(err).To(BeNil())
		Expect(traffic).To(BeEmpty())
	})
}
<|endoftext|>


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

package buildcfg

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var experimentBaseline = goexperiment.Flags{}

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := experimentBaseline

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiG = v
			flags.RegabiReflect = v
			flags.RegabiDefer = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			set(val)
		}
	}

	// regabi is only supported on amd64.
	if GOARCH != "amd64" {
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiDefer = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		panic("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil, false)
}

// AllExperiments returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func AllExperiments() []string {
	return expList(&Experiment, nil, true)
}
<commit_msg>internal/buildcfg: enable regabiwrappers by default<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildcfg

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

var regabiSupported = GOARCH == "amd64" && (GOOS == "linux" || GOOS == "darwin" || GOOS == "windows")

// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var experimentBaseline = goexperiment.Flags{
	RegabiWrappers: regabiSupported,
}

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := experimentBaseline

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiG = v
			flags.RegabiReflect = v
			flags.RegabiDefer = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			set(val)
		}
	}

	// regabi is only supported on amd64.
	if GOARCH != "amd64" {
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiDefer = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		panic("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		panic("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil, false)
}

// AllExperiments returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func AllExperiments() []string {
	return expList(&Experiment, nil, true)
}
<|endoftext|>


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

package buildcfg

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

var regabiSupported = GOARCH == "amd64"
var regabiDeveloping = GOARCH == "arm64"

// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var experimentBaseline = goexperiment.Flags{
	RegabiWrappers: regabiSupported || regabiDeveloping,
	RegabiG:        regabiSupported,
	RegabiReflect:  regabiSupported || regabiDeveloping,
	RegabiDefer:    true,
	RegabiArgs:     regabiSupported,
}

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := experimentBaseline

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiG = v
			flags.RegabiReflect = v
			flags.RegabiDefer = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			set(val)
		}
	}

	// regabi is only supported on amd64 and arm64.
	if GOARCH != "amd64" && GOARCH != "arm64" {
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		Error = fmt.Errorf("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		Error = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil, false)
}

// AllExperiments returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func AllExperiments() []string {
	return expList(&Experiment, nil, true)
}
<commit_msg>[dev.typeparams] internal/buildcfg: turn on register ABI by default on ARM64<commit_after>// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package buildcfg

import (
	"fmt"
	"os"
	"reflect"
	"strings"

	"internal/goexperiment"
)

// Experiment contains the toolchain experiments enabled for the
// current build.
//
// (This is not necessarily the set of experiments the compiler itself
// was built with.)
var Experiment goexperiment.Flags = parseExperiments()

var regabiSupported = GOARCH == "amd64" || GOARCH == "arm64"
var regabiDeveloping = false

// experimentBaseline specifies the experiment flags that are enabled by
// default in the current toolchain. This is, in effect, the "control"
// configuration and any variation from this is an experiment.
var experimentBaseline = goexperiment.Flags{
	RegabiWrappers: regabiSupported,
	RegabiG:        regabiSupported,
	RegabiReflect:  regabiSupported,
	RegabiDefer:    true,
	RegabiArgs:     regabiSupported,
}

// FramePointerEnabled enables the use of platform conventions for
// saving frame pointers.
//
// This used to be an experiment, but now it's always enabled on
// platforms that support it.
//
// Note: must agree with runtime.framepointer_enabled.
var FramePointerEnabled = GOARCH == "amd64" || GOARCH == "arm64"

func parseExperiments() goexperiment.Flags {
	// Start with the statically enabled set of experiments.
	flags := experimentBaseline

	// Pick up any changes to the baseline configuration from the
	// GOEXPERIMENT environment. This can be set at make.bash time
	// and overridden at build time.
	env := envOr("GOEXPERIMENT", defaultGOEXPERIMENT)

	if env != "" {
		// Create a map of known experiment names.
		names := make(map[string]func(bool))
		rv := reflect.ValueOf(&flags).Elem()
		rt := rv.Type()
		for i := 0; i < rt.NumField(); i++ {
			field := rv.Field(i)
			names[strings.ToLower(rt.Field(i).Name)] = field.SetBool
		}

		// "regabi" is an alias for all working regabi
		// subexperiments, and not an experiment itself. Doing
		// this as an alias make both "regabi" and "noregabi"
		// do the right thing.
		names["regabi"] = func(v bool) {
			flags.RegabiWrappers = v
			flags.RegabiG = v
			flags.RegabiReflect = v
			flags.RegabiDefer = v
			flags.RegabiArgs = v
		}

		// Parse names.
		for _, f := range strings.Split(env, ",") {
			if f == "" {
				continue
			}
			if f == "none" {
				// GOEXPERIMENT=none disables all experiment flags.
				// This is used by cmd/dist, which doesn't know how
				// to build with any experiment flags.
				flags = goexperiment.Flags{}
				continue
			}
			val := true
			if strings.HasPrefix(f, "no") {
				f, val = f[2:], false
			}
			set, ok := names[f]
			if !ok {
				fmt.Printf("unknown experiment %s\n", f)
				os.Exit(2)
			}
			set(val)
		}
	}

	// regabi is only supported on amd64 and arm64.
	if GOARCH != "amd64" && GOARCH != "arm64" {
		flags.RegabiWrappers = false
		flags.RegabiG = false
		flags.RegabiReflect = false
		flags.RegabiArgs = false
	}
	// Check regabi dependencies.
	if flags.RegabiG && !flags.RegabiWrappers {
		Error = fmt.Errorf("GOEXPERIMENT regabig requires regabiwrappers")
	}
	if flags.RegabiArgs && !(flags.RegabiWrappers && flags.RegabiG && flags.RegabiReflect && flags.RegabiDefer) {
		Error = fmt.Errorf("GOEXPERIMENT regabiargs requires regabiwrappers,regabig,regabireflect,regabidefer")
	}
	return flags
}

// expList returns the list of lower-cased experiment names for
// experiments that differ from base. base may be nil to indicate no
// experiments. If all is true, then include all experiment flags,
// regardless of base.
func expList(exp, base *goexperiment.Flags, all bool) []string {
	var list []string
	rv := reflect.ValueOf(exp).Elem()
	var rBase reflect.Value
	if base != nil {
		rBase = reflect.ValueOf(base).Elem()
	}
	rt := rv.Type()
	for i := 0; i < rt.NumField(); i++ {
		name := strings.ToLower(rt.Field(i).Name)
		val := rv.Field(i).Bool()
		baseVal := false
		if base != nil {
			baseVal = rBase.Field(i).Bool()
		}
		if all || val != baseVal {
			if val {
				list = append(list, name)
			} else {
				list = append(list, "no"+name)
			}
		}
	}
	return list
}

// GOEXPERIMENT is a comma-separated list of enabled or disabled
// experiments that differ from the baseline experiment configuration.
// GOEXPERIMENT is exactly what a user would set on the command line
// to get the set of enabled experiments.
func GOEXPERIMENT() string {
	return strings.Join(expList(&Experiment, &experimentBaseline, false), ",")
}

// EnabledExperiments returns a list of enabled experiments, as
// lower-cased experiment names.
func EnabledExperiments() []string {
	return expList(&Experiment, nil, false)
}

// AllExperiments returns a list of all experiment settings.
// Disabled experiments appear in the list prefixed by "no".
func AllExperiments() []string {
	return expList(&Experiment, nil, true)
}
<|endoftext|>


<commit_before>package consumption

import (
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/api/auth"
	"github.com/globocom/tsuru/api/service"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"net/http"
)

func CreateInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	log.Print("Receiving request to create a service instance")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Print("Got error while reading request body:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var sJson map[string]string
	err = json.Unmarshal(b, &sJson)
	if err != nil {
		log.Print("Got a problem while unmarshalling request's json:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var s service.Service
	err = validateInstanceForCreation(&s, sJson, u)
	if err != nil {
		log.Print("Got error while validation:")
		log.Print(err.Error())
		return err
	}
	var teamNames []string
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	for _, t := range teams {
		if s.HasTeam(&t) || !s.IsRestricted {
			teamNames = append(teamNames, t.Name)
		}
	}
	si := service.ServiceInstance{
		Name:        sJson["name"],
		ServiceName: sJson["service_name"],
		Teams:       teamNames,
	}
	if err = s.ProductionEndpoint().Create(&si); err != nil {
		log.Print("Error while calling create action from service api.")
		log.Print(err.Error())
		return err
	}
	err = si.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func validateInstanceForCreation(s *service.Service, sJson map[string]string, u *auth.User) error {
	err := db.Session.Services().Find(bson.M{"_id": sJson["service_name"], "status": bson.M{"$ne": "deleted"}}).One(&s)
	if err != nil {
		msg := err.Error()
		if msg == "not found" {
			msg = fmt.Sprintf("Service %s does not exist.", sJson["service_name"])
		}
		return &errors.Http{Code: http.StatusNotFound, Message: msg}
	}
	_, err = GetServiceOrError(sJson["service_name"], u)
	if err != nil {
		return err
	}
	return nil
}

func RemoveServiceInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	name := r.URL.Query().Get(":name")
	si, err := GetServiceInstanceOrError(name, u)
	if err != nil {
		return err
	}
	if len(si.Apps) > 0 {
		msg := "This service instance has binded apps. Unbind them before removing it"
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	if err = si.Service().ProductionEndpoint().Destroy(&si); err != nil {
        //TODO(flaviamissi): return err
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	err = db.Session.ServiceInstances().Remove(bson.M{"name": name})
	if err != nil {
		return err
	}
	w.Write([]byte("service instance successfuly removed"))
	return nil
}

func ServicesInstancesHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	response := ServiceAndServiceInstancesByTeams(u)
	body, err := json.Marshal(response)
	if err != nil {
		return err
	}
	n, err := w.Write(body)
	if n != len(body) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write the response body."}
	}
	return err
}

func ServiceInstanceStatusHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	// #TODO (flaviamissi) should check if user has access to service
	// just call GetServiceInstanceOrError should be enough
	siName := r.URL.Query().Get(":instance")
	var si service.ServiceInstance
	if siName == "" {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Service instance name not provided."}
	}
	err := db.Session.ServiceInstances().Find(bson.M{"name": siName}).One(&si)
	if err != nil {
		msg := fmt.Sprintf("Service instance does not exists, error: %s", err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	s := si.Service()
	var b string
	if b, err = s.ProductionEndpoint().Status(&si); err != nil {
		msg := fmt.Sprintf("Could not retrieve status of service instance, error: %s", err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	b = fmt.Sprintf(`Service instance "%s" is %s`, siName, b)
	n, err := w.Write([]byte(b))
	if n != len(b) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write response body"}
	}
	return nil
}

func ServiceInfoHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	serviceName := r.URL.Query().Get(":name")
	_, err := GetServiceOrError(serviceName, u)
	if err != nil {
		return err
	}
	instances := []service.ServiceInstance{}
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	teamsNames := auth.GetTeamsNames(teams)
	err = db.Session.ServiceInstances().Find(bson.M{"service_name": serviceName, "teams": bson.M{"$in": teamsNames}}).All(&instances)
	if err != nil {
		return err
	}
	b, err := json.Marshal(instances)
	if err != nil {
		return nil
	}
	w.Write(b)
	return nil
}

func Doc(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	sName := r.URL.Query().Get(":name")
	s, err := GetServiceOrError(sName, u)
	if err != nil {
		return err
	}
	w.Write([]byte(s.Doc))
	return nil
}
<commit_msg>api/service: go fmt<commit_after>package consumption

import (
	"encoding/json"
	"fmt"
	"github.com/globocom/tsuru/api/auth"
	"github.com/globocom/tsuru/api/service"
	"github.com/globocom/tsuru/db"
	"github.com/globocom/tsuru/errors"
	"github.com/globocom/tsuru/log"
	"io/ioutil"
	"labix.org/v2/mgo/bson"
	"net/http"
)

func CreateInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	log.Print("Receiving request to create a service instance")
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Print("Got error while reading request body:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var sJson map[string]string
	err = json.Unmarshal(b, &sJson)
	if err != nil {
		log.Print("Got a problem while unmarshalling request's json:")
		log.Print(err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	var s service.Service
	err = validateInstanceForCreation(&s, sJson, u)
	if err != nil {
		log.Print("Got error while validation:")
		log.Print(err.Error())
		return err
	}
	var teamNames []string
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	for _, t := range teams {
		if s.HasTeam(&t) || !s.IsRestricted {
			teamNames = append(teamNames, t.Name)
		}
	}
	si := service.ServiceInstance{
		Name:        sJson["name"],
		ServiceName: sJson["service_name"],
		Teams:       teamNames,
	}
	if err = s.ProductionEndpoint().Create(&si); err != nil {
		log.Print("Error while calling create action from service api.")
		log.Print(err.Error())
		return err
	}
	err = si.Create()
	if err != nil {
		return err
	}
	fmt.Fprint(w, "success")
	return nil
}

func validateInstanceForCreation(s *service.Service, sJson map[string]string, u *auth.User) error {
	err := db.Session.Services().Find(bson.M{"_id": sJson["service_name"], "status": bson.M{"$ne": "deleted"}}).One(&s)
	if err != nil {
		msg := err.Error()
		if msg == "not found" {
			msg = fmt.Sprintf("Service %s does not exist.", sJson["service_name"])
		}
		return &errors.Http{Code: http.StatusNotFound, Message: msg}
	}
	_, err = GetServiceOrError(sJson["service_name"], u)
	if err != nil {
		return err
	}
	return nil
}

func RemoveServiceInstanceHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	name := r.URL.Query().Get(":name")
	si, err := GetServiceInstanceOrError(name, u)
	if err != nil {
		return err
	}
	if len(si.Apps) > 0 {
		msg := "This service instance has binded apps. Unbind them before removing it"
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	if err = si.Service().ProductionEndpoint().Destroy(&si); err != nil {
		//TODO(flaviamissi): return err
		return &errors.Http{Code: http.StatusInternalServerError, Message: err.Error()}
	}
	err = db.Session.ServiceInstances().Remove(bson.M{"name": name})
	if err != nil {
		return err
	}
	w.Write([]byte("service instance successfuly removed"))
	return nil
}

func ServicesInstancesHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	response := ServiceAndServiceInstancesByTeams(u)
	body, err := json.Marshal(response)
	if err != nil {
		return err
	}
	n, err := w.Write(body)
	if n != len(body) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write the response body."}
	}
	return err
}

func ServiceInstanceStatusHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	// #TODO (flaviamissi) should check if user has access to service
	// just call GetServiceInstanceOrError should be enough
	siName := r.URL.Query().Get(":instance")
	var si service.ServiceInstance
	if siName == "" {
		return &errors.Http{Code: http.StatusBadRequest, Message: "Service instance name not provided."}
	}
	err := db.Session.ServiceInstances().Find(bson.M{"name": siName}).One(&si)
	if err != nil {
		msg := fmt.Sprintf("Service instance does not exists, error: %s", err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	s := si.Service()
	var b string
	if b, err = s.ProductionEndpoint().Status(&si); err != nil {
		msg := fmt.Sprintf("Could not retrieve status of service instance, error: %s", err.Error())
		return &errors.Http{Code: http.StatusInternalServerError, Message: msg}
	}
	b = fmt.Sprintf(`Service instance "%s" is %s`, siName, b)
	n, err := w.Write([]byte(b))
	if n != len(b) {
		return &errors.Http{Code: http.StatusInternalServerError, Message: "Failed to write response body"}
	}
	return nil
}

func ServiceInfoHandler(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	serviceName := r.URL.Query().Get(":name")
	_, err := GetServiceOrError(serviceName, u)
	if err != nil {
		return err
	}
	instances := []service.ServiceInstance{}
	teams, err := u.Teams()
	if err != nil {
		return err
	}
	teamsNames := auth.GetTeamsNames(teams)
	err = db.Session.ServiceInstances().Find(bson.M{"service_name": serviceName, "teams": bson.M{"$in": teamsNames}}).All(&instances)
	if err != nil {
		return err
	}
	b, err := json.Marshal(instances)
	if err != nil {
		return nil
	}
	w.Write(b)
	return nil
}

func Doc(w http.ResponseWriter, r *http.Request, u *auth.User) error {
	sName := r.URL.Query().Get(":name")
	s, err := GetServiceOrError(sName, u)
	if err != nil {
		return err
	}
	w.Write([]byte(s.Doc))
	return nil
}
<|endoftext|>


<commit_before>// CookieJar - A contestant's algorithm toolbox
// Copyright 2013 Peter Szilagyi. All rights reserved.
//
// CookieJar is dual licensed: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// The toolbox is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// Alternatively, the CookieJar toolbox may be used in accordance with the terms
// and conditions contained in a signed written agreement between you and the
// author(s).
//
// Author: peterke@gmail.com (Peter Szilagyi)

// Command depsmerge implements a command to retrieve and merge all dependencies
// of a package into a single file.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
)

// Package description from the go list command
type Package struct {
	Name     string
	Dir      string
	Standard bool
	Deps     []string
	GoFiles  []string
}

// Loads the details of the Go package.
func details(name string) (*Package, error) {
	// Create the command to retrieve the package infos
	cmd := exec.Command("go", "list", "-e", "-json", name)

	// Retrieve the output, redirect the errors
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	cmd.Stderr = os.Stderr

	// Start executing and parse the results
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	defer cmd.Process.Kill()

	info := new(Package)
	if err := json.NewDecoder(out).Decode(&info); err != nil {
		return nil, err
	}
	// Clean up and return
	if err := cmd.Wait(); err != nil {
		return nil, err
	}
	return info, nil
}

// Collects all the imported packages of a file.
func dependencies(path string) (map[string][]string, error) {
	// Retrieve the dependencies of the source file
	info, err := details(path)
	if err != nil {
		return nil, err
	}
	// Iterate over every dependency and gather the sources
	sources := make(map[string][]string)
	for _, dep := range info.Deps {
		// Retrieve the dependency details
		pkgInfo, err := details(dep)
		if err != nil {
			return nil, err
		}
		// Gather external library sources
		if !pkgInfo.Standard {
			for _, src := range pkgInfo.GoFiles {
				sources[pkgInfo.Name] = append(sources[pkgInfo.Name], filepath.Join(pkgInfo.Dir, src))
			}
		}
	}
	return sources, nil
}

// Parses a source file and scopes all global declarations.
func rewrite(src string, pkg string, deps []string) (string, error) {
	fileSet := token.NewFileSet()
	tree, err := parser.ParseFile(fileSet, src, nil, parser.ParseComments)
	if err != nil {
		return "", err
	}
	// Scope all top level declarations
	for _, decl := range tree.Decls {
		switch v := decl.(type) {
		case *ast.FuncDecl:
			if v.Recv == nil {
				rename(tree, v.Name.String(), pkg+"•"+v.Name.String())
			}
		case *ast.GenDecl:
			for _, spec := range v.Specs {
				switch spec := spec.(type) {
				case *ast.ValueSpec:
					for _, name := range spec.Names {
						rename(tree, name.String(), pkg+"•"+name.String())
					}
				case *ast.TypeSpec:
					src, dst := spec.Name.String(), pkg+"•"+spec.Name.String()
					fmt.Printf("%s: Type %s -> %s\n", pkg, src, dst)
					rename(tree, src, dst)
				default:
					fmt.Println("Unknown spec:", spec)
				}
			}
		default:
			fmt.Println(v)
		}
	}
	// Generate the new source file
	out := bytes.NewBuffer(nil)
	for _, decl := range tree.Decls {
		if err := printer.Fprint(out, fileSet, decl); err != nil {
			return "", err
		}
		fmt.Fprintf(out, "\n\n")
	}
	blob := out.Bytes()

	// Dump all import statements and scope externals
	for _, dep := range deps {
		scoper := regexp.MustCompile("\\b" + dep + "\\.(.+)")
		blob = scoper.ReplaceAll(blob, []byte(dep+"•$1"))
	}
	fmt.Println(string(blob))
	return "", nil
}

// Renames a top level declaration to something else.
func rename(tree *ast.File, old, new string) {
	// Rename top-level declarations
	for _, decl := range tree.Decls {
		switch decl := decl.(type) {
		case *ast.FuncDecl:
			// If a top level function matches, rename
			if decl.Recv == nil && decl.Name.Name == old {
				decl.Name.Name = new
				decl.Name.Obj.Name = new
			}
		case *ast.GenDecl:
			// Iterate over all the generic declaration
			for _, spec := range decl.Specs {
				switch spec := spec.(type) {
				case *ast.ValueSpec:
					// If a top level variable matches, rename
					for _, name := range spec.Names {
						if name.Name == old {
							name.Name = new
							name.Obj.Name = new
						}
					}
				case *ast.TypeSpec:
					if spec.Name.Name == old {
						spec.Name.Name = new
						spec.Name.Obj.Name = new
					}
				}
			}
		}
	}
	// Walk the AST and rename all internal occurrences
	stack := []ast.Node{}
	ast.Inspect(tree, func(node ast.Node) bool {
		// Keep a traversal stack if need to reference parent
		if node == nil {
			stack = stack[:len(stack)-1]
			return true
		}
		stack = append(stack, node)

		// Look for identifiers to rename
		id, ok := node.(*ast.Ident)
		if ok && id.Obj == nil && id.Name == old {
			// If selected identifier, leave it alone
			if _, ok := stack[len(stack)-2].(*ast.SelectorExpr); ok {
				return true
			}
			id.Name = new
		}
		if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new {
			id.Name = id.Obj.Name
		}
		return true
	})
}

func main() {
	deps, err := dependencies(os.Args[1])
	if err != nil {
		log.Fatalf("Failed to parse dependency chain: %v.", err)
	}
	pkgs := []string{}
	for pkg, _ := range deps {
		pkgs = append(pkgs, pkg)
	}
	for pkg, sources := range deps {
		for _, src := range sources {
			rewrite(src, pkg, pkgs)
		}
	}
}
<commit_msg>Drop imports, rewrite only externals.<commit_after>// CookieJar - A contestant's algorithm toolbox
// Copyright 2013 Peter Szilagyi. All rights reserved.
//
// CookieJar is dual licensed: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// The toolbox is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// Alternatively, the CookieJar toolbox may be used in accordance with the terms
// and conditions contained in a signed written agreement between you and the
// author(s).
//
// Author: peterke@gmail.com (Peter Szilagyi)

// Command depsmerge implements a command to retrieve and merge all dependencies
// of a package into a single file.
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"go/ast"
	"go/parser"
	"go/printer"
	"go/token"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
)

// Package description from the go list command
type Package struct {
	Name     string
	Dir      string
	Standard bool
	Deps     []string
	GoFiles  []string
}

// Loads the details of the Go package.
func details(name string) (*Package, error) {
	// Create the command to retrieve the package infos
	cmd := exec.Command("go", "list", "-e", "-json", name)

	// Retrieve the output, redirect the errors
	out, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}
	cmd.Stderr = os.Stderr

	// Start executing and parse the results
	if err := cmd.Start(); err != nil {
		return nil, err
	}
	defer cmd.Process.Kill()

	info := new(Package)
	if err := json.NewDecoder(out).Decode(&info); err != nil {
		return nil, err
	}
	// Clean up and return
	if err := cmd.Wait(); err != nil {
		return nil, err
	}
	return info, nil
}

// Collects all the imported packages of a file.
func dependencies(path string) (map[string][]string, error) {
	// Retrieve the dependencies of the source file
	info, err := details(path)
	if err != nil {
		return nil, err
	}
	// Iterate over every dependency and gather the sources
	sources := make(map[string][]string)
	for _, dep := range info.Deps {
		// Retrieve the dependency details
		pkgInfo, err := details(dep)
		if err != nil {
			return nil, err
		}
		// Gather external library sources
		if !pkgInfo.Standard {
			for _, src := range pkgInfo.GoFiles {
				sources[pkgInfo.Name] = append(sources[pkgInfo.Name], filepath.Join(pkgInfo.Dir, src))
			}
		}
	}
	return sources, nil
}

// Parses a source file and scopes all global declarations.
func rewrite(src string, pkg string, deps []string) (string, error) {
	fileSet := token.NewFileSet()
	tree, err := parser.ParseFile(fileSet, src, nil, parser.ParseComments)
	if err != nil {
		return "", err
	}
	// Scope all top level declarations if not main file
	if pkg != "" {
		for _, decl := range tree.Decls {
			switch v := decl.(type) {
			case *ast.FuncDecl:
				if v.Recv == nil {
					rename(tree, v.Name.String(), pkg+"•"+v.Name.String())
				}
			case *ast.GenDecl:
				for _, spec := range v.Specs {
					switch spec := spec.(type) {
					case *ast.ValueSpec:
						for _, name := range spec.Names {
							rename(tree, name.String(), pkg+"•"+name.String())
						}
					case *ast.TypeSpec:
						rename(tree, spec.Name.String(), pkg+"•"+spec.Name.String())
					default:
						fmt.Println("Unknown spec:", spec)
					}
				}
			default:
				fmt.Println(v)
			}
		}
	}
	// Generate the new source contents
	out := bytes.NewBuffer(nil)
	for _, decl := range tree.Decls {
		if gen, ok := decl.(*ast.GenDecl); !ok || gen.Tok != token.IMPORT {
			if err := printer.Fprint(out, fileSet, decl); err != nil {
				return "", err
			}
		}
		fmt.Fprintf(out, "\n\n")
	}
	blob := out.Bytes()

	// Scope all externally imported dependencies
	var fail error
	ast.Inspect(tree, func(node ast.Node) bool {
		if imp, ok := node.(*ast.GenDecl); ok && imp.Tok == token.IMPORT {
			for _, spec := range imp.Specs {
				if spec, ok := spec.(*ast.ImportSpec); ok {
					// Figure out the correct name of the import
					path := strings.Trim(spec.Path.Value, "\"")
					if info, err := details(path); err != nil {
						fail = err
						return false
					} else if !info.Standard {
						// Add scope to external import
						scoper := regexp.MustCompile("\\b" + info.Name + "\\.(.+)")
						blob = scoper.ReplaceAll(blob, []byte(info.Name+"•$1"))
					}
				}
			}
		}
		return true
	})
	if fail != nil {
		return "", fail
	}
	fmt.Println(string(blob))
	return "", nil
}

// Renames a top level declaration to something else.
func rename(tree *ast.File, old, new string) {
	// Rename top-level declarations
	for _, decl := range tree.Decls {
		switch decl := decl.(type) {
		case *ast.FuncDecl:
			// If a top level function matches, rename
			if decl.Recv == nil && decl.Name.Name == old {
				decl.Name.Name = new
				decl.Name.Obj.Name = new
			}
		case *ast.GenDecl:
			// Iterate over all the generic declaration
			for _, spec := range decl.Specs {
				switch spec := spec.(type) {
				case *ast.ValueSpec:
					// If a top level variable matches, rename
					for _, name := range spec.Names {
						if name.Name == old {
							name.Name = new
							name.Obj.Name = new
						}
					}
				case *ast.TypeSpec:
					if spec.Name.Name == old {
						spec.Name.Name = new
						spec.Name.Obj.Name = new
					}
				}
			}
		}
	}
	// Walk the AST and rename all internal occurrences
	stack := []ast.Node{}
	ast.Inspect(tree, func(node ast.Node) bool {
		// Keep a traversal stack if need to reference parent
		if node == nil {
			stack = stack[:len(stack)-1]
			return true
		}
		stack = append(stack, node)

		// Look for identifiers to rename
		id, ok := node.(*ast.Ident)
		if ok && id.Obj == nil && id.Name == old {
			// If selected identifier, leave it alone
			if _, ok := stack[len(stack)-2].(*ast.SelectorExpr); ok {
				return true
			}
			id.Name = new
		}
		if ok && id.Obj != nil && id.Name == old && id.Obj.Name == new {
			id.Name = id.Obj.Name
		}
		return true
	})
}

func main() {
	deps, err := dependencies(os.Args[1])
	if err != nil {
		log.Fatalf("Failed to parse dependency chain: %v.", err)
	}
	pkgs := []string{}
	for pkg, _ := range deps {
		pkgs = append(pkgs, pkg)
	}
	// Rewrite all the dependencies
	for pkg, sources := range deps {
		for _, src := range sources {
			fmt.Println(rewrite(src, pkg, pkgs))
		}
	}
	// Rewrite the main file itself
	fmt.Println(rewrite(os.Args[1], "", pkgs))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/thought-machine/please/tools/please_go/test"
	"log"
	"os"

	"github.com/peterebden/go-cli-init/v4/flags"

	"github.com/thought-machine/please/tools/please_go/install"
)

var opts = struct {
	Usage string

	PleaseGoInstall struct {
		SrcRoot      string `short:"r" long:"src_root" description:"The src root of the module to inspect" default:"."`
		ModuleName   string `short:"n" long:"module_name" description:"The name of the module" required:"true"`
		ImportConfig string `short:"i" long:"importcfg" description:"The import config for the modules dependencies" required:"true"`
		LDFlags      string `short:"l" long:"ld_flags" description:"The file to write linker flags to" default:"LD_FLAGS"`
		GoTool       string `short:"g" long:"go_tool" description:"The location of the go binary" default:"go"`
		CCTool       string `short:"c" long:"cc_tool" description:"The c compiler to use"`
		Out          string `short:"o" long:"out" description:"The output directory to put compiled artifacts in" required:"true"`
		Args         struct {
			Packages []string `positional-arg-name:"packages" description:"The packages to compile"`
		} `positional-args:"true" required:"true"`
	} `command:"install" alias:"i" description:"Compile a go module similarly to 'go install'"`
	Test struct {
		Dir        string   `short:"d" long:"dir" description:"Directory to search for Go package files for coverage"`
		Exclude    []string `short:"x" long:"exclude" default:"third_party/go" description:"Directories to exclude from search"`
		Output     string   `short:"o" long:"output" description:"Output filename" required:"true"`
		Package    string   `short:"p" long:"package" description:"Package containing this test" env:"PKG_DIR"`
		ImportPath string   `short:"i" long:"import_path" description:"Full import path to the package"`
		Benchmark  bool     `short:"b" long:"benchmark" description:"Whether to run benchmarks instead of tests"`
		Args       struct {
			Sources []string `positional-arg-name:"sources" description:"Test source files" required:"true"`
		} `positional-args:"true" required:"true"`
	} `command:"testmain" alias:"t" description:"Generates a go main package to run the tests in a package."`
}{
	Usage: `
please-go is used by the go build rules to compile and test go modules and packages.

Unlike 'go build', this tool doesn't rely on the go path or modules to find packages. Instead it takes in
a go import config just like 'go tool compile/link -importcfg'.
`,
}

var subCommands = map[string]func() int{
	"install": func() int {
		pleaseGoInstall := install.New(
			opts.PleaseGoInstall.SrcRoot,
			opts.PleaseGoInstall.ModuleName,
			opts.PleaseGoInstall.ImportConfig,
			opts.PleaseGoInstall.LDFlags,
			opts.PleaseGoInstall.GoTool,
			opts.PleaseGoInstall.CCTool,
			opts.PleaseGoInstall.Out,
		)
		if err := pleaseGoInstall.Install(opts.PleaseGoInstall.Args.Packages); err != nil {
			log.Fatal(err)
		}
		return 0
	},
	"testmain": func() int {
		test.PleaseGoTest(
			opts.Test.Dir,
			opts.Test.ImportPath,
			opts.Test.Package,
			opts.Test.Output,
			opts.Test.Args.Sources,
			opts.Test.Exclude,
			opts.Test.Benchmark,
		)
		return 0
	},
}

func main() {
	command := flags.ParseFlagsOrDie("please-go", &opts)
	os.Exit(subCommands[command]())
}
<commit_msg>Make please_go handle relative paths to tools (#1623)<commit_after>package main

import (
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/peterebden/go-cli-init/v4/flags"

	"github.com/thought-machine/please/tools/please_go/install"
	"github.com/thought-machine/please/tools/please_go/test"
)

var opts = struct {
	Usage string

	PleaseGoInstall struct {
		SrcRoot      string `short:"r" long:"src_root" description:"The src root of the module to inspect" default:"."`
		ModuleName   string `short:"n" long:"module_name" description:"The name of the module" required:"true"`
		ImportConfig string `short:"i" long:"importcfg" description:"The import config for the modules dependencies" required:"true"`
		LDFlags      string `short:"l" long:"ld_flags" description:"The file to write linker flags to" default:"LD_FLAGS"`
		GoTool       string `short:"g" long:"go_tool" description:"The location of the go binary" default:"go"`
		CCTool       string `short:"c" long:"cc_tool" description:"The c compiler to use"`
		Out          string `short:"o" long:"out" description:"The output directory to put compiled artifacts in" required:"true"`
		Args         struct {
			Packages []string `positional-arg-name:"packages" description:"The packages to compile"`
		} `positional-args:"true" required:"true"`
	} `command:"install" alias:"i" description:"Compile a go module similarly to 'go install'"`
	Test struct {
		Dir        string   `short:"d" long:"dir" description:"Directory to search for Go package files for coverage"`
		Exclude    []string `short:"x" long:"exclude" default:"third_party/go" description:"Directories to exclude from search"`
		Output     string   `short:"o" long:"output" description:"Output filename" required:"true"`
		Package    string   `short:"p" long:"package" description:"Package containing this test" env:"PKG_DIR"`
		ImportPath string   `short:"i" long:"import_path" description:"Full import path to the package"`
		Benchmark  bool     `short:"b" long:"benchmark" description:"Whether to run benchmarks instead of tests"`
		Args       struct {
			Sources []string `positional-arg-name:"sources" description:"Test source files" required:"true"`
		} `positional-args:"true" required:"true"`
	} `command:"testmain" alias:"t" description:"Generates a go main package to run the tests in a package."`
}{
	Usage: `
please-go is used by the go build rules to compile and test go modules and packages.

Unlike 'go build', this tool doesn't rely on the go path or modules to find packages. Instead it takes in
a go import config just like 'go tool compile/link -importcfg'.
`,
}

var subCommands = map[string]func() int{
	"install": func() int {
		pleaseGoInstall := install.New(
			opts.PleaseGoInstall.SrcRoot,
			opts.PleaseGoInstall.ModuleName,
			opts.PleaseGoInstall.ImportConfig,
			opts.PleaseGoInstall.LDFlags,
			mustResolvePath(opts.PleaseGoInstall.GoTool),
			mustResolvePath(opts.PleaseGoInstall.CCTool),
			opts.PleaseGoInstall.Out,
		)
		if err := pleaseGoInstall.Install(opts.PleaseGoInstall.Args.Packages); err != nil {
			log.Fatal(err)
		}
		return 0
	},
	"testmain": func() int {
		test.PleaseGoTest(
			opts.Test.Dir,
			opts.Test.ImportPath,
			opts.Test.Package,
			opts.Test.Output,
			opts.Test.Args.Sources,
			opts.Test.Exclude,
			opts.Test.Benchmark,
		)
		return 0
	},
}

func main() {
	command := flags.ParseFlagsOrDie("please-go", &opts)
	os.Exit(subCommands[command]())
}

// mustResolvePath converts a relative path to absolute if it has any separators in it.
func mustResolvePath(in string) string {
	if in == "" {
		return in
	}
	if !filepath.IsAbs(in) && strings.ContainsRune(in, filepath.Separator) {
		abs, err := filepath.Abs(in)
		if err != nil {
			log.Fatalf("Failed to make %s absolute: %s", in, err)
		}
		return abs
	}
	return in
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Knative 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 sources

import (
	"context"
	"testing"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	testlib "knative.dev/eventing/test/lib"
	"knative.dev/pkg/apis/duck"

	rbacv1 "k8s.io/api/rbac/v1"
)

var clusterRoleName = "eventing-sources-source-observer"
var clusterRoleLabel = map[string]string{
	duck.SourceDuckVersionLabel: "true",
}

/*
	The test checks for the following in this order:
	1. Find cluster roles that match these criteria -
		a. Has name "eventing-sources-source-observer",
		b. Has label duck.knative.dev/source: "true",
		c. Has the eventing source in Resources for a Policy Rule, and
		d. Has all the expected verbs (get, list, watch)
*/
func SourceCRDRBACTestHelperWithComponentsTestRunner(
	t *testing.T,
	sourceTestRunner testlib.ComponentsTestRunner,
	options ...testlib.SetupClientOption,
) {

	sourceTestRunner.RunTests(t, testlib.FeatureBasic, func(st *testing.T, source metav1.TypeMeta) {
		client := testlib.Setup(st, true, options...)
		defer testlib.TearDown(client)

		// From spec:
		// Each source MUST have the following:
		// kind: ClusterRole
		// apiVersion: rbac.authorization.k8s.io/v1
		// metadata:
		//   name: foos-source-observer
		//   labels:
		//     duck.knative.dev/source: "true"
		// rules:
		//   - apiGroups:
		//       - example.com
		//     resources:
		//       - foos
		//     verbs:
		//       - get
		//       - list
		//       - watch
		st.Run("Source CRD has source observer cluster role", func(t *testing.T) {
			ValidateRBAC(st, client, source)
		})

	})
}

func ValidateRBAC(st *testing.T, client *testlib.Client, object metav1.TypeMeta) {
	labelSelector := &metav1.LabelSelector{
		MatchLabels: clusterRoleLabel,
	}

	sourcePluralName := getSourcePluralName(client, object)

	//Spec: New sources MUST include a ClusterRole as part of installing themselves into a cluster.
	if !clusterRoleMeetsSpecs(client, labelSelector, sourcePluralName) {
		client.T.Fatalf("can't find source observer cluster role for CRD %q", object)
	}
}

func getSourcePluralName(client *testlib.Client, object metav1.TypeMeta) string {
	gvr, _ := meta.UnsafeGuessKindToResource(object.GroupVersionKind())
	crdName := gvr.Resource + "." + gvr.Group

	crd, err := client.Apiextensions.CustomResourceDefinitions().Get(context.Background(), crdName, metav1.GetOptions{
		TypeMeta: metav1.TypeMeta{},
	})
	if err != nil {
		client.T.Errorf("error while getting %q:%v", object, err)
	}
	return crd.Spec.Names.Plural
}

func clusterRoleMeetsSpecs(client *testlib.Client, labelSelector *metav1.LabelSelector, crdSourceName string) bool {
	crs, err := client.Kube.RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{
		FieldSelector: fields.OneTermEqualSelector("metadata.name", clusterRoleName).String(), //Cluster Role with name "eventing-sources-source-observer"
		LabelSelector: labels.Set(labelSelector.MatchLabels).String(),                         //Cluster Role with duck.knative.dev/source: "true" label
	})
	if err != nil {
		client.T.Errorf("error while getting cluster roles %v", err)
	}

	for _, cr := range crs.Items {
		for _, pr := range cr.Rules {
			if contains(pr.Resources, crdSourceName) && //Cluster Role has the eventing source listed in Resources for a Policy Rule
				((contains(pr.Verbs, "get") && contains(pr.Verbs, "list") && contains(pr.Verbs, "watch")) ||
					contains(pr.Verbs, rbacv1.VerbAll)) { //Cluster Role has all the expected Verbs
				return true
			}
		}
	}
	return false
}

func contains(s []string, str string) bool {
	for _, v := range s {
		if v == str {
			return true
		}
	}
	return false
}
<commit_msg>Changing cluster role's name check to check only suffix for source spec conformance testing (#5105)<commit_after>/*
Copyright 2021 The Knative 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 sources

import (
	"context"
	"testing"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	testlib "knative.dev/eventing/test/lib"
	"knative.dev/pkg/apis/duck"

	rbacv1 "k8s.io/api/rbac/v1"
)

var clusterRoleLabel = map[string]string{
	duck.SourceDuckVersionLabel: "true",
}

/*
	The test checks for the following in this order:
	1. Find cluster roles that match these criteria -
		a. Has label duck.knative.dev/source: "true",
		b. Has the eventing source in Resources for a Policy Rule, and
		c. Has all the expected verbs (get, list, watch)
*/
func SourceCRDRBACTestHelperWithComponentsTestRunner(
	t *testing.T,
	sourceTestRunner testlib.ComponentsTestRunner,
	options ...testlib.SetupClientOption,
) {

	sourceTestRunner.RunTests(t, testlib.FeatureBasic, func(st *testing.T, source metav1.TypeMeta) {
		client := testlib.Setup(st, true, options...)
		defer testlib.TearDown(client)

		// From spec:
		// Each source MUST have the following:
		// kind: ClusterRole
		// apiVersion: rbac.authorization.k8s.io/v1
		// metadata:
		//   name: foos-source-observer
		//   labels:
		//     duck.knative.dev/source: "true"
		// rules:
		//   - apiGroups:
		//       - example.com
		//     resources:
		//       - foos
		//     verbs:
		//       - get
		//       - list
		//       - watch
		st.Run("Source CRD has source observer cluster role", func(t *testing.T) {
			ValidateRBAC(st, client, source)
		})

	})
}

func ValidateRBAC(st *testing.T, client *testlib.Client, object metav1.TypeMeta) {
	labelSelector := &metav1.LabelSelector{
		MatchLabels: clusterRoleLabel,
	}

	sourcePluralName := getSourcePluralName(client, object)

	//Spec: New sources MUST include a ClusterRole as part of installing themselves into a cluster.
	if !clusterRoleMeetsSpecs(client, labelSelector, sourcePluralName) {
		client.T.Fatalf("can't find source observer cluster role for CRD %q", object)
	}
}

func getSourcePluralName(client *testlib.Client, object metav1.TypeMeta) string {
	gvr, _ := meta.UnsafeGuessKindToResource(object.GroupVersionKind())
	crdName := gvr.Resource + "." + gvr.Group

	crd, err := client.Apiextensions.CustomResourceDefinitions().Get(context.Background(), crdName, metav1.GetOptions{
		TypeMeta: metav1.TypeMeta{},
	})
	if err != nil {
		client.T.Errorf("error while getting %q:%v", object, err)
	}
	return crd.Spec.Names.Plural
}

func clusterRoleMeetsSpecs(client *testlib.Client, labelSelector *metav1.LabelSelector, crdSourceName string) bool {
	crs, err := client.Kube.RbacV1().ClusterRoles().List(context.Background(), metav1.ListOptions{
		LabelSelector: labels.Set(labelSelector.MatchLabels).String(), //Cluster Role with duck.knative.dev/source: "true" label
	})
	if err != nil {
		client.T.Errorf("error while getting cluster roles %v", err)
	}

	for _, cr := range crs.Items {
		for _, pr := range cr.Rules {
			if contains(pr.Resources, crdSourceName) && //Cluster Role has the eventing source listed in Resources for a Policy Rule
				((contains(pr.Verbs, "get") && contains(pr.Verbs, "list") && contains(pr.Verbs, "watch")) ||
					contains(pr.Verbs, rbacv1.VerbAll)) { //Cluster Role has all the expected Verbs
				return true
			}
		}
	}
	return false
}

func contains(s []string, str string) bool {
	for _, v := range s {
		if v == str {
			return true
		}
	}
	return false
}
<|endoftext|>