<commit_before>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// InitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type InitCommand struct {
	Meta
}

func (c *InitCommand) Help() string {
	helpText := `
Usage: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *InitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *InitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file. This job is named
# "example" so it will create a job with the ID and Name "example".

# The "job" stanza is the top-most configuration option in the job
# specification. A job is a declarative specification of tasks that Nomad
# should run. Jobs have a globally unique name, one or many task groups, which
# are themselves collections of one or many tasks.
#
# For more information and examples on the "job" stanza, please see
# the online documentation at:
#
#     https://www.nomadproject.io/docs/job-specification/job.html
#
job "example" {
  # The "region" parameter specifies the region in which to execute the job. If
  # omitted, this inherits the default region name of "global".
  # region = "global"

  # The "datacenters" parameter specifies the list of datacenters which should
  # be considered when placing this task. This must be provided.
  datacenters = ["dc1"]

  # The "type" parameter controls the type of job, which impacts the scheduler's
  # decision on placement. This configuration is optional and defaults to
  # "service". For a full list of job types and their differences, please see
  # the online documentation.
  #
  # For more information, please see the online documentation at:
  #
  #     https://www.nomadproject.io/docs/jobspec/schedulers.html
  #
  type = "service"

  # The "constraint" stanza defines additional constraints for placing this job,
  # in addition to any resource or driver constraints. This stanza may be placed
  # at the "job", "group", or "task" level, and supports variable interpolation.
  #
  # For more information and examples on the "constraint" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/constraint.html
  #
  # constraint {
  #   attribute = "${attr.kernel.name}"
  #   value     = "linux"
  # }

  # The "update" stanza specifies the update strategy of task groups. The update
  # strategy is used to control things like rolling upgrades, canaries, and
  # blue/green deployments. If omitted, no update strategy is enforced. The
  # "update" stanza may be placed at the job or task group. When placed at the
  # job, it applies to all groups within the job. When placed at both the job and
  # group level, the stanzas are merged with the group's taking precedence.
  #
  # For more information and examples on the "update" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/update.html
  #
  update {
    # The "max_parallel" parameter specifies the maximum number of updates to
    # perform in parallel. In this case, this specifies to update a single task
    # at a time.
    max_parallel = 1

    # The "min_healthy_time" parameter specifies the minimum time the allocation
    # must be in the healthy state before it is marked as healthy and unblocks
    # further allocations from being updated.
    min_healthy_time = "10s"

    # The "healthy_deadline" parameter specifies the deadline in which the
    # allocation must be marked as healthy after which the allocation is
    # automatically transitioned to unhealthy. Transitioning to unhealthy will
    # fail the deployment and potentially roll back the job if "auto_revert" is
    # set to true.
    healthy_deadline = "3m"

    # The "auto_revert" parameter specifies if the job should auto-revert to the
    # last stable job on deployment failure. A job is marked as stable if all the
    # allocations as part of its deployment were marked healthy.
    auto_revert = false

    # The "canary" parameter specifies that changes to the job that would result
    # in destructive updates should create the specified number of canaries
    # without stopping any previous allocations. Once the operator determines the
    # canaries are healthy, they can be promoted which unblocks a rolling update
    # of the remaining allocations at a rate of "max_parallel".
    #
    # Further, setting "canary" equal to the count of the task group allows
    # blue/green deployments. When the job is updated, a full set of the new
    # version is deployed and upon promotion the old version is stopped.
    canary = 0
  }

  # The "group" stanza defines a series of tasks that should be co-located on
  # the same Nomad client. Any task within a group will be placed on the same
  # client.
  #
  # For more information and examples on the "group" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/group.html
  #
  group "cache" {
    # The "count" parameter specifies the number of the task groups that should
    # be running under this group. This value must be non-negative and defaults
    # to 1.
    count = 1

    # The "restart" stanza configures a group's behavior on task failure. If
    # left unspecified, a default restart policy is used based on the job type.
    #
    # For more information and examples on the "restart" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/restart.html
    #
    restart {
      # The number of attempts to run the job within the specified interval.
      attempts = 10
      interval = "5m"

      # The "delay" parameter specifies the duration to wait before restarting
      # a task after it has failed.
      delay = "25s"

     # The "mode" parameter controls what happens when a task has restarted
     # "attempts" times within the interval. "delay" mode delays the next
     # restart until the next interval. "fail" mode does not restart the task
     # if "attempts" has been hit within the interval.
      mode = "delay"
    }

    # The "ephemeral_disk" stanza instructs Nomad to utilize an ephemeral disk
    # instead of a hard disk requirement. Clients using this stanza should
    # not specify disk requirements in the resources stanza of the task. All
    # tasks in this group will share the same ephemeral disk.
    #
    # For more information and examples on the "ephemeral_disk" stanza, please
    # see the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/ephemeral_disk.html
    #
    ephemeral_disk {
      # When sticky is true and the task group is updated, the scheduler
      # will prefer to place the updated allocation on the same node and
      # will migrate the data. This is useful for tasks that store data
      # that should persist across allocation updates.
      # sticky = true
      #
      # Setting migrate to true results in the allocation directory of a
      # sticky allocation directory to be migrated.
      # migrate = true

      # The "size" parameter specifies the size in MB of shared ephemeral disk
      # between tasks in the group.
      size = 300
    }

    # The "task" stanza creates an individual unit of work, such as a Docker
    # container, web application, or batch processing.
    #
    # For more information and examples on the "task" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/task.html
    #
    task "redis" {
      # The "driver" parameter specifies the task driver that should be used to
      # run the task.
      driver = "docker"

      # The "config" stanza specifies the driver configuration, which is passed
      # directly to the driver to start the task. The details of configurations
      # are specific to each driver, so please see specific driver
      # documentation for more information.
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }

      # The "artifact" stanza instructs Nomad to download an artifact from a
      # remote source prior to starting the task. This provides a convenient
      # mechanism for downloading configuration files or data needed to run the
      # task. It is possible to specify the "artifact" stanza multiple times to
      # download multiple artifacts.
      #
      # For more information and examples on the "artifact" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/artifact.html
      #
      # artifact {
      #   source = "http://foo.com/artifact.tar.gz"
      #   options {
      #     checksum = "md5:c4aa853ad2215426eb7d70a21922e794"
      #   }
      # }

      # The "logs" stanza instructs the Nomad client on how many log files and
      # the maximum size of those logs files to retain. Logging is enabled by
      # default, but the "logs" stanza allows for finer-grained control over
      # the log rotation and storage configuration.
      #
      # For more information and examples on the "logs" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/logs.html
      #
      # logs {
      #   max_files     = 10
      #   max_file_size = 15
      # }

      # The "resources" stanza describes the requirements a task needs to
      # execute. Resource requirements include memory, network, cpu, and more.
      # This ensures the task will execute on a machine that contains enough
      # resource capacity.
      #
      # For more information and examples on the "resources" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/resources.html
      #
      resources {
        cpu    = 500 # 500 MHz
        memory = 256 # 256MB
        network {
          mbits = 10
          port "db" {}
        }
      }

      # The "service" stanza instructs Nomad to register this task as a service
      # in the service discovery engine, which is currently Consul. This will
      # make the service addressable after Nomad has placed it on a host and
      # port.
      #
      # For more information and examples on the "service" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/service.html
      #
      service {
        name = "redis-cache"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }

      # The "template" stanza instructs Nomad to manage a template, such as
      # a configuration file or script. This template can optionally pull data
      # from Consul or Vault to populate runtime configuration data.
      #
      # For more information and examples on the "template" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/template.html
      #
      # template {
      #   data          = "---\nkey: {{ key \"service/my-key\" }}"
      #   destination   = "local/file.yml"
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # The "template" stanza can also be used to create environment variables
      # for tasks that prefer those to config files. The task will be restarted
      # when data pulled from Consul or Vault changes.
      #
      # template {
      #   data        = "KEY={{ key \"service/my-key\" }}"
      #   destination = "local/file.env"
      #   env         = true
      # }

      # The "vault" stanza instructs the Nomad client to acquire a token from
      # a HashiCorp Vault server. The Nomad servers must be configured and
      # authorized to communicate with Vault. By default, Nomad will inject
      # The token into the job via an environment variable and make the token
      # available to the "template" stanza. The Nomad client handles the renewal
      # and revocation of the Vault token.
      #
      # For more information and examples on the "vault" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/vault.html
      #
      # vault {
      #   policies      = ["cdn", "frontend"]
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # Controls the timeout between signalling a task it will be killed
      # and killing the task. If not set a default is used.
      # kill_timeout = "20s"
    }
  }
}
`)
<commit_msg>Fix nomad init to use default values for restart stanza<commit_after>package command

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

const (
	// DefaultInitName is the default name we use when
	// initializing the example file
	DefaultInitName = "example.nomad"
)

// InitCommand generates a new job template that you can customize to your
// liking, like vagrant init
type InitCommand struct {
	Meta
}

func (c *InitCommand) Help() string {
	helpText := `
Usage: nomad init

  Creates an example job file that can be used as a starting
  point to customize further.
`
	return strings.TrimSpace(helpText)
}

func (c *InitCommand) Synopsis() string {
	return "Create an example job file"
}

func (c *InitCommand) Run(args []string) int {
	// Check for misuse
	if len(args) != 0 {
		c.Ui.Error(c.Help())
		return 1
	}

	// Check if the file already exists
	_, err := os.Stat(DefaultInitName)
	if err != nil && !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", DefaultInitName, err))
		return 1
	}
	if !os.IsNotExist(err) {
		c.Ui.Error(fmt.Sprintf("Job '%s' already exists", DefaultInitName))
		return 1
	}

	// Write out the example
	err = ioutil.WriteFile(DefaultInitName, []byte(defaultJob), 0660)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", DefaultInitName, err))
		return 1
	}

	// Success
	c.Ui.Output(fmt.Sprintf("Example job file written to %s", DefaultInitName))
	return 0
}

