<commit_before>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apicapi

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sort"

	"github.com/Sirupsen/logrus"
)

func (conn *ApicConnection) apicBodyAttrCmp(class string,
	bodyc *ApicObjectBody, bodyd *ApicObjectBody) bool {
	meta, ok := metadata[class]
	if !ok {
		conn.log.Warning("No metadata for class ", class)
		return true
	}
	if bodyc.Attributes == nil {
		bodyc.Attributes = make(map[string]interface{})
	}
	if bodyd.Attributes == nil {
		bodyd.Attributes = make(map[string]interface{})
	}
	for p, def := range meta.attributes {
		ac, ok := bodyc.Attributes[p]
		if !ok {
			ac = def
		}
		ad, ok := bodyd.Attributes[p]
		if !ok {
			ad = def
		}

		if ac != ad {
			return false
		}
	}
	if class != "tagAnnotation" && class != "tagInst" {
		annotc, _ := bodyc.Attributes["annotation"]
		annotd, _ := bodyd.Attributes["annotation"]
		if annotc != annotd {
			return false
		}
	}

	return true
}

func (conn *ApicConnection) apicCntCmp(current ApicObject,
	desired ApicObject) bool {
	for classc, bodyc := range current {
		for classd, bodyd := range desired {
			if classc != classd {
				conn.log.Warning("Invalid comparison ", classc, " != ", classd)
				return false
			}

			return conn.apicBodyAttrCmp(classc, bodyc, bodyd)
		}
	}
	return true
}

func (conn *ApicConnection) apicObjCmp(current ApicObject,
	desired ApicObject) (update bool, deletes []string) {

	for classc, bodyc := range current {
		for classd, bodyd := range desired {
			if classc != classd {
				conn.log.Warning("Invalid comparison ", classc, " != ", classd)
				return
			}

			if !conn.apicBodyAttrCmp(classc, bodyc, bodyd) {
				update = true
			}

			i := 0
			j := 0
			for i < len(bodyc.Children) && j < len(bodyd.Children) {
				cmp := cmpApicObject(bodyc.Children[i], bodyd.Children[j])
				if cmp < 0 {
					deletes = append(deletes, bodyc.Children[i].GetDn())
					i++
				} else if cmp > 0 {
					update = true
					j++
				} else {
					cu, cd := conn.apicObjCmp(bodyc.Children[i],
						bodyd.Children[j])
					if cu {
						update = true
					}
					deletes = append(deletes, cd...)

					i++
					j++
				}
			}
			for i < len(bodyc.Children) {
				deletes = append(deletes, bodyc.Children[i].GetDn())
				i++
			}
			if j < len(bodyd.Children) {
				update = true
			}
		}
	}
	//if update {
	//	conn.log.WithFields(logrus.Fields{
	//		"current": current,
	//		"desired": desired,
	//	}).Debug("Update required")
	//}
	return
}

func (conn *ApicConnection) diffApicState(currentState ApicSlice,
	desiredState ApicSlice) (updates ApicSlice, deletes []string) {

	i := 0
	j := 0

	for i < len(currentState) && j < len(desiredState) {
		cmp := cmpApicObject(currentState[i], desiredState[j])
		if cmp < 0 {
			deletes = append(deletes, currentState[i].GetDn())
			i++
		} else if cmp > 0 {
			updates = append(updates, desiredState[j])
			j++
		} else {
			if conn.containerDns[currentState[i].GetDn()] {
				if !conn.apicCntCmp(currentState[i], desiredState[j]) {
					updates = append(updates, desiredState[j])
				}
			} else {
				cu, cd := conn.apicObjCmp(currentState[i], desiredState[j])
				if cu {
					updates = append(updates, desiredState[j])
				}
				deletes = append(deletes, cd...)
			}

			i++
			j++
		}
	}
	// extra old objects
	for i < len(currentState) {
		deletes = append(deletes, currentState[i].GetDn())
		i++
	}
	// extra new objects
	for j < len(desiredState) {
		updates = append(updates, desiredState[j])
		j++
	}

	return
}

func (conn *ApicConnection) applyDiff(updates ApicSlice, deletes []string,
	context string) {
	sort.Sort(updates)
	sort.Strings(deletes)

	for _, delete := range deletes {
		conn.log.WithFields(logrus.Fields{"DN": delete, "context": context}).
			Debug("Applying APIC object delete")
		conn.queueDn(delete)
	}
	for _, update := range updates {
		dn := update.GetDn()
		conn.log.WithFields(logrus.Fields{"DN": dn, "context": context}).
			Debug("Applying APIC object update")
		conn.queueDn(dn)
	}
}

func getTagFromKey(prefix string, key string) string {
	hash := sha256.Sum256([]byte(key))
	return fmt.Sprintf("%s-%s", prefix, hex.EncodeToString(hash[:16]))
}

func PrepareApicSlice(objects ApicSlice, prefix string, key string) ApicSlice {
	return prepareApicSliceTag(objects, getTagFromKey(prefix, key), false)
}

func prepareApicSliceTag(objects ApicSlice, tag string,
	useAPICInstTag bool) ApicSlice {

	sort.Sort(objects)
	for _, obj := range objects {
		for class, body := range obj {
			if class != "tagInst" && class != "tagAnnotation" {
				obj.SetTag(tag, useAPICInstTag)
				if !useAPICInstTag {
					if body.Attributes == nil {
						body.Attributes = make(map[string]interface{})
					}
					body.Attributes["annotation"] =
						aciContainersOwnerAnnotation
				}
			}
			prepareApicSliceTag(body.Children, tag, useAPICInstTag)

			if md, ok := metadata[class]; ok {
				if md.normalizer != nil {
					md.normalizer(body)
				}
			}
			break
		}
	}
	return objects
}

func prepareApicCache(parentDn string, obj ApicObject) {
	for _, body := range obj {
		if body.Attributes == nil {
			body.Attributes = make(map[string]interface{})
		}
		dn := obj.BuildDn(parentDn)
		body.Attributes["dn"] = dn

		for _, c := range body.Children {
			prepareApicCache(dn, c)
		}
		sort.Sort(body.Children)
		break
	}
}

func (conn *ApicConnection) fullSync() {
	conn.log.Info("Starting APIC full sync")
	var updates ApicSlice
	var deletes []string

	conn.indexMutex.Lock()
	for tag, current := range conn.cachedState {
		sort.Sort(current)
		key := conn.keyHashes[tag]
		u, d := conn.diffApicState(current, conn.desiredState[key])
		updates = append(updates, u...)
		deletes = append(deletes, d...)
	}

	for key, desired := range conn.desiredState {
		tag := getTagFromKey(conn.prefix, key)
		if _, ok := conn.cachedState[tag]; !ok {
			// entire key not present in current state
			a, _ := conn.diffApicState(nil, desired)
			updates = append(updates, a...)
		}
	}

	conn.syncEnabled = true
	conn.indexMutex.Unlock()

	conn.applyDiff(updates, deletes, "sync")
	conn.log.WithFields(logrus.Fields{
		"updates": len(updates),
		"deletes": len(deletes),
	}).Info("APIC full sync completed")
}

func (conn *ApicConnection) checkDeletes(oldState map[string]map[string]bool) {
	conn.indexMutex.Lock()
	for dn, ids := range oldState {
		_, found := conn.cacheDnSubIds[dn]
		if !found {
			for id, _ := range ids {
				value, ok := conn.subscriptions.ids[id]
				if !ok {
					continue
				}
				sub, ok := conn.subscriptions.subs[value]
				if !ok {
					continue
				}
				if sub.deleteHook != nil {
					sub.deleteHook(dn)
				}
			}
		}
	}
	conn.indexMutex.Unlock()
}

func (conn *ApicConnection) updateDnIndex(objects ApicSlice) {
	for _, obj := range objects {
		conn.desiredStateDn[obj.GetDn()] = obj
		for _, body := range obj {
			conn.updateDnIndex(body.Children)
		}
	}
}

func (conn *ApicConnection) removeFromDnIndex(dn string) {
	if obj, ok := conn.desiredStateDn[dn]; ok {
		delete(conn.desiredStateDn, dn)

		for _, body := range obj {
			for _, child := range body.Children {
				conn.removeFromDnIndex(child.GetDn())
			}
		}
	}
}

func (conn *ApicConnection) doWriteApicObjects(key string, objects ApicSlice,
	container bool) {
	tag := getTagFromKey(conn.prefix, key)
	prepareApicSliceTag(objects, tag, conn.UseAPICInstTag)

	conn.indexMutex.Lock()
	updates, deletes := conn.diffApicState(conn.desiredState[key], objects)

	conn.updateDnIndex(objects)
	for _, del := range deletes {
		conn.removeFromDnIndex(del)
		if container {
			delete(conn.containerDns, del)
		}
	}
	for _, update := range updates {
		dn := update.GetDn()
		if container {
			conn.containerDns[dn] = true
		}
	}

	if objects == nil {
		delete(conn.desiredState, key)
		delete(conn.keyHashes, tag)
	} else {
		conn.desiredState[key] = objects
		conn.keyHashes[tag] = key
	}

	if conn.syncEnabled {
		conn.indexMutex.Unlock()
		conn.applyDiff(updates, deletes, "write")
	} else {
		conn.indexMutex.Unlock()
	}
}

func (conn *ApicConnection) ClearApicContainer(key string) {
	conn.WriteApicContainer(key, nil)
}

func (conn *ApicConnection) WriteApicContainer(key string, objects ApicSlice) {
	conn.doWriteApicObjects(key, objects, true)
}

func (conn *ApicConnection) ClearApicObjects(key string) {
	conn.WriteApicObjects(key, nil)
}

func (conn *ApicConnection) WriteApicObjects(key string, objects ApicSlice) {
	conn.doWriteApicObjects(key, objects, false)
}

func (conn *ApicConnection) reconcileApicObject(aci ApicObject) {
	conn.indexMutex.Lock()
	if !conn.syncEnabled {
		conn.indexMutex.Unlock()
		return
	}

	dn := aci.GetDn()

	var updates ApicSlice
	var deletes []string

	if eobj, ok := conn.desiredStateDn[dn]; ok {
		if conn.containerDns[dn] {
			if !conn.apicCntCmp(aci, eobj) {
				updates = ApicSlice{eobj}
				conn.log.WithFields(logrus.Fields{
					"DN":       dn,
					"expected": eobj,
					"actual":   aci,
				}).Warning("Unexpected ACI container alteration")
			}
		} else {
			update, odels := conn.apicObjCmp(aci, eobj)
			if update {
				updates = ApicSlice{eobj}
			}
			deletes = append(deletes, odels...)

			if update || len(odels) != 0 {
				conn.log.WithFields(logrus.Fields{
					"DN":       dn,
					"expected": eobj,
					"actual":   aci,
				}).Warning("Unexpected ACI object alteration")
			}
		}
	} else {
		tag := aci.GetTag()
		if conn.isSyncTag(tag) {
			conn.log.WithFields(logrus.Fields{"DN": dn}).
				Warning("Deleting unexpected ACI object")
			deletes = append(deletes, dn)
		}
	}

	conn.indexMutex.Unlock()

	conn.applyDiff(updates, deletes, "reconcile "+dn)
}
<commit_msg>Fix annotation comparison causing spurious updates.<commit_after>// Copyright 2017 Cisco Systems, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package apicapi

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"sort"

	"github.com/Sirupsen/logrus"
)

