<commit_before>/*
Package soy is an implementation of Google's Closure Templates, which are
data-driven templates for generating HTML.

Compared to html/template, Closure Templates have a few advantages

 * Intuitive templating language that supports simple control flow, expressions and arithmetic.
 * The same templates may be used from Go, Java, and Javascript.
 * Internationalization is built in

Also, there are a few conveniences that this implementation provides:

 * Hot reload for templates
 * Add a directory of templates (including sub-directories)

Refer to the official language spec for details:

https://developers.google.com/closure/templates/

Template example

Here is Hello World

	{namespace examples.simple}

	/**
	 * Says hello to the world.*/
//	 */
/*	{template .helloWorld}
	  Hello world!
	{/template}

Here is a more customized version that addresses us by name and can use
greetings other than "Hello".

	/**
	 * Greets a person using "Hello" by default.
	 * @param name The name of the person.
	 * @param? greetingWord Optional greeting word to use instead of "Hello".*/
//	 */
/*	{template .helloName}
	  {if not $greetingWord}
	    Hello {$name}!
	  {else}
	    {$greetingWord} {$name}!
	  {/if}
	{/template}

This last example renders a greeting for each person in a list of names.

It demonstrates a [foreach] loop with an [ifempty] command. It also shows how to
call other templates and insert their output using the [call] command. Note that
the [data="all"] attribute in the call command passes all of the caller's
template data to the callee template.

	/**
	 * Greets a person and optionally a list of other people.
	 * @param name The name of the person.
	 * @param additionalNames The additional names to greet. May be an empty list.*/
//	 */
/*	{template .helloNames}
	  // Greet the person.
	  {call .helloName data="all" /}

	  // Greet the additional people.
	  {foreach $additionalName in $additionalNames}
	    {call .helloName}
	      {param name: $additionalName /}
	    {/call}
	    {if not isLast($additionalName)}

  // break after every line except the last
	    {/if}
	  {ifempty}
	    No additional people to greet.
	  {/foreach}
	{/template}

(This example is from
https://developers.google.com/closure/templates/docs/helloworld_java)

Usage example

These are the high level steps:

 * Create a soy.Bundle and add templates to it (the literal template strings,
   files, or directories).
 * Compile the bundle of templates, resulting in a "Tofu" instance. It provides
   access to all your soy.
 * Render a HTML template from Tofu by providing the template name and a data
   object.

Typically in a web application you have a directory containing views for all of
your pages.  For example:

  app/views/
  app/views/account/
  app/views/feed/
  ...

This code snippet will parse a file of globals, all soy templates within
app/views, and provide back a Tofu intance that can be used to render any
declared template.  Additionally, if "mode == dev", it will watch the soy files
for changes and update your compiled templates in the background (or log compile
errors to soy.Logger).  Error checking is omitted.

On startup:

  tofu, _ := soy.NewBundle().
      WatchFiles(true).                     // watch soy files, reload on changes
      AddGlobalsFile("views/globals.txt").  // parse a file of globals
      AddTemplateDir("views").              // load *.soy in all sub-directories
      CompileToTofu()

To render a page:

  var obj = map[string]interface{}{
    "user":    user,
    "account": account,
  }
  tofu.Render(resp, "acme.account.overview", obj)

Structs may be used as the data context too, but keep in mind that they are
converted to data maps -- unlike html/template, the context is pure data, and
you can not call methods on it.

  var obj = HomepageContext{
    User:    user,
    Account: account,
  }
  tofu.Render(resp, "acme.account.overview", obj)

See soyhtml.StructOptions for knobs to control how your structs get converted to
data maps.

Project Status

The goal is full compatibility and feature parity with the official Closure
Templates project.

The server-side templating functionality is well tested and nearly complete,
except for two notable areas: contextual autoescaping and
internationalization/bidi support.  Contributions welcome.

The Javascript generation is early and lacks many generation options, but
it successfully passes the server-side template test suite. Note that it is
possible to run the official Soy compiler to generate your javascript templates
at build time, even if you use this package for server-side templates.

Please see the TODO file for features that have yet to be implemented.

Please open a Github Issue for any bugs / problems / comments, or if you find a
template that renders differently than with the official compiler.
*/
package soy
<commit_msg>Add link to features.soy<commit_after>/*
Package soy is an implementation of Google's Closure Templates, which are
data-driven templates for generating HTML.

Compared to html/template, Closure Templates have a few advantages

 * Intuitive templating language that supports simple control flow, expressions and arithmetic.
 * The same templates may be used from Go, Java, and Javascript.
 * Internationalization is built in

Also, there are a few conveniences that this implementation provides:

 * Hot reload for templates
 * Add a directory of templates (including sub-directories)

Refer to the official language spec for details:

https://developers.google.com/closure/templates/

Template example

Here is Hello World

	{namespace examples.simple}

	/**
	 * Says hello to the world.*/
//	 */
/*	{template .helloWorld}
	  Hello world!
	{/template}

Here is a more customized version that addresses us by name and can use
greetings other than "Hello".

	/**
	 * Greets a person using "Hello" by default.
	 * @param name The name of the person.
	 * @param? greetingWord Optional greeting word to use instead of "Hello".*/
//	 */
/*	{template .helloName}
	  {if not $greetingWord}
	    Hello {$name}!
	  {else}
	    {$greetingWord} {$name}!
	  {/if}
	{/template}

This last example renders a greeting for each person in a list of names.

It demonstrates a [foreach] loop with an [ifempty] command. It also shows how to
call other templates and insert their output using the [call] command. Note that
the [data="all"] attribute in the call command passes all of the caller's
template data to the callee template.

	/**
	 * Greets a person and optionally a list of other people.
	 * @param name The name of the person.
	 * @param additionalNames The additional names to greet. May be an empty list.*/
//	 */
/*	{template .helloNames}
	  // Greet the person.
	  {call .helloName data="all" /}

	  // Greet the additional people.
	  {foreach $additionalName in $additionalNames}
	    {call .helloName}
	      {param name: $additionalName /}
	    {/call}
	    {if not isLast($additionalName)}

  // break after every line except the last
	    {/if}
	  {ifempty}
	    No additional people to greet.
	  {/foreach}
	{/template}

This example is from
https://developers.google.com/closure/templates/docs/helloworld_java.

Many more examples of soy language features/commands may be seen here:
https://github.com/robfig/soy/blob/master/testdata/features.soy

Usage example

These are the high level steps:

 * Create a soy.Bundle and add templates to it (the literal template strings,
   files, or directories).
 * Compile the bundle of templates, resulting in a "Tofu" instance. It provides
   access to all your soy.
 * Render a HTML template from Tofu by providing the template name and a data
   object.

Typically in a web application you have a directory containing views for all of
your pages.  For example:

  app/views/
  app/views/account/
  app/views/feed/
  ...

This code snippet will parse a file of globals, all soy templates within
app/views, and provide back a Tofu intance that can be used to render any
declared template.  Additionally, if "mode == dev", it will watch the soy files
for changes and update your compiled templates in the background (or log compile
errors to soy.Logger).  Error checking is omitted.

On startup:

  tofu, _ := soy.NewBundle().
      WatchFiles(true).                     // watch soy files, reload on changes
      AddGlobalsFile("views/globals.txt").  // parse a file of globals
      AddTemplateDir("views").              // load *.soy in all sub-directories
      CompileToTofu()

To render a page:

  var obj = map[string]interface{}{
    "user":    user,
    "account": account,
  }
  tofu.Render(resp, "acme.account.overview", obj)

Structs may be used as the data context too, but keep in mind that they are
converted to data maps -- unlike html/template, the context is pure data, and
you can not call methods on it.

  var obj = HomepageContext{
    User:    user,
    Account: account,
  }
  tofu.Render(resp, "acme.account.overview", obj)

See soyhtml.StructOptions for knobs to control how your structs get converted to
data maps.

Project Status

The goal is full compatibility and feature parity with the official Closure
Templates project.

The server-side templating functionality is well tested and nearly complete,
except for two notable areas: contextual autoescaping and
internationalization/bidi support.  Contributions welcome.

The Javascript generation is early and lacks many generation options, but
it successfully passes the server-side template test suite. Note that it is
possible to run the official Soy compiler to generate your javascript templates
at build time, even if you use this package for server-side templates.

Please see the TODO file for features that have yet to be implemented.

Please open a Github Issue for any bugs / problems / comments, or if you find a
template that renders differently than with the official compiler.
*/
package soy
<|endoftext|>


<commit_before>package main

import "testing"

func TestNoSpacePackage(t *testing.T) {
	buffer := `packagenospace
type T Peg {}
Grammar <- !.
`
	p := &Peg{Tree: New(false, false), Buffer: buffer}
	p.Init()
	err := p.Parse()
	if err == nil {
		t.Error("packagenospace was parsed without error")
	}
}

func TestNoSpaceType(t *testing.T) {
	buffer := `
package p
typenospace Peg {}
Grammar <- !.
`
	p := &Peg{Tree: New(false, false), Buffer: buffer}
	p.Init()
	err := p.Parse()
	if err == nil {
		t.Error("typenospace was parsed without error")
	}
}
<commit_msg>Add test that correctly spaced code parses<commit_after>package main

import "testing"

func TestCorrect(t *testing.T) {
	buffer := `package p
type T Peg {}
Grammar <- !.
`
	p := &Peg{Tree: New(false, false), Buffer: buffer}
	p.Init()
	err := p.Parse()
	if err != nil {
		t.Error(err)
	}
}

func TestNoSpacePackage(t *testing.T) {
	buffer := `packagenospace
type T Peg {}
Grammar <- !.
`
	p := &Peg{Tree: New(false, false), Buffer: buffer}
	p.Init()
	err := p.Parse()
	if err == nil {
		t.Error("packagenospace was parsed without error")
	}
}

func TestNoSpaceType(t *testing.T) {
	buffer := `
package p
typenospace Peg {}
Grammar <- !.
`
	p := &Peg{Tree: New(false, false), Buffer: buffer}
	p.Init()
	err := p.Parse()
	if err == nil {
		t.Error("typenospace was parsed without error")
	}
}
<|endoftext|>


<commit_before>package command

import (
	"fmt"
	"errors"

	"github.com/dnaeon/gru/task"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/cli"
)

func NewRunCommand() cli.Command {
	cmd := cli.Command{
		Name: "run",
		Usage: "send task to minion(s)",
		Action: execRunCommand,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name: "with-classifier",
				Value: "",
				Usage: "target minion(s) with given classifier only",
			},
		},
	}

	return cmd
}

// Executes the "run" command
func execRunCommand(c *cli.Context) {
	if len(c.Args()) < 1 {
		displayError(errors.New("Must provide command to run"), 64)
	}

	client := newEtcdMinionClientFromFlags(c)

	// If --with-classifier flag is provided then
	// send the task for processing only to
	// minion(s) which contain the given classifier.
	// Otherwise send the task to all minions
	var err error
	var minions []uuid.UUID
	cFlag := c.String("with-classifier")
	if cFlag != "" {
		// TODO: Be able to specify a classifier patterns
		minions, err = client.MinionWithClassifierKey(cFlag)
	} else {
		minions, err = client.MinionList()
	}

	if err != nil {
		displayError(err, 1)
	}

	numMinions := len(minions)
	if numMinions == 0 {
		displayError(errors.New("No minion(s) found"), 1)
	}

	fmt.Printf("Found %d minion(s) for task processing\n", numMinions)

	// The first argument is the command and anything else
	// that follows is considered as task arguments
	args := c.Args()
	taskCommand := args[0]
	taskArgs := args[1:]
	t := task.New(taskCommand, taskArgs...)

	failed := 0
	for i, minion := range minions {
		fmt.Printf("[%d/%d] Submitting task to minion %s\r", i + 1, numMinions, minion)
		err = client.MinionSubmitTask(minion, t)
		if err != nil {
			failed += 1
			fmt.Printf("\nFailed to submit task to %s: %s\n", minion, err)
		}
	}
	fmt.Println()

	fmt.Printf("Task submitted to %d minion(s), %d of which has failed\n", numMinions, failed)
	fmt.Printf("Task results can be retrieved by using this task id: %s\n", t.TaskID)
}
<commit_msg>gructl: be able to flag minion tasks as concurrent<commit_after>package command

import (
	"fmt"
	"errors"

	"github.com/dnaeon/gru/task"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/cli"
)

