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

package main

import (
	"bytes"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"

	"github.com/letsencrypt/boulder/cmd"
	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/mail"
	"github.com/letsencrypt/boulder/sa"
)

type emailContent struct {
	ExpirationDate   time.Time
	DaysToExpiration int
	DNSNames         string
}

type mailer struct {
	stats         statsd.Statter
	log           *blog.AuditLogger
	dbMap         *gorp.DbMap
	mailer        mail.Mailer
	emailTemplate *template.Template
	nagTimes      []time.Duration
	limit         int
}

func (m *mailer) sendNags(parsedCert *x509.Certificate, contacts []core.AcmeURL) error {
	expiresIn := int(parsedCert.NotAfter.Sub(time.Now()).Hours()/24) + 1
	emails := []string{}
	for _, contact := range contacts {
		if contact.Scheme == "mailto" {
			emails = append(emails, contact.Opaque)
		}
	}
	if len(emails) > 0 {
		email := emailContent{
			ExpirationDate:   parsedCert.NotAfter,
			DaysToExpiration: expiresIn,
			DNSNames:         strings.Join(parsedCert.DNSNames, ", "),
		}
		msgBuf := new(bytes.Buffer)
		err := m.emailTemplate.Execute(msgBuf, email)
		if err != nil {
			m.stats.Inc("Mailer.Expiration.Errors.SendingNag.TemplateFailure", 1, 1.0)
			return err
		}
		startSending := time.Now()
		err = m.mailer.SendMail(emails, msgBuf.String())
		if err != nil {
			m.stats.Inc("Mailer.Expiration.Errors.SendingNag.SendFailure", 1, 1.0)
			return err
		}
		m.stats.TimingDuration("Mailer.Expiration.Sending", time.Since(startSending), 1.0)
		m.stats.Inc("Mailer.Expiration.Sent", int64(len(emails)), 1.0)
	}
	return nil
}

