<commit_before>/*
Copyright 2018 The Kubernetes 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 create

import (
	"fmt"

	"github.com/spf13/cobra"

	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/genericclioptions/resource"
	batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/scheme"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
	"k8s.io/kubernetes/pkg/kubectl/util/templates"
)

var (
	jobLong = templates.LongDesc(i18n.T(`
		Create a job with the specified name.`))

	jobExample = templates.Examples(i18n.T(`
		# Create a job
		kubectl create job my-job --image=busybox

		# Create a job with command
		kubectl create job my-job --image=busybox -- date

		# Create a job from a CronJob named "a-cronjob"
		kubectl create job test-job --from=cronjob/a-cronjob`))
)

// CreateJobOptions is the command line options for 'create job'
type CreateJobOptions struct {
	PrintFlags *genericclioptions.PrintFlags

	PrintObj func(obj runtime.Object) error

	Name    string
	Image   string
	From    string
	Command []string

	Namespace string
	Client    batchv1client.BatchV1Interface
	DryRun    bool
	Builder   *resource.Builder
	Cmd       *cobra.Command

	genericclioptions.IOStreams
}

// NewCreateJobOptions initializes and returns new CreateJobOptions instance
func NewCreateJobOptions(ioStreams genericclioptions.IOStreams) *CreateJobOptions {
	return &CreateJobOptions{
		PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
		IOStreams:  ioStreams,
	}
}

// NewCmdCreateJob is a command to ease creating Jobs from CronJobs.
func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewCreateJobOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "job NAME [--image=image --from=cronjob/name] -- [COMMAND] [args...]",
		Short:   jobLong,
		Long:    jobLong,
		Example: jobExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))
			cmdutil.CheckErr(o.Validate())
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)
	cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
	cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")

	return cmd
}

// Complete completes all the required options
func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}
	o.Name = name
	if len(args) > 1 {
		o.Command = args[1:]
	}

	clientConfig, err := f.ToRESTConfig()
	if err != nil {
		return err
	}
	o.Client, err = batchv1client.NewForConfig(clientConfig)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	o.Builder = f.NewBuilder()
	o.Cmd = cmd

	o.DryRun = cmdutil.GetDryRunFlag(cmd)
	if o.DryRun {
		o.PrintFlags.Complete("%s (dry run)")
	}
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = func(obj runtime.Object) error {
		return printer.PrintObj(obj, o.Out)
	}

	return nil
}

// Validate makes sure provided values and valid Job options
func (o *CreateJobOptions) Validate() error {
	if (len(o.Image) == 0 && len(o.From) == 0) || (len(o.Image) != 0 && len(o.From) != 0) {
		return fmt.Errorf("either --image or --from must be specified")
	}
	if o.Command != nil && len(o.Command) != 0 && len(o.From) != 0 {
		return fmt.Errorf("cannot specify --from and command")
	}
	return nil
}

// Run performs the execution of 'create job' sub command
func (o *CreateJobOptions) Run() error {
	var job *batchv1.Job
	if len(o.Image) > 0 {
		job = o.createJob()
	} else {
		infos, err := o.Builder.
			Unstructured().
			NamespaceParam(o.Namespace).DefaultNamespace().
			ResourceTypeOrNameArgs(false, o.From).
			Flatten().
			Latest().
			Do().
			Infos()
		if err != nil {
			return err
		}
		if len(infos) != 1 {
			return fmt.Errorf("from must be an existing cronjob")
		}

		uncastVersionedObj, err := scheme.Scheme.ConvertToVersion(infos[0].Object, batchv1beta1.SchemeGroupVersion)
		if err != nil {
			return fmt.Errorf("from must be an existing cronjob: %v", err)
		}
		cronJob, ok := uncastVersionedObj.(*batchv1beta1.CronJob)
		if !ok {
			return fmt.Errorf("from must be an existing cronjob")
		}

		job = o.createJobFromCronJob(cronJob)
	}
	if !o.DryRun {
		var err error
		job, err = o.Client.Jobs(o.Namespace).Create(job)
		if err != nil {
			return fmt.Errorf("failed to create job: %v", err)
		}
	}

	return o.PrintObj(job)
}

func (o *CreateJobOptions) createJob() *batchv1.Job {
	return &batchv1.Job{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
		ObjectMeta: metav1.ObjectMeta{
			Name: o.Name,
		},
		Spec: batchv1.JobSpec{
			Template: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:    o.Name,
							Image:   o.Image,
							Command: o.Command,
						},
					},
					RestartPolicy: corev1.RestartPolicyNever,
				},
			},
		},
	}
}

func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1beta1.CronJob) *batchv1.Job {
	annotations := make(map[string]string)
	annotations["cronjob.kubernetes.io/instantiate"] = "manual"
	for k, v := range cronJob.Spec.JobTemplate.Annotations {
		annotations[k] = v
	}
	return &batchv1.Job{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
		ObjectMeta: metav1.ObjectMeta{
			Name:        o.Name,
			Annotations: annotations,
			Labels:      cronJob.Spec.JobTemplate.Labels,
		},
		Spec: cronJob.Spec.JobTemplate.Spec,
	}
}
<commit_msg>Image is a required parameter<commit_after>/*
Copyright 2018 The Kubernetes 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 create

import (
	"fmt"

	"github.com/spf13/cobra"

	batchv1 "k8s.io/api/batch/v1"
	batchv1beta1 "k8s.io/api/batch/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/genericclioptions/resource"
	batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/scheme"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
	"k8s.io/kubernetes/pkg/kubectl/util/templates"
)

var (
	jobLong = templates.LongDesc(i18n.T(`
		Create a job with the specified name.`))

	jobExample = templates.Examples(i18n.T(`
		# Create a job
		kubectl create job my-job --image=busybox

		# Create a job with command
		kubectl create job my-job --image=busybox -- date

		# Create a job from a CronJob named "a-cronjob"
		kubectl create job test-job --from=cronjob/a-cronjob`))
)

// CreateJobOptions is the command line options for 'create job'
type CreateJobOptions struct {
	PrintFlags *genericclioptions.PrintFlags

	PrintObj func(obj runtime.Object) error

	Name    string
	Image   string
	From    string
	Command []string

	Namespace string
	Client    batchv1client.BatchV1Interface
	DryRun    bool
	Builder   *resource.Builder
	Cmd       *cobra.Command

	genericclioptions.IOStreams
}

// NewCreateJobOptions initializes and returns new CreateJobOptions instance
func NewCreateJobOptions(ioStreams genericclioptions.IOStreams) *CreateJobOptions {
	return &CreateJobOptions{
		PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
		IOStreams:  ioStreams,
	}
}

// NewCmdCreateJob is a command to ease creating Jobs from CronJobs.
func NewCmdCreateJob(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := NewCreateJobOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "job NAME --image=image [--from=cronjob/name] -- [COMMAND] [args...]",
		Short:   jobLong,
		Long:    jobLong,
		Example: jobExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd, args))
			cmdutil.CheckErr(o.Validate())
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmdutil.AddApplyAnnotationFlags(cmd)
	cmdutil.AddValidateFlags(cmd)
	cmdutil.AddDryRunFlag(cmd)
	cmd.Flags().StringVar(&o.Image, "image", o.Image, "Image name to run.")
	cmd.Flags().StringVar(&o.From, "from", o.From, "The name of the resource to create a Job from (only cronjob is supported).")

	return cmd
}

// Complete completes all the required options
func (o *CreateJobOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
	name, err := NameFromCommandArgs(cmd, args)
	if err != nil {
		return err
	}
	o.Name = name
	if len(args) > 1 {
		o.Command = args[1:]
	}

	clientConfig, err := f.ToRESTConfig()
	if err != nil {
		return err
	}
	o.Client, err = batchv1client.NewForConfig(clientConfig)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	o.Builder = f.NewBuilder()
	o.Cmd = cmd

	o.DryRun = cmdutil.GetDryRunFlag(cmd)
	if o.DryRun {
		o.PrintFlags.Complete("%s (dry run)")
	}
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}
	o.PrintObj = func(obj runtime.Object) error {
		return printer.PrintObj(obj, o.Out)
	}

	return nil
}

// Validate makes sure provided values and valid Job options
func (o *CreateJobOptions) Validate() error {
	if (len(o.Image) == 0 && len(o.From) == 0) || (len(o.Image) != 0 && len(o.From) != 0) {
		return fmt.Errorf("either --image or --from must be specified")
	}
	if o.Command != nil && len(o.Command) != 0 && len(o.From) != 0 {
		return fmt.Errorf("cannot specify --from and command")
	}
	return nil
}

// Run performs the execution of 'create job' sub command
func (o *CreateJobOptions) Run() error {
	var job *batchv1.Job
	if len(o.Image) > 0 {
		job = o.createJob()
	} else {
		infos, err := o.Builder.
			Unstructured().
			NamespaceParam(o.Namespace).DefaultNamespace().
			ResourceTypeOrNameArgs(false, o.From).
			Flatten().
			Latest().
			Do().
			Infos()
		if err != nil {
			return err
		}
		if len(infos) != 1 {
			return fmt.Errorf("from must be an existing cronjob")
		}

		uncastVersionedObj, err := scheme.Scheme.ConvertToVersion(infos[0].Object, batchv1beta1.SchemeGroupVersion)
		if err != nil {
			return fmt.Errorf("from must be an existing cronjob: %v", err)
		}
		cronJob, ok := uncastVersionedObj.(*batchv1beta1.CronJob)
		if !ok {
			return fmt.Errorf("from must be an existing cronjob")
		}

		job = o.createJobFromCronJob(cronJob)
	}
	if !o.DryRun {
		var err error
		job, err = o.Client.Jobs(o.Namespace).Create(job)
		if err != nil {
			return fmt.Errorf("failed to create job: %v", err)
		}
	}

	return o.PrintObj(job)
}

func (o *CreateJobOptions) createJob() *batchv1.Job {
	return &batchv1.Job{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
		ObjectMeta: metav1.ObjectMeta{
			Name: o.Name,
		},
		Spec: batchv1.JobSpec{
			Template: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{
						{
							Name:    o.Name,
							Image:   o.Image,
							Command: o.Command,
						},
					},
					RestartPolicy: corev1.RestartPolicyNever,
				},
			},
		},
	}
}

func (o *CreateJobOptions) createJobFromCronJob(cronJob *batchv1beta1.CronJob) *batchv1.Job {
	annotations := make(map[string]string)
	annotations["cronjob.kubernetes.io/instantiate"] = "manual"
	for k, v := range cronJob.Spec.JobTemplate.Annotations {
		annotations[k] = v
	}
	return &batchv1.Job{
		// this is ok because we know exactly how we want to be serialized
		TypeMeta: metav1.TypeMeta{APIVersion: batchv1.SchemeGroupVersion.String(), Kind: "Job"},
		ObjectMeta: metav1.ObjectMeta{
			Name:        o.Name,
			Annotations: annotations,
			Labels:      cronJob.Spec.JobTemplate.Labels,
		},
		Spec: cronJob.Spec.JobTemplate.Spec,
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Rook 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 file

import (
	"fmt"

	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	"github.com/rook/rook/pkg/clusterd"
	"github.com/rook/rook/pkg/daemon/ceph/client"
	cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
	"github.com/rook/rook/pkg/daemon/ceph/model"
	"github.com/rook/rook/pkg/operator/ceph/file/mds"
	"github.com/rook/rook/pkg/operator/ceph/pool"
	cephver "github.com/rook/rook/pkg/operator/ceph/version"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	dataPoolSuffix     = "data"
	metadataPoolSuffix = "metadata"
	appName            = "cephfs"
)

// Filesystem represents an instance of a Ceph filesystem (CephFS)
type Filesystem struct {
	Name           string
	metadataPool   *model.Pool
	dataPools      []*model.Pool
	activeMDSCount int32
}

// createFilesystem creates a Ceph filesystem with metadata servers
func createFilesystem(
	clusterInfo *cephconfig.ClusterInfo,
	context *clusterd.Context,
	fs cephv1.CephFilesystem,
	rookVersion string,
	clusterSpec *cephv1.ClusterSpec,
	ownerRefs []metav1.OwnerReference,
	dataDirHostPath string,
	isUpgrade bool,
) error {
	if err := validateFilesystem(context, fs); err != nil {
		return err
	}

	if len(fs.Spec.DataPools) != 0 {
		var dataPools []*model.Pool
		for _, p := range fs.Spec.DataPools {
			dataPools = append(dataPools, p.ToModel(""))
		}
		f := newFS(fs.Name, fs.Spec.MetadataPool.ToModel(""), dataPools, fs.Spec.MetadataServer.ActiveCount)
		if err := f.doFilesystemCreate(context, clusterInfo.CephVersion, fs.Namespace); err != nil {
			return fmt.Errorf("failed to create filesystem %s: %+v", fs.Name, err)
		}
	}

	filesystem, err := client.GetFilesystem(context, fs.Namespace, fs.Name)
	if err != nil {
		return fmt.Errorf("failed to get filesystem %s: %+v", fs.Name, err)
	}

	// As of Nautilus, allow_standby_replay is a fs property so we need to apply it
	if clusterInfo.CephVersion.IsAtLeastNautilus() {
		if fs.Spec.MetadataServer.ActiveStandby {
			if err = client.AllowStandbyReplay(context, fs.Namespace, fs.Name, fs.Spec.MetadataServer.ActiveStandby); err != nil {
				return fmt.Errorf("failed to set allow_standby_replay to filesystem %s: %v", fs.Name, err)
			}
		}
	}

	// set the number of active mds instances
	if fs.Spec.MetadataServer.ActiveCount > 1 {
		if err = client.SetNumMDSRanks(context, clusterInfo.CephVersion, fs.Namespace, fs.Name, fs.Spec.MetadataServer.ActiveCount); err != nil {
			logger.Warningf("failed setting active mds count to %d. %+v", fs.Spec.MetadataServer.ActiveCount, err)
		}
	}

	logger.Infof("start running mdses for filesystem %s", fs.Name)
	c := mds.NewCluster(clusterInfo, context, rookVersion, clusterSpec, fs, filesystem, ownerRefs, dataDirHostPath, isUpgrade)
	if err := c.Start(); err != nil {
		return err
	}

	return nil
}

// deleteFileSystem deletes the filesystem from Ceph
func deleteFilesystem(context *clusterd.Context, cephVersion cephver.CephVersion, fs cephv1.CephFilesystem) error {
	// The most important part of deletion is that the filesystem gets removed from Ceph
	// The K8s resources will already be removed with the K8s owner references
	if err := downFilesystem(context, cephVersion, fs.Namespace, fs.Name); err != nil {
		// If the fs isn't deleted from Ceph, leave the daemons so it can still be used.
		return fmt.Errorf("failed to down filesystem %s: %+v", fs.Name, err)
	}

	// Permanently remove the filesystem if it was created by rook
	if len(fs.Spec.DataPools) != 0 {
		if err := client.RemoveFilesystem(context, fs.Namespace, fs.Name, fs.Spec.PreservePoolsOnDelete); err != nil {
			return fmt.Errorf("failed to remove filesystem %s: %+v", fs.Name, err)
		}
	}
	return nil
}

func validateFilesystem(context *clusterd.Context, f cephv1.CephFilesystem) error {
	if f.Name == "" {
		return fmt.Errorf("missing name")
	}
	if f.Namespace == "" {
		return fmt.Errorf("missing namespace")
	}
	if f.Spec.MetadataServer.ActiveCount < 1 {
		return fmt.Errorf("MetadataServer.ActiveCount must be at least 1")
	}
	// No data pool means that we expect the fs to exist already
	if len(f.Spec.DataPools) == 0 {
		return nil
	}
	if err := pool.ValidatePoolSpec(context, f.Namespace, &f.Spec.MetadataPool); err != nil {
		return fmt.Errorf("invalid metadata pool: %+v", err)
	}
	for _, p := range f.Spec.DataPools {
		if err := pool.ValidatePoolSpec(context, f.Namespace, &p); err != nil {
			return fmt.Errorf("Invalid data pool: %+v", err)
		}
	}

	return nil
}

// newFS creates a new instance of the file (MDS) service
func newFS(name string, metadataPool *model.Pool, dataPools []*model.Pool, activeMDSCount int32) *Filesystem {

	metadataPool.Name = fmt.Sprintf("%s-%s", name, metadataPoolSuffix)
	for i, pool := range dataPools {
		pool.Name = fmt.Sprintf("%s-%s%d", name, dataPoolSuffix, i)
	}

	// For the filesystem pool we don't want to enable the application pool
	// since it's being done via 'fs new' already
	metadataPool.NotEnableAppPool = true

	return &Filesystem{
		Name:           name,
		metadataPool:   metadataPool,
		dataPools:      dataPools,
		activeMDSCount: activeMDSCount,
	}
}

// doFilesystemCreate starts the Ceph file daemons and creates the filesystem in Ceph.
func (f *Filesystem) doFilesystemCreate(context *clusterd.Context, cephVersion cephver.CephVersion, clusterName string) error {
	_, err := client.GetFilesystem(context, clusterName, f.Name)
	if err == nil {
		logger.Infof("filesystem %s already exists", f.Name)
		// Even if the fs already exists, the num active mdses may have changed

		if err := client.SetNumMDSRanks(context, cephVersion, clusterName, f.Name, f.activeMDSCount); err != nil {
			logger.Errorf(
				fmt.Sprintf("failed to set num mds ranks (max_mds) to %d for filesystem %s, still continuing. ", f.activeMDSCount, f.Name) +
					"this error is not critical, but mdses may not be as failure tolerant as desired. " +
					fmt.Sprintf("USER should verify that the number of active mdses is %d with 'ceph fs get %s'", f.activeMDSCount, f.Name) +
					fmt.Sprintf(": %+v", err),
			)
		}
		return nil
	}
	if len(f.dataPools) == 0 {
		return fmt.Errorf("at least one data pool must be specified")
	}

	fslist, err := client.ListFilesystems(context, clusterName)
	if err != nil {
		return fmt.Errorf("Unable to list existing filesystem: %+v", err)
	}
	if len(fslist) > 0 && !client.IsMultiFSEnabled() {
		return fmt.Errorf("Cannot create multiple filesystems. Enable %s env variable to create more than one", client.MultiFsEnv)
	}

	poolNames, err := client.GetPoolNamesByID(context, clusterName)
	if err != nil {
		return fmt.Errorf("failed to get pool names. %+v", err)
	}

	logger.Infof("Creating filesystem %s", f.Name)

	// Make easy to locate a pool by name and avoid repeated searches
	reversedPoolMap := make(map[string]int)
	for key, value := range poolNames {
		reversedPoolMap[value] = key
	}

	pools_created := false
	if _, pool_found := reversedPoolMap[f.metadataPool.Name]; !pool_found {
		pools_created = true
		err = client.CreatePoolWithProfile(context, clusterName, *f.metadataPool, appName)
		if err != nil {
			return fmt.Errorf("failed to create metadata pool '%s': %+v", f.metadataPool.Name, err)
		}
	}

	var dataPoolNames []string
	for _, pool := range f.dataPools {
		dataPoolNames = append(dataPoolNames, pool.Name)
		if _, pool_found := reversedPoolMap[pool.Name]; !pool_found {
			pools_created = true
			err = client.CreatePoolWithProfile(context, clusterName, *pool, appName)
			if err != nil {
				return fmt.Errorf("failed to create data pool %s: %+v", pool.Name, err)
			}
			if pool.Type == model.ErasureCoded {
				// An erasure coded data pool used for a filesystem must allow overwrites
				if err := client.SetPoolProperty(context, clusterName, pool.Name, "allow_ec_overwrites", "true"); err != nil {
					logger.Warningf("failed to set ec pool property: %+v", err)
				}
			}
		}
	}

	// create the filesystem ('fs new' needs to be forced in order to reuse pre-existing pools)
	// if only one pool is created new it wont work (to avoid inconsistencies).
	if err := client.CreateFilesystem(context, clusterName, f.Name, f.metadataPool.Name, dataPoolNames, !pools_created); err != nil {
		return err
	}

	logger.Infof("created filesystem %s on %d data pool(s) and metadata pool %s", f.Name, len(f.dataPools), f.metadataPool.Name)
	return nil
}

// downFilesystem marks the filesystem as down and the MDS' as failed
func downFilesystem(context *clusterd.Context, cephVersion cephver.CephVersion, clusterName, filesystemName string) error {
	logger.Infof("Downing filesystem %s", filesystemName)

	// From Ceph nautilus onwards, a single Ceph command marks the filesystem as down and
	// MDSes as failed
	if cephVersion.IsAtLeastNautilus() {
		if err := client.FailFilesystem(context, clusterName, filesystemName); err != nil {
			return err
		}
		logger.Infof("Downed filesystem %s", filesystemName)
		return nil
	}

	// mark the cephFS instance as cluster_down before removing
	if err := client.MarkFilesystemAsDown(context, clusterName, filesystemName); err != nil {
		return err
	}

	// mark each MDS associated with the filesystem to "failed"
	fsDetails, err := client.GetFilesystem(context, clusterName, filesystemName)
	if err != nil {
		return err
	}
	for _, mdsInfo := range fsDetails.MDSMap.Info {
		if err := client.FailMDS(context, clusterName, mdsInfo.GID); err != nil {
			return err
		}
	}

	logger.Infof("Downed filesystem %s", filesystemName)
	return nil
}
<commit_msg>ceph: use golang convention on var name<commit_after>/*
Copyright 2016 The Rook 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 file

import (
	"fmt"

	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
	"github.com/rook/rook/pkg/clusterd"
	"github.com/rook/rook/pkg/daemon/ceph/client"
	cephconfig "github.com/rook/rook/pkg/daemon/ceph/config"
	"github.com/rook/rook/pkg/daemon/ceph/model"
	"github.com/rook/rook/pkg/operator/ceph/file/mds"
	"github.com/rook/rook/pkg/operator/ceph/pool"
	cephver "github.com/rook/rook/pkg/operator/ceph/version"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	dataPoolSuffix     = "data"
	metadataPoolSuffix = "metadata"
	appName            = "cephfs"
)

// Filesystem represents an instance of a Ceph filesystem (CephFS)
type Filesystem struct {
	Name           string
	metadataPool   *model.Pool
	dataPools      []*model.Pool
	activeMDSCount int32
}

// createFilesystem creates a Ceph filesystem with metadata servers
func createFilesystem(
	clusterInfo *cephconfig.ClusterInfo,
	context *clusterd.Context,
	fs cephv1.CephFilesystem,
	rookVersion string,
	clusterSpec *cephv1.ClusterSpec,
	ownerRefs []metav1.OwnerReference,
	dataDirHostPath string,
	isUpgrade bool,
) error {
	if err := validateFilesystem(context, fs); err != nil {
		return err
	}

	if len(fs.Spec.DataPools) != 0 {
		var dataPools []*model.Pool
		for _, p := range fs.Spec.DataPools {
			dataPools = append(dataPools, p.ToModel(""))
		}
		f := newFS(fs.Name, fs.Spec.MetadataPool.ToModel(""), dataPools, fs.Spec.MetadataServer.ActiveCount)
		if err := f.doFilesystemCreate(context, clusterInfo.CephVersion, fs.Namespace); err != nil {
			return fmt.Errorf("failed to create filesystem %s: %+v", fs.Name, err)
		}
	}

	filesystem, err := client.GetFilesystem(context, fs.Namespace, fs.Name)
	if err != nil {
		return fmt.Errorf("failed to get filesystem %s: %+v", fs.Name, err)
	}

	// As of Nautilus, allow_standby_replay is a fs property so we need to apply it
	if clusterInfo.CephVersion.IsAtLeastNautilus() {
		if fs.Spec.MetadataServer.ActiveStandby {
			if err = client.AllowStandbyReplay(context, fs.Namespace, fs.Name, fs.Spec.MetadataServer.ActiveStandby); err != nil {
				return fmt.Errorf("failed to set allow_standby_replay to filesystem %s: %v", fs.Name, err)
			}
		}
	}

	// set the number of active mds instances
	if fs.Spec.MetadataServer.ActiveCount > 1 {
		if err = client.SetNumMDSRanks(context, clusterInfo.CephVersion, fs.Namespace, fs.Name, fs.Spec.MetadataServer.ActiveCount); err != nil {
			logger.Warningf("failed setting active mds count to %d. %+v", fs.Spec.MetadataServer.ActiveCount, err)
		}
	}

	logger.Infof("start running mdses for filesystem %s", fs.Name)
	c := mds.NewCluster(clusterInfo, context, rookVersion, clusterSpec, fs, filesystem, ownerRefs, dataDirHostPath, isUpgrade)
	if err := c.Start(); err != nil {
		return err
	}

	return nil
}

// deleteFileSystem deletes the filesystem from Ceph
func deleteFilesystem(context *clusterd.Context, cephVersion cephver.CephVersion, fs cephv1.CephFilesystem) error {
	// The most important part of deletion is that the filesystem gets removed from Ceph
	// The K8s resources will already be removed with the K8s owner references
	if err := downFilesystem(context, cephVersion, fs.Namespace, fs.Name); err != nil {
		// If the fs isn't deleted from Ceph, leave the daemons so it can still be used.
		return fmt.Errorf("failed to down filesystem %s: %+v", fs.Name, err)
	}

	// Permanently remove the filesystem if it was created by rook
	if len(fs.Spec.DataPools) != 0 {
		if err := client.RemoveFilesystem(context, fs.Namespace, fs.Name, fs.Spec.PreservePoolsOnDelete); err != nil {
			return fmt.Errorf("failed to remove filesystem %s: %+v", fs.Name, err)
		}
	}
	return nil
}

func validateFilesystem(context *clusterd.Context, f cephv1.CephFilesystem) error {
	if f.Name == "" {
		return fmt.Errorf("missing name")
	}
	if f.Namespace == "" {
		return fmt.Errorf("missing namespace")
	}
	if f.Spec.MetadataServer.ActiveCount < 1 {
		return fmt.Errorf("MetadataServer.ActiveCount must be at least 1")
	}
	// No data pool means that we expect the fs to exist already
	if len(f.Spec.DataPools) == 0 {
		return nil
	}
	if err := pool.ValidatePoolSpec(context, f.Namespace, &f.Spec.MetadataPool); err != nil {
		return fmt.Errorf("invalid metadata pool: %+v", err)
	}
	for _, p := range f.Spec.DataPools {
		if err := pool.ValidatePoolSpec(context, f.Namespace, &p); err != nil {
			return fmt.Errorf("Invalid data pool: %+v", err)
		}
	}

	return nil
}

// newFS creates a new instance of the file (MDS) service
func newFS(name string, metadataPool *model.Pool, dataPools []*model.Pool, activeMDSCount int32) *Filesystem {

	metadataPool.Name = fmt.Sprintf("%s-%s", name, metadataPoolSuffix)
	for i, pool := range dataPools {
		pool.Name = fmt.Sprintf("%s-%s%d", name, dataPoolSuffix, i)
	}

	// For the filesystem pool we don't want to enable the application pool
	// since it's being done via 'fs new' already
	metadataPool.NotEnableAppPool = true

	return &Filesystem{
		Name:           name,
		metadataPool:   metadataPool,
		dataPools:      dataPools,
		activeMDSCount: activeMDSCount,
	}
}

// doFilesystemCreate starts the Ceph file daemons and creates the filesystem in Ceph.
func (f *Filesystem) doFilesystemCreate(context *clusterd.Context, cephVersion cephver.CephVersion, clusterName string) error {
	_, err := client.GetFilesystem(context, clusterName, f.Name)
	if err == nil {
		logger.Infof("filesystem %s already exists", f.Name)
		// Even if the fs already exists, the num active mdses may have changed

		if err := client.SetNumMDSRanks(context, cephVersion, clusterName, f.Name, f.activeMDSCount); err != nil {
			logger.Errorf(
				fmt.Sprintf("failed to set num mds ranks (max_mds) to %d for filesystem %s, still continuing. ", f.activeMDSCount, f.Name) +
					"this error is not critical, but mdses may not be as failure tolerant as desired. " +
					fmt.Sprintf("USER should verify that the number of active mdses is %d with 'ceph fs get %s'", f.activeMDSCount, f.Name) +
					fmt.Sprintf(": %+v", err),
			)
		}
		return nil
	}
	if len(f.dataPools) == 0 {
		return fmt.Errorf("at least one data pool must be specified")
	}

	fslist, err := client.ListFilesystems(context, clusterName)
	if err != nil {
		return fmt.Errorf("Unable to list existing filesystem: %+v", err)
	}
	if len(fslist) > 0 && !client.IsMultiFSEnabled() {
		return fmt.Errorf("Cannot create multiple filesystems. Enable %s env variable to create more than one", client.MultiFsEnv)
	}

	poolNames, err := client.GetPoolNamesByID(context, clusterName)
	if err != nil {
		return fmt.Errorf("failed to get pool names. %+v", err)
	}

	logger.Infof("Creating filesystem %s", f.Name)

	// Make easy to locate a pool by name and avoid repeated searches
	reversedPoolMap := make(map[string]int)
	for key, value := range poolNames {
		reversedPoolMap[value] = key
	}

	poolsCreated := false
	if _, poolFound := reversedPoolMap[f.metadataPool.Name]; !poolFound {
		poolsCreated = true
		err = client.CreatePoolWithProfile(context, clusterName, *f.metadataPool, appName)
		if err != nil {
			return fmt.Errorf("failed to create metadata pool '%s': %+v", f.metadataPool.Name, err)
		}
	}

	var dataPoolNames []string
	for _, pool := range f.dataPools {
		dataPoolNames = append(dataPoolNames, pool.Name)
		if _, poolFound := reversedPoolMap[pool.Name]; !poolFound {
			poolsCreated = true
			err = client.CreatePoolWithProfile(context, clusterName, *pool, appName)
			if err != nil {
				return fmt.Errorf("failed to create data pool %s: %+v", pool.Name, err)
			}
			if pool.Type == model.ErasureCoded {
				// An erasure coded data pool used for a filesystem must allow overwrites
				if err := client.SetPoolProperty(context, clusterName, pool.Name, "allow_ec_overwrites", "true"); err != nil {
					logger.Warningf("failed to set ec pool property: %+v", err)
				}
			}
		}
	}

	// create the filesystem ('fs new' needs to be forced in order to reuse pre-existing pools)
	// if only one pool is created new it wont work (to avoid inconsistencies).
	if err := client.CreateFilesystem(context, clusterName, f.Name, f.metadataPool.Name, dataPoolNames, !poolsCreated); err != nil {
		return err
	}

	logger.Infof("created filesystem %s on %d data pool(s) and metadata pool %s", f.Name, len(f.dataPools), f.metadataPool.Name)
	return nil
}

// downFilesystem marks the filesystem as down and the MDS' as failed
func downFilesystem(context *clusterd.Context, cephVersion cephver.CephVersion, clusterName, filesystemName string) error {
	logger.Infof("Downing filesystem %s", filesystemName)

	// From Ceph nautilus onwards, a single Ceph command marks the filesystem as down and
	// MDSes as failed
	if cephVersion.IsAtLeastNautilus() {
		if err := client.FailFilesystem(context, clusterName, filesystemName); err != nil {
			return err
		}
		logger.Infof("Downed filesystem %s", filesystemName)
		return nil
	}

	// mark the cephFS instance as cluster_down before removing
	if err := client.MarkFilesystemAsDown(context, clusterName, filesystemName); err != nil {
		return err
	}

	// mark each MDS associated with the filesystem to "failed"
	fsDetails, err := client.GetFilesystem(context, clusterName, filesystemName)
	if err != nil {
		return err
	}
	for _, mdsInfo := range fsDetails.MDSMap.Info {
		if err := client.FailMDS(context, clusterName, mdsInfo.GID); err != nil {
			return err
		}
	}

	logger.Infof("Downed filesystem %s", filesystemName)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Rook Authors. All rights reserved.

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

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

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

package version

import (
	"fmt"
	"regexp"
	"strconv"

	"github.com/coreos/pkg/capnslog"
	"github.com/pkg/errors"
)

// CephVersion represents the Ceph version format
type CephVersion struct {
	Major int
	Minor int
	Extra int
	Build int
}

const (
	unknownVersionString = "<unknown version>"
)

var (
	// Minimum supported version is 14.2.5
	Minimum = CephVersion{14, 2, 5, 0}
	// Nautilus Ceph version
	Nautilus = CephVersion{14, 0, 0, 0}
	// Octopus Ceph version
	Octopus = CephVersion{15, 0, 0, 0}
	// Pacific Ceph version
	Pacific = CephVersion{16, 0, 0, 0}

	// cephVolumeLVMDiskSortingCephVersion introduced a major regression in c-v and thus is not suitable for production
	cephVolumeLVMDiskSortingCephVersion = CephVersion{Major: 14, Minor: 2, Extra: 13}

	// supportedVersions are production-ready versions that rook supports
	supportedVersions = []CephVersion{Nautilus, Octopus}

	// unsupportedVersions are possibly Ceph pin-point release that introduced breaking changes and not recommended
	unsupportedVersions = []CephVersion{cephVolumeLVMDiskSortingCephVersion}

	// for parsing the output of `ceph --version`
	versionPattern = regexp.MustCompile(`ceph version (\d+)\.(\d+)\.(\d+)`)

	// For a build release the output is "ceph version 14.2.4-64.el8cp"
	// So we need to detect the build version change
	buildVersionPattern = regexp.MustCompile(`ceph version (\d+)\.(\d+)\.(\d+)\-(\d+)`)

	logger = capnslog.NewPackageLogger("github.com/rook/rook", "cephver")
)

func (v *CephVersion) String() string {
	return fmt.Sprintf("%d.%d.%d-%d %s",
		v.Major, v.Minor, v.Extra, v.Build, v.ReleaseName())
}

// CephVersionFormatted returns the Ceph version in a human readable format
func (v *CephVersion) CephVersionFormatted() string {
	return fmt.Sprintf("ceph version %d.%d.%d-%d %s",
		v.Major, v.Minor, v.Extra, v.Build, v.ReleaseName())
}

// ReleaseName is the name of the Ceph release
func (v *CephVersion) ReleaseName() string {
	switch v.Major {
	case Octopus.Major:
		return "octopus"
	case Nautilus.Major:
		return "nautilus"
	default:
		return unknownVersionString
	}
}

// ExtractCephVersion extracts the major, minor and extra digit of a Ceph release
func ExtractCephVersion(src string) (*CephVersion, error) {
	var build int
	m := versionPattern.FindStringSubmatch(src)
	if m == nil {
		return nil, errors.Errorf("failed to parse version from: %q", src)
	}

	major, err := strconv.Atoi(m[1])
	if err != nil {
		return nil, errors.Errorf("failed to parse version major part: %q", m[1])
	}

	minor, err := strconv.Atoi(m[2])
	if err != nil {
		return nil, errors.Errorf("failed to parse version minor part: %q", m[2])
	}

	extra, err := strconv.Atoi(m[3])
	if err != nil {
		return nil, errors.Errorf("failed to parse version extra part: %q", m[3])
	}

	// See if we are running on a build release
	mm := buildVersionPattern.FindStringSubmatch(src)
	// We don't need to handle any error here, so let's jump in only when "mm" has content
	if mm != nil {
		build, err = strconv.Atoi(mm[4])
		if err != nil {
			logger.Warningf("failed to convert version build number part %q to an integer, ignoring", mm[4])
		}
	}

	return &CephVersion{major, minor, extra, build}, nil
}

// Supported checks if a given release is supported
func (v *CephVersion) Supported() bool {
	for _, sv := range supportedVersions {
		if v.isRelease(sv) {
			return true
		}
	}
	return false
}

// Unsupported checks if a given release is supported
func (v *CephVersion) Unsupported() bool {
	for _, sv := range unsupportedVersions {
		if v.isExactly(sv) {
			return true
		}
	}
	return false
}

func (v *CephVersion) isRelease(other CephVersion) bool {
	return v.Major == other.Major
}

func (v *CephVersion) isExactly(other CephVersion) bool {
	return v.Major == other.Major && v.Minor == other.Minor && v.Extra == other.Extra
}

// IsNautilus checks if the Ceph version is Nautilus
func (v *CephVersion) IsNautilus() bool {
	return v.isRelease(Nautilus)
}

// IsOctopus checks if the Ceph version is Octopus
func (v *CephVersion) IsOctopus() bool {
	return v.isRelease(Octopus)
}

// IsPacific checks if the Ceph version is Pacific
func (v *CephVersion) IsPacific() bool {
	return v.isRelease(Pacific)
}

// IsAtLeast checks a given Ceph version is at least a given one
func (v *CephVersion) IsAtLeast(other CephVersion) bool {
	if v.Major > other.Major {
		return true
	} else if v.Major < other.Major {
		return false
	}
	// If we arrive here then v.Major == other.Major
	if v.Minor > other.Minor {
		return true
	} else if v.Minor < other.Minor {
		return false
	}
	// If we arrive here then v.Minor == other.Minor
	if v.Extra > other.Extra {
		return true
	} else if v.Extra < other.Extra {
		return false
	}
	// If we arrive here then both versions are identical
	return true
}

// IsAtLeastPacific check that the Ceph version is at least Pacific
func (v *CephVersion) IsAtLeastPacific() bool {
	return v.IsAtLeast(Pacific)
}

// IsAtLeastOctopus check that the Ceph version is at least Octopus
func (v *CephVersion) IsAtLeastOctopus() bool {
	return v.IsAtLeast(Octopus)
}

// IsAtLeastNautilus check that the Ceph version is at least Nautilus
func (v *CephVersion) IsAtLeastNautilus() bool {
	return v.IsAtLeast(Nautilus)
}

// IsIdentical checks if Ceph versions are identical
func IsIdentical(a, b CephVersion) bool {
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build == b.Build {
					return true
				}
			}
		}
	}

	return false
}

// IsSuperior checks if a given version if superior to another one
func IsSuperior(a, b CephVersion) bool {
	if a.Major > b.Major {
		return true
	}
	if a.Major == b.Major {
		if a.Minor > b.Minor {
			return true
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra > b.Extra {
				return true
			}
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build > b.Build {
					return true
				}
			}
		}
	}

	return false
}

// IsInferior checks if a given version if inferior to another one
func IsInferior(a, b CephVersion) bool {
	if a.Major < b.Major {
		return true
	}
	if a.Major == b.Major {
		if a.Minor < b.Minor {
			return true
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra < b.Extra {
				return true
			}
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build < b.Build {
					return true
				}
			}
		}
	}

	return false
}

// ValidateCephVersionsBetweenLocalAndExternalClusters makes sure an external cluster can be connected
// by checking the external ceph versions available and comparing it with the local image provided
func ValidateCephVersionsBetweenLocalAndExternalClusters(localVersion, externalVersion CephVersion) error {
	logger.Debugf("local version is %q, external version is %q", localVersion.String(), externalVersion.String())

	// We only support Nautilus or newer
	if !externalVersion.IsAtLeastNautilus() {
		return errors.Errorf("unsupported ceph version %q, need at least nautilus, delete your cluster CR and create a new one with a correct ceph version", externalVersion.String())
	}

	// Identical version, regardless if other CRs are running, it's ok!
	if IsIdentical(localVersion, externalVersion) {
		return nil
	}

	// Local version must never be higher than the external one
	if IsSuperior(localVersion, externalVersion) {
		return errors.Errorf("local cluster ceph version is higher %q than the external cluster %q, this must never happen", externalVersion.String(), localVersion.String())
	}

	// External cluster was updated to a minor version higher, consider updating too!
	if localVersion.Major == externalVersion.Major {
		if IsSuperior(externalVersion, localVersion) {
			logger.Warningf("external cluster ceph version is a minor version higher %q than the local cluster %q, consider upgrading", externalVersion.String(), localVersion.String())
			return nil
		}
	}

	// The external cluster was upgraded, consider upgrading too!
	if localVersion.Major < externalVersion.Major {
		logger.Errorf("external cluster ceph version is a major version higher %q than the local cluster %q, consider upgrading", externalVersion.String(), localVersion.String())
		return nil
	}

	return nil
}
<commit_msg>ceph: add pacific release name<commit_after>/*
Copyright 2019 The Rook Authors. All rights reserved.

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

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

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

package version

import (
	"fmt"
	"regexp"
	"strconv"

	"github.com/coreos/pkg/capnslog"
	"github.com/pkg/errors"
)

// CephVersion represents the Ceph version format
type CephVersion struct {
	Major int
	Minor int
	Extra int
	Build int
}

const (
	unknownVersionString = "<unknown version>"
)

var (
	// Minimum supported version is 14.2.5
	Minimum = CephVersion{14, 2, 5, 0}
	// Nautilus Ceph version
	Nautilus = CephVersion{14, 0, 0, 0}
	// Octopus Ceph version
	Octopus = CephVersion{15, 0, 0, 0}
	// Pacific Ceph version
	Pacific = CephVersion{16, 0, 0, 0}

	// cephVolumeLVMDiskSortingCephVersion introduced a major regression in c-v and thus is not suitable for production
	cephVolumeLVMDiskSortingCephVersion = CephVersion{Major: 14, Minor: 2, Extra: 13}

	// supportedVersions are production-ready versions that rook supports
	supportedVersions = []CephVersion{Nautilus, Octopus}

	// unsupportedVersions are possibly Ceph pin-point release that introduced breaking changes and not recommended
	unsupportedVersions = []CephVersion{cephVolumeLVMDiskSortingCephVersion}

	// for parsing the output of `ceph --version`
	versionPattern = regexp.MustCompile(`ceph version (\d+)\.(\d+)\.(\d+)`)

	// For a build release the output is "ceph version 14.2.4-64.el8cp"
	// So we need to detect the build version change
	buildVersionPattern = regexp.MustCompile(`ceph version (\d+)\.(\d+)\.(\d+)\-(\d+)`)

	logger = capnslog.NewPackageLogger("github.com/rook/rook", "cephver")
)

func (v *CephVersion) String() string {
	return fmt.Sprintf("%d.%d.%d-%d %s",
		v.Major, v.Minor, v.Extra, v.Build, v.ReleaseName())
}

// CephVersionFormatted returns the Ceph version in a human readable format
func (v *CephVersion) CephVersionFormatted() string {
	return fmt.Sprintf("ceph version %d.%d.%d-%d %s",
		v.Major, v.Minor, v.Extra, v.Build, v.ReleaseName())
}

// ReleaseName is the name of the Ceph release
func (v *CephVersion) ReleaseName() string {
	switch v.Major {
	case Nautilus.Major:
		return "nautilus"
	case Octopus.Major:
		return "octopus"
	case Pacific.Major:
		return "pacific"
	default:
		return unknownVersionString
	}
}

// ExtractCephVersion extracts the major, minor and extra digit of a Ceph release
func ExtractCephVersion(src string) (*CephVersion, error) {
	var build int
	m := versionPattern.FindStringSubmatch(src)
	if m == nil {
		return nil, errors.Errorf("failed to parse version from: %q", src)
	}

	major, err := strconv.Atoi(m[1])
	if err != nil {
		return nil, errors.Errorf("failed to parse version major part: %q", m[1])
	}

	minor, err := strconv.Atoi(m[2])
	if err != nil {
		return nil, errors.Errorf("failed to parse version minor part: %q", m[2])
	}

	extra, err := strconv.Atoi(m[3])
	if err != nil {
		return nil, errors.Errorf("failed to parse version extra part: %q", m[3])
	}

	// See if we are running on a build release
	mm := buildVersionPattern.FindStringSubmatch(src)
	// We don't need to handle any error here, so let's jump in only when "mm" has content
	if mm != nil {
		build, err = strconv.Atoi(mm[4])
		if err != nil {
			logger.Warningf("failed to convert version build number part %q to an integer, ignoring", mm[4])
		}
	}

	return &CephVersion{major, minor, extra, build}, nil
}

// Supported checks if a given release is supported
func (v *CephVersion) Supported() bool {
	for _, sv := range supportedVersions {
		if v.isRelease(sv) {
			return true
		}
	}
	return false
}

// Unsupported checks if a given release is supported
func (v *CephVersion) Unsupported() bool {
	for _, sv := range unsupportedVersions {
		if v.isExactly(sv) {
			return true
		}
	}
	return false
}

func (v *CephVersion) isRelease(other CephVersion) bool {
	return v.Major == other.Major
}

func (v *CephVersion) isExactly(other CephVersion) bool {
	return v.Major == other.Major && v.Minor == other.Minor && v.Extra == other.Extra
}

// IsNautilus checks if the Ceph version is Nautilus
func (v *CephVersion) IsNautilus() bool {
	return v.isRelease(Nautilus)
}

// IsOctopus checks if the Ceph version is Octopus
func (v *CephVersion) IsOctopus() bool {
	return v.isRelease(Octopus)
}

// IsPacific checks if the Ceph version is Pacific
func (v *CephVersion) IsPacific() bool {
	return v.isRelease(Pacific)
}

// IsAtLeast checks a given Ceph version is at least a given one
func (v *CephVersion) IsAtLeast(other CephVersion) bool {
	if v.Major > other.Major {
		return true
	} else if v.Major < other.Major {
		return false
	}
	// If we arrive here then v.Major == other.Major
	if v.Minor > other.Minor {
		return true
	} else if v.Minor < other.Minor {
		return false
	}
	// If we arrive here then v.Minor == other.Minor
	if v.Extra > other.Extra {
		return true
	} else if v.Extra < other.Extra {
		return false
	}
	// If we arrive here then both versions are identical
	return true
}

// IsAtLeastPacific check that the Ceph version is at least Pacific
func (v *CephVersion) IsAtLeastPacific() bool {
	return v.IsAtLeast(Pacific)
}

// IsAtLeastOctopus check that the Ceph version is at least Octopus
func (v *CephVersion) IsAtLeastOctopus() bool {
	return v.IsAtLeast(Octopus)
}

// IsAtLeastNautilus check that the Ceph version is at least Nautilus
func (v *CephVersion) IsAtLeastNautilus() bool {
	return v.IsAtLeast(Nautilus)
}

// IsIdentical checks if Ceph versions are identical
func IsIdentical(a, b CephVersion) bool {
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build == b.Build {
					return true
				}
			}
		}
	}

	return false
}

// IsSuperior checks if a given version if superior to another one
func IsSuperior(a, b CephVersion) bool {
	if a.Major > b.Major {
		return true
	}
	if a.Major == b.Major {
		if a.Minor > b.Minor {
			return true
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra > b.Extra {
				return true
			}
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build > b.Build {
					return true
				}
			}
		}
	}

	return false
}

// IsInferior checks if a given version if inferior to another one
func IsInferior(a, b CephVersion) bool {
	if a.Major < b.Major {
		return true
	}
	if a.Major == b.Major {
		if a.Minor < b.Minor {
			return true
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra < b.Extra {
				return true
			}
		}
	}
	if a.Major == b.Major {
		if a.Minor == b.Minor {
			if a.Extra == b.Extra {
				if a.Build < b.Build {
					return true
				}
			}
		}
	}

	return false
}

// ValidateCephVersionsBetweenLocalAndExternalClusters makes sure an external cluster can be connected
// by checking the external ceph versions available and comparing it with the local image provided
func ValidateCephVersionsBetweenLocalAndExternalClusters(localVersion, externalVersion CephVersion) error {
	logger.Debugf("local version is %q, external version is %q", localVersion.String(), externalVersion.String())

	// We only support Nautilus or newer
	if !externalVersion.IsAtLeastNautilus() {
		return errors.Errorf("unsupported ceph version %q, need at least nautilus, delete your cluster CR and create a new one with a correct ceph version", externalVersion.String())
	}

	// Identical version, regardless if other CRs are running, it's ok!
	if IsIdentical(localVersion, externalVersion) {
		return nil
	}

	// Local version must never be higher than the external one
	if IsSuperior(localVersion, externalVersion) {
		return errors.Errorf("local cluster ceph version is higher %q than the external cluster %q, this must never happen", externalVersion.String(), localVersion.String())
	}

	// External cluster was updated to a minor version higher, consider updating too!
	if localVersion.Major == externalVersion.Major {
		if IsSuperior(externalVersion, localVersion) {
			logger.Warningf("external cluster ceph version is a minor version higher %q than the local cluster %q, consider upgrading", externalVersion.String(), localVersion.String())
			return nil
		}
	}

	// The external cluster was upgraded, consider upgrading too!
	if localVersion.Major < externalVersion.Major {
		logger.Errorf("external cluster ceph version is a major version higher %q than the local cluster %q, consider upgrading", externalVersion.String(), localVersion.String())
		return nil
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 contour

import (
	"context"

	contourclient "knative.dev/net-contour/pkg/client/injection/client"
	proxyinformer "knative.dev/net-contour/pkg/client/injection/informers/projectcontour/v1/httpproxy"
	ingressinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/ingress"
	ingressreconciler "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/ingress"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	podinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod"
	serviceinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/service"

	"knative.dev/net-contour/pkg/reconciler/contour/config"
	"knative.dev/networking/pkg/apis/networking"
	"knative.dev/networking/pkg/apis/networking/v1alpha1"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/tracker"
	"knative.dev/serving/pkg/network"
	"knative.dev/serving/pkg/network/status"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
)

// NewController returns a new Ingress controller for Project Contour.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	endpointsInformer := endpointsinformer.Get(ctx)
	serviceInformer := serviceinformer.Get(ctx)
	ingressInformer := ingressinformer.Get(ctx)
	proxyInformer := proxyinformer.Get(ctx)
	podInformer := podinformer.Get(ctx)

	c := &Reconciler{
		contourClient:   contourclient.Get(ctx),
		contourLister:   proxyInformer.Lister(),
		serviceLister:   serviceInformer.Lister(),
		endpointsLister: endpointsInformer.Lister(),
	}
	myFilterFunc := reconciler.AnnotationFilterFunc(networking.IngressClassAnnotationKey, ContourIngressClassName, false)
	impl := ingressreconciler.NewImpl(ctx, c,
		func(impl *controller.Impl) controller.Options {
			logger.Info("Setting up ConfigMap receivers")
			configsToResync := []interface{}{
				&config.Contour{},
				&network.Config{},
			}

			resyncIngressesOnConfigChange := configmap.TypeFilter(configsToResync...)(func(string, interface{}) {
				impl.FilteredGlobalResync(myFilterFunc, ingressInformer.Informer())
			})
			configStore := config.NewStore(logger.Named("config-store"), resyncIngressesOnConfigChange)
			configStore.WatchConfigs(cmw)
			return controller.Options{ConfigStore: configStore}
		})

	logger.Info("Setting up event handlers")

	ingressHandler := cache.FilteringResourceEventHandler{
		FilterFunc: myFilterFunc,
		Handler:    controller.HandleAll(impl.Enqueue),
	}
	ingressInformer.Informer().AddEventHandler(ingressHandler)

	proxyInformer.Informer().AddEventHandler(controller.HandleAll(impl.EnqueueControllerOf))

	statusProber := status.NewProber(
		logger.Named("status-manager"),
		&lister{
			ServiceLister:   serviceInformer.Lister(),
			EndpointsLister: endpointsInformer.Lister(),
		},
		func(ia *v1alpha1.Ingress) { impl.Enqueue(ia) })
	c.statusManager = statusProber
	statusProber.Start(ctx.Done())

	ingressInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// Cancel probing when an Ingress is deleted
		DeleteFunc: statusProber.CancelIngressProbing,
	})
	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// Cancel probing when a Pod is deleted
		DeleteFunc: statusProber.CancelPodProbing,
	})

	// Set up our tracker to facilitate tracking cross-references to objects we don't own.
	c.tracker = tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
	serviceInformer.Informer().AddEventHandler(controller.HandleAll(
		// Call the tracker's OnChanged method, but we've seen the objects
		// coming through this path missing TypeMeta, so ensure it is properly
		// populated.
		controller.EnsureTypeMeta(
			c.tracker.OnChanged,
			corev1.SchemeGroupVersion.WithKind("Service"),
		),
	))
	endpointsInformer.Informer().AddEventHandler(controller.HandleAll(
		// Call the tracker's OnChanged method, but we've seen the objects
		// coming through this path missing TypeMeta, so ensure it is properly
		// populated.
		controller.EnsureTypeMeta(
			c.tracker.OnChanged,
			corev1.SchemeGroupVersion.WithKind("Endpoints"),
		),
	))

	return impl
}
<commit_msg>Filter on the OwnerRef GroupKind. (#163)<commit_after>/*
Copyright 2020 The Knative 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 contour

import (
	"context"

	contourclient "knative.dev/net-contour/pkg/client/injection/client"
	proxyinformer "knative.dev/net-contour/pkg/client/injection/informers/projectcontour/v1/httpproxy"
	ingressinformer "knative.dev/networking/pkg/client/injection/informers/networking/v1alpha1/ingress"
	ingressreconciler "knative.dev/networking/pkg/client/injection/reconciler/networking/v1alpha1/ingress"
	endpointsinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints"
	podinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/pod"
	serviceinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/service"

	"knative.dev/net-contour/pkg/reconciler/contour/config"
	"knative.dev/networking/pkg/apis/networking"
	"knative.dev/networking/pkg/apis/networking/v1alpha1"
	"knative.dev/pkg/configmap"
	"knative.dev/pkg/controller"
	"knative.dev/pkg/logging"
	"knative.dev/pkg/reconciler"
	"knative.dev/pkg/tracker"
	"knative.dev/serving/pkg/network"
	"knative.dev/serving/pkg/network/status"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/tools/cache"
)

// NewController returns a new Ingress controller for Project Contour.
func NewController(
	ctx context.Context,
	cmw configmap.Watcher,
) *controller.Impl {
	logger := logging.FromContext(ctx)

	endpointsInformer := endpointsinformer.Get(ctx)
	serviceInformer := serviceinformer.Get(ctx)
	ingressInformer := ingressinformer.Get(ctx)
	proxyInformer := proxyinformer.Get(ctx)
	podInformer := podinformer.Get(ctx)

	c := &Reconciler{
		contourClient:   contourclient.Get(ctx),
		contourLister:   proxyInformer.Lister(),
		serviceLister:   serviceInformer.Lister(),
		endpointsLister: endpointsInformer.Lister(),
	}
	myFilterFunc := reconciler.AnnotationFilterFunc(networking.IngressClassAnnotationKey, ContourIngressClassName, false)
	impl := ingressreconciler.NewImpl(ctx, c,
		func(impl *controller.Impl) controller.Options {
			logger.Info("Setting up ConfigMap receivers")
			configsToResync := []interface{}{
				&config.Contour{},
				&network.Config{},
			}

			resyncIngressesOnConfigChange := configmap.TypeFilter(configsToResync...)(func(string, interface{}) {
				impl.FilteredGlobalResync(myFilterFunc, ingressInformer.Informer())
			})
			configStore := config.NewStore(logger.Named("config-store"), resyncIngressesOnConfigChange)
			configStore.WatchConfigs(cmw)
			return controller.Options{ConfigStore: configStore}
		})

	logger.Info("Setting up event handlers")

	ingressHandler := cache.FilteringResourceEventHandler{
		FilterFunc: myFilterFunc,
		Handler:    controller.HandleAll(impl.Enqueue),
	}
	ingressInformer.Informer().AddEventHandler(ingressHandler)

	proxyInformer.Informer().AddEventHandler(cache.FilteringResourceEventHandler{
		FilterFunc: controller.FilterControllerGK(v1alpha1.Kind("Ingress")),
		Handler:    controller.HandleAll(impl.EnqueueControllerOf),
	})

	statusProber := status.NewProber(
		logger.Named("status-manager"),
		&lister{
			ServiceLister:   serviceInformer.Lister(),
			EndpointsLister: endpointsInformer.Lister(),
		},
		func(ia *v1alpha1.Ingress) { impl.Enqueue(ia) })
	c.statusManager = statusProber
	statusProber.Start(ctx.Done())

	ingressInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// Cancel probing when an Ingress is deleted
		DeleteFunc: statusProber.CancelIngressProbing,
	})
	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		// Cancel probing when a Pod is deleted
		DeleteFunc: statusProber.CancelPodProbing,
	})

	// Set up our tracker to facilitate tracking cross-references to objects we don't own.
	c.tracker = tracker.New(impl.EnqueueKey, controller.GetTrackerLease(ctx))
	serviceInformer.Informer().AddEventHandler(controller.HandleAll(
		// Call the tracker's OnChanged method, but we've seen the objects
		// coming through this path missing TypeMeta, so ensure it is properly
		// populated.
		controller.EnsureTypeMeta(
			c.tracker.OnChanged,
			corev1.SchemeGroupVersion.WithKind("Service"),
		),
	))
	endpointsInformer.Informer().AddEventHandler(controller.HandleAll(
		// Call the tracker's OnChanged method, but we've seen the objects
		// coming through this path missing TypeMeta, so ensure it is properly
		// populated.
		controller.EnsureTypeMeta(
			c.tracker.OnChanged,
			corev1.SchemeGroupVersion.WithKind("Endpoints"),
		),
	))

	return impl
}
<|endoftext|>


<commit_before>package launch

/* Lovingly borrowed from https://github.com/sstephenson/launch_socket_server/blob/master/src/launch/socket.go */

