<commit_before>/*
   Copyright The containerd 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 platforms

import (
	"runtime"
	"strings"
)

// isLinuxOS returns true if the operating system is Linux.
//
// The OS value should be normalized before calling this function.
func isLinuxOS(os string) bool {
	return os == "linux"
}

// These function are generated from from https://golang.org/src/go/build/syslist.go.
//
// We use switch statements because they are slightly faster than map lookups
// and use a little less memory.

// isKnownOS returns true if we know about the operating system.
//
// The OS value should be normalized before calling this function.
func isKnownOS(os string) bool {
	switch os {
	case "android", "darwin", "dragonfly", "freebsd", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
		return true
	}
	return false
}

// isArmArch returns true if the architecture is ARM.
//
// The arch value should be normalized before being passed to this function.
func isArmArch(arch string) bool {
	switch arch {
	case "arm", "arm64":
		return true
	}
	return false
}

// isKnownArch returns true if we know about the architecture.
//
// The arch value should be normalized before being passed to this function.
func isKnownArch(arch string) bool {
	switch arch {
	case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "s390", "s390x", "sparc", "sparc64":
		return true
	}
	return false
}

func normalizeOS(os string) string {
	if os == "" {
		return runtime.GOOS
	}
	os = strings.ToLower(os)

	switch os {
	case "macos":
		os = "darwin"
	}
	return os
}

// normalizeArch normalizes the architecture.
func normalizeArch(arch, variant string) (string, string) {
	arch, variant = strings.ToLower(arch), strings.ToLower(variant)
	switch arch {
	case "i386":
		arch = "386"
		variant = ""
	case "x86_64", "x86-64":
		arch = "amd64"
		variant = ""
	case "aarch64", "arm64":
		arch = "arm64"
		switch variant {
		case "8", "v8":
			variant = ""
		}
	case "armhf":
		arch = "arm"
		variant = "v7"
	case "armel":
		arch = "arm"
		variant = "v6"
	case "arm":
		switch variant {
		case "", "7":
			variant = "v7"
		case "5", "6", "8":
			variant = "v" + variant
		}
	}

	return arch, variant
}
<commit_msg>platforms: update known OS and arch values<commit_after>/*
   Copyright The containerd 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 platforms

import (
	"runtime"
	"strings"
)

// isLinuxOS returns true if the operating system is Linux.
//
// The OS value should be normalized before calling this function.
func isLinuxOS(os string) bool {
	return os == "linux"
}

// These function are generated from from https://golang.org/src/go/build/syslist.go.
//
// We use switch statements because they are slightly faster than map lookups
// and use a little less memory.

// isKnownOS returns true if we know about the operating system.
//
// The OS value should be normalized before calling this function.
func isKnownOS(os string) bool {
	switch os {
	case "aix", "android", "darwin", "dragonfly", "freebsd", "hurd", "illumos", "js", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows", "zos":
		return true
	}
	return false
}

// isArmArch returns true if the architecture is ARM.
//
// The arch value should be normalized before being passed to this function.
func isArmArch(arch string) bool {
	switch arch {
	case "arm", "arm64":
		return true
	}
	return false
}

// isKnownArch returns true if we know about the architecture.
//
// The arch value should be normalized before being passed to this function.
func isKnownArch(arch string) bool {
	switch arch {
	case "386", "amd64", "amd64p32", "arm", "armbe", "arm64", "arm64be", "ppc64", "ppc64le", "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le", "ppc", "riscv", "riscv64", "s390", "s390x", "sparc", "sparc64", "wasm":
		return true
	}
	return false
}

func normalizeOS(os string) string {
	if os == "" {
		return runtime.GOOS
	}
	os = strings.ToLower(os)

	switch os {
	case "macos":
		os = "darwin"
	}
	return os
}

// normalizeArch normalizes the architecture.
func normalizeArch(arch, variant string) (string, string) {
	arch, variant = strings.ToLower(arch), strings.ToLower(variant)
	switch arch {
	case "i386":
		arch = "386"
		variant = ""
	case "x86_64", "x86-64":
		arch = "amd64"
		variant = ""
	case "aarch64", "arm64":
		arch = "arm64"
		switch variant {
		case "8", "v8":
			variant = ""
		}
	case "armhf":
		arch = "arm"
		variant = "v7"
	case "armel":
		arch = "arm"
		variant = "v6"
	case "arm":
		switch variant {
		case "", "7":
			variant = "v7"
		case "5", "6", "8":
			variant = "v" + variant
		}
	}

	return arch, variant
}
<|endoftext|>


<commit_before>package bonjour

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

type BonjourNotify interface {
	NewMember(net.IP)
	RemoveMember(net.IP)
}

type Bonjour struct {
	ServiceName   string
	ServiceDomain string
	ServicePort   int
	InterfaceName string
	BindToIntf    bool
	Notify        BonjourNotify
}

type cacheEntry struct {
	serviceEntry *ServiceEntry
	lastSeen     time.Time
}

var dnsCache map[string]cacheEntry
var queryChan chan *ServiceEntry

func (b Bonjour) publish() {
	ifName := b.InterfaceName
	sleeper := time.Second * 30
	for {
		var iface *net.Interface = nil
		var err error
		if ifName != "" {
			iface, err = net.InterfaceByName(ifName)
			if err != nil {
				log.Fatalln(err.Error())
			}
		}
		instance, err := os.Hostname()
		_, err = Register(instance, b.ServiceName,
			b.ServiceDomain, b.ServicePort,
			[]string{"txtv=1", "key1=val1", "key2=val2"}, iface, b.BindToIntf)
		if err != nil {
			log.Fatalln(err.Error())
		}
		time.Sleep(sleeper)
	}
}

func (b Bonjour) lookup(resolver *Resolver, query chan *ServiceEntry) {
	for {
		select {
		case e := <-query:
			err := resolver.Lookup(e.Instance, e.Service, e.Domain)
			if err != nil {
				log.Println("Failed to browse:", err.Error())
			}
		}
	}
}

func (b Bonjour) resolve(resolver *Resolver, results chan *ServiceEntry) {
	err := resolver.Browse(b.ServiceName, b.ServiceDomain)
	if err != nil {
		log.Println("Failed to browse:", err.Error())
	}
	for e := range results {
		if e.AddrIPv4 == nil {
			queryChan <- e
		} else if !isMyAddress(e.AddrIPv4.String()) {
			if e.TTL > 0 {
				if _, ok := dnsCache[e.AddrIPv4.String()]; !ok {
					log.Printf("New Bonjour Member : %s, %s, %s, %s",
						e.Instance, e.Service, e.Domain, e.AddrIPv4)
					if b.Notify != nil {
						b.Notify.NewMember(e.AddrIPv4)
					}
				}
				dnsCache[e.AddrIPv4.String()] = cacheEntry{e, time.Now()}
			} else {
				log.Printf("Bonjour Member Gone : %s, %s, %s, %s", e.Instance, e.Service, e.Domain, e.AddrIPv4)
				if b.Notify != nil {
					b.Notify.RemoveMember(e.AddrIPv4)
				}
				delete(dnsCache, e.AddrIPv4.String())
			}
		}
	}
}

func isMyAddress(address string) bool {
	intAddrs, err := net.InterfaceAddrs()
	if err != nil {
		return false
	}
	for _, a := range intAddrs {
		if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.String() == address {
			return true
		}
	}
	return false
}

func (b Bonjour) keepAlive(resolver *Resolver) {
	sleeper := time.Second * 30
	for {
		for key, e := range dnsCache {
			if time.Now().Sub(e.lastSeen) > sleeper*2 {
				if b.Notify != nil {
					b.Notify.NewMember(net.ParseIP(key))
				}
				delete(dnsCache, key)
				log.Println("Bonjour Member timed out : ", key)
			}
		}
		time.Sleep(sleeper)
	}
}

func (b Bonjour) Start() error {
	dnsCache = make(map[string]cacheEntry)
	queryChan = make(chan *ServiceEntry)
	results := make(chan *ServiceEntry)
	resolver, err := NewResolver(nil, results)
	if err != nil {
		log.Println("Failed to initialize resolver:", err.Error())
		return err
	}

	go b.publish()
	go b.resolve(resolver, results)
	go b.lookup(resolver, queryChan)
	go b.keepAlive(resolver)
	return nil
}
<commit_msg>Replaced fatalln with println<commit_after>package bonjour

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

type BonjourNotify interface {
	NewMember(net.IP)
	RemoveMember(net.IP)
}

type Bonjour struct {
	ServiceName   string
	ServiceDomain string
	ServicePort   int
	InterfaceName string
	BindToIntf    bool
	Notify        BonjourNotify
}

type cacheEntry struct {
	serviceEntry *ServiceEntry
	lastSeen     time.Time
}

var dnsCache map[string]cacheEntry
var queryChan chan *ServiceEntry

func (b Bonjour) publish() {
	ifName := b.InterfaceName
	sleeper := time.Second * 30
	for {
		var iface *net.Interface = nil
		var err error
		if ifName != "" {
			iface, err = net.InterfaceByName(ifName)
			if err != nil {
				log.Println(err.Error())
			}
		}
		instance, err := os.Hostname()
		_, err = Register(instance, b.ServiceName,
			b.ServiceDomain, b.ServicePort,
			[]string{"txtv=1", "key1=val1", "key2=val2"}, iface, b.BindToIntf)
		if err != nil {
			log.Println(err.Error())
		}
		time.Sleep(sleeper)
	}
}

func (b Bonjour) lookup(resolver *Resolver, query chan *ServiceEntry) {
	for {
		select {
		case e := <-query:
			err := resolver.Lookup(e.Instance, e.Service, e.Domain)
			if err != nil {
				log.Println("Failed to browse:", err.Error())
			}
		}
	}
}

func (b Bonjour) resolve(resolver *Resolver, results chan *ServiceEntry) {
	err := resolver.Browse(b.ServiceName, b.ServiceDomain)
	if err != nil {
		log.Println("Failed to browse:", err.Error())
	}
	for e := range results {
		if e.AddrIPv4 == nil {
			queryChan <- e
		} else if !isMyAddress(e.AddrIPv4.String()) {
			if e.TTL > 0 {
				if _, ok := dnsCache[e.AddrIPv4.String()]; !ok {
					log.Printf("New Bonjour Member : %s, %s, %s, %s",
						e.Instance, e.Service, e.Domain, e.AddrIPv4)
					if b.Notify != nil {
						b.Notify.NewMember(e.AddrIPv4)
					}
				}
				dnsCache[e.AddrIPv4.String()] = cacheEntry{e, time.Now()}
			} else {
				log.Printf("Bonjour Member Gone : %s, %s, %s, %s", e.Instance, e.Service, e.Domain, e.AddrIPv4)
				if b.Notify != nil {
					b.Notify.RemoveMember(e.AddrIPv4)
				}
				delete(dnsCache, e.AddrIPv4.String())
			}
		}
	}
}

func isMyAddress(address string) bool {
	intAddrs, err := net.InterfaceAddrs()
	if err != nil {
		return false
	}
	for _, a := range intAddrs {
		if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.String() == address {
			return true
		}
	}
	return false
}

func (b Bonjour) keepAlive(resolver *Resolver) {
	sleeper := time.Second * 30
	for {
		for key, e := range dnsCache {
			if time.Now().Sub(e.lastSeen) > sleeper*2 {
				if b.Notify != nil {
					b.Notify.NewMember(net.ParseIP(key))
				}
				delete(dnsCache, key)
				log.Println("Bonjour Member timed out : ", key)
			}
		}
		time.Sleep(sleeper)
	}
}

func (b Bonjour) Start() error {
	dnsCache = make(map[string]cacheEntry)
	queryChan = make(chan *ServiceEntry)
	results := make(chan *ServiceEntry)
	resolver, err := NewResolver(nil, results)
	if err != nil {
		log.Println("Failed to initialize resolver:", err.Error())
		return err
	}

	go b.publish()
	go b.resolve(resolver, results)
	go b.lookup(resolver, queryChan)
	go b.keepAlive(resolver)
	return nil
}
<|endoftext|>


<commit_before>package bot

import "github.com/yuin/gopher-lua"

// RegisterBot will register the Bot struct to Lua
func RegisterBot(state *lua.LState) {
	// Create the Metatable
	meta := state.NewTypeMetatable("bot")
	state.SetGlobal("bot", meta)

	// Bind our functions
	state.SetField(meta, "__index", state.NewFunction(botIndex))
}

// RegisterConfig will register the Config struct to Lua
func RegisterConfig(state *lua.LState) {
	// Create the Metatable
	meta := state.NewTypeMetatable("config")
	state.SetGlobal("config", meta)

	// Bind our functions
	state.SetField(meta, "__index", state.NewFunction(configIndex))
}

// PushBot will push an existing *Bot onto the Lua stack
func PushBot(bot *Bot, state *lua.LState) {
	// Create the bot user data
	data := state.NewUserData()
	data.Value = bot

	// Set the Metatable
	state.SetMetatable(data, state.GetTypeMetatable("bot"))

	// Push the bot
	state.Push(data)
}

// PushConfig will push the given config to the Lua stack
func PushConfig(config *Config, state *lua.LState) {
	// Create the config user data
	data := state.NewUserData()
	data.Value = config

	// Set the Metatable
	state.SetMetatable(data, state.GetTypeMetatable("config"))

	// Push the config
	state.Push(data)
}

// checkBot will check wether the first argument is a *Bot
// or not.
func checkBot(state *lua.LState) *Bot {
	data := state.CheckUserData(1)
	if v, ok := data.Value.(*Bot); ok {
		return v
	}
	state.ArgError(1, "bot expected")
	return nil
}

// checkConfig will check wether the first argument is of type *Config
// and return it.
func checkConfig(state *lua.LState) *Config {
	data := state.CheckUserData(1)
	if v, ok := data.Value.(*Config); ok {
		return v
	}
	state.ArgError(1, "config expected")
	return nil
}

func botSend(state *lua.LState) int {
	bot := checkBot(state)
	rcv := state.CheckString(2)
	msg := state.CheckString(3)

	bot.Send(rcv, msg)
	return 0
}

func botJoin(state *lua.LState) int {
	bot := checkBot(state)
	channel := state.CheckString(2)

	bot.Join(channel)
	return 0
}

func botIndex(state *lua.LState) int {
	bot := checkBot(state)
	key := state.CheckString(2)

	switch key {
	case "send":
		state.Push(state.NewFunction(botSend))
	case "join":
		state.Push(state.NewFunction(botJoin))
	case "config":
		PushConfig(&bot.config, state)
	default:
		state.Push(lua.LNil)
	}

	return 1
}

func configIndex(state *lua.LState) int {
	config := checkConfig(state)
	key := state.CheckString(2)

	switch key {
	case "Hostname":
		state.Push(lua.LString(config.Hostname))
	case "Port":
		state.Push(lua.LNumber(config.Port))
	case "Secure":
		state.Push(lua.LBool(config.Secure))
	case "InsecureSkipVerify":
		state.Push(lua.LBool(config.InsecureSkipVerify))
	case "Nick":
		state.Push(lua.LString(config.Nick))
	case "User":
		state.Push(lua.LString(config.User))
	case "Name":
		state.Push(lua.LString(config.Name))
	case "Channels":
		channels := state.NewTable()
		for key, value := range config.Channels {
			channels.RawSetInt(key, lua.LString(value))
		}
		state.Push(channels)
	case "Timeout":
		state.Push(lua.LNumber(config.Timeout))
	case "TimeoutLimit":
		state.Push(lua.LNumber(config.TimeoutLimit))
	case "ReconnectLimit":
		state.Push(lua.LNumber(config.ReconnectLimit))
	default:
		state.Push(lua.LNil)
	}

	return 1
}
<commit_msg>removed some unnecessary comments<commit_after>package bot

import "github.com/yuin/gopher-lua"

// RegisterBot will register the Bot struct to Lua
func RegisterBot(state *lua.LState) {
	// Create the Metatable
	meta := state.NewTypeMetatable("bot")
	state.SetGlobal("bot", meta)

	// Bind our functions
	state.SetField(meta, "__index", state.NewFunction(botIndex))
}

// RegisterConfig will register the Config struct to Lua
func RegisterConfig(state *lua.LState) {
	// Create the Metatable
	meta := state.NewTypeMetatable("config")
	state.SetGlobal("config", meta)

	// Bind our functions
	state.SetField(meta, "__index", state.NewFunction(configIndex))
}

// PushBot will push an existing *Bot onto the Lua stack
func PushBot(bot *Bot, state *lua.LState) {
	// Create the bot user data
	data := state.NewUserData()
	data.Value = bot

	// Set the Metatable
	state.SetMetatable(data, state.GetTypeMetatable("bot"))

	// Push the bot
	state.Push(data)
}

// PushConfig will push the given config to the Lua stack
func PushConfig(config *Config, state *lua.LState) {
	// Create the config user data
	data := state.NewUserData()
	data.Value = config

	// Set the Metatable
	state.SetMetatable(data, state.GetTypeMetatable("config"))

	// Push the config
	state.Push(data)
}

func checkBot(state *lua.LState) *Bot {
	data := state.CheckUserData(1)
	if v, ok := data.Value.(*Bot); ok {
		return v
	}
	state.ArgError(1, "bot expected")
	return nil
}

func checkConfig(state *lua.LState) *Config {
	data := state.CheckUserData(1)
	if v, ok := data.Value.(*Config); ok {
		return v
	}
	state.ArgError(1, "config expected")
	return nil
}

func botSend(state *lua.LState) int {
	bot := checkBot(state)
	rcv := state.CheckString(2)
	msg := state.CheckString(3)

	bot.Send(rcv, msg)
	return 0
}

func botJoin(state *lua.LState) int {
	bot := checkBot(state)
	channel := state.CheckString(2)

	bot.Join(channel)
	return 0
}

func botIndex(state *lua.LState) int {
	bot := checkBot(state)
	key := state.CheckString(2)

	switch key {
	case "send":
		state.Push(state.NewFunction(botSend))
	case "join":
		state.Push(state.NewFunction(botJoin))
	case "config":
		PushConfig(&bot.config, state)
	default:
		state.Push(lua.LNil)
	}

	return 1
}

func configIndex(state *lua.LState) int {
	config := checkConfig(state)
	key := state.CheckString(2)

	switch key {
	case "Hostname":
		state.Push(lua.LString(config.Hostname))
	case "Port":
		state.Push(lua.LNumber(config.Port))
	case "Secure":
		state.Push(lua.LBool(config.Secure))
	case "InsecureSkipVerify":
		state.Push(lua.LBool(config.InsecureSkipVerify))
	case "Nick":
		state.Push(lua.LString(config.Nick))
	case "User":
		state.Push(lua.LString(config.User))
	case "Name":
		state.Push(lua.LString(config.Name))
	case "Channels":
		channels := state.NewTable()
		for key, value := range config.Channels {
			channels.RawSetInt(key, lua.LString(value))
		}
		state.Push(channels)
	case "Timeout":
		state.Push(lua.LNumber(config.Timeout))
	case "TimeoutLimit":
		state.Push(lua.LNumber(config.TimeoutLimit))
	case "ReconnectLimit":
		state.Push(lua.LNumber(config.ReconnectLimit))
	default:
		state.Push(lua.LNil)
	}

	return 1
}
<|endoftext|>


<commit_before>package ec2

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/goamz/s3"
	"launchpad.net/goyaml"
)

const stateFile = "provider-state"

type bootstrapState struct {
	ZookeeperInstances []string `yaml:"zookeeper-instances"`
}

func (e *environ) saveState(state *bootstrapState) error {
	data, err := goyaml.Marshal(state)
	if err != nil {
		return err
	}
	return e.PutFile(stateFile, bytes.NewBuffer(data), int64(len(data)))
}

func (e *environ) loadState() (*bootstrapState, error) {
	r, err := e.GetFile(stateFile)
	if err != nil {
		return nil, fmt.Errorf("cannot read %q: %v", stateFile, err)
	}
	defer r.Close()
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("error reading %q: %v", stateFile, err)
	}
	var state bootstrapState
	err = goyaml.Unmarshal(data, &state)
	if err != nil {
		return nil, fmt.Errorf("error unmarshalling %q: %v", stateFile, err)
	}
	return &state, nil
}

func (e *environ) deleteState() error {
	// TODO delete the bucket contents and the bucket itself.
	err := e.RemoveFile(stateFile)
	if err != nil {
		return fmt.Errorf("cannot delete provider state: %v", err)
	}
	return nil
}

// makeBucket makes the environent's control bucket, the
// place where bootstrap information and deployed charms
// are stored. To avoid two round trips on every PUT operation,
// we do this only once for each environ.
func (e *environ) makeBucket() error {
	e.checkBucket.Do(func() {
		// try to make the bucket - PutBucket will succeed if the
		// bucket already exists.
		e.checkBucketError = e.bucket().PutBucket(s3.Private)
	})
	return e.checkBucketError
}

func (e *environ) PutFile(file string, r io.Reader, length int64) error {
	if err := e.makeBucket(); err != nil {
		return fmt.Errorf("cannot make S3 control bucket: %v", err)
	}
	err := e.bucket().PutReader(file, r, length, "binary/octet-stream", s3.Private)
	if err != nil {
		return fmt.Errorf("cannot write file %q to control bucket: %v", file, err)
	}
	return nil
}

func (e *environ) GetFile(file string) (io.ReadCloser, error) {
	return e.bucket().GetReader(file)
}

func (e *environ) RemoveFile(file string) error {
	err := e.bucket().Del(file)
	// If we can't delete the object because the bucket doesn't
	// exist, then we don't care.
	if err, _ := err.(*s3.Error); err != nil && err.StatusCode == 404 {
		return nil
	}
	return err
}

func (e *environ) bucket() *s3.Bucket {
	return e.s3.Bucket(e.config.bucket)
}
<commit_msg>environs/ec2: eventual consistency for GetFile<commit_after>package ec2

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/goamz/s3"
	"launchpad.net/goyaml"
)

const stateFile = "provider-state"

type bootstrapState struct {
	ZookeeperInstances []string `yaml:"zookeeper-instances"`
}

func (e *environ) saveState(state *bootstrapState) error {
	data, err := goyaml.Marshal(state)
	if err != nil {
		return err
	}
	return e.PutFile(stateFile, bytes.NewBuffer(data), int64(len(data)))
}

func (e *environ) loadState() (*bootstrapState, error) {
	r, err := e.GetFile(stateFile)
	if err != nil {
		return nil, fmt.Errorf("cannot read %q: %v", stateFile, err)
	}
	defer r.Close()
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, fmt.Errorf("error reading %q: %v", stateFile, err)
	}
	var state bootstrapState
	err = goyaml.Unmarshal(data, &state)
	if err != nil {
		return nil, fmt.Errorf("error unmarshalling %q: %v", stateFile, err)
	}
	return &state, nil
}

func (e *environ) deleteState() error {
	// TODO delete the bucket contents and the bucket itself.
	err := e.RemoveFile(stateFile)
	if err != nil {
		return fmt.Errorf("cannot delete provider state: %v", err)
	}
	return nil
}

// makeBucket makes the environent's control bucket, the
// place where bootstrap information and deployed charms
// are stored. To avoid two round trips on every PUT operation,
// we do this only once for each environ.
func (e *environ) makeBucket() error {
	e.checkBucket.Do(func() {
		// try to make the bucket - PutBucket will succeed if the
		// bucket already exists.
		e.checkBucketError = e.bucket().PutBucket(s3.Private)
	})
	return e.checkBucketError
}

func (e *environ) PutFile(file string, r io.Reader, length int64) error {
	if err := e.makeBucket(); err != nil {
		return fmt.Errorf("cannot make S3 control bucket: %v", err)
	}
	err := e.bucket().PutReader(file, r, length, "binary/octet-stream", s3.Private)
	if err != nil {
		return fmt.Errorf("cannot write file %q to control bucket: %v", file, err)
	}
	return nil
}

func (e *environ) GetFile(file string) (r io.ReadCloser, err error) {
	for a := shortAttempt.start(); a.next(); {
		r, err = e.bucket().GetReader(file)
		if err, _ := err.(*s3.Error); err != nil && err.StatusCode == 404 {
			continue
		}
		return
	}
	return
}

func (e *environ) RemoveFile(file string) error {
	err := e.bucket().Del(file)
	// If we can't delete the object because the bucket doesn't
	// exist, then we don't care.
	if err, _ := err.(*s3.Error); err != nil && err.StatusCode == 404 {
		return nil
	}
	return err
}

func (e *environ) bucket() *s3.Bucket {
	return e.s3.Bucket(e.config.bucket)
}
<|endoftext|>


<commit_before>package util

import (
	"encoding/json"
	"fmt"
	"github.com/docker/docker/pkg/parsers"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"time"
)

type ThinImageLayer struct {
	Digest   string `json:"digest"`
	Repo     string `json:"repo,omitempty"`
	Location string `json:"location,omitempty"`
}

type ThinImage struct {
	Version    string           `json:"version"`
	MinVersion string           `json:"min_version,omitempty"`
	Layers     []ThinImageLayer `json:"layers"`
	Comment    string           `json:"comment,omitempty"`
}

func (t *ThinImage) AddLayer(newLayer ThinImageLayer) {
	t.Layers = append([]ThinImageLayer{newLayer}, t.Layers...)
}

func IsThinImageLayer(diffPath string) bool {
	magic_file_path := path.Join(diffPath, ".thin")
	_, err := os.Stat(magic_file_path)

	if err == nil {
		return true
	}

	return false
}

func GetCvmfsLayerPaths(layers []ThinImageLayer, cvmfsMountPath string) []string {
	ret := make([]string, len(layers))

	for i, layer := range layers {
		root := cvmfsMountPath
		// repo := cvmfsDefaultRepo
		repo := layer.Repo
		location := "layers"
		digest := layer.Digest

		ret[i] = path.Join(root, repo, location, digest)
	}

	return ret
}

func AppendCvmfsLayerPaths(oldArray []string, newArray []string) []string {
	l_old := len(oldArray)
	l_new := len(newArray)
	newLen := l_old + l_new - 1
	ret := make([]string, newLen)

	for i := 0; i < l_old-1; i++ {
		ret[i] = oldArray[i]
	}

	for i := range newArray {
		ret[l_old-1+i] = newArray[i]
	}

	return ret
}

func ExpandCvmfsLayerPaths(oldArray []string, newArray []string, i int) (result []string) {
	left := oldArray[:i]
	right := oldArray[i+1:]

	result = append(left, newArray...)
	result = append(result, right...)

	return result
}

func GetNestedLayerIDs(diffPath string) []ThinImageLayer {
	magic_file_path := path.Join(diffPath, ".thin")
	content, _ := ioutil.ReadFile(magic_file_path)

	var thin ThinImage
	json.Unmarshal(content, &thin)

	return thin.Layers
}

func ParseOptions(options []string) (map[string]string, error) {
	m := make(map[string]string)

	for _, v := range options {
		key, value, err := parsers.ParseKeyValueOpt(v)

		if err != nil {
			return nil, err
		}

		m[key] = value
	}

	return m, nil
}

func ReadThinFile(thinFilePath string) ThinImage {
	content, _ := ioutil.ReadFile(thinFilePath)
	var thin ThinImage
	json.Unmarshal(content, &thin)

	return thin
}

func WriteThinFile(thin ThinImage) (string, error) {
	rand.Seed(time.Now().UTC().UnixNano())
	tmp := path.Join(os.TempDir(), fmt.Sprintf("dlcg-%d", rand.Int()))
	os.MkdirAll(tmp, os.ModePerm)

	p := path.Join(tmp, ".thin")

	j, err := json.Marshal(thin)
	if err != nil {
		fmt.Println("Failed to marshal new thin file to json!")
		return "", err
	}

	if err := ioutil.WriteFile(p, j, os.ModePerm); err != nil {
		fmt.Printf("Failed to write new thin file!\nFile: %s\n", p)
		return "", err
	}

	return tmp, nil
}

type ICvmfsManager interface {
	Get(repo string) error
	GetLayers(layers ...ThinImageLayer) error
	Put(repo string) error
	PutLayers(layers ...ThinImageLayer) error
	PutAll() error
	Remount(repo string) error
	UploadNewLayer(string) (ThinImageLayer, error)
}

type cvmfsManager struct {
	mountPath string
	ctr       map[string]int
}

func NewCvmfsManager(cvmfsMountPath, cvmfsMountMethod string) ICvmfsManager {
	if cvmfsMountMethod == "external" {
		return nil
	}

	return &cvmfsManager{
		mountPath: cvmfsMountPath,
		ctr:       make(map[string]int),
	}
}

func (cm *cvmfsManager) mount(repo string) error {
	mountTarget := path.Join(cm.mountPath, repo)
	os.MkdirAll(mountTarget, os.ModePerm)

	cmd := "cvmfs2 -o rw,fsname=cvmfs2,allow_other,grab_mountpoint"
	cmd += " " + repo
	cmd += " " + mountTarget

	// TODO: check for errors!
	out, err := exec.Command("bash", "-x", "-c", cmd).CombinedOutput()

	if err != nil {
		fmt.Println("There was an error during mount!")
		fmt.Printf("%s\n", out)
		return err
	} else {
		fmt.Println("default repo mounted successfully!")
	}

	return nil
}

func (cm *cvmfsManager) umount(repo string) error {
	// TODO: check for errors!
	mountTarget := path.Join(cm.mountPath, repo)
	cmd := exec.Command("umount", mountTarget)

	if out, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("Cvmfs mount failed.\nOutput:%s\nError%s\n",
			out, err)
	}

	return nil
}

func (cm *cvmfsManager) isConfigured(repo string) error {
	fmt.Println("isConfigured()")

	confPath := "/etc/cvmfs/config.d"
	keysPath := "/etc/cvmfs/keys"

	repoConf := path.Join(confPath, repo) + ".conf"
	repoKeys := path.Join(keysPath, repo) + ".pub"

	errmsg1 := "Configuration for CVMFS repository %s is missing."
	errmsg2 := "Key for CVMFS repository %s is missing."

	if _, err := os.Stat(repoConf); os.IsNotExist(err) {
		return fmt.Errorf(errmsg1, repo)
	}

	if _, err := os.Stat(repoKeys); os.IsNotExist(err) {
		return fmt.Errorf(errmsg2, repo)
	}

	fmt.Println("Repo configuration ok!")

	return nil
}

func (cm *cvmfsManager) Get(repo string) error {
	// TODO: maybe delegate this check to the mount call itself?
	if err := cm.isConfigured(repo); err != nil {
		return err
	}

	if _, ok := cm.ctr[repo]; !ok {
		fmt.Printf("Repo %s is not in the map!\n", repo)
		cm.ctr[repo] = 1
		cm.mount(repo)
		return nil
	} else {
		fmt.Printf("Repos %s is already in the map...\n", repo)
	}

	cm.ctr[repo] += 1

	return nil
}

func (cm *cvmfsManager) Put(repo string) error {
	if _, ok := cm.ctr[repo]; !ok {
		return nil
	}

	if cm.ctr[repo] > 1 {
		cm.ctr[repo] -= 1
		return nil
	}

	cm.ctr[repo] = 0
	cm.umount(repo)
	return nil
}

func (cm *cvmfsManager) PutAll() error {
	for k := range cm.ctr {
		cm.umount(k)
		cm.ctr[k] = 0
	}
	return nil
}

func (cm *cvmfsManager) GetLayers(layers ...ThinImageLayer) error {
	for i, l := range layers {
		if err := cm.Get(l.Repo); err != nil {
			cm.PutLayers(layers[:i]...)
			return err
		}
	}
	return nil
}

func (cm *cvmfsManager) PutLayers(layers ...ThinImageLayer) error {
	for _, l := range layers {
		cm.Put(l.Repo)
	}
	return nil
}

func (cm *cvmfsManager) Remount(repo string) error {
	if _, ok := cm.ctr[repo]; !ok {
		return nil
	}

	if cm.ctr[repo] == 0 {
		return nil
	}

	cmd := "cvmfs_talk"
	args := []string{"-i", repo, "remount", "sync"}

	out, err := exec.Command(cmd, args...).CombinedOutput()
	if err != nil {
		fmt.Println(string(out))
		fmt.Println(err)
		return err
	} else {
		return nil
	}
}
<commit_msg>Removed unused code<commit_after>package util

import (
	"encoding/json"
	"fmt"
	"github.com/docker/docker/pkg/parsers"
	"io/ioutil"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"time"
)

type ThinImageLayer struct {
	Digest   string `json:"digest"`
	Repo     string `json:"repo,omitempty"`
	Location string `json:"location,omitempty"`
}

type ThinImage struct {
	Version    string           `json:"version"`
	MinVersion string           `json:"min_version,omitempty"`
	Layers     []ThinImageLayer `json:"layers"`
	Comment    string           `json:"comment,omitempty"`
}

func (t *ThinImage) AddLayer(newLayer ThinImageLayer) {
	t.Layers = append([]ThinImageLayer{newLayer}, t.Layers...)
}

func IsThinImageLayer(diffPath string) bool {
	magic_file_path := path.Join(diffPath, ".thin")
	_, err := os.Stat(magic_file_path)

	if err == nil {
		return true
	}

	return false
}

func GetCvmfsLayerPaths(layers []ThinImageLayer, cvmfsMountPath string) []string {
	ret := make([]string, len(layers))

	for i, layer := range layers {
		root := cvmfsMountPath
		// repo := cvmfsDefaultRepo
		repo := layer.Repo
		location := "layers"
		digest := layer.Digest

		ret[i] = path.Join(root, repo, location, digest)
	}

	return ret
}

func ExpandCvmfsLayerPaths(oldArray []string, newArray []string, i int) (result []string) {
	left := oldArray[:i]
	right := oldArray[i+1:]

	result = append(left, newArray...)
	result = append(result, right...)

	return result
}

func GetNestedLayerIDs(diffPath string) []ThinImageLayer {
	magic_file_path := path.Join(diffPath, ".thin")
	content, _ := ioutil.ReadFile(magic_file_path)

	var thin ThinImage
	json.Unmarshal(content, &thin)

	return thin.Layers
}

func ParseOptions(options []string) (map[string]string, error) {
	m := make(map[string]string)

	for _, v := range options {
		key, value, err := parsers.ParseKeyValueOpt(v)

		if err != nil {
			return nil, err
		}

		m[key] = value
	}

	return m, nil
}

func ReadThinFile(thinFilePath string) ThinImage {
	content, _ := ioutil.ReadFile(thinFilePath)
	var thin ThinImage
	json.Unmarshal(content, &thin)

	return thin
}

func WriteThinFile(thin ThinImage) (string, error) {
	rand.Seed(time.Now().UTC().UnixNano())
	tmp := path.Join(os.TempDir(), fmt.Sprintf("dlcg-%d", rand.Int()))
	os.MkdirAll(tmp, os.ModePerm)

	p := path.Join(tmp, ".thin")

	j, err := json.Marshal(thin)
	if err != nil {
		fmt.Println("Failed to marshal new thin file to json!")
		return "", err
	}

	if err := ioutil.WriteFile(p, j, os.ModePerm); err != nil {
		fmt.Printf("Failed to write new thin file!\nFile: %s\n", p)
		return "", err
	}

	return tmp, nil
}

type ICvmfsManager interface {
	Get(repo string) error
	GetLayers(layers ...ThinImageLayer) error
	Put(repo string) error
	PutLayers(layers ...ThinImageLayer) error
	PutAll() error
	Remount(repo string) error
	UploadNewLayer(string) (ThinImageLayer, error)
}

type cvmfsManager struct {
	mountPath string
	ctr       map[string]int
}

func NewCvmfsManager(cvmfsMountPath, cvmfsMountMethod string) ICvmfsManager {
	if cvmfsMountMethod == "external" {
		return nil
	}

	return &cvmfsManager{
		mountPath: cvmfsMountPath,
		ctr:       make(map[string]int),
	}
}

func (cm *cvmfsManager) mount(repo string) error {
	mountTarget := path.Join(cm.mountPath, repo)
	os.MkdirAll(mountTarget, os.ModePerm)

	cmd := "cvmfs2 -o rw,fsname=cvmfs2,allow_other,grab_mountpoint"
	cmd += " " + repo
	cmd += " " + mountTarget

	// TODO: check for errors!
	out, err := exec.Command("bash", "-x", "-c", cmd).CombinedOutput()

	if err != nil {
		fmt.Println("There was an error during mount!")
		fmt.Printf("%s\n", out)
		return err
	} else {
		fmt.Println("default repo mounted successfully!")
	}

	return nil
}

func (cm *cvmfsManager) umount(repo string) error {
	// TODO: check for errors!
	mountTarget := path.Join(cm.mountPath, repo)
	cmd := exec.Command("umount", mountTarget)

	if out, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("Cvmfs mount failed.\nOutput:%s\nError%s\n",
			out, err)
	}

	return nil
}

func (cm *cvmfsManager) isConfigured(repo string) error {
	fmt.Println("isConfigured()")

	confPath := "/etc/cvmfs/config.d"
	keysPath := "/etc/cvmfs/keys"

	repoConf := path.Join(confPath, repo) + ".conf"
	repoKeys := path.Join(keysPath, repo) + ".pub"

	errmsg1 := "Configuration for CVMFS repository %s is missing."
	errmsg2 := "Key for CVMFS repository %s is missing."

	if _, err := os.Stat(repoConf); os.IsNotExist(err) {
		return fmt.Errorf(errmsg1, repo)
	}

	if _, err := os.Stat(repoKeys); os.IsNotExist(err) {
		return fmt.Errorf(errmsg2, repo)
	}

	fmt.Println("Repo configuration ok!")

	return nil
}

func (cm *cvmfsManager) Get(repo string) error {
	// TODO: maybe delegate this check to the mount call itself?
	if err := cm.isConfigured(repo); err != nil {
		return err
	}

	if _, ok := cm.ctr[repo]; !ok {
		fmt.Printf("Repo %s is not in the map!\n", repo)
		cm.ctr[repo] = 1
		cm.mount(repo)
		return nil
	} else {
		fmt.Printf("Repos %s is already in the map...\n", repo)
	}

	cm.ctr[repo] += 1

	return nil
}

func (cm *cvmfsManager) Put(repo string) error {
	if _, ok := cm.ctr[repo]; !ok {
		return nil
	}

	if cm.ctr[repo] > 1 {
		cm.ctr[repo] -= 1
		return nil
	}

	cm.ctr[repo] = 0
	cm.umount(repo)
	return nil
}

func (cm *cvmfsManager) PutAll() error {
	for k := range cm.ctr {
		cm.umount(k)
		cm.ctr[k] = 0
	}
	return nil
}

func (cm *cvmfsManager) GetLayers(layers ...ThinImageLayer) error {
	for i, l := range layers {
		if err := cm.Get(l.Repo); err != nil {
			cm.PutLayers(layers[:i]...)
			return err
		}
	}
	return nil
}

func (cm *cvmfsManager) PutLayers(layers ...ThinImageLayer) error {
	for _, l := range layers {
		cm.Put(l.Repo)
	}
	return nil
}

func (cm *cvmfsManager) Remount(repo string) error {
	if _, ok := cm.ctr[repo]; !ok {
		return nil
	}

	if cm.ctr[repo] == 0 {
		return nil
	}

	cmd := "cvmfs_talk"
	args := []string{"-i", repo, "remount", "sync"}

	out, err := exec.Command(cmd, args...).CombinedOutput()
	if err != nil {
		fmt.Println(string(out))
		fmt.Println(err)
		return err
	} else {
		return nil
	}
}
<|endoftext|>


<commit_before>//   Copyright 2018 MSolution.IO
//
//   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 es

import (
	"database/sql"
	"fmt"
	"net/http"
	"strings"

	"github.com/trackit/trackit-server/aws"
	"github.com/trackit/trackit-server/models"
	"github.com/trackit/trackit-server/users"
)

// AccountsAndIndexes stores the accounts and indexes
type AccountsAndIndexes struct {
	Accounts []string
	Indexes  []string
}

// isAccountDuplicate returns true if the account already exists in the list of accounts
// returns false otherwise
func (ai *AccountsAndIndexes) isAccountDuplicate(account string) bool {
	for _, entry := range ai.Accounts {
		if entry == account {
			return true
		}
	}
	return false
}

// addAccount adds a new account in the AccountsAndIndexes if it is not already
// in the list of accounts
func (ai *AccountsAndIndexes) addAccount(account string) {
	if ai.isAccountDuplicate(account) == false {
		ai.Accounts = append(ai.Accounts, account)
	}
}

// addIndex adds a new index in the AccountsAndIndexes if it is not already
// in the list of indexes
func (ai *AccountsAndIndexes) addIndex(index string) {
	for _, entry := range ai.Indexes {
		if entry == index {
			return
		}
	}
	ai.Indexes = append(ai.Indexes, index)
}

// GetAccountIdFromRoleArn takes a role arn string and returns the account ID
// or an empty string if the role arn is not valid
func GetAccountIdFromRoleArn(roleArn string) string {
	arnElems := strings.Split(roleArn, ":")
	if len(arnElems) != 6 {
		return ""
	}
	return arnElems[4]
}

// getAllAccountsAndIndexes returns an AccountsAndIndexes struct, a status code and an error
// The AccountsAndIndexes struct will contain all the accounts available to the user
// with their indexes without duplicates
// If an account is shared with a user that already have the same account, the shared
// account and index will be skipped
func getAllAccountsAndIndexes(user users.User, tx *sql.Tx, indexPrefix string) (AccountsAndIndexes, int, error) {
	accountsAndIndexes := AccountsAndIndexes{}
	// Retrieve the user accounts and shared accounts
	userAccounts, err := models.AwsAccountsByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of accounts for current user: %s", err.Error())
	}
	sharedAccounts, err := models.SharedAccountsWithRoleByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of shared accounts for current user: %s", err.Error())
	}
	// Add all the user accounts
	for _, userAccount := range userAccounts {
		accountId := GetAccountIdFromRoleArn(userAccount.RoleArn)
		if accountId != "" {
			accountsAndIndexes.addAccount(accountId)
			accountsAndIndexes.addIndex(IndexNameForUserId(userAccount.UserID, indexPrefix))
		}
	}
	// Add all the non duplicate shared accounts
	for _, sharedAccount := range sharedAccounts {
		accountId := GetAccountIdFromRoleArn(sharedAccount.RoleArn)
		// Do not add the account if the user already own the same account
		if accountId != "" && accountsAndIndexes.isAccountDuplicate(accountId) == false {
			accountsAndIndexes.addAccount(accountId)
			accountsAndIndexes.addIndex(IndexNameForUserId(sharedAccount.OwnerID, indexPrefix))
		}
	}
	return accountsAndIndexes, http.StatusOK, nil
}

// GetAccountsAndIndexesreturns an AccountsAndIndexes struct, a status code and an error
// if the accountList parameter is empty the function will call getAllAccountsAndIndexes
// if the accountList parameter is not empty the function will validate the accounts and
// find their indexes
func GetAccountsAndIndexes(accountList []string, user users.User, tx *sql.Tx, indexPrefix string) (AccountsAndIndexes, int, error) {
	if len(accountList) == 0 {
		return getAllAccountsAndIndexes(user, tx, indexPrefix)
	}
	accountsAndIndexes := AccountsAndIndexes{}
	if err := aws.ValidateAwsAccounts(accountList); err != nil {
		return accountsAndIndexes, http.StatusBadRequest, err
	}
	// Retrieve the user's accounts and shared accounts
	userAccounts, err := models.AwsAccountsByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of accounts for current user: %s", err.Error())
	}
	sharedAccounts, err := models.SharedAccountsWithRoleByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of shared accounts for current user: %s", err.Error())
	}
	// Match the accountList parameter with the user's accounts and shared accounts
	for _, account := range accountList {
		found_match := false
		// Try to match in priority with the user's accounts
		for _, userAccount := range userAccounts {
			accountId := GetAccountIdFromRoleArn(userAccount.RoleArn)
			if accountId != "" && accountId == account {
				found_match = true
				accountsAndIndexes.addAccount(account)
				accountsAndIndexes.addIndex(IndexNameForUserId(userAccount.UserID, indexPrefix))
			}
		}
		// If no match is found in the user's accounts, try in the shared accounts
		if found_match == false {
			for _, sharedAccount := range sharedAccounts {
				accountId := GetAccountIdFromRoleArn(sharedAccount.RoleArn)
				if accountId != "" && accountId == account {
					found_match = true
					if accountsAndIndexes.isAccountDuplicate(accountId) == false {
						accountsAndIndexes.addAccount(account)
						accountsAndIndexes.addIndex(IndexNameForUserId(sharedAccount.OwnerID, indexPrefix))
					}
				}
			}
		}
		if found_match == false {
			return accountsAndIndexes, http.StatusBadRequest, fmt.Errorf("Unable to access account %s", account)
		}
	}
	return accountsAndIndexes, http.StatusOK, nil
}
<commit_msg>return an error if no index is found during account index retrieval<commit_after>//   Copyright 2018 MSolution.IO
//
//   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 es

import (
	"database/sql"
	"fmt"
	"net/http"
	"strings"

	"github.com/trackit/trackit-server/aws"
	"github.com/trackit/trackit-server/models"
	"github.com/trackit/trackit-server/users"
)

// AccountsAndIndexes stores the accounts and indexes
type AccountsAndIndexes struct {
	Accounts []string
	Indexes  []string
}

// isAccountDuplicate returns true if the account already exists in the list of accounts
// returns false otherwise
func (ai *AccountsAndIndexes) isAccountDuplicate(account string) bool {
	for _, entry := range ai.Accounts {
		if entry == account {
			return true
		}
	}
	return false
}

// addAccount adds a new account in the AccountsAndIndexes if it is not already
// in the list of accounts
func (ai *AccountsAndIndexes) addAccount(account string) {
	if ai.isAccountDuplicate(account) == false {
		ai.Accounts = append(ai.Accounts, account)
	}
}

// addIndex adds a new index in the AccountsAndIndexes if it is not already
// in the list of indexes
func (ai *AccountsAndIndexes) addIndex(index string) {
	for _, entry := range ai.Indexes {
		if entry == index {
			return
		}
	}
	ai.Indexes = append(ai.Indexes, index)
}

// GetAccountIdFromRoleArn takes a role arn string and returns the account ID
// or an empty string if the role arn is not valid
func GetAccountIdFromRoleArn(roleArn string) string {
	arnElems := strings.Split(roleArn, ":")
	if len(arnElems) != 6 {
		return ""
	}
	return arnElems[4]
}

// getAllAccountsAndIndexes returns an AccountsAndIndexes struct, a status code and an error
// The AccountsAndIndexes struct will contain all the accounts available to the user
// with their indexes without duplicates
// If an account is shared with a user that already have the same account, the shared
// account and index will be skipped
func getAllAccountsAndIndexes(user users.User, tx *sql.Tx, indexPrefix string) (AccountsAndIndexes, int, error) {
	accountsAndIndexes := AccountsAndIndexes{}
	// Retrieve the user accounts and shared accounts
	userAccounts, err := models.AwsAccountsByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of accounts for current user: %s", err.Error())
	}
	sharedAccounts, err := models.SharedAccountsWithRoleByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of shared accounts for current user: %s", err.Error())
	}
	// Add all the user accounts
	for _, userAccount := range userAccounts {
		accountId := GetAccountIdFromRoleArn(userAccount.RoleArn)
		if accountId != "" {
			accountsAndIndexes.addAccount(accountId)
			accountsAndIndexes.addIndex(IndexNameForUserId(userAccount.UserID, indexPrefix))
		}
	}
	// Add all the non duplicate shared accounts
	for _, sharedAccount := range sharedAccounts {
		accountId := GetAccountIdFromRoleArn(sharedAccount.RoleArn)
		// Do not add the account if the user already own the same account
		if accountId != "" && accountsAndIndexes.isAccountDuplicate(accountId) == false {
			accountsAndIndexes.addAccount(accountId)
			accountsAndIndexes.addIndex(IndexNameForUserId(sharedAccount.OwnerID, indexPrefix))
		}
	}
	// If no indexes where found, return an error to prevent giving access to all indexes
	if len(accountsAndIndexes.Indexes) == 0 {
		return accountsAndIndexes, http.StatusBadRequest, fmt.Errorf("No aws account found")
	}
	return accountsAndIndexes, http.StatusOK, nil
}

// GetAccountsAndIndexesreturns an AccountsAndIndexes struct, a status code and an error
// if the accountList parameter is empty the function will call getAllAccountsAndIndexes
// if the accountList parameter is not empty the function will validate the accounts and
// find their indexes
func GetAccountsAndIndexes(accountList []string, user users.User, tx *sql.Tx, indexPrefix string) (AccountsAndIndexes, int, error) {
	if len(accountList) == 0 {
		return getAllAccountsAndIndexes(user, tx, indexPrefix)
	}
	accountsAndIndexes := AccountsAndIndexes{}
	if err := aws.ValidateAwsAccounts(accountList); err != nil {
		return accountsAndIndexes, http.StatusBadRequest, err
	}
	// Retrieve the user's accounts and shared accounts
	userAccounts, err := models.AwsAccountsByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of accounts for current user: %s", err.Error())
	}
	sharedAccounts, err := models.SharedAccountsWithRoleByUserID(tx, user.Id)
	if err != nil {
		return accountsAndIndexes, http.StatusInternalServerError, fmt.Errorf("Unable to retrieve the list of shared accounts for current user: %s", err.Error())
	}
	// Match the accountList parameter with the user's accounts and shared accounts
	for _, account := range accountList {
		found_match := false
		// Try to match in priority with the user's accounts
		for _, userAccount := range userAccounts {
			accountId := GetAccountIdFromRoleArn(userAccount.RoleArn)
			if accountId != "" && accountId == account {
				found_match = true
				accountsAndIndexes.addAccount(account)
				accountsAndIndexes.addIndex(IndexNameForUserId(userAccount.UserID, indexPrefix))
			}
		}
		// If no match is found in the user's accounts, try in the shared accounts
		if found_match == false {
			for _, sharedAccount := range sharedAccounts {
				accountId := GetAccountIdFromRoleArn(sharedAccount.RoleArn)
				if accountId != "" && accountId == account {
					found_match = true
					if accountsAndIndexes.isAccountDuplicate(accountId) == false {
						accountsAndIndexes.addAccount(account)
						accountsAndIndexes.addIndex(IndexNameForUserId(sharedAccount.OwnerID, indexPrefix))
					}
				}
			}
		}
		if found_match == false {
			return accountsAndIndexes, http.StatusBadRequest, fmt.Errorf("Unable to access account %s", account)
		}
	}
	return accountsAndIndexes, http.StatusOK, nil
}
<|endoftext|>


<commit_before>// Package api provides rest-like server
package api

import (
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/didip/tollbooth/v6"
	"github.com/didip/tollbooth_chi"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/render"
	"github.com/go-pkgz/lcw"
	log "github.com/go-pkgz/lgr"
	"github.com/go-pkgz/rest"
	"github.com/go-pkgz/rest/logger"

	"github.com/umputun/feed-master/app/config"
	"github.com/umputun/feed-master/app/feed"
	"github.com/umputun/feed-master/app/proc"
	"github.com/umputun/feed-master/app/youtube"
)

// Server provides HTTP API
type Server struct {
	Version    string
	Conf       config.Conf
	Store      *proc.BoltDB
	YoutubeSvc YoutubeSvc
	httpServer *http.Server
	cache      lcw.LoadingCache
}

// YoutubeSvc provides access to youtube's audio rss
type YoutubeSvc interface {
	RSSFeed(cinfo youtube.FeedInfo) (string, error)
}

// Run starts http server for API with all routes
func (s *Server) Run(ctx context.Context, port int) {
	var err error
	if s.cache, err = lcw.NewExpirableCache(lcw.TTL(time.Minute*5), lcw.MaxCacheSize(10*1024*1024)); err != nil {
		log.Printf("[PANIC] failed to make loading cache, %v", err)
		return
	}

	go func() {
		<-ctx.Done()
		if s.httpServer != nil {
			if clsErr := s.httpServer.Close(); clsErr != nil {
				log.Printf("[ERROR] failed to close proxy http server, %v", clsErr)
			}
		}
	}()

	s.httpServer = &http.Server{
		Addr:              fmt.Sprintf(":%d", port),
		Handler:           s.router(),
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      30 * time.Second,
		IdleTimeout:       30 * time.Second,
	}
	err = s.httpServer.ListenAndServe()
	log.Printf("[WARN] http server terminated, %s", err)
}

func (s *Server) router() *chi.Mux {
	router := chi.NewRouter()
	router.Use(middleware.RealIP, rest.Recoverer(log.Default()))
	router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
	router.Use(rest.AppInfo("feed-master", "umputun", s.Version), rest.Ping)
	router.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(5, nil)))

	router.Group(func(rimg chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[DEBUG]"))
		rimg.Use(l.Handler)
		rimg.Get("/images/{name}", s.getImageCtrl)
		rimg.Get("/image/{name}", s.getImageCtrl)
		rimg.Head("/image/{name}", s.getImageHeadCtrl)
		rimg.Head("/images/{name}", s.getImageHeadCtrl)
	})

	router.Group(func(rrss chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]"))
		rrss.Use(l.Handler)
		rrss.Get("/rss/{name}", s.getFeedCtrl)
		rrss.Get("/list", s.getListCtrl)
		rrss.Get("/feed/{name}", s.getFeedPageCtrl)
		rrss.Get("/feed/{name}/sources", s.getSourcesPageCtrl)
		rrss.Get("/feeds", s.getFeedsPageCtrl)
	})

	router.Route("/yt", func(r chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]"))
		r.Use(l.Handler)
		r.Get("/rss/{channel}", s.getYoutubeFeedCtrl)
	})

	if s.Conf.YouTube.BaseURL != "" {
		baseYtURL, parseErr := url.Parse(s.Conf.YouTube.BaseURL)
		if parseErr != nil {
			log.Printf("[ERROR] failed to parse base url %s, %v", s.Conf.YouTube.BaseURL, parseErr)
		}
		ytfs, fsErr := rest.NewFileServer(baseYtURL.Path, s.Conf.YouTube.FilesLocation)
		if fsErr == nil {
			router.Mount(baseYtURL.Path, ytfs)
		} else {
			log.Printf("[WARN] can't start static file server for yt, %v", fsErr)
		}
	}

	fs, err := rest.NewFileServer("/static", filepath.Join("webapp", "static"))
	if err == nil {
		router.Mount("/static", fs)
	} else {
		log.Printf("[WARN] can't start static file server, %v", err)
	}
	return router
}

// GET /rss/{name} - returns rss for given feeds set
func (s *Server) getFeedCtrl(w http.ResponseWriter, r *http.Request) {
	feedName := chi.URLParam(r, "name")
	items, err := s.Store.Load(feedName, s.Conf.System.MaxTotal, true)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest, err, "failed to get feed")
		return
	}

	for i, itm := range items {
		// add ts suffix to titles
		switch s.Conf.Feeds[feedName].ExtendDateTitle {
		case "yyyyddmm":
			items[i].Title = fmt.Sprintf("%s (%s)", itm.Title, itm.DT.Format("2006-02-01"))
		case "yyyymmdd":
			items[i].Title = fmt.Sprintf("%s (%s)", itm.Title, itm.DT.Format("2006-01-02"))
		}
	}

	rss := feed.Rss2{
		Version:       "2.0",
		ItemList:      items,
		Title:         s.Conf.Feeds[feedName].Title,
		Description:   s.Conf.Feeds[feedName].Description,
		Language:      s.Conf.Feeds[feedName].Language,
		Link:          s.Conf.Feeds[feedName].Link,
		PubDate:       items[0].PubDate,
		LastBuildDate: time.Now().Format(time.RFC822Z),
		NsItunes:      "http://www.itunes.com/dtds/podcast-1.0.dtd",
		NsMedia:       "http://search.yahoo.com/mrss/",
	}

	// replace link to UI page
	if s.Conf.System.BaseURL != "" {
		baseURL := s.Conf.System.BaseURL
		rss.Link = baseURL + "/feed/" + feedName
		if s.Conf.Feeds[feedName].Image != "" {
			rss.ItunesImage = feed.ItunesImg{
				URL: baseURL + "/" + s.Conf.Feeds[feedName].Image,
			}
			rss.MediaThumbnail = feed.MediaThumbnail{
				URL: baseURL + "/" + s.Conf.Feeds[feedName].Image,
			}
		}
	}

	b, err := xml.MarshalIndent(&rss, "", "  ")
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusInternalServerError, err, "failed to marshal rss")
		return
	}

	w.Header().Set("Content-Type", "application/xml; charset=UTF-8")
	res := `<?xml version="1.0" encoding="UTF-8"?>` + "\n" + string(b)
	// this hack to avoid having different items for marshal and unmarshal due to "itunes" namespace
	res = strings.Replace(res, "<duration>", "<itunes:duration>", -1)
	res = strings.Replace(res, "</duration>", "</itunes:duration>", -1)

	_, _ = fmt.Fprintf(w, "%s", res)
}

// GET /image/{name}
func (s *Server) getImageCtrl(w http.ResponseWriter, r *http.Request) {
	fm := chi.URLParam(r, "name")
	fm = strings.TrimSuffix(fm, ".png")
	feedConf, found := s.Conf.Feeds[fm]
	if !found {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest,
			fmt.Errorf("image %s not found", fm), "failed to load image")
		return
	}

	b, err := os.ReadFile(feedConf.Image)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest,
			errors.New("can't read  "+chi.URLParam(r, "name")), "failed to read image")
		return
	}
	w.Header().Set("Content-Type", "image/png")
	if _, err := w.Write(b); err != nil {
		log.Printf("[WARN] failed to send image, %s", err)
	}
}

// HEAD /image/{name}
func (s *Server) getImageHeadCtrl(w http.ResponseWriter, r *http.Request) {
	fm := chi.URLParam(r, "name")
	fm = strings.TrimSuffix(fm, ".png")
	feedConf, found := s.Conf.Feeds[fm]
	if !found {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	info, err := os.Stat(feedConf.Image)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Content-Length", strconv.Itoa(int(info.Size())))
	w.WriteHeader(http.StatusOK)
}

// GET /list - returns feed's image
func (s *Server) getListCtrl(w http.ResponseWriter, r *http.Request) {
	feeds := s.feeds()
	render.JSON(w, r, feeds)
}

// GET /yt/rss/{channel} - returns rss for given youtube channel
func (s *Server) getYoutubeFeedCtrl(w http.ResponseWriter, r *http.Request) {
	channel := chi.URLParam(r, "channel")

	fi := youtube.FeedInfo{ID: channel}
	for _, f := range s.Conf.YouTube.Channels {
		if f.ID == channel {
			fi = f
			break
		}
	}

	res, err := s.YoutubeSvc.RSSFeed(fi)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusInternalServerError, err, "failed to read yt list")
		return
	}

	w.Header().Set("Content-Type", "application/xml; charset=UTF-8")
	res = `<?xml version="1.0" encoding="UTF-8"?>` + "\n" + res
	_, _ = fmt.Fprintf(w, "%s", res)
}

func (s *Server) feeds() []string {
	feeds := make([]string, 0, len(s.Conf.Feeds))
	for k := range s.Conf.Feeds {
		feeds = append(feeds, k)
	}
	return feeds
}
<commit_msg>handle base without / suffix or image part without / prefix<commit_after>// Package api provides rest-like server
package api

import (
	"context"
	"encoding/xml"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/didip/tollbooth/v6"
	"github.com/didip/tollbooth_chi"
	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
	"github.com/go-chi/render"
	"github.com/go-pkgz/lcw"
	log "github.com/go-pkgz/lgr"
	"github.com/go-pkgz/rest"
	"github.com/go-pkgz/rest/logger"

	"github.com/umputun/feed-master/app/config"
	"github.com/umputun/feed-master/app/feed"
	"github.com/umputun/feed-master/app/proc"
	"github.com/umputun/feed-master/app/youtube"
)

// Server provides HTTP API
type Server struct {
	Version    string
	Conf       config.Conf
	Store      *proc.BoltDB
	YoutubeSvc YoutubeSvc
	httpServer *http.Server
	cache      lcw.LoadingCache
}

// YoutubeSvc provides access to youtube's audio rss
type YoutubeSvc interface {
	RSSFeed(cinfo youtube.FeedInfo) (string, error)
}

// Run starts http server for API with all routes
func (s *Server) Run(ctx context.Context, port int) {
	var err error
	if s.cache, err = lcw.NewExpirableCache(lcw.TTL(time.Minute*5), lcw.MaxCacheSize(10*1024*1024)); err != nil {
		log.Printf("[PANIC] failed to make loading cache, %v", err)
		return
	}

	go func() {
		<-ctx.Done()
		if s.httpServer != nil {
			if clsErr := s.httpServer.Close(); clsErr != nil {
				log.Printf("[ERROR] failed to close proxy http server, %v", clsErr)
			}
		}
	}()

	s.httpServer = &http.Server{
		Addr:              fmt.Sprintf(":%d", port),
		Handler:           s.router(),
		ReadHeaderTimeout: 5 * time.Second,
		WriteTimeout:      30 * time.Second,
		IdleTimeout:       30 * time.Second,
	}
	err = s.httpServer.ListenAndServe()
	log.Printf("[WARN] http server terminated, %s", err)
}

func (s *Server) router() *chi.Mux {
	router := chi.NewRouter()
	router.Use(middleware.RealIP, rest.Recoverer(log.Default()))
	router.Use(middleware.Throttle(1000), middleware.Timeout(60*time.Second))
	router.Use(rest.AppInfo("feed-master", "umputun", s.Version), rest.Ping)
	router.Use(tollbooth_chi.LimitHandler(tollbooth.NewLimiter(5, nil)))

	router.Group(func(rimg chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[DEBUG]"))
		rimg.Use(l.Handler)
		rimg.Get("/images/{name}", s.getImageCtrl)
		rimg.Get("/image/{name}", s.getImageCtrl)
		rimg.Head("/image/{name}", s.getImageHeadCtrl)
		rimg.Head("/images/{name}", s.getImageHeadCtrl)
	})

	router.Group(func(rrss chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]"))
		rrss.Use(l.Handler)
		rrss.Get("/rss/{name}", s.getFeedCtrl)
		rrss.Get("/list", s.getListCtrl)
		rrss.Get("/feed/{name}", s.getFeedPageCtrl)
		rrss.Get("/feed/{name}/sources", s.getSourcesPageCtrl)
		rrss.Get("/feeds", s.getFeedsPageCtrl)
	})

	router.Route("/yt", func(r chi.Router) {
		l := logger.New(logger.Log(log.Default()), logger.Prefix("[INFO]"))
		r.Use(l.Handler)
		r.Get("/rss/{channel}", s.getYoutubeFeedCtrl)
	})

	if s.Conf.YouTube.BaseURL != "" {
		baseYtURL, parseErr := url.Parse(s.Conf.YouTube.BaseURL)
		if parseErr != nil {
			log.Printf("[ERROR] failed to parse base url %s, %v", s.Conf.YouTube.BaseURL, parseErr)
		}
		ytfs, fsErr := rest.NewFileServer(baseYtURL.Path, s.Conf.YouTube.FilesLocation)
		if fsErr == nil {
			router.Mount(baseYtURL.Path, ytfs)
		} else {
			log.Printf("[WARN] can't start static file server for yt, %v", fsErr)
		}
	}

	fs, err := rest.NewFileServer("/static", filepath.Join("webapp", "static"))
	if err == nil {
		router.Mount("/static", fs)
	} else {
		log.Printf("[WARN] can't start static file server, %v", err)
	}
	return router
}

// GET /rss/{name} - returns rss for given feeds set
func (s *Server) getFeedCtrl(w http.ResponseWriter, r *http.Request) {
	feedName := chi.URLParam(r, "name")
	items, err := s.Store.Load(feedName, s.Conf.System.MaxTotal, true)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest, err, "failed to get feed")
		return
	}

	for i, itm := range items {
		// add ts suffix to titles
		switch s.Conf.Feeds[feedName].ExtendDateTitle {
		case "yyyyddmm":
			items[i].Title = fmt.Sprintf("%s (%s)", itm.Title, itm.DT.Format("2006-02-01"))
		case "yyyymmdd":
			items[i].Title = fmt.Sprintf("%s (%s)", itm.Title, itm.DT.Format("2006-01-02"))
		}
	}

	rss := feed.Rss2{
		Version:       "2.0",
		ItemList:      items,
		Title:         s.Conf.Feeds[feedName].Title,
		Description:   s.Conf.Feeds[feedName].Description,
		Language:      s.Conf.Feeds[feedName].Language,
		Link:          s.Conf.Feeds[feedName].Link,
		PubDate:       items[0].PubDate,
		LastBuildDate: time.Now().Format(time.RFC822Z),
		NsItunes:      "http://www.itunes.com/dtds/podcast-1.0.dtd",
		NsMedia:       "http://search.yahoo.com/mrss/",
	}

	// replace link to UI page
	if s.Conf.System.BaseURL != "" {
		baseURL := s.Conf.System.BaseURL
		rss.Link = baseURL + "/feed/" + feedName
		if s.Conf.Feeds[feedName].Image != "" {
			imagesURL := baseURL + "/" + s.Conf.Feeds[feedName].Image
			if strings.HasPrefix(s.Conf.Feeds[feedName].Image, "/") || strings.HasSuffix(baseURL, "/") {
				imagesURL = baseURL + s.Conf.Feeds[feedName].Image
			}
			rss.ItunesImage = feed.ItunesImg{URL: imagesURL}
			rss.MediaThumbnail = feed.MediaThumbnail{URL: imagesURL}
		}
	}

	b, err := xml.MarshalIndent(&rss, "", "  ")
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusInternalServerError, err, "failed to marshal rss")
		return
	}

	w.Header().Set("Content-Type", "application/xml; charset=UTF-8")
	res := `<?xml version="1.0" encoding="UTF-8"?>` + "\n" + string(b)
	// this hack to avoid having different items for marshal and unmarshal due to "itunes" namespace
	res = strings.Replace(res, "<duration>", "<itunes:duration>", -1)
	res = strings.Replace(res, "</duration>", "</itunes:duration>", -1)

	_, _ = fmt.Fprintf(w, "%s", res)
}

// GET /image/{name}
func (s *Server) getImageCtrl(w http.ResponseWriter, r *http.Request) {
	fm := chi.URLParam(r, "name")
	fm = strings.TrimSuffix(fm, ".png")
	feedConf, found := s.Conf.Feeds[fm]
	if !found {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest,
			fmt.Errorf("image %s not found", fm), "failed to load image")
		return
	}

	b, err := os.ReadFile(feedConf.Image)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusBadRequest,
			errors.New("can't read  "+chi.URLParam(r, "name")), "failed to read image")
		return
	}
	w.Header().Set("Content-Type", "image/png")
	if _, err := w.Write(b); err != nil {
		log.Printf("[WARN] failed to send image, %s", err)
	}
}

// HEAD /image/{name}
func (s *Server) getImageHeadCtrl(w http.ResponseWriter, r *http.Request) {
	fm := chi.URLParam(r, "name")
	fm = strings.TrimSuffix(fm, ".png")
	feedConf, found := s.Conf.Feeds[fm]
	if !found {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	info, err := os.Stat(feedConf.Image)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}
	w.Header().Set("Content-Type", "image/png")
	w.Header().Set("Content-Length", strconv.Itoa(int(info.Size())))
	w.WriteHeader(http.StatusOK)
}

// GET /list - returns feed's image
func (s *Server) getListCtrl(w http.ResponseWriter, r *http.Request) {
	feeds := s.feeds()
	render.JSON(w, r, feeds)
}

// GET /yt/rss/{channel} - returns rss for given youtube channel
func (s *Server) getYoutubeFeedCtrl(w http.ResponseWriter, r *http.Request) {
	channel := chi.URLParam(r, "channel")

	fi := youtube.FeedInfo{ID: channel}
	for _, f := range s.Conf.YouTube.Channels {
		if f.ID == channel {
			fi = f
			break
		}
	}

	res, err := s.YoutubeSvc.RSSFeed(fi)
	if err != nil {
		rest.SendErrorJSON(w, r, log.Default(), http.StatusInternalServerError, err, "failed to read yt list")
		return
	}

	w.Header().Set("Content-Type", "application/xml; charset=UTF-8")
	res = `<?xml version="1.0" encoding="UTF-8"?>` + "\n" + res
	_, _ = fmt.Fprintf(w, "%s", res)
}

func (s *Server) feeds() []string {
	feeds := make([]string, 0, len(s.Conf.Feeds))
	for k := range s.Conf.Feeds {
		feeds = append(feeds, k)
	}
	return feeds
}
<|endoftext|>


<commit_before>package action

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/Masterminds/glide/cfg"
	"github.com/Masterminds/glide/msg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/Masterminds/glide/repo"
	"github.com/Masterminds/glide/util"
)

// Get fetches one or more dependencies and installs.
//
// This includes resolving dependency resolution and re-generating the lock file.
func Get(names []string, installer *repo.Installer, insecure, skipRecursive bool) {
	base := gpath.Basepath()
	EnsureGopath()
	EnsureVendorDir()
	conf := EnsureConfig()
	glidefile, err := gpath.Glide()
	if err != nil {
		msg.Die("Could not find Glide file: %s", err)
	}

	// Add the packages to the config.
	if err := addPkgsToConfig(conf, names, insecure); err != nil {
		msg.Die("Failed to get new packages: %s", err)
	}

	// Fetch the new packages. Can't resolve versions via installer.Update if
	// get is called while the vendor/ directory is empty so we checkout
	// everything.
	installer.Checkout(conf, false)

	// Prior to resolving dependencies we need to start working with a clone
	// of the conf because we'll be making real changes to it.
	confcopy := conf.Clone()

	if !skipRecursive {
		// Get all repos and update them.
		// TODO: Can we streamline this in any way? The reason that we update all
		// of the dependencies is that we need to re-negotiate versions. For example,
		// if an existing dependency has the constraint >1.0 and this new package
		// adds the constraint <2.0, then this may re-resolve the existing dependency
		// to be betwee 1.0 and 2.0. But changing that dependency may then result
		// in that dependency's dependencies changing... so we sorta do the whole
		// thing to be safe.
		err = installer.Update(confcopy)
		if err != nil {
			msg.Die("Could not update packages: %s", err)
		}
	}

	// Set Reference
	if err := repo.SetReference(confcopy); err != nil {
		msg.Error("Failed to set references: %s", err)
	}

	// VendoredCleanup
	if installer.UpdateVendored {
		repo.VendoredCleanup(confcopy)
	}

	// Write YAML
	if err := conf.WriteFile(glidefile); err != nil {
		msg.Die("Failed to write glide YAML file: %s", err)
	}
	if !skipRecursive {
		// Write lock
		writeLock(conf, confcopy, base)
	} else {
		msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated")
	}
}

func writeLock(conf, confcopy *cfg.Config, base string) {
	hash, err := conf.Hash()
	if err != nil {
		msg.Die("Failed to generate config hash. Unable to generate lock file.")
	}
	lock := cfg.NewLockfile(confcopy.Imports, hash)
	if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
		msg.Die("Failed to write glide lock file: %s", err)
	}
}

// addPkgsToConfig adds the given packages to the config file.
//
// Along the way it:
// - ensures that this package is not in the ignore list
// - checks to see if this is already in the dependency list.
// - splits version of of package name and adds the version attribute
// - seperates repo from packages
// - sets up insecure repo URLs where necessary
// - generates a list of subpackages
func addPkgsToConfig(conf *cfg.Config, names []string, insecure bool) error {

	msg.Info("Preparing to install %d package.", len(names))

	for _, name := range names {
		var version string
		parts := strings.Split(name, "#")
		if len(parts) > 1 {
			name = parts[0]
			version = parts[1]
		}

		root := util.GetRootFromPackage(name)
		if len(root) == 0 {
			return fmt.Errorf("Package name is required for %q.", name)
		}

		if conf.HasDependency(root) {

			// Check if the subpackage is present.
			subpkg := strings.TrimPrefix(name, root)
			subpkg = strings.TrimPrefix(subpkg, "/")
			if subpkg != "" {
				found := false
				dep := conf.Imports.Get(root)
				for _, s := range dep.Subpackages {
					if s == subpkg {
						found = true
						break
					}
				}
				if found {
					msg.Warn("Package %q is already in glide.yaml. Skipping", name)
				} else {
					dep.Subpackages = append(dep.Subpackages, subpkg)
					msg.Info("Adding sub-package %s to existing import %s", subpkg, root)
				}
			} else {
				msg.Warn("Package %q is already in glide.yaml. Skipping", root)
			}
			continue
		}

		if conf.HasIgnore(root) {
			msg.Warn("Package %q is set to be ignored in glide.yaml. Skipping", root)
			continue
		}

		dep := &cfg.Dependency{
			Name: root,
		}

		if version != "" {
			dep.Reference = version
		}

		// When retriving from an insecure location set the repo to the
		// insecure location.
		if insecure {
			dep.Repository = "http://" + root
		}

		subpkg := strings.TrimPrefix(name, root)
		if len(subpkg) > 0 && subpkg != "/" {
			dep.Subpackages = []string{strings.TrimPrefix(subpkg, "/")}
		}

		if dep.Reference != "" {
			msg.Info("Importing %s with the version %s", dep.Name, dep.Reference)
		} else {
			msg.Info("Importing %s", dep.Name)
		}

		conf.Imports = append(conf.Imports, dep)
	}
	return nil
}
<commit_msg>Fix a typo in a comment<commit_after>package action

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/Masterminds/glide/cfg"
	"github.com/Masterminds/glide/msg"
	gpath "github.com/Masterminds/glide/path"
	"github.com/Masterminds/glide/repo"
	"github.com/Masterminds/glide/util"
)

// Get fetches one or more dependencies and installs.
//
// This includes resolving dependency resolution and re-generating the lock file.
func Get(names []string, installer *repo.Installer, insecure, skipRecursive bool) {
	base := gpath.Basepath()
	EnsureGopath()
	EnsureVendorDir()
	conf := EnsureConfig()
	glidefile, err := gpath.Glide()
	if err != nil {
		msg.Die("Could not find Glide file: %s", err)
	}

	// Add the packages to the config.
	if err := addPkgsToConfig(conf, names, insecure); err != nil {
		msg.Die("Failed to get new packages: %s", err)
	}

	// Fetch the new packages. Can't resolve versions via installer.Update if
	// get is called while the vendor/ directory is empty so we checkout
	// everything.
	installer.Checkout(conf, false)

	// Prior to resolving dependencies we need to start working with a clone
	// of the conf because we'll be making real changes to it.
	confcopy := conf.Clone()

	if !skipRecursive {
		// Get all repos and update them.
		// TODO: Can we streamline this in any way? The reason that we update all
		// of the dependencies is that we need to re-negotiate versions. For example,
		// if an existing dependency has the constraint >1.0 and this new package
		// adds the constraint <2.0, then this may re-resolve the existing dependency
		// to be between 1.0 and 2.0. But changing that dependency may then result
		// in that dependency's dependencies changing... so we sorta do the whole
		// thing to be safe.
		err = installer.Update(confcopy)
		if err != nil {
			msg.Die("Could not update packages: %s", err)
		}
	}

	// Set Reference
	if err := repo.SetReference(confcopy); err != nil {
		msg.Error("Failed to set references: %s", err)
	}

	// VendoredCleanup
	if installer.UpdateVendored {
		repo.VendoredCleanup(confcopy)
	}

	// Write YAML
	if err := conf.WriteFile(glidefile); err != nil {
		msg.Die("Failed to write glide YAML file: %s", err)
	}
	if !skipRecursive {
		// Write lock
		writeLock(conf, confcopy, base)
	} else {
		msg.Warn("Skipping lockfile generation because full dependency tree is not being calculated")
	}
}

func writeLock(conf, confcopy *cfg.Config, base string) {
	hash, err := conf.Hash()
	if err != nil {
		msg.Die("Failed to generate config hash. Unable to generate lock file.")
	}
	lock := cfg.NewLockfile(confcopy.Imports, hash)
	if err := lock.WriteFile(filepath.Join(base, gpath.LockFile)); err != nil {
		msg.Die("Failed to write glide lock file: %s", err)
	}
}

// addPkgsToConfig adds the given packages to the config file.
//
// Along the way it:
// - ensures that this package is not in the ignore list
// - checks to see if this is already in the dependency list.
// - splits version of of package name and adds the version attribute
// - seperates repo from packages
// - sets up insecure repo URLs where necessary
// - generates a list of subpackages
func addPkgsToConfig(conf *cfg.Config, names []string, insecure bool) error {

	msg.Info("Preparing to install %d package.", len(names))

	for _, name := range names {
		var version string
		parts := strings.Split(name, "#")
		if len(parts) > 1 {
			name = parts[0]
			version = parts[1]
		}

		root := util.GetRootFromPackage(name)
		if len(root) == 0 {
			return fmt.Errorf("Package name is required for %q.", name)
		}

		if conf.HasDependency(root) {

			// Check if the subpackage is present.
			subpkg := strings.TrimPrefix(name, root)
			subpkg = strings.TrimPrefix(subpkg, "/")
			if subpkg != "" {
				found := false
				dep := conf.Imports.Get(root)
				for _, s := range dep.Subpackages {
					if s == subpkg {
						found = true
						break
					}
				}
				if found {
					msg.Warn("Package %q is already in glide.yaml. Skipping", name)
				} else {
					dep.Subpackages = append(dep.Subpackages, subpkg)
					msg.Info("Adding sub-package %s to existing import %s", subpkg, root)
				}
			} else {
				msg.Warn("Package %q is already in glide.yaml. Skipping", root)
			}
			continue
		}

		if conf.HasIgnore(root) {
			msg.Warn("Package %q is set to be ignored in glide.yaml. Skipping", root)
			continue
		}

		dep := &cfg.Dependency{
			Name: root,
		}

		if version != "" {
			dep.Reference = version
		}

		// When retriving from an insecure location set the repo to the
		// insecure location.
		if insecure {
			dep.Repository = "http://" + root
		}

		subpkg := strings.TrimPrefix(name, root)
		if len(subpkg) > 0 && subpkg != "/" {
			dep.Subpackages = []string{strings.TrimPrefix(subpkg, "/")}
		}

		if dep.Reference != "" {
			msg.Info("Importing %s with the version %s", dep.Name, dep.Reference)
		} else {
			msg.Info("Importing %s", dep.Name)
		}

		conf.Imports = append(conf.Imports, dep)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"time"
)

type ToggleData struct {
	Mode uint
	Data uint
}

type ActivationHandler struct {
	query_channel  chan bool
	toggle_channel chan ToggleData
	set_channel    chan bool
}

func (a *ActivationHandler) loop(quit <-chan bool, reactivationDelay uint) {
	var reactivate time.Time
	var reactivate_pending bool

	a.query_channel = make(chan bool)
	a.toggle_channel = make(chan ToggleData)
	a.set_channel = make(chan bool)

	ticker := time.Tick(1 * time.Second)

	var nextToggleTime = time.Now()

forever:
	for {
		select {
		case <-quit:
			break forever
		case <-a.query_channel:
			a.query_channel <- grimdActive
		case v := <-a.toggle_channel:
			// Firefox is sending 2 queries in a row, so debouncing is needed.
			if v.Mode == 1 && nextToggleTime.After(time.Now()) {
				logger.Warning("Toggle is too close: wait 10 seconds\n")
			} else {
				if v.Mode == 1 {
					grimdActive = !grimdActive
				} else {
					grimdActive = false
				}
				nextToggleTime = time.Now().Add(time.Duration(10) * time.Second)
				if !grimdActive && reactivationDelay > 0 {
					reactivate = time.Now().Add(time.Duration(v.Data) * time.Second)
					reactivate_pending = true
				} else {
					reactivate_pending = false
				}
				a.query_channel <- grimdActive
			}
		case v := <-a.set_channel:
			grimdActive = v
			reactivate_pending = false
			a.set_channel <- grimdActive
		case <-ticker:
			now := time.Now()
			if reactivate_pending && now.After(reactivate) {
				logger.Notice("Reactivating grimd (timer)")
				grimdActive = true
				reactivate_pending = false
			}
		}
	}
}

// Query activation state
func (a ActivationHandler) query() bool {
	a.query_channel <- true
	return <-a.query_channel
}

// Set activation state
func (a ActivationHandler) set(v bool) bool {
	a.set_channel <- v
	return <-a.set_channel
}

// Toggle activation state on or off
func (a ActivationHandler) toggle(reactivationDelay uint) bool {
	data := ToggleData{
		Mode: 1,
		Data: reactivationDelay,
	}
	a.toggle_channel <- data
	return <-a.query_channel
}

// Like toggle(), but only from on to off. Toggling when off will restart the
// timer.
func (a ActivationHandler) toggleOff(timeout uint) bool {
	a.toggle_channel <- ToggleData{
		Mode: 2,
		Data: timeout,
	}
	return <-a.query_channel
}
<commit_msg>Make linter happier<commit_after>package main

import (
	"time"
)

// ToggleData type
type ToggleData struct {
	Mode uint
	Data uint
}

// ActivationHandler type
type ActivationHandler struct {
	queryChannel  chan bool
	toggleChannel chan ToggleData
	setChannel    chan bool
}

func (a *ActivationHandler) loop(quit <-chan bool, reactivationDelay uint) {
	var reactivate time.Time
	var reactivatePending bool

	a.queryChannel = make(chan bool)
	a.toggleChannel = make(chan ToggleData)
	a.setChannel = make(chan bool)

	ticker := time.Tick(1 * time.Second)

	var nextToggleTime = time.Now()

forever:
	for {
		select {
		case <-quit:
			break forever
		case <-a.queryChannel:
			a.queryChannel <- grimdActive
		case v := <-a.toggleChannel:
			// Firefox is sending 2 queries in a row, so debouncing is needed.
			if v.Mode == 1 && nextToggleTime.After(time.Now()) {
				logger.Warning("Toggle is too close: wait 10 seconds\n")
			} else {
				if v.Mode == 1 {
					grimdActive = !grimdActive
				} else {
					grimdActive = false
				}
				nextToggleTime = time.Now().Add(time.Duration(10) * time.Second)
				if !grimdActive && reactivationDelay > 0 {
					reactivate = time.Now().Add(time.Duration(v.Data) * time.Second)
					reactivatePending = true
				} else {
					reactivatePending = false
				}
				a.queryChannel <- grimdActive
			}
		case v := <-a.setChannel:
			grimdActive = v
			reactivatePending = false
			a.setChannel <- grimdActive
		case <-ticker:
			now := time.Now()
			if reactivatePending && now.After(reactivate) {
				logger.Notice("Reactivating grimd (timer)")
				grimdActive = true
				reactivatePending = false
			}
		}
	}
}

// Query activation state
func (a ActivationHandler) query() bool {
	a.queryChannel <- true
	return <-a.queryChannel
}

// Set activation state
func (a ActivationHandler) set(v bool) bool {
	a.setChannel <- v
	return <-a.setChannel
}

// Toggle activation state on or off
func (a ActivationHandler) toggle(reactivationDelay uint) bool {
	data := ToggleData{
		Mode: 1,
		Data: reactivationDelay,
	}
	a.toggleChannel <- data
	return <-a.queryChannel
}

// Like toggle(), but only from on to off. Toggling when off will restart the
// timer.
func (a ActivationHandler) toggleOff(timeout uint) bool {
	a.toggleChannel <- ToggleData{
		Mode: 2,
		Data: timeout,
	}
	return <-a.queryChannel
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestGetAllRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rr := recordedRequests{}
	err = json.Unmarshal(body, &rr)

	expect(t, len(rr.Data), 0)
}

func TestGetAllRecordsWRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}
	// performing query
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rr := recordedRequests{}
	err = json.Unmarshal(body, &rr)

	expect(t, len(rr.Data), 5)
}

func TestGetRecordsCount(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/count", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rc := recordsCount{}
	err = json.Unmarshal(body, &rc)

	expect(t, rc.Count, 0)
}

func TestGetRecordsCountWRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}
	// performing query
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/count", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rc := recordsCount{}
	err = json.Unmarshal(body, &rc)

	expect(t, rc.Count, 5)
}

func TestExportImportRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	// deleting records
	err = dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	expect(t, err, nil)

	// using body to import records again
	importReq, err := http.NewRequest("POST", "/records", ioutil.NopCloser(bytes.NewBuffer(body)))
	//The response recorder used to record HTTP responses
	importRec := httptest.NewRecorder()

	m.ServeHTTP(importRec, importReq)
	expect(t, importRec.Code, http.StatusOK)

	// records should be there
	payloads, err := dbClient.cache.GetAllRequests()
	expect(t, err, nil)
	expect(t, len(payloads), 5)

}

func TestDeleteHandler(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}

	// checking whether we have records
	payloads, err := dbClient.cache.GetAllRequests()
	expect(t, err, nil)
	expect(t, len(payloads), 5)

	// deleting through handler
	deleteReq, err := http.NewRequest("DELETE", "/records", nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, deleteReq)
	expect(t, rec.Code, http.StatusOK)
}

func TestDeleteHandlerNoBucket(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// deleting through handler
	importReq, err := http.NewRequest("DELETE", "/records", nil)
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	importRec := httptest.NewRecorder()

	m.ServeHTTP(importRec, importReq)
	expect(t, importRec.Code, http.StatusOK)
}

func TestGetState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting initial mode
	dbClient.cfg.SetMode("virtualize")

	// deleting through handler
	req, err := http.NewRequest("GET", "/state", nil)
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(rec.Body)

	sr := stateRequest{}
	err = json.Unmarshal(body, &sr)

	expect(t, sr.Mode, "virtualize")
}

func TestSetVirtualizeState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to capture
	dbClient.cfg.SetMode("capture")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "virtualize"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "virtualize")
}

func TestSetCaptureState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "capture"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "capture")
}

func TestSetModifyState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "modify"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "modify")
}

func TestSetSynthesizeState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "synthesize"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "synthesize")
}
}
<commit_msg>test set random state which should not be allowed<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestGetAllRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rr := recordedRequests{}
	err = json.Unmarshal(body, &rr)

	expect(t, len(rr.Data), 0)
}

func TestGetAllRecordsWRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}
	// performing query
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rr := recordedRequests{}
	err = json.Unmarshal(body, &rr)

	expect(t, len(rr.Data), 5)
}

func TestGetRecordsCount(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/count", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rc := recordsCount{}
	err = json.Unmarshal(body, &rc)

	expect(t, rc.Count, 0)
}

func TestGetRecordsCountWRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}
	// performing query
	m := getBoneRouter(*dbClient)

	req, err := http.NewRequest("GET", "/count", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	rc := recordsCount{}
	err = json.Unmarshal(body, &rc)

	expect(t, rc.Count, 5)
}

func TestExportImportRecords(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}

	req, err := http.NewRequest("GET", "/records", nil)
	expect(t, err, nil)

	//The response recorder used to record HTTP responses
	respRec := httptest.NewRecorder()

	m.ServeHTTP(respRec, req)

	expect(t, respRec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(respRec.Body)

	// deleting records
	err = dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	expect(t, err, nil)

	// using body to import records again
	importReq, err := http.NewRequest("POST", "/records", ioutil.NopCloser(bytes.NewBuffer(body)))
	//The response recorder used to record HTTP responses
	importRec := httptest.NewRecorder()

	m.ServeHTTP(importRec, importReq)
	expect(t, importRec.Code, http.StatusOK)

	// records should be there
	payloads, err := dbClient.cache.GetAllRequests()
	expect(t, err, nil)
	expect(t, len(payloads), 5)

}

func TestDeleteHandler(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// inserting some payloads
	for i := 0; i < 5; i++ {
		req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com/q=%d", i), nil)
		expect(t, err, nil)
		dbClient.captureRequest(req)
	}

	// checking whether we have records
	payloads, err := dbClient.cache.GetAllRequests()
	expect(t, err, nil)
	expect(t, len(payloads), 5)

	// deleting through handler
	deleteReq, err := http.NewRequest("DELETE", "/records", nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, deleteReq)
	expect(t, rec.Code, http.StatusOK)
}

func TestDeleteHandlerNoBucket(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// deleting through handler
	importReq, err := http.NewRequest("DELETE", "/records", nil)
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	importRec := httptest.NewRecorder()

	m.ServeHTTP(importRec, importReq)
	expect(t, importRec.Code, http.StatusOK)
}

func TestGetState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting initial mode
	dbClient.cfg.SetMode("virtualize")

	// deleting through handler
	req, err := http.NewRequest("GET", "/state", nil)
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	body, err := ioutil.ReadAll(rec.Body)

	sr := stateRequest{}
	err = json.Unmarshal(body, &sr)

	expect(t, sr.Mode, "virtualize")
}

func TestSetVirtualizeState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to capture
	dbClient.cfg.SetMode("capture")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "virtualize"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "virtualize")
}

func TestSetCaptureState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "capture"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "capture")
}

func TestSetModifyState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "modify"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "modify")
}

func TestSetSynthesizeState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "synthesize"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusOK)

	// checking mode
	expect(t, dbClient.cfg.GetMode(), "synthesize")
}

func TestSetRandomState(t *testing.T) {
	server, dbClient := testTools(200, `{'message': 'here'}`)
	defer server.Close()
	defer dbClient.cache.DeleteBucket(dbClient.cache.requestsBucket)
	m := getBoneRouter(*dbClient)

	// setting mode to virtualize
	dbClient.cfg.SetMode("virtualize")

	// preparing to set mode through rest api
	var resp stateRequest
	resp.Mode = "shouldnotwork"

	bts, err := json.Marshal(&resp)
	expect(t, err, nil)

	// deleting through handler
	req, err := http.NewRequest("POST", "/state", ioutil.NopCloser(bytes.NewBuffer(bts)))
	expect(t, err, nil)
	//The response recorder used to record HTTP responses
	rec := httptest.NewRecorder()

	m.ServeHTTP(rec, req)
	expect(t, rec.Code, http.StatusBadRequest)

	// checking mode, should not have changed
	expect(t, dbClient.cfg.GetMode(), "virtualize")
}
<|endoftext|>