func (m *mailer) updateCertStatus(serial string) error {
	// Update CertificateStatus object
	tx, err := m.dbMap.Begin()
	if err != nil {
		m.log.Err(fmt.Sprintf("Error opening transaction for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	csObj, err := tx.Get(&core.CertificateStatus{}, serial)
	if err != nil {
		m.log.Err(fmt.Sprintf("Error fetching status for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}
	certStatus := csObj.(*core.CertificateStatus)
	certStatus.LastExpirationNagSent = time.Now()

	_, err = tx.Update(certStatus)
	if err != nil {
		m.log.Err(fmt.Sprintf("Error updating status for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	err = tx.Commit()
	if err != nil {
		m.log.Err(fmt.Sprintf("Error commiting transaction for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	return nil
}

func (m *mailer) processCerts(certs []core.Certificate) {
	m.log.Info(fmt.Sprintf("expiration-mailer: Found %d certificates, starting sending messages", len(certs)))
	for _, cert := range certs {
		regObj, err := m.dbMap.Get(&core.Registration{}, cert.RegistrationID)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error fetching registration %d: %s", cert.RegistrationID, err))
			m.stats.Inc("Mailer.Expiration.Errors.GetRegistration", 1, 1.0)
			continue
		}
		reg := regObj.(*core.Registration)
		parsedCert, err := x509.ParseCertificate(cert.DER)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error parsing certificate %s: %s", cert.Serial, err))
			m.stats.Inc("Mailer.Expiration.Errors.ParseCertificate", 1, 1.0)
			continue
		}
		err = m.sendNags(parsedCert, reg.Contact)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error sending nag emails: %s", err))
			m.stats.Inc("Mailer.Expiration.Errors.SendingNags", 1, 1.0)
			continue
		}
		err = m.updateCertStatus(cert.Serial)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error updating certificate status for %s: %s", cert.Serial, err))
			m.stats.Inc("Mailer.Expiration.Errors.UpdateCertificateStatus", 1, 1.0)
			continue
		}
	}
	m.log.Info("expiration-mailer: Finished sending messages")
	return
}

func (m *mailer) findExpiringCertificates() error {
	now := time.Now()
	// E.g. m.NagTimes = [1, 3, 7, 14] days from expiration
	for i, expiresIn := range m.nagTimes {
		left := now
		if i > 0 {
			left = left.Add(m.nagTimes[i-1])
		}
		right := now.Add(expiresIn)

		m.log.Info(fmt.Sprintf("expiration-mailer: Searching for certificates that expire between %s and %s", left, right))
		var certs []core.Certificate
		_, err := m.dbMap.Select(
			&certs,
			`SELECT cert.* FROM certificates AS cert
			 JOIN certificateStatus AS cs
			 ON cs.serial = cert.serial
			 JOIN registrations AS reg
			 ON cert.registrationId = reg.id
			 WHERE LOCATE("mailto", reg.contact) > 0
			 AND cert.expires > :cutoffA
			 AND cert.expires < :cutoffB
			 AND cert.status != "revoked"
			 AND cs.lastExpirationNagSent <= :nagCutoff
			 ORDER BY cert.expires ASC
			 LIMIT :limit`,
			map[string]interface{}{
				"cutoffA":   left,
				"cutoffB":   right,
				"nagCutoff": time.Now().Add(-expiresIn),
				"limit":     m.limit,
			},
		)
		if err != nil {
			m.log.Err(fmt.Sprintf("expiration-mailer: Error loading certificates: %s", err))
			return err // fatal
		}
		if len(certs) > 0 {
			processingStarted := time.Now()
			m.processCerts(certs)
			m.stats.TimingDuration("Mailer.Expiration.ProcessingCertificates", time.Since(processingStarted), 1.0)
		}
	}

	return nil
}

type durationSlice []time.Duration

func (ds durationSlice) Len() int {
	return len(ds)
}

func (ds durationSlice) Less(a, b int) bool {
	return ds[a] < ds[b]
}

func (ds durationSlice) Swap(a, b int) {
	ds[a], ds[b] = ds[b], ds[a]
}

func main() {
	app := cmd.NewAppShell("expiration-mailer")

	app.App.Flags = append(app.App.Flags, cli.IntFlag{
		Name:   "cert_limit",
		Value:  100,
		EnvVar: "CERT_LIMIT",
		Usage:  "Count of certificates to process per expiration period",
	})

	app.Config = func(c *cli.Context, config cmd.Config) cmd.Config {
		if c.GlobalInt("cert_limit") > 0 {
			config.Mailer.CertLimit = c.GlobalInt("cert_limit")
		}
		return config
	}

	app.Action = func(c cmd.Config) {
		// Set up logging
		stats, err := statsd.NewClient(c.Statsd.Server, c.Statsd.Prefix)
		cmd.FailOnError(err, "Couldn't connect to statsd")

		auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
		cmd.FailOnError(err, "Could not connect to Syslog")

		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		defer auditlogger.AuditPanic()

		blog.SetAuditLogger(auditlogger)

		auditlogger.Info(app.VersionString())

		go cmd.DebugServer(c.Mailer.DebugAddr)

		// Configure DB
		dbMap, err := sa.NewDbMap(c.Mailer.DBDriver, c.Mailer.DBConnect)
		cmd.FailOnError(err, "Could not connect to database")

		// Load email template
		emailTmpl, err := ioutil.ReadFile(c.Mailer.EmailTemplate)
		cmd.FailOnError(err, fmt.Sprintf("Could not read email template file [%s]", c.Mailer.EmailTemplate))
		tmpl, err := template.New("expiry-email").Parse(string(emailTmpl))
		cmd.FailOnError(err, "Could not parse email template")

		mailClient := mail.New(c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, c.Mailer.Password)

		var nags durationSlice
		for _, nagDuration := range c.Mailer.NagTimes {
			dur, err := time.ParseDuration(nagDuration)
			if err != nil {
				auditlogger.Err(fmt.Sprintf("Failed to parse nag duration string [%s]: %s", nagDuration, err))
				return
			}
			nags = append(nags, dur)
		}
		// Make sure durations are sorted in increasing order
		sort.Sort(nags)

		m := mailer{
			stats:         stats,
			log:           auditlogger,
			dbMap:         dbMap,
			mailer:        &mailClient,
			emailTemplate: tmpl,
			nagTimes:      nags,
			limit:         c.Mailer.CertLimit,
		}

		auditlogger.Info("expiration-mailer: Starting")
		err = m.findExpiringCertificates()
		cmd.FailOnError(err, "expiration-mailer has failed")
	}

	app.Run()
}
<commit_msg>Use LIKE instead of LOCATE<commit_after>// Copyright 2015 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package main

import (
	"bytes"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"sort"
	"strings"
	"text/template"
	"time"

	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/cactus/go-statsd-client/statsd"
	"github.com/letsencrypt/boulder/Godeps/_workspace/src/github.com/codegangsta/cli"
	"github.com/letsencrypt/boulder/Godeps/_workspace/src/gopkg.in/gorp.v1"

	"github.com/letsencrypt/boulder/cmd"
	"github.com/letsencrypt/boulder/core"
	blog "github.com/letsencrypt/boulder/log"
	"github.com/letsencrypt/boulder/mail"
	"github.com/letsencrypt/boulder/sa"
)

type emailContent struct {
	ExpirationDate   time.Time
	DaysToExpiration int
	DNSNames         string
}

type mailer struct {
	stats         statsd.Statter
	log           *blog.AuditLogger
	dbMap         *gorp.DbMap
	mailer        mail.Mailer
	emailTemplate *template.Template
	nagTimes      []time.Duration
	limit         int
}

func (m *mailer) sendNags(parsedCert *x509.Certificate, contacts []core.AcmeURL) error {
	expiresIn := int(parsedCert.NotAfter.Sub(time.Now()).Hours()/24) + 1
	emails := []string{}
	for _, contact := range contacts {
		if contact.Scheme == "mailto" {
			emails = append(emails, contact.Opaque)
		}
	}
	if len(emails) > 0 {
		email := emailContent{
			ExpirationDate:   parsedCert.NotAfter,
			DaysToExpiration: expiresIn,
			DNSNames:         strings.Join(parsedCert.DNSNames, ", "),
		}
		msgBuf := new(bytes.Buffer)
		err := m.emailTemplate.Execute(msgBuf, email)
		if err != nil {
			m.stats.Inc("Mailer.Expiration.Errors.SendingNag.TemplateFailure", 1, 1.0)
			return err
		}
		startSending := time.Now()
		err = m.mailer.SendMail(emails, msgBuf.String())
		if err != nil {
			m.stats.Inc("Mailer.Expiration.Errors.SendingNag.SendFailure", 1, 1.0)
			return err
		}
		m.stats.TimingDuration("Mailer.Expiration.Sending", time.Since(startSending), 1.0)
		m.stats.Inc("Mailer.Expiration.Sent", int64(len(emails)), 1.0)
	}
	return nil
}

func (m *mailer) updateCertStatus(serial string) error {
	// Update CertificateStatus object
	tx, err := m.dbMap.Begin()
	if err != nil {
		m.log.Err(fmt.Sprintf("Error opening transaction for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	csObj, err := tx.Get(&core.CertificateStatus{}, serial)
	if err != nil {
		m.log.Err(fmt.Sprintf("Error fetching status for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}
	certStatus := csObj.(*core.CertificateStatus)
	certStatus.LastExpirationNagSent = time.Now()

	_, err = tx.Update(certStatus)
	if err != nil {
		m.log.Err(fmt.Sprintf("Error updating status for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	err = tx.Commit()
	if err != nil {
		m.log.Err(fmt.Sprintf("Error commiting transaction for certificate %s: %s", serial, err))
		tx.Rollback()
		return err
	}

	return nil
}

func (m *mailer) processCerts(certs []core.Certificate) {
	m.log.Info(fmt.Sprintf("expiration-mailer: Found %d certificates, starting sending messages", len(certs)))
	for _, cert := range certs {
		regObj, err := m.dbMap.Get(&core.Registration{}, cert.RegistrationID)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error fetching registration %d: %s", cert.RegistrationID, err))
			m.stats.Inc("Mailer.Expiration.Errors.GetRegistration", 1, 1.0)
			continue
		}
		reg := regObj.(*core.Registration)
		parsedCert, err := x509.ParseCertificate(cert.DER)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error parsing certificate %s: %s", cert.Serial, err))
			m.stats.Inc("Mailer.Expiration.Errors.ParseCertificate", 1, 1.0)
			continue
		}
		err = m.sendNags(parsedCert, reg.Contact)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error sending nag emails: %s", err))
			m.stats.Inc("Mailer.Expiration.Errors.SendingNags", 1, 1.0)
			continue
		}
		err = m.updateCertStatus(cert.Serial)
		if err != nil {
			m.log.Err(fmt.Sprintf("Error updating certificate status for %s: %s", cert.Serial, err))
			m.stats.Inc("Mailer.Expiration.Errors.UpdateCertificateStatus", 1, 1.0)
			continue
		}
	}
	m.log.Info("expiration-mailer: Finished sending messages")
	return
}

func (m *mailer) findExpiringCertificates() error {
	now := time.Now()
	// E.g. m.NagTimes = [1, 3, 7, 14] days from expiration
	for i, expiresIn := range m.nagTimes {
		left := now
		if i > 0 {
			left = left.Add(m.nagTimes[i-1])
		}
		right := now.Add(expiresIn)

		m.log.Info(fmt.Sprintf("expiration-mailer: Searching for certificates that expire between %s and %s", left, right))
		var certs []core.Certificate
		_, err := m.dbMap.Select(
			&certs,
			`SELECT cert.* FROM certificates AS cert
			 JOIN certificateStatus AS cs
			 ON cs.serial = cert.serial
			 JOIN registrations AS reg
			 ON cert.registrationId = reg.id
			 WHERE reg.contact LIKE "%mailto%"
			 AND cert.expires > :cutoffA
			 AND cert.expires < :cutoffB
			 AND cert.status != "revoked"
			 AND cs.lastExpirationNagSent <= :nagCutoff
			 ORDER BY cert.expires ASC
			 LIMIT :limit`,
			map[string]interface{}{
				"cutoffA":   left,
				"cutoffB":   right,
				"nagCutoff": time.Now().Add(-expiresIn),
				"limit":     m.limit,
			},
		)
		if err != nil {
			m.log.Err(fmt.Sprintf("expiration-mailer: Error loading certificates: %s", err))
			return err // fatal
		}
		if len(certs) > 0 {
			processingStarted := time.Now()
			m.processCerts(certs)
			m.stats.TimingDuration("Mailer.Expiration.ProcessingCertificates", time.Since(processingStarted), 1.0)
		}
	}

	return nil
}

type durationSlice []time.Duration

func (ds durationSlice) Len() int {
	return len(ds)
}

func (ds durationSlice) Less(a, b int) bool {
	return ds[a] < ds[b]
}

func (ds durationSlice) Swap(a, b int) {
	ds[a], ds[b] = ds[b], ds[a]
}

func main() {
	app := cmd.NewAppShell("expiration-mailer")

	app.App.Flags = append(app.App.Flags, cli.IntFlag{
		Name:   "cert_limit",
		Value:  100,
		EnvVar: "CERT_LIMIT",
		Usage:  "Count of certificates to process per expiration period",
	})

	app.Config = func(c *cli.Context, config cmd.Config) cmd.Config {
		if c.GlobalInt("cert_limit") > 0 {
			config.Mailer.CertLimit = c.GlobalInt("cert_limit")
		}
		return config
	}

	app.Action = func(c cmd.Config) {
		// Set up logging
		stats, err := statsd.NewClient(c.Statsd.Server, c.Statsd.Prefix)
		cmd.FailOnError(err, "Couldn't connect to statsd")

		auditlogger, err := blog.Dial(c.Syslog.Network, c.Syslog.Server, c.Syslog.Tag, stats)
		cmd.FailOnError(err, "Could not connect to Syslog")

		// AUDIT[ Error Conditions ] 9cc4d537-8534-4970-8665-4b382abe82f3
		defer auditlogger.AuditPanic()

		blog.SetAuditLogger(auditlogger)

		auditlogger.Info(app.VersionString())

		go cmd.DebugServer(c.Mailer.DebugAddr)

		// Configure DB
		dbMap, err := sa.NewDbMap(c.Mailer.DBDriver, c.Mailer.DBConnect)
		cmd.FailOnError(err, "Could not connect to database")

		// Load email template
		emailTmpl, err := ioutil.ReadFile(c.Mailer.EmailTemplate)
		cmd.FailOnError(err, fmt.Sprintf("Could not read email template file [%s]", c.Mailer.EmailTemplate))
		tmpl, err := template.New("expiry-email").Parse(string(emailTmpl))
		cmd.FailOnError(err, "Could not parse email template")

		mailClient := mail.New(c.Mailer.Server, c.Mailer.Port, c.Mailer.Username, c.Mailer.Password)

		var nags durationSlice
		for _, nagDuration := range c.Mailer.NagTimes {
			dur, err := time.ParseDuration(nagDuration)
			if err != nil {
				auditlogger.Err(fmt.Sprintf("Failed to parse nag duration string [%s]: %s", nagDuration, err))
				return
			}
			nags = append(nags, dur)
		}
		// Make sure durations are sorted in increasing order
		sort.Sort(nags)

		m := mailer{
			stats:         stats,
			log:           auditlogger,
			dbMap:         dbMap,
			mailer:        &mailClient,
			emailTemplate: tmpl,
			nagTimes:      nags,
			limit:         c.Mailer.CertLimit,
		}

		auditlogger.Info("expiration-mailer: Starting")
		err = m.findExpiringCertificates()
		cmd.FailOnError(err, "expiration-mailer has failed")
	}

	app.Run()
}
<|endoftext|>


<commit_before>package gateway

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
)

var (
	Options struct {
		Id                      string
		Zone                    string
		ConfigFile              string
		PubHttpAddr             string
		PubHttpsAddr            string
		SubHttpAddr             string
		SubHttpsAddr            string
		ManHttpAddr             string
		ManHttpsAddr            string
		DebugHttpAddr           string
		Store                   string
		JobStore                string
		ManagerStore            string
		PidFile                 string
		CertFile                string
		KeyFile                 string
		LogFile                 string
		LogLevel                string
		CrashLogFile            string
		DummyCluster            string
		InfluxServer            string
		InfluxDbName            string
		KillFile                string
		HintedHandoffType       string
		HintedHandoffDir        string
		ShowVersion             bool
		Ratelimit               bool
		PermitStandbySub        bool
		DisableMetrics          bool
		EnableHintedHandoff     bool
		HintedHandoffBufio      bool
		FlushHintedOffOnly      bool
		AuditPub                bool
		AuditSub                bool
		EnableGzip              bool
		DryRun                  bool
		CpuAffinity             bool
		EnableAccessLog         bool
		EnableHttpPanicRecover  bool
		GolangTrace             bool
		PermitUnregisteredGroup bool
		UseCompress             bool
		Debug                   bool
		EnableRegistry          bool
		HttpHeaderMaxBytes      int
		MaxPubSize              int64
		MaxJobSize              int64
		LogRotateSize           int
		MaxMsgTagLen            int
		MinPubSize              int
		PubQpsLimit             int64
		MaxSubBatchSize         int
		MaxClients              int
		MaxRequestPerConn       int // to make load balancer distribute request even for persistent conn
		PubPoolCapcity          int
		AssignJobShardId        int // how to assign shard id for new app
		PubPoolIdleTimeout      time.Duration
		SubTimeout              time.Duration
		OffsetCommitInterval    time.Duration
		BadClientPunishDuration time.Duration
		ReporterInterval        time.Duration
		MetaRefresh             time.Duration
		ManagerRefresh          time.Duration
		HttpReadTimeout         time.Duration
		HttpWriteTimeout        time.Duration
	}
)

func ParseFlags() {
	ip, err := ctx.LocalIP()
	if err != nil {
		panic(err)
	}

	var (
		defaultPubHttpAddr  = fmt.Sprintf("%s:9191", ip.String())
		defaultPubHttpsAddr = ""
		defaultSubHttpAddr  = fmt.Sprintf("%s:9192", ip.String())
		defaultSubHttpsAddr = ""
		defaultManHttpAddr  = fmt.Sprintf("%s:9193", ip.String())
		defaultManHttpsAddr = ""
	)

	flag.StringVar(&Options.Id, "id", "", "kateway id, the id must be unique within a host")
	flag.StringVar(&Options.Zone, "zone", "", "kafka zone name")
	flag.StringVar(&Options.PubHttpAddr, "pubhttp", defaultPubHttpAddr, "pub http bind addr")
	flag.StringVar(&Options.PubHttpsAddr, "pubhttps", defaultPubHttpsAddr, "pub https bind addr")
	flag.StringVar(&Options.SubHttpAddr, "subhttp", defaultSubHttpAddr, "sub http bind addr")
	flag.StringVar(&Options.SubHttpsAddr, "subhttps", defaultSubHttpsAddr, "sub https bind addr")
	flag.StringVar(&Options.ManHttpAddr, "manhttp", defaultManHttpAddr, "management http bind addr")
	flag.StringVar(&Options.ManHttpsAddr, "manhttps", defaultManHttpsAddr, "management https bind addr")
	flag.StringVar(&Options.LogLevel, "level", "trace", "log level")
	flag.StringVar(&Options.LogFile, "log", "stdout", "log file, default stdout")
	flag.StringVar(&Options.CrashLogFile, "crashlog", "", "crash log")
	flag.StringVar(&Options.CertFile, "certfile", "", "cert file path")
	flag.StringVar(&Options.PidFile, "pid", "", "pid file")
	flag.StringVar(&Options.KeyFile, "keyfile", "", "key file path")
	flag.StringVar(&Options.DebugHttpAddr, "debughttp", "", "debug http bind addr")
	flag.StringVar(&Options.Store, "store", "kafka", "message underlying store")
	flag.StringVar(&Options.HintedHandoffType, "hhtype", "disk", "underlying hinted handoff")
	flag.StringVar(&Options.HintedHandoffDir, "hhdir", "hhdata", "hinted handoff dirs seperated by comma")
	flag.BoolVar(&Options.FlushHintedOffOnly, "hhflush", false, "flush hinted handoff and exit")
	flag.StringVar(&Options.JobStore, "jstore", "mysql", "job underlying store")
	flag.StringVar(&Options.DummyCluster, "dummycluster", "me", "dummy store's cluster name")
	flag.StringVar(&Options.ManagerStore, "mstore", "mysql", "store integration with manager")
	flag.StringVar(&Options.ConfigFile, "conf", "", "config file, defaults $HOME/.gafka.cf")
	flag.StringVar(&Options.KillFile, "kill", "", "kill running kateway by pid file")
	flag.StringVar(&Options.InfluxServer, "influxdbaddr", "", "influxdb server address for the metrics reporter")
	flag.StringVar(&Options.InfluxDbName, "influxdbname", "pubsub", "influxdb db name")
	flag.BoolVar(&Options.ShowVersion, "version", false, "show version and exit")
	flag.BoolVar(&Options.Debug, "debug", false, "enable debug mode")
	flag.BoolVar(&Options.GolangTrace, "gotrace", false, "go tool trace")
	flag.BoolVar(&Options.AuditPub, "auditpub", true, "enable Pub audit")
	flag.BoolVar(&Options.AuditSub, "auditsub", true, "enable Sub audit")
	flag.BoolVar(&Options.UseCompress, "snappy", false, "backend store will snappy compress messages")
	flag.BoolVar(&Options.EnableAccessLog, "accesslog", false, "en(dis)able access log")
	flag.BoolVar(&Options.EnableRegistry, "withreg", true, "self register in zk, otherwise isolated from cluster")
	flag.BoolVar(&Options.DryRun, "dryrun", false, "dry run mode")
	flag.BoolVar(&Options.HintedHandoffBufio, "hhbuf", false, "enable hinted handoff bufio")
	flag.BoolVar(&Options.EnableHintedHandoff, "hh", true, "enable hinted handoff for full pub availability")
	flag.BoolVar(&Options.PermitUnregisteredGroup, "unregrp", false, "permit sub group usage without being registered")
	flag.BoolVar(&Options.PermitStandbySub, "standbysub", false, "permits sub threads exceed partitions")
	flag.BoolVar(&Options.EnableGzip, "gzip", false, "enable http response gzip")
	flag.BoolVar(&Options.CpuAffinity, "cpuaffinity", false, "enable cpu affinity")
	flag.BoolVar(&Options.Ratelimit, "raltelimit", false, "enable rate limit")
	flag.BoolVar(&Options.EnableHttpPanicRecover, "httppanic", true, "enable http handler panic recover")
	flag.BoolVar(&Options.DisableMetrics, "metricsoff", false, "disable metrics reporter")
	flag.IntVar(&Options.HttpHeaderMaxBytes, "maxheader", 4<<10, "http header max size in bytes")
	flag.Int64Var(&Options.MaxPubSize, "maxpub", 512<<10, "max Pub message size")
	flag.Int64Var(&Options.MaxJobSize, "maxjob", 16<<10, "max Pub job size")
	flag.IntVar(&Options.MinPubSize, "minpub", 1, "min Pub message size")
	flag.IntVar(&Options.MaxRequestPerConn, "maxreq", -1, "max request per connection")
	flag.IntVar(&Options.AssignJobShardId, "shardid", 1, "how to assign shard id for new app")
	flag.IntVar(&Options.MaxMsgTagLen, "tagsz", 1024, "max message tag length permitted")
	// kafka Fetch maxFetchSize=1MB, so if our msg agv size is 250B, batch size can be 4000
	flag.IntVar(&Options.MaxSubBatchSize, "maxbatch", 4000, "max sub batch size")
	flag.IntVar(&Options.LogRotateSize, "logsize", 10<<30, "max unrotated log file size")
	flag.Int64Var(&Options.PubQpsLimit, "publimit", 60*10000, "pub qps limit per minute per ip")
	flag.IntVar(&Options.PubPoolCapcity, "pubpool", 100, "pub connection pool capacity")
	flag.IntVar(&Options.MaxClients, "maxclient", 100000, "max concurrent connections")
	flag.DurationVar(&Options.OffsetCommitInterval, "offsetcommit", time.Minute, "consumer offset commit interval")
	flag.DurationVar(&Options.HttpReadTimeout, "httprtimeout", time.Minute*5, "http server read timeout")
	flag.DurationVar(&Options.HttpWriteTimeout, "httpwtimeout", time.Minute, "http server write timeout")
	flag.DurationVar(&Options.SubTimeout, "subtimeout", time.Second*30, "sub timeout before send http 204")
	flag.DurationVar(&Options.ReporterInterval, "report", time.Minute, "reporter flush interval")
	flag.DurationVar(&Options.BadClientPunishDuration, "punish", time.Second*3, "punish bad client by sleep")
	flag.DurationVar(&Options.MetaRefresh, "metarefresh", time.Minute*10, "meta data refresh interval")
	flag.DurationVar(&Options.ManagerRefresh, "manrefresh", time.Minute*5, "manager integration refresh interval")
	flag.DurationVar(&Options.PubPoolIdleTimeout, "pubpoolidle", 0, "pub pool connect idle timeout")

	flag.Parse()
}

func ValidateFlags() {
	if Options.KillFile != "" {
		return
	}

	if Options.ShowVersion {
		fmt.Fprintf(os.Stderr, "%s-%s\n", gafka.Version, gafka.BuildId)
		os.Exit(0)
	}

	if gafka.BuildId == "" {
		fmt.Fprintf(os.Stderr, "empty BuildId, please rebuild with build.sh\n")
		//os.Exit(1)
	}

	if Options.Zone == "" {
		fmt.Fprintf(os.Stderr, "-zone required\n")
		os.Exit(1)
	}
}
<commit_msg>for consistent option flag name<commit_after>package gateway

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
)

var (
	Options struct {
		Id                      string
		Zone                    string
		ConfigFile              string
		PubHttpAddr             string
		PubHttpsAddr            string
		SubHttpAddr             string
		SubHttpsAddr            string
		ManHttpAddr             string
		ManHttpsAddr            string
		DebugHttpAddr           string
		Store                   string
		JobStore                string
		ManagerStore            string
		PidFile                 string
		CertFile                string
		KeyFile                 string
		LogFile                 string
		LogLevel                string
		CrashLogFile            string
		DummyCluster            string
		InfluxServer            string
		InfluxDbName            string
		KillFile                string
		HintedHandoffType       string
		HintedHandoffDir        string
		ShowVersion             bool
		Ratelimit               bool
		PermitStandbySub        bool
		DisableMetrics          bool
		EnableHintedHandoff     bool
		HintedHandoffBufio      bool
		FlushHintedOffOnly      bool
		AuditPub                bool
		AuditSub                bool
		EnableGzip              bool
		DryRun                  bool
		CpuAffinity             bool
		EnableAccessLog         bool
		EnableHttpPanicRecover  bool
		GolangTrace             bool
		PermitUnregisteredGroup bool
		UseCompress             bool
		Debug                   bool
		EnableRegistry          bool
		HttpHeaderMaxBytes      int
		MaxPubSize              int64
		MaxJobSize              int64
		LogRotateSize           int
		MaxMsgTagLen            int
		MinPubSize              int
		PubQpsLimit             int64
		MaxSubBatchSize         int
		MaxClients              int
		MaxRequestPerConn       int // to make load balancer distribute request even for persistent conn
		PubPoolCapcity          int
		AssignJobShardId        int // how to assign shard id for new app
		PubPoolIdleTimeout      time.Duration
		SubTimeout              time.Duration
		OffsetCommitInterval    time.Duration
		BadClientPunishDuration time.Duration
		ReporterInterval        time.Duration
		MetaRefresh             time.Duration
		ManagerRefresh          time.Duration
		HttpReadTimeout         time.Duration
		HttpWriteTimeout        time.Duration
	}
)

func ParseFlags() {
	ip, err := ctx.LocalIP()
	if err != nil {
		panic(err)
	}

	var (
		defaultPubHttpAddr  = fmt.Sprintf("%s:9191", ip.String())
		defaultPubHttpsAddr = ""
		defaultSubHttpAddr  = fmt.Sprintf("%s:9192", ip.String())
		defaultSubHttpsAddr = ""
		defaultManHttpAddr  = fmt.Sprintf("%s:9193", ip.String())
		defaultManHttpsAddr = ""
	)

	flag.StringVar(&Options.Id, "id", "", "kateway id, the id must be unique within a host")
	flag.StringVar(&Options.Zone, "zone", "", "kafka zone name")
	flag.StringVar(&Options.PubHttpAddr, "pubhttp", defaultPubHttpAddr, "pub http bind addr")
	flag.StringVar(&Options.PubHttpsAddr, "pubhttps", defaultPubHttpsAddr, "pub https bind addr")
	flag.StringVar(&Options.SubHttpAddr, "subhttp", defaultSubHttpAddr, "sub http bind addr")
	flag.StringVar(&Options.SubHttpsAddr, "subhttps", defaultSubHttpsAddr, "sub https bind addr")
	flag.StringVar(&Options.ManHttpAddr, "manhttp", defaultManHttpAddr, "management http bind addr")
	flag.StringVar(&Options.ManHttpsAddr, "manhttps", defaultManHttpsAddr, "management https bind addr")
	flag.StringVar(&Options.LogLevel, "level", "trace", "log level")
	flag.StringVar(&Options.LogFile, "log", "stdout", "log file, default stdout")
	flag.StringVar(&Options.CrashLogFile, "crashlog", "", "crash log")
	flag.StringVar(&Options.CertFile, "certfile", "", "cert file path")
	flag.StringVar(&Options.PidFile, "pid", "", "pid file")
	flag.StringVar(&Options.KeyFile, "keyfile", "", "key file path")
	flag.StringVar(&Options.DebugHttpAddr, "debughttp", "", "debug http bind addr")
	flag.StringVar(&Options.Store, "store", "kafka", "message underlying store")
	flag.StringVar(&Options.HintedHandoffType, "hhtype", "disk", "underlying hinted handoff")
	flag.StringVar(&Options.HintedHandoffDir, "hhdirs", "hhdata", "hinted handoff dirs seperated by comma")
	flag.BoolVar(&Options.FlushHintedOffOnly, "hhflush", false, "flush hinted handoff and exit")
	flag.StringVar(&Options.JobStore, "jstore", "mysql", "job underlying store")
	flag.StringVar(&Options.DummyCluster, "dummycluster", "me", "dummy store's cluster name")
	flag.StringVar(&Options.ManagerStore, "mstore", "mysql", "store integration with manager")
	flag.StringVar(&Options.ConfigFile, "conf", "", "config file, defaults $HOME/.gafka.cf")
	flag.StringVar(&Options.KillFile, "kill", "", "kill running kateway by pid file")
	flag.StringVar(&Options.InfluxServer, "influxdbaddr", "", "influxdb server address for the metrics reporter")
	flag.StringVar(&Options.InfluxDbName, "influxdbname", "pubsub", "influxdb db name")
	flag.BoolVar(&Options.ShowVersion, "version", false, "show version and exit")
	flag.BoolVar(&Options.Debug, "debug", false, "enable debug mode")
	flag.BoolVar(&Options.GolangTrace, "gotrace", false, "go tool trace")
	flag.BoolVar(&Options.AuditPub, "auditpub", true, "enable Pub audit")
	flag.BoolVar(&Options.AuditSub, "auditsub", true, "enable Sub audit")
	flag.BoolVar(&Options.UseCompress, "snappy", false, "backend store will snappy compress messages")
	flag.BoolVar(&Options.EnableAccessLog, "accesslog", false, "en(dis)able access log")
	flag.BoolVar(&Options.EnableRegistry, "withreg", true, "self register in zk, otherwise isolated from cluster")
	flag.BoolVar(&Options.DryRun, "dryrun", false, "dry run mode")
	flag.BoolVar(&Options.HintedHandoffBufio, "hhbuf", false, "enable hinted handoff bufio")
	flag.BoolVar(&Options.EnableHintedHandoff, "hh", true, "enable hinted handoff for full pub availability")
	flag.BoolVar(&Options.PermitUnregisteredGroup, "unregrp", false, "permit sub group usage without being registered")
	flag.BoolVar(&Options.PermitStandbySub, "standbysub", false, "permits sub threads exceed partitions")
	flag.BoolVar(&Options.EnableGzip, "gzip", false, "enable http response gzip")
	flag.BoolVar(&Options.CpuAffinity, "cpuaffinity", false, "enable cpu affinity")
	flag.BoolVar(&Options.Ratelimit, "raltelimit", false, "enable rate limit")
	flag.BoolVar(&Options.EnableHttpPanicRecover, "httppanic", true, "enable http handler panic recover")
	flag.BoolVar(&Options.DisableMetrics, "metricsoff", false, "disable metrics reporter")
	flag.IntVar(&Options.HttpHeaderMaxBytes, "maxheader", 4<<10, "http header max size in bytes")
	flag.Int64Var(&Options.MaxPubSize, "maxpub", 512<<10, "max Pub message size")
	flag.Int64Var(&Options.MaxJobSize, "maxjob", 16<<10, "max Pub job size")
	flag.IntVar(&Options.MinPubSize, "minpub", 1, "min Pub message size")
	flag.IntVar(&Options.MaxRequestPerConn, "maxreq", -1, "max request per connection")
	flag.IntVar(&Options.AssignJobShardId, "shardid", 1, "how to assign shard id for new app")
	flag.IntVar(&Options.MaxMsgTagLen, "tagsz", 1024, "max message tag length permitted")
	// kafka Fetch maxFetchSize=1MB, so if our msg agv size is 250B, batch size can be 4000
	flag.IntVar(&Options.MaxSubBatchSize, "maxbatch", 4000, "max sub batch size")
	flag.IntVar(&Options.LogRotateSize, "logsize", 10<<30, "max unrotated log file size")
	flag.Int64Var(&Options.PubQpsLimit, "publimit", 60*10000, "pub qps limit per minute per ip")
	flag.IntVar(&Options.PubPoolCapcity, "pubpool", 100, "pub connection pool capacity")
	flag.IntVar(&Options.MaxClients, "maxclient", 100000, "max concurrent connections")
	flag.DurationVar(&Options.OffsetCommitInterval, "offsetcommit", time.Minute, "consumer offset commit interval")
	flag.DurationVar(&Options.HttpReadTimeout, "httprtimeout", time.Minute*5, "http server read timeout")
	flag.DurationVar(&Options.HttpWriteTimeout, "httpwtimeout", time.Minute, "http server write timeout")
	flag.DurationVar(&Options.SubTimeout, "subtimeout", time.Second*30, "sub timeout before send http 204")
	flag.DurationVar(&Options.ReporterInterval, "report", time.Minute, "reporter flush interval")
	flag.DurationVar(&Options.BadClientPunishDuration, "punish", time.Second*3, "punish bad client by sleep")
	flag.DurationVar(&Options.MetaRefresh, "metarefresh", time.Minute*10, "meta data refresh interval")
	flag.DurationVar(&Options.ManagerRefresh, "manrefresh", time.Minute*5, "manager integration refresh interval")
	flag.DurationVar(&Options.PubPoolIdleTimeout, "pubpoolidle", 0, "pub pool connect idle timeout")

	flag.Parse()
}

func ValidateFlags() {
	if Options.KillFile != "" {
		return
	}

	if Options.ShowVersion {
		fmt.Fprintf(os.Stderr, "%s-%s\n", gafka.Version, gafka.BuildId)
		os.Exit(0)
	}

	if gafka.BuildId == "" {
		fmt.Fprintf(os.Stderr, "empty BuildId, please rebuild with build.sh\n")
		//os.Exit(1)
	}

	if Options.Zone == "" {
		fmt.Fprintf(os.Stderr, "-zone required\n")
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package monitor

import (
	"flag"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/docker/leadership"
	"github.com/docker/libkv/store"
	"github.com/docker/libkv/store/zookeeper"
	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/telementry"
	"github.com/funkygao/gafka/telementry/influxdb"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/golib/signal"
	log "github.com/funkygao/log4go"
	"github.com/julienschmidt/httprouter"
)

// Monitor is the engine that will start/stop plugin watchers.
// It itself is an implementation of Context.
type Monitor struct {
	influxdbAddr   string
	influxdbDbName string
	apiAddr        string

	router *httprouter.Router
	zkzone *zk.ZkZone

	candidate *leadership.Candidate

	inflight *sync.WaitGroup
	stop     chan struct{} // broadcast to all watchers to stop, but might restart again
	quit     chan struct{}
	leader   bool
}

func (this *Monitor) Init() {
	var logFile, zone string
	flag.StringVar(&logFile, "log", "stdout", "log filename")
	flag.StringVar(&zone, "z", "", "zone, required")
	flag.StringVar(&this.apiAddr, "http", ":10025", "api http server addr")
	flag.StringVar(&this.influxdbAddr, "influxAddr", "", "influxdb addr, required")
	flag.StringVar(&this.influxdbDbName, "db", "", "influxdb db name, required")
	flag.Parse()

	if zone == "" || this.influxdbDbName == "" || this.influxdbAddr == "" {
		panic("run help ")
	}

	ctx.LoadFromHome()
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	this.quit = make(chan struct{})

	// export RESTful api
	this.router = httprouter.New()
	this.router.GET("/metrics", this.metricsHandler)

	if logFile == "stdout" {
		log.AddFilter("stdout", log.INFO, log.NewConsoleLogWriter())
	} else {
		log.DeleteFilter("stdout")

		filer := log.NewFileLogWriter(logFile, true, false, 0644)
		filer.SetFormat("[%d %T] [%L] (%S) %M")
		log.AddFilter("file", log.INFO, filer)
	}

	rc, err := influxdb.NewConfig(this.influxdbAddr, this.influxdbDbName, "", "", time.Minute)
	if err != nil {
		panic(err)
	}
	telementry.Default = influxdb.New(metrics.DefaultRegistry, rc)
}

func (this *Monitor) Stop() {
	if this.leader {
		this.leader = false

		log.Info("stopping all watchers ...")
		close(this.stop)

		log.Info("stopping reporter and flush all metrics...")
		telementry.Default.Stop()

		this.candidate.Stop()
		log.Info("election stopped")

		// because of github.com/docker/leadership problem, /_kguard/leader is left
		// even when we stop election.
		// so we have to manually clean it here
		this.zkzone.Conn().Set(zk.KguardLeaderPath, []byte{}, -1)
	}
}

func (this *Monitor) Start() {
	this.leader = true

	this.stop = make(chan struct{})

	go func() {
		log.Info("telementry started: %s", telementry.Default.Name())

		if err := telementry.Default.Start(); err != nil {
			log.Error("telementry: %v", err)
		}
	}()

	this.inflight = new(sync.WaitGroup)
	for name, watcherFactory := range registeredWatchers {
		watcher := watcherFactory()
		watcher.Init(this)

		log.Info("created and starting watcher: %s", name)

		this.inflight.Add(1)
		go watcher.Run()
	}

	log.Info("all watchers ready!")

	<-this.stop
	this.inflight.Wait()

	log.Info("all watchers stopped")
}

func (this *Monitor) ServeForever() {
	defer this.zkzone.Close()

	log.Info("kguard[%s@%s] starting...", gafka.BuildId, gafka.BuiltAt)

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("kguard[%s@%s] received signal: %s", gafka.BuildId, gafka.BuiltAt, strings.ToUpper(sig.String()))

		this.Stop()
		close(this.quit)
	}, syscall.SIGINT, syscall.SIGTERM)

	// start the api server
	apiServer := &http.Server{
		Addr:    this.apiAddr,
		Handler: this.router,
	}
	apiListener, err := net.Listen("tcp", this.apiAddr)
	if err == nil {
		go apiServer.Serve(apiListener)
	} else {
		log.Error("api http server: %v", err)
	}

	backend, err := zookeeper.New(this.zkzone.ZkAddrList(), &store.Config{})
	if err != nil {
		panic(err)
	}

	ip, _ := ctx.LocalIP()
	this.candidate = leadership.NewCandidate(backend, zk.KguardLeaderPath, ip.String(), 25*time.Second)
	electedCh, errCh := this.candidate.RunForElection()
	if err != nil {
		panic("Cannot run for election, store is probably down")
	}

	for {
		select {
		case isElected := <-electedCh:
			if isElected {
				log.Info("Won the election, starting all watchers")

				this.Start()
			} else {
				log.Warn("Fails the election, watching election events...")
				this.Stop()
			}

		case err := <-errCh:
			if err != nil {
				log.Error("Error during election: %v", err)
			}

		case <-this.quit:
			log.Info("kguard[%s@%s] bye!", gafka.BuildId, gafka.BuiltAt)
			log.Close()
			return
		}
	}

}

func (this *Monitor) ZkZone() *zk.ZkZone {
	return this.zkzone
}

func (this *Monitor) StopChan() <-chan struct{} {
	return this.stop
}

func (this *Monitor) Inflight() *sync.WaitGroup {
	return this.inflight
}

func (this *Monitor) InfluxAddr() string {
	return this.influxdbAddr
}

func (this *Monitor) InfluxDB() string {
	return this.influxdbDbName
}
<commit_msg>log the cleanup result<commit_after>package monitor

import (
	"flag"
	"net"
	"net/http"
	"os"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/docker/leadership"
	"github.com/docker/libkv/store"
	"github.com/docker/libkv/store/zookeeper"
	"github.com/funkygao/gafka"
	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gafka/telementry"
	"github.com/funkygao/gafka/telementry/influxdb"
	"github.com/funkygao/gafka/zk"
	"github.com/funkygao/go-metrics"
	"github.com/funkygao/golib/signal"
	log "github.com/funkygao/log4go"
	"github.com/julienschmidt/httprouter"
)

// Monitor is the engine that will start/stop plugin watchers.
// It itself is an implementation of Context.
type Monitor struct {
	influxdbAddr   string
	influxdbDbName string
	apiAddr        string

	router *httprouter.Router
	zkzone *zk.ZkZone

	candidate *leadership.Candidate

	inflight *sync.WaitGroup
	stop     chan struct{} // broadcast to all watchers to stop, but might restart again
	quit     chan struct{}
	leader   bool
}

func (this *Monitor) Init() {
	var logFile, zone string
	flag.StringVar(&logFile, "log", "stdout", "log filename")
	flag.StringVar(&zone, "z", "", "zone, required")
	flag.StringVar(&this.apiAddr, "http", ":10025", "api http server addr")
	flag.StringVar(&this.influxdbAddr, "influxAddr", "", "influxdb addr, required")
	flag.StringVar(&this.influxdbDbName, "db", "", "influxdb db name, required")
	flag.Parse()

	if zone == "" || this.influxdbDbName == "" || this.influxdbAddr == "" {
		panic("run help ")
	}

	ctx.LoadFromHome()
	this.zkzone = zk.NewZkZone(zk.DefaultConfig(zone, ctx.ZoneZkAddrs(zone)))
	this.quit = make(chan struct{})

	// export RESTful api
	this.router = httprouter.New()
	this.router.GET("/metrics", this.metricsHandler)

	if logFile == "stdout" {
		log.AddFilter("stdout", log.INFO, log.NewConsoleLogWriter())
	} else {
		log.DeleteFilter("stdout")

		filer := log.NewFileLogWriter(logFile, true, false, 0644)
		filer.SetFormat("[%d %T] [%L] (%S) %M")
		log.AddFilter("file", log.INFO, filer)
	}

	rc, err := influxdb.NewConfig(this.influxdbAddr, this.influxdbDbName, "", "", time.Minute)
	if err != nil {
		panic(err)
	}
	telementry.Default = influxdb.New(metrics.DefaultRegistry, rc)
}

func (this *Monitor) Stop() {
	if this.leader {
		this.leader = false

		log.Info("stopping all watchers ...")
		close(this.stop)

		log.Info("stopping reporter and flush all metrics...")
		telementry.Default.Stop()

		this.candidate.Stop()
		log.Info("election stopped")

		// because of github.com/docker/leadership problem, /_kguard/leader is left
		// even when we stop election.
		// so we have to manually clean it here
		if _, err := this.zkzone.Conn().Set(zk.KguardLeaderPath, []byte{}, -1); err != nil {
			log.Error("cleanup: %v", err)
		} else {
			log.Info("cleanup leader zk node done")
		}
	}
}

func (this *Monitor) Start() {
	this.leader = true

	this.stop = make(chan struct{})

	go func() {
		log.Info("telementry started: %s", telementry.Default.Name())

		if err := telementry.Default.Start(); err != nil {
			log.Error("telementry: %v", err)
		}
	}()

	this.inflight = new(sync.WaitGroup)
	for name, watcherFactory := range registeredWatchers {
		watcher := watcherFactory()
		watcher.Init(this)

		log.Info("created and starting watcher: %s", name)

		this.inflight.Add(1)
		go watcher.Run()
	}

	log.Info("all watchers ready!")

	<-this.stop
	this.inflight.Wait()

	log.Info("all watchers stopped")
}

func (this *Monitor) ServeForever() {
	defer this.zkzone.Close()

	log.Info("kguard[%s@%s] starting...", gafka.BuildId, gafka.BuiltAt)

	signal.RegisterSignalsHandler(func(sig os.Signal) {
		log.Info("kguard[%s@%s] received signal: %s", gafka.BuildId, gafka.BuiltAt, strings.ToUpper(sig.String()))

		this.Stop()
		close(this.quit)
	}, syscall.SIGINT, syscall.SIGTERM)

	// start the api server
	apiServer := &http.Server{
		Addr:    this.apiAddr,
		Handler: this.router,
	}
	apiListener, err := net.Listen("tcp", this.apiAddr)
	if err == nil {
		go apiServer.Serve(apiListener)
	} else {
		log.Error("api http server: %v", err)
	}

	backend, err := zookeeper.New(this.zkzone.ZkAddrList(), &store.Config{})
	if err != nil {
		panic(err)
	}

	ip, _ := ctx.LocalIP()
	this.candidate = leadership.NewCandidate(backend, zk.KguardLeaderPath, ip.String(), 25*time.Second)
	electedCh, errCh := this.candidate.RunForElection()
	if err != nil {
		panic("Cannot run for election, store is probably down")
	}

	for {
		select {
		case isElected := <-electedCh:
			if isElected {
				log.Info("Won the election, starting all watchers")

				this.Start()
			} else {
				log.Warn("Fails the election, watching election events...")
				this.Stop()
			}

		case err := <-errCh:
			if err != nil {
				log.Error("Error during election: %v", err)
			}

		case <-this.quit:
			log.Info("kguard[%s@%s] bye!", gafka.BuildId, gafka.BuiltAt)
			log.Close()
			return
		}
	}

}

func (this *Monitor) ZkZone() *zk.ZkZone {
	return this.zkzone
}

func (this *Monitor) StopChan() <-chan struct{} {
	return this.stop
}

func (this *Monitor) Inflight() *sync.WaitGroup {
	return this.inflight
}

func (this *Monitor) InfluxAddr() string {
	return this.influxdbAddr
}

func (this *Monitor) InfluxDB() string {
	return this.influxdbDbName
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package master

import (
	"crypto/rsa"
	"crypto/x509"
	"fmt"
	"net"
	"path"

	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
	certutil "k8s.io/kubernetes/pkg/util/cert"
)

func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
	}

	config := certutil.Config{
		CommonName: "kubernetes",
	}

	cert, err := certutil.NewSelfSignedCACert(config, key)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err)
	}

	return key, cert, nil
}

func newServerKeyAndCert(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unabel to create private key [%v]", err)
	}

	internalAPIServerFQDN := []string{
		"kubernetes",
		"kubernetes.default",
		"kubernetes.default.svc",
		fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
	}

	_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
	if err != nil {
		return nil, nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
	}
	internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err)
	}

	altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
	altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)

	config := certutil.Config{
		CommonName: "kube-apiserver",
		AltNames:   altNames,
	}
	cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
	}

	return key, cert, nil
}

func newClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
	}

	config := certutil.Config{
		CommonName: "kubernetes-admin",
	}
	cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
	}

	return key, cert, nil
}

func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error {
	publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name)

	if key != nil {
		if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
			return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err)
		}
		if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil {
			if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil {
				return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err)
			}
		} else {
			return fmt.Errorf("unable to encode public key to PEM [%v]", err)
		}
	}

	if cert != nil {
		if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
			return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err)
		}
	}

	return nil
}

