<commit_before>package drouter

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	dockerclient "github.com/docker/engine-api/client"
	dockertypes "github.com/docker/engine-api/types"
	dockerevents "github.com/docker/engine-api/types/events"
	dockerfilters "github.com/docker/engine-api/types/filters"
	"github.com/vdemeester/docker-events"
	"github.com/vishvananda/netlink"
	"golang.org/x/net/context"
	"net"
	"os"
	"sync"
	"syscall"
	"time"
)

var (
	//cli options
	ipOffset         int
	aggressive       bool
	hostShortcut     bool
	containerGateway bool
	hostGateway      bool
	masquerade       bool
	p2p              *p2pNetwork
	staticRoutes     []*net.IPNet
	transitNetName   string
	selfContainerID  string
	instanceName     string

	//other globals
	dockerClient *dockerclient.Client
	transitNetID string
	connectWG    sync.WaitGroup
	stopChan     <-chan struct{}
)

//DistributedRouterOptions Options for our DistributedRouter instance
type DistributedRouterOptions struct {
	IPOffset         int
	Aggressive       bool
	HostShortcut     bool
	ContainerGateway bool
	HostGateway      bool
	Masquerade       bool
	P2PAddr          string
	StaticRoutes     []string
	TransitNet       string
	InstanceName     string
}

type distributedRouter struct {
	networks     map[string]*network
	defaultRoute net.IP
}

func newDistributedRouter(options *DistributedRouterOptions) (*distributedRouter, error) {
	var err error
	//set global vars from options
	ipOffset = options.IPOffset
	aggressive = options.Aggressive
	hostShortcut = options.HostShortcut
	containerGateway = options.ContainerGateway
	hostGateway = options.HostGateway
	masquerade = options.Masquerade
	instanceName = options.InstanceName
	//staticRoutes
	for _, sr := range options.StaticRoutes {
		_, cidr, err := net.ParseCIDR(sr)
		if err != nil {
			log.WithFields(log.Fields{
				"Subnet": sr,
				"Error":  err,
			}).Error("Failed to parse static route subnet.")
			continue
		}
		staticRoutes = append(staticRoutes, cidr)
	}

	transitNetName = options.TransitNet

	if !aggressive && len(transitNetName) == 0 {
		log.Warn("Detected --no-aggressive and --transit-net was not found. This router may not be able to route to networks on other hosts")
	}

	//get the pid of drouter
	pid := os.Getpid()
	if pid == 1 {
		return &distributedRouter{}, fmt.Errorf("Running as pid 1. Running with --pid=host required.")
	}

	//get docker client
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	dockerClient, err = dockerclient.NewClient("unix:///var/run/docker.sock", "v1.23", nil, defaultHeaders)
	if err != nil {
		logError("Error connecting to docker socket", err)
		return &distributedRouter{}, err
	}

	//process options for assumptions and validity
	if masquerade {
		log.Debug("Detected --masquerade. Assuming --host-gateway and --container-gateway and --host-shortcut.")
		hostShortcut = true
		containerGateway = true
		hostGateway = true
	} else {
		if hostGateway {
			log.Debug("Detected --host-gateway. Assuming --host-shortcut.")
			hostShortcut = true
		}
	}

	sc, err := getSelfContainer()
	if err != nil {
		log.Error("Failed to getSelfContainer(). I am running in a container, right?.")
		return nil, err
	}
	selfContainerID = sc.ID

	//disconnect from all initial networks
	log.Debug("Leaving all connected currently networks.")
	for name, settings := range sc.NetworkSettings.Networks {
		log.WithFields(log.Fields{
			"Network": name,
		}).Debug("Leaving network.")
		err = dockerClient.NetworkDisconnect(context.Background(), settings.NetworkID, selfContainerID, true)
		if err != nil {
			log.WithFields(log.Fields{
				"Network": name,
				"Error":   err,
			}).Error("Failed to leave network.")
			continue
		}
	}

	//create our distributedRouter object
	dr := &distributedRouter{
		networks: make(map[string]*network),
	}

	//initial setup
	if hostShortcut {
		var err error
		log.Debug("--host-shortcut detected, making P2P link.")
		p2p, err = newP2PNetwork(options.P2PAddr)
		if err != nil {
			log.Error("Failed to create the point to point link.")
			return nil, err
		}

		if hostGateway {
			dr.defaultRoute = p2p.hostIP
			err = dr.setDefaultRoute()
			if err != nil {
				log.Error("--host-gateway=true and unable to set default route to host's p2p address.")
				return nil, err
			}
			if masquerade {
				log.Debug("--masquerade detected, inserting masquerade rule.")
				if err := insertMasqRule(); err != nil {
					log.Error("Failed to insertMasqRule().")
					return nil, err
				}
			}
		}
	}

	log.Debug("Returning our new distributedRouter instance.")
	return dr, nil
}

//Run creates and runs a drouter instance
func Run(opts *DistributedRouterOptions, shutdown <-chan struct{}) error {
	stopChan = shutdown
	dr, err := newDistributedRouter(opts)
	if err != nil {
		return err
	}

	if len(transitNetName) > 0 {
		err = dr.initTransitNet()
		if err != nil {
			return err
		}
	}

	learnNetwork := make(chan *network)
	if aggressive {
		//syncNetworks()
		go func() {
			//we want to sync immediatly the first time
			timer := time.NewTimer(0)
			for {
				select {
				case _ = <-shutdown:
					timer.Stop()
					return
				case _ = <-timer.C:
					log.Debug("Syncing networks from docker.")

					//get all networks from docker
					dockerNets, err := dockerClient.NetworkList(context.Background(), dockertypes.NetworkListOptions{Filters: dockerfilters.NewArgs()})
					if err != nil {
						logError("Error getting network list", err)
					}

					//learn the docker networks
					for _, dn := range dockerNets {
						if dn.ID == transitNetID {
							continue
						}

						//do we know about this network already?
						drn, ok := dr.networks[dn.ID]
						if !ok {
							drn = newNetwork(&dn)
							learnNetwork <- drn
						}

						if !drn.isConnected() && drn.isDRouter() && !drn.adminDown {
							connectWG.Add(1)
							go func() {
								defer connectWG.Done()
								drn.connect()
							}()
						}
					}
					connectWG.Wait()
					//use timer instead of ticker to guarantee a 5 second delay from end to start
					timer = time.NewTimer(5 * time.Second)
				}
			}
		}()
	}

	dockerEvent := make(chan dockerevents.Message)
	defer close(dockerEvent)
	dockerEventErr := events.Monitor(context.Background(), dockerClient, dockertypes.EventsOptions{}, func(event dockerevents.Message) {
		dockerEvent <- event
		return
	})

	routeEventDone := make(chan struct{})
	defer close(routeEventDone)
	routeEvent := make(chan netlink.RouteUpdate)
	defer close(routeEvent)
	err = netlink.RouteSubscribe(routeEvent, routeEventDone)
	if err != nil {
		logError("Failed to subscribe to drouter routing table.", err)
		return err
	}

	// on startup generate connect events for every container
	go func() {
		dockerNets, err := dockerClient.NetworkList(context.Background(), dockertypes.NetworkListOptions{Filters: dockerfilters.NewArgs()})
		if err != nil {
			logError("Error getting network list", err)
			return
		}
		for _, dn := range dockerNets {
			for c, _ := range dn.Containers {
				dockerEvent <- dockerevents.Message{
					Type: "network",
					Actor: dockerevents.Actor{
						ID: dn.ID,
						Attributes: map[string]string{
							"container": c,
						},
					},
				}
			}
		}
	}()

Main:
	for {
		select {
		case drn := <-learnNetwork:
			_, ok := dr.networks[drn.ID]
			if !ok {
				dr.networks[drn.ID] = drn
			}
		case e := <-dockerEvent:
			err = dr.processDockerEvent(e, learnNetwork)
			if err != nil {
				log.WithFields(log.Fields{
					"Event": e,
					"Error": err,
				}).Error("Failed to process docker event")
			}
		case r := <-routeEvent:
			if r.Type != syscall.RTM_NEWROUTE {
				break
			}
			err = dr.processRouteAddEvent(&r)
			if err != nil {
				log.WithFields(log.Fields{
					"RouteEvent": r,
					"Error":      err,
				}).Error("Failed to process route add event")
			}
		case e := <-dockerEventErr:
			logError("Error recieved from Docker Event Subscription.", e)
		case _ = <-shutdown:
			log.Debug("Shutdown request recieved")
			break Main
		}
	}

	log.Info("Cleaning Up")
	connectWG.Wait()

	var disconnectWG sync.WaitGroup
	//leave all connected networks
	for _, drn := range dr.networks {
		if !drn.isConnected() {
			continue
		}
		disconnectWG.Add(1)
		go func(drn *network) {
			defer disconnectWG.Done()
			drn.disconnect()
		}(drn)
	}

	disconnectWait := make(chan struct{})
	go func() {
		defer close(disconnectWait)
		disconnectWG.Wait()
	}()

Done:
	for {
		select {
		case _ = <-disconnectWait:
			log.Debug("Finished all network disconnects.")
			break Done
		}
	}

	//delete the p2p link
	if hostShortcut {
		err := p2p.remove()
		if err != nil {
			logError("Failed to remove p2p link.", err)
			return err
		}
	}

	return nil
}

//sets the default route to distributedRouter.defaultRoute
func (dr *distributedRouter) setDefaultRoute() error {
	if dr.defaultRoute.Equal(net.IP{}) {
		return nil
	}

	//remove all incorrect default routes
	routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
	if err != nil {
		return err
	}
	for _, r := range routes {
		if r.Dst != nil {
			continue
		}

		//test that inteded default is already present, don't remove if so
		if r.Gw.Equal(dr.defaultRoute) {
			return nil
		}

		log.Debugf("Remove default route thru: %v", r.Gw)
		err = netlink.RouteDel(&r)
		if err != nil {
			return err
		}
	}

	gwPaths, err := netlink.RouteGet(dr.defaultRoute)
	if err != nil {
		return err
	}

	for _, r := range gwPaths {
		if r.Gw != nil {
			continue
		}
		return netlink.RouteAdd(&netlink.Route{
			LinkIndex: r.LinkIndex,
			Gw:        dr.defaultRoute,
		})
	}
	return nil
}

// Watch for container events
func (dr *distributedRouter) processDockerEvent(event dockerevents.Message, learn chan *network) error {
	// we currently only care about network events
	if event.Type != "network" {
		return nil
	}

	// have we learned this network?
	drn, ok := dr.networks[event.Actor.ID]
	if !ok {
		//inspect network
		nr, err := dockerClient.NetworkInspect(context.Background(), event.Actor.ID)
		if err != nil {
			log.Errorf("Failed to inspect network at: %v", event.Actor.ID)
			return err
		}
		drn = newNetwork(&nr)

		learn <- drn
	}

	//we dont' manage this network
	if !drn.isDRouter() {
		return nil
	}

	if event.Actor.Attributes["container"] == selfContainerID {
		switch event.Action {
		case "connect":
			return drn.connectEvent()
		case "disconnect":
			return drn.disconnectEvent()
		default:
			return nil
		}
	}

	c, err := newContainerFromID(event.Actor.Attributes["container"])
	if err != nil {
		return err
	}

	switch event.Action {
	case "connect":
		return c.connectEvent(drn)
	case "disconnect":
		return c.disconnectEvent(drn)
	default:
		//we don't handle whatever action this is (yet?)
		return nil
	}
}

