<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package params

import "github.com/juju/juju/storage"

// MachineBlockDevices holds a machine tag and the block devices present
// on that machine.
type MachineBlockDevices struct {
	Machine      string                `json:"machine"`
	BlockDevices []storage.BlockDevice `json:"blockdevices,omitempty"`
}

// SetMachineBlockDevices holds the arguments for recording the block
// devices present on a set of machines.
type SetMachineBlockDevices struct {
	MachineBlockDevices []MachineBlockDevices `json:"machineblockdevices"`
}

// BlockDeviceResult holds the result of an API call to retrieve details
// of a block device.
type BlockDeviceResult struct {
	Result storage.BlockDevice `json:"result"`
	Error  *Error              `json:"error,omitempty"`
}

// BlockDeviceResults holds the result of an API call to retrieve details
// of multiple block devices.
type BlockDeviceResults struct {
	Results []BlockDeviceResult `json:"results,omitempty"`
}

// BlockDevicesResult holds the result of an API call to retrieve details
// of all block devices relating to some entity.
type BlockDevicesResult struct {
	Result []storage.BlockDevice `json:"result"`
	Error  *Error                `json:"error,omitempty"`
}

// BlockDevicseResults holds the result of an API call to retrieve details
// of all block devices relating to some entities.
type BlockDevicesResults struct {
	Results []BlockDevicesResult `json:"results,omitempty"`
}

// StorageInstance describes a storage instance.
type StorageInstance struct {
	StorageTag string
	OwnerTag   string
	Kind       StorageKind
}

// StorageKind is the kind of a storage instance.
type StorageKind int

const (
	StorageKindUnknown StorageKind = iota
	StorageKindBlock
	StorageKindFilesystem
)

// String returns representation of StorageKind for readability.
func (k *StorageKind) String() string {
	switch *k {
	case StorageKindBlock:
		return "block"
	case StorageKindFilesystem:
		return "filesystem"
	default:
		return "unknown"
	}
}

// StorageInstanceResult holds the result of an API call to retrieve details
// of a storage instance.
type StorageInstanceResult struct {
	Result StorageInstance `json:"result"`
	Error  *Error          `json:"error,omitempty"`
}

// StorageInstanceResults holds the result of an API call to retrieve details
// of multiple storage instances.
type StorageInstanceResults struct {
	Results []StorageInstanceResult `json:"results,omitempty"`
}

// StorageAttachment describes a unit's attached storage instance.
type StorageAttachment struct {
	StorageTag string
	OwnerTag   string
	UnitTag    string

	Kind     StorageKind
	Location string
	Life     Life
}

// StorageAttachmentId identifies a storage attachment by the tags of the
// related unit and storage instance.
type StorageAttachmentId struct {
	StorageTag string `json:"storagetag"`
	UnitTag    string `json:"unittag"`
}

// StorageAttachmentIds holds a set of storage attachment identifiers.
type StorageAttachmentIds struct {
	Ids []StorageAttachmentId `json:"ids"`
}

// StorageAttachmentsResult holds the result of an API call to retrieve details
// of a unit's attached storage instances.
type StorageAttachmentsResult struct {
	Result []StorageAttachment `json:"result"`
	Error  *Error              `json:"error,omitempty"`
}

// StorageAttachmentsResults holds the result of an API call to retrieve details
// of multiple units' attached storage instances.
type StorageAttachmentsResults struct {
	Results []StorageAttachmentsResult `json:"results,omitempty"`
}

// StorageAttachmentResult holds the result of an API call to retrieve details
// of a storage attachment.
type StorageAttachmentResult struct {
	Result StorageAttachment `json:"result"`
	Error  *Error            `json:"error,omitempty"`
}

// StorageAttachmentResults holds the result of an API call to retrieve details
// of multiple storage attachments.
type StorageAttachmentResults struct {
	Results []StorageAttachmentResult `json:"results,omitempty"`
}

// MachineStorageId identifies the attachment of a storage entity
// to a machine, by their tags.
type MachineStorageId struct {
	MachineTag string `json:"machinetag"`
	// AttachmentTag is the tag of the volume or filesystem whose
	// attachment to the machine is represented.
	AttachmentTag string `json:"attachmenttag"`
}

// MachineStorageIds holds a set of machine/storage-entity
// attachment identifiers.
type MachineStorageIds struct {
	Ids []MachineStorageId `json:"ids"`
}

// Volume describes a storage volume in the environment.
type Volume struct {
	VolumeTag string `json:"volumetag"`
	VolumeId  string `json:"volumeid"`
	Serial    string `json:"serial"`
	// Size is the size of the volume in MiB.
	Size       uint64 `json:"size"`
	Persistent bool   `json:"persistent"`
}

// Volumes describes a set of storage volumes in the environment.
type Volumes struct {
	Volumes []Volume `json:"volumes"`
}

// VolumeAttachment describes a volume attachment.
type VolumeAttachment struct {
	VolumeTag  string `json:"volumetag"`
	MachineTag string `json:"machinetag"`
	DeviceName string `json:"devicename,omitempty"`
	ReadOnly   bool   `json:"readonly"`
}

// VolumeAttachments describes a set of storage volume attachments.
type VolumeAttachments struct {
	VolumeAttachments []VolumeAttachment `json:"volumeattachments"`
}

// VolumeParams holds the parameters for creating a storage volume.
type VolumeParams struct {
	VolumeTag  string                  `json:"volumetag"`
	Size       uint64                  `json:"size"`
	Provider   string                  `json:"provider"`
	Attributes map[string]interface{}  `json:"attributes,omitempty"`
	Attachment *VolumeAttachmentParams `json:"attachment,omitempty"`
}

// VolumeAttachmentParams holds the parameters for creating a volume
// attachment.
type VolumeAttachmentParams struct {
	VolumeTag  string `json:"volumetag"`
	MachineTag string `json:"machinetag"`
	InstanceId string `json:"instanceid,omitempty"`
	VolumeId   string `json:"volumeid,omitempty"`
	Provider   string `json:"provider"`
}

// VolumePreparationInfo holds the information regarding preparing
// a storage volume for use.
type VolumePreparationInfo struct {
	NeedsFilesystem bool   `json:"needsfilesystem"`
	DevicePath      string `json:"devicepath"`
}

// VolumePreparationInfoResult holds a singular VolumePreparationInfo
// result, or an error.
type VolumePreparationInfoResult struct {
	Result VolumePreparationInfo `json:"result"`
	Error  *Error                `json:"error,omitempty"`
}

// VolumePreparationInfoResult holds a set of VolumePreparationInfoResults.
type VolumePreparationInfoResults struct {
	Results []VolumePreparationInfoResult `json:"results,omitempty"`
}

// VolumeAttachmentsResult holds the volume attachments for a single
// machine, or an error.
type VolumeAttachmentsResult struct {
	Attachments []VolumeAttachment `json:"attachments,omitempty"`
	Error       *Error             `json:"error,omitempty"`
}

// VolumeAttachmentsResults holds a set of VolumeAttachmentsResults for
// a set of machines.
type VolumeAttachmentsResults struct {
	Results []VolumeAttachmentsResult `json:"results,omitempty"`
}

// VolumeAttachmentResult holds the details of a single volume attachment,
// or an error.
type VolumeAttachmentResult struct {
	Result VolumeAttachment `json:"result"`
	Error  *Error           `json:"error,omitempty"`
}

// VolumeAttachmentResults holds a set of VolumeAttachmentResults.
type VolumeAttachmentResults struct {
	Results []VolumeAttachmentResult `json:"results,omitempty"`
}

// VolumeResult holds information about a volume.
type VolumeResult struct {
	Result Volume `json:"result"`
	Error  *Error `json:"error,omitempty"`
}

// VolumeResults holds information about multiple volumes.
type VolumeResults struct {
	Results []VolumeResult `json:"results,omitempty"`
}

// VolumeParamsResults holds provisioning parameters for a volume.
type VolumeParamsResult struct {
	Result VolumeParams `json:"result"`
	Error  *Error       `json:"error,omitempty"`
}

// VolumeParamsResults holds provisioning parameters for multiple volumes.
type VolumeParamsResults struct {
	Results []VolumeParamsResult `json:"results,omitempty"`
}

// VolumeAttachmentParamsResults holds provisioning parameters for a volume
// attachment.
type VolumeAttachmentParamsResult struct {
	Result VolumeAttachmentParams `json:"result"`
	Error  *Error                 `json:"error,omitempty"`
}

// VolumeAttachmentParamsResults holds provisioning parameters for multiple
// volume attachments.
type VolumeAttachmentParamsResults struct {
	Results []VolumeAttachmentParamsResult `json:"results,omitempty"`
}

// Filesystem describes a storage filesystem in the environment.
type Filesystem struct {
	FilesystemTag string `json:"filesystemtag"`
	FilesystemId  string `json:"filesystemid"`
	// Size is the size of the filesystem in MiB.
	Size uint64 `json:"size"`
}

// Filesystems describes a set of storage filesystems in the environment.
type Filesystems struct {
	Filesystems []Filesystem `json:"filesystems"`
}

// FilesystemAttachment describes a filesystem attachment.
type FilesystemAttachment struct {
	FilesystemTag string `json:"filesystemtag"`
	MachineTag    string `json:"machinetag"`
	MountPoint    string `json:"mountpoint,omitempty"`
}

// FilesystemAttachments describes a set of storage filesystem attachments.
type FilesystemAttachments struct {
	FilesystemAttachments []FilesystemAttachment `json:"filesystemattachments"`
}

// FilesystemParams holds the parameters for creating a storage filesystem.
type FilesystemParams struct {
	FilesystemTag string                      `json:"filesystemtag"`
	Size          uint64                      `json:"size"`
	Provider      string                      `json:"provider"`
	Attributes    map[string]interface{}      `json:"attributes,omitempty"`
	Attachment    *FilesystemAttachmentParams `json:"attachment,omitempty"`
}

// FilesystemAttachmentParams holds the parameters for creating a filesystem
// attachment.
type FilesystemAttachmentParams struct {
	FilesystemTag string `json:"filesystemtag"`
	MachineTag    string `json:"machinetag"`
	InstanceId    string `json:"instanceid,omitempty"`
	FilesystemId  string `json:"filesystemid,omitempty"`
	Provider      string `json:"provider"`
	MountPoint    string `json:"mountpoint,omitempty"`
}

// FilesystemAttachmentResult holds the details of a single filesystem attachment,
// or an error.
type FilesystemAttachmentResult struct {
	Result FilesystemAttachment `json:"result"`
	Error  *Error               `json:"error,omitempty"`
}

// FilesystemAttachmentResults holds a set of FilesystemAttachmentResults.
type FilesystemAttachmentResults struct {
	Results []FilesystemAttachmentResult `json:"results,omitempty"`
}

// FilesystemResult holds information about a filesystem.
type FilesystemResult struct {
	Result Filesystem `json:"result"`
	Error  *Error     `json:"error,omitempty"`
}

// FilesystemResults holds information about multiple filesystems.
type FilesystemResults struct {
	Results []FilesystemResult `json:"results,omitempty"`
}

// FilesystemParamsResults holds provisioning parameters for a filesystem.
type FilesystemParamsResult struct {
	Result FilesystemParams `json:"result"`
	Error  *Error           `json:"error,omitempty"`
}

// FilesystemParamsResults holds provisioning parameters for multiple filesystems.
type FilesystemParamsResults struct {
	Results []FilesystemParamsResult `json:"results,omitempty"`
}

// FilesystemAttachmentParamsResults holds provisioning parameters for a filesystem
// attachment.
type FilesystemAttachmentParamsResult struct {
	Result FilesystemAttachmentParams `json:"result"`
	Error  *Error                     `json:"error,omitempty"`
}

// FilesystemAttachmentParamsResults holds provisioning parameters for multiple
// filesystem attachments.
type FilesystemAttachmentParamsResults struct {
	Results []FilesystemAttachmentParamsResult `json:"results,omitempty"`
}

// StorageDetails holds information about storage.
type StorageDetails struct {

	// StorageTag holds tag for this storage.
	StorageTag string `json:"storagetag"`

	// OwnerTag holds tag for the owner of this storage, unit or service.
	OwnerTag string `json:"ownertag"`

	// Kind holds what kind of storage this instance is.
	Kind StorageKind `json:"kind"`

	// Status indicates storage status, e.g. pending, provisioned, attached.
	Status string `json:"status,omitempty"`

	// UnitTag holds tag for unit for attached instances.
	UnitTag string `json:"unittag,omitempty"`

	// Location holds location for provisioned attached instances.
	Location string `json:"location,omitempty"`

	// Persistent indicates whether the storage is persistent or not.
	Persistent bool `json:"persistent"`
}

// StorageDetailsResult holds information about a storage instance
// or error related to its retrieval.
type StorageDetailsResult struct {
	Result StorageDetails `json:"result"`
	Error  *Error         `json:"error,omitempty"`
}