func pathsKeysCerts(pkiPath, name string) (string, string, string) {
	return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)),
		path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)),
		path.Join(pkiPath, fmt.Sprintf("%s.pem", name))
}

func newServiceAccountKey() (*rsa.PrivateKey, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, err
	}
	return key, nil
}

// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for
// signing service account tokens. It returns CA key and certificate, which is convenient for use with
// client config funcs.
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) (*rsa.PrivateKey, *x509.Certificate, error) {
	var (
		err      error
		altNames certutil.AltNames
	)

	for _, a := range cfg.API.AdvertiseAddresses {
		if ip := net.ParseIP(a); ip != nil {
			altNames.IPs = append(altNames.IPs, ip)
		} else {
			return nil, nil, fmt.Errorf("could not parse ip %q", a)
		}
	}
	altNames.DNSNames = append(altNames.DNSNames, cfg.API.ExternalDNSNames...)

	pkiPath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath)

	caKey, caCert, err := newCertificateAuthority()
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating CA keys and certificate - %v", err)
	}

	if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving CA keys and certificate - %v", err)
	}
	fmt.Printf("<master/pki> generated Certificate Authority key and certificate:\n%s\n", certutil.FormatCert(caCert))
	pub, prv, cert := pathsKeysCerts(pkiPath, "ca")
	fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)

	apiKey, apiCert, err := newServerKeyAndCert(cfg, caCert, caKey, altNames)
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating API server keys and certificate - %v", err)
	}

	if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving API server keys and certificate - %v", err)
	}
	fmt.Printf("<master/pki> generated API Server key and certificate:\n%s\n", certutil.FormatCert(apiCert))
	pub, prv, cert = pathsKeysCerts(pkiPath, "apiserver")
	fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)

	saKey, err := newServiceAccountKey()
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating service account signing keys [%v]", err)
	}
	if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving service account signing keys - %v", err)
	}
	fmt.Printf("<master/pki> generated Service Account Signing keys:\n")
	pub, prv, _ = pathsKeysCerts(pkiPath, "sa")
	fmt.Printf("Public: %s\nPrivate: %s\n", pub, prv)

	fmt.Printf("<master/pki> created keys and certificates in %q\n", pkiPath)
	return caKey, caCert, nil
}
<commit_msg>kubeadm: fixed typo in kubeadm/app/master/pki.go<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package master

