<commit_before>// Command fake-kube-apiserver simulates k8s kube-apiserver - use only in integration tests.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/golang/glog"

	"github.com/GoogleCloudPlatform/k8s-cloudkms-plugin/testutils/fakekubeapi"
	"github.com/GoogleCloudPlatform/k8s-cloudkms-plugin/testutils/kmspluginclient"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	port          = flag.Int("port", 8080, "Port on which to listen.")
	timeout       = flag.Duration("kms-plugin-timeout", 3*time.Second, "Timeout for calls to kms-plugin in seconds.")
	kmsSocketPath = flag.String("path-to-kms-socket", "", "Path to Unix Domain socket for communicating with KMS Plugin.")

	// TODO(immutableT) Supply option to consume resources from yaml files.
	namespaces = corev1.NamespaceList{
		Items: []corev1.Namespace{
			{
				ObjectMeta: metav1.ObjectMeta{
					Name: "default",
				},
			},
			{
				ObjectMeta: metav1.ObjectMeta{
					Name: "system",
				},
			},
		},
	}

	secrets = map[string][]corev1.Secret{
		"default": {
			{
				TypeMeta: metav1.TypeMeta{
					Kind:       "Secret",
					APIVersion: "v1",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:      "my-default-secret",
					Namespace: "default",
				},
				Data: map[string][]byte{"token": []byte("fakeTokenDefault")},
			},
		},
		"system": {
			{
				TypeMeta: metav1.TypeMeta{
					Kind:       "Secret",
					APIVersion: "v1",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:      "my-system-secret",
					Namespace: "system",
				},
				Data: map[string][]byte{"token": []byte("fakeTokenSystem")},
			},
		},
	}
)

func main() {

	if *kmsSocketPath == "" {
		glog.Exitln("path-to-kms-socket is mandatory argument")
	}

	k, err := kmspluginclient.New(fmt.Sprintf("unix://%s", *kmsSocketPath))
	if err != nil {
		glog.Exitf("Failed to initialize KMS Client, error: %v", err)
	}

	s, err := fakekubeapi.New(namespaces, secrets, *port, k, *timeout)
	if err != nil {
		glog.Exitf("failed to start fake kube-apiserver, error %v", err)
	}
	defer s.Close()
	glog.Infof("kube-apiserver %v is listening on port: %d", s.URL(), *port)

	signalsChan := make(chan os.Signal, 1)
	signal.Notify(signalsChan, syscall.SIGINT, syscall.SIGTERM)

	sig := <-signalsChan
	glog.Exitf("captured %v, shutting down fake kube-apiserver", sig)
}
<commit_msg>Readme stage 0.<commit_after>/*
Copyright 2018 Google LLC

 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

 https://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.
*/

// Command fake-kube-apiserver simulates k8s kube-apiserver - use only in integration tests.
package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/golang/glog"

	"github.com/GoogleCloudPlatform/k8s-cloudkms-plugin/testutils/fakekubeapi"
	"github.com/GoogleCloudPlatform/k8s-cloudkms-plugin/testutils/kmspluginclient"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

var (
	port          = flag.Int("port", 8080, "Port on which to listen.")
	timeout       = flag.Duration("kms-plugin-timeout", 3*time.Second, "Timeout for calls to kms-plugin in seconds.")
	kmsSocketPath = flag.String("path-to-kms-socket", "", "Path to Unix Domain socket for communicating with KMS Plugin.")

	// TODO(immutableT) Supply option to consume resources from yaml files.
	namespaces = corev1.NamespaceList{
		Items: []corev1.Namespace{
			{
				ObjectMeta: metav1.ObjectMeta{
					Name: "default",
				},
			},
			{
				ObjectMeta: metav1.ObjectMeta{
					Name: "system",
				},
			},
		},
	}

	secrets = map[string][]corev1.Secret{
		"default": {
			{
				TypeMeta: metav1.TypeMeta{
					Kind:       "Secret",
					APIVersion: "v1",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:      "my-default-secret",
					Namespace: "default",
				},
				Data: map[string][]byte{"token": []byte("fakeTokenDefault")},
			},
		},
		"system": {
			{
				TypeMeta: metav1.TypeMeta{
					Kind:       "Secret",
					APIVersion: "v1",
				},
				ObjectMeta: metav1.ObjectMeta{
					Name:      "my-system-secret",
					Namespace: "system",
				},
				Data: map[string][]byte{"token": []byte("fakeTokenSystem")},
			},
		},
	}
)

func main() {

	if *kmsSocketPath == "" {
		glog.Exitln("path-to-kms-socket is mandatory argument")
	}

	k, err := kmspluginclient.New(fmt.Sprintf("unix://%s", *kmsSocketPath))
	if err != nil {
		glog.Exitf("Failed to initialize KMS Client, error: %v", err)
	}

	s, err := fakekubeapi.New(namespaces, secrets, *port, k, *timeout)
	if err != nil {
		glog.Exitf("failed to start fake kube-apiserver, error %v", err)
	}
	defer s.Close()
	glog.Infof("kube-apiserver %v is listening on port: %d", s.URL(), *port)

	signalsChan := make(chan os.Signal, 1)
	signal.Notify(signalsChan, syscall.SIGINT, syscall.SIGTERM)

	sig := <-signalsChan
	glog.Exitf("captured %v, shutting down fake kube-apiserver", sig)
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/go-ozzo/ozzo-dbx"
	_ "github.com/go-sql-driver/mysql"
	"github.com/olekukonko/tablewriter"
)

type WhoisAppInfo struct {
	AppId            string `db:"AppId"`
	ApplicationName  string `db:"ApplicationName"`
	ApplicationIntro string `db:"ApplicationIntro"`
	Cluster          string `db:"Cluster"`
	CreateBy         string `db:"CreateBy"`
	CreateTime       string `db:"CreateTime"`
	Status           string `db:"Status"`
}

type WhoisTopicInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	TopicName  string `db:"TopicName"`
	TopicIntro string `db:"TopicIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type WhoisGroupInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	GroupName  string `db:"GroupName"`
	GroupIntro string `db:"GroupIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type Whois struct {
	Ui  cli.Ui
	Cmd string

	zone     string
	app      string
	topic    string
	group    string
	likeMode bool

	appInfos   []WhoisAppInfo
	topicInfos []WhoisTopicInfo
	groupInfos []WhoisGroupInfo
}

func (this *Whois) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("whois", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.app, "app", "", "")
	cmdFlags.StringVar(&this.group, "g", "", "")
	cmdFlags.StringVar(&this.topic, "t", "", "")
	cmdFlags.BoolVar(&this.likeMode, "l", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	ensureZoneValid(this.zone)

	mysqlDsns := map[string]string{
		"prod": "user_pubsub:p0nI7mEL6OLW@tcp(m3342.wdds.mysqldb.com:3342)/pubsub?charset=utf8&timeout=10s",
		"sit":  "pubsub:pubsub@tcp(10.209.44.12:10043)/pubsub?charset=utf8&timeout=10s",
		"test": "pubsub:pubsub@tcp(10.209.44.14:10044)/pubsub?charset=utf8&timeout=10s",
	}
	this.loadFromManager(mysqlDsns[this.zone])

	table := tablewriter.NewWriter(os.Stdout)
	switch {
	case this.topic+this.group == "":
		// list apps
		table.SetHeader([]string{"Id", "Name", "Cluster", "Ctime"})
		for _, ai := range this.appInfos {
			table.Append([]string{ai.AppId, ai.ApplicationName, ai.Cluster, ai.CreateTime})
		}

	case this.topic != "":
		table.SetHeader([]string{"topic", "desc", "app", "owner", "ctime"})
		for _, ti := range this.topicInfos {
			table.Append([]string{ti.TopicName, ti.TopicIntro, ti.AppName, ti.CreateBy, ti.CreateTime})
		}

	case this.group != "":
		table.SetHeader([]string{"group", "desc", "app", "owner", "ctime"})
		for _, gi := range this.groupInfos {
			table.Append([]string{gi.GroupName, gi.GroupIntro, gi.AppName, gi.CreateBy, gi.CreateTime})
		}

	case this.topic != "" && this.group != "":
		this.Ui.Error("-t and -g cannot be set at the same time！")
		return 1
	}

	table.Render()

	return
}

func (this *Whois) loadFromManager(dsn string) {
	db, err := dbx.Open("mysql", dsn)
	swallow(err)

	// TODO fetch from topics_version
	sql := "SELECT AppId,ApplicationName,ApplicationIntro,Cluster,CreateBy,CreateTime,Status FROM application"
	if this.app != "" {
		sql += " WHERE AppId IN (" + this.app + ")"
	}
	sql += " ORDER BY AppId"
	q := db.NewQuery(sql)

	swallow(q.All(&this.appInfos))

	op := "="
	if this.likeMode {
		op = "LIKE"
	}
	if this.topic != "" {
		if this.likeMode {
			this.topic = "%" + this.topic + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,TopicName,TopicIntro,CreateBy,CreateTime,Status FROM topics WHERE TopicName %s '%s'",
			op, this.topic)
		q = db.NewQuery(sql)
		swallow(q.All(&this.topicInfos))

		for _, ti := range this.topicInfos {
			ti.AppName = this.appName(ti.AppId)
		}
	}

	if this.group != "" {
		if this.likeMode {
			this.group = "%" + this.group + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,GroupName,GroupIntro,CreateBy,CreateTime,Status FROM application_group WHERE GroupName %s '%s'",
			op, this.group)
		q = db.NewQuery(sql)
		swallow(q.All(&this.groupInfos))
		for _, gi := range this.groupInfos {
			gi.AppName = this.appName(gi.AppId)
		}
	}
}

func (this *Whois) appName(appId string) string {
	for _, ai := range this.appInfos {
		if ai.AppId == appId {
			return ai.ApplicationName
		}
	}

	return "NotFound"
}

func (*Whois) Synopsis() string {
	return "Lookup PubSub App Information"
}