// StorageDetailsResults holds results for storage details or related storage error.
type StorageDetailsResults struct {
	Results []StorageDetailsResult `json:"results,omitempty"`
}

// StorageInfo contains information about a storage as well as
// potentially an error related to information retrieval.
type StorageInfo struct {
	StorageDetails `json:"result"`
	Error          *Error `json:"error,omitempty"`
}

// StorageInfosResult holds storage details.
type StorageInfosResult struct {
	Results []StorageInfo `json:"results,omitempty"`
}

// StoragePool holds data for a pool instance.
type StoragePool struct {

	// Name is the pool's name.
	Name string `json:"name"`

	// Provider is the type of storage provider this pool represents, eg "loop", "ebs".
	Provider string `json:"provider"`

	// Attrs are the pool's configuration attributes.
	Attrs map[string]interface{} `json:"attrs"`
}

// StoragePoolFilter holds a filter for pool API call.
type StoragePoolFilter struct {

	// Names are pool's names to filter on.
	Names []string `json:"names,omitempty"`

	// Providers are pool's storage provider types to filter on.
	Providers []string `json:"providers,omitempty"`
}

// StoragePoolsResult holds a collection of pool instances.
type StoragePoolsResult struct {
	Results []StoragePool `json:"results,omitempty"`
}
<commit_msg>apiserver/params: remove VolumePreparationInfo<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package params

import "github.com/juju/juju/storage"

// MachineBlockDevices holds a machine tag and the block devices present
// on that machine.
type MachineBlockDevices struct {
	Machine      string                `json:"machine"`
	BlockDevices []storage.BlockDevice `json:"blockdevices,omitempty"`
}

// SetMachineBlockDevices holds the arguments for recording the block
// devices present on a set of machines.
type SetMachineBlockDevices struct {
	MachineBlockDevices []MachineBlockDevices `json:"machineblockdevices"`
}

// BlockDeviceResult holds the result of an API call to retrieve details
// of a block device.
type BlockDeviceResult struct {
	Result storage.BlockDevice `json:"result"`
	Error  *Error              `json:"error,omitempty"`
}

// BlockDeviceResults holds the result of an API call to retrieve details
// of multiple block devices.
type BlockDeviceResults struct {
	Results []BlockDeviceResult `json:"results,omitempty"`
}

// BlockDevicesResult holds the result of an API call to retrieve details
// of all block devices relating to some entity.
type BlockDevicesResult struct {
	Result []storage.BlockDevice `json:"result"`
	Error  *Error                `json:"error,omitempty"`
}

// BlockDevicseResults holds the result of an API call to retrieve details
// of all block devices relating to some entities.
type BlockDevicesResults struct {
	Results []BlockDevicesResult `json:"results,omitempty"`
}

// StorageInstance describes a storage instance.
type StorageInstance struct {
	StorageTag string
	OwnerTag   string
	Kind       StorageKind
}

// StorageKind is the kind of a storage instance.
type StorageKind int

const (
	StorageKindUnknown StorageKind = iota
	StorageKindBlock
	StorageKindFilesystem
)

// String returns representation of StorageKind for readability.
func (k *StorageKind) String() string {
	switch *k {
	case StorageKindBlock:
		return "block"
	case StorageKindFilesystem:
		return "filesystem"
	default:
		return "unknown"
	}
}

// StorageInstanceResult holds the result of an API call to retrieve details
// of a storage instance.
type StorageInstanceResult struct {
	Result StorageInstance `json:"result"`
	Error  *Error          `json:"error,omitempty"`
}

// StorageInstanceResults holds the result of an API call to retrieve details
// of multiple storage instances.
type StorageInstanceResults struct {
	Results []StorageInstanceResult `json:"results,omitempty"`
}

// StorageAttachment describes a unit's attached storage instance.
type StorageAttachment struct {
	StorageTag string
	OwnerTag   string
	UnitTag    string

	Kind     StorageKind
	Location string
	Life     Life
}

// StorageAttachmentId identifies a storage attachment by the tags of the
// related unit and storage instance.
type StorageAttachmentId struct {
	StorageTag string `json:"storagetag"`
	UnitTag    string `json:"unittag"`
}

// StorageAttachmentIds holds a set of storage attachment identifiers.
type StorageAttachmentIds struct {
	Ids []StorageAttachmentId `json:"ids"`
}

// StorageAttachmentsResult holds the result of an API call to retrieve details
// of a unit's attached storage instances.
type StorageAttachmentsResult struct {
	Result []StorageAttachment `json:"result"`
	Error  *Error              `json:"error,omitempty"`
}

// StorageAttachmentsResults holds the result of an API call to retrieve details
// of multiple units' attached storage instances.
type StorageAttachmentsResults struct {
	Results []StorageAttachmentsResult `json:"results,omitempty"`
}

// StorageAttachmentResult holds the result of an API call to retrieve details
// of a storage attachment.
type StorageAttachmentResult struct {
	Result StorageAttachment `json:"result"`
	Error  *Error            `json:"error,omitempty"`
}

// StorageAttachmentResults holds the result of an API call to retrieve details
// of multiple storage attachments.
type StorageAttachmentResults struct {
	Results []StorageAttachmentResult `json:"results,omitempty"`
}

// MachineStorageId identifies the attachment of a storage entity
// to a machine, by their tags.
type MachineStorageId struct {
	MachineTag string `json:"machinetag"`
	// AttachmentTag is the tag of the volume or filesystem whose
	// attachment to the machine is represented.
	AttachmentTag string `json:"attachmenttag"`
}

// MachineStorageIds holds a set of machine/storage-entity
// attachment identifiers.
type MachineStorageIds struct {
	Ids []MachineStorageId `json:"ids"`
}

// Volume describes a storage volume in the environment.
type Volume struct {
	VolumeTag string `json:"volumetag"`
	VolumeId  string `json:"volumeid"`
	Serial    string `json:"serial"`
	// Size is the size of the volume in MiB.
	Size       uint64 `json:"size"`
	Persistent bool   `json:"persistent"`
}

// Volumes describes a set of storage volumes in the environment.
type Volumes struct {
	Volumes []Volume `json:"volumes"`
}

// VolumeAttachment describes a volume attachment.
type VolumeAttachment struct {
	VolumeTag  string `json:"volumetag"`
	MachineTag string `json:"machinetag"`
	DeviceName string `json:"devicename,omitempty"`
	ReadOnly   bool   `json:"readonly"`
}

// VolumeAttachments describes a set of storage volume attachments.
type VolumeAttachments struct {
	VolumeAttachments []VolumeAttachment `json:"volumeattachments"`
}

// VolumeParams holds the parameters for creating a storage volume.
type VolumeParams struct {
	VolumeTag  string                  `json:"volumetag"`
	Size       uint64                  `json:"size"`
	Provider   string                  `json:"provider"`
	Attributes map[string]interface{}  `json:"attributes,omitempty"`
	Attachment *VolumeAttachmentParams `json:"attachment,omitempty"`
}

// VolumeAttachmentParams holds the parameters for creating a volume
// attachment.
type VolumeAttachmentParams struct {
	VolumeTag  string `json:"volumetag"`
	MachineTag string `json:"machinetag"`
	InstanceId string `json:"instanceid,omitempty"`
	VolumeId   string `json:"volumeid,omitempty"`
	Provider   string `json:"provider"`
}

// VolumeAttachmentsResult holds the volume attachments for a single
// machine, or an error.
type VolumeAttachmentsResult struct {
	Attachments []VolumeAttachment `json:"attachments,omitempty"`
	Error       *Error             `json:"error,omitempty"`
}

// VolumeAttachmentsResults holds a set of VolumeAttachmentsResults for
// a set of machines.
type VolumeAttachmentsResults struct {
	Results []VolumeAttachmentsResult `json:"results,omitempty"`
}

// VolumeAttachmentResult holds the details of a single volume attachment,
// or an error.
type VolumeAttachmentResult struct {
	Result VolumeAttachment `json:"result"`
	Error  *Error           `json:"error,omitempty"`
}

// VolumeAttachmentResults holds a set of VolumeAttachmentResults.
type VolumeAttachmentResults struct {
	Results []VolumeAttachmentResult `json:"results,omitempty"`
}

// VolumeResult holds information about a volume.
type VolumeResult struct {
	Result Volume `json:"result"`
	Error  *Error `json:"error,omitempty"`
}

// VolumeResults holds information about multiple volumes.
type VolumeResults struct {
	Results []VolumeResult `json:"results,omitempty"`
}

// VolumeParamsResults holds provisioning parameters for a volume.
type VolumeParamsResult struct {
	Result VolumeParams `json:"result"`
	Error  *Error       `json:"error,omitempty"`
}

// VolumeParamsResults holds provisioning parameters for multiple volumes.
type VolumeParamsResults struct {
	Results []VolumeParamsResult `json:"results,omitempty"`
}

// VolumeAttachmentParamsResults holds provisioning parameters for a volume
// attachment.
type VolumeAttachmentParamsResult struct {
	Result VolumeAttachmentParams `json:"result"`
	Error  *Error                 `json:"error,omitempty"`
}

// VolumeAttachmentParamsResults holds provisioning parameters for multiple
// volume attachments.
type VolumeAttachmentParamsResults struct {
	Results []VolumeAttachmentParamsResult `json:"results,omitempty"`
}

// Filesystem describes a storage filesystem in the environment.
type Filesystem struct {
	FilesystemTag string `json:"filesystemtag"`
	FilesystemId  string `json:"filesystemid"`
	// Size is the size of the filesystem in MiB.
	Size uint64 `json:"size"`
}

// Filesystems describes a set of storage filesystems in the environment.
type Filesystems struct {
	Filesystems []Filesystem `json:"filesystems"`
}

// FilesystemAttachment describes a filesystem attachment.
type FilesystemAttachment struct {
	FilesystemTag string `json:"filesystemtag"`
	MachineTag    string `json:"machinetag"`
	MountPoint    string `json:"mountpoint,omitempty"`
}

// FilesystemAttachments describes a set of storage filesystem attachments.
type FilesystemAttachments struct {
	FilesystemAttachments []FilesystemAttachment `json:"filesystemattachments"`
}

// FilesystemParams holds the parameters for creating a storage filesystem.
type FilesystemParams struct {
	FilesystemTag string                      `json:"filesystemtag"`
	Size          uint64                      `json:"size"`
	Provider      string                      `json:"provider"`
	Attributes    map[string]interface{}      `json:"attributes,omitempty"`
	Attachment    *FilesystemAttachmentParams `json:"attachment,omitempty"`
}

// FilesystemAttachmentParams holds the parameters for creating a filesystem
// attachment.
type FilesystemAttachmentParams struct {
	FilesystemTag string `json:"filesystemtag"`
	MachineTag    string `json:"machinetag"`
	InstanceId    string `json:"instanceid,omitempty"`
	FilesystemId  string `json:"filesystemid,omitempty"`
	Provider      string `json:"provider"`
	MountPoint    string `json:"mountpoint,omitempty"`
}

// FilesystemAttachmentResult holds the details of a single filesystem attachment,
// or an error.
type FilesystemAttachmentResult struct {
	Result FilesystemAttachment `json:"result"`
	Error  *Error               `json:"error,omitempty"`
}

// FilesystemAttachmentResults holds a set of FilesystemAttachmentResults.
type FilesystemAttachmentResults struct {
	Results []FilesystemAttachmentResult `json:"results,omitempty"`
}

// FilesystemResult holds information about a filesystem.
type FilesystemResult struct {
	Result Filesystem `json:"result"`
	Error  *Error     `json:"error,omitempty"`
}

// FilesystemResults holds information about multiple filesystems.
type FilesystemResults struct {
	Results []FilesystemResult `json:"results,omitempty"`
}

// FilesystemParamsResults holds provisioning parameters for a filesystem.
type FilesystemParamsResult struct {
	Result FilesystemParams `json:"result"`
	Error  *Error           `json:"error,omitempty"`
}

// FilesystemParamsResults holds provisioning parameters for multiple filesystems.
type FilesystemParamsResults struct {
	Results []FilesystemParamsResult `json:"results,omitempty"`
}

// FilesystemAttachmentParamsResults holds provisioning parameters for a filesystem
// attachment.
type FilesystemAttachmentParamsResult struct {
	Result FilesystemAttachmentParams `json:"result"`
	Error  *Error                     `json:"error,omitempty"`
}

// FilesystemAttachmentParamsResults holds provisioning parameters for multiple
// filesystem attachments.
type FilesystemAttachmentParamsResults struct {
	Results []FilesystemAttachmentParamsResult `json:"results,omitempty"`
}

// StorageDetails holds information about storage.
type StorageDetails struct {

	// StorageTag holds tag for this storage.
	StorageTag string `json:"storagetag"`

	// OwnerTag holds tag for the owner of this storage, unit or service.
	OwnerTag string `json:"ownertag"`

	// Kind holds what kind of storage this instance is.
	Kind StorageKind `json:"kind"`

	// Status indicates storage status, e.g. pending, provisioned, attached.
	Status string `json:"status,omitempty"`

	// UnitTag holds tag for unit for attached instances.
	UnitTag string `json:"unittag,omitempty"`

	// Location holds location for provisioned attached instances.
	Location string `json:"location,omitempty"`

	// Persistent indicates whether the storage is persistent or not.
	Persistent bool `json:"persistent"`
}

