<commit_before>package cli

import (
	"context"
	"fmt"
	"sort"
	"sync"
	"time"

	"github.com/kopia/kopia/snapshot"
	"github.com/kopia/kopia/snapshot/snapshotfs"
	"github.com/kopia/repo"
)

var (
	migrateCommand      = snapshotCommands.Command("migrate", "Migrate snapshots from another repository")
	migrateSourceConfig = migrateCommand.Flag("source-config", "Configuration file for the source repository").Required().ExistingFile()
	migrateSources      = migrateCommand.Flag("sources", "List of sources to migrate").Strings()
	migrateAll          = migrateCommand.Flag("all", "Migrate all sources").Bool()
	migrateLatestOnly   = migrateCommand.Flag("latest-only", "Only migrate the latest snapshot").Bool()
	migrateIgnoreErrors = migrateCommand.Flag("ignore-errors", "Ignore errors when reading source backup").Bool()
	migrateParallelism  = migrateCommand.Flag("parallelism", "Number of sources to migrate in parallel").Default("1").Int()
)

func runMigrateCommand(ctx context.Context, destRepo *repo.Repository) error {
	sourceRepo, err := repo.Open(ctx, *migrateSourceConfig, mustGetPasswordFromFlags(false, false), applyOptionsFromFlags(nil))
	if err != nil {
		return fmt.Errorf("can't open source repository: %v", err)
	}

	sources, err := getSourcesToMigrate(ctx, sourceRepo)
	if err != nil {
		return fmt.Errorf("can't retrieve sources: %v", err)
	}

	semaphore := make(chan struct{}, *migrateParallelism)
	var wg sync.WaitGroup

	for _, s := range sources {
		uploader := snapshotfs.NewUploader(destRepo)
		uploader.Progress = cliProgress
		uploader.IgnoreFileErrors = *migrateIgnoreErrors
		onCtrlC(uploader.Cancel)

		wg.Add(1)
		semaphore <- struct{}{}
		go func(s snapshot.SourceInfo) {
			defer func() {
				<-semaphore
				wg.Done()
			}()

			if err := migrateSingleSource(ctx, uploader, sourceRepo, destRepo, s); err != nil {
				log.Warningf("unable to migrate source: %v", err)
			}
		}(s)
	}
	wg.Wait()

	return nil
}

func findPreviousSnapshotManifestWithStartTime(ctx context.Context, rep *repo.Repository, sourceInfo snapshot.SourceInfo, startTime time.Time) (*snapshot.Manifest, error) {
	previous, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
	if err != nil {
		return nil, fmt.Errorf("error listing previous backups: %v", err)
	}

	for _, p := range previous {
		if p.StartTime == startTime {
			return p, nil
		}
	}

	return nil, nil
}

func migrateSingleSource(ctx context.Context, uploader *snapshotfs.Uploader, sourceRepo, destRepo *repo.Repository, s snapshot.SourceInfo) error {
	manifests, err := snapshot.ListSnapshotManifests(ctx, sourceRepo, &s)
	if err != nil {
		return err
	}
	snapshots, err := snapshot.LoadSnapshots(ctx, sourceRepo, manifests)
	if err != nil {
		return fmt.Errorf("unable to load snapshot manifests for %v: %v", s, err)
	}

	sort.Slice(snapshots, func(i, j int) bool {
		return snapshots[i].StartTime.Before(snapshots[j].StartTime)
	})

	for _, m := range filterSnapshotsToMigrate(snapshots) {
		if m.IncompleteReason != "" {
			log.Infof("ignoring incomplete %v at %v", s, formatTimestamp(m.StartTime))
			continue
		}
		sourceEntry := snapshotfs.DirectoryEntry(sourceRepo, m.RootObjectID(), nil)

		existing, err := findPreviousSnapshotManifestWithStartTime(ctx, destRepo, m.Source, m.StartTime)
		if err != nil {
			return err
		}
		if existing != nil {
			log.Infof("already migrated %v at %v", s, formatTimestamp(m.StartTime))
			continue
		}

		log.Infof("migrating snapshot of %v at %v", s, formatTimestamp(m.StartTime))
		previousManifest, err := findPreviousSnapshotManifest(ctx, destRepo, m.Source, &m.StartTime)
		if err != nil {
			return err
		}

		newm, err := uploader.Upload(ctx, sourceEntry, m.Source, previousManifest)
		if err != nil {
			return fmt.Errorf("error migrating shapshot %v @ %v: %v", m.Source, m.StartTime, err)
		}

		newm.StartTime = m.StartTime
		newm.EndTime = m.EndTime
		newm.Description = m.Description
		if newm.IncompleteReason == "" {
			if _, err := snapshot.SaveSnapshot(ctx, destRepo, newm); err != nil {
				return fmt.Errorf("cannot save manifest: %v", err)
			}
		}
	}

	return nil
}

func filterSnapshotsToMigrate(s []*snapshot.Manifest) []*snapshot.Manifest {
	if *migrateLatestOnly && len(s) > 0 {
		s = s[0:1]
	}
	return s
}

func getSourcesToMigrate(ctx context.Context, rep *repo.Repository) ([]snapshot.SourceInfo, error) {
	if len(*migrateSources) > 0 {
		var result []snapshot.SourceInfo

		for _, s := range *migrateSources {
			si, err := snapshot.ParseSourceInfo(s, getHostName(), getUserName())
			if err != nil {
				return nil, err
			}

			result = append(result, si)
		}

		return result, nil
	}

	if *migrateAll {
		return snapshot.ListSources(ctx, rep)
	}

	return nil, fmt.Errorf("must specify either --all or --sources")
}

func init() {
	migrateCommand.Action(repositoryAction(runMigrateCommand))
}
<commit_msg>cli: fixed 'repo migrate' cancellation<commit_after>package cli

import (
	"context"
	"fmt"
	"sort"
	"sync"
	"time"

	"github.com/kopia/kopia/snapshot"
	"github.com/kopia/kopia/snapshot/snapshotfs"
	"github.com/kopia/repo"
)

var (
	migrateCommand      = snapshotCommands.Command("migrate", "Migrate snapshots from another repository")
	migrateSourceConfig = migrateCommand.Flag("source-config", "Configuration file for the source repository").Required().ExistingFile()
	migrateSources      = migrateCommand.Flag("sources", "List of sources to migrate").Strings()
	migrateAll          = migrateCommand.Flag("all", "Migrate all sources").Bool()
	migrateLatestOnly   = migrateCommand.Flag("latest-only", "Only migrate the latest snapshot").Bool()
	migrateIgnoreErrors = migrateCommand.Flag("ignore-errors", "Ignore errors when reading source backup").Bool()
	migrateParallelism  = migrateCommand.Flag("parallelism", "Number of sources to migrate in parallel").Default("1").Int()
)

func runMigrateCommand(ctx context.Context, destRepo *repo.Repository) error {
	sourceRepo, err := repo.Open(ctx, *migrateSourceConfig, mustGetPasswordFromFlags(false, false), applyOptionsFromFlags(nil))
	if err != nil {
		return fmt.Errorf("can't open source repository: %v", err)
	}

	sources, err := getSourcesToMigrate(ctx, sourceRepo)
	if err != nil {
		return fmt.Errorf("can't retrieve sources: %v", err)
	}

	semaphore := make(chan struct{}, *migrateParallelism)
	var wg sync.WaitGroup

	var (
		mu              sync.Mutex
		cancelled       bool
		activeUploaders = map[snapshot.SourceInfo]*snapshotfs.Uploader{}
	)

	onCtrlC(func() {
		mu.Lock()
		mu.Unlock()

		if !cancelled {
			cancelled = true
			for s, u := range activeUploaders {
				log.Warningf("cancelling active uploader for %v", s)
				u.Cancel()
			}
		}
	})

	for _, s := range sources {
		// start a new uploader unless already cancelled
		mu.Lock()
		if cancelled {
			mu.Unlock()
			break
		}

		uploader := snapshotfs.NewUploader(destRepo)
		uploader.Progress = cliProgress
		uploader.IgnoreFileErrors = *migrateIgnoreErrors
		activeUploaders[s] = uploader
		mu.Unlock()

		wg.Add(1)
		semaphore <- struct{}{}
		go func(s snapshot.SourceInfo) {
			defer func() {
				mu.Lock()
				delete(activeUploaders, s)
				mu.Unlock()

				<-semaphore
				wg.Done()
			}()

			if err := migrateSingleSource(ctx, uploader, sourceRepo, destRepo, s); err != nil {
				log.Warningf("unable to migrate source: %v", err)
			}
		}(s)
	}
	wg.Wait()

	return nil
}

func findPreviousSnapshotManifestWithStartTime(ctx context.Context, rep *repo.Repository, sourceInfo snapshot.SourceInfo, startTime time.Time) (*snapshot.Manifest, error) {
	previous, err := snapshot.ListSnapshots(ctx, rep, sourceInfo)
	if err != nil {
		return nil, fmt.Errorf("error listing previous backups: %v", err)
	}

	for _, p := range previous {
		if p.StartTime == startTime {
			return p, nil
		}
	}

	return nil, nil
}

func migrateSingleSource(ctx context.Context, uploader *snapshotfs.Uploader, sourceRepo, destRepo *repo.Repository, s snapshot.SourceInfo) error {
	manifests, err := snapshot.ListSnapshotManifests(ctx, sourceRepo, &s)
	if err != nil {
		return err
	}
	snapshots, err := snapshot.LoadSnapshots(ctx, sourceRepo, manifests)
	if err != nil {
		return fmt.Errorf("unable to load snapshot manifests for %v: %v", s, err)
	}

	sort.Slice(snapshots, func(i, j int) bool {
		return snapshots[i].StartTime.Before(snapshots[j].StartTime)
	})

	for _, m := range filterSnapshotsToMigrate(snapshots) {
		if uploader.IsCancelled() {
			break
		}
		if m.IncompleteReason != "" {
			log.Infof("ignoring incomplete %v at %v", s, formatTimestamp(m.StartTime))
			continue
		}
		sourceEntry := snapshotfs.DirectoryEntry(sourceRepo, m.RootObjectID(), nil)

		existing, err := findPreviousSnapshotManifestWithStartTime(ctx, destRepo, m.Source, m.StartTime)
		if err != nil {
			return err
		}
		if existing != nil {
			log.Infof("already migrated %v at %v", s, formatTimestamp(m.StartTime))
			continue
		}

		log.Infof("migrating snapshot of %v at %v", s, formatTimestamp(m.StartTime))
		previousManifest, err := findPreviousSnapshotManifest(ctx, destRepo, m.Source, &m.StartTime)
		if err != nil {
			return err
		}

		newm, err := uploader.Upload(ctx, sourceEntry, m.Source, previousManifest)
		if err != nil {
			return fmt.Errorf("error migrating shapshot %v @ %v: %v", m.Source, m.StartTime, err)
		}

		newm.StartTime = m.StartTime
		newm.EndTime = m.EndTime
		newm.Description = m.Description
		if newm.IncompleteReason == "" {
			if _, err := snapshot.SaveSnapshot(ctx, destRepo, newm); err != nil {
				return fmt.Errorf("cannot save manifest: %v", err)
			}
		}
	}

	return nil
}

func filterSnapshotsToMigrate(s []*snapshot.Manifest) []*snapshot.Manifest {
	if *migrateLatestOnly && len(s) > 0 {
		s = s[0:1]
	}
	return s
}