import (
	"crypto/rsa"
	"crypto/x509"
	"fmt"
	"net"
	"path"

	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
	certutil "k8s.io/kubernetes/pkg/util/cert"
)

func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
	}

	config := certutil.Config{
		CommonName: "kubernetes",
	}

	cert, err := certutil.NewSelfSignedCACert(config, key)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err)
	}

	return key, cert, nil
}

func newServerKeyAndCert(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
	}

	internalAPIServerFQDN := []string{
		"kubernetes",
		"kubernetes.default",
		"kubernetes.default.svc",
		fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
	}

	_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
	if err != nil {
		return nil, nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
	}
	internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err)
	}

	altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
	altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)

	config := certutil.Config{
		CommonName: "kube-apiserver",
		AltNames:   altNames,
	}
	cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
	}

	return key, cert, nil
}

func newClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
	}

	config := certutil.Config{
		CommonName: "kubernetes-admin",
	}
	cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
	if err != nil {
		return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
	}

	return key, cert, nil
}

func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error {
	publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name)

	if key != nil {
		if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
			return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err)
		}
		if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil {
			if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil {
				return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err)
			}
		} else {
			return fmt.Errorf("unable to encode public key to PEM [%v]", err)
		}
	}

	if cert != nil {
		if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
			return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err)
		}
	}

	return nil
}

