<commit_before>package commands

import (
	. "fmt"
	"io"
	"polydawn.net/docket/confl"
	"polydawn.net/docket/crocker"
	"polydawn.net/docket/dex"
	. "polydawn.net/docket/util"
	"polydawn.net/guitar/stream"
	"time"
)

type BuildCmdOpts struct {
	Source      string `short:"s" long:"source"      description:"Container source.      (default: graph)"`
	Destination string `short:"d" long:"destination" description:"Container destination. (default: graph)"`
	NoOp bool          `long:"noop" description:"Set the container command to /bin/true and do not modify destination image name."`
}

const DefaultBuildTarget = "build"

//Transforms a container
func (opts *BuildCmdOpts) Execute(args []string) error {
	//Get configuration
	target   := GetTarget(args, DefaultBuildTarget)
	settings := confl.NewConfigLoad(".")
	config := settings.GetConfig(target)
	var sourceGraph, destinationGraph *dex.Graph

	Println("Building from", config.Upstream, "to", config.Image)

	//If desired, set the command to /bin/true and do not modify destination image name
	//We'd love to not launch the container at all, but docker's export is completely broken.
	// 'docker export ubuntu' --> 'Error: No such container: ubuntu' --> :(
	if opts.NoOp {
		config.Command = []string{ "/bin/true" }
		config.Image = config.Upstream
	}

	//Right now, go-flags' default announation does not appear to work when in a sub-command.
	//	Will investigate and hopefully remove this later.
	if opts.Source == "" {
		opts.Source = "graph"
	}
	if opts.Destination == "" {
		opts.Destination = "graph"
	}

	//Parse input/output URIs
	sourceScheme, sourcePath           := ParseURI(opts.Source)
	destinationScheme, destinationPath := ParseURI(opts.Destination)

	//Copy of config for 'docker run'
	runConfig := config
	runConfig.Image = config.Upstream

	//Prepare input
	switch sourceScheme {
		case "graph":
			//Look up the graph, and clear any unwanted state
			sourceGraph = dex.NewGraph(settings.Graph)
			sourceGraph.Cleanse()
		case "file":
			//If the user did not specify an image path, set one
			if sourcePath == "" {
				sourcePath = "./image.tar"
			}
		case "index":
			//If pulling from the index, use the index key instead (protect URL namespace from docker)
			runConfig.Image = config.Index
	}

	//Prepare output
	switch destinationScheme {
		case "docker":
			//Nothing required here until container has ran
		case "graph":
			//Look up the graph, and clear any unwanted state
			destinationGraph = dex.NewGraph(settings.Graph)

			//Cleanse the graph unless it'd be redundant.
			if !(sourceScheme == "graph" && sourceGraph.GetDir() != destinationGraph.GetDir()) {
				destinationGraph.Cleanse()
			}
		case "file":
			//If the user did not specify an image path, set one
			if destinationPath == "" {
				destinationPath = "./image.tar"
			}

			//If the user is insane and wants to overwrite his source tar, stop him.
			//	Not at all robust (absolute paths? what are those? etc)
			if sourceScheme == "file" && sourcePath == destinationPath {
				return Errorf("Tar location is same for source and destination: " + sourcePath)
			}
		case "index":
			return Errorf("Destination " + destinationScheme + " is not supported yet.")
	}

	//Start or connect to a docker daemon
	dock := StartDocker(settings)

	//Prepare cache
	hasImage := dock.CheckCache(runConfig.Image)
	switch sourceScheme {
		case "docker":
			//Check that docker has the image needed
			if !hasImage {
				return Errorf("Docker does not have " + runConfig.Image + " loaded.")
			}
		case "graph":
			//Import the latest lineage
			if hasImage {
				Println("Docker already has", runConfig.Image, "loaded, not importing from graph.")
			} else {
				dock.Import(sourceGraph.Load(runConfig.Image), runConfig.Image, "latest")
			}
		case "file":
			//If docker already has the image loaded, warn & wait first!
			if hasImage {
				Println(
					"\n"   + "Warning: your docker cache already has " + runConfig.Image + " loaded." +
					"\n"   + "Importing will overwrite the saved image." +
					"\n\n" + "Continuing in 10 seconds, hit Ctrl-C to cancel..")
				time.Sleep(time.Second * 10)
			}

			//Load image from file
			dock.ImportFromFilenameTagstring(sourcePath, runConfig.Image)
		case "index":
			//If docker already has the image loaded, warn & wait first!
			if hasImage {
				Println(
					"\n"   + "Warning: your docker cache already has " + runConfig.Index + " loaded." +
					"\n"   + "Pulling from the index may modify the saved image." +
					"\n\n" + "Continuing in 10 seconds, hit Ctrl-C to cancel...")
				time.Sleep(time.Second * 10)
			}

			//Download from index
			dock.Pull(config.Index)
	}

	//Launch the container and wait for it to finish
	container := Launch(dock, runConfig)
	container.Wait()

	//Perform any destination operations required
	name, tag := crocker.SplitImageName(config.Image)
	switch destinationScheme {
		case "graph":
			//Create new branches as needed
			destinationGraph.PreparePublish(config.Image, config.Upstream)

			// Export a tar of the filesystem
			exportReader, exportWriter := io.Pipe()
			go container.Export(exportWriter)

			// Use guitar to write the tar's contents to the graph
			err := stream.ExportToFilesystem(exportReader, destinationGraph.GetDir())
			if err != nil {
				return err
			}

			// Commit changes
			destinationGraph.Publish(config.Image, config.Upstream)
		case "file":
			//Export a tar
			Println("Exporting to", destinationPath)
			container.ExportToFilename(destinationPath)
	}

	//Commit the image name to the cache.
	//	This is so if you run:
	//		docket build -s index  -d graph --noop
	//		docket build -s docker -d graph
	//	Docker will already know about your (much cooler) image name :)
	Println("Exporting to", name, tag)
	container.Commit(name, tag)

	//Remove if desired
	if config.Purge {
		container.Purge()
	}

	//Stop the docker daemon if it's a child process
	dock.Slay()

	return nil
}
<commit_msg>Fixed comparison error. Fixes #13.<commit_after>package commands

import (
	. "fmt"
	"io"
	"polydawn.net/docket/confl"
	"polydawn.net/docket/crocker"
	"polydawn.net/docket/dex"
	. "polydawn.net/docket/util"
	"polydawn.net/guitar/stream"
	"time"
)

type BuildCmdOpts struct {
	Source      string `short:"s" long:"source"      description:"Container source.      (default: graph)"`
	Destination string `short:"d" long:"destination" description:"Container destination. (default: graph)"`
	NoOp bool          `long:"noop" description:"Set the container command to /bin/true and do not modify destination image name."`
}

const DefaultBuildTarget = "build"

//Transforms a container
func (opts *BuildCmdOpts) Execute(args []string) error {
	//Get configuration
	target   := GetTarget(args, DefaultBuildTarget)
	settings := confl.NewConfigLoad(".")
	config := settings.GetConfig(target)
	var sourceGraph, destinationGraph *dex.Graph

	Println("Building from", config.Upstream, "to", config.Image)

	//If desired, set the command to /bin/true and do not modify destination image name
	//We'd love to not launch the container at all, but docker's export is completely broken.
	// 'docker export ubuntu' --> 'Error: No such container: ubuntu' --> :(
	if opts.NoOp {
		config.Command = []string{ "/bin/true" }
		config.Image = config.Upstream
	}

	//Right now, go-flags' default announation does not appear to work when in a sub-command.
	//	Will investigate and hopefully remove this later.
	if opts.Source == "" {
		opts.Source = "graph"
	}
	if opts.Destination == "" {
		opts.Destination = "graph"
	}

	//Parse input/output URIs
	sourceScheme, sourcePath           := ParseURI(opts.Source)
	destinationScheme, destinationPath := ParseURI(opts.Destination)

	//Copy of config for 'docker run'
	runConfig := config
	runConfig.Image = config.Upstream

	//Prepare input
	switch sourceScheme {
		case "graph":
			//Look up the graph, and clear any unwanted state
			sourceGraph = dex.NewGraph(settings.Graph)
			sourceGraph.Cleanse()
		case "file":
			//If the user did not specify an image path, set one
			if sourcePath == "" {
				sourcePath = "./image.tar"
			}
		case "index":
			//If pulling from the index, use the index key instead (protect URL namespace from docker)
			runConfig.Image = config.Index
	}

	//Prepare output
	switch destinationScheme {
		case "docker":
			//Nothing required here until container has ran
		case "graph":
			//Look up the graph, and clear any unwanted state
			destinationGraph = dex.NewGraph(settings.Graph)

			//Cleanse the graph unless it'd be redundant.
			if !(sourceScheme == "graph" && sourceGraph.GetDir() == destinationGraph.GetDir()) {
				destinationGraph.Cleanse()
			}
		case "file":
			//If the user did not specify an image path, set one
			if destinationPath == "" {
				destinationPath = "./image.tar"
			}

			//If the user is insane and wants to overwrite his source tar, stop him.
			//	Not at all robust (absolute paths? what are those? etc)
			if sourceScheme == "file" && sourcePath == destinationPath {
				return Errorf("Tar location is same for source and destination: " + sourcePath)
			}
		case "index":
			return Errorf("Destination " + destinationScheme + " is not supported yet.")
	}

	//Start or connect to a docker daemon
	dock := StartDocker(settings)

	//Prepare cache
	hasImage := dock.CheckCache(runConfig.Image)
	switch sourceScheme {
		case "docker":
			//Check that docker has the image needed
			if !hasImage {
				return Errorf("Docker does not have " + runConfig.Image + " loaded.")
			}
		case "graph":
			//Import the latest lineage
			if hasImage {
				Println("Docker already has", runConfig.Image, "loaded, not importing from graph.")
			} else {
				dock.Import(sourceGraph.Load(runConfig.Image), runConfig.Image, "latest")
			}
		case "file":
			//If docker already has the image loaded, warn & wait first!
			if hasImage {
				Println(
					"\n"   + "Warning: your docker cache already has " + runConfig.Image + " loaded." +
					"\n"   + "Importing will overwrite the saved image." +
					"\n\n" + "Continuing in 10 seconds, hit Ctrl-C to cancel..")
				time.Sleep(time.Second * 10)
			}

			//Load image from file
			dock.ImportFromFilenameTagstring(sourcePath, runConfig.Image)
		case "index":
			//If docker already has the image loaded, warn & wait first!
			if hasImage {
				Println(
					"\n"   + "Warning: your docker cache already has " + runConfig.Index + " loaded." +
					"\n"   + "Pulling from the index may modify the saved image." +
					"\n\n" + "Continuing in 10 seconds, hit Ctrl-C to cancel...")
				time.Sleep(time.Second * 10)
			}

			//Download from index
			dock.Pull(config.Index)
	}

	//Launch the container and wait for it to finish
	container := Launch(dock, runConfig)
	container.Wait()

	//Perform any destination operations required
	name, tag := crocker.SplitImageName(config.Image)
	switch destinationScheme {
		case "graph":
			//Create new branches as needed
			destinationGraph.PreparePublish(config.Image, config.Upstream)

			// Export a tar of the filesystem
			exportReader, exportWriter := io.Pipe()
			go container.Export(exportWriter)

			// Use guitar to write the tar's contents to the graph
			err := stream.ExportToFilesystem(exportReader, destinationGraph.GetDir())
			if err != nil {
				return err
			}

			// Commit changes
			destinationGraph.Publish(config.Image, config.Upstream)
		case "file":
			//Export a tar
			Println("Exporting to", destinationPath)
			container.ExportToFilename(destinationPath)
	}

	//Commit the image name to the cache.
	//	This is so if you run:
	//		docket build -s index  -d graph --noop
	//		docket build -s docker -d graph
	//	Docker will already know about your (much cooler) image name :)
	Println("Exporting to", name, tag)
	container.Commit(name, tag)

	//Remove if desired
	if config.Purge {
		container.Purge()
	}

	//Stop the docker daemon if it's a child process
	dock.Slay()

	return nil
}
<|endoftext|>