func (dr *distributedRouter) processRouteAddEvent(ru *netlink.RouteUpdate) error {
	if ru.Table == 255 {
		// We don't want entries from the local routing table
		// http://linux-ip.net/html/routing-tables.html
		return nil
	}
	if ru.Src.IsLoopback() {
		return nil
	}
	if ru.Dst == nil {
		//we manage default routes separately
		//TODO: Add logic to fix default route since it was changed by a drn.connect()
		return nil
	}
	if ru.Dst.IP.IsLoopback() {
		return nil
	}
	if ru.Src.IsLinkLocalUnicast() {
		return nil
	}
	if ru.Dst.IP.IsLinkLocalUnicast() {
		return nil
	}
	if ru.Dst.IP.IsInterfaceLocalMulticast() {
		return nil
	}
	if ru.Dst.IP.IsLinkLocalMulticast() {
		return nil
	}

	coveredByStatic := subnetCoveredByStatic(ru.Dst)

	if hostShortcut && !coveredByStatic {
		p2p.addHostRoute(ru.Dst)
	}

	modifyRoute(ru.Dst, nil, ADD_ROUTE)

	return nil
}

func (dr *distributedRouter) initTransitNet() error {
	nr, err := dockerClient.NetworkInspect(context.Background(), transitNetName)
	if err != nil {
		log.Errorf("Failed to inspect network for transit net: %v", transitNetName)
		return err
	}

	dr.networks[nr.ID] = newNetwork(&nr)

	transitNetID = nr.ID

	//if transit net has a gateway, make it drouter's default route
	if len(nr.Options["gateway"]) > 0 && !hostGateway {
		dr.defaultRoute = net.ParseIP(nr.Options["gateway"])
		log.Debugf("Gateway option detected on transit net as: %v", dr.defaultRoute)
	}
	dr.networks[nr.ID].connect()
	if err != nil {
		log.Errorf("Failed to connect to transit net: %v", nr.Name)
		return err
	}

	return nil
}
<commit_msg>move container initial connect<commit_after>package drouter

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	dockerclient "github.com/docker/engine-api/client"
	dockertypes "github.com/docker/engine-api/types"
	dockerevents "github.com/docker/engine-api/types/events"
	dockerfilters "github.com/docker/engine-api/types/filters"
	"github.com/vdemeester/docker-events"
	"github.com/vishvananda/netlink"
	"golang.org/x/net/context"
	"net"
	"os"
	"sync"
	"syscall"
	"time"
)

var (
	//cli options
	ipOffset         int
	aggressive       bool
	hostShortcut     bool
	containerGateway bool
	hostGateway      bool
	masquerade       bool
	p2p              *p2pNetwork
	staticRoutes     []*net.IPNet
	transitNetName   string
	selfContainerID  string
	instanceName     string

	//other globals
	dockerClient *dockerclient.Client
	transitNetID string
	connectWG    sync.WaitGroup
	stopChan     <-chan struct{}
)

//DistributedRouterOptions Options for our DistributedRouter instance
type DistributedRouterOptions struct {
	IPOffset         int
	Aggressive       bool
	HostShortcut     bool
	ContainerGateway bool
	HostGateway      bool
	Masquerade       bool
	P2PAddr          string
	StaticRoutes     []string
	TransitNet       string
	InstanceName     string
}

type distributedRouter struct {
	networks     map[string]*network
	defaultRoute net.IP
}

func newDistributedRouter(options *DistributedRouterOptions) (*distributedRouter, error) {
	var err error
	//set global vars from options
	ipOffset = options.IPOffset
	aggressive = options.Aggressive
	hostShortcut = options.HostShortcut
	containerGateway = options.ContainerGateway
	hostGateway = options.HostGateway
	masquerade = options.Masquerade
	instanceName = options.InstanceName
	//staticRoutes
	for _, sr := range options.StaticRoutes {
		_, cidr, err := net.ParseCIDR(sr)
		if err != nil {
			log.WithFields(log.Fields{
				"Subnet": sr,
				"Error":  err,
			}).Error("Failed to parse static route subnet.")
			continue
		}
		staticRoutes = append(staticRoutes, cidr)
	}

	transitNetName = options.TransitNet

	if !aggressive && len(transitNetName) == 0 {
		log.Warn("Detected --no-aggressive and --transit-net was not found. This router may not be able to route to networks on other hosts")
	}

	//get the pid of drouter
	pid := os.Getpid()
	if pid == 1 {
		return &distributedRouter{}, fmt.Errorf("Running as pid 1. Running with --pid=host required.")
	}

	//get docker client
	defaultHeaders := map[string]string{"User-Agent": "engine-api-cli-1.0"}
	dockerClient, err = dockerclient.NewClient("unix:///var/run/docker.sock", "v1.23", nil, defaultHeaders)
	if err != nil {
		logError("Error connecting to docker socket", err)
		return &distributedRouter{}, err
	}

	//process options for assumptions and validity
	if masquerade {
		log.Debug("Detected --masquerade. Assuming --host-gateway and --container-gateway and --host-shortcut.")
		hostShortcut = true
		containerGateway = true
		hostGateway = true
	} else {
		if hostGateway {
			log.Debug("Detected --host-gateway. Assuming --host-shortcut.")
			hostShortcut = true
		}
	}

	sc, err := getSelfContainer()
	if err != nil {
		log.Error("Failed to getSelfContainer(). I am running in a container, right?.")
		return nil, err
	}
	selfContainerID = sc.ID

	//disconnect from all initial networks
	log.Debug("Leaving all connected currently networks.")
	for name, settings := range sc.NetworkSettings.Networks {
		log.WithFields(log.Fields{
			"Network": name,
		}).Debug("Leaving network.")
		err = dockerClient.NetworkDisconnect(context.Background(), settings.NetworkID, selfContainerID, true)
		if err != nil {
			log.WithFields(log.Fields{
				"Network": name,
				"Error":   err,
			}).Error("Failed to leave network.")
			continue
		}
	}

	//create our distributedRouter object
	dr := &distributedRouter{
		networks: make(map[string]*network),
	}

	//initial setup
	if hostShortcut {
		var err error
		log.Debug("--host-shortcut detected, making P2P link.")
		p2p, err = newP2PNetwork(options.P2PAddr)
		if err != nil {
			log.Error("Failed to create the point to point link.")
			return nil, err
		}

		if hostGateway {
			dr.defaultRoute = p2p.hostIP
			err = dr.setDefaultRoute()
			if err != nil {
				log.Error("--host-gateway=true and unable to set default route to host's p2p address.")
				return nil, err
			}
			if masquerade {
				log.Debug("--masquerade detected, inserting masquerade rule.")
				if err := insertMasqRule(); err != nil {
					log.Error("Failed to insertMasqRule().")
					return nil, err
				}
			}
		}
	}

	log.Debug("Returning our new distributedRouter instance.")
	return dr, nil
}

//Run creates and runs a drouter instance
func Run(opts *DistributedRouterOptions, shutdown <-chan struct{}) error {
	stopChan = shutdown
	dr, err := newDistributedRouter(opts)
	if err != nil {
		return err
	}

	if len(transitNetName) > 0 {
		err = dr.initTransitNet()
		if err != nil {
			return err
		}
	}

	learnNetwork := make(chan *network)
	if aggressive {
		//syncNetworks()
		go func() {
			//we want to sync immediatly the first time
			timer := time.NewTimer(0)
			for {
				select {
				case _ = <-shutdown:
					timer.Stop()
					return
				case _ = <-timer.C:
					log.Debug("Syncing networks from docker.")

					//get all networks from docker
					dockerNets, err := dockerClient.NetworkList(context.Background(), dockertypes.NetworkListOptions{Filters: dockerfilters.NewArgs()})
					if err != nil {
						logError("Error getting network list", err)
					}

					//learn the docker networks
					for _, dn := range dockerNets {
						if dn.ID == transitNetID {
							continue
						}

						//do we know about this network already?
						drn, ok := dr.networks[dn.ID]
						if !ok {
							drn = newNetwork(&dn)
							learnNetwork <- drn
						}

						if !drn.isConnected() && drn.isDRouter() && !drn.adminDown {
							connectWG.Add(1)
							go func() {
								defer connectWG.Done()
								drn.connect()
							}()
						}
					}
					connectWG.Wait()
					//use timer instead of ticker to guarantee a 5 second delay from end to start
					timer = time.NewTimer(5 * time.Second)
				}
			}
		}()
	}

	dockerEvent := make(chan dockerevents.Message)
	defer close(dockerEvent)
	// on startup generate connect events for every container
	dockerNets, err := dockerClient.NetworkList(context.Background(), dockertypes.NetworkListOptions{Filters: dockerfilters.NewArgs()})
	if err != nil {
		logError("Error getting network list", err)
		return
	}
	for _, dn := range dockerNets {
		for c, _ := range dn.Containers {
			go func() {
				dockerEvent <- dockerevents.Message{
					Type:   "network",
					Action: "connect",
					Actor: dockerevents.Actor{
						ID: dn.ID,
						Attributes: map[string]string{
							"container": c,
						},
					},
				}
			}()
		}
	}
	// subscribe to real docker events
	dockerEventErr := events.Monitor(context.Background(), dockerClient, dockertypes.EventsOptions{}, func(event dockerevents.Message) {
		dockerEvent <- event
		return
	})

	routeEventDone := make(chan struct{})
	defer close(routeEventDone)
	routeEvent := make(chan netlink.RouteUpdate)
	defer close(routeEvent)
	err = netlink.RouteSubscribe(routeEvent, routeEventDone)
	if err != nil {
		logError("Failed to subscribe to drouter routing table.", err)
		return err
	}

Main:
	for {
		select {
		case drn := <-learnNetwork:
			_, ok := dr.networks[drn.ID]
			if !ok {
				dr.networks[drn.ID] = drn
			}
		case e := <-dockerEvent:
			err = dr.processDockerEvent(e, learnNetwork)
			if err != nil {
				log.WithFields(log.Fields{
					"Event": e,
					"Error": err,
				}).Error("Failed to process docker event")
			}
		case r := <-routeEvent:
			if r.Type != syscall.RTM_NEWROUTE {
				break
			}
			err = dr.processRouteAddEvent(&r)
			if err != nil {
				log.WithFields(log.Fields{
					"RouteEvent": r,
					"Error":      err,
				}).Error("Failed to process route add event")
			}
		case e := <-dockerEventErr:
			logError("Error recieved from Docker Event Subscription.", e)
		case _ = <-shutdown:
			log.Debug("Shutdown request recieved")
			break Main
		}
	}

	log.Info("Cleaning Up")
	connectWG.Wait()

	var disconnectWG sync.WaitGroup
	//leave all connected networks
	for _, drn := range dr.networks {
		if !drn.isConnected() {
			continue
		}
		disconnectWG.Add(1)
		go func(drn *network) {
			defer disconnectWG.Done()
			drn.disconnect()
		}(drn)
	}

	disconnectWait := make(chan struct{})
	go func() {
		defer close(disconnectWait)
		disconnectWG.Wait()
	}()

Done:
	for {
		select {
		case _ = <-disconnectWait:
			log.Debug("Finished all network disconnects.")
			break Done
		}
	}

	//delete the p2p link
	if hostShortcut {
		err := p2p.remove()
		if err != nil {
			logError("Failed to remove p2p link.", err)
			return err
		}
	}

	return nil
}

//sets the default route to distributedRouter.defaultRoute
func (dr *distributedRouter) setDefaultRoute() error {
	if dr.defaultRoute.Equal(net.IP{}) {
		return nil
	}

	//remove all incorrect default routes
	routes, err := netlink.RouteList(nil, netlink.FAMILY_V4)
	if err != nil {
		return err
	}
	for _, r := range routes {
		if r.Dst != nil {
			continue
		}

		//test that inteded default is already present, don't remove if so
		if r.Gw.Equal(dr.defaultRoute) {
			return nil
		}

		log.Debugf("Remove default route thru: %v", r.Gw)
		err = netlink.RouteDel(&r)
		if err != nil {
			return err
		}
	}

	gwPaths, err := netlink.RouteGet(dr.defaultRoute)
	if err != nil {
		return err
	}

	for _, r := range gwPaths {
		if r.Gw != nil {
			continue
		}
		return netlink.RouteAdd(&netlink.Route{
			LinkIndex: r.LinkIndex,
			Gw:        dr.defaultRoute,
		})
	}
	return nil
}

