<commit_before>package loader

import (
	// Stdlib
	"encoding/json"
	"fmt"
	"os"

	// Internal
	"github.com/salsaflow/salsaflow/config"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"

	// Vendor
	"github.com/fatih/color"
)

// BootstrapConfig can be used to bootstrap SalsaFlow configuration
// according to the given configuration specification.
func BootstrapConfig(spec ConfigSpec) error {
	if err := bootstrapGlobalConfig(spec); err != nil {
		return err
	}
	return bootstrapLocalConfig(spec)
}

// LoadConfig can be used to load SalsaFlow configuration
// according to the given configuration specification.
//
// The main difference between BootstrapConfig and LoadConfig is that
// LoadConfig returns an error when the local configuration is not valid.
// While `repo bootstrap` is using BootstrapConfig, all other modules
// and commands should be using LoadConfig. The local configuration file
// is only supposed to be generated once during `repo bootstrap`.
func LoadConfig(spec ConfigSpec) (err error) {
	if err := bootstrapGlobalConfig(spec); err != nil {
		return err
	}
	return loadLocalConfig(spec)
}

func bootstrapGlobalConfig(spec ConfigSpec) error {
	// Run the common loading function with the right arguments.
	task := "Bootstrap global config according to the spec"
	if err := load(&loadArgs{
		configKind:      "global",
		configKey:       spec.ConfigKey(),
		configContainer: spec.GlobalConfig(),
		readConfig:      readGlobalConfig,
		emptyConfig:     emptyGlobalConfig,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

func bootstrapLocalConfig(spec ConfigSpec) error {
	task := "Bootstrap local config according to the spec"

	// Some handy variables.
	configKey := spec.ConfigKey()
	container := spec.LocalConfig()
	moduleSpec, isModuleSpec := spec.(ModuleConfigSpec)

	// Pre-read local config.
	// It is needed the pre-write hook as well.
	local, err := config.ReadLocalConfig()
	readConfig := func() (configFile, error) {
		return local, err
	}

	// Handle module config specs a bit differently.
	var preWriteHook func() (bool, error)
	if isModuleSpec {
		// Make sure the config container is not nil in case this is a module config.
		// In case the local config container is nil, the pre-write hook is not executed
		// and the active module ID is not set, and that would be a problem.
		// Returning a nil local config container is a valid choice, but we still
		// need the pre-write hook to be executed to set the active module ID.
		if container == nil {
			container = newEmptyModuleConfigContainer(configKey, moduleSpec.ModuleKind())
		}

		// In case this is a module config spec, set the the pre-write hook
		// to modify the local config file to activate the module being configured.
		preWriteHook = func() (bool, error) {
			return SetActiveModule(local, moduleSpec.ModuleKind(), configKey)
		}
	}

	// The post-write hook simply tells the user to commit the local config file.
	postWriteHook := func() error {
		fmt.Println()
		log.Warn("Local configuration file modified, please commit it.")
		return nil
	}

	// Run the common loading function with the right arguments.
	if err := load(&loadArgs{
		configKind:      "local",
		configKey:       configKey,
		configContainer: container,
		readConfig:      readConfig,
		emptyConfig:     emptyLocalConfig,
		preWriteHook:    preWriteHook,
		postWriteHook:   postWriteHook,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

func loadLocalConfig(spec ConfigSpec) error {
	// Run the common loading function with the right arguments.
	// Setting disallowPrompt to true makes the function return an error
	// in case the config record is not valid instead of running the dialog.
	task := "Load local config according to the spec"
	if err := load(&loadArgs{
		configKind:      "local",
		configKey:       spec.ConfigKey(),
		configContainer: spec.LocalConfig(),
		readConfig:      readLocalConfig,
		disallowPrompt:  true,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

type configFile interface {
	ConfigRecord(configKey string) (*config.ConfigRecord, error)
	SetConfigRecord(configKey string, rawConfig []byte)
	SaveChanges() error
}

type loadArgs struct {
	configKind      string
	configKey       string
	configContainer ConfigContainer
	readConfig      func() (configFile, error)
	emptyConfig     func() configFile
	preWriteHook    func() (bool, error)
	postWriteHook   func() error
	disallowPrompt  bool
}

func load(args *loadArgs) error {
	// Save the args into regular variables.
	var (
		configKey      = args.configKey
		container      = args.configContainer
		readConfig     = args.readConfig
		emptyConfig    = args.emptyConfig
		disallowPrompt = args.disallowPrompt
	)

	// Do nothing in case the container is nil.
	if container == nil {
		return nil
	}

	// Read the configuration file.
	configFile, err := readConfig()
	if err != nil {
		if emptyConfig == nil || !os.IsNotExist(errs.RootCause(err)) {
			return err
		}
		// In case the file is not there, initialise a new one.
		configFile = emptyConfig()
	}

	prompt := func(err error) error {
		if disallowPrompt {
			return err
		}
		return promptAndWrite(configFile, args)
	}

	// Find the config record for the given key.
	// In case there is an error and the prompt is allowed, prompt the user.
	section, err := configFile.ConfigRecord(configKey)
	if err != nil {
		return prompt(err)
	}

	// Unmarshal the record according to the spec.
	// In case there is an error and the prompt is allowed, prompt the user.
	if err := unmarshal(section.RawConfig, container); err != nil {
		return prompt(err)
	}

	// Validate the returned object according to the spec.
	// In case there is an error and the prompt is allowed, prompt the user.
	if err := validate(container, section.Path()); err != nil {
		return prompt(err)
	}

	return nil
}

func promptAndWrite(configFile configFile, args *loadArgs) (err error) {
	// Save the args into regular variables.
	var (
		configKind    = args.configKind
		configKey     = args.configKey
		container     = args.configContainer
		preWriteHook  = args.preWriteHook
		postWriteHook = args.postWriteHook
	)

	// Tell the user what is happening.
	fmt.Println()
	color.Cyan("-----> Configuring '%v' (%v configuration)", configKey, configKind)
	fmt.Println()

	defer func() {
		// In case we are panicking, forward the panic.
		if r := recover(); r != nil {
			panic(r)
		}

		// Otherwise print the colored message.
		fmt.Println()
		if err == nil {
			color.Green("-----> Done")
		} else {
			color.Red("-----> Error")
		}
	}()

	// Prompt the user according to the spec.
	if err := container.PromptUserForConfig(); err != nil {
		return err
	}

	// Marshal the result.
	raw, err := marshal(container)
	if err != nil {
		return err
	}

	// Store the result in the config file object.
	var modified bool
	if raw != nil {
		configFile.SetConfigRecord(configKey, json.RawMessage(raw))
		modified = true
	}

	// Optionally run the pre-write hook.
	if preWriteHook != nil {
		changedConfig, err := preWriteHook()
		if err != nil {
			return err
		}
		modified = modified || changedConfig
	}

	// In case the config file hasn't been modified, we can return.
	if !modified {
		return nil
	}

	// Write the config file.
	if err := configFile.SaveChanges(); err != nil {
		return err
	}

	// Optionally run the post-write hook.
	if postWriteHook != nil {
		if err := postWriteHook(); err != nil {
			return err
		}
	}

	return nil
}

// unmarshal unmarshals data into container.
//
// In case the container implements Unmarshaller interface,
// the custom unmarshalling function is used. Otherwise data
// is simply unmarshalled into container.
func unmarshal(data []byte, container ConfigContainer) error {
	unmarshalFunc := func(v interface{}) error {
		return config.Unmarshal(data, v)
	}

	var err error
	unmarshaller, ok := container.(Unmarshaller)
	if ok {
		err = unmarshaller.Unmarshal(unmarshalFunc)
	} else {
		err = unmarshalFunc(container)
	}
	if err != nil {
		fmt.Println()
		log.Log("Failed to unmarshal configuration, will run the bootstrap dialog")
		log.NewLine(fmt.Sprintf("(err = %v)", err.Error()))
		return err
	}
	return nil
}

// marshal marshals container and returns the raw data.
//
// In case the container implements Marshaller interface,
// the custom marshalling function is used. Otherwise
// the container itself is simply marshalled directly.
func marshal(container ConfigContainer) ([]byte, error) {
	var (
		v   interface{}
		err error
	)
	marshaller, ok := container.(Marshaller)
	if ok {
		v, err = marshaller.Marshal()
	} else {
		v = container
	}
	if err != nil {
		return nil, err
	}

	return config.Marshal(v)
}

// validate validates the configuration stored in the container.
//
// In case the container implements Validator interface,
// the custom validation function is used. Otherwise config.EnsureValueFilled
// is used, which means that all exported fields must be set for the config
// to be treated as valid.
func validate(container ConfigContainer, sectionPath string) error {
	var err error
	validator, ok := container.(Validator)
	if ok {
		err = validator.Validate(sectionPath)
	} else {
		err = config.EnsureValueFilled(container, sectionPath)
	}
	if err != nil {
		fmt.Println()
		log.Log("Configuration section invalid, will run the bootstrap dialog")
		log.NewLine(fmt.Sprintf("(error = %v)", err.Error()))
		return err
	}
	return nil
}

func readLocalConfig() (configFile, error) {
	return config.ReadLocalConfig()
}

func emptyLocalConfig() configFile {
	return config.NewEmptyLocalConfig()
}

func readGlobalConfig() (configFile, error) {
	return config.ReadGlobalConfig()
}

func emptyGlobalConfig() configFile {
	return config.NewEmptyGlobalConfig()
}
<commit_msg>config/loader: Improve error messages<commit_after>package loader

import (
	// Stdlib
	"encoding/json"
	"fmt"
	"os"
	"strings"

	// Internal
	"github.com/salsaflow/salsaflow/config"
	"github.com/salsaflow/salsaflow/errs"
	"github.com/salsaflow/salsaflow/log"

	// Vendor
	"github.com/fatih/color"
)

// BootstrapConfig can be used to bootstrap SalsaFlow configuration
// according to the given configuration specification.
func BootstrapConfig(spec ConfigSpec) error {
	if err := bootstrapGlobalConfig(spec); err != nil {
		return err
	}
	return bootstrapLocalConfig(spec)
}

// LoadConfig can be used to load SalsaFlow configuration
// according to the given configuration specification.
//
// The main difference between BootstrapConfig and LoadConfig is that
// LoadConfig returns an error when the local configuration is not valid.
// While `repo bootstrap` is using BootstrapConfig, all other modules
// and commands should be using LoadConfig. The local configuration file
// is only supposed to be generated once during `repo bootstrap`.
func LoadConfig(spec ConfigSpec) (err error) {
	if err := bootstrapGlobalConfig(spec); err != nil {
		return err
	}
	return loadLocalConfig(spec)
}

func bootstrapGlobalConfig(spec ConfigSpec) error {
	// Run the common loading function with the right arguments.
	task := "Bootstrap global config according to the spec"
	if err := load(&loadArgs{
		configKind:      "global",
		configKey:       spec.ConfigKey(),
		configContainer: spec.GlobalConfig(),
		readConfig:      readGlobalConfig,
		emptyConfig:     emptyGlobalConfig,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

func bootstrapLocalConfig(spec ConfigSpec) error {
	task := "Bootstrap local config according to the spec"

	// Some handy variables.
	configKey := spec.ConfigKey()
	container := spec.LocalConfig()
	moduleSpec, isModuleSpec := spec.(ModuleConfigSpec)

	// Pre-read local config.
	// It is needed the pre-write hook as well.
	local, err := config.ReadLocalConfig()
	readConfig := func() (configFile, error) {
		return local, err
	}

	// Handle module config specs a bit differently.
	var preWriteHook func() (bool, error)
	if isModuleSpec {
		// Make sure the config container is not nil in case this is a module config.
		// In case the local config container is nil, the pre-write hook is not executed
		// and the active module ID is not set, and that would be a problem.
		// Returning a nil local config container is a valid choice, but we still
		// need the pre-write hook to be executed to set the active module ID.
		if container == nil {
			container = newEmptyModuleConfigContainer(configKey, moduleSpec.ModuleKind())
		}

		// In case this is a module config spec, set the the pre-write hook
		// to modify the local config file to activate the module being configured.
		preWriteHook = func() (bool, error) {
			return SetActiveModule(local, moduleSpec.ModuleKind(), configKey)
		}
	}

	// The post-write hook simply tells the user to commit the local config file.
	postWriteHook := func() error {
		fmt.Println()
		log.Warn("Local configuration file modified, please commit it.")
		return nil
	}

	// Run the common loading function with the right arguments.
	if err := load(&loadArgs{
		configKind:      "local",
		configKey:       configKey,
		configContainer: container,
		readConfig:      readConfig,
		emptyConfig:     emptyLocalConfig,
		preWriteHook:    preWriteHook,
		postWriteHook:   postWriteHook,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

func loadLocalConfig(spec ConfigSpec) error {
	// Run the common loading function with the right arguments.
	// Setting disallowPrompt to true makes the function return an error
	// in case the config record is not valid instead of running the dialog.
	task := "Load local config according to the spec"
	if err := load(&loadArgs{
		configKind:      "local",
		configKey:       spec.ConfigKey(),
		configContainer: spec.LocalConfig(),
		readConfig:      readLocalConfig,
		disallowPrompt:  true,
	}); err != nil {
		return errs.NewError(task, err)
	}
	return nil
}

type configFile interface {
	ConfigRecord(configKey string) (*config.ConfigRecord, error)
	SetConfigRecord(configKey string, rawConfig []byte)
	SaveChanges() error
}

type loadArgs struct {
	configKind      string
	configKey       string
	configContainer ConfigContainer
	readConfig      func() (configFile, error)
	emptyConfig     func() configFile
	preWriteHook    func() (bool, error)
	postWriteHook   func() error
	disallowPrompt  bool
}

func load(args *loadArgs) error {
	// Save the args into regular variables.
	var (
		configKind     = args.configKind
		configKey      = args.configKey
		container      = args.configContainer
		readConfig     = args.readConfig
		emptyConfig    = args.emptyConfig
		disallowPrompt = args.disallowPrompt
	)

	// Do nothing in case the container is nil.
	if container == nil {
		return nil
	}

	// Read the configuration file.
	configFile, err := readConfig()
	if err != nil {
		if emptyConfig == nil || !os.IsNotExist(errs.RootCause(err)) {
			return err
		}
		// In case the file is not there, initialise a new one.
		configFile = emptyConfig()
	}

	prompt := func(err error) error {
		if disallowPrompt {
			return err
		}
		return promptAndWrite(configFile, args)
	}

	// Find the config record for the given key.
	// In case there is an error and the prompt is allowed, prompt the user.
	section, err := configFile.ConfigRecord(configKey)
	if err != nil {
		return prompt(err)
	}

	// Unmarshal the record according to the spec.
	// In case there is an error and the prompt is allowed, prompt the user.
	if err := unmarshal(section.RawConfig, container); err != nil {
		fmt.Println()
		log.Log(fmt.Sprintf(
			"Failed to unmarshal %v configuration, will try to run the bootstrap dialog",
			configKind))
		log.NewLine(fmt.Sprintf("(err = %v)", err.Error()))
		return prompt(err)
	}

	// Validate the returned object according to the spec.
	// In case there is an error and the prompt is allowed, prompt the user.
	if err := validate(container, section.Path()); err != nil {
		fmt.Println()
		log.Log(fmt.Sprintf(
			"%v configuration section invalid, will try to run the bootstrap dialog",
			strings.Title(configKind)))
		log.NewLine(fmt.Sprintf("(error = %v)", err.Error()))
		return prompt(err)
	}

	return nil
}

func promptAndWrite(configFile configFile, args *loadArgs) (err error) {
	// Save the args into regular variables.
	var (
		configKind    = args.configKind
		configKey     = args.configKey
		container     = args.configContainer
		preWriteHook  = args.preWriteHook
		postWriteHook = args.postWriteHook
	)

	// Tell the user what is happening.
	fmt.Println()
	color.Cyan("-----> Configuring '%v' (%v configuration)", configKey, configKind)
	fmt.Println()

	defer func() {
		// In case we are panicking, forward the panic.
		if r := recover(); r != nil {
			panic(r)
		}

		// Otherwise print the colored message.
		fmt.Println()
		if err == nil {
			color.Green("-----> Done")
		} else {
			color.Red("-----> Error")
		}
	}()

	// Prompt the user according to the spec.
	if err := container.PromptUserForConfig(); err != nil {
		return err
	}

	// Marshal the result.
	raw, err := marshal(container)
	if err != nil {
		return err
	}

	// Store the result in the config file object.
	var modified bool
	if raw != nil {
		configFile.SetConfigRecord(configKey, json.RawMessage(raw))
		modified = true
	}

	// Optionally run the pre-write hook.
	if preWriteHook != nil {
		changedConfig, err := preWriteHook()
		if err != nil {
			return err
		}
		modified = modified || changedConfig
	}

	// In case the config file hasn't been modified, we can return.
	if !modified {
		return nil
	}

	// Write the config file.
	if err := configFile.SaveChanges(); err != nil {
		return err
	}

	// Optionally run the post-write hook.
	if postWriteHook != nil {
		if err := postWriteHook(); err != nil {
			return err
		}
	}

	return nil
}

// unmarshal unmarshals data into container.
//
// In case the container implements Unmarshaller interface,
// the custom unmarshalling function is used. Otherwise data
// is simply unmarshalled into container.
func unmarshal(data []byte, container ConfigContainer) error {
	unmarshalFunc := func(v interface{}) error {
		return config.Unmarshal(data, v)
	}

	var err error
	unmarshaller, ok := container.(Unmarshaller)
	if ok {
		err = unmarshaller.Unmarshal(unmarshalFunc)
	} else {
		err = unmarshalFunc(container)
	}
	return err
}

// marshal marshals container and returns the raw data.
//
// In case the container implements Marshaller interface,
// the custom marshalling function is used. Otherwise
// the container itself is simply marshalled directly.
func marshal(container ConfigContainer) ([]byte, error) {
	var (
		v   interface{}
		err error
	)
	marshaller, ok := container.(Marshaller)
	if ok {
		v, err = marshaller.Marshal()
	} else {
		v = container
	}
	if err != nil {
		return nil, err
	}

	return config.Marshal(v)
}

// validate validates the configuration stored in the container.
//
// In case the container implements Validator interface,
// the custom validation function is used. Otherwise config.EnsureValueFilled
// is used, which means that all exported fields must be set for the config
// to be treated as valid.
func validate(container ConfigContainer, sectionPath string) error {
	var err error
	validator, ok := container.(Validator)
	if ok {
		err = validator.Validate(sectionPath)
	} else {
		err = config.EnsureValueFilled(container, sectionPath)
	}
	return err
}

func readLocalConfig() (configFile, error) {
	return config.ReadLocalConfig()
}

func emptyLocalConfig() configFile {
	return config.NewEmptyLocalConfig()
}

func readGlobalConfig() (configFile, error) {
	return config.ReadGlobalConfig()
}

func emptyGlobalConfig() configFile {
	return config.NewEmptyGlobalConfig()
}
<|endoftext|>


<commit_before>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"unsafe"
)

type Player struct {
	player *C.libvlc_media_player_t
	media  *Media
}

// NewPlayer creates an instance of a single-media player.
func NewPlayer() (*Player, error) {
	if instance == nil {
		return nil, errors.New("Module must be initialized first")
	}

	if player := C.libvlc_media_player_new(instance); player != nil {
		return &Player{player: player}, nil
	}

	return nil, getError()
}

// Release destroys the media player instance.
func (p *Player) Release() error {
	if p.player == nil {
		return nil
	}

	C.libvlc_media_player_release(p.player)
	p.player = nil

	return getError()
}

// Play plays the current media.
func (p *Player) Play() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if p.IsPlaying() {
		return nil
	}

	if C.libvlc_media_player_play(p.player) < 0 {
		return getError()
	}

	return nil
}

// IsPlaying returns a boolean value specifying if the player is currently
// playing.
func (p *Player) IsPlaying() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_is_playing(p.player) != 0
}

// Stop cancels the currently playing media, if there is one.
func (p *Player) Stop() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if !p.IsPlaying() {
		return nil
	}

	C.libvlc_media_player_stop(p.player)
	return getError()
}

// SetPause sets the pause state of the media player.
// Pass in true to pause the current media, or false to resume it.
func (p *Player) SetPause(pause bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_pause(p.player, C.int(boolToInt(pause)))
	return getError()
}

// TogglePause pauses/resumes the player.
// Calling this method has no effect if there is no media.
func (p *Player) TogglePause() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_pause(p.player)
	return getError()
}

// SetFullScreen sets the fullscreen state of the media player.
// Pass in true to enable fullscreen, or false to disable it.
func (p *Player) SetFullScreen(fullscreen bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_set_fullscreen(p.player, C.int(boolToInt(fullscreen)))
	return getError()
}

// ToggleFullScreen toggles the fullscreen status of the player,
// on non-embedded video outputs.
func (p *Player) ToggleFullScreen() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_toggle_fullscreen(p.player)
	return getError()
}

// IsFullScreen gets the fullscreen status of the current player.
func (p *Player) IsFullScreen() (bool, error) {
	if p.player == nil {
		return false, errors.New("A player must be initialized first")
	}

	return (C.libvlc_get_fullscreen(p.player) != C.int(0)), getError()
}

// Volume returns the volume of the player.
func (p *Player) Volume() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_audio_get_volume(p.player)), getError()
}

// SetVolume sets the volume of the player.
func (p *Player) SetVolume(volume int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_audio_set_volume(p.player, C.int(volume))
	return getError()
}

// Media returns the current media of the player, if one exists
func (p *Player) Media() *Media {
	return p.media
}

// SetMedia sets the provided media as the current media of the player
func (p *Player) SetMedia(m *Media) error {
	return p.setMedia(m)
}

// LoadMediaFromPath loads the media from the specified path and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromPath(path string) (*Media, error) {
	return p.loadMedia(path, true)
}

// LoadMediaFromURL loads the media from the specified URL and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromURL(url string) (*Media, error) {
	return p.loadMedia(url, false)
}

// SetAudioOutput selects an audio output module.
// Any change will take be effect only after playback is stopped and restarted.
// Audio output cannot be changed while playing.
func (p *Player) SetAudioOutput(output string) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	cOutput := C.CString(output)
	defer C.free(unsafe.Pointer(cOutput))

	if C.libvlc_audio_output_set(p.player, cOutput) != 0 {
		return getError()
	}

	return nil
}

// MediaLength returns media length in milliseconds.
func (p *Player) MediaLength() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_length(p.player)), getError()
}

// MediaState returns the state of the current media.
func (p *Player) MediaState() (MediaState, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	state := int(C.libvlc_media_player_get_state(p.player))
	return MediaState(state), getError()
}

// MediaPosition returns media position as a
// float percentage between 0.0 and 1.0.
func (p *Player) MediaPosition() (float32, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return float32(C.libvlc_media_player_get_position(p.player)), getError()
}

// SetMediaPosition sets media position as percentage between 0.0 and 1.0.
// Some formats and protocols do not support this.
func (p *Player) SetMediaPosition(pos float32) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_position(p.player, C.float(pos))
	return getError()
}

// MediaTime returns media time in milliseconds.
func (p *Player) MediaTime() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_time(p.player)), getError()
}

