<commit_before>package router

import (
	"fmt"
	"io"
	"os"
	"strings"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const longDesc = `
Install or configure an OpenShift router

This command helps to setup an OpenShift router to take edge traffic and balance it to
your application. With no arguments, the command will check for an existing router
service called 'router' and perform some diagnostics to ensure the router is properly
configured and functioning.

If a router does not exist with the given name, the --create flag can be passed to
create a deployment configuration and service that will run the router. If you are
running your router in production, you should pass --replicas=2 or higher to ensure
you have failover protection.

Examples:
  Check the default router ("router"):

  $ %[1]s %[2]s

  See what the router would look like if created:

  $ %[1]s %[2]s -o json

  Create a router if it does not exist:

  $ %[1]s %[2]s router-west --create --replicas=2

  Use a different router image and see the router configuration:

  $ %[1]s %[2]s region-west -o yaml --images=myrepo/somerouter:mytag

ALPHA: This command is currently being actively developed. It is intended to simplify
  the tasks of setting up routers in a new installation.
`

type config struct {
	Type          string
	ImageTemplate variable.ImageTemplate
	Ports         string
	Replicas      int
	Labels        string
	Create        bool
	Credentials   string
}

const defaultLabel = "router=<name>"

func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &config{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    "80:80,443:443",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s [<name>]", name),
		Short: "Install and check OpenShift routers",
		Long:  fmt.Sprintf(longDesc, parentName, name),

		Run: func(cmd *cobra.Command, args []string) {
			var name string
			switch len(args) {
			case 0:
				name = "router"
			case 1:
				name = args[0]
			default:
				glog.Fatalf("You may pass zero or one arguments to provide a name for the router")
			}

			ports, err := app.ContainerPortsFromString(cfg.Ports)
			if err != nil {
				glog.Fatal(err)
			}

			label := map[string]string{"router": name}
			if cfg.Labels != defaultLabel {
				valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
				if err != nil {
					glog.Fatal(err)
				}
				if len(remove) > 0 {
					glog.Fatalf("You may not pass negative labels in %q", cfg.Labels)
				}
				label = valid
			}

			image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

			namespace, err := f.OpenShiftClientConfig.Namespace()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}
			_, kClient, err := f.Clients()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}

			p, output, err := cmdutil.PrinterForCommand(cmd)
			if err != nil {
				glog.Fatalf("Unable to configure printer: %v", err)
			}

			generate := output
			if !generate {
				_, err = kClient.Services(namespace).Get(name)
				if err != nil {
					if !errors.IsNotFound(err) {
						glog.Fatalf("Can't check for existing router %q: %v", name, err)
					}
					generate = true
				}
			}

			if generate {
				if !cfg.Create && !output {
					glog.Fatalf("Router %q does not exist (no service). Pass --create to install.", name)
				}

				// create new router
				if len(cfg.Credentials) == 0 {
					glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the router to the master with --credentials")
				}

				clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials, Precedence: []string{}}
				credentials, err := clientConfigLoadingRules.Load()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
				}
				config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be used: %v", cfg.Credentials, err)
				}
				if err := kclient.LoadTLSFiles(config); err != nil {
					glog.Fatalf("The provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
				}
				insecure := "false"
				if config.Insecure {
					insecure = "true"
				}
				env := app.Environment{
					"OPENSHIFT_MASTER":    config.Host,
					"OPENSHIFT_CA_DATA":   string(config.CAData),
					"OPENSHIFT_KEY_DATA":  string(config.KeyData),
					"OPENSHIFT_CERT_DATA": string(config.CertData),
					"OPENSHIFT_INSECURE":  insecure,
				}

				objects := []runtime.Object{
					&dapi.DeploymentConfig{
						ObjectMeta: kapi.ObjectMeta{
							Name:   name,
							Labels: label,
						},
						Triggers: []dapi.DeploymentTriggerPolicy{
							{Type: dapi.DeploymentTriggerOnConfigChange},
						},
						Template: dapi.DeploymentTemplate{
							Strategy: dapi.DeploymentStrategy{
								Type: dapi.DeploymentStrategyTypeRecreate,
							},
							ControllerTemplate: kapi.ReplicationControllerSpec{
								Replicas: cfg.Replicas,
								Selector: label,
								Template: &kapi.PodTemplateSpec{
									ObjectMeta: kapi.ObjectMeta{Labels: label},
									Spec: kapi.PodSpec{
										Containers: []kapi.Container{
											{
												Name:  "router",
												Image: image,
												Ports: ports,
												Env:   env.List(),
												LivenessProbe: &kapi.Probe{
													Handler: kapi.Handler{
														TCPSocket: &kapi.TCPSocketAction{
															Port: kutil.IntOrString{
																IntVal: ports[0].ContainerPort,
															},
														},
													},
												},
											},
										},
									},
								},
							},
						},
					},
				}
				objects = app.AddServices(objects)
				// TODO: label all created objects with the same label - router=<name>
				list := &kapi.List{Items: objects}

				if output {
					if err := p.PrintObj(list, out); err != nil {
						glog.Fatalf("Unable to print object: %v", err)
					}
					return
				}

				bulk := configcmd.Bulk{
					Factory: f.Factory,
					After:   configcmd.NewPrintNameOrErrorAfter(out, os.Stderr),
				}
				if errs := bulk.Create(list, namespace); len(errs) != 0 {
					os.Exit(1)
				}
				return
			}

			fmt.Fprintf(out, "Router %q service exists\n", name)
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "haproxy-router", "The type of router to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this router on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest images for the router instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, "A comma delimited list of ports or port pairs to expose on the router pod. The default is set for HAProxy.")
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the router; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the router and its components.")
	cmd.Flags().BoolVar(&cfg.Create, "create", cfg.Create, "Create the router if it does not exist.")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the router should use to contact the master.")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}

/*
// Example with generation - this does not have port metadata so its slightly less
// clear to end users.

registry, imageNamespace, imageName, tag, err := imageapi.SplitDockerPullSpec(image)
if err != nil {
	glog.Fatalf("The image value %q is not valid: %v", image, err)
}

image := &app.ImageRef{
	Namespace: imageNamespace,
	Name:      imageName,
	Registry:  registry,
	Tag:       tag,
}
pipeline, err := app.NewImagePipeline(name, image)
if err != nil {
	glog.Fatalf("Unable to set up an image for the router: %v", err)
}
if err := pipeline.NeedsDeployment(nil); err != nil {
	glog.Fatalf("Unable to set up a deployment for the router: %v", err)
}
objects, err := pipeline.Objects(app.NewAcceptFirst())
if err != nil {
	glog.Fatalf("Unable to configure objects for deployment: %v", err)
}
objects = app.AddServices(objects)
*/
<commit_msg>probe delay<commit_after>package router

import (
	"fmt"
	"io"
	"os"
	"strings"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	kclientcmd "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
	cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/golang/glog"
	"github.com/spf13/cobra"

	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
	"github.com/openshift/origin/pkg/cmd/util/variable"
	configcmd "github.com/openshift/origin/pkg/config/cmd"
	dapi "github.com/openshift/origin/pkg/deploy/api"
	"github.com/openshift/origin/pkg/generate/app"
)

const longDesc = `
Install or configure an OpenShift router

This command helps to setup an OpenShift router to take edge traffic and balance it to
your application. With no arguments, the command will check for an existing router
service called 'router' and perform some diagnostics to ensure the router is properly
configured and functioning.

If a router does not exist with the given name, the --create flag can be passed to
create a deployment configuration and service that will run the router. If you are
running your router in production, you should pass --replicas=2 or higher to ensure
you have failover protection.

Examples:
  Check the default router ("router"):

  $ %[1]s %[2]s

  See what the router would look like if created:

  $ %[1]s %[2]s -o json

  Create a router if it does not exist:

  $ %[1]s %[2]s router-west --create --replicas=2

  Use a different router image and see the router configuration:

  $ %[1]s %[2]s region-west -o yaml --images=myrepo/somerouter:mytag

ALPHA: This command is currently being actively developed. It is intended to simplify
  the tasks of setting up routers in a new installation.
`

type config struct {
	Type          string
	ImageTemplate variable.ImageTemplate
	Ports         string
	Replicas      int
	Labels        string
	Create        bool
	Credentials   string
}

const defaultLabel = "router=<name>"

