<commit_before>package builtin_test

import (
	"bytes"
	"testing"

	"github.com/NeowayLabs/nash"
)

func TestPrintf(t *testing.T) {
	type printfDesc struct {
		script string
		output string
	}

	tests := map[string]printfDesc{
		"textonly": {
			script: `printf("helloworld")`,
			output: "helloworld",
		},
		"fmtstring": {
			script: `printf("%s:%s", "hello", "world")`,
			output: "hello:world",
		},
		"fmtlist": {
			script: `
				list = ("1" "2" "3")
				printf("%s:%s", "list", $list)
			`,
			output: "list:1 2 3",
		},
		"listonly": {
			script: `
				list = ("1" "2" "3")
				printf($list)
			`,
			output: "1 2 3",
		},
		"listoflists": {
			script: `
				list = (("1" "2" "3") ("4" "5" "6"))
				printf("%s:%s", "listoflists", $list)
			`,
			output: "listoflists:1 2 3 4 5 6",
		},
		"listasfmt": {
			script: `
				list = ("%s" "%s")
				printf($list, "1", "2")
			`,
			output: "1 2",
		},
		"invalidFmt": {
			script: `printf("%d%s", "invalid")`,
			output: "%!d(string=invalid)%!s(MISSING)",
		},
	}

	for name, desc := range tests {
		t.Run(name, func(t *testing.T) {
			var output bytes.Buffer
			shell, err := nash.New()

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			shell.SetStdout(&output)
			err = shell.Exec("", desc.script)

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			if output.String() != desc.output {
				t.Fatalf("got %q expected %q", output.String(), desc.output)
			}
		})
	}
}

func TestPrintfErrors(t *testing.T) {
	type printfDesc struct {
		script string
	}

	tests := map[string]printfDesc{
		"noParams": {
			script: `printf()`,
		},
	}

	for name, desc := range tests {
		t.Run(name, func(t *testing.T) {
			var output bytes.Buffer
			shell, err := nash.New()

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			shell.SetStdout(&output)
			err = shell.Exec("", desc.script)

			if err == nil {
				t.Fatalf("expected err, got success, output: %s", output)
			}

			if output.Len() > 0 {
				t.Fatalf("expected empty output, got: %s", output)
			}
		})
	}
}
<commit_msg>Add printf tests for functions<commit_after>package builtin_test

import (
	"bytes"
	"testing"

	"github.com/NeowayLabs/nash"
)

func TestPrintf(t *testing.T) {
	type printfDesc struct {
		script string
		output string
	}

	tests := map[string]printfDesc{
		"textonly": {
			script: `printf("helloworld")`,
			output: "helloworld",
		},
		"fmtstring": {
			script: `printf("%s:%s", "hello", "world")`,
			output: "hello:world",
		},
		"fmtlist": {
			script: `
				list = ("1" "2" "3")
				printf("%s:%s", "list", $list)
			`,
			output: "list:1 2 3",
		},
		"funconly": {
			script: `
				fn func() {}
				printf($func)
			`,
			output: "<fn func>",
		},
		"funcfmt": {
			script: `
				fn func() {}
				printf("calling:%s", $func)
			`,
			output: "calling:<fn func>",
		},
		"listonly": {
			script: `
				list = ("1" "2" "3")
				printf($list)
			`,
			output: "1 2 3",
		},
		"listoflists": {
			script: `
				list = (("1" "2" "3") ("4" "5" "6"))
				printf("%s:%s", "listoflists", $list)
			`,
			output: "listoflists:1 2 3 4 5 6",
		},
		"listasfmt": {
			script: `
				list = ("%s" "%s")
				printf($list, "1", "2")
			`,
			output: "1 2",
		},
		"invalidFmt": {
			script: `printf("%d%s", "invalid")`,
			output: "%!d(string=invalid)%!s(MISSING)",
		},
	}

	for name, desc := range tests {
		t.Run(name, func(t *testing.T) {
			var output bytes.Buffer
			shell, err := nash.New()

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			shell.SetStdout(&output)
			err = shell.Exec("", desc.script)

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			if output.String() != desc.output {
				t.Fatalf("got %q expected %q", output.String(), desc.output)
			}
		})
	}
}

func TestPrintfErrors(t *testing.T) {
	type printfDesc struct {
		script string
	}

	tests := map[string]printfDesc{
		"noParams": {
			script: `printf()`,
		},
	}

	for name, desc := range tests {
		t.Run(name, func(t *testing.T) {
			var output bytes.Buffer
			shell, err := nash.New()

			if err != nil {
				t.Fatalf("unexpected err: %s", err)
			}

			shell.SetStdout(&output)
			err = shell.Exec("", desc.script)

			if err == nil {
				t.Fatalf("expected err, got success, output: %s", output)
			}

			if output.Len() > 0 {
				t.Fatalf("expected empty output, got: %s", output)
			}
		})
	}
}
<|endoftext|>


<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 store

import (
	"context"
	"strconv"

	"k8s.io/kube-state-metrics/v2/pkg/metric"
	generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

var (
	descPersistentVolumeClaimRefName          = "kube_persistentvolume_claim_ref"
	descPersistentVolumeClaimRefHelp          = "Information about the Persistent Volume Claim Reference."
	descPersistentVolumeClaimRefDefaultLabels = []string{"persistentvolume"}

	descPersistentVolumeAnnotationsName     = "kube_persistentvolume_annotations"
	descPersistentVolumeAnnotationsHelp     = "Kubernetes annotations converted to Prometheus labels."
	descPersistentVolumeLabelsName          = "kube_persistentvolume_labels"
	descPersistentVolumeLabelsHelp          = "Kubernetes labels converted to Prometheus labels."
	descPersistentVolumeLabelsDefaultLabels = []string{"persistentvolume"}
)

func persistentVolumeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator {
	return []generator.FamilyGenerator{
		*generator.NewFamilyGenerator(
			descPersistentVolumeClaimRefName,
			descPersistentVolumeClaimRefHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				claimRef := p.Spec.ClaimRef

				if claimRef == nil {
					return &metric.Family{
						Metrics: []*metric.Metric{},
					}
				}
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys: []string{
								"name",
								"claim_namespace",
							},
							LabelValues: []string{
								p.Spec.ClaimRef.Name,
								p.Spec.ClaimRef.Namespace,
							},
							Value: 1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			descPersistentVolumeAnnotationsName,
			descPersistentVolumeAnnotationsHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", p.Annotations, allowAnnotationsList)
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys:   annotationKeys,
							LabelValues: annotationValues,
							Value:       1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			descPersistentVolumeLabelsName,
			descPersistentVolumeLabelsHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				labelKeys, labelValues := createPrometheusLabelKeysValues("label", p.Labels, allowLabelsList)
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys:   labelKeys,
							LabelValues: labelValues,
							Value:       1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_status_phase",
			"The phase indicates if a volume is available, bound to a claim, or released by a claim.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				phase := p.Status.Phase

				if phase == "" {
					return &metric.Family{
						Metrics: []*metric.Metric{},
					}
				}

				// Set current phase to 1, others to 0 if it is set.
				ms := []*metric.Metric{
					{
						LabelValues: []string{string(v1.VolumePending)},
						Value:       boolFloat64(phase == v1.VolumePending),
					},
					{
						LabelValues: []string{string(v1.VolumeAvailable)},
						Value:       boolFloat64(phase == v1.VolumeAvailable),
					},
					{
						LabelValues: []string{string(v1.VolumeBound)},
						Value:       boolFloat64(phase == v1.VolumeBound),
					},
					{
						LabelValues: []string{string(v1.VolumeReleased)},
						Value:       boolFloat64(phase == v1.VolumeReleased),
					},
					{
						LabelValues: []string{string(v1.VolumeFailed)},
						Value:       boolFloat64(phase == v1.VolumeFailed),
					},
				}

				for _, m := range ms {
					m.LabelKeys = []string{"phase"}
				}

				return &metric.Family{
					Metrics: ms,
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_info",
			"Information about persistentvolume.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				var gcePDDiskName, ebsVolumeID, azureDiskName, fcWWIDs, fcLun, fcTargetWWNs, iscsiTargetPortal, iscsiIQN, iscsiLun, iscsiInitiatorName, nfsServer, nfsPath, csiDriver, csiVolumeHandle, localFS, localPath string

				switch {
				case p.Spec.PersistentVolumeSource.GCEPersistentDisk != nil:
					gcePDDiskName = p.Spec.PersistentVolumeSource.GCEPersistentDisk.PDName
				case p.Spec.PersistentVolumeSource.AWSElasticBlockStore != nil:
					ebsVolumeID = p.Spec.PersistentVolumeSource.AWSElasticBlockStore.VolumeID
				case p.Spec.PersistentVolumeSource.AzureDisk != nil:
					azureDiskName = p.Spec.PersistentVolumeSource.AzureDisk.DiskName
				case p.Spec.PersistentVolumeSource.FC != nil:
					if p.Spec.PersistentVolumeSource.FC.Lun != nil {
						fcLun = strconv.FormatInt(int64(*p.Spec.PersistentVolumeSource.FC.Lun), 10)
					}
					for _, wwn := range p.Spec.PersistentVolumeSource.FC.TargetWWNs {
						if len(fcTargetWWNs) != 0 {
							fcTargetWWNs += ","
						}
						fcTargetWWNs += wwn
					}
					for _, wwid := range p.Spec.PersistentVolumeSource.FC.WWIDs {
						if len(fcWWIDs) != 0 {
							fcWWIDs += ","
						}
						fcWWIDs += wwid
					}
				case p.Spec.PersistentVolumeSource.ISCSI != nil:
					iscsiTargetPortal = p.Spec.PersistentVolumeSource.ISCSI.TargetPortal
					iscsiIQN = p.Spec.PersistentVolumeSource.ISCSI.IQN
					iscsiLun = strconv.FormatInt(int64(p.Spec.PersistentVolumeSource.ISCSI.Lun), 10)
					if p.Spec.PersistentVolumeSource.ISCSI.InitiatorName != nil {
						iscsiInitiatorName = *p.Spec.PersistentVolumeSource.ISCSI.InitiatorName
					}
				case p.Spec.PersistentVolumeSource.NFS != nil:
					nfsServer = p.Spec.PersistentVolumeSource.NFS.Server
					nfsPath = p.Spec.PersistentVolumeSource.NFS.Path
				case p.Spec.PersistentVolumeSource.CSI != nil:
					csiDriver = p.Spec.PersistentVolumeSource.CSI.Driver
					csiVolumeHandle = p.Spec.PersistentVolumeSource.CSI.VolumeHandle
				case p.Spec.Local != nil:
					localPath = p.Spec.Local.Path
					if p.Spec.Local.FSType != nil {
						localFS = *p.Spec.Local.FSType
					}
				}

				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys: []string{
								"storageclass",
								"gce_persistent_disk_name",
								"ebs_volume_id",
								"azure_disk_name",
								"fc_wwids",
								"fc_lun",
								"fc_target_wwns",
								"iscsi_target_portal",
								"iscsi_iqn",
								"iscsi_lun",
								"iscsi_initiator_name",
								"nfs_server",
								"nfs_path",
								"csi_driver",
								"csi_volume_handle",
								"local_path",
								"local_fs",
							},
							LabelValues: []string{
								p.Spec.StorageClassName,
								gcePDDiskName,
								ebsVolumeID,
								azureDiskName,
								fcWWIDs,
								fcLun,
								fcTargetWWNs,
								iscsiTargetPortal,
								iscsiIQN,
								iscsiLun,
								iscsiInitiatorName,
								nfsServer,
								nfsPath,
								csiDriver,
								csiVolumeHandle,
								localPath,
								localFS,
							},
							Value: 1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_capacity_bytes",
			"Persistentvolume capacity in bytes.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				storage := p.Spec.Capacity[v1.ResourceStorage]
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							Value: float64(storage.Value()),
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_created",
			"Unix creation timestamp",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				ms := []*metric.Metric{}

				if !p.CreationTimestamp.IsZero() {
					ms = append(ms, &metric.Metric{
						LabelKeys:   []string{},
						LabelValues: []string{},
						Value:       float64(p.CreationTimestamp.Unix()),
					})
				}

				return &metric.Family{
					Metrics: ms,
				}
			}),
		),
	}
}