var defaultJob = strings.TrimSpace(`
# There can only be a single job definition per file. This job is named
# "example" so it will create a job with the ID and Name "example".

# The "job" stanza is the top-most configuration option in the job
# specification. A job is a declarative specification of tasks that Nomad
# should run. Jobs have a globally unique name, one or many task groups, which
# are themselves collections of one or many tasks.
#
# For more information and examples on the "job" stanza, please see
# the online documentation at:
#
#     https://www.nomadproject.io/docs/job-specification/job.html
#
job "example" {
  # The "region" parameter specifies the region in which to execute the job. If
  # omitted, this inherits the default region name of "global".
  # region = "global"

  # The "datacenters" parameter specifies the list of datacenters which should
  # be considered when placing this task. This must be provided.
  datacenters = ["dc1"]

  # The "type" parameter controls the type of job, which impacts the scheduler's
  # decision on placement. This configuration is optional and defaults to
  # "service". For a full list of job types and their differences, please see
  # the online documentation.
  #
  # For more information, please see the online documentation at:
  #
  #     https://www.nomadproject.io/docs/jobspec/schedulers.html
  #
  type = "service"

  # The "constraint" stanza defines additional constraints for placing this job,
  # in addition to any resource or driver constraints. This stanza may be placed
  # at the "job", "group", or "task" level, and supports variable interpolation.
  #
  # For more information and examples on the "constraint" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/constraint.html
  #
  # constraint {
  #   attribute = "${attr.kernel.name}"
  #   value     = "linux"
  # }

  # The "update" stanza specifies the update strategy of task groups. The update
  # strategy is used to control things like rolling upgrades, canaries, and
  # blue/green deployments. If omitted, no update strategy is enforced. The
  # "update" stanza may be placed at the job or task group. When placed at the
  # job, it applies to all groups within the job. When placed at both the job and
  # group level, the stanzas are merged with the group's taking precedence.
  #
  # For more information and examples on the "update" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/update.html
  #
  update {
    # The "max_parallel" parameter specifies the maximum number of updates to
    # perform in parallel. In this case, this specifies to update a single task
    # at a time.
    max_parallel = 1

    # The "min_healthy_time" parameter specifies the minimum time the allocation
    # must be in the healthy state before it is marked as healthy and unblocks
    # further allocations from being updated.
    min_healthy_time = "10s"

    # The "healthy_deadline" parameter specifies the deadline in which the
    # allocation must be marked as healthy after which the allocation is
    # automatically transitioned to unhealthy. Transitioning to unhealthy will
    # fail the deployment and potentially roll back the job if "auto_revert" is
    # set to true.
    healthy_deadline = "3m"

    # The "auto_revert" parameter specifies if the job should auto-revert to the
    # last stable job on deployment failure. A job is marked as stable if all the
    # allocations as part of its deployment were marked healthy.
    auto_revert = false

    # The "canary" parameter specifies that changes to the job that would result
    # in destructive updates should create the specified number of canaries
    # without stopping any previous allocations. Once the operator determines the
    # canaries are healthy, they can be promoted which unblocks a rolling update
    # of the remaining allocations at a rate of "max_parallel".
    #
    # Further, setting "canary" equal to the count of the task group allows
    # blue/green deployments. When the job is updated, a full set of the new
    # version is deployed and upon promotion the old version is stopped.
    canary = 0
  }

  # The "group" stanza defines a series of tasks that should be co-located on
  # the same Nomad client. Any task within a group will be placed on the same
  # client.
  #
  # For more information and examples on the "group" stanza, please see
  # the online documentation at:
  #
  #     https://www.nomadproject.io/docs/job-specification/group.html
  #
  group "cache" {
    # The "count" parameter specifies the number of the task groups that should
    # be running under this group. This value must be non-negative and defaults
    # to 1.
    count = 1

    # The "restart" stanza configures a group's behavior on task failure. If
    # left unspecified, a default restart policy is used based on the job type.
    #
    # For more information and examples on the "restart" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/restart.html
    #
    restart {
      # The number of attempts to run the job within the specified interval.
      attempts = 2
      interval = "15s"

      # The "delay" parameter specifies the duration to wait before restarting
      # a task after it has failed.
      delay = "15s"

     # The "mode" parameter controls what happens when a task has restarted
     # "attempts" times within the interval. "delay" mode delays the next
     # restart until the next interval. "fail" mode does not restart the task
     # if "attempts" has been hit within the interval.
      mode = "fail"
    }

    # The "ephemeral_disk" stanza instructs Nomad to utilize an ephemeral disk
    # instead of a hard disk requirement. Clients using this stanza should
    # not specify disk requirements in the resources stanza of the task. All
    # tasks in this group will share the same ephemeral disk.
    #
    # For more information and examples on the "ephemeral_disk" stanza, please
    # see the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/ephemeral_disk.html
    #
    ephemeral_disk {
      # When sticky is true and the task group is updated, the scheduler
      # will prefer to place the updated allocation on the same node and
      # will migrate the data. This is useful for tasks that store data
      # that should persist across allocation updates.
      # sticky = true
      #
      # Setting migrate to true results in the allocation directory of a
      # sticky allocation directory to be migrated.
      # migrate = true

      # The "size" parameter specifies the size in MB of shared ephemeral disk
      # between tasks in the group.
      size = 300
    }

    # The "task" stanza creates an individual unit of work, such as a Docker
    # container, web application, or batch processing.
    #
    # For more information and examples on the "task" stanza, please see
    # the online documentation at:
    #
    #     https://www.nomadproject.io/docs/job-specification/task.html
    #
    task "redis" {
      # The "driver" parameter specifies the task driver that should be used to
      # run the task.
      driver = "docker"

      # The "config" stanza specifies the driver configuration, which is passed
      # directly to the driver to start the task. The details of configurations
      # are specific to each driver, so please see specific driver
      # documentation for more information.
      config {
        image = "redis:3.2"
        port_map {
          db = 6379
        }
      }

      # The "artifact" stanza instructs Nomad to download an artifact from a
      # remote source prior to starting the task. This provides a convenient
      # mechanism for downloading configuration files or data needed to run the
      # task. It is possible to specify the "artifact" stanza multiple times to
      # download multiple artifacts.
      #
      # For more information and examples on the "artifact" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/artifact.html
      #
      # artifact {
      #   source = "http://foo.com/artifact.tar.gz"
      #   options {
      #     checksum = "md5:c4aa853ad2215426eb7d70a21922e794"
      #   }
      # }

      # The "logs" stanza instructs the Nomad client on how many log files and
      # the maximum size of those logs files to retain. Logging is enabled by
      # default, but the "logs" stanza allows for finer-grained control over
      # the log rotation and storage configuration.
      #
      # For more information and examples on the "logs" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/logs.html
      #
      # logs {
      #   max_files     = 10
      #   max_file_size = 15
      # }

      # The "resources" stanza describes the requirements a task needs to
      # execute. Resource requirements include memory, network, cpu, and more.
      # This ensures the task will execute on a machine that contains enough
      # resource capacity.
      #
      # For more information and examples on the "resources" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/resources.html
      #
      resources {
        cpu    = 500 # 500 MHz
        memory = 256 # 256MB
        network {
          mbits = 10
          port "db" {}
        }
      }

      # The "service" stanza instructs Nomad to register this task as a service
      # in the service discovery engine, which is currently Consul. This will
      # make the service addressable after Nomad has placed it on a host and
      # port.
      #
      # For more information and examples on the "service" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/service.html
      #
      service {
        name = "redis-cache"
        tags = ["global", "cache"]
        port = "db"
        check {
          name     = "alive"
          type     = "tcp"
          interval = "10s"
          timeout  = "2s"
        }
      }

      # The "template" stanza instructs Nomad to manage a template, such as
      # a configuration file or script. This template can optionally pull data
      # from Consul or Vault to populate runtime configuration data.
      #
      # For more information and examples on the "template" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/template.html
      #
      # template {
      #   data          = "---\nkey: {{ key \"service/my-key\" }}"
      #   destination   = "local/file.yml"
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # The "template" stanza can also be used to create environment variables
      # for tasks that prefer those to config files. The task will be restarted
      # when data pulled from Consul or Vault changes.
      #
      # template {
      #   data        = "KEY={{ key \"service/my-key\" }}"
      #   destination = "local/file.env"
      #   env         = true
      # }

      # The "vault" stanza instructs the Nomad client to acquire a token from
      # a HashiCorp Vault server. The Nomad servers must be configured and
      # authorized to communicate with Vault. By default, Nomad will inject
      # The token into the job via an environment variable and make the token
      # available to the "template" stanza. The Nomad client handles the renewal
      # and revocation of the Vault token.
      #
      # For more information and examples on the "vault" stanza, please see
      # the online documentation at:
      #
      #     https://www.nomadproject.io/docs/job-specification/vault.html
      #
      # vault {
      #   policies      = ["cdn", "frontend"]
      #   change_mode   = "signal"
      #   change_signal = "SIGHUP"
      # }

      # Controls the timeout between signalling a task it will be killed
      # and killing the task. If not set a default is used.
      # kill_timeout = "20s"
    }
  }
}
`)
<|endoftext|>


<commit_before>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package common

import (
	"bytes"
	"crypto/sha256"
	"encoding/binary"
	"fmt"

	"golang.org/x/crypto/sha3"
)

// An Entry is the element which carries user data
// https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#entry
type Entry struct {
	Version uint8
	ChainID *Hash
	ExtIDs  [][]byte
	Content []byte
}

func NewEntry() *Entry {
	e := new(Entry)
	e.ChainID = NewHash()
	e.ExtIDs = make([][]byte, 0)
	e.Content = make([]byte, 0)
	return e
}

// NewChainID generates a ChainID from an entry. ChainID = Sha(Sha(ExtIDs[0]) +
// Sha(ExtIDs[1] + ... + Sha(ExtIDs[n]))
func NewChainID(e *Entry) *Hash {
	id := new(Hash)
	sum := sha256.New()
	for _, v := range e.ExtIDs {
		x := sha256.Sum256(v)
		sum.Write(x[:])
	}
	id.SetBytes(sum.Sum(nil))

	return id
}

func (e *Entry) Hash() *Hash {
	h := NewHash()
	a, err := e.MarshalBinary()
	if err != nil {
		return h
	}
	b := sha3.Sum256(a)
	c := append(a, b[:]...)
	d := sha256.Sum256(c)
	h.SetBytes(d[:])

	return h
}

func (e *Entry) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)

	// 1 byte Version
	if err := binary.Write(buf, binary.BigEndian, e.Version); err != nil {
		return buf.Bytes(), err
	}

	// 32 byte ChainID
	buf.Write(e.ChainID.Bytes())

	// ExtIDs
	if ext, err := e.MarshalExtIDsBinary(); err != nil {
		return buf.Bytes(), err
	} else {
		// 2 byte size of ExtIDs
		if err := binary.Write(buf, binary.BigEndian, int16(len(ext))); err != nil {
			return buf.Bytes(), err
		}

		// binary ExtIDs
		buf.Write(ext)
	}

	// Content
	buf.Write(e.Content)

	return buf.Bytes(), nil
}

// MarshalExtIDsBinary marshals the ExtIDs into a []byte containing a series of
// 2 byte size of each ExtID followed by the ExtID.
func (e *Entry) MarshalExtIDsBinary() ([]byte, error) {
	buf := new(bytes.Buffer)

	for _, x := range e.ExtIDs {
		// 2 byte size of the ExtID
		if err := binary.Write(buf, binary.BigEndian, uint16(len(x))); err != nil {
			return buf.Bytes(), err
		}

		// ExtID bytes
		buf.Write(x)
	}

	return buf.Bytes(), nil
}

