<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package vsphere_volume

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/util/mount"
	utilstrings "k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/kubernetes/pkg/volume/util/volumehelper"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&vsphereVolumePlugin{}}
}

type vsphereVolumePlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &vsphereVolumePlugin{}
var _ volume.PersistentVolumePlugin = &vsphereVolumePlugin{}
var _ volume.DeletableVolumePlugin = &vsphereVolumePlugin{}
var _ volume.ProvisionableVolumePlugin = &vsphereVolumePlugin{}

const (
	vsphereVolumePluginName = "kubernetes.io/vsphere-volume"
)

// vSphere Volume Plugin
func (plugin *vsphereVolumePlugin) Init(host volume.VolumeHost) error {
	plugin.host = host
	return nil
}

func (plugin *vsphereVolumePlugin) GetPluginName() string {
	return vsphereVolumePluginName
}

func (plugin *vsphereVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
	volumeSource, _, err := getVolumeSource(spec)
	if err != nil {
		return "", err
	}

	return volumeSource.VolumePath, nil
}

func (plugin *vsphereVolumePlugin) CanSupport(spec *volume.Spec) bool {
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.VsphereVolume != nil) ||
		(spec.Volume != nil && spec.Volume.VsphereVolume != nil)
}

func (plugin *vsphereVolumePlugin) RequiresRemount() bool {
	return false
}

func (plugin *vsphereVolumePlugin) SupportsMountOption() bool {
	return true
}

func (plugin *vsphereVolumePlugin) SupportsBulkVolumeVerification() bool {
	return true
}

func (plugin *vsphereVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
	return plugin.newMounterInternal(spec, pod.UID, &VsphereDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}

func (plugin *vsphereVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
	return plugin.newUnmounterInternal(volName, podUID, &VsphereDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}

func (plugin *vsphereVolumePlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager vdManager, mounter mount.Interface) (volume.Mounter, error) {
	vvol, _, err := getVolumeSource(spec)
	if err != nil {
		return nil, err
	}

	volPath := vvol.VolumePath
	fsType := vvol.FSType

	return &vsphereVolumeMounter{
		vsphereVolume: &vsphereVolume{
			podUID:  podUID,
			volName: spec.Name(),
			volPath: volPath,
			manager: manager,
			mounter: mounter,
			plugin:  plugin,
		},
		fsType:      fsType,
		diskMounter: volumehelper.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host)}, nil
}

func (plugin *vsphereVolumePlugin) newUnmounterInternal(volName string, podUID types.UID, manager vdManager, mounter mount.Interface) (volume.Unmounter, error) {
	return &vsphereVolumeUnmounter{
		&vsphereVolume{
			podUID:  podUID,
			volName: volName,
			manager: manager,
			mounter: mounter,
			plugin:  plugin,
		}}, nil
}

func (plugin *vsphereVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
	mounter := plugin.host.GetMounter(plugin.GetPluginName())
	pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
	volumePath, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
	if err != nil {
		return nil, err
	}
	volumePath = strings.Replace(volumePath, "\\040", " ", -1)
	glog.V(5).Infof("vSphere volume path is %q", volumePath)
	vsphereVolume := &v1.Volume{
		Name: volumeName,
		VolumeSource: v1.VolumeSource{
			VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
				VolumePath: volumePath,
			},
		},
	}
	return volume.NewSpecFromVolume(vsphereVolume), nil
}

// Abstract interface to disk operations.
type vdManager interface {
	// Creates a volume
	CreateVolume(provisioner *vsphereVolumeProvisioner) (volSpec *VolumeSpec, err error)
	// Deletes a volume
	DeleteVolume(deleter *vsphereVolumeDeleter) error
}

// vspherePersistentDisk volumes are disk resources are attached to the kubelet's host machine and exposed to the pod.
type vsphereVolume struct {
	volName string
	podUID  types.UID
	// Unique identifier of the volume, used to find the disk resource in the provider.
	volPath string
	// Filesystem type, optional.
	fsType string
	//diskID for detach disk
	diskID string
	// Utility interface that provides API calls to the provider to attach/detach disks.
	manager vdManager
	// Mounter interface that provides system calls to mount the global path to the pod local path.
	mounter mount.Interface
	// diskMounter provides the interface that is used to mount the actual block device.
	diskMounter mount.Interface
	plugin      *vsphereVolumePlugin
	volume.MetricsNil
}

var _ volume.Mounter = &vsphereVolumeMounter{}

type vsphereVolumeMounter struct {
	*vsphereVolume
	fsType      string
	diskMounter *mount.SafeFormatAndMount
}

func (b *vsphereVolumeMounter) GetAttributes() volume.Attributes {
	return volume.Attributes{
		SupportsSELinux: true,
		Managed:         true,
	}
}

// SetUp attaches the disk and bind mounts to the volume path.
func (b *vsphereVolumeMounter) SetUp(fsGroup *int64) error {
	return b.SetUpAt(b.GetPath(), fsGroup)
}

// Checks prior to mount operations to verify that the required components (binaries, etc.)
// to mount the volume are available on the underlying node.
// If not, it returns an error
func (b *vsphereVolumeMounter) CanMount() error {
	return nil
}

// SetUp attaches the disk and bind mounts to the volume path.
func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
	glog.V(5).Infof("vSphere volume setup %s to %s", b.volPath, dir)

	// TODO: handle failed mounts here.
	notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
	if err != nil && !os.IsNotExist(err) {
		glog.V(4).Infof("IsLikelyNotMountPoint failed: %v", err)
		return err
	}
	if !notmnt {
		glog.V(4).Infof("Something is already mounted to target %s", dir)
		return nil
	}

	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.V(4).Infof("Could not create directory %s: %v", dir, err)
		return err
	}

	options := []string{"bind"}

	// Perform a bind mount to the full path to allow duplicate mounts of the same PD.
	globalPDPath := makeGlobalPDPath(b.plugin.host, b.volPath)
	err = b.mounter.Mount(globalPDPath, dir, "", options)
	if err != nil {
		notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
		if mntErr != nil {
			glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
			return err
		}
		if !notmnt {
			if mntErr = b.mounter.Unmount(dir); mntErr != nil {
				glog.Errorf("Failed to unmount: %v", mntErr)
				return err
			}
			notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
			if mntErr != nil {
				glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
				return err
			}
			if !notmnt {
				glog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", b.GetPath())
				return err
			}
		}
		os.Remove(dir)
		return err
	}
	volume.SetVolumeOwnership(b, fsGroup)
	glog.V(3).Infof("vSphere volume %s mounted to %s", b.volPath, dir)

	return nil
}

var _ volume.Unmounter = &vsphereVolumeUnmounter{}

type vsphereVolumeUnmounter struct {
	*vsphereVolume
}

// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (v *vsphereVolumeUnmounter) TearDown() error {
	return v.TearDownAt(v.GetPath())
}

// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (v *vsphereVolumeUnmounter) TearDownAt(dir string) error {
	return util.UnmountPath(dir, v.mounter)
}

func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
	return path.Join(host.GetPluginDir(vsphereVolumePluginName), mount.MountsInGlobalPDPath, devName)
}

func (vv *vsphereVolume) GetPath() string {
	name := vsphereVolumePluginName
	return vv.plugin.host.GetPodVolumeDir(vv.podUID, utilstrings.EscapeQualifiedNameForDisk(name), vv.volName)
}

// vSphere Persistent Volume Plugin
func (plugin *vsphereVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
	return []v1.PersistentVolumeAccessMode{
		v1.ReadWriteOnce,
	}
}

// vSphere Deletable Volume Plugin
type vsphereVolumeDeleter struct {
	*vsphereVolume
}

var _ volume.Deleter = &vsphereVolumeDeleter{}

func (plugin *vsphereVolumePlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
	return plugin.newDeleterInternal(spec, &VsphereDiskUtil{})
}

func (plugin *vsphereVolumePlugin) newDeleterInternal(spec *volume.Spec, manager vdManager) (volume.Deleter, error) {
	if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.VsphereVolume == nil {
		return nil, fmt.Errorf("spec.PersistentVolumeSource.VsphereVolume is nil")
	}
	return &vsphereVolumeDeleter{
		&vsphereVolume{
			volName: spec.Name(),
			volPath: spec.PersistentVolume.Spec.VsphereVolume.VolumePath,
			manager: manager,
			plugin:  plugin,
		}}, nil
}

func (r *vsphereVolumeDeleter) Delete() error {
	return r.manager.DeleteVolume(r)
}

// vSphere Provisionable Volume Plugin
type vsphereVolumeProvisioner struct {
	*vsphereVolume
	options volume.VolumeOptions
}

var _ volume.Provisioner = &vsphereVolumeProvisioner{}

func (plugin *vsphereVolumePlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
	return plugin.newProvisionerInternal(options, &VsphereDiskUtil{})
}

func (plugin *vsphereVolumePlugin) newProvisionerInternal(options volume.VolumeOptions, manager vdManager) (volume.Provisioner, error) {
	return &vsphereVolumeProvisioner{
		vsphereVolume: &vsphereVolume{
			manager: manager,
			plugin:  plugin,
		},
		options: options,
	}, nil
}

func (v *vsphereVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
	if !volume.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) {
		return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes())
	}

	volSpec, err := v.manager.CreateVolume(v)
	if err != nil {
		return nil, err
	}

	if volSpec.Fstype == "" {
		volSpec.Fstype = "ext4"
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:   v.options.PVName,
			Labels: map[string]string{},
			Annotations: map[string]string{
				volumehelper.VolumeDynamicallyCreatedByKey: "vsphere-volume-dynamic-provisioner",
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: v.options.PersistentVolumeReclaimPolicy,
			AccessModes:                   v.options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dKi", volSpec.Size)),
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
					VolumePath:        volSpec.Path,
					FSType:            volSpec.Fstype,
					StoragePolicyName: volSpec.StoragePolicyName,
					StoragePolicyID:   volSpec.StoragePolicyID,
				},
			},
			MountOptions: v.options.MountOptions,
		},
	}
	if len(v.options.PVC.Spec.AccessModes) == 0 {
		pv.Spec.AccessModes = v.plugin.GetAccessModes()
	}

	return pv, nil
}

func getVolumeSource(
	spec *volume.Spec) (*v1.VsphereVirtualDiskVolumeSource, bool, error) {
	if spec.Volume != nil && spec.Volume.VsphereVolume != nil {
		return spec.Volume.VsphereVolume, spec.ReadOnly, nil
	} else if spec.PersistentVolume != nil &&
		spec.PersistentVolume.Spec.VsphereVolume != nil {
		return spec.PersistentVolume.Spec.VsphereVolume, spec.ReadOnly, nil
	}

	return nil, false, fmt.Errorf("Spec does not reference a VSphere volume type")
}
<commit_msg>Adding volume metrics support for vSphere Cloud Provider<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package vsphere_volume

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/util/mount"
	utilstrings "k8s.io/kubernetes/pkg/util/strings"
	"k8s.io/kubernetes/pkg/volume"
	"k8s.io/kubernetes/pkg/volume/util"
	"k8s.io/kubernetes/pkg/volume/util/volumehelper"
)

// This is the primary entrypoint for volume plugins.
func ProbeVolumePlugins() []volume.VolumePlugin {
	return []volume.VolumePlugin{&vsphereVolumePlugin{}}
}

type vsphereVolumePlugin struct {
	host volume.VolumeHost
}

var _ volume.VolumePlugin = &vsphereVolumePlugin{}
var _ volume.PersistentVolumePlugin = &vsphereVolumePlugin{}
var _ volume.DeletableVolumePlugin = &vsphereVolumePlugin{}
var _ volume.ProvisionableVolumePlugin = &vsphereVolumePlugin{}

const (
	vsphereVolumePluginName = "kubernetes.io/vsphere-volume"
)

func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
	return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedNameForDisk(vsphereVolumePluginName), volName)
}

