<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	flag "github.com/heroku/busl/Godeps/_workspace/src/github.com/ogier/pflag"
)

const usage = "Usage: busltee <url> [-k|--insecure] [--connect-timeout N] -- <command>"

type config struct {
	insecure  bool
	timeout   float64
	retry     int
	logPrefix string
	logFile   string
}

func main() {
	conf := &config{}

	// Connection related flags
	flag.BoolVarP(&conf.insecure, "insecure", "k", false, "allows insecure SSL connections")
	flag.IntVar(&conf.retry, "retry", 5, "max retries for connect timeout errors")
	flag.Float64Var(&conf.timeout, "connect-timeout", 1, "max number of seconds to connect to busl URL")

	// Logging related flags
	flag.StringVar(&conf.logPrefix, "log-prefix", "", "log prefix")
	flag.StringVar(&conf.logFile, "log-file", "", "log file")

	if flag.Parse(); len(flag.Args()) < 2 {
		fmt.Println(usage)
		os.Exit(1)
	}

	// Setup logger
	out := getLogOutput(conf.logFile)
	log.SetPrefix(conf.logPrefix + " ")
	log.SetOutput(out)
	log.SetFlags(0)
	if f, ok := out.(io.Closer); ok {
		defer f.Close()
	}

	// Run command, piping output to stdout / stderr and to the URL.
	url := flag.Arg(0)
	args := flag.Args()[1:]

	if err := busltee(conf, url, args); err != nil {
		log.Printf("busltee.main.error count#busltee.main.error=1 error=%v", err.Error())
		os.Exit(exitStatus(err))
	}
}

func monitor(subject string, ts time.Time) {
	log.Printf("%s.time time=%f", subject, time.Now().Sub(ts).Seconds())
}

func busltee(conf *config, url string, args []string) error {
	defer monitor("busltee.busltee", time.Now())

	reader, writer := io.Pipe()
	uploaded := make(chan struct{})

	go func() {
		if err := stream(conf.retry, url, reader, conf.insecure, conf.timeout); err != nil {
			log.Printf("busltee.stream.error count#busltee.stream.error=1 error=%v", err.Error())
			// Prevent writes from blocking.
			io.Copy(ioutil.Discard, reader)
		} else {
			log.Printf("busltee.stream.success count#busltee.stream.success=1")
		}
		close(uploaded)
	}()

	err := run(args, writer, writer)
	<-uploaded

	return err
}

func stream(retry int, url string, stdin io.Reader, insecure bool, timeout float64) (err error) {
	for retries := retry; retries > 0; retries-- {
		if err = streamNoRetry(url, stdin, insecure, timeout); !isTimeout(err) {
			return err
		}
		log.Printf("count#busltee.stream.retry")
	}
	return err
}

func streamNoRetry(url string, stdin io.Reader, insecure bool, timeout float64) error {
	defer monitor("busltee.stream", time.Now())

	if url == "" {
		log.Printf("count#busltee.stream.missingurl")
		return errors.New("Missing URL")
	}

	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   time.Duration(timeout) * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	}

	if insecure {
		tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	req, err := http.NewRequest("POST", url, ioutil.NopCloser(stdin))
	if err != nil {
		return err
	}

	res, err := tr.RoundTrip(req)
	if res != nil {
		defer res.Body.Close()
	}
	return err
}

func run(args []string, stdout, stderr io.WriteCloser) error {
	defer stdout.Close()
	defer stderr.Close()
	defer monitor("busltee.run", time.Now())

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdout = io.MultiWriter(stdout, os.Stdout)
	cmd.Stderr = io.MultiWriter(stderr, os.Stderr)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc)
	go func() {
		s := <-sigc
		if cmd.Process == nil {
			log.Printf("count#busltee.run.error error=cmd.Process is nil")
		} else {
			cmd.Process.Signal(s)
		}
	}()

	return cmd.Run()
}

func isTimeout(err error) bool {
	e, ok := err.(net.Error)
	return ok && e.Timeout()
}

func exitStatus(err error) int {
	if exit, ok := err.(*exec.ExitError); ok {
		if status, ok := exit.Sys().(syscall.WaitStatus); ok {
			return status.ExitStatus()
		}
	}

	return 0
}

func getLogOutput(logFile string) io.Writer {
	if logFile == "" {
		return ioutil.Discard
	}
	if file, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND, 0660); err != nil {
		return ioutil.Discard
	} else {
		return file
	}
}
<commit_msg>Document why we're using ioutil.NopCloser<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"time"

	flag "github.com/heroku/busl/Godeps/_workspace/src/github.com/ogier/pflag"
)

const usage = "Usage: busltee <url> [-k|--insecure] [--connect-timeout N] -- <command>"

type config struct {
	insecure  bool
	timeout   float64
	retry     int
	logPrefix string
	logFile   string
}

func main() {
	conf := &config{}

	// Connection related flags
	flag.BoolVarP(&conf.insecure, "insecure", "k", false, "allows insecure SSL connections")
	flag.IntVar(&conf.retry, "retry", 5, "max retries for connect timeout errors")
	flag.Float64Var(&conf.timeout, "connect-timeout", 1, "max number of seconds to connect to busl URL")

	// Logging related flags
	flag.StringVar(&conf.logPrefix, "log-prefix", "", "log prefix")
	flag.StringVar(&conf.logFile, "log-file", "", "log file")

	if flag.Parse(); len(flag.Args()) < 2 {
		fmt.Println(usage)
		os.Exit(1)
	}

	// Setup logger
	out := getLogOutput(conf.logFile)
	log.SetPrefix(conf.logPrefix + " ")
	log.SetOutput(out)
	log.SetFlags(0)
	if f, ok := out.(io.Closer); ok {
		defer f.Close()
	}

	// Run command, piping output to stdout / stderr and to the URL.
	url := flag.Arg(0)
	args := flag.Args()[1:]

	if err := busltee(conf, url, args); err != nil {
		log.Printf("busltee.main.error count#busltee.main.error=1 error=%v", err.Error())
		os.Exit(exitStatus(err))
	}
}

func monitor(subject string, ts time.Time) {
	log.Printf("%s.time time=%f", subject, time.Now().Sub(ts).Seconds())
}

func busltee(conf *config, url string, args []string) error {
	defer monitor("busltee.busltee", time.Now())

	reader, writer := io.Pipe()
	uploaded := make(chan struct{})

	go func() {
		if err := stream(conf.retry, url, reader, conf.insecure, conf.timeout); err != nil {
			log.Printf("busltee.stream.error count#busltee.stream.error=1 error=%v", err.Error())
			// Prevent writes from blocking.
			io.Copy(ioutil.Discard, reader)
		} else {
			log.Printf("busltee.stream.success count#busltee.stream.success=1")
		}
		close(uploaded)
	}()

	err := run(args, writer, writer)
	<-uploaded

	return err
}

func stream(retry int, url string, stdin io.Reader, insecure bool, timeout float64) (err error) {
	for retries := retry; retries > 0; retries-- {
		if err = streamNoRetry(url, stdin, insecure, timeout); !isTimeout(err) {
			return err
		}
		log.Printf("count#busltee.stream.retry")
	}
	return err
}

func streamNoRetry(url string, stdin io.Reader, insecure bool, timeout float64) error {
	defer monitor("busltee.stream", time.Now())

	if url == "" {
		log.Printf("count#busltee.stream.missingurl")
		return errors.New("Missing URL")
	}

	tr := &http.Transport{
		Dial: (&net.Dialer{
			Timeout:   time.Duration(timeout) * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
	}

	if insecure {
		tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	}

	// Prevent net/http from closing the reader on failure -- otherwise
	// we'll get broken pipe errors.
	req, err := http.NewRequest("POST", url, ioutil.NopCloser(stdin))
	if err != nil {
		return err
	}

	res, err := tr.RoundTrip(req)
	if res != nil {
		defer res.Body.Close()
	}
	return err
}

func run(args []string, stdout, stderr io.WriteCloser) error {
	defer stdout.Close()
	defer stderr.Close()
	defer monitor("busltee.run", time.Now())

	cmd := exec.Command(args[0], args[1:]...)
	cmd.Stdout = io.MultiWriter(stdout, os.Stdout)
	cmd.Stderr = io.MultiWriter(stderr, os.Stderr)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc)
	go func() {
		s := <-sigc
		if cmd.Process == nil {
			log.Printf("count#busltee.run.error error=cmd.Process is nil")
		} else {
			cmd.Process.Signal(s)
		}
	}()

	return cmd.Run()
}

func isTimeout(err error) bool {
	e, ok := err.(net.Error)
	return ok && e.Timeout()
}

func exitStatus(err error) int {
	if exit, ok := err.(*exec.ExitError); ok {
		if status, ok := exit.Sys().(syscall.WaitStatus); ok {
			return status.ExitStatus()
		}
	}

	return 0
}

func getLogOutput(logFile string) io.Writer {
	if logFile == "" {
		return ioutil.Discard
	}
	if file, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND, 0660); err != nil {
		return ioutil.Discard
	} else {
		return file
	}
}
<|endoftext|>


<commit_before>// Copyright 2021 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
//
//      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 deploy

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net"
	"os/exec"
	"path/filepath"
	"time"

	dtypes "github.com/docker/docker/api/types"
	dclient "github.com/docker/docker/client"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"gopkg.in/yaml.v3"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"sigs.k8s.io/kind/pkg/cluster"
)

type ClusterSpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}

type IngressSpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}
type CNISpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}

type KindSpec struct {
	Name             string        `yaml:"name"`
	Recycle          bool          `yaml:"recycle"`
	Version          string        `yaml:"version"`
	Image            string        `yaml:"image"`
	Retain           bool          `yaml:"retain"`
	Wait             time.Duration `yaml:"wait"`
	Kubecfg          string        `yaml:"kubecfg"`
	DeployWithClient bool          `yaml:"deployWithClient"`
	Load bool `yaml:"load"`
	execer           func(string, ...string) error
}

//go:generate mockgen -source=specs.go -destination=mocks/mock_provider.go -package=mocks provider
type provider interface {
	List() ([]string, error)
	Create(name string, options ...cluster.CreateOption) error
}

var (
	// newProvider is the kind provider that is replacable for testing.
	newProvider = defaultProvider

	execLookPath = exec.LookPath
)

func defaultProvider() provider {
	return cluster.NewProvider(cluster.ProviderWithLogger(&logAdapter{log.StandardLogger()}))
}

func (k *KindSpec) Deploy(ctx context.Context) error {
	provider := newProvider()
	if k.Recycle {
		clusters, err := provider.List()
		if err != nil {
			return err
		}
		for _, v := range clusters {
			if k.Name == v {
				log.Infof("Recycling existing cluster: %s", v)
				return nil
			}
		}
	}
	if k.DeployWithClient {
		if err := provider.Create(
			k.Name,
			cluster.CreateWithNodeImage(k.Image),
			cluster.CreateWithRetain(k.Retain),
			cluster.CreateWithWaitForReady(k.Wait),
			cluster.CreateWithKubeconfigPath(k.Kubecfg),
			cluster.CreateWithDisplayUsage(true),
			cluster.CreateWithDisplaySalutation(true),
		); err != nil {
			return errors.Wrap(err, "failed to create cluster using kind client")
		}
		log.Infof("Deployed kind cluster using kind client: %s", k.Name)
		return nil
	}
	if k.execer == nil {
		k.execer = execCmd
	}
	if _, err := execLookPath("kind"); err != nil {
		return errors.Wrap(err, "install kind cli to deploy, or set the deployWithClient field")
	}
	args := []string{"create", "cluster"}
	if k.Name != "" {
		args = append(args, "--name", k.Name)
	}
	if k.Image != "" {
		args = append(args, "--image", k.Image)
	}
	if k.Retain {
		args = append(args, "--retain")
	}
	if k.Wait != 0 {
		args = append(args, "--wait", k.Wait.String())
	}
	if k.Kubecfg != "" {
		args = append(args, "--kubeconfig", k.Kubecfg)
	}
	if err := k.execer("kind", args...); err != nil {
		return errors.Wrap(err, "failed to create cluster using cli")
	}
	log.Infof("Deployed kind cluster: %s", k.Name)
	if !k.Load {
		return nil
	}
	loadArgs := []string{"load", "docker-image", "quay.io/metallb/controller:main", "quay.io/metallb/speaker:main", "hfam/meshnet", "networkop/meshnet", "networkop/init-wait"}
	if k.Name != "" {
		loadArgs = append(loadArgs, "--name", k.Name)
	}
	if err := k.execer("kind", loadArgs...); err != nil {
		return errors.Wrap(err, "failed to load docker images in cluster using cli")
	}
	return nil
}