func (this *Whois) Help() string {
	help := fmt.Sprintf(`
Usage: %s whois [options]

    Lookup PubSub App Information

Options:

    -z zone

    -app comma seperated appId

    -g group

    -t topic

    -l
      Like mode.
      Pattern wildcard match of group or topic name.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<commit_msg>bug fix: update member var didn't work because of var copy<commit_after>package command

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/funkygao/gafka/ctx"
	"github.com/funkygao/gocli"
	"github.com/go-ozzo/ozzo-dbx"
	_ "github.com/go-sql-driver/mysql"
	"github.com/olekukonko/tablewriter"
)

type WhoisAppInfo struct {
	AppId            string `db:"AppId"`
	ApplicationName  string `db:"ApplicationName"`
	ApplicationIntro string `db:"ApplicationIntro"`
	Cluster          string `db:"Cluster"`
	CreateBy         string `db:"CreateBy"`
	CreateTime       string `db:"CreateTime"`
	Status           string `db:"Status"`
}

type WhoisTopicInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	TopicName  string `db:"TopicName"`
	TopicIntro string `db:"TopicIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type WhoisGroupInfo struct {
	AppId      string `db:"AppId"`
	AppName    string
	GroupName  string `db:"GroupName"`
	GroupIntro string `db:"GroupIntro"`
	CreateBy   string `db:"CreateBy"`
	CreateTime string `db:"CreateTime"`
	Status     string `db:"Status"`
}

type Whois struct {
	Ui  cli.Ui
	Cmd string

	zone     string
	app      string
	topic    string
	group    string
	likeMode bool

	appInfos   []WhoisAppInfo
	topicInfos []WhoisTopicInfo
	groupInfos []WhoisGroupInfo
}

func (this *Whois) Run(args []string) (exitCode int) {
	cmdFlags := flag.NewFlagSet("whois", flag.ContinueOnError)
	cmdFlags.Usage = func() { this.Ui.Output(this.Help()) }
	cmdFlags.StringVar(&this.zone, "z", ctx.ZkDefaultZone(), "")
	cmdFlags.StringVar(&this.app, "app", "", "")
	cmdFlags.StringVar(&this.group, "g", "", "")
	cmdFlags.StringVar(&this.topic, "t", "", "")
	cmdFlags.BoolVar(&this.likeMode, "l", false, "")
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	ensureZoneValid(this.zone)

	mysqlDsns := map[string]string{
		"prod": "user_pubsub:p0nI7mEL6OLW@tcp(m3342.wdds.mysqldb.com:3342)/pubsub?charset=utf8&timeout=10s",
		"sit":  "pubsub:pubsub@tcp(10.209.44.12:10043)/pubsub?charset=utf8&timeout=10s",
		"test": "pubsub:pubsub@tcp(10.209.44.14:10044)/pubsub?charset=utf8&timeout=10s",
	}
	this.loadFromManager(mysqlDsns[this.zone])

	table := tablewriter.NewWriter(os.Stdout)
	switch {
	case this.topic+this.group == "":
		// list apps
		table.SetHeader([]string{"Id", "Name", "Cluster", "Ctime"})
		for _, ai := range this.appInfos {
			table.Append([]string{ai.AppId, ai.ApplicationName, ai.Cluster, ai.CreateTime})
		}

	case this.topic != "":
		table.SetHeader([]string{"topic", "desc", "app", "owner", "ctime"})
		for _, ti := range this.topicInfos {
			table.Append([]string{ti.TopicName, ti.TopicIntro, ti.AppName, ti.CreateBy, ti.CreateTime})
		}

	case this.group != "":
		table.SetHeader([]string{"group", "desc", "app", "owner", "ctime"})
		for _, gi := range this.groupInfos {
			table.Append([]string{gi.GroupName, gi.GroupIntro, gi.AppName, gi.CreateBy, gi.CreateTime})
		}

	case this.topic != "" && this.group != "":
		this.Ui.Error("-t and -g cannot be set at the same time！")
		return 1
	}

	table.Render()

	return
}

func (this *Whois) loadFromManager(dsn string) {
	db, err := dbx.Open("mysql", dsn)
	swallow(err)

	// TODO fetch from topics_version
	sql := "SELECT AppId,ApplicationName,ApplicationIntro,Cluster,CreateBy,CreateTime,Status FROM application"
	if this.app != "" {
		sql += " WHERE AppId IN (" + this.app + ")"
	}
	sql += " ORDER BY AppId"
	q := db.NewQuery(sql)

	swallow(q.All(&this.appInfos))

	op := "="
	if this.likeMode {
		op = "LIKE"
	}
	if this.topic != "" {
		if this.likeMode {
			this.topic = "%" + this.topic + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,TopicName,TopicIntro,CreateBy,CreateTime,Status FROM topics WHERE TopicName %s '%s'",
			op, this.topic)
		q = db.NewQuery(sql)
		swallow(q.All(&this.topicInfos))

		for i, ti := range this.topicInfos {
			this.topicInfos[i].AppName = this.appName(ti.AppId)
		}
	}

	if this.group != "" {
		if this.likeMode {
			this.group = "%" + this.group + "%"
		}
		sql = fmt.Sprintf("SELECT AppId,GroupName,GroupIntro,CreateBy,CreateTime,Status FROM application_group WHERE GroupName %s '%s'",
			op, this.group)
		q = db.NewQuery(sql)
		swallow(q.All(&this.groupInfos))
		for i, gi := range this.groupInfos {
			this.groupInfos[i].AppName = this.appName(gi.AppId)
		}
	}
}

func (this *Whois) appName(appId string) string {
	for _, ai := range this.appInfos {
		if ai.AppId == appId {
			return ai.ApplicationName
		}
	}

	return "NotFound"
}

func (*Whois) Synopsis() string {
	return "Lookup PubSub App Information"
}

func (this *Whois) Help() string {
	help := fmt.Sprintf(`
Usage: %s whois [options]

    Lookup PubSub App Information

Options:

    -z zone

    -app comma seperated appId

    -g group

    -t topic

    -l
      Like mode.
      Pattern wildcard match of group or topic name.

`, this.Cmd)
	return strings.TrimSpace(help)
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"
	"regexp"
	"strings"

	fleetSchema "github.com/coreos/fleet/schema"
	"github.com/monder/kaylee/spec"
)

type RktEngine struct{}

func (*RktEngine) IsValidSpecType(s *spec.Spec) bool {
	return s.Engine == "" || s.Engine == "rkt"
}

func (*RktEngine) ValidateSpec(s *spec.Spec) error {
	if len(s.Apps) == 0 {
		return fmt.Errorf("There should be at least one app")
	}
	return nil
}

func (*RktEngine) GetFleetUnit(spec *spec.Spec, name string, conflicts []string) *fleetSchema.Unit {
	uuidFileName := regexp.MustCompile("[^a-zA-Z0-9_.-]").ReplaceAllLiteralString(name, "_")
	uuidFileName = regexp.MustCompile("\\.service$").ReplaceAllLiteralString(uuidFileName, "")
	uuidFile := "/var/run/kaylee_" + uuidFileName

	var args []string

	for _, arg := range spec.Args {
		args = append(args, arg)
	}

	var options []*fleetSchema.UnitOption
	options = append(options, &fleetSchema.UnitOption{
		Section: "Unit", Name: "Requires", Value: "flanneld.service",
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Unit", Name: "After", Value: "flanneld.service",
	})

	for _, env := range spec.EnvFiles {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service", Name: "EnvironmentFile", Value: env,
		})
	}
	for _, env := range spec.Env {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service", Name: "Environment", Value: fmt.Sprintf("%s=%s", env.Name, env.Value),
		})
	}
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "Environment", Value: fmt.Sprintf("KAYLEE_ID=%s", uuidFileName),
	})

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "TimeoutStartSec", Value: "0",
	})

	for _, volume := range spec.Volumes {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service",
			Name:    "ExecStartPre",
			Value:   fmt.Sprintf("/var/lib/kaylee/plugins/volumes/%s %s %s", volume.Driver, volume.ID, volume.Options),
		})
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service",
			Name:    "ExecStopPost",
			Value:   fmt.Sprintf("/var/lib/kaylee/plugins/volumes/%s -u %s", volume.Driver, volume.ID),
		})

		args = append(args, fmt.Sprintf("--volume %s,kind=host,source=/mnt/%s/%s", volume.ID, volume.Driver, volume.ID))
		args = append(args, fmt.Sprintf("--mount volume=%s,target=%s", volume.ID, volume.Path))
	}

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStartPre", Value: fmt.Sprintf("-/usr/bin/rkt stop --force=true --uuid-file=%s", uuidFile),
	})

	if spec.Net != "" {
		args = append(args, fmt.Sprintf("--net=%s", spec.Net))
	}
	args = append(args, "--insecure-options=image")
	args = append(args, "--inherit-env")
	args = append(args, fmt.Sprintf("--uuid-file-save=%s", uuidFile))

	for _, app := range spec.Apps {
		args = append(args, fmt.Sprintf("%s -- %s ---", app.Image, strings.Join(app.Args, " ")))
	}

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service",
		Name:    "ExecStart",
		Value:   fmt.Sprintf("/usr/bin/rkt run %s", strings.Join(args, " ")),
	})

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rkt stop --uuid-file=%s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rkt rm --uuid-file=%s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rm %s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "Restart", Value: "always",
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "RestartSec", Value: "30",
	})

	for _, machine := range spec.Machine {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "MachineMetadata", Value: machine,
		})
	}
	if spec.MachineID != "" {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "MachineID", Value: spec.MachineID,
		})
	}
	if spec.Global {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "Global", Value: "true",
		})
	}

	for _, c := range conflicts {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "Conflicts", Value: c,
		})
	}
	return &fleetSchema.Unit{
		DesiredState: "launched",
		Options:      options,
		Name:         name,
	}
}
<commit_msg>Prefetch rkt images<commit_after>package engine

import (
	"fmt"
	"regexp"
	"strings"

	fleetSchema "github.com/coreos/fleet/schema"
	"github.com/monder/kaylee/spec"
)

type RktEngine struct{}

func (*RktEngine) IsValidSpecType(s *spec.Spec) bool {
	return s.Engine == "" || s.Engine == "rkt"
}

func (*RktEngine) ValidateSpec(s *spec.Spec) error {
	if len(s.Apps) == 0 {
		return fmt.Errorf("There should be at least one app")
	}
	return nil
}

func (*RktEngine) GetFleetUnit(spec *spec.Spec, name string, conflicts []string) *fleetSchema.Unit {
	uuidFileName := regexp.MustCompile("[^a-zA-Z0-9_.-]").ReplaceAllLiteralString(name, "_")
	uuidFileName = regexp.MustCompile("\\.service$").ReplaceAllLiteralString(uuidFileName, "")
	uuidFile := "/var/run/kaylee_" + uuidFileName

	var args []string

	for _, arg := range spec.Args {
		args = append(args, arg)
	}

	var options []*fleetSchema.UnitOption
	options = append(options, &fleetSchema.UnitOption{
		Section: "Unit", Name: "Requires", Value: "flanneld.service",
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Unit", Name: "After", Value: "flanneld.service",
	})

	for _, env := range spec.EnvFiles {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service", Name: "EnvironmentFile", Value: env,
		})
	}
	for _, env := range spec.Env {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service", Name: "Environment", Value: fmt.Sprintf("%s=%s", env.Name, env.Value),
		})
	}
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "Environment", Value: fmt.Sprintf("KAYLEE_ID=%s", uuidFileName),
	})

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "TimeoutStartSec", Value: "0",
	})

	for _, volume := range spec.Volumes {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service",
			Name:    "ExecStartPre",
			Value:   fmt.Sprintf("/var/lib/kaylee/plugins/volumes/%s %s %s", volume.Driver, volume.ID, volume.Options),
		})
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service",
			Name:    "ExecStopPost",
			Value:   fmt.Sprintf("/var/lib/kaylee/plugins/volumes/%s -u %s", volume.Driver, volume.ID),
		})

		args = append(args, fmt.Sprintf("--volume %s,kind=host,source=/mnt/%s/%s", volume.ID, volume.Driver, volume.ID))
		args = append(args, fmt.Sprintf("--mount volume=%s,target=%s", volume.ID, volume.Path))
	}

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStartPre", Value: fmt.Sprintf("-/usr/bin/rkt stop --force=true --uuid-file=%s", uuidFile),
	})

	if spec.Net != "" {
		args = append(args, fmt.Sprintf("--net=%s", spec.Net))
	}
	args = append(args, "--insecure-options=image")
	args = append(args, "--inherit-env")
	args = append(args, fmt.Sprintf("--uuid-file-save=%s", uuidFile))

	for _, app := range spec.Apps {
		options = append(options, &fleetSchema.UnitOption{
			Section: "Service", Name: "ExecStartPre", Value: fmt.Sprintf("/usr/bin/rkt fetch --insecure-options=image %s", app.Image),
		})
		args = append(args, fmt.Sprintf("%s -- %s ---", app.Image, strings.Join(app.Args, " ")))
	}

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service",
		Name:    "ExecStart",
		Value:   fmt.Sprintf("/usr/bin/rkt run %s", strings.Join(args, " ")),
	})

	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rkt stop --uuid-file=%s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rkt rm --uuid-file=%s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "ExecStop", Value: fmt.Sprintf("-/usr/bin/rm %s", uuidFile),
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "Restart", Value: "always",
	})
	options = append(options, &fleetSchema.UnitOption{
		Section: "Service", Name: "RestartSec", Value: "30",
	})

	for _, machine := range spec.Machine {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "MachineMetadata", Value: machine,
		})
	}
	if spec.MachineID != "" {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "MachineID", Value: spec.MachineID,
		})
	}
	if spec.Global {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "Global", Value: "true",
		})
	}

	for _, c := range conflicts {
		options = append(options, &fleetSchema.UnitOption{
			Section: "X-Fleet", Name: "Conflicts", Value: c,
		})
	}
	return &fleetSchema.Unit{
		DesiredState: "launched",
		Options:      options,
		Name:         name,
	}
}
<|endoftext|>