// SetMediaTime sets the media time in milliseconds.
// Some formats and protocals do not support this.
func (p *Player) SetMediaTime(t int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_time(p.player, C.libvlc_time_t(int64(t)))
	return getError()
}

// WillPlay returns true if the current media is not in a
// finished or error state.
func (p *Player) WillPlay() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_will_play(p.player) != 0
}

func (p *Player) loadMedia(path string, local bool) (*Media, error) {
	m, err := newMedia(path, local)
	if err != nil {
		return nil, err
	}

	if err = p.setMedia(m); err != nil {
		m.Release()
		return nil, err
	}

	return m, nil
}

func (p *Player) setMedia(m *Media) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if m.media == nil {
		return errors.New("The media must be initialized first")
	}

	p.media = m
	C.libvlc_media_player_set_media(p.player, m.media)

	return getError()
}
<commit_msg>Reimplement player Media method in order to remove unnecessary media member<commit_after>package vlc

// #cgo LDFLAGS: -lvlc
// #include <vlc/vlc.h>
// #include <stdlib.h>
import "C"
import (
	"errors"
	"unsafe"
)

type Player struct {
	player *C.libvlc_media_player_t
}

// NewPlayer creates an instance of a single-media player.
func NewPlayer() (*Player, error) {
	if instance == nil {
		return nil, errors.New("Module must be initialized first")
	}

	if player := C.libvlc_media_player_new(instance); player != nil {
		return &Player{player: player}, nil
	}

	return nil, getError()
}