/*
#include <stdlib.h>
int launch_activate_socket(const char *name, int **fds, size_t *cnt);
*/
import "C"

import (
	"errors"
	"net"
	"os"
	"unsafe"
)

func SocketFiles(name string) ([]*os.File, error) {
	fds, err := activateSocket(name)
	if err != nil {
		return nil, err
	}

	files := make([]*os.File, 0)
	for _, fd := range fds {
		file := os.NewFile(uintptr(fd), "")
		files = append(files, file)
	}

	return files, nil
}

func SocketListeners(name string) ([]net.Listener, error) {
	files, err := SocketFiles(name)
	if err != nil {
		return nil, err
	}

	listeners := make([]net.Listener, 0)
	for _, file := range files {
		listener, err := net.FileListener(file)
		if err != nil {
			return nil, err
		}
		listeners = append(listeners, listener)
	}

	return listeners, nil
}

func activateSocket(name string) ([]int, error) {
	c_name := C.CString(name)
	var c_fds *C.int
	c_cnt := C.size_t(0)

	err := C.launch_activate_socket(c_name, &c_fds, &c_cnt)
	if err != 0 {
		return nil, errors.New("couldn't activate launchd socket " + name)
	}

	length := int(c_cnt)
	pointer := unsafe.Pointer(c_fds)
	fds := (*[1 << 30]C.int)(pointer)
	result := make([]int, length)

	for i := 0; i < length; i++ {
		result[i] = int(fds[i])
	}

	C.free(pointer)
	return result, nil
}
<commit_msg>launch_activate_socket is only provided by XPC on Darwin arch, so don't build launch.go elsewhere<commit_after>// +build darwin

