<commit_before>package main

const Name string = "packer-post-processor-vagrant-metadata"
const Version string = "0.1.1"

// GitCommit describes latest commit hash.
// This value is extracted by git command when building.
// To set this from outside, use go build -ldflags "-X main.GitCommit \"$(COMMIT)\""
var GitCommit string
<commit_msg>bump version to 0.1.2<commit_after>package main

const Name string = "packer-post-processor-vagrant-metadata"
const Version string = "0.1.2"

// GitCommit describes latest commit hash.
// This value is extracted by git command when building.
// To set this from outside, use go build -ldflags "-X main.GitCommit \"$(COMMIT)\""
var GitCommit string
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type Version struct {
	Major, Minor, Patch int
}

func (v Version) String() string {
	if v.Major < 0 {
		panic(fmt.Sprintf("cannot stringify invalid version (major is %d)", v.Major))
	}
	if v.Minor < 0 {
		return fmt.Sprintf("v%d", v.Major)
	}
	if v.Patch < 0 {
		return fmt.Sprintf("v%d.%d", v.Major, v.Minor)
	}
	return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
}

func (v Version) Less(other Version) bool {
	if v.Major != other.Major {
		return v.Major < other.Major
	}
	if v.Minor != other.Minor {
		return v.Minor < other.Minor
	}
	return v.Patch < other.Patch
}

func (v Version) Contains(other Version) bool {
	if v.Patch != -1 {
		return v == other
	}
	if v.Minor != -1 {
		return v.Major == other.Major && v.Minor == other.Minor
	}
	return v.Major == other.Major
}

func (v Version) IsValid() bool {
	return v == InvalidVersion
}

var InvalidVersion = Version{-1, -1, -1}

func parseVersion(s string) (Version, bool) {
	if len(s) < 2 {
		return InvalidVersion, false
	}
	if s[0] != 'v' {
		return InvalidVersion, false
	}
	v := Version{-1, -1, -1}
	i := 1
	v.Major, i = parseVersionPart(s, i)
	if i < 0 {
		return InvalidVersion, false
	}
	if i == len(s) {
		return v, true
	}
	v.Minor, i = parseVersionPart(s, i)
	if i < 0 {
		return InvalidVersion, false
	}
	if i == len(s) {
		return v, true
	}
	v.Patch, i = parseVersionPart(s, i)
	if i < 0 || i < len(s) {
		return InvalidVersion, false
	}
	return v, true
}

func parseVersionPart(s string, i int) (part int, newi int) {
	dot := i
	for dot < len(s) && s[dot] != '.' {
		dot++
	}
	if dot == i || dot-i > 1 && s[i] == '0' {
		return -1, -1
	}
	for i < len(s) {
		if s[i] < '0' || s[i] > '9' {
			return -1, -1
		}
		part *= 10
		part += int(s[i] - '0')
		i++
		if i+1 < len(s) && s[i] == '.' {
			return part, i+1
		}
	}
	return part, i
}
<commit_msg>Check for integer overflow in parseVersionPart<commit_after>package main

import (
	"fmt"
)

type Version struct {
	Major, Minor, Patch int
}

func (v Version) String() string {
	if v.Major < 0 {
		panic(fmt.Sprintf("cannot stringify invalid version (major is %d)", v.Major))
	}
	if v.Minor < 0 {
		return fmt.Sprintf("v%d", v.Major)
	}
	if v.Patch < 0 {
		return fmt.Sprintf("v%d.%d", v.Major, v.Minor)
	}
	return fmt.Sprintf("v%d.%d.%d", v.Major, v.Minor, v.Patch)
}

func (v Version) Less(other Version) bool {
	if v.Major != other.Major {
		return v.Major < other.Major
	}
	if v.Minor != other.Minor {
		return v.Minor < other.Minor
	}
	return v.Patch < other.Patch
}

func (v Version) Contains(other Version) bool {
	if v.Patch != -1 {
		return v == other
	}
	if v.Minor != -1 {
		return v.Major == other.Major && v.Minor == other.Minor
	}
	return v.Major == other.Major
}

func (v Version) IsValid() bool {
	return v == InvalidVersion
}

var InvalidVersion = Version{-1, -1, -1}

func parseVersion(s string) (Version, bool) {
	if len(s) < 2 {
		return InvalidVersion, false
	}
	if s[0] != 'v' {
		return InvalidVersion, false
	}
	v := Version{-1, -1, -1}
	i := 1
	v.Major, i = parseVersionPart(s, i)
	if i < 0 {
		return InvalidVersion, false
	}
	if i == len(s) {
		return v, true
	}
	v.Minor, i = parseVersionPart(s, i)
	if i < 0 {
		return InvalidVersion, false
	}
	if i == len(s) {
		return v, true
	}
	v.Patch, i = parseVersionPart(s, i)
	if i < 0 || i < len(s) {
		return InvalidVersion, false
	}
	return v, true
}

func parseVersionPart(s string, i int) (part int, newi int) {
	dot := i
	for dot < len(s) && s[dot] != '.' {
		dot++
	}
	if dot == i || dot-i > 1 && s[i] == '0' {
		return -1, -1
	}
	for i < len(s) {
		if s[i] < '0' || s[i] > '9' {
			return -1, -1
		}
		part *= 10
		part += int(s[i] - '0')
		if part < 0 {
			return -1, -1
		}
		i++
		if i+1 < len(s) && s[i] == '.' {
			return part, i + 1
		}
	}
	return part, i
}
<|endoftext|>


<commit_before>package main

func getVersion() string {
	return "v1.1.0-0-gf225c83"
}
<commit_msg>version.go: bump to version v1.1.1-0-g8787f5b<commit_after>package main

func getVersion() string {
	return "v1.1.1-0-g8787f5b"
}
<|endoftext|>


<commit_before>// Licensed to The Moov Authors under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. The Moov Authors licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package ach

// Version Number
const Version = "v1.4.0-rc3"
<commit_msg>release v1.4.0<commit_after>// Licensed to The Moov Authors under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. The Moov Authors licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package ach

// Version Number
const Version = "v1.4.0"
<|endoftext|>


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

package grpc