func getSourcesToMigrate(ctx context.Context, rep *repo.Repository) ([]snapshot.SourceInfo, error) {
	if len(*migrateSources) > 0 {
		var result []snapshot.SourceInfo

		for _, s := range *migrateSources {
			si, err := snapshot.ParseSourceInfo(s, getHostName(), getUserName())
			if err != nil {
				return nil, err
			}

			result = append(result, si)
		}

		return result, nil
	}

	if *migrateAll {
		return snapshot.ListSources(ctx, rep)
	}

	return nil, fmt.Errorf("must specify either --all or --sources")
}

func init() {
	migrateCommand.Action(repositoryAction(runMigrateCommand))
}
<|endoftext|>


<commit_before>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package alertmanager

import (
	"fmt"
	"net/url"
	"path"
	"strings"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/apps/v1beta1"

	"github.com/blang/semver"
	"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
	"github.com/pkg/errors"
)

const (
	governingServiceName = "alertmanager-operated"
	defaultVersion       = "v0.7.1"
)

var (
	minReplicas         int32 = 1
	probeTimeoutSeconds int32 = 3
)

func makeStatefulSet(am *v1alpha1.Alertmanager, old *v1beta1.StatefulSet, config Config) (*v1beta1.StatefulSet, error) {
	// TODO(fabxc): is this the right point to inject defaults?
	// Ideally we would do it before storing but that's currently not possible.
	// Potentially an update handler on first insertion.

	if am.Spec.BaseImage == "" {
		am.Spec.BaseImage = config.AlertmanagerDefaultBaseImage
	}
	if am.Spec.Version == "" {
		am.Spec.Version = defaultVersion
	}
	if am.Spec.Replicas != nil && *am.Spec.Replicas < minReplicas {
		am.Spec.Replicas = &minReplicas
	}
	if am.Spec.Resources.Requests == nil {
		am.Spec.Resources.Requests = v1.ResourceList{}
	}
	if _, ok := am.Spec.Resources.Requests[v1.ResourceMemory]; !ok {
		am.Spec.Resources.Requests[v1.ResourceMemory] = resource.MustParse("200Mi")
	}

	spec, err := makeStatefulSetSpec(am, config)
	if err != nil {
		return nil, err
	}
	statefulset := &v1beta1.StatefulSet{
		ObjectMeta: metav1.ObjectMeta{
			Name:        prefixedName(am.Name),
			Labels:      am.ObjectMeta.Labels,
			Annotations: am.ObjectMeta.Annotations,
		},
		Spec: *spec,
	}

	if am.Spec.ImagePullSecrets != nil && len(am.Spec.ImagePullSecrets) > 0 {
		statefulset.Spec.Template.Spec.ImagePullSecrets = am.Spec.ImagePullSecrets
	}

	if vc := am.Spec.Storage; vc == nil {
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: &v1.EmptyDirVolumeSource{},
			},
		})
	} else {
		pvc := v1.PersistentVolumeClaim{
			ObjectMeta: metav1.ObjectMeta{
				Name: volumeName(am.Name),
			},
			Spec: v1.PersistentVolumeClaimSpec{
				AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
				Resources:   vc.Resources,
				Selector:    vc.Selector,
			},
		}
		if len(vc.Class) > 0 {
			pvc.ObjectMeta.Annotations = map[string]string{
				"volume.beta.kubernetes.io/storage-class": vc.Class,
			}
		}
		statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, pvc)
	}

	if old != nil {
		statefulset.Annotations = old.Annotations
	}
	return statefulset, nil
}

func makeStatefulSetService(p *v1alpha1.Alertmanager) *v1.Service {
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: governingServiceName,
			Labels: map[string]string{
				"operated-alertmanager": "true",
			},
		},
		Spec: v1.ServiceSpec{
			ClusterIP: "None",
			Ports: []v1.ServicePort{
				{
					Name:       "web",
					Port:       9093,
					TargetPort: intstr.FromInt(9093),
					Protocol:   v1.ProtocolTCP,
				},
				{
					Name:       "mesh",
					Port:       6783,
					TargetPort: intstr.FromInt(6783),
					Protocol:   v1.ProtocolTCP,
				},
			},
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}
	return svc
}

func makeStatefulSetSpec(a *v1alpha1.Alertmanager, config Config) (*v1beta1.StatefulSetSpec, error) {
	image := fmt.Sprintf("%s:%s", a.Spec.BaseImage, a.Spec.Version)
	versionStr := strings.TrimLeft(a.Spec.Version, "v")

	version, err := semver.Parse(versionStr)
	if err != nil {
		return nil, errors.Wrap(err, "parse version")
	}

	amArgs := []string{
		fmt.Sprintf("-config.file=%s", "/etc/alertmanager/config/alertmanager.yaml"),
		fmt.Sprintf("-web.listen-address=:%d", 9093),
		fmt.Sprintf("-mesh.listen-address=:%d", 6783),
		fmt.Sprintf("-storage.path=%s", "/etc/alertmanager/data"),
	}

	if a.Spec.ExternalURL != "" {
		amArgs = append(amArgs, "-web.external-url="+a.Spec.ExternalURL)
	}

	webRoutePrefix := "/"
	if a.Spec.RoutePrefix != "" {
		webRoutePrefix = a.Spec.RoutePrefix
	}

	switch version.Major {
	case 0:
		if version.Minor >= 7 {
			amArgs = append(amArgs, "-web.route-prefix="+webRoutePrefix)
		}
	default:
		return nil, errors.Errorf("unsupported Alertmanager major version %s", version)
	}

	localReloadURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:9093",
		Path:   path.Clean(webRoutePrefix + "/-/reload"),
	}

	probeHandler := v1.Handler{
		HTTPGet: &v1.HTTPGetAction{
			Path: path.Clean(webRoutePrefix + "/api/v1/status"),
			Port: intstr.FromString("web"),
		},
	}

	for i := int32(0); i < *a.Spec.Replicas; i++ {
		amArgs = append(amArgs, fmt.Sprintf("-mesh.peer=%s-%d.%s.%s.svc", prefixedName(a.Name), i, governingServiceName, a.Namespace))
	}

	terminationGracePeriod := int64(0)
	return &v1beta1.StatefulSetSpec{
		ServiceName: governingServiceName,
		Replicas:    a.Spec.Replicas,
		Template: v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"app":          "alertmanager",
					"alertmanager": a.Name,
				},
			},
			Spec: v1.PodSpec{
				NodeSelector:                  a.Spec.NodeSelector,
				TerminationGracePeriodSeconds: &terminationGracePeriod,
				Containers: []v1.Container{
					{
						Args:  amArgs,
						Name:  "alertmanager",
						Image: image,
						Ports: []v1.ContainerPort{
							{
								Name:          "web",
								ContainerPort: 9093,
								Protocol:      v1.ProtocolTCP,
							},
							{
								Name:          "mesh",
								ContainerPort: 6783,
								Protocol:      v1.ProtocolTCP,
							},
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								MountPath: "/etc/alertmanager/config",
							},
							{
								Name:      volumeName(a.Name),
								MountPath: "/var/alertmanager/data",
								SubPath:   subPathForStorage(a.Spec.Storage),
							},
						},
						LivenessProbe: &v1.Probe{
							Handler:          probeHandler,
							TimeoutSeconds:   probeTimeoutSeconds,
							FailureThreshold: 10,
						},
						ReadinessProbe: &v1.Probe{
							Handler:             probeHandler,
							InitialDelaySeconds: 3,
							TimeoutSeconds:      3,
							PeriodSeconds:       5,
							FailureThreshold:    10,
						},
						Resources: a.Spec.Resources,
					}, {
						Name:  "config-reloader",
						Image: config.ConfigReloaderImage,
						Args: []string{
							fmt.Sprintf("-webhook-url=%s", localReloadURL),
							"-volume-dir=/etc/alertmanager/config",
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								ReadOnly:  true,
								MountPath: "/etc/alertmanager/config",
							},
						},
						Resources: v1.ResourceRequirements{
							Limits: v1.ResourceList{
								v1.ResourceCPU:    resource.MustParse("5m"),
								v1.ResourceMemory: resource.MustParse("10Mi"),
							},
						},
					},
				},
				Volumes: []v1.Volume{
					{
						Name: "config-volume",
						VolumeSource: v1.VolumeSource{
							Secret: &v1.SecretVolumeSource{
								SecretName: configSecretName(a.Name),
							},
						},
					},
				},
			},
		},
	}, nil
}

func configSecretName(name string) string {
	return prefixedName(name)
}

func volumeName(name string) string {
	return fmt.Sprintf("%s-db", prefixedName(name))
}

func prefixedName(name string) string {
	return fmt.Sprintf("alertmanager-%s", name)
}

func subPathForStorage(s *v1alpha1.StorageSpec) string {
	if s == nil {
		return ""
	}

	return "alertmanager-db"
}
<commit_msg>alertmanager: adapt storage mounted to prometheus changes<commit_after>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package alertmanager

import (
	"fmt"
	"net/url"
	"path"
	"strings"

	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/pkg/api/v1"
	"k8s.io/client-go/pkg/apis/apps/v1beta1"

	"github.com/blang/semver"
	"github.com/coreos/prometheus-operator/pkg/client/monitoring/v1alpha1"
	"github.com/pkg/errors"
)

const (
	governingServiceName = "alertmanager-operated"
	defaultVersion       = "v0.7.1"
)

var (
	minReplicas         int32 = 1
	probeTimeoutSeconds int32 = 3
)

func makeStatefulSet(am *v1alpha1.Alertmanager, old *v1beta1.StatefulSet, config Config) (*v1beta1.StatefulSet, error) {
	// TODO(fabxc): is this the right point to inject defaults?
	// Ideally we would do it before storing but that's currently not possible.
	// Potentially an update handler on first insertion.

	if am.Spec.BaseImage == "" {
		am.Spec.BaseImage = config.AlertmanagerDefaultBaseImage
	}
	if am.Spec.Version == "" {
		am.Spec.Version = defaultVersion
	}
	if am.Spec.Replicas != nil && *am.Spec.Replicas < minReplicas {
		am.Spec.Replicas = &minReplicas
	}
	if am.Spec.Resources.Requests == nil {
		am.Spec.Resources.Requests = v1.ResourceList{}
	}
	if _, ok := am.Spec.Resources.Requests[v1.ResourceMemory]; !ok {
		am.Spec.Resources.Requests[v1.ResourceMemory] = resource.MustParse("200Mi")
	}

	spec, err := makeStatefulSetSpec(am, config)
	if err != nil {
		return nil, err
	}
	statefulset := &v1beta1.StatefulSet{
		ObjectMeta: metav1.ObjectMeta{
			Name:        prefixedName(am.Name),
			Labels:      am.ObjectMeta.Labels,
			Annotations: am.ObjectMeta.Annotations,
		},
		Spec: *spec,
	}

	if am.Spec.ImagePullSecrets != nil && len(am.Spec.ImagePullSecrets) > 0 {
		statefulset.Spec.Template.Spec.ImagePullSecrets = am.Spec.ImagePullSecrets
	}

	storageSpec := am.Spec.Storage
	if storageSpec == nil {
		statefulset.Spec.Template.Spec.Volumes = append(statefulset.Spec.Template.Spec.Volumes, v1.Volume{
			Name: volumeName(am.Name),
			VolumeSource: v1.VolumeSource{
				EmptyDir: &v1.EmptyDirVolumeSource{},
			},
		})
	} else {
		pvcTemplate := storageSpec.VolumeClaimTemplate
		pvcTemplate.Name = volumeName(am.Name)
		pvcTemplate.Spec.AccessModes = []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}
		pvcTemplate.Spec.Resources = storageSpec.VolumeClaimTemplate.Spec.Resources
		pvcTemplate.Spec.Selector = storageSpec.VolumeClaimTemplate.Spec.Selector
		statefulset.Spec.VolumeClaimTemplates = append(statefulset.Spec.VolumeClaimTemplates, pvcTemplate)
	}

	if old != nil {
		statefulset.Annotations = old.Annotations
	}
	return statefulset, nil
}

