<commit_before>package servicebroker

import (
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	osb "github.com/pmorie/go-open-service-broker-client/v2"
	//log "github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"

	csvv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/clusterserviceversion/v1alpha1"
)

const (
	versionKey = "Version"
	kindKey    = "Kind"
	crdNameKey = "Name"

	namespaceKey        = "namespace"
	csvNameLabel        = "clusterserviceversion-name"
	serviceClassIDLabel = "alm-service-broker-clusterserviceclass-id"
	servicePlanIDLabel  = "alm-service-broker-clusterserviceplan-id"
)

var (
	NamespaceRequiredErrorMessage     = "NamespaceRequired"
	NamespaceRequiredErrorDescription = "Namespace must be specified via the `SpaceGUID` parameter"

	ValidServiceNameDescription = "MUST only contain alphanumeric characters, periods, and hyphens (no spaces)."

	supportedOSBVersions = map[string]bool{
		osb.Version2_11().HeaderValue(): true,
		osb.Version2_12().HeaderValue(): true,
		osb.Version2_13().HeaderValue(): true,
	}
	validServiceName        = regexp.MustCompile(`[a-zA-Z0-9-]+`)
	invalidServiceNameChars = regexp.MustCompile(`[^a-zA-Z0-9-]+`)

	defaultPlanFree = true

	NamespaceRequiredError = osb.HTTPStatusCodeError{
		StatusCode:   http.StatusBadRequest,
		ErrorMessage: &NamespaceRequiredErrorMessage,
		Description:  &NamespaceRequiredErrorDescription,
	}
)

func serviceClassName(csv csvv1alpha1.ClusterServiceVersion) string {
	return invalidServiceNameChars.ReplaceAllString(strings.ToLower(csv.GetName()), "-")
}
func serviceClassID(csv csvv1alpha1.ClusterServiceVersion) string {
	return invalidServiceNameChars.ReplaceAllString(strings.ToLower(csv.GetName()), "-")
}

func planID(service string, plan csvv1alpha1.CRDDescription) string {
	return strings.ToLower(invalidServiceNameChars.ReplaceAllString(service+"-"+plan.Kind, "-"))
}

func planName(service string, plan csvv1alpha1.CRDDescription) string {
	return strings.ToLower(invalidServiceNameChars.ReplaceAllString(service+"-"+plan.Kind, "-"))
}

func csvToService(csv csvv1alpha1.ClusterServiceVersion) (osb.Service, error) {
	// validate CSV can be converted into a valid OpenServiceBroker ServiceInstance
	name := csv.GetName()
	if ok := validServiceName.MatchString(name); !ok {
		return osb.Service{}, fmt.Errorf("invalid service name '%s': %s", name, ValidServiceNameDescription)
	}
	plans := make([]osb.Plan, len(csv.Spec.CustomResourceDefinitions.Owned))
	for i, crdDef := range csv.Spec.CustomResourceDefinitions.Owned {
		plans[i] = crdToServicePlan(name, crdDef)
	}
	description := csv.Spec.Description
	if description == "" {
		description = fmt.Sprintf("OpenCloudService for %s", name) // TODO better default msg
	}

	service := osb.Service{
		Name:            serviceClassName(csv),
		ID:              serviceClassID(csv),
		Description:     description,
		Tags:            csv.Spec.Keywords,
		Requires:        []string{}, // not relevant to k8s
		Bindable:        false,      // overwritten by plan if CRD has specDescriptors defined
		Plans:           plans,
		DashboardClient: nil, // TODO
		Metadata: map[string]interface{}{
			"displayName":         csv.Spec.DisplayName + " " + csv.Spec.Version.String(),
			"longDescription":     csv.Spec.Description,
			"providerDisplayName": csv.Spec.Provider.Name,
			csvNameLabel:          csv.GetName(),
			"Spec":                csv.Spec,
			"Status":              csv.Status,
		},
	}
	if len(csv.Spec.Icon) > 0 {
		service.Metadata["imageUrl"] = fmt.Sprintf("data:%s;base64,%s", csv.Spec.Icon[0].MediaType, csv.Spec.Icon[0].Data)
	}
	if len(csv.Spec.Links) > 0 {
		service.Metadata["supportURL"] = csv.Spec.Links[0].URL
	}
	return service, nil
}

func specDescriptorsToInputParameters(specs []csvv1alpha1.SpecDescriptor) *osb.InputParametersSchema {
	parameters := map[string]csvv1alpha1.SpecDescriptor{}
	for _, s := range specs {
		parameters[s.Path] = s
	}
	return &osb.InputParametersSchema{parameters}
}

type CustomResourceObject struct {
	metav1.TypeMeta
	metav1.ObjectMeta

	Spec map[string]interface{}
}

func planToCustomResourceObject(plan osb.Plan, name string, spec map[string]interface{}) (*unstructured.Unstructured, error) {
	kind, ok := plan.Metadata[kindKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Kind\"]`")
	}
	version, ok := plan.Metadata[versionKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Version\"]`")
	}
	crdName, ok := plan.Metadata[crdNameKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Name\"]`")
	}
	apiVersion := schema.ParseGroupResource(crdName.(string)).WithVersion(version.(string)).GroupVersion().String()
	obj := CustomResourceObject{
		Spec: spec,
	}
	//log.Debugf("planToCustomResourceObject: plan=%+v cr=%+v", plan, obj)
	unstructuredCR, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
	if err != nil {
		return nil, err
	}
	cr := &unstructured.Unstructured{Object: unstructuredCR}
	cr.SetAPIVersion(apiVersion)
	cr.SetKind(kind.(string))
	cr.SetName(name)
	cr.SetLabels(map[string]string{
		servicePlanIDLabel: plan.ID,
	})
	return cr, nil
}

//'[{"apiVersion":"vault.security.coreos.com/v1alpha1","kind":"VaultService","metadata":{"name":"example"},"spec":{  "nodes":2,"version":"0.9.1-0"}}]'
func crdToServicePlan(service string, crd csvv1alpha1.CRDDescription) osb.Plan {
	bindable := false // no binding support implemented
	plan := osb.Plan{
		ID:          planID(service, crd),
		Name:        planName(service, crd),
		Description: crd.Description,
		Free:        &defaultPlanFree,
		Bindable:    &bindable,
		Metadata: map[string]interface{}{
			"displayName": crd.Kind,
			crdNameKey:    crd.Name,
			versionKey:    crd.Version,
			kindKey:       crd.Kind,
		},
		Schemas: &osb.Schemas{
			ServiceInstance: &osb.ServiceInstanceSchema{
				Create: specDescriptorsToInputParameters(crd.SpecDescriptors),
				Update: specDescriptorsToInputParameters(crd.SpecDescriptors),
			},
			ServiceBinding: nil,
		},
	}
	//log.Debugf("crdToServicePlan: crd=%+v plan=%+v", crd, plan)
	return plan
}
<commit_msg>fix(servicebroker/types): dedup description<commit_after>package servicebroker

import (
	"errors"
	"fmt"
	"net/http"
	"regexp"
	"strings"

	osb "github.com/pmorie/go-open-service-broker-client/v2"
	//log "github.com/sirupsen/logrus"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"

	csvv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/clusterserviceversion/v1alpha1"
)

const (
	versionKey = "Version"
	kindKey    = "Kind"
	crdNameKey = "Name"

	namespaceKey        = "namespace"
	csvNameLabel        = "clusterserviceversion-name"
	serviceClassIDLabel = "alm-service-broker-clusterserviceclass-id"
	servicePlanIDLabel  = "alm-service-broker-clusterserviceplan-id"
)

var (
	NamespaceRequiredErrorMessage     = "NamespaceRequired"
	NamespaceRequiredErrorDescription = "Namespace must be specified via the `SpaceGUID` parameter"

	ValidServiceNameDescription = "MUST only contain alphanumeric characters, periods, and hyphens (no spaces)."

	supportedOSBVersions = map[string]bool{
		osb.Version2_11().HeaderValue(): true,
		osb.Version2_12().HeaderValue(): true,
		osb.Version2_13().HeaderValue(): true,
	}
	validServiceName        = regexp.MustCompile(`[a-zA-Z0-9-]+`)
	invalidServiceNameChars = regexp.MustCompile(`[^a-zA-Z0-9-]+`)

	defaultPlanFree = true

	NamespaceRequiredError = osb.HTTPStatusCodeError{
		StatusCode:   http.StatusBadRequest,
		ErrorMessage: &NamespaceRequiredErrorMessage,
		Description:  &NamespaceRequiredErrorDescription,
	}
)

func serviceClassName(csv csvv1alpha1.ClusterServiceVersion) string {
	return invalidServiceNameChars.ReplaceAllString(strings.ToLower(csv.GetName()), "-")
}
func serviceClassID(csv csvv1alpha1.ClusterServiceVersion) string {
	return invalidServiceNameChars.ReplaceAllString(strings.ToLower(csv.GetName()), "-")
}

func planID(service string, plan csvv1alpha1.CRDDescription) string {
	return strings.ToLower(invalidServiceNameChars.ReplaceAllString(service+"-"+plan.Kind, "-"))
}

func planName(service string, plan csvv1alpha1.CRDDescription) string {
	return strings.ToLower(invalidServiceNameChars.ReplaceAllString(service+"-"+plan.Kind, "-"))
}

func csvToService(csv csvv1alpha1.ClusterServiceVersion) (osb.Service, error) {
	// validate CSV can be converted into a valid OpenServiceBroker ServiceInstance
	name := csv.GetName()
	if ok := validServiceName.MatchString(name); !ok {
		return osb.Service{}, fmt.Errorf("invalid service name '%s': %s", name, ValidServiceNameDescription)
	}
	plans := make([]osb.Plan, len(csv.Spec.CustomResourceDefinitions.Owned))
	for i, crdDef := range csv.Spec.CustomResourceDefinitions.Owned {
		plans[i] = crdToServicePlan(name, crdDef)
	}
	description := fmt.Sprintf("%s %s (%s) by %s", csv.Spec.DisplayName, csv.Spec.Version.String(), csv.Spec.Maturity, csv.Spec.Provider.Name) // TODO better default msg

	service := osb.Service{
		Name:            serviceClassName(csv),
		ID:              serviceClassID(csv),
		Description:     description,
		Tags:            csv.Spec.Keywords,
		Requires:        []string{}, // not relevant to k8s
		Bindable:        false,      // overwritten by plan if CRD has specDescriptors defined
		Plans:           plans,
		DashboardClient: nil, // TODO
		Metadata: map[string]interface{}{
			"displayName":         csv.Spec.DisplayName + " " + csv.Spec.Version.String(),
			"longDescription":     csv.Spec.Description,
			"providerDisplayName": csv.Spec.Provider.Name,
			csvNameLabel:          csv.GetName(),
			"Spec":                csv.Spec,
			"Status":              csv.Status,
		},
	}
	if len(csv.Spec.Icon) > 0 {
		service.Metadata["imageUrl"] = fmt.Sprintf("data:%s;base64,%s", csv.Spec.Icon[0].MediaType, csv.Spec.Icon[0].Data)
	}
	if len(csv.Spec.Links) > 0 {
		service.Metadata["supportURL"] = csv.Spec.Links[0].URL
	}
	return service, nil
}

func specDescriptorsToInputParameters(specs []csvv1alpha1.SpecDescriptor) *osb.InputParametersSchema {
	parameters := map[string]csvv1alpha1.SpecDescriptor{}
	for _, s := range specs {
		parameters[s.Path] = s
	}
	return &osb.InputParametersSchema{parameters}
}

type CustomResourceObject struct {
	metav1.TypeMeta
	metav1.ObjectMeta

	Spec map[string]interface{}
}

func planToCustomResourceObject(plan osb.Plan, name string, spec map[string]interface{}) (*unstructured.Unstructured, error) {
	kind, ok := plan.Metadata[kindKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Kind\"]`")
	}
	version, ok := plan.Metadata[versionKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Version\"]`")
	}
	crdName, ok := plan.Metadata[crdNameKey]
	if !ok {
		return nil, errors.New("missing required field: `Metadata[\"Name\"]`")
	}
	apiVersion := schema.ParseGroupResource(crdName.(string)).WithVersion(version.(string)).GroupVersion().String()
	obj := CustomResourceObject{
		Spec: spec,
	}
	//log.Debugf("planToCustomResourceObject: plan=%+v cr=%+v", plan, obj)
	unstructuredCR, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&obj)
	if err != nil {
		return nil, err
	}
	cr := &unstructured.Unstructured{Object: unstructuredCR}
	cr.SetAPIVersion(apiVersion)
	cr.SetKind(kind.(string))
	cr.SetName(name)
	cr.SetLabels(map[string]string{
		servicePlanIDLabel: plan.ID,
	})
	return cr, nil
}