// Version is the current grpc version.
const Version = "1.39.0-dev"
<commit_msg>Change version to 1.40.0-dev (#4543)<commit_after>/*
 *
 * Copyright 2018 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package grpc

// Version is the current grpc version.
const Version = "1.40.0-dev"
<|endoftext|>


<commit_before>package nsq

const VERSION = "0.1.20"
<commit_msg>bump to 0.1.21<commit_after>package nsq

const VERSION = "0.1.21"
<|endoftext|>


<commit_before>package main

const VERSION = "0.0.3"
<commit_msg>bump version for 0.1.0<commit_after>package main

const VERSION = "0.1.0"
<|endoftext|>


<commit_before>package trdsql

// Version is trdsql version
var Version = `v0.7.0`
<commit_msg>bump version<commit_after>package trdsql

// Version is trdsql version
<|endoftext|>


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

// Package promlint provides a linter for Prometheus metrics.
package promlint

import (
	"fmt"
	"io"
	"regexp"
	"sort"
	"strings"

	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"
)

// A Linter is a Prometheus metrics linter.  It identifies issues with metric
// names, types, and metadata, and reports them to the caller.
type Linter struct {
	r io.Reader
}

// A Problem is an issue detected by a Linter.
type Problem struct {
	// The name of the metric indicated by this Problem.
	Metric string

	// A description of the issue for this Problem.
	Text string
}

// newProblem is helper function to create a Problem.
func newProblem(mf dto.MetricFamily, text string) Problem {
	return Problem{
		Metric: mf.GetName(),
		Text:   text,
	}
}

// New creates a new Linter that reads an input stream of Prometheus metrics.
// Only the Prometheus text exposition format is supported.
func New(r io.Reader) *Linter {
	return &Linter{
		r: r,
	}
}

// Lint performs a linting pass, returning a slice of Problems indicating any
// issues found in the metrics stream.  The slice is sorted by metric name
// and issue description.
func (l *Linter) Lint() ([]Problem, error) {
	// TODO(mdlayher): support for protobuf exposition format?
	d := expfmt.NewDecoder(l.r, expfmt.FmtText)

	var problems []Problem

	var mf dto.MetricFamily
	for {
		if err := d.Decode(&mf); err != nil {
			if err == io.EOF {
				break
			}

			return nil, err
		}

		problems = append(problems, lint(mf)...)
	}

	// Ensure deterministic output.
	sort.SliceStable(problems, func(i, j int) bool {
		if problems[i].Metric == problems[j].Metric {
			return problems[i].Text < problems[j].Text
		}
		return problems[i].Metric < problems[j].Metric
	})

	return problems, nil
}

// lint is the entry point for linting a single metric.
func lint(mf dto.MetricFamily) []Problem {
	fns := []func(mf dto.MetricFamily) []Problem{
		lintHelp,
		lintMetricUnits,
		lintCounter,
		lintHistogramSummaryReserved,
		lintMetricTypeInName,
		lintReservedChars,
		lintCamelCase,
		lintUnitAbbreviations,
	}

	var problems []Problem
	for _, fn := range fns {
		problems = append(problems, fn(mf)...)
	}

	// TODO(mdlayher): lint rules for specific metrics types.
	return problems
}

// lintHelp detects issues related to the help text for a metric.
func lintHelp(mf dto.MetricFamily) []Problem {
	var problems []Problem

	// Expect all metrics to have help text available.
	if mf.Help == nil {
		problems = append(problems, newProblem(mf, "no help text"))
	}

	return problems
}

// lintMetricUnits detects issues with metric unit names.
func lintMetricUnits(mf dto.MetricFamily) []Problem {
	var problems []Problem

	unit, base, ok := metricUnits(*mf.Name)
	if !ok {
		// No known units detected.
		return nil
	}

	// Unit is already a base unit.
	if unit == base {
		return nil
	}

	problems = append(problems, newProblem(mf, fmt.Sprintf("use base unit %q instead of %q", base, unit)))

	return problems
}

// lintCounter detects issues specific to counters, as well as patterns that should
// only be used with counters.
func lintCounter(mf dto.MetricFamily) []Problem {
	var problems []Problem

	isCounter := mf.GetType() == dto.MetricType_COUNTER
	isUntyped := mf.GetType() == dto.MetricType_UNTYPED
	hasTotalSuffix := strings.HasSuffix(mf.GetName(), "_total")

	switch {
	case isCounter && !hasTotalSuffix:
		problems = append(problems, newProblem(mf, `counter metrics should have "_total" suffix`))
	case !isUntyped && !isCounter && hasTotalSuffix:
		problems = append(problems, newProblem(mf, `non-counter metrics should not have "_total" suffix`))
	}

	return problems
}

// lintHistogramSummaryReserved detects when other types of metrics use names or labels
// reserved for use by histograms and/or summaries.
func lintHistogramSummaryReserved(mf dto.MetricFamily) []Problem {
	// These rules do not apply to untyped metrics.
	t := mf.GetType()
	if t == dto.MetricType_UNTYPED {
		return nil
	}

	var problems []Problem

	isHistogram := t == dto.MetricType_HISTOGRAM
	isSummary := t == dto.MetricType_SUMMARY

	n := mf.GetName()

	if !isHistogram && strings.HasSuffix(n, "_bucket") {
		problems = append(problems, newProblem(mf, `non-histogram metrics should not have "_bucket" suffix`))
	}
	if !isHistogram && !isSummary && strings.HasSuffix(n, "_count") {
		problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_count" suffix`))
	}
	if !isHistogram && !isSummary && strings.HasSuffix(n, "_sum") {
		problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_sum" suffix`))
	}

	for _, m := range mf.GetMetric() {
		for _, l := range m.GetLabel() {
			ln := l.GetName()

			if !isHistogram && ln == "le" {
				problems = append(problems, newProblem(mf, `non-histogram metrics should not have "le" label`))
			}
			if !isSummary && ln == "quantile" {
				problems = append(problems, newProblem(mf, `non-summary metrics should not have "quantile" label`))
			}
		}
	}

	return problems
}

// lintMetricTypeInName detects when metric types are included in the metric name.
func lintMetricTypeInName(mf dto.MetricFamily) []Problem {
	var problems []Problem
	n := strings.ToLower(mf.GetName())

	for i, t := range dto.MetricType_name {
		if i == int32(dto.MetricType_UNTYPED) {
			continue
		}

		typename := strings.ToLower(t)
		if strings.Contains(n, "_"+typename+"_") || strings.HasSuffix(n, "_"+typename) {
			problems = append(problems, newProblem(mf, fmt.Sprintf(`metric name should not include type '%s'`, typename)))
		}
	}
	return problems
}

// lintReservedChars detects colons in metric names.
func lintReservedChars(mf dto.MetricFamily) []Problem {
	var problems []Problem
	if strings.Contains(mf.GetName(), ":") {
		problems = append(problems, newProblem(mf, "metric names should not contain ':'"))
	}
	return problems
}

var camelCase = regexp.MustCompile(`[a-z][A-Z]`)

// lintCamelCase detects metric names and label names written in camelCase.
func lintCamelCase(mf dto.MetricFamily) []Problem {
	var problems []Problem
	if camelCase.FindString(mf.GetName()) != "" {
		problems = append(problems, newProblem(mf, "metric names should be written in 'snake_case' not 'camelCase'"))
	}

	for _, m := range mf.GetMetric() {
		for _, l := range m.GetLabel() {
			if camelCase.FindString(l.GetName()) != "" {
				problems = append(problems, newProblem(mf, "label names should be written in 'snake_case' not 'camelCase'"))
			}
		}
	}
	return problems
}

// lintUnitAbbreviations detects abbreviated units in the metric name.
func lintUnitAbbreviations(mf dto.MetricFamily) []Problem {
	var problems []Problem
	n := strings.ToLower(mf.GetName())
	for _, s := range unitAbbreviations {
		if strings.Contains(n, "_"+s+"_") || strings.HasSuffix(n, "_"+s) {
			problems = append(problems, newProblem(mf, "metric names should not contain abbreviated units"))
		}
	}
	return problems
}

// metricUnits attempts to detect known unit types used as part of a metric name,
// e.g. "foo_bytes_total" or "bar_baz_milligrams".
func metricUnits(m string) (unit string, base string, ok bool) {
	ss := strings.Split(m, "_")

	for unit, base := range units {
		// Also check for "no prefix".
		for _, p := range append(unitPrefixes, "") {
			for _, s := range ss {
				// Attempt to explicitly match a known unit with a known prefix,
				// as some words may look like "units" when matching suffix.
				//
				// As an example, "thermometers" should not match "meters", but
				// "kilometers" should.
				if s == p+unit {
					return p + unit, base, true
				}
			}
		}
	}

	return "", "", false
}

// Units and their possible prefixes recognized by this library.  More can be
// added over time as needed.
var (
	// map a unit to the appropriate base unit.
	units = map[string]string{
		// Base units.
		"amperes": "amperes",
		"bytes":   "bytes",
		"celsius": "celsius", // Celsius is more common in practice than Kelvin.
		"grams":   "grams",
		"joules":  "joules",
		"meters":  "meters", // Both American and international spelling permitted.
		"metres":  "metres",
		"seconds": "seconds",
		"volts":   "volts",

		// Non base units.
		// Time.
		"minutes": "seconds",
		"hours":   "seconds",
		"days":    "seconds",
		"weeks":   "seconds",
		// Temperature.
		"kelvin":     "celsius",
		"kelvins":    "celsius",
		"fahrenheit": "celsius",
		"rankine":    "celsius",
		// Length.
		"inches": "meters",
		"yards":  "meters",
		"miles":  "meters",
		// Bytes.
		"bits": "bytes",
		// Energy.
		"calories": "joules",
		// Mass.
		"pounds": "grams",
		"ounces": "grams",
	}

	unitPrefixes = []string{
		"pico",
		"nano",
		"micro",
		"milli",
		"centi",
		"deci",
		"deca",
		"hecto",
		"kilo",
		"kibi",
		"mega",
		"mibi",
		"giga",
		"gibi",
		"tera",
		"tebi",
		"peta",
		"pebi",
	}

	// Common abbreviations that we'd like to discourage.
	unitAbbreviations = []string{
		"s",
		"ms",
		"us",
		"ns",
		"sec",
		"b",
		"kb",
		"mb",
		"gb",
		"tb",
		"pb",
		"m",
		"h",
		"d",
	}
)
<commit_msg>Add the capability to lint MetricFamilies directly<commit_after>// Copyright 2020 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package promlint provides a linter for Prometheus metrics.
package promlint

import (
	"fmt"
	"io"
	"regexp"
	"sort"
	"strings"

	"github.com/prometheus/common/expfmt"

	dto "github.com/prometheus/client_model/go"
)

// A Linter is a Prometheus metrics linter.  It identifies issues with metric
// names, types, and metadata, and reports them to the caller.
type Linter struct {
	r   io.Reader
	mfs []*dto.MetricFamily
}

// A Problem is an issue detected by a Linter.
type Problem struct {
	// The name of the metric indicated by this Problem.
	Metric string

	// A description of the issue for this Problem.
	Text string
}

// newProblem is helper function to create a Problem.
func newProblem(mf *dto.MetricFamily, text string) Problem {
	return Problem{
		Metric: mf.GetName(),
		Text:   text,
	}
}

// New creates a new Linter that reads an input stream of Prometheus metrics in
// the Prometheus text exposition format.
func New(r io.Reader) *Linter {
	return &Linter{
		r: r,
	}
}

// NewWithMetricFamilies creates a new Linter that reads from a slice of
// MetricFamily protobuf messages.
func NewWithMetricFamilies(mfs []*dto.MetricFamily) *Linter {
	return &Linter{
		mfs: mfs,
	}
}

// Lint performs a linting pass, returning a slice of Problems indicating any
// issues found in the metrics stream. The slice is sorted by metric name
// and issue description.
func (l *Linter) Lint() ([]Problem, error) {
	var problems []Problem

	if l.r != nil {
		d := expfmt.NewDecoder(l.r, expfmt.FmtText)

		mf := &dto.MetricFamily{}
		for {
			if err := d.Decode(mf); err != nil {
				if err == io.EOF {
					break
				}

				return nil, err
			}

			problems = append(problems, lint(mf)...)
		}
	}
	for _, mf := range l.mfs {
		problems = append(problems, lint(mf)...)
	}

	// Ensure deterministic output.
	sort.SliceStable(problems, func(i, j int) bool {
		if problems[i].Metric == problems[j].Metric {
			return problems[i].Text < problems[j].Text
		}
		return problems[i].Metric < problems[j].Metric
	})

	return problems, nil
}

// lint is the entry point for linting a single metric.
func lint(mf *dto.MetricFamily) []Problem {
	fns := []func(mf *dto.MetricFamily) []Problem{
		lintHelp,
		lintMetricUnits,
		lintCounter,
		lintHistogramSummaryReserved,
		lintMetricTypeInName,
		lintReservedChars,
		lintCamelCase,
		lintUnitAbbreviations,
	}

	var problems []Problem
	for _, fn := range fns {
		problems = append(problems, fn(mf)...)
	}

	// TODO(mdlayher): lint rules for specific metrics types.
	return problems
}

// lintHelp detects issues related to the help text for a metric.
func lintHelp(mf *dto.MetricFamily) []Problem {
	var problems []Problem

	// Expect all metrics to have help text available.
	if mf.Help == nil {
		problems = append(problems, newProblem(mf, "no help text"))
	}

	return problems
}

// lintMetricUnits detects issues with metric unit names.
func lintMetricUnits(mf *dto.MetricFamily) []Problem {
	var problems []Problem

	unit, base, ok := metricUnits(*mf.Name)
	if !ok {
		// No known units detected.
		return nil
	}

	// Unit is already a base unit.
	if unit == base {
		return nil
	}

	problems = append(problems, newProblem(mf, fmt.Sprintf("use base unit %q instead of %q", base, unit)))

	return problems
}

// lintCounter detects issues specific to counters, as well as patterns that should
// only be used with counters.
func lintCounter(mf *dto.MetricFamily) []Problem {
	var problems []Problem

	isCounter := mf.GetType() == dto.MetricType_COUNTER
	isUntyped := mf.GetType() == dto.MetricType_UNTYPED
	hasTotalSuffix := strings.HasSuffix(mf.GetName(), "_total")

	switch {
	case isCounter && !hasTotalSuffix:
		problems = append(problems, newProblem(mf, `counter metrics should have "_total" suffix`))
	case !isUntyped && !isCounter && hasTotalSuffix:
		problems = append(problems, newProblem(mf, `non-counter metrics should not have "_total" suffix`))
	}

	return problems
}

// lintHistogramSummaryReserved detects when other types of metrics use names or labels
// reserved for use by histograms and/or summaries.
func lintHistogramSummaryReserved(mf *dto.MetricFamily) []Problem {
	// These rules do not apply to untyped metrics.
	t := mf.GetType()
	if t == dto.MetricType_UNTYPED {
		return nil
	}

	var problems []Problem

	isHistogram := t == dto.MetricType_HISTOGRAM
	isSummary := t == dto.MetricType_SUMMARY

	n := mf.GetName()

	if !isHistogram && strings.HasSuffix(n, "_bucket") {
		problems = append(problems, newProblem(mf, `non-histogram metrics should not have "_bucket" suffix`))
	}
	if !isHistogram && !isSummary && strings.HasSuffix(n, "_count") {
		problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_count" suffix`))
	}
	if !isHistogram && !isSummary && strings.HasSuffix(n, "_sum") {
		problems = append(problems, newProblem(mf, `non-histogram and non-summary metrics should not have "_sum" suffix`))
	}

	for _, m := range mf.GetMetric() {
		for _, l := range m.GetLabel() {
			ln := l.GetName()

			if !isHistogram && ln == "le" {
				problems = append(problems, newProblem(mf, `non-histogram metrics should not have "le" label`))
			}
			if !isSummary && ln == "quantile" {
				problems = append(problems, newProblem(mf, `non-summary metrics should not have "quantile" label`))
			}
		}
	}

	return problems
}

// lintMetricTypeInName detects when metric types are included in the metric name.
func lintMetricTypeInName(mf *dto.MetricFamily) []Problem {
	var problems []Problem
	n := strings.ToLower(mf.GetName())

	for i, t := range dto.MetricType_name {
		if i == int32(dto.MetricType_UNTYPED) {
			continue
		}

		typename := strings.ToLower(t)
		if strings.Contains(n, "_"+typename+"_") || strings.HasSuffix(n, "_"+typename) {
			problems = append(problems, newProblem(mf, fmt.Sprintf(`metric name should not include type '%s'`, typename)))
		}
	}
	return problems
}

// lintReservedChars detects colons in metric names.
func lintReservedChars(mf *dto.MetricFamily) []Problem {
	var problems []Problem
	if strings.Contains(mf.GetName(), ":") {
		problems = append(problems, newProblem(mf, "metric names should not contain ':'"))
	}
	return problems
}

var camelCase = regexp.MustCompile(`[a-z][A-Z]`)

// lintCamelCase detects metric names and label names written in camelCase.
func lintCamelCase(mf *dto.MetricFamily) []Problem {
	var problems []Problem
	if camelCase.FindString(mf.GetName()) != "" {
		problems = append(problems, newProblem(mf, "metric names should be written in 'snake_case' not 'camelCase'"))
	}

	for _, m := range mf.GetMetric() {
		for _, l := range m.GetLabel() {
			if camelCase.FindString(l.GetName()) != "" {
				problems = append(problems, newProblem(mf, "label names should be written in 'snake_case' not 'camelCase'"))
			}
		}
	}
	return problems
}

// lintUnitAbbreviations detects abbreviated units in the metric name.
func lintUnitAbbreviations(mf *dto.MetricFamily) []Problem {
	var problems []Problem
	n := strings.ToLower(mf.GetName())
	for _, s := range unitAbbreviations {
		if strings.Contains(n, "_"+s+"_") || strings.HasSuffix(n, "_"+s) {
			problems = append(problems, newProblem(mf, "metric names should not contain abbreviated units"))
		}
	}
	return problems
}

// metricUnits attempts to detect known unit types used as part of a metric name,
// e.g. "foo_bytes_total" or "bar_baz_milligrams".
func metricUnits(m string) (unit string, base string, ok bool) {
	ss := strings.Split(m, "_")

	for unit, base := range units {
		// Also check for "no prefix".
		for _, p := range append(unitPrefixes, "") {
			for _, s := range ss {
				// Attempt to explicitly match a known unit with a known prefix,
				// as some words may look like "units" when matching suffix.
				//
				// As an example, "thermometers" should not match "meters", but
				// "kilometers" should.
				if s == p+unit {
					return p + unit, base, true
				}
			}
		}
	}

	return "", "", false
}

// Units and their possible prefixes recognized by this library.  More can be
// added over time as needed.
var (
	// map a unit to the appropriate base unit.
	units = map[string]string{
		// Base units.
		"amperes": "amperes",
		"bytes":   "bytes",
		"celsius": "celsius", // Celsius is more common in practice than Kelvin.
		"grams":   "grams",
		"joules":  "joules",
		"meters":  "meters", // Both American and international spelling permitted.
		"metres":  "metres",
		"seconds": "seconds",
		"volts":   "volts",

		// Non base units.
		// Time.
		"minutes": "seconds",
		"hours":   "seconds",
		"days":    "seconds",
		"weeks":   "seconds",
		// Temperature.
		"kelvin":     "celsius",
		"kelvins":    "celsius",
		"fahrenheit": "celsius",
		"rankine":    "celsius",
		// Length.
		"inches": "meters",
		"yards":  "meters",
		"miles":  "meters",
		// Bytes.
		"bits": "bytes",
		// Energy.
		"calories": "joules",
		// Mass.
		"pounds": "grams",
		"ounces": "grams",
	}

	unitPrefixes = []string{
		"pico",
		"nano",
		"micro",
		"milli",
		"centi",
		"deci",
		"deca",
		"hecto",
		"kilo",
		"kibi",
		"mega",
		"mibi",
		"giga",
		"gibi",
		"tera",
		"tebi",
		"peta",
		"pebi",
	}

	// Common abbreviations that we'd like to discourage.
	unitAbbreviations = []string{
		"s",
		"ms",
		"us",
		"ns",
		"sec",
		"b",
		"kb",
		"mb",
		"gb",
		"tb",
		"pb",
		"m",
		"h",
		"d",
	}
)
<|endoftext|>


<commit_before>package ansiblelocal

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/common/uuid"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

const DefaultStagingDir = "/tmp/packer-provisioner-ansible-local"

type Config struct {
	common.PackerConfig `mapstructure:",squash"`
	ctx                 interpolate.Context

	// The command to run ansible
	Command string

	// Extra options to pass to the ansible command
	ExtraArguments []string `mapstructure:"extra_arguments"`

	// Path to group_vars directory
	GroupVars string `mapstructure:"group_vars"`

	// Path to host_vars directory
	HostVars string `mapstructure:"host_vars"`

	// The playbook dir to upload.
	PlaybookDir string `mapstructure:"playbook_dir"`

	// The main playbook file to execute.
	PlaybookFile string `mapstructure:"playbook_file"`

	// An array of local paths of playbook files to upload.
	PlaybookPaths []string `mapstructure:"playbook_paths"`

	// An array of local paths of roles to upload.
	RolePaths []string `mapstructure:"role_paths"`

	// The directory where files will be uploaded. Packer requires write
	// permissions in this directory.
	StagingDir string `mapstructure:"staging_directory"`

	// The optional inventory file
	InventoryFile string `mapstructure:"inventory_file"`

	// The optional inventory groups
	InventoryGroups []string `mapstructure:"inventory_groups"`

	// The optional ansible-galaxy requirements file
	GalaxyFile string `mapstructure:"galaxy_file"`

	// The command to run ansible-galaxy
	GalaxyCommand string
}

type Provisioner struct {
	config Config
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	// Defaults
	if p.config.Command == "" {
		p.config.Command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook"
	}
	if p.config.GalaxyCommand == "" {
		p.config.GalaxyCommand = "ansible-galaxy"
	}

	if p.config.StagingDir == "" {
		p.config.StagingDir = filepath.Join(DefaultStagingDir, uuid.TimeOrderedUUID())
	}

	// Validation
	var errs *packer.MultiError
	err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true)
	if err != nil {
		errs = packer.MultiErrorAppend(errs, err)
	}

	// Check that the inventory file exists, if configured
	if len(p.config.InventoryFile) > 0 {
		err = validateFileConfig(p.config.InventoryFile, "inventory_file", true)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the galaxy file exists, if configured
	if len(p.config.GalaxyFile) > 0 {
		err = validateFileConfig(p.config.GalaxyFile, "galaxy_file", true)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the playbook_dir directory exists, if configured
	if len(p.config.PlaybookDir) > 0 {
		if err := validateDirConfig(p.config.PlaybookDir, "playbook_dir"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the group_vars directory exists, if configured
	if len(p.config.GroupVars) > 0 {
		if err := validateDirConfig(p.config.GroupVars, "group_vars"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the host_vars directory exists, if configured
	if len(p.config.HostVars) > 0 {
		if err := validateDirConfig(p.config.HostVars, "host_vars"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	for _, path := range p.config.PlaybookPaths {
		err := validateDirConfig(path, "playbook_paths")
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}
	for _, path := range p.config.RolePaths {
		if err := validateDirConfig(path, "role_paths"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}
	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	ui.Say("Provisioning with Ansible...")

	if len(p.config.PlaybookDir) > 0 {
		ui.Message("Uploading Playbook directory to Ansible staging directory...")
		if err := p.uploadDir(ui, comm, p.config.StagingDir, p.config.PlaybookDir); err != nil {
			return fmt.Errorf("Error uploading playbook_dir directory: %s", err)
		}
	} else {
		ui.Message("Creating Ansible staging directory...")
		if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
			return fmt.Errorf("Error creating staging directory: %s", err)
		}
	}

	ui.Message("Uploading main Playbook file...")
	src := p.config.PlaybookFile
	dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
	if err := p.uploadFile(ui, comm, dst, src); err != nil {
		return fmt.Errorf("Error uploading main playbook: %s", err)
	}

	if len(p.config.InventoryFile) == 0 {
		tf, err := ioutil.TempFile("", "packer-provisioner-ansible-local")
		if err != nil {
			return fmt.Errorf("Error preparing inventory file: %s", err)
		}
		defer os.Remove(tf.Name())
		if len(p.config.InventoryGroups) != 0 {
			content := ""
			for _, group := range p.config.InventoryGroups {
				content += fmt.Sprintf("[%s]\n127.0.0.1\n", group)
			}
			_, err = tf.Write([]byte(content))
		} else {
			_, err = tf.Write([]byte("127.0.0.1"))
		}
		if err != nil {
			tf.Close()
			return fmt.Errorf("Error preparing inventory file: %s", err)
		}
		tf.Close()
		p.config.InventoryFile = tf.Name()
		defer func() {
			p.config.InventoryFile = ""
		}()
	}

	if len(p.config.GalaxyFile) > 0 {
		ui.Message("Uploading galaxy file...")
		src = p.config.GalaxyFile
		dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
		if err := p.uploadFile(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading galaxy file: %s", err)
		}
	}

	ui.Message("Uploading inventory file...")
	src = p.config.InventoryFile
	dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
	if err := p.uploadFile(ui, comm, dst, src); err != nil {
		return fmt.Errorf("Error uploading inventory file: %s", err)
	}

	if len(p.config.GroupVars) > 0 {
		ui.Message("Uploading group_vars directory...")
		src := p.config.GroupVars
		dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "group_vars"))
		if err := p.uploadDir(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading group_vars directory: %s", err)
		}
	}

	if len(p.config.HostVars) > 0 {
		ui.Message("Uploading host_vars directory...")
		src := p.config.HostVars
		dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "host_vars"))
		if err := p.uploadDir(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading host_vars directory: %s", err)
		}
	}

	if len(p.config.RolePaths) > 0 {
		ui.Message("Uploading role directories...")
		for _, src := range p.config.RolePaths {
			dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles", filepath.Base(src)))
			if err := p.uploadDir(ui, comm, dst, src); err != nil {
				return fmt.Errorf("Error uploading roles: %s", err)
			}
		}
	}

	if len(p.config.PlaybookPaths) > 0 {
		ui.Message("Uploading additional Playbooks...")
		playbookDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "playbooks"))
		if err := p.createDir(ui, comm, playbookDir); err != nil {
			return fmt.Errorf("Error creating playbooks directory: %s", err)
		}
		for _, src := range p.config.PlaybookPaths {
			dst := filepath.ToSlash(filepath.Join(playbookDir, filepath.Base(src)))
			if err := p.uploadDir(ui, comm, dst, src); err != nil {
				return fmt.Errorf("Error uploading playbooks: %s", err)
			}
		}
	}

	if err := p.executeAnsible(ui, comm); err != nil {
		return fmt.Errorf("Error executing Ansible: %s", err)
	}
	return nil
}

func (p *Provisioner) Cancel() {
	// Just hard quit. It isn't a big deal if what we're doing keeps
	// running on the other side.
	os.Exit(0)
}

func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error {
	rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles"))
	galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile)))

	// ansible-galaxy install -r requirements.yml -p roles/
	command := fmt.Sprintf("cd %s && %s install -r %s -p %s",
		p.config.StagingDir, p.config.GalaxyCommand, galaxyFile, rolesDir)
	ui.Message(fmt.Sprintf("Executing Ansible Galaxy: %s", command))
	cmd := &packer.RemoteCmd{
		Command: command,
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		// ansible-galaxy version 2.0.0.2 doesn't return exit codes on error..
		return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
	}
	return nil
}