func makeStatefulSetService(p *v1alpha1.Alertmanager) *v1.Service {
	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: governingServiceName,
			Labels: map[string]string{
				"operated-alertmanager": "true",
			},
		},
		Spec: v1.ServiceSpec{
			ClusterIP: "None",
			Ports: []v1.ServicePort{
				{
					Name:       "web",
					Port:       9093,
					TargetPort: intstr.FromInt(9093),
					Protocol:   v1.ProtocolTCP,
				},
				{
					Name:       "mesh",
					Port:       6783,
					TargetPort: intstr.FromInt(6783),
					Protocol:   v1.ProtocolTCP,
				},
			},
			Selector: map[string]string{
				"app": "alertmanager",
			},
		},
	}
	return svc
}

func makeStatefulSetSpec(a *v1alpha1.Alertmanager, config Config) (*v1beta1.StatefulSetSpec, error) {
	image := fmt.Sprintf("%s:%s", a.Spec.BaseImage, a.Spec.Version)
	versionStr := strings.TrimLeft(a.Spec.Version, "v")

	version, err := semver.Parse(versionStr)
	if err != nil {
		return nil, errors.Wrap(err, "parse version")
	}

	amArgs := []string{
		fmt.Sprintf("-config.file=%s", "/etc/alertmanager/config/alertmanager.yaml"),
		fmt.Sprintf("-web.listen-address=:%d", 9093),
		fmt.Sprintf("-mesh.listen-address=:%d", 6783),
		fmt.Sprintf("-storage.path=%s", "/etc/alertmanager/data"),
	}

	if a.Spec.ExternalURL != "" {
		amArgs = append(amArgs, "-web.external-url="+a.Spec.ExternalURL)
	}

	webRoutePrefix := "/"
	if a.Spec.RoutePrefix != "" {
		webRoutePrefix = a.Spec.RoutePrefix
	}

	switch version.Major {
	case 0:
		if version.Minor >= 7 {
			amArgs = append(amArgs, "-web.route-prefix="+webRoutePrefix)
		}
	default:
		return nil, errors.Errorf("unsupported Alertmanager major version %s", version)
	}

	localReloadURL := &url.URL{
		Scheme: "http",
		Host:   "localhost:9093",
		Path:   path.Clean(webRoutePrefix + "/-/reload"),
	}

	probeHandler := v1.Handler{
		HTTPGet: &v1.HTTPGetAction{
			Path: path.Clean(webRoutePrefix + "/api/v1/status"),
			Port: intstr.FromString("web"),
		},
	}

	for i := int32(0); i < *a.Spec.Replicas; i++ {
		amArgs = append(amArgs, fmt.Sprintf("-mesh.peer=%s-%d.%s.%s.svc", prefixedName(a.Name), i, governingServiceName, a.Namespace))
	}

	terminationGracePeriod := int64(0)
	return &v1beta1.StatefulSetSpec{
		ServiceName: governingServiceName,
		Replicas:    a.Spec.Replicas,
		Template: v1.PodTemplateSpec{
			ObjectMeta: metav1.ObjectMeta{
				Labels: map[string]string{
					"app":          "alertmanager",
					"alertmanager": a.Name,
				},
			},
			Spec: v1.PodSpec{
				NodeSelector:                  a.Spec.NodeSelector,
				TerminationGracePeriodSeconds: &terminationGracePeriod,
				Containers: []v1.Container{
					{
						Args:  amArgs,
						Name:  "alertmanager",
						Image: image,
						Ports: []v1.ContainerPort{
							{
								Name:          "web",
								ContainerPort: 9093,
								Protocol:      v1.ProtocolTCP,
							},
							{
								Name:          "mesh",
								ContainerPort: 6783,
								Protocol:      v1.ProtocolTCP,
							},
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								MountPath: "/etc/alertmanager/config",
							},
							{
								Name:      volumeName(a.Name),
								MountPath: "/var/alertmanager/data",
								SubPath:   subPathForStorage(a.Spec.Storage),
							},
						},
						LivenessProbe: &v1.Probe{
							Handler:          probeHandler,
							TimeoutSeconds:   probeTimeoutSeconds,
							FailureThreshold: 10,
						},
						ReadinessProbe: &v1.Probe{
							Handler:             probeHandler,
							InitialDelaySeconds: 3,
							TimeoutSeconds:      3,
							PeriodSeconds:       5,
							FailureThreshold:    10,
						},
						Resources: a.Spec.Resources,
					}, {
						Name:  "config-reloader",
						Image: config.ConfigReloaderImage,
						Args: []string{
							fmt.Sprintf("-webhook-url=%s", localReloadURL),
							"-volume-dir=/etc/alertmanager/config",
						},
						VolumeMounts: []v1.VolumeMount{
							{
								Name:      "config-volume",
								ReadOnly:  true,
								MountPath: "/etc/alertmanager/config",
							},
						},
						Resources: v1.ResourceRequirements{
							Limits: v1.ResourceList{
								v1.ResourceCPU:    resource.MustParse("5m"),
								v1.ResourceMemory: resource.MustParse("10Mi"),
							},
						},
					},
				},
				Volumes: []v1.Volume{
					{
						Name: "config-volume",
						VolumeSource: v1.VolumeSource{
							Secret: &v1.SecretVolumeSource{
								SecretName: configSecretName(a.Name),
							},
						},
					},
				},
			},
		},
	}, nil
}

func configSecretName(name string) string {
	return prefixedName(name)
}

func volumeName(name string) string {
	return fmt.Sprintf("%s-db", prefixedName(name))
}

func prefixedName(name string) string {
	return fmt.Sprintf("alertmanager-%s", name)
}

func subPathForStorage(s *v1alpha1.StorageSpec) string {
	if s == nil {
		return ""
	}

	return "alertmanager-db"
}
<|endoftext|>


<commit_before>// Copyright 2017-2020 The Usacloud Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"errors"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/profile"
	"github.com/sacloud/usacloud/pkg/cli"
	"github.com/sacloud/usacloud/pkg/cmd/core"
	"github.com/sacloud/usacloud/pkg/config"
	"github.com/sacloud/usacloud/pkg/printer"
	"github.com/sacloud/usacloud/pkg/term"
	"github.com/sacloud/usacloud/pkg/validate"
)

var editCommand = &core.Command{
	Name:       "edit",
	Category:   "basic",
	Order:      25,
	NoProgress: true,

	ParameterInitializer: func() interface{} {
		return newEditParameter()
	},

	Func: editProfile,
	ValidateFunc: func(ctx cli.Context, parameter interface{}) error {
		p, ok := parameter.(*EditParameter)
		if !ok {
			return fmt.Errorf("invalid parameter: %v", parameter)
		}
		if len(ctx.Args()) > 0 && p.Name == "" {
			p.Name = ctx.Args()[0]
		}

		if err := validate.Exec(p); err != nil {
			return err
		}

		if !term.IsTerminal() && !p.hasValue() {
			return errors.New("stdin or stdout is not a terminal. please specify values via flags")
		}

		return nil
	},
	CustomCompletionFunc: profileCompletion,
}

type EditParameter struct {
	Name              string `validate:"omitempty,profile_name"`
	AccessToken       string `cli:"access-token,aliases=token"`
	AccessTokenSecret string `cli:"access-token-secret,aliases=secret"`
	Zone              string `validate:"omitempty,zone"`
	DefaultOutputType string `validate:"omitempty,output_type"`
	NoColor           bool
	Use               bool
}

func (p *EditParameter) hasValue() bool {
	return p.AccessToken != "" || p.AccessTokenSecret != "" || p.Zone != "" || p.DefaultOutputType != ""
}

func newEditParameter() *EditParameter {
	return &EditParameter{}
}

func init() {
	Resource.AddCommand(editCommand)
}

func editProfile(ctx cli.Context, parameter interface{}) ([]interface{}, error) {
	p, ok := parameter.(*EditParameter)
	if !ok {
		return nil, fmt.Errorf("invalid parameter: %v", parameter)
	}

	if len(ctx.Args()) > 0 && p.Name == "" {
		p.Name = ctx.Args()[0]
	}

	if p.Name == "" {
		current, err := profile.CurrentName()
		if err != nil {
			return nil, err
		}
		p.Name = current
	}

	newConfigValue := &config.Config{
		ConfigValue: profile.ConfigValue{
			AccessToken:       p.AccessToken,
			AccessTokenSecret: p.AccessTokenSecret,
			Zone:              p.Zone,
		},
		Profile:           p.Name,
		DefaultOutputType: p.DefaultOutputType,
	}

	currentConfig, err := getProfileConfigValue(p.Name)
	if err != nil {
		return nil, err
	}
	if currentConfig == nil {
		currentConfig = &config.Config{}
	}

	out := ctx.IO().Out()
	in := ctx.IO().In()
	msgWriter := &printer.Printer{NoColor: p.NoColor}

	// access token
	if newConfigValue.AccessToken == "" {
		msg := "\nSetting SakuraCloud API Token => "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.AccessToken != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.AccessToken)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change token setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			// read input
			var input string
			fmt.Fprintf(out, "\t%s: ", "Enter token")
			fmt.Fscanln(in, &input)
			currentConfig.AccessToken = input
		}
	}

	if newConfigValue.AccessTokenSecret == "" {
		msg := "\nSetting SakuraCloud API Secret=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.AccessTokenSecret != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.AccessTokenSecret)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change secret setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			// read input
			var input string
			fmt.Fprintf(out, "\t%s: ", "Enter secret")
			fmt.Fscanln(in, &input)
			currentConfig.AccessTokenSecret = input
		}
	}

	if newConfigValue.Zone == "" {
		msg := "\nSetting SakuraCloud Zone=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.Zone != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.Zone)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change zone setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			zones := currentConfig.Zones
			if len(zones) == 0 {
				zones = sacloud.SakuraCloudZones
			}
			// read input
			var input string
			for {
				fmt.Fprintf(out, "\t%s[%s]: ", "Enter Zone", strings.Join(zones, "/"))
				fmt.Fscanln(in, &input)
				if input == "" || containsString(zones, input) {
					break
				}
				fmt.Fprintf(out, "Invalid value. Please input again\n")
			}
			currentConfig.Zone = input
		}
	}

	if newConfigValue.DefaultOutputType == "" {
		msg := "\nSetting Default Output Type=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.DefaultOutputType != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.DefaultOutputType)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change default output type setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			outputTypes := []string{"table", "json", "yaml"}
			// read input
			var input string
			for {
				fmt.Fprintf(out, "\t%s[%s]: ", "Enter Default Output Type", strings.Join(outputTypes, "/"))
				fmt.Fscanln(in, &input)
				if input == "" || containsString(outputTypes, input) {
					break
				}
				fmt.Fprintf(out, "Invalid value. Please input again\n")
			}
			currentConfig.DefaultOutputType = input
		}
	}

	if err := profile.Save(p.Name, currentConfig); err != nil {
		return nil, err
	}

	wrote, err := profile.ConfigFilePath(p.Name)
	if err != nil {
		return nil, err
	}
	msgWriter.Fprintf(out, color.New(color.FgHiGreen), "\nWritten your settings to %s\n", wrote)

	current, err := profile.CurrentName()
	if err != nil {
		return nil, err
	}

	// 編集したプロファイルが現在使われていない場合は切り替え
	if current != p.Name {
		if !p.Use {
			if !cli.Confirm(in, fmt.Sprintf("Would you like to switch to profile %q?", p.Name)) {
				return nil, nil
			}
		}
		return nil, profile.SetCurrentName(p.Name)
	}
	return nil, nil
}