//'[{"apiVersion":"vault.security.coreos.com/v1alpha1","kind":"VaultService","metadata":{"name":"example"},"spec":{  "nodes":2,"version":"0.9.1-0"}}]'
func crdToServicePlan(service string, crd csvv1alpha1.CRDDescription) osb.Plan {
	bindable := false // no binding support implemented
	plan := osb.Plan{
		ID:          planID(service, crd),
		Name:        planName(service, crd),
		Description: crd.Description,
		Free:        &defaultPlanFree,
		Bindable:    &bindable,
		Metadata: map[string]interface{}{
			"displayName": crd.Kind,
			crdNameKey:    crd.Name,
			versionKey:    crd.Version,
			kindKey:       crd.Kind,
		},
		Schemas: &osb.Schemas{
			ServiceInstance: &osb.ServiceInstanceSchema{
				Create: specDescriptorsToInputParameters(crd.SpecDescriptors),
				Update: specDescriptorsToInputParameters(crd.SpecDescriptors),
			},
			ServiceBinding: nil,
		},
	}
	//log.Debugf("crdToServicePlan: crd=%+v plan=%+v", crd, plan)
	return plan
}
<|endoftext|>


<commit_before>package atlas

import (
	"fmt"
	"os"
	"path"
	"testing"
)

func TestCheckName(t *testing.T) {
	os.Setenv("HOME", "/home/foo")

	// Check tag usage
	file := "mytag"
	res := checkName(file)
	real := path.Join(os.Getenv("HOME"), fmt.Sprintf(".%s", file), "config.toml")
	if res != real {
		t.Errorf("Error: badly formed fullname %s—%s", res, real)
	}

	// Check fullname usage
	file = "/nonexistent/foobar.toml"
	res = checkName(file)
	if res != file {
		t.Errorf("Error: badly formed fullname %s", res)
	}

	// Check bad usage
	file = "/toto.yaml"
	res = checkName(file)
	if res != "" {
		t.Errorf("Error: should end with .toml: %s", res)
	}
}

func TestLoadConfig(t *testing.T) {
	file := "config.toml"
	conf, err := LoadConfig(file)
	if err != nil {
		t.Errorf("Malformed file %s: %v", file, err)
	}

	defaultProbe := 666
	if conf.DefaultProbe != defaultProbe {
		t.Errorf("Malformed default %d: %d", conf.DefaultProbe, defaultProbe)
	}

	key := "<INSERT-API-KEY>"
	if conf.APIKey != key {
		t.Errorf("Malformed default %s: %s", conf.APIKey, key)
	}
}
<commit_msg>Adjust tests to checkName/LoadConfig changes. Reorder to fix.<commit_after>package atlas

import (
	"fmt"
	"os"
	"path"
	"testing"
)

func TestCheckName(t *testing.T) {
	os.Setenv("HOME", "/home/foo")

	// Check tag usage
	file := "mytag"
	res := checkName(file)
	real := path.Join(os.Getenv("HOME"), fmt.Sprintf(".%s", file), "config.toml")
	if res != real {
		t.Errorf("Error: badly formed fullname %s—%s", res, real)
	}

	// Check fullname usage
	file = "/nonexistent/foobar.toml"
	res = checkName(file)
	if res != file {
		t.Errorf("Error: badly formed fullname %s", res)
	}

	// Check bad usage
	file = "/toto.yaml"
	res = checkName(file)
	if res != "" {
		t.Errorf("Error: should end with .toml: %s", res)
	}

	// Check plain file
	file = "foo.toml"
	res = checkName(file)
	if res != file {
		t.Errorf("Error: plein file.toml is acceptable")
	}
}

func TestLoadConfig(t *testing.T) {
	file := "newconfig.toml"
	conf, err := LoadConfig(file)
	if err != nil {
		t.Error("%s does not exist, it should not be an error", file)
	}

	file = "config.toml"
	conf, err = LoadConfig(file)
	if err != nil {
		t.Errorf("Malformed file %s: %v", file, err)
	}

	defaultProbe := 666
	if conf.DefaultProbe != defaultProbe {
		t.Errorf("Malformed default %d: %d\nconf: %#v", conf.DefaultProbe, defaultProbe, conf)
	}

	key := "<INSERT-API-KEY>"
	if conf.APIKey != key {
		t.Errorf("Malformed default %s: %s", conf.APIKey, key)
	}

	pool_size := 10
	if conf.PoolSize != pool_size {
		t.Errorf("Malformed default %s: %s", conf.PoolSize, pool_size)
	}
}
<|endoftext|>


<commit_before>package watch

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"

	virtv1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/client-go/log"
	"kubevirt.io/kubevirt/pkg/controller"
	"kubevirt.io/kubevirt/pkg/util/lookup"
)

const (
	// NodeUnresponsiveReason is in various places as reason to indicate that
	// an action was taken because virt-handler became unresponsive.
	NodeUnresponsiveReason = "NodeUnresponsive"
)

// NodeController is the main NodeController struct.
type NodeController struct {
	clientset        kubecli.KubevirtClient
	Queue            workqueue.RateLimitingInterface
	nodeInformer     cache.SharedIndexInformer
	vmiInformer      cache.SharedIndexInformer
	recorder         record.EventRecorder
	heartBeatTimeout time.Duration
	recheckInterval  time.Duration
}

// NewNodeController creates a new instance of the NodeController struct.
func NewNodeController(clientset kubecli.KubevirtClient, nodeInformer cache.SharedIndexInformer, vmiInformer cache.SharedIndexInformer, recorder record.EventRecorder) *NodeController {
	c := &NodeController{
		clientset:        clientset,
		Queue:            workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
		nodeInformer:     nodeInformer,
		vmiInformer:      vmiInformer,
		recorder:         recorder,
		heartBeatTimeout: 5 * time.Minute,
		recheckInterval:  1 * time.Minute,
	}

	c.nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.addNode,
		DeleteFunc: c.deleteNode,
		UpdateFunc: c.updateNode,
	})

	c.vmiInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.addVirtualMachine,
		DeleteFunc: func(_ interface{}) {}, // nothing to do
		UpdateFunc: c.updateVirtualMachine,
	})

	return c
}

func (c *NodeController) addNode(obj interface{}) {
	c.enqueueNode(obj)
}

func (c *NodeController) deleteNode(obj interface{}) {
	c.enqueueNode(obj)
}

func (c *NodeController) updateNode(old, curr interface{}) {
	c.enqueueNode(curr)
}

func (c *NodeController) enqueueNode(obj interface{}) {
	logger := log.Log
	node := obj.(*v1.Node)
	key, err := controller.KeyFunc(node)
	if err != nil {
		logger.Object(node).Reason(err).Error("Failed to extract key from node.")
	}
	c.Queue.Add(key)
}

func (c *NodeController) addVirtualMachine(obj interface{}) {
	vmi := obj.(*virtv1.VirtualMachineInstance)
	if vmi.Status.NodeName != "" {
		c.Queue.Add(vmi.Status.NodeName)
	}
}

func (c *NodeController) updateVirtualMachine(old, curr interface{}) {
	currVMI := curr.(*virtv1.VirtualMachineInstance)
	if currVMI.Status.NodeName != "" {
		c.Queue.Add(currVMI.Status.NodeName)
	}
}

// Run runs the passed in NodeController.
func (c *NodeController) Run(threadiness int, stopCh <-chan struct{}) {
	defer controller.HandlePanic()
	defer c.Queue.ShutDown()
	log.Log.Info("Starting node controller.")

	// Wait for cache sync before we start the node controller
	cache.WaitForCacheSync(stopCh, c.nodeInformer.HasSynced, c.vmiInformer.HasSynced)

	// Start the actual work
	for i := 0; i < threadiness; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
	log.Log.Info("Stopping node controller.")
}

func (c *NodeController) runWorker() {
	for c.Execute() {
	}
}

// Execute runs commands from the controller queue, if there is
// an error it requeues the command. Returns false if the queue
// is empty.
func (c *NodeController) Execute() bool {
	key, quit := c.Queue.Get()
	if quit {
		return false
	}
	defer c.Queue.Done(key)
	err := c.execute(key.(string))

	if err != nil {
		log.Log.Reason(err).Infof("reenqueuing node %v", key)
		c.Queue.AddRateLimited(key)
	} else {
		log.Log.V(4).Infof("processed node %v", key)
		c.Queue.Forget(key)
	}
	return true
}

func (c *NodeController) execute(key string) error {

	obj, nodeExists, err := c.nodeInformer.GetStore().GetByKey(key)
	logger := log.DefaultLogger()

	if err != nil {
		return err
	}

	nodeName := key
	var node *v1.Node
	if nodeExists {
		node = obj.(*v1.Node)
		logger = logger.Object(node)
	} else {
		logger = logger.Key(key, "Node")
	}

	if unresponsive, err := isNodeUnresponsive(node, c.heartBeatTimeout); err != nil {
		logger.Reason(err).Error("Failed to dermine if node is responsive, will not reenqueue")
		return fmt.Errorf("failed to determine if node %s is responsive: %v", nodeName, err)
	} else if unresponsive {
		if nodeExists && node.Labels[virtv1.NodeSchedulable] == "true" {
			c.recorder.Event(node, v1.EventTypeNormal, NodeUnresponsiveReason, "virt-handler is not responsive, marking node as unresponsive")
			data := []byte(fmt.Sprintf(`{"metadata": { "labels": {"%s": "false"}}}`, virtv1.NodeSchedulable))
			_, err = c.clientset.CoreV1().Nodes().Patch(nodeName, types.StrategicMergePatchType, data)
			if err != nil {
				logger.Reason(err).Error("Failed to mark node as unschedulable")
				return fmt.Errorf("failed to mark node %s as unschedulable: %v", nodeName, err)
			}
		}
		vmis, err := lookup.VirtualMachinesOnNode(c.clientset, nodeName)
		if err != nil {
			logger.Reason(err).Error("Failed fetch vmis for node")
			return err
		} else if len(vmis) == 0 {
			if nodeExists {
				c.Queue.AddAfter(key, c.recheckInterval)
			}
			return nil
		}
		pods, err := c.alivePodsOnNode(nodeName)
		if err != nil {
			logger.Reason(err).Error("Failed fetch pods for node")
			return err
		}

		vmis = filterStuckVirtualMachinesWithoutPods(vmis, pods)

		errs := []string{}
		// Do sequential updates, we don't want to create update storms in situations where something might already be wrong
		for _, vmi := range vmis {
			c.recorder.Event(vmi, v1.EventTypeNormal, NodeUnresponsiveReason, fmt.Sprintf("virt-handler on node %s is not responsive, marking VMI as failed", vmi.Status.NodeName))
			logger.V(2).Infof("Moving vmi %s in namespace %s on unresponsive node to failed state", vmi.Name, vmi.Namespace)
			phasePatch := fmt.Sprintf(`{ "op": "replace", "path": "/status/phase", "value": "%s" }`, virtv1.Failed)
			operation := "add"
			if vmi.Status.Reason != "" {
				operation = "replace"
			}
			reasonPatch := fmt.Sprintf(`{ "op": "%s", "path": "/status/reason", "value": "%s" }`, operation, NodeUnresponsiveReason)
			_, err := c.clientset.VirtualMachineInstance(vmi.Namespace).Patch(vmi.Name, types.JSONPatchType, []byte(fmt.Sprintf("[%s, %s]", phasePatch, reasonPatch)))
			if err != nil {
				errs = append(errs, fmt.Sprintf("failed to move vmi %s in namespace %s to final state: %v", vmi.Name, vmi.Namespace, err))
				logger.Reason(err).Errorf("Failed to move vmi %s in namespace %s to final state", vmi.Name, vmi.Namespace)
			}
		}

		if len(errs) > 0 {
			return fmt.Errorf("%v", strings.Join(errs, "; "))
		}
	}
	if nodeExists {
		c.Queue.AddAfter(key, c.recheckInterval)
	}
	return nil
}

func (c *NodeController) alivePodsOnNode(nodeName string) ([]*v1.Pod, error) {
	handlerNodeSelector := fields.ParseSelectorOrDie("spec.nodeName=" + nodeName)
	list, err := c.clientset.CoreV1().Pods(v1.NamespaceAll).List(metav1.ListOptions{
		FieldSelector: handlerNodeSelector.String(),
	})
	if err != nil {
		return nil, err
	}

	pods := []*v1.Pod{}

	for i := range list.Items {
		pod := &list.Items[i]
		if controllerRef := controller.GetControllerOf(pod); !isControlledByVMI(controllerRef) {
			continue
		}

		// Some pods get stuck in a pending Termination during shutdown
		// due to virt-handler not being available to unmount container disk
		// mount propagation. A pod with all containers terminated is not
		// considered alive
		allContainersTerminated := false
		if len(pod.Status.ContainerStatuses) > 0 {
			allContainersTerminated = true
			for _, status := range pod.Status.ContainerStatuses {
				if status.State.Terminated == nil {
					allContainersTerminated = false
					break
				}
			}
		}

		phase := pod.Status.Phase
		if !allContainersTerminated && phase != v1.PodFailed && phase != v1.PodSucceeded {
			pods = append(pods, pod)
			continue
		}
	}
	return pods, nil
}