type MetalLBSpec struct {
	Version     string `yaml:"version"`
	IPCount     int    `yaml:"ip_count"`
	ManifestDir string `yaml:"manifests"`
	kClient     kubernetes.Interface
	execer      func(string, ...string) error
	dClient     dclient.NetworkAPIClient
}

func (m *MetalLBSpec) SetKClient(c kubernetes.Interface) {
	m.kClient = c
}

func inc(ip net.IP, cnt int) {
	for cnt > 0 {
		for j := len(ip) - 1; j >= 0; j-- {
			ip[j]++
			if ip[j] > 0 {
				break
			}
		}
		cnt--
	}
}

type pool struct {
	Name      string   `yaml:"name"`
	Protocol  string   `yaml:"protocol"`
	Addresses []string `yaml:"addresses"`
}

type metalLBConfig struct {
	AddressPools []pool `yaml:"address-pools"`
}

func execCmd(cmd string, args ...string) error {
	c := exec.Command(cmd, args...)
	c.Stderr = log.StandardLogger().Out
	c.Stdout = log.StandardLogger().Out
	log.Info(c.String())
	if err := c.Run(); err != nil {
		return fmt.Errorf("%q failed: %v", c.String(), err)
	}
	return nil
}
func makeConfig(n *net.IPNet, count int) metalLBConfig {
	start := make(net.IP, len(n.IP))
	copy(start, n.IP)
	inc(start, 50)
	end := make(net.IP, len(start))
	copy(end, start)
	inc(end, count)
	return metalLBConfig{
		AddressPools: []pool{{
			Name:      "default",
			Protocol:  "layer2",
			Addresses: []string{fmt.Sprintf("%s - %s", start, end)},
		}},
	}
}
func (m *MetalLBSpec) Deploy(ctx context.Context) error {
	if m.execer == nil {
		m.execer = execCmd
	}
	if m.dClient == nil {
		var err error
		m.dClient, err = dclient.NewClientWithOpts(dclient.FromEnv)
		if err != nil {
			return err
		}
	}
	mPath := filepath.Join(deploymentBasePath, m.ManifestDir)
	log.Infof("Deploying metallb from: %s", mPath)
	log.Infof("Creating metallb namespace")
	if err := m.execer("kubectl", "apply", "-f", filepath.Join(mPath, "namespace.yaml")); err != nil {
		return err
	}
	_, err := m.kClient.CoreV1().Secrets("metallb-system").Get(ctx, "memberlist", metav1.GetOptions{})
	if err != nil {
		log.Infof("Creating metallb secret")
		d := make([]byte, 16)
		rand.Read(d)
		s := &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name: "memberlist",
			},
			StringData: map[string]string{
				"secretkey": base64.StdEncoding.EncodeToString(d),
			},
		}
		_, err := m.kClient.CoreV1().Secrets("metallb-system").Create(ctx, s, metav1.CreateOptions{})
		if err != nil {
			return err
		}
	}
	log.Infof("Applying metallb pods")
	if err := m.execer("kubectl", "apply", "-f", filepath.Join(mPath, "metallb.yaml")); err != nil {
		return err
	}
	_, err = m.kClient.CoreV1().ConfigMaps("metallb-system").Get(ctx, "config", metav1.GetOptions{})
	if err != nil {
		log.Infof("Applying metallb ingress config")
		// Get Network information from docker.
		nr, err := m.dClient.NetworkList(ctx, dtypes.NetworkListOptions{})
		if err != nil {
			return err
		}
		var network dtypes.NetworkResource
		for _, v := range nr {
			if v.Name == "kind" {
				network = v
				break
			}
		}
		var n *net.IPNet
		for _, ipRange := range network.IPAM.Config {
			_, ipNet, err := net.ParseCIDR(ipRange.Subnet)
			if err != nil {
				return err
			}
			if ipNet.IP.To4() != nil {
				n = ipNet
				break
			}
		}
		if n == nil {
			return fmt.Errorf("failed to find kind ipv4 docker net")
		}
		config := makeConfig(n, m.IPCount)
		b, err := yaml.Marshal(config)
		if err != nil {
			return err
		}
		cm := &corev1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: "config",
			},
			Data: map[string]string{
				"config": string(b),
			},
		}
		_, err = m.kClient.CoreV1().ConfigMaps("metallb-system").Create(ctx, cm, metav1.CreateOptions{})
		if err != nil {
			return err
		}
	}
	return nil
}

func (m *MetalLBSpec) Healthy(ctx context.Context) error {
	log.Infof("Waiting on Metallb to be Healthy")
	w, err := m.kClient.AppsV1().Deployments("metallb-system").Watch(ctx, metav1.ListOptions{})
	if err != nil {
		return err
	}
	ch := w.ResultChan()
	for {
		select {
		case <-ctx.Done():
			return fmt.Errorf("context canceled before healthy")
		case e, ok := <-ch:
			if !ok {
				return fmt.Errorf("watch channel closed before healthy")
			}
			d, ok := e.Object.(*appsv1.Deployment)
			if !ok {
				return fmt.Errorf("invalid object type: %T", d)
			}
			if d.Status.AvailableReplicas == 1 &&
				d.Status.ReadyReplicas == 1 &&
				d.Status.UnavailableReplicas == 0 &&
				d.Status.Replicas == 1 &&
				d.Status.UpdatedReplicas == 1 {
				log.Infof("Metallb Healthy")
				return nil
			}
		}
	}
}

type MeshnetSpec struct {
	Image       string `yaml:"image"`
	ManifestDir string `yaml:"manifests"`
	kClient     kubernetes.Interface
	execer      func(string, ...string) error
}

func (m *MeshnetSpec) SetKClient(c kubernetes.Interface) {
	m.kClient = c
}

func (m *MeshnetSpec) Deploy(ctx context.Context) error {
	if m.execer == nil {
		m.execer = execCmd
	}
	mPath := filepath.Join(deploymentBasePath, m.ManifestDir)
	log.Infof("Deploying Meshnet from: %s", mPath)
	if err := m.execer("kubectl", "apply", "-k", mPath); err != nil {
		return err
	}
	log.Infof("Meshnet Deployed")
	return nil
}

func (m *MeshnetSpec) Healthy(ctx context.Context) error {
	log.Infof("Waiting on Meshnet to be Healthy")
	w, err := m.kClient.AppsV1().DaemonSets("meshnet").Watch(ctx, metav1.ListOptions{
		FieldSelector: fields.SelectorFromSet(fields.Set{metav1.ObjectNameField: "meshnet"}).String(),
	})
	if err != nil {
		return err
	}
	for {
		select {
		case <-ctx.Done():
			return fmt.Errorf("context canceled before healthy")
		case e, ok := <-w.ResultChan():
			if !ok {
				return fmt.Errorf("watch channel closed before healthy")
			}
			d, ok := e.Object.(*appsv1.DaemonSet)
			if !ok {
				return fmt.Errorf("invalid object type: %T", d)
			}
			if d.Status.NumberReady == d.Status.DesiredNumberScheduled &&
				d.Status.NumberUnavailable == 0 {
				log.Infof("Meshnet Healthy")
				return nil
			}
		}
	}
}
<commit_msg>load images one at a time<commit_after>// Copyright 2021 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
//
//      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 deploy

import (
	"context"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"net"
	"os/exec"
	"path/filepath"
	"time"

	dtypes "github.com/docker/docker/api/types"
	dclient "github.com/docker/docker/client"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"
	"gopkg.in/yaml.v3"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"sigs.k8s.io/kind/pkg/cluster"
)

type ClusterSpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}

type IngressSpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}
type CNISpec struct {
	Kind string    `yaml:"kind"`
	Spec yaml.Node `yaml:"spec"`
}

type KindSpec struct {
	Name             string        `yaml:"name"`
	Recycle          bool          `yaml:"recycle"`
	Version          string        `yaml:"version"`
	Image            string        `yaml:"image"`
	Retain           bool          `yaml:"retain"`
	Wait             time.Duration `yaml:"wait"`
	Kubecfg          string        `yaml:"kubecfg"`
	DeployWithClient bool          `yaml:"deployWithClient"`
	Load bool `yaml:"load"`
	execer           func(string, ...string) error
}

//go:generate mockgen -source=specs.go -destination=mocks/mock_provider.go -package=mocks provider
type provider interface {
	List() ([]string, error)
	Create(name string, options ...cluster.CreateOption) error
}

var (
	// newProvider is the kind provider that is replacable for testing.
	newProvider = defaultProvider

	execLookPath = exec.LookPath
)

func defaultProvider() provider {
	return cluster.NewProvider(cluster.ProviderWithLogger(&logAdapter{log.StandardLogger()}))
}

func (k *KindSpec) Deploy(ctx context.Context) error {
	provider := newProvider()
	if k.Recycle {
		clusters, err := provider.List()
		if err != nil {
			return err
		}
		for _, v := range clusters {
			if k.Name == v {
				log.Infof("Recycling existing cluster: %s", v)
				return nil
			}
		}
	}
	if k.DeployWithClient {
		if err := provider.Create(
			k.Name,
			cluster.CreateWithNodeImage(k.Image),
			cluster.CreateWithRetain(k.Retain),
			cluster.CreateWithWaitForReady(k.Wait),
			cluster.CreateWithKubeconfigPath(k.Kubecfg),
			cluster.CreateWithDisplayUsage(true),
			cluster.CreateWithDisplaySalutation(true),
		); err != nil {
			return errors.Wrap(err, "failed to create cluster using kind client")
		}
		log.Infof("Deployed kind cluster using kind client: %s", k.Name)
		return nil
	}
	if k.execer == nil {
		k.execer = execCmd
	}
	if _, err := execLookPath("kind"); err != nil {
		return errors.Wrap(err, "install kind cli to deploy, or set the deployWithClient field")
	}
	args := []string{"create", "cluster"}
	if k.Name != "" {
		args = append(args, "--name", k.Name)
	}
	if k.Image != "" {
		args = append(args, "--image", k.Image)
	}
	if k.Retain {
		args = append(args, "--retain")
	}
	if k.Wait != 0 {
		args = append(args, "--wait", k.Wait.String())
	}
	if k.Kubecfg != "" {
		args = append(args, "--kubeconfig", k.Kubecfg)
	}
	if err := k.execer("kind", args...); err != nil {
		return errors.Wrap(err, "failed to create cluster using cli")
	}
	log.Infof("Deployed kind cluster: %s", k.Name)
	if !k.Load {
		return nil
	}
	images := []string{"quay.io/metallb/controller:main", "quay.io/metallb/speaker:main", "hfam/meshnet", "networkop/meshnet", "networkop/init-wait"}
	for _, im := range images {
		loadArgs := []string{"load", "docker-image", im}
		if k.Name != "" {
			loadArgs = append(loadArgs, "--name", k.Name)
		}
		if err := k.execer("kind", loadArgs...); err != nil {
			return fmt.Errorf("failed to load docker image %q in cluster: %v", im, err)
		}
	}
	return nil
}

type MetalLBSpec struct {
	Version     string `yaml:"version"`
	IPCount     int    `yaml:"ip_count"`
	ManifestDir string `yaml:"manifests"`
	kClient     kubernetes.Interface
	execer      func(string, ...string) error
	dClient     dclient.NetworkAPIClient
}

func (m *MetalLBSpec) SetKClient(c kubernetes.Interface) {
	m.kClient = c
}

func inc(ip net.IP, cnt int) {
	for cnt > 0 {
		for j := len(ip) - 1; j >= 0; j-- {
			ip[j]++
			if ip[j] > 0 {
				break
			}
		}
		cnt--
	}
}