// vSphere Volume Plugin
func (plugin *vsphereVolumePlugin) Init(host volume.VolumeHost) error {
	plugin.host = host
	return nil
}

func (plugin *vsphereVolumePlugin) GetPluginName() string {
	return vsphereVolumePluginName
}

func (plugin *vsphereVolumePlugin) GetVolumeName(spec *volume.Spec) (string, error) {
	volumeSource, _, err := getVolumeSource(spec)
	if err != nil {
		return "", err
	}

	return volumeSource.VolumePath, nil
}

func (plugin *vsphereVolumePlugin) CanSupport(spec *volume.Spec) bool {
	return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.VsphereVolume != nil) ||
		(spec.Volume != nil && spec.Volume.VsphereVolume != nil)
}

func (plugin *vsphereVolumePlugin) RequiresRemount() bool {
	return false
}

func (plugin *vsphereVolumePlugin) SupportsMountOption() bool {
	return true
}

func (plugin *vsphereVolumePlugin) SupportsBulkVolumeVerification() bool {
	return true
}

func (plugin *vsphereVolumePlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
	return plugin.newMounterInternal(spec, pod.UID, &VsphereDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}

func (plugin *vsphereVolumePlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
	return plugin.newUnmounterInternal(volName, podUID, &VsphereDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
}

func (plugin *vsphereVolumePlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager vdManager, mounter mount.Interface) (volume.Mounter, error) {
	vvol, _, err := getVolumeSource(spec)
	if err != nil {
		return nil, err
	}

	volPath := vvol.VolumePath
	fsType := vvol.FSType

	return &vsphereVolumeMounter{
		vsphereVolume: &vsphereVolume{
			podUID:          podUID,
			volName:         spec.Name(),
			volPath:         volPath,
			manager:         manager,
			mounter:         mounter,
			plugin:          plugin,
			MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)),
		},
		fsType:      fsType,
		diskMounter: volumehelper.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host)}, nil
}

func (plugin *vsphereVolumePlugin) newUnmounterInternal(volName string, podUID types.UID, manager vdManager, mounter mount.Interface) (volume.Unmounter, error) {
	return &vsphereVolumeUnmounter{
		&vsphereVolume{
			podUID:          podUID,
			volName:         volName,
			manager:         manager,
			mounter:         mounter,
			plugin:          plugin,
			MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
		}}, nil
}

func (plugin *vsphereVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
	mounter := plugin.host.GetMounter(plugin.GetPluginName())
	pluginDir := plugin.host.GetPluginDir(plugin.GetPluginName())
	volumePath, err := mounter.GetDeviceNameFromMount(mountPath, pluginDir)
	if err != nil {
		return nil, err
	}
	volumePath = strings.Replace(volumePath, "\\040", " ", -1)
	glog.V(5).Infof("vSphere volume path is %q", volumePath)
	vsphereVolume := &v1.Volume{
		Name: volumeName,
		VolumeSource: v1.VolumeSource{
			VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
				VolumePath: volumePath,
			},
		},
	}
	return volume.NewSpecFromVolume(vsphereVolume), nil
}

// Abstract interface to disk operations.
type vdManager interface {
	// Creates a volume
	CreateVolume(provisioner *vsphereVolumeProvisioner) (volSpec *VolumeSpec, err error)
	// Deletes a volume
	DeleteVolume(deleter *vsphereVolumeDeleter) error
}

// vspherePersistentDisk volumes are disk resources are attached to the kubelet's host machine and exposed to the pod.
type vsphereVolume struct {
	volName string
	podUID  types.UID
	// Unique identifier of the volume, used to find the disk resource in the provider.
	volPath string
	// Filesystem type, optional.
	fsType string
	//diskID for detach disk
	diskID string
	// Utility interface that provides API calls to the provider to attach/detach disks.
	manager vdManager
	// Mounter interface that provides system calls to mount the global path to the pod local path.
	mounter mount.Interface
	// diskMounter provides the interface that is used to mount the actual block device.
	diskMounter mount.Interface
	plugin      *vsphereVolumePlugin
	volume.MetricsProvider
}

var _ volume.Mounter = &vsphereVolumeMounter{}

type vsphereVolumeMounter struct {
	*vsphereVolume
	fsType      string
	diskMounter *mount.SafeFormatAndMount
}

func (b *vsphereVolumeMounter) GetAttributes() volume.Attributes {
	return volume.Attributes{
		SupportsSELinux: true,
		Managed:         true,
	}
}

// SetUp attaches the disk and bind mounts to the volume path.
func (b *vsphereVolumeMounter) SetUp(fsGroup *int64) error {
	return b.SetUpAt(b.GetPath(), fsGroup)
}

// Checks prior to mount operations to verify that the required components (binaries, etc.)
// to mount the volume are available on the underlying node.
// If not, it returns an error
func (b *vsphereVolumeMounter) CanMount() error {
	return nil
}

// SetUp attaches the disk and bind mounts to the volume path.
func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
	glog.V(5).Infof("vSphere volume setup %s to %s", b.volPath, dir)

	// TODO: handle failed mounts here.
	notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
	if err != nil && !os.IsNotExist(err) {
		glog.V(4).Infof("IsLikelyNotMountPoint failed: %v", err)
		return err
	}
	if !notmnt {
		glog.V(4).Infof("Something is already mounted to target %s", dir)
		return nil
	}

	if err := os.MkdirAll(dir, 0750); err != nil {
		glog.V(4).Infof("Could not create directory %s: %v", dir, err)
		return err
	}

	options := []string{"bind"}

	// Perform a bind mount to the full path to allow duplicate mounts of the same PD.
	globalPDPath := makeGlobalPDPath(b.plugin.host, b.volPath)
	err = b.mounter.Mount(globalPDPath, dir, "", options)
	if err != nil {
		notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
		if mntErr != nil {
			glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
			return err
		}
		if !notmnt {
			if mntErr = b.mounter.Unmount(dir); mntErr != nil {
				glog.Errorf("Failed to unmount: %v", mntErr)
				return err
			}
			notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
			if mntErr != nil {
				glog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
				return err
			}
			if !notmnt {
				glog.Errorf("%s is still mounted, despite call to unmount().  Will try again next sync loop.", b.GetPath())
				return err
			}
		}
		os.Remove(dir)
		return err
	}
	volume.SetVolumeOwnership(b, fsGroup)
	glog.V(3).Infof("vSphere volume %s mounted to %s", b.volPath, dir)

	return nil
}

var _ volume.Unmounter = &vsphereVolumeUnmounter{}

type vsphereVolumeUnmounter struct {
	*vsphereVolume
}

// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (v *vsphereVolumeUnmounter) TearDown() error {
	return v.TearDownAt(v.GetPath())
}

// Unmounts the bind mount, and detaches the disk only if the PD
// resource was the last reference to that disk on the kubelet.
func (v *vsphereVolumeUnmounter) TearDownAt(dir string) error {
	return util.UnmountPath(dir, v.mounter)
}

func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
	return path.Join(host.GetPluginDir(vsphereVolumePluginName), mount.MountsInGlobalPDPath, devName)
}

func (vv *vsphereVolume) GetPath() string {
	name := vsphereVolumePluginName
	return vv.plugin.host.GetPodVolumeDir(vv.podUID, utilstrings.EscapeQualifiedNameForDisk(name), vv.volName)
}

// vSphere Persistent Volume Plugin
func (plugin *vsphereVolumePlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
	return []v1.PersistentVolumeAccessMode{
		v1.ReadWriteOnce,
	}
}

// vSphere Deletable Volume Plugin
type vsphereVolumeDeleter struct {
	*vsphereVolume
}

var _ volume.Deleter = &vsphereVolumeDeleter{}

func (plugin *vsphereVolumePlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
	return plugin.newDeleterInternal(spec, &VsphereDiskUtil{})
}

func (plugin *vsphereVolumePlugin) newDeleterInternal(spec *volume.Spec, manager vdManager) (volume.Deleter, error) {
	if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.VsphereVolume == nil {
		return nil, fmt.Errorf("spec.PersistentVolumeSource.VsphereVolume is nil")
	}
	return &vsphereVolumeDeleter{
		&vsphereVolume{
			volName: spec.Name(),
			volPath: spec.PersistentVolume.Spec.VsphereVolume.VolumePath,
			manager: manager,
			plugin:  plugin,
		}}, nil
}

func (r *vsphereVolumeDeleter) Delete() error {
	return r.manager.DeleteVolume(r)
}

// vSphere Provisionable Volume Plugin
type vsphereVolumeProvisioner struct {
	*vsphereVolume
	options volume.VolumeOptions
}

var _ volume.Provisioner = &vsphereVolumeProvisioner{}

func (plugin *vsphereVolumePlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
	return plugin.newProvisionerInternal(options, &VsphereDiskUtil{})
}

func (plugin *vsphereVolumePlugin) newProvisionerInternal(options volume.VolumeOptions, manager vdManager) (volume.Provisioner, error) {
	return &vsphereVolumeProvisioner{
		vsphereVolume: &vsphereVolume{
			manager: manager,
			plugin:  plugin,
		},
		options: options,
	}, nil
}

func (v *vsphereVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
	if !volume.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) {
		return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes())
	}

	volSpec, err := v.manager.CreateVolume(v)
	if err != nil {
		return nil, err
	}

	if volSpec.Fstype == "" {
		volSpec.Fstype = "ext4"
	}

	pv := &v1.PersistentVolume{
		ObjectMeta: metav1.ObjectMeta{
			Name:   v.options.PVName,
			Labels: map[string]string{},
			Annotations: map[string]string{
				volumehelper.VolumeDynamicallyCreatedByKey: "vsphere-volume-dynamic-provisioner",
			},
		},
		Spec: v1.PersistentVolumeSpec{
			PersistentVolumeReclaimPolicy: v.options.PersistentVolumeReclaimPolicy,
			AccessModes:                   v.options.PVC.Spec.AccessModes,
			Capacity: v1.ResourceList{
				v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dKi", volSpec.Size)),
			},
			PersistentVolumeSource: v1.PersistentVolumeSource{
				VsphereVolume: &v1.VsphereVirtualDiskVolumeSource{
					VolumePath:        volSpec.Path,
					FSType:            volSpec.Fstype,
					StoragePolicyName: volSpec.StoragePolicyName,
					StoragePolicyID:   volSpec.StoragePolicyID,
				},
			},
			MountOptions: v.options.MountOptions,
		},
	}
	if len(v.options.PVC.Spec.AccessModes) == 0 {
		pv.Spec.AccessModes = v.plugin.GetAccessModes()
	}

	return pv, nil
}

func getVolumeSource(
	spec *volume.Spec) (*v1.VsphereVirtualDiskVolumeSource, bool, error) {
	if spec.Volume != nil && spec.Volume.VsphereVolume != nil {
		return spec.Volume.VsphereVolume, spec.ReadOnly, nil
	} else if spec.PersistentVolume != nil &&
		spec.PersistentVolume.Spec.VsphereVolume != nil {
		return spec.PersistentVolume.Spec.VsphereVolume, spec.ReadOnly, nil
	}

	return nil, false, fmt.Errorf("Spec does not reference a VSphere volume type")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/bitly/go-nsq"
	"log"
	"net/http"
	"encoding/json"
	"os"
	"bytes"
	"github.com/BurntSushi/toml"
	"gitlab.socrate.vsct.fr/dt/haaas"
)

var (
	ip = flag.String("ip", "4.3.2.1", "Node ip address")
	configFile = flag.String("config", "haaas.conf", "Configuration file")
	config = nsq.NewConfig()
	properties haaas.Config
	daemon *haaas.Daemon
)