<commit_before>package ops // import "cirello.io/gochatbot/rules/ops"

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

	"cirello.io/gochatbot/bot"
	"cirello.io/gochatbot/messages"
	"cirello.io/gochatbot/rules/ops/ssh"
)

type sshConf struct {
	Username   string
	SSHKeyFile string
}

type opsRuleset struct {
	outCh chan messages.Message

	mu             sync.Mutex
	hostGroups     map[string][]string
	hostGroupsConf map[string]sshConf
}

// Name returns this rules name - meant for debugging.
func (r opsRuleset) Name() string {
	return "Remote Ops Ruleset"
}

// Boot runs preparatory steps for ruleset execution
func (r opsRuleset) Boot(self *bot.Self) {
	log.Println("ops: reading from memory")
	if vs, ok := self.MemoryRead("ops", "hostGroups").(map[string]interface{}); ok {
		for hostGroup, ihosts := range vs {
			if hosts, ok := ihosts.([]interface{}); ok {
				for _, host := range hosts {
					r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], fmt.Sprint(host))
				}
			}
		}
		log.Println("ops: hostGroups read")
	}

	if vs, ok := self.MemoryRead("ops", "hostGroupsConf").(map[string]interface{}); ok {
		for hostGroup, iconf := range vs {
			if conf, ok := iconf.(map[string]interface{}); ok {
				if _, ok := conf["Username"]; !ok {
					continue
				}
				r.hostGroupsConf[hostGroup] = sshConf{
					Username:   fmt.Sprint(conf["Username"]),
					SSHKeyFile: fmt.Sprint(conf["SSHKeyFile"]),
				}
			}
		}
		log.Println("ops: hostGroupsConf read")
	}

}

func (r opsRuleset) HelpMessage(self bot.Self) string {
	botName := self.Name()
	msg := fmt.Sprintln(botName, "ops uptime host-group - get uptime of all hosts of a host-group")
	msg = fmt.Sprintln(msg, botName, "ops df host-group - get 'df -h' of all hosts of a host-group")
	msg = fmt.Sprintln(msg, botName, "ops add host host-group - add host to host-group (the group is created at first host addition)")
	msg = fmt.Sprintln(msg, botName, "ops remove host host-group - remove host from host-group (the group is removed after last host deletion)")
	msg = fmt.Sprintln(msg, botName, "ops configure hostgroup username keyfile - configure ssh login credentials (don't provide keyfile to force the use of ssh-agent)")
	return msg
}

func (r opsRuleset) ParseMessage(self bot.Self, in messages.Message) []messages.Message {
	cmd := strings.TrimSpace(strings.TrimPrefix(in.Message, self.Name()))
	parts := strings.Split(cmd, " ")

	var msg messages.Message
	if strings.HasPrefix(cmd, "ops add") {
		host, hostGroup := parts[2], parts[3]
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.add(self, host, hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops remove") {
		host, hostGroup := parts[2], parts[3]
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.remove(self, host, hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops configure") {
		var sshKeyFile string
		hostGroup, username := parts[2], parts[3]
		if len(parts) == 5 {
			sshKeyFile = parts[4]
		}
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.configure(self, hostGroup, username, sshKeyFile),
		}
	} else if strings.HasPrefix(cmd, "ops uptime") {
		hostGroup := parts[2]
		go r.uptime(in, hostGroup)
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("dispatched uptime call to", hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops df") {
		hostGroup := parts[2]
		go r.dfH(in, hostGroup)
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("dispatched 'df -h' call to", hostGroup),
		}
	}

	return []messages.Message{msg}
}

func (r *opsRuleset) add(self bot.Self, host, hostGroup string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], host)
	self.MemorySave("ops", "hostGroups", r.hostGroups)

	return fmt.Sprintln(host, "added to host group", hostGroup)
}

func (r *opsRuleset) remove(self bot.Self, host, hostGroup string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	if _, ok := r.hostGroups[hostGroup]; !ok {
		return "host group not found"
	}

	r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], host)

	var newHG []string
	for _, h := range r.hostGroups[hostGroup] {
		if h == host {
			continue
		}
		newHG = append(newHG, host)
	}
	r.hostGroups[hostGroup] = newHG
	self.MemorySave("ops", "hostGroups", r.hostGroups)

	return fmt.Sprintln(host, "added to host group", hostGroup)
}

func (r *opsRuleset) configure(self bot.Self, hostGroup, username, sshKeyFile string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.hostGroupsConf[hostGroup] = sshConf{
		Username:   username,
		SSHKeyFile: sshKeyFile,
	}
	self.MemorySave("ops", "hostGroupsConf", r.hostGroupsConf)

	return fmt.Sprintln(hostGroup, "configured")
}

func (r *opsRuleset) uptime(in messages.Message, hostGroup string) {
	r.run(in, hostGroup, "/usr/bin/uptime")
}

func (r *opsRuleset) dfH(in messages.Message, hostGroup string) {
	r.run(in, hostGroup, "/bin/df -h")
}

func (r *opsRuleset) run(in messages.Message, hostGroup, cmd string) {
	if _, ok := r.hostGroups[hostGroup]; !ok {
		r.outCh <- messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("could not find hostgroup", hostGroup),
		}
		return
	}
	if _, ok := r.hostGroupsConf[hostGroup]; !ok {
		r.outCh <- messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("could not find configuration for hostgroup", hostGroup),
		}
		return
	}

	conf := r.hostGroupsConf[hostGroup]

	for _, hostname := range r.hostGroups[hostGroup] {
		go func(conf sshConf, hostname string) {
			host, port, err := net.SplitHostPort(hostname)
			if err != nil {
				r.outCh <- messages.Message{
					Room:         in.Room,
					FromUserID:   in.ToUserID,
					FromUserName: in.ToUserName,
					ToUserID:     in.FromUserID,
					ToUserName:   in.FromUserName,
					Message:      err.Error(),
				}
				return
			}
			authMethod := ssh.PublicKeyFile(conf.SSHKeyFile)
			if conf.SSHKeyFile == "" {
				authMethod = ssh.SshAgent(os.Getenv)
			}
			out, err := ssh.Run(
				cmd,
				[]string{},
				conf.Username,
				host,
				port,
				authMethod,
			)
			if err != nil {
				r.outCh <- messages.Message{
					Room:         in.Room,
					FromUserID:   in.ToUserID,
					FromUserName: in.ToUserName,
					ToUserID:     in.FromUserID,
					ToUserName:   in.FromUserName,
					Message:      err.Error(),
				}
				return
			}
			r.outCh <- messages.Message{
				Room:         in.Room,
				FromUserID:   in.ToUserID,
				FromUserName: in.ToUserName,
				ToUserID:     in.FromUserID,
				ToUserName:   in.FromUserName,
				Message:      fmt.Sprintf("%s: %s", hostname, out),
			}
		}(conf, hostname)
	}
}

// New returns a ops ruleset
func New() *opsRuleset {
	return &opsRuleset{
		hostGroups:     make(map[string][]string),
		hostGroupsConf: make(map[string]sshConf),
	}
}

func (r *opsRuleset) SetOutgoingChannel(outCh chan messages.Message) {
	r.outCh = outCh
}
<commit_msg>ops ruleset: add 'free -m' command<commit_after>package ops // import "cirello.io/gochatbot/rules/ops"

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

	"cirello.io/gochatbot/bot"
	"cirello.io/gochatbot/messages"
	"cirello.io/gochatbot/rules/ops/ssh"
)

type sshConf struct {
	Username   string
	SSHKeyFile string
}

type opsRuleset struct {
	outCh chan messages.Message

	mu             sync.Mutex
	hostGroups     map[string][]string
	hostGroupsConf map[string]sshConf
}

// Name returns this rules name - meant for debugging.
func (r opsRuleset) Name() string {
	return "Remote Ops Ruleset"
}

// Boot runs preparatory steps for ruleset execution
func (r opsRuleset) Boot(self *bot.Self) {
	log.Println("ops: reading from memory")
	if vs, ok := self.MemoryRead("ops", "hostGroups").(map[string]interface{}); ok {
		for hostGroup, ihosts := range vs {
			if hosts, ok := ihosts.([]interface{}); ok {
				for _, host := range hosts {
					r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], fmt.Sprint(host))
				}
			}
		}
		log.Println("ops: hostGroups read")
	}

	if vs, ok := self.MemoryRead("ops", "hostGroupsConf").(map[string]interface{}); ok {
		for hostGroup, iconf := range vs {
			if conf, ok := iconf.(map[string]interface{}); ok {
				if _, ok := conf["Username"]; !ok {
					continue
				}
				r.hostGroupsConf[hostGroup] = sshConf{
					Username:   fmt.Sprint(conf["Username"]),
					SSHKeyFile: fmt.Sprint(conf["SSHKeyFile"]),
				}
			}
		}
		log.Println("ops: hostGroupsConf read")
	}

}

func (r opsRuleset) HelpMessage(self bot.Self) string {
	botName := self.Name()
	msg := fmt.Sprintln(botName, "ops uptime host-group - get uptime of all hosts of a host-group")
	msg = fmt.Sprintln(msg, botName, "ops df host-group - get 'df -h' of all hosts of a host-group")
	msg = fmt.Sprintln(msg, botName, "ops memfree host-group - get 'free -m' of all hosts of a host-group")
	msg = fmt.Sprintln(msg, botName, "ops add host host-group - add host to host-group (the group is created at first host addition)")
	msg = fmt.Sprintln(msg, botName, "ops remove host host-group - remove host from host-group (the group is removed after last host deletion)")
	msg = fmt.Sprintln(msg, botName, "ops configure hostgroup username keyfile - configure ssh login credentials (don't provide keyfile to force the use of ssh-agent)")
	return msg
}