// StorageDetailsResult holds information about a storage instance
// or error related to its retrieval.
type StorageDetailsResult struct {
	Result StorageDetails `json:"result"`
	Error  *Error         `json:"error,omitempty"`
}

// StorageDetailsResults holds results for storage details or related storage error.
type StorageDetailsResults struct {
	Results []StorageDetailsResult `json:"results,omitempty"`
}

// StorageInfo contains information about a storage as well as
// potentially an error related to information retrieval.
type StorageInfo struct {
	StorageDetails `json:"result"`
	Error          *Error `json:"error,omitempty"`
}

// StorageInfosResult holds storage details.
type StorageInfosResult struct {
	Results []StorageInfo `json:"results,omitempty"`
}

// StoragePool holds data for a pool instance.
type StoragePool struct {

	// Name is the pool's name.
	Name string `json:"name"`

	// Provider is the type of storage provider this pool represents, eg "loop", "ebs".
	Provider string `json:"provider"`

	// Attrs are the pool's configuration attributes.
	Attrs map[string]interface{} `json:"attrs"`
}

// StoragePoolFilter holds a filter for pool API call.
type StoragePoolFilter struct {

	// Names are pool's names to filter on.
	Names []string `json:"names,omitempty"`

	// Providers are pool's storage provider types to filter on.
	Providers []string `json:"providers,omitempty"`
}

// StoragePoolsResult holds a collection of pool instances.
type StoragePoolsResult struct {
	Results []StoragePool `json:"results,omitempty"`
}
<|endoftext|>


<commit_before>package blockdiag

import (
	"strings"
	"testing"
)

func TestShouldParser(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
		nodes       []string
		edges       []string
	}{
		{
			"Empty diagram",
			`
blockdiag {}
`,
			[]string{},
			[]string{},
		},
		{
			"Single Node",
			`
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			"Node chain",
			`
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
		},
		{
			"Multiple chains, using same nodes",
			`
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
		},
		{
			"Self reference",
			`
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("%s: parse error: %t with input %s", test.description, err, test.input)
		}
		gotDiag := got.(Diag)
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("%s: nodes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("%s edges error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}
	}
}
<commit_msg>Added should not parse tests.<commit_after>package blockdiag

import (
	"strings"
	"testing"
)

func TestShouldParser(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
		nodes       []string
		edges       []string
	}{
		{
			"Empty diagram",
			`
blockdiag {}
`,
			[]string{},
			[]string{},
		},
		{
			"Single Node",
			`
blockdiag {
	A;
}
`,
			[]string{"A"},
			[]string{},
		},
		{
			// TODO Add test case for node chain without tailing ;
			"Node chain",
			`
blockdiag {
	A -> B;
}
`,
			[]string{"A", "B"},
			[]string{"A|B"},
		},
		{
			"Multiple chains, using same nodes",
			`
blockdiag {
	A -> B -> C;
	A -> D;
}
`,
			[]string{"A", "B", "C", "D"},
			[]string{"A|B", "A|D", "B|C"},
		},
		{
			"Self reference",
			`
blockdiag {
	A -> A;
}
`,
			[]string{"A"},
			[]string{"A|A"},
		},
	} {
		got, err := ParseReader("shouldparse.diag", strings.NewReader(test.input))
		if err != nil {
			t.Fatalf("%s: parse error: %t with input %s", test.description, err, test.input)
		}
		gotDiag := got.(Diag)
		if gotDiag.NodesString() != strings.Join(test.nodes, ", ") {
			t.Fatalf("%s: nodes error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.nodes, ", "), gotDiag.NodesString())
		}
		if gotDiag.EdgesString() != strings.Join(test.edges, ", ") {
			t.Fatalf("%s edges error: %s, expected '%s', got: '%s'", test.description, test.input, strings.Join(test.edges, ", "), gotDiag.EdgesString())
		}
	}
}

func TestShouldNotParse(t *testing.T) {
	for _, test := range []struct {
		description string
		input       string
	}{
		{
			"No block",
			`
blockdiag
`,
		},
	} {
		_, err := ParseReader("shouldnotparse.diag", strings.NewReader(test.input))
		if err == nil {
			t.Fatalf("%s: should not parse, but didn't give an error with input %s", test.description, test.input)
		}
	}
}
<|endoftext|>


<commit_before>package slack

import (
	"os"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/rusenask/keel/constants"
	"github.com/rusenask/keel/extension/notification"
	"github.com/rusenask/keel/types"

	log "github.com/Sirupsen/logrus"
)

const timeout = 5 * time.Second

type sender struct {
	slackClient *slack.Client
	channels    []string
	botName     string
}

