<commit_before>package rfc3164

import (
  "bytes"
  "math"
  "github.com/scalingdata/syslogparser"
  message "github.com/scalingdata/syslogparser/message"
  "time"
)

type Parser struct {
  buff     []byte
  cursor   int
  l        int
  priority syslogparser.Priority
  version  int
  header   header
  message  rfc3164message
  parseSuccessful bool
  TimeFunction TimeNow
}

type TimeNow func() time.Time

type header struct {
  timestamp time.Time
  hostname  string
}

type rfc3164message struct {
  tag     string
  procId  string
  content string
}

func NewParser(buff *[]byte) *Parser {
  return &Parser{
    buff:   *buff,
    cursor: 0,
    l:      len(*buff),
    parseSuccessful: false,
    TimeFunction: time.Now,
  }
}

func (p *Parser) Parse() error {
  pri, err := p.parsePriority()
  if err != nil {
    return err
  }

  hdr, err := p.parseHeader()
  if err != nil {
    return err
  }

  p.cursor++

  if p.cursor >= p.l {
    return syslogparser.ErrEOL
  }

  msg, err := p.parsemessage()
  if err != syslogparser.ErrEOL {
    return err
  }

  p.priority = pri
  p.version = syslogparser.NO_VERSION
  p.header = hdr
  p.message = msg

  p.parseSuccessful = true
  return nil
}

func (p *Parser) Dump() syslogparser.LogParts {
  return syslogparser.LogParts{
    "timestamp": p.header.timestamp,
    "hostname":  p.header.hostname,
    "tag":       p.message.tag,
    "content":   p.message.content,
    "priority":  p.priority.P,
    "facility":  p.priority.F.Value,
    "severity":  p.priority.S.Value,
    "proc_id":   p.message.procId,
  }
}

func (p *Parser) Message() message.IMessage {
  if ! p.parseSuccessful {
    return message.NewUnparsableMessage(&p.buff)
  } else {
    return &Rfc3164Message{
      rawMsg: &p.buff,
      ts: p.header.timestamp,
      pid: p.message.procId,
      facility: message.Facility(p.priority.F.Value),
      severity: message.Severity(p.priority.S.Value),
      process: p.message.tag,
      hostname: p.header.hostname,
      message: p.message.content,
    }
  }
}

func (p *Parser) parsePriority() (syslogparser.Priority, error) {
  return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
}

func (p *Parser) parseHeader() (header, error) {
  hdr := header{}
  var err error

  ts, err := p.parseTimestamp()
  if err != nil {
    return hdr, err
  }

  hostname, err := p.parseHostname()
  if err != nil {
    return hdr, err
  }

  hdr.timestamp = ts
  hdr.hostname = hostname

  return hdr, nil
}

func (p *Parser) parsemessage() (rfc3164message, error) {
  msg := rfc3164message{}
  var err error

  tag, err := p.parseTag()
  if err != nil {
    return msg, err
  }
  msg.tag = tag

  pid, content, err := p.parseContent()
  if err != syslogparser.ErrEOL {
    return msg, err
  }
  msg.procId = pid
  msg.content = content

  return msg, err
}

// https://tools.ietf.org/html/rfc3164#section-4.1.2
func (p *Parser) parseTimestamp() (time.Time, error) {
  var ts time.Time
  var err error
  var tsFmtLen int
  var sub []byte

  tsFmts := []string{
    "Jan 02 15:04:05 2006",
    "Jan 02 15:04:05",
    "Jan  2 15:04:05 2006",
    "Jan  2 15:04:05",
  }

  found := false
  for _, tsFmt := range tsFmts {
    tsFmtLen = len(tsFmt)

    if p.cursor+tsFmtLen > p.l {
      continue
    }

    sub = p.buff[p.cursor : tsFmtLen+p.cursor]

    // Only test the date pattern if the next character is a space (or the end of the message)
    // This prevents us picking up the first 4 numbers of a hostname as a year
    if p.cursor+tsFmtLen == p.l || p.buff[p.cursor + tsFmtLen] == ' ' {
      ts, err = time.ParseInLocation(tsFmt, string(sub), time.Local)
      if err == nil {
        // Only use a pattern with a year for "reasonable" years
        if (ts.Year() > 0 && ts.Year() < 2000) || ts.Year() > 2100 {
	  continue
        }
        /* Set Year on the Timestamp before converting to UTC so that time zone and
           DST settings (which are year dependent) can be properly assessed in the
           conversion. */
        p.fixTimestampIfNeeded(&ts)
        ts = ts.UTC()
        found = true
        break
      }
    }
  }

  if !found {
    p.cursor = tsFmtLen

    // XXX : If the timestamp is invalid we try to push the cursor one byte
    // XXX : further, in case it is a space
    if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
      p.cursor++
    }

    return ts, syslogparser.ErrTimestampUnknownFormat
  }

  p.cursor += tsFmtLen

  if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
    p.cursor++
  }

  return ts, nil
}

func (p *Parser) parseHostname() (string, error) {
  return syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
}

// http://tools.ietf.org/html/rfc3164#section-4.1.3
func (p *Parser) parseTag() (string, error) {
  i := 0;
  for i < (p.l - p.cursor) {
    curChar := p.buff[p.cursor + i]
    if (curChar >= '0' && curChar <= '9') ||
      (curChar >= 'a' && curChar <= 'z') ||
      (curChar >= 'A' && curChar <= 'Z') ||
      /* Allow non-compliant tags with "-", "_", "/", "." */
       curChar == '-' || curChar == '_' ||
       curChar == '/' || curChar == '.' {
      i++
    } else {
      tag := p.buff[p.cursor:p.cursor+i]
      p.cursor = p.cursor+i
      return string(tag), nil
    }
  }
  tag := p.buff[p.cursor:p.cursor+i]
  p.cursor = p.cursor+i
  return string(tag), nil
}

func (p *Parser) parseContent() (string, string, error) {
  if p.cursor >= p.l {
    return "", "", syslogparser.ErrEOL
  }

  pid, err := p.parsePid()
  if nil != err {
    return "", "", err
  }

  /* Trim any padding that might appear after the pid */
  curChar := p.buff[p.cursor]
  for (':' == curChar || ' ' == curChar) &&  (p.cursor < p.l) {
    p.cursor++
    if p.cursor < p.l {
      curChar = p.buff[p.cursor]
    }
  }

  content := bytes.Trim(p.buff[p.cursor:p.l], " ")
  p.cursor = p.l

  return pid, string(content), syslogparser.ErrEOL
}

func (p *Parser) parsePid() (string, error) {
  if '[' != p.buff[p.cursor] {
    return "", nil
  } else {
    /* Walk past our initial '[' char until we find a non-numeric
       value or we hit the end of the buffer. */
    i := p.cursor + 1;
    curChar := p.buff[i];
    for (curChar >= '0' && curChar <= '9') && i < p.l {
      i++
      if i < p.l {
        curChar = p.buff[i]
      }
    }
    if i >= p.l {
      /* We got to the end of the buffer, and no closing bracket found */
      return "", nil
    } else if ']' == p.buff[i] {
      /* Found closing bracket, pull out the pid */
      pid := p.buff[p.cursor+1:i]
      p.cursor = i+1
      return string(pid), nil
    } else {
      /* We found a non-numeric value that wasn't the ']', not a pid */
      return "", nil
    }
  }
}

func (p *Parser) fixTimestampIfNeeded(ts *time.Time) {
  /* Don't clobber a valid year */
  if ts.Year() > 0 {
    return
  }

  /* Chanegable for testing, should be time.Now in regular use */
  now := p.TimeFunction()

  /* Compute the event timestamp this year, next year and last year.
     This covers cases where an event crosses December->January, and
     the sender's clock is ahead of ours and the event goes January-> December */

  newTs := time.Date(now.Year(), ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())
  lastYearTs := time.Date(now.Year()-1, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())
  nextYearTs := time.Date(now.Year()+1, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())

  /* Take the time in seconds between the current date and each candidate timestamp */
  lastYearDiff := float64(now.Unix() - lastYearTs.Unix())
  nextYearDiff := float64(nextYearTs.Unix() - now.Unix())
  thisYearDiff := math.Abs(float64(now.Unix() - newTs.Unix()))

  /* Set the event timestamp to the candidate which is closest to today's date */
  if lastYearDiff < nextYearDiff && lastYearDiff < thisYearDiff {
    *ts = lastYearTs
  } else if nextYearDiff < lastYearDiff && nextYearDiff < thisYearDiff {
    *ts = nextYearTs
  } else {
    *ts = newTs
  }
}
<commit_msg>Make conditions for accepting year clearer<commit_after>package rfc3164

import (
  "bytes"
  "math"
  "github.com/scalingdata/syslogparser"
  message "github.com/scalingdata/syslogparser/message"
  "time"
)

type Parser struct {
  buff     []byte
  cursor   int
  l        int
  priority syslogparser.Priority
  version  int
  header   header
  message  rfc3164message
  parseSuccessful bool
  TimeFunction TimeNow
}

type TimeNow func() time.Time

type header struct {
  timestamp time.Time
  hostname  string
}

type rfc3164message struct {
  tag     string
  procId  string
  content string
}

func NewParser(buff *[]byte) *Parser {
  return &Parser{
    buff:   *buff,
    cursor: 0,
    l:      len(*buff),
    parseSuccessful: false,
    TimeFunction: time.Now,
  }
}

func (p *Parser) Parse() error {
  pri, err := p.parsePriority()
  if err != nil {
    return err
  }

  hdr, err := p.parseHeader()
  if err != nil {
    return err
  }

  p.cursor++

  if p.cursor >= p.l {
    return syslogparser.ErrEOL
  }

  msg, err := p.parsemessage()
  if err != syslogparser.ErrEOL {
    return err
  }

  p.priority = pri
  p.version = syslogparser.NO_VERSION
  p.header = hdr
  p.message = msg

  p.parseSuccessful = true
  return nil
}