<commit_before>package bpf

/*
#include <linux/unistd.h>
#include <linux/bpf.h>

static __u64 ptr_to_u64(const void *ptr)
{
	return (__u64) (unsigned long) ptr;
}

void create_bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
			   int max_entries, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_type = map_type;
	ptr_bpf_attr->key_size = key_size;
	ptr_bpf_attr->value_size = value_size;
	ptr_bpf_attr->max_entries = max_entries;
}

void create_bpf_update_elem(int fd, const void *key, const void *value,
			    unsigned long long flags, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->value = ptr_to_u64(value);
	ptr_bpf_attr->flags = flags;
}

void create_bpf_lookup_elem(int fd, const void *key, void *value, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->value = ptr_to_u64(value);
}

void create_bpf_delete_elem(int fd, const void *key, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
}

void create_bpf_get_next_key(int fd, const void *key, void *next_key, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->next_key = ptr_to_u64(next_key);
}

void create_bpf_obj_pin(int fd, const char *pathname, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->pathname = ptr_to_u64(pathname);
	ptr_bpf_attr->bpf_fd = fd;
}

void create_bpf_obj_get(const char *pathname, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->pathname = ptr_to_u64(pathname);
}
*/
import "C"

import (
	"fmt"
	"syscall"
	"unsafe"
)

// CreateMap creates a Map of type mapType, with key size keySize, a value size of
// valueSize and the maximum amount of entries of maxEntries.
// mapType should be one of the bpf_map_type in "uapi/linux/bpf.h"
func CreateMap(mapType int, keySize, valueSize, maxEntries uint32) (int, error) {
	uba := C.union_bpf_attr{}
	C.create_bpf_create_map(
		C.enum_bpf_map_type(mapType),
		C.int(keySize),
		C.int(valueSize),
		C.int(maxEntries),
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_CREATE,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 {
		return int(ret), nil
	}
	return 0, fmt.Errorf("Unable to create map: %s", err)
}

// UpdateElement updates the map in fd with the given value in the given key.
// The flags can have the following values (if you include "uapi/linux/bpf.h"):
// C.BPF_ANY to create new element or update existing;
// C.BPF_NOEXIST to create new element if it didn't exist;
// C.BPF_EXIST to update existing element.
func UpdateElement(fd int, key, value unsafe.Pointer, flags uint64) error {
	uba := C.union_bpf_attr{}
	// TODO: fix me, this should be update
	C.create_bpf_lookup_elem(
		C.int(fd),
		key,
		value,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_UPDATE_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to update element: %s", err)
	}

	return nil
}

// LookupElement looks up for the map value stored in fd with the given key. The value
// is stored in the value unsafe.Pointer.
func LookupElement(fd int, key, value unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_lookup_elem(
		C.int(fd),
		key,
		value,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_LOOKUP_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to lookup element: %s", err)
	}

	return nil
}

// DeleteElement deletes the map element with the given key.
func DeleteElement(fd int, key unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_delete_elem(
		C.int(fd),
		key,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_DELETE_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to delete element: %s", err)
	}

	return nil
}

// GetNextKey stores, in nextKey, the next key after the key of the map in fd.
func GetNextKey(fd int, key, nextKey unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_get_next_key(
		C.int(fd),
		key,
		nextKey,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_GET_NEXT_KEY,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to get next key: %s", err)
	}

	return nil
}

// ObjPin stores the map's fd in pathname.
func ObjPin(fd int, pathname string) error {
	pathStr := C.CString(pathname)
	uba := C.union_bpf_attr{}
	C.create_bpf_obj_pin(C.int(fd), pathStr, unsafe.Pointer(&uba))
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_OBJ_PIN,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to pin object: %s", err)
	}

	return nil
}

// ObjGet reads the pathname and returns the map's fd read.
func ObjGet(pathname string) (int, error) {
	pathStr := C.CString(pathname)
	uba := C.union_bpf_attr{}
	C.create_bpf_obj_get(pathStr, unsafe.Pointer(&uba))

	fd, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_OBJ_GET,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if fd == 0 || err != 0 {
		return 0, fmt.Errorf("Unable to get object: %s", err)
	}

	return int(fd), nil
}
<commit_msg>Fixed wrong bpf_lookup_elem that should be update_elem<commit_after>package bpf

/*
#include <linux/unistd.h>
#include <linux/bpf.h>

static __u64 ptr_to_u64(const void *ptr)
{
	return (__u64) (unsigned long) ptr;
}

void create_bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
			   int max_entries, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_type = map_type;
	ptr_bpf_attr->key_size = key_size;
	ptr_bpf_attr->value_size = value_size;
	ptr_bpf_attr->max_entries = max_entries;
}

void create_bpf_update_elem(int fd, const void *key, const void *value,
			    unsigned long long flags, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->value = ptr_to_u64(value);
	ptr_bpf_attr->flags = flags;
}

void create_bpf_lookup_elem(int fd, const void *key, void *value, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->value = ptr_to_u64(value);
}

void create_bpf_delete_elem(int fd, const void *key, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
}

void create_bpf_get_next_key(int fd, const void *key, void *next_key, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->map_fd = fd;
	ptr_bpf_attr->key = ptr_to_u64(key);
	ptr_bpf_attr->next_key = ptr_to_u64(next_key);
}

void create_bpf_obj_pin(int fd, const char *pathname, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->pathname = ptr_to_u64(pathname);
	ptr_bpf_attr->bpf_fd = fd;
}

void create_bpf_obj_get(const char *pathname, void *attr)
{
	union bpf_attr* ptr_bpf_attr;
	ptr_bpf_attr = (union bpf_attr*)attr;
	ptr_bpf_attr->pathname = ptr_to_u64(pathname);
}
*/
import "C"

import (
	"fmt"
	"syscall"
	"unsafe"
)

// CreateMap creates a Map of type mapType, with key size keySize, a value size of
// valueSize and the maximum amount of entries of maxEntries.
// mapType should be one of the bpf_map_type in "uapi/linux/bpf.h"
func CreateMap(mapType int, keySize, valueSize, maxEntries uint32) (int, error) {
	uba := C.union_bpf_attr{}
	C.create_bpf_create_map(
		C.enum_bpf_map_type(mapType),
		C.int(keySize),
		C.int(valueSize),
		C.int(maxEntries),
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_CREATE,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 {
		return int(ret), nil
	}
	return 0, fmt.Errorf("Unable to create map: %s", err)
}

// UpdateElement updates the map in fd with the given value in the given key.
// The flags can have the following values (if you include "uapi/linux/bpf.h"):
// C.BPF_ANY to create new element or update existing;
// C.BPF_NOEXIST to create new element if it didn't exist;
// C.BPF_EXIST to update existing element.
func UpdateElement(fd int, key, value unsafe.Pointer, flags uint64) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_update_elem(
		C.int(fd),
		key,
		value,
		C.ulonglong(flags),
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_UPDATE_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to update element: %s", err)
	}

	return nil
}

// LookupElement looks up for the map value stored in fd with the given key. The value
// is stored in the value unsafe.Pointer.
func LookupElement(fd int, key, value unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_lookup_elem(
		C.int(fd),
		key,
		value,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_LOOKUP_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to lookup element: %s", err)
	}

	return nil
}

// DeleteElement deletes the map element with the given key.
func DeleteElement(fd int, key unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_delete_elem(
		C.int(fd),
		key,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_DELETE_ELEM,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to delete element: %s", err)
	}

	return nil
}

// GetNextKey stores, in nextKey, the next key after the key of the map in fd.
func GetNextKey(fd int, key, nextKey unsafe.Pointer) error {
	uba := C.union_bpf_attr{}
	C.create_bpf_get_next_key(
		C.int(fd),
		key,
		nextKey,
		unsafe.Pointer(&uba),
	)
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_MAP_GET_NEXT_KEY,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to get next key: %s", err)
	}

	return nil
}

// ObjPin stores the map's fd in pathname.
func ObjPin(fd int, pathname string) error {
	pathStr := C.CString(pathname)
	uba := C.union_bpf_attr{}
	C.create_bpf_obj_pin(C.int(fd), pathStr, unsafe.Pointer(&uba))
	ret, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_OBJ_PIN,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if ret != 0 || err != 0 {
		return fmt.Errorf("Unable to pin object: %s", err)
	}

	return nil
}

// ObjGet reads the pathname and returns the map's fd read.
func ObjGet(pathname string) (int, error) {
	pathStr := C.CString(pathname)
	uba := C.union_bpf_attr{}
	C.create_bpf_obj_get(pathStr, unsafe.Pointer(&uba))

	fd, _, err := syscall.Syscall(
		C.__NR_bpf,
		C.BPF_OBJ_GET,
		uintptr(unsafe.Pointer(&uba)),
		unsafe.Sizeof(uba),
	)

	if fd == 0 || err != 0 {
		return 0, fmt.Errorf("Unable to get object: %s", err)
	}

	return int(fd), nil
}
<|endoftext|>


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

// Incomplete std lib sources on Android.

// +build !android

package importgraph_test

import (
	"go/build"
	"sort"
	"testing"

	"golang.org/x/tools/refactor/importgraph"

	_ "crypto/hmac" // just for test, below
)

const this = "golang.org/x/tools/refactor/importgraph"

func TestBuild(t *testing.T) {
	forward, reverse, errors := importgraph.Build(&build.Default)

	// Test direct edges.
	// We throw in crypto/hmac to prove that external test files
	// (such as this one) are inspected.
	for _, p := range []string{"go/build", "testing", "crypto/hmac"} {
		if !forward[this][p] {
			t.Errorf("forward[importgraph][%s] not found", p)
		}
		if !reverse[p][this] {
			t.Errorf("reverse[%s][importgraph] not found", p)
		}
	}

	// Test non-existent direct edges
	for _, p := range []string{"errors", "reflect"} {
		if forward[this][p] {
			t.Errorf("unexpected: forward[importgraph][%s] found", p)
		}
		if reverse[p][this] {
			t.Errorf("unexpected: reverse[%s][importgraph] found", p)
		}
	}

	// Test Search is reflexive.
	if !forward.Search(this)[this] {
		t.Errorf("irreflexive: forward.Search(importgraph)[importgraph] not found")
	}
	if !reverse.Search(this)[this] {
		t.Errorf("irrefexive: reverse.Search(importgraph)[importgraph] not found")
	}

	// Test Search is transitive.  (There is no direct edge to these packages.)
	for _, p := range []string{"errors", "reflect", "unsafe"} {
		if !forward.Search(this)[p] {
			t.Errorf("intransitive: forward.Search(importgraph)[%s] not found", p)
		}
		if !reverse.Search(p)[this] {
			t.Errorf("intransitive: reverse.Search(%s)[importgraph] not found", p)
		}
	}

	// Test strongly-connected components.  Because A's external
	// test package can depend on B, and vice versa, most of the
	// standard libraries are mutually dependent when their external
	// tests are considered.
	//
	// For any nodes x, y in the same SCC, y appears in the results
	// of both forward and reverse searches starting from x
	if !forward.Search("fmt")["io"] ||
		!forward.Search("io")["fmt"] ||
		!reverse.Search("fmt")["io"] ||
		!reverse.Search("io")["fmt"] {
		t.Errorf("fmt and io are not mutually reachable despite being in the same SCC")
	}

	// debugging
	if false {
		for path, err := range errors {
			t.Logf("%s: %s", path, err)
		}
		printSorted := func(direction string, g importgraph.Graph, start string) {
			t.Log(direction)
			var pkgs []string
			for pkg := range g.Search(start) {
				pkgs = append(pkgs, pkg)
			}
			sort.Strings(pkgs)
			for _, pkg := range pkgs {
				t.Logf("\t%s", pkg)
			}
		}
		printSorted("forward", forward, this)
		printSorted("reverse", reverse, this)
	}
}
<commit_msg>refactor/importgraph: get test to pass when run in modules mode<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Incomplete std lib sources on Android.