type pool struct {
	Name      string   `yaml:"name"`
	Protocol  string   `yaml:"protocol"`
	Addresses []string `yaml:"addresses"`
}

type metalLBConfig struct {
	AddressPools []pool `yaml:"address-pools"`
}

func execCmd(cmd string, args ...string) error {
	c := exec.Command(cmd, args...)
	c.Stderr = log.StandardLogger().Out
	c.Stdout = log.StandardLogger().Out
	log.Info(c.String())
	if err := c.Run(); err != nil {
		return fmt.Errorf("%q failed: %v", c.String(), err)
	}
	return nil
}
func makeConfig(n *net.IPNet, count int) metalLBConfig {
	start := make(net.IP, len(n.IP))
	copy(start, n.IP)
	inc(start, 50)
	end := make(net.IP, len(start))
	copy(end, start)
	inc(end, count)
	return metalLBConfig{
		AddressPools: []pool{{
			Name:      "default",
			Protocol:  "layer2",
			Addresses: []string{fmt.Sprintf("%s - %s", start, end)},
		}},
	}
}
func (m *MetalLBSpec) Deploy(ctx context.Context) error {
	if m.execer == nil {
		m.execer = execCmd
	}
	if m.dClient == nil {
		var err error
		m.dClient, err = dclient.NewClientWithOpts(dclient.FromEnv)
		if err != nil {
			return err
		}
	}
	mPath := filepath.Join(deploymentBasePath, m.ManifestDir)
	log.Infof("Deploying metallb from: %s", mPath)
	log.Infof("Creating metallb namespace")
	if err := m.execer("kubectl", "apply", "-f", filepath.Join(mPath, "namespace.yaml")); err != nil {
		return err
	}
	_, err := m.kClient.CoreV1().Secrets("metallb-system").Get(ctx, "memberlist", metav1.GetOptions{})
	if err != nil {
		log.Infof("Creating metallb secret")
		d := make([]byte, 16)
		rand.Read(d)
		s := &corev1.Secret{
			ObjectMeta: metav1.ObjectMeta{
				Name: "memberlist",
			},
			StringData: map[string]string{
				"secretkey": base64.StdEncoding.EncodeToString(d),
			},
		}
		_, err := m.kClient.CoreV1().Secrets("metallb-system").Create(ctx, s, metav1.CreateOptions{})
		if err != nil {
			return err
		}
	}
	log.Infof("Applying metallb pods")
	if err := m.execer("kubectl", "apply", "-f", filepath.Join(mPath, "metallb.yaml")); err != nil {
		return err
	}
	_, err = m.kClient.CoreV1().ConfigMaps("metallb-system").Get(ctx, "config", metav1.GetOptions{})
	if err != nil {
		log.Infof("Applying metallb ingress config")
		// Get Network information from docker.
		nr, err := m.dClient.NetworkList(ctx, dtypes.NetworkListOptions{})
		if err != nil {
			return err
		}
		var network dtypes.NetworkResource
		for _, v := range nr {
			if v.Name == "kind" {
				network = v
				break
			}
		}
		var n *net.IPNet
		for _, ipRange := range network.IPAM.Config {
			_, ipNet, err := net.ParseCIDR(ipRange.Subnet)
			if err != nil {
				return err
			}
			if ipNet.IP.To4() != nil {
				n = ipNet
				break
			}
		}
		if n == nil {
			return fmt.Errorf("failed to find kind ipv4 docker net")
		}
		config := makeConfig(n, m.IPCount)
		b, err := yaml.Marshal(config)
		if err != nil {
			return err
		}
		cm := &corev1.ConfigMap{
			ObjectMeta: metav1.ObjectMeta{
				Name: "config",
			},
			Data: map[string]string{
				"config": string(b),
			},
		}
		_, err = m.kClient.CoreV1().ConfigMaps("metallb-system").Create(ctx, cm, metav1.CreateOptions{})
		if err != nil {
			return err
		}
	}
	return nil
}

func (m *MetalLBSpec) Healthy(ctx context.Context) error {
	log.Infof("Waiting on Metallb to be Healthy")
	w, err := m.kClient.AppsV1().Deployments("metallb-system").Watch(ctx, metav1.ListOptions{})
	if err != nil {
		return err
	}
	ch := w.ResultChan()
	for {
		select {
		case <-ctx.Done():
			return fmt.Errorf("context canceled before healthy")
		case e, ok := <-ch:
			if !ok {
				return fmt.Errorf("watch channel closed before healthy")
			}
			d, ok := e.Object.(*appsv1.Deployment)
			if !ok {
				return fmt.Errorf("invalid object type: %T", d)
			}
			if d.Status.AvailableReplicas == 1 &&
				d.Status.ReadyReplicas == 1 &&
				d.Status.UnavailableReplicas == 0 &&
				d.Status.Replicas == 1 &&
				d.Status.UpdatedReplicas == 1 {
				log.Infof("Metallb Healthy")
				return nil
			}
		}
	}
}

type MeshnetSpec struct {
	Image       string `yaml:"image"`
	ManifestDir string `yaml:"manifests"`
	kClient     kubernetes.Interface
	execer      func(string, ...string) error
}

func (m *MeshnetSpec) SetKClient(c kubernetes.Interface) {
	m.kClient = c
}

func (m *MeshnetSpec) Deploy(ctx context.Context) error {
	if m.execer == nil {
		m.execer = execCmd
	}
	mPath := filepath.Join(deploymentBasePath, m.ManifestDir)
	log.Infof("Deploying Meshnet from: %s", mPath)
	if err := m.execer("kubectl", "apply", "-k", mPath); err != nil {
		return err
	}
	log.Infof("Meshnet Deployed")
	return nil
}

func (m *MeshnetSpec) Healthy(ctx context.Context) error {
	log.Infof("Waiting on Meshnet to be Healthy")
	w, err := m.kClient.AppsV1().DaemonSets("meshnet").Watch(ctx, metav1.ListOptions{
		FieldSelector: fields.SelectorFromSet(fields.Set{metav1.ObjectNameField: "meshnet"}).String(),
	})
	if err != nil {
		return err
	}
	for {
		select {
		case <-ctx.Done():
			return fmt.Errorf("context canceled before healthy")
		case e, ok := <-w.ResultChan():
			if !ok {
				return fmt.Errorf("watch channel closed before healthy")
			}
			d, ok := e.Object.(*appsv1.DaemonSet)
			if !ok {
				return fmt.Errorf("invalid object type: %T", d)
			}
			if d.Status.NumberReady == d.Status.DesiredNumberScheduled &&
				d.Status.NumberUnavailable == 0 {
				log.Infof("Meshnet Healthy")
				return nil
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"runtime/debug"
	"runtime/pprof"
	"runtime/trace"
	"strings"
	"time"

	"github.com/evanw/esbuild/internal/logging"
	"github.com/evanw/esbuild/pkg/cli"
)

const helpText = `
Usage:
  esbuild [options] [entry points]

Options:
  --bundle              Bundle all dependencies into the output files
  --outfile=...         The output file (for one entry point)
  --outdir=...          The output directory (for multiple entry points)
  --sourcemap           Emit a source map
  --target=...          Environment target (e.g. es2017, chrome80)
  --platform=...        Platform target (browser or node, default browser)
  --external:M          Exclude module M from the bundle
  --format=...          Output format (iife, cjs, esm)
  --splitting           Enable code splitting (currently only for esm)
  --color=...           Force use of color terminal escapes (true or false)
  --global-name=...     The name of the global for the IIFE format

  --minify              Sets all --minify-* flags
  --minify-whitespace   Remove whitespace
  --minify-identifiers  Shorten identifiers
  --minify-syntax       Use equivalent but shorter syntax

  --define:K=V          Substitute K with V while parsing
  --jsx-factory=...     What to use instead of React.createElement
  --jsx-fragment=...    What to use instead of React.Fragment
  --loader:X=L          Use loader L to load file extension X, where L is
                        one of: js, jsx, ts, tsx, json, text, base64, file,
                        dataurl, binary

Advanced options:
  --version                 Print the current version and exit (` + esbuildVersion + `)
  --sourcemap=inline        Emit the source map with an inline data URL
  --sourcemap=external      Do not link to the source map with a comment
  --sourcefile=...          Set the source file for the source map (for stdin)
  --error-limit=...         Maximum error count or 0 to disable (default 10)
  --log-level=...           Disable logging (info, warning, error, silent)
  --resolve-extensions=...  A comma-separated list of implicit extensions
  --metafile=...            Write metadata about the build to a JSON file
  --strict                  Transforms handle edge cases but have more overhead
  --pure:N                  Mark the name N as a pure function for tree shaking
  --tsconfig=...            Use this tsconfig.json file instead of other ones
  --out-extension:.js=.mjs  Use a custom output extension instead of ".js"

Examples:
  # Produces dist/entry_point.js and dist/entry_point.js.map
  esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap

  # Allow JSX syntax in .js files
  esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx

  # Substitute the identifier RELEASE for the literal true
  esbuild example.js --outfile=out.js --define:RELEASE=true

  # Provide input via stdin, get output via stdout
  esbuild --minify --loader=ts < input.ts > output.js
`

func main() {
	osArgs := os.Args[1:]
	traceFile := ""
	cpuprofileFile := ""
	isRunningService := false

	// Do an initial scan over the argument list
	argsEnd := 0
	for _, arg := range osArgs {
		switch {
		// Show help if a common help flag is provided
		case arg == "-h", arg == "-help", arg == "--help", arg == "/?":
			fmt.Fprintf(os.Stderr, "%s\n", helpText)
			os.Exit(0)

		// Special-case the version flag here
		case arg == "--version":
			fmt.Fprintf(os.Stderr, "%s\n", esbuildVersion)
			os.Exit(0)

		case strings.HasPrefix(arg, "--trace="):
			traceFile = arg[len("--trace="):]

		case strings.HasPrefix(arg, "--cpuprofile="):
			cpuprofileFile = arg[len("--cpuprofile="):]

		// This flag turns the process into a long-running service that uses
		// message passing with the host process over stdin/stdout
		case arg == "--service":
			isRunningService = true

		default:
			// Strip any arguments that were handled above
			osArgs[argsEnd] = arg
			argsEnd++
		}
	}
	osArgs = osArgs[:argsEnd]

	// Run in service mode if requested
	if isRunningService {
		runService()
		return
	}

	// Print help text when there are no arguments
	if len(osArgs) == 0 && logging.GetTerminalInfo(os.Stdin).IsTTY {
		fmt.Fprintf(os.Stderr, "%s\n", helpText)
		os.Exit(0)
	}

	// Capture the defer statements below so the "done" message comes last
	exitCode := 1
	func() {
		// To view a CPU trace, use "go tool trace [file]". Note that the trace
		// viewer doesn't work under Windows Subsystem for Linux for some reason.
		if traceFile != "" {
			f, err := os.Create(traceFile)
			if err != nil {
				logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
					"Failed to create trace file: %s", err.Error()))
				return
			}
			defer f.Close()
			trace.Start(f)
			defer trace.Stop()
		}

		// To view a CPU profile, drop the file into https://speedscope.app.
		// Note: Running the CPU profiler doesn't work under Windows subsystem for
		// Linux. The profiler has to be built for native Windows and run using the
		// command prompt instead.
		if cpuprofileFile != "" {
			f, err := os.Create(cpuprofileFile)
			if err != nil {
				logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
					"Failed to create cpuprofile file: %s", err.Error()))
				return
			}
			defer f.Close()
			pprof.StartCPUProfile(f)
			defer pprof.StopCPUProfile()
		}

		if cpuprofileFile != "" {
			// The CPU profiler in Go only runs at 100 Hz, which is far too slow to
			// return useful information for esbuild, since it's so fast. Let's keep
			// running for 30 seconds straight, which should give us 3,000 samples.
			seconds := 30.0
			start := time.Now()
			for time.Since(start).Seconds() < seconds {
				exitCode = cli.Run(osArgs)
			}
		} else {
			// Disable the GC since we're just going to allocate a bunch of memory
			// and then exit anyway. This speedup is not insignificant. Make sure to
			// only do this here once we know that we're not going to be a long-lived
			// process though.
			debug.SetGCPercent(-1)

			exitCode = cli.Run(osArgs)
		}
	}()

	os.Exit(exitCode)
}
<commit_msg>add the "--heap" flag to capture a heap profile<commit_after>package main