func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) error {
	playbook := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile)))
	inventory := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile)))

	extraArgs := ""
	if len(p.config.ExtraArguments) > 0 {
		extraArgs = " " + strings.Join(p.config.ExtraArguments, " ")
	}

	// Fetch external dependencies
	if len(p.config.GalaxyFile) > 0 {
		if err := p.executeGalaxy(ui, comm); err != nil {
			return fmt.Errorf("Error executing Ansible Galaxy: %s", err)
		}
	}

	command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s",
		p.config.StagingDir, p.config.Command, playbook, extraArgs, inventory)
	ui.Message(fmt.Sprintf("Executing Ansible: %s", command))
	cmd := &packer.RemoteCmd{
		Command: command,
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		if cmd.ExitStatus == 127 {
			return fmt.Errorf("%s could not be found. Verify that it is available on the\n"+
				"PATH after connecting to the machine.",
				p.config.Command)
		}

		return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
	}
	return nil
}

func validateDirConfig(path string, config string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("%s: %s is invalid: %s", config, path, err)
	} else if !info.IsDir() {
		return fmt.Errorf("%s: %s must point to a directory", config, path)
	}
	return nil
}

func validateFileConfig(name string, config string, req bool) error {
	if req {
		if name == "" {
			return fmt.Errorf("%s must be specified.", config)
		}
	}
	info, err := os.Stat(name)
	if err != nil {
		return fmt.Errorf("%s: %s is invalid: %s", config, name, err)
	} else if info.IsDir() {
		return fmt.Errorf("%s: %s must point to a file", config, name)
	}
	return nil
}