func (conn *ApicConnection) apicBodyAttrCmp(class string,
	bodyc *ApicObjectBody, bodyd *ApicObjectBody) bool {
	meta, ok := metadata[class]
	if !ok {
		conn.log.Warning("No metadata for class ", class)
		return true
	}
	if bodyc.Attributes == nil {
		bodyc.Attributes = make(map[string]interface{})
	}
	if bodyd.Attributes == nil {
		bodyd.Attributes = make(map[string]interface{})
	}
	for p, def := range meta.attributes {
		ac, ok := bodyc.Attributes[p]
		if !ok {
			ac = def
		}
		ad, ok := bodyd.Attributes[p]
		if !ok {
			ad = def
		}

		if ac != ad {
			return false
		}
	}
	if class != "tagAnnotation" && class != "tagInst" {
		annotc, ok := bodyc.Attributes["annotation"]
		if !ok {
			annotc = ""
		}
		annotd, ok := bodyd.Attributes["annotation"]
		if !ok {
			annotd = ""
		}
		if annotc != annotd {
			return false
		}
	}

	return true
}

func (conn *ApicConnection) apicCntCmp(current ApicObject,
	desired ApicObject) bool {
	for classc, bodyc := range current {
		for classd, bodyd := range desired {
			if classc != classd {
				conn.log.Warning("Invalid comparison ", classc, " != ", classd)
				return false
			}

			return conn.apicBodyAttrCmp(classc, bodyc, bodyd)
		}
	}
	return true
}

func (conn *ApicConnection) apicObjCmp(current ApicObject,
	desired ApicObject) (update bool, deletes []string) {

	for classc, bodyc := range current {
		for classd, bodyd := range desired {
			if classc != classd {
				conn.log.Warning("Invalid comparison ", classc, " != ", classd)
				return
			}

			if !conn.apicBodyAttrCmp(classc, bodyc, bodyd) {
				update = true
			}

			i := 0
			j := 0
			for i < len(bodyc.Children) && j < len(bodyd.Children) {
				cmp := cmpApicObject(bodyc.Children[i], bodyd.Children[j])
				if cmp < 0 {
					deletes = append(deletes, bodyc.Children[i].GetDn())
					i++
				} else if cmp > 0 {
					update = true
					j++
				} else {
					cu, cd := conn.apicObjCmp(bodyc.Children[i],
						bodyd.Children[j])
					if cu {
						update = true
					}
					deletes = append(deletes, cd...)

					i++
					j++
				}
			}
			for i < len(bodyc.Children) {
				deletes = append(deletes, bodyc.Children[i].GetDn())
				i++
			}
			if j < len(bodyd.Children) {
				update = true
			}
		}
	}
	//if update {
	//	conn.log.WithFields(logrus.Fields{
	//		"current": current,
	//		"desired": desired,
	//	}).Debug("Update required")
	//}
	return
}

func (conn *ApicConnection) diffApicState(currentState ApicSlice,
	desiredState ApicSlice) (updates ApicSlice, deletes []string) {

	i := 0
	j := 0

	for i < len(currentState) && j < len(desiredState) {
		cmp := cmpApicObject(currentState[i], desiredState[j])
		if cmp < 0 {
			deletes = append(deletes, currentState[i].GetDn())
			i++
		} else if cmp > 0 {
			updates = append(updates, desiredState[j])
			j++
		} else {
			if conn.containerDns[currentState[i].GetDn()] {
				if !conn.apicCntCmp(currentState[i], desiredState[j]) {
					updates = append(updates, desiredState[j])
				}
			} else {
				cu, cd := conn.apicObjCmp(currentState[i], desiredState[j])
				if cu {
					updates = append(updates, desiredState[j])
				}
				deletes = append(deletes, cd...)
			}

			i++
			j++
		}
	}
	// extra old objects
	for i < len(currentState) {
		deletes = append(deletes, currentState[i].GetDn())
		i++
	}
	// extra new objects
	for j < len(desiredState) {
		updates = append(updates, desiredState[j])
		j++
	}

	return
}

func (conn *ApicConnection) applyDiff(updates ApicSlice, deletes []string,
	context string) {
	sort.Sort(updates)
	sort.Strings(deletes)

	for _, delete := range deletes {
		conn.log.WithFields(logrus.Fields{"DN": delete, "context": context}).
			Debug("Applying APIC object delete")
		conn.queueDn(delete)
	}
	for _, update := range updates {
		dn := update.GetDn()
		conn.log.WithFields(logrus.Fields{"DN": dn, "context": context}).
			Debug("Applying APIC object update")
		conn.queueDn(dn)
	}
}

func getTagFromKey(prefix string, key string) string {
	hash := sha256.Sum256([]byte(key))
	return fmt.Sprintf("%s-%s", prefix, hex.EncodeToString(hash[:16]))
}

func PrepareApicSlice(objects ApicSlice, prefix string, key string) ApicSlice {
	return prepareApicSliceTag(objects, getTagFromKey(prefix, key), false)
}

func prepareApicSliceTag(objects ApicSlice, tag string,
	useAPICInstTag bool) ApicSlice {

	sort.Sort(objects)
	for _, obj := range objects {
		for class, body := range obj {
			if class != "tagInst" && class != "tagAnnotation" {
				obj.SetTag(tag, useAPICInstTag)
				if !useAPICInstTag {
					if body.Attributes == nil {
						body.Attributes = make(map[string]interface{})
					}
					body.Attributes["annotation"] =
						aciContainersOwnerAnnotation
				}
			}
			prepareApicSliceTag(body.Children, tag, useAPICInstTag)

			if md, ok := metadata[class]; ok {
				if md.normalizer != nil {
					md.normalizer(body)
				}
			}
			break
		}
	}
	return objects
}

func prepareApicCache(parentDn string, obj ApicObject) {
	for _, body := range obj {
		if body.Attributes == nil {
			body.Attributes = make(map[string]interface{})
		}
		dn := obj.BuildDn(parentDn)
		body.Attributes["dn"] = dn

		for _, c := range body.Children {
			prepareApicCache(dn, c)
		}
		sort.Sort(body.Children)
		break
	}
}

func (conn *ApicConnection) fullSync() {
	conn.log.Info("Starting APIC full sync")
	var updates ApicSlice
	var deletes []string

	conn.indexMutex.Lock()
	for tag, current := range conn.cachedState {
		sort.Sort(current)
		key := conn.keyHashes[tag]
		u, d := conn.diffApicState(current, conn.desiredState[key])
		updates = append(updates, u...)
		deletes = append(deletes, d...)
	}

	for key, desired := range conn.desiredState {
		tag := getTagFromKey(conn.prefix, key)
		if _, ok := conn.cachedState[tag]; !ok {
			// entire key not present in current state
			a, _ := conn.diffApicState(nil, desired)
			updates = append(updates, a...)
		}
	}

	conn.syncEnabled = true
	conn.indexMutex.Unlock()

	conn.applyDiff(updates, deletes, "sync")
	conn.log.WithFields(logrus.Fields{
		"updates": len(updates),
		"deletes": len(deletes),
	}).Info("APIC full sync completed")
}

func (conn *ApicConnection) checkDeletes(oldState map[string]map[string]bool) {
	conn.indexMutex.Lock()
	for dn, ids := range oldState {
		_, found := conn.cacheDnSubIds[dn]
		if !found {
			for id, _ := range ids {
				value, ok := conn.subscriptions.ids[id]
				if !ok {
					continue
				}
				sub, ok := conn.subscriptions.subs[value]
				if !ok {
					continue
				}
				if sub.deleteHook != nil {
					sub.deleteHook(dn)
				}
			}
		}
	}
	conn.indexMutex.Unlock()
}

func (conn *ApicConnection) updateDnIndex(objects ApicSlice) {
	for _, obj := range objects {
		conn.desiredStateDn[obj.GetDn()] = obj
		for _, body := range obj {
			conn.updateDnIndex(body.Children)
		}
	}
}

func (conn *ApicConnection) removeFromDnIndex(dn string) {
	if obj, ok := conn.desiredStateDn[dn]; ok {
		delete(conn.desiredStateDn, dn)

		for _, body := range obj {
			for _, child := range body.Children {
				conn.removeFromDnIndex(child.GetDn())
			}
		}
	}
}

func (conn *ApicConnection) doWriteApicObjects(key string, objects ApicSlice,
	container bool) {
	tag := getTagFromKey(conn.prefix, key)
	prepareApicSliceTag(objects, tag, conn.UseAPICInstTag)

	conn.indexMutex.Lock()
	updates, deletes := conn.diffApicState(conn.desiredState[key], objects)

	conn.updateDnIndex(objects)
	for _, del := range deletes {
		conn.removeFromDnIndex(del)
		if container {
			delete(conn.containerDns, del)
		}
	}
	for _, update := range updates {
		dn := update.GetDn()
		if container {
			conn.containerDns[dn] = true
		}
	}

	if objects == nil {
		delete(conn.desiredState, key)
		delete(conn.keyHashes, tag)
	} else {
		conn.desiredState[key] = objects
		conn.keyHashes[tag] = key
	}

	if conn.syncEnabled {
		conn.indexMutex.Unlock()
		conn.applyDiff(updates, deletes, "write")
	} else {
		conn.indexMutex.Unlock()
	}
}

func (conn *ApicConnection) ClearApicContainer(key string) {
	conn.WriteApicContainer(key, nil)
}

func (conn *ApicConnection) WriteApicContainer(key string, objects ApicSlice) {
	conn.doWriteApicObjects(key, objects, true)
}

func (conn *ApicConnection) ClearApicObjects(key string) {
	conn.WriteApicObjects(key, nil)
}

func (conn *ApicConnection) WriteApicObjects(key string, objects ApicSlice) {
	conn.doWriteApicObjects(key, objects, false)
}