// +build !android

package importgraph_test

import (
	"go/build"
	"sort"
	"strings"
	"testing"

	"golang.org/x/tools/go/packages/packagestest"
	"golang.org/x/tools/refactor/importgraph"

	_ "crypto/hmac" // just for test, below
)

const this = "golang.org/x/tools/refactor/importgraph"

func TestBuild(t *testing.T) {
	exported := packagestest.Export(t, packagestest.GOPATH, []packagestest.Module{
		{Name: "golang.org/x/tools/refactor/importgraph", Files: packagestest.MustCopyFileTree(".")}})
	defer exported.Cleanup()

	var gopath string
	for _, env := range exported.Config.Env {
		if !strings.HasPrefix(env, "GOPATH=") {
			continue
		}
		gopath = strings.TrimPrefix(env, "GOPATH=")
	}
	if gopath == "" {
		t.Fatal("Failed to fish GOPATH out of env: ", exported.Config.Env)
	}

	var buildContext = build.Default
	buildContext.GOPATH = gopath

	forward, reverse, errors := importgraph.Build(&buildContext)

	// Test direct edges.
	// We throw in crypto/hmac to prove that external test files
	// (such as this one) are inspected.
	for _, p := range []string{"go/build", "testing", "crypto/hmac"} {
		if !forward[this][p] {
			t.Errorf("forward[importgraph][%s] not found", p)
		}
		if !reverse[p][this] {
			t.Errorf("reverse[%s][importgraph] not found", p)
		}
	}

	// Test non-existent direct edges
	for _, p := range []string{"errors", "reflect"} {
		if forward[this][p] {
			t.Errorf("unexpected: forward[importgraph][%s] found", p)
		}
		if reverse[p][this] {
			t.Errorf("unexpected: reverse[%s][importgraph] found", p)
		}
	}

	// Test Search is reflexive.
	if !forward.Search(this)[this] {
		t.Errorf("irreflexive: forward.Search(importgraph)[importgraph] not found")
	}
	if !reverse.Search(this)[this] {
		t.Errorf("irrefexive: reverse.Search(importgraph)[importgraph] not found")
	}

	// Test Search is transitive.  (There is no direct edge to these packages.)
	for _, p := range []string{"errors", "reflect", "unsafe"} {
		if !forward.Search(this)[p] {
			t.Errorf("intransitive: forward.Search(importgraph)[%s] not found", p)
		}
		if !reverse.Search(p)[this] {
			t.Errorf("intransitive: reverse.Search(%s)[importgraph] not found", p)
		}
	}

	// Test strongly-connected components.  Because A's external
	// test package can depend on B, and vice versa, most of the
	// standard libraries are mutually dependent when their external
	// tests are considered.
	//
	// For any nodes x, y in the same SCC, y appears in the results
	// of both forward and reverse searches starting from x
	if !forward.Search("fmt")["io"] ||
		!forward.Search("io")["fmt"] ||
		!reverse.Search("fmt")["io"] ||
		!reverse.Search("io")["fmt"] {
		t.Errorf("fmt and io are not mutually reachable despite being in the same SCC")
	}

	// debugging
	if false {
		for path, err := range errors {
			t.Logf("%s: %s", path, err)
		}
		printSorted := func(direction string, g importgraph.Graph, start string) {
			t.Log(direction)
			var pkgs []string
			for pkg := range g.Search(start) {
				pkgs = append(pkgs, pkg)
			}
			sort.Strings(pkgs)
			for _, pkg := range pkgs {
				t.Logf("\t%s", pkg)
			}
		}
		printSorted("forward", forward, this)
		printSorted("reverse", reverse, this)
	}
}
<|endoftext|>


<commit_before>package cloudformation

// IAMPolicyDocument represents an IAM policy document
type IAMPolicyDocument struct {
	Version   string `json:",omitempty"`
	Statement []IAMPolicyStatement
}

// IAMPrincipal represents a principal in an IAM policy
type IAMPrincipal struct {
	Service *StringListExpr `json:",omitempty"`
	AWS     *StringListExpr `json:",omitempty"`
}

// IAMPolicyStatement represents an IAM policy statement
type IAMPolicyStatement struct {
	Sid       string          `json:",omitempty"`
	Effect    string          `json:",omitempty"`
	Principal *IAMPrincipal   `json:",omitempty"`
	Action    *StringListExpr `json:",omitempty"`
	Resource  *StringListExpr `json:",omitempty"`
	Condition interface{}     `json:",omitempty"`
}
<commit_msg>add NotPrincipal to IAM policy statement<commit_after>package cloudformation

// IAMPolicyDocument represents an IAM policy document
type IAMPolicyDocument struct {
	Version   string `json:",omitempty"`
	Statement []IAMPolicyStatement
}

// IAMPrincipal represents a principal in an IAM policy
type IAMPrincipal struct {
	Service *StringListExpr `json:",omitempty"`
	AWS     *StringListExpr `json:",omitempty"`
}

// IAMPolicyStatement represents an IAM policy statement
type IAMPolicyStatement struct {
	Sid          string          `json:",omitempty"`
	Effect       string          `json:",omitempty"`
	Principal    *IAMPrincipal   `json:",omitempty"`
	NotPrincipal *IAMPrincipal   `json:",omitempty"`
	Action       *StringListExpr `json:",omitempty"`
	Resource     *StringListExpr `json:",omitempty"`
	Condition    interface{}     `json:",omitempty"`
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"sync"

	"github.com/PuerkitoBio/goquery"
	"github.com/coreos/go-log/log"
)

var (
	dir      string
	threadId = regexp.MustCompile(`thread-(\d*)-`)
	imageId  = regexp.MustCompile(`img/(.*)\.(jpg|png)`)
)

func worker(linkChan chan string, wg *sync.WaitGroup) {
	defer wg.Done()

	for url := range linkChan {
		resp, err := http.Get(url)
		if err != nil {
			log.Println(err)
			continue
		}
		defer resp.Body.Close()

		m, _, err := image.Decode(resp.Body)
		if err != nil {
			log.Println(err)
			continue
		}

		// Ignore small images
		bounds := m.Bounds()
		if bounds.Size().X > 400 && bounds.Size().Y > 400 {
			imgInfo := imageId.FindStringSubmatch(url)
			out, _ := os.Create(dir + "/" + imgInfo[1] + "." + imgInfo[2])
			defer out.Close()
			switch imgInfo[2] {
			case "jpg":
				jpeg.Encode(out, m, nil)
			case "png":
				png.Encode(out, m)
			}
		}
	}
}

func main() {
	var url string
	var workers int
	flag.StringVar(&url, "u", "http://ck101.com/thread-2876990-1-1.html", "Destination")
	flag.IntVar(&workers, "w", 10, "Workers number")
	flag.Parse()

	doc, err := goquery.NewDocument(url)
	if err != nil {
		panic(err)
	}

	usr, _ := user.Current()
	title := doc.Find("h1#thread_subject").Text()
	dir = fmt.Sprintf("%v/Pictures/iloveck101/%v - %v", usr.HomeDir, threadId.FindStringSubmatch(url)[1], title)

	os.MkdirAll(dir, 0755)

	linkChan := make(chan string)
	wg := new(sync.WaitGroup)
	for i := 0; i < workers; i++ {
		wg.Add(1)
		go worker(linkChan, wg)
	}

	doc.Find("div[itemprop=articleBody] img").Each(func(i int, img *goquery.Selection) {
		imgUrl, _ := img.Attr("file")
		linkChan <- imgUrl
	})

	close(linkChan)
	wg.Wait()
}
<commit_msg>Update imageId regexp.<commit_after>package main

import (
	"flag"
	"fmt"
	"image"
	"image/jpeg"
	"image/png"
	"net/http"
	"os"
	"os/user"
	"regexp"
	"sync"

	"github.com/PuerkitoBio/goquery"
	"github.com/coreos/go-log/log"
)

var (
	dir      string
	threadId = regexp.MustCompile(`thread-(\d*)-`)
	imageId  = regexp.MustCompile(`([^\/]+)\.(png|jpg)`)
)

func worker(linkChan chan string, wg *sync.WaitGroup) {
	defer wg.Done()

	for url := range linkChan {
		resp, err := http.Get(url)
		if err != nil {
			log.Println(err)
			continue
		}
		defer resp.Body.Close()

		m, _, err := image.Decode(resp.Body)
		if err != nil {
			log.Println(err)
			continue
		}

		// Ignore small images
		bounds := m.Bounds()
		if bounds.Size().X > 300 && bounds.Size().Y > 300 {
			imgInfo := imageId.FindStringSubmatch(url)
			out, _ := os.Create(dir + "/" + imgInfo[1] + "." + imgInfo[2])
			defer out.Close()
			switch imgInfo[2] {
			case "jpg":
				jpeg.Encode(out, m, nil)
			case "png":
				png.Encode(out, m)
			}
		}
	}
}

func main() {
	var url string
	var workers int
	flag.StringVar(&url, "u", "http://ck101.com/thread-2876990-1-1.html", "Destination")
	flag.IntVar(&workers, "w", 10, "Workers number")
	flag.Parse()

	doc, err := goquery.NewDocument(url)
	if err != nil {
		panic(err)
	}

	usr, _ := user.Current()
	title := doc.Find("h1#thread_subject").Text()
	dir = fmt.Sprintf("%v/Pictures/iloveck101/%v - %v", usr.HomeDir, threadId.FindStringSubmatch(url)[1], title)

	os.MkdirAll(dir, 0755)

	linkChan := make(chan string)
	wg := new(sync.WaitGroup)
	for i := 0; i < workers; i++ {
		wg.Add(1)
		go worker(linkChan, wg)
	}

	doc.Find("div[itemprop=articleBody] img").Each(func(i int, img *goquery.Selection) {
		imgUrl, _ := img.Attr("file")
		linkChan <- imgUrl
	})

	close(linkChan)
	wg.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2013 Google Inc. All rights reserved.
//
// 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 imageproxy provides an image proxy server.  For typical use of
// creating and using a Proxy, see cmd/imageproxy/main.go.
package imageproxy // import "willnorris.com/go/imageproxy"

import (
	"bufio"
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/gregjones/httpcache"
	tphttp "willnorris.com/go/imageproxy/third_party/http"
)

// Proxy serves image requests.
type Proxy struct {
	Client *http.Client // client used to fetch remote URLs
	Cache  Cache        // cache used to cache responses

	// AllowHosts specifies a list of remote hosts that images can be
	// proxied from.  An empty list means all hosts are allowed.
	AllowHosts []string

	// Whitelist should no longer be used. Use "AllowHosts" instead.
	Whitelist []string

	// DenyHosts specifies a list of remote hosts that images cannot be
	// proxied from.
	DenyHosts []string

	// Referrers, when given, requires that requests to the image
	// proxy come from a referring host. An empty list means all
	// hosts are allowed.
	Referrers []string

	// DefaultBaseURL is the URL that relative remote URLs are resolved in
	// reference to.  If nil, all remote URLs specified in requests must be
	// absolute.
	DefaultBaseURL *url.URL

	// SignatureKey is the HMAC key used to verify signed requests.
	SignatureKey []byte

	// Allow images to scale beyond their original dimensions.
	ScaleUp bool

	// Timeout specifies a time limit for requests served by this Proxy.
	// If a call runs for longer than its time limit, a 504 Gateway Timeout
	// response is returned.  A Timeout of zero means no timeout.
	Timeout time.Duration

	// If true, log additional debug messages
	Verbose bool

	// ContentTypes specifies a list of content types to allow. An empty
	// list means all content types are allowed.
	ContentTypes []string

	// The User-Agent used by imageproxy when requesting origin image
	UserAgent string
}

// NewProxy constructs a new proxy.  The provided http RoundTripper will be
// used to fetch remote URLs.  If nil is provided, http.DefaultTransport will
// be used.
func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
	if transport == nil {
		transport = http.DefaultTransport
	}
	if cache == nil {
		cache = NopCache
	}

	proxy := &Proxy{
		Cache: cache,
	}

	client := new(http.Client)
	client.Transport = &httpcache.Transport{
		Transport: &TransformingTransport{
			Transport:     transport,
			CachingClient: client,
			log: func(format string, v ...interface{}) {
				if proxy.Verbose {
					log.Printf(format, v...)
				}
			},
		},
		Cache:               cache,
		MarkCachedResponses: true,
	}

	proxy.Client = client

	return proxy
}