func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, src string) error {
	f, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("Error opening: %s", err)
	}
	defer f.Close()

	if err = comm.Upload(dst, f, nil); err != nil {
		return fmt.Errorf("Error uploading %s: %s", src, err)
	}
	return nil
}

func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
	ui.Message(fmt.Sprintf("Creating directory: %s", dir))
	cmd := &packer.RemoteCmd{
		Command: fmt.Sprintf("mkdir -p '%s'", dir),
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		return fmt.Errorf("Non-zero exit status.")
	}
	return nil
}

func (p *Provisioner) uploadDir(ui packer.Ui, comm packer.Communicator, dst, src string) error {
	if err := p.createDir(ui, comm, dst); err != nil {
		return err
	}

	// Make sure there is a trailing "/" so that the directory isn't
	// created on the other side.
	if src[len(src)-1] != '/' {
		src = src + "/"
	}
	return comm.UploadDir(dst, src, nil)
}
<commit_msg>provisioner/ansible-local: Convert Windows paths with backslashes to Unix style<commit_after>package ansiblelocal

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/common/uuid"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/template/interpolate"
)

const DefaultStagingDir = "/tmp/packer-provisioner-ansible-local"

type Config struct {
	common.PackerConfig `mapstructure:",squash"`
	ctx                 interpolate.Context

	// The command to run ansible
	Command string

	// Extra options to pass to the ansible command
	ExtraArguments []string `mapstructure:"extra_arguments"`

	// Path to group_vars directory
	GroupVars string `mapstructure:"group_vars"`

	// Path to host_vars directory
	HostVars string `mapstructure:"host_vars"`

	// The playbook dir to upload.
	PlaybookDir string `mapstructure:"playbook_dir"`

	// The main playbook file to execute.
	PlaybookFile string `mapstructure:"playbook_file"`

	// An array of local paths of playbook files to upload.
	PlaybookPaths []string `mapstructure:"playbook_paths"`

	// An array of local paths of roles to upload.
	RolePaths []string `mapstructure:"role_paths"`

	// The directory where files will be uploaded. Packer requires write
	// permissions in this directory.
	StagingDir string `mapstructure:"staging_directory"`

	// The optional inventory file
	InventoryFile string `mapstructure:"inventory_file"`

	// The optional inventory groups
	InventoryGroups []string `mapstructure:"inventory_groups"`

	// The optional ansible-galaxy requirements file
	GalaxyFile string `mapstructure:"galaxy_file"`

	// The command to run ansible-galaxy
	GalaxyCommand string
}

type Provisioner struct {
	config Config
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	// Defaults
	if p.config.Command == "" {
		p.config.Command = "ANSIBLE_FORCE_COLOR=1 PYTHONUNBUFFERED=1 ansible-playbook"
	}
	if p.config.GalaxyCommand == "" {
		p.config.GalaxyCommand = "ansible-galaxy"
	}

	if p.config.StagingDir == "" {
		p.config.StagingDir = filepath.ToSlash(filepath.Join(DefaultStagingDir, uuid.TimeOrderedUUID()))
	}

	// Validation
	var errs *packer.MultiError
	err = validateFileConfig(p.config.PlaybookFile, "playbook_file", true)
	if err != nil {
		errs = packer.MultiErrorAppend(errs, err)
	}

	// Check that the inventory file exists, if configured
	if len(p.config.InventoryFile) > 0 {
		err = validateFileConfig(p.config.InventoryFile, "inventory_file", true)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the galaxy file exists, if configured
	if len(p.config.GalaxyFile) > 0 {
		err = validateFileConfig(p.config.GalaxyFile, "galaxy_file", true)
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the playbook_dir directory exists, if configured
	if len(p.config.PlaybookDir) > 0 {
		if err := validateDirConfig(p.config.PlaybookDir, "playbook_dir"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the group_vars directory exists, if configured
	if len(p.config.GroupVars) > 0 {
		if err := validateDirConfig(p.config.GroupVars, "group_vars"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	// Check that the host_vars directory exists, if configured
	if len(p.config.HostVars) > 0 {
		if err := validateDirConfig(p.config.HostVars, "host_vars"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	for _, path := range p.config.PlaybookPaths {
		err := validateDirConfig(path, "playbook_paths")
		if err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}
	for _, path := range p.config.RolePaths {
		if err := validateDirConfig(path, "role_paths"); err != nil {
			errs = packer.MultiErrorAppend(errs, err)
		}
	}

	if errs != nil && len(errs.Errors) > 0 {
		return errs
	}
	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
	ui.Say("Provisioning with Ansible...")

	if len(p.config.PlaybookDir) > 0 {
		ui.Message("Uploading Playbook directory to Ansible staging directory...")
		if err := p.uploadDir(ui, comm, p.config.StagingDir, p.config.PlaybookDir); err != nil {
			return fmt.Errorf("Error uploading playbook_dir directory: %s", err)
		}
	} else {
		ui.Message("Creating Ansible staging directory...")
		if err := p.createDir(ui, comm, p.config.StagingDir); err != nil {
			return fmt.Errorf("Error creating staging directory: %s", err)
		}
	}

	ui.Message("Uploading main Playbook file...")
	src := p.config.PlaybookFile
	dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
	if err := p.uploadFile(ui, comm, dst, src); err != nil {
		return fmt.Errorf("Error uploading main playbook: %s", err)
	}

	if len(p.config.InventoryFile) == 0 {
		tf, err := ioutil.TempFile("", "packer-provisioner-ansible-local")
		if err != nil {
			return fmt.Errorf("Error preparing inventory file: %s", err)
		}
		defer os.Remove(tf.Name())
		if len(p.config.InventoryGroups) != 0 {
			content := ""
			for _, group := range p.config.InventoryGroups {
				content += fmt.Sprintf("[%s]\n127.0.0.1\n", group)
			}
			_, err = tf.Write([]byte(content))
		} else {
			_, err = tf.Write([]byte("127.0.0.1"))
		}
		if err != nil {
			tf.Close()
			return fmt.Errorf("Error preparing inventory file: %s", err)
		}
		tf.Close()
		p.config.InventoryFile = tf.Name()
		defer func() {
			p.config.InventoryFile = ""
		}()
	}

	if len(p.config.GalaxyFile) > 0 {
		ui.Message("Uploading galaxy file...")
		src = p.config.GalaxyFile
		dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
		if err := p.uploadFile(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading galaxy file: %s", err)
		}
	}

	ui.Message("Uploading inventory file...")
	src = p.config.InventoryFile
	dst = filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(src)))
	if err := p.uploadFile(ui, comm, dst, src); err != nil {
		return fmt.Errorf("Error uploading inventory file: %s", err)
	}

	if len(p.config.GroupVars) > 0 {
		ui.Message("Uploading group_vars directory...")
		src := p.config.GroupVars
		dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "group_vars"))
		if err := p.uploadDir(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading group_vars directory: %s", err)
		}
	}

	if len(p.config.HostVars) > 0 {
		ui.Message("Uploading host_vars directory...")
		src := p.config.HostVars
		dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "host_vars"))
		if err := p.uploadDir(ui, comm, dst, src); err != nil {
			return fmt.Errorf("Error uploading host_vars directory: %s", err)
		}
	}

	if len(p.config.RolePaths) > 0 {
		ui.Message("Uploading role directories...")
		for _, src := range p.config.RolePaths {
			dst := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles", filepath.Base(src)))
			if err := p.uploadDir(ui, comm, dst, src); err != nil {
				return fmt.Errorf("Error uploading roles: %s", err)
			}
		}
	}

	if len(p.config.PlaybookPaths) > 0 {
		ui.Message("Uploading additional Playbooks...")
		playbookDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "playbooks"))
		if err := p.createDir(ui, comm, playbookDir); err != nil {
			return fmt.Errorf("Error creating playbooks directory: %s", err)
		}
		for _, src := range p.config.PlaybookPaths {
			dst := filepath.ToSlash(filepath.Join(playbookDir, filepath.Base(src)))
			if err := p.uploadDir(ui, comm, dst, src); err != nil {
				return fmt.Errorf("Error uploading playbooks: %s", err)
			}
		}
	}

	if err := p.executeAnsible(ui, comm); err != nil {
		return fmt.Errorf("Error executing Ansible: %s", err)
	}
	return nil
}