func wrapPersistentVolumeFunc(f func(*v1.PersistentVolume) *metric.Family) func(interface{}) *metric.Family {
	return func(obj interface{}) *metric.Family {
		persistentVolume := obj.(*v1.PersistentVolume)

		metricFamily := f(persistentVolume)

		for _, m := range metricFamily.Metrics {
			m.LabelKeys, m.LabelValues = mergeKeyValues(descPersistentVolumeLabelsDefaultLabels, []string{persistentVolume.Name}, m.LabelKeys, m.LabelValues)
		}

		return metricFamily
	}
}

func createPersistentVolumeListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher {
	return &cache.ListWatch{
		ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
			return kubeClient.CoreV1().PersistentVolumes().List(context.TODO(), opts)
		},
		WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
			return kubeClient.CoreV1().PersistentVolumes().Watch(context.TODO(), opts)
		},
	}
}
<commit_msg>Make code inline with the other parts<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 store

import (
	"context"
	"strconv"

	"k8s.io/kube-state-metrics/v2/pkg/metric"
	generator "k8s.io/kube-state-metrics/v2/pkg/metric_generator"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/watch"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

var (
	descPersistentVolumeClaimRefName          = "kube_persistentvolume_claim_ref"
	descPersistentVolumeClaimRefHelp          = "Information about the Persistent Volume Claim Reference."
	descPersistentVolumeClaimRefDefaultLabels = []string{"persistentvolume"}

	descPersistentVolumeAnnotationsName     = "kube_persistentvolume_annotations"
	descPersistentVolumeAnnotationsHelp     = "Kubernetes annotations converted to Prometheus labels."
	descPersistentVolumeLabelsName          = "kube_persistentvolume_labels"
	descPersistentVolumeLabelsHelp          = "Kubernetes labels converted to Prometheus labels."
	descPersistentVolumeLabelsDefaultLabels = []string{"persistentvolume"}
)

func persistentVolumeMetricFamilies(allowAnnotationsList, allowLabelsList []string) []generator.FamilyGenerator {
	return []generator.FamilyGenerator{
		*generator.NewFamilyGenerator(
			descPersistentVolumeClaimRefName,
			descPersistentVolumeClaimRefHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				claimRef := p.Spec.ClaimRef

				if claimRef == nil {
					return &metric.Family{
						Metrics: []*metric.Metric{},
					}
				}
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys: []string{
								"name",
								"claim_namespace",
							},
							LabelValues: []string{
								p.Spec.ClaimRef.Name,
								p.Spec.ClaimRef.Namespace,
							},
							Value: 1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			descPersistentVolumeAnnotationsName,
			descPersistentVolumeAnnotationsHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				annotationKeys, annotationValues := createPrometheusLabelKeysValues("annotation", p.Annotations, allowAnnotationsList)
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys:   annotationKeys,
							LabelValues: annotationValues,
							Value:       1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			descPersistentVolumeLabelsName,
			descPersistentVolumeLabelsHelp,
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				labelKeys, labelValues := createPrometheusLabelKeysValues("label", p.Labels, allowLabelsList)
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys:   labelKeys,
							LabelValues: labelValues,
							Value:       1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_status_phase",
			"The phase indicates if a volume is available, bound to a claim, or released by a claim.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				phase := p.Status.Phase

				if phase == "" {
					return &metric.Family{
						Metrics: []*metric.Metric{},
					}
				}

				// Set current phase to 1, others to 0 if it is set.
				ms := []*metric.Metric{
					{
						LabelValues: []string{string(v1.VolumePending)},
						Value:       boolFloat64(phase == v1.VolumePending),
					},
					{
						LabelValues: []string{string(v1.VolumeAvailable)},
						Value:       boolFloat64(phase == v1.VolumeAvailable),
					},
					{
						LabelValues: []string{string(v1.VolumeBound)},
						Value:       boolFloat64(phase == v1.VolumeBound),
					},
					{
						LabelValues: []string{string(v1.VolumeReleased)},
						Value:       boolFloat64(phase == v1.VolumeReleased),
					},
					{
						LabelValues: []string{string(v1.VolumeFailed)},
						Value:       boolFloat64(phase == v1.VolumeFailed),
					},
				}

				for _, m := range ms {
					m.LabelKeys = []string{"phase"}
				}

				return &metric.Family{
					Metrics: ms,
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_info",
			"Information about persistentvolume.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				var gcePDDiskName, ebsVolumeID, azureDiskName, fcWWIDs, fcLun, fcTargetWWNs, iscsiTargetPortal, iscsiIQN, iscsiLun, iscsiInitiatorName, nfsServer, nfsPath, csiDriver, csiVolumeHandle, localFS, localPath string

				switch {
				case p.Spec.PersistentVolumeSource.GCEPersistentDisk != nil:
					gcePDDiskName = p.Spec.PersistentVolumeSource.GCEPersistentDisk.PDName
				case p.Spec.PersistentVolumeSource.AWSElasticBlockStore != nil:
					ebsVolumeID = p.Spec.PersistentVolumeSource.AWSElasticBlockStore.VolumeID
				case p.Spec.PersistentVolumeSource.AzureDisk != nil:
					azureDiskName = p.Spec.PersistentVolumeSource.AzureDisk.DiskName
				case p.Spec.PersistentVolumeSource.FC != nil:
					if p.Spec.PersistentVolumeSource.FC.Lun != nil {
						fcLun = strconv.FormatInt(int64(*p.Spec.PersistentVolumeSource.FC.Lun), 10)
					}
					for _, wwn := range p.Spec.PersistentVolumeSource.FC.TargetWWNs {
						if len(fcTargetWWNs) != 0 {
							fcTargetWWNs += ","
						}
						fcTargetWWNs += wwn
					}
					for _, wwid := range p.Spec.PersistentVolumeSource.FC.WWIDs {
						if len(fcWWIDs) != 0 {
							fcWWIDs += ","
						}
						fcWWIDs += wwid
					}
				case p.Spec.PersistentVolumeSource.ISCSI != nil:
					iscsiTargetPortal = p.Spec.PersistentVolumeSource.ISCSI.TargetPortal
					iscsiIQN = p.Spec.PersistentVolumeSource.ISCSI.IQN
					iscsiLun = strconv.FormatInt(int64(p.Spec.PersistentVolumeSource.ISCSI.Lun), 10)
					if p.Spec.PersistentVolumeSource.ISCSI.InitiatorName != nil {
						iscsiInitiatorName = *p.Spec.PersistentVolumeSource.ISCSI.InitiatorName
					}
				case p.Spec.PersistentVolumeSource.NFS != nil:
					nfsServer = p.Spec.PersistentVolumeSource.NFS.Server
					nfsPath = p.Spec.PersistentVolumeSource.NFS.Path
				case p.Spec.PersistentVolumeSource.CSI != nil:
					csiDriver = p.Spec.PersistentVolumeSource.CSI.Driver
					csiVolumeHandle = p.Spec.PersistentVolumeSource.CSI.VolumeHandle
				case p.Spec.PersistentVolumeSource.Local != nil:
					localPath = p.Spec.PersistentVolumeSource.Local.Path
					if p.Spec.PersistentVolumeSource.Local.FSType != nil {
						localFS = *p.Spec.PersistentVolumeSource.Local.FSType
					}
				}

				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							LabelKeys: []string{
								"storageclass",
								"gce_persistent_disk_name",
								"ebs_volume_id",
								"azure_disk_name",
								"fc_wwids",
								"fc_lun",
								"fc_target_wwns",
								"iscsi_target_portal",
								"iscsi_iqn",
								"iscsi_lun",
								"iscsi_initiator_name",
								"nfs_server",
								"nfs_path",
								"csi_driver",
								"csi_volume_handle",
								"local_path",
								"local_fs",
							},
							LabelValues: []string{
								p.Spec.StorageClassName,
								gcePDDiskName,
								ebsVolumeID,
								azureDiskName,
								fcWWIDs,
								fcLun,
								fcTargetWWNs,
								iscsiTargetPortal,
								iscsiIQN,
								iscsiLun,
								iscsiInitiatorName,
								nfsServer,
								nfsPath,
								csiDriver,
								csiVolumeHandle,
								localPath,
								localFS,
							},
							Value: 1,
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_capacity_bytes",
			"Persistentvolume capacity in bytes.",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				storage := p.Spec.Capacity[v1.ResourceStorage]
				return &metric.Family{
					Metrics: []*metric.Metric{
						{
							Value: float64(storage.Value()),
						},
					},
				}
			}),
		),
		*generator.NewFamilyGenerator(
			"kube_persistentvolume_created",
			"Unix creation timestamp",
			metric.Gauge,
			"",
			wrapPersistentVolumeFunc(func(p *v1.PersistentVolume) *metric.Family {
				ms := []*metric.Metric{}

				if !p.CreationTimestamp.IsZero() {
					ms = append(ms, &metric.Metric{
						LabelKeys:   []string{},
						LabelValues: []string{},
						Value:       float64(p.CreationTimestamp.Unix()),
					})
				}

				return &metric.Family{
					Metrics: ms,
				}
			}),
		),
	}
}