func (p *Parser) Dump() syslogparser.LogParts {
  return syslogparser.LogParts{
    "timestamp": p.header.timestamp,
    "hostname":  p.header.hostname,
    "tag":       p.message.tag,
    "content":   p.message.content,
    "priority":  p.priority.P,
    "facility":  p.priority.F.Value,
    "severity":  p.priority.S.Value,
    "proc_id":   p.message.procId,
  }
}

func (p *Parser) Message() message.IMessage {
  if ! p.parseSuccessful {
    return message.NewUnparsableMessage(&p.buff)
  } else {
    return &Rfc3164Message{
      rawMsg: &p.buff,
      ts: p.header.timestamp,
      pid: p.message.procId,
      facility: message.Facility(p.priority.F.Value),
      severity: message.Severity(p.priority.S.Value),
      process: p.message.tag,
      hostname: p.header.hostname,
      message: p.message.content,
    }
  }
}

func (p *Parser) parsePriority() (syslogparser.Priority, error) {
  return syslogparser.ParsePriority(p.buff, &p.cursor, p.l)
}

func (p *Parser) parseHeader() (header, error) {
  hdr := header{}
  var err error

  ts, err := p.parseTimestamp()
  if err != nil {
    return hdr, err
  }

  hostname, err := p.parseHostname()
  if err != nil {
    return hdr, err
  }

  hdr.timestamp = ts
  hdr.hostname = hostname

  return hdr, nil
}

func (p *Parser) parsemessage() (rfc3164message, error) {
  msg := rfc3164message{}
  var err error

  tag, err := p.parseTag()
  if err != nil {
    return msg, err
  }
  msg.tag = tag

  pid, content, err := p.parseContent()
  if err != syslogparser.ErrEOL {
    return msg, err
  }
  msg.procId = pid
  msg.content = content

  return msg, err
}

// https://tools.ietf.org/html/rfc3164#section-4.1.2
func (p *Parser) parseTimestamp() (time.Time, error) {
  var ts time.Time
  var err error
  var tsFmtLen int
  var sub []byte

  tsFmts := []string{
    "Jan 02 15:04:05 2006",
    "Jan  2 15:04:05 2006",
    "Jan 02 15:04:05",
    "Jan  2 15:04:05",
  }

  found := false
  for i, tsFmt := range tsFmts {
    tsFmtLen = len(tsFmt)

    if p.cursor+tsFmtLen > p.l {
      continue
    }

    sub = p.buff[p.cursor : tsFmtLen+p.cursor]

    // Only test the date pattern if the next character is a space (or the end of the message)
    // This prevents us picking up the first 4 numbers of a hostname as a year
    if p.cursor+tsFmtLen == p.l || p.buff[p.cursor + tsFmtLen] == ' ' {
      ts, err = time.ParseInLocation(tsFmt, string(sub), time.Local)
      if err == nil {
	// The first two patterns have a year component - check that it's "reasonable"
	// (1999 < year < 2100).
	if i < 2  && ! ( ts.Year() > 1999 && ts.Year() < 2100) {
          continue
	}
        /* Set Year on the Timestamp before converting to UTC so that time zone and
           DST settings (which are year dependent) can be properly assessed in the
           conversion. */
        p.fixTimestampIfNeeded(&ts)
        ts = ts.UTC()
        found = true
        break
      }
    }
  }

  if !found {
    p.cursor = tsFmtLen

    // XXX : If the timestamp is invalid we try to push the cursor one byte
    // XXX : further, in case it is a space
    if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
      p.cursor++
    }

    return ts, syslogparser.ErrTimestampUnknownFormat
  }

  p.cursor += tsFmtLen

  if (p.cursor < p.l) && (p.buff[p.cursor] == ' ') {
    p.cursor++
  }

  return ts, nil
}

func (p *Parser) parseHostname() (string, error) {
  return syslogparser.ParseHostname(p.buff, &p.cursor, p.l)
}

// http://tools.ietf.org/html/rfc3164#section-4.1.3
func (p *Parser) parseTag() (string, error) {
  i := 0;
  for i < (p.l - p.cursor) {
    curChar := p.buff[p.cursor + i]
    if (curChar >= '0' && curChar <= '9') ||
      (curChar >= 'a' && curChar <= 'z') ||
      (curChar >= 'A' && curChar <= 'Z') ||
      /* Allow non-compliant tags with "-", "_", "/", "." */
       curChar == '-' || curChar == '_' ||
       curChar == '/' || curChar == '.' {
      i++
    } else {
      tag := p.buff[p.cursor:p.cursor+i]
      p.cursor = p.cursor+i
      return string(tag), nil
    }
  }
  tag := p.buff[p.cursor:p.cursor+i]
  p.cursor = p.cursor+i
  return string(tag), nil
}

func (p *Parser) parseContent() (string, string, error) {
  if p.cursor >= p.l {
    return "", "", syslogparser.ErrEOL
  }

  pid, err := p.parsePid()
  if nil != err {
    return "", "", err
  }

  /* Trim any padding that might appear after the pid */
  curChar := p.buff[p.cursor]
  for (':' == curChar || ' ' == curChar) &&  (p.cursor < p.l) {
    p.cursor++
    if p.cursor < p.l {
      curChar = p.buff[p.cursor]
    }
  }

  content := bytes.Trim(p.buff[p.cursor:p.l], " ")
  p.cursor = p.l

  return pid, string(content), syslogparser.ErrEOL
}

func (p *Parser) parsePid() (string, error) {
  if '[' != p.buff[p.cursor] {
    return "", nil
  } else {
    /* Walk past our initial '[' char until we find a non-numeric
       value or we hit the end of the buffer. */
    i := p.cursor + 1;
    curChar := p.buff[i];
    for (curChar >= '0' && curChar <= '9') && i < p.l {
      i++
      if i < p.l {
        curChar = p.buff[i]
      }
    }
    if i >= p.l {
      /* We got to the end of the buffer, and no closing bracket found */
      return "", nil
    } else if ']' == p.buff[i] {
      /* Found closing bracket, pull out the pid */
      pid := p.buff[p.cursor+1:i]
      p.cursor = i+1
      return string(pid), nil
    } else {
      /* We found a non-numeric value that wasn't the ']', not a pid */
      return "", nil
    }
  }
}

func (p *Parser) fixTimestampIfNeeded(ts *time.Time) {
  /* Don't clobber a valid year */
  if ts.Year() > 0 {
    return
  }

  /* Chanegable for testing, should be time.Now in regular use */
  now := p.TimeFunction()

  /* Compute the event timestamp this year, next year and last year.
     This covers cases where an event crosses December->January, and
     the sender's clock is ahead of ours and the event goes January-> December */

  newTs := time.Date(now.Year(), ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())
  lastYearTs := time.Date(now.Year()-1, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())
  nextYearTs := time.Date(now.Year()+1, ts.Month(), ts.Day(), ts.Hour(), ts.Minute(),
    ts.Second(), ts.Nanosecond(), ts.Location())

  /* Take the time in seconds between the current date and each candidate timestamp */
  lastYearDiff := float64(now.Unix() - lastYearTs.Unix())
  nextYearDiff := float64(nextYearTs.Unix() - now.Unix())
  thisYearDiff := math.Abs(float64(now.Unix() - newTs.Unix()))

  /* Set the event timestamp to the candidate which is closest to today's date */
  if lastYearDiff < nextYearDiff && lastYearDiff < thisYearDiff {
    *ts = lastYearTs
  } else if nextYearDiff < lastYearDiff && nextYearDiff < thisYearDiff {
    *ts = nextYearTs
  } else {
    *ts = newTs
  }
}
<|endoftext|>


<commit_before>// +build netbsd

package netbsd

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

	"github.com/mackerelio/mackerel-agent/logging"
)

// MemoryGenerator collects the host's memory specs.
type MemoryGenerator struct {
}

// Key XXX
func (g *MemoryGenerator) Key() string {
	return "memory"
}

var memoryLogger = logging.GetLogger("spec.memory")

const bytesInKibibytes = 1024

// Generate returns memory specs.
// The returned spec must have below:
// - total (in "###kB" format, Kibibytes)
func (g *MemoryGenerator) Generate() (interface{}, error) {
	spec := map[string]string{}

	cmd := exec.Command("sysctl", "-n", "hw.physmem")
	outputBytes, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("sysctl -n hw.physmem: %s", err)
	}

	output := string(outputBytes)

	memsizeInBytes, err := strconv.ParseInt(strings.TrimSpace(output), 10, 64)
	fmt.Printf("[DEBUG] memsizeInBytes: %d", memsizeInBytes)
	if err != nil {
		fmt.Printf("[DEBUG] MemoryGenerator err != nil")
		return nil, fmt.Errorf("while parsing %q: %s", output, err)
	}

	spec["total"] = fmt.Sprintf("%dkB", memsizeInBytes/bytesInKibibytes)
	fmt.Printf("[DEBUG] spec[total]: %s", spec["total"])

	return spec, nil
}
<commit_msg>hw.physmem is obsolete<commit_after>// +build netbsd

package netbsd

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

	"github.com/mackerelio/mackerel-agent/logging"
)

// MemoryGenerator collects the host's memory specs.
type MemoryGenerator struct {
}

// Key XXX
func (g *MemoryGenerator) Key() string {
	return "memory"
}

var memoryLogger = logging.GetLogger("spec.memory")

const bytesInKibibytes = 1024