func (r opsRuleset) ParseMessage(self bot.Self, in messages.Message) []messages.Message {
	cmd := strings.TrimSpace(strings.TrimPrefix(in.Message, self.Name()))
	parts := strings.Split(cmd, " ")

	var msg messages.Message
	if strings.HasPrefix(cmd, "ops add") {
		host, hostGroup := parts[2], parts[3]
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.add(self, host, hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops remove") {
		host, hostGroup := parts[2], parts[3]
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.remove(self, host, hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops configure") {
		var sshKeyFile string
		hostGroup, username := parts[2], parts[3]
		if len(parts) == 5 {
			sshKeyFile = parts[4]
		}
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      r.configure(self, hostGroup, username, sshKeyFile),
		}
	} else if strings.HasPrefix(cmd, "ops uptime") {
		hostGroup := parts[2]
		go r.uptime(in, hostGroup)
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("dispatched uptime call to", hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops df") {
		hostGroup := parts[2]
		go r.dfH(in, hostGroup)
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("dispatched 'df -h' call to", hostGroup),
		}
	} else if strings.HasPrefix(cmd, "ops memfree") {
		hostGroup := parts[2]
		go r.freeM(in, hostGroup)
		msg = messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("dispatched 'free -m' call to", hostGroup),
		}
	}

	return []messages.Message{msg}
}

func (r *opsRuleset) add(self bot.Self, host, hostGroup string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], host)
	self.MemorySave("ops", "hostGroups", r.hostGroups)

	return fmt.Sprintln(host, "added to host group", hostGroup)
}

func (r *opsRuleset) remove(self bot.Self, host, hostGroup string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	if _, ok := r.hostGroups[hostGroup]; !ok {
		return "host group not found"
	}

	r.hostGroups[hostGroup] = append(r.hostGroups[hostGroup], host)

	var newHG []string
	for _, h := range r.hostGroups[hostGroup] {
		if h == host {
			continue
		}
		newHG = append(newHG, host)
	}
	r.hostGroups[hostGroup] = newHG
	self.MemorySave("ops", "hostGroups", r.hostGroups)

	return fmt.Sprintln(host, "added to host group", hostGroup)
}

func (r *opsRuleset) configure(self bot.Self, hostGroup, username, sshKeyFile string) string {
	r.mu.Lock()
	defer r.mu.Unlock()

	r.hostGroupsConf[hostGroup] = sshConf{
		Username:   username,
		SSHKeyFile: sshKeyFile,
	}
	self.MemorySave("ops", "hostGroupsConf", r.hostGroupsConf)

	return fmt.Sprintln(hostGroup, "configured")
}

func (r *opsRuleset) uptime(in messages.Message, hostGroup string) {
	r.run(in, hostGroup, "/usr/bin/uptime")
}

func (r *opsRuleset) dfH(in messages.Message, hostGroup string) {
	r.run(in, hostGroup, "/bin/df -h")
}

func (r *opsRuleset) freeM(in messages.Message, hostGroup string) {
	r.run(in, hostGroup, "free -m")
}

func (r *opsRuleset) run(in messages.Message, hostGroup, cmd string) {
	if _, ok := r.hostGroups[hostGroup]; !ok {
		r.outCh <- messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("could not find hostgroup", hostGroup),
		}
		return
	}
	if _, ok := r.hostGroupsConf[hostGroup]; !ok {
		r.outCh <- messages.Message{
			Room:         in.Room,
			FromUserID:   in.ToUserID,
			FromUserName: in.ToUserName,
			ToUserID:     in.FromUserID,
			ToUserName:   in.FromUserName,
			Message:      fmt.Sprintln("could not find configuration for hostgroup", hostGroup),
		}
		return
	}

	conf := r.hostGroupsConf[hostGroup]

	for _, hostname := range r.hostGroups[hostGroup] {
		go func(conf sshConf, hostname string) {
			host, port, err := net.SplitHostPort(hostname)
			if err != nil {
				r.outCh <- messages.Message{
					Room:         in.Room,
					FromUserID:   in.ToUserID,
					FromUserName: in.ToUserName,
					ToUserID:     in.FromUserID,
					ToUserName:   in.FromUserName,
					Message:      err.Error(),
				}
				return
			}
			authMethod := ssh.PublicKeyFile(conf.SSHKeyFile)
			if conf.SSHKeyFile == "" {
				authMethod = ssh.SshAgent(os.Getenv)
			}
			out, err := ssh.Run(
				cmd,
				[]string{},
				conf.Username,
				host,
				port,
				authMethod,
			)
			if err != nil {
				r.outCh <- messages.Message{
					Room:         in.Room,
					FromUserID:   in.ToUserID,
					FromUserName: in.ToUserName,
					ToUserID:     in.FromUserID,
					ToUserName:   in.FromUserName,
					Message:      err.Error(),
				}
				return
			}
			r.outCh <- messages.Message{
				Room:         in.Room,
				FromUserID:   in.ToUserID,
				FromUserName: in.ToUserName,
				ToUserID:     in.FromUserID,
				ToUserName:   in.FromUserName,
				Message:      fmt.Sprintf("%s: %s", hostname, out),
			}
		}(conf, hostname)
	}
}

// New returns a ops ruleset
func New() *opsRuleset {
	return &opsRuleset{
		hostGroups:     make(map[string][]string),
		hostGroupsConf: make(map[string]sshConf),
	}
}

func (r *opsRuleset) SetOutgoingChannel(outCh chan messages.Message) {
	r.outCh = outCh
}
<|endoftext|>


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

// agentdestroy is a module used in the upgrade protocol to kill an agent
// that has been upgraded, and remove its binary from the file system.
// The only sanity check it does, aside from validating the parameters, is
// refusing to suicide. Meaning an agent will not run this module against itself.
package agentdestroy

import (
	"encoding/json"
	"fmt"
	"mig"
	"os"
	"os/exec"
	"runtime"
)

func init() {
	mig.RegisterModule("agentdestroy", func() interface{} {
		return new(Runner)
	})
}

type Runner struct {
	Parameters Parameters
	Results    results
}

// JSON sample:
//        {
//            "module": "agentdestroy",
//            "parameters": {
//                "pid": 12345,
//                "version": "b9536d2-201403031435"
//            }
//        }
type Parameters struct {
	PID     int    `json:"pid"`
	Version string `json:"version"`
}

type results struct {
	Success bool     `json:"success"`
	Errors  []string `json:"errors,omitempty"`
}

func (r Runner) ValidateParameters() (err error) {
	if r.Parameters.PID < 2 || r.Parameters.PID > 65535 {
		return fmt.Errorf("PID '%s' is not in the range [2:65535]", r.Parameters.PID)
	}
	if r.Parameters.Version == "" {
		return fmt.Errorf("parameter 'version' is empty. Expecting version.")
	}
	return
}

func (r Runner) Run(Args []byte) string {
	err := json.Unmarshal(Args, &r.Parameters)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err))
		return r.buildResults()
	}

	err = r.ValidateParameters()
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err))
		return r.buildResults()
	}
	// Refuse to suicide
	if r.Parameters.PID == os.Getppid() {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' is mine. Refusing to suicide.", r.Parameters.PID))
		return r.buildResults()
	}

	// get the path of the agent's executable
	var binary string
	switch runtime.GOOS {
	case "linux", "darwin", "freebsd", "openbsd", "netbsd":
		binary, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", r.Parameters.PID))
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Binary path of PID '%d' not found: '%v'", r.Parameters.PID, err))
			return r.buildResults()
		}
	case "windows":
		binary = fmt.Sprintf("C:/Windows/mig-agent-%s.exe", r.Parameters.Version)
	default:
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("'%s' isn't a supported OS", runtime.GOOS))
		return r.buildResults()
	}

	// check that the binary we're removing has the right version
	version, err := getAgentVersion(binary)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to check agent version: '%v'", err))
		return r.buildResults()
	}
	if version != r.Parameters.Version {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Version mismatch. Expected '%s', found '%s'", r.Parameters.Version, version))
		return r.buildResults()
	}
	err = os.Remove(binary)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to remove binary '%s': '%v'", binary, err))
		return r.buildResults()
	}

	// Then kill the PID
	process, err := os.FindProcess(r.Parameters.PID)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not found. Returned error '%v'", r.Parameters.PID, err))
		return r.buildResults()
	} else {
		err = process.Kill()
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not killed. Returned error '%v'", r.Parameters.PID, err))
			return r.buildResults()
		}
	}
	r.Results.Success = true
	return r.buildResults()
}

// Run the agent binary to obtain its version
func getAgentVersion(binary string) (cversion string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getAgentVersion() -> %v", e)
		}
	}()
	out, err := exec.Command(binary, "-V").Output()
	if err != nil {
		panic(err)
	}
	if len(out) < 2 {
		panic("Failed to retrieve agent version.")
	}
	cversion = string(out[:len(out)-1])
	return
}

func (r Runner) buildResults() (jsonResults string) {
	jsonOutput, err := json.Marshal(r.Results)
	if err != nil {
		panic(err)
	}
	return string(jsonOutput[:])
}
<commit_msg>[medium] prevent agentdestroy module from removing executable in use<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Contributor: Julien Vehent jvehent@mozilla.com [:ulfr]

// agentdestroy is a module used in the upgrade protocol to kill an agent
// that has been upgraded. This module will refuse to suicide, meaning that
// an agent will not run this module against itself
package agentdestroy

import (
	"bitbucket.org/kardianos/osext"
	"encoding/json"
	"fmt"
	"mig"
	"os"
	"os/exec"
	"runtime"
)

func init() {
	mig.RegisterModule("agentdestroy", func() interface{} {
		return new(Runner)
	})
}

type Runner struct {
	Parameters Parameters
	Results    results
}

// JSON sample:
//        {
//            "module": "agentdestroy",
//            "parameters": {
//                "pid": 12345,
//                "version": "b9536d2-201403031435"
//            }
//        }
type Parameters struct {
	PID     int    `json:"pid"`
	Version string `json:"version"`
}

type results struct {
	Success bool     `json:"success"`
	Errors  []string `json:"errors,omitempty"`
}

func (r Runner) ValidateParameters() (err error) {
	if r.Parameters.PID < 2 || r.Parameters.PID > 65535 {
		return fmt.Errorf("PID '%s' is not in the range [2:65535]", r.Parameters.PID)
	}
	if r.Parameters.Version == "" {
		return fmt.Errorf("parameter 'version' is empty. Expecting version.")
	}
	return
}

func (r Runner) Run(Args []byte) string {
	err := json.Unmarshal(Args, &r.Parameters)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err))
		return r.buildResults()
	}

	err = r.ValidateParameters()
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("%v", err))
		return r.buildResults()
	}
	// Refuse to suicide
	if r.Parameters.PID == os.Getppid() {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' is mine. Refusing to suicide.", r.Parameters.PID))
		return r.buildResults()
	}

	// get the path of the agent's executable
	var targetExecutable string
	switch runtime.GOOS {
	case "linux", "darwin", "freebsd", "openbsd", "netbsd":
		targetExecutable, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", r.Parameters.PID))
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Executable path of PID '%d' not found: '%v'", r.Parameters.PID, err))
			return r.buildResults()
		}
	case "windows":
		targetExecutable = fmt.Sprintf("C:/Windows/mig-agent-%s.exe", r.Parameters.Version)
	default:
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("'%s' isn't a supported OS", runtime.GOOS))
		return r.buildResults()
	}
	// verify that the executable we're removing isn't in use by the current agent
	// this can happen when two agents are running of the same executable
	// in which case, do not remove the executable, and only kill the process
	myExecutable, err := osext.Executable()
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to retrieve my executable location: '%v'", err))
		return r.buildResults()
	}
	removeExecutable := true
	if myExecutable == targetExecutable {
		r.Results.Errors = append(r.Results.Errors, "Executable not removed because current agent uses it as well")
		removeExecutable = false
	}

	if removeExecutable {
		// check that the binary we're removing has the right version
		version, err := getAgentVersion(targetExecutable)
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to check agent version: '%v'", err))
			return r.buildResults()
		}
		if version != r.Parameters.Version {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Version mismatch. Expected '%s', found '%s'", r.Parameters.Version, version))
			return r.buildResults()
		}
		err = os.Remove(targetExecutable)
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("Failed to remove executable '%s': '%v'", targetExecutable, err))
			return r.buildResults()
		}
	}

	// Then kill the PID
	process, err := os.FindProcess(r.Parameters.PID)
	if err != nil {
		r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not found. Returned error '%v'", r.Parameters.PID, err))
		return r.buildResults()
	} else {
		err = process.Kill()
		if err != nil {
			r.Results.Errors = append(r.Results.Errors, fmt.Sprintf("PID '%d' not killed. Returned error '%v'", r.Parameters.PID, err))
			return r.buildResults()
		}
	}
	r.Results.Success = true
	return r.buildResults()
}