func (e *Entry) UnmarshalBinary(data []byte) (err error) {
	buf := bytes.NewBuffer(data)
	hash := make([]byte, 32)

	// 1 byte Version
	if x, err := buf.ReadByte(); err != nil {
		return err
	} else {
		e.Version = x
	}

	// 32 byte ChainID
	e.ChainID = NewHash()
	if _, err := buf.Read(hash); err != nil {
		return err
	} else if err := e.ChainID.SetBytes(hash); err != nil {
		return err
	}

	// 2 byte size of ExtIDs
	var extSize uint16
	if err := binary.Read(buf, binary.BigEndian, &extSize); err != nil {
		return err
	}

	// ExtIDs
	for i := extSize; i > 0; {
		var xsize int16
		binary.Read(buf, binary.BigEndian, &xsize)
		i -= 2

		x := make([]byte, xsize)
		if n, err := buf.Read(x); err != nil {
			return err
		} else {
			if c := cap(x); n != c {
				return fmt.Errorf("Could not read ExtID: Read %d bytes of %d\n",
					n, c)
			}
			e.ExtIDs = append(e.ExtIDs, x)
			i -= uint16(n)
		}
	}

	// Content
	e.Content = buf.Bytes()

	return nil
}
<commit_msg>updated common.Entry.Hash to use 512 + 256 hash<commit_after>// Copyright 2015 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package common

import (
	"bytes"
	"crypto/sha256"
	"crypto/sha512"
	"encoding/binary"
	"fmt"
)

// An Entry is the element which carries user data
// https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#entry
type Entry struct {
	Version uint8
	ChainID *Hash
	ExtIDs  [][]byte
	Content []byte
}

func NewEntry() *Entry {
	e := new(Entry)
	e.ChainID = NewHash()
	e.ExtIDs = make([][]byte, 0)
	e.Content = make([]byte, 0)
	return e
}

// NewChainID generates a ChainID from an entry. ChainID = Sha(Sha(ExtIDs[0]) +
// Sha(ExtIDs[1] + ... + Sha(ExtIDs[n]))
func NewChainID(e *Entry) *Hash {
	id := new(Hash)
	sum := sha256.New()
	for _, v := range e.ExtIDs {
		x := sha256.Sum256(v)
		sum.Write(x[:])
	}
	id.SetBytes(sum.Sum(nil))

	return id
}

func (e *Entry) Hash() *Hash {
	h := NewHash()
	entry, err := e.MarshalBinary()
	if err != nil {
		return h
	}

	h1 := sha512.Sum512(entry)
	h2 := sha256.Sum256(append(h1[:], entry[:]...))
	h.SetBytes(h2[:])
	return h
}

func (e *Entry) MarshalBinary() ([]byte, error) {
	buf := new(bytes.Buffer)

	// 1 byte Version
	if err := binary.Write(buf, binary.BigEndian, e.Version); err != nil {
		return buf.Bytes(), err
	}

	// 32 byte ChainID
	buf.Write(e.ChainID.Bytes())

	// ExtIDs
	if ext, err := e.MarshalExtIDsBinary(); err != nil {
		return buf.Bytes(), err
	} else {
		// 2 byte size of ExtIDs
		if err := binary.Write(buf, binary.BigEndian, int16(len(ext))); err != nil {
			return buf.Bytes(), err
		}

		// binary ExtIDs
		buf.Write(ext)
	}

	// Content
	buf.Write(e.Content)

	return buf.Bytes(), nil
}

// MarshalExtIDsBinary marshals the ExtIDs into a []byte containing a series of
// 2 byte size of each ExtID followed by the ExtID.
func (e *Entry) MarshalExtIDsBinary() ([]byte, error) {
	buf := new(bytes.Buffer)

	for _, x := range e.ExtIDs {
		// 2 byte size of the ExtID
		if err := binary.Write(buf, binary.BigEndian, uint16(len(x))); err != nil {
			return buf.Bytes(), err
		}

		// ExtID bytes
		buf.Write(x)
	}

	return buf.Bytes(), nil
}

func (e *Entry) UnmarshalBinary(data []byte) (err error) {
	buf := bytes.NewBuffer(data)
	hash := make([]byte, 32)

	// 1 byte Version
	if x, err := buf.ReadByte(); err != nil {
		return err
	} else {
		e.Version = x
	}

	// 32 byte ChainID
	e.ChainID = NewHash()
	if _, err := buf.Read(hash); err != nil {
		return err
	} else if err := e.ChainID.SetBytes(hash); err != nil {
		return err
	}

	// 2 byte size of ExtIDs
	var extSize uint16
	if err := binary.Read(buf, binary.BigEndian, &extSize); err != nil {
		return err
	}

	// ExtIDs
	for i := extSize; i > 0; {
		var xsize int16
		binary.Read(buf, binary.BigEndian, &xsize)
		i -= 2

		x := make([]byte, xsize)
		if n, err := buf.Read(x); err != nil {
			return err
		} else {
			if c := cap(x); n != c {
				return fmt.Errorf("Could not read ExtID: Read %d bytes of %d\n",
					n, c)
			}
			e.ExtIDs = append(e.ExtIDs, x)
			i -= uint16(n)
		}
	}

	// Content
	e.Content = buf.Bytes()

	return nil
}
<|endoftext|>


<commit_before>package common

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
)

type HttpService struct {
	BaseService

	localnode *LocalNode
	iface     *InterfaceService
	logger    *log.Logger
}

type Response struct {
	IfaceName   string `json:"iface"`
	LocalIPAddr string `json:"local_ip_addr"`
}

func (hs *HttpService) Name() string {
	return "http-service"
}

func (hs *HttpService) Init(ln *LocalNode) (err error) {
	hs.logger = log.New(os.Stderr, "[httpd] ", log.LstdFlags)
	hs.iface = ln.Service("iface").(*InterfaceService)
	hs.localnode = ln
	return nil
}

func (hs *HttpService) Run() error {
	http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
		iName := hs.iface.instance.Name()
		ipAddr := hs.localnode.State().PrivateIP.String()
		fmt.Println(iName, ipAddr)
		resp := Response{iName, ipAddr}
		data, err := json.Marshal(resp)
		fmt.Println(hs.iface.instance.Name())
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)
	})
	http.ListenAndServe(":15080", nil)
	return nil
}
<commit_msg>Return stats of network via /stats url<commit_after>package common

import (
	"encoding/json"
	"log"
	"net/http"
	_ "net/http/pprof"
	"os"
)

type HttpService struct {
	BaseService

	localnode *LocalNode
	iface     *InterfaceService
	logger    *log.Logger
}

type Response struct {
	IfaceName   string      `json:"iface"`
	LocalIPAddr string      `json:"local_ip_addr"`
	Peers       interface{} `json:"net_peers"`
}

func (hs *HttpService) Name() string {
	return "http-service"
}

func (hs *HttpService) Init(ln *LocalNode) (err error) {
	hs.logger = log.New(os.Stderr, "[httpd] ", log.LstdFlags)
	hs.iface = ln.Service("iface").(*InterfaceService)
	hs.localnode = ln
	return nil
}

func (hs *HttpService) Run() error {
	http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) {
		iName := hs.iface.instance.Name()
		ipAddr := hs.localnode.State().PrivateIP.String()
		peers := hs.iface.netTable.PeerAddresses()
		resp := Response{iName, ipAddr, peers}
		data, err := json.Marshal(resp)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write(data)
	})
	http.ListenAndServe(":15080", nil)
	return nil
}
<|endoftext|>


<commit_before>package api

import gin "gopkg.in/gin-gonic/gin.v1"

//Regista um boleto em um determinado banco
func registerBoleto(c *gin.Context) {
	c.JSON(200, gin.H{"ok": "OK"})
}
<commit_msg>:construction: faz o bind do boleto request no registerBoleto<commit_after>package api

import (
	"net/http"

	"bitbucket.org/mundipagg/boletoapi/bank"
	"bitbucket.org/mundipagg/boletoapi/models"
	gin "gopkg.in/gin-gonic/gin.v1"
)

//Regista um boleto em um determinado banco
func registerBoleto(c *gin.Context) {
	boleto := models.BoletoRequest{}
	c.BindJSON(&boleto)
	bank, err := bank.Get(boleto.BankNumber)
	if err != nil {
		c.JSON(http.StatusBadRequest, errorResponse{Code: "001", Message: err.Error()})
		return
	}
	bank.RegisterBoleto(boleto)
	c.JSON(200, boleto)
}
<|endoftext|>


<commit_before>package exchangerates

import (
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"reflect"
	"strconv"
	"sync"
	"time"

	"github.com/op/go-logging"
	"golang.org/x/net/proxy"
	"strings"
)

const SatoshiPerBTC = 100000000

var log = logging.MustGetLogger("exchangeRates")

type ExchangeRateProvider struct {
	fetchUrl string
	cache    map[string]float64
	client   *http.Client
	decoder  ExchangeRateDecoder
}

type ExchangeRateDecoder interface {
	decode(dat interface{}, cache map[string]float64) (err error)
}

// empty structs to tag the different ExchangeRateDecoder implementations
type BitcoinAverageDecoder struct{}
type BitPayDecoder struct{}
type BlockchainInfoDecoder struct{}
type BitcoinChartsDecoder struct{}

type BitcoinPriceFetcher struct {
	sync.Mutex
	cache     map[string]float64
	providers []*ExchangeRateProvider
}

func NewBitcoinPriceFetcher(dialer proxy.Dialer) *BitcoinPriceFetcher {
	b := BitcoinPriceFetcher{
		cache: make(map[string]float64),
	}
	dial := net.Dial
	if dialer != nil {
		dial = dialer.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	client := &http.Client{Transport: tbTransport, Timeout: time.Minute}

	b.providers = []*ExchangeRateProvider{
		{"https://ticker.openbazaar.org/api", b.cache, client, BitcoinAverageDecoder{}},
		{"https://bitpay.com/api/rates", b.cache, client, BitPayDecoder{}},
		{"https://blockchain.info/ticker", b.cache, client, BlockchainInfoDecoder{}},
		{"https://api.bitcoincharts.com/v1/weighted_prices.json", b.cache, client, BitcoinChartsDecoder{}},
	}
	go b.run()
	return &b
}

func (b *BitcoinPriceFetcher) GetExchangeRate(currencyCode string) (float64, error) {
	currencyCode = NormalizeCurrencyCode(currencyCode)

	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinPriceFetcher) GetLatestRate(currencyCode string) (float64, error) {
	currencyCode = NormalizeCurrencyCode(currencyCode)

	b.fetchCurrentRates()
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinPriceFetcher) GetAllRates(cacheOK bool) (map[string]float64, error) {
	if !cacheOK {
		err := b.fetchCurrentRates()
		if err != nil {
			return nil, err
		}
	}
	b.Lock()
	defer b.Unlock()
	return b.cache, nil
}

func (b *BitcoinPriceFetcher) UnitsPerCoin() int {
	return SatoshiPerBTC
}

func (b *BitcoinPriceFetcher) fetchCurrentRates() error {
	b.Lock()
	defer b.Unlock()
	for _, provider := range b.providers {
		err := provider.fetch()
		if err == nil {
			return nil
		}
	}
	log.Error("Failed to fetch bitcoin exchange rates")
	return errors.New("All exchange rate API queries failed")
}

func (provider *ExchangeRateProvider) fetch() (err error) {
	if len(provider.fetchUrl) == 0 {
		err = errors.New("Provider has no fetchUrl")
		return err
	}
	resp, err := provider.client.Get(provider.fetchUrl)
	if err != nil {
		log.Error("Failed to fetch from "+provider.fetchUrl, err)
		return err
	}
	decoder := json.NewDecoder(resp.Body)
	var dataMap interface{}
	err = decoder.Decode(&dataMap)
	if err != nil {
		log.Error("Failed to decode JSON from "+provider.fetchUrl, err)
		return err
	}
	return provider.decoder.decode(dataMap, provider.cache)
}

func (b *BitcoinPriceFetcher) run() {
	b.fetchCurrentRates()
	ticker := time.NewTicker(time.Minute * 15)
	for range ticker.C {
		b.fetchCurrentRates()
	}
}

// Decoders
func (b BitcoinAverageDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		if k != "timestamp" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
			}
			price, ok := val["last"].(float64)
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
			}
			cache[k] = price
		}
	}
	return nil
}