// Generate returns memory specs.
// The returned spec must have below:
// - total (in "###kB" format, Kibibytes)
func (g *MemoryGenerator) Generate() (interface{}, error) {
	spec := map[string]string{}

	cmd := exec.Command("sysctl", "-n", "hw.physmem64")
	outputBytes, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("sysctl -n hw.physmem: %s", err)
	}

	output := string(outputBytes)

	memsizeInBytes, err := strconv.ParseInt(strings.TrimSpace(output), 10, 64)
	fmt.Printf("[DEBUG] memsizeInBytes: %d", memsizeInBytes)
	if err != nil {
		fmt.Printf("[DEBUG] MemoryGenerator err != nil")
		return nil, fmt.Errorf("while parsing %q: %s", output, err)
	}

	spec["total"] = fmt.Sprintf("%dkB", memsizeInBytes/bytesInKibibytes)
	fmt.Printf("[DEBUG] spec[total]: %s", spec["total"])

	return spec, nil
}
<|endoftext|>


<commit_before>package images

import (
	"fmt"
	"time"

	"k8s.io/kubernetes/test/e2e"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
)

type SampleRepoConfig struct {
	repoName             string
	templateURL          string
	buildConfigName      string
	serviceName          string
	deploymentConfigName string
	expectedString       string
	appPath              string
}

// NewSampleRepoTest creates a function for a new ginkgo test case that will instantiate a template
// from a url, kick off the buildconfig defined in that template, wait for the build/deploy,
// and then confirm the application is serving an expected string value.
func NewSampleRepoTest(c SampleRepoConfig) func() {
	return func() {
		defer g.GinkgoRecover()
		var oc = exutil.NewCLI(c.repoName+"-repo-test", exutil.KubeConfigPath())

		g.JustBeforeEach(func() {
			g.By("Waiting for builder service account")
			err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace()))
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.Describe("Building "+c.repoName+" app from new-app", func() {
			g.It(fmt.Sprintf("should build a "+c.repoName+" image and run it in a pod"), func() {
				oc.SetOutputDir(exutil.TestContext.OutputDir)

				g.By(fmt.Sprintf("calling oc new-app with the " + c.repoName + " example template"))
				err := oc.Run("new-app").Args("-f", c.templateURL).Execute()
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("starting a build")
				buildName, err := oc.Run("start-build").Args(c.buildConfigName).Output()
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("expecting the build is in the Complete phase")
				err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), buildName, exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
				if err != nil {
					logs, _ := oc.Run("build-logs").Args(buildName).Output()
					e2e.Failf("build failed: %s", logs)
				}
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("expecting the deployment to be complete")
				err = exutil.WaitForADeployment(oc.KubeREST().ReplicationControllers(oc.Namespace()), c.deploymentConfigName, exutil.CheckDeploymentCompletedFn, exutil.CheckDeploymentFailedFn)
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("expecting the service is available")
				serviceIP, err := oc.Run("get").Args("service", c.serviceName).Template("{{ .spec.clusterIP }}").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(serviceIP).ShouldNot(o.Equal(""))

				g.By("expecting an endpoint is available")
				err = oc.KubeFramework().WaitForAnEndpoint(c.serviceName)
				o.Expect(err).NotTo(o.HaveOccurred())

				response, err := exutil.FetchURL("http://"+serviceIP+":8080"+c.appPath, time.Duration(30*time.Second))
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(response).Should(o.ContainSubstring(c.expectedString))
			})
		})
	}
}

var _ = g.Describe("samplerepo: test the sample application repositories", func() {

	g.Describe("samplerepo: images: parallel: test ruby images with rails-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"rails",
			"https://raw.githubusercontent.com/openshift/rails-ex/master/openshift/templates/rails-postgresql.json",
			"rails-postgresql-example",
			"rails-postgresql-example",
			"rails-postgresql-example",
			"Listing articles",
			"/articles",
		},
	))

	g.Describe("samplerepo: images: parallel: test python images with django-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"django",
			"https://raw.githubusercontent.com/openshift/django-ex/master/openshift/templates/django-postgresql.json",
			"django-psql-example",
			"django-psql-example",
			"django-psql-example",
			"Page views: 1",
			"",
		},
	))

	g.Describe("samplerepo: images: parallel: test nodejs images with nodejs-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"nodejs",
			"https://raw.githubusercontent.com/openshift/nodejs-ex/master/openshift/templates/nodejs-mongodb.json",
			"nodejs-mongodb-example",
			"nodejs-mongodb-example",
			"nodejs-mongodb-example",
			"1",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test php images with cakephp-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"cakephp",
			"https://raw.githubusercontent.com/openshift/cakephp-ex/master/openshift/templates/cakephp-mysql.json",
			"cakephp-mysql-example",
			"cakephp-mysql-example",
			"cakephp-mysql-example",
			"1",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test perl images with dancer-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"dancer",
			"https://raw.githubusercontent.com/openshift/dancer-ex/master/openshift/templates/dancer-mysql.json",
			"dancer-mysql-example",
			"dancer-mysql-example",
			"dancer-mysql-example",
			"1",
			"",
		},
	))

})
<commit_msg>test non-db sample templates also<commit_after>package images

import (
	"fmt"
	"time"

	"k8s.io/kubernetes/test/e2e"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
)

type SampleRepoConfig struct {
	repoName             string
	templateURL          string
	buildConfigName      string
	serviceName          string
	deploymentConfigName string
	expectedString       string
	appPath              string
}

// NewSampleRepoTest creates a function for a new ginkgo test case that will instantiate a template
// from a url, kick off the buildconfig defined in that template, wait for the build/deploy,
// and then confirm the application is serving an expected string value.
func NewSampleRepoTest(c SampleRepoConfig) func() {
	return func() {
		defer g.GinkgoRecover()
		var oc = exutil.NewCLI(c.repoName+"-repo-test", exutil.KubeConfigPath())

		g.JustBeforeEach(func() {
			g.By("Waiting for builder service account")
			err := exutil.WaitForBuilderAccount(oc.KubeREST().ServiceAccounts(oc.Namespace()))
			o.Expect(err).NotTo(o.HaveOccurred())
		})

		g.Describe("Building "+c.repoName+" app from new-app", func() {
			g.It(fmt.Sprintf("should build a "+c.repoName+" image and run it in a pod"), func() {
				oc.SetOutputDir(exutil.TestContext.OutputDir)

				g.By(fmt.Sprintf("calling oc new-app with the " + c.repoName + " example template"))
				err := oc.Run("new-app").Args("-f", c.templateURL).Execute()
				o.Expect(err).NotTo(o.HaveOccurred())

				// all the templates automatically start a build.
				buildName := c.buildConfigName + "-1"

				g.By("expecting the build is in the Complete phase")
				err = exutil.WaitForABuild(oc.REST().Builds(oc.Namespace()), buildName, exutil.CheckBuildSuccessFn, exutil.CheckBuildFailedFn)
				if err != nil {
					logs, _ := oc.Run("build-logs").Args(buildName).Output()
					e2e.Failf("build failed: %s", logs)
				}
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("expecting the deployment to be complete")
				err = exutil.WaitForADeployment(oc.KubeREST().ReplicationControllers(oc.Namespace()), c.deploymentConfigName, exutil.CheckDeploymentCompletedFn, exutil.CheckDeploymentFailedFn)
				o.Expect(err).NotTo(o.HaveOccurred())

				g.By("expecting the service is available")
				serviceIP, err := oc.Run("get").Args("service", c.serviceName).Template("{{ .spec.clusterIP }}").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(serviceIP).ShouldNot(o.Equal(""))

				g.By("expecting an endpoint is available")
				err = oc.KubeFramework().WaitForAnEndpoint(c.serviceName)
				o.Expect(err).NotTo(o.HaveOccurred())

				response, err := exutil.FetchURL("http://"+serviceIP+":8080"+c.appPath, time.Duration(30*time.Second))
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(response).Should(o.ContainSubstring(c.expectedString))
			})
		})
	}
}

var _ = g.Describe("samplerepo: test the sample application repositories", func() {

	g.Describe("samplerepo: images: parallel: test ruby images with rails-ex db repo", NewSampleRepoTest(
		SampleRepoConfig{
			"rails",
			"https://raw.githubusercontent.com/openshift/rails-ex/master/openshift/templates/rails-postgresql.json",
			"rails-postgresql-example",
			"rails-postgresql-example",
			"rails-postgresql-example",
			"Listing articles",
			"/articles",
		},
	))

	g.Describe("samplerepo: images: parallel: test python images with django-ex db repo", NewSampleRepoTest(
		SampleRepoConfig{
			"django",
			"https://raw.githubusercontent.com/openshift/django-ex/master/openshift/templates/django-postgresql.json",
			"django-psql-example",
			"django-psql-example",
			"django-psql-example",
			"Page views: 1",
			"",
		},
	))

	g.Describe("samplerepo: images: parallel: test nodejs images with nodejs-ex db repo", NewSampleRepoTest(
		SampleRepoConfig{
			"nodejs",
			"https://raw.githubusercontent.com/openshift/nodejs-ex/master/openshift/templates/nodejs-mongodb.json",
			"nodejs-mongodb-example",
			"nodejs-mongodb-example",
			"nodejs-mongodb-example",
			"1",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test php images with cakephp-ex db repo", NewSampleRepoTest(
		SampleRepoConfig{
			"cakephp",
			"https://raw.githubusercontent.com/openshift/cakephp-ex/master/openshift/templates/cakephp-mysql.json",
			"cakephp-mysql-example",
			"cakephp-mysql-example",
			"cakephp-mysql-example",
			"1",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test perl images with dancer-ex db repo", NewSampleRepoTest(
		SampleRepoConfig{
			"dancer",
			"https://raw.githubusercontent.com/openshift/dancer-ex/master/openshift/templates/dancer-mysql.json",
			"dancer-mysql-example",
			"dancer-mysql-example",
			"dancer-mysql-example",
			"1",
			"",
		},
	))

	// test the no-db templates too
	g.Describe("samplerepo: images: parallel: test python images with django-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"django",
			"https://raw.githubusercontent.com/openshift/django-ex/master/openshift/templates/django.json",
			"django-example",
			"django-example",
			"django-example",
			"Welcome",
			"",
		},
	))

	g.Describe("samplerepo: images: parallel: test nodejs images with nodejs-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"nodejs",
			"https://raw.githubusercontent.com/openshift/nodejs-ex/master/openshift/templates/nodejs.json",
			"nodejs-example",
			"nodejs-example",
			"nodejs-example",
			"Welcome",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test php images with cakephp-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"cakephp",
			"https://raw.githubusercontent.com/openshift/cakephp-ex/master/openshift/templates/cakephp.json",
			"cakephp-example",
			"cakephp-example",
			"cakephp-example",
			"Welcome",
			"",
		},
	))

	var _ = g.Describe("samplerepo: images: parallel: test perl images with dancer-ex repo", NewSampleRepoTest(
		SampleRepoConfig{
			"dancer",
			"https://raw.githubusercontent.com/openshift/dancer-ex/master/openshift/templates/dancer.json",
			"dancer-example",
			"dancer-example",
			"dancer-example",
			"Welcome",
			"",
		},
	))

})
<|endoftext|>


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