func filterStuckVirtualMachinesWithoutPods(vmis []*virtv1.VirtualMachineInstance, pods []*v1.Pod) []*virtv1.VirtualMachineInstance {
	podsPerNamespace := map[string]map[string]*v1.Pod{}

	for _, pod := range pods {
		podsForVMI, ok := podsPerNamespace[pod.Namespace]
		if !ok {
			podsForVMI = map[string]*v1.Pod{}
		}
		if controllerRef := controller.GetControllerOf(pod); isControlledByVMI(controllerRef) {
			podsForVMI[string(controllerRef.UID)] = pod
			podsPerNamespace[pod.Namespace] = podsForVMI
		}
	}

	filtered := []*virtv1.VirtualMachineInstance{}
	for _, vmi := range vmis {
		if vmi.IsScheduled() || vmi.IsRunning() {
			if podsForVMI, exists := podsPerNamespace[vmi.Namespace]; exists {
				if _, exists := podsForVMI[string(vmi.UID)]; exists {
					continue
				}
			}
			filtered = append(filtered, vmi)
		}
	}
	return filtered
}

func isControlledByVMI(controllerRef *metav1.OwnerReference) bool {
	return controllerRef != nil && controllerRef.Kind == virtv1.VirtualMachineInstanceGroupVersionKind.Kind
}

func isNodeUnresponsive(node *v1.Node, timeout time.Duration) (bool, error) {
	if node == nil {
		return true, nil
	}
	if lastHeartBeat, exists := node.Annotations[virtv1.VirtHandlerHeartbeat]; exists {

		timestamp := metav1.Time{}
		if err := json.Unmarshal([]byte(`"`+lastHeartBeat+`"`), &timestamp); err != nil {
			return false, err
		}
		if timestamp.Time.Before(metav1.Now().Add(-timeout)) {
			return true, nil
		}
	}
	return false, nil
}
<commit_msg>Add a nested comment indicating about an empty function<commit_after>package watch

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/tools/record"
	"k8s.io/client-go/util/workqueue"

	virtv1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/client-go/log"
	"kubevirt.io/kubevirt/pkg/controller"
	"kubevirt.io/kubevirt/pkg/util/lookup"
)

const (
	// NodeUnresponsiveReason is in various places as reason to indicate that
	// an action was taken because virt-handler became unresponsive.
	NodeUnresponsiveReason = "NodeUnresponsive"
)

// NodeController is the main NodeController struct.
type NodeController struct {
	clientset        kubecli.KubevirtClient
	Queue            workqueue.RateLimitingInterface
	nodeInformer     cache.SharedIndexInformer
	vmiInformer      cache.SharedIndexInformer
	recorder         record.EventRecorder
	heartBeatTimeout time.Duration
	recheckInterval  time.Duration
}

// NewNodeController creates a new instance of the NodeController struct.
func NewNodeController(clientset kubecli.KubevirtClient, nodeInformer cache.SharedIndexInformer, vmiInformer cache.SharedIndexInformer, recorder record.EventRecorder) *NodeController {
	c := &NodeController{
		clientset:        clientset,
		Queue:            workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
		nodeInformer:     nodeInformer,
		vmiInformer:      vmiInformer,
		recorder:         recorder,
		heartBeatTimeout: 5 * time.Minute,
		recheckInterval:  1 * time.Minute,
	}

	c.nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.addNode,
		DeleteFunc: c.deleteNode,
		UpdateFunc: c.updateNode,
	})

	c.vmiInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    c.addVirtualMachine,
		DeleteFunc: func(_ interface{}) { /* nothing to do */ },
		UpdateFunc: c.updateVirtualMachine,
	})

	return c
}

func (c *NodeController) addNode(obj interface{}) {
	c.enqueueNode(obj)
}

func (c *NodeController) deleteNode(obj interface{}) {
	c.enqueueNode(obj)
}

func (c *NodeController) updateNode(old, curr interface{}) {
	c.enqueueNode(curr)
}

func (c *NodeController) enqueueNode(obj interface{}) {
	logger := log.Log
	node := obj.(*v1.Node)
	key, err := controller.KeyFunc(node)
	if err != nil {
		logger.Object(node).Reason(err).Error("Failed to extract key from node.")
	}
	c.Queue.Add(key)
}

func (c *NodeController) addVirtualMachine(obj interface{}) {
	vmi := obj.(*virtv1.VirtualMachineInstance)
	if vmi.Status.NodeName != "" {
		c.Queue.Add(vmi.Status.NodeName)
	}
}

func (c *NodeController) updateVirtualMachine(old, curr interface{}) {
	currVMI := curr.(*virtv1.VirtualMachineInstance)
	if currVMI.Status.NodeName != "" {
		c.Queue.Add(currVMI.Status.NodeName)
	}
}

// Run runs the passed in NodeController.
func (c *NodeController) Run(threadiness int, stopCh <-chan struct{}) {
	defer controller.HandlePanic()
	defer c.Queue.ShutDown()
	log.Log.Info("Starting node controller.")

	// Wait for cache sync before we start the node controller
	cache.WaitForCacheSync(stopCh, c.nodeInformer.HasSynced, c.vmiInformer.HasSynced)

	// Start the actual work
	for i := 0; i < threadiness; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
	log.Log.Info("Stopping node controller.")
}

func (c *NodeController) runWorker() {
	for c.Execute() {
	}
}

// Execute runs commands from the controller queue, if there is
// an error it requeues the command. Returns false if the queue
// is empty.
func (c *NodeController) Execute() bool {
	key, quit := c.Queue.Get()
	if quit {
		return false
	}
	defer c.Queue.Done(key)
	err := c.execute(key.(string))

	if err != nil {
		log.Log.Reason(err).Infof("reenqueuing node %v", key)
		c.Queue.AddRateLimited(key)
	} else {
		log.Log.V(4).Infof("processed node %v", key)
		c.Queue.Forget(key)
	}
	return true
}

func (c *NodeController) execute(key string) error {

	obj, nodeExists, err := c.nodeInformer.GetStore().GetByKey(key)
	logger := log.DefaultLogger()

	if err != nil {
		return err
	}

	nodeName := key
	var node *v1.Node
	if nodeExists {
		node = obj.(*v1.Node)
		logger = logger.Object(node)
	} else {
		logger = logger.Key(key, "Node")
	}

	if unresponsive, err := isNodeUnresponsive(node, c.heartBeatTimeout); err != nil {
		logger.Reason(err).Error("Failed to dermine if node is responsive, will not reenqueue")
		return fmt.Errorf("failed to determine if node %s is responsive: %v", nodeName, err)
	} else if unresponsive {
		if nodeExists && node.Labels[virtv1.NodeSchedulable] == "true" {
			c.recorder.Event(node, v1.EventTypeNormal, NodeUnresponsiveReason, "virt-handler is not responsive, marking node as unresponsive")
			data := []byte(fmt.Sprintf(`{"metadata": { "labels": {"%s": "false"}}}`, virtv1.NodeSchedulable))
			_, err = c.clientset.CoreV1().Nodes().Patch(nodeName, types.StrategicMergePatchType, data)
			if err != nil {
				logger.Reason(err).Error("Failed to mark node as unschedulable")
				return fmt.Errorf("failed to mark node %s as unschedulable: %v", nodeName, err)
			}
		}
		vmis, err := lookup.VirtualMachinesOnNode(c.clientset, nodeName)
		if err != nil {
			logger.Reason(err).Error("Failed fetch vmis for node")
			return err
		} else if len(vmis) == 0 {
			if nodeExists {
				c.Queue.AddAfter(key, c.recheckInterval)
			}
			return nil
		}
		pods, err := c.alivePodsOnNode(nodeName)
		if err != nil {
			logger.Reason(err).Error("Failed fetch pods for node")
			return err
		}

		vmis = filterStuckVirtualMachinesWithoutPods(vmis, pods)

		errs := []string{}
		// Do sequential updates, we don't want to create update storms in situations where something might already be wrong
		for _, vmi := range vmis {
			c.recorder.Event(vmi, v1.EventTypeNormal, NodeUnresponsiveReason, fmt.Sprintf("virt-handler on node %s is not responsive, marking VMI as failed", vmi.Status.NodeName))
			logger.V(2).Infof("Moving vmi %s in namespace %s on unresponsive node to failed state", vmi.Name, vmi.Namespace)
			phasePatch := fmt.Sprintf(`{ "op": "replace", "path": "/status/phase", "value": "%s" }`, virtv1.Failed)
			operation := "add"
			if vmi.Status.Reason != "" {
				operation = "replace"
			}
			reasonPatch := fmt.Sprintf(`{ "op": "%s", "path": "/status/reason", "value": "%s" }`, operation, NodeUnresponsiveReason)
			_, err := c.clientset.VirtualMachineInstance(vmi.Namespace).Patch(vmi.Name, types.JSONPatchType, []byte(fmt.Sprintf("[%s, %s]", phasePatch, reasonPatch)))
			if err != nil {
				errs = append(errs, fmt.Sprintf("failed to move vmi %s in namespace %s to final state: %v", vmi.Name, vmi.Namespace, err))
				logger.Reason(err).Errorf("Failed to move vmi %s in namespace %s to final state", vmi.Name, vmi.Namespace)
			}
		}

		if len(errs) > 0 {
			return fmt.Errorf("%v", strings.Join(errs, "; "))
		}
	}
	if nodeExists {
		c.Queue.AddAfter(key, c.recheckInterval)
	}
	return nil
}

func (c *NodeController) alivePodsOnNode(nodeName string) ([]*v1.Pod, error) {
	handlerNodeSelector := fields.ParseSelectorOrDie("spec.nodeName=" + nodeName)
	list, err := c.clientset.CoreV1().Pods(v1.NamespaceAll).List(metav1.ListOptions{
		FieldSelector: handlerNodeSelector.String(),
	})
	if err != nil {
		return nil, err
	}

	pods := []*v1.Pod{}

	for i := range list.Items {
		pod := &list.Items[i]
		if controllerRef := controller.GetControllerOf(pod); !isControlledByVMI(controllerRef) {
			continue
		}

		// Some pods get stuck in a pending Termination during shutdown
		// due to virt-handler not being available to unmount container disk
		// mount propagation. A pod with all containers terminated is not
		// considered alive
		allContainersTerminated := false
		if len(pod.Status.ContainerStatuses) > 0 {
			allContainersTerminated = true
			for _, status := range pod.Status.ContainerStatuses {
				if status.State.Terminated == nil {
					allContainersTerminated = false
					break
				}
			}
		}

		phase := pod.Status.Phase
		if !allContainersTerminated && phase != v1.PodFailed && phase != v1.PodSucceeded {
			pods = append(pods, pod)
			continue
		}
	}
	return pods, nil
}

func filterStuckVirtualMachinesWithoutPods(vmis []*virtv1.VirtualMachineInstance, pods []*v1.Pod) []*virtv1.VirtualMachineInstance {
	podsPerNamespace := map[string]map[string]*v1.Pod{}

	for _, pod := range pods {
		podsForVMI, ok := podsPerNamespace[pod.Namespace]
		if !ok {
			podsForVMI = map[string]*v1.Pod{}
		}
		if controllerRef := controller.GetControllerOf(pod); isControlledByVMI(controllerRef) {
			podsForVMI[string(controllerRef.UID)] = pod
			podsPerNamespace[pod.Namespace] = podsForVMI
		}
	}

	filtered := []*virtv1.VirtualMachineInstance{}
	for _, vmi := range vmis {
		if vmi.IsScheduled() || vmi.IsRunning() {
			if podsForVMI, exists := podsPerNamespace[vmi.Namespace]; exists {
				if _, exists := podsForVMI[string(vmi.UID)]; exists {
					continue
				}
			}
			filtered = append(filtered, vmi)
		}
	}
	return filtered
}

func isControlledByVMI(controllerRef *metav1.OwnerReference) bool {
	return controllerRef != nil && controllerRef.Kind == virtv1.VirtualMachineInstanceGroupVersionKind.Kind
}

func isNodeUnresponsive(node *v1.Node, timeout time.Duration) (bool, error) {
	if node == nil {
		return true, nil
	}
	if lastHeartBeat, exists := node.Annotations[virtv1.VirtHandlerHeartbeat]; exists {

		timestamp := metav1.Time{}
		if err := json.Unmarshal([]byte(`"`+lastHeartBeat+`"`), &timestamp); err != nil {
			return false, err
		}
		if timestamp.Time.Before(metav1.Now().Add(-timeout)) {
			return true, nil
		}
	}
	return false, nil
}
<|endoftext|>


<commit_before>// Package thunder provides a means to control USB connected Dream Cheeky
// Thunder Launchers (http://dreamcheeky.com/thunder-missile-launcher).
package thunder

import (
	"fmt"

	"github.com/kylelemons/gousb/usb"
)

const (
	DOWN = 1 << iota
	UP
	LEFT
	RIGHT
	FIRE
	STOP
)

const (
	LED_OFF = iota
	LED_ON
)