// Watch for container events
func (dr *distributedRouter) processDockerEvent(event dockerevents.Message, learn chan *network) error {
	// we currently only care about network events
	if event.Type != "network" {
		return nil
	}

	// have we learned this network?
	drn, ok := dr.networks[event.Actor.ID]
	if !ok {
		//inspect network
		nr, err := dockerClient.NetworkInspect(context.Background(), event.Actor.ID)
		if err != nil {
			log.Errorf("Failed to inspect network at: %v", event.Actor.ID)
			return err
		}
		drn = newNetwork(&nr)

		learn <- drn
	}

	//we dont' manage this network
	if !drn.isDRouter() {
		return nil
	}

	if event.Actor.Attributes["container"] == selfContainerID {
		switch event.Action {
		case "connect":
			return drn.connectEvent()
		case "disconnect":
			return drn.disconnectEvent()
		default:
			return nil
		}
	}

	c, err := newContainerFromID(event.Actor.Attributes["container"])
	if err != nil {
		return err
	}

	switch event.Action {
	case "connect":
		return c.connectEvent(drn)
	case "disconnect":
		return c.disconnectEvent(drn)
	default:
		//we don't handle whatever action this is (yet?)
		return nil
	}
}

func (dr *distributedRouter) processRouteAddEvent(ru *netlink.RouteUpdate) error {
	if ru.Table == 255 {
		// We don't want entries from the local routing table
		// http://linux-ip.net/html/routing-tables.html
		return nil
	}
	if ru.Src.IsLoopback() {
		return nil
	}
	if ru.Dst == nil {
		//we manage default routes separately
		//TODO: Add logic to fix default route since it was changed by a drn.connect()
		return nil
	}
	if ru.Dst.IP.IsLoopback() {
		return nil
	}
	if ru.Src.IsLinkLocalUnicast() {
		return nil
	}
	if ru.Dst.IP.IsLinkLocalUnicast() {
		return nil
	}
	if ru.Dst.IP.IsInterfaceLocalMulticast() {
		return nil
	}
	if ru.Dst.IP.IsLinkLocalMulticast() {
		return nil
	}

	coveredByStatic := subnetCoveredByStatic(ru.Dst)

	if hostShortcut && !coveredByStatic {
		p2p.addHostRoute(ru.Dst)
	}

	modifyRoute(ru.Dst, nil, ADD_ROUTE)

	return nil
}

func (dr *distributedRouter) initTransitNet() error {
	nr, err := dockerClient.NetworkInspect(context.Background(), transitNetName)
	if err != nil {
		log.Errorf("Failed to inspect network for transit net: %v", transitNetName)
		return err
	}

	dr.networks[nr.ID] = newNetwork(&nr)

	transitNetID = nr.ID

	//if transit net has a gateway, make it drouter's default route
	if len(nr.Options["gateway"]) > 0 && !hostGateway {
		dr.defaultRoute = net.ParseIP(nr.Options["gateway"])
		log.Debugf("Gateway option detected on transit net as: %v", dr.defaultRoute)
	}
	dr.networks[nr.ID].connect()
	if err != nil {
		log.Errorf("Failed to connect to transit net: %v", nr.Name)
		return err
	}

	return nil
}
<|endoftext|>


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

package dns

import (
	"testing"
)

func TestCompareDomainName(t *testing.T) {
	s1 := "www.miek.nl."
	s2 := "miek.nl."
	s3 := "www.bla.nl."
	s4 := "nl.www.bla."
	s5 := "nl"

	if CompareDomainName(s1, s2) != 2 {
		t.Logf("%s with %s should be %d", s1, s2, 2)
		t.Fail()
	}
	if CompareDomainName(s1, s3) != 1 {
		t.Logf("%s with %s should be %d", s1, s3, 1)
		t.Fail()
	}
	if CompareDomainName(s3, s4) != 0 {
		t.Logf("%s with %s should be %d", s3, s4, 0)
		t.Fail()
	}
	if CompareDomainName(s1, s5) != 1 {
		t.Logf("%s with %s should be %d", s1, s5, 1)
		t.Fail()
	}
	if CompareDomainName(s1, ".") != 0 {
		t.Logf("%s with %s should be %d", s1, s5, 0)
		t.Fail()
	}
	if CompareDomainName(".", ".") != 0 {
		t.Logf("%s with %s should be %d", ".", ".", 0)
		t.Fail()
	}
}

func TestSplit(t *testing.T) {
	splitter := map[string]int{
		"www.miek.nl.":   3,
		"www.miek.nl":    3,
		`www\.miek.nl.`:  2,
		`www\\.miek.nl.`: 3,
		".":              0,
		"nl.":            1,
		"nl":             1,
		"com.":           1,
		".com.":          2,
	}
	for s, i := range splitter {
		if x := len(Split(s)); x != i {
			t.Logf("Labels should be %d, got %d: %s %v\n", i, x, s, Split(s))
			t.Fail()
		} else {
			t.Logf("%s %v\n", s, Split(s))
		}
	}
}

func TestCountLabel(t *testing.T) {
	labels := map[string]int{
		"miek.nl":       2,
		".":             0,
		"www.miek.nl.":  3,
		"www.miek.nl":   3,
		"www..miek.nl":  4,
		`www\.miek.nl`:  2,
		`www\\.miek.nl`: 3,
	}
	for owner, lab := range labels {
		if l := CountLabel(owner); l != lab {
			t.Logf("%s should have %d labels, got %d\n", owner, lab, l)
			t.Fail()
		}
	}
}

func BenchmarkSplitLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		SplitLabels("www.example.com")
	}
}

func BenchmarkLenLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		LenLabels("www.example.com")
	}
}

func BenchmarkCompareLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CompareLabels("www.example.com", "aa.example.com")
	}
}
<commit_msg>Use new names<commit_after>// Copyright 2011 Miek Gieben. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dns

import (
	"testing"
)

func TestCompareDomainName(t *testing.T) {
	s1 := "www.miek.nl."
	s2 := "miek.nl."
	s3 := "www.bla.nl."
	s4 := "nl.www.bla."
	s5 := "nl"

	if CompareDomainName(s1, s2) != 2 {
		t.Logf("%s with %s should be %d", s1, s2, 2)
		t.Fail()
	}
	if CompareDomainName(s1, s3) != 1 {
		t.Logf("%s with %s should be %d", s1, s3, 1)
		t.Fail()
	}
	if CompareDomainName(s3, s4) != 0 {
		t.Logf("%s with %s should be %d", s3, s4, 0)
		t.Fail()
	}
	if CompareDomainName(s1, s5) != 1 {
		t.Logf("%s with %s should be %d", s1, s5, 1)
		t.Fail()
	}
	if CompareDomainName(s1, ".") != 0 {
		t.Logf("%s with %s should be %d", s1, s5, 0)
		t.Fail()
	}
	if CompareDomainName(".", ".") != 0 {
		t.Logf("%s with %s should be %d", ".", ".", 0)
		t.Fail()
	}
}

func TestSplit(t *testing.T) {
	splitter := map[string]int{
		"www.miek.nl.":   3,
		"www.miek.nl":    3,
		`www\.miek.nl.`:  2,
		`www\\.miek.nl.`: 3,
		".":              0,
		"nl.":            1,
		"nl":             1,
		"com.":           1,
		".com.":          2,
	}
	for s, i := range splitter {
		if x := len(Split(s)); x != i {
			t.Logf("Labels should be %d, got %d: %s %v\n", i, x, s, Split(s))
			t.Fail()
		} else {
			t.Logf("%s %v\n", s, Split(s))
		}
	}
}

func TestCountLabel(t *testing.T) {
	labels := map[string]int{
		"miek.nl":       2,
		".":             0,
		"www.miek.nl.":  3,
		"www.miek.nl":   3,
		"www..miek.nl":  4,
		`www\.miek.nl`:  2,
		`www\\.miek.nl`: 3,
	}
	for owner, lab := range labels {
		if l := CountLabel(owner); l != lab {
			t.Logf("%s should have %d labels, got %d\n", owner, lab, l)
			t.Fail()
		}
	}
}

func BenchmarkSplitLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Split("www.example.com")
	}
}

func BenchmarkLenLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CountLabel("www.example.com")
	}
}

func BenchmarkCompareLabels(b *testing.B) {
	for i := 0; i < b.N; i++ {
		CompareDomainName("www.example.com", "aa.example.com")
	}
}
<|endoftext|>


<commit_before>package less

import (
	"os/exec"
	"bytes"
)

func Compile(path string, w *bytes.Buffer)error {
	dateCmd := exec.Command("lessc", path)
	dateOut, err := dateCmd.Output()
	if err != nil {
		return err
	}
	_, err = w.Write(dateOut)
	return err
}<commit_msg>update<commit_after>package less

import (
	"os/exec"
	"bytes"
	"fmt"
)

func Compile(path string, w *bytes.Buffer, paths...string)error {
	var cmd *exec.Cmd
	l := len(paths)
	if paths != nil && l > 0 {
		pathBuffer := new(bytes.Buffer)
		for i, p := range paths {
			pathBuffer.WriteString(p)
			if i >= 0 && l-1 != i {
				pathBuffer.WriteString(":")
			}
		}
		cmd = exec.Command("lessc", path, "--paths", pathBuffer.String())
	} else {
		cmd = exec.Command("lessc", path)
	}
	cmdOut, err := cmd.Output()
	if err != nil {
		fmt.Println(err)
		fmt.Println(cmdOut)
		return err
	}
	_, err = w.Write(cmdOut)
	return err
}<|endoftext|>


<commit_before>package robots

import (
	"fmt"
	"os"
	"strings"

	"github.com/gistia/slackbot/mavenlink"
	"github.com/gistia/slackbot/models"
	"github.com/gistia/slackbot/robots"
	"github.com/gistia/slackbot/utils"
)

type bot struct{}

func init() {
	s := &bot{}
	robots.RegisterRobot("mvn", s)
}

func (r bot) Run(p *robots.Payload) (slashCommandImmediateReturn string) {
	go r.DeferredAction(p)
	return ""
}

func (r bot) sendResponse(p *robots.Payload, s string) {
	r.sendWithAttachment(p, s, nil)
}

func (r bot) sendWithAttachment(p *robots.Payload, s string, atts []robots.Attachment) {
	response := &robots.IncomingWebhook{
		Domain:      p.TeamDomain,
		Channel:     p.ChannelID,
		Username:    "Mavenlink Bot",
		Text:        fmt.Sprintf("@%s: %s", p.UserName, s),
		IconEmoji:   ":chart_with_upwards_trend:",
		UnfurlLinks: true,
		Parse:       robots.ParseStyleFull,
		Attachments: atts,
	}

	response.Send()
}

func (r bot) DeferredAction(p *robots.Payload) {
	text := strings.TrimSpace(p.Text)
	if text == "" {
		r.sendResponse(p, "Please use ! mvn <command>")
		return
	}

	parts := strings.Split(p.Text, " ")
	cmd := parts[0]

	if cmd == "projects" {
		term := ""
		if len(parts) > 1 {
			term = parts[1]
		}
		r.sendProjects(p, term)
		return
	}

	if cmd == "stories" {
		if len(parts) < 2 {
			r.sendResponse(p, "Please use ! mvn stories <id|term>")
			return
		}

		r.sendStories(p, parts[1], "")
	}

	msg := fmt.Sprintf("Running mavenlink command: %s", text)
	r.sendResponse(p, msg)
}

func conn() *mavenlink.Mavenlink {
	token := os.Getenv("MAVENLINK_TOKEN")
	con := mavenlink.NewMavenlink(token, false)
	return con
}