// ServeHTTP handles incoming requests.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return // ignore favicon requests
	}

	if r.URL.Path == "/" || r.URL.Path == "/health-check" {
		fmt.Fprint(w, "OK")
		return
	}

	var h http.Handler = http.HandlerFunc(p.serveImage)
	if p.Timeout > 0 {
		h = tphttp.TimeoutHandler(h, p.Timeout, "Gateway timeout waiting for remote resource.")
	}
	h.ServeHTTP(w, r)
}

// serveImage handles incoming requests for proxied images.
func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
	req, err := NewRequest(r, p.DefaultBaseURL)
	if err != nil {
		msg := fmt.Sprintf("invalid request URL: %v", err)
		log.Print(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}

	// assign static settings from proxy to req.Options
	req.Options.ScaleUp = p.ScaleUp

	if err := p.allowed(req); err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

	actualReq, _ := http.NewRequest("GET", req.String(), nil)
	if p.UserAgent != "" {
		actualReq.Header.Set("User-Agent", p.UserAgent)
	}
	resp, err := p.Client.Do(actualReq)
	if err != nil {
		msg := fmt.Sprintf("error fetching remote image: %v", err)
		log.Print(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	cached := resp.Header.Get(httpcache.XFromCache)
	if p.Verbose {
		log.Printf("request: %v (served from cache: %v)", *req, cached == "1")
	}

	copyHeader(w.Header(), resp.Header, "Cache-Control", "Last-Modified", "Expires", "Etag", "Link")

	if should304(r, resp) {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	contentType, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if resp.ContentLength != 0 && !contentTypeMatches(p.ContentTypes, contentType) {
		msg := fmt.Sprintf("forbidden content-type: %q", contentType)
		log.Print(msg)
		http.Error(w, msg, http.StatusForbidden)
		return
	}
	w.Header().Set("Content-Type", contentType)

	copyHeader(w.Header(), resp.Header, "Content-Length")

	//Enable CORS for 3rd party applications
	w.Header().Set("Access-Control-Allow-Origin", "*")

	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}

// copyHeader copies header values from src to dst, adding to any existing
// values with the same header name.  If keys is not empty, only those header
// keys will be copied.
func copyHeader(dst, src http.Header, keys ...string) {
	if len(keys) == 0 {
		for k, _ := range src {
			keys = append(keys, k)
		}
	}
	for _, key := range keys {
		k := http.CanonicalHeaderKey(key)
		for _, v := range src[k] {
			dst.Add(k, v)
		}
	}
}

// allowed determines whether the specified request contains an allowed
// referrer, host, and signature.  It returns an error if the request is not
// allowed.
func (p *Proxy) allowed(r *Request) error {
	if p.AllowHosts == nil {
		// backwards compatible with old naming of the field
		p.AllowHosts = p.Whitelist
	}
	if len(p.Referrers) > 0 && !referrerMatches(p.Referrers, r.Original) {
		return fmt.Errorf("request does not contain an allowed referrer: %v", r)
	}

	if hostMatches(p.DenyHosts, r.URL) {
		return fmt.Errorf("request contains a denied host %v", r)
	}

	if len(p.AllowHosts) == 0 && len(p.SignatureKey) == 0 {
		return nil // no allowed hosts or signature key, all requests accepted
	}

	if len(p.AllowHosts) > 0 && hostMatches(p.AllowHosts, r.URL) {
		return nil
	}

	if len(p.SignatureKey) > 0 && validSignature(p.SignatureKey, r) {
		return nil
	}

	return fmt.Errorf("request does not contain an allowed host or valid signature: %v", r)
}

// contentTypeMatches returns whether contentType matches one of the allowed patterns.
func contentTypeMatches(patterns []string, contentType string) bool {
	if len(patterns) == 0 {
		return true
	}

	for _, pattern := range patterns {
		if ok, err := filepath.Match(pattern, contentType); ok && err == nil {
			return true
		}
	}

	return false
}

// hostMatches returns whether the host in u matches one of hosts.
func hostMatches(hosts []string, u *url.URL) bool {
	for _, host := range hosts {
		if u.Host == host {
			return true
		}
		if strings.HasPrefix(host, "*.") && strings.HasSuffix(u.Host, host[2:]) {
			return true
		}
	}

	return false
}

// returns whether the referrer from the request is in the host list.
func referrerMatches(hosts []string, r *http.Request) bool {
	u, err := url.Parse(r.Header.Get("Referer"))
	if err != nil { // malformed or blank header, just deny
		return false
	}

	return hostMatches(hosts, u)
}

// validSignature returns whether the request signature is valid.
func validSignature(key []byte, r *Request) bool {
	sig := r.Options.Signature
	if m := len(sig) % 4; m != 0 { // add padding if missing
		sig += strings.Repeat("=", 4-m)
	}

	got, err := base64.URLEncoding.DecodeString(sig)
	if err != nil {
		log.Printf("error base64 decoding signature %q", r.Options.Signature)
		return false
	}

	mac := hmac.New(sha256.New, key)
	mac.Write([]byte(r.URL.String()))
	want := mac.Sum(nil)

	return hmac.Equal(got, want)
}

// should304 returns whether we should send a 304 Not Modified in response to
// req, based on the response resp.  This is determined using the last modified
// time and the entity tag of resp.
func should304(req *http.Request, resp *http.Response) bool {
	// TODO(willnorris): if-none-match header can be a comma separated list
	// of multiple tags to be matched, or the special value "*" which
	// matches all etags
	etag := resp.Header.Get("Etag")
	if etag != "" && etag == req.Header.Get("If-None-Match") {
		return true
	}

	lastModified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
	if err != nil {
		return false
	}
	ifModSince, err := time.Parse(time.RFC1123, req.Header.Get("If-Modified-Since"))
	if err != nil {
		return false
	}
	if lastModified.Before(ifModSince) || lastModified.Equal(ifModSince) {
		return true
	}

	return false
}

// TransformingTransport is an implementation of http.RoundTripper that
// optionally transforms images using the options specified in the request URL
// fragment.
type TransformingTransport struct {
	// Transport is the underlying http.RoundTripper used to satisfy
	// non-transform requests (those that do not include a URL fragment).
	Transport http.RoundTripper

	// CachingClient is used to fetch images to be resized.  This client is
	// used rather than Transport directly in order to ensure that
	// responses are properly cached.
	CachingClient *http.Client

	log func(format string, v ...interface{})
}

// RoundTrip implements the http.RoundTripper interface.
func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL.Fragment == "" {
		// normal requests pass through
		if t.log != nil {
			t.log("fetching remote URL: %v", req.URL)
		}
		return t.Transport.RoundTrip(req)
	}

	f := req.URL.Fragment
	req.URL.Fragment = ""
	resp, err := t.CachingClient.Do(req)
	req.URL.Fragment = f
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if should304(req, resp) {
		// bare 304 response, full response will be used from cache
		return &http.Response{StatusCode: http.StatusNotModified}, nil
	}

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

	opt := ParseOptions(req.URL.Fragment)

	img, err := Transform(b, opt)
	if err != nil {
		log.Printf("error transforming image %s: %v", req.URL.String(), err)
		img = b
	}

	// replay response with transformed image and updated content length
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%s %s\n", resp.Proto, resp.Status)
	resp.Header.WriteSubset(buf, map[string]bool{
		"Content-Length": true,
		// exclude Content-Type header if the format may have changed during transformation
		"Content-Type": opt.Format != "" || resp.Header.Get("Content-Type") == "image/webp" || resp.Header.Get("Content-Type") == "image/tiff",
	})
	fmt.Fprintf(buf, "Content-Length: %d\n\n", len(img))
	buf.Write(img)

	return http.ReadResponse(bufio.NewReader(buf), req)
}
<commit_msg>remove specific denial error from response<commit_after>// Copyright 2013 Google Inc. All rights reserved.
//
// 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 imageproxy provides an image proxy server.  For typical use of
// creating and using a Proxy, see cmd/imageproxy/main.go.
package imageproxy // import "willnorris.com/go/imageproxy"

import (
	"bufio"
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"
	"time"

	"github.com/gregjones/httpcache"
	tphttp "willnorris.com/go/imageproxy/third_party/http"
)

// Proxy serves image requests.
type Proxy struct {
	Client *http.Client // client used to fetch remote URLs
	Cache  Cache        // cache used to cache responses

	// AllowHosts specifies a list of remote hosts that images can be
	// proxied from.  An empty list means all hosts are allowed.
	AllowHosts []string

	// Whitelist should no longer be used. Use "AllowHosts" instead.
	Whitelist []string

	// DenyHosts specifies a list of remote hosts that images cannot be
	// proxied from.
	DenyHosts []string

	// Referrers, when given, requires that requests to the image
	// proxy come from a referring host. An empty list means all
	// hosts are allowed.
	Referrers []string

	// DefaultBaseURL is the URL that relative remote URLs are resolved in
	// reference to.  If nil, all remote URLs specified in requests must be
	// absolute.
	DefaultBaseURL *url.URL

	// SignatureKey is the HMAC key used to verify signed requests.
	SignatureKey []byte

	// Allow images to scale beyond their original dimensions.
	ScaleUp bool

	// Timeout specifies a time limit for requests served by this Proxy.
	// If a call runs for longer than its time limit, a 504 Gateway Timeout
	// response is returned.  A Timeout of zero means no timeout.
	Timeout time.Duration

	// If true, log additional debug messages
	Verbose bool

	// ContentTypes specifies a list of content types to allow. An empty
	// list means all content types are allowed.
	ContentTypes []string

	// The User-Agent used by imageproxy when requesting origin image
	UserAgent string
}

// NewProxy constructs a new proxy.  The provided http RoundTripper will be
// used to fetch remote URLs.  If nil is provided, http.DefaultTransport will
// be used.
func NewProxy(transport http.RoundTripper, cache Cache) *Proxy {
	if transport == nil {
		transport = http.DefaultTransport
	}
	if cache == nil {
		cache = NopCache
	}

	proxy := &Proxy{
		Cache: cache,
	}

	client := new(http.Client)
	client.Transport = &httpcache.Transport{
		Transport: &TransformingTransport{
			Transport:     transport,
			CachingClient: client,
			log: func(format string, v ...interface{}) {
				if proxy.Verbose {
					log.Printf(format, v...)
				}
			},
		},
		Cache:               cache,
		MarkCachedResponses: true,
	}

	proxy.Client = client

	return proxy
}

// ServeHTTP handles incoming requests.
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/favicon.ico" {
		return // ignore favicon requests
	}

	if r.URL.Path == "/" || r.URL.Path == "/health-check" {
		fmt.Fprint(w, "OK")
		return
	}

	var h http.Handler = http.HandlerFunc(p.serveImage)
	if p.Timeout > 0 {
		h = tphttp.TimeoutHandler(h, p.Timeout, "Gateway timeout waiting for remote resource.")
	}
	h.ServeHTTP(w, r)
}