// ThunderLauncher provides funcs to control a USB connected Thunder Launcher.
type ThunderLauncher struct {
	device *usb.Device
	ledOn  bool
}

// GetConnectedThunderLaunchers returns a slice of *ThunderLaunchers, each
// member of the slice corresponding to a connected Thunder Launcher.
func GetConnectedThunderLaunchers() ([]*ThunderLauncher, error) {
	ctx := usb.NewContext()
	//defer ctx.Close()

	devices, err := ctx.ListDevices(func(d *usb.Descriptor) bool {
		return d.Vendor == 0x2123 && d.Product == 0x1010
	})
	if err != nil {
		return nil, err
	}
	if len(devices) == 0 {
		return nil, fmt.Errorf("No connected Thunder Launcher devices found")
	}

	tl := make([]*ThunderLauncher, 0)
	for _, d := range devices {
		tl = append(tl, newThunderLauncher(d))
	}

	return tl, nil
}

func newThunderLauncher(device *usb.Device) *ThunderLauncher {
	tl := &ThunderLauncher{device: device}

	tl.LedOff()

	return tl
}

// Close the USB connection to the Thunder Launcher.
func (tl *ThunderLauncher) Close() error {
	return tl.device.Close()
}

func (tl *ThunderLauncher) control(msg []byte) error {
	_, err := tl.device.Control(0x21, 0x09, 0, 0, msg)
	return err
}

func (tl *ThunderLauncher) setLed(state byte) error {
	return tl.control([]byte{3, state})
}

// Turn off the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOff() error {
	err := tl.setLed(LED_OFF)

	if err != nil {
		tl.ledOn = false
	}

	return err
}

// Turn on the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOn() error {
	err := tl.setLed(LED_ON)

	if err != nil {
		tl.ledOn = true
	}

	return err
}

func (tl *ThunderLauncher) do(action byte) error {
	return tl.control([]byte{2, action})
}

// Down starts moving the Thunder Launcher down.
func (tl *ThunderLauncher) Down() error {
	return tl.do(DOWN)
}

// Up starts moving the Thunder Launcher up.
func (tl *ThunderLauncher) Up() error {
	return tl.do(UP)
}

// Left starts moving the Thunder Launcher left.
func (tl *ThunderLauncher) Left() error {
	return tl.do(LEFT)
}

// Right starts moving the Thunder Launcher right.
func (tl *ThunderLauncher) Right() error {
	return tl.do(RIGHT)
}

// Fire starts the process of firing the Thunder Launcher.
func (tl *ThunderLauncher) Fire() error {
	return tl.do(FIRE)
}

// Stop ceases the last command sent to the Thunder Launcher. Only LedOff and
// LedOn don't require Stop to be called after their invocation.
func (tl *ThunderLauncher) Stop() error {
	return tl.do(STOP)
}
<commit_msg>Add bug to remind myself about the fire timing<commit_after>// Package thunder provides a means to control USB connected Dream Cheeky
// Thunder Launchers (http://dreamcheeky.com/thunder-missile-launcher).
package thunder

import (
	"fmt"

	"github.com/kylelemons/gousb/usb"
)

const (
	DOWN = 1 << iota
	UP
	LEFT
	RIGHT
	FIRE
	STOP
)

const (
	LED_OFF = iota
	LED_ON
)

// ThunderLauncher provides funcs to control a USB connected Thunder Launcher.
type ThunderLauncher struct {
	device *usb.Device
	ledOn  bool
}

// GetConnectedThunderLaunchers returns a slice of *ThunderLaunchers, each
// member of the slice corresponding to a connected Thunder Launcher.
func GetConnectedThunderLaunchers() ([]*ThunderLauncher, error) {
	ctx := usb.NewContext()
	//defer ctx.Close()

	devices, err := ctx.ListDevices(func(d *usb.Descriptor) bool {
		return d.Vendor == 0x2123 && d.Product == 0x1010
	})
	if err != nil {
		return nil, err
	}
	if len(devices) == 0 {
		return nil, fmt.Errorf("No connected Thunder Launcher devices found")
	}

	tl := make([]*ThunderLauncher, 0)
	for _, d := range devices {
		tl = append(tl, newThunderLauncher(d))
	}

	return tl, nil
}

func newThunderLauncher(device *usb.Device) *ThunderLauncher {
	tl := &ThunderLauncher{device: device}

	tl.LedOff()

	return tl
}

// Close the USB connection to the Thunder Launcher.
func (tl *ThunderLauncher) Close() error {
	return tl.device.Close()
}

func (tl *ThunderLauncher) control(msg []byte) error {
	_, err := tl.device.Control(0x21, 0x09, 0, 0, msg)
	return err
}

func (tl *ThunderLauncher) setLed(state byte) error {
	return tl.control([]byte{3, state})
}

// Turn off the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOff() error {
	err := tl.setLed(LED_OFF)

	if err != nil {
		tl.ledOn = false
	}

	return err
}

// Turn on the Thunder Launcher's LED.
func (tl *ThunderLauncher) LedOn() error {
	err := tl.setLed(LED_ON)

	if err != nil {
		tl.ledOn = true
	}

	return err
}

func (tl *ThunderLauncher) do(action byte) error {
	return tl.control([]byte{2, action})
}

// Down starts moving the Thunder Launcher down.
func (tl *ThunderLauncher) Down() error {
	return tl.do(DOWN)
}

// Up starts moving the Thunder Launcher up.
func (tl *ThunderLauncher) Up() error {
	return tl.do(UP)
}

// Left starts moving the Thunder Launcher left.
func (tl *ThunderLauncher) Left() error {
	return tl.do(LEFT)
}

// Right starts moving the Thunder Launcher right.
func (tl *ThunderLauncher) Right() error {
	return tl.do(RIGHT)
}

// Fire starts the process of firing the Thunder Launcher.
// BUG(ryan): Need to add the appropriate timing so Fire will perform a "complete" fire
func (tl *ThunderLauncher) Fire() error {
	return tl.do(FIRE)
}

// Stop ceases the last command sent to the Thunder Launcher. Only LedOff and
// LedOn don't require Stop to be called after their invocation.
func (tl *ThunderLauncher) Stop() error {
	return tl.do(STOP)
}
<|endoftext|>


<commit_before>package spruce

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/cloudfoundry-community/vaultkv"
	"github.com/starkandwayne/goutils/ansi"

	. "github.com/geofffranks/spruce/log"
	"github.com/starkandwayne/goutils/tree"

	// Use geofffranks forks to persist the fix in https://github.com/go-yaml/yaml/pull/133/commits
	// Also https://github.com/go-yaml/yaml/pull/195
	"github.com/geofffranks/yaml"
)

var kv *vaultkv.KV = nil

var vaultSecretCache = map[string]map[string]interface{}{}

//VaultRefs maps secret path to paths in YAML structure which call for it
var VaultRefs = map[string][]string{}

//SkipVault toggles whether calls to the Vault operator actually cause the
// Vault to be contacted and the keys substituted in.
var SkipVault bool

// The VaultOperator provides a means of injecting credentials and
// other secrets from a Vault (vaultproject.io) Secure Key Storage
// instance.
type VaultOperator struct{}

// Setup ...
func (VaultOperator) Setup() error {
	return nil
}

// Phase identifies what phase of document management the vault
// operator should be evaluated in.  Vault lives in the Eval phase
func (VaultOperator) Phase() OperatorPhase {
	return EvalPhase
}

// Dependencies collects implicit dependencies that a given `(( vault ... ))`
// call has. There are no dependencies other that those given as args to the
// command.
func (VaultOperator) Dependencies(_ *Evaluator, _ []*Expr, _ []*tree.Cursor, auto []*tree.Cursor) []*tree.Cursor {
	return auto
}

func initializeVaultClient() error {
	addr := os.Getenv("VAULT_ADDR")
	token := os.Getenv("VAULT_TOKEN")
	skip := false

	if addr == "" || token == "" {
		svtoken := struct {
			Vault      string `yaml:"vault"`
			Token      string `yaml:"token"`
			SkipVerify bool   `yaml:"skip_verify"`
		}{}
		b, err := ioutil.ReadFile(os.ExpandEnv("${HOME}/.svtoken"))
		if err == nil {
			err = yaml.Unmarshal(b, &svtoken)
			if err == nil {
				addr = svtoken.Vault
				token = svtoken.Token
				skip = svtoken.SkipVerify
			}
		}
	}

	if skipVaultVerify(os.Getenv("VAULT_SKIP_VERIFY")) {
		skip = true
	}

	if token == "" {
		b, err := ioutil.ReadFile(fmt.Sprintf("%s/.vault-token", os.Getenv("HOME")))
		if err == nil {
			token = strings.TrimSuffix(string(b), "\n")
		}
	}

	if addr == "" || token == "" {
		return fmt.Errorf("Failed to determine Vault URL / token, and the $REDACT environment variable is not set.")
	}

	roots, err := x509.SystemCertPool()
	if err != nil {
		return fmt.Errorf("unable to retrieve system root certificate authorities: %s", err)
	}

	parsedURL, err := url.Parse(addr)
	if err != nil {
		return fmt.Errorf("Could not parse Vault URL `%s': %s", addr, err)
	}

	client := &vaultkv.Client{
		AuthToken: token,
		VaultURL:  parsedURL,
		Client: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				TLSClientConfig: &tls.Config{
					RootCAs:            roots,
					InsecureSkipVerify: skip,
				},
			},
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				if len(via) > 10 {
					return fmt.Errorf("stopped after 10 redirects")
				}
				req.Header.Add("X-Vault-Token", token)
				return nil
			},
		},
	}

	if err != nil {
		return fmt.Errorf("Error setting up Vault client: %s", err)
	}

	kv = client.NewKV()

	return nil
}

// Run executes the `(( vault ... ))` operator call, which entails
// interacting with the (unsealed) Vault instance to retrieve the
// given secrets.
func (VaultOperator) Run(ev *Evaluator, args []*Expr) (*Response, error) {
	DEBUG("running (( vault ... )) operation at $.%s", ev.Here)
	defer DEBUG("done with (( vault ... )) operation at $.%s\n", ev.Here)

	// syntax: (( vault "secret/path:key" ))
	// syntax: (( vault path.object "to concat with" other.object ))
	if len(args) < 1 {
		return nil, fmt.Errorf("vault operator requires at least one argument")
	}

	var l []string
	for i, arg := range args {
		v, err := arg.Resolve(ev.Tree)
		if err != nil {
			DEBUG("  arg[%d]: failed to resolve expression to a concrete value", i)
			DEBUG("     [%d]: error was: %s", i, err)
			return nil, err
		}

		switch v.Type {
		case Literal:
			DEBUG("  arg[%d]: using string literal '%v'", i, v.Literal)
			l = append(l, fmt.Sprintf("%v", v.Literal))

		case Reference:
			DEBUG("  arg[%d]: trying to resolve reference $.%s", i, v.Reference)
			s, err := v.Reference.Resolve(ev.Tree)
			if err != nil {
				DEBUG("     [%d]: resolution failed\n    error: %s", i, err)
				return nil, fmt.Errorf("Unable to resolve `%s`: %s", v.Reference, err)
			}

			switch s.(type) {
			case map[interface{}]interface{}:
				DEBUG("  arg[%d]: %v is not a string scalar", i, s)
				return nil, ansi.Errorf("@R{tried to look up} @c{$.%s}@R{, which is not a string scalar}", v.Reference)

			case []interface{}:
				DEBUG("  arg[%d]: %v is not a string scalar", i, s)
				return nil, ansi.Errorf("@R{tried to look up} @c{$.%s}@R{, which is not a string scalar}", v.Reference)

			default:
				l = append(l, fmt.Sprintf("%v", s))
			}

		default:
			DEBUG("  arg[%d]: I don't know what to do with '%v'", i, arg)
			return nil, fmt.Errorf("vault operator only accepts string literals and key reference arguments")
		}
	}
	key := strings.Join(l, "")
	DEBUG("     [0]: Using vault key '%s'\n", key)

	//Append the location from which this operator was called to the list of
	// places from which this key was referenced
	if refs, found := VaultRefs[key]; !found {
		VaultRefs[key] = []string{ev.Here.String()}
	} else {
		VaultRefs[key] = append(refs, ev.Here.String())
	}

	secret := "REDACTED"
	var err error

	if !SkipVault {
		/*
		   user is not okay with a redacted manifest.
		   try to look up vault connection details from:
		     1. Environment Variables VAULT_ADDR and VAULT_TOKEN
		     2. ~/.svtoken file, if it exists
		     3. ~/.vault-token file, if it exists
		*/

		if kv == nil {
			err := initializeVaultClient()
			if err != nil {
				return nil, fmt.Errorf("Error during Vault client initialization: %s", err)
			}
		}

		leftPart, rightPart := parsePath(key)
		if leftPart == "" || rightPart == "" {
			return nil, ansi.Errorf("@R{invalid argument} @c{%s}@R{; must be in the form} @m{path/to/secret:key}", key)
		}
		var fullSecret map[string]interface{}
		var found bool
		if fullSecret, found = vaultSecretCache[leftPart]; found {
			DEBUG("vault: Cache hit for `%s`", leftPart)
		} else {
			DEBUG("vault: Cache MISS for `%s`", leftPart)
			// Secret isn't cached. Grab it from the vault.
			fullSecret, err = getVaultSecret(leftPart)
			if err != nil {
				//Normalize the error messages
				switch err.(type) {
				case *vaultkv.ErrNotFound:
					err = fmt.Errorf("secret %s not found", key)
				}

				return nil, err
			}
			vaultSecretCache[leftPart] = fullSecret
		}

		secret, err = extractSubkey(fullSecret, leftPart, rightPart)
		if err != nil {
			return nil, err
		}
	}

	return &Response{
		Type:  Replace,
		Value: secret,
	}, nil
}