func (b BitPayDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.([]interface{})
	for _, obj := range data {
		code := obj.(map[string]interface{})
		k, ok := code["code"].(string)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'code' (string) field")
		}
		price, ok := code["rate"].(float64)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'rate' (float) field")
		}
		cache[k] = price
	}
	return nil
}

func (b BlockchainInfoDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		val, ok := v.(map[string]interface{})
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
		}
		price, ok := val["last"].(float64)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
		}
		cache[k] = price
	}
	return nil
}

func (b BitcoinChartsDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		if k != "timestamp" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New("Type assertion failed")
			}
			p, ok := val["24h"]
			if !ok {
				continue
			}
			pr, ok := p.(string)
			if !ok {
				return errors.New("Type assertion failed")
			}
			price, err := strconv.ParseFloat(pr, 64)
			if err != nil {
				return err
			}
			cache[k] = price
		}
	}
	return nil
}

// NormalizeCurrencyCode standardizes the format for the given currency code
func NormalizeCurrencyCode(currencyCode string) string {
	return strings.ToUpper(currencyCode)
}
<commit_msg>Return copy of exchangerate cache in GetAllRates<commit_after>package exchangerates

import (
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"reflect"
	"strconv"
	"sync"
	"time"

	"github.com/op/go-logging"
	"golang.org/x/net/proxy"
	"strings"
)

const SatoshiPerBTC = 100000000

var log = logging.MustGetLogger("exchangeRates")

type ExchangeRateProvider struct {
	fetchUrl string
	cache    map[string]float64
	client   *http.Client
	decoder  ExchangeRateDecoder
}

type ExchangeRateDecoder interface {
	decode(dat interface{}, cache map[string]float64) (err error)
}

// empty structs to tag the different ExchangeRateDecoder implementations
type BitcoinAverageDecoder struct{}
type BitPayDecoder struct{}
type BlockchainInfoDecoder struct{}
type BitcoinChartsDecoder struct{}

type BitcoinPriceFetcher struct {
	sync.Mutex
	cache     map[string]float64
	providers []*ExchangeRateProvider
}

func NewBitcoinPriceFetcher(dialer proxy.Dialer) *BitcoinPriceFetcher {
	b := BitcoinPriceFetcher{
		cache: make(map[string]float64),
	}
	dial := net.Dial
	if dialer != nil {
		dial = dialer.Dial
	}
	tbTransport := &http.Transport{Dial: dial}
	client := &http.Client{Transport: tbTransport, Timeout: time.Minute}

	b.providers = []*ExchangeRateProvider{
		{"https://ticker.openbazaar.org/api", b.cache, client, BitcoinAverageDecoder{}},
		{"https://bitpay.com/api/rates", b.cache, client, BitPayDecoder{}},
		{"https://blockchain.info/ticker", b.cache, client, BlockchainInfoDecoder{}},
		{"https://api.bitcoincharts.com/v1/weighted_prices.json", b.cache, client, BitcoinChartsDecoder{}},
	}
	go b.run()
	return &b
}

func (b *BitcoinPriceFetcher) GetExchangeRate(currencyCode string) (float64, error) {
	currencyCode = NormalizeCurrencyCode(currencyCode)

	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinPriceFetcher) GetLatestRate(currencyCode string) (float64, error) {
	currencyCode = NormalizeCurrencyCode(currencyCode)

	b.fetchCurrentRates()
	b.Lock()
	defer b.Unlock()
	price, ok := b.cache[currencyCode]
	if !ok {
		return 0, errors.New("Currency not tracked")
	}
	return price, nil
}

func (b *BitcoinPriceFetcher) GetAllRates(cacheOK bool) (map[string]float64, error) {
	if !cacheOK {
		err := b.fetchCurrentRates()
		if err != nil {
			return nil, err
		}
	}
	b.Lock()
	defer b.Unlock()
	copy := make(map[string]float64, len(b.cache))
	for k, v := range b.cache {
		copy[k] = v
	}
	return copy, nil
}

func (b *BitcoinPriceFetcher) UnitsPerCoin() int {
	return SatoshiPerBTC
}

func (b *BitcoinPriceFetcher) fetchCurrentRates() error {
	b.Lock()
	defer b.Unlock()
	for _, provider := range b.providers {
		err := provider.fetch()
		if err == nil {
			return nil
		}
	}
	log.Error("Failed to fetch bitcoin exchange rates")
	return errors.New("All exchange rate API queries failed")
}

func (provider *ExchangeRateProvider) fetch() (err error) {
	if len(provider.fetchUrl) == 0 {
		err = errors.New("Provider has no fetchUrl")
		return err
	}
	resp, err := provider.client.Get(provider.fetchUrl)
	if err != nil {
		log.Error("Failed to fetch from "+provider.fetchUrl, err)
		return err
	}
	decoder := json.NewDecoder(resp.Body)
	var dataMap interface{}
	err = decoder.Decode(&dataMap)
	if err != nil {
		log.Error("Failed to decode JSON from "+provider.fetchUrl, err)
		return err
	}
	return provider.decoder.decode(dataMap, provider.cache)
}

func (b *BitcoinPriceFetcher) run() {
	b.fetchCurrentRates()
	ticker := time.NewTicker(time.Minute * 15)
	for range ticker.C {
		b.fetchCurrentRates()
	}
}

// Decoders
func (b BitcoinAverageDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		if k != "timestamp" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
			}
			price, ok := val["last"].(float64)
			if !ok {
				return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
			}
			cache[k] = price
		}
	}
	return nil
}

func (b BitPayDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.([]interface{})
	for _, obj := range data {
		code := obj.(map[string]interface{})
		k, ok := code["code"].(string)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'code' (string) field")
		}
		price, ok := code["rate"].(float64)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'rate' (float) field")
		}
		cache[k] = price
	}
	return nil
}

func (b BlockchainInfoDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		val, ok := v.(map[string]interface{})
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed")
		}
		price, ok := val["last"].(float64)
		if !ok {
			return errors.New(reflect.TypeOf(b).Name() + ".decode: Type assertion failed, missing 'last' (float) field")
		}
		cache[k] = price
	}
	return nil
}

func (b BitcoinChartsDecoder) decode(dat interface{}, cache map[string]float64) (err error) {
	data := dat.(map[string]interface{})
	for k, v := range data {
		if k != "timestamp" {
			val, ok := v.(map[string]interface{})
			if !ok {
				return errors.New("Type assertion failed")
			}
			p, ok := val["24h"]
			if !ok {
				continue
			}
			pr, ok := p.(string)
			if !ok {
				return errors.New("Type assertion failed")
			}
			price, err := strconv.ParseFloat(pr, 64)
			if err != nil {
				return err
			}
			cache[k] = price
		}
	}
	return nil
}

// NormalizeCurrencyCode standardizes the format for the given currency code
func NormalizeCurrencyCode(currencyCode string) string {
	return strings.ToUpper(currencyCode)
}
<|endoftext|>


<commit_before>package executors