func NewCmdRouter(f *clientcmd.Factory, parentName, name string, out io.Writer) *cobra.Command {
	cfg := &config{
		ImageTemplate: variable.NewDefaultImageTemplate(),

		Labels:   defaultLabel,
		Ports:    "80:80,443:443",
		Replicas: 1,
	}

	cmd := &cobra.Command{
		Use:   fmt.Sprintf("%s [<name>]", name),
		Short: "Install and check OpenShift routers",
		Long:  fmt.Sprintf(longDesc, parentName, name),

		Run: func(cmd *cobra.Command, args []string) {
			var name string
			switch len(args) {
			case 0:
				name = "router"
			case 1:
				name = args[0]
			default:
				glog.Fatalf("You may pass zero or one arguments to provide a name for the router")
			}

			ports, err := app.ContainerPortsFromString(cfg.Ports)
			if err != nil {
				glog.Fatal(err)
			}

			label := map[string]string{"router": name}
			if cfg.Labels != defaultLabel {
				valid, remove, err := app.LabelsFromSpec(strings.Split(cfg.Labels, ","))
				if err != nil {
					glog.Fatal(err)
				}
				if len(remove) > 0 {
					glog.Fatalf("You may not pass negative labels in %q", cfg.Labels)
				}
				label = valid
			}

			image := cfg.ImageTemplate.ExpandOrDie(cfg.Type)

			namespace, err := f.OpenShiftClientConfig.Namespace()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}
			_, kClient, err := f.Clients()
			if err != nil {
				glog.Fatalf("Error getting client: %v", err)
			}

			p, output, err := cmdutil.PrinterForCommand(cmd)
			if err != nil {
				glog.Fatalf("Unable to configure printer: %v", err)
			}

			generate := output
			if !generate {
				_, err = kClient.Services(namespace).Get(name)
				if err != nil {
					if !errors.IsNotFound(err) {
						glog.Fatalf("Can't check for existing router %q: %v", name, err)
					}
					generate = true
				}
			}

			if generate {
				if !cfg.Create && !output {
					glog.Fatalf("Router %q does not exist (no service). Pass --create to install.", name)
				}

				// create new router
				if len(cfg.Credentials) == 0 {
					glog.Fatalf("You must specify a .kubeconfig file path containing credentials for connecting the router to the master with --credentials")
				}

				clientConfigLoadingRules := &kclientcmd.ClientConfigLoadingRules{ExplicitPath: cfg.Credentials, Precedence: []string{}}
				credentials, err := clientConfigLoadingRules.Load()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be loaded: %v", cfg.Credentials, err)
				}
				config, err := kclientcmd.NewDefaultClientConfig(*credentials, &kclientcmd.ConfigOverrides{}).ClientConfig()
				if err != nil {
					glog.Fatalf("The provided credentials %q could not be used: %v", cfg.Credentials, err)
				}
				if err := kclient.LoadTLSFiles(config); err != nil {
					glog.Fatalf("The provided credentials %q could not load certificate info: %v", cfg.Credentials, err)
				}
				insecure := "false"
				if config.Insecure {
					insecure = "true"
				}
				env := app.Environment{
					"OPENSHIFT_MASTER":    config.Host,
					"OPENSHIFT_CA_DATA":   string(config.CAData),
					"OPENSHIFT_KEY_DATA":  string(config.KeyData),
					"OPENSHIFT_CERT_DATA": string(config.CertData),
					"OPENSHIFT_INSECURE":  insecure,
				}

				objects := []runtime.Object{
					&dapi.DeploymentConfig{
						ObjectMeta: kapi.ObjectMeta{
							Name:   name,
							Labels: label,
						},
						Triggers: []dapi.DeploymentTriggerPolicy{
							{Type: dapi.DeploymentTriggerOnConfigChange},
						},
						Template: dapi.DeploymentTemplate{
							Strategy: dapi.DeploymentStrategy{
								Type: dapi.DeploymentStrategyTypeRecreate,
							},
							ControllerTemplate: kapi.ReplicationControllerSpec{
								Replicas: cfg.Replicas,
								Selector: label,
								Template: &kapi.PodTemplateSpec{
									ObjectMeta: kapi.ObjectMeta{Labels: label},
									Spec: kapi.PodSpec{
										Containers: []kapi.Container{
											{
												Name:  "router",
												Image: image,
												Ports: ports,
												Env:   env.List(),
												LivenessProbe: &kapi.Probe{
													Handler: kapi.Handler{
														TCPSocket: &kapi.TCPSocketAction{
															Port: kutil.IntOrString{
																IntVal: ports[0].ContainerPort,
															},
														},
													},
													InitialDelaySeconds: 10,
												},
											},
										},
									},
								},
							},
						},
					},
				}
				objects = app.AddServices(objects)
				// TODO: label all created objects with the same label - router=<name>
				list := &kapi.List{Items: objects}

				if output {
					if err := p.PrintObj(list, out); err != nil {
						glog.Fatalf("Unable to print object: %v", err)
					}
					return
				}

				bulk := configcmd.Bulk{
					Factory: f.Factory,
					After:   configcmd.NewPrintNameOrErrorAfter(out, os.Stderr),
				}
				if errs := bulk.Create(list, namespace); len(errs) != 0 {
					os.Exit(1)
				}
				return
			}

			fmt.Fprintf(out, "Router %q service exists\n", name)
		},
	}

	cmd.Flags().StringVar(&cfg.Type, "type", "haproxy-router", "The type of router to use - if you specify --images this flag may be ignored.")
	cmd.Flags().StringVar(&cfg.ImageTemplate.Format, "images", cfg.ImageTemplate.Format, "The image to base this router on - ${component} will be replaced with --type")
	cmd.Flags().BoolVar(&cfg.ImageTemplate.Latest, "latest-images", cfg.ImageTemplate.Latest, "If true, attempt to use the latest images for the router instead of the latest release.")
	cmd.Flags().StringVar(&cfg.Ports, "ports", cfg.Ports, "A comma delimited list of ports or port pairs to expose on the router pod. The default is set for HAProxy.")
	cmd.Flags().IntVar(&cfg.Replicas, "replicas", cfg.Replicas, "The replication factor of the router; commonly 2 when high availability is desired.")
	cmd.Flags().StringVar(&cfg.Labels, "labels", cfg.Labels, "A set of labels to uniquely identify the router and its components.")
	cmd.Flags().BoolVar(&cfg.Create, "create", cfg.Create, "Create the router if it does not exist.")
	cmd.Flags().StringVar(&cfg.Credentials, "credentials", "", "Path to a .kubeconfig file that will contain the credentials the router should use to contact the master.")

	cmdutil.AddPrinterFlags(cmd)

	return cmd
}

/*
// Example with generation - this does not have port metadata so its slightly less
// clear to end users.

registry, imageNamespace, imageName, tag, err := imageapi.SplitDockerPullSpec(image)
if err != nil {
	glog.Fatalf("The image value %q is not valid: %v", image, err)
}

image := &app.ImageRef{
	Namespace: imageNamespace,
	Name:      imageName,
	Registry:  registry,
	Tag:       tag,
}
pipeline, err := app.NewImagePipeline(name, image)
if err != nil {
	glog.Fatalf("Unable to set up an image for the router: %v", err)
}
if err := pipeline.NeedsDeployment(nil); err != nil {
	glog.Fatalf("Unable to set up a deployment for the router: %v", err)
}
objects, err := pipeline.Objects(app.NewAcceptFirst())
if err != nil {
	glog.Fatalf("Unable to configure objects for deployment: %v", err)
}
objects = app.AddServices(objects)
*/
<|endoftext|>


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

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

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

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

package registry

import (
	"fmt"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
)

func TestServiceRegistry(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase: api.JSONBase{ID: "foo"},
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 0 {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv == nil {
		t.Errorf("Failed to find service: %s", svc.ID)
	}
}

func TestServiceRegistryExternalService(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		CreateExternalLoadBalancer: true,
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "create" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv == nil {
		t.Errorf("Failed to find service: %s", svc.ID)
	}
}

func TestServiceRegistryExternalServiceError(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{
		Err: fmt.Errorf("test error"),
	}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		CreateExternalLoadBalancer: true,
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "create" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService("foo")
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}

func TestServiceRegistryDelete(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase: api.JSONBase{ID: "foo"},
	}
	memory.CreateService(svc)

	c, _ := storage.Delete(svc.ID)
	<-c

	if len(fakeCloud.Calls) != 0 {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}

func TestServiceRegistryDeleteExternal(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		CreateExternalLoadBalancer: true,
	}
	memory.CreateService(svc)

	c, _ := storage.Delete(svc.ID)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "delete" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}
<commit_msg>Fix tests.<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

package registry

import (
	"fmt"
	"testing"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
)

func TestServiceRegistry(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase: api.JSONBase{ID: "foo"},
		Selector: map[string]string{"bar": "baz"},
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 0 {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv == nil {
		t.Errorf("Failed to find service: %s", svc.ID)
	}
}

func TestServiceRegistryExternalService(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		Selector:                   map[string]string{"bar": "baz"},
		CreateExternalLoadBalancer: true,
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "create" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv == nil {
		t.Errorf("Failed to find service: %s", svc.ID)
	}
}

func TestServiceRegistryExternalServiceError(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{
		Err: fmt.Errorf("test error"),
	}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		Selector:                   map[string]string{"bar": "baz"},
		CreateExternalLoadBalancer: true,
	}
	c, _ := storage.Create(svc)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "create" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService("foo")
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}

func TestServiceRegistryDelete(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase: api.JSONBase{ID: "foo"},
		Selector: map[string]string{"bar": "baz"},
	}
	memory.CreateService(svc)

	c, _ := storage.Delete(svc.ID)
	<-c

	if len(fakeCloud.Calls) != 0 {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}

func TestServiceRegistryDeleteExternal(t *testing.T) {
	memory := MakeMemoryRegistry()
	fakeCloud := &cloudprovider.FakeCloud{}
	machines := []string{"foo", "bar", "baz"}

	storage := MakeServiceRegistryStorage(memory, fakeCloud, MakeMinionRegistry(machines))

	svc := api.Service{
		JSONBase:                   api.JSONBase{ID: "foo"},
		Selector:                   map[string]string{"bar": "baz"},
		CreateExternalLoadBalancer: true,
	}
	memory.CreateService(svc)

	c, _ := storage.Delete(svc.ID)
	<-c

	if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "delete" {
		t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
	}
	srv, err := memory.GetService(svc.ID)
	expectNoError(t, err)
	if srv != nil {
		t.Errorf("Unexpected service: %#v", *srv)
	}
}
<|endoftext|>


<commit_before>package libstring

func StringInSlice(sliceString []string, needle string) bool {
	for _, b := range sliceString {
		if b == needle {
			return true
		}
	}
	return false
}
<commit_msg>Update godoc.<commit_after>// Package libstring provides various string related functions.
package libstring