package launch

/* Lovingly borrowed from https://github.com/sstephenson/launch_socket_server/blob/master/src/launch/socket.go */

/*
#include <stdlib.h>
int launch_activate_socket(const char *name, int **fds, size_t *cnt);
*/
import "C"

import (
	"errors"
	"net"
	"os"
	"unsafe"
)

func SocketFiles(name string) ([]*os.File, error) {
	fds, err := activateSocket(name)
	if err != nil {
		return nil, err
	}

	files := make([]*os.File, 0)
	for _, fd := range fds {
		file := os.NewFile(uintptr(fd), "")
		files = append(files, file)
	}

	return files, nil
}

func SocketListeners(name string) ([]net.Listener, error) {
	files, err := SocketFiles(name)
	if err != nil {
		return nil, err
	}

	listeners := make([]net.Listener, 0)
	for _, file := range files {
		listener, err := net.FileListener(file)
		if err != nil {
			return nil, err
		}
		listeners = append(listeners, listener)
	}

	return listeners, nil
}

func activateSocket(name string) ([]int, error) {
	c_name := C.CString(name)
	var c_fds *C.int
	c_cnt := C.size_t(0)

	err := C.launch_activate_socket(c_name, &c_fds, &c_cnt)
	if err != 0 {
		return nil, errors.New("couldn't activate launchd socket " + name)
	}

	length := int(c_cnt)
	pointer := unsafe.Pointer(c_fds)
	fds := (*[1 << 30]C.int)(pointer)
	result := make([]int, length)

	for i := 0; i < length; i++ {
		result[i] = int(fds[i])
	}

	C.free(pointer)
	return result, nil
}
<|endoftext|>