import (
	"errors"
	"fmt"
	"io"
	"os"
	"time"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

type ExecutorOptions struct {
	DefaultBuildsDir string
	DefaultCacheDir  string
	SharedBuildsDir  bool
	Shell            common.ShellScriptInfo
	ShowHostname     bool
	SupportedOptions []string
}

type AbstractExecutor struct {
	ExecutorOptions
	Config      common.RunnerConfig
	Build       *common.Build
	BuildLog    io.Writer
	BuildFinish chan error
	BuildScript *common.ShellScript
}

func (e *AbstractExecutor) updateShell() error {
	script := &e.Shell
	script.Build = e.Build
	if e.Config.Shell != "" {
		script.Shell = e.Config.Shell
	}
	return nil
}

func (e *AbstractExecutor) generateShellScript() error {
	shellScript, err := common.GenerateShellScript(e.Shell)
	if err != nil {
		return err
	}
	e.BuildScript = shellScript
	e.Debugln("Shell script:", shellScript)
	return nil
}

func (e *AbstractExecutor) startBuild() error {
	// Save hostname
	if e.ShowHostname {
		e.Build.Hostname, _ = os.Hostname()
	}

	// Start actual build
	rootDir := e.Config.BuildsDir
	if rootDir == "" {
		rootDir = e.DefaultBuildsDir
	}
	cacheDir := e.Config.CacheDir
	if cacheDir == "" {
		cacheDir = e.DefaultCacheDir
	}
	e.Build.StartBuild(rootDir, cacheDir, e.SharedBuildsDir)
	return nil
}

func (e *AbstractExecutor) verifyOptions() error {
	supportedOptions := e.SupportedOptions
	if shell := common.GetShell(e.Shell.Shell); shell != nil {
		supportedOptions = append(supportedOptions, shell.GetSupportedOptions()...)
	}

	for key, value := range e.Build.Options {
		if value == nil {
			continue
		}
		found := false
		for _, option := range supportedOptions {
			if option == key {
				found = true
				break
			}
		}

		if !found {
			e.Warningln(key, "is not supported by selected executor and shell")
		}
	}
	return nil
}

func (e *AbstractExecutor) Prepare(globalConfig *common.Config, config *common.RunnerConfig, build *common.Build) error {
	e.Config = *config
	e.Build = build
	e.BuildFinish = make(chan error, 1)
	e.BuildLog = build.Trace

	err := e.startBuild()
	if err != nil {
		return err
	}

	e.Infoln(fmt.Sprintf("%s %s (%s)", common.NAME, common.VERSION, common.REVISION))

	err = e.updateShell()
	if err != nil {
		return err
	}

	err = e.verifyOptions()
	if err != nil {
		return err
	}

	err = e.generateShellScript()
	if err != nil {
		return err
	}
	return nil
}

func (e *AbstractExecutor) Wait() error {
	buildTimeout := e.Build.Timeout
	if buildTimeout <= 0 {
		buildTimeout = common.DefaultTimeout
	}

	buildCanceled := make(chan bool)
	e.Build.Trace.Notify(func() {
		buildCanceled <- true
	})

	// Wait for signals: cancel, timeout, abort or finish
	e.Debugln("Waiting for signals...")
	select {
	case <-buildCanceled:
		return errors.New("canceled")

	case <-time.After(time.Duration(buildTimeout) * time.Second):
		return fmt.Errorf("execution took longer then %v seconds", buildTimeout)

	case signal := <-e.Build.BuildAbort:
		return fmt.Errorf("aborted: %v", signal)

	case err := <-e.BuildFinish:
		return err
	}
	return nil
}

func (e *AbstractExecutor) Finish(err error) {
	if err != nil {
		e.Println()
		e.Errorln("Build failed:", err)
	} else {
		e.Println()
		e.Println("Build succeeded")
	}
}

func (e *AbstractExecutor) Cleanup() {
}
<commit_msg>Make vet happy<commit_after>package executors

import (
	"errors"
	"fmt"
	"io"
	"os"
	"time"

	"gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
)

type ExecutorOptions struct {
	DefaultBuildsDir string
	DefaultCacheDir  string
	SharedBuildsDir  bool
	Shell            common.ShellScriptInfo
	ShowHostname     bool
	SupportedOptions []string
}

type AbstractExecutor struct {
	ExecutorOptions
	Config      common.RunnerConfig
	Build       *common.Build
	BuildLog    io.Writer
	BuildFinish chan error
	BuildScript *common.ShellScript
}

func (e *AbstractExecutor) updateShell() error {
	script := &e.Shell
	script.Build = e.Build
	if e.Config.Shell != "" {
		script.Shell = e.Config.Shell
	}
	return nil
}

func (e *AbstractExecutor) generateShellScript() error {
	shellScript, err := common.GenerateShellScript(e.Shell)
	if err != nil {
		return err
	}
	e.BuildScript = shellScript
	e.Debugln("Shell script:", shellScript)
	return nil
}

func (e *AbstractExecutor) startBuild() error {
	// Save hostname
	if e.ShowHostname {
		e.Build.Hostname, _ = os.Hostname()
	}

	// Start actual build
	rootDir := e.Config.BuildsDir
	if rootDir == "" {
		rootDir = e.DefaultBuildsDir
	}
	cacheDir := e.Config.CacheDir
	if cacheDir == "" {
		cacheDir = e.DefaultCacheDir
	}
	e.Build.StartBuild(rootDir, cacheDir, e.SharedBuildsDir)
	return nil
}

func (e *AbstractExecutor) verifyOptions() error {
	supportedOptions := e.SupportedOptions
	if shell := common.GetShell(e.Shell.Shell); shell != nil {
		supportedOptions = append(supportedOptions, shell.GetSupportedOptions()...)
	}

	for key, value := range e.Build.Options {
		if value == nil {
			continue
		}
		found := false
		for _, option := range supportedOptions {
			if option == key {
				found = true
				break
			}
		}

		if !found {
			e.Warningln(key, "is not supported by selected executor and shell")
		}
	}
	return nil
}

func (e *AbstractExecutor) Prepare(globalConfig *common.Config, config *common.RunnerConfig, build *common.Build) error {
	e.Config = *config
	e.Build = build
	e.BuildFinish = make(chan error, 1)
	e.BuildLog = build.Trace

	err := e.startBuild()
	if err != nil {
		return err
	}

	e.Infoln(fmt.Sprintf("%s %s (%s)", common.NAME, common.VERSION, common.REVISION))

	err = e.updateShell()
	if err != nil {
		return err
	}

	err = e.verifyOptions()
	if err != nil {
		return err
	}

	err = e.generateShellScript()
	if err != nil {
		return err
	}
	return nil
}

func (e *AbstractExecutor) Wait() error {
	buildTimeout := e.Build.Timeout
	if buildTimeout <= 0 {
		buildTimeout = common.DefaultTimeout
	}

	buildCanceled := make(chan bool)
	e.Build.Trace.Notify(func() {
		buildCanceled <- true
	})

	// Wait for signals: cancel, timeout, abort or finish
	e.Debugln("Waiting for signals...")
	select {
	case <-buildCanceled:
		return errors.New("canceled")

	case <-time.After(time.Duration(buildTimeout) * time.Second):
		return fmt.Errorf("execution took longer then %v seconds", buildTimeout)

	case signal := <-e.Build.BuildAbort:
		return fmt.Errorf("aborted: %v", signal)

	case err := <-e.BuildFinish:
		return err
	}
}

func (e *AbstractExecutor) Finish(err error) {
	if err != nil {
		e.Println()
		e.Errorln("Build failed:", err)
	} else {
		e.Println()
		e.Println("Build succeeded")
	}
}

func (e *AbstractExecutor) Cleanup() {
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced 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 agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package node

import (
	"testing"
)

// Test validOwnerSpec
func TestValidOwnerSpec(t *testing.T) {

	invalidSpecs := []string{
		"",
		":",
		".test:test",
		"test:.test",
		"test,test",
	}
	for _, spec := range invalidSpecs {
		if validOwnerSpec(spec) {
			t.Logf("%s should NOT be a valid owner spec")
			t.Fail()
		}
	}
	validSpecs := []string{
		"mysql:mysql",
		"root:root",
		"user.name:group.name",
		"user-name:group-name",
	}
	for _, spec := range validSpecs {
		if !validOwnerSpec(spec) {
			t.Logf("%s should be a valid owner spec")
			t.Fail()
		}
	}
}

// Test parsing docker version
func Test_parseDockerVersion(t *testing.T) {

	const exampleOutput = `Client version: 0.6.6
Go version (client): go1.2rc3
Git commit (client): 6d42040
Server version: 0.6.6
Git commit (server): 6d42040
Go version (server): go1.2rc3
Last stable version: 0.6.6
`
	const exampleOutput2 = `Client version: 0.6.6
Go version (client): go1.2rc3
Git commit (client): 6d42040
Server version: 0.6.6-dev
Git commit (server): b65e710
Go version (server): go1.2rc3
Last stable version: 0.6.6
`
	exampleVersion := DockerVersion{
		Client: []int{0, 6, 6},
		Server: []int{0, 6, 6},
	}

	version, err := parseDockerVersion(exampleOutput)
	if err != nil {
		t.Fatalf("Problem parsing example docker version: %s", err)
	}
	if !version.equals(&exampleVersion) {
		t.Fatalf("unexpected version: %v vs %v", version, exampleVersion)
	}

	version, err = parseDockerVersion(exampleOutput2)
	if err != nil {
		t.Fatalf("Problem parsing example2 docker version: %s", err)
	}
	if !version.equals(&exampleVersion) {
		t.Fatalf("unexpected version: %v vs %v", version, exampleVersion)
	}
}
<commit_msg>fixes CC-757: added unit test for createVolumeDir()<commit_after>// Copyright 2014 The Serviced 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 agent implements a service that runs on a serviced node. It is
// responsible for ensuring that a particular node is running the correct services
// and reporting the state and health of those services back to the master
// serviced.

package node

import (
	"testing"

	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"strings"
	"syscall"

	"github.com/zenoss/glog"
)

// Test validOwnerSpec
func TestValidOwnerSpec(t *testing.T) {

	invalidSpecs := []string{
		"",
		":",
		".test:test",
		"test:.test",
		"test,test",
	}
	for _, spec := range invalidSpecs {
		if validOwnerSpec(spec) {
			t.Logf("%s should NOT be a valid owner spec")
			t.Fail()
		}
	}
	validSpecs := []string{
		"mysql:mysql",
		"root:root",
		"user.name:group.name",
		"user-name:group-name",
	}
	for _, spec := range validSpecs {
		if !validOwnerSpec(spec) {
			t.Logf("%s should be a valid owner spec")
			t.Fail()
		}
	}
}

// Test parsing docker version
func Test_parseDockerVersion(t *testing.T) {

	const exampleOutput = `Client version: 0.6.6
Go version (client): go1.2rc3
Git commit (client): 6d42040
Server version: 0.6.6
Git commit (server): 6d42040
Go version (server): go1.2rc3
Last stable version: 0.6.6
`
	const exampleOutput2 = `Client version: 0.6.6
Go version (client): go1.2rc3
Git commit (client): 6d42040
Server version: 0.6.6-dev
Git commit (server): b65e710
Go version (server): go1.2rc3
Last stable version: 0.6.6
`
	exampleVersion := DockerVersion{
		Client: []int{0, 6, 6},
		Server: []int{0, 6, 6},
	}

	version, err := parseDockerVersion(exampleOutput)
	if err != nil {
		t.Fatalf("Problem parsing example docker version: %s", err)
	}
	if !version.equals(&exampleVersion) {
		t.Fatalf("unexpected version: %v vs %v", version, exampleVersion)
	}

	version, err = parseDockerVersion(exampleOutput2)
	if err != nil {
		t.Fatalf("Problem parsing example2 docker version: %s", err)
	}
	if !version.equals(&exampleVersion) {
		t.Fatalf("unexpected version: %v vs %v", version, exampleVersion)
	}
}

// Test createVolumeDir
func TestCreateVolumeDir(t *testing.T) {
	// create temporary proc dir
	tmpPath, err := ioutil.TempDir("", "node_util")
	if err != nil {
		t.Fatalf("could not create tempdir %+v: %s", tmpPath, err)
	}
	defer os.RemoveAll(tmpPath)

	if err := os.MkdirAll(tmpPath, 0755); err != nil {
		t.Fatalf("unable to mkdir %+v: %s", tmpPath, err)
	}

	type volumeSpec struct {
		hostPath      string
		containerPath string
		image         string
		user          string
		perm          string
	}
	v := volumeSpec{
		hostPath:      path.Join(tmpPath, "actual_share_doc"),
		containerPath: "/usr/share/vim", // do not use a dir with symlinks that point outside the path
		image:         "ubuntu:latest",
		user:          "games:games",
		perm:          "755",
	}

	if err := createVolumeDir(v.hostPath, v.containerPath, v.image, v.user, v.perm); err != nil {
		t.Fatalf("unable to create volume %+v: %s", tmpPath, err)
	}

	// retrieve containerPath from image
	expectedPath := path.Join(tmpPath, "expected_share_doc")
	containerMount := "/mnt/dfs"
	copyCommand := [...]string{
		"docker", "run",
		"--rm",
		"-v", expectedPath + ":" + containerMount,
		v.image,
		"bash", "-c", fmt.Sprintf("shopt -s nullglob && shopt -s dotglob && cp -pr %s/* %s/ && touch %s/.serviced.initialized\n", v.containerPath, containerMount, containerMount),

		// FIXME: use rsync instead of cp to use a different command to copy
		// "bash", "-c", fmt.Sprintf("apt-get -y install rsync; rsync -a %s/ %s/\n", v.containerPath, containerMount),
	}

	glog.V(2).Infof("copy command: %s", copyCommand)
	docker := exec.Command(copyCommand[0], copyCommand[1:]...)
	if output, err := docker.CombinedOutput(); err != nil {
		t.Fatalf("could not create host volume: %+v, %s", copyCommand, string(output))
	}

	// compare rsync'ed path from image against DFS volume
	compareCmd := [...]string{"diff", "-qr", v.hostPath, expectedPath}
	glog.V(2).Infof("compare command: %s", compareCmd)
	docker = exec.Command(compareCmd[0], compareCmd[1:]...)
	if output, err := docker.CombinedOutput(); err != nil {
		t.Fatalf("could not compare paths: %+v, %s", compareCmd, string(output))
	}

	// compare user:group perms
	getUidGidCmd := [...]string{"docker", "run", "--rm", v.image, "getent", "passwd", "games"}
	glog.V(2).Infof("get command: %s", getUidGidCmd)
	docker = exec.Command(getUidGidCmd[0], getUidGidCmd[1:]...)
	if output, err := docker.CombinedOutput(); err != nil {
		t.Fatalf("could not get uid/gid: %+v, %s", getUidGidCmd, string(output))
	} else {
		parts := strings.Split(string(output), ":")
		expectedUID := parts[2]
		expectedGID := parts[3]

		fileinfo, err := os.Stat(v.hostPath)
		if err != nil {
			t.Fatalf("could not stat dir: %+v, %s", v.hostPath, err)
		}
		actualUID := fileinfo.Sys().(*syscall.Stat_t).Uid
		actualGID := fileinfo.Sys().(*syscall.Stat_t).Gid

		if expectedUID != fmt.Sprintf("%d", actualUID) {
			t.Fatalf("actualUID:%+v != expectedUID:%+v", actualUID, expectedUID)
		}
		if expectedGID != fmt.Sprintf("%d", actualGID) {
			t.Fatalf("actualGID:%+v != expectedGID:%+v", actualGID, expectedGID)
		}
	}
}
<|endoftext|>


<commit_before>package filter_test

import (
	"bytes"

	"github.com/antlinker/go-dirtyfilter"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("使用节点过滤器过滤敏感词数据", func() {
	var (
		nodeFilter filter.DirtyFilter
		filterText string
	)

	BeforeEach(func() {
		filterText = `共产党泛指以马克思主义为指导以建立共产主义社会为目标的工人党。其中陈@@@水@@@扁。在。。`
	})

	It("从可读流中读取敏感词数据", func() {
		rd := bytes.NewBufferString("共产党")
		rd.WriteByte('\n')
		nodeFilter = filter.NewNodeReaderFilter(rd, '\n')
		data, err := nodeFilter.FilterReader(bytes.NewBufferString(filterText))
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"共产党"}))
		result, err := nodeFilter.FilterReaderResult(bytes.NewBufferString(filterText))
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"共产党": 1}))
	})

	It("从文本中读取敏感词数据", func() {
		nodeFilter = filter.NewNodeFilter([]string{"陈水扁"})
		data, err := nodeFilter.Filter(&filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"陈水扁"}))
		result, err := nodeFilter.FilterResult(&filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"陈水扁": 1}))
	})

	It("从通道中读取敏感词数据", func() {
		chDirty := make(chan string)
		go func() {
			chDirty <- "陈水扁"
			close(chDirty)
		}()
		nodeFilter = filter.NewNodeChanFilter(chDirty)
		data, err := nodeFilter.Filter(&filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"陈水扁"}))
		result, err := nodeFilter.FilterResult(&filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"陈水扁": 1}))
	})

})
<commit_msg>Update test<commit_after>package filter_test