func StringInSlice(sliceString []string, needle string) bool {
	for _, b := range sliceString {
		if b == needle {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package cmd

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	"github.com/pachyderm/pachyderm/src/client/version/versionpb"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/metrics"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	var noMetrics bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Environment variables:
  ADDRESS=<host>:<port>, the pachd server to connect to (e.g. 127.0.0.1:30650).
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				l := log.New()
				l.Level = log.FatalLevel
				grpclog.SetLogger(l)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")
	rootCmd.PersistentFlags().BoolVarP(&noMetrics, "no-metrics", "", false, "Don't report user metrics for this command")

	pfsCmds := pfscmds.Cmds(address, &noMetrics)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address, &noMetrics)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	deployCmds := deploycmds.Cmds(&noMetrics)
	for _, cmd := range deployCmds {
		rootCmd.AddCommand(cmd)
	}

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			if !noMetrics {
				metricsFn := metrics.ReportAndFlushUserAction("Version")
				defer func(start time.Time) { metricsFn(start, retErr) }(time.Now())
			}
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &types.Empty{})

			if err != nil {
				buf := bytes.NewBufferString("")
				errWriter := tabwriter.NewWriter(buf, 20, 1, 3, ' ', 0)
				fmt.Fprintf(errWriter, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				errWriter.Flush()
				return errors.New(buf.String())
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewMetricsClientFromAddress(address, !noMetrics, "user")
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	var kubeConfigFile string
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			kubeConfig := ""
			if len(kubeConfigFile) > 0 {
				kubeConfig = fmt.Sprintf("--kubeconfig=%v", kubeConfigFile)
			}
			stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=pachd | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:650
`, kubeConfig, kubeConfig, port))
			fmt.Println("Port forwarded, CTRL-C to exit.")
			return cmdutil.RunIO(cmdutil.IO{
				Stdin:  stdin,
				Stderr: os.Stderr,
			}, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	portForward.Flags().StringVarP(&kubeConfigFile, "kubeconfig", "k", "", "The k8s config file to use (defaults to ~/.kube/config)")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (versionpb.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return versionpb.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *versionpb.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<commit_msg>Generalize to all kubectl flags<commit_after>package cmd

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"
	"text/tabwriter"
	"time"

	"github.com/gogo/protobuf/types"
	"github.com/pachyderm/pachyderm/src/client"
	"github.com/pachyderm/pachyderm/src/client/version"
	"github.com/pachyderm/pachyderm/src/client/version/versionpb"
	pfscmds "github.com/pachyderm/pachyderm/src/server/pfs/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/cmdutil"
	deploycmds "github.com/pachyderm/pachyderm/src/server/pkg/deploy/cmds"
	"github.com/pachyderm/pachyderm/src/server/pkg/metrics"
	ppscmds "github.com/pachyderm/pachyderm/src/server/pps/cmds"

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/cobra"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
)

// PachctlCmd takes a pachd host-address and creates a cobra.Command
// which may interact with the host.
func PachctlCmd(address string) (*cobra.Command, error) {
	var verbose bool
	var noMetrics bool
	rootCmd := &cobra.Command{
		Use: os.Args[0],
		Long: `Access the Pachyderm API.

Environment variables:
  ADDRESS=<host>:<port>, the pachd server to connect to (e.g. 127.0.0.1:30650).
`,
		PersistentPreRun: func(cmd *cobra.Command, args []string) {
			if !verbose {
				// Silence any grpc logs
				l := log.New()
				l.Level = log.FatalLevel
				grpclog.SetLogger(l)
			}
		},
	}
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Output verbose logs")
	rootCmd.PersistentFlags().BoolVarP(&noMetrics, "no-metrics", "", false, "Don't report user metrics for this command")

	pfsCmds := pfscmds.Cmds(address, &noMetrics)
	for _, cmd := range pfsCmds {
		rootCmd.AddCommand(cmd)
	}
	ppsCmds, err := ppscmds.Cmds(address, &noMetrics)
	if err != nil {
		return nil, sanitizeErr(err)
	}
	for _, cmd := range ppsCmds {
		rootCmd.AddCommand(cmd)
	}
	deployCmds := deploycmds.Cmds(&noMetrics)
	for _, cmd := range deployCmds {
		rootCmd.AddCommand(cmd)
	}

	version := &cobra.Command{
		Use:   "version",
		Short: "Return version information.",
		Long:  "Return version information.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) (retErr error) {
			if !noMetrics {
				metricsFn := metrics.ReportAndFlushUserAction("Version")
				defer func(start time.Time) { metricsFn(start, retErr) }(time.Now())
			}
			writer := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0)
			printVersionHeader(writer)
			printVersion(writer, "pachctl", version.Version)
			writer.Flush()

			versionClient, err := getVersionAPIClient(address)
			if err != nil {
				return sanitizeErr(err)
			}
			ctx, _ := context.WithTimeout(context.Background(), time.Second)
			version, err := versionClient.GetVersion(ctx, &types.Empty{})

			if err != nil {
				buf := bytes.NewBufferString("")
				errWriter := tabwriter.NewWriter(buf, 20, 1, 3, ' ', 0)
				fmt.Fprintf(errWriter, "pachd\t(version unknown) : error connecting to pachd server at address (%v): %v\n\nplease make sure pachd is up (`kubectl get all`) and portforwarding is enabled\n", address, sanitizeErr(err))
				errWriter.Flush()
				return errors.New(buf.String())
			}

			printVersion(writer, "pachd", version)
			return writer.Flush()
		}),
	}
	deleteAll := &cobra.Command{
		Use:   "delete-all",
		Short: "Delete everything.",
		Long: `Delete all repos, commits, files, pipelines and jobs.
This resets the cluster to its initial state.`,
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			client, err := client.NewMetricsClientFromAddress(address, !noMetrics, "user")
			if err != nil {
				return sanitizeErr(err)
			}
			fmt.Printf("Are you sure you want to delete all repos, commits, files, pipelines and jobs? yN\n")
			r := bufio.NewReader(os.Stdin)
			bytes, err := r.ReadBytes('\n')
			if err != nil {
				return err
			}
			if bytes[0] == 'y' || bytes[0] == 'Y' {
				return client.DeleteAll()
			}
			return nil
		}),
	}
	var port int
	var kubeCtlFlags string
	portForward := &cobra.Command{
		Use:   "port-forward",
		Short: "Forward a port on the local machine to pachd. This command blocks.",
		Long:  "Forward a port on the local machine to pachd. This command blocks.",
		Run: cmdutil.RunFixedArgs(0, func(args []string) error {
			stdin := strings.NewReader(fmt.Sprintf(`
pod=$(kubectl %v get pod -l app=pachd | awk '{if (NR!=1) { print $1; exit 0 }}')
kubectl %v port-forward "$pod" %d:650
`, kubeCtlFlags, kubeCtlFlags, port))
			fmt.Println("Port forwarded, CTRL-C to exit.")
			return cmdutil.RunIO(cmdutil.IO{
				Stdin:  stdin,
				Stderr: os.Stderr,
			}, "sh")
		}),
	}
	portForward.Flags().IntVarP(&port, "port", "p", 30650, "The local port to bind to.")
	portForward.Flags().StringVarP(&kubeCtlFlags, "kubectlflags", "k", "", "Any kubectl flags to proxy, e.g. --kubectlflags='--kubeconfig /some/path/kubeconfig'")
	rootCmd.AddCommand(version)
	rootCmd.AddCommand(deleteAll)
	rootCmd.AddCommand(portForward)
	return rootCmd, nil
}

func getVersionAPIClient(address string) (versionpb.APIClient, error) {
	clientConn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		return nil, err
	}
	return versionpb.NewAPIClient(clientConn), nil
}

func printVersionHeader(w io.Writer) {
	fmt.Fprintf(w, "COMPONENT\tVERSION\t\n")
}

func printVersion(w io.Writer, component string, v *versionpb.Version) {
	fmt.Fprintf(w, "%s\t%s\t\n", component, version.PrettyPrintVersion(v))
}

func sanitizeErr(err error) error {
	if err == nil {
		return nil
	}

	return errors.New(grpc.ErrorDesc(err))
}
<|endoftext|>


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

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

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

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

package main

import (
	"log"
	"os"
	"os/exec"
	"reflect"
	"text/template"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util"
)

const (
	nginxConf = `
worker_processes  5;  ## Default: 1

worker_rlimit_nofile 8192;
events {
  worker_connections 4096;
}

http {
  error_log /var/log/nginx/error.log info;
  # http://nginx.org/en/docs/http/ngx_http_core_module.html
  types_hash_max_size 2048;
  server_names_hash_max_size 512;
  server_names_hash_bucket_size 64;
  client_max_body_size 50M;

  ssl_certificate /ssl/certchain.pem;
  ssl_certificate_key /ssl/key.pem;

  # performance enhancement for SSL
  ssl_stapling on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 5m;

  # Disable all weak ciphers
  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  # enables TLSv1.0, 1.1, and 1.2 but not SSLv2 or 3 as they are both weak and
  # deprecated.
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # Specifies that server ciphers should be preferred over client (e.g.
  # browser) ciphers when using SSL/TLS.
  ssl_prefer_server_ciphers on;

  # config to enable HSTS(HTTP Strict Transport Security)
  # https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
  # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
  add_header Strict-Transport-Security "max-age=31536000;";

  # nginx-stats endpoint for sysdig
  server {
    listen 127.0.0.1:80;
    location /nginx_status {
      # Don't log these, sysdig polls constantly
      access_log off;
      stub_status;
    }
  }

  server {
    listen 127.0.0.1:443 ssl;
    location /nginx_status {
      # Don't log these, sysdig polls constantly
      access_log off;
      stub_status;
    }
  }

  # HTTP redirect
  server {
    listen 80 default_server;
    return 301 https://$host$request_uri;
  }

  server {
    listen 443 ssl;
    server_name _;

    location / {
      return 503;
    }
  }

{{range $entry := .}}

  {{if $entry.Endpoints}}
  upstream {{$entry.ServiceName}} {
    {{range $endpoint := $entry.Endpoints}}
    server {{$endpoint}}:{{$entry.ServicePort}};
    {{end}}
    sticky;
  }

  server {
    listen 443 ssl;
    server_name {{$entry.Host}};
    location {{$entry.Path}} {
      proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

      add_header P3P 'CP="Please contact support."';

      proxy_redirect off;

      # Handle Web Socket connections
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://{{$entry.ServiceName}};
    }
  }
  {{end}}
{{end}}
}`
)

func shellOut(cmd string) {
	out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	if err != nil {
		log.Fatalf("Failed to execute %v: %v, err: %v", cmd, string(out), err)
	}
}

type TmplData struct {
	Path, Host, ServiceName, ServicePort string
	Endpoints                            []string
}

func main() {
	var ingClient client.IngressInterface
	var podClient client.PodInterface
	var serviceClient client.ServiceInterface
	var namespace = os.Getenv("NAMESPACE")
	var namespaceStr = ""
	if namespace == "" {
		namespace = api.NamespaceAll
		namespaceStr = "ALL"
	} else {
		namespaceStr = namespace
	}
	log.Printf("Starting csats-balancer for namespace %s", namespaceStr)
	if kubeClient, err := client.NewInCluster(); err != nil {
		log.Fatalf("Failed to create client: %v.", err)
	} else {
		ingClient = kubeClient.Extensions().Ingress(namespace)
		podClient = kubeClient.Pods(namespace)
		serviceClient = kubeClient.Services(namespace)
	}
	tmpl, _ := template.New("nginx").Parse(nginxConf)
	rateLimiter := util.NewTokenBucketRateLimiter(0.1, 1)
	knownIngresses := &extensions.IngressList{}
	knownPods := &api.PodList{}
	knownServices := &api.ServiceList{}

	// Controller loop
	shellOut("nginx")
	for {
		rateLimiter.Accept()

		ingresses, err := ingClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving ingresses: %v", err)
			continue
		}

		pods, err := podClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving pods: %v", err)
			continue
		}

		services, err := serviceClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving services: %v", err)
			continue
		}

		if reflect.DeepEqual(ingresses.Items, knownIngresses.Items) &&
			reflect.DeepEqual(pods.Items, knownPods.Items) &&
			reflect.DeepEqual(services.Items, knownServices.Items) {
			// log.Printf("Not reloading nginx")
			continue
		}

		knownIngresses = ingresses
		knownPods = pods
		knownServices = services

		// Build a map from service names to list of pod endpoints
		endpointMap := make(map[string][]string)
		for serviceIdx := 0; serviceIdx < len(services.Items); serviceIdx++ {
			service := services.Items[serviceIdx]
			endpointMap[service.Name] = make([]string, 0)
			pods, err := podClient.List(labels.SelectorFromSet(service.Spec.Selector), fields.Everything())
			if err != nil {
				log.Printf("Error retrieving services: %v", err)
				continue
			}
			for podIdx := 0; podIdx < len(pods.Items); podIdx++ {
				pod := pods.Items[podIdx]
				if len(pod.Status.PodIP) > 0 {
					endpointMap[service.Name] = append(endpointMap[service.Name], pod.Status.PodIP)
				}
			}
		}

		// Build a list of TmplData mapping ingresses to services
		var data []TmplData
		for ingressIdx := 0; ingressIdx < len(ingresses.Items); ingressIdx++ {
			ingress := ingresses.Items[ingressIdx]
			for ruleIdx := 0; ruleIdx < len(ingress.Spec.Rules); ruleIdx++ {
				rule := ingress.Spec.Rules[ruleIdx]
				for pathIdx := 0; pathIdx < len(rule.HTTP.Paths); pathIdx++ {
					path := rule.HTTP.Paths[pathIdx]
					data = append(data, TmplData{
						Host:        rule.Host,
						Path:        path.Path,
						ServiceName: path.Backend.ServiceName,
						ServicePort: path.Backend.ServicePort.String(),
						Endpoints:   endpointMap[path.Backend.ServiceName],
					})
				}
			}
		}

		// Render to file using that data and our template
		if w, err := os.Create("/etc/nginx/nginx.conf"); err != nil {
			log.Fatalf("Failed to open %v: %v", nginxConf, err)
		} else if err := tmpl.Execute(w, data); err != nil {
			log.Fatalf("Failed to write template %v", err)
		}
		log.Printf("[%s] Reloading nginx", namespaceStr)
		shellOut("nginx -s reload")
	}
}
<commit_msg>Remove sysdig<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"log"
	"os"
	"os/exec"
	"reflect"
	"text/template"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util"
)

const (
	nginxConf = `
worker_processes  5;  ## Default: 1

worker_rlimit_nofile 8192;
events {
  worker_connections 4096;
}

http {
  error_log /var/log/nginx/error.log info;
  # http://nginx.org/en/docs/http/ngx_http_core_module.html
  types_hash_max_size 2048;
  server_names_hash_max_size 512;
  server_names_hash_bucket_size 64;
  client_max_body_size 50M;

  ssl_certificate /ssl/certchain.pem;
  ssl_certificate_key /ssl/key.pem;

  # performance enhancement for SSL
  ssl_stapling on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 5m;

  # Disable all weak ciphers
  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  # enables TLSv1.0, 1.1, and 1.2 but not SSLv2 or 3 as they are both weak and
  # deprecated.
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  # Specifies that server ciphers should be preferred over client (e.g.
  # browser) ciphers when using SSL/TLS.
  ssl_prefer_server_ciphers on;

  # config to enable HSTS(HTTP Strict Transport Security)
  # https://developer.mozilla.org/en-US/docs/Security/HTTP_Strict_Transport_Security
  # to avoid ssl stripping https://en.wikipedia.org/wiki/SSL_stripping#SSL_stripping
  add_header Strict-Transport-Security "max-age=31536000;";

  # HTTP redirect
  server {
    listen 80 default_server;
    return 301 https://$host$request_uri;
  }

  server {
    listen 443 ssl;
    server_name _;

    location / {
      return 503;
    }
  }

{{range $entry := .}}

  {{if $entry.Endpoints}}
  upstream {{$entry.ServiceName}} {
    {{range $endpoint := $entry.Endpoints}}
    server {{$endpoint}}:{{$entry.ServicePort}};
    {{end}}
    sticky;
  }

  server {
    listen 443 ssl;
    server_name {{$entry.Host}};
    location {{$entry.Path}} {
      proxy_set_header X-Forwarded-For $remote_addr; # preserve client IP
      proxy_set_header Host $http_host;
      proxy_set_header X-NginX-Proxy true;

      add_header P3P 'CP="Please contact support."';

      proxy_redirect off;

      # Handle Web Socket connections
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
      proxy_pass http://{{$entry.ServiceName}};
    }
  }
  {{end}}
{{end}}
}`
)

func shellOut(cmd string) {
	out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	if err != nil {
		log.Fatalf("Failed to execute %v: %v, err: %v", cmd, string(out), err)
	}
}

type TmplData struct {
	Path, Host, ServiceName, ServicePort string
	Endpoints                            []string
}

func main() {
	var ingClient client.IngressInterface
	var podClient client.PodInterface
	var serviceClient client.ServiceInterface
	var namespace = os.Getenv("NAMESPACE")
	var namespaceStr = ""
	if namespace == "" {
		namespace = api.NamespaceAll
		namespaceStr = "ALL"
	} else {
		namespaceStr = namespace
	}
	log.Printf("Starting csats-balancer for namespace %s", namespaceStr)
	if kubeClient, err := client.NewInCluster(); err != nil {
		log.Fatalf("Failed to create client: %v.", err)
	} else {
		ingClient = kubeClient.Extensions().Ingress(namespace)
		podClient = kubeClient.Pods(namespace)
		serviceClient = kubeClient.Services(namespace)
	}
	tmpl, _ := template.New("nginx").Parse(nginxConf)
	rateLimiter := util.NewTokenBucketRateLimiter(0.1, 1)
	knownIngresses := &extensions.IngressList{}
	knownPods := &api.PodList{}
	knownServices := &api.ServiceList{}

	// Controller loop
	shellOut("nginx")
	for {
		rateLimiter.Accept()

		ingresses, err := ingClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving ingresses: %v", err)
			continue
		}

		pods, err := podClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving pods: %v", err)
			continue
		}

		services, err := serviceClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving services: %v", err)
			continue
		}

		if reflect.DeepEqual(ingresses.Items, knownIngresses.Items) &&
			reflect.DeepEqual(pods.Items, knownPods.Items) &&
			reflect.DeepEqual(services.Items, knownServices.Items) {
			// log.Printf("Not reloading nginx")
			continue
		}

		knownIngresses = ingresses
		knownPods = pods
		knownServices = services

		// Build a map from service names to list of pod endpoints
		endpointMap := make(map[string][]string)
		for serviceIdx := 0; serviceIdx < len(services.Items); serviceIdx++ {
			service := services.Items[serviceIdx]
			endpointMap[service.Name] = make([]string, 0)
			pods, err := podClient.List(labels.SelectorFromSet(service.Spec.Selector), fields.Everything())
			if err != nil {
				log.Printf("Error retrieving services: %v", err)
				continue
			}
			for podIdx := 0; podIdx < len(pods.Items); podIdx++ {
				pod := pods.Items[podIdx]
				if len(pod.Status.PodIP) > 0 {
					endpointMap[service.Name] = append(endpointMap[service.Name], pod.Status.PodIP)
				}
			}
		}

		// Build a list of TmplData mapping ingresses to services
		var data []TmplData
		for ingressIdx := 0; ingressIdx < len(ingresses.Items); ingressIdx++ {
			ingress := ingresses.Items[ingressIdx]
			for ruleIdx := 0; ruleIdx < len(ingress.Spec.Rules); ruleIdx++ {
				rule := ingress.Spec.Rules[ruleIdx]
				for pathIdx := 0; pathIdx < len(rule.HTTP.Paths); pathIdx++ {
					path := rule.HTTP.Paths[pathIdx]
					data = append(data, TmplData{
						Host:        rule.Host,
						Path:        path.Path,
						ServiceName: path.Backend.ServiceName,
						ServicePort: path.Backend.ServicePort.String(),
						Endpoints:   endpointMap[path.Backend.ServiceName],
					})
				}
			}
		}

		// Render to file using that data and our template
		if w, err := os.Create("/etc/nginx/nginx.conf"); err != nil {
			log.Fatalf("Failed to open %v: %v", nginxConf, err)
		} else if err := tmpl.Execute(w, data); err != nil {
			log.Fatalf("Failed to write template %v", err)
		}
		log.Printf("[%s] Reloading nginx", namespaceStr)
		shellOut("nginx -s reload")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 controller

import (
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/kubernetes-incubator/external-dns/plan"
	"github.com/kubernetes-incubator/external-dns/registry"
	"github.com/kubernetes-incubator/external-dns/source"
)

// Controller is responsible for orchestrating the different components.
// It works in the following way:
// * Ask the DNS provider for current list of endpoints.
// * Ask the Source for the desired list of endpoints.
// * Take both lists and calculate a Plan to move current towards desired state.
// * Tell the DNS provider to apply the changes calucated by the Plan.
type Controller struct {
	Zone string

	Source   source.Source
	Registry registry.Registry
	// The policy that defines which changes to DNS records are allowed
	Policy plan.Policy
	// The interval between individual synchronizations
	Interval time.Duration
}

// RunOnce runs a single iteration of a reconciliation loop.
func (c *Controller) RunOnce() error {
	records, err := c.Registry.Records(c.Zone)
	if err != nil {
		return err
	}

	endpoints, err := c.Source.Endpoints()
	if err != nil {
		return err
	}

	plan := &plan.Plan{
		Policy:  c.Policy,
		Current: records,
		Desired: endpoints,
	}

	plan = plan.Calculate()

	return c.Registry.ApplyChanges(c.Zone, plan.Changes)
}

// Run runs RunOnce in a loop with a delay until stopChan receives a value.
func (c *Controller) Run(stopChan <-chan struct{}) {
	for {
		err := c.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		select {
		case <-time.After(c.Interval):
		case <-stopChan:
			log.Infoln("terminating main controller loop")
			return
		}
	}
}
<commit_msg>#103 log calculated changes (#153)<commit_after>/*
Copyright 2017 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 controller

import (
	"time"

	log "github.com/Sirupsen/logrus"

	"github.com/kubernetes-incubator/external-dns/plan"
	"github.com/kubernetes-incubator/external-dns/registry"
	"github.com/kubernetes-incubator/external-dns/source"
)

// Controller is responsible for orchestrating the different components.
// It works in the following way:
// * Ask the DNS provider for current list of endpoints.
// * Ask the Source for the desired list of endpoints.
// * Take both lists and calculate a Plan to move current towards desired state.
// * Tell the DNS provider to apply the changes calucated by the Plan.
type Controller struct {
	Zone string

	Source   source.Source
	Registry registry.Registry
	// The policy that defines which changes to DNS records are allowed
	Policy plan.Policy
	// The interval between individual synchronizations
	Interval time.Duration
}

// RunOnce runs a single iteration of a reconciliation loop.
func (c *Controller) RunOnce() error {
	records, err := c.Registry.Records(c.Zone)
	if err != nil {
		return err
	}

	endpoints, err := c.Source.Endpoints()
	if err != nil {
		return err
	}

	plan := &plan.Plan{
		Policy:  c.Policy,
		Current: records,
		Desired: endpoints,
	}

	plan = plan.Calculate()

	for _, change := range plan.Changes.Create {
		log.Infof("Creating %s %s -> %s ..", change.RecordType, change.DNSName, change.Target)
	}
	for _, change := range plan.Changes.UpdateNew {
		log.Infof("Updating %s %s -> %s ..", change.RecordType, change.DNSName, change.Target)
	}
	for _, change := range plan.Changes.Delete {
		log.Infof("Deleting %s %s -> %s ..", change.RecordType, change.DNSName, change.Target)
	}

	return c.Registry.ApplyChanges(c.Zone, plan.Changes)
}

// Run runs RunOnce in a loop with a delay until stopChan receives a value.
func (c *Controller) Run(stopChan <-chan struct{}) {
	for {
		err := c.RunOnce()
		if err != nil {
			log.Fatal(err)
		}

		select {
		case <-time.After(c.Interval):
		case <-stopChan:
			log.Infoln("terminating main controller loop")
			return
		}
	}
}
<|endoftext|>


<commit_before>package logreg

import "testing"

func TestNewLogisticRegression(t *testing.T) {
	if lr := NewLogisticRegression(); lr == nil {
		t.Errorf("got nil linear regression")
	}
}

func TestInitialize(t *testing.T) {
	lr := NewLogisticRegression()
	lr.Initialize()

	if len(lr.Xn) != len(lr.Yn) {
		t.Errorf("got different size of vectors Xn Yn, wants same size")
	}

	if len(lr.Xn[0]) != len(lr.Wn) {
		t.Errorf("got different size of vectors Xn Wn, wants same size")
	}

	if len(lr.Xn) != lr.TrainingPoints {
		t.Errorf("got different size of vectors Xn and training points, wants same number")
	}

	for i := 0; i < len(lr.Xn); i++ {
		for j := 0; j < len(lr.Xn[0]); j++ {
			if lr.Xn[i][j] < lr.Interval.Min ||
				lr.Xn[i][j] > lr.Interval.Max {
				t.Errorf("got value of Xn[%d][%d] = %v, want it between %v and %v", i, j, lr.Xn[i][j], lr.Interval.Min, lr.Interval.Max)
			}
		}
	}

	for i := 0; i < len(lr.Yn); i++ {
		if lr.Yn[i] != float64(-1) && lr.Yn[i] != float64(1) {
			t.Errorf("got value of Yn[%v] = %v, want it equal to -1 or 1", i, lr.Yn[i])
		}
	}
}

func TestInitializeFromData(t *testing.T) {
	data := [][]float64{
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
	}

	lr := NewLogisticRegression()
	if err := lr.InitializeFromData(data); err != nil {
		t.Errorf("%v", err)
	}
	if len(lr.Xn) != len(data) || len(lr.Yn) != len(data) {
		t.Errorf("got difference in size of Xn or Yn and data")
	}

	if len(lr.Xn) != lr.TrainingPoints {
		t.Errorf("got difference in size of Xn or TrainingPoints and data")
	}

	if len(lr.Xn[0]) != len(lr.Wn) {
		t.Errorf("got different size of vectors Xn Wn, wants same size")
	}

	if len(lr.Xn[0]) != lr.VectorSize || len(data[0]) != lr.VectorSize {
		t.Errorf("got difference in size of Xn[0] or data[0] with VectorSize")
	}
}

func TestLearn(t *testing.T) {
	lr := NewLogisticRegression()
	data := [][]float64{
		{0.1, 1, 1},
		{0.2, 1, 1},
		{0.3, 1, 1},
		{1, 0.5, -1},
		{1, 0.6, -1},
		{1, 0.7, -1},
	}

	lr.InitializeFromData(data)
	lr.Learn()
	expectedWn := []float64{0.06586, -0.99194, 0.56851}
	if !equal(expectedWn, lr.Wn) {
		t.Errorf("Weight vector is not correct: got %v, want %v", lr.Wn, expectedWn)
	}
}

func TestGradient(t *testing.T) {
	tests := []struct {
		w    []float64
		y    float64
		wn   []float64
		want []float64
	}{
		{
			w:    []float64{0, 0, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, 0, 0, 0},
		},
		{
			w:    []float64{1, 0, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, 0, 0},
		},
		{
			w:    []float64{1, 1, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, -0.5, 0},
		},
		{
			w:    []float64{1, 1, 1},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, -0.5, -0.5},
		},
	}
	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Wn = tt.wn
		got := lr.Gradient(tt.w, tt.y)
		if !equal(got, tt.want) {
			t.Errorf("test %i: got Gradient = %v, want %v", i, got, tt.want)
		}
	}
}

func TestUpdateWeights(t *testing.T) {
	tests := []struct {
		eta            float64
		w              []float64
		gradientVector []float64
		want           []float64
	}{
		{
			eta:            0.1,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.9, 0.9, 0.9},
		},
		{
			eta:            0.5,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.5, 0.5, 0.5},
		},
		{
			eta:            0,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{1, 1, 1},
		},
		{
			eta:            0.1,
			w:              []float64{0, 0, 0},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.1, 0.1, 0.1},
		},
		{
			eta:            0.1,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{0, 0, 0},
			want:           []float64{1, 1, 1},
		},
	}

	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Eta = tt.eta
		lr.Wn = tt.w
		lr.UpdateWeights(tt.gradientVector)
		got := lr.Wn
		if !equal(got, tt.want) {
			t.Errorf("test %v: got Wn:%v, want %v", i, got, tt.want)
		}
	}
}

func TestConverged(t *testing.T) {
	tests := []struct {
		wn   []float64
		w    []float64
		want bool
	}{
		{
			wn:   []float64{0.1, 0.1, 0.1},
			w:    []float64{0.1, 0.1, 0.1},
			want: true,
		},
	}

	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Wn = tt.wn
		got := lr.Converged(tt.w)
		if got != tt.want {
			t.Errorf("test %v: got converged = %v, wants %v", i, got, tt.want)
		}
	}
}

const epsilon float64 = 0.001

func equal(a, b []float64) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if (a[i] - b[i]) > epsilon {
			return false
		}
	}
	return true
}
<commit_msg>logreg - add tests to Converged()<commit_after>package logreg

import "testing"

func TestNewLogisticRegression(t *testing.T) {
	if lr := NewLogisticRegression(); lr == nil {
		t.Errorf("got nil linear regression")
	}
}

func TestInitialize(t *testing.T) {
	lr := NewLogisticRegression()
	lr.Initialize()

	if len(lr.Xn) != len(lr.Yn) {
		t.Errorf("got different size of vectors Xn Yn, wants same size")
	}

	if len(lr.Xn[0]) != len(lr.Wn) {
		t.Errorf("got different size of vectors Xn Wn, wants same size")
	}

	if len(lr.Xn) != lr.TrainingPoints {
		t.Errorf("got different size of vectors Xn and training points, wants same number")
	}

	for i := 0; i < len(lr.Xn); i++ {
		for j := 0; j < len(lr.Xn[0]); j++ {
			if lr.Xn[i][j] < lr.Interval.Min ||
				lr.Xn[i][j] > lr.Interval.Max {
				t.Errorf("got value of Xn[%d][%d] = %v, want it between %v and %v", i, j, lr.Xn[i][j], lr.Interval.Min, lr.Interval.Max)
			}
		}
	}

	for i := 0; i < len(lr.Yn); i++ {
		if lr.Yn[i] != float64(-1) && lr.Yn[i] != float64(1) {
			t.Errorf("got value of Yn[%v] = %v, want it equal to -1 or 1", i, lr.Yn[i])
		}
	}
}

func TestInitializeFromData(t *testing.T) {
	data := [][]float64{
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
		{1, 1, 1},
	}

	lr := NewLogisticRegression()
	if err := lr.InitializeFromData(data); err != nil {
		t.Errorf("%v", err)
	}
	if len(lr.Xn) != len(data) || len(lr.Yn) != len(data) {
		t.Errorf("got difference in size of Xn or Yn and data")
	}

	if len(lr.Xn) != lr.TrainingPoints {
		t.Errorf("got difference in size of Xn or TrainingPoints and data")
	}

	if len(lr.Xn[0]) != len(lr.Wn) {
		t.Errorf("got different size of vectors Xn Wn, wants same size")
	}

	if len(lr.Xn[0]) != lr.VectorSize || len(data[0]) != lr.VectorSize {
		t.Errorf("got difference in size of Xn[0] or data[0] with VectorSize")
	}
}

func TestLearn(t *testing.T) {
	lr := NewLogisticRegression()
	data := [][]float64{
		{0.1, 1, 1},
		{0.2, 1, 1},
		{0.3, 1, 1},
		{1, 0.5, -1},
		{1, 0.6, -1},
		{1, 0.7, -1},
	}

	lr.InitializeFromData(data)
	lr.Learn()
	expectedWn := []float64{0.06586, -0.99194, 0.56851}
	if !equal(expectedWn, lr.Wn) {
		t.Errorf("Weight vector is not correct: got %v, want %v", lr.Wn, expectedWn)
	}
}

func TestGradient(t *testing.T) {
	tests := []struct {
		w    []float64
		y    float64
		wn   []float64
		want []float64
	}{
		{
			w:    []float64{0, 0, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, 0, 0, 0},
		},
		{
			w:    []float64{1, 0, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, 0, 0},
		},
		{
			w:    []float64{1, 1, 0},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, -0.5, 0},
		},
		{
			w:    []float64{1, 1, 1},
			y:    1,
			wn:   []float64{0, 0, 0, 0},
			want: []float64{-0.5, -0.5, -0.5, -0.5},
		},
	}
	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Wn = tt.wn
		got := lr.Gradient(tt.w, tt.y)
		if !equal(got, tt.want) {
			t.Errorf("test %i: got Gradient = %v, want %v", i, got, tt.want)
		}
	}
}

func TestUpdateWeights(t *testing.T) {
	tests := []struct {
		eta            float64
		w              []float64
		gradientVector []float64
		want           []float64
	}{
		{
			eta:            0.1,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.9, 0.9, 0.9},
		},
		{
			eta:            0.5,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.5, 0.5, 0.5},
		},
		{
			eta:            0,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{1, 1, 1},
		},
		{
			eta:            0.1,
			w:              []float64{0, 0, 0},
			gradientVector: []float64{1, 1, 1},
			want:           []float64{0.1, 0.1, 0.1},
		},
		{
			eta:            0.1,
			w:              []float64{1, 1, 1},
			gradientVector: []float64{0, 0, 0},
			want:           []float64{1, 1, 1},
		},
	}

	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Eta = tt.eta
		lr.Wn = tt.w
		lr.UpdateWeights(tt.gradientVector)
		got := lr.Wn
		if !equal(got, tt.want) {
			t.Errorf("test %v: got Wn:%v, want %v", i, got, tt.want)
		}
	}
}

func TestConverged(t *testing.T) {
	tests := []struct {
		epsilon float64
		wn      []float64
		w       []float64
		want    bool
	}{
		{
			epsilon: 0.001,
			wn:      []float64{0.1, 0.1, 0.1},
			w:       []float64{0.1, 0.1, 0.1},
			want:    true,
		},
		{
			epsilon: 0.001,
			wn:      []float64{0.1, 0.1, 0.1},
			w:       []float64{0.1, 0.1, 0.2},
			want:    false,
		},
		{
			epsilon: 0.2,
			wn:      []float64{0.1, 0.1, 0.1},
			w:       []float64{0.09, 0.1, 0.2},
			want:    true,
		},
	}

	for i, tt := range tests {
		lr := NewLogisticRegression()
		lr.Wn = tt.wn
		lr.Epsilon = tt.epsilon
		got := lr.Converged(tt.w)
		if got != tt.want {
			t.Errorf("test %v: got converged = %v, wants %v", i, got, tt.want)
		}
	}
}

const epsilon float64 = 0.001

func equal(a, b []float64) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if (a[i] - b[i]) > epsilon {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>package funk

type chainBuilder struct{}
<commit_msg>implement chain builder<commit_after>package funk

import "reflect"

type chainBuilder struct {
	collection interface{}
}

func (b *chainBuilder) Chunk(size int) FunkBuilder {
	return &chainBuilder{Chunk(b.collection, size)}
}
func (b *chainBuilder) Compact() FunkBuilder {
	return &chainBuilder{Compact(b.collection)}
}
func (b *chainBuilder) Drop(in interface{}, n int) FunkBuilder {
	return &chainBuilder{Drop(b.collection, n)}
}
func (b *chainBuilder) Filter(predicate interface{}) FunkBuilder {
	return &chainBuilder{Filter(b.collection, predicate)}
}
func (b *chainBuilder) FlattenDeep() FunkBuilder {
	return &chainBuilder{FlattenDeep(b.collection)}
}
func (b *chainBuilder) ForEach(predicate interface{}) FunkBuilder {
	c := deepClone(b.collection)
	ForEach(c, predicate)
	return &chainBuilder{c}
}
func (b *chainBuilder) ForEachRight(predicate interface{}) FunkBuilder {
	c := deepClone(b.collection)
	ForEachRight(c, predicate)
	return &chainBuilder{c}
}
func (b *chainBuilder) Initial() FunkBuilder {
	return &chainBuilder{Initial(b.collection)}
}
func (b *chainBuilder) Intersect(y interface{}) FunkBuilder {
	return &chainBuilder{Intersect(b.collection, y)}
}
func (b *chainBuilder) Map(mapFunc interface{}) FunkBuilder {
	return &chainBuilder{Map(b.collection, mapFunc)}
}
func (b *chainBuilder) Reverse() FunkBuilder {
	return &chainBuilder{Reverse(b.collection)}
}
func (b *chainBuilder) Shuffle() FunkBuilder {
	return &chainBuilder{Shuffle(b.collection)}
}
func (b *chainBuilder) Uniq() FunkBuilder {
	return &chainBuilder{Uniq(b.collection)}
}

func (b *chainBuilder) All() bool {
	v := reflect.ValueOf(b.collection)
	c := make([]interface{}, v.Len())

	for i := 0; i < v.Len(); i++ {
		c[i] = v.Index(i).Interface()
	}
	return All(c...)
}
func (b *chainBuilder) Any() bool {
	v := reflect.ValueOf(b.collection)
	c := make([]interface{}, v.Len())

	for i := 0; i < v.Len(); i++ {
		c[i] = v.Index(i).Interface()
	}
	return Any(c...)
}
func (b *chainBuilder) Contains(elem interface{}) bool {
	return Contains(b.collection, elem)
}
func (b *chainBuilder) Every(elements ...interface{}) bool {
	return Every(b.collection, elements...)
}
func (b *chainBuilder) Find(predicate interface{}) interface{} {
	return Find(b.collection, predicate)
}
func (b *chainBuilder) Get(path string) interface{} {
	return Get(b.collection, path)
}
func (b *chainBuilder) Head() interface{} {
	return Head(b.collection)
}
func (b *chainBuilder) Keys() interface{} {
	return Keys(b.collection)
}
func (b *chainBuilder) In(v interface{}) bool {
	return b.Contains(v)
}
func (b *chainBuilder) IndexOf(elem interface{}) int {
	return IndexOf(b.collection, elem)
}
func (b *chainBuilder) IsEmpty() bool {
	return IsEmpty(b.collection)
}
func (b *chainBuilder) IsType(actual interface{}) bool {
	return IsType(b.collection, actual)
}
func (b *chainBuilder) Last() interface{} {
	return Last(b.collection)
}
func (b *chainBuilder) NotEmpty() bool {
	return NotEmpty(b.collection)
}
func (b *chainBuilder) Product() float64 {
	return Product(b.collection)
}
func (b *chainBuilder) Reduce(reduceFunc, acc interface{}) float64 {
	return Reduce(b.collection, reduceFunc, acc)
}
func (b *chainBuilder) Sum() float64 {
	return Sum(b.collection)
}
func (b *chainBuilder) Tail() interface{} {
	return Tail(b.collection)
}
func (b *chainBuilder) Type() reflect.Type {
	return reflect.TypeOf(b.collection)
}
func (b *chainBuilder) Value() interface{} {
	return b.collection
}
func (b *chainBuilder) Values() interface{} {
	return Values(b.collection)
}
<|endoftext|>


<commit_before>package niso

import "context"

// Storage is a backend used to persist data generated by the OAuth2 server
type Storage interface {
	// Close the resources the Storage potentially holds. (Implements io.Closer)
	Close() error

	// GetClientData fetches the data for a ClientData by id
	// Should return NotFoundError, so an E_INVALID_CLIENT error will be returned instead of E_SERVER_ERROR
	GetClientData(ctx context.Context, id string) (*ClientData, error)

	// SaveAuthorize saves authorize data.
	SaveAuthorizeData(ctx context.Context, data *AuthorizeData) error

	// GetAuthorizeData looks up AuthorizeData by a code.
	//// ClientData information MUST be loaded together.
	// Optionally can return error if expired.
	GetAuthorizeData(ctx context.Context, code string) (*AuthorizeData, error)

	// RemoveAuthorize revokes or deletes the authorization code.
	DeleteAuthorizeData(ctx context.Context, code string) error

	// SaveAccess writes AccessData.
	// If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh.
	SaveAccessData(ctx context.Context, data *AccessData) error

	// GetRefreshTokenData retrieves refresh token data from the token string.
	GetRefreshTokenData(ctx context.Context, token string) (*RefreshTokenData, error)

	// SaveRefreshTokenData
	SaveRefreshTokenData(ctx context.Context, data *RefreshTokenData) error

	// DeleteRefreshTokenData revokes or deletes a RefreshToken.
	DeleteRefreshTokenData(ctx context.Context, token string) error
}

// NotFoundError can be used to differentiate between internal server errors and an entity not existing in storage.
type NotFoundError struct {
	Err error
}

func (e *NotFoundError) Error() string {
	return e.Err.Error()
}
<commit_msg>Fix comment<commit_after>package niso

import "context"

// Storage is a backend used to persist data generated by the OAuth2 server
type Storage interface {
	// Close the resources the Storage potentially holds. (Implements io.Closer)
	Close() error

	// GetClientData fetches the data for a ClientData by id
	// Should return NotFoundError, so an E_INVALID_CLIENT error will be returned instead of E_SERVER_ERROR
	GetClientData(ctx context.Context, id string) (*ClientData, error)

	// SaveAuthorize saves authorize data.
	SaveAuthorizeData(ctx context.Context, data *AuthorizeData) error

	// GetAuthorizeData looks up AuthorizeData by a code.
	//// ClientData information MUST be loaded together.
	// Optionally can return error if expired.
	GetAuthorizeData(ctx context.Context, code string) (*AuthorizeData, error)

	// RemoveAuthorize revokes or deletes the authorization code.
	DeleteAuthorizeData(ctx context.Context, code string) error

	// SaveAccess writes AccessData to storage.
	SaveAccessData(ctx context.Context, data *AccessData) error

	// GetRefreshTokenData retrieves refresh token data from the token string.
	GetRefreshTokenData(ctx context.Context, token string) (*RefreshTokenData, error)

	// SaveRefreshTokenData saves refresh token data so it can be retrieved with GetRefreshTokenData
	SaveRefreshTokenData(ctx context.Context, data *RefreshTokenData) error

	// DeleteRefreshTokenData revokes or deletes a RefreshToken.
	DeleteRefreshTokenData(ctx context.Context, token string) error
}

// NotFoundError can be used to differentiate between internal server errors and an entity not existing in storage.
type NotFoundError struct {
	Err error
}

func (e *NotFoundError) Error() string {
	return e.Err.Error()
}
<|endoftext|>


<commit_before>package thirdparty

import (
	"encoding/json"
	"github.com/astaxie/beego"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/restclient"
)

type EditController struct {
	beego.Controller
}

func (c *EditController) Get() {
	c.TplNames = "repository/thirdparty/edit.html"
	guimessage := guimessagedisplay.GetGUIMessage(c)

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")

	name := c.GetString("name")
	if name == "" {
		c.Data["actionButtonValue"] = "Create"
		c.Data["pageHeader"] = "Create third party service"
		c.Data["name"] = ""

		guimessage.OutputMessage(c.Data)
	} else {
		url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
			"/api/v1/clusterapplications/" + name
		cluster := Cluster{}
		_, err := restclient.RequestGetWithStructure(url, &cluster)
		if err != nil {
			guimessage.AddDanger("Fail to get with error" + err.Error())
			// Redirect to list
			c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

			guimessage.RedirectMessage(c)
		} else {
			environmentByteSlice, err := json.MarshalIndent(cluster.Environment, "", "    ")
			if err != nil {
				guimessage.AddDanger("Fail to get with error" + err.Error())
				// Redirect to list
				c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

				guimessage.RedirectMessage(c)
			}

			c.Data["actionButtonValue"] = "Update"
			c.Data["pageHeader"] = "Update third party service"
			c.Data["name"] = cluster.Name
			c.Data["description"] = cluster.Description
			c.Data["replicationControllerJson"] = string(cluster.ReplicationControllerJson)
			c.Data["serviceJson"] = cluster.ServiceJson
			c.Data["environment"] = string(environmentByteSlice)
			c.Data["scriptContent"] = cluster.ScriptContent

			switch cluster.ScriptType {
			case "none":
				c.Data["scriptTypeNone"] = "selected"
			case "python":
				c.Data["scriptTypePython"] = "selected"
			default:
			}

			guimessage.OutputMessage(c.Data)
		}
	}
}

func (c *EditController) Post() {
	guimessage := guimessagedisplay.GetGUIMessage(c)

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")

	name := c.GetString("name")
	description := c.GetString("description")
	replicationControllerJson := c.GetString("replicationControllerJson")
	if replicationControllerJson == "" {
		replicationControllerJson = "{}"
	}
	serviceJson := c.GetString("serviceJson")
	if serviceJson == "" {
		serviceJson = "{}"
	}
	environmentText := c.GetString("environment")
	if environmentText == "" {
		environmentText = "{}"
	}
	scriptType := c.GetString("scriptType")
	scriptContent := c.GetString("scriptContent")

	environmentJsonMap := make(map[string]string)
	err := json.Unmarshal([]byte(environmentText), &environmentJsonMap)
	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
		c.Ctx.Redirect(302, "/gui/repository/thirdparty/")
		guimessage.RedirectMessage(c)
		return
	}

	cluster := Cluster{
		name,
		description,
		replicationControllerJson,
		serviceJson,
		environmentJsonMap,
		scriptType,
		scriptContent,
	}

	url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/clusterapplications/"

	_, err = restclient.RequestPost(url, cluster, true)

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		guimessage.AddSuccess("Third party application " + name + " is edited")
	}

	c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

	guimessage.RedirectMessage(c)
}
<commit_msg>DNS 591 check<commit_after>package thirdparty

import (
	"encoding/json"
	"github.com/astaxie/beego"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/restclient"
	"regexp"
)

type EditController struct {
	beego.Controller
}

func (c *EditController) Get() {
	c.TplNames = "repository/thirdparty/edit.html"
	guimessage := guimessagedisplay.GetGUIMessage(c)

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")

	name := c.GetString("name")
	if name == "" {
		c.Data["actionButtonValue"] = "Create"
		c.Data["pageHeader"] = "Create third party service"
		c.Data["name"] = ""

		guimessage.OutputMessage(c.Data)
	} else {
		url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
			"/api/v1/clusterapplications/" + name
		cluster := Cluster{}
		_, err := restclient.RequestGetWithStructure(url, &cluster)
		if err != nil {
			guimessage.AddDanger("Fail to get with error" + err.Error())
			// Redirect to list
			c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

			guimessage.RedirectMessage(c)
		} else {
			environmentByteSlice, err := json.MarshalIndent(cluster.Environment, "", "    ")
			if err != nil {
				guimessage.AddDanger("Fail to get with error" + err.Error())
				// Redirect to list
				c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

				guimessage.RedirectMessage(c)
			}

			c.Data["actionButtonValue"] = "Update"
			c.Data["pageHeader"] = "Update third party service"
			c.Data["name"] = cluster.Name
			c.Data["description"] = cluster.Description
			c.Data["replicationControllerJson"] = string(cluster.ReplicationControllerJson)
			c.Data["serviceJson"] = cluster.ServiceJson
			c.Data["environment"] = string(environmentByteSlice)
			c.Data["scriptContent"] = cluster.ScriptContent

			switch cluster.ScriptType {
			case "none":
				c.Data["scriptTypeNone"] = "selected"
			case "python":
				c.Data["scriptTypePython"] = "selected"
			default:
			}

			guimessage.OutputMessage(c.Data)
		}
	}
}

func (c *EditController) Post() {
	guimessage := guimessagedisplay.GetGUIMessage(c)

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")

	name := c.GetString("name")
	description := c.GetString("description")

	// Name need to be a DNS 952 label
	match, _ := regexp.MatchString("^[a-z]{1}[a-z0-9-]{1,23}$", name)
	if match == false {
		guimessage.AddDanger("The name need to be a DNS 952 label ^[a-z]{1}[a-z0-9-]{1,23}$")
		c.Ctx.Redirect(302, "/gui/repository/thirdparty/")
		guimessage.RedirectMessage(c)
		return
	}

	replicationControllerJson := c.GetString("replicationControllerJson")
	if replicationControllerJson == "" {
		replicationControllerJson = "{}"
	}
	serviceJson := c.GetString("serviceJson")
	if serviceJson == "" {
		serviceJson = "{}"
	}
	environmentText := c.GetString("environment")
	if environmentText == "" {
		environmentText = "{}"
	}
	scriptType := c.GetString("scriptType")
	scriptContent := c.GetString("scriptContent")

	environmentJsonMap := make(map[string]string)
	err := json.Unmarshal([]byte(environmentText), &environmentJsonMap)
	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
		c.Ctx.Redirect(302, "/gui/repository/thirdparty/")
		guimessage.RedirectMessage(c)
		return
	}

	cluster := Cluster{
		name,
		description,
		replicationControllerJson,
		serviceJson,
		environmentJsonMap,
		scriptType,
		scriptContent,
	}

	url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/clusterapplications/"

	_, err = restclient.RequestPost(url, cluster, true)

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		guimessage.AddSuccess("Third party application " + name + " is edited")
	}

	c.Ctx.Redirect(302, "/gui/repository/thirdparty/")

	guimessage.RedirectMessage(c)
}
<|endoftext|>


<commit_before>package goproxy

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"errors"
)

type CounterEncryptorRand struct {
	cipher cipher.Block
	counter []byte
	rand []byte
	ix int
}

func NewCounterEncryptorRandFromKey(key interface{}, counter []byte) (r CounterEncryptorRand, err error) {
	var keyBytes []byte
	switch key := key.(type) {
	case *rsa.PrivateKey:
		keyBytes = x509.MarshalPKCS1PrivateKey(key)
	default:
		err = errors.New("only RSA keys supported")
		return
	}
	h := sha256.New()
	if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil {
		return
	}
	r.counter = h.Sum(counter)[:r.cipher.BlockSize()]
	r.rand = make([]byte, r.cipher.BlockSize())
	r.ix = len(r.rand)
	return
}

func (c *CounterEncryptorRand) refill() {
	c.cipher.Encrypt(c.rand, c.counter)
	for i := 0; i < len(c.counter); i++ {
		if c.counter[i]++; c.counter[i] != 0 {
			break
		}
	}
	c.ix = 0
}

func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) {
	if c.ix == len(c.rand) {
		c.refill()
	}
	if n = len(c.rand) - c.ix; n > len(b) {
		n = len(b)
	}
	copy(b, c.rand[c.ix:c.ix+n])
	c.ix += n
	return
}
<commit_msg>seed interface for countercrypto<commit_after>package goproxy

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"errors"
)

type CounterEncryptorRand struct {
	cipher cipher.Block
	counter []byte
	rand []byte
	ix int
}

func NewCounterEncryptorRandFromKey(key interface{}, seed []byte) (r CounterEncryptorRand, err error) {
	var keyBytes []byte
	switch key := key.(type) {
	case *rsa.PrivateKey:
		keyBytes = x509.MarshalPKCS1PrivateKey(key)
	default:
		err = errors.New("only RSA keys supported")
		return
	}
	h := sha256.New()
	if r.cipher, err = aes.NewCipher(h.Sum(keyBytes)[:aes.BlockSize]); err != nil {
		return
	}
	r.counter = make([]byte, r.cipher.BlockSize())
	if seed != nil {
		copy(r.counter, h.Sum(seed)[:r.cipher.BlockSize()])
	}
	r.rand = make([]byte, r.cipher.BlockSize())
	r.ix = len(r.rand)
	return
}

func (c *CounterEncryptorRand) Seed(b []byte) {
	if len(b) != len(c.counter) {
		panic("SetCounter: wrong counter size")
	}
	copy(c.counter, b)
}

func (c *CounterEncryptorRand) refill() {
	c.cipher.Encrypt(c.rand, c.counter)
	for i := 0; i < len(c.counter); i++ {
		if c.counter[i]++; c.counter[i] != 0 {
			break
		}
	}
	c.ix = 0
}

func (c *CounterEncryptorRand) Read(b []byte) (n int, err error) {
	if c.ix == len(c.rand) {
		c.refill()
	}
	if n = len(c.rand) - c.ix; n > len(b) {
		n = len(b)
	}
	copy(b, c.rand[c.ix:c.ix+n])
	c.ix += n
	return
}
<|endoftext|>


<commit_before>package device

import (
	"encoding/json"
	"fmt"
	"git.neotor.se/daenney/hemtjanst/messaging"
	"log"
	"sync"
)

type Manager struct {
	devices map[string]*Device
	client  messaging.PublishSubscriber
	sync.RWMutex
}

func NewManager(c messaging.PublishSubscriber) *Manager {
	return &Manager{
		client:  c,
		devices: make(map[string]*Device, 10),
	}
}

func (m *Manager) Add(topic string) {
	log.Print("Going to add device ", topic)
	dev := &Device{Topic: topic, transport: m.client}
	m.client.Subscribe(fmt.Sprintf("%s/meta", topic), 1, func(msg messaging.Message) {
		err := json.Unmarshal(msg.Payload(), dev)
		if err != nil {
			log.Print(err)
			return
		}
		for name, ft := range dev.Features {
			if ft.SetTopic == "" {
				ft.SetTopic = fmt.Sprintf("%s/%s/set", topic, name)
			}
			if ft.GetTopic == "" {
				ft.GetTopic = fmt.Sprintf("%s/%s/get", topic, name)
			}
		}
	})

	m.Lock()
	defer m.Unlock()
	m.devices[topic] = dev
}

func (m *Manager) Get(id string) (*Device, error) {
	log.Print("Looking for device ", id)
	m.RLock()
	defer m.RUnlock()
	if val, ok := m.devices[id]; ok {
		return val, nil
	}
	return nil, fmt.Errorf("Unknown device %s", id)
}

func (m *Manager) Remove(msg string) {
	log.Print("Attempting to remove device ", msg)
	m.Lock()
	defer m.Unlock()
	if val, ok := m.devices[msg]; ok {
		log.Print("Found device, unsubscribing and removing")
		m.client.Unsubscribe(fmt.Sprintf("%s/meta", msg))
		for f, _ := range val.Features {
			m.client.Unsubscribe(fmt.Sprintf("%s/%s/get", msg, f))
		}
		delete(m.devices, msg)
		return
	}
	for _, d := range m.devices {
		if d.LastWillID.String() == msg {
			log.Print("Found device match for LastWillUID, calling Remove")
			m.Remove(d.Topic)
		}
	}
}
<commit_msg>Add DeviceHandler interface, which gets notified about changes to devices<commit_after>package device

import (
	"encoding/json"
	"fmt"
	"git.neotor.se/daenney/hemtjanst/messaging"
	"log"
	"sync"
)

type DeviceHandler interface {
	DeviceUpdated(*Device)
	DeviceLeave(*Device)
	DeviceRemoved(*Device)
}

type Manager struct {
	devices  map[string]*Device
	handlers []DeviceHandler
	client   messaging.PublishSubscriber
	sync.RWMutex
}

func NewManager(c messaging.PublishSubscriber) *Manager {
	return &Manager{
		client:   c,
		devices:  make(map[string]*Device, 10),
		handlers: []DeviceHandler{},
	}
}

func (m *Manager) Add(topic string) {
	if _, ok := m.devices[topic]; ok {
		log.Print("Got announce for existing device ", topic)
		return
	}
	log.Print("Going to add device ", topic)
	dev := &Device{Topic: topic, transport: m.client}
	m.client.Subscribe(fmt.Sprintf("%s/meta", topic), 1, func(msg messaging.Message) {
		err := json.Unmarshal(msg.Payload(), dev)
		if err != nil {
			log.Print(err)
			return
		}
		for name, ft := range dev.Features {
			if ft.SetTopic == "" {
				ft.SetTopic = fmt.Sprintf("%s/%s/set", topic, name)
			}
			if ft.GetTopic == "" {
				ft.GetTopic = fmt.Sprintf("%s/%s/get", topic, name)
			}
		}

		go m.forHandler(func(handler DeviceHandler) {
			handler.DeviceUpdated(dev)
		})
	})

	m.Lock()
	defer m.Unlock()
	m.devices[topic] = dev
}

func (m *Manager) Get(id string) (*Device, error) {
	log.Print("Looking for device ", id)
	m.RLock()
	defer m.RUnlock()
	if val, ok := m.devices[id]; ok {
		return val, nil
	}
	return nil, fmt.Errorf("Unknown device %s", id)
}

func (m *Manager) Remove(msg string) {
	log.Print("Attempting to remove device ", msg)
	m.Lock()
	defer m.Unlock()
	if val, ok := m.devices[msg]; ok {
		log.Print("Found device, unsubscribing and removing")
		m.client.Unsubscribe(fmt.Sprintf("%s/meta", msg))
		for _, ft := range val.Features {
			m.client.Unsubscribe(ft.GetTopic)
		}

		go m.forHandler(func(handler DeviceHandler) {
			handler.DeviceLeave(val)
		})

		delete(m.devices, msg)
		return
	}
	for _, d := range m.devices {
		if d.LastWillID.String() == msg {
			log.Print("Found device match for LastWillUID, calling Remove")
			m.Remove(d.Topic)
		}
	}
}

func (m *Manager) forHandler(f func(handler DeviceHandler)) {
	for _, h := range m.handlers {
		f(h)
	}
}

func (m *Manager) AddHandler(handler DeviceHandler) {
	m.Lock()
	defer m.Unlock()
	m.handlers = append(m.handlers, handler)
	go func() {
		for _, device := range m.devices {
			handler.DeviceUpdated(device)
		}
	}()
}
<|endoftext|>