func init() {
	notification.RegisterSender("slack", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
	var token string
	// Get configuration
	if os.Getenv(constants.EnvSlackToken) != "" {
		token = os.Getenv(constants.EnvSlackToken)
	} else {
		return false, nil
	}
	if os.Getenv(constants.EnvSlackBotName) != "" {
		s.botName = os.Getenv(constants.EnvSlackBotName)
	} else {
		s.botName = "keel"
	}

	if os.Getenv(constants.EnvSlackChannels) != "" {
		channels := os.Getenv(constants.EnvSlackChannels)
		s.channels = strings.Split(channels, ",")
	} else {
		s.channels = []string{"general"}
	}

	s.slackClient = slack.New(token)

	log.WithFields(log.Fields{
		"name": "slack",
	}).Info("extension.notification.slack: sender configured")

	return true, nil
}

func (s *sender) Send(event types.EventNotification) error {
	params := slack.NewPostMessageParameters()
	params.Username = s.botName
	for _, channel := range s.channels {
		_, _, err := s.slackClient.PostMessage(channel, event.Message, params)
		if err != nil {
			log.WithFields(log.Fields{
				"error":   err,
				"channel": channel,
			}).Error("extension.notification.slack: failed to send notification")
		}
	}
	return nil
}
<commit_msg>notification format<commit_after>package slack

import (
	"encoding/json"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/nlopes/slack"

	"github.com/rusenask/keel/constants"
	"github.com/rusenask/keel/extension/notification"
	"github.com/rusenask/keel/types"

	log "github.com/Sirupsen/logrus"
)

const timeout = 5 * time.Second

type sender struct {
	slackClient *slack.Client
	channels    []string
	botName     string
}

func init() {
	notification.RegisterSender("slack", &sender{})
}

func (s *sender) Configure(config *notification.Config) (bool, error) {
	var token string
	// Get configuration
	if os.Getenv(constants.EnvSlackToken) != "" {
		token = os.Getenv(constants.EnvSlackToken)
	} else {
		return false, nil
	}
	if os.Getenv(constants.EnvSlackBotName) != "" {
		s.botName = os.Getenv(constants.EnvSlackBotName)
	} else {
		s.botName = "keel"
	}

	if os.Getenv(constants.EnvSlackChannels) != "" {
		channels := os.Getenv(constants.EnvSlackChannels)
		s.channels = strings.Split(channels, ",")
	} else {
		s.channels = []string{"general"}
	}

	s.slackClient = slack.New(token)

	log.WithFields(log.Fields{
		"name": "slack",
	}).Info("extension.notification.slack: sender configured")

	return true, nil
}

func (s *sender) Send(event types.EventNotification) error {
	params := slack.NewPostMessageParameters()
	params.Username = s.botName

	params.Attachments = []slack.Attachment{
		slack.Attachment{
			Fallback: event.Message,
			Color:    event.Level.Color(),
			Fields: []slack.AttachmentField{
				slack.AttachmentField{
					Title: event.Type.String(),
					Value: event.Message,
					Short: false,
				},
			},
			Footer: "keel.sh",
			Ts:     json.Number(strconv.Itoa(int(event.CreatedAt.Unix()))),
		},
	}

	for _, channel := range s.channels {
		_, _, err := s.slackClient.PostMessage(channel, "", params)
		if err != nil {
			log.WithFields(log.Fields{
				"error":   err,
				"channel": channel,
			}).Error("extension.notification.slack: failed to send notification")
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Package parse contains a collection of parsers for various formats in its subpackages.
package parse // import "github.com/tdewolff/parse"

import (
	"encoding/base64"
	"errors"
	"math"
	"net/url"
)

// Returned by DataURI when the byte slice does not start with 'data:' or is too short.
var ErrBadDataURI = errors.New("not a data URI")

// Number returns the number of bytes that parse as a number of the regex format (+|-)?([0-9]+(\.[0-9]+)?|\.[0-9]+)((e|E)(+|-)?[0-9]+)?.
func Number(b []byte) int {
	if len(b) == 0 {
		return 0
	}
	i := 0
	if b[i] == '+' || b[i] == '-' {
		i++
		if i >= len(b) {
			return 0
		}
	}
	firstDigit := (b[i] >= '0' && b[i] <= '9')
	if firstDigit {
		i++
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	if i < len(b) && b[i] == '.' {
		i++
		if i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
			for i < len(b) && b[i] >= '0' && b[i] <= '9' {
				i++
			}
		} else if firstDigit {
			// . could belong to the next token
			i--
			return i
		} else {
			return 0
		}
	} else if !firstDigit {
		return 0
	}
	iOld := i
	if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
		i++
		if i < len(b) && (b[i] == '+' || b[i] == '-') {
			i++
		}
		if i >= len(b) || b[i] < '0' || b[i] > '9' {
			// e could belong to next token
			return iOld
		}
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	return i
}

func Dimension(b []byte) (int, int) {
	num := Number(b)
	if num == 0 || num == len(b) {
		return num, 0
	} else if b[num] == '%' {
		return num, 1
	} else if b[num] >= 'a' && b[num] <= 'z' || b[num] >= 'A' && b[num] <= 'Z' {
		i := num + 1
		for i < len(b) && (b[i] >= 'a' && b[i] <= 'z' || b[i] >= 'A' && b[i] <= 'Z') {
			i++
		}
		return num, i - num
	}
	return num, 0
}

// Int parses a byte-slice and returns the integer it represents
func Int(b []byte) (int64, bool) {
	i := int64(0)
	neg := false
	for _, c := range b {
		if c == '-' {
			neg = true
		} else if i+1 > math.MaxInt64/10 {
			return 0, false
		} else {
			i *= 10
			i += int64(c - '0')
		}
	}
	if neg {
		return -i, true
	}
	return i, true
}

// DataURI parses the given data URI and returns the mediatype, data and ok.
func DataURI(dataURI []byte) ([]byte, []byte, error) {
	if len(dataURI) > 5 && Equal(dataURI[:5], []byte("data:")) {
		dataURI = dataURI[5:]
		inBase64 := false
		mediatype := []byte{}
		i := 0
		for j, c := range dataURI {
			if c == '=' || c == ';' || c == ',' {
				if c != '=' && Equal(Trim(dataURI[i:j], IsWhitespace), []byte("base64")) {
					if len(mediatype) > 0 {
						mediatype = mediatype[:len(mediatype)-1]
					}
					inBase64 = true
					i = j
				} else if c != ',' {
					mediatype = append(append(mediatype, Trim(dataURI[i:j], IsWhitespace)...), c)
					i = j + 1
				} else {
					mediatype = append(mediatype, Trim(dataURI[i:j], IsWhitespace)...)
				}
				if c == ',' {
					if len(mediatype) == 0 || mediatype[0] == ';' {
						mediatype = []byte("text/plain")
					}
					data := dataURI[j+1:]
					if inBase64 {
						decoded := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
						n, err := base64.StdEncoding.Decode(decoded, data)
						if err != nil {
							return []byte{}, []byte{}, err
						}
						data = decoded[:n]
					} else if unescaped, err := url.QueryUnescape(string(data)); err == nil {
						data = []byte(unescaped)
					}
					return mediatype, data, nil
				}
			}
		}
	}
	return []byte{}, []byte{}, ErrBadDataURI
}

// QuoteEntity parses the given byte slice and returns the quote that got matched (' or "), its entity length and ok.
func QuoteEntity(b []byte) (quote byte, n int) {
	if len(b) < 5 || b[0] != '&' {
		return 0, 0
	}
	if b[1] == '#' {
		if b[2] == 'x' {
			i := 3
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '2' && b[i+2] == ';' {
				if b[i+1] == '2' {
					return '"', i + 3 // "
				} else if b[i+1] == '7' {
					return '\'', i + 3 // '
				}
			}
		} else {
			i := 2
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '3' && b[i+2] == ';' {
				if b[i+1] == '4' {
					return '"', i + 3 // "
				} else if b[i+1] == '9' {
					return '\'', i + 3 // '
				}
			}
		}
	} else if len(b) >= 6 && b[5] == ';' {
		if EqualFold(b[1:5], []byte{'q', 'u', 'o', 't'}) {
			return '"', 6 // "
		} else if EqualFold(b[1:5], []byte{'a', 'p', 'o', 's'}) {
			return '\'', 6 // '
		}
	}
	return 0, 0
}
<commit_msg>Robustness added for bytes to int<commit_after>// Package parse contains a collection of parsers for various formats in its subpackages.
package parse // import "github.com/tdewolff/parse"

import (
	"encoding/base64"
	"errors"
	"math"
	"net/url"
)

// Returned by DataURI when the byte slice does not start with 'data:' or is too short.
var ErrBadDataURI = errors.New("not a data URI")

// Number returns the number of bytes that parse as a number of the regex format (+|-)?([0-9]+(\.[0-9]+)?|\.[0-9]+)((e|E)(+|-)?[0-9]+)?.
func Number(b []byte) int {
	if len(b) == 0 {
		return 0
	}
	i := 0
	if b[i] == '+' || b[i] == '-' {
		i++
		if i >= len(b) {
			return 0
		}
	}
	firstDigit := (b[i] >= '0' && b[i] <= '9')
	if firstDigit {
		i++
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	if i < len(b) && b[i] == '.' {
		i++
		if i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
			for i < len(b) && b[i] >= '0' && b[i] <= '9' {
				i++
			}
		} else if firstDigit {
			// . could belong to the next token
			i--
			return i
		} else {
			return 0
		}
	} else if !firstDigit {
		return 0
	}
	iOld := i
	if i < len(b) && (b[i] == 'e' || b[i] == 'E') {
		i++
		if i < len(b) && (b[i] == '+' || b[i] == '-') {
			i++
		}
		if i >= len(b) || b[i] < '0' || b[i] > '9' {
			// e could belong to next token
			return iOld
		}
		for i < len(b) && b[i] >= '0' && b[i] <= '9' {
			i++
		}
	}
	return i
}

func Dimension(b []byte) (int, int) {
	num := Number(b)
	if num == 0 || num == len(b) {
		return num, 0
	} else if b[num] == '%' {
		return num, 1
	} else if b[num] >= 'a' && b[num] <= 'z' || b[num] >= 'A' && b[num] <= 'Z' {
		i := num + 1
		for i < len(b) && (b[i] >= 'a' && b[i] <= 'z' || b[i] >= 'A' && b[i] <= 'Z') {
			i++
		}
		return num, i - num
	}
	return num, 0
}

// Int parses a byte-slice and returns the integer it represents
func Int(b []byte) (int64, bool) {
	i := int64(0)
	neg := false
	for _, c := range b {
		if c == '-' {
			neg = true
		} else if i+1 > math.MaxInt64/10 {
			return 0, false
		} else if c >= '0' && c <= '9' {
			i *= 10
			i += int64(c - '0')
		} else {
			return 0, false
		}
	}
	if neg {
		return -i, true
	}
	return i, true
}

// DataURI parses the given data URI and returns the mediatype, data and ok.
func DataURI(dataURI []byte) ([]byte, []byte, error) {
	if len(dataURI) > 5 && Equal(dataURI[:5], []byte("data:")) {
		dataURI = dataURI[5:]
		inBase64 := false
		mediatype := []byte{}
		i := 0
		for j, c := range dataURI {
			if c == '=' || c == ';' || c == ',' {
				if c != '=' && Equal(Trim(dataURI[i:j], IsWhitespace), []byte("base64")) {
					if len(mediatype) > 0 {
						mediatype = mediatype[:len(mediatype)-1]
					}
					inBase64 = true
					i = j
				} else if c != ',' {
					mediatype = append(append(mediatype, Trim(dataURI[i:j], IsWhitespace)...), c)
					i = j + 1
				} else {
					mediatype = append(mediatype, Trim(dataURI[i:j], IsWhitespace)...)
				}
				if c == ',' {
					if len(mediatype) == 0 || mediatype[0] == ';' {
						mediatype = []byte("text/plain")
					}
					data := dataURI[j+1:]
					if inBase64 {
						decoded := make([]byte, base64.StdEncoding.DecodedLen(len(data)))
						n, err := base64.StdEncoding.Decode(decoded, data)
						if err != nil {
							return []byte{}, []byte{}, err
						}
						data = decoded[:n]
					} else if unescaped, err := url.QueryUnescape(string(data)); err == nil {
						data = []byte(unescaped)
					}
					return mediatype, data, nil
				}
			}
		}
	}
	return []byte{}, []byte{}, ErrBadDataURI
}

// QuoteEntity parses the given byte slice and returns the quote that got matched (' or "), its entity length and ok.
func QuoteEntity(b []byte) (quote byte, n int) {
	if len(b) < 5 || b[0] != '&' {
		return 0, 0
	}
	if b[1] == '#' {
		if b[2] == 'x' {
			i := 3
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '2' && b[i+2] == ';' {
				if b[i+1] == '2' {
					return '"', i + 3 // "
				} else if b[i+1] == '7' {
					return '\'', i + 3 // '
				}
			}
		} else {
			i := 2
			for i < len(b) && b[i] == '0' {
				i++
			}
			if i+2 < len(b) && b[i] == '3' && b[i+2] == ';' {
				if b[i+1] == '4' {
					return '"', i + 3 // "
				} else if b[i+1] == '9' {
					return '\'', i + 3 // '
				}
			}
		}
	} else if len(b) >= 6 && b[5] == ';' {
		if EqualFold(b[1:5], []byte{'q', 'u', 'o', 't'}) {
			return '"', 6 // "
		} else if EqualFold(b[1:5], []byte{'a', 'p', 'o', 's'}) {
			return '\'', 6 // '
		}
	}
	return 0, 0
}
<|endoftext|>


<commit_before>package mongodb

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"

	"github.com/microservices-demo/user/users"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var (
	name                string
	password            string
	host                string
	db                  = "users"
	ErrInvalidHexID     = errors.New("Invalid Id Hex")
	ErrorSavingCardData = errors.New("There was a problem saving some card data")
	ErrorSavingAddrData = errors.New("There was a problem saving some address data")
)

func init() {
	flag.StringVar(&name, "mongo-user", os.Getenv("MONGO_USER"), "Mongo user")
	flag.StringVar(&password, "mongo-password", os.Getenv("MONGO_PASS"), "Mongo password")
	flag.StringVar(&host, "mongo-host", os.Getenv("MONGO_HOST"), "Mongo host")
}

type Mongo struct {
	Session *mgo.Session
}

type MongoUser struct {
	users.User `bson:",inline"`
	ID         bson.ObjectId   `bson:"_id"`
	AddressIDs []bson.ObjectId `bson:"addresses"`
	CardIDs    []bson.ObjectId `bson:"cards"`
}

func New() MongoUser {
	u := users.New()
	return MongoUser{
		User:       u,
		AddressIDs: make([]bson.ObjectId, 0),
		CardIDs:    make([]bson.ObjectId, 0),
	}
}

func (mu *MongoUser) AddUserIDs() {
	if mu.User.Addresses == nil {
		mu.User.Addresses = make([]users.Address, 0)
	}
	for _, id := range mu.AddressIDs {
		mu.User.Addresses = append(mu.User.Addresses, users.Address{
			ID: id.Hex(),
		})
	}
	if mu.User.Cards == nil {
		mu.User.Cards = make([]users.Card, 0)
	}
	for _, id := range mu.CardIDs {
		mu.User.Cards = append(mu.User.Cards, users.Card{ID: id.Hex()})
	}
	mu.User.UserID = mu.ID.Hex()
}

type MongoAddress struct {
	users.Address `bson:",inline"`
	ID            bson.ObjectId `bson:"_id"`
}

func (m *MongoAddress) AddID() {
	m.Address.ID = m.ID.Hex()
}

type MongoCard struct {
	users.Card `bson:",inline"`
	ID         bson.ObjectId `bson:"_id"`
}

func (m *MongoCard) AddID() {
	m.Card.ID = m.ID.Hex()
}

func (m *Mongo) Init() error {
	u := getURL()
	var err error
	m.Session, err = mgo.Dial(u.String())
	if err != nil {
		return err
	}
	return m.EnsureIndexes()
}

func (m *Mongo) CreateUser(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	id := bson.NewObjectId()
	mu := New()
	mu.User = *u
	mu.ID = id
	var carderr error
	var addrerr error
	mu.CardIDs, carderr = m.createCards(u.Cards)
	mu.AddressIDs, addrerr = m.createAddresses(u.Addresses)
	c := s.DB("").C("customers")
	_, err := c.UpsertId(mu.ID, mu)
	if err != nil {
		// Gonna clean up if we can, ignore error
		// because the user save error takes precedence.
		m.cleanAttributes(mu)
		return err
	}
	mu.User.UserID = mu.ID.Hex()
	// Cheap err for attributes
	if carderr != nil || addrerr != nil {
		return fmt.Errorf("%v %v", carderr, addrerr)
	}
	u = &mu.User
	return nil
}

func (m *Mongo) createCards(cs []users.Card) ([]bson.ObjectId, error) {
	s := m.Session.Copy()
	ids := make([]bson.ObjectId, 0)
	defer s.Close()
	for k, ca := range cs {
		id := bson.NewObjectId()
		mc := MongoCard{Card: ca, ID: id}
		c := s.DB("").C("cards")
		_, err := c.UpsertId(mc.ID, mc)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		cs[k].ID = id.Hex()
	}
	return ids, nil
}

func (m *Mongo) createAddresses(as []users.Address) ([]bson.ObjectId, error) {
	ids := make([]bson.ObjectId, 0)
	s := m.Session.Copy()
	defer s.Close()
	for k, a := range as {
		id := bson.NewObjectId()
		ma := MongoAddress{Address: a, ID: id}
		c := s.DB("").C("addresses")
		_, err := c.UpsertId(ma.ID, ma)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		as[k].ID = id.Hex()
	}
	return ids, nil
}

func (m *Mongo) cleanAttributes(mu MongoUser) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	_, err := c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.AddressIDs}})
	c = s.DB("").C("cards")
	_, err = c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.CardIDs}})
	return err
}

func (m *Mongo) appendAttributeId(attr string, id bson.ObjectId, userid string) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C(attr)
	return c.Update(bson.M{"_id": bson.ObjectIdHex(userid)},
		bson.M{"$addToSet": bson.M{"addresses": id}})
}

func (m *Mongo) GetUserByName(name string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("customers")
	mu := New()
	err := c.Find(bson.M{"username": name}).One(&mu)
	mu.AddUserIDs()
	return mu.User, err
}

func (m *Mongo) GetUser(id string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.New(), errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("customers")
	mu := New()
	err := c.FindId(bson.ObjectIdHex(id)).One(&mu)
	mu.AddUserIDs()
	return mu.User, err
}

func (m *Mongo) GetUsers() ([]users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("customers")
	var mus []MongoUser
	err := c.Find(nil).All(&mus)
	us := make([]users.User, 0)
	for _, mu := range mus {
		mu.AddUserIDs()
		us = append(us, mu.User)
	}
	return us, err
}

func (m *Mongo) GetUserAttributes(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	ids := make([]bson.ObjectId, 0)
	for _, a := range u.Addresses {
		if !bson.IsObjectIdHex(a.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(a.ID))
	}
	var ma []MongoAddress
	c := s.DB("").C("addresses")
	err := c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&ma)
	if err != nil {
		return err
	}
	na := make([]users.Address, 0)
	for _, a := range ma {
		a.Address.ID = a.ID.Hex()
		na = append(na, a.Address)
	}
	u.Addresses = na

	ids = make([]bson.ObjectId, 0)
	for _, c := range u.Cards {
		if !bson.IsObjectIdHex(c.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(c.ID))
	}
	var mc []MongoCard
	c = s.DB("").C("cards")
	err = c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&mc)
	if err != nil {
		return err
	}

	nc := make([]users.Card, 0)
	for _, ca := range mc {
		ca.Card.ID = ca.ID.Hex()
		nc = append(nc, ca.Card)
	}
	u.Cards = nc
	return nil
}

func (m *Mongo) GetCard(id string) (users.Card, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.Card{}, errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("cards")
	mc := MongoCard{}
	err := c.FindId(bson.ObjectIdHex(id)).One(&mc)
	mc.AddID()
	return mc.Card, err
}
func (m *Mongo) GetCards() ([]users.Card, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	var mcs []MongoCard
	err := c.Find(nil).All(&mcs)
	cs := make([]users.Card, 0)
	for _, mc := range mcs {
		mc.AddID()
		cs = append(cs, mc.Card)
	}
	return cs, err
}
func (m *Mongo) CreateCard(ca *users.Card, userid string) error {
	if !bson.IsObjectIdHex(userid) {
		return errors.New("Invalid Id Hex")
	}
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	id := bson.NewObjectId()
	mc := MongoCard{Card: *ca, ID: id}
	_, err := c.UpsertId(mc.ID, mc)
	if err != nil {
		return err
	}
	err = m.appendAttributeId("cards", mc.ID, userid)
	if err != nil {
		return err
	}
	mc.AddID()
	ca = &mc.Card
	return err
}