func pathsKeysCerts(pkiPath, name string) (string, string, string) {
	return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)),
		path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)),
		path.Join(pkiPath, fmt.Sprintf("%s.pem", name))
}

func newServiceAccountKey() (*rsa.PrivateKey, error) {
	key, err := certutil.NewPrivateKey()
	if err != nil {
		return nil, err
	}
	return key, nil
}

// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for
// signing service account tokens. It returns CA key and certificate, which is convenient for use with
// client config funcs.
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) (*rsa.PrivateKey, *x509.Certificate, error) {
	var (
		err      error
		altNames certutil.AltNames
	)

	for _, a := range cfg.API.AdvertiseAddresses {
		if ip := net.ParseIP(a); ip != nil {
			altNames.IPs = append(altNames.IPs, ip)
		} else {
			return nil, nil, fmt.Errorf("could not parse ip %q", a)
		}
	}
	altNames.DNSNames = append(altNames.DNSNames, cfg.API.ExternalDNSNames...)

	pkiPath := path.Join(kubeadmapi.GlobalEnvParams.HostPKIPath)

	caKey, caCert, err := newCertificateAuthority()
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating CA keys and certificate - %v", err)
	}

	if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving CA keys and certificate - %v", err)
	}
	fmt.Printf("<master/pki> generated Certificate Authority key and certificate:\n%s\n", certutil.FormatCert(caCert))
	pub, prv, cert := pathsKeysCerts(pkiPath, "ca")
	fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)

	apiKey, apiCert, err := newServerKeyAndCert(cfg, caCert, caKey, altNames)
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating API server keys and certificate - %v", err)
	}

	if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving API server keys and certificate - %v", err)
	}
	fmt.Printf("<master/pki> generated API Server key and certificate:\n%s\n", certutil.FormatCert(apiCert))
	pub, prv, cert = pathsKeysCerts(pkiPath, "apiserver")
	fmt.Printf("Public: %s\nPrivate: %s\nCert: %s\n", pub, prv, cert)

	saKey, err := newServiceAccountKey()
	if err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while creating service account signing keys [%v]", err)
	}
	if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
		return nil, nil, fmt.Errorf("<master/pki> failure while saving service account signing keys - %v", err)
	}
	fmt.Printf("<master/pki> generated Service Account Signing keys:\n")
	pub, prv, _ = pathsKeysCerts(pkiPath, "sa")
	fmt.Printf("Public: %s\nPrivate: %s\n", pub, prv)

	fmt.Printf("<master/pki> created keys and certificates in %q\n", pkiPath)
	return caKey, caCert, nil
}
<|endoftext|>