package testing

import (
	"fmt";
	"flag";
)

var chatty = flag.Bool("chatty", false, "chatty")

// Insert tabs after newlines - but not the last one
func tabify(s string) string {
	for i := 0; i < len(s) - 1; i++ {	// -1 because if last char is newline, don't bother
		if s[i] == '\n' {
			return s[0:i+1] + "\t" + tabify(s[i+1:len(s)]);
		}
	}
	return s
}

type T struct {
	errors	string;
	failed	bool;
	ch	chan *T;
}

func (t *T) Fail() {
	t.failed = true
}

func (t *T) FailNow() {
	t.Fail();
	t.ch <- t;
	sys.Goexit();
}

func (t *T) Log(args ...) {
	t.errors += "\t" + tabify(fmt.Sprintln(args));
}

func (t *T) Logf(format string, args ...) {
	t.errors += tabify(fmt.Sprintf("\t" + format, args));
	l := len(t.errors);
	if l > 0 && t.errors[l-1] != '\n' {
		t.errors += "\n"
	}
}

func (t *T) Error(args ...) {
	t.Log(args);
	t.Fail();
}

func (t *T) Errorf(format string, args ...) {
	t.Logf(format, args);
	t.Fail();
}

func (t *T) Fatal(args ...) {
	t.Log(args);
	t.FailNow();
}

func (t *T) Fatalf(format string, args ...) {
	t.Logf(format, args);
	t.FailNow();
}

type Test struct {
	Name string;
	F func(*T);
}

func tRunner(t *T, test *Test) {
	test.F(t);
	t.ch <- t;
}

func Main(tests []Test) {
	flag.Parse();
	ok := true;
	if len(tests) == 0 {
		println("testing: warning: no tests to run");
	}
	for i := 0; i < len(tests); i++ {
		if *chatty {
			println("=== RUN ", tests[i].Name);
		}
		t := new(T);
		t.ch = make(chan *T);
		go tRunner(t, &tests[i]);
		<-t.ch;
		if t.failed {
			println("--- FAIL:", tests[i].Name);
			print(t.errors);
			ok = false;
		} else if *chatty {
			println("--- PASS:", tests[i].Name);
			print(t.errors);
		}
	}
	if !ok {
		println("FAIL");
		sys.Exit(1);
	}
	println("PASS");
}
<commit_msg>document testing and incidentally gotest<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// The testing package provides support for automated testing of Go packages.
// It is intended to be used in concert with the ``gotest'' utility, which automates
// execution of any function of the form
//     func TestXxx(*testing.T)
// where Xxx can by any alphanumeric string (but the first letter must not be in
// [a-z]) and serves to identify the test routine.
// These TestXxx routines should be declared within the package they are testing.
package testing

import (
	"fmt";
	"flag";
)

// Report as tests are run; default is silent for success.
var chatty = flag.Bool("chatty", false, "chatty")

// Insert tabs after newlines - but not the last one
func tabify(s string) string {
	for i := 0; i < len(s) - 1; i++ {	// -1 because if last char is newline, don't bother
		if s[i] == '\n' {
			return s[0:i+1] + "\t" + tabify(s[i+1:len(s)]);
		}
	}
	return s
}

// T is a type passed to Test functions to manage test state and support formatted test logs.
// Logs are accumulated during execution and dumped to standard error when done.
type T struct {
	errors	string;
	failed	bool;
	ch	chan *T;
}

// Fail marks the Test function as having failed but continues execution.
func (t *T) Fail() {
	t.failed = true
}

// FailNow marks the Test function as having failed and stops its execution.
// Execution will continue at the next Test.
func (t *T) FailNow() {
	t.Fail();
	t.ch <- t;
	sys.Goexit();
}

// Log formats its arguments using default formatting, analogous to Print(),
// and records the text in the error log.
func (t *T) Log(args ...) {
	t.errors += "\t" + tabify(fmt.Sprintln(args));
}

// Log formats its arguments according to the format, analogous to Printf(),
// and records the text in the error log.
func (t *T) Logf(format string, args ...) {
	t.errors += tabify(fmt.Sprintf("\t" + format, args));
	l := len(t.errors);
	if l > 0 && t.errors[l-1] != '\n' {
		t.errors += "\n"
	}
}

// Error is equivalent to Log() followed by Fail().
func (t *T) Error(args ...) {
	t.Log(args);
	t.Fail();
}

// Errorf is equivalent to Logf() followed by Fail().
func (t *T) Errorf(format string, args ...) {
	t.Logf(format, args);
	t.Fail();
}

// Fatal is equivalent to Log() followed by FailNow().
func (t *T) Fatal(args ...) {
	t.Log(args);
	t.FailNow();
}

// Fatalf is equivalent to Logf() followed by FailNow().
func (t *T) Fatalf(format string, args ...) {
	t.Logf(format, args);
	t.FailNow();
}

// An internal type but exported because it is cross-package; part of the implementation
// of gotest.
type Test struct {
	Name string;
	F func(*T);
}

func tRunner(t *T, test *Test) {
	test.F(t);
	t.ch <- t;
}

// An internal function but exported because it is cross-package; part of the implementation
// of gotest.
func Main(tests []Test) {
	flag.Parse();
	ok := true;
	if len(tests) == 0 {
		println("testing: warning: no tests to run");
	}
	for i := 0; i < len(tests); i++ {
		if *chatty {
			println("=== RUN ", tests[i].Name);
		}
		t := new(T);
		t.ch = make(chan *T);
		go tRunner(t, &tests[i]);
		<-t.ch;
		if t.failed {
			println("--- FAIL:", tests[i].Name);
			print(t.errors);
			ok = false;
		} else if *chatty {
			println("--- PASS:", tests[i].Name);
			print(t.errors);
		}
	}
	if !ok {
		println("FAIL");
		sys.Exit(1);
	}
	println("PASS");
}
<|endoftext|>


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

package jsonmessage

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

	"github.com/Nvveen/Gotty"
	"github.com/docker/go-units"
	"github.com/fsouza/go-dockerclient/internal/term"
)

// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time isalways the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"