func init() {
	RegisterOp("vault", VaultOperator{})
}

/****** VAULT INTEGRATION ***********************************/

func getVaultSecret(secret string) (map[string]interface{}, error) {
	ret := map[string]interface{}{}

	DEBUG("Fetching Vault secret at `%s'", secret)
	_, err := kv.Get(secret, &ret, nil)
	if err != nil {
		DEBUG(" failure.")
		return nil, err
	}

	DEBUG("  success.")
	return ret, nil
}

func extractSubkey(secretMap map[string]interface{}, secret, subkey string) (string, error) {
	DEBUG("  extracting the [%s] subkey from the secret", subkey)

	secretSubkeyPath := fmt.Sprintf("%s:%s", secret, subkey)
	v, ok := secretMap[subkey]
	if !ok {
		DEBUG("    !! %s not found!\n", secretSubkeyPath)
		return "", ansi.Errorf("@R{secret} @c{%s} @R{not found}", secretSubkeyPath)
	}
	if _, ok := v.(string); !ok {
		DEBUG("    !! %s is not a string!\n", secretSubkeyPath)
		return "", ansi.Errorf("@R{secret} @c{%s} @R{is not a string}", secretSubkeyPath)
	}
	DEBUG(" success.")
	return v.(string), nil
}

func parsePath(path string) (secret, key string) {
	secret = path
	if idx := strings.LastIndex(path, ":"); idx >= 0 {
		secret = path[:idx]
		key = path[idx+1:]
	}
	return
}

func skipVaultVerify(env string) bool {
	env = strings.ToLower(env)
	if env == "" || env == "no" || env == "false" || env == "0" || env == "off" {
		return false
	}
	return true
}
<commit_msg>Make default Vault port 80/443<commit_after>package spruce

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/cloudfoundry-community/vaultkv"
	"github.com/starkandwayne/goutils/ansi"

	. "github.com/geofffranks/spruce/log"
	"github.com/starkandwayne/goutils/tree"

	// Use geofffranks forks to persist the fix in https://github.com/go-yaml/yaml/pull/133/commits
	// Also https://github.com/go-yaml/yaml/pull/195
	"github.com/geofffranks/yaml"
)

var kv *vaultkv.KV = nil

var vaultSecretCache = map[string]map[string]interface{}{}

//VaultRefs maps secret path to paths in YAML structure which call for it
var VaultRefs = map[string][]string{}

//SkipVault toggles whether calls to the Vault operator actually cause the
// Vault to be contacted and the keys substituted in.
var SkipVault bool

// The VaultOperator provides a means of injecting credentials and
// other secrets from a Vault (vaultproject.io) Secure Key Storage
// instance.
type VaultOperator struct{}

// Setup ...
func (VaultOperator) Setup() error {
	return nil
}

// Phase identifies what phase of document management the vault
// operator should be evaluated in.  Vault lives in the Eval phase
func (VaultOperator) Phase() OperatorPhase {
	return EvalPhase
}

// Dependencies collects implicit dependencies that a given `(( vault ... ))`
// call has. There are no dependencies other that those given as args to the
// command.
func (VaultOperator) Dependencies(_ *Evaluator, _ []*Expr, _ []*tree.Cursor, auto []*tree.Cursor) []*tree.Cursor {
	return auto
}

func initializeVaultClient() error {
	addr := os.Getenv("VAULT_ADDR")
	token := os.Getenv("VAULT_TOKEN")
	skip := false

	if addr == "" || token == "" {
		svtoken := struct {
			Vault      string `yaml:"vault"`
			Token      string `yaml:"token"`
			SkipVerify bool   `yaml:"skip_verify"`
		}{}
		b, err := ioutil.ReadFile(os.ExpandEnv("${HOME}/.svtoken"))
		if err == nil {
			err = yaml.Unmarshal(b, &svtoken)
			if err == nil {
				addr = svtoken.Vault
				token = svtoken.Token
				skip = svtoken.SkipVerify
			}
		}
	}

	if skipVaultVerify(os.Getenv("VAULT_SKIP_VERIFY")) {
		skip = true
	}

	if token == "" {
		b, err := ioutil.ReadFile(fmt.Sprintf("%s/.vault-token", os.Getenv("HOME")))
		if err == nil {
			token = strings.TrimSuffix(string(b), "\n")
		}
	}

	if addr == "" || token == "" {
		return fmt.Errorf("Failed to determine Vault URL / token, and the $REDACT environment variable is not set.")
	}

	roots, err := x509.SystemCertPool()
	if err != nil {
		return fmt.Errorf("unable to retrieve system root certificate authorities: %s", err)
	}

	parsedURL, err := url.Parse(addr)
	if err != nil {
		return fmt.Errorf("Could not parse Vault URL `%s': %s", addr, err)
	}

	if parsedURL.Port() == "" {
		if parsedURL.Scheme == "http" {
			parsedURL.Host = parsedURL.Host + ":80"
		} else {
			parsedURL.Host = parsedURL.Host + ":443"
		}
	}

	client := &vaultkv.Client{
		AuthToken: token,
		VaultURL:  parsedURL,
		Client: &http.Client{
			Transport: &http.Transport{
				Proxy: http.ProxyFromEnvironment,
				TLSClientConfig: &tls.Config{
					RootCAs:            roots,
					InsecureSkipVerify: skip,
				},
			},
			CheckRedirect: func(req *http.Request, via []*http.Request) error {
				if len(via) > 10 {
					return fmt.Errorf("stopped after 10 redirects")
				}
				req.Header.Add("X-Vault-Token", token)
				return nil
			},
		},
	}

	if err != nil {
		return fmt.Errorf("Error setting up Vault client: %s", err)
	}

	kv = client.NewKV()

	return nil
}

// Run executes the `(( vault ... ))` operator call, which entails
// interacting with the (unsealed) Vault instance to retrieve the
// given secrets.
func (VaultOperator) Run(ev *Evaluator, args []*Expr) (*Response, error) {
	DEBUG("running (( vault ... )) operation at $.%s", ev.Here)
	defer DEBUG("done with (( vault ... )) operation at $.%s\n", ev.Here)

	// syntax: (( vault "secret/path:key" ))
	// syntax: (( vault path.object "to concat with" other.object ))
	if len(args) < 1 {
		return nil, fmt.Errorf("vault operator requires at least one argument")
	}

	var l []string
	for i, arg := range args {
		v, err := arg.Resolve(ev.Tree)
		if err != nil {
			DEBUG("  arg[%d]: failed to resolve expression to a concrete value", i)
			DEBUG("     [%d]: error was: %s", i, err)
			return nil, err
		}

		switch v.Type {
		case Literal:
			DEBUG("  arg[%d]: using string literal '%v'", i, v.Literal)
			l = append(l, fmt.Sprintf("%v", v.Literal))

		case Reference:
			DEBUG("  arg[%d]: trying to resolve reference $.%s", i, v.Reference)
			s, err := v.Reference.Resolve(ev.Tree)
			if err != nil {
				DEBUG("     [%d]: resolution failed\n    error: %s", i, err)
				return nil, fmt.Errorf("Unable to resolve `%s`: %s", v.Reference, err)
			}

			switch s.(type) {
			case map[interface{}]interface{}:
				DEBUG("  arg[%d]: %v is not a string scalar", i, s)
				return nil, ansi.Errorf("@R{tried to look up} @c{$.%s}@R{, which is not a string scalar}", v.Reference)

			case []interface{}:
				DEBUG("  arg[%d]: %v is not a string scalar", i, s)
				return nil, ansi.Errorf("@R{tried to look up} @c{$.%s}@R{, which is not a string scalar}", v.Reference)

			default:
				l = append(l, fmt.Sprintf("%v", s))
			}

		default:
			DEBUG("  arg[%d]: I don't know what to do with '%v'", i, arg)
			return nil, fmt.Errorf("vault operator only accepts string literals and key reference arguments")
		}
	}
	key := strings.Join(l, "")
	DEBUG("     [0]: Using vault key '%s'\n", key)

	//Append the location from which this operator was called to the list of
	// places from which this key was referenced
	if refs, found := VaultRefs[key]; !found {
		VaultRefs[key] = []string{ev.Here.String()}
	} else {
		VaultRefs[key] = append(refs, ev.Here.String())
	}

	secret := "REDACTED"
	var err error

	if !SkipVault {
		/*
		   user is not okay with a redacted manifest.
		   try to look up vault connection details from:
		     1. Environment Variables VAULT_ADDR and VAULT_TOKEN
		     2. ~/.svtoken file, if it exists
		     3. ~/.vault-token file, if it exists
		*/

		if kv == nil {
			err := initializeVaultClient()
			if err != nil {
				return nil, fmt.Errorf("Error during Vault client initialization: %s", err)
			}
		}

		leftPart, rightPart := parsePath(key)
		if leftPart == "" || rightPart == "" {
			return nil, ansi.Errorf("@R{invalid argument} @c{%s}@R{; must be in the form} @m{path/to/secret:key}", key)
		}
		var fullSecret map[string]interface{}
		var found bool
		if fullSecret, found = vaultSecretCache[leftPart]; found {
			DEBUG("vault: Cache hit for `%s`", leftPart)
		} else {
			DEBUG("vault: Cache MISS for `%s`", leftPart)
			// Secret isn't cached. Grab it from the vault.
			fullSecret, err = getVaultSecret(leftPart)
			if err != nil {
				//Normalize the error messages
				switch err.(type) {
				case *vaultkv.ErrNotFound:
					err = fmt.Errorf("secret %s not found", key)
				}

				return nil, err
			}
			vaultSecretCache[leftPart] = fullSecret
		}

		secret, err = extractSubkey(fullSecret, leftPart, rightPart)
		if err != nil {
			return nil, err
		}
	}

	return &Response{
		Type:  Replace,
		Value: secret,
	}, nil
}

func init() {
	RegisterOp("vault", VaultOperator{})
}

/****** VAULT INTEGRATION ***********************************/

func getVaultSecret(secret string) (map[string]interface{}, error) {
	ret := map[string]interface{}{}

	DEBUG("Fetching Vault secret at `%s'", secret)
	_, err := kv.Get(secret, &ret, nil)
	if err != nil {
		DEBUG(" failure.")
		return nil, err
	}

	DEBUG("  success.")
	return ret, nil
}

func extractSubkey(secretMap map[string]interface{}, secret, subkey string) (string, error) {
	DEBUG("  extracting the [%s] subkey from the secret", subkey)

	secretSubkeyPath := fmt.Sprintf("%s:%s", secret, subkey)
	v, ok := secretMap[subkey]
	if !ok {
		DEBUG("    !! %s not found!\n", secretSubkeyPath)
		return "", ansi.Errorf("@R{secret} @c{%s} @R{not found}", secretSubkeyPath)
	}
	if _, ok := v.(string); !ok {
		DEBUG("    !! %s is not a string!\n", secretSubkeyPath)
		return "", ansi.Errorf("@R{secret} @c{%s} @R{is not a string}", secretSubkeyPath)
	}
	DEBUG(" success.")
	return v.(string), nil
}

func parsePath(path string) (secret, key string) {
	secret = path
	if idx := strings.LastIndex(path, ":"); idx >= 0 {
		secret = path[:idx]
		key = path[idx+1:]
	}
	return
}