func (p *Provisioner) Cancel() {
	// Just hard quit. It isn't a big deal if what we're doing keeps
	// running on the other side.
	os.Exit(0)
}

func (p *Provisioner) executeGalaxy(ui packer.Ui, comm packer.Communicator) error {
	rolesDir := filepath.ToSlash(filepath.Join(p.config.StagingDir, "roles"))
	galaxyFile := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.GalaxyFile)))

	// ansible-galaxy install -r requirements.yml -p roles/
	command := fmt.Sprintf("cd %s && %s install -r %s -p %s",
		p.config.StagingDir, p.config.GalaxyCommand, galaxyFile, rolesDir)
	ui.Message(fmt.Sprintf("Executing Ansible Galaxy: %s", command))
	cmd := &packer.RemoteCmd{
		Command: command,
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		// ansible-galaxy version 2.0.0.2 doesn't return exit codes on error..
		return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
	}
	return nil
}

func (p *Provisioner) executeAnsible(ui packer.Ui, comm packer.Communicator) error {
	playbook := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.PlaybookFile)))
	inventory := filepath.ToSlash(filepath.Join(p.config.StagingDir, filepath.Base(p.config.InventoryFile)))

	extraArgs := ""
	if len(p.config.ExtraArguments) > 0 {
		extraArgs = " " + strings.Join(p.config.ExtraArguments, " ")
	}

	// Fetch external dependencies
	if len(p.config.GalaxyFile) > 0 {
		if err := p.executeGalaxy(ui, comm); err != nil {
			return fmt.Errorf("Error executing Ansible Galaxy: %s", err)
		}
	}

	command := fmt.Sprintf("cd %s && %s %s%s -c local -i %s",
		p.config.StagingDir, p.config.Command, playbook, extraArgs, inventory)
	ui.Message(fmt.Sprintf("Executing Ansible: %s", command))
	cmd := &packer.RemoteCmd{
		Command: command,
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		if cmd.ExitStatus == 127 {
			return fmt.Errorf("%s could not be found. Verify that it is available on the\n"+
				"PATH after connecting to the machine.",
				p.config.Command)
		}

		return fmt.Errorf("Non-zero exit status: %d", cmd.ExitStatus)
	}
	return nil
}

func validateDirConfig(path string, config string) error {
	info, err := os.Stat(path)
	if err != nil {
		return fmt.Errorf("%s: %s is invalid: %s", config, path, err)
	} else if !info.IsDir() {
		return fmt.Errorf("%s: %s must point to a directory", config, path)
	}
	return nil
}

func validateFileConfig(name string, config string, req bool) error {
	if req {
		if name == "" {
			return fmt.Errorf("%s must be specified.", config)
		}
	}
	info, err := os.Stat(name)
	if err != nil {
		return fmt.Errorf("%s: %s is invalid: %s", config, name, err)
	} else if info.IsDir() {
		return fmt.Errorf("%s: %s must point to a file", config, name)
	}
	return nil
}

func (p *Provisioner) uploadFile(ui packer.Ui, comm packer.Communicator, dst, src string) error {
	f, err := os.Open(src)
	if err != nil {
		return fmt.Errorf("Error opening: %s", err)
	}
	defer f.Close()

	if err = comm.Upload(dst, f, nil); err != nil {
		return fmt.Errorf("Error uploading %s: %s", src, err)
	}
	return nil
}

func (p *Provisioner) createDir(ui packer.Ui, comm packer.Communicator, dir string) error {
	ui.Message(fmt.Sprintf("Creating directory: %s", dir))
	cmd := &packer.RemoteCmd{
		Command: fmt.Sprintf("mkdir -p '%s'", dir),
	}
	if err := cmd.StartWithUi(comm, ui); err != nil {
		return err
	}
	if cmd.ExitStatus != 0 {
		return fmt.Errorf("Non-zero exit status.")
	}
	return nil
}

func (p *Provisioner) uploadDir(ui packer.Ui, comm packer.Communicator, dst, src string) error {
	if err := p.createDir(ui, comm, dst); err != nil {
		return err
	}

	// Make sure there is a trailing "/" so that the directory isn't
	// created on the other side.
	if src[len(src)-1] != '/' {
		src = src + "/"
	}
	return comm.UploadDir(dst, src, nil)
}
<|endoftext|>


<commit_before>package builder