// JSONError wraps a concrete Code and Message, `Code` is
// is an integer error code, `Message` is the error message.
type JSONError struct {
	Code    int    `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

func (e *JSONError) Error() string {
	return e.Message
}

// JSONProgress describes a Progress. terminalFd is the fd of the current terminal,
// Start is the initial value for the operation. Current is the current status and
// value of the progress made towards Total. Total is the end value describing when
// we made 100% progress for an operation.
type JSONProgress struct {
	terminalFd uintptr
	Current    int64 `json:"current,omitempty"`
	Total      int64 `json:"total,omitempty"`
	Start      int64 `json:"start,omitempty"`
	// If true, don't show xB/yB
	HideCounts bool   `json:"hidecounts,omitempty"`
	Units      string `json:"units,omitempty"`
	nowFunc    func() time.Time
	winSize    int
}

func (p *JSONProgress) String() string {
	var (
		width       = p.width()
		pbBox       string
		numbersBox  string
		timeLeftBox string
	)
	if p.Current <= 0 && p.Total <= 0 {
		return ""
	}
	if p.Total <= 0 {
		switch p.Units {
		case "":
			current := units.HumanSize(float64(p.Current))
			return fmt.Sprintf("%8v", current)
		default:
			return fmt.Sprintf("%d %s", p.Current, p.Units)
		}
	}

	percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
	if percentage > 50 {
		percentage = 50
	}
	if width > 110 {
		// this number can't be negative gh#7136
		numSpaces := 0
		if 50-percentage > 0 {
			numSpaces = 50 - percentage
		}
		pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
	}

	switch {
	case p.HideCounts:
	case p.Units == "": // no units, use bytes
		current := units.HumanSize(float64(p.Current))
		total := units.HumanSize(float64(p.Total))

		numbersBox = fmt.Sprintf("%8v/%v", current, total)

		if p.Current > p.Total {
			// remove total display if the reported current is wonky.
			numbersBox = fmt.Sprintf("%8v", current)
		}
	default:
		numbersBox = fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units)

		if p.Current > p.Total {
			// remove total display if the reported current is wonky.
			numbersBox = fmt.Sprintf("%d %s", p.Current, p.Units)
		}
	}

	if p.Current > 0 && p.Start > 0 && percentage < 50 {
		fromStart := p.now().Sub(time.Unix(p.Start, 0))
		perEntry := fromStart / time.Duration(p.Current)
		left := time.Duration(p.Total-p.Current) * perEntry
		left = (left / time.Second) * time.Second

		if width > 50 {
			timeLeftBox = " " + left.String()
		}
	}
	return pbBox + numbersBox + timeLeftBox
}

// shim for testing
func (p *JSONProgress) now() time.Time {
	if p.nowFunc == nil {
		p.nowFunc = func() time.Time {
			return time.Now().UTC()
		}
	}
	return p.nowFunc()
}

// shim for testing
func (p *JSONProgress) width() int {
	if p.winSize != 0 {
		return p.winSize
	}
	ws, err := term.GetWinsize(p.terminalFd)
	if err == nil {
		return int(ws.Width)
	}
	return 200
}

// JSONMessage defines a message struct. It describes
// the created time, where it from, status, ID of the
// message. It's used for docker events.
type JSONMessage struct {
	Stream          string        `json:"stream,omitempty"`
	Status          string        `json:"status,omitempty"`
	Progress        *JSONProgress `json:"progressDetail,omitempty"`
	ProgressMessage string        `json:"progress,omitempty"` //deprecated
	ID              string        `json:"id,omitempty"`
	From            string        `json:"from,omitempty"`
	Time            int64         `json:"time,omitempty"`
	TimeNano        int64         `json:"timeNano,omitempty"`
	Error           *JSONError    `json:"errorDetail,omitempty"`
	ErrorMessage    string        `json:"error,omitempty"` //deprecated
	// Aux contains out-of-band data, such as digests for push signing and image id after building.
	Aux *json.RawMessage `json:"aux,omitempty"`
}

/* Satisfied by gotty.TermInfo as well as noTermInfo from below */
type termInfo interface {
	Parse(attr string, params ...interface{}) (string, error)
}

type noTermInfo struct{} // canary used when no terminfo.

func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error) {
	return "", fmt.Errorf("noTermInfo")
}

func clearLine(out io.Writer, ti termInfo) error {
	// el2 (clear whole line) is not exposed by terminfo.

	// First clear line from beginning to cursor
	if attr, err := ti.Parse("el1"); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[1K")
		if err != nil {
			return err
		}
	}
	// Then clear line from cursor to end
	if attr, err := ti.Parse("el"); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[K")
		if err != nil {
			return err
		}
	}

	return nil
}

func cursorUp(out io.Writer, ti termInfo, l int) error {
	if l == 0 { // Should never be the case, but be tolerant
		return nil
	}
	if attr, err := ti.Parse("cuu", l); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[%dA", l)
		if err != nil {
			return err
		}
	}
	return nil
}

func cursorDown(out io.Writer, ti termInfo, l int) error {
	if l == 0 { // Should never be the case, but be tolerant
		return nil
	}
	if attr, err := ti.Parse("cud", l); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[%dB", l)
		if err != nil {
			return err
		}
	}

	return nil
}

// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out`
// is a terminal. If this is the case, it will erase the entire current line
// when displaying the progressbar.
func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
	if jm.Error != nil {
		if jm.Error.Code == 401 {
			return fmt.Errorf("authentication is required")
		}
		return jm.Error
	}
	var endl string
	if termInfo != nil && jm.Stream == "" && jm.Progress != nil {
		clearLine(out, termInfo)
		endl = "\r"
		_, err := fmt.Fprintf(out, endl)
		if err != nil {
			return err
		}
	} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
		return nil
	}
	if jm.TimeNano != 0 {
		_, err := fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
		if err != nil {
			return err
		}
	} else if jm.Time != 0 {
		_, err := fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
		if err != nil {
			return err
		}
	}
	if jm.ID != "" {
		_, err := fmt.Fprintf(out, "%s: ", jm.ID)
		if err != nil {
			return err
		}
	}
	if jm.From != "" {
		_, err := fmt.Fprintf(out, "(from %s) ", jm.From)
		if err != nil {
			return err
		}
	}
	if jm.Progress != nil && termInfo != nil {
		_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
		if err != nil {
			return err
		}
	} else if jm.ProgressMessage != "" { //deprecated
		_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
		if err != nil {
			return err
		}
	} else if jm.Stream != "" {
		_, err := fmt.Fprintf(out, "%s%s", jm.Stream, endl)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
		if err != nil {
			return err
		}
	}
	return nil
}

// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
// each line and move the cursor while displaying.
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
	var (
		dec = json.NewDecoder(in)
		ids = make(map[string]int)
	)

	var termInfo termInfo

	if isTerminal {
		term := os.Getenv("TERM")
		if term == "" {
			term = "vt102"
		}

		var err error
		if termInfo, err = gotty.OpenTermInfo(term); err != nil {
			termInfo = &noTermInfo{}
		}
	}

	for {
		diff := 0
		var jm JSONMessage
		if err := dec.Decode(&jm); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		if jm.Aux != nil {
			if auxCallback != nil {
				auxCallback(jm)
			}
			continue
		}

		if jm.Progress != nil {
			jm.Progress.terminalFd = terminalFd
		}
		if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
			line, ok := ids[jm.ID]
			if !ok {
				// NOTE: This approach of using len(id) to
				// figure out the number of lines of history
				// only works as long as we clear the history
				// when we output something that's not
				// accounted for in the map, such as a line
				// with no ID.
				line = len(ids)
				ids[jm.ID] = line
				if termInfo != nil {
					_, err := fmt.Fprintf(out, "\n")
					if err != nil {
						return err
					}
				}
			}
			diff = len(ids) - line
			if termInfo != nil {
				if err := cursorUp(out, termInfo, diff); err != nil {
					return err
				}
			}
		} else {
			// When outputting something that isn't progress
			// output, clear the history of previous lines. We
			// don't want progress entries from some previous
			// operation to be updated (for example, pull -a
			// with multiple tags).
			ids = make(map[string]int)
		}
		err := jm.Display(out, termInfo)
		if jm.ID != "" && termInfo != nil {
			if err := cursorDown(out, termInfo, diff); err != nil {
				return err
			}
		}
		if err != nil {
			return err
		}
	}
	return nil
}

type stream interface {
	io.Writer
	FD() uintptr
	IsTerminal() bool
}

// DisplayJSONMessagesToStream prints json messages to the output stream
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error {
	return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback)
}
<commit_msg>internal/jsonmessage: fix staticcheck violation<commit_after>// Copyright 2014 Docker authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the DOCKER-LICENSE file.

package jsonmessage

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

	"github.com/Nvveen/Gotty"
	"github.com/docker/go-units"
	"github.com/fsouza/go-dockerclient/internal/term"
)

// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time isalways the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"

// JSONError wraps a concrete Code and Message, `Code` is
// is an integer error code, `Message` is the error message.
type JSONError struct {
	Code    int    `json:"code,omitempty"`
	Message string `json:"message,omitempty"`
}

func (e *JSONError) Error() string {
	return e.Message
}

// JSONProgress describes a Progress. terminalFd is the fd of the current terminal,
// Start is the initial value for the operation. Current is the current status and
// value of the progress made towards Total. Total is the end value describing when
// we made 100% progress for an operation.
type JSONProgress struct {
	terminalFd uintptr
	Current    int64 `json:"current,omitempty"`
	Total      int64 `json:"total,omitempty"`
	Start      int64 `json:"start,omitempty"`
	// If true, don't show xB/yB
	HideCounts bool   `json:"hidecounts,omitempty"`
	Units      string `json:"units,omitempty"`
	nowFunc    func() time.Time
	winSize    int
}

func (p *JSONProgress) String() string {
	var (
		width       = p.width()
		pbBox       string
		numbersBox  string
		timeLeftBox string
	)
	if p.Current <= 0 && p.Total <= 0 {
		return ""
	}
	if p.Total <= 0 {
		switch p.Units {
		case "":
			current := units.HumanSize(float64(p.Current))
			return fmt.Sprintf("%8v", current)
		default:
			return fmt.Sprintf("%d %s", p.Current, p.Units)
		}
	}

	percentage := int(float64(p.Current)/float64(p.Total)*100) / 2
	if percentage > 50 {
		percentage = 50
	}
	if width > 110 {
		// this number can't be negative gh#7136
		numSpaces := 0
		if 50-percentage > 0 {
			numSpaces = 50 - percentage
		}
		pbBox = fmt.Sprintf("[%s>%s] ", strings.Repeat("=", percentage), strings.Repeat(" ", numSpaces))
	}

	switch {
	case p.HideCounts:
	case p.Units == "": // no units, use bytes
		current := units.HumanSize(float64(p.Current))
		total := units.HumanSize(float64(p.Total))

		numbersBox = fmt.Sprintf("%8v/%v", current, total)

		if p.Current > p.Total {
			// remove total display if the reported current is wonky.
			numbersBox = fmt.Sprintf("%8v", current)
		}
	default:
		numbersBox = fmt.Sprintf("%d/%d %s", p.Current, p.Total, p.Units)

		if p.Current > p.Total {
			// remove total display if the reported current is wonky.
			numbersBox = fmt.Sprintf("%d %s", p.Current, p.Units)
		}
	}

	if p.Current > 0 && p.Start > 0 && percentage < 50 {
		fromStart := p.now().Sub(time.Unix(p.Start, 0))
		perEntry := fromStart / time.Duration(p.Current)
		left := time.Duration(p.Total-p.Current) * perEntry
		left = (left / time.Second) * time.Second

		if width > 50 {
			timeLeftBox = " " + left.String()
		}
	}
	return pbBox + numbersBox + timeLeftBox
}

// shim for testing
func (p *JSONProgress) now() time.Time {
	if p.nowFunc == nil {
		p.nowFunc = func() time.Time {
			return time.Now().UTC()
		}
	}
	return p.nowFunc()
}

// shim for testing
func (p *JSONProgress) width() int {
	if p.winSize != 0 {
		return p.winSize
	}
	ws, err := term.GetWinsize(p.terminalFd)
	if err == nil {
		return int(ws.Width)
	}
	return 200
}

// JSONMessage defines a message struct. It describes
// the created time, where it from, status, ID of the
// message. It's used for docker events.
type JSONMessage struct {
	Stream          string        `json:"stream,omitempty"`
	Status          string        `json:"status,omitempty"`
	Progress        *JSONProgress `json:"progressDetail,omitempty"`
	ProgressMessage string        `json:"progress,omitempty"` //deprecated
	ID              string        `json:"id,omitempty"`
	From            string        `json:"from,omitempty"`
	Time            int64         `json:"time,omitempty"`
	TimeNano        int64         `json:"timeNano,omitempty"`
	Error           *JSONError    `json:"errorDetail,omitempty"`
	ErrorMessage    string        `json:"error,omitempty"` //deprecated
	// Aux contains out-of-band data, such as digests for push signing and image id after building.
	Aux *json.RawMessage `json:"aux,omitempty"`
}