func (m *Mongo) GetAddress(id string) (users.Address, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.Address{}, errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("address")
	ma := MongoAddress{}
	err := c.FindId(bson.ObjectIdHex(id)).One(&ma)
	ma.AddID()
	return ma.Address, err
}

func (m *Mongo) GetAddresses() ([]users.Address, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	var mas []MongoAddress
	err := c.Find(nil).All(&mas)
	as := make([]users.Address, 0)
	for _, ma := range mas {
		ma.AddID()
		as = append(as, ma.Address)
	}
	return as, err
}

func (m *Mongo) CreateAddress(a *users.Address, userid string) error {
	if !bson.IsObjectIdHex(userid) {
		return errors.New("Invalid Id Hex")
	}
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	id := bson.NewObjectId()
	ma := MongoAddress{Address: *a, ID: id}
	_, err := c.UpsertId(ma.ID, ma)
	if err != nil {
		return err
	}
	err = m.appendAttributeId("addresses", ma.ID, userid)
	if err != nil {
		return err
	}

	ma.AddID()
	a = &ma.Address
	return err
}
func getURL() url.URL {
	u := url.UserPassword(name, password)
	return url.URL{
		Scheme: "mongodb",
		User:   u,
		Host:   host,
		Path:   db,
	}
}

func (m *Mongo) EnsureIndexes() error {
	s := m.Session.Copy()
	defer s.Close()
	i := mgo.Index{
		Key:        []string{"username"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     false,
	}
	c := s.DB("").C("users")
	return c.EnsureIndex(i)
}
<commit_msg>moved init<commit_after>package mongodb

import (
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"

	"github.com/microservices-demo/user/users"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

var (
	name                string
	password            string
	host                string
	db                  = "users"
	ErrInvalidHexID     = errors.New("Invalid Id Hex")
	ErrorSavingCardData = errors.New("There was a problem saving some card data")
	ErrorSavingAddrData = errors.New("There was a problem saving some address data")
)

func init() {
	flag.StringVar(&name, "mongo-user", os.Getenv("MONGO_USER"), "Mongo user")
	flag.StringVar(&password, "mongo-password", os.Getenv("MONGO_PASS"), "Mongo password")
	flag.StringVar(&host, "mongo-host", os.Getenv("MONGO_HOST"), "Mongo host")
}

type Mongo struct {
	Session *mgo.Session
}

func (m *Mongo) Init() error {
	u := getURL()
	var err error
	m.Session, err = mgo.Dial(u.String())
	if err != nil {
		return err
	}
	return m.EnsureIndexes()
}

type MongoUser struct {
	users.User `bson:",inline"`
	ID         bson.ObjectId   `bson:"_id"`
	AddressIDs []bson.ObjectId `bson:"addresses"`
	CardIDs    []bson.ObjectId `bson:"cards"`
}

func New() MongoUser {
	u := users.New()
	return MongoUser{
		User:       u,
		AddressIDs: make([]bson.ObjectId, 0),
		CardIDs:    make([]bson.ObjectId, 0),
	}
}

func (mu *MongoUser) AddUserIDs() {
	if mu.User.Addresses == nil {
		mu.User.Addresses = make([]users.Address, 0)
	}
	for _, id := range mu.AddressIDs {
		mu.User.Addresses = append(mu.User.Addresses, users.Address{
			ID: id.Hex(),
		})
	}
	if mu.User.Cards == nil {
		mu.User.Cards = make([]users.Card, 0)
	}
	for _, id := range mu.CardIDs {
		mu.User.Cards = append(mu.User.Cards, users.Card{ID: id.Hex()})
	}
	mu.User.UserID = mu.ID.Hex()
}

type MongoAddress struct {
	users.Address `bson:",inline"`
	ID            bson.ObjectId `bson:"_id"`
}

func (m *MongoAddress) AddID() {
	m.Address.ID = m.ID.Hex()
}

type MongoCard struct {
	users.Card `bson:",inline"`
	ID         bson.ObjectId `bson:"_id"`
}

func (m *MongoCard) AddID() {
	m.Card.ID = m.ID.Hex()
}

func (m *Mongo) CreateUser(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	id := bson.NewObjectId()
	mu := New()
	mu.User = *u
	mu.ID = id
	var carderr error
	var addrerr error
	mu.CardIDs, carderr = m.createCards(u.Cards)
	mu.AddressIDs, addrerr = m.createAddresses(u.Addresses)
	c := s.DB("").C("customers")
	_, err := c.UpsertId(mu.ID, mu)
	if err != nil {
		// Gonna clean up if we can, ignore error
		// because the user save error takes precedence.
		m.cleanAttributes(mu)
		return err
	}
	mu.User.UserID = mu.ID.Hex()
	// Cheap err for attributes
	if carderr != nil || addrerr != nil {
		return fmt.Errorf("%v %v", carderr, addrerr)
	}
	u = &mu.User
	return nil
}

func (m *Mongo) createCards(cs []users.Card) ([]bson.ObjectId, error) {
	s := m.Session.Copy()
	ids := make([]bson.ObjectId, 0)
	defer s.Close()
	for k, ca := range cs {
		id := bson.NewObjectId()
		mc := MongoCard{Card: ca, ID: id}
		c := s.DB("").C("cards")
		_, err := c.UpsertId(mc.ID, mc)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		cs[k].ID = id.Hex()
	}
	return ids, nil
}

func (m *Mongo) createAddresses(as []users.Address) ([]bson.ObjectId, error) {
	ids := make([]bson.ObjectId, 0)
	s := m.Session.Copy()
	defer s.Close()
	for k, a := range as {
		id := bson.NewObjectId()
		ma := MongoAddress{Address: a, ID: id}
		c := s.DB("").C("addresses")
		_, err := c.UpsertId(ma.ID, ma)
		if err != nil {
			return ids, err
		}
		ids = append(ids, id)
		as[k].ID = id.Hex()
	}
	return ids, nil
}

func (m *Mongo) cleanAttributes(mu MongoUser) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	_, err := c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.AddressIDs}})
	c = s.DB("").C("cards")
	_, err = c.RemoveAll(bson.M{"_id": bson.M{"$in": mu.CardIDs}})
	return err
}

func (m *Mongo) appendAttributeId(attr string, id bson.ObjectId, userid string) error {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C(attr)
	return c.Update(bson.M{"_id": bson.ObjectIdHex(userid)},
		bson.M{"$addToSet": bson.M{"addresses": id}})
}

func (m *Mongo) GetUserByName(name string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("customers")
	mu := New()
	err := c.Find(bson.M{"username": name}).One(&mu)
	mu.AddUserIDs()
	return mu.User, err
}

func (m *Mongo) GetUser(id string) (users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.New(), errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("customers")
	mu := New()
	err := c.FindId(bson.ObjectIdHex(id)).One(&mu)
	mu.AddUserIDs()
	return mu.User, err
}

func (m *Mongo) GetUsers() ([]users.User, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("customers")
	var mus []MongoUser
	err := c.Find(nil).All(&mus)
	us := make([]users.User, 0)
	for _, mu := range mus {
		mu.AddUserIDs()
		us = append(us, mu.User)
	}
	return us, err
}

func (m *Mongo) GetUserAttributes(u *users.User) error {
	s := m.Session.Copy()
	defer s.Close()
	ids := make([]bson.ObjectId, 0)
	for _, a := range u.Addresses {
		if !bson.IsObjectIdHex(a.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(a.ID))
	}
	var ma []MongoAddress
	c := s.DB("").C("addresses")
	err := c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&ma)
	if err != nil {
		return err
	}
	na := make([]users.Address, 0)
	for _, a := range ma {
		a.Address.ID = a.ID.Hex()
		na = append(na, a.Address)
	}
	u.Addresses = na

	ids = make([]bson.ObjectId, 0)
	for _, c := range u.Cards {
		if !bson.IsObjectIdHex(c.ID) {
			return ErrInvalidHexID
		}
		ids = append(ids, bson.ObjectIdHex(c.ID))
	}
	var mc []MongoCard
	c = s.DB("").C("cards")
	err = c.Find(bson.M{"_id": bson.M{"$in": ids}}).All(&mc)
	if err != nil {
		return err
	}

	nc := make([]users.Card, 0)
	for _, ca := range mc {
		ca.Card.ID = ca.ID.Hex()
		nc = append(nc, ca.Card)
	}
	u.Cards = nc
	return nil
}

func (m *Mongo) GetCard(id string) (users.Card, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.Card{}, errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("cards")
	mc := MongoCard{}
	err := c.FindId(bson.ObjectIdHex(id)).One(&mc)
	mc.AddID()
	return mc.Card, err
}
func (m *Mongo) GetCards() ([]users.Card, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	var mcs []MongoCard
	err := c.Find(nil).All(&mcs)
	cs := make([]users.Card, 0)
	for _, mc := range mcs {
		mc.AddID()
		cs = append(cs, mc.Card)
	}
	return cs, err
}
func (m *Mongo) CreateCard(ca *users.Card, userid string) error {
	if !bson.IsObjectIdHex(userid) {
		return errors.New("Invalid Id Hex")
	}
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	id := bson.NewObjectId()
	mc := MongoCard{Card: *ca, ID: id}
	_, err := c.UpsertId(mc.ID, mc)
	if err != nil {
		return err
	}
	err = m.appendAttributeId("cards", mc.ID, userid)
	if err != nil {
		return err
	}
	mc.AddID()
	ca = &mc.Card
	return err
}

func (m *Mongo) GetAddress(id string) (users.Address, error) {
	s := m.Session.Copy()
	defer s.Close()
	if !bson.IsObjectIdHex(id) {
		return users.Address{}, errors.New("Invalid Id Hex")
	}
	c := s.DB("").C("address")
	ma := MongoAddress{}
	err := c.FindId(bson.ObjectIdHex(id)).One(&ma)
	ma.AddID()
	return ma.Address, err
}

func (m *Mongo) GetAddresses() ([]users.Address, error) {
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("cards")
	var mas []MongoAddress
	err := c.Find(nil).All(&mas)
	as := make([]users.Address, 0)
	for _, ma := range mas {
		ma.AddID()
		as = append(as, ma.Address)
	}
	return as, err
}

func (m *Mongo) CreateAddress(a *users.Address, userid string) error {
	if !bson.IsObjectIdHex(userid) {
		return errors.New("Invalid Id Hex")
	}
	s := m.Session.Copy()
	defer s.Close()
	c := s.DB("").C("addresses")
	id := bson.NewObjectId()
	ma := MongoAddress{Address: *a, ID: id}
	_, err := c.UpsertId(ma.ID, ma)
	if err != nil {
		return err
	}
	err = m.appendAttributeId("addresses", ma.ID, userid)
	if err != nil {
		return err
	}

	ma.AddID()
	a = &ma.Address
	return err
}
func getURL() url.URL {
	u := url.UserPassword(name, password)
	return url.URL{
		Scheme: "mongodb",
		User:   u,
		Host:   host,
		Path:   db,
	}
}

func (m *Mongo) EnsureIndexes() error {
	s := m.Session.Copy()
	defer s.Close()
	i := mgo.Index{
		Key:        []string{"username"},
		Unique:     true,
		DropDups:   true,
		Background: true,
		Sparse:     false,
	}
	c := s.DB("").C("users")
	return c.EnsureIndex(i)
}
<|endoftext|>


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

package main

import (
	"bytes"
	"flag"
	"go/format"
	"log"
	"os"
	"sort"
	"text/template"
	"time"

	"github.com/tsuru/tsuru/permission"
)

var fileTpl = `// AUTOMATICALLY GENERATED FILE - DO NOT EDIT!
// Please run 'go generate' to update this file.
//
// Copyright {{.Time.Year}} tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package permission

var (
{{range .Permissions}} \
    Perm{{.Identifier}} = PermissionRegistry.get("{{.FullName}}") // {{.AllowedContexts}}
{{end}} \
)
`

type context struct {
	Time        time.Time
	Permissions permission.PermissionSchemeList
}

func main() {
	out := flag.String("o", "", "output file")
	flag.Parse()
	tmpl, err := template.New("tpl").Parse(fileTpl)
	if err != nil {
		log.Fatal(err)
	}
	lst := permission.PermissionRegistry.Permissions()
	sort.Sort(lst)
	data := context{
		Time:        time.Now(),
		Permissions: lst,
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, data)
	if err != nil {
		log.Fatal(err)
	}
	rawFile := buf.Bytes()
	rawFile = bytes.Replace(rawFile, []byte("\\\n"), []byte{}, -1)
	formatedFile, err := format.Source(rawFile)
	if err != nil {
		log.Fatalf("unable to format code: %s\n%s", err, rawFile)
	}
	file, err := os.OpenFile(*out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	file.Write(formatedFile)
}
<commit_msg>update license year<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"flag"
	"go/format"
	"log"
	"os"
	"sort"
	"text/template"
	"time"

	"github.com/tsuru/tsuru/permission"
)