func main() {

	flag.Parse()

	if _, err := toml.DecodeFile(*configFile, &properties); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	properties.IpAddr = *ip
	len := len(properties.HapHome)
	if properties.HapHome[len - 1] == '/' {
		properties.HapHome = properties.HapHome[:len - 1 ]
	}

	daemon = haaas.NewDaemon(&properties)

	log.Printf("Starting haaasd for cluster %v", properties.NodeId())

	startTryUpdateConsumer()
	startUpdateConsumer()

	starRestAPI()
}

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func startTryUpdateConsumer() {
	tryUpdateConsumer, _ := nsq.NewConsumer(fmt.Sprintf("try_update_%s", properties.ClusterId), properties.NodeId(), config)

	tryUpdateConsumer.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		defer message.Finish()

		isSlave, err := daemon.IsSlave()
		if err != nil {
			return err
		}

		if isSlave {
			log.Printf("Receive try_update event %s", message.Body)
			data, err := bodyToDatas(message.Body)
			check(err)

			hap := haaas.NewHaproxy(&properties, data.Application, data.Platform)
			err = hap.ApplyConfiguration(data)
			if err == nil {
				publishMessage("update_", data)
			}else {
				log.Fatal(err)
			}
		}

		return nil
	}))

	err := tryUpdateConsumer.ConnectToNSQLookupd(properties.LookupdAddr)
	if err != nil {
		log.Panic("Could not connect")
	}
}

func startUpdateConsumer() {
	updateConsumer, err := nsq.NewConsumer("update_" + properties.ClusterId, properties.NodeId(), config)
	check(err)

	updateConsumer.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		defer message.Finish()
		isMaster, err := daemon.IsMaster()
		if err != nil {
			return err
		}

		if isMaster {
			log.Printf("Receive update event %s", message.Body)
			data, err := bodyToDatas(message.Body)
			check(err)

			hap := haaas.NewHaproxy(&properties, data.Application, data.Platform)
			err = hap.ApplyConfiguration(data)
			if err == nil {
				publishMessage("updated_", map[string]string{"application" : data.Application, "platform": data.Platform})
			}else {
				log.Fatal(err)
			}
		}

		return nil
	}))

	err = updateConsumer.ConnectToNSQLookupd(properties.LookupdAddr)
	if err != nil {
		log.Panic("Could not connect")
	}
}

func bodyToDatas(jsonStream []byte) (haaas.EventMessage, error) {
	dec := json.NewDecoder(bytes.NewReader(jsonStream))
	var message haaas.EventMessage
	dec.Decode(&message)
	return message, nil
}

func starRestAPI() {
	http.HandleFunc("/real-ip", func(writer http.ResponseWriter, request *http.Request) {
		log.Printf("GET /real-ip")
		fmt.Fprintf(writer, "%s\n", *ip)
	})
	log.Printf("Start listening on port %d", properties.Port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", properties.Port), nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func publishMessage(topic_prefix string, data interface{}) error {
	producer, _ := nsq.NewProducer("floradora:50150", config)
	jsonMsg, _ := json.Marshal(data)
	topic := topic_prefix + properties.ClusterId
	log.Printf("Publish to %s : %s", topic, jsonMsg)
	return producer.Publish(topic, []byte(jsonMsg))
}

<commit_msg>Share producer<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/bitly/go-nsq"
	"log"
	"net/http"
	"encoding/json"
	"os"
	"bytes"
	"github.com/BurntSushi/toml"
	"gitlab.socrate.vsct.fr/dt/haaas"
)

var (
	ip = flag.String("ip", "4.3.2.1", "Node ip address")
	configFile = flag.String("config", "haaas.conf", "Configuration file")
	config = nsq.NewConfig()
	properties haaas.Config
	daemon *haaas.Daemon
	producer *nsq.Producer
)

func main() {

	flag.Parse()

	if _, err := toml.DecodeFile(*configFile, &properties); err != nil {
		log.Fatal(err)
		os.Exit(1)
	}
	properties.IpAddr = *ip
	len := len(properties.HapHome)
	if properties.HapHome[len - 1] == '/' {
		properties.HapHome = properties.HapHome[:len - 1 ]
	}

	daemon = haaas.NewDaemon(&properties)

	log.Printf("Starting haaasd (%s) with id %v", properties.Status, properties.NodeId())

	startTryUpdateConsumer()
	startUpdateConsumer()

	producer, _ = nsq.NewProducer(properties.ProducerAddr, config)

	starRestAPI()
}

func check(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func startTryUpdateConsumer() {
	tryUpdateConsumer, _ := nsq.NewConsumer(fmt.Sprintf("try_update_%s", properties.ClusterId), properties.NodeId(), config)

	tryUpdateConsumer.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		defer message.Finish()

		isSlave, err := daemon.IsSlave()
		if err != nil {
			return err
		}

		if isSlave {
			log.Printf("Receive try_update event %s", message.Body)
			data, err := bodyToDatas(message.Body)
			check(err)

			hap := haaas.NewHaproxy(&properties, data.Application, data.Platform)
			err = hap.ApplyConfiguration(data)
			if err == nil {
				commitTryUpdate(data)
			}else {
				log.Fatal(err)
			}
		}

		return nil
	}))

	err := tryUpdateConsumer.ConnectToNSQLookupd(properties.LookupdAddr)
	if err != nil {
		log.Panic("Could not connect")
	}
}

func commitTryUpdate(data haaas.EventMessage) {
	publishMessage("update_", data)
}

func startUpdateConsumer() {
	updateConsumer, err := nsq.NewConsumer("update_" + properties.ClusterId, properties.NodeId(), config)
	check(err)

	updateConsumer.AddHandler(nsq.HandlerFunc(func(message *nsq.Message) error {
		defer message.Finish()
		isMaster, err := daemon.IsMaster()
		if err != nil {
			return err
		}

		if isMaster {
			log.Printf("Receive update event %s", message.Body)
			data, err := bodyToDatas(message.Body)
			check(err)

			hap := haaas.NewHaproxy(&properties, data.Application, data.Platform)
			err = hap.ApplyConfiguration(data)

			if err == nil {
				commitUpdate(data)
			}else {
				log.Fatal(err)
			}
		}

		return nil
	}))

	err = updateConsumer.ConnectToNSQLookupd(properties.LookupdAddr)
	if err != nil {
		log.Panic("Could not connect")
	}
}

func commitUpdate(data haaas.EventMessage) {
	publishMessage("updated_", map[string]string{"application" : data.Application, "platform": data.Platform, "correlationid" : data.Correlationid})
}

// Unmarshal json to EventMessage
func bodyToDatas(jsonStream []byte) (haaas.EventMessage, error) {
	dec := json.NewDecoder(bytes.NewReader(jsonStream))
	var message haaas.EventMessage
	dec.Decode(&message)
	return message, nil
}

// Start web server
// endpoints:
//	/real-ip : get the haaasd ip address as given by the -ip command parameter
func starRestAPI() {
	http.HandleFunc("/real-ip", func(writer http.ResponseWriter, request *http.Request) {
		log.Printf("GET /real-ip")
		fmt.Fprintf(writer, "%s\n", *ip)
	})
	log.Printf("Start listening on port %d", properties.Port)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", properties.Port), nil); err != nil {
		log.Fatal("ListenAndServe:", err)
	}
}

func publishMessage(topic_prefix string, data interface{}) error {
//	producer, _ := nsq.NewProducer("floradora:50150", config)
	jsonMsg, _ := json.Marshal(data)
	topic := topic_prefix + properties.ClusterId
	log.Printf("Publish to %s : %s", topic, jsonMsg)
	return producer.Publish(topic, []byte(jsonMsg))
}

<|endoftext|>


<commit_before>package henchman

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"gopkg.in/yaml.v2"
)

type PlanProxy struct {
	Name            string       `yaml:"name"`
	Sudo            bool         `yaml:"sudo"`
	TaskProxies     []*TaskProxy `yaml:"tasks"`
	VarsProxy       *VarsProxy   `yaml:"vars"`
	InventoryGroups []string     `yaml:"hosts"`
}

// Task is for the general Task format.  Refer to task.go
// Vars are kept in scope for each Task.  So there is a Vars
// field for each task
// Include is the file name for the included Tasks list
type TaskProxy struct {
	Task        `yaml:",inline"`
	SudoState   string `yaml:omitempty`
	Include     string
	IncludeVars VarsMap `yaml:"vars"`
}

type VarsProxy struct {
	Vars VarsMap
}

// Custom unmarshaller which account for multiple include statements and include types
// NOTE: Cannot account for double includes because unmarshal(&vMap) already does
//       under the hood unmarshaling and does what any map would do, which is override
//       repeating key values
func (vp *VarsProxy) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var vMap map[string]interface{}
	var found bool
	numInclude := 0

	err := unmarshal(&vMap)
	if err != nil {
		return err
	}

	vp.Vars = make(VarsMap)
	for field, val := range vMap {
		switch field {
		case "include":
			vp.Vars["include"], found = val.([]interface{})
			if !found {
				return ErrWrongType(field, val, "[]interface{}")
			}

			numInclude++
			if numInclude > 1 {
				return fmt.Errorf("Can only have one include statement at Vars level.")
			}
		default:
			vp.Vars[field] = val
		}
	}

	return nil
}

// Custom unmarshaller which accounts for module names
func (tp *TaskProxy) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var tmap map[string]interface{}
	var found bool
	numModule := 0

	err := unmarshal(&tmap)
	if err != nil {
		return err
	}
	for field, val := range tmap {
		switch field {
		case "name":
			tp.Name, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
		case "sudo":
			tp.Sudo, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
			tp.SudoState = strconv.FormatBool(tp.Sudo)
		case "ignore_errors":
			tp.IgnoreErrors, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
		case "local":
			tp.Local, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
		case "when":
			tp.When, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if strings.Contains(tp.When, "{{") || strings.Contains(tp.When, "}}") {
				return fmt.Errorf("When field should not include {{ or }}")
			}
		case "register":
			tp.Register, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if len(strings.Fields(tp.Register)) > 1 {
				return ErrNotValidVariable(tp.Register)
			}
			if isKeyword(tp.Register) {
				return ErrKeyword(tp.Register)
			}
		case "include":
			tp.Include, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
		case "vars":
			tp.IncludeVars, found = val.(map[interface{}]interface{})
			if !found {
				return ErrWrongType(field, val, "map[interface{}]interface{}")
			}
		default:
			// We have a module
			params, found := val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if numModule > 0 {
				return fmt.Errorf("\"%v\" is an extra Module.  Can only have one module per task.", field)
			}

			tp.Module, err = NewModule(field, params)
			if err != nil {
				return fmt.Errorf("Module %v: %s", field, err.Error())
			}
			numModule++
		}
	}

	return nil
}

// Checks the a slice of TaskProxy ptrs passed in by a Plan and determines
// whether if it's an include value or a normal task.  If it's a normal task
// it appends it as a standard task, otherwise it recursively expands the include
// statement
// NOTE: task.Vars should only be valid for include taskProxies
func PreprocessTasks(taskSection []*TaskProxy, sudo bool) ([]*Task, error) {
	tasksList, err := parseTaskProxies(taskSection, nil, "", sudo)
	if err != nil {
		return nil, err
	}

	return tasksList, nil
}

func preprocessTasksHelper(buf []byte, prevVars VarsMap, prevWhen string, sudo bool) ([]*Task, error) {
	var px PlanProxy
	err := yaml.Unmarshal(buf, &px)
	if err != nil {
		return nil, err
	}

	return parseTaskProxies(px.TaskProxies, prevVars, prevWhen, sudo)
}