func containsString(values []string, v string) bool {
	for _, value := range values {
		if v == value {
			return true
		}
	}
	return false
}
<commit_msg>Fix: Save the value specified by command line option<commit_after>// Copyright 2017-2020 The Usacloud Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
	"errors"
	"fmt"
	"strings"

	"github.com/fatih/color"
	"github.com/sacloud/libsacloud/v2/sacloud"
	"github.com/sacloud/libsacloud/v2/sacloud/profile"
	"github.com/sacloud/usacloud/pkg/cli"
	"github.com/sacloud/usacloud/pkg/cmd/core"
	"github.com/sacloud/usacloud/pkg/config"
	"github.com/sacloud/usacloud/pkg/printer"
	"github.com/sacloud/usacloud/pkg/term"
	"github.com/sacloud/usacloud/pkg/validate"
)

var editCommand = &core.Command{
	Name:       "edit",
	Category:   "basic",
	Order:      25,
	NoProgress: true,

	ParameterInitializer: func() interface{} {
		return newEditParameter()
	},

	Func: editProfile,
	ValidateFunc: func(ctx cli.Context, parameter interface{}) error {
		p, ok := parameter.(*EditParameter)
		if !ok {
			return fmt.Errorf("invalid parameter: %v", parameter)
		}
		if len(ctx.Args()) > 0 && p.Name == "" {
			p.Name = ctx.Args()[0]
		}

		if err := validate.Exec(p); err != nil {
			return err
		}

		if !term.IsTerminal() && !p.hasValue() {
			return errors.New("stdin or stdout is not a terminal. please specify values via flags")
		}

		return nil
	},
	CustomCompletionFunc: profileCompletion,
}

type EditParameter struct {
	Name              string `validate:"omitempty,profile_name"`
	AccessToken       string `cli:"access-token,aliases=token"`
	AccessTokenSecret string `cli:"access-token-secret,aliases=secret"`
	Zone              string `validate:"omitempty,zone"`
	DefaultOutputType string `validate:"omitempty,output_type"`
	NoColor           bool
	Use               bool
}

func (p *EditParameter) hasValue() bool {
	return p.AccessToken != "" || p.AccessTokenSecret != "" || p.Zone != "" || p.DefaultOutputType != ""
}

func newEditParameter() *EditParameter {
	return &EditParameter{}
}

func init() {
	Resource.AddCommand(editCommand)
}

func editProfile(ctx cli.Context, parameter interface{}) ([]interface{}, error) {
	p, ok := parameter.(*EditParameter)
	if !ok {
		return nil, fmt.Errorf("invalid parameter: %v", parameter)
	}

	if len(ctx.Args()) > 0 && p.Name == "" {
		p.Name = ctx.Args()[0]
	}

	if p.Name == "" {
		current, err := profile.CurrentName()
		if err != nil {
			return nil, err
		}
		p.Name = current
	}

	newConfigValue := &config.Config{
		ConfigValue: profile.ConfigValue{
			AccessToken:       p.AccessToken,
			AccessTokenSecret: p.AccessTokenSecret,
			Zone:              p.Zone,
		},
		Profile:           p.Name,
		DefaultOutputType: p.DefaultOutputType,
	}

	currentConfig, err := getProfileConfigValue(p.Name)
	if err != nil {
		return nil, err
	}
	if currentConfig == nil {
		currentConfig = &config.Config{}
	}

	out := ctx.IO().Out()
	in := ctx.IO().In()
	msgWriter := &printer.Printer{NoColor: p.NoColor}

	// Note: currentConfigには未知のキーが入る可能性がある&それを保持しておく必要があるため、
	//       ここではnewConfigValueまたは入力値からcurrentConfigへ上書きする形で実装する

	// access token
	if newConfigValue.AccessToken == "" {
		msg := "\nSetting SakuraCloud API Token => "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.AccessToken != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.AccessToken)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change token setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			// read input
			var input string
			fmt.Fprintf(out, "\t%s: ", "Enter token")
			fmt.Fscanln(in, &input)
			currentConfig.AccessToken = input
		}
	} else {
		currentConfig.AccessToken = newConfigValue.AccessToken
	}

	if newConfigValue.AccessTokenSecret == "" {
		msg := "\nSetting SakuraCloud API Secret=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.AccessTokenSecret != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.AccessTokenSecret)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change secret setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			// read input
			var input string
			fmt.Fprintf(out, "\t%s: ", "Enter secret")
			fmt.Fscanln(in, &input)
			currentConfig.AccessTokenSecret = input
		}
	} else {
		currentConfig.AccessTokenSecret = newConfigValue.AccessTokenSecret
	}

	if newConfigValue.Zone == "" {
		msg := "\nSetting SakuraCloud Zone=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.Zone != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.Zone)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change zone setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			zones := currentConfig.Zones
			if len(zones) == 0 {
				zones = sacloud.SakuraCloudZones
			}
			// read input
			var input string
			for {
				fmt.Fprintf(out, "\t%s[%s]: ", "Enter Zone", strings.Join(zones, "/"))
				fmt.Fscanln(in, &input)
				if input == "" || containsString(zones, input) {
					break
				}
				fmt.Fprintf(out, "Invalid value. Please input again\n")
			}
			currentConfig.Zone = input
		}
	} else {
		currentConfig.Zone = newConfigValue.Zone
	}

	if newConfigValue.DefaultOutputType == "" {
		msg := "\nSetting Default Output Type=> "
		fmt.Fprintf(out, "%s", msg)

		doChange := true
		if currentConfig.DefaultOutputType != "" {
			fmt.Fprintf(out, "(Current = ")
			msgWriter.Fprintf(out, color.New(color.FgCyan), "%q", currentConfig.DefaultOutputType)
			fmt.Fprintf(out, ")")
			doChange = cli.ConfirmContinue(in, "change default output type setting")
		} else {
			fmt.Fprintf(out, "\n")
		}

		if doChange {
			outputTypes := []string{"table", "json", "yaml"}
			// read input
			var input string
			for {
				fmt.Fprintf(out, "\t%s[%s]: ", "Enter Default Output Type", strings.Join(outputTypes, "/"))
				fmt.Fscanln(in, &input)
				if input == "" || containsString(outputTypes, input) {
					break
				}
				fmt.Fprintf(out, "Invalid value. Please input again\n")
			}
			currentConfig.DefaultOutputType = input
		}
	} else {
		currentConfig.DefaultOutputType = newConfigValue.DefaultOutputType
	}

	if err := profile.Save(p.Name, currentConfig); err != nil {
		return nil, err
	}

	wrote, err := profile.ConfigFilePath(p.Name)
	if err != nil {
		return nil, err
	}
	msgWriter.Fprintf(out, color.New(color.FgHiGreen), "\nWritten your settings to %s\n", wrote)

	current, err := profile.CurrentName()
	if err != nil {
		return nil, err
	}

	// 編集したプロファイルが現在使われていない場合は切り替え
	if current != p.Name {
		if !p.Use {
			if !cli.Confirm(in, fmt.Sprintf("Would you like to switch to profile %q?", p.Name)) {
				return nil, nil
			}
		}
		return nil, profile.SetCurrentName(p.Name)
	}
	return nil, nil
}

func containsString(values []string, v string) bool {
	for _, value := range values {
		if v == value {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>/**
 * The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
 * by described by Sun Wu, Udi Manber and Gene Myers
 */

package onp

import (
	"math"
)

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

type Diff struct {
	str1, str2 string
	m, n       int
}

func NewDiff(str1, str2 string) *Diff {
	if len(str1) > len(str2) {
		return &Diff{str2, str1, len(str2), len(str1)}
	}
	return &Diff{str1, str2, len(str1), len(str2)}
}

func (diff *Diff) snake(k, p, pp int) int {

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.str1[x] == diff.str2[y] {
		x += 1
		y += 1
	}

	return y
}

const NOT_SOLVED = -1

func (diff *Diff) EditDistance() int {

	offset := diff.m + 1
	delta  := diff.n - diff.m
	size   := diff.m + diff.n + 3
	fp     := make([]int, size)

	for i := range fp {
		fp[i] = -1
	}

	for p := 0; ; p++ {

		done := make(chan bool)
		go func() {
			for k := -p; k <= delta-1; k++ {
				fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset])
			}

			done <- true
		}()

		go func() {
			for k := delta + p; k >= delta+1; k-- {
				fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset])
			}
			done <- true
		}()

		for i := 0; i < 2; i++ {
			<-done
		}
		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset])

		if fp[delta+offset] >= diff.n {
			return delta + 2*p
		}
	}

	return NOT_SOLVED
}
<commit_msg>go fmt<commit_after>/**
 * The algorithm implemented here is based on "An O(NP) Sequence Comparison Algorithm"
 * by described by Sun Wu, Udi Manber and Gene Myers
 */

package onp

import (
	"math"
)

func max(x, y int) int {
	return int(math.Max(float64(x), float64(y)))
}

type Diff struct {
	str1, str2 string
	m, n       int
}

func NewDiff(str1, str2 string) *Diff {
	if len(str1) > len(str2) {
		return &Diff{str2, str1, len(str2), len(str1)}
	}
	return &Diff{str1, str2, len(str1), len(str2)}
}

func (diff *Diff) snake(k, p, pp int) int {

	y := max(p, pp)
	x := y - k

	for x < diff.m && y < diff.n && diff.str1[x] == diff.str2[y] {
		x += 1
		y += 1
	}

	return y
}

const NOT_SOLVED = -1

func (diff *Diff) EditDistance() int {

	offset := diff.m + 1
	delta := diff.n - diff.m
	size := diff.m + diff.n + 3
	fp := make([]int, size)

	for i := range fp {
		fp[i] = -1
	}

	for p := 0; ; p++ {

		done := make(chan bool)
		go func() {
			for k := -p; k <= delta-1; k++ {
				fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset])
			}

			done <- true
		}()

		go func() {
			for k := delta + p; k >= delta+1; k-- {
				fp[k+offset] = diff.snake(k, fp[k-1+offset]+1, fp[k+1+offset])
			}
			done <- true
		}()

		for i := 0; i < 2; i++ {
			<-done
		}
		fp[delta+offset] = diff.snake(delta, fp[delta-1+offset]+1, fp[delta+1+offset])

		if fp[delta+offset] >= diff.n {
			return delta + 2*p
		}
	}

	return NOT_SOLVED
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"kite/cmd/build"
	"koding/tools/config"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
)

var (
	profile = flag.String("c", "", "Define config profile to be included")
	region  = flag.String("r", "", "Define region profile to be included")

	// Proxy only
	proxy = flag.String("p", "", "Select user proxy or koding proxy")
)