func (conn *ApicConnection) reconcileApicObject(aci ApicObject) {
	conn.indexMutex.Lock()
	if !conn.syncEnabled {
		conn.indexMutex.Unlock()
		return
	}

	dn := aci.GetDn()

	var updates ApicSlice
	var deletes []string

	if eobj, ok := conn.desiredStateDn[dn]; ok {
		if conn.containerDns[dn] {
			if !conn.apicCntCmp(aci, eobj) {
				updates = ApicSlice{eobj}
				conn.log.WithFields(logrus.Fields{
					"DN":       dn,
					"expected": eobj,
					"actual":   aci,
				}).Warning("Unexpected ACI container alteration")
			}
		} else {
			update, odels := conn.apicObjCmp(aci, eobj)
			if update {
				updates = ApicSlice{eobj}
			}
			deletes = append(deletes, odels...)

			if update || len(odels) != 0 {
				conn.log.WithFields(logrus.Fields{
					"DN":       dn,
					"expected": eobj,
					"actual":   aci,
				}).Warning("Unexpected ACI object alteration")
			}
		}
	} else {
		tag := aci.GetTag()
		if conn.isSyncTag(tag) {
			conn.log.WithFields(logrus.Fields{"DN": dn}).
				Warning("Deleting unexpected ACI object")
			deletes = append(deletes, dn)
		}
	}

	conn.indexMutex.Unlock()

	conn.applyDiff(updates, deletes, "reconcile "+dn)
}
<|endoftext|>


<commit_before>package cookbook

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"text/template"
	"time"

	"github.com/GeertJohan/go.rice"
	"github.com/jarosser06/fastfood/pkg/util"
)

type OSTarget struct {
	Distro  string
	Version string
}

type Cookbook struct {
	Berks        []string
	Dependencies []string
	Name         string
	Path         string
	Target       OSTarget
	Year         int
}

func NewCookbook(cookbookPath string, name string) Cookbook {
	cookbook := Cookbook{
		Year: time.Now().Year(),
		Path: path.Join(cookbookPath, name),
		Name: name,
	}

	return cookbook
}

// Given a cookbook path, return a cookbook struct pre-populated
func NewCookbookFromPath(cookbookPath string) (Cookbook, error) {
	var cookbook Cookbook

	if PathIsCookbook(cookbookPath) {
		f, err := os.Open(path.Join(cookbookPath, "metadata.rb"))

		if err != nil {
			return cookbook, errors.New(
				fmt.Sprintf("cookbook.NewCookbookFromPath: %v", err),
			)
		}

		defer f.Close()

		scanner := bufio.NewScanner(f)
		scanner.Split(bufio.ScanWords)

		depFlag := false
		nameFlag := false

		for scanner.Scan() {
			switch {
			case depFlag:
				cookbook.Dependencies = append(
					cookbook.Dependencies,
					strings.Trim(scanner.Text(), "'"),
				)
				depFlag = false
			case nameFlag:
				cookbook.Name = strings.Trim(scanner.Text(), "'")
				nameFlag = false
			case scanner.Text() == "depends":
				depFlag = true
			case scanner.Text() == "name":
				nameFlag = true
			}
		}

		if !(len(cookbook.Name) > 0) {
			return cookbook, errors.New("unable to determine cookbook name")
		}

		return cookbook, nil
	} else {
		return cookbook, errors.New(
			fmt.Sprintf("%s is not a cookbook", cookbookPath),
		)
	}
}

func PathIsCookbook(cookbookPath string) bool {
	_, err := os.Stat(path.Join(cookbookPath, "metadata.rb"))
	if err == nil {
		return true
	} else {
		return false
	}
}

func (c *Cookbook) GenFiles() error {

	cookbookFiles := [8]string{
		"Berksfile",
		"CHANGELOG.md",
		"Gemfile",
		"metadata.rb",
		"README.md",
		"recipes/default.rb",
		"test/unit/spec/default_spec.rb",
		"test/unit/spec/spec_helper.rb",
	}

	templateBox, _ := rice.FindBox("templates")
	for _, cookbookFile := range cookbookFiles {
		tmpStr, _ := templateBox.String(cookbookFile)
		t, _ := template.New(cookbookFile).Parse(tmpStr)

		f, err := os.Create(path.Join(c.Path, cookbookFile))
		if err != nil {
			return errors.New(fmt.Sprintf("cookbook.GenFiles(): %v", err))
		}

		// Write template output to a buffer
		var buffer bytes.Buffer
		t.Execute(&buffer, c)

		// Clean up rendered templates and write to file
		cleanStr := util.CollapseNewlines(buffer.String())
		io.WriteString(f, cleanStr)

		f.Close()
	}

	return nil
}

func (c *Cookbook) GenDirs() error {
	err := os.Mkdir(c.Path, 0755)
	if err != nil {
		return errors.New(fmt.Sprintf("Gendirs(): %v", err))
	}

	dirs := [10]string{
		"attributes",
		"files",
		"libraries",
		"providers",
		"recipes",
		"resources",
		"templates",
		"test",
		"test/unit",
		"test/unit/spec",
	}

	for _, dir := range dirs {
		err := os.Mkdir(path.Join(c.Path, dir), 0755)
		if err != nil {
			return errors.New(fmt.Sprintf("cookbook.Gendirs(): %v", err))
		}
	}

	return nil
}
<commit_msg>replace mkdir with mkdirall<commit_after>package cookbook

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"text/template"
	"time"

	"github.com/GeertJohan/go.rice"
	"github.com/jarosser06/fastfood/pkg/util"
)

type OSTarget struct {
	Distro  string
	Version string
}

type Cookbook struct {
	Berks        []string
	Dependencies []string
	Name         string
	Path         string
	Target       OSTarget
	Year         int
}

func NewCookbook(cookbookPath string, name string) Cookbook {
	cookbook := Cookbook{
		Year: time.Now().Year(),
		Path: path.Join(cookbookPath, name),
		Name: name,
	}

	return cookbook
}

// Given a cookbook path, return a cookbook struct pre-populated
func NewCookbookFromPath(cookbookPath string) (Cookbook, error) {
	var cookbook Cookbook

	if PathIsCookbook(cookbookPath) {
		f, err := os.Open(path.Join(cookbookPath, "metadata.rb"))

		if err != nil {
			return cookbook, errors.New(
				fmt.Sprintf("cookbook.NewCookbookFromPath: %v", err),
			)
		}

		defer f.Close()

		scanner := bufio.NewScanner(f)
		scanner.Split(bufio.ScanWords)

		depFlag := false
		nameFlag := false

		for scanner.Scan() {
			switch {
			case depFlag:
				cookbook.Dependencies = append(
					cookbook.Dependencies,
					strings.Trim(scanner.Text(), "'"),
				)
				depFlag = false
			case nameFlag:
				cookbook.Name = strings.Trim(scanner.Text(), "'")
				nameFlag = false
			case scanner.Text() == "depends":
				depFlag = true
			case scanner.Text() == "name":
				nameFlag = true
			}
		}

		if !(len(cookbook.Name) > 0) {
			return cookbook, errors.New("unable to determine cookbook name")
		}

		return cookbook, nil
	} else {
		return cookbook, errors.New(
			fmt.Sprintf("%s is not a cookbook", cookbookPath),
		)
	}
}

func PathIsCookbook(cookbookPath string) bool {
	_, err := os.Stat(path.Join(cookbookPath, "metadata.rb"))
	if err == nil {
		return true
	} else {
		return false
	}
}

func (c *Cookbook) GenFiles() error {

	cookbookFiles := [8]string{
		"Berksfile",
		"CHANGELOG.md",
		"Gemfile",
		"metadata.rb",
		"README.md",
		"recipes/default.rb",
		"test/unit/spec/default_spec.rb",
		"test/unit/spec/spec_helper.rb",
	}

	templateBox, _ := rice.FindBox("templates")
	for _, cookbookFile := range cookbookFiles {
		tmpStr, _ := templateBox.String(cookbookFile)
		t, _ := template.New(cookbookFile).Parse(tmpStr)

		f, err := os.Create(path.Join(c.Path, cookbookFile))
		if err != nil {
			return errors.New(fmt.Sprintf("cookbook.GenFiles(): %v", err))
		}

		// Write template output to a buffer
		var buffer bytes.Buffer
		t.Execute(&buffer, c)

		// Clean up rendered templates and write to file
		cleanStr := util.CollapseNewlines(buffer.String())
		io.WriteString(f, cleanStr)

		f.Close()
	}

	return nil
}