func NewRunCommand() cli.Command {
	cmd := cli.Command{
		Name: "run",
		Usage: "send task to minion(s)",
		Action: execRunCommand,
		Flags: []cli.Flag{
			cli.StringFlag{
				Name: "with-classifier",
				Value: "",
				Usage: "target minion(s) with given classifier only",
			},
			cli.BoolFlag{
				Name: "is-concurrent",
				Usage: "flag task as concurrent",
			},
		},
	}

	return cmd
}

// Executes the "run" command
func execRunCommand(c *cli.Context) {
	if len(c.Args()) < 1 {
		displayError(errors.New("Must provide command to run"), 64)
	}

	client := newEtcdMinionClientFromFlags(c)

	// If --with-classifier flag is provided then
	// send the task for processing only to
	// minion(s) which contain the given classifier.
	// Otherwise send the task to all minions
	var err error
	var minions []uuid.UUID
	cFlag := c.String("with-classifier")
	if cFlag != "" {
		// TODO: Be able to specify a classifier patterns
		minions, err = client.MinionWithClassifierKey(cFlag)
	} else {
		minions, err = client.MinionList()
	}

	if err != nil {
		displayError(err, 1)
	}

	numMinions := len(minions)
	if numMinions == 0 {
		displayError(errors.New("No minion(s) found"), 1)
	}

	fmt.Printf("Found %d minion(s) for task processing\n", numMinions)

	// The first argument is the command and anything else
	// that follows is considered as task arguments
	args := c.Args()
	isConcurrent := c.Bool("is-concurrent")
	taskCommand := args[0]
	taskArgs := args[1:]
	t := task.New(taskCommand, taskArgs...)
	t.IsConcurrent = isConcurrent

	failed := 0
	for i, minion := range minions {
		fmt.Printf("[%d/%d] Submitting task to minion %s\r", i + 1, numMinions, minion)
		err = client.MinionSubmitTask(minion, t)
		if err != nil {
			failed += 1
			fmt.Printf("\nFailed to submit task to %s: %s\n", minion, err)
		}
	}
	fmt.Println()

	fmt.Printf("Task submitted to %d minion(s), %d of which has failed\n", numMinions, failed)
	fmt.Printf("Task results can be retrieved by using this task id: %s\n", t.TaskID)
}
<|endoftext|>


<commit_before>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"
)

// MaxPayloadLen is the maximum allowed payload length (after JSON encoding)
const MaxPayloadLen = 256

// Payload represents a notification payload
type Payload map[string]interface{}

// NotificationIdentifier represents a notification identifier
type NotificationIdentifier uint32

// NotificationPriority represents a notification priority
type NotificationPriority uint8

// Notification represents a notification
type Notification struct {
	deviceToken string
	payload     Payload
	identifier  *NotificationIdentifier
	expiry      time.Time
	priority    NotificationPriority
}