// Run the agent binary to obtain its version
func getAgentVersion(binary string) (cversion string, err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("getAgentVersion() -> %v", e)
		}
	}()
	out, err := exec.Command(binary, "-V").Output()
	if err != nil {
		panic(err)
	}
	if len(out) < 2 {
		panic("Failed to retrieve agent version.")
	}
	cversion = string(out[:len(out)-1])
	return
}

func (r Runner) buildResults() (jsonResults string) {
	jsonOutput, err := json.Marshal(r.Results)
	if err != nil {
		panic(err)
	}
	return string(jsonOutput[:])
}
<|endoftext|>


<commit_before>package geometry

import (
	dax "github.com/dlespiau/dax/lib"
	"github.com/dlespiau/dax/math"
)

type Box struct {
	Width, Height, Depth                                  float32
	NumWidthSegments, NumHeightSegments, NumDepthSegments int
}

type BoxOptions struct {
	NumWidthSegments, NumHeightSegments, NumDepthSegments int
}

var defaultBox = Box{
	Width:             1.0,
	Height:            1.0,
	Depth:             1.0,
	NumWidthSegments:  1,
	NumHeightSegments: 1,
	NumDepthSegments:  1,
}

// NewBox creates a new box geometry.
func NewBox(width, height, depth float32, options ...BoxOptions) *Box {
	box := defaultBox
	box.Width = width
	box.Height = height
	box.Depth = depth

	if len(options) == 0 {
		return &box
	}

	if options[0].NumWidthSegments > 0 {
		box.NumWidthSegments = options[0].NumWidthSegments
	}
	if options[0].NumHeightSegments > 0 {
		box.NumHeightSegments = options[0].NumHeightSegments
	}
	if options[0].NumDepthSegments > 0 {
		box.NumDepthSegments = options[0].NumDepthSegments
	}

	return &box
}

type boxContext struct {
	nVertices int
	positions []float32
	normals   []float32
	uvs       []float32
	indices   []uint
}

func buildPlane(ctx *boxContext,
	u, v, w int,
	udir, vdir float32,
	width, height, depth float32,
	gridX, gridY int) {

	segmentWidth := width / float32(gridX)
	segmentHeight := height / float32(gridY)

	widthHalf := width / 2
	heightHalf := height / 2
	depthHalf := depth / 2

	gridX1 := gridX + 1
	gridY1 := gridY + 1

	vertexCounter := 0

	vector := math.Vec3{}

	// generate vertices, normals and uvs

	for iy := 0; iy < gridY1; iy++ {
		y := float32(iy)*segmentHeight - heightHalf

		for ix := 0; ix < gridX1; ix++ {
			x := float32(ix)*segmentWidth - widthHalf

			// position
			vector[u] = x * udir
			vector[v] = y * vdir
			vector[w] = depthHalf

			ctx.positions = append(ctx.positions, vector[0], vector[1], vector[2])

			// normal
			vector[u] = 0
			vector[v] = 0
			if depth > 0 {
				vector[w] = 1
			} else {
				vector[w] = -1
			}

			ctx.normals = append(ctx.normals, vector[0], vector[1], vector[2])

			// uvs
			ctx.uvs = append(ctx.uvs, float32(ix)/float32(gridX), 1-(float32(iy)/float32(gridY)))

			// counters
			vertexCounter += 1

		}

	}

	// indices

	// 1. you need three indices to draw a single face
	// 2. a single segment consists of two faces
	// 3. so we need to generate six (2*3) indices per segment

	for iy := 0; iy < gridY; iy++ {
		for ix := 0; ix < gridX; ix++ {
			a := uint(ctx.nVertices + ix + gridX1*iy)
			b := uint(ctx.nVertices + ix + gridX1*(iy+1))
			c := uint(ctx.nVertices + (ix + 1) + gridX1*(iy+1))
			d := uint(ctx.nVertices + (ix + 1) + gridX1*iy)

			// faces
			ctx.indices = append(ctx.indices, a, b, d, b, c, d)
		}

	}

	// update total number of vertices
	ctx.nVertices += vertexCounter
}

func (b *Box) GetMesh() *dax.Mesh {

	m := dax.NewMesh()

	ctx := &boxContext{}

	width := b.Width
	height := b.Height
	depth := b.Depth
	widthSegments := b.NumWidthSegments
	heightSegments := b.NumHeightSegments
	depthSegments := b.NumDepthSegments

	buildPlane(ctx, 2, 1, 0, -1, -1, depth, height, width, depthSegments, heightSegments)  // px
	buildPlane(ctx, 2, 1, 0, 1, -1, depth, height, -width, depthSegments, heightSegments)  // nx
	buildPlane(ctx, 0, 2, 1, 1, 1, width, depth, height, widthSegments, depthSegments)     // py
	buildPlane(ctx, 0, 2, 1, 1, -1, width, depth, -height, widthSegments, depthSegments)   // ny
	buildPlane(ctx, 0, 1, 2, 1, -1, width, height, depth, widthSegments, heightSegments)   // pz
	buildPlane(ctx, 0, 1, 2, -1, -1, width, height, -depth, widthSegments, heightSegments) // nz

	m.AddAttribute("position", ctx.positions, 3)
	m.AddAttribute("normal", ctx.normals, 3)
	m.AddAttribute("uv", ctx.uvs, 2)
	m.AddIndices(ctx.indices)

	return m
}
<commit_msg>box: Add some godoc strings<commit_after>package geometry

import (
	dax "github.com/dlespiau/dax/lib"
	"github.com/dlespiau/dax/math"
)

// Box is a rectangular cuboid centered around (0, 0, 0) with sizes Width,
// Height and Depth on the X, Y and Z axis respectively. Some control over the
// tesselation of each face is given through the number of segments on each
// direction.
type Box struct {
	Width, Height, Depth                                  float32
	NumWidthSegments, NumHeightSegments, NumDepthSegments int
}

// BoxOptions contains optional parameters for the Box constructors.
type BoxOptions struct {
	NumWidthSegments, NumHeightSegments, NumDepthSegments int
}

var defaultBox = Box{
	Width:             1.0,
	Height:            1.0,
	Depth:             1.0,
	NumWidthSegments:  1,
	NumHeightSegments: 1,
	NumDepthSegments:  1,
}

// NewBox creates a new box.
func NewBox(width, height, depth float32, options ...BoxOptions) *Box {
	box := defaultBox
	box.Width = width
	box.Height = height
	box.Depth = depth

	if len(options) == 0 {
		return &box
	}

	if options[0].NumWidthSegments > 0 {
		box.NumWidthSegments = options[0].NumWidthSegments
	}
	if options[0].NumHeightSegments > 0 {
		box.NumHeightSegments = options[0].NumHeightSegments
	}
	if options[0].NumDepthSegments > 0 {
		box.NumDepthSegments = options[0].NumDepthSegments
	}

	return &box
}

type boxContext struct {
	nVertices int
	positions []float32
	normals   []float32
	uvs       []float32
	indices   []uint
}

func buildPlane(ctx *boxContext,
	u, v, w int,
	udir, vdir float32,
	width, height, depth float32,
	gridX, gridY int) {

	segmentWidth := width / float32(gridX)
	segmentHeight := height / float32(gridY)

	widthHalf := width / 2
	heightHalf := height / 2
	depthHalf := depth / 2

	gridX1 := gridX + 1
	gridY1 := gridY + 1

	vertexCounter := 0

	vector := math.Vec3{}

	// generate vertices, normals and uvs

	for iy := 0; iy < gridY1; iy++ {
		y := float32(iy)*segmentHeight - heightHalf

		for ix := 0; ix < gridX1; ix++ {
			x := float32(ix)*segmentWidth - widthHalf

			// position
			vector[u] = x * udir
			vector[v] = y * vdir
			vector[w] = depthHalf

			ctx.positions = append(ctx.positions, vector[0], vector[1], vector[2])

			// normal
			vector[u] = 0
			vector[v] = 0
			if depth > 0 {
				vector[w] = 1
			} else {
				vector[w] = -1
			}

			ctx.normals = append(ctx.normals, vector[0], vector[1], vector[2])

			// uvs
			ctx.uvs = append(ctx.uvs, float32(ix)/float32(gridX), 1-(float32(iy)/float32(gridY)))

			// counters
			vertexCounter++

		}

	}

	// indices

	// 1. you need three indices to draw a single face
	// 2. a single segment consists of two faces
	// 3. so we need to generate six (2*3) indices per segment

	for iy := 0; iy < gridY; iy++ {
		for ix := 0; ix < gridX; ix++ {
			a := uint(ctx.nVertices + ix + gridX1*iy)
			b := uint(ctx.nVertices + ix + gridX1*(iy+1))
			c := uint(ctx.nVertices + (ix + 1) + gridX1*(iy+1))
			d := uint(ctx.nVertices + (ix + 1) + gridX1*iy)

			// faces
			ctx.indices = append(ctx.indices, a, b, d, b, c, d)
		}

	}

	// update total number of vertices
	ctx.nVertices += vertexCounter
}

// GetMesh is part of the dax.Mesher interface.
func (b *Box) GetMesh() *dax.Mesh {

	m := dax.NewMesh()

	ctx := &boxContext{}

	width := b.Width
	height := b.Height
	depth := b.Depth
	widthSegments := b.NumWidthSegments
	heightSegments := b.NumHeightSegments
	depthSegments := b.NumDepthSegments

	buildPlane(ctx, 2, 1, 0, -1, -1, depth, height, width, depthSegments, heightSegments)  // px
	buildPlane(ctx, 2, 1, 0, 1, -1, depth, height, -width, depthSegments, heightSegments)  // nx
	buildPlane(ctx, 0, 2, 1, 1, 1, width, depth, height, widthSegments, depthSegments)     // py
	buildPlane(ctx, 0, 2, 1, 1, -1, width, depth, -height, widthSegments, depthSegments)   // ny
	buildPlane(ctx, 0, 1, 2, 1, -1, width, height, depth, widthSegments, heightSegments)   // pz
	buildPlane(ctx, 0, 1, 2, -1, -1, width, height, -depth, widthSegments, heightSegments) // nz

	m.AddAttribute("position", ctx.positions, 3)
	m.AddAttribute("normal", ctx.normals, 3)
	m.AddAttribute("uv", ctx.uvs, 2)
	m.AddIndices(ctx.indices)

	return m
}
<|endoftext|>


<commit_before>package client

import (
	"fmt"
	"io"
	"net/http"
)

type clientImpl struct {
	url, username, password string
}