<commit_before>package algoliasearch

func checkQuery(query Map, ignore ...string) error {
Outer:
	for k, v := range query {
		// Continue if `k` is to be ignored.
		for _, s := range ignore {
			if s == k {
				continue Outer
			}
		}

		switch k {
		case "query",
			"queryType",
			"typoTolerance",
			"removeWordsIfNoResults",
			"restrictSearchableAttributes",
			"highlightPreTag",
			"highlightPostTag",
			"snippetEllipsisText",
			"filters",
			"analyticsTags",
			"optionalWords",
			"numericFilters",
			"tagFilters",
			"facets",
			"facetFilters",
			"aroundLatLng",
			"insideBoundingBox",
			"insidePolygon",
			"exactOnSingleWordQuery":
			if _, ok := v.(string); !ok {
				return invalidType(k, "string")
			}

		case "attributesToRetrieve",
			"disableTypoToleranceOnAttributes",
			"attributesToSnippet",
			"attributesToHighlight",
			"alternativesAsExact":
			if _, ok := v.([]string); !ok {
				return invalidType(k, "[]string")
			}

		case "minWordSizefor1Typo",
			"minWordSizefor2Typos",
			"minProximity",
			"page",
			"hitsPerPage",
			"getRankingInfo",
			"distinct",
			"maxValuesPerFacet",
			"aroundPrecision",
			"minimumAroundRadius":
			if _, ok := v.(int); !ok {
				return invalidType(k, "int")
			}

		case "allowTyposOnNumericTokens",
			"ignorePlurals",
			"advancedSyntax",
			"analytics",
			"synonyms",
			"replaceSynonymsInHighlight",
			"aroundLatLngViaIP":
			if _, ok := v.(bool); !ok {
				return invalidType(k, "bool")
			}

		case "removeStopWords":
			switch v.(type) {
			case []string, bool:
				// OK
			default:
				return invalidType(k, "[]string or bool")
			}

		case "aroundRadius":
			switch v.(type) {
			case int, string:
				// OK
			default:
				return invalidType(k, "int or string")
			}

		default:
		}

	}
	return nil
}
<commit_msg>feat(Query): Handle facetingAfterDistinct parameter<commit_after>package algoliasearch