func parseTaskProxies(taskProxies []*TaskProxy, prevVars VarsMap, prevWhen string, sudo bool) ([]*Task, error) {
	var tasks []*Task
	for _, tp := range taskProxies {
		task := Task{}
		// links when paramter
		// put out here b/c every task can have a when
		if tp.When != "" && prevWhen != "" {
			tp.When = tp.When + " && " + prevWhen
		} else if prevWhen != "" {
			tp.When = prevWhen
		}

		if tp.Include != "" {
			buf, err := ioutil.ReadFile(tp.Include)
			if err != nil {
				return nil, err
			}

			// links previous vars
			if prevVars != nil {
				MergeMap(prevVars, tp.IncludeVars, false)
			}
			includedTasks, err := preprocessTasksHelper(buf, tp.IncludeVars, tp.When, sudo)
			if err != nil {
				return nil, err
			}

			tasks = append(tasks, includedTasks...)
		} else {
			if tp.Module == nil {
				return nil, fmt.Errorf("This task doesn't have a valid module")
			}
			task.Name = tp.Name
			task.Module = tp.Module
			task.IgnoreErrors = tp.IgnoreErrors
			task.Local = tp.Local
			task.Register = tp.Register
			task.When = tp.When
			// NOTE: plan.Vars is merged in plan.execute(...) before Render
			// Creates an empty map for later use if there are no
			// included vars
			if prevVars == nil {
				task.Vars = make(VarsMap)
			} else {
				task.Vars = prevVars
			}
			task.Sudo = sudo
			if tp.SudoState != "" {
				var err error
				if task.Sudo, err = strconv.ParseBool(tp.SudoState); err != nil {
					return nil, err
				}
			}
			tasks = append(tasks, &task)
		}
	}

	return tasks, nil
}

// Processes plan level vars with includes
// All plan level vars will be in the vars map
// And any repeat vars in the includes will be a FCFS priority
// NOTE: if the user has multipl include blocks it'll grab the one closest to
//       the bottom

func PreprocessVars(vars VarsMap) (VarsMap, error) {
	newVars := vars

	// parses include statements in vars
	if fileList, present := vars["include"]; present {
		for _, fName := range fileList.([]interface{}) {
			tempVars, err := preprocessVarsHelper(fName)
			if err != nil {
				return nil, fmt.Errorf("While checking includes - %s", err.Error())
			}
			MergeMap(tempVars, newVars, false)
		}
	}
	delete(newVars, "include")
	return newVars, nil
}

func preprocessVarsHelper(fName interface{}) (VarsMap, error) {
	newFName, found := fName.(string)
	if !found {
		return nil, ErrWrongType("Include", fName, "string")
	}

	buf, err := ioutil.ReadFile(newFName)
	if err != nil {
		return nil, err
	}

	var px PlanProxy
	err = yaml.Unmarshal(buf, &px)
	if err != nil {
		return nil, err
	}

	return px.VarsProxy.Vars, nil
}

// Process hosts list.  Checks the host list to see if any of the
// hosts entries are valid sections and will extract it based on
func filterInventory(groups []string, fullInventory Inventory) Inventory {
	// FIXME: Support globbing in the groups
	// No groups? No problem. Just return the full inventory
	//	return fullInventory
	if len(groups) == 0 {
		return fullInventory
	} else {
		filtered := Inventory{}
		filtered.Groups = make(map[string]HostGroup)
		//filtered.HostVars = fullInventory.HostVars
		for _, group := range groups {
			machines, present := fullInventory.Groups[group]
			if present {
				filtered.Groups[group] = machines
			}
		}
		filtered.HostVars = fullInventory.HostVars
		return filtered
	}
}

func newPlanProxy(buf []byte) (PlanProxy, error) {
	var px PlanProxy
	err := yaml.Unmarshal(buf, &px)
	if err != nil {
		fmt.Errorf("Error processing plan - %s", err.Error())
		return px, err
	}
	return px, nil
}

func GetInventoryGroups(buf []byte) ([]string, error) {
	px, err := newPlanProxy(buf)
	if err != nil {
		return nil, err
	}
	return px.InventoryGroups, nil

}

// For Plan
// NOTE: inventory should always be initialized and passed in?
//       or should we just check to see if it's nil?
func PreprocessPlan(buf []byte, inv Inventory) (*Plan, error) {
	px, err := newPlanProxy(buf)
	if err != nil {
		return nil, err
	}

	plan := Plan{}
	plan.Inventory = inv
	plan.Name = px.Name

	//common vars processing
	vars := make(VarsMap)
	if px.VarsProxy != nil {
		vars, err = PreprocessVars(px.VarsProxy.Vars)
		if err != nil {
			return nil, fmt.Errorf("Error processing vars - %s", err.Error())
		}
	}
	plan.Vars = vars
	tasks, err := PreprocessTasks(px.TaskProxies, px.Sudo)
	if err != nil {
		return nil, fmt.Errorf("Error processing tasks - %s", err.Error())
	}
	plan.Tasks = tasks

	return &plan, err
}
<commit_msg>#52 Fixed build 74<commit_after>package henchman

import (
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"

	"gopkg.in/yaml.v2"
)

type PlanProxy struct {
	Name            string       `yaml:"name"`
	Sudo            bool         `yaml:"sudo"`
	TaskProxies     []*TaskProxy `yaml:"tasks"`
	VarsProxy       *VarsProxy   `yaml:"vars"`
	InventoryGroups []string     `yaml:"hosts"`
}

// Task is for the general Task format.  Refer to task.go
// Vars are kept in scope for each Task.  So there is a Vars
// field for each task
// Include is the file name for the included Tasks list
type TaskProxy struct {
	Task        `yaml:",inline"`
	SudoState   string
	Include     string
	IncludeVars VarsMap `yaml:"vars"`
}

type VarsProxy struct {
	Vars VarsMap
}

// Custom unmarshaller which account for multiple include statements and include types
// NOTE: Cannot account for double includes because unmarshal(&vMap) already does
//       under the hood unmarshaling and does what any map would do, which is override
//       repeating key values
func (vp *VarsProxy) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var vMap map[string]interface{}
	var found bool
	numInclude := 0

	err := unmarshal(&vMap)
	if err != nil {
		return err
	}

	vp.Vars = make(VarsMap)
	for field, val := range vMap {
		switch field {
		case "include":
			vp.Vars["include"], found = val.([]interface{})
			if !found {
				return ErrWrongType(field, val, "[]interface{}")
			}

			numInclude++
			if numInclude > 1 {
				return fmt.Errorf("Can only have one include statement at Vars level.")
			}
		default:
			vp.Vars[field] = val
		}
	}

	return nil
}

// Custom unmarshaller which accounts for module names
func (tp *TaskProxy) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var tmap map[string]interface{}
	var found bool
	numModule := 0

	err := unmarshal(&tmap)
	if err != nil {
		return err
	}
	for field, val := range tmap {
		switch field {
		case "name":
			tp.Name, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
		case "sudo":
			tp.Sudo, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
			tp.SudoState = strconv.FormatBool(tp.Sudo)
		case "ignore_errors":
			tp.IgnoreErrors, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
		case "local":
			tp.Local, found = val.(bool)
			if !found {
				return ErrWrongType(field, val, "bool")
			}
		case "when":
			tp.When, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if strings.Contains(tp.When, "{{") || strings.Contains(tp.When, "}}") {
				return fmt.Errorf("When field should not include {{ or }}")
			}
		case "register":
			tp.Register, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if len(strings.Fields(tp.Register)) > 1 {
				return ErrNotValidVariable(tp.Register)
			}
			if isKeyword(tp.Register) {
				return ErrKeyword(tp.Register)
			}
		case "include":
			tp.Include, found = val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
		case "vars":
			tp.IncludeVars, found = val.(map[interface{}]interface{})
			if !found {
				return ErrWrongType(field, val, "map[interface{}]interface{}")
			}
		default:
			// We have a module
			params, found := val.(string)
			if !found {
				return ErrWrongType(field, val, "string")
			}
			if numModule > 0 {
				return fmt.Errorf("\"%v\" is an extra Module.  Can only have one module per task.", field)
			}

			tp.Module, err = NewModule(field, params)
			if err != nil {
				return fmt.Errorf("Module %v: %s", field, err.Error())
			}
			numModule++
		}
	}

	return nil
}

// Checks the a slice of TaskProxy ptrs passed in by a Plan and determines
// whether if it's an include value or a normal task.  If it's a normal task
// it appends it as a standard task, otherwise it recursively expands the include
// statement
// NOTE: task.Vars should only be valid for include taskProxies
func PreprocessTasks(taskSection []*TaskProxy, sudo bool) ([]*Task, error) {
	tasksList, err := parseTaskProxies(taskSection, nil, "", sudo)
	if err != nil {
		return nil, err
	}

	return tasksList, nil
}

func preprocessTasksHelper(buf []byte, prevVars VarsMap, prevWhen string, sudo bool) ([]*Task, error) {
	var px PlanProxy
	err := yaml.Unmarshal(buf, &px)
	if err != nil {
		return nil, err
	}

	return parseTaskProxies(px.TaskProxies, prevVars, prevWhen, sudo)
}

func parseTaskProxies(taskProxies []*TaskProxy, prevVars VarsMap, prevWhen string, sudo bool) ([]*Task, error) {
	var tasks []*Task
	for _, tp := range taskProxies {
		task := Task{}
		// links when paramter
		// put out here b/c every task can have a when
		if tp.When != "" && prevWhen != "" {
			tp.When = tp.When + " && " + prevWhen
		} else if prevWhen != "" {
			tp.When = prevWhen
		}

		if tp.Include != "" {
			buf, err := ioutil.ReadFile(tp.Include)
			if err != nil {
				return nil, err
			}

			// links previous vars
			if prevVars != nil {
				MergeMap(prevVars, tp.IncludeVars, false)
			}
			includedTasks, err := preprocessTasksHelper(buf, tp.IncludeVars, tp.When, sudo)
			if err != nil {
				return nil, err
			}

			tasks = append(tasks, includedTasks...)
		} else {
			if tp.Module == nil {
				return nil, fmt.Errorf("This task doesn't have a valid module")
			}
			task.Name = tp.Name
			task.Module = tp.Module
			task.IgnoreErrors = tp.IgnoreErrors
			task.Local = tp.Local
			task.Register = tp.Register
			task.When = tp.When
			// NOTE: plan.Vars is merged in plan.execute(...) before Render
			// Creates an empty map for later use if there are no
			// included vars
			if prevVars == nil {
				task.Vars = make(VarsMap)
			} else {
				task.Vars = prevVars
			}
			task.Sudo = sudo
			if tp.SudoState != "" {
				var err error
				if task.Sudo, err = strconv.ParseBool(tp.SudoState); err != nil {
					return nil, err
				}
			}
			tasks = append(tasks, &task)
		}
	}

	return tasks, nil
}

// Processes plan level vars with includes
// All plan level vars will be in the vars map
// And any repeat vars in the includes will be a FCFS priority
// NOTE: if the user has multipl include blocks it'll grab the one closest to
//       the bottom

func PreprocessVars(vars VarsMap) (VarsMap, error) {
	newVars := vars

	// parses include statements in vars
	if fileList, present := vars["include"]; present {
		for _, fName := range fileList.([]interface{}) {
			tempVars, err := preprocessVarsHelper(fName)
			if err != nil {
				return nil, fmt.Errorf("While checking includes - %s", err.Error())
			}
			MergeMap(tempVars, newVars, false)
		}
	}
	delete(newVars, "include")
	return newVars, nil
}

func preprocessVarsHelper(fName interface{}) (VarsMap, error) {
	newFName, found := fName.(string)
	if !found {
		return nil, ErrWrongType("Include", fName, "string")
	}

	buf, err := ioutil.ReadFile(newFName)
	if err != nil {
		return nil, err
	}

	var px PlanProxy
	err = yaml.Unmarshal(buf, &px)
	if err != nil {
		return nil, err
	}

	return px.VarsProxy.Vars, nil
}

// Process hosts list.  Checks the host list to see if any of the
// hosts entries are valid sections and will extract it based on
func filterInventory(groups []string, fullInventory Inventory) Inventory {
	// FIXME: Support globbing in the groups
	// No groups? No problem. Just return the full inventory
	//	return fullInventory
	if len(groups) == 0 {
		return fullInventory
	} else {
		filtered := Inventory{}
		filtered.Groups = make(map[string]HostGroup)
		//filtered.HostVars = fullInventory.HostVars
		for _, group := range groups {
			machines, present := fullInventory.Groups[group]
			if present {
				filtered.Groups[group] = machines
			}
		}
		filtered.HostVars = fullInventory.HostVars
		return filtered
	}
}