// BasicHTTP is a client for the server using Basic HTTP Auth as it's
// authorization mechanism.
func BasicHTTP(url, username, password string) *clientImpl {
	return &clientImpl{url, username, password}
}

func (c *clientImpl) Get(key string) (io.ReadCloser, error) {
	res, err := c.dial("GET", key, nil)
	if err != nil {
		return nil, err
	}
	return res.Body, nil
}

func (c *clientImpl) Put(key string, source io.Reader) error {
	_, err := c.dial("PUT", key, source)
	return err
}

func (c *clientImpl) dial(method, key string, data io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, c.urlFor(key), data)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(c.username, c.password)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Invalid response code: %d", res.StatusCode)
	}
	return res, nil
}

func (c *clientImpl) urlFor(key string) string {
	return fmt.Sprintf("%s/?key=%s", c.url, key)
}
<commit_msg>Implement Delete on the Client (#4)<commit_after>package client

import (
	"fmt"
	"io"
	"net/http"
)

type clientImpl struct {
	url, username, password string
}

// BasicHTTP is a client for the server using Basic HTTP Auth as it's
// authorization mechanism.
func BasicHTTP(url, username, password string) *clientImpl {
	return &clientImpl{url, username, password}
}

func (c *clientImpl) Get(key string) (io.ReadCloser, error) {
	res, err := c.dial("GET", key, nil)
	if err != nil {
		return nil, err
	}
	return res.Body, nil
}

func (c *clientImpl) Delete(key string) error {
	_, err := c.dial("DELETE", key, nil)
	return err
}

func (c *clientImpl) Put(key string, source io.Reader) error {
	_, err := c.dial("PUT", key, source)
	return err
}

func (c *clientImpl) dial(method, key string, data io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, c.urlFor(key), data)
	if err != nil {
		return nil, err
	}
	req.SetBasicAuth(c.username, c.password)
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	if res.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("Invalid response code: %d", res.StatusCode)
	}
	return res, nil
}

func (c *clientImpl) urlFor(key string) string {
	return fmt.Sprintf("%s/?key=%s", c.url, key)
}
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten 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 text_test

import (
	"errors"
	"image/color"
	"os"
	"testing"

	"github.com/hajimehoshi/bitmapfont"
	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/testflock"
	. "github.com/hajimehoshi/ebiten/text"
)

func TestMain(m *testing.M) {
	testflock.Lock()
	defer testflock.Unlock()

	code := 0
	// Run an Ebiten process so that (*Image).At is available.
	regularTermination := errors.New("regular termination")
	f := func(screen *ebiten.Image) error {
		code = m.Run()
		return regularTermination
	}
	if err := ebiten.Run(f, 320, 240, 1, "Test"); err != nil && err != regularTermination {
		panic(err)
	}
	os.Exit(code)
}

func TestTextColor(t *testing.T) {
	clr := color.RGBA{0x80, 0x80, 0x80, 0x80}
	img, _ := ebiten.NewImage(30, 30, ebiten.FilterNearest)
	Draw(img, "Hello", bitmapfont.Gothic12r, 12, 12, clr)

	w, h := img.Size()
	allTransparent := true
	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			got := img.At(i, j)
			want1 := color.RGBA{0x80, 0x80, 0x80, 0x80}
			want2 := color.RGBA{}
			if got != want1 && got != want2 {
				t.Errorf("img At(%d, %d): got %v; want %v or %v", i, j, got, want1, want2)
			}
			if got == want1 {
				allTransparent = false
			}
		}
	}
	if allTransparent {
		t.Fail()
	}
}
<commit_msg>text: Fix style<commit_after>// Copyright 2018 The Ebiten 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 text_test

import (
	"errors"
	"image/color"
	"os"
	"testing"

	"github.com/hajimehoshi/bitmapfont"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/internal/testflock"
	. "github.com/hajimehoshi/ebiten/text"
)

func TestMain(m *testing.M) {
	testflock.Lock()
	defer testflock.Unlock()

	code := 0
	// Run an Ebiten process so that (*Image).At is available.
	regularTermination := errors.New("regular termination")
	f := func(screen *ebiten.Image) error {
		code = m.Run()
		return regularTermination
	}
	if err := ebiten.Run(f, 320, 240, 1, "Test"); err != nil && err != regularTermination {
		panic(err)
	}
	os.Exit(code)
}

func TestTextColor(t *testing.T) {
	clr := color.RGBA{0x80, 0x80, 0x80, 0x80}
	img, _ := ebiten.NewImage(30, 30, ebiten.FilterNearest)
	Draw(img, "Hello", bitmapfont.Gothic12r, 12, 12, clr)

	w, h := img.Size()
	allTransparent := true
	for j := 0; j < h; j++ {
		for i := 0; i < w; i++ {
			got := img.At(i, j)
			want1 := color.RGBA{0x80, 0x80, 0x80, 0x80}
			want2 := color.RGBA{}
			if got != want1 && got != want2 {
				t.Errorf("img At(%d, %d): got %v; want %v or %v", i, j, got, want1, want2)
			}
			if got == want1 {
				allTransparent = false
			}
		}
	}
	if allTransparent {
		t.Fail()
	}
}
<|endoftext|>


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

package main_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

var godocTests = []struct {
	args    []string
	matches []string // regular expressions
}{
	{
		[]string{"fmt"},
		[]string{
			`import "fmt"`,
			`Package fmt implements formatted I/O`,
		},
	},
	{
		[]string{"io", "WriteString"},
		[]string{
			`func WriteString\(`,
			`WriteString writes the contents of the string s to w`,
		},
	},
	{
		[]string{"nonexistingpkg"},
		[]string{
			`no such file or directory`,
		},
	},
	{
		[]string{"fmt", "NonexistentSymbol"},
		[]string{
			`No match found\.`,
		},
	},
}

// Basic regression test for godoc command-line tool.
func TestGodoc(t *testing.T) {
	tmp, err := ioutil.TempDir("", "godoc-regtest-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmp)

	bin := filepath.Join(tmp, "godoc")
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}
	cmd := exec.Command("go", "build", "-o", bin)
	if err := cmd.Run(); err != nil {
		t.Fatalf("Building godoc: %v", err)
	}

	for _, test := range godocTests {
		cmd := exec.Command(bin, test.args...)
		cmd.Args[0] = "godoc"
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("Running with args %#v: %v", test.args, err)
			continue
		}
		for _, pat := range test.matches {
			re := regexp.MustCompile(pat)
			if !re.Match(out) {
				t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
			}
		}
	}
}
<commit_msg>go.tools/cmd/godoc: fix test on Plan 9<commit_after>// Copyright 2013 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
	"testing"
)

var godocTests = []struct {
	args    []string
	matches []string // regular expressions
}{
	{
		[]string{"fmt"},
		[]string{
			`import "fmt"`,
			`Package fmt implements formatted I/O`,
		},
	},
	{
		[]string{"io", "WriteString"},
		[]string{
			`func WriteString\(`,
			`WriteString writes the contents of the string s to w`,
		},
	},
	{
		[]string{"nonexistingpkg"},
		[]string{
			`no such file or directory|does not exist`,
		},
	},
	{
		[]string{"fmt", "NonexistentSymbol"},
		[]string{
			`No match found\.`,
		},
	},
}

// Basic regression test for godoc command-line tool.
func TestGodoc(t *testing.T) {
	tmp, err := ioutil.TempDir("", "godoc-regtest-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(tmp)

	bin := filepath.Join(tmp, "godoc")
	if runtime.GOOS == "windows" {
		bin += ".exe"
	}
	cmd := exec.Command("go", "build", "-o", bin)
	if err := cmd.Run(); err != nil {
		t.Fatalf("Building godoc: %v", err)
	}

	for _, test := range godocTests {
		cmd := exec.Command(bin, test.args...)
		cmd.Args[0] = "godoc"
		out, err := cmd.CombinedOutput()
		if err != nil {
			t.Errorf("Running with args %#v: %v", test.args, err)
			continue
		}
		for _, pat := range test.matches {
			re := regexp.MustCompile(pat)
			if !re.Match(out) {
				t.Errorf("godoc %v =\n%s\nwanted /%v/", strings.Join(test.args, " "), out, pat)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * This program is a Docker entrypoint for Apache Cassandra.
 */

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/tobert/sprok"
	"gopkg.in/yaml.v2"
)

const ugid = 1337

type CassandraDockerConfig struct {
	VolDir         string   // read/write data, should be a volume
	SrcConfDir     string   // root path for assets to copy to the volume
	ConfDir        string   // conf directory
	DataDir        string   // data directory
	CommitLogDir   string   // cl directory
	LogDir         string   // log directory
	SavedCachesDir string   // saved_caches directory
	CqlshDotDir    string   // ~/.cassandra
	CassandraYaml  string   // conf/cassandra.yaml
	SprokDir       string   // conf/sproks directory
	ExtraArgs      []string // extra args not caught by flag.Parse
	// Cassandra configuration items
	ClusterName      string // cluster_name in cassandra.yaml
	Seeds            string // seeds value for cassandra.yaml
	CassandraLogfile string // system.log
	DefaultIP        string // IP of the default route interface
	JmxPort          int
}

func main() {
	cdc := CassandraDockerConfig{
		VolDir:           "/data",
		SrcConfDir:       "/src/conf",
		ConfDir:          "/data/conf",
		DataDir:          "/data/data",
		CommitLogDir:     "/data/commitlog",
		LogDir:           "/data/log",
		SavedCachesDir:   "/data/saved_caches",
		CqlshDotDir:      "/data/.cassandra",
		CassandraYaml:    "/data/conf/cassandra.yaml",
		SprokDir:         "/data/conf/sproks",
		ClusterName:      "Docker Cluster",
		Seeds:            "127.0.0.1",
		CassandraLogfile: "/data/log/system.log",
		DefaultIP:        "127.0.0.1",
		JmxPort:          7199,
	}

	var command, sprokFile string
	var args []string

	// handle symlink commands, e.g. ln -s /bin/cassandra-docker /bin/cqlsh
	if path.Base(os.Args[0]) != "cassandra-docker" {
		command = path.Base(os.Args[0])
		args = os.Args[1:]
	} else {
		// extract the subcommand from os.Args
		switch len(os.Args) {
		// no arguments, just start Cassandra
		// TODO: this should probably only happen if it's pid 1
		case 1:
			command = "cassandra"
		// exactly one argument with no args
		case 2:
			command = os.Args[1]
			args = []string{}
		// command + arguments
		default:
			command = os.Args[1]
			args = os.Args[2:]
		}
	}

	// parse the subcommand and arguments to it
	fs := flag.NewFlagSet("cassandra-docker", flag.ExitOnError)
	switch command {
	case "cassandra":
		fs.StringVar(&cdc.Seeds, "seeds", "127.0.0.1", "comma-delimited list of seeds for clustering")
		fs.StringVar(&cdc.ClusterName, "name", "Docker Cluster", "cluster_name")
		sprokFile = path.Join(cdc.SprokDir, "cassandra.yaml")
	case "cqlsh":
		sprokFile = path.Join(cdc.SprokDir, "cqlsh.yaml")
	case "nodetool":
		fs.IntVar(&cdc.JmxPort, "p", 7199, "jmx port")
		sprokFile = path.Join(cdc.SprokDir, "nodetool.yaml")
	case "cassandra-stress":
		sprokFile = path.Join(cdc.SprokDir, "cassandra-stress.yaml")
	default:
		log.Fatalf("invalid command '%s'", command)
	}

	fs.Parse(args)

	// copy the remaining command-line args to cdc
	cdc.ExtraArgs = fs.Args()

	// bootstrap - find the default IP, make directories, copy files
	cdc.setDefaultIP()
	cdc.mkdirs()
	// copies files from src to data, running them through as templates
	// in the process. existing files are not overwritten
	cdc.tmplCopy()

	// load the sprok config
	fd, err := os.Open(sprokFile)
	if err != nil {
		log.Fatalf("error opening '%s' for read: %s\n", sprokFile, err)
	}

	// render the config template before unmarshaling
	// this allows sprok files to work across upgrades with smart use
	// of glob() to work around files with version numbers in them
	var data bytes.Buffer
	cdc.render(fd, &data)

	// configure the process from the yaml
	proc := sprok.NewProcess()
	err = yaml.Unmarshal(data.Bytes(), &proc)
	if err != nil {
		log.Fatalf("could not parse YAML in file '%s': %s\n", sprokFile, err)
	}

	// this is an actual execve(3p), this process is replaced with the new one
	proc.Exec()
}

func (cdc *CassandraDockerConfig) mkdirs() {
	mkdirAll(cdc.ConfDir)
	mkdirAll(cdc.DataDir)
	mkdirAll(cdc.CommitLogDir)
	mkdirAll(cdc.LogDir)
	mkdirAll(cdc.SavedCachesDir)
	mkdirAll(cdc.CqlshDotDir)
	mkdirAll(cdc.SprokDir)

	chownAll(cdc.DataDir)
	chownAll(cdc.CommitLogDir)
	chownAll(cdc.LogDir)
	chownAll(cdc.SavedCachesDir)
	chownAll(cdc.CqlshDotDir)
}

// tmplCopy reads all the files in cdc.SrcConfDir, treating them as text
// templates, then writes them to cdc.ConfDir. If a file exists in ConfDir
// it is not overwritten.
func (cdc *CassandraDockerConfig) tmplCopy() {
	walk := func(fromName string, fromFi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("failed to find files in '%s': %s\n", cdc.SrcConfDir, err)
		}

		// only safe for same filesystem with no relative paths or symlinks
		toName := strings.Replace(fromName, cdc.SrcConfDir, cdc.ConfDir, 1)

		if exists(toName) {
			return nil // try not to overwrite any existing files
		} else if strings.HasPrefix(path.Base(fromName), ".") {
			return nil
		} else if fromFi.IsDir() {
			if exists(toName) {
				return nil
			} else {
				mkdirAll(toName)
			}
		} else if fromFi.Mode().IsRegular() {
			// don't render sprok files, only copy them
			// they will get rendered at run time
			if strings.HasSuffix(path.Dir(fromName), "sproks") {
				cp(fromName, toName)
			} else {
				cdc.renderFile(fromName, toName)
			}
		} else {
			log.Fatalf("unsupported file mode on file '%s'\n", fromName)
		}

		return nil
	}

	err := filepath.Walk(cdc.SrcConfDir, walk)
	if err != nil {
		log.Fatalf("tmplCopy() failed: %s\n", err)
	}
}

// renderFile renders one file to another using text/template
func (cdc *CassandraDockerConfig) renderFile(src, dest string) {
	in, err := os.Open(src)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", src, err)
	}
	defer in.Close()

	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for write: %s\n", dest, err)
	}
	defer out.Close()

	cdc.render(in, out)
}

// render renders an io.Reader to an io.Writer using text/template
func (cdc *CassandraDockerConfig) render(in io.Reader, out io.Writer) {
	funcMap := template.FuncMap{
		"glob": Globber,
	}

	tdata, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("template read failed: %s\n", err)
	}

	tmpl, err := template.New("whatever").Funcs(funcMap).Parse(string(tdata))
	if err != nil {
		log.Fatalf("template parsing failed: %s", err)
	}

	err = tmpl.Execute(out, cdc)
	if err != nil {
		log.Fatalf("template rendering failed: %s\n", err)
	}
}