type pkg struct {
	appName       string
	importPath    string
	files         []string
	version       string
	upstartScript string
}

func main() {
	flag.Parse()
	if *profile == "" || *region == "" {
		fmt.Println("Please define config -c and region -r")
		os.Exit(1)
	}

	fmt.Println(*profile, *region, *proxy)

	err := buildPackages()
	if err != nil {
		fmt.Println(err)
	}
}

func buildPackages() error {
	if err := buildKontrolProxy(); err != nil {
		return err
	}

	if err := buildOsKite(); err != nil {
		return err
	}

	return nil
}

func buildOsKite() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	oskitePath := "koding/kites/os"
	temps := struct {
		Profile string
		Region  string
	}{
		Profile: *profile,
		Region:  *region,
	}

	var files = make([]string, 0)
	files = append(files, filepath.Join(gopath, "src", oskitePath, "files"))

	// change our upstartscript because it's a template
	oskiteUpstart := filepath.Join(gopath, "src", oskitePath, "files/oskite.conf")
	configUpstart, err := prepareUpstart(oskiteUpstart, temps)
	if err != nil {
		return err
	}
	defer os.Remove(configUpstart)

	oskite := pkg{
		appName:       "oskite",
		importPath:    oskitePath,
		files:         files,
		version:       "0.0.2",
		upstartScript: configUpstart,
	}

	return oskite.build()
}

func buildKontrolProxy() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	kdproxyPath := "koding/kontrol/kontrolproxy"

	// include certs
	if *proxy == "" {
		return errors.New("Please define proxy target. Example: -p koding or -p user")
	}

	temps := struct {
		Profile   string
		Region    string
		UserProxy string
	}{
		Profile: *profile,
		Region:  *region,
	}

	var files = make([]string, 0)
	switch *proxy {
	case "koding":
		files = append(files, "certs/koding_com_cert.pem", "certs/koding_com_key.pem")
	case "user":
		temps.UserProxy = "-v"
		files = append(files, "certs/kd_io_cert.pem", "certs/kd_io_key.pem")
	default:
		return errors.New("-p can accept either user or koding")
	}

	files = append(files, filepath.Join(gopath, "src", kdproxyPath, "files"))

	// change our upstartscript because it's a template
	kdproxyUpstart := filepath.Join(gopath, "src", kdproxyPath, "files/kontrolproxy.conf")
	configUpstart, err := prepareUpstart(kdproxyUpstart, temps)
	if err != nil {
		return err
	}
	defer os.Remove(configUpstart)

	kontrolproxy := pkg{
		appName:       "kontrolproxy",
		importPath:    kdproxyPath,
		files:         files,
		version:       "0.0.1",
		upstartScript: configUpstart,
	}

	return kontrolproxy.build()
}

func (p *pkg) build() error {
	fmt.Printf("building '%s' for config '%s' and region '%s'\n", p.appName, *profile, *region)

	// prepare config folder
	tempDir, err := ioutil.TempDir(".", "gopackage_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	configDir := filepath.Join(tempDir, "config")
	os.MkdirAll(configDir, 0755)

	// koding-config-manager needs it
	err = ioutil.WriteFile("VERSION", []byte(p.version), 0755)
	if err != nil {
		return err
	}
	defer os.Remove("VERSION")

	c, err := config.ReadConfigManager(*profile)
	if err != nil {
		return err
	}

	configPretty, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return err
	}

	configFile := filepath.Join(configDir, fmt.Sprintf("main.%s.json", *profile))
	err = ioutil.WriteFile(configFile, configPretty, 0644)
	if err != nil {
		return err
	}

	p.files = append(p.files, configDir)

	// Now it's time to build
	if runtime.GOOS != "linux" {
		return errors.New("Not supported. Please run on a linux machine.")
	}

	deb := &build.Deb{
		AppName:       p.appName,
		Version:       p.version,
		ImportPath:    p.importPath,
		Files:         strings.Join(p.files, ","),
		InstallPrefix: "opt/kite",
		UpstartScript: p.upstartScript,
	}

	debFile, err := deb.Build()
	if err != nil {
		log.Println("linux:", err)
	}

	// rename file to see for which region and env it is created
	oldname := debFile
	newname := fmt.Sprintf("%s_%s_%s-%s_%s.deb", p.appName, p.version, *profile, *region, deb.Arch)

	if err := os.Rename(oldname, newname); err != nil {
		return err
	}

	fmt.Printf("success '%s' is ready. Some helpful commands for you:\n\n", newname)
	fmt.Printf("  show deb content   : dpkg -c %s\n", newname)
	fmt.Printf("  show basic info    : dpkg -f %s\n", newname)
	fmt.Printf("  install to machine : dpkg -i %s\n\n", newname)

	return nil
}

func prepareUpstart(path string, v interface{}) (string, error) {
	file, err := ioutil.TempFile(".", "gopackage_")
	if err != nil {
		return "", err
	}
	defer file.Close()

	t, err := template.ParseFiles(path)
	if err != nil {
		return "", err
	}

	if err := t.Execute(file, v); err != nil {
		return "", err
	}

	return file.Name(), nil
}
<commit_msg>oskite: upgrade to 0.0.3<commit_after>package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"kite/cmd/build"
	"koding/tools/config"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"text/template"
)

var (
	profile = flag.String("c", "", "Define config profile to be included")
	region  = flag.String("r", "", "Define region profile to be included")

	// Proxy only
	proxy = flag.String("p", "", "Select user proxy or koding proxy")
)

type pkg struct {
	appName       string
	importPath    string
	files         []string
	version       string
	upstartScript string
}

func main() {
	flag.Parse()
	if *profile == "" || *region == "" {
		fmt.Println("Please define config -c and region -r")
		os.Exit(1)
	}

	fmt.Println(*profile, *region, *proxy)

	err := buildPackages()
	if err != nil {
		fmt.Println(err)
	}
}

func buildPackages() error {
	if err := buildKontrolProxy(); err != nil {
		return err
	}

	if err := buildOsKite(); err != nil {
		return err
	}

	return nil
}

func buildOsKite() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	oskitePath := "koding/kites/os"
	temps := struct {
		Profile string
		Region  string
	}{
		Profile: *profile,
		Region:  *region,
	}

	var files = make([]string, 0)
	files = append(files, filepath.Join(gopath, "src", oskitePath, "files"))

	// change our upstartscript because it's a template
	oskiteUpstart := filepath.Join(gopath, "src", oskitePath, "files/oskite.conf")
	configUpstart, err := prepareUpstart(oskiteUpstart, temps)
	if err != nil {
		return err
	}
	defer os.Remove(configUpstart)

	oskite := pkg{
		appName:       "oskite",
		importPath:    oskitePath,
		files:         files,
		version:       "0.0.3",
		upstartScript: configUpstart,
	}

	return oskite.build()
}

func buildKontrolProxy() error {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return errors.New("GOPATH is not set")
	}

	kdproxyPath := "koding/kontrol/kontrolproxy"

	// include certs
	if *proxy == "" {
		return errors.New("Please define proxy target. Example: -p koding or -p user")
	}

	temps := struct {
		Profile   string
		Region    string
		UserProxy string
	}{
		Profile: *profile,
		Region:  *region,
	}

	var files = make([]string, 0)
	switch *proxy {
	case "koding":
		files = append(files, "certs/koding_com_cert.pem", "certs/koding_com_key.pem")
	case "user":
		temps.UserProxy = "-v"
		files = append(files, "certs/kd_io_cert.pem", "certs/kd_io_key.pem")
	default:
		return errors.New("-p can accept either user or koding")
	}

	files = append(files, filepath.Join(gopath, "src", kdproxyPath, "files"))

	// change our upstartscript because it's a template
	kdproxyUpstart := filepath.Join(gopath, "src", kdproxyPath, "files/kontrolproxy.conf")
	configUpstart, err := prepareUpstart(kdproxyUpstart, temps)
	if err != nil {
		return err
	}
	defer os.Remove(configUpstart)

	kontrolproxy := pkg{
		appName:       "kontrolproxy",
		importPath:    kdproxyPath,
		files:         files,
		version:       "0.0.1",
		upstartScript: configUpstart,
	}

	return kontrolproxy.build()
}

func (p *pkg) build() error {
	fmt.Printf("building '%s' for config '%s' and region '%s'\n", p.appName, *profile, *region)

	// prepare config folder
	tempDir, err := ioutil.TempDir(".", "gopackage_")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDir)

	configDir := filepath.Join(tempDir, "config")
	os.MkdirAll(configDir, 0755)

	// koding-config-manager needs it
	err = ioutil.WriteFile("VERSION", []byte(p.version), 0755)
	if err != nil {
		return err
	}
	defer os.Remove("VERSION")

	c, err := config.ReadConfigManager(*profile)
	if err != nil {
		return err
	}

	configPretty, err := json.MarshalIndent(c, "", "  ")
	if err != nil {
		return err
	}

	configFile := filepath.Join(configDir, fmt.Sprintf("main.%s.json", *profile))
	err = ioutil.WriteFile(configFile, configPretty, 0644)
	if err != nil {
		return err
	}

	p.files = append(p.files, configDir)

	// Now it's time to build
	if runtime.GOOS != "linux" {
		return errors.New("Not supported. Please run on a linux machine.")
	}

	deb := &build.Deb{
		AppName:       p.appName,
		Version:       p.version,
		ImportPath:    p.importPath,
		Files:         strings.Join(p.files, ","),
		InstallPrefix: "opt/kite",
		UpstartScript: p.upstartScript,
	}

	debFile, err := deb.Build()
	if err != nil {
		log.Println("linux:", err)
	}

	// rename file to see for which region and env it is created
	oldname := debFile
	newname := fmt.Sprintf("%s_%s_%s-%s_%s.deb", p.appName, p.version, *profile, *region, deb.Arch)

	if err := os.Rename(oldname, newname); err != nil {
		return err
	}

	fmt.Printf("success '%s' is ready. Some helpful commands for you:\n\n", newname)
	fmt.Printf("  show deb content   : dpkg -c %s\n", newname)
	fmt.Printf("  show basic info    : dpkg -f %s\n", newname)
	fmt.Printf("  install to machine : dpkg -i %s\n\n", newname)

	return nil
}

func prepareUpstart(path string, v interface{}) (string, error) {
	file, err := ioutil.TempFile(".", "gopackage_")
	if err != nil {
		return "", err
	}
	defer file.Close()

	t, err := template.ParseFiles(path)
	if err != nil {
		return "", err
	}

	if err := t.Execute(file, v); err != nil {
		return "", err
	}

	return file.Name(), nil
}
<|endoftext|>