import (
	"fmt"
	"os"
	"runtime/debug"
	"runtime/pprof"
	"runtime/trace"
	"strings"
	"time"

	"github.com/evanw/esbuild/internal/logging"
	"github.com/evanw/esbuild/pkg/cli"
)

const helpText = `
Usage:
  esbuild [options] [entry points]

Options:
  --bundle              Bundle all dependencies into the output files
  --outfile=...         The output file (for one entry point)
  --outdir=...          The output directory (for multiple entry points)
  --sourcemap           Emit a source map
  --target=...          Environment target (e.g. es2017, chrome80)
  --platform=...        Platform target (browser or node, default browser)
  --external:M          Exclude module M from the bundle
  --format=...          Output format (iife, cjs, esm)
  --splitting           Enable code splitting (currently only for esm)
  --color=...           Force use of color terminal escapes (true or false)
  --global-name=...     The name of the global for the IIFE format

  --minify              Sets all --minify-* flags
  --minify-whitespace   Remove whitespace
  --minify-identifiers  Shorten identifiers
  --minify-syntax       Use equivalent but shorter syntax

  --define:K=V          Substitute K with V while parsing
  --jsx-factory=...     What to use instead of React.createElement
  --jsx-fragment=...    What to use instead of React.Fragment
  --loader:X=L          Use loader L to load file extension X, where L is
                        one of: js, jsx, ts, tsx, json, text, base64, file,
                        dataurl, binary

Advanced options:
  --version                 Print the current version and exit (` + esbuildVersion + `)
  --sourcemap=inline        Emit the source map with an inline data URL
  --sourcemap=external      Do not link to the source map with a comment
  --sourcefile=...          Set the source file for the source map (for stdin)
  --error-limit=...         Maximum error count or 0 to disable (default 10)
  --log-level=...           Disable logging (info, warning, error, silent)
  --resolve-extensions=...  A comma-separated list of implicit extensions
  --metafile=...            Write metadata about the build to a JSON file
  --strict                  Transforms handle edge cases but have more overhead
  --pure:N                  Mark the name N as a pure function for tree shaking
  --tsconfig=...            Use this tsconfig.json file instead of other ones
  --out-extension:.js=.mjs  Use a custom output extension instead of ".js"

Examples:
  # Produces dist/entry_point.js and dist/entry_point.js.map
  esbuild --bundle entry_point.js --outdir=dist --minify --sourcemap

  # Allow JSX syntax in .js files
  esbuild --bundle entry_point.js --outfile=out.js --loader:.js=jsx

  # Substitute the identifier RELEASE for the literal true
  esbuild example.js --outfile=out.js --define:RELEASE=true

  # Provide input via stdin, get output via stdout
  esbuild --minify --loader=ts < input.ts > output.js
`

func main() {
	osArgs := os.Args[1:]
	heapFile := ""
	traceFile := ""
	cpuprofileFile := ""
	isRunningService := false

	// Do an initial scan over the argument list
	argsEnd := 0
	for _, arg := range osArgs {
		switch {
		// Show help if a common help flag is provided
		case arg == "-h", arg == "-help", arg == "--help", arg == "/?":
			fmt.Fprintf(os.Stderr, "%s\n", helpText)
			os.Exit(0)

		// Special-case the version flag here
		case arg == "--version":
			fmt.Fprintf(os.Stderr, "%s\n", esbuildVersion)
			os.Exit(0)

		case strings.HasPrefix(arg, "--heap="):
			heapFile = arg[len("--heap="):]

		case strings.HasPrefix(arg, "--trace="):
			traceFile = arg[len("--trace="):]

		case strings.HasPrefix(arg, "--cpuprofile="):
			cpuprofileFile = arg[len("--cpuprofile="):]

		// This flag turns the process into a long-running service that uses
		// message passing with the host process over stdin/stdout
		case arg == "--service":
			isRunningService = true

		default:
			// Strip any arguments that were handled above
			osArgs[argsEnd] = arg
			argsEnd++
		}
	}
	osArgs = osArgs[:argsEnd]

	// Run in service mode if requested
	if isRunningService {
		runService()
		return
	}

	// Print help text when there are no arguments
	if len(osArgs) == 0 && logging.GetTerminalInfo(os.Stdin).IsTTY {
		fmt.Fprintf(os.Stderr, "%s\n", helpText)
		os.Exit(0)
	}

	// Capture the defer statements below so the "done" message comes last
	exitCode := 1
	func() {
		// To view a CPU trace, use "go tool trace [file]". Note that the trace
		// viewer doesn't work under Windows Subsystem for Linux for some reason.
		if traceFile != "" {
			f, err := os.Create(traceFile)
			if err != nil {
				logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
					"Failed to create trace file: %s", err.Error()))
				return
			}
			defer f.Close()
			trace.Start(f)
			defer trace.Stop()
		}

		// To view a heap trace, use "go tool pprof [file]" and type "top". You can
		// also drop it into https://speedscope.app and use the "left heavy" or
		// "sandwich" view modes.
		if heapFile != "" {
			f, err := os.Create(heapFile)
			if err != nil {
				logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
					"Failed to create heap file: %s", err.Error()))
				return
			}
			defer func() {
				if err := pprof.WriteHeapProfile(f); err != nil {
					logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
						"Failed to write heap profile: %s", err.Error()))
				}
				f.Close()
			}()
		}

		// To view a CPU profile, drop the file into https://speedscope.app.
		// Note: Running the CPU profiler doesn't work under Windows subsystem for
		// Linux. The profiler has to be built for native Windows and run using the
		// command prompt instead.
		if cpuprofileFile != "" {
			f, err := os.Create(cpuprofileFile)
			if err != nil {
				logging.PrintErrorToStderr(osArgs, fmt.Sprintf(
					"Failed to create cpuprofile file: %s", err.Error()))
				return
			}
			defer f.Close()
			pprof.StartCPUProfile(f)
			defer pprof.StopCPUProfile()
		}

		if cpuprofileFile != "" {
			// The CPU profiler in Go only runs at 100 Hz, which is far too slow to
			// return useful information for esbuild, since it's so fast. Let's keep
			// running for 30 seconds straight, which should give us 3,000 samples.
			seconds := 30.0
			start := time.Now()
			for time.Since(start).Seconds() < seconds {
				exitCode = cli.Run(osArgs)
			}
		} else {
			// Disable the GC since we're just going to allocate a bunch of memory
			// and then exit anyway. This speedup is not insignificant. Make sure to
			// only do this here once we know that we're not going to be a long-lived
			// process though.
			debug.SetGCPercent(-1)

			exitCode = cli.Run(osArgs)
		}
	}()

	os.Exit(exitCode)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/cloudflare/cloudflare-go"
	"github.com/codegangsta/cli"
)

func formatDNSRecord(record cloudflare.DNSRecord) []string {
	return []string{
		record.ID,
		record.Name,
		record.Type,
		record.Content,
		strconv.FormatInt(int64(record.TTL), 10),
		strconv.FormatBool(record.Proxiable),
		strconv.FormatBool(record.Proxied),
		strconv.FormatBool(record.Locked),
	}
}

func dnsCreate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
		return
	}
	zone := c.String("zone")
	name := c.String("name")
	rtype := c.String("type")
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	record := cloudflare.DNSRecord{
		Name:    name,
		Type:    strings.ToUpper(rtype),
		Content: content,
		TTL:     ttl,
		Proxied: proxy,
	}
	resp, err := api.CreateDNSRecord(zoneID, record)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error creating DNS record: ", err)
		return
	}

	output := [][]string{
		formatDNSRecord(resp.Result),
	}

	writeTable(output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked")
}

func dnsCreateOrUpdate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	name := c.String("name")
	rtype := strings.ToUpper(c.String("type"))
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err)
		return
	}

	// Look for an existing record
	rr := cloudflare.DNSRecord{
		Name: name + "." + zone,
	}
	records, err := api.DNSRecords(zoneID, rr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error fetching DNS records: ", err)
		return
	}

	var resp *cloudflare.DNSRecordResponse
	if len(records) > 0 {
		// Record exists - find the ID and update it.
		// This is imprecise without knowing the original content; if a label
		// has multiple RRs we'll just update the first one.
		for _, r := range records {
			if r.Type == rtype {
				rr.ID = r.ID
				rr.Type = r.Type
				rr.Content = content
				rr.TTL = ttl
				rr.Proxied = proxy
				err := api.UpdateDNSRecord(zoneID, r.ID, rr)
				if err != nil {
					fmt.Println("Error updating DNS record:", err)
				}
			}
		}
	} else {
		// Record doesn't exist - create it
		rr.Type = rtype
		rr.Content = content
		rr.TTL = ttl
		rr.Proxied = proxy
		// TODO: Print the response.
		resp, err = api.CreateDNSRecord(zoneID, rr)
		if err != nil {
			fmt.Println("Error creating DNS record:", err)
		}

	}

	output := [][]string{
		formatDNSRecord(resp.Result),
	}

	writeTable(output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked")
}

func dnsUpdate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "id"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	recordID := c.String("id")
	name := c.String("name")
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	record := cloudflare.DNSRecord{
		ID:      recordID,
		Name:    name,
		Content: content,
		TTL:     ttl,
		Proxied: proxy,
	}
	err = api.UpdateDNSRecord(zoneID, recordID, record)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err)
		return
	}
}

func dnsDelete(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "id"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	recordID := c.String("id")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	err = api.DeleteDNSRecord(zoneID, recordID)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error deleting DNS record: ", err)
		return
	}
}
<commit_msg>fix https://github.com/cloudflare/cloudflare-go/issues/249<commit_after>package main

import (
	"fmt"
	"os"
	"strconv"
	"strings"

	"github.com/cloudflare/cloudflare-go"
	"github.com/codegangsta/cli"
)

func formatDNSRecord(record cloudflare.DNSRecord) []string {
	return []string{
		record.ID,
		record.Name,
		record.Type,
		record.Content,
		strconv.FormatInt(int64(record.TTL), 10),
		strconv.FormatBool(record.Proxiable),
		strconv.FormatBool(record.Proxied),
		strconv.FormatBool(record.Locked),
	}
}

func dnsCreate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
		return
	}
	zone := c.String("zone")
	name := c.String("name")
	rtype := c.String("type")
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	record := cloudflare.DNSRecord{
		Name:    name,
		Type:    strings.ToUpper(rtype),
		Content: content,
		TTL:     ttl,
		Proxied: proxy,
	}
	resp, err := api.CreateDNSRecord(zoneID, record)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error creating DNS record: ", err)
		return
	}

	output := [][]string{
		formatDNSRecord(resp.Result),
	}

	writeTable(output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked")
}

func dnsCreateOrUpdate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "name", "type", "content"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	name := c.String("name")
	rtype := strings.ToUpper(c.String("type"))
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err)
		return
	}

	// Look for an existing record
	rr := cloudflare.DNSRecord{
		Name: name + "." + zone,
	}
	records, err := api.DNSRecords(zoneID, rr)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error fetching DNS records: ", err)
		return
	}

	var resp *cloudflare.DNSRecordResponse
	if len(records) > 0 {
		// Record exists - find the ID and update it.
		// This is imprecise without knowing the original content; if a label
		// has multiple RRs we'll just update the first one.
		for _, r := range records {
			if r.Type == rtype {
				rr.ID = r.ID
				rr.Type = r.Type
				rr.Content = content
				rr.TTL = ttl
				rr.Proxied = proxy
				err := api.UpdateDNSRecord(zoneID, r.ID, rr)
				if err != nil {
					fmt.Println("Error updating DNS record:", err)
					return
				}
			}
		}
	} else {
		// Record doesn't exist - create it
		rr.Type = rtype
		rr.Content = content
		rr.TTL = ttl
		rr.Proxied = proxy
		// TODO: Print the response.
		resp, err = api.CreateDNSRecord(zoneID, rr)
		if err != nil {
			fmt.Println("Error creating DNS record:", err)
			return
		}

	}

	output := [][]string{
		formatDNSRecord(resp.Result),
	}

	writeTable(output, "ID", "Name", "Type", "Content", "TTL", "Proxiable", "Proxy", "Locked")
}

