<commit_before>/*
Copyright 2017 The Kubernetes Authors All rights reserved.

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 kobject

import (
	"github.com/docker/libcompose/yaml"
	"k8s.io/kubernetes/pkg/api"
)

// KomposeObject holds the generic struct of Kompose transformation
type KomposeObject struct {
	ServiceConfigs map[string]ServiceConfig
	// LoadedFrom is name of the loader that created KomposeObject
	// Transformer need to know origin format in order to tell user what tag is not supported in origin format
	// as they can have different names. For example environment variables  are called environment in compose but Env in bundle.
	LoadedFrom string
}

// ConvertOptions holds all options that controls transformation process
type ConvertOptions struct {
	ToStdout                    bool
	CreateD                     bool
	CreateRC                    bool
	CreateDS                    bool
	CreateDeploymentConfig      bool
	BuildRepo                   string
	BuildBranch                 string
	Build                       string
	CreateChart                 bool
	GenerateYaml                bool
	GenerateJSON                bool
	EmptyVols                   bool
	InsecureRepository          bool
	Replicas                    int
	InputFiles                  []string
	OutFile                     string
	Provider                    string
	Namespace                   string
	IsDeploymentFlag            bool
	IsDaemonSetFlag             bool
	IsReplicationControllerFlag bool
	IsReplicaSetFlag            bool
	IsDeploymentConfigFlag      bool
	IsNamespaceFlag             bool
}

// ServiceConfig holds the basic struct of a container
type ServiceConfig struct {
	// use tags to mark from what element this value comes
	ContainerName string
	Image         string   `compose:"image" bundle:"Image"`
	Environment   []EnvVar `compose:"environment" bundle:"Env"`
	Port          []Ports  `compose:"ports" bundle:"Ports"`
	Command       []string `compose:"command" bundle:"Command"`
	WorkingDir    string   `compose:"" bundle:"WorkingDir"`
	Args          []string `compose:"args" bundle:"Args"`
	// VolList is list of volumes extracted from docker-compose file
	VolList         []string            `compose:"volumes" bundle:"Volumes"`
	Network         []string            `compose:"network" bundle:"Networks"`
	Labels          map[string]string   `compose:"labels" bundle:"Labels"`
	Annotations     map[string]string   `compose:"" bundle:""`
	CPUSet          string              `compose:"cpuset" bundle:""`
	CPUShares       int64               `compose:"cpu_shares" bundle:""`
	CPUQuota        int64               `compose:"cpu_quota" bundle:""`
	CPULimit        int64               `compose:"" bundle:""`
	CPUReservation  int64               `compose:"" bundle:""`
	CapAdd          []string            `compose:"cap_add" bundle:""`
	CapDrop         []string            `compose:"cap_drop" bundle:""`
	Expose          []string            `compose:"expose" bundle:""`
	Pid             string              `compose:"pid" bundle:""`
	Privileged      bool                `compose:"privileged" bundle:""`
	Restart         string              `compose:"restart" bundle:""`
	User            string              `compose:"user" bundle:"User"`
	VolumesFrom     []string            `compose:"volumes_from" bundle:""`
	ServiceType     string              `compose:"kompose.service.type" bundle:""`
	StopGracePeriod string              `compose:"stop_grace_period" bundle:""`
	Build           string              `compose:"build" bundle:""`
	BuildArgs       map[string]*string  `compose:"build-args" bundle:""`
	ExposeService   string              `compose:"kompose.service.expose" bundle:""`
	Stdin           bool                `compose:"stdin_open" bundle:""`
	Tty             bool                `compose:"tty" bundle:""`
	MemLimit        yaml.MemStringorInt `compose:"mem_limit" bundle:""`
	MemReservation  yaml.MemStringorInt `compose:"" bundle:""`
	TmpFs           []string            `compose:"tmpfs" bundle:""`
	Dockerfile      string              `compose:"dockerfile" bundle:""`
	Replicas        int                 `compose:"replicas" bundle:""`
	// Volumes is a struct which contains all information about each volume
	Volumes []Volumes `compose:"" bundle:""`
}

// EnvVar holds the environment variable struct of a container
type EnvVar struct {
	Name  string
	Value string
}

// Ports holds the ports struct of a container
type Ports struct {
	HostPort      int32
	ContainerPort int32
	HostIP        string
	Protocol      api.Protocol
}

// Volumes holds the volume struct of container
type Volumes struct {
	SvcName    string // Service name to which volume is linked
	MountPath  string // Mountpath extracted from docker-compose file
	VFrom      string // denotes service name from which volume is coming
	VolumeName string // name of volume if provided explicitly
	Host       string // host machine address
	Container  string // Mountpath
	Mode       string // access mode for volume
	PVCName    string // name of PVC
}
<commit_msg>Remove bundle references in kobject<commit_after>/*
Copyright 2017 The Kubernetes Authors All rights reserved.

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 kobject

import (
	"github.com/docker/libcompose/yaml"
	"k8s.io/kubernetes/pkg/api"
)

// KomposeObject holds the generic struct of Kompose transformation
type KomposeObject struct {
	ServiceConfigs map[string]ServiceConfig
	// LoadedFrom is name of the loader that created KomposeObject
	// Transformer need to know origin format in order to tell user what tag is not supported in origin format
	// as they can have different names. For example environment variables  are called environment in compose but Env in bundle.
	LoadedFrom string
}

// ConvertOptions holds all options that controls transformation process
type ConvertOptions struct {
	ToStdout                    bool
	CreateD                     bool
	CreateRC                    bool
	CreateDS                    bool
	CreateDeploymentConfig      bool
	BuildRepo                   string
	BuildBranch                 string
	Build                       string
	CreateChart                 bool
	GenerateYaml                bool
	GenerateJSON                bool
	EmptyVols                   bool
	InsecureRepository          bool
	Replicas                    int
	InputFiles                  []string
	OutFile                     string
	Provider                    string
	Namespace                   string
	IsDeploymentFlag            bool
	IsDaemonSetFlag             bool
	IsReplicationControllerFlag bool
	IsReplicaSetFlag            bool
	IsDeploymentConfigFlag      bool
	IsNamespaceFlag             bool
}

// ServiceConfig holds the basic struct of a container
type ServiceConfig struct {
	// use tags to mark from what element this value comes
	ContainerName string
	Image         string   `compose:"image"`
	Environment   []EnvVar `compose:"environment"`
	Port          []Ports  `compose:"ports"`
	Command       []string `compose:"command"`
	WorkingDir    string   `compose:""`
	Args          []string `compose:"args"`
	// VolList is list of volumes extracted from docker-compose file
	VolList         []string            `compose:"volumes"`
	Network         []string            `compose:"network"`
	Labels          map[string]string   `compose:"labels"`
	Annotations     map[string]string   `compose:""`
	CPUSet          string              `compose:"cpuset"`
	CPUShares       int64               `compose:"cpu_shares"`
	CPUQuota        int64               `compose:"cpu_quota"`
	CPULimit        int64               `compose:""`
	CPUReservation  int64               `compose:""`
	CapAdd          []string            `compose:"cap_add"`
	CapDrop         []string            `compose:"cap_drop"`
	Expose          []string            `compose:"expose"`
	Pid             string              `compose:"pid"`
	Privileged      bool                `compose:"privileged"`
	Restart         string              `compose:"restart"`
	User            string              `compose:"user"`
	VolumesFrom     []string            `compose:"volumes_from"`
	ServiceType     string              `compose:"kompose.service.type"`
	StopGracePeriod string              `compose:"stop_grace_period"`
	Build           string              `compose:"build"`
	BuildArgs       map[string]*string  `compose:"build-args"`
	ExposeService   string              `compose:"kompose.service.expose"`
	Stdin           bool                `compose:"stdin_open"`
	Tty             bool                `compose:"tty"`
	MemLimit        yaml.MemStringorInt `compose:"mem_limit"`
	MemReservation  yaml.MemStringorInt `compose:""`
	TmpFs           []string            `compose:"tmpfs"`
	Dockerfile      string              `compose:"dockerfile"`
	Replicas        int                 `compose:"replicas"`
	// Volumes is a struct which contains all information about each volume
	Volumes []Volumes `compose:""`
}

// EnvVar holds the environment variable struct of a container
type EnvVar struct {
	Name  string
	Value string
}

// Ports holds the ports struct of a container
type Ports struct {
	HostPort      int32
	ContainerPort int32
	HostIP        string
	Protocol      api.Protocol
}

// Volumes holds the volume struct of container
type Volumes struct {
	SvcName    string // Service name to which volume is linked
	MountPath  string // Mountpath extracted from docker-compose file
	VFrom      string // denotes service name from which volume is coming
	VolumeName string // name of volume if provided explicitly
	Host       string // host machine address
	Container  string // Mountpath
	Mode       string // access mode for volume
	PVCName    string // name of PVC
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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 cmd

import (
	"io"

	"github.com/spf13/cobra"
)

func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "log <pod> [<container>]",
		Short: "Print the logs for a container in a pod.",
		Long:  "Print the logs for a container in a pod. If the pod has only one container, the container name is optional.",
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) == 0 {
				usageError(cmd, "<pod> is required for log")
			}

			if len(args) > 2 {
				usageError(cmd, "log <pod> [<container>]")
			}

			namespace := GetKubeNamespace(cmd)
			client, err := f.ClientBuilder.Client()
			checkErr(err)

			podID := args[0]

			pod, err := client.Pods(namespace).Get(podID)
			checkErr(err)

			var container string
			if len(args) == 1 {
				if len(pod.Spec.Containers) != 1 {
					usageError(cmd, "<container> is required for pods with multiple containers")
				}

				// Get logs for the only container in the pod
				container = pod.Spec.Containers[0].Name
			} else {
				container = args[1]
			}

			data, err := client.RESTClient.Get().
				Path("proxy/minions").
				Path(pod.Status.Host).
				Path("containerLogs").
				Path(namespace).
				Path(podID).
				Path(container).
				Do().
				Raw()
			checkErr(err)
			out.Write(data)

		},
	}
	return cmd
}
<commit_msg>Streaming container logs<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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 cmd

import (
	"io"
	"strconv"

	"github.com/spf13/cobra"
)

func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "log [-f] <pod> [<container>]",
		Short: "Print the logs for a container in a pod.",
		Long: `Print the logs for a container in a pod. If the pod has only one container, the container name is optional
Examples:
  $ kubectl log 123456-7890 ruby-container
  <returns snapshot of ruby-container logs from pod 123456-7890>

  $ kubectl log -f 123456-7890 ruby-container
  <starts streaming of ruby-container logs from pod 123456-7890>`,
		Run: func(cmd *cobra.Command, args []string) {
			if len(args) == 0 {
				usageError(cmd, "<pod> is required for log")
			}

			if len(args) > 2 {
				usageError(cmd, "log <pod> [<container>]")
			}

			namespace := GetKubeNamespace(cmd)
			client, err := f.ClientBuilder.Client()
			checkErr(err)

			podID := args[0]

			pod, err := client.Pods(namespace).Get(podID)
			checkErr(err)

			var container string
			if len(args) == 1 {
				if len(pod.Spec.Containers) != 1 {
					usageError(cmd, "<container> is required for pods with multiple containers")
				}

				// Get logs for the only container in the pod
				container = pod.Spec.Containers[0].Name
			} else {
				container = args[1]
			}

			follow := false
			if GetFlagBool(cmd, "follow") {
				follow = true
			}

			readCloser, err := client.RESTClient.Get().
				Path("proxy/minions").
				Path(pod.Status.Host).
				Path("containerLogs").
				Path(namespace).
				Path(podID).
				Path(container).
				Param("follow", strconv.FormatBool(follow)).
				Stream()
			checkErr(err)

			defer readCloser.Close()
			_, err = io.Copy(out, readCloser)
			checkErr(err)
		},
	}
	cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.")
	return cmd
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 kubectl

import (
	"fmt"
	"io"
	"sort"
	"strconv"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	"k8s.io/kubernetes/pkg/runtime"
	deploymentutil "k8s.io/kubernetes/pkg/util/deployment"
	"k8s.io/kubernetes/pkg/util/errors"
)

const (
	ChangeCauseAnnotation = "kubernetes.io/change-cause"
)

// HistoryViewer provides an interface for resources that can be rolled back.
type HistoryViewer interface {
	History(namespace, name string) (HistoryInfo, error)
}

func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) {
	switch kind {
	case extensions.Kind("Deployment"):
		return &DeploymentHistoryViewer{c}, nil
	}
	return nil, fmt.Errorf("no history viewer has been implemented for %q", kind)
}

// HistoryInfo stores the mapping from revision to podTemplate;
// note that change-cause annotation should be copied to podTemplate
type HistoryInfo struct {
	RevisionToTemplate map[int64]*api.PodTemplateSpec
}

type DeploymentHistoryViewer struct {
	c clientset.Interface
}

// History returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, error) {
	historyInfo := HistoryInfo{
		RevisionToTemplate: make(map[int64]*api.PodTemplateSpec),
	}
	deployment, err := h.c.Extensions().Deployments(namespace).Get(name)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
	}
	_, allOldRSs, err := deploymentutil.GetOldReplicaSets(deployment, h.c)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve old replica sets from deployment %s: %v", name, err)
	}
	newRS, err := deploymentutil.GetNewReplicaSet(deployment, h.c)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve new replica set from deployment %s: %v", name, err)
	}
	allRSs := append(allOldRSs, newRS)
	for _, rs := range allRSs {
		v, err := deploymentutil.Revision(rs)
		if err != nil {
			continue
		}
		historyInfo.RevisionToTemplate[v] = &rs.Spec.Template
		changeCause := getChangeCause(rs)
		if historyInfo.RevisionToTemplate[v].Annotations == nil {
			historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string)
		}
		if len(changeCause) > 0 {
			historyInfo.RevisionToTemplate[v].Annotations[ChangeCauseAnnotation] = changeCause
		}
	}
	return historyInfo, nil
}

// PrintRolloutHistory prints a formatted table of the input revision history of the deployment
func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string, error) {
	if len(historyInfo.RevisionToTemplate) == 0 {
		return fmt.Sprintf("No rollout history found in %s %q", resource, name), nil
	}
	// Sort the revisionToChangeCause map by revision
	var revisions []string
	for k := range historyInfo.RevisionToTemplate {
		revisions = append(revisions, strconv.FormatInt(k, 10))
	}
	sort.Strings(revisions)

	return tabbedString(func(out io.Writer) error {
		fmt.Fprintf(out, "%s %q:\n", resource, name)
		fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n")
		errs := []error{}
		for _, r := range revisions {
			// Find the change-cause of revision r
			r64, err := strconv.ParseInt(r, 10, 64)
			if err != nil {
				errs = append(errs, err)
				continue
			}
			changeCause := historyInfo.RevisionToTemplate[r64].Annotations[ChangeCauseAnnotation]
			if len(changeCause) == 0 {
				changeCause = "<none>"
			}
			fmt.Fprintf(out, "%s\t%s\n", r, changeCause)
		}
		return errors.NewAggregate(errs)
	})
}

// getChangeCause returns the change-cause annotation of the input object
func getChangeCause(obj runtime.Object) string {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return ""
	}
	return accessor.GetAnnotations()[ChangeCauseAnnotation]
}
<commit_msg>fix rollout nil panice issue<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 kubectl

import (
	"fmt"
	"io"
	"sort"
	"strconv"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apis/extensions"
	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
	"k8s.io/kubernetes/pkg/runtime"
	deploymentutil "k8s.io/kubernetes/pkg/util/deployment"
	"k8s.io/kubernetes/pkg/util/errors"
)

const (
	ChangeCauseAnnotation = "kubernetes.io/change-cause"
)

// HistoryViewer provides an interface for resources that can be rolled back.
type HistoryViewer interface {
	History(namespace, name string) (HistoryInfo, error)
}

func HistoryViewerFor(kind unversioned.GroupKind, c clientset.Interface) (HistoryViewer, error) {
	switch kind {
	case extensions.Kind("Deployment"):
		return &DeploymentHistoryViewer{c}, nil
	}
	return nil, fmt.Errorf("no history viewer has been implemented for %q", kind)
}

// HistoryInfo stores the mapping from revision to podTemplate;
// note that change-cause annotation should be copied to podTemplate
type HistoryInfo struct {
	RevisionToTemplate map[int64]*api.PodTemplateSpec
}

type DeploymentHistoryViewer struct {
	c clientset.Interface
}

// History returns a revision-to-replicaset map as the revision history of a deployment
func (h *DeploymentHistoryViewer) History(namespace, name string) (HistoryInfo, error) {
	historyInfo := HistoryInfo{
		RevisionToTemplate: make(map[int64]*api.PodTemplateSpec),
	}
	deployment, err := h.c.Extensions().Deployments(namespace).Get(name)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve deployment %s: %v", name, err)
	}
	_, allOldRSs, err := deploymentutil.GetOldReplicaSets(deployment, h.c)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve old replica sets from deployment %s: %v", name, err)
	}
	newRS, err := deploymentutil.GetNewReplicaSet(deployment, h.c)
	if err != nil {
		return historyInfo, fmt.Errorf("failed to retrieve new replica set from deployment %s: %v", name, err)
	}
	allRSs := allOldRSs
	if newRS != nil {
		allRSs = append(allRSs, newRS)
	}
	for _, rs := range allRSs {
		v, err := deploymentutil.Revision(rs)
		if err != nil {
			continue
		}
		historyInfo.RevisionToTemplate[v] = &rs.Spec.Template
		changeCause := getChangeCause(rs)
		if historyInfo.RevisionToTemplate[v].Annotations == nil {
			historyInfo.RevisionToTemplate[v].Annotations = make(map[string]string)
		}
		if len(changeCause) > 0 {
			historyInfo.RevisionToTemplate[v].Annotations[ChangeCauseAnnotation] = changeCause
		}
	}
	return historyInfo, nil
}

// PrintRolloutHistory prints a formatted table of the input revision history of the deployment
func PrintRolloutHistory(historyInfo HistoryInfo, resource, name string) (string, error) {
	if len(historyInfo.RevisionToTemplate) == 0 {
		return fmt.Sprintf("No rollout history found in %s %q", resource, name), nil
	}
	// Sort the revisionToChangeCause map by revision
	var revisions []string
	for k := range historyInfo.RevisionToTemplate {
		revisions = append(revisions, strconv.FormatInt(k, 10))
	}
	sort.Strings(revisions)

	return tabbedString(func(out io.Writer) error {
		fmt.Fprintf(out, "%s %q:\n", resource, name)
		fmt.Fprintf(out, "REVISION\tCHANGE-CAUSE\n")
		errs := []error{}
		for _, r := range revisions {
			// Find the change-cause of revision r
			r64, err := strconv.ParseInt(r, 10, 64)
			if err != nil {
				errs = append(errs, err)
				continue
			}
			changeCause := historyInfo.RevisionToTemplate[r64].Annotations[ChangeCauseAnnotation]
			if len(changeCause) == 0 {
				changeCause = "<none>"
			}
			fmt.Fprintf(out, "%s\t%s\n", r, changeCause)
		}
		return errors.NewAggregate(errs)
	})
}

// getChangeCause returns the change-cause annotation of the input object
func getChangeCause(obj runtime.Object) string {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return ""
	}
	return accessor.GetAnnotations()[ChangeCauseAnnotation]
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 logging

import (
	"bufio"
	"bytes"
	"fmt"
	"log/syslog"
	"os"
	"regexp"
	"sync/atomic"
	"time"

	"github.com/sirupsen/logrus"
	logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)

const (
	SLevel = "syslog.level"

	Syslog = "syslog"
)

var (
	// DefaultLogger is the base logrus logger. It is different from the logrus
	// default to avoid external dependencies from writing out unexpectedly
	DefaultLogger = InitializeDefaultLogger()

	// DefaultLogLevel is the alternative we provide to Debug
	DefaultLogLevel = logrus.InfoLevel

	// syslogOpts is the set of supported options for syslog configuration.
	syslogOpts = map[string]bool{
		"syslog.level": true,
	}

	// syslogLevelMap maps logrus.Level values to syslog.Priority levels.
	syslogLevelMap = map[logrus.Level]syslog.Priority{
		logrus.PanicLevel: syslog.LOG_ALERT,
		logrus.FatalLevel: syslog.LOG_CRIT,
		logrus.ErrorLevel: syslog.LOG_ERR,
		logrus.WarnLevel:  syslog.LOG_WARNING,
		logrus.InfoLevel:  syslog.LOG_INFO,
		logrus.DebugLevel: syslog.LOG_DEBUG,
	}
)

// setFireLevels returns a slice of logrus.Level values higher in priority
// and including level, excluding any levels lower in priority.
func setFireLevels(level logrus.Level) []logrus.Level {
	switch level {
	case logrus.PanicLevel:
		return []logrus.Level{logrus.PanicLevel}
	case logrus.FatalLevel:
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel}
	case logrus.ErrorLevel:
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel}
	case logrus.WarnLevel:
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel}
	case logrus.InfoLevel:
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel, logrus.InfoLevel}
	case logrus.DebugLevel:
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel, logrus.InfoLevel, logrus.DebugLevel}
	default:
		logrus.Infof("logrus level %v is not supported at this time; defaulting to info level", level)
		return []logrus.Level{logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel, logrus.WarnLevel, logrus.InfoLevel}
	}
}

// InitializeDefaultLogger returns a logrus Logger with a custom text formatter.
func InitializeDefaultLogger() *logrus.Logger {
	logger := logrus.New()
	logger.Formatter = setupFormatter()
	return logger
}

// SetupLogging sets up each logging service provided in loggers and configures
// each logger with the provided logOpts.
func SetupLogging(loggers []string, logOpts map[string]string, tag string, debug bool) error {
	// Set default logger to output to stdout if no loggers are provided.
	if len(loggers) == 0 {
		// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
		logrus.SetOutput(os.Stdout)
	}

	SetLogLevel(DefaultLogLevel)
	ToggleDebugLogs(debug)

	// always suppress the default logger so libraries don't print things
	logrus.SetLevel(logrus.PanicLevel)

	// Iterate through all provided loggers and configure them according
	// to user-provided settings.
	for _, logger := range loggers {
		switch logger {
		case Syslog:
			valuesToValidate := getLogDriverConfig(Syslog, logOpts)
			err := validateOpts(Syslog, valuesToValidate, syslogOpts)
			if err != nil {
				return err
			}
			setupSyslog(valuesToValidate, tag, debug)
		default:
			return fmt.Errorf("provided log driver %q is not a supported log driver", logger)
		}
	}

	return nil
}

// SetLogLevel sets the log level on DefaultLogger. This logger is, by
// convention, the base logger for package specific ones thus setting the level
// here impacts the default logging behaviour.
// This function is thread-safe when logging, reading DefaultLogger.Level is
// not protected this way, however.
func SetLogLevel(level logrus.Level) {
	DefaultLogger.SetLevel(level)
}

// ToggleDebugLogs switches on or off debugging logs. It will select
// DefaultLogLevel when turning debug off.
// It is thread-safe.
func ToggleDebugLogs(debug bool) {
	if debug {
		SetLogLevel(logrus.DebugLevel)
	} else {
		SetLogLevel(DefaultLogLevel)
	}
}

// setupSyslog sets up and configures syslog with the provided options in
// logOpts. If some options are not provided, sensible defaults are used.
func setupSyslog(logOpts map[string]string, tag string, debug bool) {
	logLevel, ok := logOpts[SLevel]
	if !ok {
		if debug {
			logLevel = "debug"
		} else {
			logLevel = "info"
		}
	}

	//Validate provided log level.
	level, err := logrus.ParseLevel(logLevel)
	if err != nil {
		DefaultLogger.Fatal(err)
	}

	DefaultLogger.SetLevel(level)
	// Create syslog hook.
	h, err := logrus_syslog.NewSyslogHook("", "", syslogLevelMap[level], tag)
	if err != nil {
		DefaultLogger.Fatal(err)
	}
	// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
	logrus.AddHook(h)
}

// setupFormatter sets up the text formatting for logs output by logrus.
func setupFormatter() logrus.Formatter {
	fileFormat := new(logrus.TextFormatter)
	fileFormat.DisableTimestamp = true
	fileFormat.DisableColors = true
	switch os.Getenv("INITSYSTEM") {
	case "SYSTEMD":
		fileFormat.FullTimestamp = true
	default:
		fileFormat.TimestampFormat = time.RFC3339
	}

	// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
	return fileFormat
}

// validateOpts iterates through all of the keys in logOpts, and errors out if
// the key in logOpts is not a key in supportedOpts.
func validateOpts(logDriver string, logOpts map[string]string, supportedOpts map[string]bool) error {
	for k := range logOpts {
		if !supportedOpts[k] {
			return fmt.Errorf("provided configuration value %q is not supported as a logging option for log driver %s", k, logDriver)
		}
	}
	return nil
}

// getLogDriverConfig returns a map containing the key-value pairs that start
// with string logDriver from map logOpts.
func getLogDriverConfig(logDriver string, logOpts map[string]string) map[string]string {
	keysToValidate := make(map[string]string)
	for k, v := range logOpts {
		ok, err := regexp.MatchString(logDriver+".*", k)
		if err != nil {
			DefaultLogger.Fatal(err)
		}
		if ok {
			keysToValidate[k] = v
		}
	}
	return keysToValidate
}

// MultiLine breaks a multi line text into individual log entries and calls the
// logging function to log each entry
func MultiLine(logFn func(args ...interface{}), output string) {
	scanner := bufio.NewScanner(bytes.NewReader([]byte(output)))
	for scanner.Scan() {
		logFn(scanner.Text())
	}
}

// CanLogAt returns whether a log message at the given level would be
// logged by the given logger.
func CanLogAt(logger *logrus.Logger, level logrus.Level) bool {
	return GetLevel(logger) >= level
}

// GetLevel returns the log level of the given logger.
func GetLevel(logger *logrus.Logger) logrus.Level {
	return logrus.Level(atomic.LoadUint32((*uint32)(&logger.Level)))
}
<commit_msg>logging: Remove unused setFireLevels() function<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 logging

import (
	"bufio"
	"bytes"
	"fmt"
	"log/syslog"
	"os"
	"regexp"
	"sync/atomic"
	"time"

	"github.com/sirupsen/logrus"
	logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
)

const (
	SLevel = "syslog.level"

	Syslog = "syslog"
)

var (
	// DefaultLogger is the base logrus logger. It is different from the logrus
	// default to avoid external dependencies from writing out unexpectedly
	DefaultLogger = InitializeDefaultLogger()

	// DefaultLogLevel is the alternative we provide to Debug
	DefaultLogLevel = logrus.InfoLevel

	// syslogOpts is the set of supported options for syslog configuration.
	syslogOpts = map[string]bool{
		"syslog.level": true,
	}

	// syslogLevelMap maps logrus.Level values to syslog.Priority levels.
	syslogLevelMap = map[logrus.Level]syslog.Priority{
		logrus.PanicLevel: syslog.LOG_ALERT,
		logrus.FatalLevel: syslog.LOG_CRIT,
		logrus.ErrorLevel: syslog.LOG_ERR,
		logrus.WarnLevel:  syslog.LOG_WARNING,
		logrus.InfoLevel:  syslog.LOG_INFO,
		logrus.DebugLevel: syslog.LOG_DEBUG,
	}
)

// InitializeDefaultLogger returns a logrus Logger with a custom text formatter.
func InitializeDefaultLogger() *logrus.Logger {
	logger := logrus.New()
	logger.Formatter = setupFormatter()
	return logger
}

// SetupLogging sets up each logging service provided in loggers and configures
// each logger with the provided logOpts.
func SetupLogging(loggers []string, logOpts map[string]string, tag string, debug bool) error {
	// Set default logger to output to stdout if no loggers are provided.
	if len(loggers) == 0 {
		// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
		logrus.SetOutput(os.Stdout)
	}

	SetLogLevel(DefaultLogLevel)
	ToggleDebugLogs(debug)

	// always suppress the default logger so libraries don't print things
	logrus.SetLevel(logrus.PanicLevel)

	// Iterate through all provided loggers and configure them according
	// to user-provided settings.
	for _, logger := range loggers {
		switch logger {
		case Syslog:
			valuesToValidate := getLogDriverConfig(Syslog, logOpts)
			err := validateOpts(Syslog, valuesToValidate, syslogOpts)
			if err != nil {
				return err
			}
			setupSyslog(valuesToValidate, tag, debug)
		default:
			return fmt.Errorf("provided log driver %q is not a supported log driver", logger)
		}
	}

	return nil
}

// SetLogLevel sets the log level on DefaultLogger. This logger is, by
// convention, the base logger for package specific ones thus setting the level
// here impacts the default logging behaviour.
// This function is thread-safe when logging, reading DefaultLogger.Level is
// not protected this way, however.
func SetLogLevel(level logrus.Level) {
	DefaultLogger.SetLevel(level)
}

// ToggleDebugLogs switches on or off debugging logs. It will select
// DefaultLogLevel when turning debug off.
// It is thread-safe.
func ToggleDebugLogs(debug bool) {
	if debug {
		SetLogLevel(logrus.DebugLevel)
	} else {
		SetLogLevel(DefaultLogLevel)
	}
}

// setupSyslog sets up and configures syslog with the provided options in
// logOpts. If some options are not provided, sensible defaults are used.
func setupSyslog(logOpts map[string]string, tag string, debug bool) {
	logLevel, ok := logOpts[SLevel]
	if !ok {
		if debug {
			logLevel = "debug"
		} else {
			logLevel = "info"
		}
	}

	//Validate provided log level.
	level, err := logrus.ParseLevel(logLevel)
	if err != nil {
		DefaultLogger.Fatal(err)
	}

	DefaultLogger.SetLevel(level)
	// Create syslog hook.
	h, err := logrus_syslog.NewSyslogHook("", "", syslogLevelMap[level], tag)
	if err != nil {
		DefaultLogger.Fatal(err)
	}
	// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
	logrus.AddHook(h)
}

// setupFormatter sets up the text formatting for logs output by logrus.
func setupFormatter() logrus.Formatter {
	fileFormat := new(logrus.TextFormatter)
	fileFormat.DisableTimestamp = true
	fileFormat.DisableColors = true
	switch os.Getenv("INITSYSTEM") {
	case "SYSTEMD":
		fileFormat.FullTimestamp = true
	default:
		fileFormat.TimestampFormat = time.RFC3339
	}

	// TODO: switch to a per-logger version when we upgrade to logrus >1.0.3
	return fileFormat
}

// validateOpts iterates through all of the keys in logOpts, and errors out if
// the key in logOpts is not a key in supportedOpts.
func validateOpts(logDriver string, logOpts map[string]string, supportedOpts map[string]bool) error {
	for k := range logOpts {
		if !supportedOpts[k] {
			return fmt.Errorf("provided configuration value %q is not supported as a logging option for log driver %s", k, logDriver)
		}
	}
	return nil
}

// getLogDriverConfig returns a map containing the key-value pairs that start
// with string logDriver from map logOpts.
func getLogDriverConfig(logDriver string, logOpts map[string]string) map[string]string {
	keysToValidate := make(map[string]string)
	for k, v := range logOpts {
		ok, err := regexp.MatchString(logDriver+".*", k)
		if err != nil {
			DefaultLogger.Fatal(err)
		}
		if ok {
			keysToValidate[k] = v
		}
	}
	return keysToValidate
}

// MultiLine breaks a multi line text into individual log entries and calls the
// logging function to log each entry
func MultiLine(logFn func(args ...interface{}), output string) {
	scanner := bufio.NewScanner(bytes.NewReader([]byte(output)))
	for scanner.Scan() {
		logFn(scanner.Text())
	}
}

// CanLogAt returns whether a log message at the given level would be
// logged by the given logger.
func CanLogAt(logger *logrus.Logger, level logrus.Level) bool {
	return GetLevel(logger) >= level
}

// GetLevel returns the log level of the given logger.
func GetLevel(logger *logrus.Logger) logrus.Level {
	return logrus.Level(atomic.LoadUint32((*uint32)(&logger.Level)))
}
<|endoftext|>


<commit_before>// +build go1.13

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
	"net"

	"github.com/pkg/errors"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 11
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "dev"
	// DevelopmentVersion indicates whether or not this a development version of
	// Mutagen. If this is set to true, then Mutagen's data directory will be
	// changed to ~/.mutagen-dev. This setting should be considered independent
	// of whether or not VersionTag is empty.
	DevelopmentVersion = true
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// sendVersion writes the current version to the specified writer. Version tag
// components are neither transmitted nor received.
func sendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// receiveVersion reads version information from the specified reader. Version
// tag components are neither transmitted nor received.
func receiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ClientVersionHandshake performs the client side of a version handshake,
// returning an error if the received server version is not compatible with the
// client version.
//
// TODO: Add some ability to support version skew in this function.
func ClientVersionHandshake(connection net.Conn) error {
	// Receive the server's version.
	serverMajor, serverMinor, serverPatch, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive server version")
	}

	// Send our version to the server.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send client version")
	}

	// Ensure that our Mutagen versions are compatible. For now, we enforce that
	// they're equal.
	// TODO: Once we lock-in an internal protocol that we're going to support
	// for some time, we can allow some version skew. On the client side in
	// particular, we'll probably want to look out for the specific "locked-in"
	// server protocol that we support and instantiate some frozen client
	// implementation from that version.
	versionMatch := serverMajor == VersionMajor &&
		serverMinor == VersionMinor &&
		serverPatch == VersionPatch
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}

// ServerVersionHandshake performs the server side of a version handshake,
// returning an error if the received client version is not compatible with the
// server version.
//
// TODO: Add some ability to support version skew in this function.
func ServerVersionHandshake(connection net.Conn) error {
	// Send our version to the client.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send server version")
	}

	// Receive the client's version.
	clientMajor, clientMinor, clientPatch, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive client version")
	}

	// Ensure that our versions are compatible. For now, we enforce that they're
	// equal.
	versionMatch := clientMajor == VersionMajor &&
		clientMinor == VersionMinor &&
		clientPatch == VersionPatch
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}
<commit_msg>Modified version handshake to only require match to minor version.<commit_after>// +build go1.13

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
	"net"

	"github.com/pkg/errors"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 11
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "dev"
	// DevelopmentVersion indicates whether or not this a development version of
	// Mutagen. If this is set to true, then Mutagen's data directory will be
	// changed to ~/.mutagen-dev. This setting should be considered independent
	// of whether or not VersionTag is empty.
	DevelopmentVersion = true
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// sendVersion writes the current version to the specified writer. Version tag
// components are neither transmitted nor received.
func sendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// receiveVersion reads version information from the specified reader. Version
// tag components are neither transmitted nor received.
func receiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ClientVersionHandshake performs the client side of a version handshake,
// returning an error if the received server version is not compatible with the
// client version.
//
// TODO: Add some ability to support version skew in this function.
func ClientVersionHandshake(connection net.Conn) error {
	// Receive the server's version.
	serverMajor, serverMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive server version")
	}

	// Send our version to the server.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send client version")
	}

	// Ensure that our Mutagen versions are compatible. For now, we enforce that
	// they're equal at the minor release level.
	// TODO: Once we lock-in an internal protocol that we're going to support
	// for some time, we can allow some version skew. On the client side in
	// particular, we'll probably want to look out for the specific "locked-in"
	// server protocol that we support and instantiate some frozen client
	// implementation from that version.
	versionMatch := serverMajor == VersionMajor && serverMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}

// ServerVersionHandshake performs the server side of a version handshake,
// returning an error if the received client version is not compatible with the
// server version.
//
// TODO: Add some ability to support version skew in this function.
func ServerVersionHandshake(connection net.Conn) error {
	// Send our version to the client.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send server version")
	}

	// Receive the client's version.
	clientMajor, clientMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive client version")
	}

	// Ensure that our versions are compatible. For now, we enforce that they're
	// equal at the minor release level.
	versionMatch := clientMajor == VersionMajor && clientMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}
<|endoftext|>


<commit_before>// +build go1.11

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 7
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "dev"
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// SendVersion writes the current Mutagen version to the specified writer.
func SendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// ReceiveVersion reads version information from the specified reader.
func ReceiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ReceiveAndCompareVersion reads version information from the specified reader
// and ensures that it matches the current Mutagen version.
func ReceiveAndCompareVersion(reader io.Reader) (bool, error) {
	// Receive the version.
	major, minor, patch, err := ReceiveVersion(reader)
	if err != nil {
		return false, err
	}

	// Compare the version.
	return major == VersionMajor &&
		minor == VersionMinor &&
		patch == VersionPatch, nil
}
<commit_msg>Bumped version to v0.7.0-beta1.<commit_after>// +build go1.11

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 7
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "beta1"
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// SendVersion writes the current Mutagen version to the specified writer.
func SendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// ReceiveVersion reads version information from the specified reader.
func ReceiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ReceiveAndCompareVersion reads version information from the specified reader
// and ensures that it matches the current Mutagen version.
func ReceiveAndCompareVersion(reader io.Reader) (bool, error) {
	// Receive the version.
	major, minor, patch, err := ReceiveVersion(reader)
	if err != nil {
		return false, err
	}

	// Compare the version.
	return major == VersionMajor &&
		minor == VersionMinor &&
		patch == VersionPatch, nil
}
<|endoftext|>


<commit_before>package store

import (
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	zk "github.com/samuel/go-zookeeper/zk"
)

const defaultTimeout = 10 * time.Second

// Zookeeper embeds the zookeeper client
type Zookeeper struct {
	timeout time.Duration
	client  *zk.Conn
}

type zookeeperLock struct {
	client *zk.Conn
	lock   *zk.Lock
	key    string
	value  []byte
}

// InitializeZookeeper creates a new Zookeeper client
// given a list of endpoints and optional tls config
func InitializeZookeeper(endpoints []string, options *Config) (Store, error) {
	s := &Zookeeper{}
	s.timeout = defaultTimeout

	// Set options
	if options != nil {
		if options.ConnectionTimeout != 0 {
			s.setTimeout(options.ConnectionTimeout)
		}
	}

	conn, _, err := zk.Connect(endpoints, s.timeout)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	s.client = conn
	return s, nil
}

// SetTimeout sets the timout for connecting to Zookeeper
func (s *Zookeeper) setTimeout(time time.Duration) {
	s.timeout = time
}

// Get the value at "key", returns the last modified index
// to use in conjunction to CAS calls
func (s *Zookeeper) Get(key string) (*KVPair, error) {
	resp, meta, err := s.client.Get(normalize(key))
	if err != nil {
		return nil, err
	}
	if resp == nil {
		return nil, ErrKeyNotFound
	}
	return &KVPair{key, resp, uint64(meta.Mzxid)}, nil
}

// Create the entire path for a directory that does not exist
func (s *Zookeeper) createFullpath(path []string, ephemeral bool) error {
	for i := 1; i <= len(path); i++ {
		newpath := "/" + strings.Join(path[:i], "/")
		if i == len(path) && ephemeral {
			_, err := s.client.Create(newpath, []byte{1}, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
			return err
		}
		_, err := s.client.Create(newpath, []byte{1}, 0, zk.WorldACL(zk.PermAll))
		if err != nil {
			// Skip if node already exists
			if err != zk.ErrNodeExists {
				return err
			}
		}
	}
	return nil
}

// Put a value at "key"
func (s *Zookeeper) Put(key string, value []byte, opts *WriteOptions) error {
	fkey := normalize(key)
	exists, err := s.Exists(key)
	if err != nil {
		return err
	}
	if !exists {
		if opts != nil && opts.Ephemeral {
			s.createFullpath(splitKey(key), opts.Ephemeral)
		} else {
			s.createFullpath(splitKey(key), false)
		}
	}
	_, err = s.client.Set(fkey, value, -1)
	return err
}

// Delete a value at "key"
func (s *Zookeeper) Delete(key string) error {
	err := s.client.Delete(normalize(key), -1)
	return err
}

// Exists checks if the key exists inside the store
func (s *Zookeeper) Exists(key string) (bool, error) {
	exists, _, err := s.client.Exists(normalize(key))
	if err != nil {
		return false, err
	}
	return exists, nil
}

// Watch changes on a key.
// Returns a channel that will receive changes or an error.
// Upon creating a watch, the current value will be sent to the channel.
// Providing a non-nil stopCh can be used to stop watching.
func (s *Zookeeper) Watch(key string, stopCh <-chan struct{}) (<-chan *KVPair, error) {
	fkey := normalize(key)
	resp, meta, eventCh, err := s.client.GetW(fkey)
	if err != nil {
		return nil, err
	}

	// Catch zk notifications and fire changes into the channel.
	watchCh := make(chan *KVPair)
	go func() {
		defer close(watchCh)

		// GetW returns the current value before setting the watch.
		watchCh <- &KVPair{key, resp, uint64(meta.Mzxid)}
		for {
			select {
			case e := <-eventCh:
				if e.Type == zk.EventNodeDataChanged {
					if entry, err := s.Get(key); err == nil {
						watchCh <- entry
					}
				}
			case <-stopCh:
				// There is no way to stop GetW so just quit
				return
			}
		}
	}()

	return watchCh, nil
}

// WatchTree watches changes on a "directory"
// Returns a channel that will receive changes or an error.
// Upon creating a watch, the current value will be sent to the channel.
// Providing a non-nil stopCh can be used to stop watching.
func (s *Zookeeper) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*KVPair, error) {
	fprefix := normalize(prefix)
	entries, stat, eventCh, err := s.client.ChildrenW(fprefix)
	if err != nil {
		return nil, err
	}

	// Catch zk notifications and fire changes into the channel.
	watchCh := make(chan []*KVPair)
	go func() {
		defer close(watchCh)

		// GetW returns the current value before setting the watch.
		kv := []*KVPair{}
		for _, item := range entries {
			kv = append(kv, &KVPair{prefix, []byte(item), uint64(stat.Mzxid)})
		}
		watchCh <- kv

		for {
			select {
			case e := <-eventCh:
				if e.Type == zk.EventNodeChildrenChanged {
					if kv, err := s.List(prefix); err == nil {
						watchCh <- kv
					}
				}
			case <-stopCh:
				// There is no way to stop GetW so just quit
				return
			}
		}
	}()

	return watchCh, nil
}

// List the content of a given prefix
func (s *Zookeeper) List(prefix string) ([]*KVPair, error) {
	prefix = normalize(prefix)
	entries, stat, err := s.client.Children(prefix)
	if err != nil {
		return nil, err
	}
	kv := []*KVPair{}
	for _, item := range entries {
		kv = append(kv, &KVPair{prefix, []byte(item), uint64(stat.Mzxid)})
	}
	return kv, err
}

// DeleteTree deletes a range of keys based on prefix
func (s *Zookeeper) DeleteTree(prefix string) error {
	err := s.client.Delete(normalize(prefix), -1)
	return err
}

// AtomicPut put a value at "key" if the key has not been
// modified in the meantime, throws an error if this is the case
func (s *Zookeeper) AtomicPut(key string, value []byte, previous *KVPair, options *WriteOptions) (bool, error) {
	// Use index of Set method to implement CAS
	return false, ErrNotImplemented
}

// AtomicDelete deletes a value at "key" if the key has not
// been modified in the meantime, throws an error if this is the case
func (s *Zookeeper) AtomicDelete(key string, previous *KVPair) (bool, error) {
	return false, ErrNotImplemented
}

// NewLock returns a handle to a lock struct which can be used to acquire and
// release the mutex.
func (s *Zookeeper) NewLock(key string, options *LockOptions) (Locker, error) {
	value := []byte("")

	// Apply options
	if options != nil {
		if options.Value != nil {
			value = options.Value
		}
	}

	return &zookeeperLock{
		client: s.client,
		key:    normalize(key),
		value:  value,
		lock:   zk.NewLock(s.client, normalize(key), zk.WorldACL(zk.PermAll)),
	}, nil
}

// Lock attempts to acquire the lock and blocks while doing so.
// Returns a channel that is closed if our lock is lost or an error.
func (l *zookeeperLock) Lock() (<-chan struct{}, error) {
	err := l.lock.Lock()

	if err == nil {
		// We hold the lock, we can set our value
		// FIXME: When the last leader leaves the election, this value will be left behind
		_, err = l.client.Set(l.key, l.value, -1)
	}

	return nil, err
}

// Unlock released the lock. It is an error to call this
// if the lock is not currently held.
func (l *zookeeperLock) Unlock() error {
	return l.lock.Unlock()
}
<commit_msg>Fix zookeeper watch that was triggered only once<commit_after>package store

import (
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	zk "github.com/samuel/go-zookeeper/zk"
)

const defaultTimeout = 10 * time.Second

// Zookeeper embeds the zookeeper client
type Zookeeper struct {
	timeout time.Duration
	client  *zk.Conn
}

type zookeeperLock struct {
	client *zk.Conn
	lock   *zk.Lock
	key    string
	value  []byte
}

// InitializeZookeeper creates a new Zookeeper client
// given a list of endpoints and optional tls config
func InitializeZookeeper(endpoints []string, options *Config) (Store, error) {
	s := &Zookeeper{}
	s.timeout = defaultTimeout

	// Set options
	if options != nil {
		if options.ConnectionTimeout != 0 {
			s.setTimeout(options.ConnectionTimeout)
		}
	}

	conn, _, err := zk.Connect(endpoints, s.timeout)
	if err != nil {
		log.Error(err)
		return nil, err
	}
	s.client = conn
	return s, nil
}

// SetTimeout sets the timout for connecting to Zookeeper
func (s *Zookeeper) setTimeout(time time.Duration) {
	s.timeout = time
}

// Get the value at "key", returns the last modified index
// to use in conjunction to CAS calls
func (s *Zookeeper) Get(key string) (*KVPair, error) {
	resp, meta, err := s.client.Get(normalize(key))
	if err != nil {
		return nil, err
	}
	if resp == nil {
		return nil, ErrKeyNotFound
	}
	return &KVPair{key, resp, uint64(meta.Mzxid)}, nil
}

// Create the entire path for a directory that does not exist
func (s *Zookeeper) createFullpath(path []string, ephemeral bool) error {
	for i := 1; i <= len(path); i++ {
		newpath := "/" + strings.Join(path[:i], "/")
		if i == len(path) && ephemeral {
			_, err := s.client.Create(newpath, []byte{1}, zk.FlagEphemeral, zk.WorldACL(zk.PermAll))
			return err
		}
		_, err := s.client.Create(newpath, []byte{1}, 0, zk.WorldACL(zk.PermAll))
		if err != nil {
			// Skip if node already exists
			if err != zk.ErrNodeExists {
				return err
			}
		}
	}
	return nil
}

// Put a value at "key"
func (s *Zookeeper) Put(key string, value []byte, opts *WriteOptions) error {
	fkey := normalize(key)
	exists, err := s.Exists(key)
	if err != nil {
		return err
	}
	if !exists {
		if opts != nil && opts.Ephemeral {
			s.createFullpath(splitKey(key), opts.Ephemeral)
		} else {
			s.createFullpath(splitKey(key), false)
		}
	}
	_, err = s.client.Set(fkey, value, -1)
	return err
}

// Delete a value at "key"
func (s *Zookeeper) Delete(key string) error {
	err := s.client.Delete(normalize(key), -1)
	return err
}

// Exists checks if the key exists inside the store
func (s *Zookeeper) Exists(key string) (bool, error) {
	exists, _, err := s.client.Exists(normalize(key))
	if err != nil {
		return false, err
	}
	return exists, nil
}

// Watch changes on a key.
// Returns a channel that will receive changes or an error.
// Upon creating a watch, the current value will be sent to the channel.
// Providing a non-nil stopCh can be used to stop watching.
func (s *Zookeeper) Watch(key string, stopCh <-chan struct{}) (<-chan *KVPair, error) {
	fkey := normalize(key)
	resp, meta, eventCh, err := s.client.GetW(fkey)
	if err != nil {
		return nil, err
	}

	// Catch zk notifications and fire changes into the channel.
	watchCh := make(chan *KVPair)
	go func() {
		defer close(watchCh)

		// GetW returns the current value before setting the watch.
		watchCh <- &KVPair{key, resp, uint64(meta.Mzxid)}
		for {
			select {
			case e := <-eventCh:
				if e.Type == zk.EventNodeDataChanged {
					if entry, err := s.Get(key); err == nil {
						watchCh <- entry
					}
				}
			case <-stopCh:
				// There is no way to stop GetW so just quit
				return
			}
		}
	}()

	return watchCh, nil
}

// WatchTree watches changes on a "directory"
// Returns a channel that will receive changes or an error.
// Upon creating a watch, the current value will be sent to the channel.
// Providing a non-nil stopCh can be used to stop watching.
func (s *Zookeeper) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*KVPair, error) {
	fprefix := normalize(prefix)
	entries, err := s.List(prefix)
	if err != nil {
		return nil, err
	}

	// Catch zk notifications and fire changes into the channel.
	watchCh := make(chan []*KVPair)
	go func() {
		defer close(watchCh)

		// GetW returns the current value before setting the watch.
		watchCh <- entries

		for {
			_, _, eventCh, err := s.client.ChildrenW(fprefix)
			if err != nil {
				return
			}
			select {
			case e := <-eventCh:
				if e.Type == zk.EventNodeChildrenChanged {
					if kv, err := s.List(prefix); err == nil {
						watchCh <- kv
					}
				}
			case <-stopCh:
				// There is no way to stop GetW so just quit
				return
			}
		}
	}()

	return watchCh, nil
}

// List the content of a given prefix
func (s *Zookeeper) List(prefix string) ([]*KVPair, error) {
	prefix = normalize(prefix)
	entries, stat, err := s.client.Children(prefix)
	if err != nil {
		return nil, err
	}
	kv := []*KVPair{}
	for _, item := range entries {
		kv = append(kv, &KVPair{prefix, []byte(item), uint64(stat.Mzxid)})
	}
	return kv, err
}

// DeleteTree deletes a range of keys based on prefix
func (s *Zookeeper) DeleteTree(prefix string) error {
	err := s.client.Delete(normalize(prefix), -1)
	return err
}

// AtomicPut put a value at "key" if the key has not been
// modified in the meantime, throws an error if this is the case
func (s *Zookeeper) AtomicPut(key string, value []byte, previous *KVPair, options *WriteOptions) (bool, error) {
	// Use index of Set method to implement CAS
	return false, ErrNotImplemented
}

// AtomicDelete deletes a value at "key" if the key has not
// been modified in the meantime, throws an error if this is the case
func (s *Zookeeper) AtomicDelete(key string, previous *KVPair) (bool, error) {
	return false, ErrNotImplemented
}

// NewLock returns a handle to a lock struct which can be used to acquire and
// release the mutex.
func (s *Zookeeper) NewLock(key string, options *LockOptions) (Locker, error) {
	value := []byte("")

	// Apply options
	if options != nil {
		if options.Value != nil {
			value = options.Value
		}
	}

	return &zookeeperLock{
		client: s.client,
		key:    normalize(key),
		value:  value,
		lock:   zk.NewLock(s.client, normalize(key), zk.WorldACL(zk.PermAll)),
	}, nil
}

// Lock attempts to acquire the lock and blocks while doing so.
// Returns a channel that is closed if our lock is lost or an error.
func (l *zookeeperLock) Lock() (<-chan struct{}, error) {
	err := l.lock.Lock()

	if err == nil {
		// We hold the lock, we can set our value
		// FIXME: When the last leader leaves the election, this value will be left behind
		_, err = l.client.Set(l.key, l.value, -1)
	}

	return nil, err
}

// Unlock released the lock. It is an error to call this
// if the lock is not currently held.
func (l *zookeeperLock) Unlock() error {
	return l.lock.Unlock()
}
<|endoftext|>


<commit_before>package sysinfo

import (
	"github.com/dotcloud/docker/pkg/cgroups"
	"io/ioutil"
	"log"
	"os"
	"path"
)

type SysInfo struct {
	MemoryLimit            bool
	SwapLimit              bool
	IPv4ForwardingDisabled bool
	AppArmor               bool
}

func New(quiet bool) *SysInfo {
	sysInfo := &SysInfo{}
	if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
		if !quiet {
			log.Printf("WARNING: %s\n", err)
		}
	} else {
		_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
		sysInfo.MemoryLimit = err1 == nil && err2 == nil
		if !sysInfo.MemoryLimit && !quiet {
			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
		}

		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
		sysInfo.SwapLimit = err == nil
		if !sysInfo.SwapLimit && !quiet {
			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
		}
	}

	content, err3 := ioutil.ReadFile("/proc/sys/net/ipv4/ip_forward")
	sysInfo.IPv4ForwardingDisabled = err3 != nil || len(content) == 0 || content[0] != '1'
	if sysInfo.IPv4ForwardingDisabled && !quiet {
		log.Printf("WARNING: IPv4 forwarding is disabled.")
	}

	// Check if AppArmor seems to be enabled on this system.
	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
		sysInfo.AppArmor = false
	} else {
		sysInfo.AppArmor = true
	}
	return sysInfo
}
<commit_msg>remove ip_forward warning<commit_after>package sysinfo

import (
	"github.com/dotcloud/docker/pkg/cgroups"
	"io/ioutil"
	"log"
	"os"
	"path"
)

type SysInfo struct {
	MemoryLimit            bool
	SwapLimit              bool
	IPv4ForwardingDisabled bool
	AppArmor               bool
}

func New(quiet bool) *SysInfo {
	sysInfo := &SysInfo{}
	if cgroupMemoryMountpoint, err := cgroups.FindCgroupMountpoint("memory"); err != nil {
		if !quiet {
			log.Printf("WARNING: %s\n", err)
		}
	} else {
		_, err1 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.limit_in_bytes"))
		_, err2 := ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.soft_limit_in_bytes"))
		sysInfo.MemoryLimit = err1 == nil && err2 == nil
		if !sysInfo.MemoryLimit && !quiet {
			log.Printf("WARNING: Your kernel does not support cgroup memory limit.")
		}

		_, err = ioutil.ReadFile(path.Join(cgroupMemoryMountpoint, "memory.memsw.limit_in_bytes"))
		sysInfo.SwapLimit = err == nil
		if !sysInfo.SwapLimit && !quiet {
			log.Printf("WARNING: Your kernel does not support cgroup swap limit.")
		}
	}

	// Check if AppArmor seems to be enabled on this system.
	if _, err := os.Stat("/sys/kernel/security/apparmor"); os.IsNotExist(err) {
		sysInfo.AppArmor = false
	} else {
		sysInfo.AppArmor = true
	}
	return sysInfo
}
<|endoftext|>


<commit_before>package version

// VERSION is supplied with the git committish this is built from
var VERSION = ""

// IMPORTANT: These versions are overridden by version ldflags specifications VERSION_VARIABLES in the Makefile

// DdevVersion is the current version of ddev, by default the git committish (should be current git tag)
var DdevVersion = "v0.0.0-overridden-by-make" // Note that this is overridden by make

// DockerVersionConstraint is the current minimum version of docker required for ddev.
// See https://godoc.org/github.com/Masterminds/semver#hdr-Checking_Version_Constraints
// for examples defining version constraints.
// REMEMBER TO CHANGE docs/index.md if you touch this!
var DockerVersionConstraint = ">= 18.06.0-ce"

// DockerComposeVersionConstraint is the current minimum version of docker-compose required for ddev.
// REMEMBER TO CHANGE docs/index.md if you touch this!
var DockerComposeVersionConstraint = ">= 1.20.0"

// DockerComposeFileFormatVersion is the compose version to be used
var DockerComposeFileFormatVersion = "3.6"

// WebImg defines the default web image used for applications.
var WebImg = "drud/ddev-webserver"

// WebTag defines the default web image tag for drud dev
var WebTag = "20180929_nginx_quit_immediately" // Note that this can be overridden by make

// DBImg defines the default db image used for applications.
var DBImg = "drud/ddev-dbserver"

// DBTag defines the default db image tag for drud dev
var DBTag = "20181006_mariadb_upgrade" // Note that this may be overridden by make

// DBAImg defines the default phpmyadmin image tag used for applications.
var DBAImg = "drud/phpmyadmin"

// DBATag defines the default phpmyadmin image tag used for applications.
var DBATag = "v1.2.0" // Note that this can be overridden by make

// RouterImage defines the image used for the router.
var RouterImage = "drud/ddev-router"

// RouterTag defines the tag used for the router.
var RouterTag = "20180922_upgrade_debian_stretch" // Note that this can be overridden by make

// COMMIT is the actual committish, supplied by make
var COMMIT = "COMMIT should be overridden"

// BUILDINFO is information with date and context, supplied by make
var BUILDINFO = "BUILDINFO should have new info"

// DDevTLD defines the tld to use for DDev site URLs.
const DDevTLD = "ddev.local"

// GetVersionInfo returns a map containing the version info defined above.
func GetVersionInfo() map[string]string {
	versionInfo := make(map[string]string)

	versionInfo["cli"] = DdevVersion
	versionInfo["web"] = WebImg + ":" + WebTag
	versionInfo["db"] = DBImg + ":" + DBTag
	versionInfo["dba"] = DBAImg + ":" + DBATag
	versionInfo["router"] = RouterImage + ":" + RouterTag
	versionInfo["commit"] = COMMIT
	versionInfo["domain"] = DDevTLD
	versionInfo["build info"] = BUILDINFO

	return versionInfo
}
<commit_msg>Bump image versions for v1.3.0 release (#1171)<commit_after>package version

// VERSION is supplied with the git committish this is built from
var VERSION = ""

// IMPORTANT: These versions are overridden by version ldflags specifications VERSION_VARIABLES in the Makefile

// DdevVersion is the current version of ddev, by default the git committish (should be current git tag)
var DdevVersion = "v0.0.0-overridden-by-make" // Note that this is overridden by make

// DockerVersionConstraint is the current minimum version of docker required for ddev.
// See https://godoc.org/github.com/Masterminds/semver#hdr-Checking_Version_Constraints
// for examples defining version constraints.
// REMEMBER TO CHANGE docs/index.md if you touch this!
var DockerVersionConstraint = ">= 18.06.0-ce"

// DockerComposeVersionConstraint is the current minimum version of docker-compose required for ddev.
// REMEMBER TO CHANGE docs/index.md if you touch this!
var DockerComposeVersionConstraint = ">= 1.20.0"

// DockerComposeFileFormatVersion is the compose version to be used
var DockerComposeFileFormatVersion = "3.6"

// WebImg defines the default web image used for applications.
var WebImg = "drud/ddev-webserver"

// WebTag defines the default web image tag for drud dev
var WebTag = "v1.3.0" // Note that this can be overridden by make

// DBImg defines the default db image used for applications.
var DBImg = "drud/ddev-dbserver"

// DBTag defines the default db image tag for drud dev
var DBTag = "v1.3.0" // Note that this may be overridden by make

// DBAImg defines the default phpmyadmin image tag used for applications.
var DBAImg = "drud/phpmyadmin"

// DBATag defines the default phpmyadmin image tag used for applications.
var DBATag = "v1.3.0" // Note that this can be overridden by make

// RouterImage defines the image used for the router.
var RouterImage = "drud/ddev-router"

// RouterTag defines the tag used for the router.
var RouterTag = "v1.3.0" // Note that this can be overridden by make

// COMMIT is the actual committish, supplied by make
var COMMIT = "COMMIT should be overridden"

// BUILDINFO is information with date and context, supplied by make
var BUILDINFO = "BUILDINFO should have new info"

// DDevTLD defines the tld to use for DDev site URLs.
const DDevTLD = "ddev.local"

// GetVersionInfo returns a map containing the version info defined above.
func GetVersionInfo() map[string]string {
	versionInfo := make(map[string]string)

	versionInfo["cli"] = DdevVersion
	versionInfo["web"] = WebImg + ":" + WebTag
	versionInfo["db"] = DBImg + ":" + DBTag
	versionInfo["dba"] = DBAImg + ":" + DBATag
	versionInfo["router"] = RouterImage + ":" + RouterTag
	versionInfo["commit"] = COMMIT
	versionInfo["domain"] = DDevTLD
	versionInfo["build info"] = BUILDINFO

	return versionInfo
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 version

import (
	"strings"

	"github.com/blang/semver"
)

// VersionPrefix is the prefix of the git tag for a version
const VersionPrefix = "v"

// The current version of the minikube

// version is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.version=vX.Y.Z"
var version = "v0.0.0-unset"

// version is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.gitCommitID=<commit-id>"
var gitCommitID = ""

// isoVersion is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.isoVersion=vX.Y.Z"
var isoVersion = "v0.0.0-unset"

var isoPath = "minikube/iso"

// GetVersion returns the current minikube version
func GetVersion() string {
	return version
}

// GetGitCommitID returns the git commit id from which it is being built
func GetGitCommitID() string {
	return gitCommitID
}

// GetISOVersion returns the current minikube.iso version
func GetISOVersion() string {
	return isoVersion
}

// GetISOPath returns the remote path to the minikube.iso
func GetISOPath() string {
	return isoPath
}

// GetSemverVersion returns the current minikube semantic version (semver)
func GetSemverVersion() (semver.Version, error) {
	return semver.Make(strings.TrimPrefix(GetVersion(), VersionPrefix))
}
<commit_msg>Fix doc comment version.gitCommitID doc<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 version

import (
	"strings"

	"github.com/blang/semver"
)

// VersionPrefix is the prefix of the git tag for a version
const VersionPrefix = "v"

// The current version of the minikube

// version is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.version=vX.Y.Z"
var version = "v0.0.0-unset"

// gitCommitID is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.gitCommitID=<commit-id>"
var gitCommitID = ""

// isoVersion is a private field and should be set when compiling with --ldflags="-X k8s.io/minikube/pkg/version.isoVersion=vX.Y.Z"
var isoVersion = "v0.0.0-unset"

var isoPath = "minikube/iso"

// GetVersion returns the current minikube version
func GetVersion() string {
	return version
}

// GetGitCommitID returns the git commit id from which it is being built
func GetGitCommitID() string {
	return gitCommitID
}

// GetISOVersion returns the current minikube.iso version
func GetISOVersion() string {
	return isoVersion
}

// GetISOPath returns the remote path to the minikube.iso
func GetISOPath() string {
	return isoPath
}

// GetSemverVersion returns the current minikube semantic version (semver)
func GetSemverVersion() (semver.Version, error) {
	return semver.Make(strings.TrimPrefix(GetVersion(), VersionPrefix))
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 local

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/etcdserver"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types"
)

const (
	memberName  = "simple"
	clusterName = "simple-cluster"
	tempPrefix  = "simple-etcd-"

	// No peer URL exists but etcd doesn't allow the value to be empty.
	peerURL    = "http://localhost:0"
	clusterCfg = memberName + "=" + peerURL
)

// SimpleEtcd provides a single node etcd server.
type SimpleEtcd struct {
	Port     int
	listener net.Listener
	server   *etcdserver.EtcdServer
	dataDir  string
}

func NewSimpleEtcd() (*SimpleEtcd, error) {
	var err error
	se := &SimpleEtcd{}
	se.listener, err = net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	se.Port = se.listener.Addr().(*net.TCPAddr).Port
	clientURLs, err := interfaceURLs(se.Port)
	if err != nil {
		se.Destroy()
		return nil, err
	}

	se.dataDir, err = ioutil.TempDir("", tempPrefix)
	if err != nil {
		se.Destroy()
		return nil, err
	}

	peerURLs, err := types.NewURLs([]string{peerURL})
	if err != nil {
		se.Destroy()
		return nil, err
	}

	cfg := &etcdserver.ServerConfig{
		Name:       memberName,
		ClientURLs: clientURLs,
		PeerURLs:   peerURLs,
		DataDir:    se.dataDir,
		InitialPeerURLsMap: types.URLsMap{
			memberName: peerURLs,
		},
		NewCluster:    true,
		Transport:     &http.Transport{},
		TickMs:        100,
		ElectionTicks: 10,
	}

	se.server, err = etcdserver.NewServer(cfg)
	if err != nil {
		return nil, err
	}

	se.server.Start()
	go http.Serve(se.listener, etcdhttp.NewClientHandler(se.server))

	return se, nil
}

func (se *SimpleEtcd) Destroy() error {
	var err error
	firstErr := func(e error) {
		if e != nil && err == nil {
			err = e
		}
	}

	if se.listener != nil {
		firstErr(se.listener.Close())
	}

	if se.server != nil {
		se.server.Stop()
	}

	if se.dataDir != "" {
		firstErr(os.RemoveAll(se.dataDir))
	}

	return err
}

// Generate all publishable URLs for a given HTTP port.
func interfaceURLs(port int) (types.URLs, error) {
	allAddrs, err := net.InterfaceAddrs()
	if err != nil {
		return []url.URL{}, err
	}

	var allURLs types.URLs
	for _, a := range allAddrs {
		ip, ok := a.(*net.IPNet)
		if !ok || !ip.IP.IsGlobalUnicast() {
			continue
		}

		tcp := net.TCPAddr{
			IP:   ip.IP,
			Port: port,
		}

		u := url.URL{
			Scheme: "http",
			Host:   tcp.String(),
		}
		allURLs = append(allURLs, u)
	}

	if len(allAddrs) == 0 {
		return []url.URL{}, fmt.Errorf("no publishable addresses")
	}

	return allURLs, nil
}
<commit_msg>platform/local: update for new etcd API<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 local

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"os"

	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/etcdserver"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/etcdserver/etcdhttp"
	"github.com/coreos/mantle/Godeps/_workspace/src/github.com/coreos/etcd/pkg/types"
)

const (
	memberName  = "simple"
	clusterName = "simple-cluster"
	tempPrefix  = "simple-etcd-"

	// No peer URL exists but etcd doesn't allow the value to be empty.
	peerURL    = "http://localhost:0"
	clusterCfg = memberName + "=" + peerURL
)

// SimpleEtcd provides a single node etcd server.
type SimpleEtcd struct {
	Port     int
	listener net.Listener
	server   *etcdserver.EtcdServer
	dataDir  string
}

func NewSimpleEtcd() (*SimpleEtcd, error) {
	var err error
	se := &SimpleEtcd{}
	se.listener, err = net.Listen("tcp", ":0")
	if err != nil {
		return nil, err
	}

	se.Port = se.listener.Addr().(*net.TCPAddr).Port
	clientURLs, err := interfaceURLs(se.Port)
	if err != nil {
		se.Destroy()
		return nil, err
	}

	se.dataDir, err = ioutil.TempDir("", tempPrefix)
	if err != nil {
		se.Destroy()
		return nil, err
	}

	peerURLs, err := types.NewURLs([]string{peerURL})
	if err != nil {
		se.Destroy()
		return nil, err
	}

	cfg := &etcdserver.ServerConfig{
		Name:       memberName,
		ClientURLs: clientURLs,
		PeerURLs:   peerURLs,
		DataDir:    se.dataDir,
		InitialPeerURLsMap: types.URLsMap{
			memberName: peerURLs,
		},
		NewCluster:    true,
		Transport:     &http.Transport{},
		TickMs:        100,
		ElectionTicks: 10,
	}

	se.server, err = etcdserver.NewServer(cfg)
	if err != nil {
		return nil, err
	}

	se.server.Start()
	go http.Serve(se.listener,
		etcdhttp.NewClientHandler(se.server, cfg.ReqTimeout()))

	return se, nil
}

func (se *SimpleEtcd) Destroy() error {
	var err error
	firstErr := func(e error) {
		if e != nil && err == nil {
			err = e
		}
	}

	if se.listener != nil {
		firstErr(se.listener.Close())
	}

	if se.server != nil {
		se.server.Stop()
	}

	if se.dataDir != "" {
		firstErr(os.RemoveAll(se.dataDir))
	}

	return err
}

// Generate all publishable URLs for a given HTTP port.
func interfaceURLs(port int) (types.URLs, error) {
	allAddrs, err := net.InterfaceAddrs()
	if err != nil {
		return []url.URL{}, err
	}

	var allURLs types.URLs
	for _, a := range allAddrs {
		ip, ok := a.(*net.IPNet)
		if !ok || !ip.IP.IsGlobalUnicast() {
			continue
		}

		tcp := net.TCPAddr{
			IP:   ip.IP,
			Port: port,
		}

		u := url.URL{
			Scheme: "http",
			Host:   tcp.String(),
		}
		allURLs = append(allURLs, u)
	}

	if len(allAddrs) == 0 {
		return []url.URL{}, fmt.Errorf("no publishable addresses")
	}

	return allURLs, nil
}
<|endoftext|>


<commit_before>package cwl

import (
	"fmt"
	"os"
	"testing"

	. "github.com/otiai10/mint"
)

const version = "1.0"

// Provides path for testable official .cwl files.
func cwl(name string) string {
	return fmt.Sprintf("./cwl/v%[1]s/v%[1]s/%s", version, name)
}
func TestDecode_bwa_mem_tool(t *testing.T) {
	f, err := os.Open(cwl("bwa-mem-tool.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.Hints).TypeOf("cwl.Hints")
	Expect(t, root.Hints[0]["class"]).ToBe("ResourceRequirement")
	Expect(t, root.Hints[0]["coresMin"]).ToBe(float64(2))

	Expect(t, len(root.RequiredInputs)).ToBe(int(5))
	Expect(t, root.RequiredInputs[0]).TypeOf("cwl.RequiredInput")
	Expect(t, root.RequiredInputs[0].ID).ToBe("reference")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(2)
	Expect(t, root.RequiredInputs[1].ID).ToBe("reads")
	Expect(t, root.RequiredInputs[1].Type.Type).ToBe("array")
	Expect(t, root.RequiredInputs[1].Type.Items).ToBe("File")
	Expect(t, root.RequiredInputs[1].Binding.Position).ToBe(3)
	Expect(t, root.RequiredInputs[2].Binding.Prefix).ToBe("-m")
	Expect(t, root.RequiredInputs[3].Binding.Separator).ToBe(",")
	Expect(t, root.RequiredInputs[4].Default.Class).ToBe("File")
	Expect(t, root.RequiredInputs[4].Default.Location).ToBe("args.py")
	Expect(t, root.Outputs[0].ID).ToBe("sam")
	Expect(t, root.Outputs[0].Types[0].Type).ToBe("null")
	Expect(t, root.Outputs[1].ID).ToBe("args")
	Expect(t, root.Outputs[1].Types[0].Type).ToBe("array")
	Expect(t, root.Outputs[1].Types[0].Items).ToBe("string")
}
func TestDecode_cat3_nodocker(t *testing.T) {
	f, err := os.Open(cwl("cat3-nodocker.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Doc).ToBe("Print the contents of a file to stdout using 'cat'.")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.BaseCommand).ToBe("cat")
	Expect(t, root.Stdout).ToBe("output.txt")
	Expect(t, len(root.RequiredInputs)).ToBe(int(1))
	Expect(t, root.RequiredInputs[0].ID).ToBe("file1")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Label).ToBe("Input File")
	Expect(t, root.RequiredInputs[0].Doc).ToBe("The file that will be copied using 'cat'")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(1)
}
func TestDecode_cat3_tool_mediumcut(t *testing.T) {
	f, err := os.Open(cwl("cat3-tool-mediumcut.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Doc).ToBe("Print the contents of a file to stdout using 'cat' running in a docker container.")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.BaseCommand).ToBe("cat")
	Expect(t, root.Stdout).ToBe("cat-out")
	Expect(t, root.Hints).TypeOf("cwl.Hints")
	Expect(t, root.Hints[0]["class"]).ToBe("DockerRequirement")
	Expect(t, root.Hints[0]["dockerPull"]).ToBe("debian:wheezy")
	Expect(t, len(root.RequiredInputs)).ToBe(int(1))
	Expect(t, root.RequiredInputs[0].ID).ToBe("file1")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Label).ToBe("Input File")
	Expect(t, root.RequiredInputs[0].Doc).ToBe("The file that will be copied using 'cat'")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(1)
}
<commit_msg>Add cat3-tool-shortcut.cwl in test case<commit_after>package cwl

import (
	"fmt"
	"os"
	"testing"

	. "github.com/otiai10/mint"
)

const version = "1.0"

// Provides path for testable official .cwl files.
func cwl(name string) string {
	return fmt.Sprintf("./cwl/v%[1]s/v%[1]s/%s", version, name)
}
func TestDecode_bwa_mem_tool(t *testing.T) {
	f, err := os.Open(cwl("bwa-mem-tool.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.Hints).TypeOf("cwl.Hints")
	Expect(t, root.Hints[0]["class"]).ToBe("ResourceRequirement")
	Expect(t, root.Hints[0]["coresMin"]).ToBe(float64(2))

	Expect(t, len(root.RequiredInputs)).ToBe(int(5))
	Expect(t, root.RequiredInputs[0]).TypeOf("cwl.RequiredInput")
	Expect(t, root.RequiredInputs[0].ID).ToBe("reference")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(2)
	Expect(t, root.RequiredInputs[1].ID).ToBe("reads")
	Expect(t, root.RequiredInputs[1].Type.Type).ToBe("array")
	Expect(t, root.RequiredInputs[1].Type.Items).ToBe("File")
	Expect(t, root.RequiredInputs[1].Binding.Position).ToBe(3)
	Expect(t, root.RequiredInputs[2].Binding.Prefix).ToBe("-m")
	Expect(t, root.RequiredInputs[3].Binding.Separator).ToBe(",")
	Expect(t, root.RequiredInputs[4].Default.Class).ToBe("File")
	Expect(t, root.RequiredInputs[4].Default.Location).ToBe("args.py")
	Expect(t, root.Outputs[0].ID).ToBe("sam")
	Expect(t, root.Outputs[0].Types[0].Type).ToBe("null")
	Expect(t, root.Outputs[1].ID).ToBe("args")
	Expect(t, root.Outputs[1].Types[0].Type).ToBe("array")
	Expect(t, root.Outputs[1].Types[0].Items).ToBe("string")
}
func TestDecode_cat3_nodocker(t *testing.T) {
	f, err := os.Open(cwl("cat3-nodocker.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Doc).ToBe("Print the contents of a file to stdout using 'cat'.")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.BaseCommand).ToBe("cat")
	Expect(t, root.Stdout).ToBe("output.txt")
	Expect(t, len(root.RequiredInputs)).ToBe(int(1))
	Expect(t, root.RequiredInputs[0].ID).ToBe("file1")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Label).ToBe("Input File")
	Expect(t, root.RequiredInputs[0].Doc).ToBe("The file that will be copied using 'cat'")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(1)
}
func TestDecode_cat3_tool_mediumcut(t *testing.T) {
	f, err := os.Open(cwl("cat3-tool-mediumcut.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Doc).ToBe("Print the contents of a file to stdout using 'cat' running in a docker container.")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.BaseCommand).ToBe("cat")
	Expect(t, root.Stdout).ToBe("cat-out")
	Expect(t, root.Hints).TypeOf("cwl.Hints")
	Expect(t, root.Hints[0]["class"]).ToBe("DockerRequirement")
	Expect(t, root.Hints[0]["dockerPull"]).ToBe("debian:wheezy")
	Expect(t, len(root.RequiredInputs)).ToBe(int(1))
	Expect(t, root.RequiredInputs[0].ID).ToBe("file1")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Label).ToBe("Input File")
	Expect(t, root.RequiredInputs[0].Doc).ToBe("The file that will be copied using 'cat'")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(1)
}
func TestDecode_cat3_tool_shortcut(t *testing.T) {
	f, err := os.Open(cwl("cat3-tool-shortcut.cwl"))
	if err != nil {
		panic(err)
	}
	root := NewCWL()
	Expect(t, root).TypeOf("*cwl.Root")
	err = root.Decode(f)
	Expect(t, err).ToBe(nil)
	Expect(t, root.Version).ToBe("v1.0")
	Expect(t, root.Doc).ToBe("Print the contents of a file to stdout using 'cat' running in a docker container.")
	Expect(t, root.Class).ToBe("CommandLineTool")
	Expect(t, root.BaseCommand).ToBe("cat")
	Expect(t, root.Hints).TypeOf("cwl.Hints")
	Expect(t, root.Hints[0]["class"]).ToBe("DockerRequirement")
	Expect(t, root.Hints[0]["dockerPull"]).ToBe("debian:wheezy")
	Expect(t, len(root.RequiredInputs)).ToBe(int(1))
	Expect(t, root.RequiredInputs[0].ID).ToBe("file1")
	Expect(t, root.RequiredInputs[0].Type.Type).ToBe("File")
	Expect(t, root.RequiredInputs[0].Label).ToBe("Input File")
	Expect(t, root.RequiredInputs[0].Doc).ToBe("The file that will be copied using 'cat'")
	Expect(t, root.RequiredInputs[0].Binding.Position).ToBe(1)
}
<|endoftext|>