func newPlanProxy(buf []byte) (PlanProxy, error) {
	var px PlanProxy
	err := yaml.Unmarshal(buf, &px)
	if err != nil {
		fmt.Errorf("Error processing plan - %s", err.Error())
		return px, err
	}
	return px, nil
}

func GetInventoryGroups(buf []byte) ([]string, error) {
	px, err := newPlanProxy(buf)
	if err != nil {
		return nil, err
	}
	return px.InventoryGroups, nil

}

// For Plan
// NOTE: inventory should always be initialized and passed in?
//       or should we just check to see if it's nil?
func PreprocessPlan(buf []byte, inv Inventory) (*Plan, error) {
	px, err := newPlanProxy(buf)
	if err != nil {
		return nil, err
	}

	plan := Plan{}
	plan.Inventory = inv
	plan.Name = px.Name

	//common vars processing
	vars := make(VarsMap)
	if px.VarsProxy != nil {
		vars, err = PreprocessVars(px.VarsProxy.Vars)
		if err != nil {
			return nil, fmt.Errorf("Error processing vars - %s", err.Error())
		}
	}
	plan.Vars = vars
	tasks, err := PreprocessTasks(px.TaskProxies, px.Sudo)
	if err != nil {
		return nil, fmt.Errorf("Error processing tasks - %s", err.Error())
	}
	plan.Tasks = tasks

	return &plan, err
}
<|endoftext|>


<commit_before>package types

import (
	"encoding/json"

	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/tendermint/tendermint/crypto"
)

type AccountHeader struct {
	Address sdk.AccAddress `json:"address" yaml:"address"`
	PubKey  crypto.PubKey  `json:"public_key" yaml:"public_key"`
}

// Request Payload for a account headers query
type QueryAccountHeadersParams struct {
	Skip int
	Take int
}

func NewQueryAccountHeadersParams(skip int, take int) *QueryAccountHeadersParams {
	return &QueryAccountHeadersParams{Skip: skip, Take: take}
}

// Result Payload for a account headers query
type QueryAccountHeadersResult []AccountHeader

// Implement fmt.Stringer
func (n QueryAccountHeadersResult) String() string {
	res, err := json.Marshal(n)

	if err != nil {
		panic(err)
	}

	return string(res)
}
<commit_msg>Fixed merge conflicts<commit_after>package types

import (
	"encoding/json"

	sdk "github.com/cosmos/cosmos-sdk/types"
	"github.com/tendermint/tendermint/crypto"
)

type AccountHeader struct {
	Address sdk.AccAddress `json:"address" yaml:"address"`
	PubKey  crypto.PubKey  `json:"public_key" yaml:"public_key"`
}

// Request Payload for a account headers query
type QueryAccountHeadersParams struct {
	Skip int
	Take int
}

func NewQueryAccountHeadersParams(skip int, take int) QueryAccountHeadersParams {
	return QueryAccountHeadersParams{Skip: skip, Take: take}
}

// Result Payload for a account headers query
type QueryAccountHeadersResult []AccountHeader

// Implement fmt.Stringer
func (n QueryAccountHeadersResult) String() string {
	res, err := json.Marshal(n)

	if err != nil {
		panic(err)
	}

	return string(res)
}
<|endoftext|>


<commit_before>package database

import (
	"bytes"
	"fmt"
	"github.com/SlyMarbo/rss"
	"io/ioutil"
	"math/rand"
	"net/http"
	sec "rs3/security"
	"sync"
	"time"
)

var db *database

func init() {
	rand.Seed(time.Now().UnixNano())
	db = newDatabase()
}

type User struct {
	Uid      []byte
	Pswrd    []byte
	Salt     *sec.Salt
	Nick     string
	Cookies  CookieJar
	Feeds    []*rss.Feed
	FeedUrls []string
	mutex    *sync.RWMutex
}

func newUser(uid, pwd []byte, salt *sec.Salt, nick string) *User {
	user := User{
		uid,
		pwd,
		salt,
		nick,
		make(CookieJar, 0),
		make([]*rss.Feed, 0),
		make([]string, 0),
		new(sync.RWMutex),
	}
	return &user
}

//256 rand number
type Cookie struct {
	exp     time.Time
	replace time.Time
	cookie  string
}

type CookieJar []Cookie

func newCookie() Cookie {
	var buf bytes.Buffer
	for i := 0; i < 5; i++ {
		buf.WriteString(fmt.Sprintf("%x", rand.Int63()))
	}
	cookie := Cookie{
		time.Now().Add(7 * 24 * time.Hour),
		time.Now().Add(14 * 24 * time.Hour),
		buf.String()[:256]}
	return cookie
}

type UserAlreadyExists struct{}

func (err UserAlreadyExists) Error() string {
	return "User ID Already Exists"
}

type UserDoesNotExist struct{}

func (err UserDoesNotExist) Error() string {
	return "User ID Does Not Exist"
}

type EmailAlreadyExists struct{}

func (err EmailAlreadyExists) Error() string {
	return "Email Already Exists"
}

type AuthenticationError struct{}

func (err AuthenticationError) Error() string {
	return "Authentication Failure, User ID or Password Incorrect"
}

type BackupFailure struct{}

func (err BackupFailure) Error() string {
	return "Failed to Backup Database"
}

type RestoreFailure struct{}

func (err RestoreFailure) Error() string {
	return "Failed to Restore Database"
}

type database struct {
	users  map[string]*User     //uid -> User
	salts  map[string]*sec.Salt //email -> Salt
	emails map[string]struct{}  //email -> null (for email existence check)
	mutex  *sync.RWMutex
}

type Database struct{}

// func NewDatabase() *Database
func newDatabase() *database {
	db := database{
		make(map[string]*User),
		make(map[string]*sec.Salt),
		make(map[string]struct{}),
		new(sync.RWMutex),
	}
	return &db
}

func Salt(email string) *sec.Salt {
	return db.salts[email]
}

//AddUser creates a new user and adds it to the database. It establishes that both the user
//does not exist and that their email is not in use
func AddUser(uid, pwd []byte, salt *sec.Salt, nick, email string) error {
	db.mutex.RLock()
	if _, ok := db.users[string(uid)]; ok {
		db.mutex.RUnlock()
		return new(UserAlreadyExists)
	}
	if _, ok := db.emails[string(uid)]; ok {
		db.mutex.RUnlock()
		return new(EmailAlreadyExists)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user := newUser(uid, pwd, salt, nick)
	db.users[string(uid)] = user
	db.emails[email] = *new(struct{})
	return nil
}

//Delete user removes a user from the database provided they exist in the system
func DeleteUser(uid []byte) error {
	db.mutex.Lock()
	defer db.mutex.Unlock()
	if _, ok := db.users[string(uid)]; !ok {
		return new(UserDoesNotExist)
	}
	delete(db.users, string(uid))
	return nil
}

//Exists confirms whether or not a user exists in the system
func Exists(uid []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	_, ok := db.users[string(uid)]
	return ok
}

//Authenticate checks that the userID and password match.
func Authenticate(uid, pswd []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok {
		return false
	} else {
		user.mutex.RLock()
		defer user.mutex.RUnlock()
		return (bytes.Compare(user.Pswrd, pswd) == 0)
	}
}

//Login logs a user into the system, returning the cookie string
//Authenticates the user first
//if user already has a valid cookie, returns that cookie as opposed to creating one
func Login(uid, pswd []byte) (string, time.Time, error) {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	if !Authenticate(uid, pswd) {
		return "", time.Now(), new(AuthenticationError)
	}
	user, ok := db.users[string(uid)]
	if !ok {
		return "", time.Now(), new(UserDoesNotExist)
	}
	if len(user.Cookies) != 0 {
		for i := 0; i < len(user.Cookies); i++ {
			if user.Cookies[i].exp.After(time.Now()) {
				return user.Cookies[i].cookie, user.Cookies[i].exp, nil
			}
		}
		c := newCookie()
		user.Cookies = append(user.Cookies, c)
		return c.cookie, c.exp, nil
	} else {
		user.Cookies = append(user.Cookies, newCookie())
		return user.Cookies[0].cookie, user.Cookies[0].exp, nil
	}
}

//Validate checks the cookie.
func Validate(cookie string, uid []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok { //user does not exist
		return false
	}
	for _, uCookie := range user.Cookies {
		if uCookie.cookie == cookie {
			return true
		}
	}
	return false
}

//Nickname validates the cookie and returns the user's nickname
func Nickname(uid []byte, cookie string) (string, error) {
	if !Validate(cookie, uid) {
		return "", new(AuthenticationError)
	}
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok {
		return "", new(UserDoesNotExist)
	}
	return user.Nick, nil
}

//UpdatePassword changes the password of a given user
func UpdatePassword(uid, pwd, nPwd []byte) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	if !ok {
		db.mutex.RUnlock()
		return new(UserDoesNotExist)
	}
	if !Authenticate(uid, pwd) {
		db.mutex.RUnlock()
		return new(AuthenticationError)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Pswrd = nPwd
	return nil
}

//UpdateNickname updates a users nickname having first established the existence
//of a user and validated the cookie
func UpdateNickname(uid []byte, cookie string, nickname string) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	if !ok {
		db.mutex.RUnlock()
		return new(UserDoesNotExist)
	}
	if !Validate(cookie, uid) {
		db.mutex.RUnlock()
		return new(AuthenticationError)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Nick = nickname
	return nil
}

// removes all feeds from user’s account.
func ResetUserFeeds(uid []byte) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	db.mutex.Unlock()
	if !ok {
		return new(UserDoesNotExist)
	}
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Feeds = make([]*rss.Feed, 0)
	user.FeedUrls = make([]string, 0)
	return nil
}

// retrieves feed info.
func Feeds(uid []byte) ([]*rss.Feed, error) {
	if !Exists(uid) {
		return nil, new(UserDoesNotExist)
	}
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, _ := db.users[string(uid)]
	return user.Feeds, nil
}

// pushes update to user’s account.
// func Update(userID []byte, delta *Delta) error {
// 	return nil
// }