// nginx
const (
	NginxVersion           = "1.17.3"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.43"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.0.2s"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "2.9.2"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.15.8.2"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.2"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<commit_msg>bumped openssl versin to 1.0.2t.<commit_after>package builder

// nginx
const (
	NginxVersion           = "1.17.3"
	NginxDownloadURLPrefix = "https://nginx.org/download"
)

// pcre
const (
	PcreVersion           = "8.43"
	PcreDownloadURLPrefix = "https://ftp.pcre.org/pub/pcre"
)

// openssl
const (
	OpenSSLVersion           = "1.0.2t"
	OpenSSLDownloadURLPrefix = "https://www.openssl.org/source"
)

// libressl
const (
	LibreSSLVersion           = "2.9.2"
	LibreSSLDownloadURLPrefix = "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL"
)

// zlib
const (
	ZlibVersion           = "1.2.11"
	ZlibDownloadURLPrefix = "https://zlib.net/fossils"
)

// openResty
const (
	OpenRestyVersion           = "1.15.8.2"
	OpenRestyDownloadURLPrefix = "https://openresty.org/download"
)

// tengine
const (
	TengineVersion           = "2.3.2"
	TengineDownloadURLPrefix = "https://tengine.taobao.org/download"
)

// component enumerations
const (
	ComponentNginx = iota
	ComponentOpenResty
	ComponentTengine
	ComponentPcre
	ComponentOpenSSL
	ComponentLibreSSL
	ComponentZlib
	ComponentMax
)
<|endoftext|>


<commit_before>package schego

import (
	"strings"
	"testing"
)

type DummyConsole struct {
	consoleOutput string
}

func (d *DummyConsole) Write(line string) {
	// trim null
	d.consoleOutput = strings.TrimRight(line, "\x00")
}

func TestHelloWorld(t *testing.T) {
	opcodes := []byte{
		0x05, // pushs
		0x48, // H
		0x65, // e
		0x6C, // l
		0x6C, // l
		0x6F, // o
		0x2C, // ,
		0x20, // space
		0x57, // W
		0x6F, // o
		0x72, // r
		0x6C, // l
		0x64, // d
		0x21, // !
		0x0A, // \n
		0x00, // null
		0x43, // syscall
		0x05, // print string
		0x03, // pushi
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00, // 0
		0x43, // syscall
		0x06, // exit
	}
	console := DummyConsole{}
	retcode := RunVM(opcodes, &console)
	if retcode != 0 {
		t.Error("Expected return code of 0, got %d\n", retcode)
	}
	if console.consoleOutput != "Hello, World!\n" {
		t.Error("Incorrect output, got: ", console.consoleOutput)
	}
}
<commit_msg>Fix minor formatting issues in vm.go and vm_test.go<commit_after>package schego

import (
	"strings"
	"testing"
)

type DummyConsole struct {
	consoleOutput string
}

func (d *DummyConsole) Write(line string) {
	// trim null
	d.consoleOutput = strings.TrimRight(line, "\x00")
}

func TestHelloWorld(t *testing.T) {
	opcodes := []byte{
		0x05, // pushs
		0x48, // H
		0x65, // e
		0x6C, // l
		0x6C, // l
		0x6F, // o
		0x2C, // ,
		0x20, // space
		0x57, // W
		0x6F, // o
		0x72, // r
		0x6C, // l
		0x64, // d
		0x21, // !
		0x0A, // \n
		0x00, // null
		0x43, // syscall
		0x05, // print string
		0x03, // pushi
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00,
		0x00, // 0
		0x43, // syscall
		0x06, // exit
	}
	console := DummyConsole{}
	retcode := RunVM(opcodes, &console)
	if retcode != 0 {
		t.Error("Expected return code of 0, got:\n", retcode)
	}
	if console.consoleOutput != "Hello, World!\n" {
		t.Error("Incorrect output, got: ", console.consoleOutput)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package template

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"

	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/config"
	"github.com/kelseyhightower/confd/log"
	"github.com/kelseyhightower/confd/node"
)

var replacer = strings.NewReplacer("/", "_")

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest        string
	FileMode    os.FileMode
	Gid         int
	Keys        []string
	Mode        string
	Uid         int
	ReloadCmd   string `toml:"reload_cmd"`
	CheckCmd    string `toml:"check_cmd"`
	StageFile   *os.File
	Src         string
	Vars        map[string]interface{}
	Dirs        node.Directory
	storeClient StoreClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied StoreClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, s StoreClient) (*TemplateResource, error) {
	if s == nil {
		return nil, errors.New("A valid StoreClient is required.")
	}
	var tc *TemplateResourceConfig
	log.Debug("Loading template resource from " + path)
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.storeClient = s
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	log.Debug("Retrieving keys from store")
	log.Debug("Key prefix set to " + config.Prefix())
	vars, err := t.storeClient.GetValues(appendPrefix(config.Prefix(), t.Keys))
	if err != nil {
		return err
	}
	t.setDirs(vars)
	t.Vars = cleanKeys(vars, config.Prefix())
	return nil
}

// setDirs sets the Dirs for the template resource.
// All keys are grouped based on their directory path names.
// For example, /upstream/app1 and upstream/app2 will be grouped as
//	{
//		"upstream": []Node{
//			{"app1": value}},
//			{"app2": value}},
//		 }
//	}
//
// Dirs are exposed to resource templated to enable iteration.
func (t *TemplateResource) setDirs(vars map[string]interface{}) {
	d := node.NewDirectory()
	for k, v := range vars {
		directory := filepath.Dir(filepath.Join("/", strings.TrimPrefix(k, config.Prefix())))
		d.Add(pathToKey(directory, config.Prefix()), node.Node{filepath.Base(k), v})
	}
	t.Dirs = d
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(config.TemplateDir(), t.Src)
	log.Debug("Using source template " + t.Src)
	if !isFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	// create TempFile in Dest directory to avoid cross-filesystem issues
	temp, err := ioutil.TempFile(filepath.Dir(t.Dest), "."+filepath.Base(t.Dest))
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
	defer temp.Close()
	log.Debug("Compiling source template " + t.Src)
	tplFuncMap := make(template.FuncMap)
	tplFuncMap["Base"] = path.Base

	tplFuncMap["GetDir"] = t.Dirs.Get
	tplFuncMap["MapDir"] = mapNodes
	tplFuncMap["GetEnv"] = os.Getenv
	tmpl := template.Must(template.New(path.Base(t.Src)).Funcs(tplFuncMap).ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	log.Debug("Comparing candidate config to " + t.Dest)
	ok, err := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if config.Noop() {
		log.Warning("Noop mode enabled " + t.Dest + " will not be modified")
		return nil
	}
	if !ok {
		log.Info("Target config " + t.Dest + " out of sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		log.Debug("Overwriting target config " + t.Dest)
		if err := os.Rename(staged, t.Dest); err != nil {
			return err
		}
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
		log.Info("Target config " + t.Dest + " has been updated")
	} else {
		log.Info("Target config " + t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	log.Debug("Running " + t.ReloadCmd)
	c := exec.Command("/bin/sh", "-c", t.ReloadCmd)
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !isFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It returns a list of errors if any.
func ProcessTemplateResources(s StoreClient) []error {
	runErrors := make([]error, 0)
	var err error
	if s == nil {
		runErrors = append(runErrors, errors.New("A StoreClient client is required"))
		return runErrors
	}
	log.Debug("Loading template resources from confdir " + config.ConfDir())
	if !isFileExist(config.ConfDir()) {
		log.Warning(fmt.Sprintf("Cannot load template resources confdir '%s' does not exist", config.ConfDir()))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(config.ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		log.Debug("Processing template resource " + p)
		tplFuncMap := make(template.FuncMap)
		tplFuncMap["Base"] = path.Base
		tplFuncMap["GetEnv"] = os.Getenv
		data := make(map[string]string)
		data["file"] = path.Base(p)
		data["src"] = "{{ .src }}"
		data["dest"] = "{{ .dest }}"
		resTemp, terr := template.New(path.Base(p)).Funcs(tplFuncMap).ParseFiles(p)
		if terr != nil {
			panic(terr)
		}
		var doc bytes.Buffer
		resTemp.Execute(&doc, data)
		docs := doc.String()
		var tc *TemplateResourceConfig
		log.Debug(fmt.Sprintf("Resource parsed as: %s", docs))
		_, err := toml.Decode(docs, &tc)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		tc.TemplateResource.storeClient = s
		if err := tc.TemplateResource.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		log.Debug("Processing of template resource " + p + " complete")
	}
	return runErrors
}
<commit_msg>post-parsing reload command<commit_after>// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
// Use of this source code is governed by the Apache License, Version 2.0
// that can be found in the LICENSE file.
package template

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"text/template"

	"github.com/BurntSushi/toml"
	"github.com/kelseyhightower/confd/config"
	"github.com/kelseyhightower/confd/log"
	"github.com/kelseyhightower/confd/node"
)

var replacer = strings.NewReplacer("/", "_")

// TemplateResourceConfig holds the parsed template resource.
type TemplateResourceConfig struct {
	TemplateResource TemplateResource `toml:"template"`
}

// TemplateResource is the representation of a parsed template resource.
type TemplateResource struct {
	Dest        string
	FileMode    os.FileMode
	Gid         int
	Keys        []string
	Mode        string
	Uid         int
	ReloadCmd   string `toml:"reload_cmd"`
	CheckCmd    string `toml:"check_cmd"`
	StageFile   *os.File
	Src         string
	Vars        map[string]interface{}
	Dirs        node.Directory
	storeClient StoreClient
}

// NewTemplateResourceFromPath creates a TemplateResource using a decoded file path
// and the supplied StoreClient as input.
// It returns a TemplateResource and an error if any.
func NewTemplateResourceFromPath(path string, s StoreClient) (*TemplateResource, error) {
	if s == nil {
		return nil, errors.New("A valid StoreClient is required.")
	}
	var tc *TemplateResourceConfig
	log.Debug("Loading template resource from " + path)
	_, err := toml.DecodeFile(path, &tc)
	if err != nil {
		return nil, fmt.Errorf("Cannot process template resource %s - %s", path, err.Error())
	}
	tc.TemplateResource.storeClient = s
	return &tc.TemplateResource, nil
}

// setVars sets the Vars for template resource.
func (t *TemplateResource) setVars() error {
	var err error
	log.Debug("Retrieving keys from store")
	log.Debug("Key prefix set to " + config.Prefix())
	vars, err := t.storeClient.GetValues(appendPrefix(config.Prefix(), t.Keys))
	if err != nil {
		return err
	}
	t.setDirs(vars)
	t.Vars = cleanKeys(vars, config.Prefix())
	return nil
}

// setDirs sets the Dirs for the template resource.
// All keys are grouped based on their directory path names.
// For example, /upstream/app1 and upstream/app2 will be grouped as
//	{
//		"upstream": []Node{
//			{"app1": value}},
//			{"app2": value}},
//		 }
//	}
//
// Dirs are exposed to resource templated to enable iteration.
func (t *TemplateResource) setDirs(vars map[string]interface{}) {
	d := node.NewDirectory()
	for k, v := range vars {
		directory := filepath.Dir(filepath.Join("/", strings.TrimPrefix(k, config.Prefix())))
		d.Add(pathToKey(directory, config.Prefix()), node.Node{filepath.Base(k), v})
	}
	t.Dirs = d
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (t *TemplateResource) createStageFile() error {
	t.Src = filepath.Join(config.TemplateDir(), t.Src)
	log.Debug("Using source template " + t.Src)
	if !isFileExist(t.Src) {
		return errors.New("Missing template: " + t.Src)
	}
	// create TempFile in Dest directory to avoid cross-filesystem issues
	temp, err := ioutil.TempFile(filepath.Dir(t.Dest), "."+filepath.Base(t.Dest))
	if err != nil {
		os.Remove(temp.Name())
		return err
	}
	defer temp.Close()
	log.Debug("Compiling source template " + t.Src)
	tplFuncMap := make(template.FuncMap)
	tplFuncMap["Base"] = path.Base

	tplFuncMap["GetDir"] = t.Dirs.Get
	tplFuncMap["MapDir"] = mapNodes
	tplFuncMap["GetEnv"] = os.Getenv
	tmpl := template.Must(template.New(path.Base(t.Src)).Funcs(tplFuncMap).ParseFiles(t.Src))
	if err = tmpl.Execute(temp, t.Vars); err != nil {
		return err
	}
	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), t.FileMode)
	os.Chown(temp.Name(), t.Uid, t.Gid)
	t.StageFile = temp
	return nil
}

// sync compares the staged and dest config files and attempts to sync them
// if they differ. sync will run a config check command if set before
// overwriting the target config file. Finally, sync will run a reload command
// if set to have the application or service pick up the changes.
// It returns an error if any.
func (t *TemplateResource) sync() error {
	staged := t.StageFile.Name()
	defer os.Remove(staged)
	log.Debug("Comparing candidate config to " + t.Dest)
	ok, err := sameConfig(staged, t.Dest)
	if err != nil {
		log.Error(err.Error())
	}
	if config.Noop() {
		log.Warning("Noop mode enabled " + t.Dest + " will not be modified")
		return nil
	}
	if !ok {
		log.Info("Target config " + t.Dest + " out of sync")
		if t.CheckCmd != "" {
			if err := t.check(); err != nil {
				return errors.New("Config check failed: " + err.Error())
			}
		}
		log.Debug("Overwriting target config " + t.Dest)
		if err := os.Rename(staged, t.Dest); err != nil {
			return err
		}
		if t.ReloadCmd != "" {
			if err := t.reload(); err != nil {
				return err
			}
		}
		log.Info("Target config " + t.Dest + " has been updated")
	} else {
		log.Info("Target config " + t.Dest + " in sync")
	}
	return nil
}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config
// file.
// It returns nil if the check command returns 0 and there are no other errors.
func (t *TemplateResource) check() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = t.StageFile.Name()
	tmpl, err := template.New("checkcmd").Parse(t.CheckCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0.
func (t *TemplateResource) reload() error {
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["dest"] = t.Dest
	tmpl, err := template.New("reloadcmd").Parse(t.ReloadCmd)
	if err != nil {
		return err
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return err
	}
	log.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())
	if err := c.Run(); err != nil {
		return err
	}
	return nil
}

// process is a convenience function that wraps calls to the three main tasks
// required to keep local configuration files in sync. First we gather vars
// from the store, then we stage a candidate configuration file, and finally sync
// things up.
// It returns an error if any.
func (t *TemplateResource) process() error {
	if err := t.setFileMode(); err != nil {
		return err
	}
	if err := t.setVars(); err != nil {
		return err
	}
	if err := t.createStageFile(); err != nil {
		return err
	}
	if err := t.sync(); err != nil {
		return err
	}
	return nil
}

// setFileMode sets the FileMode.
func (t *TemplateResource) setFileMode() error {
	if t.Mode == "" {
		if !isFileExist(t.Dest) {
			t.FileMode = 0644
		} else {
			fi, err := os.Stat(t.Dest)
			if err != nil {
				return err
			}
			t.FileMode = fi.Mode()
		}
	} else {
		mode, err := strconv.ParseUint(t.Mode, 0, 32)
		if err != nil {
			return err
		}
		t.FileMode = os.FileMode(mode)
	}
	return nil
}

// ProcessTemplateResources is a convenience function that loads all the
// template resources and processes them serially. Called from main.
// It returns a list of errors if any.
func ProcessTemplateResources(s StoreClient) []error {
	runErrors := make([]error, 0)
	var err error
	if s == nil {
		runErrors = append(runErrors, errors.New("A StoreClient client is required"))
		return runErrors
	}
	log.Debug("Loading template resources from confdir " + config.ConfDir())
	if !isFileExist(config.ConfDir()) {
		log.Warning(fmt.Sprintf("Cannot load template resources confdir '%s' does not exist", config.ConfDir()))
		return runErrors
	}
	paths, err := filepath.Glob(filepath.Join(config.ConfigDir(), "*toml"))
	if err != nil {
		runErrors = append(runErrors, err)
		return runErrors
	}
	for _, p := range paths {
		log.Debug("Processing template resource " + p)
		tplFuncMap := make(template.FuncMap)
		tplFuncMap["Base"] = path.Base
		tplFuncMap["GetEnv"] = os.Getenv
		data := make(map[string]string)
		data["file"] = path.Base(p)
		data["src"] = "{{ .src }}"
		data["dest"] = "{{ .dest }}"
		resTemp, terr := template.New(path.Base(p)).Funcs(tplFuncMap).ParseFiles(p)
		if terr != nil {
			panic(terr)
		}
		var doc bytes.Buffer
		resTemp.Execute(&doc, data)
		docs := doc.String()
		var tc *TemplateResourceConfig
		log.Debug(fmt.Sprintf("Resource parsed as: %s", docs))
		_, err := toml.Decode(docs, &tc)
		if err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		tc.TemplateResource.storeClient = s
		if err := tc.TemplateResource.process(); err != nil {
			runErrors = append(runErrors, err)
			log.Error(err.Error())
			continue
		}
		log.Debug("Processing of template resource " + p + " complete")
	}
	return runErrors
}
<|endoftext|>


<commit_before>package gitlab

import (
	"fmt"
	"github.com/stretchr/testify/require"
	"net/http"
	"testing"
)

func TestResourceStateEventsService_ListIssueStateEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
		  {
			"id": 142,
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/root"
			},
			"resource_type": "Issue",
			"resource_id": 11,
			"state": "opened"
		  }
		]`)
	})

	opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}}

	ses, _, err := client.ResourceStateEvents.ListIssueStateEvents(5, 11, opt)
	require.NoError(t, err)

	want := []*StateEvent{{
		ID: 142,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "Issue",
		ResourceID:   11,
		State:        "opened",
	}}
	require.Equal(t, want, ses)
}

func TestResourceStateEventsService_GetIssueStateEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events/143", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
			  "id": 143,
			  "user": {
				"id": 1,
				"name": "Administrator",
				"username": "root",
				"state": "active",
				"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				"web_url": "http://gitlab.example.com/root"
			  },
			  "resource_type": "Issue",
			  "resource_id": 11,
			  "state": "closed"
			}`,
		)
	})

	se, _, err := client.ResourceStateEvents.GetIssueStateEvent(5, 11, 143)
	require.NoError(t, err)

	want := &StateEvent{
		ID: 143,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "Issue",
		ResourceID:   11,
		State:        "closed",
	}
	require.Equal(t, want, se)
}