// setDefaultIP finds the first configured interface that is not a loopback
// and sets the cdc.DefaultIP value
func (cdc *CassandraDockerConfig) setDefaultIP() {
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("error while listing network interfaces: %s\n", err)
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface is down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // ignore loopback interface
		}

		addrs, err := iface.Addrs()
		if err != nil {
			log.Fatalf("error while examining network interface: %s\n", err)
		}

		// for now, just go with the first interface that is up
		// and is not a loopback, which should cover most Docker setups
		for _, addr := range addrs {
			switch v := addr.(type) {
			case *net.IPNet:
				cdc.DefaultIP = v.IP.String()
				return
			}
		}
	}
}

// Globber takes paths, performs a glob match, then returns
// all the results joined with the specified seperator.
func Globber(sep string, globs ...string) string {
	if len(globs) == 0 {
		log.Fatalf("Globber() requires at least one path.\n")
	}

	out := []string{}

	for _, glob := range globs {
		filenames, err := filepath.Glob(glob)
		if err != nil {
			log.Fatalf("file glob failed: %s\n", err)
		}

		for _, filename := range filenames {
			out = append(out, filename)
		}
	}

	return strings.Join(out, sep)
}

// exists returns boolean whether a path exists or not
func exists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	} else if err != nil {
		log.Fatalf("could not stat file '%s': %s\n", name, err)
	}

	return true
}

// mkdirAll creates a directory recursively, crashes the program on error.
func mkdirAll(name string) {
	err := os.MkdirAll(name, 0755)
	if err != nil {
		log.Fatalf("os.MkdirAll('%s') failed: %s\n", name)
	}
}

func chownAll(name string) {
	walk := func(fname string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("error during fs walk of '%s': %s\n", fname, err)

		}

		return os.Chown(fname, ugid, ugid)
	}

	err := filepath.Walk(name, walk)
	if err != nil {
		log.Fatalf("chownAll('%s') failed: %s\n", name, err)
	}
}

// cp copies a file, crashing the program on any errors
// It does not attempt to use rename.
func cp(from, to string) {
	in, err := os.Open(from)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", from, err)
	}
	defer in.Close()

	out, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for writing: %s\n", to, err)
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		log.Fatalf("data copy failed for file '%s': %s\n", to, err)
	}
}
<commit_msg>Fix #9 seed address set to listen address by default<commit_after>package main

/*
 * Copyright 2014 Albert P. Tobey <atobey@datastax.com> @AlTobey
 *
 * 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.
 *
 * This program is a Docker entrypoint for Apache Cassandra.
 */

import (
	"bytes"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"
	"text/template"

	"github.com/tobert/sprok"
	"gopkg.in/yaml.v2"
)

const ugid = 1337

type CassandraDockerConfig struct {
	VolDir         string   // read/write data, should be a volume
	SrcConfDir     string   // root path for assets to copy to the volume
	ConfDir        string   // conf directory
	DataDir        string   // data directory
	CommitLogDir   string   // cl directory
	LogDir         string   // log directory
	SavedCachesDir string   // saved_caches directory
	CqlshDotDir    string   // ~/.cassandra
	CassandraYaml  string   // conf/cassandra.yaml
	SprokDir       string   // conf/sproks directory
	ExtraArgs      []string // extra args not caught by flag.Parse
	// Cassandra configuration items
	ClusterName      string // cluster_name in cassandra.yaml
	Seeds            string // seeds value for cassandra.yaml
	CassandraLogfile string // system.log
	DefaultIP        string // IP of the default route interface
	JmxPort          int
}

func main() {
	cdc := CassandraDockerConfig{
		VolDir:           "/data",
		SrcConfDir:       "/src/conf",
		ConfDir:          "/data/conf",
		DataDir:          "/data/data",
		CommitLogDir:     "/data/commitlog",
		LogDir:           "/data/log",
		SavedCachesDir:   "/data/saved_caches",
		CqlshDotDir:      "/data/.cassandra",
		CassandraYaml:    "/data/conf/cassandra.yaml",
		SprokDir:         "/data/conf/sproks",
		ClusterName:      "Docker Cluster",
		Seeds:            "127.0.0.1",
		CassandraLogfile: "/data/log/system.log",
		DefaultIP:        "127.0.0.1",
		JmxPort:          7199,
	}

	var command, sprokFile string
	var args []string

	// handle symlink commands, e.g. ln -s /bin/cassandra-docker /bin/cqlsh
	if path.Base(os.Args[0]) != "cassandra-docker" {
		command = path.Base(os.Args[0])
		args = os.Args[1:]
	} else {
		// extract the subcommand from os.Args
		switch len(os.Args) {
		// no arguments, just start Cassandra
		// TODO: this should probably only happen if it's pid 1
		case 1:
			command = "cassandra"
		// exactly one argument with no args
		case 2:
			command = os.Args[1]
			args = []string{}
		// command + arguments
		default:
			command = os.Args[1]
			args = os.Args[2:]
		}
	}

	// parse the subcommand and arguments to it
	fs := flag.NewFlagSet("cassandra-docker", flag.ExitOnError)
	switch command {
	case "cassandra":
		fs.StringVar(&cdc.Seeds, "seeds", "127.0.0.1", "comma-delimited list of seeds for clustering")
		fs.StringVar(&cdc.ClusterName, "name", "Docker Cluster", "cluster_name")
		sprokFile = path.Join(cdc.SprokDir, "cassandra.yaml")
	case "cqlsh":
		sprokFile = path.Join(cdc.SprokDir, "cqlsh.yaml")
	case "nodetool":
		fs.IntVar(&cdc.JmxPort, "p", 7199, "jmx port")
		sprokFile = path.Join(cdc.SprokDir, "nodetool.yaml")
	case "cassandra-stress":
		sprokFile = path.Join(cdc.SprokDir, "cassandra-stress.yaml")
	default:
		log.Fatalf("invalid command '%s'", command)
	}

	fs.Parse(args)

	// copy the remaining command-line args to cdc
	cdc.ExtraArgs = fs.Args()

	// bootstrap - find the default IP, make directories, copy files
	cdc.setDefaultIP()
	if strings.EqualFold(cdc.Seeds,"127.0.0.1") {
		cdc.Seeds=cdc.DefaultIP;
	}

	cdc.mkdirs()
	// copies files from src to data, running them through as templates
	// in the process. existing files are not overwritten
	cdc.tmplCopy()

	// load the sprok config
	fd, err := os.Open(sprokFile)
	if err != nil {
		log.Fatalf("error opening '%s' for read: %s\n", sprokFile, err)
	}

	// render the config template before unmarshaling
	// this allows sprok files to work across upgrades with smart use
	// of glob() to work around files with version numbers in them
	var data bytes.Buffer
	cdc.render(fd, &data)

	// configure the process from the yaml
	proc := sprok.NewProcess()
	err = yaml.Unmarshal(data.Bytes(), &proc)
	if err != nil {
		log.Fatalf("could not parse YAML in file '%s': %s\n", sprokFile, err)
	}

	// this is an actual execve(3p), this process is replaced with the new one
	proc.Exec()
}

func (cdc *CassandraDockerConfig) mkdirs() {
	mkdirAll(cdc.ConfDir)
	mkdirAll(cdc.DataDir)
	mkdirAll(cdc.CommitLogDir)
	mkdirAll(cdc.LogDir)
	mkdirAll(cdc.SavedCachesDir)
	mkdirAll(cdc.CqlshDotDir)
	mkdirAll(cdc.SprokDir)

	chownAll(cdc.DataDir)
	chownAll(cdc.CommitLogDir)
	chownAll(cdc.LogDir)
	chownAll(cdc.SavedCachesDir)
	chownAll(cdc.CqlshDotDir)
}