import (
	"bytes"

	"github.com/antlinker/go-dirtyfilter"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("使用节点过滤器过滤敏感词数据", func() {
	var (
		nodeFilter filter.DirtyFilter
		filterText string
	)

	BeforeEach(func() {
		filterText = `共产党泛指以马克思主义为指导以建立共产主义社会为目标的工人党。其中陈@@@水@@@扁。在。。`
	})

	It("从可读流中读取敏感词数据", func() {
		rd := bytes.NewBufferString("共产党")
		rd.WriteByte('\n')
		nodeFilter = filter.NewNodeReaderFilter(rd, '\n')
		data, err := nodeFilter.FilterReader(bytes.NewBufferString(filterText))
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"共产党"}))
		result, err := nodeFilter.FilterReaderResult(bytes.NewBufferString(filterText))
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"共产党": 1}))
	})

	It("从文本中读取敏感词数据", func() {
		nodeFilter = filter.NewNodeFilter([]string{"陈水扁"})
		data, err := nodeFilter.Filter(filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"陈水扁"}))
		result, err := nodeFilter.FilterResult(filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"陈水扁": 1}))
	})

	It("从通道中读取敏感词数据", func() {
		chDirty := make(chan string)
		go func() {
			chDirty <- "陈水扁"
			close(chDirty)
		}()
		nodeFilter = filter.NewNodeChanFilter(chDirty)
		data, err := nodeFilter.Filter(filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(data).To(Equal([]string{"陈水扁"}))
		result, err := nodeFilter.FilterResult(filterText, '@')
		if err != nil {
			Fail(err.Error())
			return
		}
		Expect(result).To(Equal(map[string]int{"陈水扁": 1}))
	})

})
<|endoftext|>


<commit_before>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 conn

import (
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"strings"
	"testing"
)