<commit_before>package mount

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestMount(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("not root tests would fail")
	}

	source, err := ioutil.TempDir("", "mount-test-source-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(source)

	// Ensure we have a known start point by mounting tmpfs with given options
	if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil {
		t.Fatal(err)
	}
	defer ensureUnmount(t, source)
	validateMount(t, source, "", "", "")
	if t.Failed() {
		t.FailNow()
	}

	target, err := ioutil.TempDir("", "mount-test-target-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(target)

	tests := []struct {
		source           string
		ftype            string
		options          string
		expectedOpts     string
		expectedOptional string
		expectedVFS      string
	}{
		// No options
		{"tmpfs", "tmpfs", "", "", "", ""},
		// Default rw / ro test
		{source, "", "bind", "", "", ""},
		{source, "", "bind,private", "", "", ""},
		{source, "", "bind,shared", "", "shared", ""},
		{source, "", "bind,slave", "", "master", ""},
		{source, "", "bind,unbindable", "", "unbindable", ""},
		// Read Write tests
		{source, "", "bind,rw", "rw", "", ""},
		{source, "", "bind,rw,private", "rw", "", ""},
		{source, "", "bind,rw,shared", "rw", "shared", ""},
		{source, "", "bind,rw,slave", "rw", "master", ""},
		{source, "", "bind,rw,unbindable", "rw", "unbindable", ""},
		// Read Only tests
		{source, "", "bind,ro", "ro", "", ""},
		{source, "", "bind,ro,private", "ro", "", ""},
		{source, "", "bind,ro,shared", "ro", "shared", ""},
		{source, "", "bind,ro,slave", "ro", "master", ""},
		{source, "", "bind,ro,unbindable", "ro", "unbindable", ""},
		// Remount tests to change per filesystem options
		{"", "", "remount,size=128k", "rw", "", "rw,size=128k"},
		{"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"},
	}

	for _, tc := range tests {
		ftype, options := tc.ftype, tc.options
		if tc.ftype == "" {
			ftype = none
		}
		if tc.options == "" {
			options = none
		}

		t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) {
			if strings.Contains(tc.options, "slave") {
				// Slave requires a shared source
				if err := MakeShared(source); err != nil {
					t.Fatal(err)
				}
				defer func() {
					if err := MakePrivate(source); err != nil {
						t.Fatal(err)
					}
				}()
			}
			if strings.Contains(tc.options, "remount") {
				// create a new mount to remount first
				if err := Mount("tmpfs", target, "tmpfs", ""); err != nil {
					t.Fatal(err)
				}
			}
			if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil {
				t.Fatal(err)
			}
			defer ensureUnmount(t, target)
			validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS)
		})
	}
}

// ensureUnmount umounts mnt checking for errors
func ensureUnmount(t *testing.T, mnt string) {
	if err := Unmount(mnt); err != nil {
		t.Error(err)
	}
}

// validateMount checks that mnt has the given options
func validateMount(t *testing.T, mnt string, opts, optional, vfs string) {
	info, err := GetMounts()
	if err != nil {
		t.Fatal(err)
	}

	wantedOpts := make(map[string]struct{})
	if opts != "" {
		for _, opt := range strings.Split(opts, ",") {
			wantedOpts[opt] = struct{}{}
		}
	}

	wantedOptional := make(map[string]struct{})
	if optional != "" {
		for _, opt := range strings.Split(optional, ",") {
			wantedOptional[opt] = struct{}{}
		}
	}

	wantedVFS := make(map[string]struct{})
	if vfs != "" {
		for _, opt := range strings.Split(vfs, ",") {
			wantedVFS[opt] = struct{}{}
		}
	}
	volunteeredVFS := map[string]struct{}{"seclabel": {}}
	volunteeredOPT := map[string]struct{}{"relatime": {}}

	mnts := make(map[int]*Info, len(info))
	for _, mi := range info {
		mnts[mi.ID] = mi
	}

	for _, mi := range info {
		if mi.Mountpoint != mnt {
			continue
		}

		// Use parent info as the defaults
		p := mnts[mi.Parent]
		pOpts := make(map[string]struct{})
		if p.Opts != "" {
			for _, opt := range strings.Split(p.Opts, ",") {
				pOpts[clean(opt)] = struct{}{}
			}
		}
		pOptional := make(map[string]struct{})
		if p.Optional != "" {
			for _, field := range strings.Split(p.Optional, ",") {
				pOptional[clean(field)] = struct{}{}
			}
		}

		// Validate Opts
		if mi.Opts != "" {
			for _, opt := range strings.Split(mi.Opts, ",") {
				opt = clean(opt)
				if !has(volunteeredOPT, opt) && !has(wantedOpts, opt) && !has(pOpts, opt) {
					t.Errorf("unexpected mount option %q expected %q", opt, opts)
				}
				delete(wantedOpts, opt)
			}
		}
		for opt := range wantedOpts {
			t.Errorf("missing mount option %q found %q", opt, mi.Opts)
		}

		// Validate Optional
		if mi.Optional != "" {
			for _, field := range strings.Split(mi.Optional, ",") {
				field = clean(field)
				if !has(wantedOptional, field) && !has(pOptional, field) {
					t.Errorf("unexpected optional failed %q expected %q", field, optional)
				}
				delete(wantedOptional, field)
			}
		}
		for field := range wantedOptional {
			t.Errorf("missing optional field %q found %q", field, mi.Optional)
		}

		// Validate VFS if set
		if vfs != "" {
			if mi.VfsOpts != "" {
				for _, opt := range strings.Split(mi.VfsOpts, ",") {
					opt = clean(opt)
					if !has(wantedVFS, opt) && !has(volunteeredVFS, opt) {
						t.Errorf("unexpected mount option %q expected %q", opt, vfs)
					}
					delete(wantedVFS, opt)
				}
			}
			for opt := range wantedVFS {
				t.Errorf("missing mount option %q found %q", opt, mi.VfsOpts)
			}
		}

		return
	}

	t.Errorf("failed to find mount %q", mnt)
}

// clean strips off any value param after the colon
func clean(v string) string {
	return strings.SplitN(v, ":", 2)[0]
}

// has returns true if key is a member of m
func has(m map[string]struct{}, key string) bool {
	_, ok := m[key]
	return ok
}
<commit_msg>pkg/mount/mounter_linux_test: nits<commit_after>package mount

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"testing"
)

func TestMount(t *testing.T) {
	if os.Getuid() != 0 {
		t.Skip("not root tests would fail")
	}

	source, err := ioutil.TempDir("", "mount-test-source-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(source)

	// Ensure we have a known start point by mounting tmpfs with given options
	if err := Mount("tmpfs", source, "tmpfs", "private"); err != nil {
		t.Fatal(err)
	}
	defer ensureUnmount(t, source)
	validateMount(t, source, "", "", "")
	if t.Failed() {
		t.FailNow()
	}

	target, err := ioutil.TempDir("", "mount-test-target-")
	if err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(target)

	tests := []struct {
		source           string
		ftype            string
		options          string
		expectedOpts     string
		expectedOptional string
		expectedVFS      string
	}{
		// No options
		{"tmpfs", "tmpfs", "", "", "", ""},
		// Default rw / ro test
		{source, "", "bind", "", "", ""},
		{source, "", "bind,private", "", "", ""},
		{source, "", "bind,shared", "", "shared", ""},
		{source, "", "bind,slave", "", "master", ""},
		{source, "", "bind,unbindable", "", "unbindable", ""},
		// Read Write tests
		{source, "", "bind,rw", "rw", "", ""},
		{source, "", "bind,rw,private", "rw", "", ""},
		{source, "", "bind,rw,shared", "rw", "shared", ""},
		{source, "", "bind,rw,slave", "rw", "master", ""},
		{source, "", "bind,rw,unbindable", "rw", "unbindable", ""},
		// Read Only tests
		{source, "", "bind,ro", "ro", "", ""},
		{source, "", "bind,ro,private", "ro", "", ""},
		{source, "", "bind,ro,shared", "ro", "shared", ""},
		{source, "", "bind,ro,slave", "ro", "master", ""},
		{source, "", "bind,ro,unbindable", "ro", "unbindable", ""},
		// Remount tests to change per filesystem options
		{"", "", "remount,size=128k", "rw", "", "rw,size=128k"},
		{"", "", "remount,ro,size=128k", "ro", "", "ro,size=128k"},
	}

	for _, tc := range tests {
		ftype, options := tc.ftype, tc.options
		if tc.ftype == "" {
			ftype = none
		}
		if tc.options == "" {
			options = none
		}

		t.Run(fmt.Sprintf("%v-%v", ftype, options), func(t *testing.T) {
			if strings.Contains(tc.options, "slave") {
				// Slave requires a shared source
				if err := MakeShared(source); err != nil {
					t.Fatal(err)
				}
				defer func() {
					if err := MakePrivate(source); err != nil {
						t.Fatal(err)
					}
				}()
			}
			if strings.Contains(tc.options, "remount") {
				// create a new mount to remount first
				if err := Mount("tmpfs", target, "tmpfs", ""); err != nil {
					t.Fatal(err)
				}
			}
			if err := Mount(tc.source, target, tc.ftype, tc.options); err != nil {
				t.Fatal(err)
			}
			defer ensureUnmount(t, target)
			validateMount(t, target, tc.expectedOpts, tc.expectedOptional, tc.expectedVFS)
		})
	}
}

// ensureUnmount umounts mnt checking for errors
func ensureUnmount(t *testing.T, mnt string) {
	if err := Unmount(mnt); err != nil {
		t.Error(err)
	}
}

// validateMount checks that mnt has the given options
func validateMount(t *testing.T, mnt string, opts, optional, vfs string) {
	info, err := GetMounts()
	if err != nil {
		t.Fatal(err)
	}

	wantedOpts := make(map[string]struct{})
	if opts != "" {
		for _, opt := range strings.Split(opts, ",") {
			wantedOpts[opt] = struct{}{}
		}
	}

	wantedOptional := make(map[string]struct{})
	if optional != "" {
		for _, opt := range strings.Split(optional, ",") {
			wantedOptional[opt] = struct{}{}
		}
	}

	wantedVFS := make(map[string]struct{})
	if vfs != "" {
		for _, opt := range strings.Split(vfs, ",") {
			wantedVFS[opt] = struct{}{}
		}
	}
	volunteeredVFS := map[string]struct{}{"seclabel": {}}
	volunteeredOPT := map[string]struct{}{"relatime": {}}

	mnts := make(map[int]*Info, len(info))
	for _, mi := range info {
		mnts[mi.ID] = mi
	}

	for _, mi := range info {
		if mi.Mountpoint != mnt {
			continue
		}

		// Use parent info as the defaults
		p := mnts[mi.Parent]
		pOpts := make(map[string]struct{})
		if p.Opts != "" {
			for _, opt := range strings.Split(p.Opts, ",") {
				pOpts[clean(opt)] = struct{}{}
			}
		}
		pOptional := make(map[string]struct{})
		if p.Optional != "" {
			for _, field := range strings.Split(p.Optional, ",") {
				pOptional[clean(field)] = struct{}{}
			}
		}

		// Validate Opts
		if mi.Opts != "" {
			for _, opt := range strings.Split(mi.Opts, ",") {
				opt = clean(opt)
				if !has(volunteeredOPT, opt) && !has(wantedOpts, opt) && !has(pOpts, opt) {
					t.Errorf("unexpected mount option %q, expected %q", opt, opts)
				}
				delete(wantedOpts, opt)
			}
		}
		for opt := range wantedOpts {
			t.Errorf("missing mount option %q found %q", opt, mi.Opts)
		}

		// Validate Optional
		if mi.Optional != "" {
			for _, field := range strings.Split(mi.Optional, ",") {
				field = clean(field)
				if !has(wantedOptional, field) && !has(pOptional, field) {
					t.Errorf("unexpected optional field %q, expected %q", field, optional)
				}
				delete(wantedOptional, field)
			}
		}
		for field := range wantedOptional {
			t.Errorf("missing optional field %q, found %q", field, mi.Optional)
		}

		// Validate VFS if set
		if vfs != "" {
			if mi.VfsOpts != "" {
				for _, opt := range strings.Split(mi.VfsOpts, ",") {
					opt = clean(opt)
					if !has(wantedVFS, opt) && !has(volunteeredVFS, opt) {
						t.Errorf("unexpected vfs option %q, expected %q", opt, vfs)
					}
					delete(wantedVFS, opt)
				}
			}
			for opt := range wantedVFS {
				t.Errorf("missing vfs option %q, found %q", opt, mi.VfsOpts)
			}
		}

		return
	}

	t.Errorf("failed to find mount %q", mnt)
}

// clean strips off any value param after the colon
func clean(v string) string {
	return strings.SplitN(v, ":", 2)[0]
}

// has returns true if key is a member of m
func has(m map[string]struct{}, key string) bool {
	_, ok := m[key]
	return ok
}
<|endoftext|>


<commit_before>package pov

type Tree struct {
	// Add the needed fields here
}

// New creates and returns a new Tree with the given root value and children.
func New(value string, children ...*Tree) *Tree {
	panic("Please implement this function")
}

// Value returns the value at the root of a tree.
func (tr *Tree) Value() string {
	panic("Please implement this function")
}

// Children returns a slice containing the children of a tree.
// There is no need to sort the elements in the result slice,
// they can be in any order.
func (tr *Tree) Children() []*Tree {
	panic("Please implement this function")
}

// String describes a tree in a compact S-expression format.
// This helps to make test outputs more readable.
// Feel free to adapt this method as you see fit.
func (tr *Tree) String() string {
	if tr == nil {
		return "nil"
	}
	result := tr.Value()
	if len(tr.Children()) == 0 {
		return result
	}
	for _, ch := range tr.Children() {
		result += " " + ch.String()
	}
	return "(" + result + ")"
}

// POV problem-specific functions

// FromPov returns the pov from the node specified in the argument.
func (tr *Tree) FromPov(from string) *Tree {
	panic("Please implement this function")
}

// PathTo returns the shortest path between two nodes in the tree.
func (tr *Tree) PathTo(from, to string) []string {
	panic("Please implement this function")
}
<commit_msg>Implement New, Value, Children<commit_after>package pov

type Tree struct {
	root string
	children []*Tree
}

// New creates and returns a new Tree with the given root value and children.
func New(value string, children ...*Tree) *Tree {
	return &Tree{
		root: value,
		children: children,
	}
}

// Value returns the value at the root of a tree.
func (tr *Tree) Value() string {
	if tr == nil {
		return ""
	}
	return tr.root
}

// Children returns a slice containing the children of a tree.
// There is no need to sort the elements in the result slice,
// they can be in any order.
func (tr *Tree) Children() []*Tree {
	if tr == nil {
		return []*Tree{}
	}
	return tr.children
}

// String describes a tree in a compact S-expression format.
// This helps to make test outputs more readable.
// Feel free to adapt this method as you see fit.
func (tr *Tree) String() string {
	if tr == nil {
		return "nil"
	}
	result := tr.Value()
	if len(tr.Children()) == 0 {
		return result
	}
	for _, ch := range tr.Children() {
		result += " " + ch.String()
	}
	return "(" + result + ")"
}

// POV problem-specific functions

// FromPov returns the pov from the node specified in the argument.
func (tr *Tree) FromPov(from string) *Tree {
	panic("Please implement this function")
}

// PathTo returns the shortest path between two nodes in the tree.
func (tr *Tree) PathTo(from, to string) []string {
	panic("Please implement this function")
}
<|endoftext|>


<commit_before>package querier

import (
	"context"
	"net/http"
	"strings"
	"time"

	cortex_distributor "github.com/cortexproject/cortex/pkg/distributor"
	"github.com/cortexproject/cortex/pkg/ring"
	ring_client "github.com/cortexproject/cortex/pkg/ring/client"
	"github.com/cortexproject/cortex/pkg/util"
	"github.com/cortexproject/cortex/pkg/util/services"
	"github.com/pkg/errors"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/weaveworks/common/httpgrpc"

	"github.com/grafana/loki/pkg/ingester/client"
	"github.com/grafana/loki/pkg/iter"
	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
	"github.com/grafana/loki/pkg/logql/stats"
)

type responseFromIngesters struct {
	addr     string
	response interface{}
}

// IngesterQuerier helps with querying the ingesters.
type IngesterQuerier struct {
	ring            ring.ReadRing
	pool            *ring_client.Pool
	extraQueryDelay time.Duration
}

func NewIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration) (*IngesterQuerier, error) {
	factory := func(addr string) (ring_client.PoolClient, error) {
		return client.New(clientCfg, addr)
	}

	return newIngesterQuerier(clientCfg, ring, extraQueryDelay, factory)
}