// Release destroys the media player instance.
func (p *Player) Release() error {
	if p.player == nil {
		return nil
	}

	C.libvlc_media_player_release(p.player)
	p.player = nil

	return getError()
}

// Play plays the current media.
func (p *Player) Play() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if p.IsPlaying() {
		return nil
	}

	if C.libvlc_media_player_play(p.player) < 0 {
		return getError()
	}

	return nil
}

// IsPlaying returns a boolean value specifying if the player is currently
// playing.
func (p *Player) IsPlaying() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_is_playing(p.player) != 0
}

// Stop cancels the currently playing media, if there is one.
func (p *Player) Stop() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if !p.IsPlaying() {
		return nil
	}

	C.libvlc_media_player_stop(p.player)
	return getError()
}

// SetPause sets the pause state of the media player.
// Pass in true to pause the current media, or false to resume it.
func (p *Player) SetPause(pause bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_pause(p.player, C.int(boolToInt(pause)))
	return getError()
}

// TogglePause pauses/resumes the player.
// Calling this method has no effect if there is no media.
func (p *Player) TogglePause() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_pause(p.player)
	return getError()
}

// SetFullScreen sets the fullscreen state of the media player.
// Pass in true to enable fullscreen, or false to disable it.
func (p *Player) SetFullScreen(fullscreen bool) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_set_fullscreen(p.player, C.int(boolToInt(fullscreen)))
	return getError()
}

// ToggleFullScreen toggles the fullscreen status of the player,
// on non-embedded video outputs.
func (p *Player) ToggleFullScreen() error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_toggle_fullscreen(p.player)
	return getError()
}

// IsFullScreen gets the fullscreen status of the current player.
func (p *Player) IsFullScreen() (bool, error) {
	if p.player == nil {
		return false, errors.New("A player must be initialized first")
	}

	return (C.libvlc_get_fullscreen(p.player) != C.int(0)), getError()
}

// Volume returns the volume of the player.
func (p *Player) Volume() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_audio_get_volume(p.player)), getError()
}

// SetVolume sets the volume of the player.
func (p *Player) SetVolume(volume int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_audio_set_volume(p.player, C.int(volume))
	return getError()
}

// Media returns the current media of the player, if one exists
func (p *Player) Media() (*Media, error) {
	if p.player == nil {
		return nil, errors.New("A player must be initialized first")
	}

	media := C.libvlc_media_player_get_media(p.player)
	if media == nil {
		return nil, nil
	}

	// This call will not release the media. Instead, it will decrement
	// the reference count increased by libvlc_media_player_get_media.
	C.libvlc_media_release(media)

	return &Media{media}, nil
}

// SetMedia sets the provided media as the current media of the player
func (p *Player) SetMedia(m *Media) error {
	return p.setMedia(m)
}

// LoadMediaFromPath loads the media from the specified path and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromPath(path string) (*Media, error) {
	return p.loadMedia(path, true)
}

// LoadMediaFromURL loads the media from the specified URL and adds it as the
// current media of the player.
func (p *Player) LoadMediaFromURL(url string) (*Media, error) {
	return p.loadMedia(url, false)
}

// SetAudioOutput selects an audio output module.
// Any change will take be effect only after playback is stopped and restarted.
// Audio output cannot be changed while playing.
func (p *Player) SetAudioOutput(output string) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	cOutput := C.CString(output)
	defer C.free(unsafe.Pointer(cOutput))

	if C.libvlc_audio_output_set(p.player, cOutput) != 0 {
		return getError()
	}

	return nil
}

// MediaLength returns media length in milliseconds.
func (p *Player) MediaLength() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_length(p.player)), getError()
}

// MediaState returns the state of the current media.
func (p *Player) MediaState() (MediaState, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	state := int(C.libvlc_media_player_get_state(p.player))
	return MediaState(state), getError()
}

// MediaPosition returns media position as a
// float percentage between 0.0 and 1.0.
func (p *Player) MediaPosition() (float32, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return float32(C.libvlc_media_player_get_position(p.player)), getError()
}

// SetMediaPosition sets media position as percentage between 0.0 and 1.0.
// Some formats and protocols do not support this.
func (p *Player) SetMediaPosition(pos float32) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_position(p.player, C.float(pos))
	return getError()
}

// MediaTime returns media time in milliseconds.
func (p *Player) MediaTime() (int, error) {
	if p.player == nil {
		return 0, errors.New("A player must be initialized first")
	}

	return int(C.libvlc_media_player_get_time(p.player)), getError()
}

// SetMediaTime sets the media time in milliseconds.
// Some formats and protocals do not support this.
func (p *Player) SetMediaTime(t int) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}

	C.libvlc_media_player_set_time(p.player, C.libvlc_time_t(int64(t)))
	return getError()
}

// WillPlay returns true if the current media is not in a
// finished or error state.
func (p *Player) WillPlay() bool {
	if p.player == nil {
		return false
	}

	return C.libvlc_media_player_will_play(p.player) != 0
}

func (p *Player) loadMedia(path string, local bool) (*Media, error) {
	m, err := newMedia(path, local)
	if err != nil {
		return nil, err
	}

	if err = p.setMedia(m); err != nil {
		m.Release()
		return nil, err
	}

	return m, nil
}

func (p *Player) setMedia(m *Media) error {
	if p.player == nil {
		return errors.New("A player must be initialized first")
	}
	if m.media == nil {
		return errors.New("The media must be initialized first")
	}

	C.libvlc_media_player_set_media(p.player, m.media)
	return getError()
}
<|endoftext|>


<commit_before>package freetds

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"
)

const statusRow string = `;
   select cast(coalesce(scope_identity(), -1) as bigint) last_insert_id,
          cast(@@rowcount as bigint) rows_affected
`

//Execute sql query with arguments.
//? in query are arguments placeholders.
//  ExecuteSql("select * from authors where au_fname = ?", "John")
func (conn *Conn) ExecuteSql(query string, params ...driver.Value) ([]*Result, error) {
	statement, numParams := query2Statement(query)
	if numParams != len(params) {
		return nil, errors.New(fmt.Sprintf("Incorect number of params, expecting %d got %d", numParams, len(params)))
	}
	paramDef, paramVal := parseParams(params...)
	statement += statusRow
	sql := fmt.Sprintf("exec sp_executesql N'%s', N'%s', %s", statement, paramDef, paramVal)
	if numParams == 0 {
		sql = fmt.Sprintf("exec sp_executesql N'%s'", statement)
	}
	return conn.Exec(sql)
}

//converts query to SqlServer statement for sp_executesql
//replaces ? in query with params @p1, @p2, ...
//returns statement and number of params
func query2Statement(query string) (string, int) {
	parts := strings.Split(query, "?")
	var statement string
	numParams := len(parts) - 1
	statement = parts[0]
	for i, part := range parts {
		if i > 0 {
			statement = fmt.Sprintf("%s@p%d%s", statement, i, part)
		}
	}
	return quote(statement), numParams
}

func parseParams(params ...driver.Value) (paramDef, paramVal string) {
	paramDef = ""
	paramVal = ""
	for i, param := range params {
		if i > 0 {
			paramVal += ", "
			paramDef += ", "
		}
		sqlType, sqlValue := go2SqlDataType(param)
		paramName := fmt.Sprintf("@p%d", i+1)
		paramDef += fmt.Sprintf("%s %s", paramName, sqlType)
		paramVal += fmt.Sprintf("%s=%s", paramName, sqlValue)
	}
	return
}

func quote(in string) string {
	return strings.Replace(in, "'", "''", -1)
}

func go2SqlDataType(value interface{}) (string, string) {
	//TODO - bool value
	strValue := fmt.Sprintf("%v", value)
	switch t := value.(type) {
	case uint8, int8:
		return "tinyint", strValue
	case uint16, int16:
		return "smallint", strValue
	case uint32, int32, int:
		return "int", strValue
	case uint64, int64:
		return "bigint", strValue
	case float32, float64:
		return "real", strValue
	case string:
		{
		}
	case time.Time:
		{
			t, _ := value.(time.Time)
			strValue = t.Format(time.RFC3339)
		}
	case []byte:
		{
			b, _ := value.([]byte)
			return fmt.Sprintf("varbinary (%d)", len(b)),
				fmt.Sprintf("0x%x", b)
		}
	default:
		log.Printf("unknown dataType %t", t)
	}
	return fmt.Sprintf("nvarchar (%d)", len(strValue)),
		fmt.Sprintf("'%s'", quote(strValue))

}
<commit_msg>Added bool to bit datatype conversion<commit_after>package freetds

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"
)

const statusRow string = `;
   select cast(coalesce(scope_identity(), -1) as bigint) last_insert_id,
          cast(@@rowcount as bigint) rows_affected
`

//Execute sql query with arguments.
//? in query are arguments placeholders.
//  ExecuteSql("select * from authors where au_fname = ?", "John")
func (conn *Conn) ExecuteSql(query string, params ...driver.Value) ([]*Result, error) {
	statement, numParams := query2Statement(query)
	if numParams != len(params) {
		return nil, errors.New(fmt.Sprintf("Incorect number of params, expecting %d got %d", numParams, len(params)))
	}
	paramDef, paramVal := parseParams(params...)
	statement += statusRow
	sql := fmt.Sprintf("exec sp_executesql N'%s', N'%s', %s", statement, paramDef, paramVal)
	if numParams == 0 {
		sql = fmt.Sprintf("exec sp_executesql N'%s'", statement)
	}
	return conn.Exec(sql)
}

