<commit_before>// +build e2e

/*
Copyright 2019 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 runtime

import (
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/knative/pkg/ptr"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/test"
	v1a1test "github.com/knative/serving/test/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// TestMustNotContainerContraints tests that attempting to set unsupported fields or invalid values as
// defined by "MUST NOT" statements from the runtime contract results in a user facing error.
func TestMustNotContainerConstraints(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	testCases := []struct {
		name    string
		options func(s *v1alpha1.Service)
	}{{
		name: "TestArbitraryPortName",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Ports = []corev1.ContainerPort{{
				Name:          "arbitrary",
				ContainerPort: 8080,
			}}
		},
	}, {
		name: "TestMountPropagation",
		options: func(s *v1alpha1.Service) {
			propagationMode := corev1.MountPropagationHostToContainer
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().VolumeMounts = []corev1.VolumeMount{{
				Name:             "VolumeMount",
				MountPath:        "/",
				MountPropagation: &propagationMode,
			}}
		},
	}, {
		name: "TestReadinessHTTPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().ReadinessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "/",
						Port: intstr.FromInt(8888),
					},
				},
			}
		},
	}, {
		name: "TestLivenessHTTPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().LivenessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "/",
						Port: intstr.FromInt(8888),
					},
				},
			}
		},
	}, {
		name: "TestReadinessTCPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().ReadinessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(8888)},
				},
			}
		},
	}, {
		name: "TestLivenessTCPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().LivenessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(8888)},
				},
			}
		},
	}}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			names := test.ResourceNames{
				Service: test.ObjectNameForTest(t),
				Image:   test.Runtime,
			}
			if svc, err := v1a1test.CreateLatestService(t, clients, names, &v1a1test.Options{}, tc.options); err == nil {
				t.Errorf("CreateLatestService = %v, want: error", spew.Sdump(svc))
			}
		})
	}
}

// TestShouldNotContainerContraints tests that attempting to set unsupported fields or invalid values as
// defined by "SHOULD NOT" statements from the runtime contract results in a user facing error.
func TestShouldNotContainerConstraints(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	testCases := []struct {
		name    string
		options func(s *v1alpha1.Service)
	}{{
		name: "TestPoststartHook",
		options: func(s *v1alpha1.Service) {
			lifecycleHandler := &corev1.ExecAction{
				Command: []string{"/bin/sh", "-c", "echo Hello from the post start handler > /usr/share/message"},
			}
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Lifecycle = &corev1.Lifecycle{
				PostStart: &corev1.Handler{Exec: lifecycleHandler},
			}
		},
	}, {
		name: "TestPrestopHook",
		options: func(s *v1alpha1.Service) {
			lifecycleHandler := &corev1.ExecAction{
				Command: []string{"/bin/sh", "-c", "echo Hello from the pre stop handler > /usr/share/message"},
			}
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Lifecycle = &corev1.Lifecycle{
				PreStop: &corev1.Handler{Exec: lifecycleHandler},
			}
		},
	}, {
		name: "TestMultiplePorts",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Ports = []corev1.ContainerPort{
				{ContainerPort: 80},
				{ContainerPort: 81},
			}
		},
	}, {
		name: "TestHostPort",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Ports = []corev1.ContainerPort{{
				ContainerPort: 8081,
				HostPort:      80,
			}}
		},
	}, {
		name: "TestStdin",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().Stdin = true
		},
	}, {
		name: "TestStdinOnce",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().StdinOnce = true
		},
	}, {
		name: "TestTTY",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().TTY = true
		},
	}, {
		name: "TestInvalidUID",
		options: func(s *v1alpha1.Service) {
			s.Spec.ConfigurationSpec.GetTemplate().Spec.GetContainer().SecurityContext = &corev1.SecurityContext{
				RunAsUser: ptr.Int64(-10),
			}
		},
	}}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			names := test.ResourceNames{
				Service: test.ObjectNameForTest(t),
				Image:   test.Runtime,
			}
			if svc, err := v1a1test.CreateLatestService(t, clients, names, &v1a1test.Options{}, tc.options); err == nil {
				t.Errorf("CreateLatestService = %v, want: error", spew.Sdump(svc))
			}
		})
	}
}
<commit_msg>Prepare container_test for beta (#4392)<commit_after>// +build e2e

/*
Copyright 2019 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 runtime

import (
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/knative/pkg/ptr"
	"github.com/knative/serving/pkg/apis/serving/v1alpha1"
	"github.com/knative/serving/test"
	v1a1test "github.com/knative/serving/test/v1alpha1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
)

// TestMustNotContainerContraints tests that attempting to set unsupported fields or invalid values as
// defined by "MUST NOT" statements from the runtime contract results in a user facing error.
func TestMustNotContainerConstraints(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	testCases := []struct {
		name    string
		options func(s *v1alpha1.Service)
	}{{
		name: "TestArbitraryPortName",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{{
				Name:          "arbitrary",
				ContainerPort: 8080,
			}}
		},
	}, {
		name: "TestMountPropagation",
		options: func(s *v1alpha1.Service) {
			propagationMode := corev1.MountPropagationHostToContainer
			s.Spec.Template.Spec.Containers[0].VolumeMounts = []corev1.VolumeMount{{
				Name:             "VolumeMount",
				MountPath:        "/",
				MountPropagation: &propagationMode,
			}}
		},
	}, {
		name: "TestReadinessHTTPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "/",
						Port: intstr.FromInt(8888),
					},
				},
			}
		},
	}, {
		name: "TestLivenessHTTPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					HTTPGet: &corev1.HTTPGetAction{
						Path: "/",
						Port: intstr.FromInt(8888),
					},
				},
			}
		},
	}, {
		name: "TestReadinessTCPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].ReadinessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(8888)},
				},
			}
		},
	}, {
		name: "TestLivenessTCPProbePort",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].LivenessProbe = &corev1.Probe{
				Handler: corev1.Handler{
					TCPSocket: &corev1.TCPSocketAction{Port: intstr.FromInt(8888)},
				},
			}
		},
	}}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			names := test.ResourceNames{
				Service: test.ObjectNameForTest(t),
				Image:   test.Runtime,
			}
			if svc, err := v1a1test.CreateLatestService(t, clients, names, &v1a1test.Options{}, tc.options); err == nil {
				t.Errorf("CreateService = %v, want: error", spew.Sdump(svc))
			}
		})
	}
}

// TestShouldNotContainerContraints tests that attempting to set unsupported fields or invalid values as
// defined by "SHOULD NOT" statements from the runtime contract results in a user facing error.
func TestShouldNotContainerConstraints(t *testing.T) {
	t.Parallel()
	clients := test.Setup(t)

	testCases := []struct {
		name    string
		options func(s *v1alpha1.Service)
	}{{
		name: "TestPoststartHook",
		options: func(s *v1alpha1.Service) {
			lifecycleHandler := &corev1.ExecAction{
				Command: []string{"/bin/sh", "-c", "echo Hello from the post start handler > /usr/share/message"},
			}
			s.Spec.Template.Spec.Containers[0].Lifecycle = &corev1.Lifecycle{
				PostStart: &corev1.Handler{Exec: lifecycleHandler},
			}
		},
	}, {
		name: "TestPrestopHook",
		options: func(s *v1alpha1.Service) {
			lifecycleHandler := &corev1.ExecAction{
				Command: []string{"/bin/sh", "-c", "echo Hello from the pre stop handler > /usr/share/message"},
			}
			s.Spec.Template.Spec.Containers[0].Lifecycle = &corev1.Lifecycle{
				PreStop: &corev1.Handler{Exec: lifecycleHandler},
			}
		},
	}, {
		name: "TestMultiplePorts",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{
				{ContainerPort: 80},
				{ContainerPort: 81},
			}
		},
	}, {
		name: "TestHostPort",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].Ports = []corev1.ContainerPort{{
				ContainerPort: 8081,
				HostPort:      80,
			}}
		},
	}, {
		name: "TestStdin",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].Stdin = true
		},
	}, {
		name: "TestStdinOnce",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].StdinOnce = true
		},
	}, {
		name: "TestTTY",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].TTY = true
		},
	}, {
		name: "TestInvalidUID",
		options: func(s *v1alpha1.Service) {
			s.Spec.Template.Spec.Containers[0].SecurityContext = &corev1.SecurityContext{
				RunAsUser: ptr.Int64(-10),
			}
		},
	}}

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			names := test.ResourceNames{
				Service: test.ObjectNameForTest(t),
				Image:   test.Runtime,
			}
			if svc, err := v1a1test.CreateLatestService(t, clients, names, &v1a1test.Options{}, tc.options); err == nil {
				t.Errorf("CreateLatestService = %v, want: error", spew.Sdump(svc))
			}
		})
	}
}
<|endoftext|>


<commit_before>package actors

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/cloudfoundry/bosh-bootloader/integration-test"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const (
	AWSIAAS = iota
	GCPIAAS
)

type BBL struct {
	stateDirectory string
	pathToBBL      string
	configuration  integration.Config
	envID          string
}

type IAAS int

func NewBBL(stateDirectory string, pathToBBL string, configuration integration.Config, envIDSuffix string) BBL {
	envIDPrefix := os.Getenv("BBL_TEST_ENV_ID_PREFIX")
	if envIDPrefix == "" {
		envIDPrefix = "bbl-test"
	}

	return BBL{
		stateDirectory: stateDirectory,
		pathToBBL:      pathToBBL,
		configuration:  configuration,
		envID:          fmt.Sprintf("%s-%s", envIDPrefix, envIDSuffix),
	}
}

func (b BBL) PredefinedEnvID() string {
	return b.envID
}

func (b BBL) Up(iaas IAAS, additionalArgs []string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"--debug",
		"up",
	}

	args = append(args, additionalArgs...)

	switch iaas {
	case AWSIAAS:
		args = append(args, []string{
			"--iaas", "aws",
			"--aws-access-key-id", b.configuration.AWSAccessKeyID,
			"--aws-secret-access-key", b.configuration.AWSSecretAccessKey,
			"--aws-region", b.configuration.AWSRegion,
		}...)

		if b.configuration.EnableTerraformFlag {
			args = append(args, "--terraform")
		}
	case GCPIAAS:
		args = append(args, []string{
			"--iaas", "gcp",
			"--gcp-service-account-key", b.configuration.GCPServiceAccountKeyPath,
			"--gcp-project-id", b.configuration.GCPProjectID,
			"--gcp-region", b.configuration.GCPRegion,
			"--gcp-zone", b.configuration.GCPZone,
		}...)
	default:
		panic(errors.New("invalid iaas"))
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) UpWithInvalidAWSCredentials() {
	args := []string{
		"--state-dir", b.stateDirectory,
		"up",
		"--iaas", "aws",
		"--aws-access-key-id", "some-bad-access-key-id",
		"--aws-secret-access-key", "some-bad-secret-access-key",
		"--aws-region", b.configuration.AWSRegion,
	}
	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Second).Should(gexec.Exit(1))
}

func (b BBL) Destroy() {
	session := b.execute([]string{
		"--state-dir", b.stateDirectory,
		"destroy",
		"--no-confirm",
	}, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) SaveDirectorCA() string {
	stdout := bytes.NewBuffer([]byte{})
	session := b.execute([]string{
		"--state-dir", b.stateDirectory,
		"director-ca-cert",
	}, stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

	file, err := ioutil.TempFile("", "")
	defer file.Close()
	Expect(err).NotTo(HaveOccurred())

	file.Write(stdout.Bytes())

	return file.Name()
}

func (b BBL) DirectorUsername() string {
	return b.fetchValue("director-username")
}

func (b BBL) DirectorPassword() string {
	return b.fetchValue("director-password")
}

func (b BBL) DirectorAddress() string {
	return b.fetchValue("director-address")
}

func (b BBL) LBs() string {
	return b.fetchValue("lbs")
}

func (b BBL) DirectorCACert() string {
	return b.fetchValue("director-ca-cert")
}

func (b BBL) SSHKey() string {
	return b.fetchValue("ssh-key")
}

func (b BBL) EnvID() string {
	return b.fetchValue("env-id")
}

func (b BBL) CreateLB(loadBalancerType string, cert string, key string, chain string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"create-lbs",
		"--type", loadBalancerType,
	}

	if loadBalancerType == "cf" || GetIAAS(b.configuration) == AWSIAAS {
		args = append(args,
			"--cert", cert,
			"--key", key,
			"--chain", chain,
		)
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) CreateGCPLB(loadBalancerType string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"create-lbs",
		"--type", loadBalancerType,
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) LBs() *gexec.Session {
	args := []string{
		"--state-dir", b.stateDirectory,
		"lbs",
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

	return session
}

func (b BBL) UpdateLB(certPath, keyPath string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"update-lbs",
		"--cert", certPath,
		"--key", keyPath,
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) DeleteLBs() {
	args := []string{
		"--state-dir", b.stateDirectory,
		"delete-lbs",
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) fetchValue(value string) string {
	args := []string{
		"--state-dir", b.stateDirectory,
		value,
	}

	stdout := bytes.NewBuffer([]byte{})
	stderr := bytes.NewBuffer([]byte{})
	b.execute(args, stdout, stderr).Wait()

	return strings.TrimSpace(string(stdout.Bytes()))
}

func (b BBL) execute(args []string, stdout io.Writer, stderr io.Writer) *gexec.Session {
	cmd := exec.Command(b.pathToBBL, args...)
	session, err := gexec.Start(cmd, stdout, stderr)
	Expect(err).NotTo(HaveOccurred())

	return session
}

func LBURL(config integration.Config, bbl BBL, state integration.State) (string, error) {
	lbs := bbl.LBs()
	cutLBsPrefix := strings.Split(lbs, "[")[1]
	url := strings.Split(cutLBsPrefix, "]")[0]

	return fmt.Sprintf("https://%s", url), nil
}

func IAASString(config integration.Config) string {
	if config.AWSAccessKeyID != "" && config.AWSSecretAccessKey != "" && config.AWSRegion != "" {
		return "aws"
	}
	if config.GCPServiceAccountKeyPath != "" && config.GCPProjectID != "" && config.GCPRegion != "" && config.GCPZone != "" {
		return "gcp"
	}

	return ""
}

func GetIAAS(config integration.Config) IAAS {
	if config.AWSAccessKeyID != "" && config.AWSSecretAccessKey != "" && config.AWSRegion != "" {
		return AWSIAAS
	}
	if config.GCPServiceAccountKeyPath != "" && config.GCPProjectID != "" && config.GCPRegion != "" && config.GCPZone != "" {
		return GCPIAAS
	}

	return -1
}
<commit_msg>Use correct function to get lbs<commit_after>package actors

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/cloudfoundry/bosh-bootloader/integration-test"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

const (
	AWSIAAS = iota
	GCPIAAS
)

type BBL struct {
	stateDirectory string
	pathToBBL      string
	configuration  integration.Config
	envID          string
}

type IAAS int

func NewBBL(stateDirectory string, pathToBBL string, configuration integration.Config, envIDSuffix string) BBL {
	envIDPrefix := os.Getenv("BBL_TEST_ENV_ID_PREFIX")
	if envIDPrefix == "" {
		envIDPrefix = "bbl-test"
	}

	return BBL{
		stateDirectory: stateDirectory,
		pathToBBL:      pathToBBL,
		configuration:  configuration,
		envID:          fmt.Sprintf("%s-%s", envIDPrefix, envIDSuffix),
	}
}

func (b BBL) PredefinedEnvID() string {
	return b.envID
}

func (b BBL) Up(iaas IAAS, additionalArgs []string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"--debug",
		"up",
	}

	args = append(args, additionalArgs...)

	switch iaas {
	case AWSIAAS:
		args = append(args, []string{
			"--iaas", "aws",
			"--aws-access-key-id", b.configuration.AWSAccessKeyID,
			"--aws-secret-access-key", b.configuration.AWSSecretAccessKey,
			"--aws-region", b.configuration.AWSRegion,
		}...)

		if b.configuration.EnableTerraformFlag {
			args = append(args, "--terraform")
		}
	case GCPIAAS:
		args = append(args, []string{
			"--iaas", "gcp",
			"--gcp-service-account-key", b.configuration.GCPServiceAccountKeyPath,
			"--gcp-project-id", b.configuration.GCPProjectID,
			"--gcp-region", b.configuration.GCPRegion,
			"--gcp-zone", b.configuration.GCPZone,
		}...)
	default:
		panic(errors.New("invalid iaas"))
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 40*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) UpWithInvalidAWSCredentials() {
	args := []string{
		"--state-dir", b.stateDirectory,
		"up",
		"--iaas", "aws",
		"--aws-access-key-id", "some-bad-access-key-id",
		"--aws-secret-access-key", "some-bad-secret-access-key",
		"--aws-region", b.configuration.AWSRegion,
	}
	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Second).Should(gexec.Exit(1))
}

func (b BBL) Destroy() {
	session := b.execute([]string{
		"--state-dir", b.stateDirectory,
		"destroy",
		"--no-confirm",
	}, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) SaveDirectorCA() string {
	stdout := bytes.NewBuffer([]byte{})
	session := b.execute([]string{
		"--state-dir", b.stateDirectory,
		"director-ca-cert",
	}, stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

	file, err := ioutil.TempFile("", "")
	defer file.Close()
	Expect(err).NotTo(HaveOccurred())

	file.Write(stdout.Bytes())

	return file.Name()
}

func (b BBL) DirectorUsername() string {
	return b.fetchValue("director-username")
}

func (b BBL) DirectorPassword() string {
	return b.fetchValue("director-password")
}

func (b BBL) DirectorAddress() string {
	return b.fetchValue("director-address")
}

func (b BBL) DirectorCACert() string {
	return b.fetchValue("director-ca-cert")
}

func (b BBL) SSHKey() string {
	return b.fetchValue("ssh-key")
}

func (b BBL) EnvID() string {
	return b.fetchValue("env-id")
}

func (b BBL) CreateLB(loadBalancerType string, cert string, key string, chain string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"create-lbs",
		"--type", loadBalancerType,
	}

	if loadBalancerType == "cf" || GetIAAS(b.configuration) == AWSIAAS {
		args = append(args,
			"--cert", cert,
			"--key", key,
			"--chain", chain,
		)
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) CreateGCPLB(loadBalancerType string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"create-lbs",
		"--type", loadBalancerType,
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) LBs() *gexec.Session {
	args := []string{
		"--state-dir", b.stateDirectory,
		"lbs",
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))

	return session
}

func (b BBL) UpdateLB(certPath, keyPath string) {
	args := []string{
		"--state-dir", b.stateDirectory,
		"update-lbs",
		"--cert", certPath,
		"--key", keyPath,
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) DeleteLBs() {
	args := []string{
		"--state-dir", b.stateDirectory,
		"delete-lbs",
	}

	session := b.execute(args, os.Stdout, os.Stderr)
	Eventually(session, 10*time.Minute).Should(gexec.Exit(0))
}

func (b BBL) fetchValue(value string) string {
	args := []string{
		"--state-dir", b.stateDirectory,
		value,
	}

	stdout := bytes.NewBuffer([]byte{})
	stderr := bytes.NewBuffer([]byte{})
	b.execute(args, stdout, stderr).Wait()

	return strings.TrimSpace(string(stdout.Bytes()))
}

func (b BBL) execute(args []string, stdout io.Writer, stderr io.Writer) *gexec.Session {
	cmd := exec.Command(b.pathToBBL, args...)
	session, err := gexec.Start(cmd, stdout, stderr)
	Expect(err).NotTo(HaveOccurred())

	return session
}

func LBURL(config integration.Config, bbl BBL, state integration.State) (string, error) {
	lbs := bbl.fetchValue("lbs")
	cutLBsPrefix := strings.Split(lbs, "[")[1]
	url := strings.Split(cutLBsPrefix, "]")[0]

	return fmt.Sprintf("https://%s", url), nil
}

func IAASString(config integration.Config) string {
	if config.AWSAccessKeyID != "" && config.AWSSecretAccessKey != "" && config.AWSRegion != "" {
		return "aws"
	}
	if config.GCPServiceAccountKeyPath != "" && config.GCPProjectID != "" && config.GCPRegion != "" && config.GCPZone != "" {
		return "gcp"
	}

	return ""
}

func GetIAAS(config integration.Config) IAAS {
	if config.AWSAccessKeyID != "" && config.AWSSecretAccessKey != "" && config.AWSRegion != "" {
		return AWSIAAS
	}
	if config.GCPServiceAccountKeyPath != "" && config.GCPProjectID != "" && config.GCPRegion != "" && config.GCPZone != "" {
		return GCPIAAS
	}

	return -1
}
<|endoftext|>


<commit_before>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package catmsg

import (
	"fmt"
	"testing"
)

func TestEncodeUint(t *testing.T) {
	testCases := []struct {
		x   uint64
		enc string
	}{
		{0, "\x00"},
		{1, "\x01"},
		{2, "\x02"},
		{0x7f, "\x7f"},
		{0x80, "\x80\x01"},
		{1 << 14, "\x80\x80\x01"},
		{0xffffffff, "\xff\xff\xff\xff\x0f"},
		{0xffffffffffffffff, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"},
	}
	for _, tc := range testCases {
		buf := [maxVarintBytes]byte{}
		got := string(buf[:encodeUint(buf[:], tc.x)])
		if got != tc.enc {
			t.Errorf("EncodeUint(%#x) = %q; want %q", tc.x, got, tc.enc)
		}
	}
}

func TestDecodeUint(t *testing.T) {
	testCases := []struct {
		x    uint64
		size int
		enc  string
		err  error
	}{{
		x:    0,
		size: 0,
		enc:  "",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 1,
		enc:  "\x80",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 3,
		enc:  "\x80\x80\x80",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 1,
		enc:  "\x00",
	}, {
		x:    1,
		size: 1,
		enc:  "\x01",
	}, {
		x:    2,
		size: 1,
		enc:  "\x02",
	}, {
		x:    0x7f,
		size: 1,
		enc:  "\x7f",
	}, {
		x:    0x80,
		size: 2,
		enc:  "\x80\x01",
	}, {
		x:    1 << 14,
		size: 3,
		enc:  "\x80\x80\x01",
	}, {
		x:    0xffffffff,
		size: 5,
		enc:  "\xff\xff\xff\xff\x0f",
	}, {
		x:    0xffffffffffffffff,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01",
	}, {
		x:    0xffffffffffffffff,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00",
	}, {
		x:    0,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01",
		err:  errVarintTooLarge,
	}}
	forms := []struct {
		name   string
		decode func(s string) (x uint64, size int, err error)
	}{
		{"decode", func(s string) (x uint64, size int, err error) {
			return decodeUint([]byte(s))
		}},
		{"decodeString", decodeUintString},
	}
	for _, f := range forms {
		for _, tc := range testCases {
			t.Run(fmt.Sprintf("%s:%q", f.name, tc.enc), func(t *testing.T) {
				x, size, err := f.decode(tc.enc)
				if err != tc.err {
					t.Error("err = %q; want %q", err, tc.err)
				}
				if size != tc.size {
					t.Errorf("size = %d; want %d", size, tc.size)
				}
				if x != tc.x {
					t.Errorf("decode = %#x; want %#x", x, tc.x)
				}
			})
		}
	}
}
<commit_msg>internal: use t.Errorf in test output with formatting directives<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package catmsg

import (
	"fmt"
	"testing"
)

func TestEncodeUint(t *testing.T) {
	testCases := []struct {
		x   uint64
		enc string
	}{
		{0, "\x00"},
		{1, "\x01"},
		{2, "\x02"},
		{0x7f, "\x7f"},
		{0x80, "\x80\x01"},
		{1 << 14, "\x80\x80\x01"},
		{0xffffffff, "\xff\xff\xff\xff\x0f"},
		{0xffffffffffffffff, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01"},
	}
	for _, tc := range testCases {
		buf := [maxVarintBytes]byte{}
		got := string(buf[:encodeUint(buf[:], tc.x)])
		if got != tc.enc {
			t.Errorf("EncodeUint(%#x) = %q; want %q", tc.x, got, tc.enc)
		}
	}
}

func TestDecodeUint(t *testing.T) {
	testCases := []struct {
		x    uint64
		size int
		enc  string
		err  error
	}{{
		x:    0,
		size: 0,
		enc:  "",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 1,
		enc:  "\x80",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 3,
		enc:  "\x80\x80\x80",
		err:  errIllegalVarint,
	}, {
		x:    0,
		size: 1,
		enc:  "\x00",
	}, {
		x:    1,
		size: 1,
		enc:  "\x01",
	}, {
		x:    2,
		size: 1,
		enc:  "\x02",
	}, {
		x:    0x7f,
		size: 1,
		enc:  "\x7f",
	}, {
		x:    0x80,
		size: 2,
		enc:  "\x80\x01",
	}, {
		x:    1 << 14,
		size: 3,
		enc:  "\x80\x80\x01",
	}, {
		x:    0xffffffff,
		size: 5,
		enc:  "\xff\xff\xff\xff\x0f",
	}, {
		x:    0xffffffffffffffff,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01",
	}, {
		x:    0xffffffffffffffff,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x00",
	}, {
		x:    0,
		size: 10,
		enc:  "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01",
		err:  errVarintTooLarge,
	}}
	forms := []struct {
		name   string
		decode func(s string) (x uint64, size int, err error)
	}{
		{"decode", func(s string) (x uint64, size int, err error) {
			return decodeUint([]byte(s))
		}},
		{"decodeString", decodeUintString},
	}
	for _, f := range forms {
		for _, tc := range testCases {
			t.Run(fmt.Sprintf("%s:%q", f.name, tc.enc), func(t *testing.T) {
				x, size, err := f.decode(tc.enc)
				if err != tc.err {
					t.Errorf("err = %q; want %q", err, tc.err)
				}
				if size != tc.size {
					t.Errorf("size = %d; want %d", size, tc.size)
				}
				if x != tc.x {
					t.Errorf("decode = %#x; want %#x", x, tc.x)
				}
			})
		}
	}
}
<|endoftext|>


<commit_before>package human

import (
	"fmt"
	"net"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/hashicorp/go-version"
	"github.com/scaleway/scaleway-cli/internal/terminal"
	"github.com/scaleway/scaleway-sdk-go/scw"
)

type MarshalerFunc func(interface{}, *MarshalOpt) (string, error)

// marshalerFuncs is the register of all marshal func bindings
var marshalerFuncs sync.Map

func init() {
	marshalerFuncs.Store(reflect.TypeOf(int(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(int32(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(int64(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(uint32(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(uint64(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(string("")), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(bool(false)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(time.Time{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		return humanize.Time(i.(time.Time)), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.Size(0)), func(i interface{}, opt *MarshalOpt) (string, error) {
		size := uint64(i.(scw.Size))

		if isIECNotation := size%1024 == 0 && size%1000 != 0; isIECNotation {
			return humanize.IBytes(size), nil
		}

		return humanize.Bytes(size), nil
	})
	marshalerFuncs.Store(reflect.TypeOf([]scw.Size{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		sizes := i.([]scw.Size)
		strs := []string(nil)
		for _, size := range sizes {
			s, err := Marshal(size, nil)
			if err != nil {
				return "", err
			}
			strs = append(strs, s)
		}
		return strings.Join(strs, ", "), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(net.IP{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		return fmt.Sprintf("%v", i.(net.IP)), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.IPNet{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(scw.IPNet)
		return v.String(), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(version.Version{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(version.Version)
		return v.String(), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.Duration{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(scw.Duration)
		const (
			minutes = int64(60)
			hours   = 60 * minutes
			days    = 24 * hours
		)
		d := v.Seconds / days
		h := (v.Seconds - d*days) / hours
		m := (v.Seconds - (d*days + h*hours)) / minutes
		s := v.Seconds % 60
		res := []string(nil)
		if d != 0 {
			res = append(res, fmt.Sprintf("%d days", d))
		}
		if h != 0 {
			res = append(res, fmt.Sprintf("%d hours", h))
		}
		if m != 0 {
			res = append(res, fmt.Sprintf("%d minutes", m))
		}
		if s != 0 {
			res = append(res, fmt.Sprintf("%d seconds", s))
		}
		if v.Nanos != 0 {
			res = append(res, fmt.Sprintf("%d nanoseconds", v.Nanos))
		}
		if len(res) == 0 {
			return "0 seconds", nil
		}
		return strings.Join(res, " "), nil
	})
}

// TODO: implement the same logic as args.RegisterMarshalFunc(), where i must be a pointer
// RegisterMarshalerFunc bind the given type of i with the given MarshalerFunc
func RegisterMarshalerFunc(i interface{}, f MarshalerFunc) {
	marshalerFuncs.Store(reflect.TypeOf(i), f)
}

func getMarshalerFunc(key reflect.Type) (MarshalerFunc, bool) {
	value, _ := marshalerFuncs.Load(key)
	if f, ok := value.(func(interface{}, *MarshalOpt) (string, error)); ok {
		return MarshalerFunc(f), true
	}
	if mf, ok := value.(MarshalerFunc); ok {
		return mf, true
	}
	return nil, false
}

// DefaultMarshalerFunc is used by default for all non-registered type
func defaultMarshalerFunc(i interface{}, opt *MarshalOpt) (string, error) {
	if i == nil {
		i = "-"
	}

	switch v := i.(type) {
	case string:
		if v == "" {
			i = "-"
		}
	}

	return fmt.Sprint(i), nil
}

// isMarshalable checks if a type is Marshalable based on one of the following conditions:
// - type is not a struct, nor a map, nor a pointer
// - a marshal func was registered for this type
// - the type implements the Marshaler, error, or Stringer interface
// - pointer of the type matches one of the above conditions
func isMarshalable(t reflect.Type) bool {
	_, hasMarshalerFunc := getMarshalerFunc(t)

	return (t.Kind() != reflect.Struct && t.Kind() != reflect.Map && t.Kind() != reflect.Ptr) ||
		hasMarshalerFunc ||
		t.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) ||
		t.Implements(reflect.TypeOf((*error)(nil)).Elem()) ||
		t.Implements(reflect.TypeOf((*fmt.Stringer)(nil)).Elem()) ||
		(t.Kind() == reflect.Ptr && isMarshalable(t.Elem()))
}

// EnumMarshalSpec contains specs used by EnumMarshalFunc.
type EnumMarshalSpec struct {
	// Attribute (mainly colors) to use.
	Attribute color.Attribute

	// Value is the value that will be printed for the given value.
	Value string
}

type EnumMarshalSpecs map[interface{}]*EnumMarshalSpec

// EnumMarshalFunc returns a marshal func to marshal an enum.
func EnumMarshalFunc(specs EnumMarshalSpecs) MarshalerFunc {
	return func(i interface{}, opt *MarshalOpt) (s string, e error) {
		value, _ := defaultMarshalerFunc(i, opt)
		spec, exist := specs[i]
		if exist {
			if spec.Value != "" {
				value = spec.Value
			}
			value = terminal.Style(value, spec.Attribute)
		}
		return value, nil
	}
}
<commit_msg>core: add coloring for boolean values (#1252)<commit_after>package human

import (
	"fmt"
	"net"
	"reflect"
	"strings"
	"sync"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/fatih/color"
	"github.com/hashicorp/go-version"
	"github.com/scaleway/scaleway-cli/internal/terminal"
	"github.com/scaleway/scaleway-sdk-go/scw"
)

type MarshalerFunc func(interface{}, *MarshalOpt) (string, error)

// marshalerFuncs is the register of all marshal func bindings
var marshalerFuncs sync.Map

func init() {
	marshalerFuncs.Store(reflect.TypeOf(int(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(int32(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(int64(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(uint32(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(uint64(0)), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(string("")), defaultMarshalerFunc)
	marshalerFuncs.Store(reflect.TypeOf(bool(false)), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(bool)
		if v {
			return terminal.Style("true", color.FgGreen), nil
		}
		return terminal.Style("false", color.FgRed), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(time.Time{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		return humanize.Time(i.(time.Time)), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.Size(0)), func(i interface{}, opt *MarshalOpt) (string, error) {
		size := uint64(i.(scw.Size))

		if isIECNotation := size%1024 == 0 && size%1000 != 0; isIECNotation {
			return humanize.IBytes(size), nil
		}

		return humanize.Bytes(size), nil
	})
	marshalerFuncs.Store(reflect.TypeOf([]scw.Size{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		sizes := i.([]scw.Size)
		strs := []string(nil)
		for _, size := range sizes {
			s, err := Marshal(size, nil)
			if err != nil {
				return "", err
			}
			strs = append(strs, s)
		}
		return strings.Join(strs, ", "), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(net.IP{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		return fmt.Sprintf("%v", i.(net.IP)), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.IPNet{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(scw.IPNet)
		return v.String(), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(version.Version{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(version.Version)
		return v.String(), nil
	})
	marshalerFuncs.Store(reflect.TypeOf(scw.Duration{}), func(i interface{}, opt *MarshalOpt) (string, error) {
		v := i.(scw.Duration)
		const (
			minutes = int64(60)
			hours   = 60 * minutes
			days    = 24 * hours
		)
		d := v.Seconds / days
		h := (v.Seconds - d*days) / hours
		m := (v.Seconds - (d*days + h*hours)) / minutes
		s := v.Seconds % 60
		res := []string(nil)
		if d != 0 {
			res = append(res, fmt.Sprintf("%d days", d))
		}
		if h != 0 {
			res = append(res, fmt.Sprintf("%d hours", h))
		}
		if m != 0 {
			res = append(res, fmt.Sprintf("%d minutes", m))
		}
		if s != 0 {
			res = append(res, fmt.Sprintf("%d seconds", s))
		}
		if v.Nanos != 0 {
			res = append(res, fmt.Sprintf("%d nanoseconds", v.Nanos))
		}
		if len(res) == 0 {
			return "0 seconds", nil
		}
		return strings.Join(res, " "), nil
	})
}

// TODO: implement the same logic as args.RegisterMarshalFunc(), where i must be a pointer
// RegisterMarshalerFunc bind the given type of i with the given MarshalerFunc
func RegisterMarshalerFunc(i interface{}, f MarshalerFunc) {
	marshalerFuncs.Store(reflect.TypeOf(i), f)
}

func getMarshalerFunc(key reflect.Type) (MarshalerFunc, bool) {
	value, _ := marshalerFuncs.Load(key)
	if f, ok := value.(func(interface{}, *MarshalOpt) (string, error)); ok {
		return MarshalerFunc(f), true
	}
	if mf, ok := value.(MarshalerFunc); ok {
		return mf, true
	}
	return nil, false
}

// DefaultMarshalerFunc is used by default for all non-registered type
func defaultMarshalerFunc(i interface{}, opt *MarshalOpt) (string, error) {
	if i == nil {
		i = "-"
	}

	switch v := i.(type) {
	case string:
		if v == "" {
			i = "-"
		}
	}

	return fmt.Sprint(i), nil
}

// isMarshalable checks if a type is Marshalable based on one of the following conditions:
// - type is not a struct, nor a map, nor a pointer
// - a marshal func was registered for this type
// - the type implements the Marshaler, error, or Stringer interface
// - pointer of the type matches one of the above conditions
func isMarshalable(t reflect.Type) bool {
	_, hasMarshalerFunc := getMarshalerFunc(t)

	return (t.Kind() != reflect.Struct && t.Kind() != reflect.Map && t.Kind() != reflect.Ptr) ||
		hasMarshalerFunc ||
		t.Implements(reflect.TypeOf((*Marshaler)(nil)).Elem()) ||
		t.Implements(reflect.TypeOf((*error)(nil)).Elem()) ||
		t.Implements(reflect.TypeOf((*fmt.Stringer)(nil)).Elem()) ||
		(t.Kind() == reflect.Ptr && isMarshalable(t.Elem()))
}

// EnumMarshalSpec contains specs used by EnumMarshalFunc.
type EnumMarshalSpec struct {
	// Attribute (mainly colors) to use.
	Attribute color.Attribute

	// Value is the value that will be printed for the given value.
	Value string
}

type EnumMarshalSpecs map[interface{}]*EnumMarshalSpec

// EnumMarshalFunc returns a marshal func to marshal an enum.
func EnumMarshalFunc(specs EnumMarshalSpecs) MarshalerFunc {
	return func(i interface{}, opt *MarshalOpt) (s string, e error) {
		value, _ := defaultMarshalerFunc(i, opt)
		spec, exist := specs[i]
		if exist {
			if spec.Value != "" {
				value = spec.Value
			}
			value = terminal.Style(value, spec.Attribute)
		}
		return value, nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cache

import (
	"context"
	"io/ioutil"
	"os"

	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/trace"
)

// ioLimit limits the number of parallel file reads per process.
var ioLimit = make(chan struct{}, 128)

// nativeFileSystem implements FileSystem reading from the normal os file system.
type nativeFileSystem struct{}

// nativeFileHandle implements FileHandle for nativeFileSystem
type nativeFileHandle struct {
	fs       *nativeFileSystem
	identity source.FileIdentity
}

func (fs *nativeFileSystem) GetFile(uri span.URI) source.FileHandle {
	identifier := "DOES NOT EXIST"
	if fi, err := os.Stat(uri.Filename()); err == nil {
		identifier = fi.ModTime().String()
	}
	kind := source.DetectLanguage("", uri.Filename())
	return &nativeFileHandle{
		fs: fs,
		identity: source.FileIdentity{
			URI:        uri,
			Identifier: identifier,
			Kind:       kind,
		},
	}
}

func (h *nativeFileHandle) FileSystem() source.FileSystem {
	return h.fs
}

func (h *nativeFileHandle) Identity() source.FileIdentity {
	return h.identity
}

func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) {
	ctx, done := trace.StartSpan(ctx, "cache.nativeFileHandle.Read", telemetry.File.Of(h.identity.URI.Filename()))
	_ = ctx
	defer done()

	ioLimit <- struct{}{}
	defer func() { <-ioLimit }()
	// TODO: this should fail if the version is not the same as the handle
	data, err := ioutil.ReadFile(h.identity.URI.Filename())
	if err != nil {
		return nil, "", err
	}
	return data, hashContents(data), nil
}
<commit_msg>internal/lsp: check that a file handle is unmodified before read<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cache

import (
	"context"
	"io/ioutil"
	"os"

	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/lsp/telemetry"
	"golang.org/x/tools/internal/span"
	"golang.org/x/tools/internal/telemetry/trace"
	errors "golang.org/x/xerrors"
)

// ioLimit limits the number of parallel file reads per process.
var ioLimit = make(chan struct{}, 128)

// nativeFileSystem implements FileSystem reading from the normal os file system.
type nativeFileSystem struct{}

// nativeFileHandle implements FileHandle for nativeFileSystem
type nativeFileHandle struct {
	fs       *nativeFileSystem
	identity source.FileIdentity
}

func (fs *nativeFileSystem) GetFile(uri span.URI) source.FileHandle {
	return &nativeFileHandle{
		fs: fs,
		identity: source.FileIdentity{
			URI:        uri,
			Identifier: identifier(uri.Filename()),
			Kind:       source.DetectLanguage("", uri.Filename()),
		},
	}
}

func (h *nativeFileHandle) FileSystem() source.FileSystem {
	return h.fs
}

func (h *nativeFileHandle) Identity() source.FileIdentity {
	return h.identity
}

func (h *nativeFileHandle) Read(ctx context.Context) ([]byte, string, error) {
	ctx, done := trace.StartSpan(ctx, "cache.nativeFileHandle.Read", telemetry.File.Of(h.identity.URI.Filename()))
	_ = ctx
	defer done()

	ioLimit <- struct{}{}
	defer func() { <-ioLimit }()

	if id := identifier(h.identity.URI.Filename()); id != h.identity.Identifier {
		return nil, "", errors.Errorf("%s: file has been modified", h.identity.URI.Filename())
	}
	data, err := ioutil.ReadFile(h.identity.URI.Filename())
	if err != nil {
		return nil, "", err
	}
	return data, hashContents(data), nil
}

func identifier(filename string) string {
	if fi, err := os.Stat(filename); err == nil {
		return fi.ModTime().String()
	}
	return "DOES NOT EXIST"
}
<|endoftext|>


<commit_before>package protocol

import (
	"context"
	"testing"
)

func TestPingHTTP(t *testing.T) {
	type args struct {
		addr string
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "http://baidu.com",
			args: args{
				addr: "http://baidu.com",
			},
			wantErr: false,
		},
		{
			name: "https://baidu.com",
			args: args{
				addr: "https://baidu.com",
			},
			wantErr: false,
		},
		{
			name: "https://not.exist.domain.com",
			args: args{
				addr: "https://not.exist.domain.com",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := PingHTTP(context.Background(), tt.args.addr); (err != nil) != tt.wantErr {
				t.Errorf("PingHTTP() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}
<commit_msg>fix: http test<commit_after>package protocol

import (
	"context"
	"testing"
)

func TestPingHTTP(t *testing.T) {
	type args struct {
		addr string
	}
	tests := []struct {
		name    string
		args    args
		wantErr bool
	}{
		{
			name: "https://baidu.com",
			args: args{
				addr: "https://baidu.com",
			},
			wantErr: false,
		},
		{
			name: "https://not.exist.domain.com",
			args: args{
				addr: "https://not.exist.domain.com",
			},
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if err := PingHTTP(context.Background(), tt.args.addr); (err != nil) != tt.wantErr {
				t.Errorf("PingHTTP() error = %v, wantErr %v", err, tt.wantErr)
			}
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 Google Inc.

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

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

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

// Package memcache provides a client for the memcached cache server.
package memcache

import (
	"context"
	"errors"
	"sort"
	"strconv"
	"strings"

	"net"
	"os/exec"

	"testing"
	"time"
)

const (
	testPollingTime          = 2 * time.Millisecond
	portLow                  = 49152
	portHigh                 = 65535
	memcachedCreationTimeout = 5 * time.Second

	teardownTimeout                = 100 * time.Millisecond
	discoveryStabilizationDuration = 10 * time.Millisecond
	memcachedStabilizationDuration = 20 * time.Millisecond
)

var fakeDiscoveryServer = fakeDiscoveryMemcacheServer{}
var ready = make(chan bool)
var exit = make(chan bool)

// Run the memcached binary as a child process and connect to its unix socket.
func TestDiscoveryUnixSocket(t *testing.T) {
	ctx := context.Background()
	defer ctx.Done()
	fakeDiscoveryServer.start(ctx)
	t.Cleanup(teardown)
	openPorts := findOpenLocalHostPort(portLow, portHigh, 2)
	if openPorts == nil || len(openPorts) < 2 {
		t.Fatalf("could not find two open ports, openPorts:%v", openPorts)
		return
	}

	go startMemcachedServer(t, openPorts[0], ready, exit)
	go startMemcachedServer(t, openPorts[1], ready, exit)
	s1Ready, waitErr := waitOnChannelWithTimeout(ready, memcachedCreationTimeout)
	if waitErr != nil {
		t.Fatalf("memcache server could not be created due to %v", waitErr)
	}
	s2Ready, waitErr := waitOnChannelWithTimeout(ready, memcachedCreationTimeout)
	if waitErr != nil {
		t.Fatalf("memcache server could not be created due to %v", waitErr)
	}
	if !s1Ready || !s2Ready {
		t.Skipf("one of the memcached server was not ready.")
	}

	fakeDiscoveryServer.updateDiscoveryInformation(1, openPorts)
	discoveryClient, err := newDiscoveryClient(fakeDiscoveryServer.currentAddress, testPollingTime)
	if err != nil {
		t.Fatalf("could not create discovery client due to %v", err)
	}
	testWithDiscoveryClient(t, &fakeDiscoveryServer, discoveryClient)
	discoveryClient.stopPolling()
}

func waitOnChannelWithTimeout(c chan bool, timeout time.Duration) (bool, error) {
	select {
	case res := <-c:
		return res, nil
	case <-time.After(timeout):
		return false, errors.New("channel timed out")
	}
}

func teardown() {
	fakeDiscoveryServer.stop()
	close(exit)
	time.Sleep(teardownTimeout)
}

func getCurrentServerPorts(c *Client) []int {
	serverAddress := make([]int, 0, 3)
	recordAllServers := func(a net.Addr) error {
		_, portStr, err := net.SplitHostPort(a.String())
		if err != nil {
			return err
		}
		port, err := strconv.ParseInt(portStr, 10, 0)
		if err != nil {
			return err
		}
		serverAddress = append(serverAddress, int(port))
		return nil
	}
	c.selector.Each(recordAllServers)
	sort.Ints(serverAddress)
	return serverAddress
}

// Equal tells whether a and b contain the same elements.
func equalIntArray(slice1, slice2 []int) bool {
	if len(slice1) != len(slice2) {
		return false
	}
	for i, value := range slice1 {
		if value != slice2[i] {
			return false
		}
	}
	return true
}

func testSingleConfigCase(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client, discoveryID int, portsToSet, expectedPorts []int) {
	fakeMemcacheServer.updateDiscoveryInformation(discoveryID, portsToSet)
	time.Sleep(discoveryStabilizationDuration)
	discoveryPorts := getCurrentServerPorts(c)
	if !equalIntArray(expectedPorts, discoveryPorts) {
		t.Fatalf("configId:%v want: %v != got %v", discoveryID, expectedPorts, discoveryPorts)
	}
}

func testSingleInvalidConfigCase(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client, discoveryResponse string, expectedPorts []int) {
	fakeMemcacheServer.updateDiscoveryResponse(discoveryResponse)
	time.Sleep(discoveryStabilizationDuration)
	discoveryPorts := getCurrentServerPorts(c)
	if !equalIntArray(expectedPorts, discoveryPorts) {
		t.Fatalf("discoveryResponse:%v want: %v != got %v", discoveryResponse, expectedPorts, discoveryPorts)
	}
}

func testValidConfigChange(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	originalPortList := getCurrentServerPorts(c)

	// Greater config id should update discovery information
	newPorts := []int{1, 2, 3}
	testSingleConfigCase(t, fakeMemcacheServer, c, 3, newPorts, newPorts)

	// Update to original configuration
	testSingleConfigCase(t, fakeMemcacheServer, c, 20, originalPortList, originalPortList)

	// Same config id should not change the config
	testSingleConfigCase(t, fakeMemcacheServer, c, 20, newPorts, originalPortList)

	// Older config id should
	testSingleConfigCase(t, fakeMemcacheServer, c, 19, newPorts, originalPortList)

	// Not found case with config id 0
	testSingleConfigCase(t, fakeMemcacheServer, c, 0, newPorts, originalPortList)
}

func testInvalidConfigChange(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	originalPortList := getCurrentServerPorts(c)

	// Completely broken response
	testSingleInvalidConfigCase(t, fakeMemcacheServer, c, "broken", originalPortList)

	// Partially broken response with intparse error
	var result strings.Builder
	result.WriteString("CONFIG cluster 0 80\r\n")
	result.WriteString("100\r\n")
	result.WriteString("localhost|localhost|brokenInt")
	result.WriteString("\n\r\n")
	testSingleInvalidConfigCase(t, fakeMemcacheServer, c, result.String(), originalPortList)
}

func testWithDiscoveryClient(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	// Run discovery config tests
	if fakeMemcacheServer != nil {
		testValidConfigChange(t, fakeMemcacheServer, c)
		testInvalidConfigChange(t, fakeMemcacheServer, c)
	}

	// reuse the other test library
	testWithClient(t, c)
}

func startMemcachedServer(t *testing.T, port int, ready chan<- bool, exit <-chan bool) error {
	t.Logf("starting memcached server on port: %d", port)
	cmd := exec.Command("memcached", "-p", strconv.Itoa(port))
	t.Logf("starting memcached server with command : %v", cmd)
	if err := cmd.Start(); err != nil {
		ready <- false
		return errors.New("could not find memcached server")
	}
	t.Logf("started memcached server on port:%d", port)
	// Allow the server to come up
	time.Sleep(memcachedStabilizationDuration)
	ready <- true

	<-exit
	cmd.Process.Kill()
	time.Sleep(memcachedStabilizationDuration)
	t.Logf("memcached server on port:%d exited", port)
	return nil
}

func findOpenLocalHostPort(portLow, portHigh, openPortsToFind int) []int {
	timeout := 10 * time.Millisecond
	openPorts := make([]int, 0, 2)
	for i := portLow; i < portHigh; i++ {
		addressToTry := net.JoinHostPort("localhost", strconv.Itoa(i))
		conn, err := net.DialTimeout("tcp", addressToTry, timeout)
		// if connection is refused, it could be a free port
		if err != nil && strings.Contains(err.Error(), "connection refused") {
			// try opening a tcp connection and if it succeeds this is a good port
			l, err1 := net.Listen("tcp", addressToTry)
			if err1 == nil {
				openPorts = append(openPorts, i)
				l.Close()
				if len(openPorts) == 2 {
					break
				}
			}
		} else if conn != nil {
			conn.Close()
		}
	}
	return openPorts
}
<commit_msg>Fix StopPolling() in memcache_discovery_test.go<commit_after>/*
Copyright 2020 Google Inc.

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

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

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

// Package memcache provides a client for the memcached cache server.
package memcache

import (
	"context"
	"errors"
	"sort"
	"strconv"
	"strings"

	"net"
	"os/exec"

	"testing"
	"time"
)

const (
	testPollingTime          = 2 * time.Millisecond
	portLow                  = 49152
	portHigh                 = 65535
	memcachedCreationTimeout = 5 * time.Second

	teardownTimeout                = 100 * time.Millisecond
	discoveryStabilizationDuration = 10 * time.Millisecond
	memcachedStabilizationDuration = 20 * time.Millisecond
)

var fakeDiscoveryServer = fakeDiscoveryMemcacheServer{}
var ready = make(chan bool)
var exit = make(chan bool)

// Run the memcached binary as a child process and connect to its unix socket.
func TestDiscoveryUnixSocket(t *testing.T) {
	ctx := context.Background()
	defer ctx.Done()
	fakeDiscoveryServer.start(ctx)
	t.Cleanup(teardown)
	openPorts := findOpenLocalHostPort(portLow, portHigh, 2)
	if openPorts == nil || len(openPorts) < 2 {
		t.Fatalf("could not find two open ports, openPorts:%v", openPorts)
		return
	}

	go startMemcachedServer(t, openPorts[0], ready, exit)
	go startMemcachedServer(t, openPorts[1], ready, exit)
	s1Ready, waitErr := waitOnChannelWithTimeout(ready, memcachedCreationTimeout)
	if waitErr != nil {
		t.Fatalf("memcache server could not be created due to %v", waitErr)
	}
	s2Ready, waitErr := waitOnChannelWithTimeout(ready, memcachedCreationTimeout)
	if waitErr != nil {
		t.Fatalf("memcache server could not be created due to %v", waitErr)
	}
	if !s1Ready || !s2Ready {
		t.Skipf("one of the memcached server was not ready.")
	}

	fakeDiscoveryServer.updateDiscoveryInformation(1, openPorts)
	discoveryClient, err := newDiscoveryClient(fakeDiscoveryServer.currentAddress, testPollingTime)
	if err != nil {
		t.Fatalf("could not create discovery client due to %v", err)
	}
	testWithDiscoveryClient(t, &fakeDiscoveryServer, discoveryClient)
	discoveryClient.StopPolling()
}

func waitOnChannelWithTimeout(c chan bool, timeout time.Duration) (bool, error) {
	select {
	case res := <-c:
		return res, nil
	case <-time.After(timeout):
		return false, errors.New("channel timed out")
	}
}

func teardown() {
	fakeDiscoveryServer.stop()
	close(exit)
	time.Sleep(teardownTimeout)
}

func getCurrentServerPorts(c *Client) []int {
	serverAddress := make([]int, 0, 3)
	recordAllServers := func(a net.Addr) error {
		_, portStr, err := net.SplitHostPort(a.String())
		if err != nil {
			return err
		}
		port, err := strconv.ParseInt(portStr, 10, 0)
		if err != nil {
			return err
		}
		serverAddress = append(serverAddress, int(port))
		return nil
	}
	c.selector.Each(recordAllServers)
	sort.Ints(serverAddress)
	return serverAddress
}

// Equal tells whether a and b contain the same elements.
func equalIntArray(slice1, slice2 []int) bool {
	if len(slice1) != len(slice2) {
		return false
	}
	for i, value := range slice1 {
		if value != slice2[i] {
			return false
		}
	}
	return true
}

func testSingleConfigCase(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client, discoveryID int, portsToSet, expectedPorts []int) {
	fakeMemcacheServer.updateDiscoveryInformation(discoveryID, portsToSet)
	time.Sleep(discoveryStabilizationDuration)
	discoveryPorts := getCurrentServerPorts(c)
	if !equalIntArray(expectedPorts, discoveryPorts) {
		t.Fatalf("configId:%v want: %v != got %v", discoveryID, expectedPorts, discoveryPorts)
	}
}

func testSingleInvalidConfigCase(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client, discoveryResponse string, expectedPorts []int) {
	fakeMemcacheServer.updateDiscoveryResponse(discoveryResponse)
	time.Sleep(discoveryStabilizationDuration)
	discoveryPorts := getCurrentServerPorts(c)
	if !equalIntArray(expectedPorts, discoveryPorts) {
		t.Fatalf("discoveryResponse:%v want: %v != got %v", discoveryResponse, expectedPorts, discoveryPorts)
	}
}

func testValidConfigChange(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	originalPortList := getCurrentServerPorts(c)

	// Greater config id should update discovery information
	newPorts := []int{1, 2, 3}
	testSingleConfigCase(t, fakeMemcacheServer, c, 3, newPorts, newPorts)

	// Update to original configuration
	testSingleConfigCase(t, fakeMemcacheServer, c, 20, originalPortList, originalPortList)

	// Same config id should not change the config
	testSingleConfigCase(t, fakeMemcacheServer, c, 20, newPorts, originalPortList)

	// Older config id should
	testSingleConfigCase(t, fakeMemcacheServer, c, 19, newPorts, originalPortList)

	// Not found case with config id 0
	testSingleConfigCase(t, fakeMemcacheServer, c, 0, newPorts, originalPortList)
}

func testInvalidConfigChange(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	originalPortList := getCurrentServerPorts(c)

	// Completely broken response
	testSingleInvalidConfigCase(t, fakeMemcacheServer, c, "broken", originalPortList)

	// Partially broken response with intparse error
	var result strings.Builder
	result.WriteString("CONFIG cluster 0 80\r\n")
	result.WriteString("100\r\n")
	result.WriteString("localhost|localhost|brokenInt")
	result.WriteString("\n\r\n")
	testSingleInvalidConfigCase(t, fakeMemcacheServer, c, result.String(), originalPortList)
}

func testWithDiscoveryClient(t *testing.T, fakeMemcacheServer *fakeDiscoveryMemcacheServer, c *Client) {
	// Run discovery config tests
	if fakeMemcacheServer != nil {
		testValidConfigChange(t, fakeMemcacheServer, c)
		testInvalidConfigChange(t, fakeMemcacheServer, c)
	}

	// reuse the other test library
	testWithClient(t, c)
}

func startMemcachedServer(t *testing.T, port int, ready chan<- bool, exit <-chan bool) error {
	t.Logf("starting memcached server on port: %d", port)
	cmd := exec.Command("memcached", "-p", strconv.Itoa(port))
	t.Logf("starting memcached server with command : %v", cmd)
	if err := cmd.Start(); err != nil {
		ready <- false
		return errors.New("could not find memcached server")
	}
	t.Logf("started memcached server on port:%d", port)
	// Allow the server to come up
	time.Sleep(memcachedStabilizationDuration)
	ready <- true

	<-exit
	cmd.Process.Kill()
	time.Sleep(memcachedStabilizationDuration)
	t.Logf("memcached server on port:%d exited", port)
	return nil
}

func findOpenLocalHostPort(portLow, portHigh, openPortsToFind int) []int {
	timeout := 10 * time.Millisecond
	openPorts := make([]int, 0, 2)
	for i := portLow; i < portHigh; i++ {
		addressToTry := net.JoinHostPort("localhost", strconv.Itoa(i))
		conn, err := net.DialTimeout("tcp", addressToTry, timeout)
		// if connection is refused, it could be a free port
		if err != nil && strings.Contains(err.Error(), "connection refused") {
			// try opening a tcp connection and if it succeeds this is a good port
			l, err1 := net.Listen("tcp", addressToTry)
			if err1 == nil {
				openPorts = append(openPorts, i)
				l.Close()
				if len(openPorts) == 2 {
					break
				}
			}
		} else if conn != nil {
			conn.Close()
		}
	}
	return openPorts
}
<|endoftext|>


<commit_before>package molecule

import (
	"fmt"

	bits "github.com/willf/bitset"

	cmn "github.com/RxnWeaver/rxnweaver/common"
)

// Ring represents a simple cycle in a molecule.
//
// A ring holds information of the atoms and the bonds it comprises.
// It also knows its neighbouring rings.
//
// Rings are supposed to be immutable: once completed, their
// composition should never change.
//
// The atom IDs held by rings are their input IDs, to match those held
// by bonds.  That makes using atoms and bonds together easier, when
// doing ring detection, etc.
type Ring struct {
	mol  *Molecule // Containing molecule of this ring.
	id   uint8     // A unique identifier for this ring.
	rsId uint8     // ID of the ring system to which this ring belongs.

	atoms []uint16 // List of atoms participating in this ring.
	bonds []uint16 // List of bonds participating in this ring.
	nbrs  []uint8  // List of rings neighbouring this ring.

	atomBitSet *bits.BitSet // For faster comparison.
	bondBitSet *bits.BitSet // For faster comparison.

	isAro    bool // Is this ring aromatic?
	isHetAro bool // Is this an aromatic ring with at least one hetero atom?

	isComplete bool // Has this ring been finalised?
}

// newRing creates and initialises a new ring.
func newRing(mol *Molecule, id uint8) {
	r := new(Ring)
	r.mol = mol
	r.id = id

	r.atoms = make([]uint16, 0, cmn.ListSizeSmall)
	r.bonds = make([]uint16, 0, cmn.ListSizeSmall)
	r.nbrs = make([]uint8, 0, cmn.ListSizeSmall)

	r.atomBitSet = bits.New(cmn.ListSizeSmall)
	r.bondBitSet = bits.New(cmn.ListSizeSmall)
}

// size answers the size of this ring.  It is equivalently the number
// of atoms or the number of bonds participating in this ring.
func (r *Ring) size() int {
	return len(r.atoms)
}

// hasAtom answers if this ring includes the given atom.
func (r *Ring) hasAtom(aid uint16) bool {
	return r.atomBitSet.Test(uint(aid))
}

// atomIndex answers the index of the given atom in this ring, if it
// is found.  Answers `-1` otherwise.
//
// Note that the answer may or may not be idempotent, depending on the
// normalisation status of the ring.  Should the ring be normalised
// between two invocations of this method, the answers could vary.
func (r *Ring) atomIndex(aid uint16) int {
	if !r.hasAtom(aid) {
		return -1
	}

	for i, id := range r.atoms {
		if id == aid {
			return i
		}
	}

	panic("Should never be here!")
}

// hasBond answers if this ring includes the given bond.
func (r *Ring) hasBond(bid uint16) bool {
	return r.bondBitSet.Test(uint(bid))
}

// addAtom adds the given atom to this ring.
//
// This method errors if the ring is already completed.
//
// It checks to see that a bond exists between the most-recently-added
// atom, if one such exists, and the given atom.  Answers a non-nil
// error otherwise.
//
// This method is idempotent: the given atom is ignored if it is
// already a member of this ring.
func (r *Ring) addAtom(aid uint16) error {
	if r.isComplete {
		return fmt.Errorf("Ring already complete.  ID : %d.", r.id)
	}

	if r.hasAtom(aid) {
		return nil
	}

	size := len(r.atoms)
	if size == 0 {
		r.atoms = append(r.atoms, aid)
		return nil
	}

	prev := r.atoms[size-1]
	b := r.mol.bondBetween(prev, aid)
	if b == nil {
		return fmt.Errorf("No bond exists between atom %d and atom %d.", prev, aid)
	}

	r.bonds = append(r.bonds, b.id)
	r.atoms = append(r.atoms, aid)
	return nil
}

// complete closes the link between the last atom in the ring and the
// first.  This operation effectively freezes the ring.
//
// This method is idempotent.
func (r *Ring) complete() error {
	if r.isComplete {
		return nil
	}

	size := len(r.atoms)
	if size < 3 {
		return fmt.Errorf("A ring must have at least 3 atoms.  This ring has only %d.", size)
	}

	aid1 := r.atoms[0]
	aid2 := r.atoms[size-1]
	b := r.mol.bondBetween(aid1, aid2)
	if b == nil {
		return fmt.Errorf("No bond between first atom %d and last atom %d.", aid1, aid2)
	}

	r.bonds = append(r.bonds, b.id)

	for _, aid := range r.atoms {
		r.atomBitSet.Set(uint(aid))
	}
	for _, bid := range r.bonds {
		r.bondBitSet.Set(uint(bid))
	}

	r.isComplete = true
	return nil
}

// isAromatic answers if this ring is aromatic.
//
// The actual aromaticity determination happens when
// `determineAromaticity` is called.  This method merely answers the
// set flag.
func (r *Ring) isAromatic() bool {
	return r.isAro
}

// isHeteroAromatic answers if this ring is aromatic with at least one
// hetero atom.
//
// The actual aromaticity determination happens when
// `determineAromaticity` is called.  This method merely answers the
// set flag.
func (r *Ring) isHeteroAromatic() bool {
	return r.isHetAro
}
<commit_msg>Add a method for ring normalisation<commit_after>package molecule

import (
	"fmt"
	"math"

	bits "github.com/willf/bitset"

	cmn "github.com/RxnWeaver/rxnweaver/common"
)

// Ring represents a simple cycle in a molecule.
//
// A ring holds information of the atoms and the bonds it comprises.
// It also knows its neighbouring rings.
//
// Rings are supposed to be immutable: once completed, their
// composition should never change.
//
// The atom IDs held by rings are their input IDs, to match those held
// by bonds.  That makes using atoms and bonds together easier, when
// doing ring detection, etc.
type Ring struct {
	mol  *Molecule // Containing molecule of this ring.
	id   uint8     // A unique identifier for this ring.
	rsId uint8     // ID of the ring system to which this ring belongs.

	atoms []uint16 // List of atoms participating in this ring.
	bonds []uint16 // List of bonds participating in this ring.
	nbrs  []uint8  // List of rings neighbouring this ring.

	atomBitSet *bits.BitSet // For faster comparison.
	bondBitSet *bits.BitSet // For faster comparison.

	isAro    bool // Is this ring aromatic?
	isHetAro bool // Is this an aromatic ring with at least one hetero atom?

	isComplete bool // Has this ring been finalised?
}

// newRing creates and initialises a new ring.
func newRing(mol *Molecule, id uint8) {
	r := new(Ring)
	r.mol = mol
	r.id = id

	r.atoms = make([]uint16, 0, cmn.ListSizeSmall)
	r.bonds = make([]uint16, 0, cmn.ListSizeSmall)
	r.nbrs = make([]uint8, 0, cmn.ListSizeSmall)

	r.atomBitSet = bits.New(cmn.ListSizeSmall)
	r.bondBitSet = bits.New(cmn.ListSizeSmall)
}

// size answers the size of this ring.  It is equivalently the number
// of atoms or the number of bonds participating in this ring.
func (r *Ring) size() int {
	return len(r.atoms)
}

// hasAtom answers if this ring includes the given atom.
func (r *Ring) hasAtom(aid uint16) bool {
	return r.atomBitSet.Test(uint(aid))
}

// atomIndex answers the index of the given atom in this ring, if it
// is found.  Answers `-1` otherwise.
//
// Note that the answer may or may not be idempotent, depending on the
// normalisation status of the ring.  Should the ring be normalised
// between two invocations of this method, the answers could vary.
func (r *Ring) atomIndex(aid uint16) int {
	if !r.hasAtom(aid) {
		return -1
	}

	for i, id := range r.atoms {
		if id == aid {
			return i
		}
	}

	panic("Should never be here!")
}

// hasBond answers if this ring includes the given bond.
func (r *Ring) hasBond(bid uint16) bool {
	return r.bondBitSet.Test(uint(bid))
}

// addAtom adds the given atom to this ring.
//
// This method errors if the ring is already completed.
//
// It checks to see that a bond exists between the most-recently-added
// atom, if one such exists, and the given atom.  Answers a non-nil
// error otherwise.
//
// This method is idempotent: the given atom is ignored if it is
// already a member of this ring.
func (r *Ring) addAtom(aid uint16) error {
	if r.isComplete {
		return fmt.Errorf("Ring already complete.  ID : %d.", r.id)
	}

	if r.hasAtom(aid) {
		return nil
	}

	size := len(r.atoms)
	if size == 0 {
		r.atoms = append(r.atoms, aid)
		return nil
	}

	prev := r.atoms[size-1]
	b := r.mol.bondBetween(prev, aid)
	if b == nil {
		return fmt.Errorf("No bond exists between atom %d and atom %d.", prev, aid)
	}

	r.bonds = append(r.bonds, b.id)
	r.atoms = append(r.atoms, aid)
	return nil
}

// complete closes the link between the last atom in the ring and the
// first.  This operation effectively freezes the ring.
//
// This method is idempotent.
func (r *Ring) complete() error {
	if r.isComplete {
		return nil
	}

	size := len(r.atoms)
	if size < 3 {
		return fmt.Errorf("A ring must have at least 3 atoms.  This ring has only %d.", size)
	}

	aid1 := r.atoms[0]
	aid2 := r.atoms[size-1]
	b := r.mol.bondBetween(aid1, aid2)
	if b == nil {
		return fmt.Errorf("No bond between first atom %d and last atom %d.", aid1, aid2)
	}

	r.bonds = append(r.bonds, b.id)

	for _, aid := range r.atoms {
		r.atomBitSet.Set(uint(aid))
	}
	for _, bid := range r.bonds {
		r.bondBitSet.Set(uint(bid))
	}

	r.isComplete = true
	return nil
}

// isAromatic answers if this ring is aromatic.
//
// The actual aromaticity determination happens when
// `determineAromaticity` is called.  This method merely answers the
// set flag.
func (r *Ring) isAromatic() bool {
	return r.isAro
}

// isHeteroAromatic answers if this ring is aromatic with at least one
// hetero atom.
//
// The actual aromaticity determination happens when
// `determineAromaticity` is called.  This method merely answers the
// set flag.
func (r *Ring) isHeteroAromatic() bool {
	return r.isHetAro
}

// normalise transforms the ring into a `standard' representation, in
// which the ring logically begins with that atom which has the lowest
// normalised ID.
func (r *Ring) normalise() error {
	l := len(r.atoms)
	if l == 0 {
		return fmt.Errorf("Cannot normalise an empty ring!")
	}

	nids := make([]uint16, l, l)

	mol := r.mol
	for i, aiid := range r.atoms {
		a := mol.atomWithIid(aiid)
		nids[i] = a.nId
	}

	min := uint16(math.MaxUint16)
	idx := -1
	for i, nid := range nids {
		if nid < min {
			idx = i
			min = nid
		}
	}

	// Rotate the ring so that the atom at `idx` becomes the first.
	r.atoms = append(r.atoms[idx:], r.atoms[:idx]...)
	return nil
}
<|endoftext|>


<commit_before>package iterkernel_test

import (
	"errors"
	"fmt"

	"github.com/reiver/go-iter/kernel"
)

var (
	errInternalError = errors.New("Internal Error")
	errNilReceiver   = errors.New("Nil Receiver")
)

type MyStruct struct {
	Apple  string
	Banana int
	Cherry float64
}

type MyStructIterator struct {
	kernel iterkernel.Kernel
	Slice []MyStruct
}

func (receiver *MyStructIterator) Close() error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelClose()
}

func (receiver *MyStructIterator) Decode(x interface{}) error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelDecode(receiver.decode, x)
}

func (receiver *MyStructIterator) decode(x interface{}) (bool, error) {
	if nil == receiver {
		return false, errNilReceiver
	}

	if nil == x {
		return false, nil
	}

	datum, err := receiver.kernel.KernelDatum()
	if nil != err {
		return false, err
	}

	strt, ok := datum.(MyStruct)
	if !ok {
		return false, errInternalError
	}

	switch p := x.(type) {
	case *MyStruct:
		if nil == p {
			return true, nil
		}

		*p = strt

		return true, nil
	default:
		return false, nil
	}
}

func (receiver *MyStructIterator) Err() error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelErr()
}

func (receiver *MyStructIterator) Next() bool {
	if nil == receiver {
		return false
	}

	return receiver.kernel.KernelNext(receiver.next)
}

func (receiver *MyStructIterator) next(index int, v interface{}) (bool, error) {
	if nil == receiver {
		return false, errNilReceiver
	}

	slice := receiver.Slice
	if nil == slice  {
		return false, nil
	}

	if len(slice) <= index {
		return false, nil
	}

	datum := slice[index]

	switch t := v.(type) {
	case *interface{}:
                *t = datum
	default:
		return false, fmt.Errorf("Bad Type: %T", t)
	}

	return true, nil
}

func ExampleSlice() {

	slice := []MyStruct{
		MyStruct{
			Apple: "ONE",
			Banana: 1,
			Cherry: 1.1,
		},
		MyStruct{
			Apple: "TWO",
			Banana: 2,
			Cherry: 2.2,
		},
		MyStruct{
			Apple: "THREE",
			Banana: 3,
			Cherry: 3.3,
		},
	}

	iterator := MyStructIterator{Slice:slice}

	for iterator.Next() {
		var datum MyStruct

		if err := iterator.Decode(&datum); nil != err {
			fmt.Printf("ERROR: (%T) %v \n", err, err)
			return
		}

		fmt.Printf("%v\n", datum)
	}
	if err := iterator.Err(); nil != err {
		fmt.Printf("ERROR: (%T) %v \n", err, err)
		return
	}

	// Output:
	//  {ONE 1 1.1}
	// {TWO 2 2.2}
	// {THREE 3 3.3}
}

<commit_msg>added more docs<commit_after>package iterkernel_test

import (
	"errors"
	"fmt"

	"github.com/reiver/go-iter/kernel"
)

var (
	errInternalError = errors.New("Internal Error")
	errNilReceiver   = errors.New("Nil Receiver")
)

type MyStruct struct {
	Apple  string
	Banana int
	Cherry float64
}

type MyStructIterator struct {
	kernel iterkernel.Kernel
	Slice []MyStruct
}

func (receiver *MyStructIterator) Close() error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelClose()
}

func (receiver *MyStructIterator) Decode(x interface{}) error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelDecode(receiver.decode, x)
}

func (receiver *MyStructIterator) decode(x interface{}) (bool, error) {
	if nil == receiver {
		return false, errNilReceiver
	}

	if nil == x {
		return false, nil
	}

	datum, err := receiver.kernel.KernelDatum()
	if nil != err {
		return false, err
	}

	strt, ok := datum.(MyStruct)
	if !ok {
		return false, errInternalError
	}

	switch p := x.(type) {
	case *MyStruct:
		if nil == p {
			return true, nil
		}

		*p = strt

		return true, nil
	default:
		return false, nil
	}
}

func (receiver *MyStructIterator) Err() error {
	if nil == receiver {
		return errNilReceiver
	}

	return receiver.kernel.KernelErr()
}

func (receiver *MyStructIterator) Next() bool {
	if nil == receiver {
		return false
	}

	return receiver.kernel.KernelNext(receiver.next)
}

func (receiver *MyStructIterator) next(index int, v interface{}) (bool, error) {
	if nil == receiver {
		return false, errNilReceiver
	}

	slice := receiver.Slice
	if nil == slice  {
		return false, nil
	}

	if len(slice) <= index {
		return false, nil
	}

	datum := slice[index]

	switch t := v.(type) {
	case *interface{}:
                *t = datum
	default:
		return false, fmt.Errorf("Bad Type: %T", t)
	}

	return true, nil
}

func ExampleKernel() {

	slice := []MyStruct{
		MyStruct{
			Apple: "ONE",
			Banana: 1,
			Cherry: 1.1,
		},
		MyStruct{
			Apple: "TWO",
			Banana: 2,
			Cherry: 2.2,
		},
		MyStruct{
			Apple: "THREE",
			Banana: 3,
			Cherry: 3.3,
		},
	}

	iterator := MyStructIterator{Slice:slice}

	for iterator.Next() {
		var datum MyStruct

		if err := iterator.Decode(&datum); nil != err {
			fmt.Printf("ERROR: (%T) %v \n", err, err)
			return
		}

		fmt.Printf("%v\n", datum)
	}
	if err := iterator.Err(); nil != err {
		fmt.Printf("ERROR: (%T) %v \n", err, err)
		return
	}

	// Output:
	//  {ONE 1 1.1}
	// {TWO 2 2.2}
	// {THREE 3 3.3}
}

<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
)

func main() {
	var version string
	var whatsNewUrl string
	var releaseNotesUrl string
	var dryRun bool
	var enterprise bool
	var fromLocal bool
	var nightly bool
	var apiKey string

	flag.StringVar(&version, "version", "", "Grafana version (ex: --version v5.2.0-beta1)")
	flag.StringVar(&whatsNewUrl, "wn", "", "What's new url (ex: --wn http://docs.grafana.org/guides/whats-new-in-v5-2/)")
	flag.StringVar(&releaseNotesUrl, "rn", "", "Grafana version (ex: --rn https://community.grafana.com/t/release-notes-v5-2-x/7894)")
	flag.StringVar(&apiKey, "apikey", "", "Grafana.com API key (ex: --apikey ABCDEF)")
	flag.BoolVar(&dryRun, "dry-run", false, "--dry-run")
	flag.BoolVar(&enterprise, "enterprise", false, "--enterprise")
	flag.BoolVar(&fromLocal, "from-local", false, "--from-local (builds will be tagged as nightly)")
	flag.Parse()

	nightly = fromLocal

	if len(os.Args) == 1 {
		fmt.Println("Usage: go run publisher.go main.go --version <v> --wn <what's new url> --rn <release notes url> --apikey <api key> --dry-run false --enterprise false --nightly false")
		fmt.Println("example: go run publisher.go main.go --version v5.2.0-beta2 --wn http://docs.grafana.org/guides/whats-new-in-v5-2/ --rn https://community.grafana.com/t/release-notes-v5-2-x/7894 --apikey ASDF123 --dry-run --enterprise")
		os.Exit(1)
	}

	if dryRun {
		log.Println("Dry-run has been enabled.")
	}
	var baseUrl string
	var builder releaseBuilder
	var product string

	archiveProviderRoot := "https://s3-us-west-2.amazonaws.com"
	buildArtifacts := completeBuildArtifactConfigurations

	if enterprise {
		product = "grafana-enterprise"
		baseUrl = createBaseUrl(archiveProviderRoot, "grafana-enterprise-releases", product, nightly)
		var err error
		buildArtifacts, err = filterBuildArtifacts([]artifactFilter{
			{os: "deb", arch: "amd64"},
			{os: "rhel", arch: "amd64"},
			{os: "linux", arch: "amd64"},
			{os: "win", arch: "amd64"},
		})

		if err != nil {
			log.Fatalf("Could not filter to the selected build artifacts, err=%v", err)
		}

	} else {
		product = "grafana"
		baseUrl = createBaseUrl(archiveProviderRoot, "grafana-releases", product, nightly)
	}

	if fromLocal {
		path, _ := os.Getwd()
		builder = releaseLocalSources{
			path:                   path,
			artifactConfigurations: buildArtifacts,
		}
	} else {
		builder = releaseFromExternalContent{
			getter:                 getHttpContents{},
			rawVersion:             version,
			artifactConfigurations: buildArtifacts,
		}
	}

	p := publisher{
		apiKey:         apiKey,
		apiUri:         "https://grafana.com/api",
		product:        product,
		dryRun:         dryRun,
		enterprise:     enterprise,
		baseArchiveUrl: baseUrl,
		builder:        builder,
	}
	if err := p.doRelease(whatsNewUrl, releaseNotesUrl, nightly); err != nil {
		log.Fatalf("error: %v", err)
	}
}
func createBaseUrl(root string, bucketName string, product string, nightly bool) string {
	var subPath string
	if nightly {
		subPath = "master"
	} else {
		subPath = "release"
	}

	return fmt.Sprintf("%s/%s/%s/%s", root, bucketName, subPath, product)
}
<commit_msg>build: packages linked to dl.grafana.com.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"
)

func main() {
	var version string
	var whatsNewUrl string
	var releaseNotesUrl string
	var dryRun bool
	var enterprise bool
	var fromLocal bool
	var nightly bool
	var apiKey string

	flag.StringVar(&version, "version", "", "Grafana version (ex: --version v5.2.0-beta1)")
	flag.StringVar(&whatsNewUrl, "wn", "", "What's new url (ex: --wn http://docs.grafana.org/guides/whats-new-in-v5-2/)")
	flag.StringVar(&releaseNotesUrl, "rn", "", "Grafana version (ex: --rn https://community.grafana.com/t/release-notes-v5-2-x/7894)")
	flag.StringVar(&apiKey, "apikey", "", "Grafana.com API key (ex: --apikey ABCDEF)")
	flag.BoolVar(&dryRun, "dry-run", false, "--dry-run")
	flag.BoolVar(&enterprise, "enterprise", false, "--enterprise")
	flag.BoolVar(&fromLocal, "from-local", false, "--from-local (builds will be tagged as nightly)")
	flag.Parse()

	nightly = fromLocal

	if len(os.Args) == 1 {
		fmt.Println("Usage: go run publisher.go main.go --version <v> --wn <what's new url> --rn <release notes url> --apikey <api key> --dry-run false --enterprise false --nightly false")
		fmt.Println("example: go run publisher.go main.go --version v5.2.0-beta2 --wn http://docs.grafana.org/guides/whats-new-in-v5-2/ --rn https://community.grafana.com/t/release-notes-v5-2-x/7894 --apikey ASDF123 --dry-run --enterprise")
		os.Exit(1)
	}

	if dryRun {
		log.Println("Dry-run has been enabled.")
	}
	var baseUrl string
	var builder releaseBuilder
	var product string

	archiveProviderRoot := "https://dl.grafana.com"
	buildArtifacts := completeBuildArtifactConfigurations

	if enterprise {
		product = "grafana-enterprise"
		baseUrl = createBaseUrl(archiveProviderRoot, "enterprise", product, nightly)
		var err error
		buildArtifacts, err = filterBuildArtifacts([]artifactFilter{
			{os: "deb", arch: "amd64"},
			{os: "rhel", arch: "amd64"},
			{os: "linux", arch: "amd64"},
			{os: "win", arch: "amd64"},
		})

		if err != nil {
			log.Fatalf("Could not filter to the selected build artifacts, err=%v", err)
		}

	} else {
		product = "grafana"
		baseUrl = createBaseUrl(archiveProviderRoot, "oss", product, nightly)
	}

	if fromLocal {
		path, _ := os.Getwd()
		builder = releaseLocalSources{
			path:                   path,
			artifactConfigurations: buildArtifacts,
		}
	} else {
		builder = releaseFromExternalContent{
			getter:                 getHttpContents{},
			rawVersion:             version,
			artifactConfigurations: buildArtifacts,
		}
	}

	p := publisher{
		apiKey:         apiKey,
		apiUri:         "https://grafana.com/api",
		product:        product,
		dryRun:         dryRun,
		enterprise:     enterprise,
		baseArchiveUrl: baseUrl,
		builder:        builder,
	}
	if err := p.doRelease(whatsNewUrl, releaseNotesUrl, nightly); err != nil {
		log.Fatalf("error: %v", err)
	}
}
func createBaseUrl(root string, bucketName string, product string, nightly bool) string {
	var subPath string
	if nightly {
		subPath = "master"
	} else {
		subPath = "release"
	}

	return fmt.Sprintf("%s/%s/%s/%s", root, bucketName, subPath, product)
}
<|endoftext|>


<commit_before>package distributor

import (
	"errors"
	"log"

	components "github.com/LoRaWanSoFa/LoRaWanSoFa/Components"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/ByteConverter"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/restUplinkConnector"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/DBC/DatabaseConnector"
)

type Distributor interface {
	InputUplink(components.MessageUplinkI) (components.MessageUplinkI, error)
	InputDownlink(components.MessageDownLink)
}

type distributor struct {
	byteConverter       byteConverter.ByteConverter
	restUplinkConnector restUplink.RestUplinkConnector
}

func New() Distributor {
	dist := new(distributor)
	dist.byteConverter = byteConverter.New()
	config := components.GetConfiguration().Rest
	dist.restUplinkConnector = restUplink.NewRestUplinkConnector(config.Ip, config.ApiKey)
	return dist
}

func (d *distributor) InputUplink(message components.MessageUplinkI) (components.MessageUplinkI, error) {
	if d.deduplicate(message) {
		newMessage := d.convertMessage(message)
		err := DatabaseConnector.StoreMessagePayloads(newMessage)
		if err != nil {
			log.Fatal(err)
		}
		d.restUplinkConnector.NewData(newMessage.GetDevEUI(), newMessage)
		return newMessage, nil
	} else {
		err := errors.New("message was a duplicate")
		return nil, err
	}
}

func (d *distributor) InputDownlink(message components.MessageDownLink) {

}

//The deduplicate method should deduplicate messages that come in once from the
//TTN side of things(semi-private) as well as our own private backend and return
// true only if the message has not been received yet.
func (d *distributor) deduplicate(message components.MessageUplinkI) bool {
	// TODO: deduplicate messages that could come in checking with the database
	// or createing a small cache for it.
	return true
}

func (d *distributor) convertMessage(message components.MessageUplinkI) components.MessageUplinkI {
	bytePayloads := message.GetPayloads()
	message.RemovePayloads()
	for i := range bytePayloads {
		payload, ok := bytePayloads[i].GetPayload().([]byte)
		if ok {
			sensor := bytePayloads[i].GetSensor()
			payloadS, err := d.byteConverter.ConvertSingleValue(payload, sensor.DataType)
			if err != nil {
				log.Fatal(err)
			} else {
				message.AddPayloadString(payloadS, sensor)
			}
		}
	}
	return message
}
<commit_msg>made log.Fatal a changeable variable for testing purposes<commit_after>package distributor

import (
	"errors"
	"log"

	components "github.com/LoRaWanSoFa/LoRaWanSoFa/Components"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/ByteConverter"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/Core/restUplinkConnector"
	"github.com/LoRaWanSoFa/LoRaWanSoFa/DBC/DatabaseConnector"
)

var logFatal = log.Fatal

type Distributor interface {
	InputUplink(components.MessageUplinkI) (components.MessageUplinkI, error)
	InputDownlink(components.MessageDownLink)
}

type distributor struct {
	byteConverter       byteConverter.ByteConverter
	restUplinkConnector restUplink.RestUplinkConnector
}

func New() Distributor {
	dist := new(distributor)
	dist.byteConverter = byteConverter.New()
	config := components.GetConfiguration().Rest
	dist.restUplinkConnector = restUplink.NewRestUplinkConnector(config.Ip, config.ApiKey)
	return dist
}

func (d *distributor) InputUplink(message components.MessageUplinkI) (components.MessageUplinkI, error) {
	if d.deduplicate(message) {
		newMessage := d.convertMessage(message)
		err := DatabaseConnector.StoreMessagePayloads(newMessage)
		if err != nil {
			logFatal(err)
		}
		d.restUplinkConnector.NewData(newMessage.GetDevEUI(), newMessage)
		return newMessage, nil
	} else {
		err := errors.New("message was a duplicate")
		return nil, err
	}
}

func (d *distributor) InputDownlink(message components.MessageDownLink) {

}

//The deduplicate method should deduplicate messages that come in once from the
//TTN side of things(semi-private) as well as our own private backend and return
// true only if the message has not been received yet.
func (d *distributor) deduplicate(message components.MessageUplinkI) bool {
	// TODO: deduplicate messages that could come in checking with the database
	// or createing a small cache for it.
	return true
}

func (d *distributor) convertMessage(message components.MessageUplinkI) components.MessageUplinkI {
	bytePayloads := message.GetPayloads()
	message.RemovePayloads()
	for i := range bytePayloads {
		payload, ok := bytePayloads[i].GetPayload().([]byte)
		if ok {
			sensor := bytePayloads[i].GetSensor()
			payloadS, err := d.byteConverter.ConvertSingleValue(payload, sensor.DataType)
			if err != nil {
				logFatal(err)
			} else {
				message.AddPayloadString(payloadS, sensor)
			}
		}
	}
	return message
}
<|endoftext|>