func wrapPersistentVolumeFunc(f func(*v1.PersistentVolume) *metric.Family) func(interface{}) *metric.Family {
	return func(obj interface{}) *metric.Family {
		persistentVolume := obj.(*v1.PersistentVolume)

		metricFamily := f(persistentVolume)

		for _, m := range metricFamily.Metrics {
			m.LabelKeys, m.LabelValues = mergeKeyValues(descPersistentVolumeLabelsDefaultLabels, []string{persistentVolume.Name}, m.LabelKeys, m.LabelValues)
		}

		return metricFamily
	}
}

func createPersistentVolumeListWatch(kubeClient clientset.Interface, ns string, fieldSelector string) cache.ListerWatcher {
	return &cache.ListWatch{
		ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
			return kubeClient.CoreV1().PersistentVolumes().List(context.TODO(), opts)
		},
		WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
			return kubeClient.CoreV1().PersistentVolumes().Watch(context.TODO(), opts)
		},
	}
}
<|endoftext|>


<commit_before>package output

import (
	"os"
	"testing"
)

// We are using os.Chmod for some tests,
// so it is expected to work on *nix based systems only.
//
//+build linux

func TestNewType(t *testing.T) {
	// We are making sure there are no any panics.
	typ := NewType("test", "./output.go")
	if typ.Package != "test" {
		t.Error("package name was not initialized")
	}
}

func TestNewType_IncorrectPath(t *testing.T) {
	defer expectPanic("when we are not able to read template file, a panic must occur")
	NewType("test", "./pathThatDoesNotExist")
}

func TestCreateDir_ExistingDirectory(t *testing.T) {
	typ := Type{}
	typ.CreateDir("./testdata")
	if typ.Path != "./testdata" {
		t.Error("Type.Path is expected to be initialized")
	}
}

func TestCreateDir_NoWritePrivileges(t *testing.T) {
	// Prepare a readonly directory.
	os.Chmod("./testdata/readonly", 0544)

	typ := Type{}
	defer expectPanic("we have no write privileges for './testdata/readonly', so panic expected")
	typ.CreateDir("./testdata/readonly/something")
}

func TestCreateDir(t *testing.T) {
	typ := Type{}
	typ.CreateDir("./testdata/assets/something")

	// Make sure the directory exists.
	if _, err := os.Stat("./testdata/assets/something"); err != nil {
		if os.IsNotExist(err) {
			t.Errorf("directory was not created, error: '%s'", err)
		}
	}

	// Remove the directories we have created.
	os.RemoveAll("./testdata/assets")
}

func TestGenerate_IncorrectTemplate(t *testing.T) {
	typ := NewType("test", "./testdata/incorrect_template.html")
	typ.Path = "./testdata"
	defer expectPanic("template has errors and thus an error expected")
	typ.Generate()
}

func expectPanic(msg string) {
	if err := recover(); err == nil {
		panic(msg)
	}
}
<commit_msg>Update +build comment<commit_after>// +build linux

package output

import (
	"os"
	"testing"
)

func TestNewType(t *testing.T) {
	// We are making sure there are no any panics.
	typ := NewType("test", "./output.go")
	if typ.Package != "test" {
		t.Error("package name was not initialized")
	}
}

func TestNewType_IncorrectPath(t *testing.T) {
	defer expectPanic("when we are not able to read template file, a panic must occur")
	NewType("test", "./pathThatDoesNotExist")
}

func TestCreateDir_ExistingDirectory(t *testing.T) {
	typ := Type{}
	typ.CreateDir("./testdata")
	if typ.Path != "./testdata" {
		t.Error("Type.Path is expected to be initialized")
	}
}

func TestCreateDir_NoWritePrivileges(t *testing.T) {
	// Prepare a readonly directory.
	os.Chmod("./testdata/readonly", 0544)

	typ := Type{}
	defer expectPanic("we have no write privileges for './testdata/readonly', so panic expected")
	typ.CreateDir("./testdata/readonly/something")
}

func TestCreateDir(t *testing.T) {
	typ := Type{}
	typ.CreateDir("./testdata/assets/something")

	// Make sure the directory exists.
	if _, err := os.Stat("./testdata/assets/something"); err != nil {
		if os.IsNotExist(err) {
			t.Errorf("directory was not created, error: '%s'", err)
		}
	}

	// Remove the directories we have created.
	os.RemoveAll("./testdata/assets")
}

func TestGenerate_IncorrectTemplate(t *testing.T) {
	typ := NewType("test", "./testdata/incorrect_template.html")
	typ.Path = "./testdata"
	defer expectPanic("template has errors and thus an error expected")
	typ.Generate()
}

func expectPanic(msg string) {
	if err := recover(); err == nil {
		panic(msg)
	}
}
<|endoftext|>


<commit_before>// Package easyssh provides a simple implementation of some SSH protocol
// features in Go. You can simply run a command on a remote server or get a file
// even simpler than native console SSH client. You don't need to think about
// Dials, sessions, defers, or public keys... Let easyssh think about it!
package easyssh

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/ScaleFT/sshkeys"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

var defaultTimeout = 60 * time.Second

type (
	// MakeConfig Contains main authority information.
	// User field should be a name of user on remote server (ex. john in ssh john@example.com).
	// Server field should be a remote machine address (ex. example.com in ssh john@example.com)
	// Key is a path to private key on your local machine.
	// Port is SSH server port on remote machine.
	// Note: easyssh looking for private key in user's home directory (ex. /home/john + Key).
	// Then ensure your Key begins from '/' (ex. /.ssh/id_rsa)
	MakeConfig struct {
		User         string
		Server       string
		Key          string
		KeyPath      string
		Port         string
		Passphrase   string
		Password     string
		Timeout      time.Duration
		Proxy        DefaultConfig
		Ciphers      []string
		KeyExchanges []string
		Fingerprint  string

		// Enable the use of insecure ciphers and key exchange methods.
		// This enables the use of the the following insecure ciphers and key exchange methods:
		// - aes128-cbc
		// - aes192-cbc
		// - aes256-cbc
		// - 3des-cbc
		// - diffie-hellman-group-exchange-sha256
		// - diffie-hellman-group-exchange-sha1
		// Those algorithms are insecure and may allow plaintext data to be recovered by an attacker.
		UseInsecureCipher bool
	}

	// DefaultConfig for ssh proxy config
	DefaultConfig struct {
		User         string
		Server       string
		Key          string
		KeyPath      string
		Port         string
		Passphrase   string
		Password     string
		Timeout      time.Duration
		Ciphers      []string
		KeyExchanges []string
		Fingerprint  string

		// Enable the use of insecure ciphers and key exchange methods.
		// This enables the use of the the following insecure ciphers and key exchange methods:
		// - aes128-cbc
		// - aes192-cbc
		// - aes256-cbc
		// - 3des-cbc
		// - diffie-hellman-group-exchange-sha256
		// - diffie-hellman-group-exchange-sha1
		// Those algorithms are insecure and may allow plaintext data to be recovered by an attacker.
		UseInsecureCipher bool
	}
)

// returns ssh.Signer from user you running app home path + cutted key path.
// (ex. pubkey,err := getKeyFile("/.ssh/id_rsa") )
func getKeyFile(keypath, passphrase string) (ssh.Signer, error) {
	var pubkey ssh.Signer
	var err error
	buf, err := ioutil.ReadFile(keypath)
	if err != nil {
		return nil, err
	}

	if passphrase != "" {
		pubkey, err = sshkeys.ParseEncryptedPrivateKey(buf, []byte(passphrase))
	} else {
		pubkey, err = ssh.ParsePrivateKey(buf)
	}

	if err != nil {
		return nil, err
	}

	return pubkey, nil
}

// returns *ssh.ClientConfig and io.Closer.
// if io.Closer is not nil, io.Closer.Close() should be called when
// *ssh.ClientConfig is no longer used.
func getSSHConfig(config DefaultConfig) (*ssh.ClientConfig, io.Closer) {
	var sshAgent io.Closer

	// auths holds the detected ssh auth methods
	auths := []ssh.AuthMethod{}

	// figure out what auths are requested, what is supported
	if config.Password != "" {
		auths = append(auths, ssh.Password(config.Password))
	}
	if config.KeyPath != "" {
		if pubkey, err := getKeyFile(config.KeyPath, config.Passphrase); err != nil {
			log.Printf("getKeyFile error: %v\n", err)
		} else {
			auths = append(auths, ssh.PublicKeys(pubkey))
		}
	}

	if config.Key != "" {
		var signer ssh.Signer
		var err error
		if config.Passphrase != "" {
			signer, err = sshkeys.ParseEncryptedPrivateKey([]byte(config.Key), []byte(config.Passphrase))
		} else {
			signer, err = ssh.ParsePrivateKey([]byte(config.Key))
		}

		if err != nil {
			log.Printf("ssh.ParsePrivateKey: %v\n", err)
		} else {
			auths = append(auths, ssh.PublicKeys(signer))
		}
	}

	if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
		auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
	}

	c := ssh.Config{}
	if config.UseInsecureCipher {
		c.SetDefaults()
		c.Ciphers = append(c.Ciphers, "aes128-cbc")
		c.KeyExchanges = append(c.KeyExchanges, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
	}

	if len(config.Ciphers) > 0 {
		c.Ciphers = append(c.Ciphers, config.Ciphers...)
	}

	if len(config.KeyExchanges) > 0 {
		c.KeyExchanges = append(c.KeyExchanges, config.KeyExchanges...)
	}

	hostKeyCallback := ssh.InsecureIgnoreHostKey()
	if config.Fingerprint != "" {
		hostKeyCallback = func(hostname string, remote net.Addr, publicKey ssh.PublicKey) error {
			if ssh.FingerprintSHA256(publicKey) != config.Fingerprint {
				return fmt.Errorf("ssh: host key fingerprint mismatch")
			}
			return nil
		}
	}

	return &ssh.ClientConfig{
		Config:          c,
		Timeout:         config.Timeout,
		User:            config.User,
		Auth:            auths,
		HostKeyCallback: hostKeyCallback,
	}, sshAgent
}