func dnsUpdate(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "id"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	recordID := c.String("id")
	name := c.String("name")
	content := c.String("content")
	ttl := c.Int("ttl")
	proxy := c.Bool("proxy")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	record := cloudflare.DNSRecord{
		ID:      recordID,
		Name:    name,
		Content: content,
		TTL:     ttl,
		Proxied: proxy,
	}
	err = api.UpdateDNSRecord(zoneID, recordID, record)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error updating DNS record: ", err)
		return
	}
}

func dnsDelete(c *cli.Context) {
	if err := checkEnv(); err != nil {
		fmt.Println(err)
		return
	}
	if err := checkFlags(c, "zone", "id"); err != nil {
		fmt.Println(err)
		return
	}
	zone := c.String("zone")
	recordID := c.String("id")

	zoneID, err := api.ZoneIDByName(zone)
	if err != nil {
		fmt.Println(err)
		return
	}

	err = api.DeleteDNSRecord(zoneID, recordID)
	if err != nil {
		fmt.Fprintln(os.Stderr, "Error deleting DNS record: ", err)
		return
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"

	"github.com/Clever/gearcmd/gearcmd"
)

func main() {
	functionName := flag.String("name", "", "Name of the Gearman function")
	functionCmd := flag.String("cmd", "", "The command to run")
	gearmanHost := flag.String("host", "localhost", "The Gearman host")
	gearmanPort := flag.String("port", "4730", "The Gearman port")
	flag.Parse()
	if len(*functionName) == 0 {
		log.Printf("Error: name not defined")
		flag.PrintDefaults()
		os.Exit(2)
	}
	if len(*functionCmd) == 0 {
		log.Printf("Error: cmd not defined")
		flag.PrintDefaults()
		os.Exit(3)
	}

	config := gearcmd.TaskConfig{FunctionName: *functionName, FunctionCmd: *functionCmd, WarningLines: 5}
	worker := baseworker.NewWorker(*functionName, config.Process)
	defer worker.Close()
	log.Printf("Listening for job: " + *functionName)
	if err := worker.Listen(*gearmanHost, *gearmanPort); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>cmd/gearcmd: add missing baseworker-go import<commit_after>package main

import (
	"flag"
	"log"
	"os"

	"github.com/Clever/baseworker-go"
	"github.com/Clever/gearcmd/gearcmd"
)

func main() {
	functionName := flag.String("name", "", "Name of the Gearman function")
	functionCmd := flag.String("cmd", "", "The command to run")
	gearmanHost := flag.String("host", "localhost", "The Gearman host")
	gearmanPort := flag.String("port", "4730", "The Gearman port")
	flag.Parse()
	if len(*functionName) == 0 {
		log.Printf("Error: name not defined")
		flag.PrintDefaults()
		os.Exit(2)
	}
	if len(*functionCmd) == 0 {
		log.Printf("Error: cmd not defined")
		flag.PrintDefaults()
		os.Exit(3)
	}

	config := gearcmd.TaskConfig{FunctionName: *functionName, FunctionCmd: *functionCmd, WarningLines: 5}
	worker := baseworker.NewWorker(*functionName, config.Process)
	defer worker.Close()
	log.Printf("Listening for job: " + *functionName)
	if err := worker.Listen(*gearmanHost, *gearmanPort); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/Masterminds/cookoo"
	"path"
	"fmt"
	"os"
)

func LinkPackage(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	cfg := c.Get("cfg", "").(*Config)
	pname := p.Get("path", cfg.Name).(string)

	//here, err := os.Getwd()
	//if err != nil {
	//	return nil, fmt.Errorf("Could not get current directory: %s", err)
	//}

	// Per issue #10, this may be nicer to work with in cases where repos are
	// moved.
	here := "../.."

	gopath := os.Getenv("GOPATH")
	if len(gopath) == 0 {
		return nil, fmt.Errorf("$GOPATH appears to be unset.")
	}
	if len(pname) == 0 {
		return nil, fmt.Errorf("glide.yaml is missing 'package:'")
	}

	base := path.Dir(pname)
	if base != "." {
		dir := fmt.Sprintf("%s/src/%s", gopath, base)
		if err := os.MkdirAll(dir, os.ModeDir | 0755); err != nil {
			return nil, fmt.Errorf("Failed to make directory %s: %s", dir, err)
		}
	}

	ldest := fmt.Sprintf("%s/src/%s", gopath, pname)
	if err := os.Symlink(here, ldest); err != nil {
		if os.IsExist(err) {
			Info("Link to %s already exists. Skipping.\n", ldest)
		} else {
			return nil, fmt.Errorf("Failed to create symlink from %s to %s: %s", gopath, ldest, err)
		}
	}

	return ldest, nil
}
<commit_msg>Fixed the generator for relative links.<commit_after>package cmd

import (
	"github.com/Masterminds/cookoo"
	"strings"
	"path"
	"fmt"
	"os"
)

func LinkPackage(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	cfg := c.Get("cfg", "").(*Config)
	pname := p.Get("path", cfg.Name).(string)

	//here, err := os.Getwd()
	//if err != nil {
	//	return nil, fmt.Errorf("Could not get current directory: %s", err)
	//}

	// Per issue #10, this may be nicer to work with in cases where repos are
	// moved.
	//here := "../.."
	depth := strings.Count(pname, "/")
	here := "../.." + strings.Repeat("/..", depth)

	gopath := os.Getenv("GOPATH")
	if len(gopath) == 0 {
		return nil, fmt.Errorf("$GOPATH appears to be unset.")
	}
	if len(pname) == 0 {
		return nil, fmt.Errorf("glide.yaml is missing 'package:'")
	}

	base := path.Dir(pname)
	if base != "." {
		dir := fmt.Sprintf("%s/src/%s", gopath, base)
		if err := os.MkdirAll(dir, os.ModeDir | 0755); err != nil {
			return nil, fmt.Errorf("Failed to make directory %s: %s", dir, err)
		}
	}

	ldest := fmt.Sprintf("%s/src/%s", gopath, pname)
	if err := os.Symlink(here, ldest); err != nil {
		if os.IsExist(err) {
			Info("Link to %s already exists. Skipping.\n", ldest)
		} else {
			return nil, fmt.Errorf("Failed to create symlink from %s to %s: %s", gopath, ldest, err)
		}
	}

	return ldest, nil
}
<|endoftext|>


<commit_before>// Command mylogin allows to dump the content of ~/.my.cnf.

package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"text/template"

	"github.com/dolmen-go/mylogin"
)

type outputFormat interface {
	Help() (string, string)
	flag.Getter
	Print(w io.Writer, login *mylogin.Login) error
}

type formatReplay bool

func (formatReplay) Help() (string, string) {
	return "replay", "mysql_config_editor commands format"
}

func (formatReplay) IsBoolFlag() bool {
	return true
}

func (f *formatReplay) String() string {
	return strconv.FormatBool(bool(*f))
}

func (f *formatReplay) Set(s string) error {
	ok, err := strconv.ParseBool(s)
	if err != nil {
		return err
	}
	*f = formatReplay(ok)
	return nil
}

func (f *formatReplay) Get() interface{} {
	if !*f {
		return nil
	}
	return f
}

func (formatReplay) Print(w io.Writer, login *mylogin.Login) error {
	args := make([]string, 5, 5+5*2)
	args[0] = `mysql_config_editor`
	args[1] = `set`
	args[2] = `--skip-warn`
	args[3] = `-G`
	args[4] = flag.Arg(0)
	if login.User != nil {
		args = append(args, `-u`, *login.User)
	}
	if login.Password != nil {
		args = append(args, `-p`)
	}
	if login.Host != nil {
		args = append(args, `-h`, *login.Host)
	}
	if login.Port != nil {
		args = append(args, `-P`, *login.Port)
	}
	if login.Socket != nil {
		args = append(args, `-S`, *login.Socket)
	}
	_, err := fmt.Fprintln(w, strings.Join(args, " "))
	return err
}

func loginAsMap(login *mylogin.Login) map[string]interface{} {
	// The login struct contains *string
	// This is not convenient to use in templates
	// So we remap it to a map, skipping nil values
	m := make(map[string]interface{})
	for _, x := range []struct {
		key   string
		value *string
	}{
		{"user", login.User},
		{"password", login.Password},
		{"host", login.Host},
		{"socket", login.Socket},
		{"port", login.Port},
	} {
		if x.value != nil {
			m[x.key] = *x.value
		}
	}

	return m
}

type formatJSON bool

func (formatJSON) Help() (string, string) {
	return "json", "JSON format"
}

func (formatJSON) IsBoolFlag() bool {
	return true
}

func (f *formatJSON) String() string {
	return strconv.FormatBool(bool(*f))
}

func (f *formatJSON) Set(s string) error {
	ok, err := strconv.ParseBool(s)
	if err != nil {
		return err
	}
	*f = formatJSON(ok)
	return nil
}

func (f *formatJSON) Get() interface{} {
	if !*f {
		return nil
	}
	return f
}

func (formatJSON) Print(w io.Writer, login *mylogin.Login) error {
	enc := json.NewEncoder(os.Stdout)
	enc.SetEscapeHTML(false)
	enc.SetIndent("", "  ")
	return enc.Encode(loginAsMap(login))
}

type formatTemplate struct {
	tmpl *template.Template
}

func (formatTemplate) Help() (string, string) {
	return "template", "text/template format"
}

func (f *formatTemplate) String() string {
	if (*f).tmpl == nil {
		return ""
	}
	return "<template>"
}

func (f *formatTemplate) Set(s string) error {
	tmpl, err := template.New("user-template").Parse(s)
	if err != nil {
		return err
	}
	(*f).tmpl = tmpl
	return nil
}

func (f *formatTemplate) Get() interface{} {
	if f.tmpl == nil {
		return nil
	}
	return f
}

func (f *formatTemplate) Print(w io.Writer, login *mylogin.Login) error {
	err := f.tmpl.Execute(os.Stdout, loginAsMap(login))
	if err != nil {
		return err
	}
	_, err = fmt.Fprint(w)
	return err
}

func main() {
	var filename string
	flag.StringVar(&filename, "file", mylogin.DefaultFile(), "mylogin.cnf path")
	//var formatJSON, formatReplay bool
	//var formatTemplate string

	fmtReplay := formatReplay(false)
	fmtJSON := formatJSON(false)
	formats := []outputFormat{
		&fmtReplay,
		&fmtJSON,
		&formatTemplate{},
	}

	for _, fmt := range formats {
		name, usage := fmt.Help()
		flag.Var(fmt, name, usage)
	}

	flag.Parse()

	var selectedFormat outputFormat
	for _, ft := range formats {
		f := ft.Get()
		if f == nil {
			continue
		}
		if selectedFormat != nil {
			h1, _ := ft.Help()
			h2, _ := selectedFormat.Help()
			fmt.Fprintf(os.Stderr, "options -%s and -%s are exclusive.\n", h1, h2)
			flag.Usage()
			os.Exit(1)
		}
		selectedFormat = ft
	}

	if selectedFormat != nil {

		if flag.NArg() != 0 {

			for _, arg := range flag.Args() {
				login, err := mylogin.ReadLogin(filename, []string{arg})
				if err != nil {
					log.Fatal(err)
				}
				if login == nil {
					log.Fatal("section doesn't exists")
				}

				err = selectedFormat.Print(os.Stdout, login)
				if err != nil {
					log.Fatal(err)
				}
			}
		} else {
			sections, err := mylogin.ReadSections(filename)
			if err != nil {
				log.Fatal(err)
			}

			for _, login := range sections {
				err = selectedFormat.Print(os.Stdout, &login.Login)
				if err != nil {
					log.Fatal(err)
				}
			}

		}

	} else {
		file, err := os.Open(filename)
		if err != nil {
			log.Fatal(err)
		}
		defer file.Close()

		f, err := mylogin.Decode(bufio.NewReader(file))
		if err != nil {
			log.Fatal(err)
		}
		rd := f.PlainText()

		if flag.NArg() > 0 {
			rd = mylogin.FilterSection(rd, flag.Arg(0))
		}

		_, err = io.Copy(os.Stdout, rd)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<commit_msg>cmd/mylogin: fix exporting section name with -replay<commit_after>// Command mylogin allows to dump the content of ~/.my.cnf.

package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"strconv"
	"strings"
	"text/template"

	"github.com/dolmen-go/mylogin"
)

type outputFormat interface {
	Help() (string, string)
	flag.Getter
	Print(w io.Writer, section *mylogin.Section) error
}

type formatReplay bool

func (formatReplay) Help() (string, string) {
	return "replay", "mysql_config_editor commands format"
}

func (formatReplay) IsBoolFlag() bool {
	return true
}

func (f *formatReplay) String() string {
	return strconv.FormatBool(bool(*f))
}

func (f *formatReplay) Set(s string) error {
	ok, err := strconv.ParseBool(s)
	if err != nil {
		return err
	}
	*f = formatReplay(ok)
	return nil
}

func (f *formatReplay) Get() interface{} {
	if !*f {
		return nil
	}
	return f
}

func (formatReplay) Print(w io.Writer, section *mylogin.Section) error {
	args := make([]string, 5, 5+5*2)
	args[0] = `mysql_config_editor`
	args[1] = `set`
	args[2] = `--skip-warn`
	args[3] = `-G`
	args[4] = section.Name
	if section.Login.User != nil {
		args = append(args, `-u`, *section.Login.User)
	}
	if section.Login.Password != nil {
		args = append(args, `-p`)
	}
	if section.Login.Host != nil {
		args = append(args, `-h`, *section.Login.Host)
	}
	if section.Login.Port != nil {
		args = append(args, `-P`, *section.Login.Port)
	}
	if section.Login.Socket != nil {
		args = append(args, `-S`, *section.Login.Socket)
	}
	_, err := fmt.Fprintln(w, strings.Join(args, " "))
	return err
}

func loginAsMap(login *mylogin.Login) map[string]interface{} {
	// The login struct contains *string
	// This is not convenient to use in templates
	// So we remap it to a map, skipping nil values
	m := make(map[string]interface{})
	for _, x := range []struct {
		key   string
		value *string
	}{
		{"user", login.User},
		{"password", login.Password},
		{"host", login.Host},
		{"socket", login.Socket},
		{"port", login.Port},
	} {
		if x.value != nil {
			m[x.key] = *x.value
		}
	}

	return m
}

type formatJSON bool

func (formatJSON) Help() (string, string) {
	return "json", "JSON format"
}

func (formatJSON) IsBoolFlag() bool {
	return true
}

func (f *formatJSON) String() string {
	return strconv.FormatBool(bool(*f))
}

func (f *formatJSON) Set(s string) error {
	ok, err := strconv.ParseBool(s)
	if err != nil {
		return err
	}
	*f = formatJSON(ok)
	return nil
}

func (f *formatJSON) Get() interface{} {
	if !*f {
		return nil
	}
	return f
}

func (formatJSON) Print(w io.Writer, section *mylogin.Section) error {
	enc := json.NewEncoder(os.Stdout)
	enc.SetEscapeHTML(false)
	enc.SetIndent("", "  ")
	return enc.Encode(loginAsMap(&section.Login))
}

type formatTemplate struct {
	tmpl *template.Template
}

func (formatTemplate) Help() (string, string) {
	return "template", "text/template format"
}

func (f *formatTemplate) String() string {
	if (*f).tmpl == nil {
		return ""
	}
	return "<template>"
}

func (f *formatTemplate) Set(s string) error {
	tmpl, err := template.New("user-template").Parse(s)
	if err != nil {
		return err
	}
	(*f).tmpl = tmpl
	return nil
}

func (f *formatTemplate) Get() interface{} {
	if f.tmpl == nil {
		return nil
	}
	return f
}

func (f *formatTemplate) Print(w io.Writer, section *mylogin.Section) error {
	err := f.tmpl.Execute(os.Stdout, loginAsMap(&section.Login))
	if err != nil {
		return err
	}
	_, err = fmt.Fprint(w)
	return err
}

func main() {
	var filename string
	flag.StringVar(&filename, "file", mylogin.DefaultFile(), "mylogin.cnf path")
	//var formatJSON, formatReplay bool
	//var formatTemplate string

	fmtReplay := formatReplay(false)
	fmtJSON := formatJSON(false)
	formats := []outputFormat{
		&fmtReplay,
		&fmtJSON,
		&formatTemplate{},
	}

	for _, fmt := range formats {
		name, usage := fmt.Help()
		flag.Var(fmt, name, usage)
	}

	flag.Parse()

	var selectedFormat outputFormat
	for _, ft := range formats {
		f := ft.Get()
		if f == nil {
			continue
		}
		if selectedFormat != nil {
			h1, _ := ft.Help()
			h2, _ := selectedFormat.Help()
			fmt.Fprintf(os.Stderr, "options -%s and -%s are exclusive.\n", h1, h2)
			flag.Usage()
			os.Exit(1)
		}
		selectedFormat = ft
	}

	if selectedFormat != nil {

		if flag.NArg() != 0 {

			for _, name := range flag.Args() {
				login, err := mylogin.ReadLogin(filename, []string{name})
				if err != nil {
					log.Fatal(err)
				}
				if login == nil {
					log.Fatal("section doesn't exists")
				}

				err = selectedFormat.Print(os.Stdout, &mylogin.Section{Name: name, Login: *login})
				if err != nil {
					log.Fatal(err)
				}
			}
		} else {
			sections, err := mylogin.ReadSections(filename)
			if err != nil {
				log.Fatal(err)
			}

			for i := range sections {
				err = selectedFormat.Print(os.Stdout, &sections[i])
				if err != nil {
					log.Fatal(err)
				}
			}

		}

	} else {
		file, err := os.Open(filename)
		if err != nil {
			log.Fatal(err)
		}
		defer file.Close()

		f, err := mylogin.Decode(bufio.NewReader(file))
		if err != nil {
			log.Fatal(err)
		}
		rd := f.PlainText()

		if flag.NArg() > 0 {
			rd = mylogin.FilterSection(rd, flag.Arg(0))
		}

		_, err = io.Copy(os.Stdout, rd)
		if err != nil {
			log.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"net/http"
	"strings"
	"log"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/xyproto/permissions2"
)

func main() {
	e := echo.New()

	// New permissions middleware
	perm, err := permissions.New2()
	if err != nil {
		log.Fatalln(err)
	}

	// Blank slate, no default permissions
	//perm.Clear()

	// Set up a middleware handler for Echo, with a custom "permission denied" message.
	permissionHandler := func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) error {
			// Check if the user has the right admin/user rights
			if perm.Rejected(c.Response().Writer(), c.Request()) {
				// Deny the request
				return echo.NewHTTPError(http.StatusForbidden, "Permission denied!")
			}
			// Continue the chain of middleware
			return next(c)
		}
	}

	// Logging middleware
	e.Use(middleware.Logger())

	// Enable the permissions middleware, must come before recovery
	e.Use(permissionHandler)

	// Recovery middleware
	e.Use(middleware.Recover())

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	e.Get("/", func(c *echo.Context) error {
		var buf bytes.Buffer
		b2s := map[bool]string{false: "false", true: "true"}
		buf.WriteString("Has user bob: " + b2s[userstate.HasUser("bob")] + "\n")
		buf.WriteString("Logged in on server: " + b2s[userstate.IsLoggedIn("bob")] + "\n")
		buf.WriteString("Is confirmed: " + b2s[userstate.IsConfirmed("bob")] + "\n")
		buf.WriteString("Username stored in cookies (or blank): " + userstate.Username(c.Request()) + "\n")
		buf.WriteString("Current user is logged in, has a valid cookie and *user rights*: " + b2s[userstate.UserRights(c.Request())] + "\n")
		buf.WriteString("Current user is logged in, has a valid cookie and *admin rights*: " + b2s[userstate.AdminRights(c.Request())] + "\n")
		buf.WriteString("\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
		return c.String(http.StatusOK, buf.String())
	})

	e.Get("/register", func(c *echo.Context) error {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was created: %v\n", userstate.HasUser("bob")))
	})

	e.Get("/confirm", func(c *echo.Context) error {
		userstate.MarkConfirmed("bob")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was confirmed: %v\n", userstate.IsConfirmed("bob")))
	})

	e.Get("/remove", func(c *echo.Context) error {
		userstate.RemoveUser("bob")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was removed: %v\n", !userstate.HasUser("bob")))
	})

	e.Get("/login", func(c *echo.Context) error {
		// Headers will be written, for storing a cookie
		userstate.Login(c.Response().Writer(), "bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now logged in: %v\n", userstate.IsLoggedIn("bob")))
	})

	e.Get("/logout", func(c *echo.Context) error {
		userstate.Logout("bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now logged out: %v\n", !userstate.IsLoggedIn("bob")))
	})

	e.Get("/makeadmin", func(c *echo.Context) error {
		userstate.SetAdminStatus("bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now administrator: %v\n", userstate.IsAdmin("bob")))
	})

	e.Get("/clear", func(c *echo.Context) error {
		userstate.ClearCookie(c.Response().Writer())
		return c.String(http.StatusOK, "Clearing cookie")
	})

	e.Get("/data", func(c *echo.Context) error {
		return c.String(http.StatusOK, "user page that only logged in users must see!")
	})

	e.Get("/admin", func(c *echo.Context) error {
		var buf bytes.Buffer
		buf.WriteString("super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.AllUsernames(); err == nil {
			buf.WriteString("list of all users: " + strings.Join(usernames, ", "))
		}
		return c.String(http.StatusOK, buf.String())
	})

	// Serve
	e.Run(":3000")
}
<commit_msg>go fmt<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"net/http"
	"strings"

	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/xyproto/permissions2"
)