// serveImage handles incoming requests for proxied images.
func (p *Proxy) serveImage(w http.ResponseWriter, r *http.Request) {
	req, err := NewRequest(r, p.DefaultBaseURL)
	if err != nil {
		msg := fmt.Sprintf("invalid request URL: %v", err)
		log.Print(msg)
		http.Error(w, msg, http.StatusBadRequest)
		return
	}

	// assign static settings from proxy to req.Options
	req.Options.ScaleUp = p.ScaleUp

	if err := p.allowed(req); err != nil {
		log.Printf("%s: %v", err, req)
		http.Error(w, msgNotAllowed, http.StatusForbidden)
		return
	}

	actualReq, _ := http.NewRequest("GET", req.String(), nil)
	if p.UserAgent != "" {
		actualReq.Header.Set("User-Agent", p.UserAgent)
	}
	resp, err := p.Client.Do(actualReq)
	if err != nil {
		msg := fmt.Sprintf("error fetching remote image: %v", err)
		log.Print(msg)
		http.Error(w, msg, http.StatusInternalServerError)
		return
	}
	defer resp.Body.Close()

	cached := resp.Header.Get(httpcache.XFromCache)
	if p.Verbose {
		log.Printf("request: %v (served from cache: %v)", *req, cached == "1")
	}

	copyHeader(w.Header(), resp.Header, "Cache-Control", "Last-Modified", "Expires", "Etag", "Link")

	if should304(r, resp) {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	contentType, _, _ := mime.ParseMediaType(resp.Header.Get("Content-Type"))
	if resp.ContentLength != 0 && !contentTypeMatches(p.ContentTypes, contentType) {
		log.Printf("content-type not allowed: %q", contentType)
		http.Error(w, msgNotAllowed, http.StatusForbidden)
		return
	}
	w.Header().Set("Content-Type", contentType)

	copyHeader(w.Header(), resp.Header, "Content-Length")

	//Enable CORS for 3rd party applications
	w.Header().Set("Access-Control-Allow-Origin", "*")

	w.WriteHeader(resp.StatusCode)
	io.Copy(w, resp.Body)
}

// copyHeader copies header values from src to dst, adding to any existing
// values with the same header name.  If keys is not empty, only those header
// keys will be copied.
func copyHeader(dst, src http.Header, keys ...string) {
	if len(keys) == 0 {
		for k, _ := range src {
			keys = append(keys, k)
		}
	}
	for _, key := range keys {
		k := http.CanonicalHeaderKey(key)
		for _, v := range src[k] {
			dst.Add(k, v)
		}
	}
}

var (
	errReferrer   = errors.New("request does not contain an allowed referrer")
	errDeniedHost = errors.New("request contains a denied host")
	errNotAllowed = errors.New("request does not contain an allowed host or valid signature")

	msgNotAllowed = "requested URL is not allowed"
)

// allowed determines whether the specified request contains an allowed
// referrer, host, and signature.  It returns an error if the request is not
// allowed.
func (p *Proxy) allowed(r *Request) error {
	if p.AllowHosts == nil {
		// backwards compatible with old naming of the field
		p.AllowHosts = p.Whitelist
	}
	if len(p.Referrers) > 0 && !referrerMatches(p.Referrers, r.Original) {
		return errReferrer
	}

	if hostMatches(p.DenyHosts, r.URL) {
		return errDeniedHost
	}

	if len(p.AllowHosts) == 0 && len(p.SignatureKey) == 0 {
		return nil // no allowed hosts or signature key, all requests accepted
	}

	if len(p.AllowHosts) > 0 && hostMatches(p.AllowHosts, r.URL) {
		return nil
	}

	if len(p.SignatureKey) > 0 && validSignature(p.SignatureKey, r) {
		return nil
	}

	return errNotAllowed
}

// contentTypeMatches returns whether contentType matches one of the allowed patterns.
func contentTypeMatches(patterns []string, contentType string) bool {
	if len(patterns) == 0 {
		return true
	}

	for _, pattern := range patterns {
		if ok, err := filepath.Match(pattern, contentType); ok && err == nil {
			return true
		}
	}

	return false
}

// hostMatches returns whether the host in u matches one of hosts.
func hostMatches(hosts []string, u *url.URL) bool {
	for _, host := range hosts {
		if u.Host == host {
			return true
		}
		if strings.HasPrefix(host, "*.") && strings.HasSuffix(u.Host, host[2:]) {
			return true
		}
	}

	return false
}

// returns whether the referrer from the request is in the host list.
func referrerMatches(hosts []string, r *http.Request) bool {
	u, err := url.Parse(r.Header.Get("Referer"))
	if err != nil { // malformed or blank header, just deny
		return false
	}

	return hostMatches(hosts, u)
}

// validSignature returns whether the request signature is valid.
func validSignature(key []byte, r *Request) bool {
	sig := r.Options.Signature
	if m := len(sig) % 4; m != 0 { // add padding if missing
		sig += strings.Repeat("=", 4-m)
	}

	got, err := base64.URLEncoding.DecodeString(sig)
	if err != nil {
		log.Printf("error base64 decoding signature %q", r.Options.Signature)
		return false
	}

	mac := hmac.New(sha256.New, key)
	mac.Write([]byte(r.URL.String()))
	want := mac.Sum(nil)

	return hmac.Equal(got, want)
}

// should304 returns whether we should send a 304 Not Modified in response to
// req, based on the response resp.  This is determined using the last modified
// time and the entity tag of resp.
func should304(req *http.Request, resp *http.Response) bool {
	// TODO(willnorris): if-none-match header can be a comma separated list
	// of multiple tags to be matched, or the special value "*" which
	// matches all etags
	etag := resp.Header.Get("Etag")
	if etag != "" && etag == req.Header.Get("If-None-Match") {
		return true
	}

	lastModified, err := time.Parse(time.RFC1123, resp.Header.Get("Last-Modified"))
	if err != nil {
		return false
	}
	ifModSince, err := time.Parse(time.RFC1123, req.Header.Get("If-Modified-Since"))
	if err != nil {
		return false
	}
	if lastModified.Before(ifModSince) || lastModified.Equal(ifModSince) {
		return true
	}

	return false
}

// TransformingTransport is an implementation of http.RoundTripper that
// optionally transforms images using the options specified in the request URL
// fragment.
type TransformingTransport struct {
	// Transport is the underlying http.RoundTripper used to satisfy
	// non-transform requests (those that do not include a URL fragment).
	Transport http.RoundTripper

	// CachingClient is used to fetch images to be resized.  This client is
	// used rather than Transport directly in order to ensure that
	// responses are properly cached.
	CachingClient *http.Client

	log func(format string, v ...interface{})
}

// RoundTrip implements the http.RoundTripper interface.
func (t *TransformingTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.URL.Fragment == "" {
		// normal requests pass through
		if t.log != nil {
			t.log("fetching remote URL: %v", req.URL)
		}
		return t.Transport.RoundTrip(req)
	}

	f := req.URL.Fragment
	req.URL.Fragment = ""
	resp, err := t.CachingClient.Do(req)
	req.URL.Fragment = f
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if should304(req, resp) {
		// bare 304 response, full response will be used from cache
		return &http.Response{StatusCode: http.StatusNotModified}, nil
	}

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

	opt := ParseOptions(req.URL.Fragment)

	img, err := Transform(b, opt)
	if err != nil {
		log.Printf("error transforming image %s: %v", req.URL.String(), err)
		img = b
	}

	// replay response with transformed image and updated content length
	buf := new(bytes.Buffer)
	fmt.Fprintf(buf, "%s %s\n", resp.Proto, resp.Status)
	resp.Header.WriteSubset(buf, map[string]bool{
		"Content-Length": true,
		// exclude Content-Type header if the format may have changed during transformation
		"Content-Type": opt.Format != "" || resp.Header.Get("Content-Type") == "image/webp" || resp.Header.Get("Content-Type") == "image/tiff",
	})
	fmt.Fprintf(buf, "Content-Length: %d\n\n", len(img))
	buf.Write(img)

	return http.ReadResponse(bufio.NewReader(buf), req)
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
)

const (
	errSecondaryDNSTSIGMissingID = "secondary DNS TSIG ID is required"
)

// SecondaryDNSTSIG contains the structure for a secondary DNS TSIG.
type SecondaryDNSTSIG struct {
	ID     string `json:"id,omitempty"`
	Name   string `json:"name"`
	Secret string `json:"secret"`
	Algo   string `json:"algo"`
}

// SecondaryDNSTSIGDetailResponse is the API response for a single secondary
// DNS TSIG.
type SecondaryDNSTSIGDetailResponse struct {
	Response
	Result SecondaryDNSTSIG `json:"result"`
}

// SecondaryDNSTSIGListResponse is the API response for all secondary DNS TSIGs.
type SecondaryDNSTSIGListResponse struct {
	Response
	Result []SecondaryDNSTSIG `json:"result"`
}

// GetSecondaryDNSTSIG gets a single account level TSIG for a secondary DNS
// configuration.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--tsig-details
func (api *API) GetSecondaryDNSTSIG(ctx context.Context, accountID, tsigID string) (SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsigID)

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return SecondaryDNSTSIG{}, err
	}

	var r SecondaryDNSTSIGDetailResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ListSecondaryDNSTSIGs gets all account level TSIG for a secondary DNS
// configuration.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--list-tsigs
func (api *API) ListSecondaryDNSTSIGs(ctx context.Context, accountID string) ([]SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs", accountID)

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []SecondaryDNSTSIG{}, err
	}

	var r SecondaryDNSTSIGListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// CreateSecondaryDNSTSIG creates a secondary DNS TSIG at the account level.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--create-tsig
func (api *API) CreateSecondaryDNSTSIG(ctx context.Context, accountID string, tsig SecondaryDNSTSIG) (SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs", accountID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri,
		SecondaryDNSTSIG{
			Name:   tsig.Name,
			Secret: tsig.Secret,
			Algo:   tsig.Algo,
		},
	)

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

	result := SecondaryDNSTSIGDetailResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result, nil
}

// UpdateSecondaryDNSTSIG updates an existing secondary DNS TSIG at
// the account level.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--update-tsig
func (api *API) UpdateSecondaryDNSTSIG(ctx context.Context, accountID string, tsig SecondaryDNSTSIG) (SecondaryDNSTSIG, error) {
	if tsig.ID == "" {
		return SecondaryDNSTSIG{}, errors.New(errSecondaryDNSTSIGMissingID)
	}

	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsig.ID)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri,
		SecondaryDNSTSIG{
			Name:   tsig.Name,
			Secret: tsig.Secret,
			Algo:   tsig.Algo,
		},
	)

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

	result := SecondaryDNSTSIGDetailResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result, nil
}

// DeleteSecondaryDNSTSIG deletes a secondary DNS TSIG.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--delete-tsig
func (api *API) DeleteSecondaryDNSTSIG(ctx context.Context, accountID, tsigID string) error {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsigID)
	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

	if err != nil {
		return err
	}

	return nil
}
<commit_msg>secondary_dns_tsig: no need to reconstruct what we already have<commit_after>package cloudflare

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
)

const (
	errSecondaryDNSTSIGMissingID = "secondary DNS TSIG ID is required"
)

// SecondaryDNSTSIG contains the structure for a secondary DNS TSIG.
type SecondaryDNSTSIG struct {
	ID     string `json:"id,omitempty"`
	Name   string `json:"name"`
	Secret string `json:"secret"`
	Algo   string `json:"algo"`
}

// SecondaryDNSTSIGDetailResponse is the API response for a single secondary
// DNS TSIG.
type SecondaryDNSTSIGDetailResponse struct {
	Response
	Result SecondaryDNSTSIG `json:"result"`
}

// SecondaryDNSTSIGListResponse is the API response for all secondary DNS TSIGs.
type SecondaryDNSTSIGListResponse struct {
	Response
	Result []SecondaryDNSTSIG `json:"result"`
}

// GetSecondaryDNSTSIG gets a single account level TSIG for a secondary DNS
// configuration.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--tsig-details
func (api *API) GetSecondaryDNSTSIG(ctx context.Context, accountID, tsigID string) (SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsigID)

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return SecondaryDNSTSIG{}, err
	}

	var r SecondaryDNSTSIGDetailResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// ListSecondaryDNSTSIGs gets all account level TSIG for a secondary DNS