// Connect to remote server using MakeConfig struct and returns *ssh.Session
func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) {
	var client *ssh.Client
	var err error

	targetConfig, closer := getSSHConfig(DefaultConfig{
		User:              ssh_conf.User,
		Key:               ssh_conf.Key,
		KeyPath:           ssh_conf.KeyPath,
		Passphrase:        ssh_conf.Passphrase,
		Password:          ssh_conf.Password,
		Timeout:           ssh_conf.Timeout,
		Ciphers:           ssh_conf.Ciphers,
		KeyExchanges:      ssh_conf.KeyExchanges,
		Fingerprint:       ssh_conf.Fingerprint,
		UseInsecureCipher: ssh_conf.UseInsecureCipher,
	})
	if closer != nil {
		defer closer.Close()
	}

	// Enable proxy command
	if ssh_conf.Proxy.Server != "" {
		proxyConfig, closer := getSSHConfig(DefaultConfig{
			User:              ssh_conf.Proxy.User,
			Key:               ssh_conf.Proxy.Key,
			KeyPath:           ssh_conf.Proxy.KeyPath,
			Passphrase:        ssh_conf.Proxy.Passphrase,
			Password:          ssh_conf.Proxy.Password,
			Timeout:           ssh_conf.Proxy.Timeout,
			Ciphers:           ssh_conf.Proxy.Ciphers,
			KeyExchanges:      ssh_conf.Proxy.KeyExchanges,
			Fingerprint:       ssh_conf.Proxy.Fingerprint,
			UseInsecureCipher: ssh_conf.Proxy.UseInsecureCipher,
		})
		if closer != nil {
			defer closer.Close()
		}

		proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig)
		if err != nil {
			return nil, nil, err
		}

		conn, err := proxyClient.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port))
		if err != nil {
			return nil, nil, err
		}

		ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)
		if err != nil {
			return nil, nil, err
		}

		client = ssh.NewClient(ncc, chans, reqs)
	} else {
		client, err = ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)
		if err != nil {
			return nil, nil, err
		}
	}

	session, err := client.NewSession()
	if err != nil {
		return nil, nil, err
	}

	return session, client, nil
}

// Stream returns one channel that combines the stdout and stderr of the command
// as it is run on the remote machine, and another that sends true when the
// command is done. The sessions and channels will then be closed.
func (ssh_conf *MakeConfig) Stream(command string, timeout ...time.Duration) (<-chan string, <-chan string, <-chan bool, <-chan error, error) {
	// continuously send the command's output over the channel
	stdoutChan := make(chan string)
	stderrChan := make(chan string)
	doneChan := make(chan bool)
	errChan := make(chan error)

	// connect to remote host
	session, client, err := ssh_conf.Connect()
	if err != nil {
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	// defer session.Close()
	// connect to both outputs (they are of type io.Reader)
	outReader, err := session.StdoutPipe()
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	errReader, err := session.StderrPipe()
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	err = session.Start(command)
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}

	// combine outputs, create a line-by-line scanner
	stdoutReader := io.MultiReader(outReader)
	stderrReader := io.MultiReader(errReader)
	stdoutScanner := bufio.NewScanner(stdoutReader)
	stderrScanner := bufio.NewScanner(stderrReader)

	go func(stdoutScanner, stderrScanner *bufio.Scanner, stdoutChan, stderrChan chan string, doneChan chan bool, errChan chan error) {
		defer close(stdoutChan)
		defer close(stderrChan)
		defer close(doneChan)
		defer close(errChan)
		defer client.Close()
		defer session.Close()

		// default timeout value
		executeTimeout := defaultTimeout
		if len(timeout) > 0 {
			executeTimeout = timeout[0]
		}
		timeoutChan := time.After(executeTimeout)
		res := make(chan struct{}, 1)
		var resWg sync.WaitGroup
		resWg.Add(2)

		go func() {
			for stdoutScanner.Scan() {
				stdoutChan <- stdoutScanner.Text()
			}
			resWg.Done()
		}()

		go func() {
			for stderrScanner.Scan() {
				stderrChan <- stderrScanner.Text()
			}
			resWg.Done()
		}()

		go func() {
			resWg.Wait()
			// close all of our open resources
			res <- struct{}{}
		}()

		select {
		case <-res:
			errChan <- session.Wait()
			doneChan <- true
		case <-timeoutChan:
			stderrChan <- "Run Command Timeout!"
			errChan <- nil
			doneChan <- false
		}
	}(stdoutScanner, stderrScanner, stdoutChan, stderrChan, doneChan, errChan)

	return stdoutChan, stderrChan, doneChan, errChan, err
}

// Run command on remote machine and returns its stdout as a string
func (ssh_conf *MakeConfig) Run(command string, timeout ...time.Duration) (outStr string, errStr string, isTimeout bool, err error) {
	stdoutChan, stderrChan, doneChan, errChan, err := ssh_conf.Stream(command, timeout...)
	if err != nil {
		return outStr, errStr, isTimeout, err
	}
	// read from the output channel until the done signal is passed
loop:
	for {
		select {
		case isTimeout = <-doneChan:
			break loop
		case outline := <-stdoutChan:
			if outline != "" {
				outStr += outline + "\n"
			}
		case errline := <-stderrChan:
			if errline != "" {
				errStr += errline + "\n"
			}
		case err = <-errChan:
		}
	}
	// return the concatenation of all signals from the output channel
	return outStr, errStr, isTimeout, err
}

// Scp uploads sourceFile to remote machine like native scp console app.
func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error {
	session, client, err := ssh_conf.Connect()

	if err != nil {
		return err
	}
	defer client.Close()
	defer session.Close()

	targetFile := filepath.Base(etargetFile)

	src, srcErr := os.Open(sourceFile)

	if srcErr != nil {
		return srcErr
	}

	srcStat, statErr := src.Stat()

	if statErr != nil {
		return statErr
	}

	w, err := session.StdinPipe()
	if err != nil {
		return err
	}

	copyF := func() error {
		_, err := fmt.Fprintln(w, "C0644", srcStat.Size(), targetFile)
		if err != nil {
			return err
		}

		if srcStat.Size() > 0 {
			_, err = io.Copy(w, src)
			if err != nil {
				return err
			}
		}

		_, err = fmt.Fprint(w, "\x00")
		if err != nil {
			return err
		}

		return nil
	}

	copyErrC := make(chan error, 1)
	go func() {
		defer w.Close()
		copyErrC <- copyF()
	}()

	err = session.Run(fmt.Sprintf("scp -tr %s", etargetFile))
	if err != nil {
		return err
	}

	err = <-copyErrC
	return err
}
<commit_msg>chore: update cipher config<commit_after>// Package easyssh provides a simple implementation of some SSH protocol
// features in Go. You can simply run a command on a remote server or get a file
// even simpler than native console SSH client. You don't need to think about
// Dials, sessions, defers, or public keys... Let easyssh think about it!
package easyssh

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/ScaleFT/sshkeys"
	"golang.org/x/crypto/ssh"
	"golang.org/x/crypto/ssh/agent"
)

var defaultTimeout = 60 * time.Second

type (
	// MakeConfig Contains main authority information.
	// User field should be a name of user on remote server (ex. john in ssh john@example.com).
	// Server field should be a remote machine address (ex. example.com in ssh john@example.com)
	// Key is a path to private key on your local machine.
	// Port is SSH server port on remote machine.
	// Note: easyssh looking for private key in user's home directory (ex. /home/john + Key).
	// Then ensure your Key begins from '/' (ex. /.ssh/id_rsa)
	MakeConfig struct {
		User         string
		Server       string
		Key          string
		KeyPath      string
		Port         string
		Passphrase   string
		Password     string
		Timeout      time.Duration
		Proxy        DefaultConfig
		Ciphers      []string
		KeyExchanges []string
		Fingerprint  string

		// Enable the use of insecure ciphers and key exchange methods.
		// This enables the use of the the following insecure ciphers and key exchange methods:
		// - aes128-cbc
		// - aes192-cbc
		// - aes256-cbc
		// - 3des-cbc
		// - diffie-hellman-group-exchange-sha256
		// - diffie-hellman-group-exchange-sha1
		// Those algorithms are insecure and may allow plaintext data to be recovered by an attacker.
		UseInsecureCipher bool
	}

	// DefaultConfig for ssh proxy config
	DefaultConfig struct {
		User         string
		Server       string
		Key          string
		KeyPath      string
		Port         string
		Passphrase   string
		Password     string
		Timeout      time.Duration
		Ciphers      []string
		KeyExchanges []string
		Fingerprint  string

		// Enable the use of insecure ciphers and key exchange methods.
		// This enables the use of the the following insecure ciphers and key exchange methods:
		// - aes128-cbc
		// - aes192-cbc
		// - aes256-cbc
		// - 3des-cbc
		// - diffie-hellman-group-exchange-sha256
		// - diffie-hellman-group-exchange-sha1
		// Those algorithms are insecure and may allow plaintext data to be recovered by an attacker.
		UseInsecureCipher bool
	}
)

// returns ssh.Signer from user you running app home path + cutted key path.
// (ex. pubkey,err := getKeyFile("/.ssh/id_rsa") )
func getKeyFile(keypath, passphrase string) (ssh.Signer, error) {
	var pubkey ssh.Signer
	var err error
	buf, err := ioutil.ReadFile(keypath)
	if err != nil {
		return nil, err
	}

	if passphrase != "" {
		pubkey, err = sshkeys.ParseEncryptedPrivateKey(buf, []byte(passphrase))
	} else {
		pubkey, err = ssh.ParsePrivateKey(buf)
	}

	if err != nil {
		return nil, err
	}

	return pubkey, nil
}