var fileTpl = `// AUTOMATICALLY GENERATED FILE - DO NOT EDIT!
// Please run 'go generate' to update this file.
//
// Copyright {{.Time.Year}} tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package permission

var (
{{range .Permissions}} \
    Perm{{.Identifier}} = PermissionRegistry.get("{{.FullName}}") // {{.AllowedContexts}}
{{end}} \
)
`

type context struct {
	Time        time.Time
	Permissions permission.PermissionSchemeList
}

func main() {
	out := flag.String("o", "", "output file")
	flag.Parse()
	tmpl, err := template.New("tpl").Parse(fileTpl)
	if err != nil {
		log.Fatal(err)
	}
	lst := permission.PermissionRegistry.Permissions()
	sort.Sort(lst)
	data := context{
		Time:        time.Now(),
		Permissions: lst,
	}
	var buf bytes.Buffer
	err = tmpl.Execute(&buf, data)
	if err != nil {
		log.Fatal(err)
	}
	rawFile := buf.Bytes()
	rawFile = bytes.Replace(rawFile, []byte("\\\n"), []byte{}, -1)
	formatedFile, err := format.Source(rawFile)
	if err != nil {
		log.Fatalf("unable to format code: %s\n%s", err, rawFile)
	}
	file, err := os.OpenFile(*out, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0660)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	file.Write(formatedFile)
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	"fmt"
	"os"
	"path"
	"path/filepath"
)

type Config struct {
	Files struct {
		Pubring  string
		Mlist2   string
		Pubkey   string
		Secring  string
		Adminkey string
		Help     string
		Pooldir  string
		Maildir  string
		IDlog    string
		ChunkDB  string
	}
	Urls struct {
		Fetch   bool
		Pubring string
		Mlist2  string
	}
	Mail struct {
		Sendmail       bool
		Pipe           string
		Outfile        bool
		UseTLS         bool
		SMTPRelay      string
		SMTPPort       int
		MXRelay        bool
		EnvelopeSender string
		Username       string
		Password       string
		OutboundName   string
		OutboundAddy   string
		CustomFrom     bool
	}
	Stats struct {
		Minlat    int
		Maxlat    int
		Minrel    float32
		Relfinal  float32
		Chain     string
		Numcopies int
		Distance  int
		StaleHrs  int
	}
	Pool struct {
		Size   int
		Rate   int
		Loop   int
		MaxAge int
	}
	Remailer struct {
		Name        string
		Address     string
		Exit        bool
		MaxSize     int
		IDexp       int
		ChunkExpire int
		Keylife     int
		Keygrace    int
		Loglevel    string
		Daemon      bool
	}
}

func init() {
	var err error
	// Function as a client
	flag.BoolVar(&flag_client, "mail", false, "Function as a client")
	flag.BoolVar(&flag_client, "m", false, "Function as a client")
	// Send (from pool)
	flag.BoolVar(&flag_send, "send", false, "Force pool send")
	flag.BoolVar(&flag_send, "S", false, "Force pool send")
	// Perform remailer actions
	flag.BoolVar(&flag_remailer, "remailer", false,
		"Perform routine remailer actions")
	flag.BoolVar(&flag_remailer, "M", false,
		"Perform routine remailer actions")
	// Start remailer as a daemon
	flag.BoolVar(&flag_daemon, "daemon", false,
		"Start remailer as a daemon. (Requires -M")
	flag.BoolVar(&flag_daemon, "D", false,
		"Start remailer as a daemon. (Requires -M")
	// Remailer chain
	flag.StringVar(&flag_chain, "chain", "", "Remailer chain")
	flag.StringVar(&flag_chain, "l", "", "Remailer chain")
	// Recipient address
	flag.StringVar(&flag_to, "to", "", "Recipient email address")
	flag.StringVar(&flag_to, "t", "", "Recipient email address")
	// Subject header
	flag.StringVar(&flag_subject, "subject", "", "Subject header")
	flag.StringVar(&flag_subject, "s", "", "Subject header")
	// Number of copies
	flag.IntVar(&flag_copies, "copies", 0, "Number of copies")
	flag.IntVar(&flag_copies, "c", 0, "Number of copies")
	// Config file
	flag.StringVar(&flag_config, "config", "", "Config file")
	// Read STDIN
	flag.BoolVar(&flag_stdin, "read-mail", false, "Read a message from stdin")
	flag.BoolVar(&flag_stdin, "R", false, "Read a message from stdin")
	// Write to STDOUT
	flag.BoolVar(&flag_stdout, "stdout", false, "Write message to stdout")
	// Inject dummy
	flag.BoolVar(&flag_dummy, "dummy", false, "Inject a dummy message")
	flag.BoolVar(&flag_dummy, "d", false, "Inject a dummy message")
	// Disable dummy messaging
	flag.BoolVar(&flag_nodummy, "nodummy", false, "Don't send dummies")
	// Print Version
	flag.BoolVar(&flag_version, "version", false, "Print version string")
	flag.BoolVar(&flag_version, "V", false, "Print version string")
	// Memory usage
	flag.BoolVar(&flag_meminfo, "meminfo", false, "Print memory info")

	// Figure out the dir of the yamn binary
	var dir string
	dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		panic(err)
	}
	flag.StringVar(&flag_basedir, "dir", dir, "Base directory")
}

func setDefaultConfig() {
	// Set defaults and read config file
	cfg.Files.Pubkey = path.Join(flag_basedir, "key.txt")
	cfg.Files.Pubring = path.Join(flag_basedir, "pubring.mix")
	cfg.Files.Secring = path.Join(flag_basedir, "secring.mix")
	cfg.Files.Mlist2 = path.Join(flag_basedir, "mlist2.txt")
	cfg.Files.Adminkey = path.Join(flag_basedir, "adminkey.txt")
	cfg.Files.Help = path.Join(flag_basedir, "help.txt")
	cfg.Files.Pooldir = path.Join(flag_basedir, "pool")
	cfg.Files.Maildir = path.Join(flag_basedir, "Maildir")
	cfg.Files.IDlog = path.Join(flag_basedir, "idlog")
	cfg.Files.ChunkDB = path.Join(flag_basedir, "chunkdb")
	cfg.Urls.Fetch = true
	cfg.Urls.Pubring = "http://www.mixmin.net/yamn/pubring.mix"
	cfg.Urls.Mlist2 = "http://www.mixmin.net/yamn/mlist2.txt"
	cfg.Mail.Sendmail = false
	cfg.Mail.Outfile = false
	cfg.Mail.SMTPRelay = "snorky.mixmin.net"
	cfg.Mail.SMTPPort = 587
	cfg.Mail.UseTLS = true
	cfg.Mail.MXRelay = true
	cfg.Mail.EnvelopeSender = "nobody@nowhere.invalid"
	cfg.Mail.Username = ""
	cfg.Mail.Password = ""
	cfg.Mail.OutboundName = "Anonymous Remailer"
	cfg.Mail.OutboundAddy = "remailer@domain.invalid"
	cfg.Mail.CustomFrom = false
	cfg.Stats.Minrel = 98.0
	cfg.Stats.Relfinal = 99.0
	cfg.Stats.Minlat = 2
	cfg.Stats.Maxlat = 60
	cfg.Stats.Chain = "yamn4,*,*"
	cfg.Stats.Numcopies = 1
	cfg.Stats.Distance = 2
	cfg.Stats.StaleHrs = 24
	cfg.Pool.Size = 45
	cfg.Pool.Rate = 65
	cfg.Pool.Loop = 300
	cfg.Pool.MaxAge = 28
	cfg.Remailer.Name = "anon"
	cfg.Remailer.Address = "mix@nowhere.invalid"
	cfg.Remailer.Exit = false
	cfg.Remailer.MaxSize = 12
	cfg.Remailer.IDexp = 14
	cfg.Remailer.ChunkExpire = 60
	cfg.Remailer.Keylife = 60
	cfg.Remailer.Keygrace = 28
	cfg.Remailer.Loglevel = "info"
	cfg.Remailer.Daemon = false
}