// tmplCopy reads all the files in cdc.SrcConfDir, treating them as text
// templates, then writes them to cdc.ConfDir. If a file exists in ConfDir
// it is not overwritten.
func (cdc *CassandraDockerConfig) tmplCopy() {
	walk := func(fromName string, fromFi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("failed to find files in '%s': %s\n", cdc.SrcConfDir, err)
		}

		// only safe for same filesystem with no relative paths or symlinks
		toName := strings.Replace(fromName, cdc.SrcConfDir, cdc.ConfDir, 1)

		if exists(toName) {
			return nil // try not to overwrite any existing files
		} else if strings.HasPrefix(path.Base(fromName), ".") {
			return nil
		} else if fromFi.IsDir() {
			if exists(toName) {
				return nil
			} else {
				mkdirAll(toName)
			}
		} else if fromFi.Mode().IsRegular() {
			// don't render sprok files, only copy them
			// they will get rendered at run time
			if strings.HasSuffix(path.Dir(fromName), "sproks") {
				cp(fromName, toName)
			} else {
				cdc.renderFile(fromName, toName)
			}
		} else {
			log.Fatalf("unsupported file mode on file '%s'\n", fromName)
		}

		return nil
	}

	err := filepath.Walk(cdc.SrcConfDir, walk)
	if err != nil {
		log.Fatalf("tmplCopy() failed: %s\n", err)
	}
}

// renderFile renders one file to another using text/template
func (cdc *CassandraDockerConfig) renderFile(src, dest string) {
	in, err := os.Open(src)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", src, err)
	}
	defer in.Close()

	out, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for write: %s\n", dest, err)
	}
	defer out.Close()

	cdc.render(in, out)
}

// render renders an io.Reader to an io.Writer using text/template
func (cdc *CassandraDockerConfig) render(in io.Reader, out io.Writer) {
	funcMap := template.FuncMap{
		"glob": Globber,
	}

	tdata, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("template read failed: %s\n", err)
	}

	tmpl, err := template.New("whatever").Funcs(funcMap).Parse(string(tdata))
	if err != nil {
		log.Fatalf("template parsing failed: %s", err)
	}

	err = tmpl.Execute(out, cdc)
	if err != nil {
		log.Fatalf("template rendering failed: %s\n", err)
	}
}

// setDefaultIP finds the first configured interface that is not a loopback
// and sets the cdc.DefaultIP value
func (cdc *CassandraDockerConfig) setDefaultIP() {
	ifaces, err := net.Interfaces()
	if err != nil {
		log.Fatalf("error while listing network interfaces: %s\n", err)
	}

	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface is down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // ignore loopback interface
		}

		addrs, err := iface.Addrs()
		if err != nil {
			log.Fatalf("error while examining network interface: %s\n", err)
		}

		// for now, just go with the first interface that is up
		// and is not a loopback, which should cover most Docker setups
		for _, addr := range addrs {
			switch v := addr.(type) {
			case *net.IPNet:
				cdc.DefaultIP = v.IP.String()
				return
			}
		}
	}
}

// Globber takes paths, performs a glob match, then returns
// all the results joined with the specified seperator.
func Globber(sep string, globs ...string) string {
	if len(globs) == 0 {
		log.Fatalf("Globber() requires at least one path.\n")
	}

	out := []string{}

	for _, glob := range globs {
		filenames, err := filepath.Glob(glob)
		if err != nil {
			log.Fatalf("file glob failed: %s\n", err)
		}

		for _, filename := range filenames {
			out = append(out, filename)
		}
	}

	return strings.Join(out, sep)
}

// exists returns boolean whether a path exists or not
func exists(name string) bool {
	_, err := os.Stat(name)
	if os.IsNotExist(err) {
		return false
	} else if err != nil {
		log.Fatalf("could not stat file '%s': %s\n", name, err)
	}

	return true
}

// mkdirAll creates a directory recursively, crashes the program on error.
func mkdirAll(name string) {
	err := os.MkdirAll(name, 0755)
	if err != nil {
		log.Fatalf("os.MkdirAll('%s') failed: %s\n", name)
	}
}

func chownAll(name string) {
	walk := func(fname string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("error during fs walk of '%s': %s\n", fname, err)

		}

		return os.Chown(fname, ugid, ugid)
	}

	err := filepath.Walk(name, walk)
	if err != nil {
		log.Fatalf("chownAll('%s') failed: %s\n", name, err)
	}
}

// cp copies a file, crashing the program on any errors
// It does not attempt to use rename.
func cp(from, to string) {
	in, err := os.Open(from)
	if err != nil {
		log.Fatalf("could not open '%s' for reading: %s\n", from, err)
	}
	defer in.Close()

	out, err := os.OpenFile(to, os.O_WRONLY|os.O_CREATE, 0644)
	if err != nil {
		log.Fatalf("could not open '%s' for writing: %s\n", to, err)
	}
	defer out.Close()

	_, err = io.Copy(out, in)
	if err != nil {
		log.Fatalf("data copy failed for file '%s': %s\n", to, err)
	}
}
<|endoftext|>


<commit_before>package logrus

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
	gray    = 37
)

var (
	baseTimestamp time.Time
	isTerminal    bool
)

func init() {
	baseTimestamp = time.Now()
	isTerminal = IsTerminal()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool

	// Force disabling colors.
	DisableColors bool

	// Disable timestamp logging. useful when output is redirected to logging
	// system that already adds timestamps.
	DisableTimestamp bool

	// Enable logging the full timestamp when a TTY is attached instead of just
	// the time passed since beginning of execution.
	FullTimestamp bool

	// TimestampFormat to use for display when a full timestamp is printed
	TimestampFormat string

	// The fields are sorted by default for a consistent output. For applications
	// that log extremely frequently and don't use the JSON formatter this may not
	// be desired.
	DisableSorting bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	var keys []string = make([]string, 0, len(entry.Data))
	for k := range entry.Data {
		keys = append(keys, k)
	}

	if !f.DisableSorting {
		sort.Strings(keys)
	}

	b := &bytes.Buffer{}

	prefixFieldClashes(entry.Data)

	isColorTerminal := isTerminal && (runtime.GOOS != "windows")
	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors

	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = DefaultTimestampFormat
	}
	if isColored {
		f.printColored(b, entry, keys, timestampFormat)
	} else {
		if !f.DisableTimestamp {
			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
		}
		f.appendKeyValue(b, "level", entry.Level.String())
		if entry.Message != "" {
			f.appendKeyValue(b, "msg", entry.Message)
		}
		for _, key := range keys {
			f.appendKeyValue(b, key, entry.Data[key])
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
	var levelColor int
	switch entry.Level {
	case DebugLevel:
		levelColor = gray
	case WarnLevel:
		levelColor = yellow
	case ErrorLevel, FatalLevel, PanicLevel:
		levelColor = red
	default:
		levelColor = blue
	}

	levelText := strings.ToUpper(entry.Level.String())[0:4]

	if !f.FullTimestamp {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
	} else {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
	}
	for _, k := range keys {
		v := entry.Data[k]
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
	}
}

func needsQuoting(text string) bool {
	for _, ch := range text {
		if !((ch >= 'a' && ch <= 'z') ||
			(ch >= 'A' && ch <= 'Z') ||
			(ch >= '0' && ch <= '9') ||
			ch == '-' || ch == '.') {
			return false
		}
	}
	return true
}

func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {

	b.WriteString(key)
	b.WriteByte('=')

	switch value := value.(type) {
	case string:
		if needsQuoting(value) {
			b.WriteString(value)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	case error:
		errmsg := value.Error()
		if needsQuoting(errmsg) {
			b.WriteString(errmsg)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	default:
		fmt.Fprint(b, value)
	}

	b.WriteByte(' ')
}
<commit_msg>match name to semantic in `needsQuoting`<commit_after>package logrus

import (
	"bytes"
	"fmt"
	"runtime"
	"sort"
	"strings"
	"time"
)

const (
	nocolor = 0
	red     = 31
	green   = 32
	yellow  = 33
	blue    = 34
	gray    = 37
)

var (
	baseTimestamp time.Time
	isTerminal    bool
)

func init() {
	baseTimestamp = time.Now()
	isTerminal = IsTerminal()
}

func miniTS() int {
	return int(time.Since(baseTimestamp) / time.Second)
}

type TextFormatter struct {
	// Set to true to bypass checking for a TTY before outputting colors.
	ForceColors bool

	// Force disabling colors.
	DisableColors bool

	// Disable timestamp logging. useful when output is redirected to logging
	// system that already adds timestamps.
	DisableTimestamp bool

	// Enable logging the full timestamp when a TTY is attached instead of just
	// the time passed since beginning of execution.
	FullTimestamp bool

	// TimestampFormat to use for display when a full timestamp is printed
	TimestampFormat string

	// The fields are sorted by default for a consistent output. For applications
	// that log extremely frequently and don't use the JSON formatter this may not
	// be desired.
	DisableSorting bool
}

func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
	var keys []string = make([]string, 0, len(entry.Data))
	for k := range entry.Data {
		keys = append(keys, k)
	}

	if !f.DisableSorting {
		sort.Strings(keys)
	}

	b := &bytes.Buffer{}

	prefixFieldClashes(entry.Data)

	isColorTerminal := isTerminal && (runtime.GOOS != "windows")
	isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors

	timestampFormat := f.TimestampFormat
	if timestampFormat == "" {
		timestampFormat = DefaultTimestampFormat
	}
	if isColored {
		f.printColored(b, entry, keys, timestampFormat)
	} else {
		if !f.DisableTimestamp {
			f.appendKeyValue(b, "time", entry.Time.Format(timestampFormat))
		}
		f.appendKeyValue(b, "level", entry.Level.String())
		if entry.Message != "" {
			f.appendKeyValue(b, "msg", entry.Message)
		}
		for _, key := range keys {
			f.appendKeyValue(b, key, entry.Data[key])
		}
	}

	b.WriteByte('\n')
	return b.Bytes(), nil
}

func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, timestampFormat string) {
	var levelColor int
	switch entry.Level {
	case DebugLevel:
		levelColor = gray
	case WarnLevel:
		levelColor = yellow
	case ErrorLevel, FatalLevel, PanicLevel:
		levelColor = red
	default:
		levelColor = blue
	}

	levelText := strings.ToUpper(entry.Level.String())[0:4]

	if !f.FullTimestamp {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
	} else {
		fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
	}
	for _, k := range keys {
		v := entry.Data[k]
		fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
	}
}

func needsQuoting(text string) bool {
	for _, ch := range text {
		if !((ch >= 'a' && ch <= 'z') ||
			(ch >= 'A' && ch <= 'Z') ||
			(ch >= '0' && ch <= '9') ||
			ch == '-' || ch == '.') {
			return true
		}
	}
	return false
}

func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {

	b.WriteString(key)
	b.WriteByte('=')

	switch value := value.(type) {
	case string:
		if !needsQuoting(value) {
			b.WriteString(value)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	case error:
		errmsg := value.Error()
		if !needsQuoting(errmsg) {
			b.WriteString(errmsg)
		} else {
			fmt.Fprintf(b, "%q", value)
		}
	default:
		fmt.Fprint(b, value)
	}

	b.WriteByte(' ')
}
<|endoftext|>