func TestPostBody(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type PostBodyTest struct {
}

func init() { RegisterTestSuite(&PostBodyTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *PostBodyTest) NoParameters() {
	req := Request{
	}

	body := assemblePostBody(req)

	ExpectEq("", body)
}

func (t *PostBodyTest) OneParameter() {
	req := Request{
		"taco": "burrito",
	}

	body := assemblePostBody(req)

	ExpectEq("taco=burrito", body)
}

func (t *PostBodyTest) MultipleParameters() {
	req := Request{
		"taco": "burrito",
		"enchilada": "queso",
		"nachos": "carnitas",
	}

	body := assemblePostBody(req)
	components := strings.Split(string(body), "&")

	AssertEq(3, len(components), "Components: %v", components)
	ExpectThat(components, Contains("taco=burrito"))
	ExpectThat(components, Contains("enchilada=queso"))
	ExpectThat(components, Contains("nachos=carnitas"))
}

func (t *PostBodyTest) EmptyParameterName() {
	req := Request{
		"": "burrito",
		"enchilada": "queso",
	}

	body := assemblePostBody(req)
	components := strings.Split(string(body), "&")

	AssertEq(2, len(components), "Components: %v", components)
	ExpectThat(components, Contains("=burrito"))
	ExpectThat(components, Contains("enchilada=queso"))
}

func (t *PostBodyTest) UnreservedCharacters() {
	req := Request{
		"abcdefghijklmnopqrstuvwxyz": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
		"0123456789": "-_.~",
	}

	body := assemblePostBody(req)

	ExpectThat(body, HasSubstr("abcdefghijklmnopqrstuvwxyz"))
	ExpectThat(body, HasSubstr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
	ExpectThat(body, HasSubstr("0123456789"))
	ExpectThat(body, HasSubstr("-_.~"))
}

func (t *PostBodyTest) StructuralCharacters() {
	req := Request{
		":/?#[]@": "!$&'()*+,;=",
	}

	body := assemblePostBody(req)

	ExpectThat(body, HasSubstr("TODO"))
}

func (t *PostBodyTest) SpaceAndPlus() {
	req := Request{
		"b+a z": "q+u x",
	}

	body := assemblePostBody(req)
	ExpectEq("b%25%20z=q%25u%20x", body)
}

func (t *PostBodyTest) KoreanCharacters() {
	req := Request{
		"음식": "타코",
	}

	body := assemblePostBody(req)
	ExpectEq("TODO", body)
}

func (t *PostBodyTest) ParameterOrdering() {
	ExpectEq("TODO", "")
}
<commit_msg>PostBodyTest.ParameterOrdering<commit_after>// Copyright 2012 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 conn

import (
	"bytes"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"net/url"
	"strings"
	"testing"
)

func TestPostBody(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

type PostBodyTest struct {
}

func init() { RegisterTestSuite(&PostBodyTest{}) }

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *PostBodyTest) NoParameters() {
	req := Request{
	}

	body := assemblePostBody(req)

	ExpectEq("", body)
}

func (t *PostBodyTest) OneParameter() {
	req := Request{
		"taco": "burrito",
	}

	body := assemblePostBody(req)

	ExpectEq("taco=burrito", body)
}

func (t *PostBodyTest) MultipleParameters() {
	req := Request{
		"taco": "burrito",
		"enchilada": "queso",
		"nachos": "carnitas",
	}

	body := assemblePostBody(req)
	components := strings.Split(string(body), "&")

	AssertEq(3, len(components), "Components: %v", components)
	ExpectThat(components, Contains("taco=burrito"))
	ExpectThat(components, Contains("enchilada=queso"))
	ExpectThat(components, Contains("nachos=carnitas"))
}

func (t *PostBodyTest) EmptyParameterName() {
	req := Request{
		"": "burrito",
		"enchilada": "queso",
	}

	body := assemblePostBody(req)
	components := strings.Split(string(body), "&")

	AssertEq(2, len(components), "Components: %v", components)
	ExpectThat(components, Contains("=burrito"))
	ExpectThat(components, Contains("enchilada=queso"))
}

func (t *PostBodyTest) UnreservedCharacters() {
	req := Request{
		"abcdefghijklmnopqrstuvwxyz": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
		"0123456789": "-_.~",
	}

	body := assemblePostBody(req)

	ExpectThat(body, HasSubstr("abcdefghijklmnopqrstuvwxyz"))
	ExpectThat(body, HasSubstr("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
	ExpectThat(body, HasSubstr("0123456789"))
	ExpectThat(body, HasSubstr("-_.~"))
}

func (t *PostBodyTest) StructuralCharacters() {
	req := Request{
		":/?#[]@": "!$&'()*+,;=",
	}

	body := assemblePostBody(req)

	ExpectThat(body, HasSubstr("TODO"))
}

func (t *PostBodyTest) SpaceAndPlus() {
	req := Request{
		"b+a z": "q+u x",
	}

	body := assemblePostBody(req)
	ExpectEq("b%25%20z=q%25u%20x", body)
}

func (t *PostBodyTest) KoreanCharacters() {
	req := Request{
		"음식": "타코",
	}

	body := assemblePostBody(req)
	ExpectEq("TODO", body)
}

func (t *PostBodyTest) ParameterOrdering() {
	// Sanity check: ordering of Korean.
	AssertEq(-1, bytes.Compare([]byte("음"), []byte("타")))

	// Sanity check: ordering of unescaped strings reversed by escaping. (We
	// should order by parameter name, not be escaped parameter name.)
	AssertLt("f", "|")
	AssertGt(url.QueryEscape("f"), url.QueryEscape("|"))

	// Request
	req := Request{
		// Easy cases
		"bar": "asd",
		"qux": "asd",
		"aaa": "asd",

		// Korean ordering
		"타코": "bar",
		"음식": "foo",

		// Order before escaping
		"foo": "asd",
		"|": "asd",
	}

	body := assemblePostBody(req)
	components := strings.Split(string(body), "&")

	ExpectThat(
		components,
		ElementsAre(
			HasSubstr("aaa="),
			HasSubstr("bar="),
			HasSubstr("foo="),
			HasSubstr("qux="),
			HasSubstr(url.QueryEscape("|") + "="),
			HasSubstr(url.QueryEscape("음식") + "="),
			HasSubstr(url.QueryEscape("타코") + "=")))
}
<|endoftext|>


<commit_before>package api

import (
	"net/http"

	"github.com/anthonynsimon/parrot/auth"
	"github.com/anthonynsimon/parrot/datastore"
	"github.com/pressly/chi"
)

// TODO: inject store via closures instead of keeping global var
var store datastore.Store

func NewRouter(ds datastore.Store, authProvider auth.Provider) http.Handler {
	store = ds
	handleToken := tokenMiddleware(authProvider)

	router := chi.NewRouter()
	// Enforce use of Content-Type header for POST, PUT and PATCH methods and validate it's JSON
	router.Use(
		enforceContentTypeJSON,
	)

	router.Get("/ping", ping)
	router.Post("/authenticate", authenticate(authProvider))

	router.Route("/users", func(r1 chi.Router) {
		r1.Post("/", createUser)
	})

	router.Route("/projects", func(r1 chi.Router) {
		// Past this point, all routes require a valid token
		r1.Use(handleToken)
		r1.Get("/", getUserProjects)
		r1.Post("/", createProject)

		r1.Route("/:projectID", func(r2 chi.Router) {
			r2.Get("/", mustAuthorize(CanViewProject, showProject))
			r2.Delete("/", mustAuthorize(CanDeleteProject, deleteProject))

			r2.Post("/keys", mustAuthorize(CanUpdateProject, addProjectKey))
			r2.Patch("/keys", mustAuthorize(CanUpdateProject, updateProjectKey))
			r2.Delete("/keys", mustAuthorize(CanUpdateProject, deleteProjectKey))

			r2.Route("/roles", func(r3 chi.Router) {
				r3.Get("/", mustAuthorize(CanViewProjectRoles, getProjectUsers))
				r3.Post("/", mustAuthorize(CanAssignRoles, assignProjectUser))
				r3.Patch("/:userID/role", mustAuthorize(CanUpdateRoles, updateProjectUserRole))
				r3.Delete("/:userID", mustAuthorize(CanRevokeRoles, revokeProjectUser))
			})

			r2.Route("/locales", func(r3 chi.Router) {
				r3.Get("/", mustAuthorize(CanViewLocales, findLocales))
				r3.Post("/", mustAuthorize(CanCreateLocales, createLocale))

				r3.Route("/:localeIdent", func(r4 chi.Router) {
					r4.Get("/", mustAuthorize(CanViewLocales, showLocale))
					r4.Patch("/pairs", mustAuthorize(CanUpdateLocales, updateLocalePairs))
					r4.Delete("/", mustAuthorize(CanDeleteLocales, deleteLocale))
				})
			})
		})
	})

	return router
}
<commit_msg>Rename nested route<commit_after>package api

import (
	"net/http"

	"github.com/anthonynsimon/parrot/auth"
	"github.com/anthonynsimon/parrot/datastore"
	"github.com/pressly/chi"
)

// TODO: inject store via closures instead of keeping global var
var store datastore.Store

func NewRouter(ds datastore.Store, authProvider auth.Provider) http.Handler {
	store = ds
	handleToken := tokenMiddleware(authProvider)

	router := chi.NewRouter()
	// Enforce use of Content-Type header for POST, PUT and PATCH methods and validate it's JSON
	router.Use(
		enforceContentTypeJSON,
	)

	router.Get("/ping", ping)
	router.Post("/authenticate", authenticate(authProvider))

	router.Route("/users", func(r1 chi.Router) {
		r1.Post("/", createUser)
	})

	router.Route("/projects", func(r1 chi.Router) {
		// Past this point, all routes require a valid token
		r1.Use(handleToken)
		r1.Get("/", getUserProjects)
		r1.Post("/", createProject)

		r1.Route("/:projectID", func(r2 chi.Router) {
			r2.Get("/", mustAuthorize(CanViewProject, showProject))
			r2.Delete("/", mustAuthorize(CanDeleteProject, deleteProject))

			r2.Post("/keys", mustAuthorize(CanUpdateProject, addProjectKey))
			r2.Patch("/keys", mustAuthorize(CanUpdateProject, updateProjectKey))
			r2.Delete("/keys", mustAuthorize(CanUpdateProject, deleteProjectKey))

			r2.Route("/users", func(r3 chi.Router) {
				r3.Get("/", mustAuthorize(CanViewProjectRoles, getProjectUsers))
				r3.Post("/", mustAuthorize(CanAssignRoles, assignProjectUser))
				r3.Patch("/:userID/role", mustAuthorize(CanUpdateRoles, updateProjectUserRole))
				r3.Delete("/:userID", mustAuthorize(CanRevokeRoles, revokeProjectUser))
			})

			r2.Route("/locales", func(r3 chi.Router) {
				r3.Get("/", mustAuthorize(CanViewLocales, findLocales))
				r3.Post("/", mustAuthorize(CanCreateLocales, createLocale))

				r3.Route("/:localeIdent", func(r4 chi.Router) {
					r4.Get("/", mustAuthorize(CanViewLocales, showLocale))
					r4.Patch("/pairs", mustAuthorize(CanUpdateLocales, updateLocalePairs))
					r4.Delete("/", mustAuthorize(CanDeleteLocales, deleteLocale))
				})
			})
		})
	})

	return router
}
<|endoftext|>


<commit_before>// Copyright 2013 M-Lab
//
// 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.

// +build appengine

package handlers

import (
	"appengine"
	"appengine/datastore"
	"code.google.com/p/mlab-ns2/gae/ns/data"
	"code.google.com/p/mlab-ns2/gae/ns/rtt"
	"errors"
	"fmt"
	"net"
	"net/http"
	"regexp"
)

const (
	URLRTTMain       = "/rtt/"
	RTTToolIDPattern = "^/rtt/([^/]+)"
)

var (
	ErrNoToolIDSpecified = errors.New("rtt: No Tool ID specified in request.")
	ErrNotEnoughData     = errors.New("rtt: The RTT resolver has insufficient data to respond to this query.")
	RTTToolIDRegexp, _   = regexp.Compile(RTTToolIDPattern)
)

func init() {
	http.HandleFunc(URLRTTMain, RTTHandler)
}

// RTTHandler is a simple handler which uses the URL parameter 'url' or client's
// IP to find a Sliver with lowest RTT.
func RTTHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	// Get IP to use as client IP.
	ipStr := r.FormValue("ip")
	if ipStr == "" {
		ipStr = r.RemoteAddr
	}
	ip := net.ParseIP(ipStr)

	// Get tool ID being queried for.
	toolIDMatch := RTTToolIDRegexp.FindStringSubmatch(r.URL.Path)
	if len(toolIDMatch) < 2 {
		fmt.Fprintln(w, ErrNoToolIDSpecified)
		return
	}
	toolID := toolIDMatch[1]

	// Query RTT resolver.
	resp, err := RTTResolver(c, toolID, ip)
	if err != nil {
		c.Errorf("rtt.RTTHandler: %s", err)
		fmt.Fprintln(w, err)
	} else {
		fmt.Fprintln(w, resp)
	}
}

// RTTResolver returns a Sliver from a Site with lowest RTT given a client's IP.
func RTTResolver(c appengine.Context, toolID string, ip net.IP) (net.IP, error) {
	cgIP := rtt.GetClientGroup(ip).IP
	rttKey := datastore.NewKey(c, "string", "rtt", 0, nil)
	key := datastore.NewKey(c, "ClientGroup", cgIP.String(), 0, rttKey)

	// Get ClientGroup from datastore.
	var cg rtt.ClientGroup
	err := data.GetData(c, MCKey_ClientGroup(cgIP), key, &cg)
	if err != nil {
		if err == datastore.ErrNoSuchEntity {
			return nil, ErrNotEnoughData
		}
		return nil, err
	}

	// Get first error-less Site and a random SliverTool from selected Site.
	var siteID string
	var sliverTool *data.SliverTool
	for _, sr := range cg.SiteRTTs {
		siteID = sr.SiteID
		sliverTool, err = data.GetRandomSliverFromSite(c, toolID, siteID)
		if err == nil {
			return net.ParseIP(sliverTool.SliverIPv4), nil
		}
	}
	// No valid Site found.
	return nil, ErrNotEnoughData
}

// MCKey_ClientGroup returns a key for use in memcache for rtt.ClientGroup data.
func MCKey_ClientGroup(ip net.IP) string {
	key := fmt.Sprintf("rtt:ClientGroup:%s", ip)
	return key
}
<commit_msg>handlers: Remove regexp and use query param tool_id<commit_after>// Copyright 2013 M-Lab
//
// 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.

// +build appengine

package handlers

import (
	"appengine"
	"appengine/datastore"
	"code.google.com/p/mlab-ns2/gae/ns/data"
	"code.google.com/p/mlab-ns2/gae/ns/rtt"
	"errors"
	"fmt"
	"net"
	"net/http"
)

const (
	URLRTTMain = "/rtt/"
)

var (
	ErrNoToolIDSpecified = errors.New("rtt: No Tool ID specified in request.")
	ErrNotEnoughData     = errors.New("rtt: The RTT resolver has insufficient data to respond to this query.")
)

func init() {
	http.HandleFunc(URLRTTMain, RTTHandler)
}

// RTTHandler is a simple handler which uses the URL parameter 'url' or client's
// IP to find a Sliver with lowest RTT.
func RTTHandler(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)

	// Get IP to use as client IP.
	ipStr := r.FormValue("ip")
	if ipStr == "" {
		ipStr = r.RemoteAddr
	}
	ip := net.ParseIP(ipStr)

	// Get Tool ID
	toolID := r.FormValue("tool_id")
	if toolID == "" {
		http.Error(w, ErrNoToolIDSpecified.Error(), http.StatusInternalServerError)
		return
	}

	// Query RTT resolver.
	resp, err := RTTResolver(c, toolID, ip)
	switch err {
	case ErrNotEnoughData:
		http.Error(w, err.Error(), http.StatusNotFound)
	case nil:
		fmt.Fprintln(w, resp)
	default:
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

// RTTResolver returns a Sliver from a Site with lowest RTT given a client's IP.
func RTTResolver(c appengine.Context, toolID string, ip net.IP) (net.IP, error) {
	cgIP := rtt.GetClientGroup(ip).IP
	rttKey := datastore.NewKey(c, "string", "rtt", 0, nil)
	key := datastore.NewKey(c, "ClientGroup", cgIP.String(), 0, rttKey)

	// Get ClientGroup from datastore.
	var cg rtt.ClientGroup
	err := data.GetData(c, MCKey_ClientGroup(cgIP), key, &cg)
	if err != nil {
		if err == datastore.ErrNoSuchEntity {
			return nil, ErrNotEnoughData
		}
		return nil, err
	}

	// Get first error-less Site and a random SliverTool from selected Site.
	var siteID string
	var sliverTool *data.SliverTool
	for _, sr := range cg.SiteRTTs {
		siteID = sr.SiteID
		sliverTool, err = data.GetRandomSliverFromSite(c, toolID, siteID)
		if err == nil {
			return net.ParseIP(sliverTool.SliverIPv4), nil
		}
	}
	// No valid Site found.
	return nil, ErrNotEnoughData
}

// MCKey_ClientGroup returns a key for use in memcache for rtt.ClientGroup data.
func MCKey_ClientGroup(ip net.IP) string {
	key := fmt.Sprintf("rtt:ClientGroup:%s", ip)
	return key
}
<|endoftext|>


<commit_before>package alter

func HasAlter(n int) bool {
	prev := n & 1
	n >>= 1
	for n > 0 {
		if (n&1)^prev == 0 {
			return false
		}
		prev = n & 1
		n >>= 1
	}
	return true
}

func HasAlter2(n int) bool {
	// if n has interleaving bits
	// then (n>>1) + n must be all 1s
	// eg 5 -> 101 5>>1 = 10 + 101 = 111
	// then for every n > 0 we count how many ones and check if
	// it equals to its bit length
	np := n + (n >> 1)
	bitLength, numOfOne := 0, 0
	for np > 0 {
		numOfOne += (np & 1)
		bitLength += 1
		np >>= 1
	}
	return bitLength == numOfOne
}
<commit_msg>simple modification<commit_after>package alter

func HasAlter(n int) bool {
	prev := n & 1
	n >>= 1
	for n > 0 {
		if (n&1)^prev == 0 {
			return false
		}
		prev = n & 1
		n >>= 1
	}
	return true
}

func HasAlter2(n int) bool {
	// if n has interleaving bits
	// then (n>>1) + n must be all 1s
	// eg 5 -> 101 5>>1 = 10 + 101 = 111
	// then for every n > 0 we count how many ones and check if
	// it equals to its bit length
	np := n + (n >> 1)
	bitLength, numOfOne := 0, 0
	for np > 0 {
		// no need to travers all bits
		if np&1 == 0 {
			return false
		}
		numOfOne += (np & 1)
		bitLength += 1
		np >>= 1
	}
	return bitLength == numOfOne
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/stefanprodan/mgob/config"
	"github.com/stefanprodan/mgob/db"
	"strings"
)

type HttpServer struct {
	Config *config.AppConfig
	Stats  *db.StatusStore
}

func (s *HttpServer) Start(version string) {

	r := chi.NewRouter()
	r.Use(middleware.Recoverer)
	if s.Config.LogLevel == "debug" {
		r.Use(middleware.DefaultLogger)
	}

	r.Mount("/metrics", metricsRouter())
	r.Mount("/debug", middleware.Profiler())

	r.Route("/version", func(r chi.Router) {
		r.Use(appVersionCtx(version))
		r.Get("/", getVersion)
	})

	r.Route("/status", func(r chi.Router) {
		r.Use(statusCtx(s.Stats))
		r.Get("/", getStatus)
		r.Get("/:planID", getPlanStatus)
	})

	r.Route("/backup", func(r chi.Router) {
		r.Use(configCtx(*s.Config))
		r.Post("/:planID", postBackup)
	})

	FileServer(r,"/storage", http.Dir(s.Config.StoragePath))

	logrus.Error(http.ListenAndServe(fmt.Sprintf(":%v", s.Config.Port), r))
}

func FileServer(r chi.Router, path string, root http.FileSystem) {
	if strings.ContainsAny(path, "{}*") {
		panic("FileServer does not permit URL parameters.")
	}

	fs := http.StripPrefix(path, http.FileServer(root))

	if path != "/" && path[len(path)-1] != '/' {
		r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
		path += "/"
	}
	path += "*"

	r.Get(path, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fs.ServeHTTP(w, r)
	}))
}
<commit_msg>Fix chi routing<commit_after>package api

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/stefanprodan/mgob/config"
	"github.com/stefanprodan/mgob/db"
	"strings"
)

type HttpServer struct {
	Config *config.AppConfig
	Stats  *db.StatusStore
}

func (s *HttpServer) Start(version string) {

	r := chi.NewRouter()
	r.Use(middleware.Recoverer)
	if s.Config.LogLevel == "debug" {
		r.Use(middleware.DefaultLogger)
	}

	r.Mount("/metrics", metricsRouter())
	r.Mount("/debug", middleware.Profiler())

	r.Route("/version", func(r chi.Router) {
		r.Use(appVersionCtx(version))
		r.Get("/", getVersion)
	})

	r.Route("/status", func(r chi.Router) {
		r.Use(statusCtx(s.Stats))
		r.Get("/", getStatus)
		r.Get("/{planID}", getPlanStatus)
	})

	r.Route("/backup", func(r chi.Router) {
		r.Use(configCtx(*s.Config))
		r.Post("/{planID}", postBackup)
	})

	FileServer(r,"/storage", http.Dir(s.Config.StoragePath))

	logrus.Error(http.ListenAndServe(fmt.Sprintf(":%v", s.Config.Port), r))
}

func FileServer(r chi.Router, path string, root http.FileSystem) {
	if strings.ContainsAny(path, "{}*") {
		panic("FileServer does not permit URL parameters.")
	}

	fs := http.StripPrefix(path, http.FileServer(root))

	if path != "/" && path[len(path)-1] != '/' {
		r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
		path += "/"
	}
	path += "*"

	r.Get(path, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fs.ServeHTTP(w, r)
	}))
}
<|endoftext|>