//converts query to SqlServer statement for sp_executesql
//replaces ? in query with params @p1, @p2, ...
//returns statement and number of params
func query2Statement(query string) (string, int) {
	parts := strings.Split(query, "?")
	var statement string
	numParams := len(parts) - 1
	statement = parts[0]
	for i, part := range parts {
		if i > 0 {
			statement = fmt.Sprintf("%s@p%d%s", statement, i, part)
		}
	}
	return quote(statement), numParams
}

func parseParams(params ...driver.Value) (paramDef, paramVal string) {
	paramDef = ""
	paramVal = ""
	for i, param := range params {
		if i > 0 {
			paramVal += ", "
			paramDef += ", "
		}
		sqlType, sqlValue := go2SqlDataType(param)
		paramName := fmt.Sprintf("@p%d", i+1)
		paramDef += fmt.Sprintf("%s %s", paramName, sqlType)
		paramVal += fmt.Sprintf("%s=%s", paramName, sqlValue)
	}
	return
}

func quote(in string) string {
	return strings.Replace(in, "'", "''", -1)
}

func go2SqlDataType(value interface{}) (string, string) {
	strValue := fmt.Sprintf("%v", value)
	switch t := value.(type) {
	case bool:
		bitStrValue := "0"
		if strValue = "true" {
			bitStrValue = "1"
		}
		return "bit", bitStrValue
	case uint8, int8:
		return "tinyint", strValue
	case uint16, int16:
		return "smallint", strValue
	case uint32, int32, int:
		return "int", strValue
	case uint64, int64:
		return "bigint", strValue
	case float32, float64:
		return "real", strValue
	case string:
		{
		}
	case time.Time:
		{
			t, _ := value.(time.Time)
			strValue = t.Format(time.RFC3339)
		}
	case []byte:
		{
			b, _ := value.([]byte)
			return fmt.Sprintf("varbinary (%d)", len(b)),
				fmt.Sprintf("0x%x", b)
		}
	default:
		log.Printf("unknown dataType %t", t)
	}
	return fmt.Sprintf("nvarchar (%d)", len(strValue)),
		fmt.Sprintf("'%s'", quote(strValue))

}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2016 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
 The example for kernel.
*/
package kernel_test

import (
	oa "github.com/ossrs/go-oryx-lib/asprocess"
	"github.com/ossrs/go-oryx/kernel"
	"net"
	"os/exec"
	"syscall"
	"time"
)

func ExampleWorkerGroup() {
	// other goroutine to notify worker group to quit.
	closing := make(chan bool, 1)
	// for example, user can use asprocess to watch without exit to write closing.
	oa.WatchNoExit(nil, oa.Interval, closing)

	// use group to sync workers.
	wg := kernel.NewWorkerGroup()
	defer wg.Close()

	// quit for external events.
	wg.QuitForChan(closing)
	// quit for signals.
	wg.QuitForSignals(nil, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// start goroutine to worker, quit when worker quit.
	// for example, the listener for http.
	var listener net.Listener
	wg.ForkGoroutine(func() {
		for {
			if conn, err := listener.Accept(); err != nil {
				if !wg.Closed() {
					// log error.
				}
				return
			} else {
				// serve conn
				defer conn.Close()
			}
		}
	}, func() {
		listener.Close()
	})

	// start goroutine without cleanup.
	wg.ForkGoroutine(func() {
		for !wg.Closed() {
			// do something util quit.
			time.Sleep(time.Duration(1) * time.Second)
		}
	}, nil)

	// wait for quit.
	wg.Wait()
}

func ExampleProcessPool() {
	pp := kernel.NewProcessPool()
	defer pp.Close()

	// start a worker.
	var err error
	var cmd *exec.Cmd
	if cmd, err = pp.Start(nil, "ls", "-al"); err != nil {
		return
	}

	// start other workers.

	// wait for some processes to quit.
	var c *exec.Cmd
	if c, err = pp.Wait(); err != nil {
		return
	}

	// do something for the terminated process.
	if cmd == c {
		// cmd must be c.
	}
}
<commit_msg>add examples for kernel<commit_after>/*
The MIT License (MIT)

Copyright (c) 2016 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
 The example for kernel.
*/
package kernel_test

import (
	oa "github.com/ossrs/go-oryx-lib/asprocess"
	ol "github.com/ossrs/go-oryx-lib/logger"
	"github.com/ossrs/go-oryx/kernel"
	"net"
	"os/exec"
	"syscall"
	"time"
)

func ExampleWorkerGroup() {
	// other goroutine to notify worker group to quit.
	closing := make(chan bool, 1)
	// for example, user can use asprocess to watch without exit to write closing.
	oa.WatchNoExit(nil, oa.Interval, closing)

	// use group to sync workers.
	wg := kernel.NewWorkerGroup()
	defer wg.Close()

	// quit for external events.
	wg.QuitForChan(closing)
	// quit for signals.
	wg.QuitForSignals(nil, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)

	// start goroutine to worker, quit when worker quit.
	// for example, the listener for http.
	var listener net.Listener
	wg.ForkGoroutine(func() {
		for {
			if conn, err := listener.Accept(); err != nil {
				if !wg.Closed() {
					// log error.
				}
				return
			} else {
				// serve conn

				defer conn.Close()
			}
		}
	}, func() {
		listener.Close()
	})

	// start goroutine without cleanup.
	wg.ForkGoroutine(func() {
		for !wg.Closed() {
			// do something util quit.
			time.Sleep(time.Duration(1) * time.Second)
		}
	}, nil)

	// wait for quit.
	wg.Wait()
}

func ExampleProcessPool() {
	pp := kernel.NewProcessPool()
	defer pp.Close()

	// start a worker.
	var err error
	var cmd *exec.Cmd
	if cmd, err = pp.Start(nil, "ls", "-al"); err != nil {
		return
	}

	// start other workers.

	// wait for some processes to quit.
	var c *exec.Cmd
	if c, err = pp.Wait(); err != nil {
		return
	}

	// do something for the terminated process.
	if cmd == c {
		// cmd must be c.
	}
}

func ExampleTcpListeners() {
	// create listener by addresses
	ls, err := kernel.NewTcpListeners([]string{"tcp://:1935", "tcp://:1985", "tcp://:8080"})
	if err != nil {
		return
	}
	defer ls.Close()

	// listen all addresses
	if err = ls.ListenTCP(); err != nil {
		return
	}

	// accept conn from any listeners
	for {
		conn, err := ls.AcceptTCP()
		if err != nil {
			return
		}

		// serve conn

		defer conn.Close()
	}
}

func ExampleContext() {
	ctx := &kernel.Context{}
	ol.T(ctx, "create context ok")
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"reflect"
	"testing"
)

func testIconsFindHelper(terms []string) icons {
	return newIcons().find(terms)
}

func TestIcons_iconsYamlPath_TestEnv(t *testing.T) {
	actual := iconsYamlPath()
	expected := "workflow/icons.yml"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_iconsYamlPath_ProductionEnv(t *testing.T) {
	resetEnv := setTestEnvHelper("FAW_ICONS_YAML_PATH", "")
	defer resetEnv()

	actual := iconsYamlPath()
	expected := "icons.yml"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_iconsReadYaml(t *testing.T) {
	path := "workflow/icons.yml"
	actual, _ := iconsReadYaml(path)

	expected, _ := ioutil.ReadFile(path)
	if !reflect.DeepEqual(actual, expected) {
		t.Error("failed to read file")
	}
}

func TestIcons_iconsReadYaml_Error(t *testing.T) {
	path := ""
	_, err := iconsReadYaml(path)

	if err == nil {
		t.Error("expected error, but nil")
	}
}

func TestIcons_iconsUnmarshalYaml(t *testing.T) {
	b := []byte(`
icons:
- name: Accessible Icon
  id: accessible-icon
  unicode: f368
  created: 5.0.0
  filter:
  - accessibility
  - wheelchair
  - handicap
  - person
  - wheelchair-alt
  categories: unknown
`)
	actual, _ := iconsUnmarshalYaml(b)

	icon := icon{
		Name:    "Accessible Icon",
		ID:      "accessible-icon",
		Unicode: "f368",
		Created: "5.0.0",
		Filter:  []string{"accessibility", "wheelchair", "handicap", "person", "wheelchair-alt"},
	}
	expected := iconsYaml{icons{icon}}
	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_AllIcons(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := len(fi)
	expected := 873
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_ZeroIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"foo-bar-baz"})

	actual := len(fi)
	expected := 0
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_OneIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"github-square"})

	actual := len(fi)
	expected := 1
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_TwoIcons(t *testing.T) {
	fi := testIconsFindHelper([]string{"github-"})

	actual := len(fi)
	expected := 2
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_FirstIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := fi[0].ID
	expected := "500px"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_LastIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := fi[len(fi)-1].ID
	expected := "youtube-square"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_TaxiIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"taxi"})

	actual := fi[0].Name
	expected := "Taxi"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].ID
	expected = "taxi"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].Unicode
	expected = "f1ba"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].Created
	expected = "4.1"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	// actual = fi[0].Aliases[0]
	// expected = "cab"
	// if actual != expected {
	// 	t.Errorf("expected %v to eq %v", actual, expected)
	// }

	actual = fi[0].Filter[0]
	expected = "vehicle"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	// actual = fi[0].Categories[0]
	// expected = "Web Application Icons"
	// if actual != expected {
	// 	t.Errorf("expected %v to eq %v", actual, expected)
	// }
}

// func TestIcons_find_Aliases(t *testing.T) {
// 	fi := testIconsFindHelper([]string{"navicon"})

// 	actual := fi[0].ID
// 	expected := "bars"
// 	if actual != expected {
// 		t.Errorf("expected %v to eq %v", actual, expected)
// 	}

// 	if len(fi) != 1 {
// 		t.Errorf("expected %v to eq %v", len(fi), 1)
// 	}
// }

func TestIcons_findByUnicode(t *testing.T) {
	fi := newIcons().findByUnicode("f067")

	actual := fi[0].ID
	expected := "plus"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	if len(fi) != 1 {
		t.Errorf("expected %v to eq %v", len(fi), 1)
	}
}
<commit_msg>(Font Awesome 5.0.10) Fix test<commit_after>package main

import (
	"io/ioutil"
	"reflect"
	"testing"
)

func testIconsFindHelper(terms []string) icons {
	return newIcons().find(terms)
}