func (r bot) sendProjects(payload *robots.Payload, term string) {
	var ps []models.Project
	var err error

	go r.sendResponse(payload, "Retrieving mavenlink projects...\n")

	mvn := conn()
	s := "Projects"

	if len(term) > 0 {
		fmt.Printf("Retrieving projects with term \"%s\"...\n\n", term)
		s += fmt.Sprintf(" matching '%s':\n", term)
		ps, err = mvn.SearchProject(term)
	} else {
		s += ":\n"
		fmt.Println("Retrieving projects...\n")
		ps, err = mvn.Projects()
	}

	if err != nil {
		fmt.Println(err.Error())
		return
	}

	r.sendResponse(payload, s+projectTable(ps))
}

func (r bot) sendStories(payload *robots.Payload, term string, parent string) {
	mvn := conn()

	var p models.Project
	var stories []models.Story
	var err error

	if term != "" {
		ps, err := getProject(term)

		if err != nil {
			msg := fmt.Sprintf("Error retrieving project for \"%s\": %s\n", term, err.Error())
			r.sendResponse(payload, msg)
			return
		}

		if len(ps) < 1 {
			msg := fmt.Sprintf("No projects matched \"%s\"\n", term)
			r.sendResponse(payload, msg)
			return
		} else if len(ps) > 1 {
			s := fmt.Sprintf("More than one project matched \"%s\":\n\n", term)
			r.sendResponse(payload, s+projectTable(ps))
			return
		} else {
			p = ps[0]
		}
	}

	if parent == "" {
		stories, err = mvn.Stories(p.Id)
		if err != nil {
			msg := fmt.Sprintf("Error retrieving stories for project \"%s - %s\": %s\n",
				p.Id, p.Title, err.Error())
			r.sendResponse(payload, msg)
			return
		}
		r.storyTable(payload, stories)
		return
	}

	if utils.IsNumber(parent) {
		stories, err = mvn.ChildStories(parent)
		if err != nil {
			fmt.Printf("Error child stories for \"%s\": %s\n", parent, err.Error())
			return
		}
		r.storyTable(payload, stories)
		return
	}

	r.sendResponse(payload, "Not implemented")
}

func getProject(term string) ([]models.Project, error) {
	mvn := conn()

	if utils.IsNumber(term) {
		p, err := mvn.Project(term)
		if err != nil {
			return nil, err
		}

		return []models.Project{*p}, nil
	}

	ps, err := mvn.SearchProject(term)
	if err != nil {
		return nil, err
	}

	return ps, nil
}

func (r bot) Description() (description string) {
	return "Mavenlink bot\n\tUsage: ! mvn <command>\n"
}

func projectTable(ps []models.Project) string {
	s := ""

	for _, p := range ps {
		s += fmt.Sprintf("%s - %s\n", p.Id, p.Title)
	}

	return s
}

func formatHour(h int64) string {
	if h == 0 {
		return ""
	}

	v := float64(h) / 60
	return fmt.Sprintf("%.2f", v)
}

func (r bot) storyTable(payload *robots.Payload, stories []models.Story) {
	for _, s := range stories {
		atts := []robots.Attachment{}
		a := robots.Attachment{}
		a.Color = "#7CD197"
		a.Fallback = fmt.Sprintf("%s - *%s* %s (%s)\n",
			strings.Title(s.StoryType), s.Id, s.Title, s.State)
		a.Title = fmt.Sprintf("Task #%s - %s\n", s.Id, s.Title)
		a.TitleLink = fmt.Sprintf(
			"https://app.mavenlink.com/workspaces/%s/#tracker/%s",
			s.WorkspaceId, s.Id)
		a.Text = strings.Title(s.StoryType)
		atts = append(atts, a)

		r.sendWithAttachment(payload, "", atts)
	}
}
<commit_msg>Added parent support<commit_after>package robots

import (
	"fmt"
	"os"
	"strings"

	"github.com/gistia/slackbot/mavenlink"
	"github.com/gistia/slackbot/models"
	"github.com/gistia/slackbot/robots"
	"github.com/gistia/slackbot/utils"
)

type bot struct{}

func init() {
	s := &bot{}
	robots.RegisterRobot("mvn", s)
}

func (r bot) Run(p *robots.Payload) (slashCommandImmediateReturn string) {
	go r.DeferredAction(p)
	return ""
}

func (r bot) sendResponse(p *robots.Payload, s string) {
	r.sendWithAttachment(p, s, nil)
}

func (r bot) sendWithAttachment(p *robots.Payload, s string, atts []robots.Attachment) {
	response := &robots.IncomingWebhook{
		Domain:      p.TeamDomain,
		Channel:     p.ChannelID,
		Username:    "Mavenlink Bot",
		Text:        fmt.Sprintf("@%s: %s", p.UserName, s),
		IconEmoji:   ":chart_with_upwards_trend:",
		UnfurlLinks: true,
		Parse:       robots.ParseStyleFull,
		Attachments: atts,
	}

	response.Send()
}

func (r bot) DeferredAction(p *robots.Payload) {
	text := strings.TrimSpace(p.Text)
	if text == "" {
		r.sendResponse(p, "Please use ! mvn <command>")
		return
	}

	msg := fmt.Sprintf("Running mavenlink command: %s", text)
	go r.sendResponse(p, msg)

	parts := strings.Split(p.Text, " ")
	cmd := parts[0]

	if cmd == "projects" {
		term := ""
		if len(parts) > 1 {
			term = parts[1]
		}
		r.sendProjects(p, term)
		return
	}

	if cmd == "stories" {
		if len(parts) < 2 {
			r.sendResponse(p, "Please use ! mvn stories <id|term> [<parent>]")
			return
		}

		parent := ""
		if len(parts) > 2 {
			parent = parts[2]
		}

		r.sendStories(p, parts[1], parent)
	}
}

func conn() *mavenlink.Mavenlink {
	token := os.Getenv("MAVENLINK_TOKEN")
	con := mavenlink.NewMavenlink(token, false)
	return con
}

func (r bot) sendProjects(payload *robots.Payload, term string) {
	var ps []models.Project
	var err error

	go r.sendResponse(payload, "Retrieving mavenlink projects...\n")

	mvn := conn()
	s := "Projects"

	if len(term) > 0 {
		fmt.Printf("Retrieving projects with term \"%s\"...\n\n", term)
		s += fmt.Sprintf(" matching '%s':\n", term)
		ps, err = mvn.SearchProject(term)
	} else {
		s += ":\n"
		fmt.Println("Retrieving projects...\n")
		ps, err = mvn.Projects()
	}

	if err != nil {
		fmt.Println(err.Error())
		return
	}

	r.sendResponse(payload, s+projectTable(ps))
}

func (r bot) sendStories(payload *robots.Payload, term string, parent string) {
	mvn := conn()

	var p models.Project
	var stories []models.Story
	var err error

	if term != "" {
		ps, err := getProject(term)

		if err != nil {
			msg := fmt.Sprintf("Error retrieving project for \"%s\": %s\n", term, err.Error())
			r.sendResponse(payload, msg)
			return
		}

		if len(ps) < 1 {
			msg := fmt.Sprintf("No projects matched \"%s\"\n", term)
			r.sendResponse(payload, msg)
			return
		} else if len(ps) > 1 {
			s := fmt.Sprintf("More than one project matched \"%s\":\n\n", term)
			r.sendResponse(payload, s+projectTable(ps))
			return
		} else {
			p = ps[0]
		}
	}

	if parent == "" {
		stories, err = mvn.Stories(p.Id)
		if err != nil {
			msg := fmt.Sprintf("Error retrieving stories for project \"%s - %s\": %s\n",
				p.Id, p.Title, err.Error())
			r.sendResponse(payload, msg)
			return
		}
		r.storyTable(payload, stories)
		return
	}

	if utils.IsNumber(parent) {
		stories, err = mvn.ChildStories(parent)
		if err != nil {
			fmt.Printf("Error child stories for \"%s\": %s\n", parent, err.Error())
			return
		}
		r.storyTable(payload, stories)
		return
	}

	r.sendResponse(payload, "Not implemented")
}

func getProject(term string) ([]models.Project, error) {
	mvn := conn()

	if utils.IsNumber(term) {
		p, err := mvn.Project(term)
		if err != nil {
			return nil, err
		}

		return []models.Project{*p}, nil
	}

	ps, err := mvn.SearchProject(term)
	if err != nil {
		return nil, err
	}

	return ps, nil
}

func (r bot) Description() (description string) {
	return "Mavenlink bot\n\tUsage: ! mvn <command>\n"
}

func projectTable(ps []models.Project) string {
	s := ""

	for _, p := range ps {
		s += fmt.Sprintf("%s - %s\n", p.Id, p.Title)
	}

	return s
}

func formatHour(h int64) string {
	if h == 0 {
		return ""
	}

	v := float64(h) / 60
	return fmt.Sprintf("%.2f", v)
}

func (r bot) storyTable(payload *robots.Payload, stories []models.Story) {
	for _, s := range stories {
		atts := []robots.Attachment{}
		a := robots.Attachment{}
		a.Color = "#7CD197"
		a.Fallback = fmt.Sprintf("%s - *%s* %s (%s)\n",
			strings.Title(s.StoryType), s.Id, s.Title, s.State)
		a.Title = fmt.Sprintf("Task #%s - %s\n", s.Id, s.Title)
		a.TitleLink = fmt.Sprintf(
			"https://app.mavenlink.com/workspaces/%s/#tracker/%s",
			s.WorkspaceId, s.Id)
		a.Text = strings.Title(s.StoryType)
		atts = append(atts, a)

		r.sendWithAttachment(payload, "", atts)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 hugolib

import (
	"bytes"
	"fmt"
	"github.com/spf13/hugo/template/bundle"
	"strings"
	"unicode"
)

var _ = fmt.Println

type ShortcodeFunc func([]string) string

type Shortcode struct {
	Name string
	Func ShortcodeFunc
}

type ShortcodeWithPage struct {
	Params interface{}
	Page   *Page
}

type Shortcodes map[string]ShortcodeFunc

func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
	posStart := strings.Index(stringToParse, "{{%")
	if posStart > 0 {
		posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
		if posEnd > posStart {
			name, par := SplitParams(stringToParse[posStart+3 : posEnd])
			params := Tokenize(par)
			var data = &ShortcodeWithPage{Params: params, Page: p}
			newString := stringToParse[:posStart] + ShortcodeRender(name, data, t) + ShortcodesHandle(stringToParse[posEnd+3:], p, t)
			return newString
		}
	}
	return stringToParse
}

func StripShortcodes(stringToParse string) string {
	posStart := strings.Index(stringToParse, "{{%")
	if posStart > 0 {
		posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
		if posEnd > posStart {
			newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:])
			return newString
		}
	}
	return stringToParse
}

func Tokenize(in string) interface{} {
	first := strings.Fields(in)
	var final = make([]string, 0)
	var keys = make([]string, 0)
	inQuote := false
	start := 0

	for i, v := range first {
		index := strings.Index(v, "=")

		if !inQuote {
			if index > 1 {
				keys = append(keys, v[:index])
				v = v[index+1:]
			}
		}

		if !strings.HasPrefix(v, "“") && !inQuote {
			final = append(final, v)
		} else if inQuote && strings.HasSuffix(v, "”") && !strings.HasSuffix(v, "\\\"") {
			first[i] = v[:len(v)-7]
			final = append(final, strings.Join(first[start:i+1], " "))
			inQuote = false
		} else if strings.HasPrefix(v, "“") && !inQuote {
			if strings.HasSuffix(v, "”") {
				final = append(final, v[7:len(v)-7])
			} else {
				start = i
				first[i] = v[7:]
				inQuote = true
			}
		}

		// No closing "... just make remainder the final token
		if inQuote && i == len(first) {
			final = append(final, first[start:len(first)]...)
		}
	}

	if len(keys) > 0 {
		var m = make(map[string]string)
		for i, k := range keys {
			m[k] = final[i]
		}

		return m
	}

	return final
}

func SplitParams(in string) (name string, par2 string) {
	i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace)
	if i < 1 {
		return strings.TrimSpace(in), ""
	}

	return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
}