/* Satisfied by gotty.TermInfo as well as noTermInfo from below */
type termInfo interface {
	Parse(attr string, params ...interface{}) (string, error)
}

type noTermInfo struct{} // canary used when no terminfo.

func (ti *noTermInfo) Parse(attr string, params ...interface{}) (string, error) {
	return "", fmt.Errorf("noTermInfo")
}

func clearLine(out io.Writer, ti termInfo) error {
	// el2 (clear whole line) is not exposed by terminfo.

	// First clear line from beginning to cursor
	if attr, err := ti.Parse("el1"); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[1K")
		if err != nil {
			return err
		}
	}
	// Then clear line from cursor to end
	if attr, err := ti.Parse("el"); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[K")
		if err != nil {
			return err
		}
	}

	return nil
}

func cursorUp(out io.Writer, ti termInfo, l int) error {
	if l == 0 { // Should never be the case, but be tolerant
		return nil
	}
	if attr, err := ti.Parse("cuu", l); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[%dA", l)
		if err != nil {
			return err
		}
	}
	return nil
}

func cursorDown(out io.Writer, ti termInfo, l int) error {
	if l == 0 { // Should never be the case, but be tolerant
		return nil
	}
	if attr, err := ti.Parse("cud", l); err == nil {
		_, err = fmt.Fprintf(out, "%s", attr)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "\x1b[%dB", l)
		if err != nil {
			return err
		}
	}

	return nil
}

// Display displays the JSONMessage to `out`. `termInfo` is non-nil if `out`
// is a terminal. If this is the case, it will erase the entire current line
// when displaying the progressbar.
func (jm *JSONMessage) Display(out io.Writer, termInfo termInfo) error {
	if jm.Error != nil {
		if jm.Error.Code == 401 {
			return fmt.Errorf("authentication is required")
		}
		return jm.Error
	}
	var endl string
	if termInfo != nil && jm.Stream == "" && jm.Progress != nil {
		clearLine(out, termInfo)
		endl = "\r"
		_, err := fmt.Fprint(out, endl)
		if err != nil {
			return err
		}
	} else if jm.Progress != nil && jm.Progress.String() != "" { //disable progressbar in non-terminal
		return nil
	}
	if jm.TimeNano != 0 {
		_, err := fmt.Fprintf(out, "%s ", time.Unix(0, jm.TimeNano).Format(RFC3339NanoFixed))
		if err != nil {
			return err
		}
	} else if jm.Time != 0 {
		_, err := fmt.Fprintf(out, "%s ", time.Unix(jm.Time, 0).Format(RFC3339NanoFixed))
		if err != nil {
			return err
		}
	}
	if jm.ID != "" {
		_, err := fmt.Fprintf(out, "%s: ", jm.ID)
		if err != nil {
			return err
		}
	}
	if jm.From != "" {
		_, err := fmt.Fprintf(out, "(from %s) ", jm.From)
		if err != nil {
			return err
		}
	}
	if jm.Progress != nil && termInfo != nil {
		_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
		if err != nil {
			return err
		}
	} else if jm.ProgressMessage != "" { //deprecated
		_, err := fmt.Fprintf(out, "%s %s%s", jm.Status, jm.ProgressMessage, endl)
		if err != nil {
			return err
		}
	} else if jm.Stream != "" {
		_, err := fmt.Fprintf(out, "%s%s", jm.Stream, endl)
		if err != nil {
			return err
		}
	} else {
		_, err := fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
		if err != nil {
			return err
		}
	}
	return nil
}

// DisplayJSONMessagesStream displays a json message stream from `in` to `out`, `isTerminal`
// describes if `out` is a terminal. If this is the case, it will print `\n` at the end of
// each line and move the cursor while displaying.
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
	var (
		dec = json.NewDecoder(in)
		ids = make(map[string]int)
	)

	var termInfo termInfo

	if isTerminal {
		term := os.Getenv("TERM")
		if term == "" {
			term = "vt102"
		}

		var err error
		if termInfo, err = gotty.OpenTermInfo(term); err != nil {
			termInfo = &noTermInfo{}
		}
	}

	for {
		diff := 0
		var jm JSONMessage
		if err := dec.Decode(&jm); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		if jm.Aux != nil {
			if auxCallback != nil {
				auxCallback(jm)
			}
			continue
		}

		if jm.Progress != nil {
			jm.Progress.terminalFd = terminalFd
		}
		if jm.ID != "" && (jm.Progress != nil || jm.ProgressMessage != "") {
			line, ok := ids[jm.ID]
			if !ok {
				// NOTE: This approach of using len(id) to
				// figure out the number of lines of history
				// only works as long as we clear the history
				// when we output something that's not
				// accounted for in the map, such as a line
				// with no ID.
				line = len(ids)
				ids[jm.ID] = line
				if termInfo != nil {
					_, err := fmt.Fprintf(out, "\n")
					if err != nil {
						return err
					}
				}
			}
			diff = len(ids) - line
			if termInfo != nil {
				if err := cursorUp(out, termInfo, diff); err != nil {
					return err
				}
			}
		} else {
			// When outputting something that isn't progress
			// output, clear the history of previous lines. We
			// don't want progress entries from some previous
			// operation to be updated (for example, pull -a
			// with multiple tags).
			ids = make(map[string]int)
		}
		err := jm.Display(out, termInfo)
		if jm.ID != "" && termInfo != nil {
			if err := cursorDown(out, termInfo, diff); err != nil {
				return err
			}
		}
		if err != nil {
			return err
		}
	}
	return nil
}

type stream interface {
	io.Writer
	FD() uintptr
	IsTerminal() bool
}

// DisplayJSONMessagesToStream prints json messages to the output stream
func DisplayJSONMessagesToStream(in io.Reader, stream stream, auxCallback func(JSONMessage)) error {
	return DisplayJSONMessagesStream(in, stream, stream.FD(), stream.IsTerminal(), auxCallback)
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":              validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                 validate.Optional(validate.IsNetworkMTU),
		"vlan":                validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":    validate.IsAny,
		"maas.subnet.ipv6":    validate.IsAny,
		"ipv4.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":         validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast": validate.Optional(validate.IsBool),
		"ipv6.routes":         validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast": validate.Optional(validate.IsBool),
		"dns.nameservers":     validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode": validate.Optional(func(value string) error {
			return validate.IsOneOf(value, []string{"l2proxy", "routed"})
		}),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return n.common.create(clientType)
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *physical) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<commit_msg>lxd/network/driver/physical: Check that physical parent interface has no global unicast IPs configured on it before starting<commit_after>package network

import (
	"fmt"
	"net"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/validate"
)

// physical represents a LXD physical network.
type physical struct {
	common
}

// Type returns the network type.
func (n *physical) Type() string {
	return "physical"
}

// DBType returns the network type DB ID.
func (n *physical) DBType() db.NetworkType {
	return db.NetworkTypePhysical
}

// Validate network config.
func (n *physical) Validate(config map[string]string) error {
	rules := map[string]func(value string) error{
		"parent":              validate.Required(validate.IsNotEmpty, validate.IsInterfaceName),
		"mtu":                 validate.Optional(validate.IsNetworkMTU),
		"vlan":                validate.Optional(validate.IsNetworkVLAN),
		"gvrp":                validate.Optional(validate.IsBool),
		"maas.subnet.ipv4":    validate.IsAny,
		"maas.subnet.ipv6":    validate.IsAny,
		"ipv4.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV4),
		"ipv6.gateway":        validate.Optional(validate.IsNetworkAddressCIDRV6),
		"ipv4.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV4List),
		"ipv6.ovn.ranges":     validate.Optional(validate.IsNetworkRangeV6List),
		"ipv4.routes":         validate.Optional(validate.IsNetworkV4List),
		"ipv4.routes.anycast": validate.Optional(validate.IsBool),
		"ipv6.routes":         validate.Optional(validate.IsNetworkV6List),
		"ipv6.routes.anycast": validate.Optional(validate.IsBool),
		"dns.nameservers":     validate.Optional(validate.IsNetworkAddressList),
		"ovn.ingress_mode": validate.Optional(func(value string) error {
			return validate.IsOneOf(value, []string{"l2proxy", "routed"})
		}),
		"volatile.last_state.created": validate.Optional(validate.IsBool),
	}

	err := n.validate(config, rules)
	if err != nil {
		return err
	}

	return nil
}

// checkParentUse checks if parent is already in use by another network or instance device.
func (n *physical) checkParentUse(ourConfig map[string]string) (bool, error) {
	// Get all managed networks across all projects.
	var err error
	var projectNetworks map[string]map[int64]api.Network

	err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		projectNetworks, err = tx.GetCreatedNetworks()
		return err
	})
	if err != nil {
		return false, errors.Wrapf(err, "Failed to load all networks")
	}

	for projectName, networks := range projectNetworks {
		if projectName != project.Default {
			continue // Only default project networks can possibly reference a physical interface.
		}

		for _, network := range networks {
			if network.Name == n.name {
				continue // Ignore our own DB record.
			}

			// Check if another network is using our parent.
			if network.Config["parent"] == ourConfig["parent"] {
				// If either network doesn't specify a vlan, or both specify same vlan,
				// then we can't use this parent.
				if (network.Config["vlan"] == "" || ourConfig["vlan"] == "") || network.Config["vlan"] == ourConfig["vlan"] {
					return true, nil
				}
			}
		}
	}

	return false, nil
}