// returns *ssh.ClientConfig and io.Closer.
// if io.Closer is not nil, io.Closer.Close() should be called when
// *ssh.ClientConfig is no longer used.
func getSSHConfig(config DefaultConfig) (*ssh.ClientConfig, io.Closer) {
	var sshAgent io.Closer

	// auths holds the detected ssh auth methods
	auths := []ssh.AuthMethod{}

	// figure out what auths are requested, what is supported
	if config.Password != "" {
		auths = append(auths, ssh.Password(config.Password))
	}
	if config.KeyPath != "" {
		if pubkey, err := getKeyFile(config.KeyPath, config.Passphrase); err != nil {
			log.Printf("getKeyFile error: %v\n", err)
		} else {
			auths = append(auths, ssh.PublicKeys(pubkey))
		}
	}

	if config.Key != "" {
		var signer ssh.Signer
		var err error
		if config.Passphrase != "" {
			signer, err = sshkeys.ParseEncryptedPrivateKey([]byte(config.Key), []byte(config.Passphrase))
		} else {
			signer, err = ssh.ParsePrivateKey([]byte(config.Key))
		}

		if err != nil {
			log.Printf("ssh.ParsePrivateKey: %v\n", err)
		} else {
			auths = append(auths, ssh.PublicKeys(signer))
		}
	}

	if sshAgent, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")); err == nil {
		auths = append(auths, ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers))
	}

	c := ssh.Config{}
	if config.UseInsecureCipher {
		c.SetDefaults()
		c.Ciphers = append(c.Ciphers, "aes128-cbc", "aes192-cbc", "aes256-cbc", "3des-cbc")
		c.KeyExchanges = append(c.KeyExchanges, "diffie-hellman-group-exchange-sha1", "diffie-hellman-group-exchange-sha256")
	}

	if len(config.Ciphers) > 0 {
		c.Ciphers = append(c.Ciphers, config.Ciphers...)
	}

	if len(config.KeyExchanges) > 0 {
		c.KeyExchanges = append(c.KeyExchanges, config.KeyExchanges...)
	}

	hostKeyCallback := ssh.InsecureIgnoreHostKey()
	if config.Fingerprint != "" {
		hostKeyCallback = func(hostname string, remote net.Addr, publicKey ssh.PublicKey) error {
			if ssh.FingerprintSHA256(publicKey) != config.Fingerprint {
				return fmt.Errorf("ssh: host key fingerprint mismatch")
			}
			return nil
		}
	}

	return &ssh.ClientConfig{
		Config:          c,
		Timeout:         config.Timeout,
		User:            config.User,
		Auth:            auths,
		HostKeyCallback: hostKeyCallback,
	}, sshAgent
}

// Connect to remote server using MakeConfig struct and returns *ssh.Session
func (ssh_conf *MakeConfig) Connect() (*ssh.Session, *ssh.Client, error) {
	var client *ssh.Client
	var err error

	targetConfig, closer := getSSHConfig(DefaultConfig{
		User:              ssh_conf.User,
		Key:               ssh_conf.Key,
		KeyPath:           ssh_conf.KeyPath,
		Passphrase:        ssh_conf.Passphrase,
		Password:          ssh_conf.Password,
		Timeout:           ssh_conf.Timeout,
		Ciphers:           ssh_conf.Ciphers,
		KeyExchanges:      ssh_conf.KeyExchanges,
		Fingerprint:       ssh_conf.Fingerprint,
		UseInsecureCipher: ssh_conf.UseInsecureCipher,
	})
	if closer != nil {
		defer closer.Close()
	}

	// Enable proxy command
	if ssh_conf.Proxy.Server != "" {
		proxyConfig, closer := getSSHConfig(DefaultConfig{
			User:              ssh_conf.Proxy.User,
			Key:               ssh_conf.Proxy.Key,
			KeyPath:           ssh_conf.Proxy.KeyPath,
			Passphrase:        ssh_conf.Proxy.Passphrase,
			Password:          ssh_conf.Proxy.Password,
			Timeout:           ssh_conf.Proxy.Timeout,
			Ciphers:           ssh_conf.Proxy.Ciphers,
			KeyExchanges:      ssh_conf.Proxy.KeyExchanges,
			Fingerprint:       ssh_conf.Proxy.Fingerprint,
			UseInsecureCipher: ssh_conf.Proxy.UseInsecureCipher,
		})
		if closer != nil {
			defer closer.Close()
		}

		proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Proxy.Server, ssh_conf.Proxy.Port), proxyConfig)
		if err != nil {
			return nil, nil, err
		}

		conn, err := proxyClient.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port))
		if err != nil {
			return nil, nil, err
		}

		ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)
		if err != nil {
			return nil, nil, err
		}

		client = ssh.NewClient(ncc, chans, reqs)
	} else {
		client, err = ssh.Dial("tcp", net.JoinHostPort(ssh_conf.Server, ssh_conf.Port), targetConfig)
		if err != nil {
			return nil, nil, err
		}
	}

	session, err := client.NewSession()
	if err != nil {
		return nil, nil, err
	}

	return session, client, nil
}

// Stream returns one channel that combines the stdout and stderr of the command
// as it is run on the remote machine, and another that sends true when the
// command is done. The sessions and channels will then be closed.
func (ssh_conf *MakeConfig) Stream(command string, timeout ...time.Duration) (<-chan string, <-chan string, <-chan bool, <-chan error, error) {
	// continuously send the command's output over the channel
	stdoutChan := make(chan string)
	stderrChan := make(chan string)
	doneChan := make(chan bool)
	errChan := make(chan error)

	// connect to remote host
	session, client, err := ssh_conf.Connect()
	if err != nil {
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	// defer session.Close()
	// connect to both outputs (they are of type io.Reader)
	outReader, err := session.StdoutPipe()
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	errReader, err := session.StderrPipe()
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}
	err = session.Start(command)
	if err != nil {
		client.Close()
		session.Close()
		return stdoutChan, stderrChan, doneChan, errChan, err
	}

	// combine outputs, create a line-by-line scanner
	stdoutReader := io.MultiReader(outReader)
	stderrReader := io.MultiReader(errReader)
	stdoutScanner := bufio.NewScanner(stdoutReader)
	stderrScanner := bufio.NewScanner(stderrReader)

	go func(stdoutScanner, stderrScanner *bufio.Scanner, stdoutChan, stderrChan chan string, doneChan chan bool, errChan chan error) {
		defer close(stdoutChan)
		defer close(stderrChan)
		defer close(doneChan)
		defer close(errChan)
		defer client.Close()
		defer session.Close()

		// default timeout value
		executeTimeout := defaultTimeout
		if len(timeout) > 0 {
			executeTimeout = timeout[0]
		}
		timeoutChan := time.After(executeTimeout)
		res := make(chan struct{}, 1)
		var resWg sync.WaitGroup
		resWg.Add(2)

		go func() {
			for stdoutScanner.Scan() {
				stdoutChan <- stdoutScanner.Text()
			}
			resWg.Done()
		}()

		go func() {
			for stderrScanner.Scan() {
				stderrChan <- stderrScanner.Text()
			}
			resWg.Done()
		}()

		go func() {
			resWg.Wait()
			// close all of our open resources
			res <- struct{}{}
		}()

		select {
		case <-res:
			errChan <- session.Wait()
			doneChan <- true
		case <-timeoutChan:
			stderrChan <- "Run Command Timeout!"
			errChan <- nil
			doneChan <- false
		}
	}(stdoutScanner, stderrScanner, stdoutChan, stderrChan, doneChan, errChan)

	return stdoutChan, stderrChan, doneChan, errChan, err
}

// Run command on remote machine and returns its stdout as a string
func (ssh_conf *MakeConfig) Run(command string, timeout ...time.Duration) (outStr string, errStr string, isTimeout bool, err error) {
	stdoutChan, stderrChan, doneChan, errChan, err := ssh_conf.Stream(command, timeout...)
	if err != nil {
		return outStr, errStr, isTimeout, err
	}
	// read from the output channel until the done signal is passed
loop:
	for {
		select {
		case isTimeout = <-doneChan:
			break loop
		case outline := <-stdoutChan:
			if outline != "" {
				outStr += outline + "\n"
			}
		case errline := <-stderrChan:
			if errline != "" {
				errStr += errline + "\n"
			}
		case err = <-errChan:
		}
	}
	// return the concatenation of all signals from the output channel
	return outStr, errStr, isTimeout, err
}

// Scp uploads sourceFile to remote machine like native scp console app.
func (ssh_conf *MakeConfig) Scp(sourceFile string, etargetFile string) error {
	session, client, err := ssh_conf.Connect()

	if err != nil {
		return err
	}
	defer client.Close()
	defer session.Close()

	targetFile := filepath.Base(etargetFile)

	src, srcErr := os.Open(sourceFile)

	if srcErr != nil {
		return srcErr
	}

	srcStat, statErr := src.Stat()

	if statErr != nil {
		return statErr
	}

	w, err := session.StdinPipe()
	if err != nil {
		return err
	}

	copyF := func() error {
		_, err := fmt.Fprintln(w, "C0644", srcStat.Size(), targetFile)
		if err != nil {
			return err
		}

		if srcStat.Size() > 0 {
			_, err = io.Copy(w, src)
			if err != nil {
				return err
			}
		}

		_, err = fmt.Fprint(w, "\x00")
		if err != nil {
			return err
		}

		return nil
	}

	copyErrC := make(chan error, 1)
	go func() {
		defer w.Close()
		copyErrC <- copyF()
	}()

	err = session.Run(fmt.Sprintf("scp -tr %s", etargetFile))
	if err != nil {
		return err
	}

	err = <-copyErrC
	return err
}
<|endoftext|>


<commit_before>package kairos

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	libVersion     = "1.0"
	defaultBaseURL = "http://api.kairos.com/"
	userAgent      = "go-kairos/" + libVersion
)

type (
	// A Client manages communications with the Kairos API.
	Kairos struct {
		// HTTP client
		client *http.Client

		// Base URL for API request
		BaseUrl *url.URL

		// User agent used when communicating with the Kairos API
		UserAgent string

		// App info used to save the id and app key
		AppInfo *url.Userinfo
	}

	// Kairos options
	Options struct {
		selector      string
		symmetricFill bool
	}
)

// New returns a new Kairos API client.
func New(api, appId, appKey string) (*Kairos, error) {
	if api == "" {
		api = defaultBaseURL
	}

	if !strings.HasSuffix(api, "/") {
		api += "/"
	}

	baseUrl, err := url.Parse(api)
	if err != nil {
		return nil, err
	}

	k := &Kairos{
		client:    http.DefaultClient,
		BaseUrl:   baseUrl,
		UserAgent: userAgent,
		AppInfo:   url.UserPassword(appId, appKey),
	}

	return k, nil
}

// Enroll an image
func (k Kairos) Enroll(image string, subjectId, galleryName string, options ...Options) error {
	// TODO
	return nil
}

// Recognize an image
func (k Kairos) Recognize(image string, galleryName string) error {
	// TODO
	return nil
}

// Detect image attributes
func (k Kairos) Detect(image string) error {
	// TODO
	return nil
}

// List galleries
func (k Kairos) ListGalleries() error {
	// TODO
	return nil
}

// View a gallery
func (k Kairos) ViewGallery(galleryName string) error {
	// TODO
	return nil
}

// Remove a gallery
func (k Kairos) RemoveGallery(galleryName string) error {
	// TODO
	return nil
}

// Remove a subject
func (k Kairos) RemoveSubject(subjectId, galleryName string) error {
	// TODO
	return nil
}