<commit_before>package resource

import (
	"fmt"

	"github.com/hashicorp/hcl/hcl/ast"
)

// Provider is used to create new resources from an HCL AST object item
type Provider func(item *ast.ObjectItem) (Resource, error)

// Registry contains all known resource types and their providers
var registry = make(map[string]Provider)

// Register registers a resource type and it's provider
func Register(name string, p Provider) error {
	_, ok := registry[name]
	if ok {
		return fmt.Errorf("Resource provider for '%s' is already registered", name)
	}

	registry[name] = p

	return nil
}

// Get retrieves the provider for a given resource type
func Get(name string) (Provider, bool) {
	p, ok := registry[name]

	return p, ok
}

// Resource is the base interface type for all resources
type Resource interface {
	// Type of the resource
	Type() string

	// ID returns the unique identifier of a resource
	ID() string

	// Validates the resource
	Validate() error

	// Returns the wanted resources/dependencies
	Want() []string

	// Evaluates the resource and returns it's state
	Evaluate() (State, error)

	// Creates the resource
	Create() error

	// Deletes the resource
	Delete() error

	// Updates the resource
	Update() error
}

// BaseResource is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type BaseResource struct {
	// Name of the resource
	Name string `json:"name"`

	// Desired state of the resource
	State string `json:"state"`

	// Type of the resource
	ResourceType string `json:"-"`

	// Resource dependencies
	WantResource []string `json:"want,omitempty" hcl:"want"`
}

// Type returns the resource type name
func (b *BaseResource) Type() string {
	return b.ResourceType
}

// ID returns the unique resource id
func (b *BaseResource) ID() string {
	return fmt.Sprintf("%s[%s]", b.ResourceType, b.Name)
}

// Validate checks if the resource contains valid information
func (b *BaseResource) Validate() error {
	if b.Name == "" {
		return fmt.Errorf("Missing name for resource %s", b.ID())
	}

	return nil
}

// Want returns the wanted resources/dependencies
func (b *BaseResource) Want() []string {
	return b.WantResource
}
<commit_msg>Add HCL tags to BaseResource fields<commit_after>package resource

import (
	"fmt"

	"github.com/hashicorp/hcl/hcl/ast"
)

// Provider is used to create new resources from an HCL AST object item
type Provider func(item *ast.ObjectItem) (Resource, error)

// Registry contains all known resource types and their providers
var registry = make(map[string]Provider)

// Register registers a resource type and it's provider
func Register(name string, p Provider) error {
	_, ok := registry[name]
	if ok {
		return fmt.Errorf("Resource provider for '%s' is already registered", name)
	}

	registry[name] = p

	return nil
}

// Get retrieves the provider for a given resource type
func Get(name string) (Provider, bool) {
	p, ok := registry[name]

	return p, ok
}

// Resource is the base interface type for all resources
type Resource interface {
	// Type of the resource
	Type() string

	// ID returns the unique identifier of a resource
	ID() string

	// Validates the resource
	Validate() error

	// Returns the wanted resources/dependencies
	Want() []string

	// Evaluates the resource and returns it's state
	Evaluate() (State, error)

	// Creates the resource
	Create() error

	// Deletes the resource
	Delete() error

	// Updates the resource
	Update() error
}

// BaseResource is the base resource type for all resources
// The purpose of this type is to be embedded into other resources
// Partially implements the Resource interface
type BaseResource struct {
	// Name of the resource
	Name string `hcl:"name" json:"name"`

	// Desired state of the resource
	State string `hcl:"state" json:"state"`

	// Type of the resource
	ResourceType string `json:"-"`

	// Resource dependencies
	WantResource []string `hcl:"want" json:"want,omitempty"`
}

// Type returns the resource type name
func (b *BaseResource) Type() string {
	return b.ResourceType
}

// ID returns the unique resource id
func (b *BaseResource) ID() string {
	return fmt.Sprintf("%s[%s]", b.ResourceType, b.Name)
}

// Validate checks if the resource contains valid information
func (b *BaseResource) Validate() error {
	if b.Name == "" {
		return fmt.Errorf("Missing name for resource %s", b.ID())
	}

	return nil
}

// Want returns the wanted resources/dependencies
func (b *BaseResource) Want() []string {
	return b.WantResource
}
<|endoftext|>


<commit_before><commit_msg>Add FetchTest<commit_after><|endoftext|>


<commit_before>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package archive

import (
	"os"

	"pault.ag/go/debian/control"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/version"
)

// Package {{{

type Package struct {
	control.Paragraph

	Package       string `required:"true"`
	Source        string
	Version       version.Version `required:"true"`
	Section       string
	Priority      string
	Architecture  dependency.Arch `required:"true"`
	Essential     string
	InstalledSize int    `control:"Installed-Size"`
	Maintainer    string `required:"true"`
	Description   string `required:"true"`
	Homepage      string

	Filename       string `required:"true"`
	Size           int    `required:"true"`
	MD5sum         string
	SHA1           string
	SHA256         string
	DescriptionMD5 string `control:"Description-md5"`
}

// }}}

// Packages {{{

type Packages struct {
	decoder *control.Decoder
}

// Next {{{

func (p *Packages) Next() (*Package, error) {
	next := Package{}
	return &next, p.decoder.Decode(&next)
}

// }}}

// LoadPackages {{{

func LoadPackages(path string) (*Packages, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	/* Packages files aren't signed */
	decoder, err := control.NewDecoder(fd, nil)
	if err != nil {
		return nil, err
	}
	return &Packages{decoder: decoder}, nil
}

// }}}

// }}}

// vim: foldmethod=marker
<commit_msg>fiddle<commit_after>/* {{{ Copyright (c) Paul R. Tagliamonte <paultag@debian.org>, 2015
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE. }}} */

package archive

import (
	"os"
	"strconv"

	"pault.ag/go/debian/control"
	"pault.ag/go/debian/deb"
	"pault.ag/go/debian/dependency"
	"pault.ag/go/debian/version"
)

// Package {{{

type Package struct {
	control.Paragraph

	Package       string `required:"true"`
	Source        string
	Version       version.Version `required:"true"`
	Section       string
	Priority      string
	Architecture  dependency.Arch `required:"true"`
	Essential     string
	InstalledSize int    `control:"Installed-Size"`
	Maintainer    string `required:"true"`
	Description   string `required:"true"`
	Homepage      string

	Filename       string `required:"true"`
	Size           int    `required:"true"`
	MD5sum         string
	SHA1           string
	SHA256         string
	DescriptionMD5 string `control:"Description-md5"`
}

// PackageFromDeb {{{

func PackageFromDeb(debFile deb.Deb) (*Package, error) {
	pkg := Package{}

	paragraph := debFile.Control.Paragraph
	paragraph.Set("Filename", debFile.Path)
	/* Now, let's do some magic */

	fd, err := os.Open(debFile.Path)
	if err != nil {
		return nil, err
	}
	stat, err := fd.Stat()
	if err != nil {
		return nil, err
	}
	paragraph.Set("Size", strconv.Itoa(int(stat.Size())))

	return &pkg, control.UnpackFromParagraph(debFile.Control.Paragraph, &pkg)
}

// }}}

// }}}

// Packages {{{

type Packages struct {
	decoder *control.Decoder
}

// Next {{{

func (p *Packages) Next() (*Package, error) {
	next := Package{}
	return &next, p.decoder.Decode(&next)
}

// }}}

// LoadPackages {{{

func LoadPackages(path string) (*Packages, error) {
	fd, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	/* Packages files aren't signed */
	decoder, err := control.NewDecoder(fd, nil)
	if err != nil {
		return nil, err
	}
	return &Packages{decoder: decoder}, nil
}

// }}}

// }}}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 ldap

import (
	"atlantis/crypto"
	"crypto/tls"
	"errors"
	"github.com/mavricknz/ldap"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	SessionDestroyChan   chan *Request
	BaseDomain           string
	LdapServer           string
	LdapPort             uint16
	TlsConfig            *tls.Config
	skipLogin            bool
	SessionMap           map[string]map[string]*Session // map user -> (map secret -> *Session)
	AppClass             string
	UsernameAttr         string
	TeamAdminAttr        string
	AllowedAppAttr       string
	AllowedAppCommonName string
	TeamClass            string
	UserOu               string
	TeamOu               string
	UserCommonName       string
	TeamCommonName       string
	SuperUserGroup       string
	UserClass            string
	UserClassAttr        string
	SkipAuthorization    bool
	SearchUserDn         string
	SearchUserPwd	     string
	TeamBlackList        []string
)

type Request struct {
	User     string
	Secret   string
	LoggedIn bool
	RespChan chan bool
}

type Session struct {
	LDAPConn *ldap.LDAPConnection
	Team     []string
	Timer    *time.Timer
}

func Init(lserver string, lport uint16, baseDomain string) {
	if lserver == "" {
		// if we're being initialized empty, then don't try to log people in
		skipLogin = true
		return
	}
	TlsConfig = &tls.Config{}
	// TODO : Remove InsecureSkipVerify
	TlsConfig.InsecureSkipVerify = true
	LdapServer = lserver
	LdapPort = lport
	BaseDomain = baseDomain
	SessionMap = make(map[string]map[string]*Session)
	SessionDestroyChan = make(chan *Request)
	go SessionExpiryRoutine()
}