// Create checks whether the referenced parent interface is used by other networks or instance devices, as we
// need to have exclusive access to the interface.
func (n *physical) Create(clientType request.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		inUse, err := n.checkParentUse(n.config)
		if err != nil {
			return err
		}
		if inUse {
			return fmt.Errorf("Parent interface %q in use by another network", n.config["parent"])
		}
	}

	return n.common.create(clientType)
}

// Delete deletes a network.
func (n *physical) Delete(clientType request.ClientType) error {
	n.logger.Debug("Delete", log.Ctx{"clientType": clientType})

	err := n.Stop()
	if err != nil {
		return err
	}

	return n.common.delete(clientType)
}

// Rename renames a network.
func (n *physical) Rename(newName string) error {
	n.logger.Debug("Rename", log.Ctx{"newName": newName})

	// Rename common steps.
	err := n.common.rename(newName)
	if err != nil {
		return err
	}

	return nil
}

// Start starts is a no-op.
func (n *physical) Start() error {
	n.logger.Debug("Start")

	revert := revert.New()
	defer revert.Fail()

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])
	created, err := VLANInterfaceCreate(n.config["parent"], hostName, n.config["vlan"], shared.IsTrue(n.config["gvrp"]))
	if err != nil {
		return err
	}
	if created {
		revert.Add(func() { InterfaceRemove(hostName) })
	}

	// Check no global unicast IPs defined on parent, as that may indicate it is in use by another application.
	addresses, _, err := InterfaceStatus(hostName)
	if err != nil {
		return errors.Wrapf(err, "Failed getting interface status for %q", hostName)
	}

	if len(addresses) > 0 {
		return fmt.Errorf("Cannot start network as parent interface %q has one or more IP addresses configured on it", hostName)
	}

	// Set the MTU.
	if n.config["mtu"] != "" {
		phyLink := &ip.Link{Name: hostName}
		err = phyLink.SetMTU(n.config["mtu"])
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", n.config["mtu"], phyLink.Name)
		}
	}

	// Record if we created this device or not (if we have not already recorded that we created it previously),
	// so it can be removed on stop. This way we won't overwrite the setting on LXD restart.
	if !shared.IsTrue(n.config["volatile.last_state.created"]) {
		n.config["volatile.last_state.created"] = fmt.Sprintf("%t", created)
		err = n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
			return tx.UpdateNetwork(n.id, n.description, n.config)
		})
		if err != nil {
			return errors.Wrapf(err, "Failed saving volatile config")
		}
	}

	revert.Success()
	return nil
}

// Stop stops is a no-op.
func (n *physical) Stop() error {
	n.logger.Debug("Stop")

	hostName := GetHostDevice(n.config["parent"], n.config["vlan"])

	// Only try and remove created VLAN interfaces.
	if n.config["vlan"] != "" && shared.IsTrue(n.config["volatile.last_state.created"]) && InterfaceExists(hostName) {
		err := InterfaceRemove(hostName)
		if err != nil {
			return err
		}
	}

	// Reset MTU back to 1500 if overridden in config.
	if n.config["mtu"] != "" && InterfaceExists(hostName) {
		resetMTU := "1500"
		link := &ip.Link{Name: hostName}
		err := link.SetMTU(resetMTU)
		if err != nil {
			return errors.Wrapf(err, "Failed setting MTU %q on %q", link, link.Name)
		}
	}

	// Remove last state config.
	delete(n.config, "volatile.last_state.created")
	err := n.state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.UpdateNetwork(n.id, n.description, n.config)
	})
	if err != nil {
		return errors.Wrapf(err, "Failed removing volatile config")
	}

	return nil
}

// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
// cluster notification, in which case do not update the database, just apply local changes needed.
func (n *physical) Update(newNetwork api.NetworkPut, targetNode string, clientType request.ClientType) error {
	n.logger.Debug("Update", log.Ctx{"clientType": clientType, "newNetwork": newNetwork})

	dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
	if err != nil {
		return err
	}

	if !dbUpdateNeeeded {
		return nil // Nothing changed.
	}

	// If the network as a whole has not had any previous creation attempts, or the node itself is still
	// pending, then don't apply the new settings to the node, just to the database record (ready for the
	// actual global create request to be initiated).
	if n.Status() == api.NetworkStatusPending || n.LocalStatus() == api.NetworkStatusPending {
		return n.common.update(newNetwork, targetNode, clientType)
	}

	revert := revert.New()
	defer revert.Fail()

	hostNameChanged := shared.StringInSlice("vlan", changedKeys) || shared.StringInSlice("parent", changedKeys)

	// We only need to check in the database once, not on every clustered node.
	if clientType == request.ClientTypeNormal {
		if hostNameChanged {
			isUsed, err := n.IsUsed()
			if isUsed || err != nil {
				return fmt.Errorf("Cannot update network parent interface when in use")
			}

			inUse, err := n.checkParentUse(newNetwork.Config)
			if err != nil {
				return err
			}
			if inUse {
				return fmt.Errorf("Parent interface %q in use by another network", newNetwork.Config["parent"])
			}
		}
	}

	if hostNameChanged {
		err = n.Stop()
		if err != nil {
			return err
		}

		// Remove the volatile last state from submitted new config if present.
		delete(newNetwork.Config, "volatile.last_state.created")
	}

	// Define a function which reverts everything.
	revert.Add(func() {
		// Reset changes to all nodes and database.
		n.common.update(oldNetwork, targetNode, clientType)
	})

	// Apply changes to all nodes and databse.
	err = n.common.update(newNetwork, targetNode, clientType)
	if err != nil {
		return err
	}

	err = n.Start()
	if err != nil {
		return err
	}

	revert.Success()

	// Notify dependent networks (those using this network as their uplink) of the changes.
	// Do this after the network has been successfully updated so that a failure to notify a dependent network
	// doesn't prevent the network itself from being updated.
	if clientType == request.ClientTypeNormal && len(changedKeys) > 0 {
		n.common.notifyDependentNetworks(changedKeys)
	}

	return nil
}

// DHCPv4Subnet returns the DHCPv4 subnet (if DHCP is enabled on network).
func (n *physical) DHCPv4Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv4.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}