func skipVaultVerify(env string) bool {
	env = strings.ToLower(env)
	if env == "" || env == "no" || env == "false" || env == "0" || env == "off" {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>package ops

import (
	"bsearch/index"
	"sort"
)

type attr []index.IbDoc

// QueryOp that is the set of all documents for one attribute.
func NewAttr(in *index.Index, key string) QueryOp {
	a := attr(in.Attrs[key])
	return &a
}

func (ba attr) CurrentDoc() *index.IbDoc {
	if ba == nil {
		return nil
	}
	return &ba[0]
}

func (ba *attr) NextDoc(search *index.IbDoc) *index.IbDoc {
	l := len(*ba)
	i := sort.Search(l, func(i int) bool {
		d := (*ba)[i]
		if search.Order > d.Order {
			return true
		} else if search.Order == d.Order {
			return search.Id >= d.Id
		}
		return false
	})
	if i == l {
		ba = nil
		return nil
	}
	(*ba) = (*ba)[i:]
	return &(*ba)[0]
}

func (ba attr) ProcessHeaders(hc HeaderCollector) {
}
<commit_msg>Inline expand sort.Search for a 30% performance increase.<commit_after>package ops

import (
	"bsearch/index"
)

type attr []index.IbDoc

// QueryOp that is the set of all documents for one attribute.
func NewAttr(in *index.Index, key string) QueryOp {
	a := attr(in.Attrs[key])
	return &a
}

func (ba attr) CurrentDoc() *index.IbDoc {
	if ba == nil {
		return nil
	}
	return &ba[0]
}

func (ba *attr) NextDoc(search *index.IbDoc) *index.IbDoc {
	l := len(*ba)

/*
	i := sort.Search(l, func(i int) bool {
		d := (*ba)[i]
		return search.Order > d.Order || (search.Order == d.Order && search.Id >= d.Id)
	})
 The code below is an expanded version of this call. Inlining gives us a 30% speedup.
*/
	i, j := 0, l
	for i < j {
		h := i + (j - i)/2
		d := (*ba)[h]
		if (search.Order > d.Order || (search.Order == d.Order && search.Id >= d.Id)) {
			j = h
		} else {
			i = h + 1
		}
	}
	/* End of inline expanded sort.Search */

	if i == l {
		ba = nil
		return nil
	}
	(*ba) = (*ba)[i:]
	return &(*ba)[0]
}

func (ba attr) ProcessHeaders(hc HeaderCollector) {
}
<|endoftext|>


<commit_before>package net

import (
	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/repo"
	"github.com/ipfs/go-ipfs/core"
	"golang.org/x/net/context"
	"time"
)

type PointerRepublisher struct {
	ipfsNode *core.IpfsNode
	db       repo.Datastore
}

func NewPointerRepublisher(node *core.IpfsNode, database repo.Datastore) *PointerRepublisher {
	return &PointerRepublisher{
		ipfsNode: node,
		db:       database,
	}
}

func (r *PointerRepublisher) Run() {
	tick := time.NewTicker(time.Hour * 24)
	defer tick.Stop()
	go r.republish()
	for range tick.C {
		go r.republish()
	}
}

func (r *PointerRepublisher) republish() {
	pointers, err := r.db.Pointers().GetAll()
	if err != nil {
		return
	}
	ctx := context.Background()
	for _, p := range pointers {
		if p.Purpose != ipfs.MESSAGE {
			ipfs.RePublishPointer(r.ipfsNode, ctx, p)
		} else {
			if time.Now().Sub(p.Timestamp) > time.Hour*24*30 {
				r.db.Pointers().Delete(p.Value.ID)
			} else {
				ipfs.RePublishPointer(r.ipfsNode, ctx, p)
			}
		}
	}
}
<commit_msg>CLEANUP: Fix import order to gofmt format.<commit_after>package net

import (
	"time"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/repo"
	"github.com/ipfs/go-ipfs/core"
	"golang.org/x/net/context"
)

type PointerRepublisher struct {
	ipfsNode *core.IpfsNode
	db       repo.Datastore
}

func NewPointerRepublisher(node *core.IpfsNode, database repo.Datastore) *PointerRepublisher {
	return &PointerRepublisher{
		ipfsNode: node,
		db:       database,
	}
}

func (r *PointerRepublisher) Run() {
	tick := time.NewTicker(time.Hour * 24)
	defer tick.Stop()
	go r.republish()
	for range tick.C {
		go r.republish()
	}
}

func (r *PointerRepublisher) republish() {
	pointers, err := r.db.Pointers().GetAll()
	if err != nil {
		return
	}
	ctx := context.Background()
	for _, p := range pointers {
		if p.Purpose != ipfs.MESSAGE {
			ipfs.RePublishPointer(r.ipfsNode, ctx, p)
		} else {
			if time.Now().Sub(p.Timestamp) > time.Hour*24*30 {
				r.db.Pointers().Delete(p.Value.ID)
			} else {
				ipfs.RePublishPointer(r.ipfsNode, ctx, p)
			}
		}
	}
}
<|endoftext|>


<commit_before>// From Go GUI with Fyne, Chap 4.  I believe it will be enhanced in later chapters, but this is what is it for now.
/*
REVISION HISTORY
-------- -------
 9 Aug 21 -- I realized that this will not be enhanced, as I went thru more of the book.  I'll have to enhance it myself.
             First, I'm changing the function constants to the version that's more readable to me.  That's working, but I had to
             import more parts of fyne.io than the unmodified version.
12 Aug 21 -- Now called img.go, so I can display 1 image.  I'll start here.
13 Aug 21 -- Now called imgfyne.go.  Same purpose as img.go, but so I can test non-fyne code there and fyne code here.
*/

package main

import (
	"flag"
	"fmt"
	"fyne.io/fyne/v2/layout"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	//_ "golang.org/x/image"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/storage"

	"github.com/nfnt/resize"
)

const LastModified = "August 14, 2021"
const maxWidth = 3000
const maxHeight = 2000

type bgImageLoad struct {
	uri fyne.URI
	img *canvas.Image
}

var loads = make(chan bgImageLoad, 1024)

var imgFileInfoChan = make(chan []os.FileInfo)  // unbuffered channel

func scaleImage(img image.Image) image.Image {
	return resize.Thumbnail(320, 240, img, resize.Lanczos3)
}

func doLoadImage(u fyne.URI, img *canvas.Image) {
	//read, err := storage.OpenFileFromURI(u)  I'm getting a message from goland that this function is depracated.
	read, err := storage.Reader(u)
	if err != nil {
		log.Println("Error opening image", err)
		return
	}

	defer read.Close()
	raw, _, err := image.Decode(read)
	if err != nil {
		log.Println("Error decoding image", err)
		return
	}

	img.Image = scaleImage(raw)
	img.Refresh()
}

func doLoadImages() {
	for load := range loads {
		doLoadImage(load.uri, load.img)
	}
}

func loadImage(u fyne.URI) fyne.CanvasObject {
	img := canvas.NewImageFromResource(nil)
	img.FillMode = canvas.ImageFillContain

	loads <- bgImageLoad{u, img} // typed constant
	return img
}

type itemLayout struct {
	bg, text, gradient fyne.CanvasObject
}

type imgLayout = itemLayout

func (i *itemLayout) MinSize([]fyne.CanvasObject) fyne.Size { // I removed an underscore from this line that I thought was a mistake.
	return fyne.NewSize(160, 120)
}

func (i *itemLayout) Layout(objs []fyne.CanvasObject, size fyne.Size) {
	textHeight := float32(22)
	for _, o := range objs {
		if o == i.text {
			o.Move(fyne.NewPos(0, size.Height-textHeight))
			o.Resize(fyne.NewSize(size.Width, textHeight))
		} else if o == i.bg {
			o.Move(fyne.NewPos(0, size.Height-textHeight))
			o.Resize(fyne.NewSize(size.Width, textHeight))
		} else if o == i.gradient {
			o.Move(fyne.NewPos(0, size.Height-(textHeight*1.5)))
			o.Resize(fyne.NewSize(size.Width, textHeight/2))
		} else {
			o.Move(fyne.NewPos(0, 0))
			o.Resize(size)
		}
	}
}

func makeImageItem(u fyne.URI) fyne.CanvasObject {
	label := canvas.NewText(u.Name(), color.Gray{128})
	label.Alignment = fyne.TextAlignCenter

	bgColor := &color.NRGBA{R: 255, G: 255, B: 255, A: 224}
	bg := canvas.NewRectangle(bgColor)
	fade := canvas.NewLinearGradient(color.Transparent, bgColor, 0)
	return container.New(&itemLayout{text: label, bg: bg, gradient: fade},
		loadImage(u), bg, fade, label)
}

func isImageURI(file fyne.URI) bool {
	ext := strings.ToLower(file.Extension())

	return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".webp"
}

func filterImages(files []fyne.URI) []fyne.URI {
	images := []fyne.URI{}

	for _, file := range files {
		if isImageURI(file) {
			images = append(images, file)
		}
	}

	return images
}

func makeImageGrid(images []fyne.URI) fyne.CanvasObject {
	items := []fyne.CanvasObject{}

	for _, item := range images {
		img := makeImageItem(item)
		items = append(items, img)
	}

	cellSize := fyne.NewSize(160, 120)
	return container.NewGridWrap(cellSize, items...)
}

func makeStatus(dir fyne.ListableURI, images []fyne.URI) fyne.CanvasObject {
	status := fmt.Sprintf("Directory %s, %d items", dir.Name(), len(images))
	return canvas.NewText(status, color.Gray{128})
}

func makeUI(dir fyne.ListableURI) fyne.CanvasObject {
	list, err := dir.List()
	if err != nil {
		log.Println("Error listing directory", err)
	}
	images := filterImages(list)
	status := makeStatus(dir, images)
	content := container.NewScroll(makeImageGrid(images))
	return container.NewBorder(nil, status, nil, nil, status, content)
}

func chooseDirectory(w fyne.Window) {
	listableURIfunc := func(dir fyne.ListableURI, err error) {
		if err != nil {
			dialog.ShowError(err, w)
			return
		}
		w.SetContent(makeUI(dir))
	}
	dialog.ShowFolderOpen(listableURIfunc, w)
}

/*
	dialog.ShowFolderOpen(func(dir fyne.ListableURI, err error) {
		if err != nil {
			dialog.ShowError(err, w)
			return
		}
		w.SetContent(makeUI(dir))
	}, w)
*/

func startDirectory() fyne.ListableURI {
	flag.Parse()
	if len(flag.Args()) < 1 {
		cwd, _ := os.Getwd()
		list, _ := storage.ListerForURI(storage.NewFileURI(cwd))
		return list
	}

	dir, err := filepath.Abs(flag.Arg(0))
	if err != nil {
		log.Println("Could not find directory", dir)
		cwd, _ := os.Getwd()
		list, _ := storage.ListerForURI(storage.NewFileURI(cwd))
		return list
	}

	list, _ := storage.ListerForURI(storage.NewFileURI(dir))
	return list
}

func isNotImageStr(name string) bool {
	ext := filepath.Ext(name)
	isImage := ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".webp"
	return !isImage
}

func main() {
	flag.Parse()
	if flag.NArg() < 1 {
		fmt.Fprintln(os.Stderr, " Usage: img <image file name>")
		os.Exit(1)
	}

	imgfilename := flag.Arg(0)
	_, err := os.Stat(imgfilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from os.Stat(", imgfilename, ") is", err)
		os.Exit(1)
	}

	if isNotImageStr(imgfilename) {
		fmt.Fprintln(os.Stderr, imgfilename, "does not have an image extension.")
		os.Exit(1)
	}

	fullFilename, err := filepath.Abs(imgfilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from filepath.Abs on", imgfilename, "is", err)
		os.Exit(1)
	}

	imgFileHandle, err := os.Open(fullFilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from opening", fullFilename, "is", err)
		os.Exit(1)
	}

	imgConfig, _, err := image.DecodeConfig(imgFileHandle) // img is of type image.Config
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from decode config on", fullFilename, "is", err)
		os.Exit(1)
	}
	imgFileHandle.Close()

	width := imgConfig.Width
	height := imgConfig.Height

	if width > maxWidth {
		width = maxWidth
	}
	if height > maxHeight {
		height = maxHeight
	}

	fmt.Println()
	fmt.Printf(" Type for DecodeConfig is %T \n", imgConfig) // answer is image.Config
	fmt.Println(" Image", imgfilename, fullFilename, "width =", width, " and height =", height)
	fmt.Println()

	a := app.New()
	str := fmt.Sprintf("Single Image Viewer last modified %s, compiled using %s", LastModified, runtime.Version())
	fmt.Println(str) // this works as intended
	w := a.NewWindow(str)
	w.Resize(fyne.NewSize(maxWidth, maxHeight))
	w.Show()

	cwd, _ := os.Getwd()
	imageURI := storage.NewFileURI(fullFilename) // needs to be a type = fyne.CanvasObject
	imgRead, err := storage.Reader(imageURI)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from storage.Reader of", fullFilename, "is", err)
		os.Exit(1)
	}
	defer imgRead.Close()
	img, imgFmtName, err := image.Decode(imgRead)  // imgFmtName is a string of the format name used during format registration by the init function.
	bounds := img.Bounds()
	imgHeight := bounds.Max.Y
	imgWidth := bounds.Max.X
	fmt.Println(" Using image.Decode, width=", imgWidth, "and height=", imgHeight, "and imgFmtName=", imgFmtName)
	fmt.Println()
	if imgWidth > maxWidth || imgHeight > maxHeight {
		img = resize.Resize(0, maxWidth, img, resize.Lanczos3)
	}

	label := canvas.NewText(imgfilename, color.Gray{128})  // MakeImageItem in orig viewerf1.go
	label.Alignment = fyne.TextAlignCenter                    // MakeImageItem in orig viewerf1.go
	//bgColor := &color.NRGBA{R: 255, G: 255, B: 255, A: 224}  // MakeImageItem in orig viewerf1.go
	//bg := canvas.NewRectangle(bgColor)  // MakeImageItem in orig viewerf1.go
	//fade := canvas.NewLinearGradient(color.Transparent, bgColor, 0)  // MakeImageItem in orig viewerf1.go
	//imagelayoutliteral := imgLayout{text: label, bg: bg, gradient: fade}  // MakeImageItem in orig viewerf1.go, but this syntax not allowed here.

	loadedimg := canvas.NewImageFromImage(img) // from web, not in original viewerf1.go
	loadedimg.FillMode = canvas.ImageFillContain      // loadimage in orig viewerf1.go

	//imgcontainer := container.New(&imgLayout{text: label, bg: bg, gradient: fade}, loadedimg, bg, fade, label) //MakeImageItem in viewerf1.go
	imgContainer := container.New(layout.NewVBoxLayout(), label, loadedimg)

	//picContainer := container.New(layout.NewVBoxLayout(), label, imgRect)  from my notes of the book.  Not needed now.
	//imgRect := canvas.NewRectangle(color.Black)
	//imgPic := canvas.NewImageFromResource(imageURI)  // code above has nil here
	//imgPic.FillMode = canvas.ImageFillContain
	//imgAndTitle := container.NewBorder(nil, label, nil, nil, imgRect) // top, left and right are all nil here.  bottom=label, center=imgRect

	w.SetContent(imgContainer)
	imgtitle := fmt.Sprintf("%s, %d x %d", imgfilename, imgWidth, imgHeight)
	w.SetTitle(imgtitle)
	w.SetContent(loadedimg)

	//	chooseDirFunc := func() {
	//		chooseDirectory(w)
	//	}
	//	w.SetMainMenu(fyne.NewMainMenu(fyne.NewMenu("File", fyne.NewMenuItem("Open Directory...", chooseDirFunc))))

	//	go doLoadImages()
	go MyReadDirForImages(cwd, imgFileInfoChan)
	w.ShowAndRun()

	imageInfo := make([]os.FileInfo,0, 1024)

	select { // this syntax works and is blocking.
	case imageInfo = <- imgFileInfoChan :  // this ackward syntax is what's needed to read from a channel.
	}

	fmt.Println(" Have the slice of image file infos.  Len =", len(imageInfo))
	fmt.Println()

} // end main