func AddFeeds(uid []byte, cookie string, urls ...string) error {
	if !Exists(uid) {
		return new(UserDoesNotExist)
	}
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user, _ := db.users[string(uid)]
	for _, url := range urls {
		resp, err := http.Get(url)
		if err != nil {
			return err
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		feed, err := rss.Parse(body)
		if err != nil {
			return nil
		}
		user.Feeds = append(user.Feeds, feed)
		user.FeedUrls = append(user.FeedUrls, feed.Link)
	}
	return nil
}

func Debug() []byte {
	return nil
}

func (d *Database) Write([]byte) (int, error) {
	// restore
	return 0, nil
}

func (d *Database) Read([]byte) (int, error) {
	// backup
	return 0, nil
}
<commit_msg>Add method for Salt retrieval<commit_after>package database

import (
	"bytes"
	"fmt"
	"github.com/SlyMarbo/rss"
	"io/ioutil"
	"math/rand"
	"net/http"
	sec "rs3/security"
	"sync"
	"time"
)

var db *database

func init() {
	rand.Seed(time.Now().UnixNano())
	db = newDatabase()
}

type User struct {
	Uid      []byte
	Pswrd    []byte
	Salt     *sec.Salt
	Nick     string
	Cookies  CookieJar
	Feeds    []*rss.Feed
	FeedUrls []string
	mutex    *sync.RWMutex
}

func newUser(uid, pwd []byte, salt *sec.Salt, nick string) *User {
	user := User{
		uid,
		pwd,
		salt,
		nick,
		make(CookieJar, 0),
		make([]*rss.Feed, 0),
		make([]string, 0),
		new(sync.RWMutex),
	}
	return &user
}

//256 rand number
type Cookie struct {
	exp     time.Time
	replace time.Time
	cookie  string
}

type CookieJar []Cookie

func newCookie() Cookie {
	var buf bytes.Buffer
	for i := 0; i < 5; i++ {
		buf.WriteString(fmt.Sprintf("%x", rand.Int63()))
	}
	cookie := Cookie{
		time.Now().Add(7 * 24 * time.Hour),
		time.Now().Add(14 * 24 * time.Hour),
		buf.String()[:256]}
	return cookie
}

type UserAlreadyExists struct{}

func (err UserAlreadyExists) Error() string {
	return "User ID Already Exists"
}

type UserDoesNotExist struct{}

func (err UserDoesNotExist) Error() string {
	return "User ID Does Not Exist"
}

type EmailAlreadyExists struct{}

func (err EmailAlreadyExists) Error() string {
	return "Email Already Exists"
}

type AuthenticationError struct{}

func (err AuthenticationError) Error() string {
	return "Authentication Failure, User ID or Password Incorrect"
}

type BackupFailure struct{}

func (err BackupFailure) Error() string {
	return "Failed to Backup Database"
}

type RestoreFailure struct{}

func (err RestoreFailure) Error() string {
	return "Failed to Restore Database"
}

type database struct {
	users  map[string]*User     //uid -> User
	salts  map[string]*sec.Salt //email -> Salt
	emails map[string]struct{}  //email -> null (for email existence check)
	mutex  *sync.RWMutex
}

type Database struct{}

// func NewDatabase() *Database
func newDatabase() *database {
	db := database{
		make(map[string]*User),
		make(map[string]*sec.Salt),
		make(map[string]struct{}),
		new(sync.RWMutex),
	}
	return &db
}

func Salt(email string) *sec.Salt {
	return db.salts[email]
}

//AddUser creates a new user and adds it to the database. It establishes that both the user
//does not exist and that their email is not in use
func AddUser(uid, pwd []byte, salt *sec.Salt, nick, email string) error {
	db.mutex.RLock()
	if _, ok := db.users[string(uid)]; ok {
		db.mutex.RUnlock()
		return new(UserAlreadyExists)
	}
	if _, ok := db.emails[string(uid)]; ok {
		db.mutex.RUnlock()
		return new(EmailAlreadyExists)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user := newUser(uid, pwd, salt, nick)
	db.users[string(uid)] = user
	db.emails[email] = *new(struct{})
	return nil
}

//Delete user removes a user from the database provided they exist in the system
func DeleteUser(uid []byte) error {
	db.mutex.Lock()
	defer db.mutex.Unlock()
	if _, ok := db.users[string(uid)]; !ok {
		return new(UserDoesNotExist)
	}
	delete(db.users, string(uid))
	return nil
}

//Exists confirms whether or not a user exists in the system
func Exists(uid []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	_, ok := db.users[string(uid)]
	return ok
}

//Authenticate checks that the userID and password match.
func Authenticate(uid, pswd []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok {
		return false
	} else {
		user.mutex.RLock()
		defer user.mutex.RUnlock()
		return (bytes.Compare(user.Pswrd, pswd) == 0)
	}
}

//Login logs a user into the system, returning the cookie string
//Authenticates the user first
//if user already has a valid cookie, returns that cookie as opposed to creating one
func Login(uid, pswd []byte) (string, time.Time, error) {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	if !Authenticate(uid, pswd) {
		return "", time.Now(), new(AuthenticationError)
	}
	user, ok := db.users[string(uid)]
	if !ok {
		return "", time.Now(), new(UserDoesNotExist)
	}
	if len(user.Cookies) != 0 {
		for i := 0; i < len(user.Cookies); i++ {
			if user.Cookies[i].exp.After(time.Now()) {
				return user.Cookies[i].cookie, user.Cookies[i].exp, nil
			}
		}
		c := newCookie()
		user.Cookies = append(user.Cookies, c)
		return c.cookie, c.exp, nil
	} else {
		user.Cookies = append(user.Cookies, newCookie())
		return user.Cookies[0].cookie, user.Cookies[0].exp, nil
	}
}

//Validate checks the cookie.
func Validate(cookie string, uid []byte) bool {
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok { //user does not exist
		return false
	}
	for _, uCookie := range user.Cookies {
		if uCookie.cookie == cookie {
			return true
		}
	}
	return false
}

//Nickname validates the cookie and returns the user's nickname
func Nickname(uid []byte, cookie string) (string, error) {
	if !Validate(cookie, uid) {
		return "", new(AuthenticationError)
	}
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, ok := db.users[string(uid)]
	if !ok {
		return "", new(UserDoesNotExist)
	}
	return user.Nick, nil
}

//UpdatePassword changes the password of a given user
func UpdatePassword(uid, pwd, nPwd []byte) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	if !ok {
		db.mutex.RUnlock()
		return new(UserDoesNotExist)
	}
	if !Authenticate(uid, pwd) {
		db.mutex.RUnlock()
		return new(AuthenticationError)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Pswrd = nPwd
	return nil
}

//UpdateNickname updates a users nickname having first established the existence
//of a user and validated the cookie
func UpdateNickname(uid []byte, cookie string, nickname string) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	if !ok {
		db.mutex.RUnlock()
		return new(UserDoesNotExist)
	}
	if !Validate(cookie, uid) {
		db.mutex.RUnlock()
		return new(AuthenticationError)
	}
	db.mutex.RUnlock()
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Nick = nickname
	return nil
}

// removes all feeds from user’s account.
func ResetUserFeeds(uid []byte) error {
	db.mutex.RLock()
	user, ok := db.users[string(uid)]
	db.mutex.Unlock()
	if !ok {
		return new(UserDoesNotExist)
	}
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user.Feeds = make([]*rss.Feed, 0)
	user.FeedUrls = make([]string, 0)
	return nil
}

// retrieves feed info.
func Feeds(uid []byte) ([]*rss.Feed, error) {
	if !Exists(uid) {
		return nil, new(UserDoesNotExist)
	}
	db.mutex.RLock()
	defer db.mutex.RUnlock()
	user, _ := db.users[string(uid)]
	return user.Feeds, nil
}

// pushes update to user’s account.
// func Update(userID []byte, delta *Delta) error {
// 	return nil
// }

func AddFeeds(uid []byte, cookie string, urls ...string) error {
	if !Exists(uid) {
		return new(UserDoesNotExist)
	}
	db.mutex.Lock()
	defer db.mutex.Unlock()
	user, _ := db.users[string(uid)]
	for _, url := range urls {
		resp, err := http.Get(url)
		if err != nil {
			return err
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		feed, err := rss.Parse(body)
		if err != nil {
			return nil
		}
		user.Feeds = append(user.Feeds, feed)
		user.FeedUrls = append(user.FeedUrls, feed.Link)
	}
	return nil
}

func Debug() []byte {
	return nil
}

func (d *Database) Write([]byte) (int, error) {
	// restore
	return 0, nil
}

func (d *Database) Read([]byte) (int, error) {
	// backup
	return 0, nil
}

func Backup(path string) error {
	_, err := os.Create(path)
	if err != nil {
		return err
	}
	return nil
}

func Restore(path string) {
	return nil
}
<|endoftext|>


<commit_before>package handler

import (
	"net/http"

	"github.com/gilcrest/go-api-basic/app"
	"github.com/gilcrest/go-api-basic/controller"
	"github.com/rs/xid"
)

// AppHandler is the struct that serves the application
// and methods for handling all HTTP requests
type AppHandler struct {
	App *app.Application
	controller.StandardResponseFields
}

// AddStandardResponseHeaders middleware is used to add any
// standard HTTP response headers
func (ah *AppHandler) AddStandardResponseHeaders(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add("Content-Type", "application/json")
			h.ServeHTTP(w, r) // call original
		})
}

// SetStandardResponseFields middleware is used to set the fields
// that are part of every response
func (ah *AppHandler) SetStandardResponseFields(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			// get byte Array representation of guid from xid package (12 bytes)
			id := xid.New()

			// Send a new TraceID and the http.Request to the
			// StandardResponseFields constructor to set the
			// StandardResponseFields of the AppHandler
			ah.StandardResponseFields = controller.NewStandardResponseFields(controller.NewTraceID(id), r)

			h.ServeHTTP(w, r) // call original
		})
}

// NewAppHandler initializes the AppHandler
func NewAppHandler(app *app.Application) *AppHandler {
	return &AppHandler{App: app}
}

// NewMockAppHandler initializes the AppHandler
func NewMockAppHandler(app *app.Application, r *http.Request) *AppHandler {

	appHandler := &AppHandler{App: app}
	// Send a mocked TraceID and the http.Request to the
	// StandardResponseFields constructor to set the
	// StandardResponseFields of the AppHandler
	appHandler.StandardResponseFields = controller.NewStandardResponseFields(controller.NewMockTraceID(), r)

	return appHandler
}
<commit_msg>If mocked app, always return a static trace id<commit_after>package handler

import (
	"net/http"

	"github.com/gilcrest/go-api-basic/app"
	"github.com/gilcrest/go-api-basic/controller"
	"github.com/rs/xid"
)

// AppHandler is the struct that serves the application
// and methods for handling all HTTP requests
type AppHandler struct {
	App *app.Application
	controller.StandardResponseFields
}

// AddStandardResponseHeaders middleware is used to add any
// standard HTTP response headers
func (ah *AppHandler) AddStandardResponseHeaders(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			w.Header().Add("Content-Type", "application/json")
			h.ServeHTTP(w, r) // call original
		})
}

// SetStandardResponseFields middleware is used to set the fields
// that are part of every response
func (ah *AppHandler) SetStandardResponseFields(h http.Handler) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			// If the app is being mocked, then we want a static TraceID for the response
			if ah.App.Mock {
				ah.StandardResponseFields = controller.NewStandardResponseFields(controller.NewMockTraceID(), r)
			} else {
				// get byte Array representation of guid from xid package (12 bytes)
				id := xid.New()

				// Send a new TraceID and the http.Request to the
				// StandardResponseFields constructor to set the
				// StandardResponseFields of the AppHandler
				ah.StandardResponseFields = controller.NewStandardResponseFields(controller.NewTraceID(id), r)
			}

			h.ServeHTTP(w, r) // call original
		})
}

// NewAppHandler initializes the AppHandler
func NewAppHandler(app *app.Application) *AppHandler {
	return &AppHandler{App: app}
}
<|endoftext|>


<commit_before>package handlers

import (
	"fmt"
	"net/http"
    "net/url"
    "io/ioutil"
    "strings"
    "github.com/nelsonleduc/calmanbot/handlers/models"
)

type GMHook struct {}

func isValidHTTPURLString(s string)  bool {
    URL, _ := url.Parse(s)
    return (URL.Scheme == "http" || URL.Scheme == "https")
}

func HandleCalman(w http.ResponseWriter, r *http.Request) {

    act, _ := models.FetchAction(12)
    UpdateAction(&act, "ambiguity strikes again")

    if act.IsURLType() {
        HandleURLAction(act, w)
    }
}

func HandleURLAction(a models.Action, w http.ResponseWriter) {
    resp, err := http.Get(a.Content)
    if err == nil {

        content, _ := ioutil.ReadAll(resp.Body)
        pathString := "responseData.results.{_randomInt_}.url"

        str := ParseJSON(content, pathString)

        success := func(s string) {
            fmt.Printf("Success: %v\n", s)
            fmt.Fprintln(w, s)
        }
        failure := func() {
            //Actually perform fallback here

            fmt.Printf("Failed")
        }

        if !ValidateURL(str, success) {
            fmt.Printf("Invalid URL: %v\n", str)

            oldStr := str
            for i := 0; i < 3 && oldStr == str; i++ {
                str = ParseJSON(content, pathString)
            }

            if !ValidateURL(str, success) {
                failure()
            }
        }
    }

    resp.Body.Close()
}

func ValidateURL(u string, success func(string)) bool {
    if isValidHTTPURLString(u) {
        resp, err := http.Get(u)

        if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
            success(u)
        } else {
            return false
        }
    } else {
        success(u)
    }

    return true
}