// configuration.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--list-tsigs
func (api *API) ListSecondaryDNSTSIGs(ctx context.Context, accountID string) ([]SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs", accountID)

	res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil)
	if err != nil {
		return []SecondaryDNSTSIG{}, err
	}

	var r SecondaryDNSTSIGListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

// CreateSecondaryDNSTSIG creates a secondary DNS TSIG at the account level.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--create-tsig
func (api *API) CreateSecondaryDNSTSIG(ctx context.Context, accountID string, tsig SecondaryDNSTSIG) (SecondaryDNSTSIG, error) {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs", accountID)
	res, err := api.makeRequestContext(ctx, http.MethodPost, uri, tsig)

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

	result := SecondaryDNSTSIGDetailResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result, nil
}

// UpdateSecondaryDNSTSIG updates an existing secondary DNS TSIG at
// the account level.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--update-tsig
func (api *API) UpdateSecondaryDNSTSIG(ctx context.Context, accountID string, tsig SecondaryDNSTSIG) (SecondaryDNSTSIG, error) {
	if tsig.ID == "" {
		return SecondaryDNSTSIG{}, errors.New(errSecondaryDNSTSIGMissingID)
	}

	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsig.ID)
	res, err := api.makeRequestContext(ctx, http.MethodPut, uri, tsig)

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

	result := SecondaryDNSTSIGDetailResponse{}
	if err := json.Unmarshal(res, &result); err != nil {
		return SecondaryDNSTSIG{}, errors.Wrap(err, errUnmarshalError)
	}

	return result.Result, nil
}

// DeleteSecondaryDNSTSIG deletes a secondary DNS TSIG.
//
// API reference: https://api.cloudflare.com/#secondary-dns-tsig--delete-tsig
func (api *API) DeleteSecondaryDNSTSIG(ctx context.Context, accountID, tsigID string) error {
	uri := fmt.Sprintf("/accounts/%s/secondary_dns/tsigs/%s", accountID, tsigID)
	_, err := api.makeRequestContext(ctx, http.MethodDelete, uri, nil)

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var queryRegexp = regexp.MustCompile(`^([\w]+)\((.*)\)`)

type QueryParseError struct {
	StatusCode int
	Message    string
}

func (qpe QueryParseError) Error() string {
	return qpe.Message
}

func parseQuery(queryStr string) (readFilters, error) {
	query := newReadFilters()

	if strings.HasPrefix(queryStr, "AND") || strings.HasPrefix(queryStr, "and") ||
		strings.HasPrefix(queryStr, "OR") || strings.HasPrefix(queryStr, "or") {
		// Format: and([key, comparitor, value],[key, comparitor, value] ...)
		//          or([key, comparitor, value],[key, comparitor, value] ...)
		log.Debugf("Starts with AND/OR")
		matches := queryRegexp.FindStringSubmatch(queryStr)
		if matches != nil {
			if len(matches) != 3 {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    "Invalid query format",
				}
			}

			var filtersStr string
			if strings.HasPrefix(matches[2], "[[") {
				filtersStr = matches[2]
			} else if strings.HasPrefix(matches[2], "[") {
				filtersStr = "[" + matches[2] + "]"
			} else {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    "Invalid query filter format",
				}
			}

			var filters []interface{}
			err := json.Unmarshal([]byte(filtersStr), &filters)
			if err != nil {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    err.Error(),
				}
			}

			queryData := map[string]interface{}{
				"logical_operator": matches[1],
				"conditions":       filters,
			}

			err = mapToReadFilters(queryData, &query)

			return query, err

		} else {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Invalid query format",
			}
		}

	} else if strings.HasPrefix(queryStr, "{") {
		// Format: {"logical_operator": "and", "conditions": [[key, comparitor, value], ...]}
		log.Debugf("Is JSON hash")
		var queryData map[string]interface{}
		err := json.Unmarshal([]byte(queryStr), &queryData)
		if err != nil {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    err.Error(),
			}
		}

		if _, ok := queryData["logical_operator"]; !ok {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Missing key: 'logical_operator'",
			}
		}

		if _, ok := queryData["conditions"]; !ok {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Missing key: 'conditions'",
			}
		}
		err = mapToReadFilters(queryData, &query)

		return query, err

	} else if strings.HasPrefix(queryStr, "[[") {
		// Format: [[key, comparitor, value], ...]
		log.Debugf("Is JSON array of arrays")
		var filters []interface{}
		err := json.Unmarshal([]byte(queryStr), &filters)
		if err != nil {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    err.Error(),
			}
		}
		queryData := map[string]interface{}{
			"logical_operator": "add",
			"conditions":       filters,
		}
		err = mapToReadFilters(queryData, &query)
		return query, err

	}

	return query, QueryParseError{
		StatusCode: http.StatusBadRequest,
		Message:    "Invalid query format",
	}

}

func mapToReadFilters(queryMap map[string]interface{}, query *readFilters) error {

	op, ok := queryMap["logical_operator"]
	if !ok {
		return QueryParseError{
			StatusCode: http.StatusInternalServerError,
			Message:    "Missing key: 'logical_operator'",
		}
	}

	conditions, ok := queryMap["conditions"]
	if !ok {
		return QueryParseError{
			StatusCode: http.StatusInternalServerError,
			Message:    "Missing key: 'conditions'",
		}
	}

	query.LogicalOperator = strings.ToLower(op.(string))

	log.Debugf("Conditions: %v", conditions)
	for _, condition := range conditions.([]interface{}) {
		filter := condition.([]interface{})
		log.Debugf("Filter: %v", filter)
		cond := arrayToQueryCondition(filter)
		log.Debugf("Condition: %v", cond)
		query.AddCondition(cond)
	}
	return nil
}

func arrayToQueryCondition(filter []interface{}) queryCondition {
	return newQueryCondition(
		filter[0].(string), // name
		filter[1].(string), // relation
		filter[2],          // values
	)
}
<commit_msg>Fixed typo in query for array queries. Fixes #1<commit_after>package main

import (
	"encoding/json"
	"net/http"
	"regexp"
	"strings"

	log "github.com/Sirupsen/logrus"
)

var queryRegexp = regexp.MustCompile(`^([\w]+)\((.*)\)`)

type QueryParseError struct {
	StatusCode int
	Message    string
}

func (qpe QueryParseError) Error() string {
	return qpe.Message
}

func parseQuery(queryStr string) (readFilters, error) {
	query := newReadFilters()

	if strings.HasPrefix(queryStr, "AND") || strings.HasPrefix(queryStr, "and") ||
		strings.HasPrefix(queryStr, "OR") || strings.HasPrefix(queryStr, "or") {
		// Format: and([key, comparitor, value],[key, comparitor, value] ...)
		//          or([key, comparitor, value],[key, comparitor, value] ...)
		log.Debugf("Starts with AND/OR")
		matches := queryRegexp.FindStringSubmatch(queryStr)
		if matches != nil {
			if len(matches) != 3 {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    "Invalid query format",
				}
			}

			var filtersStr string
			if strings.HasPrefix(matches[2], "[[") {
				filtersStr = matches[2]
			} else if strings.HasPrefix(matches[2], "[") {
				filtersStr = "[" + matches[2] + "]"
			} else {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    "Invalid query filter format",
				}
			}

			var filters []interface{}
			err := json.Unmarshal([]byte(filtersStr), &filters)
			if err != nil {
				return query, QueryParseError{
					StatusCode: http.StatusBadRequest,
					Message:    err.Error(),
				}
			}

			queryData := map[string]interface{}{
				"logical_operator": matches[1],
				"conditions":       filters,
			}

			err = mapToReadFilters(queryData, &query)

			return query, err

		} else {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Invalid query format",
			}
		}

	} else if strings.HasPrefix(queryStr, "{") {
		// Format: {"logical_operator": "and", "conditions": [[key, comparitor, value], ...]}
		log.Debugf("Is JSON hash")
		var queryData map[string]interface{}
		err := json.Unmarshal([]byte(queryStr), &queryData)
		if err != nil {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    err.Error(),
			}
		}

		if _, ok := queryData["logical_operator"]; !ok {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Missing key: 'logical_operator'",
			}
		}

		if _, ok := queryData["conditions"]; !ok {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    "Missing key: 'conditions'",
			}
		}
		err = mapToReadFilters(queryData, &query)

		return query, err

	} else if strings.HasPrefix(queryStr, "[[") {
		// Format: [[key, comparitor, value], ...]
		log.Debugf("Is JSON array of arrays")
		var filters []interface{}
		err := json.Unmarshal([]byte(queryStr), &filters)
		if err != nil {
			return query, QueryParseError{
				StatusCode: http.StatusBadRequest,
				Message:    err.Error(),
			}
		}
		queryData := map[string]interface{}{
			"logical_operator": "and",
			"conditions":       filters,
		}
		err = mapToReadFilters(queryData, &query)
		return query, err

	}

	return query, QueryParseError{
		StatusCode: http.StatusBadRequest,
		Message:    "Invalid query format",
	}

}

func mapToReadFilters(queryMap map[string]interface{}, query *readFilters) error {

	op, ok := queryMap["logical_operator"]
	if !ok {
		return QueryParseError{
			StatusCode: http.StatusInternalServerError,
			Message:    "Missing key: 'logical_operator'",
		}
	}

	conditions, ok := queryMap["conditions"]
	if !ok {
		return QueryParseError{
			StatusCode: http.StatusInternalServerError,
			Message:    "Missing key: 'conditions'",
		}
	}

	query.LogicalOperator = strings.ToLower(op.(string))

	log.Debugf("Conditions: %v", conditions)
	for _, condition := range conditions.([]interface{}) {
		filter := condition.([]interface{})
		log.Debugf("Filter: %v", filter)
		cond := arrayToQueryCondition(filter)
		log.Debugf("Condition: %v", cond)
		query.AddCondition(cond)
	}
	return nil
}

func arrayToQueryCondition(filter []interface{}) queryCondition {
	return newQueryCondition(
		filter[0].(string), // name
		filter[1].(string), // relation
		filter[2],          // values
	)
}
<|endoftext|>


<commit_before>// Copyright 2015 Dave Gradwell
// Under BSD-style license (see LICENSE file)

package ff

import (
	"os/exec"
)

// Used for faking it to test the below function
type CommandInterface interface {
	Output() ([]byte, error)
}

// Used for faking it to test the below function
type CommandFunc func(string, ...string) CommandInterface

// Used for faking it to test the below function
var DefaultCommandFunc CommandFunc = func(name string, arg ...string) CommandInterface {
	return exec.Command(name, arg...)
}

// Actually calls ffprobe on a file and returns a ProbeInfo object
func Probe(path string) (info *ProbeInfo, err error) {
	input := NewInput(
		path,
		NewParamSet(
			NewParam("v", "quiet"),
			NewParam("print_format", "json"),
			NewParam("show_format", nil),
			NewParam("show_streams", nil),
		),
	)

	cmdline, err := NewCommand("ffprobe", input)
	if err != nil {
		return
	}

	var cmd CommandInterface
	cmd = DefaultCommandFunc(cmdline.Path, cmdline.Slice()...)
	out, err := cmd.Output()
	if err != nil {
		return
	}

	return NewInfo(string(out))
}
<commit_msg>info_probe: clarify comments.<commit_after>// Copyright 2015 Dave Gradwell
// Under BSD-style license (see LICENSE file)

package ff

import (
	"os/exec"
)

// Used for faking exec.Cmd.Output() to test Probe()
type CommandInterface interface {
	Output() ([]byte, error)
}

// Used for faking exec.Command() to test Probe()
type CommandFunc func(string, ...string) CommandInterface

// Used for faking exec.Command() to test Probe()
var DefaultCommandFunc CommandFunc = func(name string, arg ...string) CommandInterface {
	return exec.Command(name, arg...)
}