func TestIcons_iconsYamlPath_TestEnv(t *testing.T) {
	actual := iconsYamlPath()
	expected := "workflow/icons.yml"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_iconsYamlPath_ProductionEnv(t *testing.T) {
	resetEnv := setTestEnvHelper("FAW_ICONS_YAML_PATH", "")
	defer resetEnv()

	actual := iconsYamlPath()
	expected := "icons.yml"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_iconsReadYaml(t *testing.T) {
	path := "workflow/icons.yml"
	actual, _ := iconsReadYaml(path)

	expected, _ := ioutil.ReadFile(path)
	if !reflect.DeepEqual(actual, expected) {
		t.Error("failed to read file")
	}
}

func TestIcons_iconsReadYaml_Error(t *testing.T) {
	path := ""
	_, err := iconsReadYaml(path)

	if err == nil {
		t.Error("expected error, but nil")
	}
}

func TestIcons_iconsUnmarshalYaml(t *testing.T) {
	b := []byte(`
icons:
- name: Accessible Icon
  id: accessible-icon
  unicode: f368
  created: 5.0.0
  filter:
  - accessibility
  - wheelchair
  - handicap
  - person
  - wheelchair-alt
  categories: unknown
`)
	actual, _ := iconsUnmarshalYaml(b)

	icon := icon{
		Name:    "Accessible Icon",
		ID:      "accessible-icon",
		Unicode: "f368",
		Created: "5.0.0",
		Filter:  []string{"accessibility", "wheelchair", "handicap", "person", "wheelchair-alt"},
	}
	expected := iconsYaml{icons{icon}}
	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_AllIcons(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := len(fi)
	expected := 875
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_ZeroIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"foo-bar-baz"})

	actual := len(fi)
	expected := 0
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_OneIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"github-square"})

	actual := len(fi)
	expected := 1
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_TwoIcons(t *testing.T) {
	fi := testIconsFindHelper([]string{"github-"})

	actual := len(fi)
	expected := 2
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_FirstIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := fi[0].ID
	expected := "500px"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_LastIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{""})

	actual := fi[len(fi)-1].ID
	expected := "youtube-square"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}
}

func TestIcons_find_TaxiIcon(t *testing.T) {
	fi := testIconsFindHelper([]string{"taxi"})

	actual := fi[0].Name
	expected := "Taxi"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].ID
	expected = "taxi"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].Unicode
	expected = "f1ba"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	actual = fi[0].Created
	expected = "4.1"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	// actual = fi[0].Aliases[0]
	// expected = "cab"
	// if actual != expected {
	// 	t.Errorf("expected %v to eq %v", actual, expected)
	// }

	actual = fi[0].Filter[0]
	expected = "vehicle"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	// actual = fi[0].Categories[0]
	// expected = "Web Application Icons"
	// if actual != expected {
	// 	t.Errorf("expected %v to eq %v", actual, expected)
	// }
}

// func TestIcons_find_Aliases(t *testing.T) {
// 	fi := testIconsFindHelper([]string{"navicon"})

// 	actual := fi[0].ID
// 	expected := "bars"
// 	if actual != expected {
// 		t.Errorf("expected %v to eq %v", actual, expected)
// 	}

// 	if len(fi) != 1 {
// 		t.Errorf("expected %v to eq %v", len(fi), 1)
// 	}
// }

func TestIcons_findByUnicode(t *testing.T) {
	fi := newIcons().findByUnicode("f067")

	actual := fi[0].ID
	expected := "plus"
	if actual != expected {
		t.Errorf("expected %v to eq %v", actual, expected)
	}

	if len(fi) != 1 {
		t.Errorf("expected %v to eq %v", len(fi), 1)
	}
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"strings"
)

// RuleConfig 验证规则配置
type RuleConfig struct {
	Name       string
	Desc       string              //规则描述
	Validators []map[string]string //验证器列表,必须要有name
}

// ValidateRuleConfig 验证规则配置
type ValidateRuleConfig struct {
	Rules  []RuleConfig
	SName  string          //服务的名称
	parsed validateRuleMap //解析后的结果
}

// Parse 解析验证的配置
func (p *ValidateRuleConfig) Parse() error {
	if p == nil {
		Warnf("no validate conf")
		return nil
	}
	rules := make(validateRuleMap)
	for _, ruleConfig := range p.Rules {
		ruleName := strings.TrimSpace(ruleConfig.Name)
		if len(ruleName) == 0 {
			panic("The rule name must not be empty")
		}
		validators := make([]StrValidator, 0, len(ruleConfig.Validators))
		for _, validatorConf := range ruleConfig.Validators {
			validators = append(validators, NewValidatorByConf(validatorConf))
		}
		rule := &ValidateRule{
			desc:       ruleConfig.Desc,
			validators: validators}
		rules[ruleName] = rule
		Debugf("Add validate rule:%s", ruleName)
	}
	p.parsed = rules
	return nil
}

// NewService 根据配置解析的结果创建验证服务
func (p *ValidateRuleConfig) NewService() Service {
	if p.parsed == nil {
		panic("Can't create ValidateService from nil")
	}
	svr := RuleValidateService{}
	svr.SName = p.SName
	svr.rules = p.parsed
	return &svr
}

//ValidateRule 定义验证规则
type ValidateRule struct {
	desc       string         //规则描述
	validators []StrValidator //通过Rules构建出来的验证规则
}

type validateRuleMap map[string]*ValidateRule

// ValidateConfigurer validateConfig
type ValidateConfigurer interface {
	GetValidateRuleConfig() *ValidateRuleConfig
}

// ValidateService 验证服务
type ValidateService interface {
	Service
	//Validate 使用name指定验证规则,对value进行验证,验证通过返回nil,否则返回错误原因
	Validate(name string, value string) error
}

// RuleValidateService  根据规则进行的验证服务
type RuleValidateService struct {
	BaseService
	Config ValidateConfigurer `inject:"_"`
	rules  validateRuleMap
}

// Init implements Initable
func (p *RuleValidateService) Init() error {
	if p.Config == nil || p.Config.GetValidateRuleConfig() == nil {
		return fmt.Errorf("no validate config")
	}
	config := p.Config.GetValidateRuleConfig()
	p.SName = config.SName
	p.rules = config.parsed
	return nil
}

// Validate 验证
func (p *RuleValidateService) Validate(ruleName string, s string) error {
	rule := p.rules[ruleName]
	if rule == nil {
		return fmt.Errorf("can't find validate rule %s", ruleName)
	}

	for _, v := range rule.validators {
		if !v.Validate(s) {
			return NewValidateError(rule.desc)
		}
	}
	return nil
}

// ValidatePair 定义验证规则名称其需要验证的值
type ValidatePair struct {
	Name  string
	Value string
	Msg   string
}

// NewValidatePair create ValidatePair
func NewValidatePair(name, value string) *ValidatePair {
	return &ValidatePair{Name: name, Value: value}
}

// NewValidatePairMsg create ValidatePair with msg
func NewValidatePairMsg(name, value, msg string) *ValidatePair {
	return &ValidatePair{Name: name, Value: value, Msg: msg}
}

// ValidateAll 验证所有的规则
func ValidateAll(validateService ValidateService, nameAndValues ...*ValidatePair) error {
	for _, nv := range nameAndValues {
		if err := validateService.Validate(nv.Name, nv.Value); err != nil {
			if nv.Msg != "" {
				return NewValidateError(nv.Msg)
			}
			return err
		}
	}
	return nil
}

// ValidateError error
type ValidateError struct {
	msg string //错误消息
}

// NewValidateError new
func NewValidateError(msg string) *ValidateError {
	return &ValidateError{msg: msg}
}

func (p *ValidateError) Error() string {
	if p == nil {
		return ""
	}
	return p.msg
}

// Human impls HumanError.Human
func (p *ValidateError) Human() bool {
	if p == nil {
		return false
	}
	return true
}
<commit_msg>allow validate config empty<commit_after>package common

import (
	"fmt"
	"strings"
)

// RuleConfig 验证规则配置
type RuleConfig struct {
	Name       string
	Desc       string              //规则描述
	Validators []map[string]string //验证器列表,必须要有name
}

// ValidateRuleConfig 验证规则配置
type ValidateRuleConfig struct {
	Rules  []RuleConfig
	SName  string          //服务的名称
	parsed validateRuleMap //解析后的结果
}

// Parse 解析验证的配置
func (p *ValidateRuleConfig) Parse() error {
	if p == nil {
		Warnf("no validate conf")
		return nil
	}
	rules := make(validateRuleMap)
	for _, ruleConfig := range p.Rules {
		ruleName := strings.TrimSpace(ruleConfig.Name)
		if len(ruleName) == 0 {
			panic("The rule name must not be empty")
		}
		validators := make([]StrValidator, 0, len(ruleConfig.Validators))
		for _, validatorConf := range ruleConfig.Validators {
			validators = append(validators, NewValidatorByConf(validatorConf))
		}
		rule := &ValidateRule{
			desc:       ruleConfig.Desc,
			validators: validators}
		rules[ruleName] = rule
		Debugf("Add validate rule:%s", ruleName)
	}
	p.parsed = rules
	return nil
}

// NewService 根据配置解析的结果创建验证服务
func (p *ValidateRuleConfig) NewService() Service {
	if p.parsed == nil {
		panic("Can't create ValidateService from nil")
	}
	svr := RuleValidateService{}
	svr.SName = p.SName
	svr.rules = p.parsed
	return &svr
}

//ValidateRule 定义验证规则
type ValidateRule struct {
	desc       string         //规则描述
	validators []StrValidator //通过Rules构建出来的验证规则
}

type validateRuleMap map[string]*ValidateRule

// ValidateConfigurer validateConfig
type ValidateConfigurer interface {
	GetValidateRuleConfig() *ValidateRuleConfig
}

// ValidateService 验证服务
type ValidateService interface {
	Service
	//Validate 使用name指定验证规则,对value进行验证,验证通过返回nil,否则返回错误原因
	Validate(name string, value string) error
}

// RuleValidateService  根据规则进行的验证服务
type RuleValidateService struct {
	BaseService
	Config ValidateConfigurer `inject:"_"`
	rules  validateRuleMap
}

// Init implements Initable
func (p *RuleValidateService) Init() error {
	if p.Config == nil {
		Warnf("no validate config")
		return nil
	}
	if p.Config.GetValidateRuleConfig() == nil {
		return fmt.Errorf("no validate config rule")
	}
	config := p.Config.GetValidateRuleConfig()
	p.SName = config.SName
	p.rules = config.parsed
	return nil
}

// Validate 验证
func (p *RuleValidateService) Validate(ruleName string, s string) error {
	rule := p.rules[ruleName]
	if rule == nil {
		return fmt.Errorf("can't find validate rule %s", ruleName)
	}

	for _, v := range rule.validators {
		if !v.Validate(s) {
			return NewValidateError(rule.desc)
		}
	}
	return nil
}

// ValidatePair 定义验证规则名称其需要验证的值
type ValidatePair struct {
	Name  string
	Value string
	Msg   string
}

// NewValidatePair create ValidatePair
func NewValidatePair(name, value string) *ValidatePair {
	return &ValidatePair{Name: name, Value: value}
}

// NewValidatePairMsg create ValidatePair with msg
func NewValidatePairMsg(name, value, msg string) *ValidatePair {
	return &ValidatePair{Name: name, Value: value, Msg: msg}
}

// ValidateAll 验证所有的规则
func ValidateAll(validateService ValidateService, nameAndValues ...*ValidatePair) error {
	for _, nv := range nameAndValues {
		if err := validateService.Validate(nv.Name, nv.Value); err != nil {
			if nv.Msg != "" {
				return NewValidateError(nv.Msg)
			}
			return err
		}
	}
	return nil
}

// ValidateError error
type ValidateError struct {
	msg string //错误消息
}

// NewValidateError new
func NewValidateError(msg string) *ValidateError {
	return &ValidateError{msg: msg}
}