func UpdateAction(a *models.Action, text string) {
    text = url.QueryEscape(text)

    a.Content = strings.Replace(a.Content, "{_text_}", text, -1)
}<commit_msg>Add logging<commit_after>package handlers

import (
	"fmt"
	"net/http"
    "net/url"
    "io/ioutil"
    "strings"
    "github.com/nelsonleduc/calmanbot/handlers/models"
)

type GMHook struct {}

func isValidHTTPURLString(s string)  bool {
    URL, _ := url.Parse(s)
    return (URL.Scheme == "http" || URL.Scheme == "https")
}

func HandleCalman(w http.ResponseWriter, r *http.Request) {

    act, _ := models.FetchAction(12)
    UpdateAction(&act, "ambiguity strikes again")

    if act.IsURLType() {
        HandleURLAction(act, w)
    }
}

func HandleURLAction(a models.Action, w http.ResponseWriter) {

    fmt.Fprintln(w, a)
    resp, err := http.Get(a.Content)
    if err == nil {

        content, _ := ioutil.ReadAll(resp.Body)
        pathString := "responseData.results.{_randomInt_}.url"

        str := ParseJSON(content, pathString)

        success := func(s string) {
            fmt.Printf("Success: %v\n", s)
            fmt.Fprintln(w, s)
        }
        failure := func() {
            //Actually perform fallback here

            fmt.Printf("Failed")
        }

        if !ValidateURL(str, success) {
            fmt.Printf("Invalid URL: %v\n", str)

            oldStr := str
            for i := 0; i < 3 && oldStr == str; i++ {
                str = ParseJSON(content, pathString)
            }

            if !ValidateURL(str, success) {
                failure()
            }
        }
    }

    resp.Body.Close()
}

func ValidateURL(u string, success func(string)) bool {
    if isValidHTTPURLString(u) {
        resp, err := http.Get(u)

        if err == nil && resp.StatusCode >= 200 && resp.StatusCode < 300 {
            success(u)
        } else {
            return false
        }
    } else {
        success(u)
    }

    return true
}

func UpdateAction(a *models.Action, text string) {
    text = url.QueryEscape(text)

    a.Content = strings.Replace(a.Content, "{_text_}", text, -1)
}<|endoftext|>


<commit_before>package wallet

import (
	"math"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"
)

// updateConfirmedSet uses a consensus change to update the confirmed set of
// outputs as understood by the wallet.
func (w *Wallet) updateConfirmedSet(cc modules.ConsensusChange) {
	for _, diff := range cc.CoinOutputDiffs {
		// Verify that the diff is relevant to the wallet.
		_, exists := w.keys[diff.CoinOutput.UnlockHash]
		if !exists {
			continue
		}

		_, exists = w.coinOutputs[diff.ID]
		if diff.Direction == modules.DiffApply {
			if build.DEBUG && exists {
				panic("adding an existing output to wallet")
			}
			w.coinOutputs[diff.ID] = diff.CoinOutput
		} else {
			if build.DEBUG && !exists {
				panic("deleting nonexisting output from wallet")
			}
			delete(w.coinOutputs, diff.ID)
		}
	}
	for _, diff := range cc.BlockStakeOutputDiffs {
		// Verify that the diff is relevant to the wallet.
		_, exists := w.keys[diff.BlockStakeOutput.UnlockHash]
		if !exists {
			continue
		}

		_, exists = w.blockstakeOutputs[diff.ID]
		if diff.Direction == modules.DiffApply {
			if build.DEBUG && exists {
				panic("adding an existing output to wallet")
			}
			w.blockstakeOutputs[diff.ID] = diff.BlockStakeOutput
		} else {
			if build.DEBUG && !exists {
				panic("deleting nonexisting output from wallet")
			}
			delete(w.blockstakeOutputs, diff.ID)
		}
	}
}

// revertHistory reverts any transaction history that was destroyed by reverted
// blocks in the consensus change.
func (w *Wallet) revertHistory(cc modules.ConsensusChange) {
	for _, block := range cc.RevertedBlocks {
		// Remove any transactions that have been reverted.
		for i := len(block.Transactions) - 1; i >= 0; i-- {
			// If the transaction is relevant to the wallet, it will be the
			// most recent transaction appended to w.processedTransactions.
			// Relevance can be determined just by looking at the last element
			// of w.processedTransactions.
			txn := block.Transactions[i]
			txid := txn.ID()
			if len(w.processedTransactions) > 0 && txid == w.processedTransactions[len(w.processedTransactions)-1].TransactionID {
				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
				delete(w.processedTransactionMap, txid)
			}
		}

		// Remove the miner payout transaction if applicable.
		for _, mp := range block.MinerPayouts {
			_, exists := w.keys[mp.UnlockHash]
			if exists {
				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
				delete(w.processedTransactionMap, types.TransactionID(block.ID()))
				break
			}
		}
		w.consensusSetHeight--
	}
}

// applyHistory applies any transaction history that was introduced by the
// applied blocks.
func (w *Wallet) applyHistory(cc modules.ConsensusChange) {
	for _, block := range cc.AppliedBlocks {
		w.consensusSetHeight++
		// Apply the miner payout transaction if applicable.
		minerPT := modules.ProcessedTransaction{
			Transaction:           types.Transaction{},
			TransactionID:         types.TransactionID(block.ID()),
			ConfirmationHeight:    w.consensusSetHeight,
			ConfirmationTimestamp: block.Timestamp,
		}
		relevant := false
		for i, mp := range block.MinerPayouts {
			_, exists := w.keys[mp.UnlockHash]
			if exists {
				relevant = true
			}
			minerPT.Outputs = append(minerPT.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierMinerPayout,
				MaturityHeight: w.consensusSetHeight + types.MaturityDelay,
				WalletAddress:  exists,
				RelatedAddress: mp.UnlockHash,
				Value:          mp.Value,
			})
			w.historicOutputs[types.OutputID(block.MinerPayoutID(uint64(i)))] = mp.Value
		}
		if relevant {
			w.processedTransactions = append(w.processedTransactions, minerPT)
			w.processedTransactionMap[minerPT.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
		}
		for _, txn := range block.Transactions {
			relevant := false
			pt := modules.ProcessedTransaction{
				Transaction:           txn,
				TransactionID:         txn.ID(),
				ConfirmationHeight:    w.consensusSetHeight,
				ConfirmationTimestamp: block.Timestamp,
			}
			for _, sci := range txn.CoinInputs {
				_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
				if exists {
					relevant = true
				}
				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
					FundType:       types.SpecifierCoinInput,
					WalletAddress:  exists,
					RelatedAddress: sci.UnlockConditions.UnlockHash(),
					Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
				})
			}
			for i, sco := range txn.CoinOutputs {
				_, exists := w.keys[sco.UnlockHash]
				if exists {
					relevant = true
				}
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType:       types.SpecifierCoinOutput,
					MaturityHeight: w.consensusSetHeight,
					WalletAddress:  exists,
					RelatedAddress: sco.UnlockHash,
					Value:          sco.Value,
				})
				w.historicOutputs[types.OutputID(txn.CoinOutputID(uint64(i)))] = sco.Value
			}
			for _, sfi := range txn.BlockStakeInputs {
				_, exists := w.keys[sfi.UnlockConditions.UnlockHash()]
				if exists {
					relevant = true
				}
				sfiValue := w.historicOutputs[types.OutputID(sfi.ParentID)]
				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
					FundType:       types.SpecifierBlockStakeInput,
					WalletAddress:  exists,
					RelatedAddress: sfi.UnlockConditions.UnlockHash(),
					Value:          sfiValue,
				})
			}
			for i, sfo := range txn.BlockStakeOutputs {
				_, exists := w.keys[sfo.UnlockHash]
				if exists {
					relevant = true
				}
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType:       types.SpecifierBlockStakeOutput,
					MaturityHeight: w.consensusSetHeight,
					WalletAddress:  exists,
					RelatedAddress: sfo.UnlockHash,
					Value:          sfo.Value,
				})
				w.historicOutputs[types.OutputID(txn.BlockStakeOutputID(uint64(i)))] = sfo.Value
			}
			for _, fee := range txn.MinerFees {
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType: types.SpecifierMinerFee,
					Value:    fee,
				})
			}
			if relevant {
				w.processedTransactions = append(w.processedTransactions, pt)
				w.processedTransactionMap[pt.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
			}
		}
	}
}

// ProcessConsensusChange parses a consensus change to update the set of
// confirmed outputs known to the wallet.
func (w *Wallet) ProcessConsensusChange(cc modules.ConsensusChange) {
	if err := w.tg.Add(); err != nil {
		// The wallet should gracefully reject updates from the consensus set
		// or transaction pool that are sent after the wallet's Close method
		// has closed the wallet's ThreadGroup.
		return
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()
	w.updateConfirmedSet(cc)
	w.revertHistory(cc)
	w.applyHistory(cc)
}

// ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed
// transaction set.
func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) {
	if err := w.tg.Add(); err != nil {
		// Gracefully reject transactions if the wallet's Close method has
		// closed the wallet's ThreadGroup already.
		return
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	w.unconfirmedProcessedTransactions = nil
	for _, txn := range txns {
		// To save on code complexity, relevancy is determined while building
		// up the wallet transaction.
		relevant := false
		pt := modules.ProcessedTransaction{
			Transaction:           txn,
			TransactionID:         txn.ID(),
			ConfirmationHeight:    types.BlockHeight(math.MaxUint64),
			ConfirmationTimestamp: types.Timestamp(math.MaxUint64),
		}
		for _, sci := range txn.CoinInputs {
			_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
			if exists {
				relevant = true
			}
			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
				FundType:       types.SpecifierCoinInput,
				WalletAddress:  exists,
				RelatedAddress: sci.UnlockConditions.UnlockHash(),
				Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
			})
		}
		for i, sco := range txn.CoinOutputs {
			_, exists := w.keys[sco.UnlockHash]
			if exists {
				relevant = true
			}
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierCoinOutput,
				MaturityHeight: types.BlockHeight(math.MaxUint64),
				WalletAddress:  exists,
				RelatedAddress: sco.UnlockHash,
				Value:          sco.Value,
			})
			w.historicOutputs[types.OutputID(txn.CoinOutputID(uint64(i)))] = sco.Value
		}

		for _, sfi := range txn.BlockStakeInputs {
			_, exists := w.keys[sfi.UnlockConditions.UnlockHash()]
			if exists {
				relevant = true
			}
			sfiValue := w.historicOutputs[types.OutputID(sfi.ParentID)]
			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
				FundType:       types.SpecifierBlockStakeInput,
				WalletAddress:  exists,
				RelatedAddress: sfi.UnlockConditions.UnlockHash(),
				Value:          sfiValue,
			})
		}
		for i, sfo := range txn.BlockStakeOutputs {
			_, exists := w.keys[sfo.UnlockHash]
			if exists {
				relevant = true
			}
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierBlockStakeOutput,
				MaturityHeight: w.consensusSetHeight,
				WalletAddress:  exists,
				RelatedAddress: sfo.UnlockHash,
				Value:          sfo.Value,
			})
			w.historicOutputs[types.OutputID(txn.BlockStakeOutputID(uint64(i)))] = sfo.Value
		}

		for _, fee := range txn.MinerFees {
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType: types.SpecifierMinerFee,
				Value:    fee,
			})
		}
		if relevant {
			w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt)
		}
	}
}
<commit_msg>Reverse part of f19d1911788280b079a4956c27363571bf9fd69c<commit_after>package wallet

import (
	"math"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"
)