func checkQuery(query Map, ignore ...string) error {
Outer:
	for k, v := range query {
		// Continue if `k` is to be ignored.
		for _, s := range ignore {
			if s == k {
				continue Outer
			}
		}

		switch k {
		case "query",
			"queryType",
			"typoTolerance",
			"removeWordsIfNoResults",
			"restrictSearchableAttributes",
			"highlightPreTag",
			"highlightPostTag",
			"snippetEllipsisText",
			"filters",
			"analyticsTags",
			"optionalWords",
			"numericFilters",
			"tagFilters",
			"facets",
			"facetFilters",
			"aroundLatLng",
			"insideBoundingBox",
			"insidePolygon",
			"exactOnSingleWordQuery":
			if _, ok := v.(string); !ok {
				return invalidType(k, "string")
			}

		case "attributesToRetrieve",
			"disableTypoToleranceOnAttributes",
			"attributesToSnippet",
			"attributesToHighlight",
			"alternativesAsExact":
			if _, ok := v.([]string); !ok {
				return invalidType(k, "[]string")
			}

		case "minWordSizefor1Typo",
			"minWordSizefor2Typos",
			"minProximity",
			"page",
			"hitsPerPage",
			"getRankingInfo",
			"distinct",
			"maxValuesPerFacet",
			"aroundPrecision",
			"minimumAroundRadius":
			if _, ok := v.(int); !ok {
				return invalidType(k, "int")
			}

		case "allowTyposOnNumericTokens",
			"ignorePlurals",
			"advancedSyntax",
			"analytics",
			"synonyms",
			"replaceSynonymsInHighlight",
			"aroundLatLngViaIP",
			"facetingAfterDistinct":
			if _, ok := v.(bool); !ok {
				return invalidType(k, "bool")
			}

		case "removeStopWords":
			switch v.(type) {
			case []string, bool:
				// OK
			default:
				return invalidType(k, "[]string or bool")
			}

		case "aroundRadius":
			switch v.(type) {
			case int, string:
				// OK
			default:
				return invalidType(k, "int or string")
			}

		default:
		}

	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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.

//go:generate protoc -I ./model/cni --go_out=plugins=grpc:./model/cni ./model/cni/cni.proto

package contiv

import (
	"git.fd.io/govpp.git/api"
	"github.com/contiv/vpp/plugins/contiv/containeridx"
	"github.com/contiv/vpp/plugins/contiv/model/cni"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/clientv1/linux"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
	"context"
)

// Plugin transforms GRPC requests into configuration for the VPP in order
// to connect a container into the network.
type Plugin struct {
	Deps
	govppCh *api.Channel

	configuredContainers *containeridx.ConfigIndex
	cniServer            *remoteCNIserver

	ctx           context.Context
	ctxCancelFunc context.CancelFunc
}

// Deps groups the dependencies of the Plugin.
type Deps struct {
	local.PluginInfraDeps
	GRPC   grpc.Server
	Proxy  *kvdbproxy.Plugin
	VPP    *defaultplugins.Plugin
	GoVPP  govppmux.API
	Resync resync.Subscriber
}

// Init initializes the grpc server handling the request from the CNI.
func (plugin *Plugin) Init() error {
	plugin.configuredContainers = containeridx.NewConfigIndex(plugin.Log, plugin.PluginName, "containers")

	plugin.ctx, plugin.ctxCancelFunc = context.WithCancel(context.Background())

	var err error
	plugin.govppCh, err = plugin.GoVPP.NewAPIChannel()
	if err != nil {
		return err
	}

	if plugin.Resync != nil {
		reg := plugin.Resync.Register(string(plugin.PluginName))
		go plugin.handleResync(reg.StatusChan())
	}

	plugin.cniServer = newRemoteCNIServer(plugin.Log,
		func() linux.DataChangeDSL { return localclient.DataChangeRequest(plugin.PluginName) },
		plugin.Proxy,
		plugin.configuredContainers,
		plugin.govppCh,
		plugin.VPP.GetSwIfIndexes(),
		plugin.ServiceLabel.GetAgentLabel())
	cni.RegisterRemoteCNIServer(plugin.GRPC.Server(), plugin.cniServer)
	return nil
}

// Close cleans up the resources allocated by the plugin
func (plugin *Plugin) Close() error {
	plugin.ctxCancelFunc()
	plugin.cniServer.close()
	return safeclose.Close(plugin.govppCh)
}

// GetIfName looks up logical interface name that corresponds to the interface associated with the given pod.
func (plugin *Plugin) GetIfName(podNamespace string, podName string) (name string, exists bool) {
	podNamesMatch := plugin.configuredContainers.LookupPodName(podName)
	podNamespacesMatch := plugin.configuredContainers.LookupPodNamespace(podNamespace)

	for _, pod1 := range podNamespacesMatch {
		for _, pod2 := range podNamesMatch {
			if pod1 == pod2 {
				found, data := plugin.configuredContainers.LookupContainer(pod1)
				if found && data != nil && data.Afpacket != nil {
					return data.Afpacket.Name, true
				}
			}
		}
	}

	plugin.Log.WithFields(logging.Fields{"podNamespace": podNamespace, "podName": podName}).Warn("No matching result found")
	return "", false
}

func (plugin *Plugin) handleResync(resyncChan chan resync.StatusEvent) {
	for {
		select {
		case ev := <-resyncChan:
			status := ev.ResyncStatus()
			if status == resync.Started {
				err := plugin.cniServer.resync()
				if err != nil {
					plugin.Log.Error(err)
				}
			}
			ev.Ack()
		case <-plugin.ctx.Done():
			return
		}
	}
}
<commit_msg>Fix formatting<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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.

//go:generate protoc -I ./model/cni --go_out=plugins=grpc:./model/cni ./model/cni/cni.proto

package contiv

import (
	"context"
	"git.fd.io/govpp.git/api"
	"github.com/contiv/vpp/plugins/contiv/containeridx"
	"github.com/contiv/vpp/plugins/contiv/model/cni"
	"github.com/contiv/vpp/plugins/kvdbproxy"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/ligato/cn-infra/logging"
	"github.com/ligato/cn-infra/rpc/grpc"
	"github.com/ligato/cn-infra/utils/safeclose"
	"github.com/ligato/vpp-agent/clientv1/linux"
	"github.com/ligato/vpp-agent/clientv1/linux/localclient"
	"github.com/ligato/vpp-agent/plugins/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/govppmux"
)

// Plugin transforms GRPC requests into configuration for the VPP in order
// to connect a container into the network.
type Plugin struct {
	Deps
	govppCh *api.Channel

	configuredContainers *containeridx.ConfigIndex
	cniServer            *remoteCNIserver

	ctx           context.Context
	ctxCancelFunc context.CancelFunc
}

// Deps groups the dependencies of the Plugin.
type Deps struct {
	local.PluginInfraDeps
	GRPC   grpc.Server
	Proxy  *kvdbproxy.Plugin
	VPP    *defaultplugins.Plugin
	GoVPP  govppmux.API
	Resync resync.Subscriber
}

// Init initializes the grpc server handling the request from the CNI.
func (plugin *Plugin) Init() error {
	plugin.configuredContainers = containeridx.NewConfigIndex(plugin.Log, plugin.PluginName, "containers")

	plugin.ctx, plugin.ctxCancelFunc = context.WithCancel(context.Background())

	var err error
	plugin.govppCh, err = plugin.GoVPP.NewAPIChannel()
	if err != nil {
		return err
	}

	if plugin.Resync != nil {
		reg := plugin.Resync.Register(string(plugin.PluginName))
		go plugin.handleResync(reg.StatusChan())
	}

	plugin.cniServer = newRemoteCNIServer(plugin.Log,
		func() linux.DataChangeDSL { return localclient.DataChangeRequest(plugin.PluginName) },
		plugin.Proxy,
		plugin.configuredContainers,
		plugin.govppCh,
		plugin.VPP.GetSwIfIndexes(),
		plugin.ServiceLabel.GetAgentLabel())
	cni.RegisterRemoteCNIServer(plugin.GRPC.Server(), plugin.cniServer)
	return nil
}

// Close cleans up the resources allocated by the plugin
func (plugin *Plugin) Close() error {
	plugin.ctxCancelFunc()
	plugin.cniServer.close()
	return safeclose.Close(plugin.govppCh)
}

// GetIfName looks up logical interface name that corresponds to the interface associated with the given pod.
func (plugin *Plugin) GetIfName(podNamespace string, podName string) (name string, exists bool) {
	podNamesMatch := plugin.configuredContainers.LookupPodName(podName)
	podNamespacesMatch := plugin.configuredContainers.LookupPodNamespace(podNamespace)

	for _, pod1 := range podNamespacesMatch {
		for _, pod2 := range podNamesMatch {
			if pod1 == pod2 {
				found, data := plugin.configuredContainers.LookupContainer(pod1)
				if found && data != nil && data.Afpacket != nil {
					return data.Afpacket.Name, true
				}
			}
		}
	}

	plugin.Log.WithFields(logging.Fields{"podNamespace": podNamespace, "podName": podName}).Warn("No matching result found")
	return "", false
}

func (plugin *Plugin) handleResync(resyncChan chan resync.StatusEvent) {
	for {
		select {
		case ev := <-resyncChan:
			status := ev.ResyncStatus()
			if status == resync.Started {
				err := plugin.cniServer.resync()
				if err != nil {
					plugin.Log.Error(err)
				}
			}
			ev.Ack()
		case <-plugin.ctx.Done():
			return
		}
	}
}
<|endoftext|>


<commit_before>package search

import (
	"encoding/json"
	"time"
)

func Aggregate(name string) *AggregateDsl {
	return &AggregateDsl{Name: name}
}

type AggregateDsl struct {
	Name          string
	TypeName      string
	Type          interface{}
	Filters       *FilterWrap              `json:"filters,omitempty"`
	AggregatesVal map[string]*AggregateDsl `json:"aggregations,omitempty"`
}

type FieldAggregate struct {
	Field string `json:"field"`
}

/**
 * Aggregates accepts n "sub-aggregates" to be applied to this aggregate
 *
 * agg := Aggregate("user").Term("user_id")
 * agg.Aggregates(
 *   Aggregate("total_spent").Sum("price"),
 *   Aggregate("total_saved").Sum("discount"),
 * )
 */
func (d *AggregateDsl) Aggregates(aggs ...*AggregateDsl) *AggregateDsl {
	if len(aggs) < 1 {
		return d
	}
	if len(d.AggregatesVal) == 0 {
		d.AggregatesVal = make(map[string]*AggregateDsl)
	}

	for _, agg := range aggs {
		d.AggregatesVal[agg.Name] = agg
	}
	return d
}

func (d *AggregateDsl) Min(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "min"
	return d
}

func (d *AggregateDsl) Max(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "max"
	return d
}

func (d *AggregateDsl) Sum(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "sum"
	return d
}

func (d *AggregateDsl) Avg(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "avg"
	return d
}

func (d *AggregateDsl) Stats(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "stats"
	return d
}

func (d *AggregateDsl) ExtendedStats(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "extended_stats"
	return d
}

func (d *AggregateDsl) ValueCount(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "value_count"
	return d
}

func (d *AggregateDsl) Percentiles(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "percentiles"
	return d
}

type Cardinality struct {
	Field              string  `json:"field"`
	PrecisionThreshold float64 `json:"precision_threshold,omitempty"`
	Rehash             bool    `json:"rehash,omitempty"`
}

/**
 * Cardinality(
 *	 "field_name",
 *	 true,
 *   0,
 * )
 */
func (d *AggregateDsl) Cardinality(field string, rehash bool, threshold int) *AggregateDsl {
	c := Cardinality{Field: field}

	// Only set if it's false, since the default is true
	if !rehash {
		c.Rehash = false
	}

	if threshold > 0 {
		c.PrecisionThreshold = float64(threshold)
	}
	d.Type = c
	d.TypeName = "cardinality"
	return d
}

func (d *AggregateDsl) Global() *AggregateDsl {
	d.Type = struct{}{}
	d.TypeName = "global"
	return d
}

func (d *AggregateDsl) Filter(filters ...interface{}) *AggregateDsl {

	if len(filters) == 0 {
		return d
	}

	if d.Filters == nil {
		d.Filters = NewFilterWrap()
	}

	d.Filters.addFilters(filters)
	return d
}

func (d *AggregateDsl) Missing(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "missing"
	return d
}

func (d *AggregateDsl) Terms(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "terms"
	return d
}

func (d *AggregateDsl) SignificantTerms(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "significant_terms"
	return d
}

type Histogram struct {
	Field          string      `json:"field"`
	Interval       float64     `json:"interval"`
	MinDocCount    float64     `json:"min_doc_count"`
	ExtendedBounds interface{} `json:"extended_bounds,omitempty"`
}

func (d *AggregateDsl) Histogram(field string, interval int) *AggregateDsl {
	d.Type = Histogram{
		Field:       field,
		Interval:    float64(interval),
		MinDocCount: 1,
	}
	d.TypeName = "histogram"
	return d
}

type DateHistogram struct {
	Field          string      `json:"field"`
	Interval       string      `json:"interval"`
	MinDocCount    float64     `json:"min_doc_count"`
	ExtendedBounds interface{} `json:"extended_bounds,omitempty"`
}

func (d *AggregateDsl) DateHistogram(field, interval string) *AggregateDsl {
	d.Type = DateHistogram{
		Field:       field,
		Interval:    interval,
		MinDocCount: 1,
	}
	d.TypeName = "date_histogram"
	return d
}

// Sets the min doc count for a date histogram or histogram
// This will no-op if used on an inappropriate dsl type
func (d *AggregateDsl) MinDocCount(i float64) *AggregateDsl {

	if d.TypeName == "date_histogram" {
		t := d.Type.(DateHistogram)
		t.MinDocCount = i
		d.Type = t
	} else if d.TypeName == "histogram" {
		t := d.Type.(Histogram)
		t.MinDocCount = i
		d.Type = t
	}

	return d
}

// Hackety hack function that expects different types depending on the type of aggregate
// Not very idiomatic, but fits the elastigo DSL
func (d *AggregateDsl) ExtendedBounds(min, max interface{}) *AggregateDsl {
	if min == nil && max == nil {
		return d
	}

	if d.TypeName == "date_histogram" {
		var n time.Time
		var x time.Time
		t := d.Type.(DateHistogram)
		if min != nil {
			switch min.(type) {
			case time.Time:
				n = min.(time.Time)
			}
		}
		if max != nil {
			switch max.(type) {
			case time.Time:
				x = max.(time.Time)
			}
		}

		if min == nil {
			bounds := struct {
				Max time.Time `json:"max"`
			}{x}
			t.ExtendedBounds = &bounds
		} else if max == nil {
			bounds := struct {
				Min time.Time `json:"min"`
			}{n}
			t.ExtendedBounds = &bounds
		} else {
			bounds := struct {
				Min time.Time `json:"min"`
				Max time.Time `json:"max"`
			}{n, x}
			t.ExtendedBounds = &bounds
		}

		d.Type = t
	}
	if d.TypeName == "histogram" {
		var n float64
		var x float64
		t := d.Type.(Histogram)
		if min != nil {
			switch min.(type) {
			case time.Time:
				n = min.(float64)
			}
		}
		if max != nil {
			switch max.(type) {
			case time.Time:
				x = max.(float64)
			}
		}

		if min == nil {
			bounds := struct {
				Max float64 `json:"max"`
			}{x}
			t.ExtendedBounds = &bounds
		} else if max == nil {
			bounds := struct {
				Min float64 `json:"min"`
			}{n}
			t.ExtendedBounds = &bounds
		} else {
			bounds := struct {
				Min float64 `json:"min"`
				Max float64 `json:"max"`
			}{n, x}
			t.ExtendedBounds = &bounds
		}

		d.Type = t
	}

	return d
}
func (d *AggregateDsl) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.toMap())
}