func (k *Kairos) newRequest(method, path string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	uri := k.BaseUrl.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, uri.String(), buf)
	if err != nil {
		return nil, err
	}

	// Set up header
	req.Header.Add("User-Agent", k.UserAgent)
	if appInfo := k.AppInfo; appInfo != nil {
		key, _ := appInfo.Password()
		req.Header.Add("app_id", appInfo.Username())
		req.Header.Add("app_key", key)
	}

	return req, nil
}

func (k *Kairos) do(req *http.Request, v interface{}) error {
	resp, err := k.client.Do(req)
	if err != nil {
		return err
	}

	defer resp.Body.Close()

	err = checkResponse(resp)
	if err != nil {
		return err
	}

	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

func checkResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	var errorR interface{}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, &errorR)
	}
	return fmt.Errorf("%+v", errorR)
}
<commit_msg>Added credentials<commit_after>package kairos

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

const (
	libVersion     = "1.0"
	defaultBaseURL = "http://api.kairos.com/"
	userAgent      = "go-kairos/" + libVersion
)

type (
	credential struct {
		apiId  string
		apiKey string
	}

	// A Client manages communications with the Kairos API.
	Kairos struct {
		// HTTP client
		client *http.Client

		// Base URL for API request
		BaseUrl *url.URL

		// User agent used when communicating with the Kairos API
		UserAgent string

		// App info used to save the id and app key
		AppInfo *credential
	}

	// Kairos options
	Options struct {
		selector      string
		symmetricFill bool
	}
)

// New returns a new Kairos API client.
func New(api, appId, appKey string) (*Kairos, error) {
	if api == "" {
		api = defaultBaseURL
	}

	if !strings.HasSuffix(api, "/") {
		api += "/"
	}

	baseUrl, err := url.Parse(api)
	if err != nil {
		return nil, err
	}

	k := &Kairos{
		client:    http.DefaultClient,
		BaseUrl:   baseUrl,
		UserAgent: userAgent,
		AppInfo:   &credential{appId, appKey},
	}

	return k, nil
}

// Enroll an image
func (k Kairos) Enroll(image string, subjectId, galleryName string, options ...Options) error {
	// TODO
	return nil
}

// Recognize an image
func (k Kairos) Recognize(image string, galleryName string) error {
	// TODO
	return nil
}

// Detect image attributes
func (k Kairos) Detect(image string) error {
	// TODO
	return nil
}

// List galleries
func (k Kairos) ListGalleries() error {
	// TODO
	return nil
}

// View a gallery
func (k Kairos) ViewGallery(galleryName string) error {
	// TODO
	return nil
}

// Remove a gallery
func (k Kairos) RemoveGallery(galleryName string) error {
	// TODO
	return nil
}

// Remove a subject
func (k Kairos) RemoveSubject(subjectId, galleryName string) error {
	// TODO
	return nil
}

func (k *Kairos) newRequest(method, path string, body interface{}) (*http.Request, error) {
	rel, err := url.Parse(path)
	if err != nil {
		return nil, err
	}

	uri := k.BaseUrl.ResolveReference(rel)

	buf := new(bytes.Buffer)
	if body != nil {
		err := json.NewEncoder(buf).Encode(body)
		if err != nil {
			return nil, err
		}
	}

	req, err := http.NewRequest(method, uri.String(), buf)
	if err != nil {
		return nil, err
	}

	// Set up header
	req.Header.Add("User-Agent", k.UserAgent)
	if appInfo := k.AppInfo; appInfo != nil {
		req.Header.Add("app_id", appInfo.apiId)
		req.Header.Add("app_key", appInfo.apiKey)
	}

	return req, nil
}

func (k *Kairos) do(req *http.Request, v interface{}) error {
	resp, err := k.client.Do(req)
	if err != nil {
		return err
	}

	defer resp.Body.Close()

	err = checkResponse(resp)
	if err != nil {
		return err
	}

	resBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	if v != nil {
		return json.Unmarshal(resBody, v)
	}

	return nil
}

func checkResponse(r *http.Response) error {
	if c := r.StatusCode; 200 <= c && c <= 299 {
		return nil
	}
	var errorR interface{}
	data, err := ioutil.ReadAll(r.Body)
	if err == nil && data != nil {
		json.Unmarshal(data, &errorR)
	}
	return fmt.Errorf("%+v", errorR)
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build darwin

package client

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"time"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/launchd"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

func NewCmdLaunchd(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "launchd",
		ArgumentHelp: "[arguments...]",
		Subcommands: []cli.Command{
			NewCmdLaunchdInstall(cl, g),
			NewCmdLaunchdUninstall(cl, g),
			NewCmdLaunchdList(cl, g),
			NewCmdLaunchdStatus(cl, g),
			NewCmdLaunchdStart(cl, g),
			NewCmdLaunchdStop(cl, g),
			NewCmdLaunchdRestart(cl, g),
		},
	}
}

func NewCmdLaunchdInstall(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "install",
		ArgumentHelp: "<label> <path/to/keybase> <args>",
		Usage:        "Install a launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			if len(args) < 2 {
				g.Log.Fatalf("No path to keybase executable specified.")
			}

			label := args[0]
			binPath := args[1]
			plistArgs := args[2:]
			envVars := defaultEnvVars(g, label)

			plist := launchd.NewPlist(label, binPath, plistArgs, envVars)
			err := launchd.Install(plist, os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdUninstall(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "uninstall",
		ArgumentHelp: "<label>",
		Usage:        "Uninstall a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Uninstall(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdList(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:  "list",
		Usage: "List keybase launchd services",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "f, format",
				Usage: "Format for output. Specify 'j' for JSON or blank for default.",
			},
		},
		Action: func(c *cli.Context) {
			cl.SetLogForward(libcmdline.LogForwardNone)
			cl.ChooseCommand(NewCmdLaunchdListRunner(g), "list", c)
		},
	}
}

func NewCmdLaunchdRestart(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "restart",
		ArgumentHelp: "<label>",
		Usage:        "Restart a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Restart(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStart(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "start",
		ArgumentHelp: "<label>",
		Usage:        "Start a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified")
			}
			err := launchd.Start(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStop(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "stop",
		ArgumentHelp: "<label>",
		Usage:        "Stop a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Stop(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStatus(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name: "status",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "b, bundle-version",
				Usage: "Bundle version",
			},
			cli.StringFlag{
				Name:  "f, format",
				Usage: "Format for output. Specify 'j' for JSON or blank for default.",
			},
		},
		ArgumentHelp: "<service-name>",
		Usage:        "Status for keybase launchd service, including for installing or updating",
		Action: func(c *cli.Context) {
			// This is to bypass the logui protocol registration in main.go which is
			// triggering a connection before our Run() is called. See that file for
			// more info.
			cl.SetLogForward(libcmdline.LogForwardNone)
			cl.ChooseCommand(NewCmdLaunchdStatusRunner(g), "status", c)
		},
	}
}

type CmdLaunchdList struct {
	libkb.Contextified
	format string
}

func NewCmdLaunchdListRunner(g *libkb.GlobalContext) *CmdLaunchdList {
	return &CmdLaunchdList{
		Contextified: libkb.NewContextified(g),
	}
}

func (v *CmdLaunchdList) GetUsage() libkb.Usage {
	return libkb.Usage{}
}

func (v *CmdLaunchdList) ParseArgv(ctx *cli.Context) error {
	v.format = ctx.String("format")
	return nil
}

func (v *CmdLaunchdList) Run() error {
	if v.format == "json" {
		servicesStatus, err := ListServices()
		if err != nil {
			return err
		}
		out, err := json.MarshalIndent(servicesStatus, "", "  ")
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stdout, "%s\n", out)
	} else if v.format == "" {
		return ShowServices(v.G().UI.GetTerminalUI().OutputWriter())
	} else {
		return fmt.Errorf("Invalid format: %s", v.format)
	}
	return nil
}

type CmdLaunchdStatus struct {
	libkb.Contextified
	format        string
	label         string
	name          string
	bundleVersion string
}

func NewCmdLaunchdStatusRunner(g *libkb.GlobalContext) *CmdLaunchdStatus {
	return &CmdLaunchdStatus{
		Contextified: libkb.NewContextified(g),
	}
}

func (v *CmdLaunchdStatus) GetUsage() libkb.Usage {
	return libkb.Usage{}
}

func (v *CmdLaunchdStatus) ParseArgv(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) < 1 {
		return fmt.Errorf("No service name specified.")
	}
	v.name = args[0]
	v.bundleVersion = ctx.String("bundle-version")
	v.format = ctx.String("format")
	return nil
}

func (v *CmdLaunchdStatus) Run() error {
	var st keybase1.ServiceStatus
	if v.name == "service" {
		st = KeybaseServiceStatus(v.G(), v.label, v.bundleVersion)
	} else if v.name == "kbfs" {
		st = KBFSServiceStatus(v.G(), v.label, v.bundleVersion)
	} else {
		return fmt.Errorf("Invalid service name: %s", v.name)
	}

	if v.format == "json" {
		out, err := json.MarshalIndent(st, "", "  ")
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stdout, "%s\n", out)
	} else if v.format == "" {

		fmt.Fprintf(os.Stdout, "%#v\n", st)
	}
	return nil
}

func defaultEnvVars(g *libkb.GlobalContext, label string) map[string]string {
	envVars := make(map[string]string)
	envVars["PATH"] = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
	envVars["KEYBASE_LABEL"] = label
	envVars["KEYBASE_LOG_FORMAT"] = "file"
	envVars["KEYBASE_RUNTIME_DIR"] = g.Env.GetRuntimeDir()
	return envVars
}

func BrewAutoInstall(g *libkb.GlobalContext) error {
	label := defaultBrewServiceLabel(g.Env.GetRunMode())
	if label == "" {
		return fmt.Errorf("No service label to install")
	}

	// Check if plist is installed. If so we're already installed and return.
	plistPath := launchd.PlistDestination(label)
	if _, err := os.Stat(plistPath); err == nil {
		return nil
	}

	// Get the full path to this executable using the brew opt bin directory.
	binName := filepath.Base(os.Args[0])
	binPath := filepath.Join("/usr/local/opt", binName, "bin", binName)
	plistArgs := []string{"service"}
	envVars := defaultEnvVars(g, label)

	plist := launchd.NewPlist(label, binPath, plistArgs, envVars)
	err := launchd.Install(plist, ioutil.Discard)
	if err != nil {
		return err
	}

	// Get service install status. This causes us to pause (with timeout) until
	// the service is up.
	kbService := launchd.NewService(label)
	ServiceStatusFromLaunchd(kbService, path.Join(g.Env.GetRuntimeDir(), "keybased.info"))

	return nil
}