<commit_before>package topicfeed

import (
	"math/rand"
	"socialapi/models"
	"testing"

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

func TestMarkedAsTroll(t *testing.T) {
	Convey("while extracting topics", t, func() {
		Convey("duplicates should be returned as unique", func() {
			So(len(extractTopics("hi #topic #topic my topic")), ShouldEqual, 1)
		})

		Convey("public should be removed from topics list", func() {
			topics := extractTopics("hi #topic #public my topic")
			So(len(topics), ShouldEqual, 1)
			So(topics[0], ShouldEqual, "topic")
		})

		Convey("duplicate public should be removed from topics list", func() {
			topics := extractTopics("hi #public  #public  my topic")
			So(len(topics), ShouldEqual, 0)
		})
	})
}

func TestIsEligible(t *testing.T) {
	Convey("while testing isEligible", t, func() {
		Convey("initial channel id should be set", func() {
			c := models.NewChannelMessage()
			c.InitialChannelId = 0
			eligible, err := isEligible(c)
			So(err, ShouldBeNil)
			So(eligible, ShouldBeFalse)
		})

		Convey("type_constant should be Post", func() {
			c := models.NewChannelMessage()
			eligible, err := isEligible(c)
			So(err, ShouldBeNil)
			So(eligible, ShouldBeFalse)

			Convey("when it is set to Post, should be eligible", func() {
				c.InitialChannelId = rand.Int63()
				c.TypeConstant = models.ChannelMessage_TYPE_POST
				eligible, err := isEligible(c)
				So(err, ShouldBeNil)
				So(eligible, ShouldBeTrue)
			})
		})
	})
}
<commit_msg>socialapi: added new test case for moderation needed topic search operations<commit_after>package topicfeed

import (
	"fmt"
	"math/rand"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/runner"
	"socialapi/workers/topicfeed/topicfeed"
	"testing"

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

func TestMarkedAsTroll(t *testing.T) {
	Convey("while extracting topics", t, func() {
		Convey("duplicates should be returned as unique", func() {
			So(len(extractTopics("hi #topic #topic my topic")), ShouldEqual, 1)
		})

		Convey("public should be removed from topics list", func() {
			topics := extractTopics("hi #topic #public my topic")
			So(len(topics), ShouldEqual, 1)
			So(topics[0], ShouldEqual, "topic")
		})

		Convey("duplicate public should be removed from topics list", func() {
			topics := extractTopics("hi #public  #public  my topic")
			So(len(topics), ShouldEqual, 0)
		})
	})
}

func TestIsEligible(t *testing.T) {
	Convey("while testing isEligible", t, func() {
		Convey("initial channel id should be set", func() {
			c := models.NewChannelMessage()
			c.InitialChannelId = 0
			eligible, err := isEligible(c)
			So(err, ShouldBeNil)
			So(eligible, ShouldBeFalse)
		})

		Convey("type_constant should be Post", func() {
			c := models.NewChannelMessage()
			eligible, err := isEligible(c)
			So(err, ShouldBeNil)
			So(eligible, ShouldBeFalse)

			Convey("when it is set to Post, should be eligible", func() {
				c.InitialChannelId = rand.Int63()
				c.TypeConstant = models.ChannelMessage_TYPE_POST
				eligible, err := isEligible(c)
				So(err, ShouldBeNil)
				So(eligible, ShouldBeTrue)
			})
		})
	})
}

func TestMessageSaved(t *testing.T) {
	r := runner.New("test")
	if err := r.Init(); err != nil {
		t.Fatalf("couldnt start bongo %s", err.Error())
	}
	defer r.Close()

	controller := topicfeed.New(r.Log)

	Convey("while testing MessageSaved", t, func() {

		Convey("newly created channels should be marked with needs moderation", func() {
			account := models.CreateAccountWithTest()
			groupChannel := models.CreateTypedPublicChannelWithTest(account.Id, models.Channel_TYPE_GROUP)

			// just a random topic name
			topicName := models.RandomName()
			fmt.Println("topicName-->", topicName)
			c := models.NewChannelMessage()
			c.InitialChannelId = groupChannel.Id
			c.AccountId = account.Id
			c.Body = "my test topic #" + topicName
			c.TypeConstant = models.ChannelMessage_TYPE_POST

			// create with unscoped
			err := bongo.B.Unscoped().Table(c.TableName()).Create(c).Error
			So(err, ShouldBeNil)

			So(controller.MessageSaved(c), ShouldBeNil)

			Convey("we should not be able to find them", func() {
				channels, err := models.NewChannel().Search(&request.Query{
					Name:      topicName,
					GroupName: groupChannel.GroupName,
					AccountId: account.Id,
				})
				So(err, ShouldBeNil)
				So(len(channels), ShouldEqual, 0)
			})

			Convey("after removing needs moderation flag", func() {
				// byname doesnt filter
				channel, err := models.NewChannel().ByName(&request.Query{
					Name:      topicName,
					GroupName: groupChannel.GroupName,
					AccountId: account.Id,
				})
				So(err, ShouldBeNil)
				So(channel, ShouldNotBeNil)

				channel.MetaBits.UnMark(models.NeedsModeration)

				So(channel.Update(), ShouldBeNil)

				Convey("we should be able to search them", func() {
					channels, err := models.NewChannel().Search(&request.Query{
						Name:      topicName,
						GroupName: groupChannel.GroupName,
						AccountId: account.Id,
						Privacy:   channel.PrivacyConstant,
					})
					So(err, ShouldBeNil)
					So(len(channels), ShouldEqual, 1)
					So(channels[0].Id, ShouldEqual, channel.Id)
				})
			})
		})
	})
}
<|endoftext|>