func (d *AggregateDsl) toMap() map[string]interface{} {
	root := map[string]interface{}{}

	if d.Type != nil {
		root[d.TypeName] = d.Type
	}
	aggregates := d.aggregatesMap()

	if d.Filters != nil {
		root["filter"] = d.Filters
	}

	if len(aggregates) > 0 {
		root["aggregations"] = aggregates
	}
	return root

}
func (d *AggregateDsl) aggregatesMap() map[string]interface{} {
	root := map[string]interface{}{}

	if len(d.AggregatesVal) > 0 {
		for _, agg := range d.AggregatesVal {
			root[agg.Name] = agg.toMap()
		}
	}
	return root
}
<commit_msg>Aggregate Type sizes are settable<commit_after>package search

import (
	"encoding/json"
	"time"
)

func Aggregate(name string) *AggregateDsl {
	return &AggregateDsl{Name: name}
}

type AggregateDsl struct {
	Name          string
	TypeName      string
	Type          interface{}
	Filters       *FilterWrap              `json:"filters,omitempty"`
	AggregatesVal map[string]*AggregateDsl `json:"aggregations,omitempty"`
}

type FieldAggregate struct {
	Field   string `json:"field"`
	SizeVal *int   `json:"size,omitempty"`
}

/**
 * Aggregates accepts n "sub-aggregates" to be applied to this aggregate
 *
 * agg := Aggregate("user").Term("user_id")
 * agg.Aggregates(
 *   Aggregate("total_spent").Sum("price"),
 *   Aggregate("total_saved").Sum("discount"),
 * )
 */