// DHCPv6Subnet returns the DHCPv6 subnet (if DHCP or SLAAC is enabled on network).
func (n *physical) DHCPv6Subnet() *net.IPNet {
	_, subnet, err := net.ParseCIDR(n.config["ipv6.gateway"])
	if err != nil {
		return nil
	}

	return subnet
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]float64) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"] / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]float64) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"] / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]float64) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	if err := fetchPercentageOfClients(c, stat); err != nil {
		return err
	}
	return nil
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]float64, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]float64)

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expired := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			stat["keys"] += keysFv

			expiredKv := strings.SplitN(expired, "=", 2)
			expiredFv, err := strconv.ParseFloat(expiredKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expired. %s", err)
			}
			stat["expired"] += expiredFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expired"]; !ok {
		stat["expired"] = 0
	}

	if err := calculateCapacity(c, stat); err != nil {
		return nil, err
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string](mp.Graphs) {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string](mp.Graphs){
		(m.Prefix + ".queries"): mp.Graphs{
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "instantaneous_ops_per_sec", Label: "Queries", Diff: false},
			},
		},
		(m.Prefix + ".connections"): mp.Graphs{
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				mp.Metrics{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		(m.Prefix + ".clients"): mp.Graphs{
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				mp.Metrics{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				mp.Metrics{Name: "connected_slaves", Label: "Blocked Clients", Diff: false, Stacked: true},
			},
		},
		(m.Prefix + ".keys"): mp.Graphs{
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "keys", Label: "Keys", Diff: false},
				mp.Metrics{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		(m.Prefix + ".keyspace"): mp.Graphs{
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				mp.Metrics{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		(m.Prefix + ".memory"): mp.Graphs{
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "used_memory", Label: "Used Memory", Diff: false},
				mp.Metrics{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				mp.Metrics{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				mp.Metrics{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		(m.Prefix + ".capacity"): mp.Graphs{
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				mp.Metrics{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
	}
	helper := mp.NewMackerelPlugin(redis)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		if redis.Socket != "" {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-redis-%s", fmt.Sprintf("%x", md5.Sum([]byte(redis.Socket))))
		} else {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-redis-%s-%s", redis.Host, redis.Port)
		}
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>[redis] skip to calculate capacity when CONFIG command failed (#214)<commit_after>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/fzzy/radix/redis"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"github.com/mackerelio/mackerel-agent/logging"
)

var logger = logging.GetLogger("metrics.plugin.redis")

// RedisPlugin mackerel plugin for Redis
type RedisPlugin struct {
	Host     string
	Port     string
	Socket   string
	Prefix   string
	Timeout  int
	Tempfile string
}

func fetchPercentageOfMemory(c *redis.Client, stat map[string]float64) error {
	r := c.Cmd("CONFIG", "GET", "maxmemory")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxmemory` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxmemory. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxmemory"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxmemory. %s", err)
		return err
	}

	if maxsize == 0.0 {
		stat["percentage_of_memory"] = 0.0
	} else {
		stat["percentage_of_memory"] = 100.0 * stat["used_memory"] / maxsize
	}

	return nil
}

func fetchPercentageOfClients(c *redis.Client, stat map[string]float64) error {
	r := c.Cmd("CONFIG", "GET", "maxclients")
	if r.Err != nil {
		logger.Errorf("Failed to run `CONFIG GET maxclients` command. %s", r.Err)
		return r.Err
	}

	res, err := r.Hash()
	if err != nil {
		logger.Errorf("Failed to fetch maxclients. %s", err)
		return err
	}

	maxsize, err := strconv.ParseFloat(res["maxclients"], 64)
	if err != nil {
		logger.Errorf("Failed to parse maxclients. %s", err)
		return err
	}

	stat["percentage_of_clients"] = 100.0 * stat["connected_clients"] / maxsize

	return nil
}

func calculateCapacity(c *redis.Client, stat map[string]float64) error {
	if err := fetchPercentageOfMemory(c, stat); err != nil {
		return err
	}
	if err := fetchPercentageOfClients(c, stat); err != nil {
		return err
	}
	return nil
}

// FetchMetrics interface for mackerelplugin
func (m RedisPlugin) FetchMetrics() (map[string]float64, error) {
	network := "tcp"
	target := fmt.Sprintf("%s:%s", m.Host, m.Port)
	if m.Socket != "" {
		target = m.Socket
		network = "unix"
	}
	c, err := redis.DialTimeout(network, target, time.Duration(m.Timeout)*time.Second)
	if err != nil {
		logger.Errorf("Failed to connect redis. %s", err)
		return nil, err
	}
	defer c.Close()

	r := c.Cmd("info")
	if r.Err != nil {
		logger.Errorf("Failed to run info command. %s", r.Err)
		return nil, r.Err
	}
	str, err := r.Str()
	if err != nil {
		logger.Errorf("Failed to fetch information. %s", err)
		return nil, err
	}

	stat := make(map[string]float64)

	for _, line := range strings.Split(str, "\r\n") {
		if line == "" {
			continue
		}
		if re, _ := regexp.MatchString("^#", line); re {
			continue
		}

		record := strings.SplitN(line, ":", 2)
		if len(record) < 2 {
			continue
		}
		key, value := record[0], record[1]

		if re, _ := regexp.MatchString("^db", key); re {
			kv := strings.SplitN(value, ",", 3)
			keys, expired := kv[0], kv[1]

			keysKv := strings.SplitN(keys, "=", 2)
			keysFv, err := strconv.ParseFloat(keysKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db keys. %s", err)
			}
			stat["keys"] += keysFv

			expiredKv := strings.SplitN(expired, "=", 2)
			expiredFv, err := strconv.ParseFloat(expiredKv[1], 64)
			if err != nil {
				logger.Warningf("Failed to parse db expired. %s", err)
			}
			stat["expired"] += expiredFv

			continue
		}

		stat[key], err = strconv.ParseFloat(value, 64)
		if err != nil {
			continue
		}
	}

	if _, ok := stat["keys"]; !ok {
		stat["keys"] = 0
	}
	if _, ok := stat["expired"]; !ok {
		stat["expired"] = 0
	}

	if err := calculateCapacity(c, stat); err != nil {
		logger.Infof("Failed to calculate capacity. (The cause may be that AWS Elasticache Redis has no `CONFIG` command.) Skip these metrics. %s", err)
	}

	return stat, nil
}

// GraphDefinition interface for mackerelplugin
func (m RedisPlugin) GraphDefinition() map[string](mp.Graphs) {
	labelPrefix := strings.Title(m.Prefix)

	var graphdef = map[string](mp.Graphs){
		(m.Prefix + ".queries"): mp.Graphs{
			Label: (labelPrefix + " Queries"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "instantaneous_ops_per_sec", Label: "Queries", Diff: false},
			},
		},
		(m.Prefix + ".connections"): mp.Graphs{
			Label: (labelPrefix + " Connections"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "total_connections_received", Label: "Connections", Diff: true, Stacked: true},
				mp.Metrics{Name: "rejected_connections", Label: "Rejected Connections", Diff: true, Stacked: true},
			},
		},
		(m.Prefix + ".clients"): mp.Graphs{
			Label: (labelPrefix + " Clients"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "connected_clients", Label: "Connected Clients", Diff: false, Stacked: true},
				mp.Metrics{Name: "blocked_clients", Label: "Blocked Clients", Diff: false, Stacked: true},
				mp.Metrics{Name: "connected_slaves", Label: "Blocked Clients", Diff: false, Stacked: true},
			},
		},
		(m.Prefix + ".keys"): mp.Graphs{
			Label: (labelPrefix + " Keys"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "keys", Label: "Keys", Diff: false},
				mp.Metrics{Name: "expired", Label: "Expired Keys", Diff: false},
			},
		},
		(m.Prefix + ".keyspace"): mp.Graphs{
			Label: (labelPrefix + " Keyspace"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "keyspace_hits", Label: "Keyspace Hits", Diff: true},
				mp.Metrics{Name: "keyspace_misses", Label: "Keyspace Missed", Diff: true},
			},
		},
		(m.Prefix + ".memory"): mp.Graphs{
			Label: (labelPrefix + " Memory"),
			Unit:  "integer",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "used_memory", Label: "Used Memory", Diff: false},
				mp.Metrics{Name: "used_memory_rss", Label: "Used Memory RSS", Diff: false},
				mp.Metrics{Name: "used_memory_peak", Label: "Used Memory Peak", Diff: false},
				mp.Metrics{Name: "used_memory_lua", Label: "Used Memory Lua engine", Diff: false},
			},
		},
		(m.Prefix + ".capacity"): mp.Graphs{
			Label: (labelPrefix + " Capacity"),
			Unit:  "percentage",
			Metrics: [](mp.Metrics){
				mp.Metrics{Name: "percentage_of_memory", Label: "Percentage of memory", Diff: false},
				mp.Metrics{Name: "percentage_of_clients", Label: "Percentage of clients", Diff: false},
			},
		},
	}

	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "6379", "Port")
	optSocket := flag.String("socket", "", "Server socket (overrides host and port)")
	optPrefix := flag.String("metric-key-prefix", "redis", "Metric key prefix")
	optTimeout := flag.Int("timeout", 5, "Timeout")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	redis := RedisPlugin{
		Timeout: *optTimeout,
		Prefix:  *optPrefix,
	}
	if *optSocket != "" {
		redis.Socket = *optSocket
	} else {
		redis.Host = *optHost
		redis.Port = *optPort
	}
	helper := mp.NewMackerelPlugin(redis)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		if redis.Socket != "" {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-redis-%s", fmt.Sprintf("%x", md5.Sum([]byte(redis.Socket))))
		} else {
			helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-redis-%s-%s", redis.Host, redis.Port)
		}
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// +build libsqlite3

package sqlite3

/*
#cgo CFLAGS: -DUSE_LIBSQLITE3
#cgo linux LDFLAGS: -lsqlite3
#cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3
#cgo openbsd LDFLAGS: -lsqlite3
#cgo solaris LDFLAGS: -lsqlite3
*/
import "C"
<commit_msg>darwin/libsqlite3: Also use the homebrew include path<commit_after>// Copyright (C) 2014 Yasuhiro Matsumoto <mattn.jp@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// +build libsqlite3

package sqlite3

/*
#cgo CFLAGS: -DUSE_LIBSQLITE3
#cgo linux LDFLAGS: -lsqlite3
#cgo darwin LDFLAGS: -L/usr/local/opt/sqlite/lib -lsqlite3
#cgo darwin CFLAGS: -I/usr/local/opt/sqlite/include
#cgo openbsd LDFLAGS: -lsqlite3
#cgo solaris LDFLAGS: -lsqlite3
*/
import "C"
<|endoftext|>


<commit_before>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (interface{}, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}
	var cores int64

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
		if key == "core_count" {
			var err error
			cores, err = strconv.ParseInt(val, 10, 32)
			if err != nil {
				cpuLogger.Errorf("while parsing %q: %s", val, err)
				return nil, err
			}
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}

	wholeResults := []cpuSpec{}
	for i := 0; int64(i) < cores; i++ {
		wholeResults = append(wholeResults, results)
	}
	return wholeResults, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	return g.parseSysCtlBytes(cpuInfoBytes)
}
<commit_msg>Atoi is enough<commit_after>// +build darwin

package darwin

import (
	"bufio"
	"bytes"
	"os/exec"
	"strconv"
	"strings"

	"github.com/mackerelio/mackerel-agent/logging"
)

// CPUGenerator Collects CPU specs
type CPUGenerator struct {
}

// Key XXX
func (g *CPUGenerator) Key() string {
	return "cpu"
}

var cpuLogger = logging.GetLogger("spec.cpu")

type cpuSpec map[string]interface{}

var sysCtlKeyMap = map[string]string{
	"core_count":   "cores",
	"brand_string": "model_name",
	"model":        "model",
	"vendor":       "vendor_id",
	"family":       "family",
	"stepping":     "stepping",
}

func (g *CPUGenerator) parseSysCtlBytes(res []byte) (interface{}, error) {
	scanner := bufio.NewScanner(bytes.NewBuffer(res))

	results := cpuSpec{}
	var cores int

	for scanner.Scan() {
		line := scanner.Text()
		kv := strings.SplitN(line, ":", 2)
		if len(kv) < 2 {
			continue
		}
		key := strings.TrimPrefix(strings.TrimSpace(kv[0]), "machdep.cpu.")
		val := strings.TrimSpace(kv[1])
		if label, ok := sysCtlKeyMap[key]; ok {
			results[label] = val
		}
		if key == "core_count" {
			var err error
			cores, err = strconv.Atoi(val)
			if err != nil {
				cpuLogger.Errorf("while parsing %q: %s", val, err)
				return nil, err
			}
		}
	}

	if err := scanner.Err(); err != nil {
		cpuLogger.Errorf("Failed (skip this spec): %s", err)
		return nil, err
	}

	wholeResults := []cpuSpec{}
	for i := 0; i < cores; i++ {
		wholeResults = append(wholeResults, results)
	}
	return wholeResults, nil
}

// MEMO: sysctl -a machdep.cpu.brand_string

// Generate collects CPU specs.
// Returns an array of cpuSpec.
// Each spec is expected to have keys below:
// - model_name (used in Web)
// - vendor_id
// - family
// - model
// - stepping
// - physical_id
// - core_id
// - cores
// - mhz
// - cache_size
// - flags
func (g *CPUGenerator) Generate() (interface{}, error) {
	cpuInfoBytes, err := exec.Command("sysctl", "-a", "machdep.cpu").Output()
	if err != nil {
		cpuLogger.Errorf("Failed: %s", err)
		return nil, err
	}
	return g.parseSysCtlBytes(cpuInfoBytes)
}
<|endoftext|>