func ShortcodeRender(name string, data *ShortcodeWithPage, t bundle.Template) string {
	buffer := new(bytes.Buffer)
	t.ExecuteTemplate(buffer, "shortcodes/"+name+".html", data)
	return buffer.String()
}
<commit_msg>adding support for shortcodes with opening and closing tags<commit_after>// Copyright © 2013 Steve Francia <spf@spf13.com>.
//
// Licensed under the Simple Public 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://opensource.org/licenses/Simple-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 hugolib

import (
	"bytes"
	"fmt"
	"github.com/spf13/hugo/template/bundle"
	"html/template"
	"strings"
	"unicode"
)

var _ = fmt.Println

type ShortcodeFunc func([]string) string

type Shortcode struct {
	Name string
	Func ShortcodeFunc
}

type ShortcodeWithPage struct {
	Params interface{}
	Inner  template.HTML
	Page   *Page
}

type Shortcodes map[string]ShortcodeFunc

func ShortcodesHandle(stringToParse string, p *Page, t bundle.Template) string {
	leadStart := strings.Index(stringToParse, `{{%`)
	if leadStart >= 0 {
		leadEnd := strings.Index(stringToParse[leadStart:], `%}}`) + leadStart
		if leadEnd > leadStart {
			name, par := SplitParams(stringToParse[leadStart+3 : leadEnd])
			tmpl := GetTemplate(name, t)
			if tmpl == nil {
				return stringToParse
			}
			params := Tokenize(par)
			// Always look for closing tag.
			endStart, endEnd := FindEnd(stringToParse[leadEnd:], name)
			var data = &ShortcodeWithPage{Params: params, Page: p}
			if endStart > 0 {
				s := stringToParse[leadEnd+3 : leadEnd+endStart]
				data.Inner = template.HTML(CleanP(ShortcodesHandle(s, p, t)))
				remainder := CleanP(stringToParse[leadEnd+endEnd:])

				return CleanP(stringToParse[:leadStart]) +
					ShortcodeRender(tmpl, data) +
					CleanP(ShortcodesHandle(remainder, p, t))
			}
			return CleanP(stringToParse[:leadStart]) +
				ShortcodeRender(tmpl, data) +
				CleanP(ShortcodesHandle(stringToParse[leadEnd+3:], p,
					t))
		}
	}
	return stringToParse
}

// Clean up odd behavior when closing tag is on first line
// or opening tag is on the last line due to extra line in markdown file
func CleanP(str string) string {
	if strings.HasSuffix(strings.TrimSpace(str), "
") {
		idx := strings.LastIndex(str, "
")
		str = str[:idx]
	}

	if strings.HasPrefix(strings.TrimSpace(str), "
") {
		str = str[strings.Index(str, "
")+5:]
	}

	return str
}

func FindEnd(str string, name string) (int, int) {
	var endPos int
	var startPos int
	var try []string

	try = append(try, "{{% /"+name+" %}}")
	try = append(try, "{{% /"+name+"%}}")
	try = append(try, "{{%/"+name+"%}}")
	try = append(try, "{{%/"+name+" %}}")

	lowest := len(str)
	for _, x := range try {
		start := strings.Index(str, x)
		if start < lowest && start > 0 {
			startPos = start
			endPos = startPos + len(x)
		}
	}

	return startPos, endPos
}

func GetTemplate(name string, t bundle.Template) *template.Template {
	return t.Lookup("shortcodes/" + name + ".html")
}

func StripShortcodes(stringToParse string) string {
	posStart := strings.Index(stringToParse, "{{%")
	if posStart > 0 {
		posEnd := strings.Index(stringToParse[posStart:], "%}}") + posStart
		if posEnd > posStart {
			newString := stringToParse[:posStart] + StripShortcodes(stringToParse[posEnd+3:])
			return newString
		}
	}
	return stringToParse
}

func Tokenize(in string) interface{} {
	first := strings.Fields(in)
	var final = make([]string, 0)

	// if don't need to parse, don't parse.
	if strings.Index(in, " ") < 0 && strings.Index(in, "=") < 1 {
		return append(final, in)
	}

	var keys = make([]string, 0)
	inQuote := false
	start := 0

	for i, v := range first {
		index := strings.Index(v, "=")

		if !inQuote {
			if index > 1 {
				keys = append(keys, v[:index])
				v = v[index+1:]
			}
		}

		// Adjusted to handle htmlencoded and non htmlencoded input
		if !strings.HasPrefix(v, "“") && !strings.HasPrefix(v, "\"") && !inQuote {
			final = append(final, v)
		} else if inQuote && (strings.HasSuffix(v, "”") ||
			strings.HasSuffix(v, "\"")) && !strings.HasSuffix(v, "\\\"") {
			if strings.HasSuffix(v, "\"") {
				first[i] = v[:len(v)-1]
			} else {
				first[i] = v[:len(v)-7]
			}
			final = append(final, strings.Join(first[start:i+1], " "))
			inQuote = false
		} else if (strings.HasPrefix(v, "“") ||
			strings.HasPrefix(v, "\"")) && !inQuote {
			if strings.HasSuffix(v, "”") || strings.HasSuffix(v,
				"\"") {
				if strings.HasSuffix(v, "\"") {
					if len(v) > 1 {
						final = append(final, v[1:len(v)-1])
					} else {
						final = append(final, "")
					}
				} else {
					final = append(final, v[7:len(v)-7])
				}
			} else {
				start = i
				if strings.HasPrefix(v, "\"") {
					first[i] = v[1:]
				} else {
					first[i] = v[7:]
				}
				inQuote = true
			}
		}

		// No closing "... just make remainder the final token
		if inQuote && i == len(first) {
			final = append(final, first[start:len(first)]...)
		}
	}

	if len(keys) > 0 && (len(keys) != len(final)) {
		panic("keys and final different lengths")
	}

	if len(keys) > 0 {
		var m = make(map[string]string)
		for i, k := range keys {
			m[k] = final[i]
		}

		return m
	}

	return final
}

func SplitParams(in string) (name string, par2 string) {
	i := strings.IndexFunc(strings.TrimSpace(in), unicode.IsSpace)
	if i < 1 {
		return strings.TrimSpace(in), ""
	}

	return strings.TrimSpace(in[:i+1]), strings.TrimSpace(in[i+1:])
}

func ShortcodeRender(tmpl *template.Template, data *ShortcodeWithPage) string {
	buffer := new(bytes.Buffer)
	tmpl.Execute(buffer, data)
	return buffer.String()
}
<|endoftext|>


<commit_before>package client

import (
	"crypto"
	"fmt"
	"io"
	"math"

	tpmpb "github.com/google/go-tpm-tools/proto"
	"github.com/google/go-tpm/tpm2"
)

// NumPCRs is set to the spec minimum of 24, as that's all go-tpm supports.
const NumPCRs = 24

// We hard-code SHA256 as the policy session hash algorithms. Note that this
// differs from the PCR hash algorithm (which selects the bank of PCRs to use)
// and the Public area Name algorithm. We also chose this for compatibility with
// github.com/google/go-tpm/tpm2, as it hardcodes the nameAlg as SHA256 in
// several places. Two constants are used to avoid repeated conversions.
const (
	SessionHashAlg    = crypto.SHA256
	SessionHashAlgTpm = tpm2.AlgSHA256
)

// CertifyHashAlgTpm is the hard-coded algorithm used in certify PCRs.
const CertifyHashAlgTpm = tpm2.AlgSHA256

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// Get a list of selections corresponding to the TPM's implemented PCRs
func implementedPCRs(rw io.ReadWriter) ([]tpm2.PCRSelection, error) {
	caps, moreData, err := tpm2.GetCapability(rw, tpm2.CapabilityPCRs, math.MaxUint32, 0)
	if err != nil {
		return nil, fmt.Errorf("listing implemented PCR banks: %w", err)
	}
	if moreData {
		return nil, fmt.Errorf("extra data from GetCapability")
	}
	sels := make([]tpm2.PCRSelection, len(caps))
	for i, cap := range caps {
		sel, ok := cap.(tpm2.PCRSelection)
		if !ok {
			return nil, fmt.Errorf("unexpected data from GetCapability")
		}
		sels[i] = sel
	}
	return sels, nil
}

// ReadPCRs fetches all the PCR values specified in sel, making multiple calls
// to the TPM if necessary.
func ReadPCRs(rw io.ReadWriter, sel tpm2.PCRSelection) (*tpmpb.Pcrs, error) {
	pl := tpmpb.Pcrs{
		Hash: tpmpb.HashAlgo(sel.Hash),
		Pcrs: map[uint32][]byte{},
	}

	for i := 0; i < len(sel.PCRs); i += 8 {
		end := min(i+8, len(sel.PCRs))
		pcrSel := tpm2.PCRSelection{
			Hash: sel.Hash,
			PCRs: sel.PCRs[i:end],
		}

		pcrMap, err := tpm2.ReadPCRs(rw, pcrSel)
		if err != nil {
			return nil, err
		}

		for pcr, val := range pcrMap {
			pl.Pcrs[uint32(pcr)] = val
		}
	}

	return &pl, nil
}

// ReadAllPCRs fetches all the PCR values from all implemented PCR banks.
func ReadAllPCRs(rw io.ReadWriter) ([]*tpmpb.Pcrs, error) {
	sels, err := implementedPCRs(rw)
	if err != nil {
		return nil, err
	}

	allPcrs := make([]*tpmpb.Pcrs, len(sels))
	for i, sel := range sels {
		allPcrs[i], err = ReadPCRs(rw, sel)
		if err != nil {
			return nil, fmt.Errorf("reading bank %x PCRs: %w", sel.Hash, err)
		}
	}
	return allPcrs, nil
}

// SealCurrent seals data to the current specified PCR selection.
type SealCurrent struct{ tpm2.PCRSelection }

// SealTarget predicatively seals data to the given specified PCR values.
type SealTarget struct{ *tpmpb.Pcrs }

// SealOpts specifies the PCR values that should be used for Seal().
type SealOpts interface {
	PCRsForSealing(rw io.ReadWriter) (*tpmpb.Pcrs, error)
}

// PCRsForSealing read from TPM and return the selected PCRs.
func (p SealCurrent) PCRsForSealing(rw io.ReadWriter) (*tpmpb.Pcrs, error) {
	if len(p.PCRSelection.PCRs) == 0 {
		panic("SealCurrent contains 0 PCRs")
	}
	return ReadPCRs(rw, p.PCRSelection)
}

// PCRsForSealing return the target PCRs.
func (p SealTarget) PCRsForSealing(_ io.ReadWriter) (*tpmpb.Pcrs, error) {
	if len(p.Pcrs.GetPcrs()) == 0 {
		panic("SealTarget contains 0 PCRs")
	}
	return p.Pcrs, nil
}

// CertifyCurrent certifies that a selection of current PCRs have the same value when sealing.
// Hash Algorithm in the selection should be CertifyHashAlgTpm.
type CertifyCurrent struct{ tpm2.PCRSelection }

// CertifyExpected certifies that the TPM had a specific set of PCR values when sealing.
// Hash Algorithm in the PCR proto should be CertifyHashAlgTpm.
type CertifyExpected struct{ *tpmpb.Pcrs }

// CertifyOpts determines if the given PCR value can pass certification in Unseal().
type CertifyOpts interface {
	CertifyPCRs(rw io.ReadWriter, certified *tpmpb.Pcrs) error
}

// CertifyPCRs from CurrentPCRs will read PCR values from TPM and compare the digest.
func (p CertifyCurrent) CertifyPCRs(rw io.ReadWriter, pcrs *tpmpb.Pcrs) error {
	if len(p.PCRSelection.PCRs) == 0 {
		panic("CertifyCurrent contains 0 PCRs")
	}
	current, err := ReadPCRs(rw, p.PCRSelection)
	if err != nil {
		return err
	}
	return current.CheckIfSubsetOf(pcrs)
}

// CertifyPCRs will compare the digest with given expected PCRs values.
func (p CertifyExpected) CertifyPCRs(_ io.ReadWriter, pcrs *tpmpb.Pcrs) error {
	if len(p.Pcrs.GetPcrs()) == 0 {
		panic("CertifyExpected contains 0 PCRs")
	}
	return p.Pcrs.CheckIfSubsetOf(pcrs)
}

// FullPcrSel will return a full PCR selection based on the total PCR number
// of the TPM with the given hash algo.
func FullPcrSel(hash tpm2.Algorithm) tpm2.PCRSelection {
	sel := tpm2.PCRSelection{Hash: hash}
	for i := 0; i < NumPCRs; i++ {
		sel.PCRs = append(sel.PCRs, int(i))
	}
	return sel
}
<commit_msg>client: Don't embed proto types<commit_after>package client

import (
	"crypto"
	"fmt"
	"io"
	"math"

	tpmpb "github.com/google/go-tpm-tools/proto"
	"github.com/google/go-tpm/tpm2"
)

// NumPCRs is set to the spec minimum of 24, as that's all go-tpm supports.
const NumPCRs = 24

// We hard-code SHA256 as the policy session hash algorithms. Note that this
// differs from the PCR hash algorithm (which selects the bank of PCRs to use)
// and the Public area Name algorithm. We also chose this for compatibility with
// github.com/google/go-tpm/tpm2, as it hardcodes the nameAlg as SHA256 in
// several places. Two constants are used to avoid repeated conversions.
const (
	SessionHashAlg    = crypto.SHA256
	SessionHashAlgTpm = tpm2.AlgSHA256
)

// CertifyHashAlgTpm is the hard-coded algorithm used in certify PCRs.
const CertifyHashAlgTpm = tpm2.AlgSHA256

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// Get a list of selections corresponding to the TPM's implemented PCRs
func implementedPCRs(rw io.ReadWriter) ([]tpm2.PCRSelection, error) {
	caps, moreData, err := tpm2.GetCapability(rw, tpm2.CapabilityPCRs, math.MaxUint32, 0)
	if err != nil {
		return nil, fmt.Errorf("listing implemented PCR banks: %w", err)
	}
	if moreData {
		return nil, fmt.Errorf("extra data from GetCapability")
	}
	sels := make([]tpm2.PCRSelection, len(caps))
	for i, cap := range caps {
		sel, ok := cap.(tpm2.PCRSelection)
		if !ok {
			return nil, fmt.Errorf("unexpected data from GetCapability")
		}
		sels[i] = sel
	}
	return sels, nil
}

// ReadPCRs fetches all the PCR values specified in sel, making multiple calls
// to the TPM if necessary.
func ReadPCRs(rw io.ReadWriter, sel tpm2.PCRSelection) (*tpmpb.Pcrs, error) {
	pl := tpmpb.Pcrs{
		Hash: tpmpb.HashAlgo(sel.Hash),
		Pcrs: map[uint32][]byte{},
	}

	for i := 0; i < len(sel.PCRs); i += 8 {
		end := min(i+8, len(sel.PCRs))
		pcrSel := tpm2.PCRSelection{
			Hash: sel.Hash,
			PCRs: sel.PCRs[i:end],
		}

		pcrMap, err := tpm2.ReadPCRs(rw, pcrSel)
		if err != nil {
			return nil, err
		}

		for pcr, val := range pcrMap {
			pl.Pcrs[uint32(pcr)] = val
		}
	}

	return &pl, nil
}

// ReadAllPCRs fetches all the PCR values from all implemented PCR banks.
func ReadAllPCRs(rw io.ReadWriter) ([]*tpmpb.Pcrs, error) {
	sels, err := implementedPCRs(rw)
	if err != nil {
		return nil, err
	}

	allPcrs := make([]*tpmpb.Pcrs, len(sels))
	for i, sel := range sels {
		allPcrs[i], err = ReadPCRs(rw, sel)
		if err != nil {
			return nil, fmt.Errorf("reading bank %x PCRs: %w", sel.Hash, err)
		}
	}
	return allPcrs, nil
}

// SealCurrent seals data to the current specified PCR selection.
type SealCurrent struct{ tpm2.PCRSelection }

// SealTarget predicatively seals data to the given specified PCR values.
type SealTarget struct{ Pcrs *tpmpb.Pcrs }

// SealOpts specifies the PCR values that should be used for Seal().
type SealOpts interface {
	PCRsForSealing(rw io.ReadWriter) (*tpmpb.Pcrs, error)
}

// PCRsForSealing read from TPM and return the selected PCRs.
func (p SealCurrent) PCRsForSealing(rw io.ReadWriter) (*tpmpb.Pcrs, error) {
	if len(p.PCRSelection.PCRs) == 0 {
		panic("SealCurrent contains 0 PCRs")
	}
	return ReadPCRs(rw, p.PCRSelection)
}

// PCRsForSealing return the target PCRs.
func (p SealTarget) PCRsForSealing(_ io.ReadWriter) (*tpmpb.Pcrs, error) {
	if len(p.Pcrs.GetPcrs()) == 0 {
		panic("SealTarget contains 0 PCRs")
	}
	return p.Pcrs, nil
}

// CertifyCurrent certifies that a selection of current PCRs have the same value when sealing.
// Hash Algorithm in the selection should be CertifyHashAlgTpm.
type CertifyCurrent struct{ tpm2.PCRSelection }

// CertifyExpected certifies that the TPM had a specific set of PCR values when sealing.
// Hash Algorithm in the PCR proto should be CertifyHashAlgTpm.
type CertifyExpected struct{ Pcrs *tpmpb.Pcrs }

// CertifyOpts determines if the given PCR value can pass certification in Unseal().
type CertifyOpts interface {
	CertifyPCRs(rw io.ReadWriter, certified *tpmpb.Pcrs) error
}

// CertifyPCRs from CurrentPCRs will read PCR values from TPM and compare the digest.
func (p CertifyCurrent) CertifyPCRs(rw io.ReadWriter, pcrs *tpmpb.Pcrs) error {
	if len(p.PCRSelection.PCRs) == 0 {
		panic("CertifyCurrent contains 0 PCRs")
	}
	current, err := ReadPCRs(rw, p.PCRSelection)
	if err != nil {
		return err
	}
	return current.CheckIfSubsetOf(pcrs)
}

// CertifyPCRs will compare the digest with given expected PCRs values.
func (p CertifyExpected) CertifyPCRs(_ io.ReadWriter, pcrs *tpmpb.Pcrs) error {
	if len(p.Pcrs.GetPcrs()) == 0 {
		panic("CertifyExpected contains 0 PCRs")
	}
	return p.Pcrs.CheckIfSubsetOf(pcrs)
}

// FullPcrSel will return a full PCR selection based on the total PCR number
// of the TPM with the given hash algo.
func FullPcrSel(hash tpm2.Algorithm) tpm2.PCRSelection {
	sel := tpm2.PCRSelection{Hash: hash}
	for i := 0; i < NumPCRs; i++ {
		sel.PCRs = append(sel.PCRs, int(i))
	}
	return sel
}
<|endoftext|>


<commit_before>package test

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// Fixture is a test binary.
type Fixture struct {
	// Name is the short name of the fixture.
	Name string
	// Path is the absolute path to the test binary.
	Path string
	// Source is the absolute path of the test binary source.
	Source string
}

// Fixtures is a map of Fixture.Name to Fixture.
var Fixtures map[string]Fixture = make(map[string]Fixture)

// RunTestsWithFixtures will pre-compile test fixtures before running test
// methods. Test binaries are deleted before exiting.
func RunTestsWithFixtures(m *testing.M) {
	// Find the fixtures directory; this is necessary because the test file's
	// nesting level can vary. Only look up to a maxdepth of 10 (which seems
	// like a sane alternative to recursion).
	parent := ".."
	fixturesDir := filepath.Join(parent, "_fixtures")
	for depth := 0; depth < 10; depth++ {
		if _, err := os.Stat(fixturesDir); err == nil {
			break
		}
		fixturesDir = filepath.Join("..", fixturesDir)
	}
	if _, err := os.Stat(fixturesDir); err != nil {
		fmt.Println("Couldn't locate fixtures directory")
		os.Exit(1)
	}

	// Collect all files which look like fixture source files.
	sources, err := ioutil.ReadDir(fixturesDir)
	if err != nil {
		fmt.Printf("Couldn't read fixtures dir: %v\n", err)
		os.Exit(1)
	}

	// Compile the fixtures.
	for _, src := range sources {
		if src.IsDir() {
			continue
		}
		// Make a (good enough) random temporary file name
		r := make([]byte, 4)
		rand.Read(r)
		path := filepath.Join(fixturesDir, src.Name())
		name := strings.TrimSuffix(src.Name(), filepath.Ext(src.Name()))
		tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r)))

		// Build the test binary
		if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", tmpfile, path).Run(); err != nil {
			fmt.Printf("Error compiling %s: %s\n", path, err)
			os.Exit(1)
		}
		fmt.Printf("Compiled test binary %s: %s\n", name, tmpfile)

		source, _ := filepath.Abs(path)
		Fixtures[name] = Fixture{Name: name, Path: tmpfile, Source: source}
	}

	status := m.Run()

	// Remove the fixtures.
	// TODO(danmace): Not sure why yet, but doing these removes in a defer isn't
	// working.
	for _, f := range Fixtures {
		os.Remove(f.Path)
	}

	os.Exit(status)
}
<commit_msg>Replace string constant with variable<commit_after>package test

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