func (d *AggregateDsl) Aggregates(aggs ...*AggregateDsl) *AggregateDsl {
	if len(aggs) < 1 {
		return d
	}
	if len(d.AggregatesVal) == 0 {
		d.AggregatesVal = make(map[string]*AggregateDsl)
	}

	for _, agg := range aggs {
		d.AggregatesVal[agg.Name] = agg
	}
	return d
}

func (d *AggregateDsl) Min(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "min"
	return d
}

func (d *AggregateDsl) Max(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "max"
	return d
}

func (d *AggregateDsl) Sum(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "sum"
	return d
}

func (d *AggregateDsl) Avg(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "avg"
	return d
}

func (d *AggregateDsl) Stats(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "stats"
	return d
}

func (d *AggregateDsl) ExtendedStats(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "extended_stats"
	return d
}

func (d *AggregateDsl) ValueCount(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "value_count"
	return d
}

func (d *AggregateDsl) Percentiles(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "percentiles"
	return d
}

type Cardinality struct {
	Field              string  `json:"field"`
	PrecisionThreshold float64 `json:"precision_threshold,omitempty"`
	Rehash             bool    `json:"rehash,omitempty"`
}

/**
 * Cardinality(
 *	 "field_name",
 *	 true,
 *   0,
 * )
 */