func (c *Cookbook) GenDirs() error {
	err := os.Mkdir(c.Path, 0755)
	if err != nil {
		return errors.New(fmt.Sprintf("Gendirs(): %v", err))
	}

	dirs := [10]string{
		"attributes",
		"files",
		"libraries",
		"providers",
		"recipes",
		"resources",
		"templates",
		"test/unit/spec",
	}

	for _, dir := range dirs {
		err := os.MkdirAll(path.Join(c.Path, dir), 0755)
		if err != nil {
			return errors.New(fmt.Sprintf("cookbook.Gendirs(): %v", err))
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package iptables

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

var IPTablesPath = "iptables"

func init() {

	err := CheckIPTables()
	if err != nil {
		log.Printf("Unable to find iptables, using fallback")
		wd, err := os.Getwd()
		if err != nil {
			return
		}
		IPTablesPath = filepath.Join(wd, IPTablesPath)
	}

}

// NOTEs from messing with iptables proxying:
// For external:
// iptables -A PREROUTING -t nat -p tcp -m tcp --dport 5555 -j REDIRECT --to-ports 49278
// For internal:
// iptables -A OUTPUT -t nat -p tcp -m tcp --dport 5555 -j REDIRECT --to-ports 49278
// To delete a rule, use -D rather than -A.

type Action bool

const (
	INSERT Action = true
	DELETE        = false
)

func CheckIPTables() error {
	cmd := exec.Command(IPTablesPath, "--list", "--wait")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// Invoke one iptables command.
// Expects "iptables" in the path to be runnable with reasonable permissions.
func iptables(action Action, chain string, args ...string) *exec.Cmd {
	switch action {
	case INSERT:
		args = append([]string{"--insert", chain, "1"}, args...)
	case DELETE:
		args = append([]string{"--delete", chain}, args...)
	}

	cmd := exec.Command(IPTablesPath, args...)
	cmd.Stderr = os.Stderr
	return cmd
}

// Configure one port redirect from `source` to `target` using iptables.
// Returns an error and a function which undoes the change to the firewall.
func ConfigureRedirect(source, target int, ipAddress string) (func(), error) {
	args := []string{
		"--table", "nat",
		"--protocol", "tcp",
		// Prevent redirection of packets already going to the container
		"--match", "tcp", "!", "--destination", ipAddress,
		// Prevent redirection of ports on remote servers
		// (i.e, don't make google:80 hit our container)
		"--match", "addrtype", "--dst-type", "LOCAL",
		"--dport", fmt.Sprint(source),
		"--jump", "REDIRECT",
		"--to-ports", fmt.Sprint(target), "--wait",
	}

	err := iptables(INSERT, "PREROUTING", args...).Run()
	if err != nil {
		return nil, err
	}
	err = iptables(INSERT, "OUTPUT", args...).Run()
	if err != nil {
		return nil, err
	}

	remove := func() {
		err := iptables(DELETE, "PREROUTING", args...).Run()
		if err != nil {
			log.Println("Failed to remove iptables rule:", source, target)
		}
		err = iptables(DELETE, "OUTPUT", args...).Run()
		if err != nil {
			log.Println("Failed to remove iptables rule:", source, target)
		}
	}
	return remove, nil
}
<commit_msg>Move Run() into iptables()<commit_after>package iptables

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
)

var IPTablesPath = "iptables"

func init() {

	err := CheckIPTables()
	if err != nil {
		log.Printf("Unable to find iptables, using fallback")
		wd, err := os.Getwd()
		if err != nil {
			return
		}
		IPTablesPath = filepath.Join(wd, IPTablesPath)
	}

}

// NOTEs from messing with iptables proxying:
// For external:
// iptables -A PREROUTING -t nat -p tcp -m tcp --dport 5555 -j REDIRECT --to-ports 49278
// For internal:
// iptables -A OUTPUT -t nat -p tcp -m tcp --dport 5555 -j REDIRECT --to-ports 49278
// To delete a rule, use -D rather than -A.

type Action bool

const (
	INSERT Action = true
	DELETE        = false
)

func CheckIPTables() error {
	cmd := exec.Command(IPTablesPath, "--list", "--wait")
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// Invoke one iptables command.
// Expects "iptables" in the path to be runnable with reasonable permissions.
func iptables(action Action, chain string, args ...string) error {
	switch action {
	case INSERT:
		args = append([]string{"--insert", chain, "1"}, args...)
	case DELETE:
		args = append([]string{"--delete", chain}, args...)
	}

	cmd := exec.Command(IPTablesPath, args...)
	cmd.Stderr = os.Stderr
	return cmd.Run()
}

// Configure one port redirect from `source` to `target` using iptables.
// Returns an error and a function which undoes the change to the firewall.
func ConfigureRedirect(source, target int, ipAddress string) (func(), error) {
	args := []string{
		"--table", "nat",
		"--protocol", "tcp",
		// Prevent redirection of packets already going to the container
		"--match", "tcp", "!", "--destination", ipAddress,
		// Prevent redirection of ports on remote servers
		// (i.e, don't make google:80 hit our container)
		"--match", "addrtype", "--dst-type", "LOCAL",
		"--dport", fmt.Sprint(source),
		"--jump", "REDIRECT",
		"--to-ports", fmt.Sprint(target), "--wait",
	}

	err := iptables(INSERT, "PREROUTING", args...)
	if err != nil {
		return nil, err
	}
	err = iptables(INSERT, "OUTPUT", args...)
	if err != nil {
		return nil, err
	}

	remove := func() {
		err := iptables(DELETE, "PREROUTING", args...)
		if err != nil {
			log.Println("Failed to remove iptables rule:", source, target)
		}
		err = iptables(DELETE, "OUTPUT", args...)
		if err != nil {
			log.Println("Failed to remove iptables rule:", source, target)
		}
	}
	return remove, nil
}
<|endoftext|>


<commit_before>package sharings

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

	"github.com/cozy/cozy-stack/client/request"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Sharing contains all the information about a sharing
type Sharing struct {
	SID         string `json:"_id,omitempty"`
	SRev        string `json:"_rev,omitempty"`
	Type        string `json:"type,omitempty"`
	Owner       bool   `json:"owner"`
	Desc        string `json:"desc,omitempty"`
	SharingID   string `json:"sharing_id,omitempty"`
	SharingType string `json:"sharing_type"`

	Permissions      permissions.Set    `json:"permissions,omitempty"`
	RecipientsStatus []*RecipientStatus `json:"recipients,omitempty"`
	Sharer           *Sharer            `json:"sharer,omitempty"`
}

// RecipientStatus contains the information about a recipient for a sharing
type RecipientStatus struct {
	Status       string `json:"status,omitempty"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`

	RefRecipient jsonapi.ResourceIdentifier `json:"recipient,omitempty"`

	recipient *Recipient
}

// Sharer contains the information about the sharer from the recipient's
// perspective.
//
// ATTENTION: This structure will only be filled by the recipients as it is
// recipient specific. The `ClientID` is different for each recipient and only
// known by them.
type Sharer struct {
	ClientID string `json:"client_id"`
	URL      string `json:"url"`
}

// SharingAnswer contains the necessary information to answer a sharing
// request, be it accepted or refused.
// A refusal only contains the mandatory fields: SharingID and ClientID.
// An acceptance contains **everything**.
type SharingAnswer struct {
	SharingID    string `json:"state"`
	ClientID     string `json:"client_id"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
}

// ID returns the sharing qualified identifier
func (s *Sharing) ID() string { return s.SID }

// Rev returns the sharing revision
func (s *Sharing) Rev() string { return s.SRev }

// DocType returns the sharing document type
func (s *Sharing) DocType() string { return consts.Sharings }

// SetID changes the sharing qualified identifier
func (s *Sharing) SetID(id string) { s.SID = id }

// SetRev changes the sharing revision
func (s *Sharing) SetRev(rev string) { s.SRev = rev }

// Links implements jsonapi.Doc
func (s *Sharing) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/sharings/" + s.SID}
}

// RecStatus returns the sharing recipients status
func (s *Sharing) RecStatus(db couchdb.Database) ([]*RecipientStatus, error) {
	var rStatus []*RecipientStatus

	for _, rec := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, rec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		rec.recipient = recipient
		rStatus = append(rStatus, rec)
	}

	s.RecipientsStatus = rStatus
	return rStatus, nil
}

// Recipients returns the sharing recipients
func (s *Sharing) Recipients(db couchdb.Database) ([]*Recipient, error) {
	var recipients []*Recipient

	for _, rec := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, rec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		rec.recipient = recipient
		recipients = append(recipients, recipient)
	}

	return recipients, nil
}

// Relationships is part of the jsonapi.Object interface
// It is used to generate the recipients relationships
func (s *Sharing) Relationships() jsonapi.RelationshipMap {
	l := len(s.RecipientsStatus)
	i := 0

	data := make([]jsonapi.ResourceIdentifier, l)
	for _, rec := range s.RecipientsStatus {
		r := rec.recipient
		data[i] = jsonapi.ResourceIdentifier{ID: r.ID(), Type: r.DocType()}
		i++
	}
	contents := jsonapi.Relationship{Data: data}
	return jsonapi.RelationshipMap{"recipients": contents}
}

// Included is part of the jsonapi.Object interface
func (s *Sharing) Included() []jsonapi.Object {
	var included []jsonapi.Object
	for _, rec := range s.RecipientsStatus {
		r := rec.recipient
		included = append(included, r)
	}
	return included
}

// GetRecipient returns the Recipient stored in database from a given ID
func GetRecipient(db couchdb.Database, recID string) (*Recipient, error) {
	doc := &Recipient{}
	err := couchdb.GetDoc(db, consts.Recipients, recID, doc)
	if couchdb.IsNotFoundError(err) {
		err = ErrRecipientDoesNotExist
	}
	return doc, err
}

// GetSharingRecipientFromClientID returns the Recipient associated with the given clientID
func (s *Sharing) GetSharingRecipientFromClientID(db couchdb.Database, clientID string) (*RecipientStatus, error) {
	for _, recStatus := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, recStatus.RefRecipient.ID)
		if err != nil {
			return nil, err
		}

		recStatus.recipient = recipient

		if recipient.Client == nil {
			return nil, nil
		}
		if recipient.Client.ClientID == clientID {
			return recStatus, nil
		}
	}
	return nil, nil
}

//CheckSharingType returns an error if the sharing type is incorrect
func CheckSharingType(sharingType string) error {
	switch sharingType {
	case consts.OneShotSharing, consts.MasterSlaveSharing, consts.MasterMasterSharing:
		return nil
	}
	return ErrBadSharingType
}

// findSharingRecipient retrieve a sharing recipient from its clientID and sharingID
func findSharingRecipient(db couchdb.Database, sharingID, clientID string) (*Sharing, *RecipientStatus, error) {
	var res []Sharing

	err := couchdb.FindDocs(db, consts.Sharings, &couchdb.FindRequest{
		UseIndex: "by-sharing-id",
		Selector: mango.Equal("sharing_id", sharingID),
	}, &res)
	if err != nil {
		return nil, nil, err
	}
	if len(res) < 1 {
		return nil, nil, ErrSharingDoesNotExist
	} else if len(res) > 2 {
		return nil, nil, ErrSharingIDNotUnique
	}

	sharing := &res[0]
	sRec, err := sharing.GetSharingRecipientFromClientID(db, clientID)
	if err != nil {
		return nil, nil, err
	}
	if sRec == nil {
		return nil, nil, ErrRecipientDoesNotExist
	}
	return sharing, sRec, nil
}

// addTrigger creates a new trigger on the updates of the shared documents
func addTrigger(instance *instance.Instance, rule permissions.Rule, sharingID string) error {
	scheduler := instance.JobsScheduler()

	eventArgs := rule.Type + ":UPDATED:" + strings.Join(rule.Values, ",")
	type TriggerMessage struct {
		SharingID string `json:"sharing_id"`
	}
	m := TriggerMessage{sharingID}
	workerArgs, err := json.Marshal(m)
	if err != nil {
		return err
	}
	t, err := jobs.NewTrigger(&jobs.TriggerInfos{
		Type:       "@event",
		WorkerType: "sharing",
		Arguments:  eventArgs,
		Message: &jobs.Message{
			Type: jobs.JSONEncoding,
			Data: workerArgs,
		},
	})
	if err != nil {
		return err
	}
	return scheduler.Add(t)
}

// sendDoc sends a JSON document to a recipient
func sendDoc(docType, id string, doc *couchdb.JSONDoc, recStatus *RecipientStatus) error {
	// Get the recipient info
	token := recStatus.AccessToken
	rec := recStatus.recipient
	domain, err := rec.ExtractDomain()
	if err != nil {
		return err
	}
	path := fmt.Sprintf("/data/%s/%s", docType, id)
	// A new doc will be created on the recipient side
	delete(doc.M, "_id")
	delete(doc.M, "_rev")
	body, err := request.WriteJSON(doc.M)
	if err != nil {
		return err
	}

	_, err = request.Req(&request.Options{
		Domain: domain,
		Method: "PUT",
		Path:   path,
		Headers: request.Headers{
			"Content-Type":  "application/json",
			"Accept":        "application/json",
			"Authorization": "Bearer " + token,
		},
		Body: body,
	})
	return err
}

// ShareDoc shares the documents specified in the Sharing structure to the
// specified recipient
func ShareDoc(instance *instance.Instance, sharing *Sharing, recStatus *RecipientStatus) error {
	// Lookup all the sharing permissions
	for _, rule := range sharing.Permissions {
		// Only static values are supported yet
		if len(rule.Values) == 0 {
			return nil
		}
		docType := rule.Type
		// Create a trigger for the updates on the rule
		if err := addTrigger(instance, rule, sharing.SharingID); err != nil {
			return err
		}
		// Get each document referenced in Values and sent it to the recipient
		for _, val := range rule.Values {
			doc := &couchdb.JSONDoc{}
			if err := couchdb.GetDoc(instance, docType, val, doc); err != nil {
				return err
			}
			if err := sendDoc(docType, val, doc, recStatus); err != nil {
				return err
			}

		}
	}
	return nil
}

// SharingAccepted handles an accepted sharing on the sharer side
func SharingAccepted(instance *instance.Instance, state, clientID, accessCode string) (string, error) {
	sharing, recStatus, err := findSharingRecipient(instance, state, clientID)
	if err != nil {
		return "", err
	}
	recStatus.Status = consts.AcceptedSharingStatus

	access, err := recStatus.recipient.GetAccessToken(accessCode)
	if err != nil {
		return "", err
	}
	recStatus.AccessToken = access.AccessToken
	recStatus.RefreshToken = access.RefreshToken
	err = couchdb.UpdateDoc(instance, sharing)
	if err != nil {
		return "", err
	}
	// Share all the documents with the recipient
	err = ShareDoc(instance, sharing, recStatus)

	// Redirect the recipient after acceptation
	redirect := recStatus.recipient.URL
	return redirect, err
}

// SharingRefused handles a rejected sharing on the sharer side
func SharingRefused(db couchdb.Database, state, clientID string) (string, error) {
	sharing, recStatus, err := findSharingRecipient(db, state, clientID)
	if err != nil {
		return "", err
	}
	recStatus.Status = consts.RefusedSharingStatus
	err = couchdb.UpdateDoc(db, sharing)
	redirect := recStatus.recipient.URL
	return redirect, err
}

// RecipientRefusedSharing executes all the actions induced by a refusal from
// the recipient: the sharing document is deleted and the sharer is informed.
func RecipientRefusedSharing(db couchdb.Database, sharingID, clientID string) (string, error) {
	// We get the sharing document through its sharing id…
	var res []Sharing
	err := couchdb.FindDocs(db, consts.Sharings, &couchdb.FindRequest{
		Selector: mango.Equal("sharing_id", sharingID),
	}, &res)
	if err != nil {
		return "", err
	} else if len(res) < 1 {
		return "", ErrSharingDoesNotExist
	} else if len(res) > 1 {
		return "", ErrSharingIDNotUnique
	}
	sharing := &res[0]

	// … and we delete it because it is no longer needed.
	err = couchdb.DeleteDoc(db, sharing)
	if err != nil {
		return "", err
	}
	// We get the sharer's oauth client so that we can get her Cozy's url
	// through the `ClientURI`.
	sharer := &oauth.Client{}
	err = couchdb.GetDoc(db, consts.OAuthClients, clientID, sharer)
	if err != nil {
		return "", ErrNoOAuthClient
	}

	u := fmt.Sprintf("%s/sharings/answer", sharer.ClientURI)
	return u, nil
}

// CreateSharingRequest checks fields integrity and creates a sharing document
// for an incoming sharing request
func CreateSharingRequest(db couchdb.Database, desc, state, sharingType, scope, clientID string) (*Sharing, error) {
	if state == "" {
		return nil, ErrMissingState
	}
	if err := CheckSharingType(sharingType); err != nil {
		return nil, err
	}
	if scope == "" {
		return nil, ErrMissingScope
	}
	if clientID == "" {
		return nil, ErrNoOAuthClient
	}
	permissions, err := permissions.UnmarshalScopeString(scope)
	if err != nil {
		return nil, err
	}

	sharerClient := &oauth.Client{}
	err = couchdb.GetDoc(db, consts.OAuthClients, clientID, sharerClient)
	if err != nil {
		return nil, ErrNoOAuthClient
	}

	sharer := &Sharer{
		ClientID: clientID,
		URL:      sharerClient.ClientURI,
	}

	sharing := &Sharing{
		SharingType: sharingType,
		SharingID:   state,
		Permissions: permissions,
		Owner:       false,
		Desc:        desc,
		Sharer:      sharer,
	}

	err = Create(db, sharing)

	return sharing, err
}

// CheckSharingCreation initializes and check some sharing fields at creation
func CheckSharingCreation(db couchdb.Database, sharing *Sharing) error {

	sharingType := sharing.SharingType
	if err := CheckSharingType(sharingType); err != nil {
		return err
	}

	recStatus, err := sharing.RecStatus(db)
	if err != nil {
		return err
	}
	for _, rec := range recStatus {
		rec.Status = consts.PendingSharingStatus
	}

	sharing.Owner = true
	sharing.SharingID = utils.RandomString(32)

	return nil
}

// Create inserts a Sharing document in database
func Create(db couchdb.Database, doc *Sharing) error {
	err := couchdb.CreateDoc(db, doc)
	return err
}

var (
	_ couchdb.Doc    = &Sharing{}
	_ jsonapi.Object = &Sharing{}
)
<commit_msg>Simplify struct declaration<commit_after>package sharings

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

	"github.com/cozy/cozy-stack/client/request"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/couchdb/mango"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/jobs"
	"github.com/cozy/cozy-stack/pkg/oauth"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/pkg/utils"
	"github.com/cozy/cozy-stack/web/jsonapi"
)

// Sharing contains all the information about a sharing
type Sharing struct {
	SID         string `json:"_id,omitempty"`
	SRev        string `json:"_rev,omitempty"`
	Type        string `json:"type,omitempty"`
	Owner       bool   `json:"owner"`
	Desc        string `json:"desc,omitempty"`
	SharingID   string `json:"sharing_id,omitempty"`
	SharingType string `json:"sharing_type"`

	Permissions      permissions.Set    `json:"permissions,omitempty"`
	RecipientsStatus []*RecipientStatus `json:"recipients,omitempty"`
	Sharer           *Sharer            `json:"sharer,omitempty"`
}

// RecipientStatus contains the information about a recipient for a sharing
type RecipientStatus struct {
	Status       string `json:"status,omitempty"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`

	RefRecipient jsonapi.ResourceIdentifier `json:"recipient,omitempty"`

	recipient *Recipient
}

// Sharer contains the information about the sharer from the recipient's
// perspective.
//
// ATTENTION: This structure will only be filled by the recipients as it is
// recipient specific. The `ClientID` is different for each recipient and only
// known by them.
type Sharer struct {
	ClientID string `json:"client_id"`
	URL      string `json:"url"`
}

// SharingAnswer contains the necessary information to answer a sharing
// request, be it accepted or refused.
// A refusal only contains the mandatory fields: SharingID and ClientID.
// An acceptance contains **everything**.
type SharingAnswer struct {
	SharingID    string `json:"state"`
	ClientID     string `json:"client_id"`
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
}

// ID returns the sharing qualified identifier
func (s *Sharing) ID() string { return s.SID }

// Rev returns the sharing revision
func (s *Sharing) Rev() string { return s.SRev }

// DocType returns the sharing document type
func (s *Sharing) DocType() string { return consts.Sharings }

// SetID changes the sharing qualified identifier
func (s *Sharing) SetID(id string) { s.SID = id }

// SetRev changes the sharing revision
func (s *Sharing) SetRev(rev string) { s.SRev = rev }

// Links implements jsonapi.Doc
func (s *Sharing) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/sharings/" + s.SID}
}

// RecStatus returns the sharing recipients status
func (s *Sharing) RecStatus(db couchdb.Database) ([]*RecipientStatus, error) {
	var rStatus []*RecipientStatus

	for _, rec := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, rec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		rec.recipient = recipient
		rStatus = append(rStatus, rec)
	}

	s.RecipientsStatus = rStatus
	return rStatus, nil
}

// Recipients returns the sharing recipients
func (s *Sharing) Recipients(db couchdb.Database) ([]*Recipient, error) {
	var recipients []*Recipient

	for _, rec := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, rec.RefRecipient.ID)
		if err != nil {
			return nil, err
		}
		rec.recipient = recipient
		recipients = append(recipients, recipient)
	}

	return recipients, nil
}

// Relationships is part of the jsonapi.Object interface
// It is used to generate the recipients relationships
func (s *Sharing) Relationships() jsonapi.RelationshipMap {
	l := len(s.RecipientsStatus)
	i := 0

	data := make([]jsonapi.ResourceIdentifier, l)
	for _, rec := range s.RecipientsStatus {
		r := rec.recipient
		data[i] = jsonapi.ResourceIdentifier{ID: r.ID(), Type: r.DocType()}
		i++
	}
	contents := jsonapi.Relationship{Data: data}
	return jsonapi.RelationshipMap{"recipients": contents}
}

// Included is part of the jsonapi.Object interface
func (s *Sharing) Included() []jsonapi.Object {
	var included []jsonapi.Object
	for _, rec := range s.RecipientsStatus {
		r := rec.recipient
		included = append(included, r)
	}
	return included
}

// GetRecipient returns the Recipient stored in database from a given ID
func GetRecipient(db couchdb.Database, recID string) (*Recipient, error) {
	doc := &Recipient{}
	err := couchdb.GetDoc(db, consts.Recipients, recID, doc)
	if couchdb.IsNotFoundError(err) {
		err = ErrRecipientDoesNotExist
	}
	return doc, err
}

// GetSharingRecipientFromClientID returns the Recipient associated with the given clientID
func (s *Sharing) GetSharingRecipientFromClientID(db couchdb.Database, clientID string) (*RecipientStatus, error) {
	for _, recStatus := range s.RecipientsStatus {
		recipient, err := GetRecipient(db, recStatus.RefRecipient.ID)
		if err != nil {
			return nil, err
		}

		recStatus.recipient = recipient

		if recipient.Client == nil {
			return nil, nil
		}
		if recipient.Client.ClientID == clientID {
			return recStatus, nil
		}
	}
	return nil, nil
}

//CheckSharingType returns an error if the sharing type is incorrect
func CheckSharingType(sharingType string) error {
	switch sharingType {
	case consts.OneShotSharing, consts.MasterSlaveSharing, consts.MasterMasterSharing:
		return nil
	}
	return ErrBadSharingType
}

// findSharingRecipient retrieve a sharing recipient from its clientID and sharingID
func findSharingRecipient(db couchdb.Database, sharingID, clientID string) (*Sharing, *RecipientStatus, error) {
	var res []Sharing

	err := couchdb.FindDocs(db, consts.Sharings, &couchdb.FindRequest{
		UseIndex: "by-sharing-id",
		Selector: mango.Equal("sharing_id", sharingID),
	}, &res)
	if err != nil {
		return nil, nil, err
	}
	if len(res) < 1 {
		return nil, nil, ErrSharingDoesNotExist
	} else if len(res) > 2 {
		return nil, nil, ErrSharingIDNotUnique
	}

	sharing := &res[0]
	sRec, err := sharing.GetSharingRecipientFromClientID(db, clientID)
	if err != nil {
		return nil, nil, err
	}
	if sRec == nil {
		return nil, nil, ErrRecipientDoesNotExist
	}
	return sharing, sRec, nil
}

// addTrigger creates a new trigger on the updates of the shared documents
func addTrigger(instance *instance.Instance, rule permissions.Rule, sharingID string) error {
	scheduler := instance.JobsScheduler()

	eventArgs := rule.Type + ":UPDATED:" + strings.Join(rule.Values, ",")
	type TriggerMessage struct {
		SharingID string `json:"sharing_id"`
	}
	m := struct {
		SharingID string `json:"sharing_id"`
	}{sharingID}

	workerArgs, err := json.Marshal(m)
	if err != nil {
		return err
	}
	t, err := jobs.NewTrigger(&jobs.TriggerInfos{
		Type:       "@event",
		WorkerType: "sharing",
		Arguments:  eventArgs,
		Message: &jobs.Message{
			Type: jobs.JSONEncoding,
			Data: workerArgs,
		},
	})
	if err != nil {
		return err
	}
	return scheduler.Add(t)
}

// sendDoc sends a JSON document to a recipient
func sendDoc(docType, id string, doc *couchdb.JSONDoc, recStatus *RecipientStatus) error {
	// Get the recipient info
	token := recStatus.AccessToken
	rec := recStatus.recipient
	domain, err := rec.ExtractDomain()
	if err != nil {
		return err
	}
	path := fmt.Sprintf("/data/%s/%s", docType, id)
	// A new doc will be created on the recipient side
	delete(doc.M, "_id")
	delete(doc.M, "_rev")
	body, err := request.WriteJSON(doc.M)
	if err != nil {
		return err
	}

	_, err = request.Req(&request.Options{
		Domain: domain,
		Method: "PUT",
		Path:   path,
		Headers: request.Headers{
			"Content-Type":  "application/json",
			"Accept":        "application/json",
			"Authorization": "Bearer " + token,
		},
		Body: body,
	})
	return err
}

// ShareDoc shares the documents specified in the Sharing structure to the
// specified recipient
func ShareDoc(instance *instance.Instance, sharing *Sharing, recStatus *RecipientStatus) error {
	// Lookup all the sharing permissions
	for _, rule := range sharing.Permissions {
		// Only static values are supported yet
		if len(rule.Values) == 0 {
			return nil
		}
		docType := rule.Type
		// Create a trigger for the updates on the rule
		if err := addTrigger(instance, rule, sharing.SharingID); err != nil {
			return err
		}
		// Get each document referenced in Values and sent it to the recipient
		for _, val := range rule.Values {
			doc := &couchdb.JSONDoc{}
			if err := couchdb.GetDoc(instance, docType, val, doc); err != nil {
				return err
			}
			if err := sendDoc(docType, val, doc, recStatus); err != nil {
				return err
			}

		}
	}
	return nil
}

// SharingAccepted handles an accepted sharing on the sharer side
func SharingAccepted(instance *instance.Instance, state, clientID, accessCode string) (string, error) {
	sharing, recStatus, err := findSharingRecipient(instance, state, clientID)
	if err != nil {
		return "", err
	}
	recStatus.Status = consts.AcceptedSharingStatus

	access, err := recStatus.recipient.GetAccessToken(accessCode)
	if err != nil {
		return "", err
	}
	recStatus.AccessToken = access.AccessToken
	recStatus.RefreshToken = access.RefreshToken
	err = couchdb.UpdateDoc(instance, sharing)
	if err != nil {
		return "", err
	}
	// Share all the documents with the recipient
	err = ShareDoc(instance, sharing, recStatus)

	// Redirect the recipient after acceptation
	redirect := recStatus.recipient.URL
	return redirect, err
}

// SharingRefused handles a rejected sharing on the sharer side
func SharingRefused(db couchdb.Database, state, clientID string) (string, error) {
	sharing, recStatus, err := findSharingRecipient(db, state, clientID)
	if err != nil {
		return "", err
	}
	recStatus.Status = consts.RefusedSharingStatus
	err = couchdb.UpdateDoc(db, sharing)
	redirect := recStatus.recipient.URL
	return redirect, err
}

// RecipientRefusedSharing executes all the actions induced by a refusal from
// the recipient: the sharing document is deleted and the sharer is informed.
func RecipientRefusedSharing(db couchdb.Database, sharingID, clientID string) (string, error) {
	// We get the sharing document through its sharing id…
	var res []Sharing
	err := couchdb.FindDocs(db, consts.Sharings, &couchdb.FindRequest{
		Selector: mango.Equal("sharing_id", sharingID),
	}, &res)
	if err != nil {
		return "", err
	} else if len(res) < 1 {
		return "", ErrSharingDoesNotExist
	} else if len(res) > 1 {
		return "", ErrSharingIDNotUnique
	}
	sharing := &res[0]

	// … and we delete it because it is no longer needed.
	err = couchdb.DeleteDoc(db, sharing)
	if err != nil {
		return "", err
	}
	// We get the sharer's oauth client so that we can get her Cozy's url
	// through the `ClientURI`.
	sharer := &oauth.Client{}
	err = couchdb.GetDoc(db, consts.OAuthClients, clientID, sharer)
	if err != nil {
		return "", ErrNoOAuthClient
	}

	u := fmt.Sprintf("%s/sharings/answer", sharer.ClientURI)
	return u, nil
}

// CreateSharingRequest checks fields integrity and creates a sharing document
// for an incoming sharing request
func CreateSharingRequest(db couchdb.Database, desc, state, sharingType, scope, clientID string) (*Sharing, error) {
	if state == "" {
		return nil, ErrMissingState
	}
	if err := CheckSharingType(sharingType); err != nil {
		return nil, err
	}
	if scope == "" {
		return nil, ErrMissingScope
	}
	if clientID == "" {
		return nil, ErrNoOAuthClient
	}
	permissions, err := permissions.UnmarshalScopeString(scope)
	if err != nil {
		return nil, err
	}

	sharerClient := &oauth.Client{}
	err = couchdb.GetDoc(db, consts.OAuthClients, clientID, sharerClient)
	if err != nil {
		return nil, ErrNoOAuthClient
	}

	sharer := &Sharer{
		ClientID: clientID,
		URL:      sharerClient.ClientURI,
	}

	sharing := &Sharing{
		SharingType: sharingType,
		SharingID:   state,
		Permissions: permissions,
		Owner:       false,
		Desc:        desc,
		Sharer:      sharer,
	}

	err = Create(db, sharing)

	return sharing, err
}

// CheckSharingCreation initializes and check some sharing fields at creation
func CheckSharingCreation(db couchdb.Database, sharing *Sharing) error {

	sharingType := sharing.SharingType
	if err := CheckSharingType(sharingType); err != nil {
		return err
	}

	recStatus, err := sharing.RecStatus(db)
	if err != nil {
		return err
	}
	for _, rec := range recStatus {
		rec.Status = consts.PendingSharingStatus
	}

	sharing.Owner = true
	sharing.SharingID = utils.RandomString(32)

	return nil
}

// Create inserts a Sharing document in database
func Create(db couchdb.Database, doc *Sharing) error {
	err := couchdb.CreateDoc(db, doc)
	return err
}

var (
	_ couchdb.Doc    = &Sharing{}
	_ jsonapi.Object = &Sharing{}
)
<|endoftext|>


<commit_before>// +build integration

package snap

import (
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestSnap(t *testing.T) {
	Convey("Creating a Snap experiment session", t, func() {
		session, err := NewSession(
			"test-session",
			[]string{"/intel/mock/foo"},
			1*time.Second,
		)

		Convey("Shouldn't return any errors", func() {
			So(err, ShouldBeNil)
		})

		Convey("Starting a session", func() {
			err := session.Start()

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			Convey("Contacting snap to get the task status", func() {
				status, err := session.Status()

				Convey("Shouldn't return any errors", func() {
					So(err, ShouldBeNil)
				})

				Convey("And the task should be running", func() {
					So(status, ShouldEqual, "Running")
				})
			})
		})

		Convey("Stopping a session", func() {
			err := session.Stop()

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			Convey("And the task should not be available", func() {
			})
		})
	})
}
<commit_msg>Session stops tasks in test<commit_after>// +build integration

package snap

import (
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestSnap(t *testing.T) {
	Convey("Creating a Snap experiment session", t, func() {
		session, err := NewSession(
			"test-session",
			[]string{"/intel/mock/foo"},
			1*time.Second,
		)

		Convey("Shouldn't return any errors", func() {
			So(err, ShouldBeNil)
		})

		Convey("Starting a session", func() {
			err := session.Start()
			defer session.Stop()

			Convey("Shouldn't return any errors", func() {
				So(err, ShouldBeNil)
			})

			Convey("Contacting snap to get the task status", func() {
				status, err := session.Status()

				Convey("Shouldn't return any errors", func() {
					So(err, ShouldBeNil)
				})

				Convey("And the task should be running", func() {
					So(status, ShouldEqual, "Running")
				})
			})

			Convey("Stopping a session", func() {
				err := session.Stop()

				Convey("Shouldn't return any errors", func() {
					So(err, ShouldBeNil)
				})

				Convey("And the task should not be available", func() {
					_, err := session.Status()
					So(err, ShouldNotBeNil)
				})
			})
		})
	})
}
<|endoftext|>


<commit_before>// +build windows
package term

import (
	"io"

	"github.com/docker/docker/pkg/term/winconsole"
)

// State holds the console mode for the terminal.
type State struct {
	mode uint32
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

// GetWinsize gets the window size of the given terminal
func GetWinsize(fd uintptr) (*Winsize, error) {
	ws := &Winsize{}
	var info *winconsole.CONSOLE_SCREEN_BUFFER_INFO
	info, err := winconsole.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	ws.Width = uint16(info.Window.Right - info.Window.Left + 1)
	ws.Height = uint16(info.Window.Bottom - info.Window.Top + 1)

	ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
	ws.y = 0

	return ws, nil
}

// SetWinsize sets the terminal connected to the given file descriptor to a
// given size.
func SetWinsize(fd uintptr, ws *Winsize) error {
	return nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	_, e := winconsole.GetConsoleMode(fd)
	return e == nil
}

// RestoreTerminal restores the terminal connected to the given file descriptor to a
// previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	return winconsole.SetConsoleMode(fd, state.mode)
}

// SaveState saves the state of the given console
func SaveState(fd uintptr) (*State, error) {
	mode, e := winconsole.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}
	return &State{mode}, nil
}

// DisableEcho disbales the echo for given file descriptor and returns previous state
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
func DisableEcho(fd uintptr, state *State) error {
	state.mode &^= (winconsole.ENABLE_ECHO_INPUT)
	state.mode |= (winconsole.ENABLE_PROCESSED_INPUT | winconsole.ENABLE_LINE_INPUT)
	return winconsole.SetConsoleMode(fd, state.mode)
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func SetRawTerminal(fd uintptr) (*State, error) {
	oldState, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}
	// TODO (azlinux): implement handling interrupt and restore state of terminal
	return oldState, err
}

// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
	var state *State
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
	// All three input modes, along with processed output mode, are designed to work together.
	// It is best to either enable or disable all of these modes as a group.
	// When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application.
	// When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.
	state.mode = 0
	err = winconsole.SetConsoleMode(fd, state.mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal
func GetFdInfo(in interface{}) (uintptr, bool) {
	return winconsole.GetHandleInfo(in)
}

func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	return winconsole.StdStreams()
}
<commit_msg>Disable ANSI emulation in certain windows shells<commit_after>// +build windows
package term

import (
	"io"
	"os"

	"github.com/docker/docker/pkg/term/winconsole"
)

// State holds the console mode for the terminal.
type State struct {
	mode uint32
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
	x      uint16
	y      uint16
}

// GetWinsize gets the window size of the given terminal
func GetWinsize(fd uintptr) (*Winsize, error) {
	ws := &Winsize{}
	var info *winconsole.CONSOLE_SCREEN_BUFFER_INFO
	info, err := winconsole.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	ws.Width = uint16(info.Window.Right - info.Window.Left + 1)
	ws.Height = uint16(info.Window.Bottom - info.Window.Top + 1)

	ws.x = 0 // todo azlinux -- this is the pixel size of the Window, and not currently used by any caller
	ws.y = 0

	return ws, nil
}

// SetWinsize sets the terminal connected to the given file descriptor to a
// given size.
func SetWinsize(fd uintptr, ws *Winsize) error {
	return nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	_, e := winconsole.GetConsoleMode(fd)
	return e == nil
}

// RestoreTerminal restores the terminal connected to the given file descriptor to a
// previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	return winconsole.SetConsoleMode(fd, state.mode)
}

// SaveState saves the state of the given console
func SaveState(fd uintptr) (*State, error) {
	mode, e := winconsole.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}
	return &State{mode}, nil
}

// DisableEcho disbales the echo for given file descriptor and returns previous state
// see http://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx for these flag settings
func DisableEcho(fd uintptr, state *State) error {
	state.mode &^= (winconsole.ENABLE_ECHO_INPUT)
	state.mode |= (winconsole.ENABLE_PROCESSED_INPUT | winconsole.ENABLE_LINE_INPUT)
	return winconsole.SetConsoleMode(fd, state.mode)
}

// SetRawTerminal puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func SetRawTerminal(fd uintptr) (*State, error) {
	oldState, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}
	// TODO (azlinux): implement handling interrupt and restore state of terminal
	return oldState, err
}

// MakeRaw puts the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
	var state *State
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	// https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
	// All three input modes, along with processed output mode, are designed to work together.
	// It is best to either enable or disable all of these modes as a group.
	// When all are enabled, the application is said to be in "cooked" mode, which means that most of the processing is handled for the application.
	// When all are disabled, the application is in "raw" mode, which means that input is unfiltered and any processing is left to the application.
	state.mode = 0
	err = winconsole.SetConsoleMode(fd, state.mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

// GetFdInfo returns file descriptor and bool indicating whether the file is a terminal
func GetFdInfo(in interface{}) (uintptr, bool) {
	return winconsole.GetHandleInfo(in)
}

func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	var shouldEmulateANSI bool
	switch {
	case os.Getenv("ConEmuANSI") == "ON":
		// ConEmu shell, ansi emulated by default and ConEmu does an extensively
		// good emulation.
		shouldEmulateANSI = false
	case os.Getenv("MSYSTEM") != "":
		// MSYS (mingw) cannot fully emulate well and still shows escape characters
		// mostly because it's still running on cmd.exe window.
		shouldEmulateANSI = true
	default:
		shouldEmulateANSI = true
	}

	if shouldEmulateANSI {
		return winconsole.StdStreams()
	}

	return os.Stdin, os.Stdout, os.Stderr
}
<|endoftext|>


<commit_before>package pki

import (
	"crypto/x509"
	"encoding/asn1"
)

// Copied from x509.go
var oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}

// RFC 5280, 4.2.1.12  Extended Key Usage
//
// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
//
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
//
// id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
// id-kp-clientAuth             OBJECT IDENTIFIER ::= { id-kp 2 }
// id-kp-codeSigning            OBJECT IDENTIFIER ::= { id-kp 3 }
// id-kp-emailProtection        OBJECT IDENTIFIER ::= { id-kp 4 }
// id-kp-timeStamping           OBJECT IDENTIFIER ::= { id-kp 8 }
// id-kp-OCSPSigning            OBJECT IDENTIFIER ::= { id-kp 9 }
var (
	oidExtKeyUsageAny                            = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
	oidExtKeyUsageServerAuth                     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
	oidExtKeyUsageClientAuth                     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
	oidExtKeyUsageCodeSigning                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
	oidExtKeyUsageEmailProtection                = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
	oidExtKeyUsageIPSECEndSystem                 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
	oidExtKeyUsageIPSECTunnel                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
	oidExtKeyUsageIPSECUser                      = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
	oidExtKeyUsageTimeStamping                   = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
	oidExtKeyUsageOCSPSigning                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
	oidExtKeyUsageMicrosoftServerGatedCrypto     = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
	oidExtKeyUsageNetscapeServerGatedCrypto      = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
	oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22}
	oidExtKeyUsageMicrosoftKernelCodeSigning     = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1}
)

// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID.
var extKeyUsageOIDs = []struct {
	extKeyUsage x509.ExtKeyUsage
	oid         asn1.ObjectIdentifier
}{
	{x509.ExtKeyUsageAny, oidExtKeyUsageAny},
	{x509.ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth},
	{x509.ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth},
	{x509.ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning},
	{x509.ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection},
	{x509.ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem},
	{x509.ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel},
	{x509.ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser},
	{x509.ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping},
	{x509.ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning},
	{x509.ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto},
	{x509.ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto},
	{x509.ExtKeyUsageMicrosoftCommercialCodeSigning, oidExtKeyUsageMicrosoftCommercialCodeSigning},
	{x509.ExtKeyUsageMicrosoftKernelCodeSigning, oidExtKeyUsageMicrosoftKernelCodeSigning},
}

// OIDFromExtKeyUsage returns the ASN1 Identifier for a x509.ExtKeyUsage
func OIDFromExtKeyUsage(eku x509.ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) {
	for _, pair := range extKeyUsageOIDs {
		if eku == pair.extKeyUsage {
			return pair.oid, true
		}
	}
	return
}
<commit_msg>Add boilerplate<commit_after>/*
Copyright 2020 The Jetstack cert-manager contributors.

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

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

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

package pki

import (
	"crypto/x509"
	"encoding/asn1"
)

// Copied from x509.go
var oidExtensionExtendedKeyUsage = []int{2, 5, 29, 37}

// RFC 5280, 4.2.1.12  Extended Key Usage
//
// anyExtendedKeyUsage OBJECT IDENTIFIER ::= { id-ce-extKeyUsage 0 }
//
// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
//
// id-kp-serverAuth             OBJECT IDENTIFIER ::= { id-kp 1 }
// id-kp-clientAuth             OBJECT IDENTIFIER ::= { id-kp 2 }
// id-kp-codeSigning            OBJECT IDENTIFIER ::= { id-kp 3 }
// id-kp-emailProtection        OBJECT IDENTIFIER ::= { id-kp 4 }
// id-kp-timeStamping           OBJECT IDENTIFIER ::= { id-kp 8 }
// id-kp-OCSPSigning            OBJECT IDENTIFIER ::= { id-kp 9 }
var (
	oidExtKeyUsageAny                            = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
	oidExtKeyUsageServerAuth                     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
	oidExtKeyUsageClientAuth                     = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
	oidExtKeyUsageCodeSigning                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
	oidExtKeyUsageEmailProtection                = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
	oidExtKeyUsageIPSECEndSystem                 = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
	oidExtKeyUsageIPSECTunnel                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
	oidExtKeyUsageIPSECUser                      = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
	oidExtKeyUsageTimeStamping                   = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
	oidExtKeyUsageOCSPSigning                    = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
	oidExtKeyUsageMicrosoftServerGatedCrypto     = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
	oidExtKeyUsageNetscapeServerGatedCrypto      = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
	oidExtKeyUsageMicrosoftCommercialCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 2, 1, 22}
	oidExtKeyUsageMicrosoftKernelCodeSigning     = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1}
)

// extKeyUsageOIDs contains the mapping between an ExtKeyUsage and its OID.
var extKeyUsageOIDs = []struct {
	extKeyUsage x509.ExtKeyUsage
	oid         asn1.ObjectIdentifier
}{
	{x509.ExtKeyUsageAny, oidExtKeyUsageAny},
	{x509.ExtKeyUsageServerAuth, oidExtKeyUsageServerAuth},
	{x509.ExtKeyUsageClientAuth, oidExtKeyUsageClientAuth},
	{x509.ExtKeyUsageCodeSigning, oidExtKeyUsageCodeSigning},
	{x509.ExtKeyUsageEmailProtection, oidExtKeyUsageEmailProtection},
	{x509.ExtKeyUsageIPSECEndSystem, oidExtKeyUsageIPSECEndSystem},
	{x509.ExtKeyUsageIPSECTunnel, oidExtKeyUsageIPSECTunnel},
	{x509.ExtKeyUsageIPSECUser, oidExtKeyUsageIPSECUser},
	{x509.ExtKeyUsageTimeStamping, oidExtKeyUsageTimeStamping},
	{x509.ExtKeyUsageOCSPSigning, oidExtKeyUsageOCSPSigning},
	{x509.ExtKeyUsageMicrosoftServerGatedCrypto, oidExtKeyUsageMicrosoftServerGatedCrypto},
	{x509.ExtKeyUsageNetscapeServerGatedCrypto, oidExtKeyUsageNetscapeServerGatedCrypto},
	{x509.ExtKeyUsageMicrosoftCommercialCodeSigning, oidExtKeyUsageMicrosoftCommercialCodeSigning},
	{x509.ExtKeyUsageMicrosoftKernelCodeSigning, oidExtKeyUsageMicrosoftKernelCodeSigning},
}

// OIDFromExtKeyUsage returns the ASN1 Identifier for a x509.ExtKeyUsage
func OIDFromExtKeyUsage(eku x509.ExtKeyUsage) (oid asn1.ObjectIdentifier, ok bool) {
	for _, pair := range extKeyUsageOIDs {
		if eku == pair.extKeyUsage {
			return pair.oid, true
		}
	}
	return
}
<|endoftext|>


<commit_before>package ble

import (
	"bytes"

	"github.com/hybridgroup/gobot"
)

var _ gobot.Driver = (*BLEBatteryDriver)(nil)

type BLEBatteryDriver struct {
	name       string
	connection gobot.Connection
	gobot.Eventer
}

// NewBLEBatteryDriver creates a BLEBatteryDriver by name
func NewBLEBatteryDriver(a *BLEAdaptor, name string) *BLEBatteryDriver {
	n := &BLEBatteryDriver{
		name:       name,
		connection: a,
		Eventer:    gobot.NewEventer(),
	}

	// n.AddEvent("extended")
	// n.AddEvent("signal")
	// n.AddEvent("attention")
	// n.AddEvent("meditation")
	// n.AddEvent("blink")
	// n.AddEvent("wave")
	// n.AddEvent("eeg")
	// n.AddEvent("error")

	return n
}
func (b *BLEBatteryDriver) Connection() gobot.Connection { return b.connection }
func (b *BLEBatteryDriver) Name() string                 { return b.name }

// adaptor returns BLE adaptor
func (b *BLEBatteryDriver) adaptor() *BLEAdaptor {
	return b.Connection().(*BLEAdaptor)
}

// Start tells driver to get ready to do work
func (b *BLEBatteryDriver) Start() (errs []error) {
	return
}

// Halt stops battery driver (void)
func (b *BLEBatteryDriver) Halt() (errs []error) { return }

func (b *BLEBatteryDriver) GetBatteryLevel() (level uint8) {
	var l uint8
	c, _ := b.adaptor().ReadCharacteristic("180f", "2a19")
	buf := bytes.NewBuffer(c)
	val, _ := buf.ReadByte()
	l = uint8(val)
	return l
}
<commit_msg>Remove commented lines<commit_after>package ble

import (
	"bytes"

	"github.com/hybridgroup/gobot"
)

var _ gobot.Driver = (*BLEBatteryDriver)(nil)

type BLEBatteryDriver struct {
	name       string
	connection gobot.Connection
	gobot.Eventer
}

// NewBLEBatteryDriver creates a BLEBatteryDriver by name
func NewBLEBatteryDriver(a *BLEAdaptor, name string) *BLEBatteryDriver {
	n := &BLEBatteryDriver{
		name:       name,
		connection: a,
		Eventer:    gobot.NewEventer(),
	}

	return n
}
func (b *BLEBatteryDriver) Connection() gobot.Connection { return b.connection }
func (b *BLEBatteryDriver) Name() string                 { return b.name }

// adaptor returns BLE adaptor
func (b *BLEBatteryDriver) adaptor() *BLEAdaptor {
	return b.Connection().(*BLEAdaptor)
}

// Start tells driver to get ready to do work
func (b *BLEBatteryDriver) Start() (errs []error) {
	return
}

// Halt stops battery driver (void)
func (b *BLEBatteryDriver) Halt() (errs []error) { return }

func (b *BLEBatteryDriver) GetBatteryLevel() (level uint8) {
	var l uint8
	c, _ := b.adaptor().ReadCharacteristic("180f", "2a19")
	buf := bytes.NewBuffer(c)
	val, _ := buf.ReadByte()
	l = uint8(val)
	return l
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"

	sh "github.com/codeskyblue/go-sh"
)

// ShellCmd represents a shell command to be run for dokku
type ShellCmd struct {
	Env           map[string]string
	Command       *exec.Cmd
	CommandString string
	Args          []string
	ShowOutput    bool
}

// NewShellCmd returns a new ShellCmd struct
func NewShellCmd(command string) *ShellCmd {
	items := strings.Split(command, " ")
	cmd := items[0]
	args := items[1:]
	return &ShellCmd{
		Command:       exec.Command(cmd, args...),
		CommandString: command,
		Args:          args,
		ShowOutput:    true,
	}
}

// Execute is a lightweight wrapper around exec.Command
func (sc *ShellCmd) Execute() bool {
	env := os.Environ()
	for k, v := range sc.Env {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}
	sc.Command.Env = env
	if sc.ShowOutput {
		sc.Command.Stdout = os.Stdout
		sc.Command.Stderr = os.Stderr
	}
	err := sc.Command.Run()
	if err != nil {
		return false
	}
	return true
}

// VerifyAppName verifies app name format and app existence"
func VerifyAppName(appName string) (err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
	_, err = os.Stat(appRoot)
	if os.IsNotExist(err) {
		return fmt.Errorf("App %s does not exist: %v\n", appName, err)
	}
	r, _ := regexp.Compile("^[a-z].*")
	if !r.MatchString(appName) {
		return fmt.Errorf("App name (%s) must begin with lowercase alphanumeric character\n", appName)
	}
	return err
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) string {
	value := os.Getenv(key)
	if value == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return value
}

// LogFail is the failure log formatter
// prints text to stderr and exits with status 1
func LogFail(text string) {
	fmt.Fprintln(os.Stderr, fmt.Sprintf("FAILED: %s", text))
	os.Exit(1)
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if appName == "" {
		LogFail("(GetDeployingAppImageName) APP must not be empty")
	}

	b, err := sh.Command("plugn", "trigger", "deployed-app-repository", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	imageRemoteRepository := string(b[:])

	b, err = sh.Command("plugn", "trigger", "deployed-app-image-tag", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	newImageTag := string(b[:])

	b, err = sh.Command("plugn", "trigger", "deployed-app-image-repo", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	newImageRepo := string(b[:])

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName = fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		LogFail(fmt.Sprintf("app image (%s) not found", imageName))
	}
	return
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// VerifyImage returns true if docker image exists in local repo
func VerifyImage(image string) bool {
	imageCmd := NewShellCmd(strings.Join([]string{"docker inspect", image}, " "))
	imageCmd.ShowOutput = false
	if imageCmd.Execute() {
		return true
	}
	return false
}
<commit_msg>[common] add NewTokenizedShellCmd to ShellCmd<commit_after>package common

import (
	"fmt"
	"os"
	"os/exec"
	"regexp"
	"strings"

	sh "github.com/codeskyblue/go-sh"
)

// ShellCmd represents a shell command to be run for dokku
type ShellCmd struct {
	Env           map[string]string
	Command       *exec.Cmd
	CommandString string
	Args          []string
	ShowOutput    bool
}

// NewShellCmd returns a new ShellCmd struct
func NewShellCmd(command string) *ShellCmd {
	items := strings.Split(command, " ")
	return NewTokenizedShellCmd(items...)
}

// NewTokenizedShellCmd creates a new ShellCmd
func NewTokenizedShellCmd(command ...string) *ShellCmd {
	cmd := command[0]
	args := command[1:]
	return &ShellCmd{
		Command:       exec.Command(cmd, args...),
		CommandString: strings.Join(command, " "),
		Args:          args,
		ShowOutput:    true,
	}
}

// Execute is a lightweight wrapper around exec.Command
func (sc *ShellCmd) Execute() bool {
	env := os.Environ()
	for k, v := range sc.Env {
		env = append(env, fmt.Sprintf("%s=%s", k, v))
	}
	sc.Command.Env = env
	if sc.ShowOutput {
		sc.Command.Stdout = os.Stdout
		sc.Command.Stderr = os.Stderr
	}
	err := sc.Command.Run()
	if err != nil {
		return false
	}
	return true
}

// VerifyAppName verifies app name format and app existence"
func VerifyAppName(appName string) (err error) {
	dokkuRoot := MustGetEnv("DOKKU_ROOT")
	appRoot := strings.Join([]string{dokkuRoot, appName}, "/")
	_, err = os.Stat(appRoot)
	if os.IsNotExist(err) {
		return fmt.Errorf("App %s does not exist: %v\n", appName, err)
	}
	r, _ := regexp.Compile("^[a-z].*")
	if !r.MatchString(appName) {
		return fmt.Errorf("App name (%s) must begin with lowercase alphanumeric character\n", appName)
	}
	return err
}

// MustGetEnv returns env variable or fails if it's not set
func MustGetEnv(key string) string {
	value := os.Getenv(key)
	if value == "" {
		LogFail(fmt.Sprintf("%s not set!", key))
	}
	return value
}

// LogFail is the failure log formatter
// prints text to stderr and exits with status 1
func LogFail(text string) {
	fmt.Fprintln(os.Stderr, fmt.Sprintf("FAILED: %s", text))
	os.Exit(1)
}

// GetDeployingAppImageName returns deploying image identifier for a given app, tag tuple. validate if tag is presented
func GetDeployingAppImageName(appName, imageTag, imageRepo string) (imageName string) {
	if appName == "" {
		LogFail("(GetDeployingAppImageName) APP must not be empty")
	}

	b, err := sh.Command("plugn", "trigger", "deployed-app-repository", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	imageRemoteRepository := string(b[:])

	b, err = sh.Command("plugn", "trigger", "deployed-app-image-tag", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	newImageTag := string(b[:])

	b, err = sh.Command("plugn", "trigger", "deployed-app-image-repo", appName).Output()
	if err != nil {
		LogFail(err.Error())
	}
	newImageRepo := string(b[:])

	if newImageRepo != "" {
		imageRepo = newImageRepo
	}
	if newImageTag != "" {
		imageTag = newImageTag
	}
	if imageRepo == "" {
		imageRepo = GetAppImageRepo(appName)
	}
	if imageTag == "" {
		imageTag = "latest"
	}

	imageName = fmt.Sprintf("%s%s:%s", imageRemoteRepository, imageRepo, imageTag)
	if !VerifyImage(imageName) {
		LogFail(fmt.Sprintf("app image (%s) not found", imageName))
	}
	return
}

// GetAppImageRepo is the central definition of a dokku image repo pattern
func GetAppImageRepo(appName string) string {
	return strings.Join([]string{"dokku", appName}, "/")
}

// VerifyImage returns true if docker image exists in local repo
func VerifyImage(image string) bool {
	imageCmd := NewShellCmd(strings.Join([]string{"docker inspect", image}, " "))
	imageCmd.ShowOutput = false
	if imageCmd.Execute() {
		return true
	}
	return false
}
<|endoftext|>