// Fixture is a test binary.
type Fixture struct {
	// Name is the short name of the fixture.
	Name string
	// Path is the absolute path to the test binary.
	Path string
	// Source is the absolute path of the test binary source.
	Source string
}

// Fixtures is a map of Fixture.Name to Fixture.
var Fixtures map[string]Fixture = make(map[string]Fixture)

// RunTestsWithFixtures will pre-compile test fixtures before running test
// methods. Test binaries are deleted before exiting.
func RunTestsWithFixtures(m *testing.M) {
	// Find the fixtures directory; this is necessary because the test file's
	// nesting level can vary. Only look up to a maxdepth of 10 (which seems
	// like a sane alternative to recursion).
	parent := ".."
	fixturesDir := filepath.Join(parent, "_fixtures")
	for depth := 0; depth < 10; depth++ {
		if _, err := os.Stat(fixturesDir); err == nil {
			break
		}
		fixturesDir = filepath.Join(parent, fixturesDir)
	}
	if _, err := os.Stat(fixturesDir); err != nil {
		fmt.Println("Couldn't locate fixtures directory")
		os.Exit(1)
	}

	// Collect all files which look like fixture source files.
	sources, err := ioutil.ReadDir(fixturesDir)
	if err != nil {
		fmt.Printf("Couldn't read fixtures dir: %v\n", err)
		os.Exit(1)
	}

	// Compile the fixtures.
	for _, src := range sources {
		if src.IsDir() {
			continue
		}
		// Make a (good enough) random temporary file name
		r := make([]byte, 4)
		rand.Read(r)
		path := filepath.Join(fixturesDir, src.Name())
		name := strings.TrimSuffix(src.Name(), filepath.Ext(src.Name()))
		tmpfile := filepath.Join(os.TempDir(), fmt.Sprintf("%s.%s", name, hex.EncodeToString(r)))

		// Build the test binary
		if err := exec.Command("go", "build", "-gcflags=-N -l", "-o", tmpfile, path).Run(); err != nil {
			fmt.Printf("Error compiling %s: %s\n", path, err)
			os.Exit(1)
		}
		fmt.Printf("Compiled test binary %s: %s\n", name, tmpfile)

		source, _ := filepath.Abs(path)
		Fixtures[name] = Fixture{Name: name, Path: tmpfile, Source: source}
	}

	status := m.Run()

	// Remove the fixtures.
	// TODO(danmace): Not sure why yet, but doing these removes in a defer isn't
	// working.
	for _, f := range Fixtures {
		os.Remove(f.Path)
	}

	os.Exit(status)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	//	"log"
	"io/ioutil"

	"gopkg.in/yaml.v2"
)