// Actually calls ffprobe on a file and returns a ProbeInfo object
func Probe(path string) (info *ProbeInfo, err error) {
	input := NewInput(
		path,
		NewParamSet(
			NewParam("v", "quiet"),
			NewParam("print_format", "json"),
			NewParam("show_format", nil),
			NewParam("show_streams", nil),
		),
	)

	cmdline, err := NewCommand("ffprobe", input)
	if err != nil {
		return
	}

	var cmd CommandInterface
	cmd = DefaultCommandFunc(cmdline.Path, cmdline.Slice()...)
	out, err := cmd.Output()
	if err != nil {
		return
	}

	return NewInfo(string(out))
}
<|endoftext|>


<commit_before>package backuper

import (
	"fmt"
	"github.com/mwf/golidays/model"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/mwf/golidays/service/logger"
	"github.com/mwf/golidays/service/store"
	"gopkg.in/yaml.v2"
)

const (
	// no reason to keep it higher
	minPeriod = time.Minute
)

type Backuper struct {
	storage store.Store
	period  time.Duration
	logger  logger.Logger

	basePath string
	files    *stringStack

	runOnce sync.Once
	done    chan struct{}
}

// New returns new backuper instance
func New(storage store.Store, period time.Duration, basePath string, maxBackups int, log logger.Logger) (*Backuper, error) {
	if period < minPeriod {
		return nil, fmt.Errorf("period is too low: %s < %s", period, minPeriod)
	}
	if maxBackups < 0 {
		return nil, fmt.Errorf("too few backups: %d", maxBackups)
	}

	info, err := os.Stat(basePath)
	if err != nil {
		return nil, err
	}
	if !info.IsDir() {
		return nil, fmt.Errorf("'%s' is not a directory", basePath)
	}

	b := &Backuper{
		storage:  storage,
		period:   period,
		logger:   log,
		basePath: basePath,
		files:    newStringStack(maxBackups),
		done:     make(chan struct{}),
	}

	b.restoreList()
	return b, nil
}

func (b *Backuper) String() string {
	return fmt.Sprintf("Backuper {period: %s}", b.period)
}

// Run runs asynchronous update loop. Multiple calls do nothing - the loop started
// exactly once.
func (b *Backuper) Run() {
	b.runOnce.Do(func() {
		go b.loop()
	})
}

// Stop stops update loop.
func (b *Backuper) Stop() {
	b.runOnce.Do(func() {
		close(b.done)
	})
}

// RestoreStorage restores storage from the last backup
func (b *Backuper) RestoreStorage() error {
	lastBackupPath := b.files.Head()
	if lastBackupPath == "" {
		return fmt.Errorf("no backup files")
	}
	b.logger.Debugf("restoring last backup from '%s'", lastBackupPath)

	bytes, err := ioutil.ReadFile(lastBackupPath)
	if err != nil {
		return fmt.Errorf("error reading backup '%s': %s", lastBackupPath, err)
	}

	if err := b.restoreData(bytes); err != nil {
		return err
	}

	b.logger.Infof("backup '%s' restored OK", lastBackupPath)
	return nil
}

func (b *Backuper) restoreData(bytes []byte) error {
	holidays := make(model.Holidays, 0)
	if err := yaml.Unmarshal(bytes, &holidays); err != nil {
		return fmt.Errorf("error unmarshaling data: %s", err)
	}

	if err := b.storage.Restore(holidays); err != nil {
		return fmt.Errorf("error restoring storage: %s", err)
	}
	return nil
}

func (b *Backuper) restoreList() {
	// try to restore backup list
	pattern := filepath.Join(b.basePath, "holidays.*\\.yml")
	matches, err := filepath.Glob(pattern)
	if err != nil {
		b.logger.Warningf("backups list restore failed: %s", err)
	}

	b.logger.Debugf("found backups: %s", matches)
	sort.Strings(matches)

	for _, fpath := range matches {
		b.preserveFile(fpath)
	}
}

func (b *Backuper) loop() {
	b.logger.Infof("%s started", b)
	defer b.logger.Infof("%s stopped", b)

	for {
		select {
		case <-time.After(b.period):
			if err := b.perform(); err != nil {
				b.logger.Error(err.Error())
			}
		case <-b.done:
			return
		}
	}
}

func (b *Backuper) perform() error {
	startedAt := time.Now()

	b.logger.Debugf("perform %s", b)
	defer func() {
		b.logger.Infof("perform finished in %s", time.Now().Sub(startedAt))
	}()

	filename := b.generateBackupName()
	fpath := filepath.Join(b.basePath, filename)
	f, err := os.Create(fpath)
	if err != nil {
		return fmt.Errorf("os.Create error: %s", err)
	}
	defer f.Close()

	if err := b.collectAndWrite(f); err != nil {
		// remove file on error
		os.Remove(fpath)
		return err
	}

	b.preserveFile(fpath)
	return nil
}

func (b *Backuper) generateBackupName() string {
	dt := time.Now().Format("2006-01-02T03:04")
	return fmt.Sprintf("holidays.%s.yml", dt)
}

func (b *Backuper) collectAndWrite(f *os.File) error {
	holidays := b.storage.Dump()
	sort.Sort(model.HolidaysByDate(holidays))

	bytes, err := yaml.Marshal(holidays)
	if err != nil {
		return fmt.Errorf("error marshaling data: %s", err)
	}
	if _, err := f.Write(bytes); err != nil {
		return fmt.Errorf("error writing data: %s", err)
	}
	return nil
}

func (b *Backuper) preserveFile(fpath string) {
	// put new file in list and remove purged, if any
	name, purged := b.files.Put(fpath)
	if purged {
		if err := os.Remove(name); err != nil {
			b.logger.Warningf("failed to purge '%s': %s", name, err)
		}
	}
}
<commit_msg>fix backuper file format<commit_after>package backuper

import (
	"fmt"
	"github.com/mwf/golidays/model"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"sync"
	"time"

	"github.com/mwf/golidays/service/logger"
	"github.com/mwf/golidays/service/store"
	"gopkg.in/yaml.v2"
)

const (
	// no reason to keep it higher
	minPeriod = time.Minute
)

type Backuper struct {
	storage store.Store
	period  time.Duration
	logger  logger.Logger

	basePath string
	files    *stringStack

	runOnce sync.Once
	done    chan struct{}
}

// New returns new backuper instance
func New(storage store.Store, period time.Duration, basePath string, maxBackups int, log logger.Logger) (*Backuper, error) {
	if period < minPeriod {
		return nil, fmt.Errorf("period is too low: %s < %s", period, minPeriod)
	}
	if maxBackups < 0 {
		return nil, fmt.Errorf("too few backups: %d", maxBackups)
	}

	info, err := os.Stat(basePath)
	if err != nil {
		return nil, err
	}
	if !info.IsDir() {
		return nil, fmt.Errorf("'%s' is not a directory", basePath)
	}

	b := &Backuper{
		storage:  storage,
		period:   period,
		logger:   log,
		basePath: basePath,
		files:    newStringStack(maxBackups),
		done:     make(chan struct{}),
	}

	b.restoreList()
	return b, nil
}

func (b *Backuper) String() string {
	return fmt.Sprintf("Backuper {period: %s}", b.period)
}

// Run runs asynchronous update loop. Multiple calls do nothing - the loop started
// exactly once.
func (b *Backuper) Run() {
	b.runOnce.Do(func() {
		go b.loop()
	})
}

// Stop stops update loop.
func (b *Backuper) Stop() {
	b.runOnce.Do(func() {
		close(b.done)
	})
}

// RestoreStorage restores storage from the last backup
func (b *Backuper) RestoreStorage() error {
	lastBackupPath := b.files.Head()
	if lastBackupPath == "" {
		return fmt.Errorf("no backup files")
	}
	b.logger.Debugf("restoring last backup from '%s'", lastBackupPath)

	bytes, err := ioutil.ReadFile(lastBackupPath)
	if err != nil {
		return fmt.Errorf("error reading backup '%s': %s", lastBackupPath, err)
	}

	if err := b.restoreData(bytes); err != nil {
		return err
	}

	b.logger.Infof("backup '%s' restored OK", lastBackupPath)
	return nil
}

func (b *Backuper) restoreData(bytes []byte) error {
	holidays := make(model.Holidays, 0)
	if err := yaml.Unmarshal(bytes, &holidays); err != nil {
		return fmt.Errorf("error unmarshaling data: %s", err)
	}

	if err := b.storage.Restore(holidays); err != nil {
		return fmt.Errorf("error restoring storage: %s", err)
	}
	return nil
}

func (b *Backuper) restoreList() {
	// try to restore backup list
	pattern := filepath.Join(b.basePath, "holidays.*\\.yml")
	matches, err := filepath.Glob(pattern)
	if err != nil {
		b.logger.Warningf("backups list restore failed: %s", err)
	}

	b.logger.Debugf("found backups: %s", matches)
	sort.Strings(matches)

	for _, fpath := range matches {
		b.preserveFile(fpath)
	}
}

func (b *Backuper) loop() {
	b.logger.Infof("%s started", b)
	defer b.logger.Infof("%s stopped", b)

	for {
		select {
		case <-time.After(b.period):
			if err := b.perform(); err != nil {
				b.logger.Error(err.Error())
			}
		case <-b.done:
			return
		}
	}
}

func (b *Backuper) perform() error {
	startedAt := time.Now()

	b.logger.Debugf("perform %s", b)
	defer func() {
		b.logger.Infof("perform finished in %s", time.Now().Sub(startedAt))
	}()

	filename := b.generateBackupName()
	fpath := filepath.Join(b.basePath, filename)
	f, err := os.Create(fpath)
	if err != nil {
		return fmt.Errorf("os.Create error: %s", err)
	}
	defer f.Close()

	if err := b.collectAndWrite(f); err != nil {
		// remove file on error
		os.Remove(fpath)
		return err
	}

	b.preserveFile(fpath)
	return nil
}

func (b *Backuper) generateBackupName() string {
	dt := time.Now().Format("2006-01-02T13:04")
	return fmt.Sprintf("holidays.%s.yml", dt)
}

func (b *Backuper) collectAndWrite(f *os.File) error {
	holidays := b.storage.Dump()
	sort.Sort(model.HolidaysByDate(holidays))

	bytes, err := yaml.Marshal(holidays)
	if err != nil {
		return fmt.Errorf("error marshaling data: %s", err)
	}
	if _, err := f.Write(bytes); err != nil {
		return fmt.Errorf("error writing data: %s", err)
	}
	return nil
}