func main() {
	e := echo.New()

	// New permissions middleware
	perm, err := permissions.New2()
	if err != nil {
		log.Fatalln(err)
	}

	// Blank slate, no default permissions
	//perm.Clear()

	// Set up a middleware handler for Echo, with a custom "permission denied" message.
	permissionHandler := func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) error {
			// Check if the user has the right admin/user rights
			if perm.Rejected(c.Response().Writer(), c.Request()) {
				// Deny the request
				return echo.NewHTTPError(http.StatusForbidden, "Permission denied!")
			}
			// Continue the chain of middleware
			return next(c)
		}
	}

	// Logging middleware
	e.Use(middleware.Logger())

	// Enable the permissions middleware, must come before recovery
	e.Use(permissionHandler)

	// Recovery middleware
	e.Use(middleware.Recover())

	// Get the userstate, used in the handlers below
	userstate := perm.UserState()

	e.Get("/", func(c *echo.Context) error {
		var buf bytes.Buffer
		b2s := map[bool]string{false: "false", true: "true"}
		buf.WriteString("Has user bob: " + b2s[userstate.HasUser("bob")] + "\n")
		buf.WriteString("Logged in on server: " + b2s[userstate.IsLoggedIn("bob")] + "\n")
		buf.WriteString("Is confirmed: " + b2s[userstate.IsConfirmed("bob")] + "\n")
		buf.WriteString("Username stored in cookies (or blank): " + userstate.Username(c.Request()) + "\n")
		buf.WriteString("Current user is logged in, has a valid cookie and *user rights*: " + b2s[userstate.UserRights(c.Request())] + "\n")
		buf.WriteString("Current user is logged in, has a valid cookie and *admin rights*: " + b2s[userstate.AdminRights(c.Request())] + "\n")
		buf.WriteString("\nTry: /register, /confirm, /remove, /login, /logout, /makeadmin, /clear, /data and /admin")
		return c.String(http.StatusOK, buf.String())
	})

	e.Get("/register", func(c *echo.Context) error {
		userstate.AddUser("bob", "hunter1", "bob@zombo.com")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was created: %v\n", userstate.HasUser("bob")))
	})

	e.Get("/confirm", func(c *echo.Context) error {
		userstate.MarkConfirmed("bob")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was confirmed: %v\n", userstate.IsConfirmed("bob")))
	})

	e.Get("/remove", func(c *echo.Context) error {
		userstate.RemoveUser("bob")
		return c.String(http.StatusOK, fmt.Sprintf("User bob was removed: %v\n", !userstate.HasUser("bob")))
	})

	e.Get("/login", func(c *echo.Context) error {
		// Headers will be written, for storing a cookie
		userstate.Login(c.Response().Writer(), "bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now logged in: %v\n", userstate.IsLoggedIn("bob")))
	})

	e.Get("/logout", func(c *echo.Context) error {
		userstate.Logout("bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now logged out: %v\n", !userstate.IsLoggedIn("bob")))
	})

	e.Get("/makeadmin", func(c *echo.Context) error {
		userstate.SetAdminStatus("bob")
		return c.String(http.StatusOK, fmt.Sprintf("bob is now administrator: %v\n", userstate.IsAdmin("bob")))
	})

	e.Get("/clear", func(c *echo.Context) error {
		userstate.ClearCookie(c.Response().Writer())
		return c.String(http.StatusOK, "Clearing cookie")
	})

	e.Get("/data", func(c *echo.Context) error {
		return c.String(http.StatusOK, "user page that only logged in users must see!")
	})

	e.Get("/admin", func(c *echo.Context) error {
		var buf bytes.Buffer
		buf.WriteString("super secret information that only logged in administrators must see!\n\n")
		if usernames, err := userstate.AllUsernames(); err == nil {
			buf.WriteString("list of all users: " + strings.Join(usernames, ", "))
		}
		return c.String(http.StatusOK, buf.String())
	})

	// Serve
	e.Run(":3000")
}
<|endoftext|>