type Ingredient struct {
	Name     string
	Quantity float32
	Unit     string
}

type Recipe struct {
	Title       string
	Subtitle    string
	Time        string
	Servings    int
	Calories    int
	Ingredients []Ingredient
	Steps       []string
	Attributes  []string
}

func main() {
	fmt.Println("test")

	// vars
	var rec1 Recipe

	// TO-DO open all files in folder

	// open generic filename
	filename := "recipes/PennePastaAndBeefBolognese.yaml"
	source, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}

	fmt.Println(len(source))

	// unmarshal data
	err = yaml.Unmarshal(source, &rec1)
	if err != nil {
		//log.Fatalf("error: %v", err)
		panic(err)
	}

	// print confirmation file
	fmt.Printf("--- Recipe:\n%v\n\n", rec1)
}
<commit_msg>converted main to functions<commit_after>package main

import (
	"fmt"
	"io/ioutil"

	"gopkg.in/yaml.v2"
)

type Ingredient struct {
	Name     string
	Quantity float32
	Unit     string
}

type Recipe struct {
	Title       string
	Subtitle    string
	Time        string
	Servings    int
	Calories    int
	Ingredients []Ingredient
	Steps       []string
	Attributes  []string
}

var recipeDirectory = "./recipes"

func main() {
	var recipes []Recipe
	recipes = readRecipes(recipeDirectory)

	// print confirmation file
	for _, rec := range recipes {
		fmt.Printf("--- Recipe: %v\n%v\n\n", rec.Title, rec)
	}
}

/**
 * Remove element from recipes array.
 */
func remove(s []Recipe, i int) []Recipe {
    s[len(s)-1], s[i] = s[i], s[len(s)-1]
    return s[:len(s)-1]
}

/**
 * Reads the recipes from the given dirctory and returns them as an array.
 */
func readRecipes(dir string) []Recipe {
	// get files in folder
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		panic(err)
	}

	// array of recipes
	recipes := make([]Recipe, len(files), len(files))

	for i, file := range files {
	// open file
		source, err := ioutil.ReadFile(dir + "/" + file.Name())
		if err != nil {
			panic(err)
		}

		// unmarshal data
		err = yaml.Unmarshal(source, &recipes[i])
		if err != nil {
			panic(err)
		}

	}

	return recipes
}

<|endoftext|>


<commit_before>/*
Package cloudflare implements the CloudFlare v4 API.

New API requests created like:

    api := cloudflare.New(apikey, apiemail)

*/
package cloudflare

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
)

const apiURL = "https://api.cloudflare.com/client/v4"

type API struct {
	APIKey   string
	APIEmail string
}

// Initializes the API configuration.
func New(key, email string) *API {
	return &API{key, email}
}

// Initializes a new zone.
func NewZone() *Zone {
	return &Zone{}
}

// Params can be turned into a URL query string or a body
// TODO: Give this func a better name
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
	// Replace nil with a JSON object if needed
	var reqBody io.Reader
	switch method {
	case "GET", "DELETE":
		reqBody = nil
	default:
		json, err := json.Marshal(params)
		if err != nil {
			fmt.Println("Error marshalling params to JSON:", err)
			return nil, err
		}
		// fmt.Printf("json: %s\n", json)
		reqBody = bytes.NewReader(json)
	}
	req, err := http.NewRequest(method, apiURL+uri, reqBody)
	if err != nil {
		fmt.Println("Herp derp making request:", err)
		return nil, err
	}
	req.Header.Add("X-Auth-Key", api.APIKey)
	req.Header.Add("X-Auth-Email", api.APIEmail)
	// Could be application/json or multipart/form-data
	// req.Header.Add("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	// TODO(jamesog): Check resp.StatusCode != http.StatusOK
	// if resp.StatusCode != http.StatusOK
	defer resp.Body.Close()
	resBody, err := ioutil.ReadAll(resp.Body)
	fmt.Printf("makeRequest: %s\n", resBody)
	return resBody, nil
}

// The Response struct is a template.  There will also be a result struct.
// There will be a unique response type for each response, which will include
// this type.
type Response struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
}

type ResultInfo struct {
	Page    int `json:"page"`
	PerPage int `json:"per_page"`
	Count   int `json:"count"`
	Total   int `json:"total_count"`
}

// An Organization describes a multi-user organization. (Enterprise only.)
type Organization struct {
	ID          string
	Name        string
	Status      string
	Permissions []string
	Roles       []string
}

// A User describes a user account.
type User struct {
	ID            string         `json:"id"`
	Email         string         `json:"email"`
	FirstName     string         `json:"first_name"`
	LastName      string         `json:"last_name"`
	Username      string         `json:"username"`
	Telephone     string         `json:"telephone"`
	Country       string         `json:"country"`
	Zipcode       string         `json:"zipcode"`
	CreatedOn     string         `json:"created_on` // Should this be a time.Date?
	ModifiedOn    string         `json:"modified_on"`
	APIKey        string         `json:"api_key"`
	TwoFA         bool           `json:"two_factor_authentication_enabled"`
	Betas         []string       `json:"betas"`
	Organizations []Organization `json:"organizations"`
}

type UserResponse struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
	Result   User     `json:"result"`
}

type Owner struct {
	ID        string `json:"id"`
	Email     string `json:"email"`
	OwnerType string `json:"owner_type"`
}

// A Zone describes a CloudFlare zone.
type Zone struct {
	ID                string   `json:"id"`
	Name              string   `json:"name"`
	DevMode           int      `json:"development_mode"`
	OriginalNS        []string `json:"original_name_servers"`
	OriginalRegistrar string   `json:"original_registrar"`
	OriginalDNSHost   string   `json:"original_dnshost"`
	CreatedOn         string   `json:"created_on"`
	ModifiedOn        string   `json:"modified_on"`
	NameServers       []string `json:"name_servers"`
	Owner             Owner    `json:"owner"`
	Permissions       []string `json:"permissions"`
	Plan              ZonePlan `json:"plan"`
	Status            string   `json:"success"`
	Paused            bool     `json:"paused"`
	Type              string   `json:"type"`
	Host              struct {
		Name    string
		Website string
	} `json:"host"`
	VanityNS    []string `json:"vanity_name_servers"`
	Betas       []string `json:"betas"`
	DeactReason string   `json:"deactivation_reason"`
	Meta        ZoneMeta `json:"meta"`
}

// Contains metadata about a zone.
type ZoneMeta struct {
	// custom_certificate_quota is broken - sometimes it's a string, sometimes a number!
	// CustCertQuota     int    `json:"custom_certificate_quota"`
	PageRuleQuota     string `json:"page_rule_quota"`
	WildcardProxiable bool   `json:"wildcard_proxiable"`
	PhishingDetected  bool   `json:"phishing_detected"`
}

// Contains the plan information for a zone.
type ZonePlan struct {
	ID           string `json:"id"`
	Name         string `json:"name"`
	Price        int    `json:"price"`
	Currency     string `json:"currency"`
	Frequency    string `json:"frequency"`
	LegacyID     string `json:"legacy_id"`
	IsSubscribed bool   `json:"is_subscribed"`
	CanSubscribe bool   `json:"can_subscribe"`
}

type ZoneResponse struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
	Result   []Zone   `json:"result"`
}

type ZonePlanResponse struct {
	Success  bool       `json:"success"`
	Errors   []string   `json:"errors"`
	Messages []string   `json:"messages"`
	Result   []ZonePlan `json:"result"`
}

// type zoneSetting struct {
// 	ID         string `json:"id"`
// 	Editable   bool   `json:"editable"`
// 	ModifiedOn string `json:"modified_on"`
// }
// type zoneSettingStringVal struct {
// 	zoneSetting
// 	Value string `json:"value"`
// }
// type zoneSettingIntVal struct {
// 	zoneSetting
// 	Value int64 `json:"value"`
// }

type ZoneSetting struct {
	ID            string      `json:"id"`
	Editable      bool        `json:"editable"`
	ModifiedOn    string      `json:"modified_on"`
	Value         interface{} `json:"value""`
	TimeRemaining int         `json:"time_remaining""`
}

type ZoneSettingResponse struct {
	Success  bool          `json:"success"`
	Errors   []string      `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []ZoneSetting `json:"result"`
}

// Describes a DNS record for a zone.
type DNSRecord struct {
	ID         string      `json:"id,omitempty"`
	Type       string      `json:"type"`
	Name       string      `json:"name"`
	Content    string      `json:"content"`
	Proxiable  bool        `json:"proxiable,omitempty"`
	Proxied    bool        `json:"proxied"`
	TTL        int         `json:"ttl"`
	Locked     bool        `json:"locked,omitempty"`
	ZoneID     string      `json:"zone_id,omitempty"`
	ZoneName   string      `json:"zone_name,omitempty"`
	CreatedOn  string      `json:"created_on,omitempty"`
	ModifiedOn string      `json:"modified_on,omitempty"`
	Data       interface{} `json:"data,omitempty"` // data returned by: SRV, LOC
	Meta       interface{} `json:"meta,omitempty"`
	Priority   int         `json:"priority"`
}

// The response for creating or updating a DNS record.
type DNSRecordResponse struct {
	Success  bool          `json:"success"`
	Errors   []interface{} `json:"errors"`
	Messages []string      `json:"messages"`
	Result   DNSRecord     `json:"result"`
}

// The response for listing DNS records.
type DNSListResponse struct {
	Success  bool          `json:"success"`
	Errors   []interface{} `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []DNSRecord   `json:"result"`
}

// Railgun status for a zone.
type ZoneRailgun struct {
	ID        string `json:"id"`
	Name      string `json:"string"`
	Enabled   bool   `json:"enabled"`
	Connected bool   `json:"connected"`
}