func CreateSession(req *Request, lc *ldap.LDAPConnection, team []string) {
	if SessionMap[req.User] == nil {
		SessionMap[req.User] = map[string]*Session{req.Secret: &Session{LDAPConn: lc, Team: team,
			Timer: time.AfterFunc(30*time.Minute, func() {
				SessionDestroyChan <- req
			})},
		}
	} else if SessionMap[req.User][req.Secret] == nil {
		SessionMap[req.User][req.Secret] = &Session{LDAPConn: lc, Team: team, Timer: time.AfterFunc(30*time.Minute, func() {
			SessionDestroyChan <- req
		})}
	} else {
		SessionMap[req.User][req.Secret].Timer.Reset(30*time.Minute)
	}
}

func LookupSession(req *Request) {
	req.LoggedIn = false
	if SessionMap[req.User] != nil && req.Secret != "" {
		if SessionMap[req.User][req.Secret] != nil {
			req.LoggedIn = true
		}
	}
}

func LookupConnection(user, secret string) *ldap.LDAPConnection {
	if SessionMap[user] != nil && SessionMap[user][secret] != nil {
		return SessionMap[user][secret].LDAPConn
	}
	return nil
}

func LookupTeam(user, secret string) []string {
	if SessionMap[user] != nil && SessionMap[user][secret] != nil {
		return SessionMap[user][secret].Team
	}
	return nil
}

func SessionExpiryRoutine() {
	for {
		select {
		case req := <-SessionDestroyChan:
			SessionMap[req.User][req.Secret].LDAPConn.Close()
			delete(SessionMap[req.User], req.Secret)
			break
		}
	}
}

func Login(user, pass, secret string) (string, error) {
	if skipLogin {
		return "dummysecret", nil // just let everything pass
	}
	// Checking if we are already logged in
	var LDAPConn *ldap.LDAPConnection
	var err error
	req := &Request{user, secret, false, make(chan bool)}
	LookupSession(req)
	teamList := []string{}
	if !req.LoggedIn {
		LDAPConn, err = CreateLdapConn(LdapServer, LdapPort, TlsConfig)

		if err != nil {
			return "", err
		}
		err = LoginBind(user, pass, LDAPConn)
		if err != nil {
			return "", err
		}

		//bind with account with dn search enabled
		err = LoginBind(SearchUserDn, SearchUserPwd, LDAPConn)
		if err != nil {
			log.Println("Warning: ldap binding with dn search account failed; ", err)
		}

		now := strconv.FormatInt(time.Now().Unix(), 10)
		sec := string(crypto.Encrypt([]byte(pass + now)))
		re := regexp.MustCompile("[^a-zA-Z0-9]")
		sec = re.ReplaceAllString(sec, "")
		req.Secret = sec
		teamList, err = GetTeamList(LDAPConn, user)
		if err != nil {
			log.Println("Warning: get team list failed for user; ", err)
		}
	}
	CreateSession(req, LDAPConn, teamList)
	return req.Secret, nil
}

func CreateLdapConn(server string, port uint16, tlsConf *tls.Config) (*ldap.LDAPConnection, error) {
	LDAPConn := ldap.NewLDAPSSLConnection(server, port, tlsConf)
	err := LDAPConn.Connect()
	if err != nil {
		return nil, err
	}
	return LDAPConn, nil
}

func GetTeamList(LDAPConn *ldap.LDAPConnection , user string) ([]string, error){
	//should be something like (&(objectClass=posixAccount)(uid=xxxx))
        filterStr := "(&(objectClass=" + UserClass + ")(" + UserCommonName + "=" + user + "))"

	searchReq := ldap.NewSimpleSearchRequest(BaseDomain, 2, filterStr, []string{"memberOf"})
	sr, err := LDAPConn.Search(searchReq)

	ret := []string{}
	if err != nil || sr == nil {
		return ret, err
	}

	for _, entry := range sr.Entries {
		vals := entry.GetAttributeValues("memberOf")
		r, _ := regexp.Compile("^cn=([^,]+)")
		if len(vals) > 0 {
			for _, teamDn := range vals {
				substrings := strings.Split(r.FindString(teamDn), "=")

				if len(substrings) == 2 && !contains(TeamBlackList, substrings[1]) {
					ret = append(ret, substrings[1])
				}
			}
		}
	}

	return ret, nil
}

func LoginBind(user, pass string, lc *ldap.LDAPConnection) error {
	var dnInfo string
	dnInfo = UserClassAttr + "=" + user + "," + UserOu
	err := lc.Bind(dnInfo, pass)
	if err != nil {
		log.Println("ERROR : Login not Successful")
		return errors.New("Session Expired/Invalid Credentials")
	}
	return nil
}