// updateConfirmedSet uses a consensus change to update the confirmed set of
// outputs as understood by the wallet.
func (w *Wallet) updateConfirmedSet(cc modules.ConsensusChange) {
	for _, diff := range cc.CoinOutputDiffs {
		// Verify that the diff is relevant to the wallet.
		_, exists := w.keys[diff.CoinOutput.UnlockHash]
		if !exists {
			continue
		}

		_, exists = w.coinOutputs[diff.ID]
		if diff.Direction == modules.DiffApply {
			if build.DEBUG && exists {
				panic("adding an existing output to wallet")
			}
			w.coinOutputs[diff.ID] = diff.CoinOutput
		} else {
			if build.DEBUG && !exists {
				panic("deleting nonexisting output from wallet")
			}
			delete(w.coinOutputs, diff.ID)
		}
	}
	for _, diff := range cc.BlockStakeOutputDiffs {
		// Verify that the diff is relevant to the wallet.
		_, exists := w.keys[diff.BlockStakeOutput.UnlockHash]
		if !exists {
			continue
		}

		_, exists = w.blockstakeOutputs[diff.ID]
		if diff.Direction == modules.DiffApply {
			if build.DEBUG && exists {
				panic("adding an existing output to wallet")
			}
			w.blockstakeOutputs[diff.ID] = diff.BlockStakeOutput
		} else {
			if build.DEBUG && !exists {
				panic("deleting nonexisting output from wallet")
			}
			delete(w.blockstakeOutputs, diff.ID)
		}
	}
}

// revertHistory reverts any transaction history that was destroyed by reverted
// blocks in the consensus change.
func (w *Wallet) revertHistory(cc modules.ConsensusChange) {
	for _, block := range cc.RevertedBlocks {
		// Remove any transactions that have been reverted.
		for i := len(block.Transactions) - 1; i >= 0; i-- {
			// If the transaction is relevant to the wallet, it will be the
			// most recent transaction appended to w.processedTransactions.
			// Relevance can be determined just by looking at the last element
			// of w.processedTransactions.
			txn := block.Transactions[i]
			txid := txn.ID()
			if len(w.processedTransactions) > 0 && txid == w.processedTransactions[len(w.processedTransactions)-1].TransactionID {
				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
				delete(w.processedTransactionMap, txid)
			}
		}

		// Remove the miner payout transaction if applicable.
		for _, mp := range block.MinerPayouts {
			_, exists := w.keys[mp.UnlockHash]
			if exists {
				w.processedTransactions = w.processedTransactions[:len(w.processedTransactions)-1]
				delete(w.processedTransactionMap, types.TransactionID(block.ID()))
				break
			}
		}
		w.consensusSetHeight--
	}
}

// applyHistory applies any transaction history that was introduced by the
// applied blocks.
func (w *Wallet) applyHistory(cc modules.ConsensusChange) {
	for _, block := range cc.AppliedBlocks {
		w.consensusSetHeight++
		// Apply the miner payout transaction if applicable.
		minerPT := modules.ProcessedTransaction{
			Transaction:           types.Transaction{},
			TransactionID:         types.TransactionID(block.ID()),
			ConfirmationHeight:    w.consensusSetHeight,
			ConfirmationTimestamp: block.Timestamp,
		}
		relevant := false
		for i, mp := range block.MinerPayouts {
			_, exists := w.keys[mp.UnlockHash]
			if exists {
				relevant = true
			}
			minerPT.Outputs = append(minerPT.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierMinerPayout,
				MaturityHeight: w.consensusSetHeight + types.MaturityDelay,
				WalletAddress:  exists,
				RelatedAddress: mp.UnlockHash,
				Value:          mp.Value,
			})
			w.historicOutputs[types.OutputID(block.MinerPayoutID(uint64(i)))] = mp.Value
		}
		if relevant {
			w.processedTransactions = append(w.processedTransactions, minerPT)
			w.processedTransactionMap[minerPT.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
		}
		for _, txn := range block.Transactions {
			relevant := false
			pt := modules.ProcessedTransaction{
				Transaction:           txn,
				TransactionID:         txn.ID(),
				ConfirmationHeight:    w.consensusSetHeight,
				ConfirmationTimestamp: block.Timestamp,
			}
			for _, sci := range txn.CoinInputs {
				_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
				if exists {
					relevant = true
				}
				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
					FundType:       types.SpecifierCoinInput,
					WalletAddress:  exists,
					RelatedAddress: sci.UnlockConditions.UnlockHash(),
					Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
				})
			}
			for i, sco := range txn.CoinOutputs {
				_, exists := w.keys[sco.UnlockHash]
				if exists {
					relevant = true
				}
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType:       types.SpecifierCoinOutput,
					MaturityHeight: w.consensusSetHeight,
					WalletAddress:  exists,
					RelatedAddress: sco.UnlockHash,
					Value:          sco.Value,
				})
				w.historicOutputs[types.OutputID(txn.CoinOutputID(uint64(i)))] = sco.Value
			}
			for _, sfi := range txn.BlockStakeInputs {
				_, exists := w.keys[sfi.UnlockConditions.UnlockHash()]
				if exists {
					relevant = true
				}
				sfiValue := w.historicOutputs[types.OutputID(sfi.ParentID)]
				pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
					FundType:       types.SpecifierBlockStakeInput,
					WalletAddress:  exists,
					RelatedAddress: sfi.UnlockConditions.UnlockHash(),
					Value:          sfiValue,
				})
			}
			for i, sfo := range txn.BlockStakeOutputs {
				_, exists := w.keys[sfo.UnlockHash]
				if exists {
					relevant = true
				}
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType:       types.SpecifierBlockStakeOutput,
					MaturityHeight: w.consensusSetHeight,
					WalletAddress:  exists,
					RelatedAddress: sfo.UnlockHash,
					Value:          sfo.Value,
				})
				w.historicOutputs[types.OutputID(txn.BlockStakeOutputID(uint64(i)))] = sfo.Value
			}
			for _, fee := range txn.MinerFees {
				pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
					FundType: types.SpecifierMinerFee,
					Value:    fee,
				})
			}
			if relevant {
				w.processedTransactions = append(w.processedTransactions, pt)
				w.processedTransactionMap[pt.TransactionID] = &w.processedTransactions[len(w.processedTransactions)-1]
			}
		}
	}
}

// ProcessConsensusChange parses a consensus change to update the set of
// confirmed outputs known to the wallet.
func (w *Wallet) ProcessConsensusChange(cc modules.ConsensusChange) {
	if err := w.tg.Add(); err != nil {
		// The wallet should gracefully reject updates from the consensus set
		// or transaction pool that are sent after the wallet's Close method
		// has closed the wallet's ThreadGroup.
		return
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()
	w.updateConfirmedSet(cc)
	w.revertHistory(cc)
	w.applyHistory(cc)
}

// ReceiveUpdatedUnconfirmedTransactions updates the wallet's unconfirmed
// transaction set.
func (w *Wallet) ReceiveUpdatedUnconfirmedTransactions(txns []types.Transaction, _ modules.ConsensusChange) {
	if err := w.tg.Add(); err != nil {
		// Gracefully reject transactions if the wallet's Close method has
		// closed the wallet's ThreadGroup already.
		return
	}
	defer w.tg.Done()
	w.mu.Lock()
	defer w.mu.Unlock()

	w.unconfirmedProcessedTransactions = nil
	for _, txn := range txns {
		// To save on code complexity, relevancy is determined while building
		// up the wallet transaction.
		relevant := false
		pt := modules.ProcessedTransaction{
			Transaction:           txn,
			TransactionID:         txn.ID(),
			ConfirmationHeight:    types.BlockHeight(math.MaxUint64),
			ConfirmationTimestamp: types.Timestamp(math.MaxUint64),
		}
		for _, sci := range txn.CoinInputs {
			_, exists := w.keys[sci.UnlockConditions.UnlockHash()]
			if exists {
				relevant = true
			}
			pt.Inputs = append(pt.Inputs, modules.ProcessedInput{
				FundType:       types.SpecifierCoinInput,
				WalletAddress:  exists,
				RelatedAddress: sci.UnlockConditions.UnlockHash(),
				Value:          w.historicOutputs[types.OutputID(sci.ParentID)],
			})
		}
		for i, sco := range txn.CoinOutputs {
			_, exists := w.keys[sco.UnlockHash]
			if exists {
				relevant = true
			}
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType:       types.SpecifierCoinOutput,
				MaturityHeight: types.BlockHeight(math.MaxUint64),
				WalletAddress:  exists,
				RelatedAddress: sco.UnlockHash,
				Value:          sco.Value,
			})
			w.historicOutputs[types.OutputID(txn.CoinOutputID(uint64(i)))] = sco.Value
		}
		for _, fee := range txn.MinerFees {
			pt.Outputs = append(pt.Outputs, modules.ProcessedOutput{
				FundType: types.SpecifierMinerFee,
				Value:    fee,
			})
		}
		if relevant {
			w.unconfirmedProcessedTransactions = append(w.unconfirmedProcessedTransactions, pt)
		}
	}
}
<|endoftext|>


<commit_before>package cemi

// A Priority determines the priority.
type Priority uint8

// These are known priorities.
const (
	PrioritySystem Priority = 0
	PriorityNormal Priority = 1
	PriorityUrgent Priority = 2
	PriorityLow    Priority = 3
)

// ControlField1 contains various control information.
type ControlField1 uint8

// MakeControlField1 generates a control field 1 value.
func MakeControlField1(
	stdFrame bool,
	isRepeated bool,
	sysBroadcast bool,
	prio Priority,
	wantAck bool,
	isErr bool,
) (ret ControlField1) {
	if stdFrame {
		ret |= 1 << 7
	}

	if !isRepeated {
		ret |= 1 << 5
	}

	if !sysBroadcast {
		ret |= 1 << 4
	}

	ret |= ControlField1(prio&3) << 2

	if wantAck {
		ret |= 1 << 1
	}

	if isErr {
		ret |= 1
	}

	return
}

// ControlField2 contains various control information.
type ControlField2 uint8

// MakeControlField2 generates a control field 2 value.
func MakeControlField2(isGroupAddr bool, hopCount uint8, frameFormat uint8) (ret ControlField2) {
	if isGroupAddr {
		ret |= 1 << 7
	}

	ret |= ControlField2(hopCount&7) << 4
	ret |= ControlField2(frameFormat) & 15

	return
}
<commit_msg>Extend information about CEMI control fields<commit_after>package cemi

// A Priority determines the priority.
type Priority uint8

const (
	// PrioSystem is for high priority, configuration and management frames.
	PrioSystem Priority = 0

	// PrioNormal indicates normal priority. Ideal for short frames.
	PrioNormal Priority = 1

	// PrioUrgent indicates urgent priority.
	PrioUrgent Priority = 2

	// PrioLow indicates low priority. Ideal for long frames.
	PrioLow Priority = 3
)

// ControlField1 contains various control information.
type ControlField1 uint8

const (
	// Control1StdFrame indicate that the frame is not an extended frame. Extended frames contain
	// APDUs greater than 15 bytes.
	Control1StdFrame ControlField1 = 1 << 7

	// Control1NoRepeat causes the frame not to be repeated on the medium in case of an error.
	Control1NoRepeat ControlField1 = 1 << 5

	// Control1NoSysBroadcast causes the frame to be transmitted in normal broadcast mode, instead
	// of system broadcast mode.
	Control1NoSysBroadcast ControlField1 = 1 << 4

	// Control1WantAck requests an acknowledgement. Only works for L_Data.req.
	Control1WantAck ControlField1 = 1 << 1

	// Control1HasError indicates an error. Only relevant in L_Data.con.
	Control1HasError ControlField1 = 1
)

// Control1Prio generates the control field 1 flag for the given priority.
func Control1Prio(prio Priority) ControlField1 {
	return ControlField1(prio&3) << 2
}

// ControlField2 contains various control information.
type ControlField2 uint8

const (
	// Control2GrpAddr determines that the destination address inside the frame is a group address,
	// instead of an individual address.
	Control2GrpAddr ControlField2 = 1 << 7

	// Control2LTEFrame indicates that the frame is a LTE-frame.
	Control2LTEFrame ControlField2 = 1 << 2
)

// Control2Hops generates the control field 2 flag for the given number of hops.
func Control2Hops(hops uint8) ControlField2 {
	return Control2Hops(hops&7) << 4
}
<|endoftext|>