<commit_before>package kubernetes

import (
	"testing"

	"github.com/coredns/coredns/middleware/pkg/dnsrecorder"
	"github.com/coredns/coredns/middleware/test"

	"github.com/miekg/dns"
	"golang.org/x/net/context"
	"k8s.io/client-go/1.5/pkg/api"
)

type APIConnReverseTest struct{}

func (APIConnReverseTest) Run()                          { return }
func (APIConnReverseTest) Stop() error                   { return nil }
func (APIConnReverseTest) PodIndex(string) []interface{} { return nil }

func (APIConnReverseTest) ServiceList() []*api.Service {
	svcs := []*api.Service{
		{
			ObjectMeta: api.ObjectMeta{
				Name:      "svc1",
				Namespace: "testns",
			},
			Spec: api.ServiceSpec{
				ClusterIP: "192.168.1.100",
				Ports: []api.ServicePort{{
					Name:     "http",
					Protocol: "tcp",
					Port:     80,
				}},
			},
		},
	}
	return svcs
}

func (APIConnReverseTest) EndpointsList() api.EndpointsList {
	return api.EndpointsList{
		Items: []api.Endpoints{
			{
				Subsets: []api.EndpointSubset{
					{
						Addresses: []api.EndpointAddress{
							{
								IP:       "10.0.0.100",
								Hostname: "ep1a",
							},
						},
						Ports: []api.EndpointPort{
							{
								Port:     80,
								Protocol: "tcp",
								Name:     "http",
							},
						},
					},
				},
				ObjectMeta: api.ObjectMeta{
					Name:      "svc1",
					Namespace: "testns",
				},
			},
		},
	}
}

func (APIConnReverseTest) GetNodeByName(name string) (api.Node, error) {
	return api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "test.node.foo.bar",
		},
	}, nil
}