// AlertDictionary is a localized alert text
type AlertDictionary struct {
	Body         string   `json:"body,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

const (
	// ImmediatePriority sets the push message to be sent immediately. This is
	// the default.
	ImmediatePriority NotificationPriority = 10
	// PowerSavingPriority sets the push message to be sent at a time that conserves power on the device receiving it
	PowerSavingPriority NotificationPriority = 5
)

const pushCommandValue = 2

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemid            = 1
	payloadItemid                = 2
	notificationIdentifierItemid = 3
	expirationDateItemid         = 4
	priorityItemid               = 5
	deviceTokenLength            = 32
	notificationIdentifierLength = 4
	expirationDateLength         = 4
	priorityLength               = 1
)

// SetAlertString sets the alert item as a string
func (p Payload) SetAlertString(alert string) {
	p.aps()["alert"] = alert
}

// SetAlertDictionary sets the alert item as a dictionary
func (p Payload) SetAlertDictionary(alert *AlertDictionary) {
	p.aps()["alert"] = alert
}

// SetBadge sets the badge item
func (p Payload) SetBadge(badge int) {
	p.aps()["badge"] = badge
}

// SetSound sets the sound item
func (p Payload) SetSound(sound string) {
	p.aps()["sound"] = sound
}

// Set sets a custom item outside the aps namespace
func (p Payload) Set(name string, value interface{}) {
	p[name] = value
}

// ToJSON encodes the Payload to JSON. The encoded payload cannot exceed
// MaxPayloadLen bytes
func (p Payload) ToJSON() ([]byte, error) {
	return json.Marshal(p)
}

func (p Payload) aps() map[string]interface{} {
	if e, ok := p["aps"]; ok {
		if aps, ok := e.(map[string]interface{}); ok {
			return aps
		}
	}
	aps := map[string]interface{}{}
	p["aps"] = aps
	return aps
}

// NewNotification creates a new Notification
func NewNotification() *Notification {
	n := &Notification{}
	n.payload = Payload{}
	n.priority = ImmediatePriority
	return n
}

// SetDeviceToken sets the device token. Must be a 64 bytes hex string.
func (n *Notification) SetDeviceToken(token string) {
	n.deviceToken = token
}

// DeviceToken returns the device token
func (n *Notification) DeviceToken() string {
	return n.deviceToken
}

// Payload returns the Payload
func (n *Notification) Payload() Payload {
	return n.payload
}

// SetIdentifier sets a custom identifier. Two notifications sent to the
// same Sender must have different identifiers. Sender automatically chooses an
// identifier if one was not set.
func (n *Notification) SetIdentifier(identifier NotificationIdentifier) {
	n.identifier = &identifier
}

// Identifier returns the Identifier
func (n *Notification) Identifier() NotificationIdentifier {
	if n.identifier != nil {
		return *n.identifier
	}
	return 0
}

// HasIdentifier returns whether the Identifier has been set
func (n *Notification) HasIdentifier() bool {
	return n.identifier != nil
}

// SetExpiry sets the expiry. Fractions of seconds are truncated. APNS discards
// the notification if it wasn't able to send it after this duration. An expiry
// of 0 means that the notification is discarded immediately by APNS if it can
// not be sent (the is the default).
func (n *Notification) SetExpiry(expiry time.Time) {
	n.expiry = expiry
}

// Expiry returns the expiry
func (n *Notification) Expiry() time.Time {
	return n.expiry
}

// SetPriority sets the priority. The default is ImmediatePriority.
func (n *Notification) SetPriority(priority NotificationPriority) {
	n.priority = priority
}

// Priority returns the Priority
func (n *Notification) Priority() NotificationPriority {
	return n.priority
}

// NewAlertDictionary creates a new AlertDictionary
func NewAlertDictionary() *AlertDictionary {
	return &AlertDictionary{}
}

// Encode encodes a notification packet
func (n *Notification) Encode() ([]byte, error) {

	token, err := hex.DecodeString(n.deviceToken)
	if err != nil {
		return nil, fmt.Errorf("failed decoding device token %q: %v", n.deviceToken, err)
	}

	payload, err := n.payload.ToJSON()
	if err != nil {
		return nil, err
	}

	if len(payload) > MaxPayloadLen {
		return nil, fmt.Errorf("payload is larger than the %v byte limit", MaxPayloadLen)
	}

	BE := binary.BigEndian

	frameBuffer := &bytes.Buffer{}

	binary.Write(frameBuffer, BE, uint8(deviceTokenItemid))
	binary.Write(frameBuffer, BE, uint16(deviceTokenLength))
	binary.Write(frameBuffer, BE, token)

	binary.Write(frameBuffer, BE, uint8(payloadItemid))
	binary.Write(frameBuffer, BE, uint16(len(payload)))
	binary.Write(frameBuffer, BE, payload)

	if n.identifier == nil {
		return nil, fmt.Errorf("identifier was not set")
	}
	binary.Write(frameBuffer, BE, uint8(notificationIdentifierItemid))
	binary.Write(frameBuffer, BE, uint16(notificationIdentifierLength))
	binary.Write(frameBuffer, BE, *n.identifier)

	binary.Write(frameBuffer, BE, uint8(expirationDateItemid))
	binary.Write(frameBuffer, BE, uint16(expirationDateLength))
	binary.Write(frameBuffer, BE, uint32(n.expiry.Unix()))

	binary.Write(frameBuffer, BE, uint8(priorityItemid))
	binary.Write(frameBuffer, BE, uint16(priorityLength))
	binary.Write(frameBuffer, BE, n.priority)

	buffer := bytes.NewBuffer([]byte{})
	binary.Write(buffer, BE, uint8(pushCommandValue))
	binary.Write(buffer, BE, uint32(frameBuffer.Len()))
	binary.Write(buffer, BE, frameBuffer.Bytes())

	return buffer.Bytes(), nil
}
<commit_msg>add Topic interface for all Payload, refactoring consts for create bytes message, add SetPayload method for Notification<commit_after>package apns

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"time"
)

// MaxPayloadLen is the maximum allowed payload length (after JSON encoding)
const MaxPayloadLen = 256

// Interface for universal notification payload
type Topic interface {
	Bytes() ([]byte, error)
}

// Payload represents a notification payload
type Payload map[string]interface{}

// NotificationIdentifier represents a notification identifier
type NotificationIdentifier uint32

// NotificationPriority represents a notification priority
type NotificationPriority uint8

// Notification represents a notification
type Notification struct {
	deviceToken string
	payload     Topic
	identifier  *NotificationIdentifier
	expiry      time.Time
	priority    NotificationPriority
}

// AlertDictionary is a localized alert text
type AlertDictionary struct {
	Body         string   `json:"body,omitempty"`
	ActionLocKey string   `json:"action-loc-key,omitempty"`
	LocKey       string   `json:"loc-key,omitempty"`
	LocArgs      []string `json:"loc-args,omitempty"`
	LaunchImage  string   `json:"launch-image,omitempty"`
}

const (
	// ImmediatePriority sets the push message to be sent immediately. This is
	// the default.
	ImmediatePriority NotificationPriority = 10
	// PowerSavingPriority sets the push message to be sent at a time that conserves power on the device receiving it
	PowerSavingPriority NotificationPriority = 5
)

const pushCommandValue uint8 = 2

// Constants related to the payload fields and their lengths.
const (
	deviceTokenItemid            uint8  = 1
	payloadItemid                uint8  = 2
	notificationIdentifierItemid uint8  = 3
	expirationDateItemid         uint8  = 4
	priorityItemid               uint8  = 5
	deviceTokenLength            uint16 = 32
	notificationIdentifierLength uint16 = 4
	expirationDateLength         uint16 = 4
	priorityLength               uint16 = 1
)

// SetAlertString sets the alert item as a string
func (p Payload) SetAlertString(alert string) {
	p.aps()["alert"] = alert
}

// SetAlertDictionary sets the alert item as a dictionary
func (p Payload) SetAlertDictionary(alert *AlertDictionary) {
	p.aps()["alert"] = alert
}

// SetBadge sets the badge item
func (p Payload) SetBadge(badge int) {
	p.aps()["badge"] = badge
}

// SetSound sets the sound item
func (p Payload) SetSound(sound string) {
	p.aps()["sound"] = sound
}

// Set sets a custom item outside the aps namespace
func (p Payload) Set(name string, value interface{}) {
	p[name] = value
}

// ToJSON encodes the Payload to JSON. The encoded payload cannot exceed
// MaxPayloadLen bytes
// deprecated
func (p Payload) ToJSON() ([]byte, error) {
	return p.Bytes()
}

// ToJSON encodes the Payload to JSON. The encoded payload cannot exceed
// MaxPayloadLen bytes
func (p Payload) Bytes() ([]byte, error) {
	return json.Marshal(p)
}

func (p Payload) aps() map[string]interface{} {
	if e, ok := p["aps"]; ok {
		if aps, ok := e.(map[string]interface{}); ok {
			return aps
		}
	}
	aps := map[string]interface{}{}
	p["aps"] = aps
	return aps
}

// NewNotification creates a new Notification
func NewNotification() *Notification {
	n := &Notification{}
	n.payload = Payload{}
	n.priority = ImmediatePriority
	return n
}

// SetDeviceToken sets the device token. Must be a 64 bytes hex string.
func (n *Notification) SetDeviceToken(token string) {
	n.deviceToken = token
}

// DeviceToken returns the device token
func (n *Notification) DeviceToken() string {
	return n.deviceToken
}

// SetPayload sets the Payload.
func (n *Notification) SetPayload(payload Topic) {
	n.payload = payload
}

// Payload returns the Payload
func (n *Notification) Payload() Topic {
	return n.payload
}

// SetIdentifier sets a custom identifier. Two notifications sent to the
// same Sender must have different identifiers. Sender automatically chooses an
// identifier if one was not set.
func (n *Notification) SetIdentifier(identifier NotificationIdentifier) {
	n.identifier = &identifier
}

// Identifier returns the Identifier
func (n *Notification) Identifier() NotificationIdentifier {
	if n.identifier != nil {
		return *n.identifier
	}
	return 0
}

// HasIdentifier returns whether the Identifier has been set
func (n *Notification) HasIdentifier() bool {
	return n.identifier != nil
}

// SetExpiry sets the expiry. Fractions of seconds are truncated. APNS discards
// the notification if it wasn't able to send it after this duration. An expiry
// of 0 means that the notification is discarded immediately by APNS if it can
// not be sent (the is the default).
func (n *Notification) SetExpiry(expiry time.Time) {
	n.expiry = expiry
}

// Expiry returns the expiry
func (n *Notification) Expiry() time.Time {
	return n.expiry
}

// SetPriority sets the priority. The default is ImmediatePriority.
func (n *Notification) SetPriority(priority NotificationPriority) {
	n.priority = priority
}

// Priority returns the Priority
func (n *Notification) Priority() NotificationPriority {
	return n.priority
}

// NewAlertDictionary creates a new AlertDictionary
func NewAlertDictionary() *AlertDictionary {
	return &AlertDictionary{}
}

// Encode encodes a notification packet
func (n *Notification) Encode() ([]byte, error) {
	token, err := hex.DecodeString(n.deviceToken)
	if err != nil {
		return nil, fmt.Errorf("failed decoding device token %q: %v", n.deviceToken, err)
	}

	payload, err := n.payload.Bytes()
	if err != nil {
		return nil, err
	}

	if len(payload) > MaxPayloadLen {
		return nil, fmt.Errorf("payload is larger than the %v byte limit", MaxPayloadLen)
	}

	BE := binary.BigEndian

	frameBuffer := &bytes.Buffer{}

	binary.Write(frameBuffer, BE, deviceTokenItemid)
	binary.Write(frameBuffer, BE, deviceTokenLength)
	binary.Write(frameBuffer, BE, token)

	binary.Write(frameBuffer, BE, payloadItemid)
	binary.Write(frameBuffer, BE, uint16(len(payload)))
	binary.Write(frameBuffer, BE, payload)

	if n.identifier == nil {
		return nil, fmt.Errorf("identifier was not set")
	}
	binary.Write(frameBuffer, BE, notificationIdentifierItemid)
	binary.Write(frameBuffer, BE, notificationIdentifierLength)
	binary.Write(frameBuffer, BE, *n.identifier)

	binary.Write(frameBuffer, BE, expirationDateItemid)
	binary.Write(frameBuffer, BE, expirationDateLength)
	binary.Write(frameBuffer, BE, uint32(n.expiry.Unix()))

	binary.Write(frameBuffer, BE, priorityItemid)
	binary.Write(frameBuffer, BE, priorityLength)
	binary.Write(frameBuffer, BE, n.priority)

	buffer := bytes.NewBuffer([]byte{})
	binary.Write(buffer, BE, pushCommandValue)
	binary.Write(buffer, BE, uint32(frameBuffer.Len()))
	binary.Write(buffer, BE, frameBuffer.Bytes())

	return buffer.Bytes(), nil
}
<|endoftext|>


<commit_before>package goopenzwave

// #include "gzw_notification.h"
// #include <stdlib.h>
import "C"
import (
	"fmt"
)

// NotificationType defines a type for the notification type enum.
type NotificationType int

const (
	NotificationTypeValueAdded NotificationType = iota
	NotificationTypeValueRemoved
	NotificationTypeValueChanged
	NotificationTypeValueRefreshed
	NotificationTypeGroup
	NotificationTypeNodeNew
	NotificationTypeNodeAdded
	NotificationTypeNodeRemoved
	NotificationTypeNodeProtocolInfo
	NotificationTypeNodeNaming
	NotificationTypeNodeEvent
	NotificationTypePollingDisabled
	NotificationTypePollingEnabled
	NotificationTypeSceneEvent
	NotificationTypeCreateButton
	NotificationTypeDeleteButton
	NotificationTypeButtonOn
	NotificationTypeButtonOff
	NotificationTypeDriverReady
	NotificationTypeDriverFailed
	NotificationTypeDriverReset
	NotificationTypeEssentialNodeQueriesComplete
	NotificationTypeNodeQueriesComplete
	NotificationTypeAwakeNodesQueried
	NotificationTypeAllNodesQueriedSomeDead
	NotificationTypeAllNodesQueried
	NotificationTypeNotification
	NotificationTypeDriverRemoved
	NotificationTypeControllerCommand
	NotificationTypeNodeReset
)

func (nt NotificationType) String() string {
	switch nt {
	case NotificationTypeValueAdded:
		return "ValueAdded"
	case NotificationTypeValueRemoved:
		return "ValueRemoved"
	case NotificationTypeValueChanged:
		return "ValueChanged"
	case NotificationTypeValueRefreshed:
		return "ValueRefreshed"
	case NotificationTypeGroup:
		return "Group"
	case NotificationTypeNodeNew:
		return "NodeNew"
	case NotificationTypeNodeAdded:
		return "NodeAdded"
	case NotificationTypeNodeRemoved:
		return "NodeRemoved"
	case NotificationTypeNodeProtocolInfo:
		return "NodeProtocolInfo"
	case NotificationTypeNodeNaming:
		return "NodeNaming"
	case NotificationTypeNodeEvent:
		return "NodeEvent"
	case NotificationTypePollingDisabled:
		return "PollingDisabled"
	case NotificationTypePollingEnabled:
		return "PollingEnabled"
	case NotificationTypeSceneEvent:
		return "SceneEvent"
	case NotificationTypeCreateButton:
		return "CreateButton"
	case NotificationTypeDeleteButton:
		return "DeleteButton"
	case NotificationTypeButtonOn:
		return "ButtonOn"
	case NotificationTypeButtonOff:
		return "ButtonOff"
	case NotificationTypeDriverReady:
		return "DriverReady"
	case NotificationTypeDriverFailed:
		return "DriverFailed"
	case NotificationTypeDriverReset:
		return "DriverReset"
	case NotificationTypeEssentialNodeQueriesComplete:
		return "EssentialNodeQueriesComplete"
	case NotificationTypeNodeQueriesComplete:
		return "NodeQueriesComplete"
	case NotificationTypeAwakeNodesQueried:
		return "AwakeNodesQueried"
	case NotificationTypeAllNodesQueriedSomeDead:
		return "AllNodesQueriedSomeDead"
	case NotificationTypeAllNodesQueried:
		return "AllNodesQueried"
	case NotificationTypeNotification:
		return "Notification"
	case NotificationTypeDriverRemoved:
		return "DriverRemoved"
	case NotificationTypeControllerCommand:
		return "ControllerCommand"
	case NotificationTypeNodeReset:
		return "NodeReset"
	}
	return "UNKNOWN"
}

// NotificationCode defines a type for the notification code enum.
type NotificationCode int

const (
	NotificationCodeMsgComplete NotificationCode = iota // C.notification_code_msgComplete
	NotificationCodeTimeout                             // C.notification_code_timeout
	NotificationCodeNoOperation                         // C.notification_code_noOperation
	NotificationCodeAwake                               // C.notification_code_awake
	NotificationCodeSleep                               // C.notification_code_sleep
	NotificationCodeDead                                // C.notification_code_dead
	NotificationCodeAlive                               // C.notification_code_alive
)

func (nc NotificationCode) String() string {
	switch nc {
	case NotificationCodeMsgComplete:
		return "MsgComplete"
	case NotificationCodeTimeout:
		return "Timeout"
	case NotificationCodeNoOperation:
		return "NoOperation"
	case NotificationCodeAwake:
		return "Awake"
	case NotificationCodeSleep:
		return "Sleep"
	case NotificationCodeDead:
		return "Dead"
	case NotificationCodeAlive:
		return "Alive"
	}
	return "UNKNOWN"
}

// Notification is a container for the C++ OpenZWave library Notification class.
type Notification struct {
	Type         NotificationType
	HomeID       uint32
	NodeID       uint8
	ValueID      *ValueID
	GroupIDX     *uint8
	Event        *uint8
	ButtonID     *uint8
	SceneID      *uint8
	Notification *NotificationCode
}

// buildNotification builds a new Notification filled with the relevant
// information from the OpenZWave::Notification as received from the OpenZWave
// library.
func buildNotification(n C.notification_t) *Notification {
	notification := &Notification{
		HomeID: uint32(C.notification_getHomeId(n)),
		NodeID: uint8(C.notification_getNodeId(n)),
	}

	switch C.notification_getType(n) {
	case C.notification_type_valueAdded:
		notification.Type = NotificationTypeValueAdded
	case C.notification_type_valueRemoved:
		notification.Type = NotificationTypeValueRemoved
	case C.notification_type_valueChanged:
		notification.Type = NotificationTypeValueChanged
	case C.notification_type_valueRefreshed:
		notification.Type = NotificationTypeValueRefreshed
	case C.notification_type_group:
		notification.Type = NotificationTypeGroup
	case C.notification_type_nodeNew:
		notification.Type = NotificationTypeNodeNew
	case C.notification_type_nodeAdded:
		notification.Type = NotificationTypeNodeAdded
	case C.notification_type_nodeRemoved:
		notification.Type = NotificationTypeNodeRemoved
	case C.notification_type_nodeProtocolInfo:
		notification.Type = NotificationTypeNodeProtocolInfo
	case C.notification_type_nodeNaming:
		notification.Type = NotificationTypeNodeNaming
	case C.notification_type_nodeEvent:
		notification.Type = NotificationTypeNodeEvent
	case C.notification_type_pollingDisabled:
		notification.Type = NotificationTypePollingDisabled
	case C.notification_type_pollingEnabled:
		notification.Type = NotificationTypePollingEnabled
	case C.notification_type_sceneEvent:
		notification.Type = NotificationTypeSceneEvent
	case C.notification_type_createButton:
		notification.Type = NotificationTypeCreateButton
	case C.notification_type_deleteButton:
		notification.Type = NotificationTypeDeleteButton
	case C.notification_type_buttonOn:
		notification.Type = NotificationTypeButtonOn
	case C.notification_type_buttonOff:
		notification.Type = NotificationTypeButtonOff
	case C.notification_type_driverReady:
		notification.Type = NotificationTypeDriverReady
	case C.notification_type_driverFailed:
		notification.Type = NotificationTypeDriverFailed
	case C.notification_type_driverReset:
		notification.Type = NotificationTypeDriverReset
	case C.notification_type_essentialNodeQueriesComplete:
		notification.Type = NotificationTypeEssentialNodeQueriesComplete
	case C.notification_type_nodeQueriesComplete:
		notification.Type = NotificationTypeNodeQueriesComplete
	case C.notification_type_awakeNodesQueried:
		notification.Type = NotificationTypeAwakeNodesQueried
	case C.notification_type_allNodesQueriedSomeDead:
		notification.Type = NotificationTypeAllNodesQueriedSomeDead
	case C.notification_type_allNodesQueried:
		notification.Type = NotificationTypeAllNodesQueried
	case C.notification_type_notification:
		notification.Type = NotificationTypeNotification
	case C.notification_type_driverRemoved:
		notification.Type = NotificationTypeDriverRemoved
	case C.notification_type_controllerCommand:
		notification.Type = NotificationTypeControllerCommand
	case C.notification_type_nodeReset:
		notification.Type = NotificationTypeNodeReset
	}

	switch notification.Type {
	case NotificationTypeCreateButton, NotificationTypeDeleteButton, NotificationTypeButtonOn, NotificationTypeButtonOff:
		if notification.ButtonID == nil {
			notification.ButtonID = new(uint8)
		}
		*(notification.ButtonID) = uint8(C.notification_getButtonId(n))

	case NotificationTypeNodeEvent:
		if notification.Event == nil {
			notification.Event = new(uint8)
		}
		*(notification.Event) = uint8(C.notification_getEvent(n))

	case NotificationTypeGroup:
		if notification.GroupIDX == nil {
			notification.GroupIDX = new(uint8)
		}
		*(notification.GroupIDX) = uint8(C.notification_getGroupIdx(n))

	case NotificationTypeNotification:
		if notification.Notification == nil {
			notification.Notification = new(NotificationCode)
		}
		switch C.notification_getNotification(n) {
		case C.notification_code_msgComplete:
			*notification.Notification = NotificationCodeMsgComplete
		case C.notification_code_timeout:
			*notification.Notification = NotificationCodeTimeout
		case C.notification_code_noOperation:
			*notification.Notification = NotificationCodeNoOperation
		case C.notification_code_awake:
			*notification.Notification = NotificationCodeAwake
		case C.notification_code_sleep:
			*notification.Notification = NotificationCodeSleep
		case C.notification_code_dead:
			*notification.Notification = NotificationCodeDead
		case C.notification_code_alive:
			*notification.Notification = NotificationCodeAlive
		}

	case NotificationTypeControllerCommand:
		if notification.Event == nil {
			notification.Event = new(uint8)
		}
		*(notification.Event) = uint8(C.notification_getEvent(n))
		if notification.Notification == nil {
			notification.Notification = new(NotificationCode)
		}
		*(notification.Notification) = NotificationCode(C.notification_getNotification(n))

	case NotificationTypeSceneEvent:
		if notification.SceneID == nil {
			notification.SceneID = new(uint8)
		}
		*(notification.SceneID) = uint8(C.notification_getSceneId(n))
	}

	return notification
}

func (n *Notification) String() string {
	var pointed []string
	if n.ValueID != nil {
		pointed = append(pointed, fmt.Sprintf("ValueID: %s", n.ValueID))
	}
	if n.GroupIDX != nil {
		pointed = append(pointed, fmt.Sprintf("GroupIDX: %d", *(n.GroupIDX)))
	}
	if n.Event != nil {
		pointed = append(pointed, fmt.Sprintf("Event: %d", *(n.Event)))
	}
	if n.ButtonID != nil {
		pointed = append(pointed, fmt.Sprintf("ButtonID: %d", *(n.ButtonID)))
	}
	if n.SceneID != nil {
		pointed = append(pointed, fmt.Sprintf("SceneID: %d", *(n.SceneID)))
	}
	if n.Notification != nil {
		pointed = append(pointed, fmt.Sprintf("Notification: %d", *(n.Notification)))
	}
	output := fmt.Sprintf("<%s, HomeID: %x, NodeID: %d", n.Type, n.HomeID, n.NodeID)
	for i := range pointed {
		output = fmt.Sprintf("%s, %s", output, pointed[i])
	}
	output = fmt.Sprintf("%s>", output)
	return output
}
<commit_msg>notification: handle all notification types<commit_after>package goopenzwave

// #include "gzw_notification.h"
// #include <stdlib.h>
import "C"
import (
	"fmt"
)

// NotificationType defines a type for the notification type enum.
type NotificationType int

const (
	NotificationTypeValueAdded NotificationType = iota
	NotificationTypeValueRemoved
	NotificationTypeValueChanged
	NotificationTypeValueRefreshed
	NotificationTypeGroup
	NotificationTypeNodeNew
	NotificationTypeNodeAdded
	NotificationTypeNodeRemoved
	NotificationTypeNodeProtocolInfo
	NotificationTypeNodeNaming
	NotificationTypeNodeEvent
	NotificationTypePollingDisabled
	NotificationTypePollingEnabled
	NotificationTypeSceneEvent
	NotificationTypeCreateButton
	NotificationTypeDeleteButton
	NotificationTypeButtonOn
	NotificationTypeButtonOff
	NotificationTypeDriverReady
	NotificationTypeDriverFailed
	NotificationTypeDriverReset
	NotificationTypeEssentialNodeQueriesComplete
	NotificationTypeNodeQueriesComplete
	NotificationTypeAwakeNodesQueried
	NotificationTypeAllNodesQueriedSomeDead
	NotificationTypeAllNodesQueried
	NotificationTypeNotification
	NotificationTypeDriverRemoved
	NotificationTypeControllerCommand
	NotificationTypeNodeReset
)

func (nt NotificationType) String() string {
	switch nt {
	case NotificationTypeValueAdded:
		return "ValueAdded"
	case NotificationTypeValueRemoved:
		return "ValueRemoved"
	case NotificationTypeValueChanged:
		return "ValueChanged"
	case NotificationTypeValueRefreshed:
		return "ValueRefreshed"
	case NotificationTypeGroup:
		return "Group"
	case NotificationTypeNodeNew:
		return "NodeNew"
	case NotificationTypeNodeAdded:
		return "NodeAdded"
	case NotificationTypeNodeRemoved:
		return "NodeRemoved"
	case NotificationTypeNodeProtocolInfo:
		return "NodeProtocolInfo"
	case NotificationTypeNodeNaming:
		return "NodeNaming"
	case NotificationTypeNodeEvent:
		return "NodeEvent"
	case NotificationTypePollingDisabled:
		return "PollingDisabled"
	case NotificationTypePollingEnabled:
		return "PollingEnabled"
	case NotificationTypeSceneEvent:
		return "SceneEvent"
	case NotificationTypeCreateButton:
		return "CreateButton"
	case NotificationTypeDeleteButton:
		return "DeleteButton"
	case NotificationTypeButtonOn:
		return "ButtonOn"
	case NotificationTypeButtonOff:
		return "ButtonOff"
	case NotificationTypeDriverReady:
		return "DriverReady"
	case NotificationTypeDriverFailed:
		return "DriverFailed"
	case NotificationTypeDriverReset:
		return "DriverReset"
	case NotificationTypeEssentialNodeQueriesComplete:
		return "EssentialNodeQueriesComplete"
	case NotificationTypeNodeQueriesComplete:
		return "NodeQueriesComplete"
	case NotificationTypeAwakeNodesQueried:
		return "AwakeNodesQueried"
	case NotificationTypeAllNodesQueriedSomeDead:
		return "AllNodesQueriedSomeDead"
	case NotificationTypeAllNodesQueried:
		return "AllNodesQueried"
	case NotificationTypeNotification:
		return "Notification"
	case NotificationTypeDriverRemoved:
		return "DriverRemoved"
	case NotificationTypeControllerCommand:
		return "ControllerCommand"
	case NotificationTypeNodeReset:
		return "NodeReset"
	}
	return "UNKNOWN"
}

// NotificationCode defines a type for the notification code enum.
type NotificationCode int

const (
	NotificationCodeMsgComplete NotificationCode = iota // C.notification_code_msgComplete
	NotificationCodeTimeout                             // C.notification_code_timeout
	NotificationCodeNoOperation                         // C.notification_code_noOperation
	NotificationCodeAwake                               // C.notification_code_awake
	NotificationCodeSleep                               // C.notification_code_sleep
	NotificationCodeDead                                // C.notification_code_dead
	NotificationCodeAlive                               // C.notification_code_alive
)

func (nc NotificationCode) String() string {
	switch nc {
	case NotificationCodeMsgComplete:
		return "MsgComplete"
	case NotificationCodeTimeout:
		return "Timeout"
	case NotificationCodeNoOperation:
		return "NoOperation"
	case NotificationCodeAwake:
		return "Awake"
	case NotificationCodeSleep:
		return "Sleep"
	case NotificationCodeDead:
		return "Dead"
	case NotificationCodeAlive:
		return "Alive"
	}
	return "UNKNOWN"
}

// Notification is a container for the C++ OpenZWave library Notification class.
type Notification struct {
	Type         NotificationType
	HomeID       uint32
	NodeID       uint8
	ValueID      *ValueID
	GroupIDX     *uint8
	Event        *uint8
	ButtonID     *uint8
	SceneID      *uint8
	Notification *NotificationCode
}

// buildNotification builds a new Notification filled with the relevant
// information from the OpenZWave::Notification as received from the OpenZWave
// library.
func buildNotification(n C.notification_t) *Notification {
	notification := &Notification{
		HomeID: uint32(C.notification_getHomeId(n)),
		NodeID: uint8(C.notification_getNodeId(n)),
	}

	switch C.notification_getType(n) {
	case C.notification_type_valueAdded:
		notification.Type = NotificationTypeValueAdded
	case C.notification_type_valueRemoved:
		notification.Type = NotificationTypeValueRemoved
	case C.notification_type_valueChanged:
		notification.Type = NotificationTypeValueChanged
	case C.notification_type_valueRefreshed:
		notification.Type = NotificationTypeValueRefreshed
	case C.notification_type_group:
		notification.Type = NotificationTypeGroup
	case C.notification_type_nodeNew:
		notification.Type = NotificationTypeNodeNew
	case C.notification_type_nodeAdded:
		notification.Type = NotificationTypeNodeAdded
	case C.notification_type_nodeRemoved:
		notification.Type = NotificationTypeNodeRemoved
	case C.notification_type_nodeProtocolInfo:
		notification.Type = NotificationTypeNodeProtocolInfo
	case C.notification_type_nodeNaming:
		notification.Type = NotificationTypeNodeNaming
	case C.notification_type_nodeEvent:
		notification.Type = NotificationTypeNodeEvent
	case C.notification_type_pollingDisabled:
		notification.Type = NotificationTypePollingDisabled
	case C.notification_type_pollingEnabled:
		notification.Type = NotificationTypePollingEnabled
	case C.notification_type_sceneEvent:
		notification.Type = NotificationTypeSceneEvent
	case C.notification_type_createButton:
		notification.Type = NotificationTypeCreateButton
	case C.notification_type_deleteButton:
		notification.Type = NotificationTypeDeleteButton
	case C.notification_type_buttonOn:
		notification.Type = NotificationTypeButtonOn
	case C.notification_type_buttonOff:
		notification.Type = NotificationTypeButtonOff
	case C.notification_type_driverReady:
		notification.Type = NotificationTypeDriverReady
	case C.notification_type_driverFailed:
		notification.Type = NotificationTypeDriverFailed
	case C.notification_type_driverReset:
		notification.Type = NotificationTypeDriverReset
	case C.notification_type_essentialNodeQueriesComplete:
		notification.Type = NotificationTypeEssentialNodeQueriesComplete
	case C.notification_type_nodeQueriesComplete:
		notification.Type = NotificationTypeNodeQueriesComplete
	case C.notification_type_awakeNodesQueried:
		notification.Type = NotificationTypeAwakeNodesQueried
	case C.notification_type_allNodesQueriedSomeDead:
		notification.Type = NotificationTypeAllNodesQueriedSomeDead
	case C.notification_type_allNodesQueried:
		notification.Type = NotificationTypeAllNodesQueried
	case C.notification_type_notification:
		notification.Type = NotificationTypeNotification
	case C.notification_type_driverRemoved:
		notification.Type = NotificationTypeDriverRemoved
	case C.notification_type_controllerCommand:
		notification.Type = NotificationTypeControllerCommand
	case C.notification_type_nodeReset:
		notification.Type = NotificationTypeNodeReset
	}

	switch notification.Type {
	case NotificationTypeValueAdded, NotificationTypeValueRemoved, NotificationTypeValueChanged, NotificationTypeValueRefreshed:
		notification.ValueID = buildValueID(C.notification_getValueId(n))

	case NotificationTypeGroup:
		if notification.GroupIDX == nil {
			notification.GroupIDX = new(uint8)
		}
		*(notification.GroupIDX) = uint8(C.notification_getGroupIdx(n))

	case NotificationTypeNodeNew, NotificationTypeNodeAdded, NotificationTypeNodeRemoved, NotificationTypeNodeProtocolInfo, NotificationTypeNodeNaming:
		// No notification info.

	case NotificationTypeNodeEvent:
		if notification.Event == nil {
			notification.Event = new(uint8)
		}
		*(notification.Event) = uint8(C.notification_getEvent(n))

	case NotificationTypePollingDisabled, NotificationTypePollingEnabled:
		// No notification info.

	case NotificationTypeSceneEvent:
		if notification.SceneID == nil {
			notification.SceneID = new(uint8)
		}
		*(notification.SceneID) = uint8(C.notification_getSceneId(n))

	case NotificationTypeCreateButton, NotificationTypeDeleteButton, NotificationTypeButtonOn, NotificationTypeButtonOff:
		if notification.ButtonID == nil {
			notification.ButtonID = new(uint8)
		}
		*(notification.ButtonID) = uint8(C.notification_getButtonId(n))

	case NotificationTypeDriverReady, NotificationTypeDriverFailed, NotificationTypeDriverReset:
		// No notification info.

	case NotificationTypeEssentialNodeQueriesComplete, NotificationTypeNodeQueriesComplete, NotificationTypeAwakeNodesQueried, NotificationTypeAllNodesQueriedSomeDead, NotificationTypeAllNodesQueried:
		// No notification info.

	case NotificationTypeNotification:
		if notification.Notification == nil {
			notification.Notification = new(NotificationCode)
		}
		switch C.notification_getNotification(n) {
		case C.notification_code_msgComplete:
			*notification.Notification = NotificationCodeMsgComplete
		case C.notification_code_timeout:
			*notification.Notification = NotificationCodeTimeout
		case C.notification_code_noOperation:
			*notification.Notification = NotificationCodeNoOperation
		case C.notification_code_awake:
			*notification.Notification = NotificationCodeAwake
		case C.notification_code_sleep:
			*notification.Notification = NotificationCodeSleep
		case C.notification_code_dead:
			*notification.Notification = NotificationCodeDead
		case C.notification_code_alive:
			*notification.Notification = NotificationCodeAlive
		}

	case NotificationTypeDriverRemoved:
		// No notification info.

	case NotificationTypeControllerCommand:
		if notification.Event == nil {
			notification.Event = new(uint8)
		}
		*(notification.Event) = uint8(C.notification_getEvent(n))
		if notification.Notification == nil {
			notification.Notification = new(NotificationCode)
		}
		*(notification.Notification) = NotificationCode(C.notification_getNotification(n))

	case NotificationTypeNodeReset:
		// No notification info.
	}

	return notification
}

func (n *Notification) String() string {
	var pointed []string
	if n.ValueID != nil {
		pointed = append(pointed, fmt.Sprintf("ValueID: %s", n.ValueID))
	}
	if n.GroupIDX != nil {
		pointed = append(pointed, fmt.Sprintf("GroupIDX: %d", *(n.GroupIDX)))
	}
	if n.Event != nil {
		pointed = append(pointed, fmt.Sprintf("Event: %d", *(n.Event)))
	}
	if n.ButtonID != nil {
		pointed = append(pointed, fmt.Sprintf("ButtonID: %d", *(n.ButtonID)))
	}
	if n.SceneID != nil {
		pointed = append(pointed, fmt.Sprintf("SceneID: %d", *(n.SceneID)))
	}
	if n.Notification != nil {
		pointed = append(pointed, fmt.Sprintf("Notification: %d", *(n.Notification)))
	}
	output := fmt.Sprintf("<%s, HomeID: %x, NodeID: %d", n.Type, n.HomeID, n.NodeID)
	for i := range pointed {
		output = fmt.Sprintf("%s, %s", output, pointed[i])
	}
	output = fmt.Sprintf("%s>", output)
	return output
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manageddisk

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

	"github.com/r3labs/terraform/helper/schema"

	aes "github.com/ernestio/crypto/aes"
	"github.com/ernestio/ernestprovider/event"
	"github.com/ernestio/ernestprovider/providers/azure"
)

// Event : This is the Ernest representation of an azure lb
type Event struct {
	event.Base
	ID                 string            `json:"id"`
	Name               string            `json:"name" validate:"required"`
	ResourceGroupName  string            `json:"resource_group_name" validate:"required"`
	Location           string            `json:"location"`
	StorageAccountType string            `json:"storage_account_type"`
	CreateOption       string            `json:"create_option"`
	SourceURI          string            `json:"source_uri"`
	SourceResourceID   string            `json:"source_resource_id"`
	OSType             string            `json:"os_type"`
	DiskSizeGB         int32             `json:"disk_size_gb"`
	Tags               map[string]string `json:"tags"`
	ClientID           string            `json:"azure_client_id"`
	ClientSecret       string            `json:"azure_client_secret"`
	TenantID           string            `json:"azure_tenant_id"`
	SubscriptionID     string            `json:"azure_subscription_id"`
	Environment        string            `json:"environment"`
	ErrorMessage       string            `json:"error,omitempty"`
	Components         []json.RawMessage `json:"components"`
	CryptoKey          string            `json:"-"`
	Validator          *event.Validator  `json:"-"`
}

// New : Constructor
func New(subject, cryptoKey string, body []byte, val *event.Validator) (event.Event, error) {
	var ev event.Resource
	ev = &Event{CryptoKey: cryptoKey, Validator: val}
	body = []byte(strings.Replace(string(body), `"_component":"managed_disks"`, `"_component":"managed_disk"`, 1))
	if err := json.Unmarshal(body, &ev); err != nil {
		err := fmt.Errorf("Error on input message : %s", err)
		return nil, err
	}

	return azure.New(subject, "azurerm_managed_disk", body, val, ev)
}

// SetComponents : ....
func (ev *Event) SetComponents(components []event.Event) {
	for _, v := range components {
		ev.Components = append(ev.Components, v.GetBody())
	}
}

// ValidateID : determines if the given id is valid for this resource type
func (ev *Event) ValidateID(id string) bool {
	parts := strings.Split(strings.ToLower(id), "/")
	if len(parts) != 9 {
		return false
	}
	if parts[6] != "microsoft.compute" {
		return false
	}
	if parts[7] != "disks" {
		return false
	}
	return true
}

// SetID : id setter
func (ev *Event) SetID(id string) {
	ev.ID = id
}

// GetID : id getter
func (ev *Event) GetID() string {
	return ev.ID
}

// SetState : state setter
func (ev *Event) SetState(state string) {
	ev.State = state
}

// ResourceDataToEvent : Translates a ResourceData on a valid Ernest Event
func (ev *Event) ResourceDataToEvent(d *schema.ResourceData) error {
	idParts := strings.Split(d.Id(), "/")
	ev.ID = d.Id()
	ev.Name = idParts[len(idParts)-1]
	ev.ComponentID = "lb::" + ev.Name
	ev.ResourceGroupName = d.Get("resource_group_name").(string)
	ev.Location = d.Get("location").(string)
	ev.StorageAccountType = d.Get("storage_account_type").(string)
	ev.CreateOption = d.Get("create_option").(string)
	ev.SourceURI = d.Get("source_uri").(string)
	ev.SourceResourceID = d.Get("source_resource_id").(string)
	ev.OSType = d.Get("os_type").(string)
	ev.DiskSizeGB = d.Get("disk_size_gb").(int32)
	tags := make(map[string]string, 0)
	for k, v := range d.Get("tags").(map[string]interface{}) {
		tags[k] = v.(string)
	}
	ev.Tags = tags

	return nil
}

// EventToResourceData : Translates the current event on a valid ResourceData
func (ev *Event) EventToResourceData(d *schema.ResourceData) error {
	crypto := aes.New()

	encFields := make(map[string]string)
	encFields["subscription_id"] = ev.SubscriptionID
	encFields["client_id"] = ev.ClientID
	encFields["client_secret"] = ev.ClientSecret
	encFields["tenant_id"] = ev.TenantID
	encFields["environment"] = ev.Environment
	for k, v := range encFields {
		dec, err := crypto.Decrypt(v, ev.CryptoKey)
		if err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
		if err := d.Set(k, dec); err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
	}

	fields := make(map[string]interface{})
	fields["name"] = ev.Name
	fields["location"] = ev.Location
	fields["resource_group_name"] = ev.ResourceGroupName
	fields["storage_account_type"] = ev.StorageAccountType
	fields["create_option"] = ev.CreateOption
	fields["source_uri"] = ev.SourceURI
	fields["source_resource_id"] = ev.SourceResourceID
	fields["os_type"] = ev.OSType
	fields["disk_size_gb"] = ev.DiskSizeGB
	fields["tags"] = ev.Tags
	for k, v := range fields {
		if err := d.Set(k, v); err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
	}

	return nil
}

// Clone : will mark the event as errored
func (ev *Event) Clone() (event.Event, error) {
	body, _ := json.Marshal(ev)
	return New(ev.Subject, ev.CryptoKey, body, ev.Validator)
}

// Error : will mark the event as errored
func (ev *Event) Error(err error) {
	ev.ErrorMessage = err.Error()
	ev.Body, err = json.Marshal(ev)
}
<commit_msg>don't assign empty string for image on managed disk<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package manageddisk

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

	"github.com/r3labs/terraform/helper/schema"

	aes "github.com/ernestio/crypto/aes"
	"github.com/ernestio/ernestprovider/event"
	"github.com/ernestio/ernestprovider/providers/azure"
)

// Event : This is the Ernest representation of an azure lb
type Event struct {
	event.Base
	ID                 string            `json:"id"`
	Name               string            `json:"name" validate:"required"`
	ResourceGroupName  string            `json:"resource_group_name" validate:"required"`
	Location           string            `json:"location"`
	StorageAccountType string            `json:"storage_account_type"`
	CreateOption       string            `json:"create_option"`
	SourceURI          string            `json:"source_uri"`
	SourceResourceID   string            `json:"source_resource_id"`
	OSType             string            `json:"os_type"`
	DiskSizeGB         int32             `json:"disk_size_gb"`
	Tags               map[string]string `json:"tags"`
	ClientID           string            `json:"azure_client_id"`
	ClientSecret       string            `json:"azure_client_secret"`
	TenantID           string            `json:"azure_tenant_id"`
	SubscriptionID     string            `json:"azure_subscription_id"`
	Environment        string            `json:"environment"`
	ErrorMessage       string            `json:"error,omitempty"`
	Components         []json.RawMessage `json:"components"`
	CryptoKey          string            `json:"-"`
	Validator          *event.Validator  `json:"-"`
}

// New : Constructor
func New(subject, cryptoKey string, body []byte, val *event.Validator) (event.Event, error) {
	var ev event.Resource
	ev = &Event{CryptoKey: cryptoKey, Validator: val}
	body = []byte(strings.Replace(string(body), `"_component":"managed_disks"`, `"_component":"managed_disk"`, 1))
	if err := json.Unmarshal(body, &ev); err != nil {
		err := fmt.Errorf("Error on input message : %s", err)
		return nil, err
	}

	return azure.New(subject, "azurerm_managed_disk", body, val, ev)
}

// SetComponents : ....
func (ev *Event) SetComponents(components []event.Event) {
	for _, v := range components {
		ev.Components = append(ev.Components, v.GetBody())
	}
}

// ValidateID : determines if the given id is valid for this resource type
func (ev *Event) ValidateID(id string) bool {
	parts := strings.Split(strings.ToLower(id), "/")
	if len(parts) != 9 {
		return false
	}
	if parts[6] != "microsoft.compute" {
		return false
	}
	if parts[7] != "disks" {
		return false
	}
	return true
}

// SetID : id setter
func (ev *Event) SetID(id string) {
	ev.ID = id
}

// GetID : id getter
func (ev *Event) GetID() string {
	return ev.ID
}

// SetState : state setter
func (ev *Event) SetState(state string) {
	ev.State = state
}

// ResourceDataToEvent : Translates a ResourceData on a valid Ernest Event
func (ev *Event) ResourceDataToEvent(d *schema.ResourceData) error {
	idParts := strings.Split(d.Id(), "/")
	ev.ID = d.Id()
	ev.Name = idParts[len(idParts)-1]
	ev.ComponentID = "lb::" + ev.Name
	ev.ResourceGroupName = d.Get("resource_group_name").(string)
	ev.Location = d.Get("location").(string)
	ev.StorageAccountType = d.Get("storage_account_type").(string)
	ev.CreateOption = d.Get("create_option").(string)
	ev.SourceURI = d.Get("source_uri").(string)
	ev.SourceResourceID = d.Get("source_resource_id").(string)
	ev.OSType = d.Get("os_type").(string)
	ev.DiskSizeGB = d.Get("disk_size_gb").(int32)
	tags := make(map[string]string, 0)
	for k, v := range d.Get("tags").(map[string]interface{}) {
		tags[k] = v.(string)
	}
	ev.Tags = tags

	return nil
}

// EventToResourceData : Translates the current event on a valid ResourceData
func (ev *Event) EventToResourceData(d *schema.ResourceData) error {
	crypto := aes.New()

	encFields := make(map[string]string)
	encFields["subscription_id"] = ev.SubscriptionID
	encFields["client_id"] = ev.ClientID
	encFields["client_secret"] = ev.ClientSecret
	encFields["tenant_id"] = ev.TenantID
	encFields["environment"] = ev.Environment
	for k, v := range encFields {
		dec, err := crypto.Decrypt(v, ev.CryptoKey)
		if err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
		if err := d.Set(k, dec); err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
	}

	fields := make(map[string]interface{})
	fields["name"] = ev.Name
	fields["location"] = ev.Location
	fields["resource_group_name"] = ev.ResourceGroupName
	fields["storage_account_type"] = ev.StorageAccountType
	fields["create_option"] = ev.CreateOption
	if ev.SourceURI != "" {
		fields["source_uri"] = ev.SourceURI
	}
	if ev.SourceResourceID != "" {
		fields["source_resource_id"] = ev.SourceResourceID
	}
	fields["os_type"] = ev.OSType
	fields["disk_size_gb"] = ev.DiskSizeGB
	fields["tags"] = ev.Tags
	for k, v := range fields {
		if err := d.Set(k, v); err != nil {
			err := fmt.Errorf("Field '%s' not valid : %s", k, err)
			ev.Log("error", err.Error())
			return err
		}
	}

	return nil
}

// Clone : will mark the event as errored
func (ev *Event) Clone() (event.Event, error) {
	body, _ := json.Marshal(ev)
	return New(ev.Subject, ev.CryptoKey, body, ev.Validator)
}

// Error : will mark the event as errored
func (ev *Event) Error(err error) {
	ev.ErrorMessage = err.Error()
	ev.Body, err = json.Marshal(ev)
}
<|endoftext|>


<commit_before>package system

import (
	"fmt"
	"net"
	"os/exec"
	"strings"
	"time"

	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink"
)

const (
	runtimeNetworkPath = "/run/systemd/network"
)

func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) {
	defer func() {
		if e := restartNetworkd(); e != nil {
			err = e
			return
		}
		// TODO(crawford): Get rid of this once networkd fixes the race
		// https://bugs.freedesktop.org/show_bug.cgi?id=76077
		time.Sleep(5 * time.Second)
		if e := restartNetworkd(); e != nil {
			err = e
		}
	}()

	if err = downNetworkInterfaces(interfaces); err != nil {
		return
	}

	if err = maybeProbe8012q(interfaces); err != nil {
		return
	}
	return maybeProbeBonding(interfaces)
}

func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
	sysInterfaceMap := make(map[string]*net.Interface)
	if systemInterfaces, err := net.Interfaces(); err == nil {
		for _, iface := range systemInterfaces {
			iface := iface
			sysInterfaceMap[iface.Name] = &iface
		}
	} else {
		return err
	}

	for _, iface := range interfaces {
		if systemInterface, ok := sysInterfaceMap[iface.Name()]; ok {
			if err := netlink.NetworkLinkDown(systemInterface); err != nil {
				fmt.Printf("Error while downing interface %q (%s). Continuing...\n", systemInterface.Name, err)
			}
		}
	}

	return nil
}

func maybeProbe8012q(interfaces []network.InterfaceGenerator) error {
	for _, iface := range interfaces {
		if iface.Type() == "vlan" {
			return exec.Command("modprobe", "8021q").Run()
		}
	}
	return nil
}

func maybeProbeBonding(interfaces []network.InterfaceGenerator) error {
	args := []string{"bonding"}
	for _, iface := range interfaces {
		if iface.Type() == "bond" {
			args = append(args, strings.Split(iface.ModprobeParams(), " ")...)
			break
		}
	}
	return exec.Command("modprobe", args...).Run()
}

func restartNetworkd() error {
	_, err := NewUnitManager("").RunUnitCommand("restart", "systemd-networkd.service")
	return err
}

func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error {
	for _, iface := range interfaces {
		filename := fmt.Sprintf("%s.netdev", iface.Filename())
		if err := writeConfig(filename, iface.Netdev()); err != nil {
			return err
		}
		filename = fmt.Sprintf("%s.link", iface.Filename())
		if err := writeConfig(filename, iface.Link()); err != nil {
			return err
		}
		filename = fmt.Sprintf("%s.network", iface.Filename())
		if err := writeConfig(filename, iface.Network()); err != nil {
			return err
		}
	}
	return nil
}

func writeConfig(filename string, config string) error {
	if config == "" {
		return nil
	}
	_, err := WriteFile(&File{Content: config, Path: filename}, runtimeNetworkPath)
	return err
}
<commit_msg>system: Add more logging for networkd<commit_after>package system

import (
	"fmt"
	"log"
	"net"
	"os/exec"
	"strings"
	"time"

	"github.com/coreos/coreos-cloudinit/network"
	"github.com/coreos/coreos-cloudinit/third_party/github.com/dotcloud/docker/pkg/netlink"
)

const (
	runtimeNetworkPath = "/run/systemd/network"
)

func RestartNetwork(interfaces []network.InterfaceGenerator) (err error) {
	defer func() {
		if e := restartNetworkd(); e != nil {
			err = e
			return
		}
		// TODO(crawford): Get rid of this once networkd fixes the race
		// https://bugs.freedesktop.org/show_bug.cgi?id=76077
		time.Sleep(5 * time.Second)
		if e := restartNetworkd(); e != nil {
			err = e
		}
	}()

	if err = downNetworkInterfaces(interfaces); err != nil {
		return
	}

	if err = maybeProbe8012q(interfaces); err != nil {
		return
	}
	return maybeProbeBonding(interfaces)
}

func downNetworkInterfaces(interfaces []network.InterfaceGenerator) error {
	sysInterfaceMap := make(map[string]*net.Interface)
	if systemInterfaces, err := net.Interfaces(); err == nil {
		for _, iface := range systemInterfaces {
			iface := iface
			sysInterfaceMap[iface.Name] = &iface
		}
	} else {
		return err
	}

	for _, iface := range interfaces {
		if systemInterface, ok := sysInterfaceMap[iface.Name()]; ok {
			log.Printf("Taking down interface %q\n", systemInterface.Name)
			if err := netlink.NetworkLinkDown(systemInterface); err != nil {
				fmt.Printf("Error while downing interface %q (%s). Continuing...\n", systemInterface.Name, err)
			}
		}
	}

	return nil
}

func maybeProbe8012q(interfaces []network.InterfaceGenerator) error {
	for _, iface := range interfaces {
		if iface.Type() == "vlan" {
			log.Printf("Probing LKM %q (%q)\n", "8021q", "8021q")
			return exec.Command("modprobe", "8021q").Run()
		}
	}
	return nil
}

func maybeProbeBonding(interfaces []network.InterfaceGenerator) error {
	args := []string{"bonding"}
	for _, iface := range interfaces {
		if iface.Type() == "bond" {
			args = append(args, strings.Split(iface.ModprobeParams(), " ")...)
			break
		}
	}
	log.Printf("Probing LKM %q (%q)\n", "bonding", args)
	return exec.Command("modprobe", args...).Run()
}

func restartNetworkd() error {
	log.Printf("Restarting networkd.service\n")
	_, err := NewUnitManager("").RunUnitCommand("restart", "systemd-networkd.service")
	return err
}

func WriteNetworkdConfigs(interfaces []network.InterfaceGenerator) error {
	for _, iface := range interfaces {
		filename := fmt.Sprintf("%s.netdev", iface.Filename())
		if err := writeConfig(filename, iface.Netdev()); err != nil {
			return err
		}
		filename = fmt.Sprintf("%s.link", iface.Filename())
		if err := writeConfig(filename, iface.Link()); err != nil {
			return err
		}
		filename = fmt.Sprintf("%s.network", iface.Filename())
		if err := writeConfig(filename, iface.Network()); err != nil {
			return err
		}
	}
	return nil
}

func writeConfig(filename string, config string) error {
	if config == "" {
		return nil
	}
	log.Printf("Writing networkd unit %q\n", filename)
	_, err := WriteFile(&File{Content: config, Path: filename}, runtimeNetworkPath)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/jpoehls/go-dayone"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"time"
	//	"strings"
)

type Journal struct {
	Id    string    `json:"Id"`
	Title string    `json:"Title"`
	Tags  []string  `json:"Tags"`
	Date  time.Time `json:"Date"`
}

// ByAge implements sort.Interface for []Journal based on
// the Date field.
type ByDate []Journal

func (a ByDate) Len() int           { return len(a) }
func (a ByDate) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByDate) Less(i, j int) bool { return a[i].Date.Before(a[j].Date) }

func main() {
	var journal string
	flag.StringVar(&journal, "journal", "./", "Journal Directory name")
	flag.Parse()

	//jd := strings.Join([]string{journal, "entries"}, string(os.PathSeparator))
	jd := filepath.Join(journal, "entries")
	files, err := ioutil.ReadDir(jd)
	if err != nil {
		fmt.Printf("ERROR: Journal directory is not readable: %s\n", jd)
		os.Exit(1)
	}

	fmt.Printf("Found %d journal entries in %s\n", len(files), journal)
	//l := make([]Journal, len(files))
	var journals []Journal

	j := dayone.NewJournal(journal)

	parse := func(e *dayone.Entry, err error) error {
		if err != nil {
			return err
		}

		// Do something with the entry,
		// or return dayone.ErrStopRead to break.
		fmt.Printf("Date: %s [%s] %s\n", e.CreationDate.Local(), e.UUID(), e.Tags)
		//const layout = time.RubyDate
		const layout = "Mon, 02 Jan 2006"
		journals = append(journals, Journal{
			Id:    e.UUID(),
			Title: e.CreationDate.Local().Format(layout),
			Tags:  e.Tags,
			Date:  e.CreationDate,
		})
		return nil
	}

	err = j.Read(parse)
	if err != nil {
		panic(err)
	}

	//fmt.Println(l)
	sort.Sort(ByDate(journals))
	b, err := json.MarshalIndent(journals, "", "    ")
	if err != nil {
		fmt.Printf("ERROR: encoding JSON: %s\n", err)
		os.Exit(1)
	}
	os.Stdout.Write(b)

}
<commit_msg>added entry parameter and photo<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/jpoehls/go-dayone"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"time"
	//	"strings"
)

type Journal struct {
	Id      string      `json:"id"`
	Title   string      `json:"title"`
	Starred bool        `json:"starred"`
	Tags    []string    `json:"tags"`
	Date    time.Time   `json:"date"`
	Photo   interface{} `json:"photo"` // interface is needed here so we can assign "nil" to entry that has no photo
}

// ByDate implements sort.Interface for []Journal based on
// the Date field.
type ByDate []Journal

func (a ByDate) Len() int           { return len(a) }
func (a ByDate) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByDate) Less(i, j int) bool { return a[i].Date.Before(a[j].Date) }

func main() {
	var journal string
	var entry string
	flag.StringVar(&journal, "journal", "./", "Journal Directory name")
	flag.StringVar(&entry, "entry", "", "Entry UUID")
	flag.Parse()

	jd := filepath.Join(journal, "entries")
	files, err := ioutil.ReadDir(jd)
	if err != nil {
		fmt.Printf("ERROR: Journal directory is not readable: %s\n", jd)
		os.Exit(1)
	}

	fmt.Printf("Found %d journal entries in %s\n", len(files), journal)
	var journals []Journal

	j := dayone.NewJournal(journal)

	parse := func(e *dayone.Entry, err error) error {
		var photo interface{}
		if err != nil {
			return err
		}

		fmt.Printf("Date: %s [%s] %s\n", e.CreationDate.Local(), e.UUID(), e.Tags)
		const layout = "Mon, 02 Jan 2006"

		p, err := j.PhotoStat(e.UUID())
		if (err == nil) && (p != nil) {
			photo = p.Name()
		} else {
			photo = nil
		}

		journals = append(journals, Journal{
			Id:      e.UUID(),
			Title:   e.CreationDate.Local().Format(layout),
			Starred: e.Starred,
			Tags:    e.Tags,
			Date:    e.CreationDate,
			Photo:   photo,
		})
		return nil
	}

	if entry != "" {
		e, err := j.ReadEntry(entry)
		if err != nil {
			panic(err)
		}
		err = parse(e, nil)
		if err != nil {
			panic(err)
		}

	} else {
		err = j.Read(parse)
		if err != nil {
			panic(err)
		}
	}

	sort.Sort(ByDate(journals))
	b, err := json.MarshalIndent(journals, "", "    ")
	if err != nil {
		fmt.Printf("ERROR: encoding JSON: %s\n", err)
		os.Exit(1)
	}
	os.Stdout.Write(b)

}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package integration

import (
	"context"
	"testing"
	"time"

	"google.golang.org/grpc"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/storage/storagebackend"
	clientset "k8s.io/client-go/kubernetes"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"

	"go.etcd.io/etcd/clientv3"
	"go.etcd.io/etcd/pkg/transport"
)

// DeletePodOrErrorf deletes a pod or fails with a call to t.Errorf.
func DeletePodOrErrorf(t *testing.T, c clientset.Interface, ns, name string) {
	if err := c.CoreV1().Pods(ns).Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil {
		t.Errorf("unable to delete pod %v: %v", name, err)
	}
}

// Requests to try.  Each one should be forbidden or not forbidden
// depending on the authentication and authorization setup of the master.
var (
	Code200 = map[int]bool{200: true}
	Code201 = map[int]bool{201: true}
	Code400 = map[int]bool{400: true}
	Code401 = map[int]bool{401: true}
	Code403 = map[int]bool{403: true}
	Code404 = map[int]bool{404: true}
	Code405 = map[int]bool{405: true}
	Code409 = map[int]bool{409: true}
	Code422 = map[int]bool{422: true}
	Code500 = map[int]bool{500: true}
	Code503 = map[int]bool{503: true}
)

// WaitForPodToDisappear polls the API server if the pod has been deleted.
func WaitForPodToDisappear(podClient coreclient.PodInterface, podName string, interval, timeout time.Duration) error {
	return wait.PollImmediate(interval, timeout, func() (bool, error) {
		_, err := podClient.Get(context.TODO(), podName, metav1.GetOptions{})
		if err == nil {
			return false, nil
		}
		if apierrors.IsNotFound(err) {
			return true, nil
		}
		return false, err
	})
}

// GetEtcdClients returns an initialized  clientv3.Client and clientv3.KV.
func GetEtcdClients(config storagebackend.TransportConfig) (*clientv3.Client, clientv3.KV, error) {
	tlsInfo := transport.TLSInfo{
		CertFile:      config.CertFile,
		KeyFile:       config.KeyFile,
		TrustedCAFile: config.TrustedCAFile,
	}

	tlsConfig, err := tlsInfo.ClientConfig()
	if err != nil {
		return nil, nil, err
	}

	cfg := clientv3.Config{
		Endpoints:   config.ServerList,
		DialTimeout: 20 * time.Second,
		DialOptions: []grpc.DialOption{
			grpc.WithBlock(), // block until the underlying connection is up
		},
		TLS: tlsConfig,
	}

	c, err := clientv3.New(cfg)
	if err != nil {
		return nil, nil, err
	}

	return c, clientv3.NewKV(c), nil
}
<commit_msg>remove unused variable of Code<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package integration

import (
	"context"
	"testing"
	"time"

	"google.golang.org/grpc"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apiserver/pkg/storage/storagebackend"
	clientset "k8s.io/client-go/kubernetes"
	coreclient "k8s.io/client-go/kubernetes/typed/core/v1"

	"go.etcd.io/etcd/clientv3"
	"go.etcd.io/etcd/pkg/transport"
)

// DeletePodOrErrorf deletes a pod or fails with a call to t.Errorf.
func DeletePodOrErrorf(t *testing.T, c clientset.Interface, ns, name string) {
	if err := c.CoreV1().Pods(ns).Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil {
		t.Errorf("unable to delete pod %v: %v", name, err)
	}
}

// Requests to try.  Each one should be forbidden or not forbidden
// depending on the authentication and authorization setup of the master.
var (
	Code200 = map[int]bool{200: true}
	Code201 = map[int]bool{201: true}
	Code400 = map[int]bool{400: true}
	Code401 = map[int]bool{401: true}
	Code403 = map[int]bool{403: true}
	Code404 = map[int]bool{404: true}
	Code405 = map[int]bool{405: true}
	Code503 = map[int]bool{503: true}
)

// WaitForPodToDisappear polls the API server if the pod has been deleted.
func WaitForPodToDisappear(podClient coreclient.PodInterface, podName string, interval, timeout time.Duration) error {
	return wait.PollImmediate(interval, timeout, func() (bool, error) {
		_, err := podClient.Get(context.TODO(), podName, metav1.GetOptions{})
		if err == nil {
			return false, nil
		}
		if apierrors.IsNotFound(err) {
			return true, nil
		}
		return false, err
	})
}

// GetEtcdClients returns an initialized  clientv3.Client and clientv3.KV.
func GetEtcdClients(config storagebackend.TransportConfig) (*clientv3.Client, clientv3.KV, error) {
	tlsInfo := transport.TLSInfo{
		CertFile:      config.CertFile,
		KeyFile:       config.KeyFile,
		TrustedCAFile: config.TrustedCAFile,
	}

	tlsConfig, err := tlsInfo.ClientConfig()
	if err != nil {
		return nil, nil, err
	}

	cfg := clientv3.Config{
		Endpoints:   config.ServerList,
		DialTimeout: 20 * time.Second,
		DialOptions: []grpc.DialOption{
			grpc.WithBlock(), // block until the underlying connection is up
		},
		TLS: tlsConfig,
	}

	c, err := clientv3.New(cfg)
	if err != nil {
		return nil, nil, err
	}

	return c, clientv3.NewKV(c), nil
}
<|endoftext|>


<commit_before>package framework

import (
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/coreos/go-etcd/etcd"
	"github.com/taskgraph/taskgraph"
	"github.com/taskgraph/taskgraph/framework/frameworkhttp"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
)

type taskRole int

const (
	roleNone taskRole = iota
	roleParent
	roleChild
)

// One need to pass in at least these two for framework to start.
func NewBootStrap(jobName string, etcdURLs []string, ln net.Listener, logger *log.Logger) taskgraph.Bootstrap {
	return &framework{
		name:     jobName,
		etcdURLs: etcdURLs,
		ln:       ln,
		log:      logger,
	}
}

func (f *framework) SetTaskBuilder(taskBuilder taskgraph.TaskBuilder) { f.taskBuilder = taskBuilder }

func (f *framework) SetTopology(topology taskgraph.Topology) { f.topology = topology }

func (f *framework) Start() {
	var err error

	if f.log == nil {
		f.log = log.New(os.Stdout, "", log.Lshortfile|log.Ltime|log.Ldate)
	}

	f.etcdClient = etcd.NewClient(f.etcdURLs)

	if err = f.occupyTask(); err != nil {
		f.log.Panicf("occupyTask() failed: %v", err)
	}

	f.log.SetPrefix(fmt.Sprintf("task %d: ", f.taskID))

	f.epochChan = make(chan uint64, 1) // grab epoch from etcd
	f.epochStop = make(chan bool, 1)   // stop etcd watch
	// meta will have epoch prepended so we must get epoch before any watch on meta
	f.epoch, err = etcdutil.GetAndWatchEpoch(f.etcdClient, f.name, f.epochChan, f.epochStop)
	if err != nil {
		f.log.Fatalf("WatchEpoch failed: %v", err)
	}
	if f.epoch == exitEpoch {
		f.log.Printf("found that job has finished\n")
		f.epochStop <- true
		return
	}
	f.log.Printf("starting at epoch %d\n", f.epoch)

	// task builder and topology are defined by applications.
	// Both should be initialized at this point.
	// Get the task implementation and topology for this node (indentified by taskID)
	f.task = f.taskBuilder.GetTask(f.taskID)
	f.topology.SetTaskID(f.taskID)

	go f.startHTTP()

	f.heartbeat()
	f.setupChannels()
	f.task.Init(f.taskID, f)
	f.run()
	f.releaseResource()
	f.task.Exit()
}

func (f *framework) setupChannels() {
	f.httpStop = make(chan struct{})
	f.metaChan = make(chan *metaChange, 100)
	f.dataReqtoSendChan = make(chan *dataRequest, 100)
	f.dataReqChan = make(chan *dataRequest, 100)
	f.dataRespToSendChan = make(chan *dataResponse, 100)
	f.dataRespChan = make(chan *frameworkhttp.DataResponse, 100)
}

func (f *framework) run() {
	f.log.Printf("framework starts to run")
	defer f.log.Printf("framework stops running.")
	f.setEpochStarted()
	// this for-select is primarily used to synchronize epoch specific events.
	for {
		select {
		case nextEpoch, ok := <-f.epochChan:
			f.releaseEpochResource()
			if !ok { // single task exit
				nextEpoch = exitEpoch
				return
			}
			f.epoch = nextEpoch
			if f.epoch == exitEpoch {
				return
			}
			// start the next epoch's work
			f.setEpochStarted()
		case meta := <-f.metaChan:
			if meta.epoch != f.epoch {
				break
			}
			// We need to create a context before handling next event. The context saves
			// the epoch that was meant for this event. This context will be passed
			// to user event handler functions and used to ask framework to do work later
			// with previous information.
			f.handleMetaChange(f.createContext(), meta.who, meta.from, meta.meta)
		case req := <-f.dataReqtoSendChan:
			if req.epoch != f.epoch {
				f.log.Printf("epoch mismatch: req-to-send epoch: %d, current epoch: %d", req.epoch, f.epoch)
				break
			}
			go f.sendRequest(req)
		case req := <-f.dataReqChan:
			if req.epoch != f.epoch {
				f.log.Printf("epoch mismatch: request epoch: %d, current epoch: %d", req.epoch, f.epoch)
				req.notifyEpochMismatch()
				break
			}
			go f.handleDataReq(req)
		case resp := <-f.dataRespToSendChan:
			if resp.epoch != f.epoch {
				f.log.Printf("epoch mismatch: resp-to-send epoch: %d, current epoch: %d", resp.epoch, f.epoch)
				resp.notifyEpochMismatch()
				break
			}
			go f.sendResponse(resp)
		case resp := <-f.dataRespChan:
			if resp.Epoch != f.epoch {
				f.log.Printf("epoch mismatch: response epoch: %d, current epoch: %d", resp.Epoch, f.epoch)
				break
			}
			f.handleDataResp(f.createContext(), resp)
		}
	}
}

func (f *framework) setEpochStarted() {
	f.epochPassed = make(chan struct{})
	// Each epoch have a new meta map
	f.metaNotified = make(map[string]bool)

	f.task.SetEpoch(f.createContext(), f.epoch)
	// setup etcd watches
	// - create self's parent and child meta flag
	// - watch parents' child meta flag
	// - watch children's parent meta flag
	f.watchMeta(roleParent, f.topology.GetParents(f.epoch))
	f.watchMeta(roleChild, f.topology.GetChildren(f.epoch))
}

func (f *framework) releaseEpochResource() {
	close(f.epochPassed)
	for _, c := range f.metaStops {
		c <- true
	}
	f.metaStops = nil
}

// release resources: heartbeat, epoch watch.
func (f *framework) releaseResource() {
	f.log.Printf("framework is releasing resources...\n")
	f.epochStop <- true
	close(f.heartbeatStop)
	f.stopHTTP()
}

// occupyTask will grab the first unassigned task and register itself on etcd.
func (f *framework) occupyTask() error {
	for {
		freeTask, err := etcdutil.WaitFreeTask(f.etcdClient, f.name, f.log)
		if err != nil {
			return err
		}
		f.log.Printf("standby grabbed free task %d", freeTask)
		ok := etcdutil.TryOccupyTask(f.etcdClient, f.name, freeTask, f.ln.Addr().String())
		if ok {
			f.taskID = freeTask
			return nil
		}
		f.log.Printf("standby tried task %d failed. Wait free task again.", freeTask)
	}
}

func (f *framework) watchMeta(who taskRole, taskIDs []uint64) {
	stops := make([]chan bool, len(taskIDs))

	for i, taskID := range taskIDs {
		stop := make(chan bool, 1)
		stops[i] = stop

		var watchPath string
		switch who {
		case roleParent:
			// Watch parent's child-meta.
			watchPath = etcdutil.ChildMetaPath(f.name, taskID)
		case roleChild:
			// Watch child's parent-meta.
			watchPath = etcdutil.ParentMetaPath(f.name, taskID)
		default:
			f.log.Panic("unexpected role")
		}

		// When a node working for a task crashed, a new node will take over
		// the task and continue what's left. It assumes that progress is stalled
		// until the new node comes (i.e. epoch won't change).

		responseHandler := func(resp *etcd.Response, taskID uint64) {
			if resp.Action != "set" && resp.Action != "get" {
				return
			}
			// epoch is prepended to meta. When a new one starts and replaces
			// the old one, it doesn't need to handle previous things, whose
			// epoch is smaller than current one.
			values := strings.SplitN(resp.Node.Value, "-", 2)
			ep, err := strconv.ParseUint(values[0], 10, 64)
			if err != nil {
				f.log.Panicf("WARN: not a unit64 prepended to meta: %s", values[0])
			}
			f.metaChan <- &metaChange{
				from:  taskID,
				who:   who,
				epoch: ep,
				meta:  values[1],
			}
		}

		// Need to pass in taskID to make it work. Didn't know why.
		err := etcdutil.WatchMeta(f.etcdClient, taskID, watchPath, stop, responseHandler)
		if err != nil {
			f.log.Panicf("WatchMeta failed. path: %s, err: %v", watchPath, err)
		}
	}
	f.metaStops = append(f.metaStops, stops...)
}

func (f *framework) handleMetaChange(ctx taskgraph.Context, who taskRole, taskID uint64, meta string) {
	// check if meta is handled before.
	tm := taskMeta(taskID, meta)
	if _, ok := f.metaNotified[tm]; ok {
		return
	}
	f.metaNotified[tm] = true

	switch who {
	case roleParent:
		f.task.ParentMetaReady(ctx, taskID, meta)
	case roleChild:
		f.task.ChildMetaReady(ctx, taskID, meta)
	}
}

func taskMeta(taskID uint64, meta string) string {
	return fmt.Sprintf("%s-%s", strconv.FormatUint(taskID, 10), meta)
}
<commit_msg>forget to remove go for handleDataReq<commit_after>package framework

import (
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"strings"

	"github.com/coreos/go-etcd/etcd"
	"github.com/taskgraph/taskgraph"
	"github.com/taskgraph/taskgraph/framework/frameworkhttp"
	"github.com/taskgraph/taskgraph/pkg/etcdutil"
)

type taskRole int

const (
	roleNone taskRole = iota
	roleParent
	roleChild
)

// One need to pass in at least these two for framework to start.
func NewBootStrap(jobName string, etcdURLs []string, ln net.Listener, logger *log.Logger) taskgraph.Bootstrap {
	return &framework{
		name:     jobName,
		etcdURLs: etcdURLs,
		ln:       ln,
		log:      logger,
	}
}

func (f *framework) SetTaskBuilder(taskBuilder taskgraph.TaskBuilder) { f.taskBuilder = taskBuilder }

func (f *framework) SetTopology(topology taskgraph.Topology) { f.topology = topology }

func (f *framework) Start() {
	var err error

	if f.log == nil {
		f.log = log.New(os.Stdout, "", log.Lshortfile|log.Ltime|log.Ldate)
	}

	f.etcdClient = etcd.NewClient(f.etcdURLs)

	if err = f.occupyTask(); err != nil {
		f.log.Panicf("occupyTask() failed: %v", err)
	}

	f.log.SetPrefix(fmt.Sprintf("task %d: ", f.taskID))

	f.epochChan = make(chan uint64, 1) // grab epoch from etcd
	f.epochStop = make(chan bool, 1)   // stop etcd watch
	// meta will have epoch prepended so we must get epoch before any watch on meta
	f.epoch, err = etcdutil.GetAndWatchEpoch(f.etcdClient, f.name, f.epochChan, f.epochStop)
	if err != nil {
		f.log.Fatalf("WatchEpoch failed: %v", err)
	}
	if f.epoch == exitEpoch {
		f.log.Printf("found that job has finished\n")
		f.epochStop <- true
		return
	}
	f.log.Printf("starting at epoch %d\n", f.epoch)

	// task builder and topology are defined by applications.
	// Both should be initialized at this point.
	// Get the task implementation and topology for this node (indentified by taskID)
	f.task = f.taskBuilder.GetTask(f.taskID)
	f.topology.SetTaskID(f.taskID)

	go f.startHTTP()

	f.heartbeat()
	f.setupChannels()
	f.task.Init(f.taskID, f)
	f.run()
	f.releaseResource()
	f.task.Exit()
}

func (f *framework) setupChannels() {
	f.httpStop = make(chan struct{})
	f.metaChan = make(chan *metaChange, 100)
	f.dataReqtoSendChan = make(chan *dataRequest, 100)
	f.dataReqChan = make(chan *dataRequest, 100)
	f.dataRespToSendChan = make(chan *dataResponse, 100)
	f.dataRespChan = make(chan *frameworkhttp.DataResponse, 100)
}

func (f *framework) run() {
	f.log.Printf("framework starts to run")
	defer f.log.Printf("framework stops running.")
	f.setEpochStarted()
	// this for-select is primarily used to synchronize epoch specific events.
	for {
		select {
		case nextEpoch, ok := <-f.epochChan:
			f.releaseEpochResource()
			if !ok { // single task exit
				nextEpoch = exitEpoch
				return
			}
			f.epoch = nextEpoch
			if f.epoch == exitEpoch {
				return
			}
			// start the next epoch's work
			f.setEpochStarted()
		case meta := <-f.metaChan:
			if meta.epoch != f.epoch {
				break
			}
			// We need to create a context before handling next event. The context saves
			// the epoch that was meant for this event. This context will be passed
			// to user event handler functions and used to ask framework to do work later
			// with previous information.
			f.handleMetaChange(f.createContext(), meta.who, meta.from, meta.meta)
		case req := <-f.dataReqtoSendChan:
			if req.epoch != f.epoch {
				f.log.Printf("epoch mismatch: req-to-send epoch: %d, current epoch: %d", req.epoch, f.epoch)
				break
			}
			go f.sendRequest(req)
		case req := <-f.dataReqChan:
			if req.epoch != f.epoch {
				f.log.Printf("epoch mismatch: request epoch: %d, current epoch: %d", req.epoch, f.epoch)
				req.notifyEpochMismatch()
				break
			}
			f.handleDataReq(req)
		case resp := <-f.dataRespToSendChan:
			if resp.epoch != f.epoch {
				f.log.Printf("epoch mismatch: resp-to-send epoch: %d, current epoch: %d", resp.epoch, f.epoch)
				resp.notifyEpochMismatch()
				break
			}
			go f.sendResponse(resp)
		case resp := <-f.dataRespChan:
			if resp.Epoch != f.epoch {
				f.log.Printf("epoch mismatch: response epoch: %d, current epoch: %d", resp.Epoch, f.epoch)
				break
			}
			f.handleDataResp(f.createContext(), resp)
		}
	}
}

func (f *framework) setEpochStarted() {
	f.epochPassed = make(chan struct{})
	// Each epoch have a new meta map
	f.metaNotified = make(map[string]bool)

	f.task.SetEpoch(f.createContext(), f.epoch)
	// setup etcd watches
	// - create self's parent and child meta flag
	// - watch parents' child meta flag
	// - watch children's parent meta flag
	f.watchMeta(roleParent, f.topology.GetParents(f.epoch))
	f.watchMeta(roleChild, f.topology.GetChildren(f.epoch))
}

func (f *framework) releaseEpochResource() {
	close(f.epochPassed)
	for _, c := range f.metaStops {
		c <- true
	}
	f.metaStops = nil
}

// release resources: heartbeat, epoch watch.
func (f *framework) releaseResource() {
	f.log.Printf("framework is releasing resources...\n")
	f.epochStop <- true
	close(f.heartbeatStop)
	f.stopHTTP()
}

// occupyTask will grab the first unassigned task and register itself on etcd.
func (f *framework) occupyTask() error {
	for {
		freeTask, err := etcdutil.WaitFreeTask(f.etcdClient, f.name, f.log)
		if err != nil {
			return err
		}
		f.log.Printf("standby grabbed free task %d", freeTask)
		ok := etcdutil.TryOccupyTask(f.etcdClient, f.name, freeTask, f.ln.Addr().String())
		if ok {
			f.taskID = freeTask
			return nil
		}
		f.log.Printf("standby tried task %d failed. Wait free task again.", freeTask)
	}
}

func (f *framework) watchMeta(who taskRole, taskIDs []uint64) {
	stops := make([]chan bool, len(taskIDs))

	for i, taskID := range taskIDs {
		stop := make(chan bool, 1)
		stops[i] = stop

		var watchPath string
		switch who {
		case roleParent:
			// Watch parent's child-meta.
			watchPath = etcdutil.ChildMetaPath(f.name, taskID)
		case roleChild:
			// Watch child's parent-meta.
			watchPath = etcdutil.ParentMetaPath(f.name, taskID)
		default:
			f.log.Panic("unexpected role")
		}

		// When a node working for a task crashed, a new node will take over
		// the task and continue what's left. It assumes that progress is stalled
		// until the new node comes (i.e. epoch won't change).

		responseHandler := func(resp *etcd.Response, taskID uint64) {
			if resp.Action != "set" && resp.Action != "get" {
				return
			}
			// epoch is prepended to meta. When a new one starts and replaces
			// the old one, it doesn't need to handle previous things, whose
			// epoch is smaller than current one.
			values := strings.SplitN(resp.Node.Value, "-", 2)
			ep, err := strconv.ParseUint(values[0], 10, 64)
			if err != nil {
				f.log.Panicf("WARN: not a unit64 prepended to meta: %s", values[0])
			}
			f.metaChan <- &metaChange{
				from:  taskID,
				who:   who,
				epoch: ep,
				meta:  values[1],
			}
		}

		// Need to pass in taskID to make it work. Didn't know why.
		err := etcdutil.WatchMeta(f.etcdClient, taskID, watchPath, stop, responseHandler)
		if err != nil {
			f.log.Panicf("WatchMeta failed. path: %s, err: %v", watchPath, err)
		}
	}
	f.metaStops = append(f.metaStops, stops...)
}

func (f *framework) handleMetaChange(ctx taskgraph.Context, who taskRole, taskID uint64, meta string) {
	// check if meta is handled before.
	tm := taskMeta(taskID, meta)
	if _, ok := f.metaNotified[tm]; ok {
		return
	}
	f.metaNotified[tm] = true

	switch who {
	case roleParent:
		f.task.ParentMetaReady(ctx, taskID, meta)
	case roleChild:
		f.task.ChildMetaReady(ctx, taskID, meta)
	}
}

func taskMeta(taskID uint64, meta string) string {
	return fmt.Sprintf("%s-%s", strconv.FormatUint(taskID, 10), meta)
}
<|endoftext|>


<commit_before>package goat

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"time"
)

func LogManager(doneChan chan bool, logChan chan string) {
	// Create log directory and file, and pull current date to add to logfile name
	now := time.Now()
	os.Mkdir("logs", os.ModeDir|os.ModePerm)
	logFile, err := os.Create(fmt.Sprintf("logs/goat-%d-%d-%d.log", now.Year(), now.Month(), now.Day()))
	if err != nil {
		fmt.Println(err)
	}

	// create a logger that will use the writer created above
	logger := log.New(bufio.NewWriter(logFile), "", log.Lmicroseconds|log.Lshortfile)
	amIDone := false
	msg := ""

	// Start the system status logger
	go StatusLogger(logChan)

	// Wait for error to be passed on the logChan channel, or termination signal on doneChan
	for !amIDone {
		select {
		case amIDone = <-doneChan:
			logFile.Close()
		case msg = <-logChan:
			logger.Println(APP, ":", msg)
			fmt.Println(APP, ":", msg)
		}
	}
}

// Log and display system status every minute
func StatusLogger(logChan chan string) {
	// Poll status every 30 seconds
	ticker := time.NewTicker(30 * time.Second)

	// Loop infinitely, trigger events via ticker
	for {
		select {
		case <- ticker.C:
			// Fetch status, log it
			go PrintCurrentStatus(logChan)
		}
	}
}
<commit_msg>Add current time to logs<commit_after>package goat

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"time"
)

func LogManager(doneChan chan bool, logChan chan string) {
	// Create log directory and file, and pull current date to add to logfile name
	now := time.Now()
	os.Mkdir("logs", os.ModeDir|os.ModePerm)
	logFile, err := os.Create(fmt.Sprintf("logs/goat-%d-%d-%d.log", now.Year(), now.Month(), now.Day()))
	if err != nil {
		fmt.Println(err)
	}

	// create a logger that will use the writer created above
	logger := log.New(bufio.NewWriter(logFile), "", log.Lmicroseconds|log.Lshortfile)
	amIDone := false
	msg := ""

	// Start the system status logger
	go StatusLogger(logChan)

	// Wait for error to be passed on the logChan channel, or termination signal on doneChan
	for !amIDone {
		select {
		case amIDone = <-doneChan:
			logFile.Close()
		case msg = <-logChan:
			now := time.Now()
			logger.Printf("%s : [%2d:%2d:%2d] %s\n", APP, now.Hour(), now.Minute(), now.Second(), msg)
			fmt.Printf("%s : [%2d:%2d:%2d] %s\n", APP, now.Hour(), now.Minute(), now.Second(), msg)
		}
	}
}

// Log and display system status every minute
func StatusLogger(logChan chan string) {
	// Poll status every 30 seconds
	ticker := time.NewTicker(30 * time.Second)

	// Loop infinitely, trigger events via ticker
	for {
		select {
		case <- ticker.C:
			// Fetch status, log it
			go PrintCurrentStatus(logChan)
		}
	}
}
<|endoftext|>