func waitForFile(path string, maxAttempts int, wait time.Duration, reason string) error {
	attempt := 1
	_, err := os.Stat(path)
	for attempt < maxAttempts && os.IsNotExist(err) {
		attempt++
		time.Sleep(wait)
		_, err = os.Stat(path)
	}

	return err
}

func defaultBrewServiceLabel(runMode libkb.RunMode) string {
	name := "homebrew.mxcl.keybase"
	switch runMode {
	case libkb.DevelRunMode:
		return fmt.Sprintf("%s.devel", name)
	case libkb.StagingRunMode:
		return fmt.Sprintf("%s.staging", name)
	case libkb.ProductionRunMode:
		return name
	default:
		return ""
	}
}
<commit_msg>Remove stray code<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

// +build darwin

package client

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/launchd"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
)

func NewCmdLaunchd(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "launchd",
		ArgumentHelp: "[arguments...]",
		Subcommands: []cli.Command{
			NewCmdLaunchdInstall(cl, g),
			NewCmdLaunchdUninstall(cl, g),
			NewCmdLaunchdList(cl, g),
			NewCmdLaunchdStatus(cl, g),
			NewCmdLaunchdStart(cl, g),
			NewCmdLaunchdStop(cl, g),
			NewCmdLaunchdRestart(cl, g),
		},
	}
}

func NewCmdLaunchdInstall(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "install",
		ArgumentHelp: "<label> <path/to/keybase> <args>",
		Usage:        "Install a launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			if len(args) < 2 {
				g.Log.Fatalf("No path to keybase executable specified.")
			}

			label := args[0]
			binPath := args[1]
			plistArgs := args[2:]
			envVars := defaultEnvVars(g, label)

			plist := launchd.NewPlist(label, binPath, plistArgs, envVars)
			err := launchd.Install(plist, os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdUninstall(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "uninstall",
		ArgumentHelp: "<label>",
		Usage:        "Uninstall a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Uninstall(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdList(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:  "list",
		Usage: "List keybase launchd services",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "f, format",
				Usage: "Format for output. Specify 'j' for JSON or blank for default.",
			},
		},
		Action: func(c *cli.Context) {
			cl.SetLogForward(libcmdline.LogForwardNone)
			cl.ChooseCommand(NewCmdLaunchdListRunner(g), "list", c)
		},
	}
}

func NewCmdLaunchdRestart(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "restart",
		ArgumentHelp: "<label>",
		Usage:        "Restart a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Restart(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStart(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "start",
		ArgumentHelp: "<label>",
		Usage:        "Start a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified")
			}
			err := launchd.Start(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStop(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "stop",
		ArgumentHelp: "<label>",
		Usage:        "Stop a keybase launchd service",
		Action: func(c *cli.Context) {
			// TODO: Use ChooseCommand
			args := c.Args()
			if len(args) < 1 {
				g.Log.Fatalf("No label specified.")
			}
			err := launchd.Stop(args[0], os.Stdout)
			if err != nil {
				g.Log.Fatalf("%v", err)
			}
			os.Exit(0)
		},
	}
}

func NewCmdLaunchdStatus(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name: "status",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "b, bundle-version",
				Usage: "Bundle version",
			},
			cli.StringFlag{
				Name:  "f, format",
				Usage: "Format for output. Specify 'j' for JSON or blank for default.",
			},
		},
		ArgumentHelp: "<service-name>",
		Usage:        "Status for keybase launchd service, including for installing or updating",
		Action: func(c *cli.Context) {
			// This is to bypass the logui protocol registration in main.go which is
			// triggering a connection before our Run() is called. See that file for
			// more info.
			cl.SetLogForward(libcmdline.LogForwardNone)
			cl.ChooseCommand(NewCmdLaunchdStatusRunner(g), "status", c)
		},
	}
}

type CmdLaunchdList struct {
	libkb.Contextified
	format string
}

func NewCmdLaunchdListRunner(g *libkb.GlobalContext) *CmdLaunchdList {
	return &CmdLaunchdList{
		Contextified: libkb.NewContextified(g),
	}
}

func (v *CmdLaunchdList) GetUsage() libkb.Usage {
	return libkb.Usage{}
}

func (v *CmdLaunchdList) ParseArgv(ctx *cli.Context) error {
	v.format = ctx.String("format")
	return nil
}

func (v *CmdLaunchdList) Run() error {
	if v.format == "json" {
		servicesStatus, err := ListServices()
		if err != nil {
			return err
		}
		out, err := json.MarshalIndent(servicesStatus, "", "  ")
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stdout, "%s\n", out)
	} else if v.format == "" {
		return ShowServices(v.G().UI.GetTerminalUI().OutputWriter())
	} else {
		return fmt.Errorf("Invalid format: %s", v.format)
	}
	return nil
}

type CmdLaunchdStatus struct {
	libkb.Contextified
	format        string
	label         string
	name          string
	bundleVersion string
}

func NewCmdLaunchdStatusRunner(g *libkb.GlobalContext) *CmdLaunchdStatus {
	return &CmdLaunchdStatus{
		Contextified: libkb.NewContextified(g),
	}
}

func (v *CmdLaunchdStatus) GetUsage() libkb.Usage {
	return libkb.Usage{}
}

func (v *CmdLaunchdStatus) ParseArgv(ctx *cli.Context) error {
	args := ctx.Args()
	if len(args) < 1 {
		return fmt.Errorf("No service name specified.")
	}
	v.name = args[0]
	v.bundleVersion = ctx.String("bundle-version")
	v.format = ctx.String("format")
	return nil
}

func (v *CmdLaunchdStatus) Run() error {
	var st keybase1.ServiceStatus
	if v.name == "service" {
		st = KeybaseServiceStatus(v.G(), v.label, v.bundleVersion)
	} else if v.name == "kbfs" {
		st = KBFSServiceStatus(v.G(), v.label, v.bundleVersion)
	} else {
		return fmt.Errorf("Invalid service name: %s", v.name)
	}

	if v.format == "json" {
		out, err := json.MarshalIndent(st, "", "  ")
		if err != nil {
			return err
		}
		fmt.Fprintf(os.Stdout, "%s\n", out)
	} else if v.format == "" {

		fmt.Fprintf(os.Stdout, "%#v\n", st)
	}
	return nil
}

func defaultEnvVars(g *libkb.GlobalContext, label string) map[string]string {
	envVars := make(map[string]string)
	envVars["PATH"] = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin"
	envVars["KEYBASE_LABEL"] = label
	envVars["KEYBASE_LOG_FORMAT"] = "file"
	envVars["KEYBASE_RUNTIME_DIR"] = g.Env.GetRuntimeDir()
	return envVars
}

func BrewAutoInstall(g *libkb.GlobalContext) error {
	label := defaultBrewServiceLabel(g.Env.GetRunMode())
	if label == "" {
		return fmt.Errorf("No service label to install")
	}

	// Check if plist is installed. If so we're already installed and return.
	plistPath := launchd.PlistDestination(label)
	if _, err := os.Stat(plistPath); err == nil {
		return nil
	}

	// Get the full path to this executable using the brew opt bin directory.
	binName := filepath.Base(os.Args[0])
	binPath := filepath.Join("/usr/local/opt", binName, "bin", binName)
	plistArgs := []string{"service"}
	envVars := defaultEnvVars(g, label)

	plist := launchd.NewPlist(label, binPath, plistArgs, envVars)
	err := launchd.Install(plist, ioutil.Discard)
	if err != nil {
		return err
	}

	// Get service install status. This causes us to pause (with timeout) until
	// the service is up.
	kbService := launchd.NewService(label)
	ServiceStatusFromLaunchd(kbService, path.Join(g.Env.GetRuntimeDir(), "keybased.info"))

	return nil
}

func defaultBrewServiceLabel(runMode libkb.RunMode) string {
	name := "homebrew.mxcl.keybase"
	switch runMode {
	case libkb.DevelRunMode:
		return fmt.Sprintf("%s.devel", name)
	case libkb.StagingRunMode:
		return fmt.Sprintf("%s.staging", name)
	case libkb.ProductionRunMode:
		return name
	default:
		return ""
	}
}
<|endoftext|>


<commit_before>package eventbus