func TestReverse(t *testing.T) {

	k := Kubernetes{Zones: []string{"cluster.local.", "0.10.in-addr.arpa."}}
	k.interfaceAddrsFunc = localPodIP
	k.APIConn = &APIConnReverseTest{}

	tests := []test.Case{
		{
			Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
			Rcode: dns.RcodeSuccess,
			Answer: []dns.RR{
				test.PTR("100.0.0.10.in-addr.arpa.      303    IN      PTR       ep1a.svc1.testns.svc.cluster.local."),
			},
		},
		{
			Qname: "101.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
			Rcode: dns.RcodeSuccess,
			Ns: []dns.RR{
				test.SOA("0.10.in-addr.arpa.	300	IN	SOA	ns.dns.0.10.in-addr.arpa. hostmaster.0.10.in-addr.arpa. 1502782828 7200 1800 86400 60"),
			},
		},
	}

	ctx := context.TODO()
	for i, tc := range tests {
		r := tc.Msg()

		w := dnsrecorder.New(&test.ResponseWriter{})

		_, err := k.ServeDNS(ctx, w, r)
		if err != tc.Error {
			t.Errorf("Test %d: expected no error, got %v", i, err)
			return
		}
		if tc.Error != nil {
			continue
		}

		resp := w.Msg
		if resp == nil {
			t.Fatalf("Test %d: got nil message and no error for: %s %d", i, r.Question[0].Name, r.Question[0].Qtype)
		}
		test.SortAndCheck(t, resp, tc)
	}
}
<commit_msg>mw/kubernetes: add reverse test case (#932)<commit_after>package kubernetes

import (
	"testing"

	"github.com/coredns/coredns/middleware/pkg/dnsrecorder"
	"github.com/coredns/coredns/middleware/test"

	"github.com/miekg/dns"
	"golang.org/x/net/context"
	"k8s.io/client-go/1.5/pkg/api"
)

type APIConnReverseTest struct{}

func (APIConnReverseTest) Run()                          { return }
func (APIConnReverseTest) Stop() error                   { return nil }
func (APIConnReverseTest) PodIndex(string) []interface{} { return nil }

func (APIConnReverseTest) ServiceList() []*api.Service {
	svcs := []*api.Service{
		{
			ObjectMeta: api.ObjectMeta{
				Name:      "svc1",
				Namespace: "testns",
			},
			Spec: api.ServiceSpec{
				ClusterIP: "192.168.1.100",
				Ports: []api.ServicePort{{
					Name:     "http",
					Protocol: "tcp",
					Port:     80,
				}},
			},
		},
	}
	return svcs
}

func (APIConnReverseTest) EndpointsList() api.EndpointsList {
	return api.EndpointsList{
		Items: []api.Endpoints{
			{
				Subsets: []api.EndpointSubset{
					{
						Addresses: []api.EndpointAddress{
							{
								IP:       "10.0.0.100",
								Hostname: "ep1a",
							},
						},
						Ports: []api.EndpointPort{
							{
								Port:     80,
								Protocol: "tcp",
								Name:     "http",
							},
						},
					},
				},
				ObjectMeta: api.ObjectMeta{
					Name:      "svc1",
					Namespace: "testns",
				},
			},
		},
	}
}

func (APIConnReverseTest) GetNodeByName(name string) (api.Node, error) {
	return api.Node{
		ObjectMeta: api.ObjectMeta{
			Name: "test.node.foo.bar",
		},
	}, nil
}

func TestReverse(t *testing.T) {

	k := Kubernetes{Zones: []string{"cluster.local.", "0.10.in-addr.arpa."}}
	k.interfaceAddrsFunc = localPodIP
	k.APIConn = &APIConnReverseTest{}

	tests := []test.Case{
		{
			Qname: "100.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
			Rcode: dns.RcodeSuccess,
			Answer: []dns.RR{
				test.PTR("100.0.0.10.in-addr.arpa.      303    IN      PTR       ep1a.svc1.testns.svc.cluster.local."),
			},
		},
		{
			Qname: "101.0.0.10.in-addr.arpa.", Qtype: dns.TypePTR,
			Rcode: dns.RcodeSuccess,
			Ns: []dns.RR{
				test.SOA("0.10.in-addr.arpa.	300	IN	SOA	ns.dns.0.10.in-addr.arpa. hostmaster.0.10.in-addr.arpa. 1502782828 7200 1800 86400 60"),
			},
		},
		{
			Qname: "example.org.cluster.local.", Qtype: dns.TypePTR,
			Rcode: dns.RcodeSuccess,
			Ns: []dns.RR{
				test.SOA("cluster.local.       300     IN      SOA     ns.dns.cluster.local. hostmaster.cluster.local. 1502989566 7200 1800 86400 60"),
			},
		},
	}

	ctx := context.TODO()
	for i, tc := range tests {
		r := tc.Msg()

		w := dnsrecorder.New(&test.ResponseWriter{})

		_, err := k.ServeDNS(ctx, w, r)
		if err != tc.Error {
			t.Errorf("Test %d: expected no error, got %v", i, err)
			return
		}

		resp := w.Msg
		if resp == nil {
			t.Fatalf("Test %d: got nil message and no error for: %s %d", i, r.Question[0].Name, r.Question[0].Qtype)
		}
		test.SortAndCheck(t, resp, tc)
	}
}
<|endoftext|>


<commit_before>package trello

import (
	"bytes"
	log "github.com/Sirupsen/logrus"
	"github.com/eternnoir/mmic/mmic/config"

	"github.com/eternnoir/mmic/mmic/senders"

	"fmt"
	"github.com/eternnoir/mmic/mmic/payloads/mattermost"
	"github.com/eternnoir/mmic/mmic/payloads/trello"
	"github.com/labstack/echo"
	"net/http"
	"strings"
	"text/template"
)

type TrelloHandler struct {
	Config    *config.TrelloConfig
	RoutePath string
	Sender    senders.Sender
}

func NewTrelloHandler(config config.TrelloConfig) *TrelloHandler {
	th := &TrelloHandler{Config: &config}
	th.Sender = senders.NewMatterMostSender(config.TargetMM)
	return th
}

func (th *TrelloHandler) Handle(c echo.Context) error {
	trelloUpdate := &trello.UpdateAction{}
	if err := c.Bind(trelloUpdate); err != nil {
		return err
	}
	log.Infof("TrelloHandler get new update %#v", trelloUpdate)
	if trelloUpdate.GetType() == "" {
		log.Infof("Not Support type. %#v", trelloUpdate)
		return c.String(http.StatusOK, "")
	}
	payload, err := th.convertToMMPayload(trelloUpdate)
	if err != nil {
		log.Errorf("Convert payload fail.%s", err)
		return err
	}
	serr := th.Sender.Send(payload)
	if serr != nil {
		log.Errorf("Send Paylod fail.%s", err)
		return err
	}
	return c.String(http.StatusOK, "")
}

func (th *TrelloHandler) convertToMMPayload(trelloupdate *trello.UpdateAction) (*mattermost.MatterMostPayload, error) {
	tmpl, err := template.New("mmsendertemplate").Parse(th.Config.TextTemplate)
	if err != nil {
		return nil, fmt.Errorf("Parse template error. %s", err)
	}
	var doc bytes.Buffer
	err = tmpl.Execute(&doc, *trelloupdate)
	if err != nil {
		return nil, fmt.Errorf("Template Exec error. %s", err)
	}
	payload := &mattermost.MatterMostPayload{}
	mmconfig := th.Config.TargetMM

	if mmconfig.Channel != "" {
		payload.Channel = &mmconfig.Channel
	}
	if mmconfig.Username != "" {
		payload.Username = &mmconfig.Username
	}
	textStr := doc.String()
	if strings.TrimSpace(textStr) == "" {
		return nil, fmt.Errorf("Nothing to send. %#v", trelloupdate)
	}
	payload.Text = &textStr

	return payload, nil
}
<commit_msg>Remove type check.<commit_after>package trello

import (
	"bytes"
	log "github.com/Sirupsen/logrus"
	"github.com/eternnoir/mmic/mmic/config"

	"github.com/eternnoir/mmic/mmic/senders"

	"fmt"
	"github.com/eternnoir/mmic/mmic/payloads/mattermost"
	"github.com/eternnoir/mmic/mmic/payloads/trello"
	"github.com/labstack/echo"
	"net/http"
	"strings"
	"text/template"
)

type TrelloHandler struct {
	Config    *config.TrelloConfig
	RoutePath string
	Sender    senders.Sender
}

func NewTrelloHandler(config config.TrelloConfig) *TrelloHandler {
	th := &TrelloHandler{Config: &config}
	th.Sender = senders.NewMatterMostSender(config.TargetMM)
	return th
}

func (th *TrelloHandler) Handle(c echo.Context) error {
	trelloUpdate := &trello.UpdateAction{}
	if err := c.Bind(trelloUpdate); err != nil {
		return err
	}
	log.Infof("TrelloHandler get new update %#v", trelloUpdate)
	payload, err := th.convertToMMPayload(trelloUpdate)
	if err != nil {
		log.Errorf("Convert payload fail.%s", err)
		return err
	}
	serr := th.Sender.Send(payload)
	if serr != nil {
		log.Errorf("Send Paylod fail.%s", err)
		return err
	}
	return c.String(http.StatusOK, "")
}

func (th *TrelloHandler) convertToMMPayload(trelloupdate *trello.UpdateAction) (*mattermost.MatterMostPayload, error) {
	tmpl, err := template.New("mmsendertemplate").Parse(th.Config.TextTemplate)
	if err != nil {
		return nil, fmt.Errorf("Parse template error. %s", err)
	}
	var doc bytes.Buffer
	err = tmpl.Execute(&doc, *trelloupdate)
	if err != nil {
		return nil, fmt.Errorf("Template Exec error. %s", err)
	}
	payload := &mattermost.MatterMostPayload{}
	mmconfig := th.Config.TargetMM

	if mmconfig.Channel != "" {
		payload.Channel = &mmconfig.Channel
	}
	if mmconfig.Username != "" {
		payload.Username = &mmconfig.Username
	}
	textStr := doc.String()
	if strings.TrimSpace(textStr) == "" {
		return nil, fmt.Errorf("Nothing to send. %#v", trelloupdate)
	}
	payload.Text = &textStr

	return payload, nil
}
<|endoftext|>


<commit_before>package effects

import "github.com/faiface/beep"

// Pan balances the wrapped Streamer between the left and the right channel. The Pan field value of
// -1 means that both original channels go through the left channel. The value of +1 means the same
// for the right channel. The value of 0 changes nothing.
type Pan struct {
	Streamer beep.Streamer
	Pan      float64
}

// Stream streams the wrapped Streamer balanced by Pan.
func (p *Pan) Stream(samples [][2]float64) (n int, ok bool) {
	n, ok = p.Streamer.Stream(samples)
	for i := range samples[:n] {
		l := samples[i][0]
		r := samples[i][1]
		switch {
		case p.Pan < 0:
			samples[i][0] += -p.Pan * r
			samples[i][1] -= -p.Pan * r
		case p.Pan > 0:
			samples[i][0] -= p.Pan * l
			samples[i][1] += p.Pan * l
		}
	}
	return n, ok
}

// Err propagates the wrapped Streamer's errors.
func (p *Pan) Err() error {
	return p.Streamer.Err()
}
<commit_msg>minor change<commit_after>package effects

import "github.com/faiface/beep"

// Pan balances the wrapped Streamer between the left and the right channel. The Pan field value of
// -1 means that both original channels go through the left channel. The value of +1 means the same
// for the right channel. The value of 0 changes nothing.
type Pan struct {
	Streamer beep.Streamer
	Pan      float64
}

// Stream streams the wrapped Streamer balanced by Pan.
func (p *Pan) Stream(samples [][2]float64) (n int, ok bool) {
	n, ok = p.Streamer.Stream(samples)
	switch {
	case p.Pan < 0:
		for i := range samples[:n] {
			r := samples[i][1]
			samples[i][0] += -p.Pan * r
			samples[i][1] -= -p.Pan * r
		}
	case p.Pan > 0:
		for i := range samples[:n] {
			l := samples[i][0]
			samples[i][0] -= p.Pan * l
			samples[i][1] += p.Pan * l
		}
	}
	return n, ok
}

// Err propagates the wrapped Streamer's errors.
func (p *Pan) Err() error {
	return p.Streamer.Err()
}
<|endoftext|>


<commit_before>// +build go1.7

package goel

import (
	"fmt"
	"reflect"

	"github.com/apaxa-go/eval"

	"github.com/nikolay-turpitko/structor/el"
)

// Interpreter implements "github.com/nikolay-turpitko/structor/el".Interpreter
// using "github.com/apaxa-go/eval". Thus, it evaluates Go-style expressions.
//
// Besides eval.Args passed to it, Interpreter creates these additional
// custom objects for use in EL expressions:
//
//  - ctx
//  - ctxStruct
//  - ctxExtra
//  - ctxSub
//  - eval
//
// Structure "ctx" is a context of type *el.Context.
//
// Types "ctxStruct", "ctxExtra" and "ctxSub" are the EL aliases of actual
// types of correspondent fields of "ctx" and can be used to convert inteface{}
// types of these fields to proper types to access their fields.
//
// Function "eval" with signature `func(intrpr, expr string) interface{}`
// can be used to evaluate given expression with given interpreter.
// This can be useful to evaluate expression passed as a string within context.
// This feature works with support of the calling code, which passes actual
// implementation of the "eval" function in Context.EvalExpr().
// Interpreter name should be known to calling code. For example, for known
// implementation (structor.NewEvaluator()) interpreter name is a tag name,
// onto which given interpreter is mapped during creation of evaluator.
//
// Due restrictions of "github.com/apaxa-go/eval", only custom functions
// returning one or two results are  allowed. If custom function returns two
// results, its second result must be of error type and it's converted to
// panic (which is catched and converted back to error by the
// "github.com/apaxa-go/eval").
type Interpreter struct {
	// Arguments for expression.
	Args eval.Args
}

// Execute implements Interpreter.Execute()
func (i *Interpreter) Execute(
	expression string,
	ctx *el.Context) (interface{}, error) {
	expr, err := eval.ParseString(expression, "")
	if err != nil {
		return nil, fmt.Errorf("structor parse: <<%s>>: %v", ctx.LongName, err)
	}
	funcEval := func(intrpr, expr string) interface{} {
		res, err := ctx.EvalExpr(intrpr, expr, ctx)
		if err != nil {
			panic(err)
		}
		return res
	}
	args := eval.Args{
		"eval":      eval.MakeDataRegularInterface(funcEval),
		"ctx":       eval.MakeDataRegularInterface(ctx),
		"ctxStruct": eval.MakeTypeInterface(ctx.Struct),
	}
	if ctx.Extra != nil {
		args["ctxExtra"] = eval.MakeTypeInterface(ctx.Extra)
	}
	if ctx.Sub != nil {
		args["ctxSub"] = eval.MakeTypeInterface(ctx.Sub)
	}
	for k, v := range i.Args {
		args[k] = wrapFunc(v)
	}
	res, err := expr.EvalToInterface(args)
	if err != nil {
		return nil, fmt.Errorf("structor eval: <<%s>>: %v", ctx.LongName, err)
	}
	return res, nil
}

var errorType = reflect.TypeOf((*error)(nil)).Elem()

// wrapFunc check if argument is function with two return values, last of which
// is error, and wraps such a function to return only one value, as apaxa-go
// permits.
func wrapFunc(v eval.Value) eval.Value {
	if v.Kind() != eval.Datas {
		return v
	}
	if v.Data().Kind() != eval.Regular {
		return v
	}
	r := v.Data().Regular()
	if r.Kind() != reflect.Func {
		return v
	}
	t := r.Type()
	n := t.NumOut()
	if n != 2 {
		return v
	}
	if t.Out(1) != errorType {
		return v
	}
	in := make([]reflect.Type, 0, t.NumIn())
	for i, l := 0, t.NumIn(); i < l; i++ {
		in = append(in, t.In(i))
	}
	out := []reflect.Type{t.Out(0)}
	twraper := reflect.FuncOf(in, out, false)
	wraper := reflect.MakeFunc(twraper, func(args []reflect.Value) []reflect.Value {
		result := r.Call(args)
		if len(result) == 2 && !result[1].IsNil() {
			panic(result[1])
		}
		return result[:1]
	})
	return eval.MakeDataRegular(wraper)
}
<commit_msg>Pkg doc<commit_after>// +build go1.7

/*
Package goel provides an implementation of expression language (EL) interpreter
for struct tags.

This implementation is based on "github.com/apaxa-go/eval" and enables usage of
Go expressions in structor.
*/
package goel

import (
	"fmt"
	"reflect"

	"github.com/apaxa-go/eval"

	"github.com/nikolay-turpitko/structor/el"
)

// Interpreter implements "github.com/nikolay-turpitko/structor/el".Interpreter
// using "github.com/apaxa-go/eval". Thus, it evaluates Go-style expressions.
//
// Besides eval.Args passed to it, Interpreter creates these additional
// custom objects for use in EL expressions:
//
//  - ctx
//  - ctxStruct
//  - ctxExtra
//  - ctxSub
//  - eval
//
// Structure "ctx" is a context of type *el.Context.
//
// Types "ctxStruct", "ctxExtra" and "ctxSub" are the EL aliases of actual
// types of correspondent fields of "ctx" and can be used to convert inteface{}
// types of these fields to proper types to access their fields.
//
// Function "eval" with signature `func(intrpr, expr string) interface{}`
// can be used to evaluate given expression with given interpreter.
// This can be useful to evaluate expression passed as a string within context.
// This feature works with support of the calling code, which passes actual
// implementation of the "eval" function in Context.EvalExpr().
// Interpreter name should be known to calling code. For example, for known
// implementation (structor.NewEvaluator()) interpreter name is a tag name,
// onto which given interpreter is mapped during creation of evaluator.
//
// Due restrictions of "github.com/apaxa-go/eval", only custom functions
// returning one or two results are  allowed. If custom function returns two
// results, its second result must be of error type and it's converted to
// panic (which is catched and converted back to error by the
// "github.com/apaxa-go/eval").
type Interpreter struct {
	// Arguments for expression.
	Args eval.Args
}

// Execute implements Interpreter.Execute()
func (i *Interpreter) Execute(
	expression string,
	ctx *el.Context) (interface{}, error) {
	expr, err := eval.ParseString(expression, "")
	if err != nil {
		return nil, fmt.Errorf("structor parse: <<%s>>: %v", ctx.LongName, err)
	}
	funcEval := func(intrpr, expr string) interface{} {
		res, err := ctx.EvalExpr(intrpr, expr, ctx)
		if err != nil {
			panic(err)
		}
		return res
	}
	args := eval.Args{
		"eval":      eval.MakeDataRegularInterface(funcEval),
		"ctx":       eval.MakeDataRegularInterface(ctx),
		"ctxStruct": eval.MakeTypeInterface(ctx.Struct),
	}
	if ctx.Extra != nil {
		args["ctxExtra"] = eval.MakeTypeInterface(ctx.Extra)
	}
	if ctx.Sub != nil {
		args["ctxSub"] = eval.MakeTypeInterface(ctx.Sub)
	}
	for k, v := range i.Args {
		args[k] = wrapFunc(v)
	}
	res, err := expr.EvalToInterface(args)
	if err != nil {
		return nil, fmt.Errorf("structor eval: <<%s>>: %v", ctx.LongName, err)
	}
	return res, nil
}

var errorType = reflect.TypeOf((*error)(nil)).Elem()

// wrapFunc check if argument is function with two return values, last of which
// is error, and wraps such a function to return only one value, as apaxa-go
// permits.
func wrapFunc(v eval.Value) eval.Value {
	if v.Kind() != eval.Datas {
		return v
	}
	if v.Data().Kind() != eval.Regular {
		return v
	}
	r := v.Data().Regular()
	if r.Kind() != reflect.Func {
		return v
	}
	t := r.Type()
	n := t.NumOut()
	if n != 2 {
		return v
	}
	if t.Out(1) != errorType {
		return v
	}
	in := make([]reflect.Type, 0, t.NumIn())
	for i, l := 0, t.NumIn(); i < l; i++ {
		in = append(in, t.In(i))
	}
	out := []reflect.Type{t.Out(0)}
	twraper := reflect.FuncOf(in, out, false)
	wraper := reflect.MakeFunc(twraper, func(args []reflect.Value) []reflect.Value {
		result := r.Call(args)
		if len(result) == 2 && !result[1].IsNil() {
			panic(result[1])
		}
		return result[:1]
	})
	return eval.MakeDataRegular(wraper)
}
<|endoftext|>