func (d *AggregateDsl) Cardinality(field string, rehash bool, threshold int) *AggregateDsl {
	c := Cardinality{Field: field}

	// Only set if it's false, since the default is true
	if !rehash {
		c.Rehash = false
	}

	if threshold > 0 {
		c.PrecisionThreshold = float64(threshold)
	}
	d.Type = c
	d.TypeName = "cardinality"
	return d
}

func (d *AggregateDsl) Global() *AggregateDsl {
	d.Type = struct{}{}
	d.TypeName = "global"
	return d
}

func (d *AggregateDsl) Filter(filters ...interface{}) *AggregateDsl {

	if len(filters) == 0 {
		return d
	}

	if d.Filters == nil {
		d.Filters = NewFilterWrap()
	}

	d.Filters.addFilters(filters)
	return d
}

func (d *AggregateDsl) Missing(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "missing"
	return d
}

func (d *AggregateDsl) Terms(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "terms"
	return d
}

func (d *AggregateDsl) SignificantTerms(field string) *AggregateDsl {
	d.Type = FieldAggregate{Field: field}
	d.TypeName = "significant_terms"
	return d
}

type Histogram struct {
	Field          string      `json:"field"`
	Interval       float64     `json:"interval"`
	MinDocCount    float64     `json:"min_doc_count"`
	ExtendedBounds interface{} `json:"extended_bounds,omitempty"`
}

func (d *AggregateDsl) Histogram(field string, interval int) *AggregateDsl {
	d.Type = Histogram{
		Field:       field,
		Interval:    float64(interval),
		MinDocCount: 1,
	}
	d.TypeName = "histogram"
	return d
}

type DateHistogram struct {
	Field          string      `json:"field"`
	Interval       string      `json:"interval"`
	MinDocCount    float64     `json:"min_doc_count"`
	ExtendedBounds interface{} `json:"extended_bounds,omitempty"`
}

func (d *AggregateDsl) DateHistogram(field, interval string) *AggregateDsl {
	d.Type = DateHistogram{
		Field:       field,
		Interval:    interval,
		MinDocCount: 1,
	}
	d.TypeName = "date_histogram"
	return d
}

func (d *AggregateDsl) Size(size int) *AggregateDsl {
	switch d.Type.(type) {
	case FieldAggregate:
		typ := d.Type.(FieldAggregate)
		typ.SizeVal = &size
		d.Type = typ
	}
	return d
}

// Sets the min doc count for a date histogram or histogram
// This will no-op if used on an inappropriate dsl type
func (d *AggregateDsl) MinDocCount(i float64) *AggregateDsl {

	if d.TypeName == "date_histogram" {
		t := d.Type.(DateHistogram)
		t.MinDocCount = i
		d.Type = t
	} else if d.TypeName == "histogram" {
		t := d.Type.(Histogram)
		t.MinDocCount = i
		d.Type = t
	}

	return d
}

// Hackety hack function that expects different types depending on the type of aggregate
// Not very idiomatic, but fits the elastigo DSL
func (d *AggregateDsl) ExtendedBounds(min, max interface{}) *AggregateDsl {
	if min == nil && max == nil {
		return d
	}

	if d.TypeName == "date_histogram" {
		var n time.Time
		var x time.Time
		t := d.Type.(DateHistogram)
		if min != nil {
			switch min.(type) {
			case time.Time:
				n = min.(time.Time)
			}
		}
		if max != nil {
			switch max.(type) {
			case time.Time:
				x = max.(time.Time)
			}
		}

		if min == nil {
			bounds := struct {
				Max time.Time `json:"max"`
			}{x}
			t.ExtendedBounds = &bounds
		} else if max == nil {
			bounds := struct {
				Min time.Time `json:"min"`
			}{n}
			t.ExtendedBounds = &bounds
		} else {
			bounds := struct {
				Min time.Time `json:"min"`
				Max time.Time `json:"max"`
			}{n, x}
			t.ExtendedBounds = &bounds
		}

		d.Type = t
	}
	if d.TypeName == "histogram" {
		var n float64
		var x float64
		t := d.Type.(Histogram)
		if min != nil {
			switch min.(type) {
			case time.Time:
				n = min.(float64)
			}
		}
		if max != nil {
			switch max.(type) {
			case time.Time:
				x = max.(float64)
			}
		}

		if min == nil {
			bounds := struct {
				Max float64 `json:"max"`
			}{x}
			t.ExtendedBounds = &bounds
		} else if max == nil {
			bounds := struct {
				Min float64 `json:"min"`
			}{n}
			t.ExtendedBounds = &bounds
		} else {
			bounds := struct {
				Min float64 `json:"min"`
				Max float64 `json:"max"`
			}{n, x}
			t.ExtendedBounds = &bounds
		}

		d.Type = t
	}

	return d
}
func (d *AggregateDsl) MarshalJSON() ([]byte, error) {
	return json.Marshal(d.toMap())
}

func (d *AggregateDsl) toMap() map[string]interface{} {
	root := map[string]interface{}{}

	if d.Type != nil {
		root[d.TypeName] = d.Type
	}
	aggregates := d.aggregatesMap()

	if d.Filters != nil {
		root["filter"] = d.Filters
	}

	if len(aggregates) > 0 {
		root["aggregations"] = aggregates
	}
	return root

}
func (d *AggregateDsl) aggregatesMap() map[string]interface{} {
	root := map[string]interface{}{}

	if len(d.AggregatesVal) > 0 {
		for _, agg := range d.AggregatesVal {
			root[agg.Name] = agg.toMap()
		}
	}
	return root
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net"
	"regexp"
	"strings"
)

func takeAction(client *Client, userInput string) {
	fmt.Println("Input: ", userInput)
	userInputNormal := strings.ToLower(userInput)

	quitExp := regexp.MustCompile(`\b(quit|exit|bye)\b`)
	if quitExp.MatchString(userInputNormal) == true {
		fmt.Println("closing connection for ", client.Name)
		client.sendMessage("you have fled the dungeon!")
		client.Close()
	}

	lookExp := regexp.MustCompile(`\b(l|look)\b`)
	if lookExp.MatchString(userInputNormal) == true {
		client.sendMessage(client.CurrentRoom.Description)
	}

}

// Wait for data to appear in a chan and call takeAction with the data
func handleUserInput(client *Client) {
	for {
		select {
		case userInput := <-client.InputChan:
			go takeAction(client, string(userInput))
			break
		}
	}
}

func handleUserOutput(userConn net.Conn, outputChan chan []byte) {
	for {
		select {
		case data := <-outputChan:
			go writeToConn(userConn, data)
			break
		}
	}
}

func writeToConn(userConn net.Conn, data []byte) {
	_, err := userConn.Write([]byte(data))
	if err != nil {
		fmt.Println("error while writing")
		return
	}
}

// Read on a Conn for data, then add that data to a chan
func clientReader(client *Client) {
	input := make([]byte, 2048)
	for client.Read(input) {
		client.InputChan <- input
	}
}

func acceptAndMakeNewConnection(listener net.Listener) {
	inputChan := make(chan []byte)
	outputChan := make(chan []byte)

	conn, err := listener.Accept()
	if err != nil {
		fmt.Println("error making connection:", err)
		return
	}

	fmt.Println("connection made")

	client := &Client{Name: "foobar", InputChan: inputChan, OutputChan: outputChan, Conn: conn}
	go clientReader(client)
	go handleUserInput(client)
	go handleUserOutput(conn, outputChan)
	client.sendMessage("greetings and welcome to the dungeon!")

	dungeon := NewDungeon()
	client.Dungeon = dungeon
	client.CurrentRoom = dungeon.Rooms[Point{0, 0}]
}

// ----
// main
func main() {
	fmt.Println("starting up")

	listener, err := net.Listen("tcp", ":8889")
	if err != nil {
		fmt.Println("error starting server:", err)
		return
	}
	defer listener.Close()

	for {
		acceptAndMakeNewConnection(listener)
	}
}
<commit_msg>Add a default response for takeAction<commit_after>package main

import (
	"fmt"
	"net"
	"regexp"
	"strings"
)

func takeAction(client *Client, userInput string) {
	fmt.Println("Input: ", userInput)
	userInputNormal := strings.ToLower(userInput)

	quitExp := regexp.MustCompile(`\b(quit|exit|bye)\b`)
	if quitExp.MatchString(userInputNormal) == true {
		fmt.Println("closing connection for ", client.Name)
		client.sendMessage("you have fled the dungeon!")
		client.Close()
		return
	}

	lookExp := regexp.MustCompile(`\b(l|look)\b`)
	if lookExp.MatchString(userInputNormal) == true {
		client.sendMessage(client.CurrentRoom.Description)
		return
	}

	client.sendMessage("Sorry, I didn't understand what you said.")

}

// Wait for data to appear in a chan and call takeAction with the data
func handleUserInput(client *Client) {
	for {
		select {
		case userInput := <-client.InputChan:
			go takeAction(client, string(userInput))
			break
		}
	}
}

func handleUserOutput(userConn net.Conn, outputChan chan []byte) {
	for {
		select {
		case data := <-outputChan:
			go writeToConn(userConn, data)
			break
		}
	}
}

func writeToConn(userConn net.Conn, data []byte) {
	_, err := userConn.Write([]byte(data))
	if err != nil {
		fmt.Println("error while writing")
		return
	}
}

// Read on a Conn for data, then add that data to a chan
func clientReader(client *Client) {
	input := make([]byte, 2048)
	for client.Read(input) {
		client.InputChan <- input
	}
}

func acceptAndMakeNewConnection(listener net.Listener) {
	inputChan := make(chan []byte)
	outputChan := make(chan []byte)

	conn, err := listener.Accept()
	if err != nil {
		fmt.Println("error making connection:", err)
		return
	}

	fmt.Println("connection made")

	client := &Client{Name: "foobar", InputChan: inputChan, OutputChan: outputChan, Conn: conn}
	go clientReader(client)
	go handleUserInput(client)
	go handleUserOutput(conn, outputChan)
	client.sendMessage("greetings and welcome to the dungeon!")

	dungeon := NewDungeon()
	client.Dungeon = dungeon
	client.CurrentRoom = dungeon.Rooms[Point{0, 0}]
}

// ----
// main
func main() {
	fmt.Println("starting up")

	listener, err := net.Listen("tcp", ":8889")
	if err != nil {
		fmt.Println("error starting server:", err)
		return
	}
	defer listener.Close()

	for {
		acceptAndMakeNewConnection(listener)
	}
}
<|endoftext|>