func TestResourceStateEventsService_ListMergeStateEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
		  {
			"id": 142,
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/root"
			},
			"resource_type": "MergeRequest",
			"resource_id": 11,
			"state": "opened"
		  }]`,
		)
	})

	opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}}

	ses, _, err := client.ResourceStateEvents.ListMergeStateEvents(5, 11, opt)
	require.NoError(t, err)

	want := []*StateEvent{{
		ID: 142,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "MergeRequest",
		ResourceID:   11,
		State:        "opened",
	}}
	require.Equal(t, want, ses)
}

func TestResourceStateEventsService_GetMergeRequestStateEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events/120", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
		  "id": 120,
		  "user": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			"web_url": "http://gitlab.example.com/root"
		  },
		  "resource_type": "MergeRequest",
		  "resource_id": 11,
		  "state": "closed"
		}`)
	})

	se, _, err := client.ResourceStateEvents.GetMergeRequestStateEvent(5, 11, 120)
	require.NoError(t, err)

	want := &StateEvent{
		ID: 120,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "MergeRequest",
		ResourceID:   11,
		State:        "closed",
	}
	require.Equal(t, want, se)
}
<commit_msg>Update resource_state_events_test.go<commit_after>package gitlab

import (
	"fmt"
	"net/http"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestResourceStateEventsService_ListIssueStateEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
		  {
			"id": 142,
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/root"
			},
			"resource_type": "Issue",
			"resource_id": 11,
			"state": "opened"
		  }
		]`)
	})

	opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}}

	ses, _, err := client.ResourceStateEvents.ListIssueStateEvents(5, 11, opt)
	require.NoError(t, err)

	want := []*StateEvent{{
		ID: 142,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "Issue",
		ResourceID:   11,
		State:        "opened",
	}}
	require.Equal(t, want, ses)
}

func TestResourceStateEventsService_GetIssueStateEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/issues/11/resource_state_events/143", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `
			{
			  "id": 143,
			  "user": {
				"id": 1,
				"name": "Administrator",
				"username": "root",
				"state": "active",
				"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
				"web_url": "http://gitlab.example.com/root"
			  },
			  "resource_type": "Issue",
			  "resource_id": 11,
			  "state": "closed"
			}`,
		)
	})

	se, _, err := client.ResourceStateEvents.GetIssueStateEvent(5, 11, 143)
	require.NoError(t, err)

	want := &StateEvent{
		ID: 143,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "Issue",
		ResourceID:   11,
		State:        "closed",
	}
	require.Equal(t, want, se)
}

func TestResourceStateEventsService_ListMergeStateEvents(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `[
		  {
			"id": 142,
			"user": {
			  "id": 1,
			  "name": "Administrator",
			  "username": "root",
			  "state": "active",
			  "avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			  "web_url": "http://gitlab.example.com/root"
			},
			"resource_type": "MergeRequest",
			"resource_id": 11,
			"state": "opened"
		  }]`,
		)
	})

	opt := &ListStateEventsOptions{ListOptions{Page: 1, PerPage: 10}}

	ses, _, err := client.ResourceStateEvents.ListMergeStateEvents(5, 11, opt)
	require.NoError(t, err)

	want := []*StateEvent{{
		ID: 142,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "MergeRequest",
		ResourceID:   11,
		State:        "opened",
	}}
	require.Equal(t, want, ses)
}

func TestResourceStateEventsService_GetMergeRequestStateEvent(t *testing.T) {
	mux, server, client := setup(t)
	defer teardown(server)

	mux.HandleFunc("/api/v4/projects/5/merge_requests/11/resource_state_events/120", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, http.MethodGet)
		fmt.Fprintf(w, `{
		  "id": 120,
		  "user": {
			"id": 1,
			"name": "Administrator",
			"username": "root",
			"state": "active",
			"avatar_url": "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			"web_url": "http://gitlab.example.com/root"
		  },
		  "resource_type": "MergeRequest",
		  "resource_id": 11,
		  "state": "closed"
		}`)
	})

	se, _, err := client.ResourceStateEvents.GetMergeRequestStateEvent(5, 11, 120)
	require.NoError(t, err)

	want := &StateEvent{
		ID: 120,
		User: &BasicUser{
			ID:        1,
			Username:  "root",
			Name:      "Administrator",
			State:     "active",
			AvatarURL: "https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
			WebURL:    "http://gitlab.example.com/root",
		},
		ResourceType: "MergeRequest",
		ResourceID:   11,
		State:        "closed",
	}
	require.Equal(t, want, se)
}
<|endoftext|>


<commit_before>package hypervisors

import (
	"bufio"
	"fmt"
	"net/http"
	"sort"

	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/json"
	"github.com/Symantec/Dominator/lib/url"
	proto "github.com/Symantec/Dominator/proto/fleetmanager"
)

const (
	showOK = iota
	showConnected
	showAll
	showOff
)

type hypervisorList []*hypervisorType

func (h *hypervisorType) getHealthStatus() string {
	healthStatus := h.probeStatus.String()
	if h.probeStatus == probeStatusConnected {
		if h.healthStatus != "" {
			healthStatus = h.healthStatus
		}
	}
	return healthStatus
}

func (h *hypervisorType) getNumVMs() uint {
	h.mutex.RLock()
	defer h.mutex.RUnlock()
	return uint(len(h.vms))
}

func (m *Manager) listHypervisors(topologyDir string, showFilter int,
	subnetId string) (hypervisorList, error) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()
	machines, err := m.topology.ListMachines(topologyDir)
	if err != nil {
		return nil, err
	}
	hypervisors := make([]*hypervisorType, 0, len(machines))
	for _, machine := range machines {
		if subnetId != "" {
			hasSubnet, _ := m.topology.CheckIfMachineHasSubnet(
				machine.Hostname, subnetId)
			if !hasSubnet {
				continue
			}
		}
		hypervisor := m.hypervisors[machine.Hostname]
		switch showFilter {
		case showOK:
			if hypervisor.probeStatus == probeStatusConnected &&
				(hypervisor.healthStatus == "" ||
					hypervisor.healthStatus == "healthy") {
				hypervisors = append(hypervisors, hypervisor)
			}
		case showConnected:
			if hypervisor.probeStatus == probeStatusConnected {
				hypervisors = append(hypervisors, hypervisor)
			}
		case showAll:
			hypervisors = append(hypervisors, hypervisor)
		case showOff:
			if hypervisor.probeStatus == probeStatusOff {
				hypervisors = append(hypervisors, hypervisor)
			}
		}
	}
	return hypervisors, nil
}

func (m *Manager) listHypervisorsHandler(w http.ResponseWriter,
	req *http.Request) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	_, err := m.getTopology()
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	parsedQuery := url.ParseQuery(req.URL)
	showFilter := showAll
	switch parsedQuery.Table["state"] {
	case "connected":
		showFilter = showConnected
	case "OK":
		showFilter = showOK
	case "off":
		showFilter = showOff
	}
	hypervisors, err := m.listHypervisors("", showFilter, "")
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	sort.Sort(hypervisors)
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, hypervisor := range hypervisors {
			fmt.Fprintln(writer, hypervisor.machine.Hostname)
		}
		return
	}
	if parsedQuery.OutputType() == url.OutputTypeJson {
		json.WriteWithIndent(writer, "    ", hypervisors)
		return
	}
	fmt.Fprintf(writer, "<title>List of hypervisors</title>\n")
	writer.WriteString(commonStyleSheet)
	fmt.Fprintln(writer, "")
	fmt.Fprintln(writer, `<table border="1" style="width:100%">`)
	fmt.Fprintln(writer, "
")
	fmt.Fprintln(writer, "    Name	")
	fmt.Fprintln(writer, "    Status	")
	fmt.Fprintln(writer, "    IP Addr	")
	fmt.Fprintln(writer, "    Serial Number	")
	fmt.Fprintln(writer, "    Location	")
	fmt.Fprintln(writer, "    NumVMs	")
	fmt.Fprintln(writer, "
")
	lastRowHighlighted := true
	for _, hypervisor := range hypervisors {
		machine := hypervisor.machine
		if lastRowHighlighted {
			lastRowHighlighted = false
			fmt.Fprintf(writer, "
\n")
		} else {
			lastRowHighlighted = true
			fmt.Fprintf(writer, "
\n",
				rowStyles[rowStyleHighlight].html)
		}
		fmt.Fprintf(writer,
			"    %s	\n",
			machine.Hostname, machine.Hostname)
		fmt.Fprintf(writer, "    %s	\n",
			machine.Hostname, constants.HypervisorPortNumber,
			hypervisor.getHealthStatus())
		fmt.Fprintf(writer, "    %s	\n", machine.HostIpAddress)
		fmt.Fprintf(writer, "    %s	\n", hypervisor.serialNumber)
		fmt.Fprintf(writer, "    %s	\n", hypervisor.location)
		fmt.Fprintf(writer,
			"    %d	\n",
			machine.Hostname, constants.HypervisorPortNumber,
			hypervisor.getNumVMs())
		fmt.Fprintf(writer, "
\n")
	}
	fmt.Fprintln(writer, "
")
	fmt.Fprintln(writer, "")
}

func (m *Manager) listHypervisorsInLocation(
	request proto.ListHypervisorsInLocationRequest) ([]string, error) {
	hypervisors, err := m.listHypervisors(request.Location, showOK,
		request.SubnetId)
	if err != nil {
		return nil, err
	}
	addresses := make([]string, 0, len(hypervisors))
	for _, hypervisor := range hypervisors {
		addresses = append(addresses,
			fmt.Sprintf("%s:%d",
				hypervisor.machine.Hostname, constants.HypervisorPortNumber))
	}
	return addresses, nil
}

func (list hypervisorList) Len() int {
	return len(list)
}

func (list hypervisorList) Less(i, j int) bool {
	if list[i].location < list[j].location {
		return true
	} else if list[i].location > list[j].location {
		return false
	} else {
		return list[i].machine.Hostname < list[j].machine.Hostname
	}
}

func (list hypervisorList) Swap(i, j int) {
	list[i], list[j] = list[j], list[i]
}
<commit_msg>Fleet Manager: optionally include unhealthy hypervisors in list.<commit_after>package hypervisors

import (
	"bufio"
	"fmt"
	"net/http"
	"sort"

	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/json"
	"github.com/Symantec/Dominator/lib/url"
	proto "github.com/Symantec/Dominator/proto/fleetmanager"
)

const (
	showOK = iota
	showConnected
	showAll
	showOff
)

type hypervisorList []*hypervisorType

func (h *hypervisorType) getHealthStatus() string {
	healthStatus := h.probeStatus.String()
	if h.probeStatus == probeStatusConnected {
		if h.healthStatus != "" {
			healthStatus = h.healthStatus
		}
	}
	return healthStatus
}

func (h *hypervisorType) getNumVMs() uint {
	h.mutex.RLock()
	defer h.mutex.RUnlock()
	return uint(len(h.vms))
}

func (m *Manager) listHypervisors(topologyDir string, showFilter int,
	subnetId string) (hypervisorList, error) {
	m.mutex.RLock()
	defer m.mutex.RUnlock()
	machines, err := m.topology.ListMachines(topologyDir)
	if err != nil {
		return nil, err
	}
	hypervisors := make([]*hypervisorType, 0, len(machines))
	for _, machine := range machines {
		if subnetId != "" {
			hasSubnet, _ := m.topology.CheckIfMachineHasSubnet(
				machine.Hostname, subnetId)
			if !hasSubnet {
				continue
			}
		}
		hypervisor := m.hypervisors[machine.Hostname]
		switch showFilter {
		case showOK:
			if hypervisor.probeStatus == probeStatusConnected &&
				(hypervisor.healthStatus == "" ||
					hypervisor.healthStatus == "healthy") {
				hypervisors = append(hypervisors, hypervisor)
			}
		case showConnected:
			if hypervisor.probeStatus == probeStatusConnected {
				hypervisors = append(hypervisors, hypervisor)
			}
		case showAll:
			hypervisors = append(hypervisors, hypervisor)
		case showOff:
			if hypervisor.probeStatus == probeStatusOff {
				hypervisors = append(hypervisors, hypervisor)
			}
		}
	}
	return hypervisors, nil
}

func (m *Manager) listHypervisorsHandler(w http.ResponseWriter,
	req *http.Request) {
	writer := bufio.NewWriter(w)
	defer writer.Flush()
	_, err := m.getTopology()
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	parsedQuery := url.ParseQuery(req.URL)
	showFilter := showAll
	switch parsedQuery.Table["state"] {
	case "connected":
		showFilter = showConnected
	case "OK":
		showFilter = showOK
	case "off":
		showFilter = showOff
	}
	hypervisors, err := m.listHypervisors("", showFilter, "")
	if err != nil {
		fmt.Fprintln(writer, err)
		return
	}
	sort.Sort(hypervisors)
	if parsedQuery.OutputType() == url.OutputTypeText {
		for _, hypervisor := range hypervisors {
			fmt.Fprintln(writer, hypervisor.machine.Hostname)
		}
		return
	}
	if parsedQuery.OutputType() == url.OutputTypeJson {
		json.WriteWithIndent(writer, "    ", hypervisors)
		return
	}
	fmt.Fprintf(writer, "<title>List of hypervisors</title>\n")
	writer.WriteString(commonStyleSheet)
	fmt.Fprintln(writer, "")
	fmt.Fprintln(writer, `<table border="1" style="width:100%">`)
	fmt.Fprintln(writer, "
")
	fmt.Fprintln(writer, "    Name	")
	fmt.Fprintln(writer, "    Status	")
	fmt.Fprintln(writer, "    IP Addr	")
	fmt.Fprintln(writer, "    Serial Number	")
	fmt.Fprintln(writer, "    Location	")
	fmt.Fprintln(writer, "    NumVMs	")
	fmt.Fprintln(writer, "
")
	lastRowHighlighted := true
	for _, hypervisor := range hypervisors {
		machine := hypervisor.machine
		if lastRowHighlighted {
			lastRowHighlighted = false
			fmt.Fprintf(writer, "
\n")
		} else {
			lastRowHighlighted = true
			fmt.Fprintf(writer, "
\n",
				rowStyles[rowStyleHighlight].html)
		}
		fmt.Fprintf(writer,
			"    %s	\n",
			machine.Hostname, machine.Hostname)
		fmt.Fprintf(writer, "    %s	\n",
			machine.Hostname, constants.HypervisorPortNumber,
			hypervisor.getHealthStatus())
		fmt.Fprintf(writer, "    %s	\n", machine.HostIpAddress)
		fmt.Fprintf(writer, "    %s	\n", hypervisor.serialNumber)
		fmt.Fprintf(writer, "    %s	\n", hypervisor.location)
		fmt.Fprintf(writer,
			"    %d	\n",
			machine.Hostname, constants.HypervisorPortNumber,
			hypervisor.getNumVMs())
		fmt.Fprintf(writer, "
\n")
	}
	fmt.Fprintln(writer, "
")
	fmt.Fprintln(writer, "")
}

func (m *Manager) listHypervisorsInLocation(
	request proto.ListHypervisorsInLocationRequest) ([]string, error) {
	showFilter := showOK
	if request.IncludeUnhealthy {
		showFilter = showConnected
	}
	hypervisors, err := m.listHypervisors(request.Location, showFilter,
		request.SubnetId)
	if err != nil {
		return nil, err
	}
	addresses := make([]string, 0, len(hypervisors))
	for _, hypervisor := range hypervisors {
		addresses = append(addresses,
			fmt.Sprintf("%s:%d",
				hypervisor.machine.Hostname, constants.HypervisorPortNumber))
	}
	return addresses, nil
}

func (list hypervisorList) Len() int {
	return len(list)
}

func (list hypervisorList) Less(i, j int) bool {
	if list[i].location < list[j].location {
		return true
	} else if list[i].location > list[j].location {
		return false
	} else {
		return list[i].machine.Hostname < list[j].machine.Hostname
	}
}

func (list hypervisorList) Swap(i, j int) {
	list[i], list[j] = list[j], list[i]
}
<|endoftext|>