func (b *Backuper) preserveFile(fpath string) {
	// put new file in list and remove purged, if any
	name, purged := b.files.Put(fpath)
	if purged {
		if err := os.Remove(name); err != nil {
			b.logger.Warningf("failed to purge '%s': %s", name, err)
		}
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Intel Corporation
 *
 * 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 service

import (
	"fmt"
	"strings"

	log "github.com/cihub/seelog"
	"github.com/cloudfoundry-community/types-cf"
	"github.com/trustedanalytics/application-broker/cloud"
	"github.com/trustedanalytics/application-broker/dao"
	"github.com/trustedanalytics/application-broker/messagebus"
	"github.com/trustedanalytics/application-broker/misc"
	"github.com/trustedanalytics/application-broker/types"
)

// LaunchingService wraps access to db, cloud controller and messagebus
type LaunchingService struct {
	db         dao.Facade
	cloud      cloud.API
	msgBus     messagebus.MessageBus
	msgFactory messagebus.MessageFactory
}

// New creates an instance of the LaunchingService
func New(db dao.Facade, cloud cloud.API, natsInstance messagebus.MessageBus, messageFactory messagebus.MessageFactory) *LaunchingService {
	s := &LaunchingService{
		db:         db,
		cloud:      cloud,
		msgBus:     natsInstance,
		msgFactory: messageFactory,
	}
	return s
}

// InsertToCatalog adds new application description that can be spawned/duplicated on demand
// Description is stored in underlying implementation of Catalog interface
func (p *LaunchingService) InsertToCatalog(svc *types.ServiceExtension) error {
	if !types.Validate(svc) {
		return misc.InvalidInputError{}
	}

	if err := p.db.Append(svc); err != nil {
		return err
	}

	if err := p.cloud.CheckIfServiceExists(svc.Name); err != nil {
		p.db.Remove(svc.ID)
		return err
	}
	if err := p.UpdateBroker(); err != nil {
		p.db.Remove(svc.ID)
		return err
	}

	return nil
}

// UpdateCatalog update application description that can be spawned/duplicated on demand
// Description is stored in underlying implementation of Catalog interface
func (p *LaunchingService) UpdateCatalog(svc *types.ServiceExtension) error {
	if !types.Validate(svc) {
		return misc.InvalidInputError{}
	}
	if err := p.db.Update(svc); err != nil {
		return err
	}

	if err := p.UpdateBroker(); err != nil {
		return err
	}

	return nil
}

// DeleteFromCatalog deletes data pointing to reference application from internal storage
func (p *LaunchingService) DeleteFromCatalog(serviceID string) error {
	if _, err := p.db.Find(serviceID); err != nil {
		return err
	}

	services, err := p.db.Get()
	if err != nil {
		return err
	}
	if len(services) <= 1 {
		return misc.InternalServerError{Context: "Cannot delete the only service offering. Catalog cannot be empty."}
	}

	hasInstances, err := p.db.HasInstancesOf(serviceID)
	if err != nil {
		return err
	}

	if hasInstances {
		return misc.ExistingInstancesError{}
	}

	if err := p.db.Remove(serviceID); err != nil {
		return err
	}

	if err := p.UpdateBroker(); err != nil {
		return err
	}

	return nil
}

// GetCatalog parses catalog response
func (p *LaunchingService) GetCatalog() (*types.CatalogExtension, error) {
	log.Debug("getting catalog...")

	var err error
	toReturn := types.CatalogExtension{}
	toReturn.Services, err = p.db.Get()

	if err != nil {
		return nil, err
	}
	return &toReturn, nil
}

func (p *LaunchingService) DryRun(sourceAppGUID string) ([]types.Component, error) {
	resp, err := p.cloud.DryRun(sourceAppGUID)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// CreateService creates a service instance
func (p *LaunchingService) CreateService(r *cf.ServiceCreationRequest) (*cf.ServiceCreationResponse, error) {
	service, err := p.db.Find(r.ServiceID)
	if err != nil {
		return nil, err
	}

	if r.Parameters["name"] == "" {
		r.Parameters["name"] = service.Name
	}

	idx := strings.Index(r.InstanceID, "-")
	if idx > 0 {
		// Take only first part of the GUID
		r.Parameters["name"] = r.Parameters["name"] + "-" + r.InstanceID[0:idx]
	}

	name := r.Parameters["name"]
	log.Infof("create service: [%v]", name)

	stype := service.Name
	org := r.OrganizationGUID

	msg := p.msgFactory.NewServiceStatus(name, stype, org, "CreateService operation started")
	p.msgBus.Publish(msg)

	//TODO: instead of referenceApp.GUID we should pass entire app object
	resp, err := p.cloud.Provision(service.ReferenceApp.Meta.GUID, r)
	if err != nil {
		msg = p.msgFactory.NewServiceStatus(name, stype, org, "Service spawning failed with error: "+err.Error())
		p.msgBus.Publish(msg)
		return nil, err
	}
	msg = p.msgFactory.NewServiceStatus(name, stype, org, "Service spawning succeded")
	p.msgBus.Publish(msg)

	toAppend := types.ServiceInstanceExtension{
		App:       resp.App,
		ID:        r.InstanceID,
		ServiceID: r.ServiceID,
	}
	if err := p.db.AppendInstance(toAppend); err != nil {
		return nil, err
	}
	return &resp.ServiceCreationResponse, nil
}

// DeleteService deletes service instance and its dependencies
func (p *LaunchingService) DeleteService(instanceID string) error {
	log.Debugf("Deleting service %s...", instanceID)

	service, err := p.db.FindInstance(instanceID)
	if err != nil {
		return err
	}

	if err := p.cloud.Deprovision(service.App.Meta.GUID); err != nil {
		return err
	}
	p.db.RemoveInstance(service.ID)
	return nil
}

// BindService creates a (service instance <-> application) binding
func (p *LaunchingService) BindService(r *cf.ServiceBindingRequest) (*types.ServiceBindingResponse, error) {
	instance, err := p.db.FindInstance(r.InstanceID)
	if err != nil {
		return nil, err
	}

	resp := new(types.ServiceBindingResponse)
	resp.Credentials = make(map[string]string)
	resp.Credentials["url"] = instance.App.Meta.URL
	return resp, nil
}

func (p *LaunchingService) UpdateBroker() error {
	vcap := types.GetVcapApplication()
	username := misc.GetEnvVarAsString("AUTH_USER", "")
	password := misc.GetEnvVarAsString("AUTH_PASS", "")

	if len(vcap.Name) == 0 {
		return misc.InternalServerError{Context: "Application name is not set"}
	}

	if len(vcap.Uris) == 0 || len(vcap.Uris[0]) == 0 {
		return misc.InternalServerError{Context: "Application has no url set"}
	}
	url := fmt.Sprintf("http://%v", vcap.Uris[0])

	return p.cloud.UpdateBroker(vcap.Name, url, username, password)
}
<commit_msg>Removed generated part from service name instance to match it with input name.<commit_after>/**
 * Copyright (c) 2015 Intel Corporation
 *
 * 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 service

import (
	"fmt"
	log "github.com/cihub/seelog"
	"github.com/cloudfoundry-community/types-cf"
	"github.com/trustedanalytics/application-broker/cloud"
	"github.com/trustedanalytics/application-broker/dao"
	"github.com/trustedanalytics/application-broker/messagebus"
	"github.com/trustedanalytics/application-broker/misc"
	"github.com/trustedanalytics/application-broker/types"
)

// LaunchingService wraps access to db, cloud controller and messagebus
type LaunchingService struct {
	db         dao.Facade
	cloud      cloud.API
	msgBus     messagebus.MessageBus
	msgFactory messagebus.MessageFactory
}

// New creates an instance of the LaunchingService
func New(db dao.Facade, cloud cloud.API, natsInstance messagebus.MessageBus, messageFactory messagebus.MessageFactory) *LaunchingService {
	s := &LaunchingService{
		db:         db,
		cloud:      cloud,
		msgBus:     natsInstance,
		msgFactory: messageFactory,
	}
	return s
}

// InsertToCatalog adds new application description that can be spawned/duplicated on demand
// Description is stored in underlying implementation of Catalog interface
func (p *LaunchingService) InsertToCatalog(svc *types.ServiceExtension) error {
	if !types.Validate(svc) {
		return misc.InvalidInputError{}
	}

	if err := p.db.Append(svc); err != nil {
		return err
	}

	if err := p.cloud.CheckIfServiceExists(svc.Name); err != nil {
		p.db.Remove(svc.ID)
		return err
	}
	if err := p.UpdateBroker(); err != nil {
		p.db.Remove(svc.ID)
		return err
	}

	return nil
}

// UpdateCatalog update application description that can be spawned/duplicated on demand
// Description is stored in underlying implementation of Catalog interface
func (p *LaunchingService) UpdateCatalog(svc *types.ServiceExtension) error {
	if !types.Validate(svc) {
		return misc.InvalidInputError{}
	}
	if err := p.db.Update(svc); err != nil {
		return err
	}

	if err := p.UpdateBroker(); err != nil {
		return err
	}

	return nil
}

// DeleteFromCatalog deletes data pointing to reference application from internal storage
func (p *LaunchingService) DeleteFromCatalog(serviceID string) error {
	if _, err := p.db.Find(serviceID); err != nil {
		return err
	}

	services, err := p.db.Get()
	if err != nil {
		return err
	}
	if len(services) <= 1 {
		return misc.InternalServerError{Context: "Cannot delete the only service offering. Catalog cannot be empty."}
	}

	hasInstances, err := p.db.HasInstancesOf(serviceID)
	if err != nil {
		return err
	}

	if hasInstances {
		return misc.ExistingInstancesError{}
	}

	if err := p.db.Remove(serviceID); err != nil {
		return err
	}

	if err := p.UpdateBroker(); err != nil {
		return err
	}

	return nil
}

// GetCatalog parses catalog response
func (p *LaunchingService) GetCatalog() (*types.CatalogExtension, error) {
	log.Debug("getting catalog...")

	var err error
	toReturn := types.CatalogExtension{}
	toReturn.Services, err = p.db.Get()

	if err != nil {
		return nil, err
	}
	return &toReturn, nil
}

func (p *LaunchingService) DryRun(sourceAppGUID string) ([]types.Component, error) {
	resp, err := p.cloud.DryRun(sourceAppGUID)
	if err != nil {
		return nil, err
	}
	return resp, nil
}

// CreateService creates a service instance
func (p *LaunchingService) CreateService(r *cf.ServiceCreationRequest) (*cf.ServiceCreationResponse, error) {
	service, err := p.db.Find(r.ServiceID)
	if err != nil {
		return nil, err
	}

	if r.Parameters["name"] == "" {
		r.Parameters["name"] = service.Name
	}

	name := r.Parameters["name"]
	log.Infof("create service: [%v]", name)

	stype := service.Name
	org := r.OrganizationGUID

	msg := p.msgFactory.NewServiceStatus(name, stype, org, "CreateService operation started")
	p.msgBus.Publish(msg)

	//TODO: instead of referenceApp.GUID we should pass entire app object
	resp, err := p.cloud.Provision(service.ReferenceApp.Meta.GUID, r)
	if err != nil {
		msg = p.msgFactory.NewServiceStatus(name, stype, org, "Service spawning failed with error: "+err.Error())
		p.msgBus.Publish(msg)
		return nil, err
	}
	msg = p.msgFactory.NewServiceStatus(name, stype, org, "Service spawning succeded")
	p.msgBus.Publish(msg)

	toAppend := types.ServiceInstanceExtension{
		App:       resp.App,
		ID:        r.InstanceID,
		ServiceID: r.ServiceID,
	}
	if err := p.db.AppendInstance(toAppend); err != nil {
		return nil, err
	}
	return &resp.ServiceCreationResponse, nil
}

// DeleteService deletes service instance and its dependencies
func (p *LaunchingService) DeleteService(instanceID string) error {
	log.Debugf("Deleting service %s...", instanceID)

	service, err := p.db.FindInstance(instanceID)
	if err != nil {
		return err
	}

	if err := p.cloud.Deprovision(service.App.Meta.GUID); err != nil {
		return err
	}
	p.db.RemoveInstance(service.ID)
	return nil
}

// BindService creates a (service instance <-> application) binding
func (p *LaunchingService) BindService(r *cf.ServiceBindingRequest) (*types.ServiceBindingResponse, error) {
	instance, err := p.db.FindInstance(r.InstanceID)
	if err != nil {
		return nil, err
	}

	resp := new(types.ServiceBindingResponse)
	resp.Credentials = make(map[string]string)
	resp.Credentials["url"] = instance.App.Meta.URL
	return resp, nil
}

func (p *LaunchingService) UpdateBroker() error {
	vcap := types.GetVcapApplication()
	username := misc.GetEnvVarAsString("AUTH_USER", "")
	password := misc.GetEnvVarAsString("AUTH_PASS", "")

	if len(vcap.Name) == 0 {
		return misc.InternalServerError{Context: "Application name is not set"}
	}

	if len(vcap.Uris) == 0 || len(vcap.Uris[0]) == 0 {
		return misc.InternalServerError{Context: "Application has no url set"}
	}
	url := fmt.Sprintf("http://%v", vcap.Uris[0])

	return p.cloud.UpdateBroker(vcap.Name, url, username, password)
}
<|endoftext|>