func contains(arr []string, str string) bool {
   for _, a := range arr {
      if a == str {
         return true
      }
   }
   return false
}
<commit_msg>update todo list<commit_after>/* Copyright 2014 Ooyala, Inc. All rights reserved.
 *
 * This file is 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 ldap

import (
	"atlantis/crypto"
	"crypto/tls"
	"errors"
	"github.com/mavricknz/ldap"
	"log"
	"regexp"
	"strconv"
	"strings"
	"time"
)

var (
	SessionDestroyChan   chan *Request
	BaseDomain           string
	LdapServer           string
	LdapPort             uint16
	TlsConfig            *tls.Config
	skipLogin            bool
	SessionMap           map[string]map[string]*Session // map user -> (map secret -> *Session)
	AppClass             string
	UsernameAttr         string
	TeamAdminAttr        string
	AllowedAppAttr       string
	AllowedAppCommonName string
	TeamClass            string
	UserOu               string
	TeamOu               string
	UserCommonName       string
	TeamCommonName       string
	SuperUserGroup       string
	UserClass            string
	UserClassAttr        string
	SkipAuthorization    bool
	SearchUserDn         string
	SearchUserPwd	     string
	TeamBlackList        []string
)

type Request struct {
	User     string
	Secret   string
	LoggedIn bool
	RespChan chan bool
}

type Session struct {

	//TODO remove LDAPConn from session
	//since we can no longer re-use ldap conn with jump cloud

	LDAPConn *ldap.LDAPConnection
	Team     []string
	Timer    *time.Timer
}

func Init(lserver string, lport uint16, baseDomain string) {
	if lserver == "" {
		// if we're being initialized empty, then don't try to log people in
		skipLogin = true
		return
	}
	TlsConfig = &tls.Config{}
	// TODO : Remove InsecureSkipVerify
	TlsConfig.InsecureSkipVerify = true
	LdapServer = lserver
	LdapPort = lport
	BaseDomain = baseDomain
	SessionMap = make(map[string]map[string]*Session)
	SessionDestroyChan = make(chan *Request)
	go SessionExpiryRoutine()
}

func CreateSession(req *Request, lc *ldap.LDAPConnection, team []string) {
	if SessionMap[req.User] == nil {
		SessionMap[req.User] = map[string]*Session{req.Secret: &Session{LDAPConn: lc, Team: team,
			Timer: time.AfterFunc(30*time.Minute, func() {
				SessionDestroyChan <- req
			})},
		}
	} else if SessionMap[req.User][req.Secret] == nil {
		SessionMap[req.User][req.Secret] = &Session{LDAPConn: lc, Team: team, Timer: time.AfterFunc(30*time.Minute, func() {
			SessionDestroyChan <- req
		})}
	} else {
		SessionMap[req.User][req.Secret].Timer.Reset(30*time.Minute)
	}
}

func LookupSession(req *Request) {
	req.LoggedIn = false
	if SessionMap[req.User] != nil && req.Secret != "" {
		if SessionMap[req.User][req.Secret] != nil {
			req.LoggedIn = true
		}
	}
}

func LookupConnection(user, secret string) *ldap.LDAPConnection {
	if SessionMap[user] != nil && SessionMap[user][secret] != nil {
		return SessionMap[user][secret].LDAPConn
	}
	return nil
}

func LookupTeam(user, secret string) []string {
	if SessionMap[user] != nil && SessionMap[user][secret] != nil {
		return SessionMap[user][secret].Team
	}
	return nil
}

func SessionExpiryRoutine() {
	for {
		select {
		case req := <-SessionDestroyChan:
			SessionMap[req.User][req.Secret].LDAPConn.Close()
			delete(SessionMap[req.User], req.Secret)
			break
		}
	}
}

func Login(user, pass, secret string) (string, error) {
	if skipLogin {
		return "dummysecret", nil // just let everything pass
	}
	// Checking if we are already logged in
	var LDAPConn *ldap.LDAPConnection
	var err error
	req := &Request{user, secret, false, make(chan bool)}
	LookupSession(req)
	teamList := []string{}
	if !req.LoggedIn {
		LDAPConn, err = CreateLdapConn(LdapServer, LdapPort, TlsConfig)

		if err != nil {
			return "", err
		}
		err = LoginBind(user, pass, LDAPConn)
		if err != nil {
			return "", err
		}

		//bind with account with dn search enabled
		err = LoginBind(SearchUserDn, SearchUserPwd, LDAPConn)
		if err != nil {
			log.Println("Warning: ldap binding with dn search account failed; ", err)
		}

		now := strconv.FormatInt(time.Now().Unix(), 10)
		sec := string(crypto.Encrypt([]byte(pass + now)))
		re := regexp.MustCompile("[^a-zA-Z0-9]")
		sec = re.ReplaceAllString(sec, "")
		req.Secret = sec
		teamList, err = GetTeamList(LDAPConn, user)
		if err != nil {
			log.Println("Warning: get team list failed for user; ", err)
		}
	}
	CreateSession(req, LDAPConn, teamList)
	return req.Secret, nil
}

func CreateLdapConn(server string, port uint16, tlsConf *tls.Config) (*ldap.LDAPConnection, error) {
	LDAPConn := ldap.NewLDAPSSLConnection(server, port, tlsConf)
	err := LDAPConn.Connect()
	if err != nil {
		return nil, err
	}
	return LDAPConn, nil
}

func GetTeamList(LDAPConn *ldap.LDAPConnection , user string) ([]string, error){
	//should be something like (&(objectClass=posixAccount)(uid=xxxx))
        filterStr := "(&(objectClass=" + UserClass + ")(" + UserCommonName + "=" + user + "))"

	searchReq := ldap.NewSimpleSearchRequest(BaseDomain, 2, filterStr, []string{"memberOf"})
	sr, err := LDAPConn.Search(searchReq)

	ret := []string{}
	if err != nil || sr == nil {
		return ret, err
	}

	for _, entry := range sr.Entries {
		vals := entry.GetAttributeValues("memberOf")
		r, _ := regexp.Compile("^cn=([^,]+)")
		if len(vals) > 0 {
			for _, teamDn := range vals {
				substrings := strings.Split(r.FindString(teamDn), "=")

				if len(substrings) == 2 && !contains(TeamBlackList, substrings[1]) {
					ret = append(ret, substrings[1])
				}
			}
		}
	}

	return ret, nil
}

func LoginBind(user, pass string, lc *ldap.LDAPConnection) error {
	var dnInfo string
	dnInfo = UserClassAttr + "=" + user + "," + UserOu
	err := lc.Bind(dnInfo, pass)
	if err != nil {
		log.Println("ERROR : Login not Successful")
		return errors.New("Session Expired/Invalid Credentials")
	}
	return nil
}

func contains(arr []string, str string) bool {
   for _, a := range arr {
      if a == str {
         return true
      }
   }
   return false
}
<|endoftext|>


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

package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"io/ioutil"
	"os"

	"github.com/FactomProject/factom"
)

var (
	_ = hex.EncodeToString
	_ = fmt.Sprint("testing")
)

func eckey(args []string) error {
	os.Args = args
	flag.Parse()
	args = flag.Args()

	switch args[0] {
	case "new":
		return newECKey()
	case "pub":
		return printPubKey()
	default:
		return man("eckey")
	}

	panic("Something went really wrong with eckey!")
}

func newECKey() error {
	key := factom.NewECKey()
	fmt.Printf("%x", *key)
	return nil
}

func printPubKey() error {
	pub, err := ecPubKey()
	if err != nil {
		return err
	}
	fmt.Println(hex.EncodeToString(pub[:]))

	return nil
}

func ecPrivKey() (*[64]byte, error) {
	priv := new([64]byte)

	in, err := os.Open(wallet)
	if err != nil {
		return nil, err
	}
	p, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}
	key, err := hex.DecodeString(string(p))
	if err != nil {
		return nil, err
	}
	copy(priv[:], key)

	return priv, nil
}

func ecPubKey() (*[32]byte, error) {
	pub := new([32]byte)

	in, err := os.Open(wallet)
	if err != nil {
		return nil, err
	}
	p, err := ioutil.ReadAll(in)
	if err != nil {
		return nil, err
	}
	key, err := hex.DecodeString(string(p))
	if err != nil {
		return nil, err
	}
	copy(pub[:], key[32:64])

	return pub, nil
}
<commit_msg>orginization<commit_after><|endoftext|>


<commit_before>package drip

// Code is an Code returned with errors from the Drip API.
type Code string

const (
	// PresenceError means the attribute is required.
	PresenceError Code = "presence_error"
	// LengthError means the length of the attribute is out of bounds.
	LengthError Code = "length_error"
	// UniquenessError means the attribute must be unique.
	UniquenessError Code = "uniqueness_error"
	// EmailError means the attribute must be a valid email address.
	EmailError Code = "email_error"
	// URLError means the attribute must be a valid URL.
	URLError Code = "url_error"
	// DomainError means the attribute must be a valid domain name.
	DomainError Code = "domain_error"
	// TimeError means the attribute must be a valid time in ISO-8601 format.
	TimeError Code = "time_error"
	// EmailAddressListError means the attribute must be a valid comma-separated list of email addresses.
	EmailAddressListError Code = "email_address_list_error"
	// DaysOfTheWeekError means the attribute must be a valid days of the week mask of the format /\A(0|1){7}\z/ (excluding 0000000).
	DaysOfTheWeekError Code = "days_of_the_week_error"
	// UnavailableError means a resource has been disabled or deleted.
	UnavailableError Code = "unavailable_error"
	// FormatError means a resource identifier or object is not formatted correctly.
	FormatError Code = "format_error"
	// RangeError means a numeric value is out of range.
	RangeError Code = "range_error"
)

// CodeError is a error with a code.
// https://www.getdrip.com/docs/rest-api#errors
type CodeError struct {
	Code      string `json:"code"`
	Attribute string `json:"attribute"`
	Message   string `json:"message"`
}

// Error returns the error message.
func (e CodeError) Error() string {
	return e.Message
}
<commit_msg>add doc link to error codes<commit_after>package drip

// Code is an Code returned with errors from the Drip API.
// https://www.getdrip.com/docs/rest-api#errors
type Code string

const (
	// PresenceError means the attribute is required.
	PresenceError Code = "presence_error"
	// LengthError means the length of the attribute is out of bounds.
	LengthError Code = "length_error"
	// UniquenessError means the attribute must be unique.
	UniquenessError Code = "uniqueness_error"
	// EmailError means the attribute must be a valid email address.
	EmailError Code = "email_error"
	// URLError means the attribute must be a valid URL.
	URLError Code = "url_error"
	// DomainError means the attribute must be a valid domain name.
	DomainError Code = "domain_error"
	// TimeError means the attribute must be a valid time in ISO-8601 format.
	TimeError Code = "time_error"
	// EmailAddressListError means the attribute must be a valid comma-separated list of email addresses.
	EmailAddressListError Code = "email_address_list_error"
	// DaysOfTheWeekError means the attribute must be a valid days of the week mask of the format /\A(0|1){7}\z/ (excluding 0000000).
	DaysOfTheWeekError Code = "days_of_the_week_error"
	// UnavailableError means a resource has been disabled or deleted.
	UnavailableError Code = "unavailable_error"
	// FormatError means a resource identifier or object is not formatted correctly.
	FormatError Code = "format_error"
	// RangeError means a numeric value is out of range.
	RangeError Code = "range_error"
)

// CodeError is a error with a code.
// https://www.getdrip.com/docs/rest-api#errors
type CodeError struct {
	Code      string `json:"code"`
	Attribute string `json:"attribute"`
	Message   string `json:"message"`
}

// Error returns the error message.
func (e CodeError) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"os/exec"
	"strings"
)

type (
	Facts struct {
		Hostname   string
		FQDN       string
		DomainName string
		OS         OperatingSystemFacts
		Net        NetworkFacts
	}

	OperatingSystemFacts struct {
		Version string
		Arch    string
		Name    string
	}

	NetworkFacts struct {
		Interfaces []NetworkInterfaceFacts
	}

	NetworkInterfaceFacts struct {
		Name string
		Addr []string
		MAC  string
	}
)

func GatherFacts() (facts *Facts, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(e.(string))
		}
	}()

	osFacts := OperatingSystemFacts{
		Version: GetFact(CmdOSVersion),
		Arch:    GetFact(CmdOSArch),
		Name:    GetFact(CmdOSName)}

	facts = &Facts{
		Hostname:   GetFact(CmdHostname),
		FQDN:       GetFact(CmdFQDN),
		DomainName: "",
		OS:         osFacts}
	return
}

func GetFact(cmd *exec.Cmd) (output string) {
	out, err := cmd.CombinedOutput()
	if err != nil {
		panic(err)
	}
	output = strings.Trim(string(out), "\n")
	return
}
<commit_msg>Started on network interface facts.<commit_after>package main

import (
	"errors"
	"net"
	"os/exec"
	"strings"
)

type (
	Facts struct {
		Hostname   string
		FQDN       string
		DomainName string
		OS         OperatingSystemFacts
		Net        NetworkFacts
	}

	OperatingSystemFacts struct {
		Version string
		Arch    string
		Name    string
	}

	NetworkFacts struct {
		Interfaces []NetworkInterfaceFact
	}

	NetworkInterfaceFact struct {
		Name string
		Addr []string
		MAC  string
	}
)

func GatherFacts() (facts *Facts, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(e.(string))
		}
	}()

	// Gather operating system facts.
	osFacts := OperatingSystemFacts{
		Version: GetFact(CmdOSVersion),
		Arch:    GetFact(CmdOSArch),
		Name:    GetFact(CmdOSName)}

	// Gather networking facts.
	var interfaces []net.Interface
	interfaces, err = net.Interfaces()
	if err != nil {
		return
	}
	ifaceFacts := make([]NetworkInterfaceFact, len(interfaces))
	for i, iface := range interfaces {
		fact := NetworkInterfaceFact{
			Name: iface.Name,
			MAC:  iface.HardwareAddr.String()}

		// Get all of the addresses for the network interface.
		var addrs []net.Addr
		if addrs, err = iface.Addrs(); err != nil {
			return
		}
		ifaddrs := make([]string, len(addrs))
		for _, addr := range addrs {
			ifaddrs = append(ifaddrs, addr.String())
		}
		fact.Addr = ifaddrs

		ifaceFacts[i] = fact
	}

	// Put it all together, and whaddaya get?
	facts = &Facts{
		Hostname:   GetFact(CmdHostname),
		FQDN:       GetFact(CmdFQDN),
		DomainName: "",
		OS:         osFacts,
		Net:        NetworkFacts{Interfaces: ifaceFacts}}
	return
}

func GetFact(cmd *exec.Cmd) (output string) {
	out, err := cmd.CombinedOutput()
	if err != nil {
		panic(err)
	}
	output = strings.Trim(string(out), "\n")
	return
}
<|endoftext|>


<commit_before>package boxstrapper_test

import (
	"testing"
	. "boxstrapper"
  	"github.com/stretchr/testify/assert"
)

func TestSinglePackage_NoGroups(t *testing.T) {
	s := "i3"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackage_PrefixWhitespace_NoGroups(t *testing.T) {
	s := " i3"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackage_PostfixWhitespace_NoGroups(t *testing.T) {
	s := "i3 "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackageWithLeadingWhitespace(t *testing.T) {
	s := "i3:  system"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackage_LeadingWhitespace_WithGroup(t *testing.T) {
	s := " i3: system"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackageWithTrailingWhitespace(t *testing.T) {
	s := "i3: system "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackage_MultipleGroups(t *testing.T) {
	s := "i3: system, boxstrapper"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}

func TestSinglePackage_MultipleGroups_PrefixWhitespace(t *testing.T) {
	s := "i3: system,  boxstrapper"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}

func TestSinglePackage_MultipleGroups_PostfixWhitespace(t *testing.T) {
	s := "i3: system, boxstrapper "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}<commit_msg>TestSinglePackage_PostfixWhitespace_WithGroup<commit_after>package boxstrapper_test

import (
	"testing"
	. "boxstrapper"
  	"github.com/stretchr/testify/assert"
)

func TestSinglePackage_NoGroups(t *testing.T) {
	s := "i3"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackage_PrefixWhitespace_NoGroups(t *testing.T) {
	s := " i3"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackage_PostfixWhitespace_NoGroups(t *testing.T) {
	s := "i3 "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "default", packages[0].Groups[0])
}

func TestSinglePackageWithLeadingWhitespace(t *testing.T) {
	s := "i3:  system"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackage_LeadinWhitespace_WithGroup(t *testing.T) {
	s := " i3: system"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackage_PostfixWhitespace_WithGroup(t *testing.T) {
	s := "i3 : system"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackageWithTrailingWhitespace(t *testing.T) {
	s := "i3: system "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 1, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
}

func TestSinglePackage_MultipleGroups(t *testing.T) {
	s := "i3: system, boxstrapper"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}

func TestSinglePackage_MultipleGroups_PrefixWhitespace(t *testing.T) {
	s := "i3: system,  boxstrapper"

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}

func TestSinglePackage_MultipleGroups_PostfixWhitespace(t *testing.T) {
	s := "i3: system, boxstrapper "

	packages := NewPackage(s)

	assert.Equal(t, 1, len(packages))
	assert.Equal(t, "i3", packages[0].Package)
	assert.Equal(t, 2, len(packages[0].Groups))
	assert.Equal(t, "system", packages[0].Groups[0])
	assert.Equal(t, "boxstrapper", packages[0].Groups[1])
}<|endoftext|>