import (
	"sort"
	"testing"
	"time"

	"go.skia.org/infra/go/geventbus"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/util"

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

const GLOBAL_TOPIC = "global-topic"
const LOCAL_TOPIC = "local-topic"

const NSQD_ADDR = "127.0.0.1:4150"

type testType struct {
	ID    int
	Value string
}

func init() {
	RegisterGlobalEvent(GLOBAL_TOPIC, util.JSONCodec(&testType{}))
}

func TestEventBus(t *testing.T) {
	eventBus := New(nil)

	ch := make(chan int, 5)
	eventBus.SubscribeAsync("topic1", func(e interface{}) { ch <- 1 })
	eventBus.SubscribeAsync("topic2", func(e interface{}) { ch <- (e.(int)) + 1 })
	eventBus.SubscribeAsync("topic2", func(e interface{}) { ch <- e.(int) })

	eventBus.Publish("topic1", nil)
	eventBus.Publish("topic2", 2)
	eventBus.Wait("topic1")
	eventBus.Wait("topic2")
	assert.Equal(t, 3, len(ch))

	vals := []int{<-ch, <-ch, <-ch}
	sort.Ints(vals)
	assert.Equal(t, []int{1, 2, 3}, vals)
}

func TestEventBusGlobally(t *testing.T) {
	testutils.SkipIfShort(t)

	globalEventBus, err := geventbus.NewNSQEventBus(NSQD_ADDR)
	assert.Nil(t, err)

	secondGlobalBus, err := geventbus.NewNSQEventBus(NSQD_ADDR)
	assert.Nil(t, err)

	eventBus := New(globalEventBus)
	ch := make(chan interface{}, 100)
	eventBus.SubscribeAsync(GLOBAL_TOPIC, func(e interface{}) { ch <- e })

	secondCh := make(chan interface{}, 100)
	errCh := make(chan error, 100)
	assert.Nil(t, secondGlobalBus.SubscribeAsync(GLOBAL_TOPIC, geventbus.JSONCallback(&testType{}, func(data interface{}, err error) {
		if err != nil {
			errCh <- err
		} else {
			secondCh <- data
		}
	})))

	eventBus.Publish(GLOBAL_TOPIC, &testType{0, "message-1"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{1, "message-2"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{2, "message-3"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{3, "message-4"})
	time.Sleep(5 * time.Second)
	assert.Equal(t, 4, len(ch))
	assert.Equal(t, 4, len(secondCh))
	close(ch)
	close(secondCh)

	found := map[int]bool{}
	for m := range ch {
		assert.IsType(t, &testType{}, m)
		temp := m.(*testType)
		assert.False(t, found[temp.ID])
		found[temp.ID] = true
	}

	for m := range secondCh {
		assert.IsType(t, &testType{}, m)
	}
}
<commit_msg>Temporarily disable TestEventBusGlobally<commit_after>package eventbus

import (
	"sort"
	"testing"
	"time"

	"go.skia.org/infra/go/geventbus"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/util"

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

const GLOBAL_TOPIC = "global-topic"
const LOCAL_TOPIC = "local-topic"

const NSQD_ADDR = "127.0.0.1:4150"

type testType struct {
	ID    int
	Value string
}

func init() {
	RegisterGlobalEvent(GLOBAL_TOPIC, util.JSONCodec(&testType{}))
}

func TestEventBus(t *testing.T) {
	eventBus := New(nil)

	ch := make(chan int, 5)
	eventBus.SubscribeAsync("topic1", func(e interface{}) { ch <- 1 })
	eventBus.SubscribeAsync("topic2", func(e interface{}) { ch <- (e.(int)) + 1 })
	eventBus.SubscribeAsync("topic2", func(e interface{}) { ch <- e.(int) })

	eventBus.Publish("topic1", nil)
	eventBus.Publish("topic2", 2)
	eventBus.Wait("topic1")
	eventBus.Wait("topic2")
	assert.Equal(t, 3, len(ch))

	vals := []int{<-ch, <-ch, <-ch}
	sort.Ints(vals)
	assert.Equal(t, []int{1, 2, 3}, vals)
}

// TODO(stephana): TestEventBusGlobally is temporarilly disabled because it is
// flaky. To be re-enable once fixed.
func xTestEventBusGlobally(t *testing.T) {
	testutils.SkipIfShort(t)

	globalEventBus, err := geventbus.NewNSQEventBus(NSQD_ADDR)
	assert.Nil(t, err)

	secondGlobalBus, err := geventbus.NewNSQEventBus(NSQD_ADDR)
	assert.Nil(t, err)

	eventBus := New(globalEventBus)
	ch := make(chan interface{}, 100)
	eventBus.SubscribeAsync(GLOBAL_TOPIC, func(e interface{}) { ch <- e })

	secondCh := make(chan interface{}, 100)
	errCh := make(chan error, 100)
	assert.Nil(t, secondGlobalBus.SubscribeAsync(GLOBAL_TOPIC, geventbus.JSONCallback(&testType{}, func(data interface{}, err error) {
		if err != nil {
			errCh <- err
		} else {
			secondCh <- data
		}
	})))

	eventBus.Publish(GLOBAL_TOPIC, &testType{0, "message-1"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{1, "message-2"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{2, "message-3"})
	eventBus.Publish(GLOBAL_TOPIC, &testType{3, "message-4"})
	time.Sleep(5 * time.Second)
	assert.Equal(t, 4, len(ch))
	assert.Equal(t, 4, len(secondCh))
	close(ch)
	close(secondCh)

	found := map[int]bool{}
	for m := range ch {
		assert.IsType(t, &testType{}, m)
		temp := m.(*testType)
		assert.False(t, found[temp.ID])
		found[temp.ID] = true
	}

	for m := range secondCh {
		assert.IsType(t, &testType{}, m)
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"context"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/OJ/gobuster/v3/libgobuster"
)

func ruler() {
	fmt.Println("=====================================================")
}

func banner() {
	fmt.Printf("Gobuster v%s              OJ Reeves (@TheColonial)\n", libgobuster.VERSION)
}

func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup) {
	defer wg.Done()

	var f *os.File
	var err error
	if filename != "" {
		f, err = os.Create(filename)
		if err != nil {
			log.Fatalf("error on creating output file: %v", err)
		}
		defer f.Close()
	}

	for r := range g.Results() {
		s, err := r.ToString(g)
		if err != nil {
			log.Fatal(err)
		}
		if s != "" {
			g.ClearProgress()
			s = strings.TrimSpace(s)
			fmt.Println(s)
			if f != nil {
				err = writeToFile(f, s)
				if err != nil {
					log.Fatalf("error on writing output file: %v", err)
				}
			}
		}
	}
}

func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup) {
	defer wg.Done()

	for e := range g.Errors() {
		if !g.Opts.Quiet {
			g.ClearProgress()
			log.Printf("[!] %v", e)
		}
	}
}

func progressWorker(c context.Context, g *libgobuster.Gobuster, wg *sync.WaitGroup) {
	defer wg.Done()

	tick := time.NewTicker(1 * time.Second)

	for {
		select {
		case <-tick.C:
			g.PrintProgress()
		case <-c.Done():
			return
		}
	}
}

func writeToFile(f *os.File, output string) error {
	_, err := f.WriteString(fmt.Sprintf("%s\n", output))
	if err != nil {
		return fmt.Errorf("[!] Unable to write to file %v", err)
	}
	return nil
}

// Gobuster is the main entry point for the CLI
func Gobuster(prevCtx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin) error {
	// Sanity checks
	if opts == nil {
		return fmt.Errorf("please provide valid options")
	}

	if plugin == nil {
		return fmt.Errorf("please provide a valid plugin")
	}

	ctx, cancel := context.WithCancel(prevCtx)
	defer cancel()

	gobuster, err := libgobuster.NewGobuster(ctx, opts, plugin)
	if err != nil {
		log.Fatalf("[!] %v", err)
	}

	if !opts.Quiet {
		fmt.Println("")
		ruler()
		banner()
		ruler()
		c, err := gobuster.GetConfigString()
		if err != nil {
			log.Fatalf("error on creating config string: %v", err)
		}
		fmt.Println(c)
		ruler()
		log.Println("Starting gobuster")
		ruler()
	}

	var wg sync.WaitGroup
	wg.Add(2)
	go errorWorker(gobuster, &wg)
	go resultWorker(gobuster, opts.OutputFilename, &wg)

	if !opts.Quiet && !opts.NoProgress {
		wg.Add(1)
		go progressWorker(ctx, gobuster, &wg)
	}

	if err := gobuster.Start(); err != nil {
		log.Printf("[!] %v", err)
	}

	// call cancel func to free ressources and stop progressFunc
	cancel()
	// wait for all output funcs to finish
	wg.Wait()

	if !opts.Quiet {
		gobuster.ClearProgress()
		ruler()
		log.Println("Finished")
		ruler()
	}
	return nil
}
<commit_msg>comments<commit_after>package cli

import (
	"context"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/OJ/gobuster/v3/libgobuster"
)

func ruler() {
	fmt.Println("=====================================================")
}

func banner() {
	fmt.Printf("Gobuster v%s              OJ Reeves (@TheColonial)\n", libgobuster.VERSION)
}

// resultWorker outputs the results as they come in. This needs to be a range and should not handle
// the context so the channel always has a receiver and libgobuster will not block.
func resultWorker(g *libgobuster.Gobuster, filename string, wg *sync.WaitGroup) {
	defer wg.Done()

	var f *os.File
	var err error
	if filename != "" {
		f, err = os.Create(filename)
		if err != nil {
			log.Fatalf("error on creating output file: %v", err)
		}
		defer f.Close()
	}

	for r := range g.Results() {
		s, err := r.ToString(g)
		if err != nil {
			log.Fatal(err)
		}
		if s != "" {
			g.ClearProgress()
			s = strings.TrimSpace(s)
			fmt.Println(s)
			if f != nil {
				err = writeToFile(f, s)
				if err != nil {
					log.Fatalf("error on writing output file: %v", err)
				}
			}
		}
	}
}

// errorWorker outputs the errors as they come in. This needs to be a range and should not handle
// the context so the channel always has a receiver and libgobuster will not block.
func errorWorker(g *libgobuster.Gobuster, wg *sync.WaitGroup) {
	defer wg.Done()

	for e := range g.Errors() {
		if !g.Opts.Quiet {
			g.ClearProgress()
			log.Printf("[!] %v", e)
		}
	}
}

// progressWorker outputs the progress every tick. It will stop once cancel() is called
// on the context
func progressWorker(c context.Context, g *libgobuster.Gobuster, wg *sync.WaitGroup) {
	defer wg.Done()

	tick := time.NewTicker(1 * time.Second)

	for {
		select {
		case <-tick.C:
			g.PrintProgress()
		case <-c.Done():
			return
		}
	}
}

func writeToFile(f *os.File, output string) error {
	_, err := f.WriteString(fmt.Sprintf("%s\n", output))
	if err != nil {
		return fmt.Errorf("[!] Unable to write to file %v", err)
	}
	return nil
}

// Gobuster is the main entry point for the CLI
func Gobuster(prevCtx context.Context, opts *libgobuster.Options, plugin libgobuster.GobusterPlugin) error {
	// Sanity checks
	if opts == nil {
		return fmt.Errorf("please provide valid options")
	}

	if plugin == nil {
		return fmt.Errorf("please provide a valid plugin")
	}

	ctx, cancel := context.WithCancel(prevCtx)
	defer cancel()

	gobuster, err := libgobuster.NewGobuster(ctx, opts, plugin)
	if err != nil {
		log.Fatalf("[!] %v", err)
	}

	if !opts.Quiet {
		fmt.Println("")
		ruler()
		banner()
		ruler()
		c, err := gobuster.GetConfigString()
		if err != nil {
			log.Fatalf("error on creating config string: %v", err)
		}
		fmt.Println(c)
		ruler()
		log.Println("Starting gobuster")
		ruler()
	}

	// our waitgroup for all goroutines
	// this ensures all goroutines are finished
	// when we call wg.Wait()
	var wg sync.WaitGroup
	// 2 is the number of goroutines we spin up
	wg.Add(2)
	go errorWorker(gobuster, &wg)
	go resultWorker(gobuster, opts.OutputFilename, &wg)

	if !opts.Quiet && !opts.NoProgress {
		// if not quiet add a new workgroup entry and start the goroutine
		wg.Add(1)
		go progressWorker(ctx, gobuster, &wg)
	}

	if err := gobuster.Start(); err != nil {
		log.Printf("[!] %v", err)
	}

	// call cancel func so progressWorker will exit (the only goroutine in this
	// file using the context) and to free ressources
	cancel()
	// wait for all spun up goroutines to finsih (all have to call wg.Done())
	wg.Wait()

	if !opts.Quiet {
		gobuster.ClearProgress()
		ruler()
		log.Println("Finished")
		ruler()
	}
	return nil
}
<|endoftext|>