// newIngesterQuerier creates a new IngesterQuerier and allows to pass a custom ingester client factory
// used for testing purposes
func newIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration, clientFactory ring_client.PoolFactory) (*IngesterQuerier, error) {
	iq := IngesterQuerier{
		ring:            ring,
		pool:            cortex_distributor.NewPool(clientCfg.PoolConfig, ring, clientFactory, util.Logger),
		extraQueryDelay: extraQueryDelay,
	}

	err := services.StartAndAwaitRunning(context.Background(), iq.pool)
	if err != nil {
		return nil, errors.Wrap(err, "querier pool")
	}

	return &iq, nil
}

// forAllIngesters runs f, in parallel, for all ingesters
// TODO taken from Cortex, see if we can refactor out an usable interface.
func (q *IngesterQuerier) forAllIngesters(ctx context.Context, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
	if err != nil {
		return nil, err
	}

	return q.forGivenIngesters(ctx, replicationSet, f)
}

// forGivenIngesters runs f, in parallel, for given ingesters
// TODO taken from Cortex, see if we can refactor out an usable interface.
func (q *IngesterQuerier) forGivenIngesters(ctx context.Context, replicationSet ring.ReplicationSet, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
	results, err := replicationSet.Do(ctx, q.extraQueryDelay, func(ctx context.Context, ingester *ring.IngesterDesc) (interface{}, error) {
		client, err := q.pool.GetClientFor(ingester.Addr)
		if err != nil {
			return nil, err
		}

		resp, err := f(client.(logproto.QuerierClient))
		if err != nil {
			return nil, err
		}

		return responseFromIngesters{ingester.Addr, resp}, nil
	})
	if err != nil {
		return nil, err
	}

	responses := make([]responseFromIngesters, 0, len(results))
	for _, result := range results {
		responses = append(responses, result.(responseFromIngesters))
	}

	return responses, err
}

func (q *IngesterQuerier) SelectLogs(ctx context.Context, params logql.SelectLogParams) ([]iter.EntryIterator, error) {
	clients, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Query(ctx, params.QueryRequest, stats.CollectTrailer(ctx))
	})
	if err != nil {
		return nil, err
	}

	iterators := make([]iter.EntryIterator, len(clients))
	for i := range clients {
		iterators[i] = iter.NewQueryClientIterator(clients[i].response.(logproto.Querier_QueryClient), params.Direction)
	}
	return iterators, nil
}

func (q *IngesterQuerier) SelectSample(ctx context.Context, params logql.SelectSampleParams) ([]iter.SampleIterator, error) {
	clients, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.QuerySample(ctx, params.SampleQueryRequest, stats.CollectTrailer(ctx))
	})
	if err != nil {
		return nil, err
	}

	iterators := make([]iter.SampleIterator, len(clients))
	for i := range clients {
		iterators[i] = iter.NewSampleQueryClientIterator(clients[i].response.(logproto.Querier_QuerySampleClient))
	}
	return iterators, nil
}