// ----------------------------------isImage // ----------------------------------------------
func isImage(file string) bool {
	ext := strings.ToLower(filepath.Ext(file))
	ext = strings.ToLower(ext)

	return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif"
}

// ------------------------------- MyReadDirForImages -----------------------------------

func MyReadDirForImages(dir string, imageInfoChan chan []os.FileInfo) {

	dirname, err := os.Open(dir)
	if err != nil {
		return
	}
	defer dirname.Close()

	names, err := dirname.Readdirnames(0) // zero means read all names into the returned []string
	if err != nil {
		return
	}

	fi := make([]os.FileInfo, 0, len(names))
	for _, name := range names {
            if isImage(name) {
		imgInfo, err := os.Lstat(name)
		if err != nil {
			fmt.Fprintln(os.Stderr, " Error from os.Lstat ", err)
			continue
		}
		fi = append(fi, imgInfo)
            }
	}

	imageInfoChan <- fi
	return
} // MyReadDirForImages

<commit_msg>08/15/2021 11:56:58 AM imgfyne/imgfyne.go -- so far, so good.<commit_after>// From Go GUI with Fyne, Chap 4.  I believe it will be enhanced in later chapters, but this is what is it for now.
/*
REVISION HISTORY
-------- -------
 9 Aug 21 -- I realized that this will not be enhanced, as I went thru more of the book.  I'll have to enhance it myself.
             First, I'm changing the function constants to the version that's more readable to me.  That's working, but I had to
             import more parts of fyne.io than the unmodified version.
12 Aug 21 -- Now called img.go, so I can display 1 image.  I'll start here.
13 Aug 21 -- Now called imgfyne.go.  Same purpose as img.go, but so I can test non-fyne code there and fyne code here.
*/

package main

import (
	"flag"
	"fmt"
	"fyne.io/fyne/v2/layout"
	"image"
	"image/color"
	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"strings"
	"time"

	//_ "golang.org/x/image"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/canvas"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/storage"

	"github.com/nfnt/resize"
)

const LastModified = "August 15, 2021"
const maxWidth = 2500
const maxHeight = 2000

func isNotImageStr(name string) bool {
	ext := filepath.Ext(name)
	isImage := ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".webp"
	return !isImage
}

func main() {
	flag.Parse()
	if flag.NArg() < 1 {
		fmt.Fprintln(os.Stderr, " Usage: img <image file name>")
		os.Exit(1)
	}

	str := fmt.Sprintf("Single Image Viewer last modified %s, compiled using %s", LastModified, runtime.Version())
	fmt.Println(str) // this works as intended

	imgfilename := flag.Arg(0)
	_, err := os.Stat(imgfilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from os.Stat(", imgfilename, ") is", err)
		os.Exit(1)
	}

	if isNotImageStr(imgfilename) {
		fmt.Fprintln(os.Stderr, imgfilename, "does not have an image extension.")
		os.Exit(1)
	}

	basefilename := filepath.Base(imgfilename)
	fullFilename, err := filepath.Abs(imgfilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from filepath.Abs on", imgfilename, "is", err)
		os.Exit(1)
	}

	imgFileHandle, err := os.Open(fullFilename)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from opening", fullFilename, "is", err)
		os.Exit(1)
	}

	imgConfig, _, err := image.DecodeConfig(imgFileHandle) // img is of type image.Config
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from decode config on", fullFilename, "is", err)
		os.Exit(1)
	}
	imgFileHandle.Close()

	var width = float32(imgConfig.Width)
	var height = float32(imgConfig.Height)
	var aspectRatio = width/height
	if aspectRatio > 1 {
		aspectRatio = 1/aspectRatio
	}

	fmt.Println(" image.Config", imgfilename, fullFilename, basefilename, "width =", width, ", height =", height, "and aspect ratio =", aspectRatio)

	if width > maxWidth || height > maxHeight {
		width = maxWidth * aspectRatio
		height = maxHeight * aspectRatio
	}

	fmt.Println()
	//fmt.Printf(" Type for DecodeConfig is %T \n", imgConfig) // answer is image.Config
	fmt.Println(" adjusted image.Config width =", width, ", height =", height, " but these values are not used to show the image.")
	fmt.Println()

	a := app.New()
	w := a.NewWindow(str)

	//cwd, _ := os.Getwd()
	cwd := filepath.Dir(fullFilename)
	imageURI := storage.NewFileURI(fullFilename) // needs to be a type = fyne.CanvasObject
	imgRead, err := storage.Reader(imageURI)
	if err != nil {
		fmt.Fprintln(os.Stderr, " Error from storage.Reader of", fullFilename, "is", err)
		os.Exit(1)
	}
	defer imgRead.Close()
	img, imgFmtName, err := image.Decode(imgRead)  // imgFmtName is a string of the format name used during format registration by the init function.
	bounds := img.Bounds()
	imgHeight := bounds.Max.Y
	imgWidth := bounds.Max.X
	fmt.Println(" image.Decode, width=", imgWidth, "and height=", imgHeight, ", imgFmtName=", imgFmtName, "and cwd=", cwd)
	fmt.Println()
	if imgWidth > maxWidth {
		img = resize.Resize(maxWidth, 0, img, resize.Lanczos3)
	} else if imgHeight > maxHeight {
		img = resize.Resize(0, maxHeight, img, resize.Lanczos3)
	}

	label := canvas.NewText(imgfilename, color.Gray{128})  // MakeImageItem in orig viewerf1.go
	label.Alignment = fyne.TextAlignCenter                    // MakeImageItem in orig viewerf1.go
	//bgColor := &color.NRGBA{R: 255, G: 255, B: 255, A: 224}  // MakeImageItem in orig viewerf1.go
	//bg := canvas.NewRectangle(bgColor)  // MakeImageItem in orig viewerf1.go
	//fade := canvas.NewLinearGradient(color.Transparent, bgColor, 0)  // MakeImageItem in orig viewerf1.go
	//imagelayoutliteral := imgLayout{text: label, bg: bg, gradient: fade}  // MakeImageItem in orig viewerf1.go, but this syntax not allowed here.

	loadedimg := canvas.NewImageFromImage(img) // from web, not in original viewerf1.go
	loadedimg.FillMode = canvas.ImageFillContain      // loadimage in orig viewerf1.go

	//imgcontainer := container.New(&imgLayout{text: label, bg: bg, gradient: fade}, loadedimg, bg, fade, label) //MakeImageItem in viewerf1.go
	imgContainer := container.New(layout.NewVBoxLayout(), label, loadedimg)

	//picContainer := container.New(layout.NewVBoxLayout(), label, imgRect)  from my notes of the book.  Not needed now.
	//imgRect := canvas.NewRectangle(color.Black)
	//imgPic := canvas.NewImageFromResource(imageURI)  // code above has nil here
	//imgPic.FillMode = canvas.ImageFillContain
	//imgAndTitle := container.NewBorder(nil, label, nil, nil, imgRect) // top, left and right are all nil here.  bottom=label, center=imgRect

	imgFileInfoChan := make(chan []os.FileInfo)  // unbuffered channel
	go MyReadDirForImages(cwd, imgFileInfoChan)
	w.SetContent(imgContainer)
	imgtitle := fmt.Sprintf("%s, %d x %d", imgfilename, imgWidth, imgHeight)
	w.SetTitle(imgtitle)
	w.SetContent(loadedimg)
	w.Resize(fyne.NewSize(float32(imgWidth), float32(imgHeight)))

	w.ShowAndRun()

	var imageInfo []os.FileInfo

	select { // this syntax works and is blocking.
	case imageInfo = <- imgFileInfoChan :  // this ackward syntax is what's needed to read from a channel.
	}

	t0:= time.Now()
	sortfcn := func(i, j int) bool { // this is a closure anonymous function
		return imageInfo[i].ModTime().After(imageInfo[j].ModTime()) // I want a newest-first sort.  Changed 12/20/20
	}
	sort.Slice(imageInfo, sortfcn)
	elapsedtime := time.Since(t0)

	fmt.Println(" Have the slice of image file infos.  Len =", len(imageInfo), "and sorted in", elapsedtime.String())
	fmt.Println()

	t0 = time.Now()
	//index := filenameIndex(imageInfo, basefilename)
	indexchan := make(chan int)
	index := 0
	go filenameIndex(imageInfo, basefilename, indexchan)
	select {
	case index = <- indexchan:
	}
	elapsedtime = time.Since(t0)

	fmt.Println(" Index of", basefilename, "in imageInfo slice=", index, "taking", elapsedtime, "to find in a linear sequential search.")
	fmt.Println(" as a check, imageInfo[", index, "] =", imageInfo[index].Name(), ".")
	fmt.Println()

} // end main

// -------------------------------          filenameIndex --------------------------------------
func filenameIndex(fileinfos []os.FileInfo, name string, intchan chan int) {
	for i, fi := range fileinfos {
		if fi.Name() == name {
			intchan <- i
			return
		}
	}
	intchan <- -1
	return
}

// ----------------------------------isImage ----------------------------------------------
func isImage(file string) bool {
	ext := strings.ToLower(filepath.Ext(file))
	ext = strings.ToLower(ext)

	return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif"
}

// ------------------------------- MyReadDirForImages -----------------------------------

func MyReadDirForImages(dir string, imageInfoChan chan []os.FileInfo) {

	dirname, err := os.Open(dir)
	if err != nil {
		return
	}
	defer dirname.Close()

	names, err := dirname.Readdirnames(0) // zero means read all names into the returned []string
	if err != nil {
		return
	}

	fi := make([]os.FileInfo, 0, len(names))
	for _, name := range names {
            if isImage(name) {
		imgInfo, err := os.Lstat(name)
		if err != nil {
			fmt.Fprintln(os.Stderr, " Error from os.Lstat ", err)
			continue
		}
		fi = append(fi, imgInfo)
            }
	}

	imageInfoChan <- fi
	return
} // MyReadDirForImages

<|endoftext|>


<commit_before>package card_holder

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/sorribas/shamir3pass"
	"github.com/whereswaldon/cryptage/card"
	"math/big"
)

// Card represents a single card with methods to
// find the face of the card and each permutation
// of its encrypted state.
type Card interface {
	Face() (card.CardFace, error)
	Mine() (*big.Int, error)
	Theirs() (*big.Int, error)
	Both() (*big.Int, error)
	SetMine(*big.Int) error
	SetTheirKey(*shamir3pass.Key) error
	HasTheirKey() bool
	CanDecrypt() bool
	HasEncrypted() bool
	Validate() error
}