type ZoneRailgunResponse struct {
	Success  bool          `json:"success"`
	Errors   []string      `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []ZoneRailgun `json:"result"`
}

// Custom SSL certificates for a zone.
type ZoneCustomSSL struct {
	ID            string     `json:"id"`
	Hosts         []string   `json:"hosts"`
	Issuer        string     `json:"issuer"`
	Priority      int        `json:"priority"`
	Status        string     `json:"success"`
	BundleMethod  string     `json:"bundle_method"`
	ZoneID        string     `json:"zone_id"`
	Permissions   []string   `json:"permissions"`
	UploadedOn    string     `json:"uploaded_on"`
	ModifiedOn    string     `json:"modified_on"`
	ExpiresOn     string     `json:"expires_on"`
	KeylessServer KeylessSSL `json:"keyless_server"`
}

type ZoneCustomSSLResponse struct {
	Success  bool            `json:"success"`
	Errors   []string        `json:"errors"`
	Messages []string        `json:"messages"`
	Result   []ZoneCustomSSL `json:"result"`
}

type KeylessSSL struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Host        string   `json:"host"`
	Port        int      `json:"port"`
	Status      string   `json:"success"`
	Enabled     bool     `json:"enabled"`
	Permissions []string `json:"permissions"`
	CreatedOn   string   `json:"created_on"`
	ModifiedOn  string   `json:"modifed_on"`
}

type KeylessSSLResponse struct {
	Success  bool         `json:"success"`
	Errors   []string     `json:"errors"`
	Messages []string     `json:"messages"`
	Result   []KeylessSSL `json:"result"`
}

type Railgun struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	Status         string `json:"success"`
	Enabled        bool   `json:"enabled"`
	ZonesConnected int    `json:"zones_connected"`
	Build          string `json:"build"`
	Version        string `json:"version"`
	Revision       string `json:"revision"`
	ActivationKey  string `json:"activation_key"`
	ActivatedOn    string `json:"activated_on"`
	CreatedOn      string `json:"created_on"`
	ModifiedOn     string `json:"modified_on"`
	// XXX: UpgradeInfo struct {
	// version string
	// url string
	// } `json:"upgrade_info"`
}

type RailgunResponse struct {
	Success  bool      `json:"success"`
	Errors   []string  `json:"errors"`
	Messages []string  `json:"messages"`
	Result   []Railgun `json:"result"`
}

// Custom error pages.
type CustomPage struct {
	CreatedOn      string   `json:"created_on"`
	ModifiedOn     string   `json:"modified_on"`
	URL            string   `json:"url"`
	State          string   `json:"state"`
	RequiredTokens []string `json:"required_tokens"`
	PreviewTarget  string   `json:"preview_target"`
	Description    string   `json:"description"`
}

type CustomPageResponse struct {
	Success  bool         `json:"success"`
	Errors   []string     `json:"errors"`
	Messages []string     `json:"messages"`
	Result   []CustomPage `json:"result"`
}
<commit_msg>Change all DNSRecord fields to ,omitempty<commit_after>/*
Package cloudflare implements the CloudFlare v4 API.

New API requests created like:

    api := cloudflare.New(apikey, apiemail)

*/
package cloudflare

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
)

const apiURL = "https://api.cloudflare.com/client/v4"

type API struct {
	APIKey   string
	APIEmail string
}

// Initializes the API configuration.
func New(key, email string) *API {
	return &API{key, email}
}

// Initializes a new zone.
func NewZone() *Zone {
	return &Zone{}
}

// Params can be turned into a URL query string or a body
// TODO: Give this func a better name
func (api *API) makeRequest(method, uri string, params interface{}) ([]byte, error) {
	// Replace nil with a JSON object if needed
	var reqBody io.Reader
	switch method {
	case "GET", "DELETE":
		reqBody = nil
	default:
		json, err := json.Marshal(params)
		if err != nil {
			fmt.Println("Error marshalling params to JSON:", err)
			return nil, err
		}
		// fmt.Printf("json: %s\n", json)
		reqBody = bytes.NewReader(json)
	}
	req, err := http.NewRequest(method, apiURL+uri, reqBody)
	if err != nil {
		fmt.Println("Herp derp making request:", err)
		return nil, err
	}
	req.Header.Add("X-Auth-Key", api.APIKey)
	req.Header.Add("X-Auth-Email", api.APIEmail)
	// Could be application/json or multipart/form-data
	// req.Header.Add("Content-Type", "application/json")
	client := &http.Client{}
	resp, err := client.Do(req)
	// TODO(jamesog): Check resp.StatusCode != http.StatusOK
	// if resp.StatusCode != http.StatusOK
	defer resp.Body.Close()
	resBody, err := ioutil.ReadAll(resp.Body)
	fmt.Printf("makeRequest: %s\n", resBody)
	return resBody, nil
}

// The Response struct is a template.  There will also be a result struct.
// There will be a unique response type for each response, which will include
// this type.
type Response struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
}

type ResultInfo struct {
	Page    int `json:"page"`
	PerPage int `json:"per_page"`
	Count   int `json:"count"`
	Total   int `json:"total_count"`
}

// An Organization describes a multi-user organization. (Enterprise only.)
type Organization struct {
	ID          string
	Name        string
	Status      string
	Permissions []string
	Roles       []string
}

// A User describes a user account.
type User struct {
	ID            string         `json:"id"`
	Email         string         `json:"email"`
	FirstName     string         `json:"first_name"`
	LastName      string         `json:"last_name"`
	Username      string         `json:"username"`
	Telephone     string         `json:"telephone"`
	Country       string         `json:"country"`
	Zipcode       string         `json:"zipcode"`
	CreatedOn     string         `json:"created_on` // Should this be a time.Date?
	ModifiedOn    string         `json:"modified_on"`
	APIKey        string         `json:"api_key"`
	TwoFA         bool           `json:"two_factor_authentication_enabled"`
	Betas         []string       `json:"betas"`
	Organizations []Organization `json:"organizations"`
}

type UserResponse struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
	Result   User     `json:"result"`
}

type Owner struct {
	ID        string `json:"id"`
	Email     string `json:"email"`
	OwnerType string `json:"owner_type"`
}

// A Zone describes a CloudFlare zone.
type Zone struct {
	ID                string   `json:"id"`
	Name              string   `json:"name"`
	DevMode           int      `json:"development_mode"`
	OriginalNS        []string `json:"original_name_servers"`
	OriginalRegistrar string   `json:"original_registrar"`
	OriginalDNSHost   string   `json:"original_dnshost"`
	CreatedOn         string   `json:"created_on"`
	ModifiedOn        string   `json:"modified_on"`
	NameServers       []string `json:"name_servers"`
	Owner             Owner    `json:"owner"`
	Permissions       []string `json:"permissions"`
	Plan              ZonePlan `json:"plan"`
	Status            string   `json:"success"`
	Paused            bool     `json:"paused"`
	Type              string   `json:"type"`
	Host              struct {
		Name    string
		Website string
	} `json:"host"`
	VanityNS    []string `json:"vanity_name_servers"`
	Betas       []string `json:"betas"`
	DeactReason string   `json:"deactivation_reason"`
	Meta        ZoneMeta `json:"meta"`
}

// Contains metadata about a zone.
type ZoneMeta struct {
	// custom_certificate_quota is broken - sometimes it's a string, sometimes a number!
	// CustCertQuota     int    `json:"custom_certificate_quota"`
	PageRuleQuota     string `json:"page_rule_quota"`
	WildcardProxiable bool   `json:"wildcard_proxiable"`
	PhishingDetected  bool   `json:"phishing_detected"`
}

// Contains the plan information for a zone.
type ZonePlan struct {
	ID           string `json:"id"`
	Name         string `json:"name"`
	Price        int    `json:"price"`
	Currency     string `json:"currency"`
	Frequency    string `json:"frequency"`
	LegacyID     string `json:"legacy_id"`
	IsSubscribed bool   `json:"is_subscribed"`
	CanSubscribe bool   `json:"can_subscribe"`
}

type ZoneResponse struct {
	Success  bool     `json:"success"`
	Errors   []string `json:"errors"`
	Messages []string `json:"messages"`
	Result   []Zone   `json:"result"`
}

type ZonePlanResponse struct {
	Success  bool       `json:"success"`
	Errors   []string   `json:"errors"`
	Messages []string   `json:"messages"`
	Result   []ZonePlan `json:"result"`
}

// type zoneSetting struct {
// 	ID         string `json:"id"`
// 	Editable   bool   `json:"editable"`
// 	ModifiedOn string `json:"modified_on"`
// }
// type zoneSettingStringVal struct {
// 	zoneSetting
// 	Value string `json:"value"`
// }
// type zoneSettingIntVal struct {
// 	zoneSetting
// 	Value int64 `json:"value"`
// }

type ZoneSetting struct {
	ID            string      `json:"id"`
	Editable      bool        `json:"editable"`
	ModifiedOn    string      `json:"modified_on"`
	Value         interface{} `json:"value""`
	TimeRemaining int         `json:"time_remaining""`
}

type ZoneSettingResponse struct {
	Success  bool          `json:"success"`
	Errors   []string      `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []ZoneSetting `json:"result"`
}

// Describes a DNS record for a zone.
type DNSRecord struct {
	ID         string      `json:"id,omitempty"`
	Type       string      `json:"type,omitempty"`
	Name       string      `json:"name,omitempty"`
	Content    string      `json:"content,omitempty"`
	Proxiable  bool        `json:"proxiable,omitempty"`
	Proxied    bool        `json:"proxied,omitempty"`
	TTL        int         `json:"ttl,omitempty"`
	Locked     bool        `json:"locked,omitempty"`
	ZoneID     string      `json:"zone_id,omitempty"`
	ZoneName   string      `json:"zone_name,omitempty"`
	CreatedOn  string      `json:"created_on,omitempty"`
	ModifiedOn string      `json:"modified_on,omitempty"`
	Data       interface{} `json:"data,omitempty"` // data returned by: SRV, LOC
	Meta       interface{} `json:"meta,omitempty"`
	Priority   int         `json:"priority,omitempty"`
}

// The response for creating or updating a DNS record.
type DNSRecordResponse struct {
	Success  bool          `json:"success"`
	Errors   []interface{} `json:"errors"`
	Messages []string      `json:"messages"`
	Result   DNSRecord     `json:"result"`
}

// The response for listing DNS records.
type DNSListResponse struct {
	Success  bool          `json:"success"`
	Errors   []interface{} `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []DNSRecord   `json:"result"`
}

// Railgun status for a zone.
type ZoneRailgun struct {
	ID        string `json:"id"`
	Name      string `json:"string"`
	Enabled   bool   `json:"enabled"`
	Connected bool   `json:"connected"`
}

type ZoneRailgunResponse struct {
	Success  bool          `json:"success"`
	Errors   []string      `json:"errors"`
	Messages []string      `json:"messages"`
	Result   []ZoneRailgun `json:"result"`
}

// Custom SSL certificates for a zone.
type ZoneCustomSSL struct {
	ID            string     `json:"id"`
	Hosts         []string   `json:"hosts"`
	Issuer        string     `json:"issuer"`
	Priority      int        `json:"priority"`
	Status        string     `json:"success"`
	BundleMethod  string     `json:"bundle_method"`
	ZoneID        string     `json:"zone_id"`
	Permissions   []string   `json:"permissions"`
	UploadedOn    string     `json:"uploaded_on"`
	ModifiedOn    string     `json:"modified_on"`
	ExpiresOn     string     `json:"expires_on"`
	KeylessServer KeylessSSL `json:"keyless_server"`
}

type ZoneCustomSSLResponse struct {
	Success  bool            `json:"success"`
	Errors   []string        `json:"errors"`
	Messages []string        `json:"messages"`
	Result   []ZoneCustomSSL `json:"result"`
}

type KeylessSSL struct {
	ID          string   `json:"id"`
	Name        string   `json:"name"`
	Host        string   `json:"host"`
	Port        int      `json:"port"`
	Status      string   `json:"success"`
	Enabled     bool     `json:"enabled"`
	Permissions []string `json:"permissions"`
	CreatedOn   string   `json:"created_on"`
	ModifiedOn  string   `json:"modifed_on"`
}

type KeylessSSLResponse struct {
	Success  bool         `json:"success"`
	Errors   []string     `json:"errors"`
	Messages []string     `json:"messages"`
	Result   []KeylessSSL `json:"result"`
}

type Railgun struct {
	ID             string `json:"id"`
	Name           string `json:"name"`
	Status         string `json:"success"`
	Enabled        bool   `json:"enabled"`
	ZonesConnected int    `json:"zones_connected"`
	Build          string `json:"build"`
	Version        string `json:"version"`
	Revision       string `json:"revision"`
	ActivationKey  string `json:"activation_key"`
	ActivatedOn    string `json:"activated_on"`
	CreatedOn      string `json:"created_on"`
	ModifiedOn     string `json:"modified_on"`
	// XXX: UpgradeInfo struct {
	// version string
	// url string
	// } `json:"upgrade_info"`
}

type RailgunResponse struct {
	Success  bool      `json:"success"`
	Errors   []string  `json:"errors"`
	Messages []string  `json:"messages"`
	Result   []Railgun `json:"result"`
}

// Custom error pages.
type CustomPage struct {
	CreatedOn      string   `json:"created_on"`
	ModifiedOn     string   `json:"modified_on"`
	URL            string   `json:"url"`
	State          string   `json:"state"`
	RequiredTokens []string `json:"required_tokens"`
	PreviewTarget  string   `json:"preview_target"`
	Description    string   `json:"description"`
}

type CustomPageResponse struct {
	Success  bool         `json:"success"`
	Errors   []string     `json:"errors"`
	Messages []string     `json:"messages"`
	Result   []CustomPage `json:"result"`
}
<|endoftext|>