func (p *ValidateError) Error() string {
	if p == nil {
		return ""
	}
	return p.msg
}

// Human impls HumanError.Human
func (p *ValidateError) Human() bool {
	if p == nil {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package image

import (
	"bytes"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/thoas/storages"
	"image"
	"math"
	"mime"
	"net/url"
	"path"
	"strconv"
	"strings"
)

type ImageFile struct {
	Source   image.Image
	Key      string
	Headers  map[string]string
	Filepath string
	Storage  storages.Storage
}

type Transformation func(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA

func (i *ImageFile) ImageSize() (int, int) {
	return i.Source.Bounds().Max.X, i.Source.Bounds().Max.Y
}

func (i *ImageFile) scale(width int, height int, trans Transformation) *image.NRGBA {
	return trans(i.Source, width, height, imaging.Lanczos)
}

func (i *ImageFile) Scale(geometry []int, upscale bool, trans Transformation) *image.NRGBA {
	width, height := i.ImageSize()

	factor := scalingFactor(width, height, geometry[0], geometry[1])

	if factor < 1 || upscale {
		width = int(float64(width) * factor)
		height = int(float64(height) * factor)

		return i.scale(width, height, trans)
	}

	return imaging.Clone(i.Source)
}

func (i *ImageFile) Transform(operation *Operation, qs map[string]string) (*ImageFile, error) {
	_, ok := qs["upscale"]

	if !ok {
		qs["upscale"] = "1"
	}

	switch operation {
	case Resize, Thumbnail:
		var w int
		var h int
		var err error

		if _, ok := qs["w"]; !ok {
			w = 0
		} else {
			w, err = strconv.Atoi(qs["w"])

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

		if _, ok := qs["h"]; !ok {
			h = 0
		} else {
			h, err = strconv.Atoi(qs["h"])

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

		upscale, err := strconv.ParseBool(qs["upscale"])

		if err != nil {
			return nil, err
		}

		dest := i.Scale([]int{w, h}, upscale, operation.Transformation)

		file := &ImageFile{
			Source:   dest,
			Key:      i.Key,
			Headers:  i.Headers,
			Filepath: i.Filepath,
		}

		return file, err
	}

	return nil, fmt.Errorf("Operation not found for %s", operation)
}

func (i *ImageFile) ToBytes() ([]byte, error) {
	format, ok := Formats[i.ContentType()]

	if !ok {
		format = DefaultFormat
	}

	return i.ToBytesWithFormat(format)
}

func (i *ImageFile) URL() string {
	return i.Storage.URL(i.Filepath)
}

func (i *ImageFile) Path() string {
	return i.Storage.Path(i.Filepath)
}

func (i *ImageFile) ToBytesWithFormat(format imaging.Format) ([]byte, error) {
	buf := &bytes.Buffer{}

	err := imaging.Encode(buf, i.Source, format)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func (i *ImageFile) Format() string {
	return Extensions[i.ContentType()]
}

func (i *ImageFile) ContentType() string {
	return mime.TypeByExtension(i.FilenameExt())
}

func (i *ImageFile) Filename() string {
	return i.Filepath[strings.LastIndex(i.Filepath, "/")+1:]
}

func (i *ImageFile) FilenameExt() string {
	return path.Ext(i.Filename())
}

func (i *ImageFile) LoadFromStorage(storage storages.Storage) (*ImageFile, error) {
	var file *ImageFile
	var err error

	// URL provided we use http protocol to retrieve it
	if storage.HasBaseURL() {
		u, err := url.Parse(storage.URL(i.Filepath))

		if err != nil {
			return nil, err
		}

		file, err = ImageFileFromURL(u)

		if err != nil {
			return nil, err
		}
	} else {
		body, err := storage.Open(i.Filepath)

		if err != nil {
			return nil, err
		}

		modifiedTime, err := storage.ModifiedTime(i.Filepath)

		if err != nil {
			return nil, err
		}

		contentType := i.ContentType()

		headers := map[string]string{
			"Last-Modified": modifiedTime.Format(storages.LastModifiedFormat),
			"Content-Type":  contentType,
		}

		reader := bytes.NewReader(body)

		dest, err := imaging.Decode(reader)

		if err != nil {
			return nil, err
		}

		return &ImageFile{
			Source:   dest,
			Storage:  storage,
			Headers:  headers,
			Filepath: i.Filepath,
		}, nil
	}

	return file, err
}

func scalingFactor(srcWidth int, srcHeight int, destWidth int, destHeight int) float64 {
	return math.Max(float64(destWidth)/float64(srcWidth), float64(destHeight)/float64(srcHeight))
}
<commit_msg>Remove useless method<commit_after>package image

import (
	"bytes"
	"fmt"
	"github.com/disintegration/imaging"
	"github.com/thoas/storages"
	"image"
	"math"
	"mime"
	"net/url"
	"path"
	"strconv"
	"strings"
)

type ImageFile struct {
	Source   image.Image
	Key      string
	Headers  map[string]string
	Filepath string
	Storage  storages.Storage
}

type Transformation func(img image.Image, width, height int, filter imaging.ResampleFilter) *image.NRGBA

func (i *ImageFile) ImageSize() (int, int) {
	return i.Source.Bounds().Max.X, i.Source.Bounds().Max.Y
}

func (i *ImageFile) Scale(geometry []int, upscale bool, trans Transformation) *image.NRGBA {
	width, height := i.ImageSize()

	factor := scalingFactor(width, height, geometry[0], geometry[1])

	if factor < 1 || upscale {
		width = int(float64(width) * factor)
		height = int(float64(height) * factor)

		return trans(i.Source, width, height, imaging.Lanczos)
	}

	return imaging.Clone(i.Source)
}

func (i *ImageFile) Transform(operation *Operation, qs map[string]string) (*ImageFile, error) {
	_, ok := qs["upscale"]

	if !ok {
		qs["upscale"] = "1"
	}

	switch operation {
	case Resize, Thumbnail:
		var w int
		var h int
		var err error

		if _, ok := qs["w"]; !ok {
			w = 0
		} else {
			w, err = strconv.Atoi(qs["w"])

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

		if _, ok := qs["h"]; !ok {
			h = 0
		} else {
			h, err = strconv.Atoi(qs["h"])

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

		upscale, err := strconv.ParseBool(qs["upscale"])

		if err != nil {
			return nil, err
		}

		dest := i.Scale([]int{w, h}, upscale, operation.Transformation)

		file := &ImageFile{
			Source:   dest,
			Key:      i.Key,
			Headers:  i.Headers,
			Filepath: i.Filepath,
		}

		return file, err
	}

	return nil, fmt.Errorf("Operation not found for %s", operation)
}

func (i *ImageFile) ToBytes() ([]byte, error) {
	format, ok := Formats[i.ContentType()]

	if !ok {
		format = DefaultFormat
	}

	return i.ToBytesWithFormat(format)
}

func (i *ImageFile) URL() string {
	return i.Storage.URL(i.Filepath)
}

func (i *ImageFile) Path() string {
	return i.Storage.Path(i.Filepath)
}

func (i *ImageFile) ToBytesWithFormat(format imaging.Format) ([]byte, error) {
	buf := &bytes.Buffer{}

	err := imaging.Encode(buf, i.Source, format)

	if err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func (i *ImageFile) Format() string {
	return Extensions[i.ContentType()]
}

func (i *ImageFile) ContentType() string {
	return mime.TypeByExtension(i.FilenameExt())
}

func (i *ImageFile) Filename() string {
	return i.Filepath[strings.LastIndex(i.Filepath, "/")+1:]
}

func (i *ImageFile) FilenameExt() string {
	return path.Ext(i.Filename())
}

func (i *ImageFile) LoadFromStorage(storage storages.Storage) (*ImageFile, error) {
	var file *ImageFile
	var err error

	// URL provided we use http protocol to retrieve it
	if storage.HasBaseURL() {
		u, err := url.Parse(storage.URL(i.Filepath))

		if err != nil {
			return nil, err
		}

		file, err = ImageFileFromURL(u)

		if err != nil {
			return nil, err
		}
	} else {
		body, err := storage.Open(i.Filepath)

		if err != nil {
			return nil, err
		}

		modifiedTime, err := storage.ModifiedTime(i.Filepath)

		if err != nil {
			return nil, err
		}

		contentType := i.ContentType()

		headers := map[string]string{
			"Last-Modified": modifiedTime.Format(storages.LastModifiedFormat),
			"Content-Type":  contentType,
		}

		reader := bytes.NewReader(body)

		dest, err := imaging.Decode(reader)

		if err != nil {
			return nil, err
		}

		return &ImageFile{
			Source:   dest,
			Storage:  storage,
			Headers:  headers,
			Filepath: i.Filepath,
		}, nil
	}

	return file, err
}

func scalingFactor(srcWidth int, srcHeight int, destWidth int, destHeight int) float64 {
	return math.Max(float64(destWidth)/float64(srcWidth), float64(destHeight)/float64(srcHeight))
}
<|endoftext|>


<commit_before>// +build darwin

package cocoa

import (
	"bufio"
	"image"
	"image/draw"
	"os"
	"testing"
)

func TestNewImage(t *testing.T) {
	img := NewImage(gopherNRGBA)

	if img.Ref() == nil {
		t.Error("invalid nil image reference")
	}
}

func TestNewImageWrap(t *testing.T) {
	img1 := CGImageCreate(gopherNRGBA)
	img2 := NewImageWrap(img1)

	if ref := img2.Ref(); ref != img1 {
		t.Error("invalid image reference:", ref)
	}
}

func TestImageRelease(t *testing.T) {
	img := NewImage(gopherNRGBA)
	img.release()

	if img.Ref() != nil {
		t.Error("invalid non-nil image reference found after releasing")
	}
}

func loadImage() image.Image {
	f, _ := os.Open("fixtures/gopher.png")
	i, _, _ := image.Decode(bufio.NewReader(f))
	f.Close()
	return i
}

func copyImage(dst draw.Image, src image.Image) image.Image {
	draw.Draw(dst, dst.Bounds(), src, image.Point{}, draw.Over)
	return dst
}

func imageToAlpha(img image.Image) image.Image {
	return copyImage(image.NewAlpha(img.Bounds()), img)
}

func imageToAlpha16(img image.Image) image.Image {
	return copyImage(image.NewAlpha16(img.Bounds()), img)
}

func imageToGray(img image.Image) image.Image {
	return copyImage(image.NewGray(img.Bounds()), img)
}

func imageToGray16(img image.Image) image.Image {
	return copyImage(image.NewGray16(img.Bounds()), img)
}

func imageToRGBA(img image.Image) image.Image {
	return copyImage(image.NewRGBA(img.Bounds()), img)
}

func imageToRGBA64(img image.Image) image.Image {
	return copyImage(image.NewRGBA64(img.Bounds()), img)
}

func imageToNRGBA64(img image.Image) image.Image {
	return copyImage(image.NewNRGBA64(img.Bounds()), img)
}

func imageToCMYK(img image.Image) image.Image {
	return copyImage(image.NewCMYK(img.Bounds()), img)
}

var (
	gopherNRGBA   = loadImage()
	gopherAlpha   = imageToAlpha(gopherNRGBA)
	gopherAlpha16 = imageToAlpha16(gopherNRGBA)
	gopherGray    = imageToGray(gopherNRGBA)
	gopherGray16  = imageToGray16(gopherNRGBA)
	gopherRGBA    = imageToRGBA(gopherNRGBA)
	gopherRGBA64  = imageToRGBA64(gopherNRGBA)
	gopherNRGBA64 = imageToNRGBA64(gopherNRGBA)
	gopherCMYK    = imageToCMYK(gopherNRGBA)

	gopherImages = [...]image.Image{
		gopherNRGBA,
		gopherAlpha,
		gopherAlpha16,
		gopherGray,
		gopherGray16,
		gopherRGBA,
		gopherRGBA64,
		gopherNRGBA64,
		gopherCMYK,
	}
)
<commit_msg>100% test coverage<commit_after>// +build darwin

package cocoa

import (
	"bufio"
	"image"
	"image/draw"
	"os"
	"testing"
)

func TestNewImage(t *testing.T) {
	for _, test := range gopherImages {
		img := NewImage(test)

		if img.Ref() == nil {
			t.Error("invalid nil image reference")
		}
	}
}

func TestNewImageWrap(t *testing.T) {
	for _, test := range gopherImages {
		img1 := CGImageCreate(test)
		img2 := NewImageWrap(img1)

		if ref := img2.Ref(); ref != img1 {
			t.Error("invalid image reference:", ref)
		}
	}
}

func TestImageRelease(t *testing.T) {
	for _, test := range gopherImages {
		img := NewImage(gopherNRGBA)
		img.release()

		if img.Ref() != nil {
			t.Error("invalid non-nil image reference found after releasing")
		}
	}
}

func loadImage() image.Image {
	f, _ := os.Open("fixtures/gopher.png")
	i, _, _ := image.Decode(bufio.NewReader(f))
	f.Close()
	return i
}

func copyImage(dst draw.Image, src image.Image) image.Image {
	draw.Draw(dst, dst.Bounds(), src, image.Point{}, draw.Over)
	return dst
}

func imageToAlpha(img image.Image) image.Image {
	return copyImage(image.NewAlpha(img.Bounds()), img)
}

func imageToAlpha16(img image.Image) image.Image {
	return copyImage(image.NewAlpha16(img.Bounds()), img)
}

func imageToGray(img image.Image) image.Image {
	return copyImage(image.NewGray(img.Bounds()), img)
}

func imageToGray16(img image.Image) image.Image {
	return copyImage(image.NewGray16(img.Bounds()), img)
}

func imageToRGBA(img image.Image) image.Image {
	return copyImage(image.NewRGBA(img.Bounds()), img)
}

func imageToRGBA64(img image.Image) image.Image {
	return copyImage(image.NewRGBA64(img.Bounds()), img)
}

func imageToNRGBA64(img image.Image) image.Image {
	return copyImage(image.NewNRGBA64(img.Bounds()), img)
}

func imageToCMYK(img image.Image) image.Image {
	return copyImage(image.NewCMYK(img.Bounds()), img)
}

var (
	gopherNRGBA   = loadImage()
	gopherAlpha   = imageToAlpha(gopherNRGBA)
	gopherAlpha16 = imageToAlpha16(gopherNRGBA)
	gopherGray    = imageToGray(gopherNRGBA)
	gopherGray16  = imageToGray16(gopherNRGBA)
	gopherRGBA    = imageToRGBA(gopherNRGBA)
	gopherRGBA64  = imageToRGBA64(gopherNRGBA)
	gopherNRGBA64 = imageToNRGBA64(gopherNRGBA)
	gopherCMYK    = imageToCMYK(gopherNRGBA)

	gopherImages = [...]image.Image{
		gopherNRGBA,
		gopherAlpha,
		gopherAlpha16,
		gopherGray,
		gopherGray16,
		gopherRGBA,
		gopherRGBA64,
		gopherNRGBA64,
		gopherCMYK,
	}
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/alecthomas/template"
)

var HELM_BIN = "/bin/helm"
var KUBECONFIG = "/root/.kube/kubeconfig"
var CONFIG = "/root/.kube/config"

type (
	// Config maps the params we need to run Helm
	Config struct {
		APIServer     string   `json:"api_server"`
		Token         string   `json:"token"`
		HelmCommand   []string `json:"helm_command"`
		SkipTLSVerify bool     `json:"tls_skip_verify"`
		Namespace     string   `json:"namespace"`
		Release       string   `json:"release"`
		Chart         string   `json:"chart"`
		Values        string   `json:"values"`
		ValuesFiles   string   `json:"values_files"`
		Debug         bool     `json:"debug"`
		DryRun        bool     `json:"dry_run"`
		Secrets       []string `json:"secrets"`
		Prefix        string   `json:"prefix"`
		TillerNs      string   `json:"tiller_ns"`
		Wait          bool     `json:"wait"`
		Upgrade       bool     `json:"upgrade"`
		ClientOnly    bool     `json:"client_only"`
	}
	// Plugin default
	Plugin struct {
		Config Config
	}
)

func setHelmHelp(p *Plugin) {
	p.Config.HelmCommand = []string{""}
}
func setDeleteEventCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "delete"
	upgrade[1] = p.Config.Release

	p.Config.HelmCommand = upgrade
}

func setPushEventCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "upgrade"
	upgrade[1] = "--install"

	if p.Config.Release != "" {
		upgrade = append(upgrade, p.Config.Release)
	}
	upgrade = append(upgrade, p.Config.Chart)
	if p.Config.Values != "" {
		upgrade = append(upgrade, "--set")
		upgrade = append(upgrade, p.Config.Values)
	}
	if p.Config.ValuesFiles != "" {
		for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") {
			upgrade = append(upgrade, "--values")
			upgrade = append(upgrade, valuesFile)
		}
	}
	if p.Config.Namespace != "" {
		upgrade = append(upgrade, "--namespace")
		upgrade = append(upgrade, p.Config.Namespace)
	}
	if p.Config.DryRun {
		upgrade = append(upgrade, "--dry-run")
	}
	if p.Config.Debug {
		upgrade = append(upgrade, "--debug")
	}
	if p.Config.Wait {
		upgrade = append(upgrade, "--wait")
	}
	p.Config.HelmCommand = upgrade

}

func setHelmCommand(p *Plugin) {
	buildEvent := os.Getenv("DRONE_BUILD_EVENT")
	switch buildEvent {
	case "push":
		setPushEventCommand(p)
	case "delete":
		setDeleteEventCommand(p)
	default:
		setHelmHelp(p)
	}

}

func doHelmInit(p *Plugin) []string {
	init := make([]string, 1)
	init[0] = "init"
	if p.Config.TillerNs != "" {
		init = append(init, "--tiller-namespace")
		init = append(init, p.Config.TillerNs)
	}
	if p.Config.ClientOnly {
		init = append(init, "--client-only")
	}
	if p.Config.Upgrade {
		init = append(init, "--upgrade")
	}

	return init

}

// Exec default method
func (p *Plugin) Exec() error {
	resolveSecrets(p)
	if p.Config.APIServer == "" {
		return fmt.Errorf("Error: API Server is needed to deploy.")
	}
	if p.Config.Token == "" {
		return fmt.Errorf("Error: Token is needed to deploy.")
	}
	initialiseKubeconfig(&p.Config, KUBECONFIG, CONFIG)

	if p.Config.Debug {
		p.debug()
	}

	init := doHelmInit(p)
	err := runCommand(init)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(init[:], " "))
	}
	setHelmCommand(p)

	if p.Config.Debug {
		log.Println("helm command: " + strings.Join(p.Config.HelmCommand[:], " "))
	}
	err = runCommand(p.Config.HelmCommand)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(p.Config.HelmCommand[:], " "))
	}
	return nil
}

func initialiseKubeconfig(params *Config, source string, target string) error {
	t, _ := template.ParseFiles(source)
	f, err := os.Create(target)
	err = t.Execute(f, params)
	f.Close()
	return err
}

func runCommand(params []string) error {
	cmd := new(exec.Cmd)
	cmd = exec.Command(HELM_BIN, params...)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	return err
}

func resolveSecrets(p *Plugin) {
	p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix)
	p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix)
	p.Config.Token = resolveEnvVar("${KUBERNETES_TOKEN}", p.Config.Prefix)
}