func (q *IngesterQuerier) Label(ctx context.Context, req *logproto.LabelRequest) ([][]string, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Label(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	results := make([][]string, 0, len(resps))
	for _, resp := range resps {
		results = append(results, resp.response.(*logproto.LabelResponse).Values)
	}

	return results, nil
}

func (q *IngesterQuerier) Tail(ctx context.Context, req *logproto.TailRequest) (map[string]logproto.Querier_TailClient, error) {
	clients, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Tail(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	tailClients := make(map[string]logproto.Querier_TailClient)
	for i := range clients {
		tailClients[clients[i].addr] = clients[i].response.(logproto.Querier_TailClient)
	}

	return tailClients, nil
}

func (q *IngesterQuerier) TailDisconnectedIngesters(ctx context.Context, req *logproto.TailRequest, connectedIngestersAddr []string) (map[string]logproto.Querier_TailClient, error) {
	// Build a map to easily check if an ingester address is already connected
	connected := make(map[string]bool)
	for _, addr := range connectedIngestersAddr {
		connected[addr] = true
	}

	// Get the current replication set from the ring
	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
	if err != nil {
		return nil, err
	}

	// Look for disconnected ingesters or new one we should (re)connect to
	reconnectIngesters := []ring.IngesterDesc{}

	for _, ingester := range replicationSet.Ingesters {
		if _, ok := connected[ingester.Addr]; ok {
			continue
		}

		// Skip ingesters which are leaving or joining the cluster
		if ingester.State != ring.ACTIVE {
			continue
		}

		reconnectIngesters = append(reconnectIngesters, ingester)
	}

	if len(reconnectIngesters) == 0 {
		return nil, nil
	}

	// Instance a tail client for each ingester to re(connect)
	reconnectClients, err := q.forGivenIngesters(ctx, ring.ReplicationSet{Ingesters: reconnectIngesters}, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Tail(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	reconnectClientsMap := make(map[string]logproto.Querier_TailClient)
	for _, client := range reconnectClients {
		reconnectClientsMap[client.addr] = client.response.(logproto.Querier_TailClient)
	}

	return reconnectClientsMap, nil
}

func (q *IngesterQuerier) Series(ctx context.Context, req *logproto.SeriesRequest) ([][]logproto.SeriesIdentifier, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Series(ctx, req)
	})
	if err != nil {
		return nil, err
	}
	var acc [][]logproto.SeriesIdentifier
	for _, resp := range resps {
		acc = append(acc, resp.response.(*logproto.SeriesResponse).Series)
	}

	return acc, nil
}

func (q *IngesterQuerier) TailersCount(ctx context.Context) ([]uint32, error) {
	replicationSet, err := q.ring.GetAllHealthy(ring.Read)
	if err != nil {
		return nil, err
	}

	// we want to check count of active tailers with only active ingesters
	ingesters := make([]ring.IngesterDesc, 0, 1)
	for i := range replicationSet.Ingesters {
		if replicationSet.Ingesters[i].State == ring.ACTIVE {
			ingesters = append(ingesters, replicationSet.Ingesters[i])
		}
	}

	if len(ingesters) == 0 {
		return nil, httpgrpc.Errorf(http.StatusInternalServerError, "no active ingester found")
	}

	responses, err := q.forGivenIngesters(ctx, replicationSet, func(querierClient logproto.QuerierClient) (interface{}, error) {
		resp, err := querierClient.TailersCount(ctx, nil)
		if err != nil {
			return nil, err
		}
		return resp.Count, nil
	})
	// We are only checking active ingesters, and any error returned stops checking other ingesters
	// so return that error here as well.
	if err != nil {
		return nil, err
	}

	counts := make([]uint32, len(responses))

	for _, resp := range responses {
		counts = append(counts, resp.response.(uint32))
	}

	return counts, nil
}

func (q *IngesterQuerier) GetChunkIDs(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]string, error) {
	resps, err := q.forAllIngesters(ctx, func(querierClient logproto.QuerierClient) (interface{}, error) {
		return querierClient.GetChunkIDs(ctx, &logproto.GetChunkIDsRequest{
			Matchers: convertMatchersToString(matchers),
			Start:    from.Time(),
			End:      through.Time(),
		})
	})

	if err != nil {
		return nil, err
	}

	var chunkIDs []string
	for i := range resps {
		chunkIDs = append(chunkIDs, resps[i].response.(*logproto.GetChunkIDsResponse).ChunkIDs...)
	}

	return chunkIDs, nil
}

func convertMatchersToString(matchers []*labels.Matcher) string {
	out := strings.Builder{}
	out.WriteRune('{')

	for idx, m := range matchers {
		if idx > 0 {
			out.WriteRune(',')
		}

		out.WriteString(m.String())
	}

	out.WriteRune('}')
	return out.String()
}
<commit_msg>[ingester_query] change var "clients" to "reps" (#3274)<commit_after>package querier

import (
	"context"
	"net/http"
	"strings"
	"time"

	cortex_distributor "github.com/cortexproject/cortex/pkg/distributor"
	"github.com/cortexproject/cortex/pkg/ring"
	ring_client "github.com/cortexproject/cortex/pkg/ring/client"
	"github.com/cortexproject/cortex/pkg/util"
	"github.com/cortexproject/cortex/pkg/util/services"
	"github.com/pkg/errors"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/weaveworks/common/httpgrpc"

	"github.com/grafana/loki/pkg/ingester/client"
	"github.com/grafana/loki/pkg/iter"
	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
	"github.com/grafana/loki/pkg/logql/stats"
)

type responseFromIngesters struct {
	addr     string
	response interface{}
}

// IngesterQuerier helps with querying the ingesters.
type IngesterQuerier struct {
	ring            ring.ReadRing
	pool            *ring_client.Pool
	extraQueryDelay time.Duration
}

func NewIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration) (*IngesterQuerier, error) {
	factory := func(addr string) (ring_client.PoolClient, error) {
		return client.New(clientCfg, addr)
	}

	return newIngesterQuerier(clientCfg, ring, extraQueryDelay, factory)
}

// newIngesterQuerier creates a new IngesterQuerier and allows to pass a custom ingester client factory
// used for testing purposes
func newIngesterQuerier(clientCfg client.Config, ring ring.ReadRing, extraQueryDelay time.Duration, clientFactory ring_client.PoolFactory) (*IngesterQuerier, error) {
	iq := IngesterQuerier{
		ring:            ring,
		pool:            cortex_distributor.NewPool(clientCfg.PoolConfig, ring, clientFactory, util.Logger),
		extraQueryDelay: extraQueryDelay,
	}

	err := services.StartAndAwaitRunning(context.Background(), iq.pool)
	if err != nil {
		return nil, errors.Wrap(err, "querier pool")
	}

	return &iq, nil
}

// forAllIngesters runs f, in parallel, for all ingesters
// TODO taken from Cortex, see if we can refactor out an usable interface.
func (q *IngesterQuerier) forAllIngesters(ctx context.Context, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
	if err != nil {
		return nil, err
	}

	return q.forGivenIngesters(ctx, replicationSet, f)
}

// forGivenIngesters runs f, in parallel, for given ingesters
// TODO taken from Cortex, see if we can refactor out an usable interface.
func (q *IngesterQuerier) forGivenIngesters(ctx context.Context, replicationSet ring.ReplicationSet, f func(logproto.QuerierClient) (interface{}, error)) ([]responseFromIngesters, error) {
	results, err := replicationSet.Do(ctx, q.extraQueryDelay, func(ctx context.Context, ingester *ring.IngesterDesc) (interface{}, error) {
		client, err := q.pool.GetClientFor(ingester.Addr)
		if err != nil {
			return nil, err
		}

		resp, err := f(client.(logproto.QuerierClient))
		if err != nil {
			return nil, err
		}

		return responseFromIngesters{ingester.Addr, resp}, nil
	})
	if err != nil {
		return nil, err
	}

	responses := make([]responseFromIngesters, 0, len(results))
	for _, result := range results {
		responses = append(responses, result.(responseFromIngesters))
	}

	return responses, err
}

func (q *IngesterQuerier) SelectLogs(ctx context.Context, params logql.SelectLogParams) ([]iter.EntryIterator, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Query(ctx, params.QueryRequest, stats.CollectTrailer(ctx))
	})
	if err != nil {
		return nil, err
	}

	iterators := make([]iter.EntryIterator, len(resps))
	for i := range resps {
		iterators[i] = iter.NewQueryClientIterator(resps[i].response.(logproto.Querier_QueryClient), params.Direction)
	}
	return iterators, nil
}

func (q *IngesterQuerier) SelectSample(ctx context.Context, params logql.SelectSampleParams) ([]iter.SampleIterator, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.QuerySample(ctx, params.SampleQueryRequest, stats.CollectTrailer(ctx))
	})
	if err != nil {
		return nil, err
	}

	iterators := make([]iter.SampleIterator, len(resps))
	for i := range resps {
		iterators[i] = iter.NewSampleQueryClientIterator(resps[i].response.(logproto.Querier_QuerySampleClient))
	}
	return iterators, nil
}

func (q *IngesterQuerier) Label(ctx context.Context, req *logproto.LabelRequest) ([][]string, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Label(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	results := make([][]string, 0, len(resps))
	for _, resp := range resps {
		results = append(results, resp.response.(*logproto.LabelResponse).Values)
	}

	return results, nil
}

func (q *IngesterQuerier) Tail(ctx context.Context, req *logproto.TailRequest) (map[string]logproto.Querier_TailClient, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Tail(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	tailClients := make(map[string]logproto.Querier_TailClient)
	for i := range resps {
		tailClients[resps[i].addr] = resps[i].response.(logproto.Querier_TailClient)
	}

	return tailClients, nil
}

func (q *IngesterQuerier) TailDisconnectedIngesters(ctx context.Context, req *logproto.TailRequest, connectedIngestersAddr []string) (map[string]logproto.Querier_TailClient, error) {
	// Build a map to easily check if an ingester address is already connected
	connected := make(map[string]bool)
	for _, addr := range connectedIngestersAddr {
		connected[addr] = true
	}

	// Get the current replication set from the ring
	replicationSet, err := q.ring.GetReplicationSetForOperation(ring.Read)
	if err != nil {
		return nil, err
	}

	// Look for disconnected ingesters or new one we should (re)connect to
	reconnectIngesters := []ring.IngesterDesc{}

	for _, ingester := range replicationSet.Ingesters {
		if _, ok := connected[ingester.Addr]; ok {
			continue
		}

		// Skip ingesters which are leaving or joining the cluster
		if ingester.State != ring.ACTIVE {
			continue
		}

		reconnectIngesters = append(reconnectIngesters, ingester)
	}

	if len(reconnectIngesters) == 0 {
		return nil, nil
	}

	// Instance a tail client for each ingester to re(connect)
	reconnectClients, err := q.forGivenIngesters(ctx, ring.ReplicationSet{Ingesters: reconnectIngesters}, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Tail(ctx, req)
	})
	if err != nil {
		return nil, err
	}

	reconnectClientsMap := make(map[string]logproto.Querier_TailClient)
	for _, client := range reconnectClients {
		reconnectClientsMap[client.addr] = client.response.(logproto.Querier_TailClient)
	}

	return reconnectClientsMap, nil
}

func (q *IngesterQuerier) Series(ctx context.Context, req *logproto.SeriesRequest) ([][]logproto.SeriesIdentifier, error) {
	resps, err := q.forAllIngesters(ctx, func(client logproto.QuerierClient) (interface{}, error) {
		return client.Series(ctx, req)
	})
	if err != nil {
		return nil, err
	}
	var acc [][]logproto.SeriesIdentifier
	for _, resp := range resps {
		acc = append(acc, resp.response.(*logproto.SeriesResponse).Series)
	}

	return acc, nil
}

func (q *IngesterQuerier) TailersCount(ctx context.Context) ([]uint32, error) {
	replicationSet, err := q.ring.GetAllHealthy(ring.Read)
	if err != nil {
		return nil, err
	}

	// we want to check count of active tailers with only active ingesters
	ingesters := make([]ring.IngesterDesc, 0, 1)
	for i := range replicationSet.Ingesters {
		if replicationSet.Ingesters[i].State == ring.ACTIVE {
			ingesters = append(ingesters, replicationSet.Ingesters[i])
		}
	}

	if len(ingesters) == 0 {
		return nil, httpgrpc.Errorf(http.StatusInternalServerError, "no active ingester found")
	}

	responses, err := q.forGivenIngesters(ctx, replicationSet, func(querierClient logproto.QuerierClient) (interface{}, error) {
		resp, err := querierClient.TailersCount(ctx, nil)
		if err != nil {
			return nil, err
		}
		return resp.Count, nil
	})
	// We are only checking active ingesters, and any error returned stops checking other ingesters
	// so return that error here as well.
	if err != nil {
		return nil, err
	}

	counts := make([]uint32, len(responses))

	for _, resp := range responses {
		counts = append(counts, resp.response.(uint32))
	}

	return counts, nil
}

func (q *IngesterQuerier) GetChunkIDs(ctx context.Context, from, through model.Time, matchers ...*labels.Matcher) ([]string, error) {
	resps, err := q.forAllIngesters(ctx, func(querierClient logproto.QuerierClient) (interface{}, error) {
		return querierClient.GetChunkIDs(ctx, &logproto.GetChunkIDsRequest{
			Matchers: convertMatchersToString(matchers),
			Start:    from.Time(),
			End:      through.Time(),
		})
	})

	if err != nil {
		return nil, err
	}

	var chunkIDs []string
	for i := range resps {
		chunkIDs = append(chunkIDs, resps[i].response.(*logproto.GetChunkIDsResponse).ChunkIDs...)
	}

	return chunkIDs, nil
}

func convertMatchersToString(matchers []*labels.Matcher) string {
	out := strings.Builder{}
	out.WriteRune('{')

	for idx, m := range matchers {
		if idx > 0 {
			out.WriteRune(',')
		}

		out.WriteString(m.String())
	}

	out.WriteRune('}')
	return out.String()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold Authors

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

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

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

package local

import (
	"context"
	"io"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha3"
	"github.com/pkg/errors"
)

func (b *Builder) buildJibMaven(ctx context.Context, out io.Writer, workspace string, a *v1alpha3.JibMavenArtifact) (string, error) {
	return "", errors.New("buildJibMaven is unimplemented")
}

func (b *Builder) buildJibGradle(ctx context.Context, out io.Writer, workspace string, a *v1alpha3.JibGradleArtifact) (string, error) {
	return "", errors.New("buildJibGradle is unimplemented")
}
<commit_msg>Simple use of ctx for linter<commit_after>/*
Copyright 2018 The Skaffold Authors

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

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

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

package local

import (
	"context"
	"io"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha3"
	"github.com/pkg/errors"
)

func (b *Builder) buildJibMaven(ctx context.Context, out io.Writer, workspace string, a *v1alpha3.JibMavenArtifact) (string, error) {
	select {
	case <-ctx.Done():
		return "", ctx.Err()
	default:
	}
	return "", errors.New("buildJibMaven is unimplemented")
}

func (b *Builder) buildJibGradle(ctx context.Context, out io.Writer, workspace string, a *v1alpha3.JibGradleArtifact) (string, error) {
	select {
	case <-ctx.Done():
		return "", ctx.Err()
	default:
	}
	return "", errors.New("buildJibGradle is unimplemented")
}
<|endoftext|>