func flags() {
	var err error
	flag.Parse()
	flag_args = flag.Args()
	setDefaultConfig()
	if flag_version {
		fmt.Printf("Version=%s\n", version)
		fmt.Printf("Basedir=%s\n", flag_basedir)
		os.Exit(0)
	} else if flag_config != "" {
		err = gcfg.ReadFileInto(&cfg, flag_config)
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else if os.Getenv("YAMNCFG") != "" {
		err = gcfg.ReadFileInto(&cfg, os.Getenv("YAMNCFG"))
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else {
		fn := path.Join(flag_basedir, "yamn.cfg")
		err = gcfg.ReadFileInto(&cfg, fn)
		if err != nil {
			if !flag_client {
				fmt.Println(err)
			}
			fmt.Println("Using internal, default config.")
		}
	}
}

var flag_basedir string
var flag_client bool
var flag_send bool
var flag_remailer bool
var flag_daemon bool
var flag_chain string
var flag_to string
var flag_subject string
var flag_args []string
var flag_config string
var flag_copies int
var flag_stdin bool
var flag_stdout bool
var flag_dummy bool
var flag_nodummy bool
var flag_version bool
var flag_meminfo bool
var cfg Config
<commit_msg>Use pwd as the basedir instead of binary location<commit_after>package main

import (
	"code.google.com/p/gcfg"
	"flag"
	"fmt"
	"os"
	"path"
)

type Config struct {
	Files struct {
		Pubring  string
		Mlist2   string
		Pubkey   string
		Secring  string
		Adminkey string
		Help     string
		Pooldir  string
		Maildir  string
		IDlog    string
		ChunkDB  string
	}
	Urls struct {
		Fetch   bool
		Pubring string
		Mlist2  string
	}
	Mail struct {
		Sendmail       bool
		Pipe           string
		Outfile        bool
		UseTLS         bool
		SMTPRelay      string
		SMTPPort       int
		MXRelay        bool
		EnvelopeSender string
		Username       string
		Password       string
		OutboundName   string
		OutboundAddy   string
		CustomFrom     bool
	}
	Stats struct {
		Minlat    int
		Maxlat    int
		Minrel    float32
		Relfinal  float32
		Chain     string
		Numcopies int
		Distance  int
		StaleHrs  int
	}
	Pool struct {
		Size   int
		Rate   int
		Loop   int
		MaxAge int
	}
	Remailer struct {
		Name        string
		Address     string
		Exit        bool
		MaxSize     int
		IDexp       int
		ChunkExpire int
		Keylife     int
		Keygrace    int
		Loglevel    string
		Daemon      bool
	}
}

func init() {
	var err error
	// Function as a client
	flag.BoolVar(&flag_client, "mail", false, "Function as a client")
	flag.BoolVar(&flag_client, "m", false, "Function as a client")
	// Send (from pool)
	flag.BoolVar(&flag_send, "send", false, "Force pool send")
	flag.BoolVar(&flag_send, "S", false, "Force pool send")
	// Perform remailer actions
	flag.BoolVar(&flag_remailer, "remailer", false,
		"Perform routine remailer actions")
	flag.BoolVar(&flag_remailer, "M", false,
		"Perform routine remailer actions")
	// Start remailer as a daemon
	flag.BoolVar(&flag_daemon, "daemon", false,
		"Start remailer as a daemon. (Requires -M")
	flag.BoolVar(&flag_daemon, "D", false,
		"Start remailer as a daemon. (Requires -M")
	// Remailer chain
	flag.StringVar(&flag_chain, "chain", "", "Remailer chain")
	flag.StringVar(&flag_chain, "l", "", "Remailer chain")
	// Recipient address
	flag.StringVar(&flag_to, "to", "", "Recipient email address")
	flag.StringVar(&flag_to, "t", "", "Recipient email address")
	// Subject header
	flag.StringVar(&flag_subject, "subject", "", "Subject header")
	flag.StringVar(&flag_subject, "s", "", "Subject header")
	// Number of copies
	flag.IntVar(&flag_copies, "copies", 0, "Number of copies")
	flag.IntVar(&flag_copies, "c", 0, "Number of copies")
	// Config file
	flag.StringVar(&flag_config, "config", "", "Config file")
	// Read STDIN
	flag.BoolVar(&flag_stdin, "read-mail", false, "Read a message from stdin")
	flag.BoolVar(&flag_stdin, "R", false, "Read a message from stdin")
	// Write to STDOUT
	flag.BoolVar(&flag_stdout, "stdout", false, "Write message to stdout")
	// Inject dummy
	flag.BoolVar(&flag_dummy, "dummy", false, "Inject a dummy message")
	flag.BoolVar(&flag_dummy, "d", false, "Inject a dummy message")
	// Disable dummy messaging
	flag.BoolVar(&flag_nodummy, "nodummy", false, "Don't send dummies")
	// Print Version
	flag.BoolVar(&flag_version, "version", false, "Print version string")
	flag.BoolVar(&flag_version, "V", false, "Print version string")
	// Memory usage
	flag.BoolVar(&flag_meminfo, "meminfo", false, "Print memory info")

	// Define our base working directory
	var dir string
	//dir, err = filepath.Abs(filepath.Dir(os.Args[0]))
	dir, err = os.Getwd()
	if err != nil {
		panic(err)
	}
	flag.StringVar(&flag_basedir, "dir", dir, "Base directory")
}

func setDefaultConfig() {
	// Set defaults and read config file
	cfg.Files.Pubkey = path.Join(flag_basedir, "key.txt")
	cfg.Files.Pubring = path.Join(flag_basedir, "pubring.mix")
	cfg.Files.Secring = path.Join(flag_basedir, "secring.mix")
	cfg.Files.Mlist2 = path.Join(flag_basedir, "mlist2.txt")
	cfg.Files.Adminkey = path.Join(flag_basedir, "adminkey.txt")
	cfg.Files.Help = path.Join(flag_basedir, "help.txt")
	cfg.Files.Pooldir = path.Join(flag_basedir, "pool")
	cfg.Files.Maildir = path.Join(flag_basedir, "Maildir")
	cfg.Files.IDlog = path.Join(flag_basedir, "idlog")
	cfg.Files.ChunkDB = path.Join(flag_basedir, "chunkdb")
	cfg.Urls.Fetch = true
	cfg.Urls.Pubring = "http://www.mixmin.net/yamn/pubring.mix"
	cfg.Urls.Mlist2 = "http://www.mixmin.net/yamn/mlist2.txt"
	cfg.Mail.Sendmail = false
	cfg.Mail.Outfile = false
	cfg.Mail.SMTPRelay = "snorky.mixmin.net"
	cfg.Mail.SMTPPort = 587
	cfg.Mail.UseTLS = true
	cfg.Mail.MXRelay = true
	cfg.Mail.EnvelopeSender = "nobody@nowhere.invalid"
	cfg.Mail.Username = ""
	cfg.Mail.Password = ""
	cfg.Mail.OutboundName = "Anonymous Remailer"
	cfg.Mail.OutboundAddy = "remailer@domain.invalid"
	cfg.Mail.CustomFrom = false
	cfg.Stats.Minrel = 98.0
	cfg.Stats.Relfinal = 99.0
	cfg.Stats.Minlat = 2
	cfg.Stats.Maxlat = 60
	cfg.Stats.Chain = "yamn4,*,*"
	cfg.Stats.Numcopies = 1
	cfg.Stats.Distance = 2
	cfg.Stats.StaleHrs = 24
	cfg.Pool.Size = 45
	cfg.Pool.Rate = 65
	cfg.Pool.Loop = 300
	cfg.Pool.MaxAge = 28
	cfg.Remailer.Name = "anon"
	cfg.Remailer.Address = "mix@nowhere.invalid"
	cfg.Remailer.Exit = false
	cfg.Remailer.MaxSize = 12
	cfg.Remailer.IDexp = 14
	cfg.Remailer.ChunkExpire = 60
	cfg.Remailer.Keylife = 60
	cfg.Remailer.Keygrace = 28
	cfg.Remailer.Loglevel = "info"
	cfg.Remailer.Daemon = false
}

func flags() {
	var err error
	flag.Parse()
	flag_args = flag.Args()
	setDefaultConfig()
	if flag_version {
		fmt.Printf("Version=%s\n", version)
		fmt.Printf("Basedir=%s\n", flag_basedir)
		os.Exit(0)
	} else if flag_config != "" {
		err = gcfg.ReadFileInto(&cfg, flag_config)
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else if os.Getenv("YAMNCFG") != "" {
		err = gcfg.ReadFileInto(&cfg, os.Getenv("YAMNCFG"))
		if err != nil {
			fmt.Fprintf(
				os.Stderr, "Unable to read %s\n", flag_config)
			os.Exit(1)
		}
	} else {
		fn := path.Join(flag_basedir, "yamn.cfg")
		err = gcfg.ReadFileInto(&cfg, fn)
		if err != nil {
			if !flag_client {
				fmt.Println(err)
			}
			fmt.Println("Using internal, default config.")
		}
	}
}

var flag_basedir string
var flag_client bool
var flag_send bool
var flag_remailer bool
var flag_daemon bool
var flag_chain string
var flag_to string
var flag_subject string
var flag_args []string
var flag_config string
var flag_copies int
var flag_stdin bool
var flag_stdout bool
var flag_dummy bool
var flag_nodummy bool
var flag_version bool
var flag_meminfo bool
var cfg Config
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/spf13/pflag"
	"gopkg.in/yaml.v2"
)

// Config is vv application config struct.
type Config struct {
	MPD struct {
		Network        string `yaml:"network"`
		Addr           string `yaml:"addr"`
		MusicDirectory string `yaml:"music_directory"`
	} `yaml:"mpd"`
	Server struct {
		Addr string `yaml:"addr"`
	} `yaml:"server"`
	Playlist struct {
		Tree      map[string]*ListNode `yaml:"tree"`
		TreeOrder []string             `yaml:"tree_order"`
	}
	debug bool
}

// ParseConfig parse yaml config and flags.
func ParseConfig(dir []string) (*Config, time.Time, error) {
	c := &Config{}
	date := time.Time{}
	for _, d := range dir {
		path := filepath.Join(d, "config.yaml")
		_, err := os.Stat(path)
		if err == nil {
			f, err := os.Open(path)
			if err != nil {
				return nil, date, err
			}
			s, err := f.Stat()
			if err != nil {
				return nil, date, err
			}
			date = s.ModTime()
			defer f.Close()
			c := Config{}
			if err := yaml.NewDecoder(f).Decode(&c); err != nil {
				return nil, date, err
			}
		}
	}

	mn := pflag.String("mpd.network", "", "mpd server network to connect")
	ma := pflag.String("mpd.addr", "", "mpd server address to connect")
	mm := pflag.String("mpd.music_directory", "", "set music_directory in mpd.conf value to search album cover image")
	sa := pflag.String("server.addr", "", "this app serving address")
	d := pflag.BoolP("debug", "d", false, "use local assets if exists")
	pflag.Parse()
	if len(*mn) != 0 {
		c.MPD.Network = *mn
	}
	if len(*ma) != 0 {
		c.MPD.Addr = *ma
	}
	if len(*mm) != 0 {
		c.MPD.MusicDirectory = *mm
	}
	if len(*sa) != 0 {
		c.Server.Addr = *sa
	}
	c.debug = *d
	c.setDefault()
	return c, date, nil
}

func (c *Config) setDefault() {
	if c.MPD.Network == "" {
		c.MPD.Network = "tcp"
	}
	if c.MPD.Addr == "" {
		c.MPD.Addr = ":6600"
	}
	if c.Server.Addr == "" {
		c.Server.Addr = ":8080"
	}
	if c.Playlist.Tree == nil && c.Playlist.TreeOrder == nil {
		c.Playlist.Tree = defaultTree
		c.Playlist.TreeOrder = defaultTreeOrder
	}
}

// Validate validates config data.
func (c *Config) Validate() error {
	set := make(map[string]struct{}, len(c.Playlist.TreeOrder))
	for _, label := range c.Playlist.TreeOrder {
		if _, ok := set[label]; ok {
			return fmt.Errorf("playlist.tree_order %s is duplicated", label)
		}
		set[label] = struct{}{}
		node, ok := c.Playlist.Tree[label]
		if !ok {
			return fmt.Errorf("playlist.tree_order %s is not defined in tree", label)
		}
		if err := node.Validate(); err != nil {
			return fmt.Errorf("playlist.tree label %s: %w", label, err)
		}
	}
	if t, o := len(c.Playlist.Tree), len(c.Playlist.TreeOrder); o != t {
		return fmt.Errorf("playlist.tree length (%d) and playlist.tree_order length (%d) mismatch", t, o)
	}
	return nil
}

var (
	defaultTree = map[string]*ListNode{
		"AlbumArtist": {
			Sort: []string{"AlbumArtist", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"AlbumArtist", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Album": {
			Sort: []string{"AlbumArtist-Date-Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"AlbumArtist-Date-Album", "album"}, {"Title", "song"}},
		},
		"Artist": {
			Sort: []string{"Artist", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Artist", "plain"}, {"Title", "song"}},
		},
		"Genre": {
			Sort: []string{"Genre", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Genre", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Date": {
			Sort: []string{"Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Date", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Composer": {
			Sort: []string{"Composer", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Composer", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Performer": {
			Sort: []string{"Performer", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Performer", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
	}
	defaultTreeOrder = []string{"AlbumArtist", "Album", "Artist", "Genre", "Date", "Composer", "Performer"}
	supportTreeViews = []string{"plain", "album", "song"}
)

type ListNode struct {
	Sort []string    `json:"sort"`
	Tree [][2]string `json:"tree"`
}

// Validate ListNode data struct.
func (l *ListNode) Validate() error {
	if len(l.Tree) > 4 {
		return fmt.Errorf("maximum tree length is 4; got %d", len(l.Tree))
	}
	for i, leef := range l.Tree {
		for _, view := range supportTreeViews {
			if view == leef[1] {
				goto OK
			}
		}
		return fmt.Errorf("index %d, supported tree element views are %v; got %s", i, supportTreeViews, leef[1])
	OK:
	}
	return nil

}
<commit_msg>fix doc<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"time"

	"github.com/spf13/pflag"
	"gopkg.in/yaml.v2"
)

// Config is vv application config struct.
type Config struct {
	MPD struct {
		Network        string `yaml:"network"`
		Addr           string `yaml:"addr"`
		MusicDirectory string `yaml:"music_directory"`
	} `yaml:"mpd"`
	Server struct {
		Addr string `yaml:"addr"`
	} `yaml:"server"`
	Playlist struct {
		Tree      map[string]*ListNode `yaml:"tree"`
		TreeOrder []string             `yaml:"tree_order"`
	}
	debug bool
}

// ParseConfig parse yaml config and flags.
func ParseConfig(dir []string) (*Config, time.Time, error) {
	c := &Config{}
	date := time.Time{}
	for _, d := range dir {
		path := filepath.Join(d, "config.yaml")
		_, err := os.Stat(path)
		if err == nil {
			f, err := os.Open(path)
			if err != nil {
				return nil, date, err
			}
			s, err := f.Stat()
			if err != nil {
				return nil, date, err
			}
			date = s.ModTime()
			defer f.Close()
			c := Config{}
			if err := yaml.NewDecoder(f).Decode(&c); err != nil {
				return nil, date, err
			}
		}
	}

	mn := pflag.String("mpd.network", "", "mpd server network to connect")
	ma := pflag.String("mpd.addr", "", "mpd server address to connect")
	mm := pflag.String("mpd.music_directory", "", "set music_directory in mpd.conf value to search album cover image")
	sa := pflag.String("server.addr", "", "this app serving address")
	d := pflag.BoolP("debug", "d", false, "use local assets if exists")
	pflag.Parse()
	if len(*mn) != 0 {
		c.MPD.Network = *mn
	}
	if len(*ma) != 0 {
		c.MPD.Addr = *ma
	}
	if len(*mm) != 0 {
		c.MPD.MusicDirectory = *mm
	}
	if len(*sa) != 0 {
		c.Server.Addr = *sa
	}
	c.debug = *d
	c.setDefault()
	return c, date, nil
}

func (c *Config) setDefault() {
	if c.MPD.Network == "" {
		c.MPD.Network = "tcp"
	}
	if c.MPD.Addr == "" {
		c.MPD.Addr = ":6600"
	}
	if c.Server.Addr == "" {
		c.Server.Addr = ":8080"
	}
	if c.Playlist.Tree == nil && c.Playlist.TreeOrder == nil {
		c.Playlist.Tree = defaultTree
		c.Playlist.TreeOrder = defaultTreeOrder
	}
}

// Validate validates config data.
func (c *Config) Validate() error {
	set := make(map[string]struct{}, len(c.Playlist.TreeOrder))
	for _, label := range c.Playlist.TreeOrder {
		if _, ok := set[label]; ok {
			return fmt.Errorf("playlist.tree_order %s is duplicated", label)
		}
		set[label] = struct{}{}
		node, ok := c.Playlist.Tree[label]
		if !ok {
			return fmt.Errorf("playlist.tree_order %s is not defined in tree", label)
		}
		if err := node.Validate(); err != nil {
			return fmt.Errorf("playlist.tree label %s: %w", label, err)
		}
	}
	if t, o := len(c.Playlist.Tree), len(c.Playlist.TreeOrder); o != t {
		return fmt.Errorf("playlist.tree length (%d) and playlist.tree_order length (%d) mismatch", t, o)
	}
	return nil
}

var (
	defaultTree = map[string]*ListNode{
		"AlbumArtist": {
			Sort: []string{"AlbumArtist", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"AlbumArtist", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Album": {
			Sort: []string{"AlbumArtist-Date-Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"AlbumArtist-Date-Album", "album"}, {"Title", "song"}},
		},
		"Artist": {
			Sort: []string{"Artist", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Artist", "plain"}, {"Title", "song"}},
		},
		"Genre": {
			Sort: []string{"Genre", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Genre", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Date": {
			Sort: []string{"Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Date", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Composer": {
			Sort: []string{"Composer", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Composer", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
		"Performer": {
			Sort: []string{"Performer", "Date", "Album", "DiscNumber", "TrackNumber", "Title", "file"},
			Tree: [][2]string{{"Performer", "plain"}, {"Album", "album"}, {"Title", "song"}},
		},
	}
	defaultTreeOrder = []string{"AlbumArtist", "Album", "Artist", "Genre", "Date", "Composer", "Performer"}
	supportTreeViews = []string{"plain", "album", "song"}
)

// ListNode represents smart playlist node.
type ListNode struct {
	Sort []string    `json:"sort"`
	Tree [][2]string `json:"tree"`
}

// Validate ListNode data struct.
func (l *ListNode) Validate() error {
	if len(l.Tree) > 4 {
		return fmt.Errorf("maximum tree length is 4; got %d", len(l.Tree))
	}
	for i, leef := range l.Tree {
		for _, view := range supportTreeViews {
			if view == leef[1] {
				goto OK
			}
		}
		return fmt.Errorf("index %d, supported tree element views are %v; got %s", i, supportTreeViews, leef[1])
	OK:
	}
	return nil

}
<|endoftext|>


<commit_before>package gnosis

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"sync"
)

var staticConfig *Config
var configLock = new(sync.RWMutex)

type GlobalSection struct {
	Port     string
	Hostname string
}

type ServerSection struct {
	Path        string
	Prefix      string
	DefaultPage string
	ServerType  string
	Restricted  []string
}

type Config struct {
	Global     GlobalSection
	Mainserver ServerSection
	Server     []ServerSection
}

var defaultConfig = []byte(`{
  "Global": {
    "Port": "8080",
    "Hostname": "localhost"
  },
  "MainServer": {
      "Path": "/var/www/wiki/",
      "Prefix": "/",
      "Default": "index",
      "ServerType": "markdown",
      "Restricted": [
        "internal",
        "handbook"
      ]
    },
  "Server": [
  ]
}`)

func GetConfig() *Config {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}

	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Printf("Problem loading config file: %s", err.Error())
	}

	// UnMarshal the config file that was read in
	temp := new(Config)

	err = json.Unmarshal(defaultConfig, temp)

	if err != nil {
		log.Println("problem parsing built in default configuration - this should not happen")
		return false
	}

	err = json.Unmarshal(fileContents, temp)
	//Make sure you were able to read it in
	if err != nil {
		log.Printf("parse config error: %s", err.Error())
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}
<commit_msg>changed Default to DefaultPage, and MainServer to Mainserver (proper capitalization counts)<commit_after>package gnosis

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"sync"
)

var staticConfig *Config
var configLock = new(sync.RWMutex)

type GlobalSection struct {
	Port     string
	Hostname string
}

type ServerSection struct {
	Path        string
	Prefix      string
	DefaultPage string
	ServerType  string
	Restricted  []string
}

type Config struct {
	Global     GlobalSection
	Mainserver ServerSection
	Server     []ServerSection
}

var defaultConfig = []byte(`{
  "Global": {
    "Port": "8080",
    "Hostname": "localhost"
  },
  "Mainserver": {
      "Path": "/var/www/wiki/",
      "Prefix": "/",
      "DefaultPage": "index",
      "ServerType": "markdown",
      "Restricted": [
        "internal",
        "handbook"
      ]
    },
  "Server": [
  ]
}`)

func GetConfig() *Config {
	configLock.RLock()
	defer configLock.RUnlock()
	return staticConfig
}

func LoadConfig(configFile string) bool {

	if configFile == "" {
		log.Println("no configuration file specified, using ./config.json")
		// return an empty config file
		configFile = "config.json"
	}

	// have to read in the line into a byte[] array
	fileContents, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Printf("Problem loading config file: %s", err.Error())
	}

	// UnMarshal the config file that was read in
	temp := new(Config)

	err = json.Unmarshal(defaultConfig, temp)

	if err != nil {
		log.Println("problem parsing built in default configuration - this should not happen")
		return false
	}

	err = json.Unmarshal(fileContents, temp)
	//Make sure you were able to read it in
	if err != nil {
		log.Printf("parse config error: %s", err.Error())
		return false
	}

	configLock.Lock()
	staticConfig = temp
	configLock.Unlock()

	return true
}
<|endoftext|>


<commit_before>package config

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

	"github.com/nuveo/log"
	"github.com/spf13/viper"
)

// TablesConf informations
type TablesConf struct {
	Name        string   `mapstructure:"name"`
	Permissions []string `mapstructure:"permissions"`
	Fields      []string `mapstructure:"fields"`
}

// AccessConf informations
type AccessConf struct {
	Restrict bool
	Tables   []TablesConf
}

// Prest basic config
type Prest struct {
	// HTTPPort Declare which http port the PREST used
	HTTPPort        int
	PGHost          string
	PGPort          int
	PGUser          string
	PGPass          string
	PGDatabase      string
	PGMaxIdleConn   int
	PGMAxOpenConn   int
	PGConnTimeout   int
	JWTKey          string
	MigrationsPath  string
	QueriesPath     string
	AccessConf      AccessConf
	CORSAllowOrigin []string
	Debug           bool
}

// PrestConf config variable
var PrestConf *Prest

func viperCfg() {
	filePath := getDefaultPrestConf(os.Getenv("PREST_CONF"))
	dir, file := filepath.Split(filePath)
	file = strings.TrimSuffix(file, filepath.Ext(file))
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvPrefix("PREST")
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(replacer)
	viper.AddConfigPath(dir)
	viper.SetConfigName(file)
	viper.SetConfigType("toml")
	viper.SetDefault("http.port", 3000)
	viper.SetDefault("pg.host", "127.0.0.1")
	viper.SetDefault("pg.port", 5432)
	viper.SetDefault("pg.maxidleconn", 10)
	viper.SetDefault("pg.maxopenconn", 10)
	viper.SetDefault("pg.conntimeout", 10)
	viper.SetDefault("debug", false)

	user, err := user.Current()
	if err != nil {
		log.Println("{viperCfg}", err)
	}

	viper.SetDefault("queries.location", filepath.Join(user.HomeDir, "queries"))
}

func getDefaultPrestConf(prestConf string) string {
	if prestConf == "" {
		return "./prest.toml"
	}
	return prestConf
}

// Parse pREST config
func Parse(cfg *Prest) (err error) {
	err = viper.ReadInConfig()
	if err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			log.Warningln("Running without config file.")
		default:
			return
		}
	}
	cfg.HTTPPort = viper.GetInt("http.port")
	cfg.PGHost = viper.GetString("pg.host")
	cfg.PGPort = viper.GetInt("pg.port")
	cfg.PGUser = viper.GetString("pg.user")
	cfg.PGPass = viper.GetString("pg.pass")
	cfg.PGDatabase = viper.GetString("pg.database")
	cfg.PGMaxIdleConn = viper.GetInt("pg.maxidleconn")
	cfg.PGMAxOpenConn = viper.GetInt("pg.maxopenconn")
	cfg.PGConnTimeout = viper.GetInt("pg.conntimeout")
	cfg.JWTKey = viper.GetString("jwt.key")
	cfg.MigrationsPath = viper.GetString("migrations")
	cfg.AccessConf.Restrict = viper.GetBool("access.restrict")
	cfg.QueriesPath = viper.GetString("queries.location")
	cfg.CORSAllowOrigin = viper.GetStringSlice("cors.alloworigin")
	cfg.Debug = viper.GetBool("debug")

	var t []TablesConf
	err = viper.UnmarshalKey("access.tables", &t)
	if err != nil {
		return err
	}

	cfg.AccessConf.Tables = t

	return
}

// Load configuration
func Load() {
	viperCfg()
	PrestConf = &Prest{}
	err := Parse(PrestConf)
	if err != nil {
		panic(err)
	}

	if !PrestConf.AccessConf.Restrict {
		log.Warningln("You are running pREST in public mode.")
	}

	if PrestConf.Debug {
		log.DebugMode = PrestConf.Debug
		log.Warningln("You are running pREST in debug mode.")
	}

	if _, err = os.Stat(PrestConf.QueriesPath); os.IsNotExist(err) {
		if err = os.MkdirAll(PrestConf.QueriesPath, 0700); os.IsNotExist(err) {
			log.Errorf("Queries directory %s is not created", PrestConf.QueriesPath)
		}
	}
}
<commit_msg>improving support for CORS by handling preflight request and configurable allowed headers header (#6)<commit_after>package config

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

	"github.com/nuveo/log"
	"github.com/spf13/viper"
)

// TablesConf informations
type TablesConf struct {
	Name        string   `mapstructure:"name"`
	Permissions []string `mapstructure:"permissions"`
	Fields      []string `mapstructure:"fields"`
}

// AccessConf informations
type AccessConf struct {
	Restrict bool
	Tables   []TablesConf
}

// Prest basic config
type Prest struct {
	// HTTPPort Declare which http port the PREST used
	HTTPPort         int
	PGHost           string
	PGPort           int
	PGUser           string
	PGPass           string
	PGDatabase       string
	PGMaxIdleConn    int
	PGMAxOpenConn    int
	PGConnTimeout    int
	JWTKey           string
	MigrationsPath   string
	QueriesPath      string
	AccessConf       AccessConf
	CORSAllowOrigin  []string
	CORSAllowHeaders []string
	Debug            bool
}

// PrestConf config variable
var PrestConf *Prest

func viperCfg() {
	filePath := getDefaultPrestConf(os.Getenv("PREST_CONF"))
	dir, file := filepath.Split(filePath)
	file = strings.TrimSuffix(file, filepath.Ext(file))
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvPrefix("PREST")
	viper.AutomaticEnv()
	viper.SetEnvKeyReplacer(replacer)
	viper.AddConfigPath(dir)
	viper.SetConfigName(file)
	viper.SetConfigType("toml")
	viper.SetDefault("http.port", 3000)
	viper.SetDefault("pg.host", "127.0.0.1")
	viper.SetDefault("pg.port", 5432)
	viper.SetDefault("pg.maxidleconn", 10)
	viper.SetDefault("pg.maxopenconn", 10)
	viper.SetDefault("pg.conntimeout", 10)
	viper.SetDefault("debug", false)
	viper.SetDefault("cors.allowheaders", []string{"*"})

	user, err := user.Current()
	if err != nil {
		log.Println("{viperCfg}", err)
	}

	viper.SetDefault("queries.location", filepath.Join(user.HomeDir, "queries"))
}

func getDefaultPrestConf(prestConf string) string {
	if prestConf == "" {
		return "./prest.toml"
	}
	return prestConf
}

// Parse pREST config
func Parse(cfg *Prest) (err error) {
	err = viper.ReadInConfig()
	if err != nil {
		switch err.(type) {
		case viper.ConfigFileNotFoundError:
			log.Warningln("Running without config file.")
		default:
			return
		}
	}
	cfg.HTTPPort = viper.GetInt("http.port")
	cfg.PGHost = viper.GetString("pg.host")
	cfg.PGPort = viper.GetInt("pg.port")
	cfg.PGUser = viper.GetString("pg.user")
	cfg.PGPass = viper.GetString("pg.pass")
	cfg.PGDatabase = viper.GetString("pg.database")
	cfg.PGMaxIdleConn = viper.GetInt("pg.maxidleconn")
	cfg.PGMAxOpenConn = viper.GetInt("pg.maxopenconn")
	cfg.PGConnTimeout = viper.GetInt("pg.conntimeout")
	cfg.JWTKey = viper.GetString("jwt.key")
	cfg.MigrationsPath = viper.GetString("migrations")
	cfg.AccessConf.Restrict = viper.GetBool("access.restrict")
	cfg.QueriesPath = viper.GetString("queries.location")
	cfg.CORSAllowOrigin = viper.GetStringSlice("cors.alloworigin")
	cfg.CORSAllowHeaders = viper.GetStringSlice("cors.allowheaders")
	cfg.Debug = viper.GetBool("debug")

	var t []TablesConf
	err = viper.UnmarshalKey("access.tables", &t)
	if err != nil {
		return err
	}

	cfg.AccessConf.Tables = t

	return
}

// Load configuration
func Load() {
	viperCfg()
	PrestConf = &Prest{}
	err := Parse(PrestConf)
	if err != nil {
		panic(err)
	}

	if !PrestConf.AccessConf.Restrict {
		log.Warningln("You are running pREST in public mode.")
	}

	if PrestConf.Debug {
		log.DebugMode = PrestConf.Debug
		log.Warningln("You are running pREST in debug mode.")
	}

	if _, err = os.Stat(PrestConf.QueriesPath); os.IsNotExist(err) {
		if err = os.MkdirAll(PrestConf.QueriesPath, 0700); os.IsNotExist(err) {
			log.Errorf("Queries directory %s is not created", PrestConf.QueriesPath)
		}
	}
}
<|endoftext|>