// getEnvVars will return [${TAG} {TAG} TAG]
func getEnvVars(envvars string) [][]string {
	re := regexp.MustCompile(`\$(\{?(\w+)\}?)\.?`)
	extracted := re.FindAllStringSubmatch(envvars, -1)
	return extracted
}

func resolveEnvVar(key string, prefix string) string {
	envvars := getEnvVars(key)
	return replaceEnvvars(envvars, prefix, key)
}

func replaceEnvvars(envvars [][]string, prefix string, s string) string {
	for _, envvar := range envvars {
		envvarName := envvar[0]
		envvarKey := envvar[2]
		envval := os.Getenv(prefix + "_" + envvarKey)
		if envval == "" {
			envval = os.Getenv(envvarKey)
		}
		if strings.Contains(s, envvarName) {
			s = strings.Replace(s, envvarName, envval, -1)
		}
	}
	return s
}

func (p *Plugin) debug() {
	fmt.Println(p)
	// debug env vars
	for _, e := range os.Environ() {
		fmt.Println("-Var:--", e)
	}
	// debug plugin obj
	fmt.Printf("Api server: %s \n", p.Config.APIServer)
	fmt.Printf("Values: %s \n", p.Config.Values)
	fmt.Printf("Secrets: %s \n", p.Config.Secrets)
	fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles)

	kubeconfig, err := ioutil.ReadFile(KUBECONFIG)
	if err == nil {
		fmt.Println(string(kubeconfig))
	}
	config, err := ioutil.ReadFile(CONFIG)
	if err == nil {
		fmt.Println(string(config))
	}

}
<commit_msg>Add support to push and deployment event<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"regexp"
	"strings"

	"github.com/alecthomas/template"
)

var HELM_BIN = "/bin/helm"
var KUBECONFIG = "/root/.kube/kubeconfig"
var CONFIG = "/root/.kube/config"

type (
	// Config maps the params we need to run Helm
	Config struct {
		APIServer     string   `json:"api_server"`
		Token         string   `json:"token"`
		HelmCommand   []string `json:"helm_command"`
		SkipTLSVerify bool     `json:"tls_skip_verify"`
		Namespace     string   `json:"namespace"`
		Release       string   `json:"release"`
		Chart         string   `json:"chart"`
		Values        string   `json:"values"`
		ValuesFiles   string   `json:"values_files"`
		Debug         bool     `json:"debug"`
		DryRun        bool     `json:"dry_run"`
		Secrets       []string `json:"secrets"`
		Prefix        string   `json:"prefix"`
		TillerNs      string   `json:"tiller_ns"`
		Wait          bool     `json:"wait"`
		Upgrade       bool     `json:"upgrade"`
		ClientOnly    bool     `json:"client_only"`
	}
	// Plugin default
	Plugin struct {
		Config Config
	}
)

func setHelmHelp(p *Plugin) {
	p.Config.HelmCommand = []string{""}
}
func setDeleteEventCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "delete"
	upgrade[1] = p.Config.Release

	p.Config.HelmCommand = upgrade
}