// NewHolder creates a CardHolder from a key and a slice of card faces
func NewHolder(key *shamir3pass.Key, faces []card.CardFace) (*CardHolder, error) {
	if key == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil key")
	} else if faces == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil card faces")
	} else if len(faces) < 1 {
		return nil, fmt.Errorf("Cannot construct CardHolder with empty card faces")
	}
	cards := make([]Card, len(faces))
	var err error
	for i, c := range faces {
		cards[i], err = card.NewCard(c, key)
		if err != nil {
			return nil, errors.Wrap(err, "Error creating card")
		}
	}
	return &CardHolder{cards: cards, key: key}, nil
}

// HolderFromEncrypted creates a CardHolder from a kay and a slice of card
// faces that have already been encrypted.
func HolderFromEncrypted(key *shamir3pass.Key, theirEncrypted []*big.Int) (*CardHolder, error) {
	if key == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil key")
	} else if theirEncrypted == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil encrypted faces")
	} else if len(theirEncrypted) < 1 {
		return nil, fmt.Errorf("Cannot construct CardHolder with empty encrypted faces")
	}
	cards := make([]Card, len(theirEncrypted))
	var err error
	for i, c := range theirEncrypted {
		cards[i], err = card.CardFromTheirs(c, key)
		if err != nil {
			return nil, errors.Wrap(err, "Error creating card")
		}
	}
	return &CardHolder{cards: cards, key: key}, nil
}

type CardHolder struct {
	cards []Card
	key   *shamir3pass.Key
}

func (h *CardHolder) Size() uint {
	return uint(len(h.cards))
}

// Get returns the card face at the given position within the deck,
// if possible
func (h *CardHolder) Get(index uint) (card.CardFace, error) {
	if can, err := h.CanGet(index); !can {
		if err != nil {
			return nil, errors.Wrapf(err, "Unable to get card")
		} else {
			return nil, fmt.Errorf("Unable to get card %d", index)
		}
	}
	return h.cards[index].Face()
}

// CanGet determines whether it is currently possible to get the card
// face at the given position.
func (h *CardHolder) CanGet(index uint) (bool, error) {
	if index < uint(len(h.cards)) {
		return h.cards[index].CanDecrypt(), nil
	}
	return false, fmt.Errorf("Index %d out of bounds (%d cards)", index, len(h.cards))
}

// GetTheirs returns the card at the given position, but only encrypted with
// the opponent's key.
func (h *CardHolder) GetTheirs(index uint) (*big.Int, error) {
	if can, err := h.CanGetTheirs(index); !can {
		if err != nil {
			return nil, errors.Wrapf(err, "Unable to get card")
		} else {
			return nil, fmt.Errorf("Unable to get card %d", index)
		}
	}
	return h.cards[index].Theirs()
}

// CanGetTheirs determines whether it is currently possible to get the card
// face at the given position.
func (h *CardHolder) CanGetTheirs(index uint) (bool, error) {
	if index < uint(len(h.cards)) {
		return h.cards[index].HasEncrypted(), nil
	}
	return false, fmt.Errorf("Index %d out of bounds (%d cards)", index, len(h.cards))
}

// SetMine informs the card at the given index that its encrypted state
// with only the local player's key is the given big.Int. Knowing this
// value allows a card to be decrypted.
func (h *CardHolder) SetMine(index uint, mine *big.Int) error {
	if index < uint(len(h.cards)) {
		return h.cards[index].SetMine(mine)
	}
	return fmt.Errorf("Index %d out of bounds (max %d)", index, len(h.cards))
}

func (h *CardHolder) SetTheirKey(key *shamir3pass.Key) error {
	if key == nil {
		return fmt.Errorf("Key cannot be nil")
	}
	for i := range h.cards {
		if err := h.cards[i].SetTheirKey(key); err != nil {
			return errors.Wrapf(err, "Unable to set key on index %d", i)
		}
	}
	return nil
}

// SetBothEncrypted erases the current cards within the deck and creates
// a new deck of cards with the given values as the values that have been
// encrypted by both players. This does not erase the encryption key stored
// within the collection (which is important, since this is needed to decrypt
// cards later)
func (h *CardHolder) SetBothEncrypted(encryptedFaces []*big.Int) error {
	if encryptedFaces == nil {
		return fmt.Errorf("Cannot set both encrypted to nil slice")
	} else if len(encryptedFaces) != len(h.cards) {
		return fmt.Errorf("Cannot set both encrypted to slice of length %d"+
			" when cardCardHolder has %d cards", len(encryptedFaces),
			len(h.cards))
	}
	var err error
	newCards := make([]Card, len(h.cards))
	for i, c := range encryptedFaces {
		newCards[i], err = card.CardFromBoth(c, h.key)
		if err != nil {
			return errors.Wrap(err, "Unable to create card from both encrypted face")
		}
	}
	h.cards = newCards
	return nil
}

// GetAllMine returns all known mine values for the cards. If all cards had known
// values, the second return value will be true.
func (h *CardHolder) GetAllMine() ([]*big.Int, bool, error) {
	cards := make([]*big.Int, len(h.cards))
	allDecryptable := true
	var err error
	for i, c := range h.cards {
		if c.CanDecrypt() {
			cards[i], err = c.Mine()
			if err != nil {
				return cards, false, err
			}
		} else {
			allDecryptable = false
		}
	}
	return cards, allDecryptable, nil
}

// GetAllBoth returns all known doubly-encrypted values for the cards. If all cards had known
// values, the second return value will be true.
func (h *CardHolder) GetAllBoth() ([]*big.Int, bool, error) {
	cards := make([]*big.Int, len(h.cards))
	allEncryptable := true
	var err error
	for i, c := range h.cards {
		if c.HasEncrypted() {
			cards[i], err = c.Both()
			if err != nil {
				return cards, false, err
			}
		} else {
			allEncryptable = false
		}
	}
	return cards, allEncryptable, nil
}

// ValidateAll checks that all cards are internally consistent, if it is possible
func (h *CardHolder) ValidateAll() error {
	for i, c := range h.cards {
		if err := c.Validate(); err != nil {
			return errors.Wrapf(err, "Failed to validate all cards at index %d", i)
		}
	}
	return nil
}
<commit_msg>Shuffle the cards<commit_after>package card_holder

import (
	"fmt"
	"github.com/pkg/errors"
	"github.com/sorribas/shamir3pass"
	"github.com/whereswaldon/cryptage/card"
	"math/big"
	"math/rand"
)

// Card represents a single card with methods to
// find the face of the card and each permutation
// of its encrypted state.
type Card interface {
	Face() (card.CardFace, error)
	Mine() (*big.Int, error)
	Theirs() (*big.Int, error)
	Both() (*big.Int, error)
	SetMine(*big.Int) error
	SetTheirKey(*shamir3pass.Key) error
	HasTheirKey() bool
	CanDecrypt() bool
	HasEncrypted() bool
	Validate() error
}

// NewHolder creates a CardHolder from a key and a slice of card faces
func NewHolder(key *shamir3pass.Key, faces []card.CardFace) (*CardHolder, error) {
	if key == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil key")
	} else if faces == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil card faces")
	} else if len(faces) < 1 {
		return nil, fmt.Errorf("Cannot construct CardHolder with empty card faces")
	}
	cards := make([]Card, len(faces))
	shuffle := rand.Perm(len(faces))
	for i, v := range shuffle {
		c, err := card.NewCard(faces[i], key)
		if err != nil {
			return nil, errors.Wrap(err, "Error creating card")
		}
		cards[v] = c
	}

	return &CardHolder{cards: cards, key: key}, nil
}

// HolderFromEncrypted creates a CardHolder from a kay and a slice of card
// faces that have already been encrypted.
func HolderFromEncrypted(key *shamir3pass.Key, theirEncrypted []*big.Int) (*CardHolder, error) {
	if key == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil key")
	} else if theirEncrypted == nil {
		return nil, fmt.Errorf("Cannot construct CardHolder with nil encrypted faces")
	} else if len(theirEncrypted) < 1 {
		return nil, fmt.Errorf("Cannot construct CardHolder with empty encrypted faces")
	}
	cards := make([]Card, len(theirEncrypted))
	shuffle := rand.Perm(len(theirEncrypted))
	for i, v := range shuffle {
		c, err := card.CardFromTheirs(theirEncrypted[i], key)
		if err != nil {
			return nil, errors.Wrap(err, "Error creating card")
		}
		cards[v] = c
	}

	return &CardHolder{cards: cards, key: key}, nil
}

type CardHolder struct {
	cards []Card
	key   *shamir3pass.Key
}

func (h *CardHolder) Size() uint {
	return uint(len(h.cards))
}

// Get returns the card face at the given position within the deck,
// if possible
func (h *CardHolder) Get(index uint) (card.CardFace, error) {
	if can, err := h.CanGet(index); !can {
		if err != nil {
			return nil, errors.Wrapf(err, "Unable to get card")
		} else {
			return nil, fmt.Errorf("Unable to get card %d", index)
		}
	}
	return h.cards[index].Face()
}

// CanGet determines whether it is currently possible to get the card
// face at the given position.
func (h *CardHolder) CanGet(index uint) (bool, error) {
	if index < uint(len(h.cards)) {
		return h.cards[index].CanDecrypt(), nil
	}
	return false, fmt.Errorf("Index %d out of bounds (%d cards)", index, len(h.cards))
}

// GetTheirs returns the card at the given position, but only encrypted with
// the opponent's key.
func (h *CardHolder) GetTheirs(index uint) (*big.Int, error) {
	if can, err := h.CanGetTheirs(index); !can {
		if err != nil {
			return nil, errors.Wrapf(err, "Unable to get card")
		} else {
			return nil, fmt.Errorf("Unable to get card %d", index)
		}
	}
	return h.cards[index].Theirs()
}

// CanGetTheirs determines whether it is currently possible to get the card
// face at the given position.
func (h *CardHolder) CanGetTheirs(index uint) (bool, error) {
	if index < uint(len(h.cards)) {
		return h.cards[index].HasEncrypted(), nil
	}
	return false, fmt.Errorf("Index %d out of bounds (%d cards)", index, len(h.cards))
}

// SetMine informs the card at the given index that its encrypted state
// with only the local player's key is the given big.Int. Knowing this
// value allows a card to be decrypted.
func (h *CardHolder) SetMine(index uint, mine *big.Int) error {
	if index < uint(len(h.cards)) {
		return h.cards[index].SetMine(mine)
	}
	return fmt.Errorf("Index %d out of bounds (max %d)", index, len(h.cards))
}

func (h *CardHolder) SetTheirKey(key *shamir3pass.Key) error {
	if key == nil {
		return fmt.Errorf("Key cannot be nil")
	}
	for i := range h.cards {
		if err := h.cards[i].SetTheirKey(key); err != nil {
			return errors.Wrapf(err, "Unable to set key on index %d", i)
		}
	}
	return nil
}

// SetBothEncrypted erases the current cards within the deck and creates
// a new deck of cards with the given values as the values that have been
// encrypted by both players. This does not erase the encryption key stored
// within the collection (which is important, since this is needed to decrypt
// cards later)
func (h *CardHolder) SetBothEncrypted(encryptedFaces []*big.Int) error {
	if encryptedFaces == nil {
		return fmt.Errorf("Cannot set both encrypted to nil slice")
	} else if len(encryptedFaces) != len(h.cards) {
		return fmt.Errorf("Cannot set both encrypted to slice of length %d"+
			" when cardCardHolder has %d cards", len(encryptedFaces),
			len(h.cards))
	}
	var err error
	newCards := make([]Card, len(h.cards))
	for i, c := range encryptedFaces {
		newCards[i], err = card.CardFromBoth(c, h.key)
		if err != nil {
			return errors.Wrap(err, "Unable to create card from both encrypted face")
		}
	}
	h.cards = newCards
	return nil
}

// GetAllMine returns all known mine values for the cards. If all cards had known
// values, the second return value will be true.
func (h *CardHolder) GetAllMine() ([]*big.Int, bool, error) {
	cards := make([]*big.Int, len(h.cards))
	allDecryptable := true
	var err error
	for i, c := range h.cards {
		if c.CanDecrypt() {
			cards[i], err = c.Mine()
			if err != nil {
				return cards, false, err
			}
		} else {
			allDecryptable = false
		}
	}
	return cards, allDecryptable, nil
}

// GetAllBoth returns all known doubly-encrypted values for the cards. If all cards had known
// values, the second return value will be true.
func (h *CardHolder) GetAllBoth() ([]*big.Int, bool, error) {
	cards := make([]*big.Int, len(h.cards))
	allEncryptable := true
	var err error
	for i, c := range h.cards {
		if c.HasEncrypted() {
			cards[i], err = c.Both()
			if err != nil {
				return cards, false, err
			}
		} else {
			allEncryptable = false
		}
	}
	return cards, allEncryptable, nil
}

// ValidateAll checks that all cards are internally consistent, if it is possible
func (h *CardHolder) ValidateAll() error {
	for i, c := range h.cards {
		if err := c.Validate(); err != nil {
			return errors.Wrapf(err, "Failed to validate all cards at index %d", i)
		}
	}
	return nil
}
<|endoftext|>