func setPushEventCommand(p *Plugin) {
	upgrade := make([]string, 2)
	upgrade[0] = "upgrade"
	upgrade[1] = "--install"

	if p.Config.Release != "" {
		upgrade = append(upgrade, p.Config.Release)
	}
	upgrade = append(upgrade, p.Config.Chart)
	if p.Config.Values != "" {
		upgrade = append(upgrade, "--set")
		upgrade = append(upgrade, p.Config.Values)
	}
	if p.Config.ValuesFiles != "" {
		for _, valuesFile := range strings.Split(p.Config.ValuesFiles, ",") {
			upgrade = append(upgrade, "--values")
			upgrade = append(upgrade, valuesFile)
		}
	}
	if p.Config.Namespace != "" {
		upgrade = append(upgrade, "--namespace")
		upgrade = append(upgrade, p.Config.Namespace)
	}
	if p.Config.DryRun {
		upgrade = append(upgrade, "--dry-run")
	}
	if p.Config.Debug {
		upgrade = append(upgrade, "--debug")
	}
	if p.Config.Wait {
		upgrade = append(upgrade, "--wait")
	}
	p.Config.HelmCommand = upgrade

}

func setHelmCommand(p *Plugin) {
	buildEvent := os.Getenv("DRONE_BUILD_EVENT")
	switch buildEvent {
	case "push":
		setPushEventCommand(p)
	case "tag":
		setPushEventCommand(p)
	case "deployment":
		setPushEventCommand(p)
	case "delete":
		setDeleteEventCommand(p)
	default:
		setHelmHelp(p)
	}

}

func doHelmInit(p *Plugin) []string {
	init := make([]string, 1)
	init[0] = "init"
	if p.Config.TillerNs != "" {
		init = append(init, "--tiller-namespace")
		init = append(init, p.Config.TillerNs)
	}
	if p.Config.ClientOnly {
		init = append(init, "--client-only")
	}
	if p.Config.Upgrade {
		init = append(init, "--upgrade")
	}

	return init

}

// Exec default method
func (p *Plugin) Exec() error {
	resolveSecrets(p)
	if p.Config.APIServer == "" {
		return fmt.Errorf("Error: API Server is needed to deploy.")
	}
	if p.Config.Token == "" {
		return fmt.Errorf("Error: Token is needed to deploy.")
	}
	initialiseKubeconfig(&p.Config, KUBECONFIG, CONFIG)

	if p.Config.Debug {
		p.debug()
	}

	init := doHelmInit(p)
	err := runCommand(init)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(init[:], " "))
	}
	setHelmCommand(p)

	if p.Config.Debug {
		log.Println("helm command: " + strings.Join(p.Config.HelmCommand[:], " "))
	}
	err = runCommand(p.Config.HelmCommand)
	if err != nil {
		return fmt.Errorf("Error running helm command: " + strings.Join(p.Config.HelmCommand[:], " "))
	}
	return nil
}

func initialiseKubeconfig(params *Config, source string, target string) error {
	t, _ := template.ParseFiles(source)
	f, err := os.Create(target)
	err = t.Execute(f, params)
	f.Close()
	return err
}

func runCommand(params []string) error {
	cmd := new(exec.Cmd)
	cmd = exec.Command(HELM_BIN, params...)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	return err
}

func resolveSecrets(p *Plugin) {
	p.Config.Values = resolveEnvVar(p.Config.Values, p.Config.Prefix)
	p.Config.APIServer = resolveEnvVar("${API_SERVER}", p.Config.Prefix)
	p.Config.Token = resolveEnvVar("${KUBERNETES_TOKEN}", p.Config.Prefix)
}

// getEnvVars will return [${TAG} {TAG} TAG]
func getEnvVars(envvars string) [][]string {
	re := regexp.MustCompile(`\$(\{?(\w+)\}?)\.?`)
	extracted := re.FindAllStringSubmatch(envvars, -1)
	return extracted
}

func resolveEnvVar(key string, prefix string) string {
	envvars := getEnvVars(key)
	return replaceEnvvars(envvars, prefix, key)
}

func replaceEnvvars(envvars [][]string, prefix string, s string) string {
	for _, envvar := range envvars {
		envvarName := envvar[0]
		envvarKey := envvar[2]
		envval := os.Getenv(prefix + "_" + envvarKey)
		if envval == "" {
			envval = os.Getenv(envvarKey)
		}
		if strings.Contains(s, envvarName) {
			s = strings.Replace(s, envvarName, envval, -1)
		}
	}
	return s
}

func (p *Plugin) debug() {
	fmt.Println(p)
	// debug env vars
	for _, e := range os.Environ() {
		fmt.Println("-Var:--", e)
	}
	// debug plugin obj
	fmt.Printf("Api server: %s \n", p.Config.APIServer)
	fmt.Printf("Values: %s \n", p.Config.Values)
	fmt.Printf("Secrets: %s \n", p.Config.Secrets)
	fmt.Printf("ValuesFiles: %s \n", p.Config.ValuesFiles)

	kubeconfig, err := ioutil.ReadFile(KUBECONFIG)
	if err == nil {
		fmt.Println(string(kubeconfig))
	}
	config, err := ioutil.ReadFile(CONFIG)
	if err == nil {
		fmt.Println(string(config))
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path/filepath"
	"regexp"

	dockerapi "github.com/docker/docker/api"
	dockerclient "github.com/docker/engine-api/client"
	"github.com/docker/go-plugins-helpers/authorization"
)

func newPlugin(dockerHost, certPath string, tlsVerify bool) (*novolume, error) {
	var transport *http.Transport
	if certPath != "" {
		tlsc := &tls.Config{}

		cert, err := tls.LoadX509KeyPair(filepath.Join(certPath, "cert.pem"), filepath.Join(certPath, "key.pem"))
		if err != nil {
			return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
		}

		tlsc.Certificates = append(tlsc.Certificates, cert)
		tlsc.InsecureSkipVerify = !tlsVerify
		transport = &http.Transport{
			TLSClientConfig: tlsc,
		}
	}

	client, err := dockerclient.NewClient(dockerHost, dockerapi.DefaultVersion.String(), transport, nil)
	if err != nil {
		return nil, err
	}
	return &novolume{client: client}, nil
}

var (
	startRegExp = regexp.MustCompile(`/containers/(.*)/start`)
)

type novolume struct {
	client *dockerclient.Client
}

func (p *novolume) AuthZReq(req authorization.Request) authorization.Response {
	ruri := url.QueryEscape(req.RequestURI)
	if req.RequestMethod == "POST" && startRegExp.MatchString(ruri) {
		// this is deprecated in docker, remove once hostConfig is dropped to
		// being available at start time
		if req.RequestBody != nil {
			type vfrom struct {
				VolumesFrom []string
			}
			vf := &vfrom{}
			if err := json.NewDecoder(bytes.NewReader(req.RequestBody)).Decode(vf); err != nil {
				return authorization.Response{Err: err.Error()}
			}
			if len(vf.VolumesFrom) > 0 {
				goto noallow
			}
		}
		res := startRegExp.FindStringSubmatch(ruri)
		if len(res) < 1 {
			return authorization.Response{Err: "unable to find container name"}
		}
		container, err := p.client.ContainerInspect(res[1])
		if err != nil {
			return authorization.Response{Err: err.Error()}
		}
		bindDests := []string{}
		for _, m := range container.Mounts {
			if m.Driver != "" {
				goto noallow
			}
			bindDests = append(bindDests, m.Destination)
		}
		image, _, err := p.client.ImageInspectWithRaw(container.Image, false)
		if err != nil {
			return authorization.Response{Err: err.Error()}
		}
		if len(bindDests) == 0 && len(image.Config.Volumes) > 0 {
			goto noallow
		}
		if len(image.Config.Volumes) > 0 {
			for _, bd := range bindDests {
				if _, ok := image.Config.Volumes[bd]; !ok {
					goto noallow
				}
			}
		}
		if len(container.HostConfig.VolumesFrom) > 0 {
			goto noallow
		}
		// TODO(runcom): FROM scratch ?!?!
	}
	return authorization.Response{Allow: true}

noallow:
	return authorization.Response{Msg: "volumes are not allowed"}
}

func (p *novolume) AuthZRes(req authorization.Request) authorization.Response {
	return authorization.Response{Allow: true}
}
<commit_msg>correctlly UNescape the request URI<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"path/filepath"
	"regexp"

	dockerapi "github.com/docker/docker/api"
	dockerclient "github.com/docker/engine-api/client"
	"github.com/docker/go-plugins-helpers/authorization"
)

func newPlugin(dockerHost, certPath string, tlsVerify bool) (*novolume, error) {
	var transport *http.Transport
	if certPath != "" {
		tlsc := &tls.Config{}

		cert, err := tls.LoadX509KeyPair(filepath.Join(certPath, "cert.pem"), filepath.Join(certPath, "key.pem"))
		if err != nil {
			return nil, fmt.Errorf("Error loading x509 key pair: %s", err)
		}

		tlsc.Certificates = append(tlsc.Certificates, cert)
		tlsc.InsecureSkipVerify = !tlsVerify
		transport = &http.Transport{
			TLSClientConfig: tlsc,
		}
	}

	client, err := dockerclient.NewClient(dockerHost, dockerapi.DefaultVersion.String(), transport, nil)
	if err != nil {
		return nil, err
	}
	return &novolume{client: client}, nil
}

var (
	startRegExp = regexp.MustCompile(`/containers/(.*)/start`)
)

type novolume struct {
	client *dockerclient.Client
}

func (p *novolume) AuthZReq(req authorization.Request) authorization.Response {
	ruri, err := url.QueryUnescape(req.RequestURI)
	if err != nil {
		return authorization.Response{Err: err.Error()}
	}
	if req.RequestMethod == "POST" && startRegExp.MatchString(ruri) {
		// this is deprecated in docker, remove once hostConfig is dropped to
		// being available at start time
		if req.RequestBody != nil {
			type vfrom struct {
				VolumesFrom []string
			}
			vf := &vfrom{}
			if err := json.NewDecoder(bytes.NewReader(req.RequestBody)).Decode(vf); err != nil {
				return authorization.Response{Err: err.Error()}
			}
			if len(vf.VolumesFrom) > 0 {
				goto noallow
			}
		}
		res := startRegExp.FindStringSubmatch(ruri)
		if len(res) < 1 {
			return authorization.Response{Err: "unable to find container name"}
		}
		container, err := p.client.ContainerInspect(res[1])
		if err != nil {
			return authorization.Response{Err: err.Error()}
		}
		bindDests := []string{}
		for _, m := range container.Mounts {
			if m.Driver != "" {
				goto noallow
			}
			bindDests = append(bindDests, m.Destination)
		}
		image, _, err := p.client.ImageInspectWithRaw(container.Image, false)
		if err != nil {
			return authorization.Response{Err: err.Error()}
		}
		if len(bindDests) == 0 && len(image.Config.Volumes) > 0 {
			goto noallow
		}
		if len(image.Config.Volumes) > 0 {
			for _, bd := range bindDests {
				if _, ok := image.Config.Volumes[bd]; !ok {
					goto noallow
				}
			}
		}
		if len(container.HostConfig.VolumesFrom) > 0 {
			goto noallow
		}
		// TODO(runcom): FROM scratch ?!?!
	}
	return authorization.Response{Allow: true}

noallow:
	return authorization.Response{Msg: "volumes are not allowed"}
}

func (p *novolume) AuthZRes(req authorization.Request) authorization.Response {
	return authorization.Response{Allow: true}
}
<|endoftext|>