<commit_before>/*
Copyright 2016 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 common

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	utilversion "k8s.io/kubernetes/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

var hostIPVersion = utilversion.MustParseSemantic("v1.8.0")

var _ = framework.KubeDescribe("Downward API", func() {
	f := framework.NewDefaultFramework("downward-api")

	It("should provide pod name and namespace as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.name",
					},
				},
			},
			{
				Name: "POD_NAMESPACE",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.namespace",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_NAME=%v", podName),
			fmt.Sprintf("POD_NAMESPACE=%v", f.Namespace.Name),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide pod and host IP as an env var [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(hostIPVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.podIP",
					},
				},
			},
			{
				Name: "HOST_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.hostIP",
					},
				},
			},
		}

		expectations := []string{
			"POD_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
			"HOST_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide container's limits.cpu/memory and requests.cpu/memory as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			{
				Name: "CPU_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
					},
				},
			},
			{
				Name: "MEMORY_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=2"),
			fmt.Sprintf("MEMORY_LIMIT=67108864"),
			fmt.Sprintf("CPU_REQUEST=1"),
			fmt.Sprintf("MEMORY_REQUEST=33554432"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide default limits.cpu/memory from node allocatable [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=[1-9]"),
			fmt.Sprintf("MEMORY_LIMIT=[1-9]"),
		}
		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   podName,
				Labels: map[string]string{"name": podName},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "dapi-container",
						Image:   busyboxImage,
						Command: []string{"sh", "-c", "env"},
						Env:     env,
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		testDownwardAPIUsingPod(f, pod, env, expectations)
	})
})

func testDownwardAPI(f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    "dapi-container",
					Image:   busyboxImage,
					Command: []string{"sh", "-c", "env"},
					Resources: v1.ResourceRequirements{
						Requests: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("250m"),
							v1.ResourceMemory: resource.MustParse("32Mi"),
						},
						Limits: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("1250m"),
							v1.ResourceMemory: resource.MustParse("64Mi"),
						},
					},
					Env: env,
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	testDownwardAPIUsingPod(f, pod, env, expectations)
}

func testDownwardAPIUsingPod(f *framework.Framework, pod *v1.Pod, env []v1.EnvVar, expectations []string) {
	f.TestContainerOutputRegexp("downward api env vars", pod, 0, expectations)
}
<commit_msg>Split downward API e2e test case for pod/host IP into two<commit_after>/*
Copyright 2016 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 common

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	utilversion "k8s.io/kubernetes/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

var hostIPVersion = utilversion.MustParseSemantic("v1.8.0")

var _ = framework.KubeDescribe("Downward API", func() {
	f := framework.NewDefaultFramework("downward-api")

	It("should provide pod name and namespace as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.name",
					},
				},
			},
			{
				Name: "POD_NAMESPACE",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.namespace",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_NAME=%v", podName),
			fmt.Sprintf("POD_NAMESPACE=%v", f.Namespace.Name),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide pod IP as an env var [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.podIP",
					},
				},
			},
		}

		expectations := []string{
			"POD_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide host IP as an env var [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(hostIPVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "HOST_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.hostIP",
					},
				},
			},
		}

		expectations := []string{
			"HOST_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide container's limits.cpu/memory and requests.cpu/memory as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			{
				Name: "CPU_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
					},
				},
			},
			{
				Name: "MEMORY_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=2"),
			fmt.Sprintf("MEMORY_LIMIT=67108864"),
			fmt.Sprintf("CPU_REQUEST=1"),
			fmt.Sprintf("MEMORY_REQUEST=33554432"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide default limits.cpu/memory from node allocatable [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=[1-9]"),
			fmt.Sprintf("MEMORY_LIMIT=[1-9]"),
		}
		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   podName,
				Labels: map[string]string{"name": podName},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "dapi-container",
						Image:   busyboxImage,
						Command: []string{"sh", "-c", "env"},
						Env:     env,
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		testDownwardAPIUsingPod(f, pod, env, expectations)
	})
})

func testDownwardAPI(f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    "dapi-container",
					Image:   busyboxImage,
					Command: []string{"sh", "-c", "env"},
					Resources: v1.ResourceRequirements{
						Requests: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("250m"),
							v1.ResourceMemory: resource.MustParse("32Mi"),
						},
						Limits: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("1250m"),
							v1.ResourceMemory: resource.MustParse("64Mi"),
						},
					},
					Env: env,
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	testDownwardAPIUsingPod(f, pod, env, expectations)
}

func testDownwardAPIUsingPod(f *framework.Framework, pod *v1.Pod, env []v1.EnvVar, expectations []string) {
	f.TestContainerOutputRegexp("downward api env vars", pod, 0, expectations)
}
<|endoftext|>


<commit_before>package cli

import (
	"bufio"
	"compress/gzip"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	oauthv1 "github.com/openshift/api/oauth/v1"
	"github.com/openshift/client-go/image/clientset/versioned"
	oauthv1client "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
	"github.com/openshift/library-go/pkg/image/imageutil"
	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/ibmcloud"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = g.Describe("[sig-cli] oc adm must-gather", func() {
	defer g.GinkgoRecover()

	oc := exutil.NewCLI("oc-adm-must-gather").AsAdmin()

	g.JustBeforeEach(func() {
		// wait for the default service account to be avaiable
		err := exutil.WaitForServiceAccount(oc.KubeClient().CoreV1().ServiceAccounts(oc.Namespace()), "default")
		o.Expect(err).NotTo(o.HaveOccurred())
	})

	g.It("runs successfully", func() {
		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(tempDir)
		o.Expect(oc.Run("adm", "must-gather").Args("--dest-dir", tempDir).Execute()).To(o.Succeed())

		pluginOutputDir := getPluginOutputDir(oc, tempDir)

		expectedDirectories := [][]string{
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io"},
			{pluginOutputDir, "cluster-scoped-resources", "operator.openshift.io"},
			{pluginOutputDir, "cluster-scoped-resources", "core"},
			{pluginOutputDir, "cluster-scoped-resources", "apiregistration.k8s.io"},
			{pluginOutputDir, "namespaces", "openshift"},
			{pluginOutputDir, "namespaces", "openshift-kube-apiserver-operator"},
		}

		expectedFiles := [][]string{
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "apiservers.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "authentications.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "builds.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "clusteroperators.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "clusterversions.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "consoles.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "dnses.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "featuregates.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "images.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "infrastructures.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "ingresses.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "networks.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "oauths.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "projects.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "schedulers.yaml"},
			// TODO: This got broken and we need to fix this. Disabled temporarily.
			// {pluginOutputDir, "namespaces", "openshift-kube-apiserver", "core", "configmaps.yaml"},
			// {pluginOutputDir, "namespaces", "openshift-kube-apiserver", "core", "secrets.yaml"},
			{pluginOutputDir, "host_service_logs", "masters", "crio_service.log"},
			{pluginOutputDir, "host_service_logs", "masters", "kubelet_service.log"},
		}

		for _, expectedDirectory := range expectedDirectories {
			o.Expect(path.Join(expectedDirectory...)).To(o.BeADirectory())
		}

		emptyFiles := []string{}
		for _, expectedFile := range expectedFiles {
			expectedFilePath := path.Join(expectedFile...)
			o.Expect(expectedFilePath).To(o.BeAnExistingFile())
			stat, err := os.Stat(expectedFilePath)
			o.Expect(err).NotTo(o.HaveOccurred())
			if size := stat.Size(); size < 50 {
				emptyFiles = append(emptyFiles, expectedFilePath)
			}
		}
		if len(emptyFiles) > 0 {
			o.Expect(fmt.Errorf("expected files should not be empty: %s", strings.Join(emptyFiles, ","))).NotTo(o.HaveOccurred())
		}
	})

	g.It("runs successfully with options", func() {
		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(tempDir)
		args := []string{
			"--dest-dir", tempDir,
			"--source-dir", "/artifacts",
			"--",
			"/bin/bash", "-c",
			"ls -l > /artifacts/ls.log",
		}
		o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())
		expectedFilePath := path.Join(getPluginOutputDir(oc, tempDir), "ls.log")
		o.Expect(expectedFilePath).To(o.BeAnExistingFile())
		stat, err := os.Stat(expectedFilePath)
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(stat.Size()).To(o.BeNumerically(">", 0))
	})

	g.It("runs successfully for audit logs", func() {
		// On IBM ROKS, events will not be part of the output, since audit logs do not include control plane logs.
		if e2e.TestContext.Provider == ibmcloud.ProviderName {
			g.Skip("ROKs doesn't have audit logs")
		}

		upgradedFrom45 := false
		cv, err := oc.AdminConfigClient().ConfigV1().ClusterVersions().Get(context.TODO(), "version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		for _, history := range cv.Status.History {
			if strings.HasPrefix(history.Version, "4.5.") {
				upgradedFrom45 = true
			}
		}

		// makes some tokens that should not show in the audit logs
		_, sha256TokenName := exutil.GenerateOAuthTokenPair()
		oauthClient := oauthv1client.NewForConfigOrDie(oc.AdminConfig())
		_, err1 := oauthClient.OAuthAccessTokens().Create(context.Background(), &oauthv1.OAuthAccessToken{
			ObjectMeta: metav1.ObjectMeta{
				Name: sha256TokenName,
			},
			ClientName:  "openshift-challenging-client",
			ExpiresIn:   30,
			Scopes:      []string{"user:info"},
			RedirectURI: "https://127.0.0.1:12000/oauth/token/implicit",
			UserName:    "a",
			UserUID:     "1",
		}, metav1.CreateOptions{})
		o.Expect(err1).NotTo(o.HaveOccurred())
		_, err2 := oauthClient.OAuthAuthorizeTokens().Create(context.Background(), &oauthv1.OAuthAuthorizeToken{
			ObjectMeta: metav1.ObjectMeta{
				Name: sha256TokenName,
			},
			ClientName:  "openshift-challenging-client",
			ExpiresIn:   30,
			Scopes:      []string{"user:info"},
			RedirectURI: "https://127.0.0.1:12000/oauth/token/implicit",
			UserName:    "a",
			UserUID:     "1",
		}, metav1.CreateOptions{})
		o.Expect(err2).NotTo(o.HaveOccurred())

		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		// defer os.RemoveAll(tempDir)

		args := []string{
			"--dest-dir", tempDir,
			"--",
			"/usr/bin/gather_audit_logs",
		}

		o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())
		// wait for the contents to show up in the plugin output directory, avoiding EOF errors
		time.Sleep(10 * time.Second)

		pluginOutputDir := getPluginOutputDir(oc, tempDir)

		expectedDirectoriesToExpectedCount := map[string]int{
			path.Join(pluginOutputDir, "audit_logs", "kube-apiserver"):      1000,
			path.Join(pluginOutputDir, "audit_logs", "openshift-apiserver"): 10, // openshift apiservers don't necessarily get much traffic.  Especially early in a run
		}

		expectedFiles := [][]string{
			{pluginOutputDir, "audit_logs", "kube-apiserver.audit_logs_listing"},
			{pluginOutputDir, "audit_logs", "openshift-apiserver.audit_logs_listing"},
		}

		// for some crazy reason, it seems that the files from must-gather take time to appear on disk for reading.  I don't understand why
		// but this was in a previous commit and I don't want to immediately flake: https://github.com/openshift/origin/commit/006745a535848e84dcbcdd1c83ae86deddd3a229#diff-ad1c47fa4213de16d8b3237df5d71724R168
		// so we're going to try to get a pass every 10 seconds for a minute.  If we pass, great.  If we don't, we report the
		// last error we had.
		var lastErr error
		err = wait.PollImmediate(10*time.Second, 1*time.Minute, func() (bool, error) {
			// make sure we do not log OAuth tokens
			for auditDirectory, expectedNumberOfAuditEntries := range expectedDirectoriesToExpectedCount {
				eventsChecked := 0
				err := filepath.Walk(auditDirectory, func(path string, info os.FileInfo, err error) error {
					g.By(path)
					o.Expect(err).NotTo(o.HaveOccurred())
					if info.IsDir() {
						return nil
					}

					if (strings.Contains(path, "-termination-") && strings.HasSuffix(path, ".log.gz")) || strings.HasSuffix(path, "termination.log.gz") {
						// these are expected, but have unstructured log format
						return nil
					}

					isAuditFile := (strings.Contains(path, "-audit-") && strings.HasSuffix(path, ".log.gz")) || strings.HasSuffix(path, "audit.log.gz")
					o.Expect(isAuditFile).To(o.BeTrue())

					// at this point, we expect only audit files with json events, one per line

					readFile := false

					file, err := os.Open(path)
					o.Expect(err).NotTo(o.HaveOccurred())
					defer file.Close()

					fi, err := file.Stat()
					o.Expect(err).NotTo(o.HaveOccurred())

					// it will happen that the audit files are sometimes empty, we can
					// safely ignore these files since they don't provide valuable information
					// TODO this doesn't seem right.  It should be really unlikely, but we'll deal with later
					if fi.Size() == 0 {
						return nil
					}

					gzipReader, err := gzip.NewReader(file)
					o.Expect(err).NotTo(o.HaveOccurred())

					scanner := bufio.NewScanner(gzipReader)
					for scanner.Scan() {
						text := scanner.Text()
						if !strings.HasSuffix(text, "}") {
							continue // ignore truncated data
						}
						o.Expect(text).To(o.HavePrefix(`{"kind":"Event",`))

						// TODO(4.7+): remove this check
						// - if on 4.6 but not upgraded => disable check
						// - if on 4.6 and we have been upgraded from older release => keep the check.
						if upgradedFrom45 {
							for _, token := range []string{"oauthaccesstokens", "oauthauthorizetokens", sha256TokenName} {
								o.Expect(text).NotTo(o.ContainSubstring(token))
							}
						}
						readFile = true
						eventsChecked++
					}
					// ignore this error as we usually fail to read the whole GZ file
					// o.Expect(scanner.Err()).NotTo(o.HaveOccurred())
					o.Expect(readFile).To(o.BeTrue())

					return nil
				})
				o.Expect(err).NotTo(o.HaveOccurred())

				if eventsChecked <= expectedNumberOfAuditEntries {
					lastErr = fmt.Errorf("expected %d audit events for %q, but only got %d", expectedNumberOfAuditEntries, auditDirectory, eventsChecked)
					return false, nil
				}

				// reset lastErr if we succeeded.
				lastErr = nil
			}

			// if we get here, it means both directories checked out ok
			return true, nil
		})
		o.Expect(lastErr).NotTo(o.HaveOccurred()) // print the last error first if we have one
		o.Expect(err).NotTo(o.HaveOccurred())     // otherwise be sure we fail on the timeout if it happened

		emptyFiles := []string{}
		for _, expectedFile := range expectedFiles {
			expectedFilePath := path.Join(expectedFile...)
			o.Expect(expectedFilePath).To(o.BeAnExistingFile())
			stat, err := os.Stat(expectedFilePath)
			o.Expect(err).NotTo(o.HaveOccurred())
			if size := stat.Size(); size < 50 {
				emptyFiles = append(emptyFiles, expectedFilePath)
			}
		}
		if len(emptyFiles) > 0 {
			o.Expect(fmt.Errorf("expected files should not be empty: %s", strings.Join(emptyFiles, ","))).NotTo(o.HaveOccurred())
		}
	})
})

func getPluginOutputDir(oc *exutil.CLI, tempDir string) string {
	imageClient := versioned.NewForConfigOrDie(oc.AdminConfig())
	stream, err := imageClient.ImageV1().ImageStreams("openshift").Get(context.Background(), "must-gather", metav1.GetOptions{})
	o.Expect(err).NotTo(o.HaveOccurred())
	imageId, ok := imageutil.ResolveLatestTaggedImage(stream, "latest")
	o.Expect(ok).To(o.BeTrue())
	pluginOutputDir := path.Join(tempDir, regexp.MustCompile("[^A-Za-z0-9]+").ReplaceAllString(imageId, "-"))
	fileInfo, err := os.Stat(pluginOutputDir)
	if err != nil || !fileInfo.IsDir() {
		pluginOutputDir = tempDir
	}
	return pluginOutputDir
}
<commit_msg>must-gather: add oauth-apiserver to expected audit logs<commit_after>package cli

import (
	"bufio"
	"compress/gzip"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"
	oauthv1 "github.com/openshift/api/oauth/v1"
	"github.com/openshift/client-go/image/clientset/versioned"
	oauthv1client "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
	"github.com/openshift/library-go/pkg/image/imageutil"
	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/ibmcloud"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = g.Describe("[sig-cli] oc adm must-gather", func() {
	defer g.GinkgoRecover()

	oc := exutil.NewCLI("oc-adm-must-gather").AsAdmin()

	g.JustBeforeEach(func() {
		// wait for the default service account to be avaiable
		err := exutil.WaitForServiceAccount(oc.KubeClient().CoreV1().ServiceAccounts(oc.Namespace()), "default")
		o.Expect(err).NotTo(o.HaveOccurred())
	})

	g.It("runs successfully", func() {
		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(tempDir)
		o.Expect(oc.Run("adm", "must-gather").Args("--dest-dir", tempDir).Execute()).To(o.Succeed())

		pluginOutputDir := getPluginOutputDir(oc, tempDir)

		expectedDirectories := [][]string{
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io"},
			{pluginOutputDir, "cluster-scoped-resources", "operator.openshift.io"},
			{pluginOutputDir, "cluster-scoped-resources", "core"},
			{pluginOutputDir, "cluster-scoped-resources", "apiregistration.k8s.io"},
			{pluginOutputDir, "namespaces", "openshift"},
			{pluginOutputDir, "namespaces", "openshift-kube-apiserver-operator"},
		}

		expectedFiles := [][]string{
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "apiservers.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "authentications.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "builds.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "clusteroperators.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "clusterversions.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "consoles.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "dnses.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "featuregates.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "images.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "infrastructures.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "ingresses.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "networks.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "oauths.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "projects.yaml"},
			{pluginOutputDir, "cluster-scoped-resources", "config.openshift.io", "schedulers.yaml"},
			// TODO: This got broken and we need to fix this. Disabled temporarily.
			// {pluginOutputDir, "namespaces", "openshift-kube-apiserver", "core", "configmaps.yaml"},
			// {pluginOutputDir, "namespaces", "openshift-kube-apiserver", "core", "secrets.yaml"},
			{pluginOutputDir, "host_service_logs", "masters", "crio_service.log"},
			{pluginOutputDir, "host_service_logs", "masters", "kubelet_service.log"},
		}

		for _, expectedDirectory := range expectedDirectories {
			o.Expect(path.Join(expectedDirectory...)).To(o.BeADirectory())
		}

		emptyFiles := []string{}
		for _, expectedFile := range expectedFiles {
			expectedFilePath := path.Join(expectedFile...)
			o.Expect(expectedFilePath).To(o.BeAnExistingFile())
			stat, err := os.Stat(expectedFilePath)
			o.Expect(err).NotTo(o.HaveOccurred())
			if size := stat.Size(); size < 50 {
				emptyFiles = append(emptyFiles, expectedFilePath)
			}
		}
		if len(emptyFiles) > 0 {
			o.Expect(fmt.Errorf("expected files should not be empty: %s", strings.Join(emptyFiles, ","))).NotTo(o.HaveOccurred())
		}
	})

	g.It("runs successfully with options", func() {
		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		defer os.RemoveAll(tempDir)
		args := []string{
			"--dest-dir", tempDir,
			"--source-dir", "/artifacts",
			"--",
			"/bin/bash", "-c",
			"ls -l > /artifacts/ls.log",
		}
		o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())
		expectedFilePath := path.Join(getPluginOutputDir(oc, tempDir), "ls.log")
		o.Expect(expectedFilePath).To(o.BeAnExistingFile())
		stat, err := os.Stat(expectedFilePath)
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(stat.Size()).To(o.BeNumerically(">", 0))
	})

	g.It("runs successfully for audit logs", func() {
		// On IBM ROKS, events will not be part of the output, since audit logs do not include control plane logs.
		if e2e.TestContext.Provider == ibmcloud.ProviderName {
			g.Skip("ROKs doesn't have audit logs")
		}

		upgradedFrom45 := false
		cv, err := oc.AdminConfigClient().ConfigV1().ClusterVersions().Get(context.TODO(), "version", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		for _, history := range cv.Status.History {
			if strings.HasPrefix(history.Version, "4.5.") {
				upgradedFrom45 = true
			}
		}

		// makes some tokens that should not show in the audit logs
		_, sha256TokenName := exutil.GenerateOAuthTokenPair()
		oauthClient := oauthv1client.NewForConfigOrDie(oc.AdminConfig())
		_, err1 := oauthClient.OAuthAccessTokens().Create(context.Background(), &oauthv1.OAuthAccessToken{
			ObjectMeta: metav1.ObjectMeta{
				Name: sha256TokenName,
			},
			ClientName:  "openshift-challenging-client",
			ExpiresIn:   30,
			Scopes:      []string{"user:info"},
			RedirectURI: "https://127.0.0.1:12000/oauth/token/implicit",
			UserName:    "a",
			UserUID:     "1",
		}, metav1.CreateOptions{})
		o.Expect(err1).NotTo(o.HaveOccurred())
		_, err2 := oauthClient.OAuthAuthorizeTokens().Create(context.Background(), &oauthv1.OAuthAuthorizeToken{
			ObjectMeta: metav1.ObjectMeta{
				Name: sha256TokenName,
			},
			ClientName:  "openshift-challenging-client",
			ExpiresIn:   30,
			Scopes:      []string{"user:info"},
			RedirectURI: "https://127.0.0.1:12000/oauth/token/implicit",
			UserName:    "a",
			UserUID:     "1",
		}, metav1.CreateOptions{})
		o.Expect(err2).NotTo(o.HaveOccurred())

		tempDir, err := ioutil.TempDir("", "test.oc-adm-must-gather.")
		o.Expect(err).NotTo(o.HaveOccurred())
		// defer os.RemoveAll(tempDir)

		args := []string{
			"--dest-dir", tempDir,
			"--",
			"/usr/bin/gather_audit_logs",
		}

		o.Expect(oc.Run("adm", "must-gather").Args(args...).Execute()).To(o.Succeed())
		// wait for the contents to show up in the plugin output directory, avoiding EOF errors
		time.Sleep(10 * time.Second)

		pluginOutputDir := getPluginOutputDir(oc, tempDir)

		expectedDirectoriesToExpectedCount := map[string]int{
			path.Join(pluginOutputDir, "audit_logs", "kube-apiserver"):      1000,
			path.Join(pluginOutputDir, "audit_logs", "openshift-apiserver"): 10, // openshift apiservers don't necessarily get much traffic.  Especially early in a run
			path.Join(pluginOutputDir, "audit_logs", "oauth-apiserver"):     10, // oauth apiservers don't necessarily get much traffic.  Especially early in a run
		}

		expectedFiles := [][]string{
			{pluginOutputDir, "audit_logs", "kube-apiserver.audit_logs_listing"},
			{pluginOutputDir, "audit_logs", "openshift-apiserver.audit_logs_listing"},
			{pluginOutputDir, "audit_logs", "oauth-apiserver.audit_logs_listing"},
		}

		// for some crazy reason, it seems that the files from must-gather take time to appear on disk for reading.  I don't understand why
		// but this was in a previous commit and I don't want to immediately flake: https://github.com/openshift/origin/commit/006745a535848e84dcbcdd1c83ae86deddd3a229#diff-ad1c47fa4213de16d8b3237df5d71724R168
		// so we're going to try to get a pass every 10 seconds for a minute.  If we pass, great.  If we don't, we report the
		// last error we had.
		var lastErr error
		err = wait.PollImmediate(10*time.Second, 1*time.Minute, func() (bool, error) {
			// make sure we do not log OAuth tokens
			for auditDirectory, expectedNumberOfAuditEntries := range expectedDirectoriesToExpectedCount {
				eventsChecked := 0
				err := filepath.Walk(auditDirectory, func(path string, info os.FileInfo, err error) error {
					g.By(path)
					o.Expect(err).NotTo(o.HaveOccurred())
					if info.IsDir() {
						return nil
					}

					if (strings.Contains(path, "-termination-") && strings.HasSuffix(path, ".log.gz")) || strings.HasSuffix(path, "termination.log.gz") {
						// these are expected, but have unstructured log format
						return nil
					}

					isAuditFile := (strings.Contains(path, "-audit-") && strings.HasSuffix(path, ".log.gz")) || strings.HasSuffix(path, "audit.log.gz")
					o.Expect(isAuditFile).To(o.BeTrue())

					// at this point, we expect only audit files with json events, one per line

					readFile := false

					file, err := os.Open(path)
					o.Expect(err).NotTo(o.HaveOccurred())
					defer file.Close()

					fi, err := file.Stat()
					o.Expect(err).NotTo(o.HaveOccurred())

					// it will happen that the audit files are sometimes empty, we can
					// safely ignore these files since they don't provide valuable information
					// TODO this doesn't seem right.  It should be really unlikely, but we'll deal with later
					if fi.Size() == 0 {
						return nil
					}

					gzipReader, err := gzip.NewReader(file)
					o.Expect(err).NotTo(o.HaveOccurred())

					scanner := bufio.NewScanner(gzipReader)
					for scanner.Scan() {
						text := scanner.Text()
						if !strings.HasSuffix(text, "}") {
							continue // ignore truncated data
						}
						o.Expect(text).To(o.HavePrefix(`{"kind":"Event",`))

						// TODO(4.7+): remove this check
						// - if on 4.6 but not upgraded => disable check
						// - if on 4.6 and we have been upgraded from older release => keep the check.
						if upgradedFrom45 {
							for _, token := range []string{"oauthaccesstokens", "oauthauthorizetokens", sha256TokenName} {
								o.Expect(text).NotTo(o.ContainSubstring(token))
							}
						}
						readFile = true
						eventsChecked++
					}
					// ignore this error as we usually fail to read the whole GZ file
					// o.Expect(scanner.Err()).NotTo(o.HaveOccurred())
					o.Expect(readFile).To(o.BeTrue())

					return nil
				})
				o.Expect(err).NotTo(o.HaveOccurred())

				if eventsChecked <= expectedNumberOfAuditEntries {
					lastErr = fmt.Errorf("expected %d audit events for %q, but only got %d", expectedNumberOfAuditEntries, auditDirectory, eventsChecked)
					return false, nil
				}

				// reset lastErr if we succeeded.
				lastErr = nil
			}

			// if we get here, it means both directories checked out ok
			return true, nil
		})
		o.Expect(lastErr).NotTo(o.HaveOccurred()) // print the last error first if we have one
		o.Expect(err).NotTo(o.HaveOccurred())     // otherwise be sure we fail on the timeout if it happened

		emptyFiles := []string{}
		for _, expectedFile := range expectedFiles {
			expectedFilePath := path.Join(expectedFile...)
			o.Expect(expectedFilePath).To(o.BeAnExistingFile())
			stat, err := os.Stat(expectedFilePath)
			o.Expect(err).NotTo(o.HaveOccurred())
			if size := stat.Size(); size < 50 {
				emptyFiles = append(emptyFiles, expectedFilePath)
			}
		}
		if len(emptyFiles) > 0 {
			o.Expect(fmt.Errorf("expected files should not be empty: %s", strings.Join(emptyFiles, ","))).NotTo(o.HaveOccurred())
		}
	})
})

func getPluginOutputDir(oc *exutil.CLI, tempDir string) string {
	imageClient := versioned.NewForConfigOrDie(oc.AdminConfig())
	stream, err := imageClient.ImageV1().ImageStreams("openshift").Get(context.Background(), "must-gather", metav1.GetOptions{})
	o.Expect(err).NotTo(o.HaveOccurred())
	imageId, ok := imageutil.ResolveLatestTaggedImage(stream, "latest")
	o.Expect(ok).To(o.BeTrue())
	pluginOutputDir := path.Join(tempDir, regexp.MustCompile("[^A-Za-z0-9]+").ReplaceAllString(imageId, "-"))
	fileInfo, err := os.Stat(pluginOutputDir)
	if err != nil || !fileInfo.IsDir() {
		pluginOutputDir = tempDir
	}
	return pluginOutputDir
}
<|endoftext|>


<commit_before>package manta

// Required for CWorld to work:
// ----------------------------
// - CHandle< CBaseEntity >
// - CBodyComponent
// - CNetworkedQuantizedFloat
// - CGameSceneNodeHandle
// - CStrongHandle< InfoForResourceTypeCTextureBase >
// - CStrongHandle< InfoForResourceTypeCModel >
// - CUtlStringToken
// - CUtlVector< CAnimationLayer >
// - CEntityIdentity*
// - CUtlSymbolLarge
// - CPhysicsComponent
// - CRenderComponent
//
// - Color
// - QAngle
// - HSequence
// - Vector
// - SolidType_t
// - SurroundingBoundsType_t
// - MoveCollide_t
// - MoveType_t
// - gender_t
// - RenderMode_t
// - RenderFx_t
//
// - bool
// - uint8
// - uint16
// - uint32
// - uint64
// - int8
// - int32
// - float32
//
// - float32[24]
// - CStrongHandle< InfoForResourceTypeIMaterial2 >[6]
<commit_msg>Changed property_serializers structure<commit_after>package manta

// Required for CWorld to work:
// ----------------------------
// - CHandle< CBaseEntity >
// - CBodyComponent
// - CNetworkedQuantizedFloat
// - CGameSceneNodeHandle
// - CStrongHandle< InfoForResourceTypeCTextureBase >
// - CStrongHandle< InfoForResourceTypeCModel >
// - CUtlStringToken
// - CUtlVector< CAnimationLayer >
// - CEntityIdentity*
// - CUtlSymbolLarge
// - CPhysicsComponent
// - CRenderComponent
//
// - Color
// - QAngle
// - HSequence
// - Vector
// - SolidType_t
// - SurroundingBoundsType_t
// - MoveCollide_t
// - MoveType_t
// - gender_t
// - RenderMode_t
// - RenderFx_t
//
// - bool
// - uint8
// - uint16
// - uint32
// - uint64
// - int8
// - int32
// - float32
//
// - float32[24]
// - CStrongHandle< InfoForResourceTypeIMaterial2 >[6]

// Type for a decoder function
type DecodeFcn func(*reader) interface{}

// Type for an array decoder function
type DecodeArrayFcn func(*reader) interface{}

// PropertySerializer interface
type PropertySerializer struct {
	Decode      DecodeFcn
	DecodeArray DecodeArrayFcn
	IsArray     bool
	Length      uint32
}

// Contains a list of available property serializers
type PropertySerializerTable struct {
	Serializers map[string]*PropertySerializer
}

// Returns a table containing all know property serializers
func GetDefaultPropertySerializerTable() *PropertySerializerTable {
	// Init table
	tbl := &PropertySerializerTable{}
	tbl.Serializers = make(map[string]*PropertySerializer)

	// Append default serializers/decoders

	return tbl
}
<|endoftext|>


<commit_before>package providers

import (
	log "github.com/Sirupsen/logrus"
	"github.com/camptocamp/conplicity/handler"
	"github.com/fsouza/go-dockerclient"
)

type PostgreSQLProvider struct {
	handler   *handler.Conplicity
	vol       *docker.Volume
	backupDir string
}

func (p *PostgreSQLProvider) GetName() string {
	return "PostgreSQL"
}

func (p *PostgreSQLProvider) GetHandler() *handler.Conplicity {
	return p.handler
}

func (p *PostgreSQLProvider) GetBackupDir() string {
	return p.backupDir
}

func (p *PostgreSQLProvider) PrepareBackup() (err error) {
	c := p.handler
	vol := p.vol
	log.Infof("Looking for a postgres container using this volume...")
	containers, err := c.ListContainers(docker.ListContainersOptions{})
	checkErr(err, "Failed to list containers: %v", -1)
	for _, container := range containers {
		for _, mount := range container.Mounts {
			if mount.Name == vol.Name {
				log.Infof("Volume %v is used by container %v", vol.Name, container.ID)
				log.Infof("Launching pg_dumpall in container %v...", container.ID)
				exec, err := c.CreateExec(
					docker.CreateExecOptions{
						Container: container.ID,
						Cmd: []string{
							"sh",
							"-c",
							"mkdir -p " + mount.Destination + "/backups && pg_dumpall -Upostgres > " + mount.Destination + "/backups/all.sql",
						},
					},
				)

				checkErr(err, "Failed to create exec", 1)

				err = c.StartExec(
					exec.ID,
					docker.StartExecOptions{},
				)

				checkErr(err, "Failed to create exec", 1)

				p.backupDir = "backups"
			}
		}
	}
	return
}
<commit_msg>Fix for Docker < 1.11 (Fixes #13)<commit_after>package providers

import (
	log "github.com/Sirupsen/logrus"
	"github.com/camptocamp/conplicity/handler"
	"github.com/fsouza/go-dockerclient"
)

type PostgreSQLProvider struct {
	handler   *handler.Conplicity
	vol       *docker.Volume
	backupDir string
}

func (p *PostgreSQLProvider) GetName() string {
	return "PostgreSQL"
}

func (p *PostgreSQLProvider) GetHandler() *handler.Conplicity {
	return p.handler
}

func (p *PostgreSQLProvider) GetBackupDir() string {
	return p.backupDir
}

func (p *PostgreSQLProvider) PrepareBackup() (err error) {
	c := p.handler
	vol := p.vol
	log.Infof("Looking for a postgres container using this volume...")
	containers, err := c.ListContainers(docker.ListContainersOptions{})
	checkErr(err, "Failed to list containers: %v", -1)
	for _, container := range containers {
		container, err := c.InspectContainer(container.ID)
		checkErr(err, "Failed to inspect container "+container.ID+": %v", -1)
		for _, mount := range container.Mounts {
			if mount.Name == vol.Name {
				log.Infof("Volume %v is used by container %v", vol.Name, container.ID)
				log.Infof("Launching pg_dumpall in container %v...", container.ID)
				exec, err := c.CreateExec(
					docker.CreateExecOptions{
						Container: container.ID,
						Cmd: []string{
							"sh",
							"-c",
							"mkdir -p " + mount.Destination + "/backups && pg_dumpall -Upostgres > " + mount.Destination + "/backups/all.sql",
						},
					},
				)

				checkErr(err, "Failed to create exec", 1)

				err = c.StartExec(
					exec.ID,
					docker.StartExecOptions{},
				)

				checkErr(err, "Failed to create exec", 1)

				p.backupDir = "backups"
			}
		}
	}
	return
}
<|endoftext|>


<commit_before>package torrent

import (
	"math/rand"
	"sort"
	"time"
	"log"
)

// BitTorrent choking policy.

// The choking policy's view of a peer. For current policies we only care
// about identity and download bandwidth.
type Choker interface {
	DownloadBPS() float32 // bps
}

type ChokePolicy interface {
	// Only pass in interested peers.
	// mutate the chokers into a list where the first N are to be unchoked.
	Choke(chokers []Choker) (unchokeCount int, err error)
}

// Our naive never-choke policy
type NeverChokePolicy struct{}

func (n *NeverChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	return len(chokers), nil
}

// Random Choke Policy
type RandomChokePolicy struct{}

func (rcp *RandomChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	// Randomly Shuffle Choker List
	rand.Seed(time.Now().UnixNano())
	Shuffle(chokers)
	// Unchoke a random number of peers
	return rand.Intn(len(chokers)), nil
}

func Shuffle(a []Choker) {
    for i := range a {
        j := rand.Intn(i + 1)
        a[i], a[j] = a[j], a[i]
    }
}

// Our interpretation of the classic bittorrent choke policy.
// Expects to be called once every 10 seconds.
// See the section "Choking and optimistic unchoking" in
// https://wiki.theory.org/BitTorrentSpecification
type ClassicChokePolicy struct {
	optimisticUnchoker Choker // The choker we unchoked optimistically
	counter            int    // When to choose a new optimisticUnchoker.
}

type ByDownloadBPS []Choker

func (a ByDownloadBPS) Len() int {
	return len(a)
}

func (a ByDownloadBPS) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a ByDownloadBPS) Less(i, j int) bool {
	return a[i].DownloadBPS() > a[j].DownloadBPS()
}

const HIGH_BANDWIDTH_SLOTS = 3
const OPTIMISTIC_UNCHOKE_INDEX = HIGH_BANDWIDTH_SLOTS

// How many cycles of this algorithm before we pick a new optimistic
const OPTIMISTIC_UNCHOKE_COUNT = 3

func (ccp *ClassicChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	sort.Sort(ByDownloadBPS(chokers))
	log.Printf("%v", chokers)
	optimistIndex := ccp.findOptimist(chokers)
	if optimistIndex >= 0 {
		if optimistIndex < OPTIMISTIC_UNCHOKE_INDEX {
			// Forget optimistic choke
			optimistIndex = -1
		} else {
			ByDownloadBPS(chokers).Swap(OPTIMISTIC_UNCHOKE_INDEX, optimistIndex)
			optimistIndex = OPTIMISTIC_UNCHOKE_INDEX
		}
	}

	if optimistIndex >= 0 {
		ccp.counter++
		if ccp.counter >= OPTIMISTIC_UNCHOKE_COUNT {
			ccp.counter = 0
			optimistIndex = -1
		}
	}

	if optimistIndex < 0 {
		candidateCount := len(chokers) - OPTIMISTIC_UNCHOKE_INDEX
		if candidateCount > 0 {
			candidate := OPTIMISTIC_UNCHOKE_INDEX + rand.Intn(candidateCount)
			ByDownloadBPS(chokers).Swap(OPTIMISTIC_UNCHOKE_INDEX, candidate)
			ccp.counter = 0
			ccp.optimisticUnchoker = chokers[OPTIMISTIC_UNCHOKE_INDEX]
		}
	}
	unchokeCount = OPTIMISTIC_UNCHOKE_INDEX + 1
	if unchokeCount > len(chokers) {
		unchokeCount = len(chokers)
	}
	return
}

func (ccp *ClassicChokePolicy) findOptimist(chokers []Choker) (index int) {
	for i, c := range chokers {
		if c == ccp.optimisticUnchoker {
			return i
		}
	}
	return -1
}
<commit_msg>Attempt At FairTorrent Choking Policy<commit_after>package torrent

import (
	"math/rand"
	"sort"
	"time"
	"log"
)

// BitTorrent choking policy.

// The choking policy's view of a peer. For current policies we only care
// about identity and download bandwidth.
type Choker interface {
	DownloadBPS() float32 // bps
	UploadBPS() float32 // bps
}

type ChokePolicy interface {
	// Only pass in interested peers.
	// mutate the chokers into a list where the first N are to be unchoked.
	Choke(chokers []Choker) (unchokeCount int, err error)
}

// Our naive never-choke policy
type NeverChokePolicy struct{}

func (n *NeverChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	return len(chokers), nil
}

// Random Choke Policy
type RandomChokePolicy struct{}

func (rcp *RandomChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	// Randomly Shuffle Choker List
	rand.Seed(time.Now().UnixNano())
	Shuffle(chokers)
	// Unchoke a random number of peers
	return rand.Intn(len(chokers)), nil
}

func Shuffle(a []Choker) {
    for i := range a {
        j := rand.Intn(i + 1)
        a[i], a[j] = a[j], a[i]
    }
}

type DeficitBPS []Choker

func (a DeficitBPS) Len() int {
	return len(a)
}

func (a DeficitBPS) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a DeficitBPS) Less(i, j int) bool {
	return (a[i].DownloadBPS() - a[i].UploadBPS()) > (a[j].DownloadBPS() - a[j].UploadBPS())
}

// Fair-Torrent Choke Policy
type FairChokePolicy struct{}

func (fcp *FairChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	sort.Sort(DeficitBPS(chokers))
	return 4, nil
}

// Our interpretation of the classic bittorrent choke policy.
// Expects to be called once every 10 seconds.
// See the section "Choking and optimistic unchoking" in
// https://wiki.theory.org/BitTorrentSpecification
type ClassicChokePolicy struct {
	optimisticUnchoker Choker // The choker we unchoked optimistically
	counter            int    // When to choose a new optimisticUnchoker.
}

type ByDownloadBPS []Choker

func (a ByDownloadBPS) Len() int {
	return len(a)
}

func (a ByDownloadBPS) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a ByDownloadBPS) Less(i, j int) bool {
	return a[i].DownloadBPS() > a[j].DownloadBPS()
}

const HIGH_BANDWIDTH_SLOTS = 3
const OPTIMISTIC_UNCHOKE_INDEX = HIGH_BANDWIDTH_SLOTS

// How many cycles of this algorithm before we pick a new optimistic
const OPTIMISTIC_UNCHOKE_COUNT = 3

func (ccp *ClassicChokePolicy) Choke(chokers []Choker) (unchokeCount int, err error) {
	sort.Sort(ByDownloadBPS(chokers))
	log.Printf("%v", chokers)
	optimistIndex := ccp.findOptimist(chokers)
	if optimistIndex >= 0 {
		if optimistIndex < OPTIMISTIC_UNCHOKE_INDEX {
			// Forget optimistic choke
			optimistIndex = -1
		} else {
			ByDownloadBPS(chokers).Swap(OPTIMISTIC_UNCHOKE_INDEX, optimistIndex)
			optimistIndex = OPTIMISTIC_UNCHOKE_INDEX
		}
	}

	if optimistIndex >= 0 {
		ccp.counter++
		if ccp.counter >= OPTIMISTIC_UNCHOKE_COUNT {
			ccp.counter = 0
			optimistIndex = -1
		}
	}

	if optimistIndex < 0 {
		candidateCount := len(chokers) - OPTIMISTIC_UNCHOKE_INDEX
		if candidateCount > 0 {
			candidate := OPTIMISTIC_UNCHOKE_INDEX + rand.Intn(candidateCount)
			ByDownloadBPS(chokers).Swap(OPTIMISTIC_UNCHOKE_INDEX, candidate)
			ccp.counter = 0
			ccp.optimisticUnchoker = chokers[OPTIMISTIC_UNCHOKE_INDEX]
		}
	}
	unchokeCount = OPTIMISTIC_UNCHOKE_INDEX + 1
	if unchokeCount > len(chokers) {
		unchokeCount = len(chokers)
	}
	return
}

func (ccp *ClassicChokePolicy) findOptimist(chokers []Choker) (index int) {
	for i, c := range chokers {
		if c == ccp.optimisticUnchoker {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before>package torrent

import torrentapi "github.com/qopher/go-torrentapi"

func goodEnoughTorrent(results torrentapi.TorrentResults) string {
	for _, t := range results {
		if t.Seeders > 0 || t.Leechers > 0 {
			return t.Download
		}
	}
	return ""
}

func Search(name string) (string, error) {
	searchName := name + " 720p"
	api, err := torrentapi.Init()
	if err != nil {
		return "", err
	}
	api.Format("json_extended")
	api.SearchString(searchName)
	results, err := api.Search()
	if err != nil {
		return "", err
	}

	if len(results) == 0 {
		return "", nil
	}
	return goodEnoughTorrent(results), nil
}
<commit_msg>Change searching<commit_after>package torrent

import torrentapi "github.com/qopher/go-torrentapi"
import "showrss/dao"
import "strconv"

func goodEnoughTorrent(results torrentapi.TorrentResults) string {
	for _, t := range results {
		if t.Seeders > 0 || t.Leechers > 0 {
			return t.Download
		}
	}
	return ""
}

func Search(episode dao.Episode) (string, error) {
	api, err := torrentapi.Init()
	if err != nil {
		return "", err
	}
	api.Format("json_extended")
	api.SearchTVDB(strconv.Itoa(episode.ShowID))
	api.SearchString(episode.Code)
	results, err := api.Search()
	if err != nil {
		return "", err
	}

	if len(results) == 0 {
		return "", nil
	}
	return goodEnoughTorrent(results), nil
}
<|endoftext|>


<commit_before>// Copyright 2010 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 cmplx

import "math"

// The original C code, the long comment, and the constants
// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
// The go code is a simplified version of the original C.
//
// Cephes Math Library Release 2.8:  June, 2000
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
//
// The readme file at http://netlib.sandia.gov/cephes/ says:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.
//
//   The two known misprints in the book are repaired here in the
// source listings for the gamma function and the incomplete beta
// integral.
//
//   Stephen L. Moshier
//   moshier@na-net.ornl.gov

// Complex square root
//
// DESCRIPTION:
//
// If z = x + iy,  r = |z|, then
//
//                       1/2
// Re w  =  [ (r + x)/2 ]   ,
//
//                       1/2
// Im w  =  [ (r - x)/2 ]   .
//
// Cancellation error in r-x or r+x is avoided by using the
// identity  2 Re w Im w  =  y.
//
// Note that -w is also a square root of z.  The root chosen
// is always in the right half plane and Im w has the same sign as y.
//
// ACCURACY:
//
//                      Relative error:
// arithmetic   domain     # trials      peak         rms
//    DEC       -10,+10     25000       3.2e-17     9.6e-18
//    IEEE      -10,+10   1,000,000     2.9e-16     6.1e-17

// Sqrt returns the square root of x.
func Sqrt(x complex128) complex128 {
	if imag(x) == 0 {
		if real(x) == 0 {
			return complex(0, 0)
		}
		if real(x) < 0 {
			return complex(0, math.Sqrt(-real(x)))
		}
		return complex(math.Sqrt(real(x)), 0)
	}
	if real(x) == 0 {
		if imag(x) < 0 {
			r := math.Sqrt(-0.5 * imag(x))
			return complex(r, -r)
		}
		r := math.Sqrt(0.5 * imag(x))
		return complex(r, r)
	}
	a := real(x)
	b := imag(x)
	var scale float64
	// Rescale to avoid internal overflow or underflow.
	if math.Abs(a) > 4 || math.Abs(b) > 4 {
		a *= 0.25
		b *= 0.25
		scale = 2
	} else {
		a *= 1.8014398509481984e16 // 2**54
		b *= 1.8014398509481984e16
		scale = 7.450580596923828125e-9 // 2**-27
	}
	r := math.Hypot(a, b)
	var t float64
	if a > 0 {
		t = math.Sqrt(0.5*r + 0.5*a)
		r = scale * math.Abs((0.5*b)/t)
		t *= scale
	} else {
		r = math.Sqrt(0.5*r - 0.5*a)
		t = scale * math.Abs((0.5*b)/r)
		r *= scale
	}
	if b < 0 {
		return complex(t, -r)
	}
	return complex(t, r)
}
<commit_msg>math/cmplx: specify which square root Sqrt returns<commit_after>// Copyright 2010 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 cmplx

import "math"

// The original C code, the long comment, and the constants
// below are from http://netlib.sandia.gov/cephes/c9x-complex/clog.c.
// The go code is a simplified version of the original C.
//
// Cephes Math Library Release 2.8:  June, 2000
// Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
//
// The readme file at http://netlib.sandia.gov/cephes/ says:
//    Some software in this archive may be from the book _Methods and
// Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
// International, 1989) or from the Cephes Mathematical Library, a
// commercial product. In either event, it is copyrighted by the author.
// What you see here may be used freely but it comes with no support or
// guarantee.
//
//   The two known misprints in the book are repaired here in the
// source listings for the gamma function and the incomplete beta
// integral.
//
//   Stephen L. Moshier
//   moshier@na-net.ornl.gov

// Complex square root
//
// DESCRIPTION:
//
// If z = x + iy,  r = |z|, then
//
//                       1/2
// Re w  =  [ (r + x)/2 ]   ,
//
//                       1/2
// Im w  =  [ (r - x)/2 ]   .
//
// Cancellation error in r-x or r+x is avoided by using the
// identity  2 Re w Im w  =  y.
//
// Note that -w is also a square root of z.  The root chosen
// is always in the right half plane and Im w has the same sign as y.
//
// ACCURACY:
//
//                      Relative error:
// arithmetic   domain     # trials      peak         rms
//    DEC       -10,+10     25000       3.2e-17     9.6e-18
//    IEEE      -10,+10   1,000,000     2.9e-16     6.1e-17

// Sqrt returns the square root of x.
// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
func Sqrt(x complex128) complex128 {
	if imag(x) == 0 {
		if real(x) == 0 {
			return complex(0, 0)
		}
		if real(x) < 0 {
			return complex(0, math.Sqrt(-real(x)))
		}
		return complex(math.Sqrt(real(x)), 0)
	}
	if real(x) == 0 {
		if imag(x) < 0 {
			r := math.Sqrt(-0.5 * imag(x))
			return complex(r, -r)
		}
		r := math.Sqrt(0.5 * imag(x))
		return complex(r, r)
	}
	a := real(x)
	b := imag(x)
	var scale float64
	// Rescale to avoid internal overflow or underflow.
	if math.Abs(a) > 4 || math.Abs(b) > 4 {
		a *= 0.25
		b *= 0.25
		scale = 2
	} else {
		a *= 1.8014398509481984e16 // 2**54
		b *= 1.8014398509481984e16
		scale = 7.450580596923828125e-9 // 2**-27
	}
	r := math.Hypot(a, b)
	var t float64
	if a > 0 {
		t = math.Sqrt(0.5*r + 0.5*a)
		r = scale * math.Abs((0.5*b)/t)
		t *= scale
	} else {
		r = math.Sqrt(0.5*r - 0.5*a)
		t = scale * math.Abs((0.5*b)/r)
		r *= scale
	}
	if b < 0 {
		return complex(t, -r)
	}
	return complex(t, r)
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"log"
	"net/http"
)

var (
	ServerVersion string = "pre version"
	APIVersion    string = "pre api"
)

func main() {
	fmt.Println("TRACKMON SERVER\nCopyright (c) 2017, Paul Kramme under BSD 2-Clause")
	fmt.Println("Please report bugs to https://github.com/trackmon/trackmon-server")

	// Configure flags
	CreateConfigFlag := flag.Bool("createconfig", false, "Creates a standard configuration and exits")
	ConfigLocation := flag.String("config", "./trackmonserv.conf", "Location of config file. Standard is ./trackmonserv")
	ShowLicenses := flag.Bool("licenses", false, "Shows licenses and exits")
	ShowVersion := flag.Bool("version", false, "Shows version and exits")
	ShowJsonVersion := flag.Bool("versionjson", false, "Shows version in json and exits")

	// Check flags
	flag.Parse()
	if *CreateConfigFlag == true {
		CreateConfig()
		return
	}
	if *ShowLicenses == true {
		fmt.Println("trackmon servers license\n")
		fmt.Print(trackmonlicense)
		fmt.Println("\n")

		fmt.Println("This project uses github.com/gorilla/mux\n")
		fmt.Print(muxlicense)
		fmt.Println("\n")

		return
	}
	if *ShowVersion == true {
		fmt.Printf("Server Version: %s\nAPI Version: %s\n", ServerVersion, APIVersion)
		return
	}
	if *ShowJsonVersion == true {
		fmt.Printf("{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	}

	// Load config
	var Config Configuration
	Configfile, err := ioutil.ReadFile(*ConfigLocation)
	if err != nil {
		fmt.Println("Couldn't find or open config file. Create one with -createconfig")
		panic(err)
	}
	err = fromjson(string(Configfile), &Config)
	if err != nil {
		panic(err)
	}

	// Setup database connection
	db_connection_string := fmt.Sprintf("dbname=trackmon_server_production user=trackmon host=%s password=%s", Config.DatabaseAddress, Config.DatabasePassword)
	db, err := sql.Open("postgres", db_connection_string)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// Configure router and server
	r := mux.NewRouter()
	r.HandleFunc("/", RootHandler) // Returnes 200 OK, can be used for health checks
	r.HandleFunc("/version", VersionHandler)
	r.HandleFunc("/user/{user_id}", UserHandler)
	r.HandleFunc("/user/{user_id}/{account}", AccountHandler)
	r.HandleFunc("/signup", NewUserHandler)

	srv := &http.Server{
		Handler: r,
		Addr:    Config.ListeningAddress,
	}

	// Start the server
	log.Println("Initialization complete")
	srv.ListenAndServe()
}

/*
 ██████  ██████  ███    ██ ███████ ██  ██████
██      ██    ██ ████   ██ ██      ██ ██
██      ██    ██ ██ ██  ██ █████   ██ ██   ███
██      ██    ██ ██  ██ ██ ██      ██ ██    ██
 ██████  ██████  ██   ████ ██      ██  ██████
*/

type Configuration struct {
	ListeningAddress string
	DatabaseAddress  string
	DatabasePassword string
}

func CreateConfig() {
	var Config Configuration

	// Standard config
	Config.ListeningAddress = ":80"
	Config.DatabaseAddress = "localhost"
	Config.DatabasePassword = ""

	ByteJsonConfig, err := toprettyjson(Config)
	if err != nil {
		panic(err)
	}
	fmt.Println("Writing configuration to ./trackmonserv.conf")
	err = ioutil.WriteFile("./trackmonserv.conf", ByteJsonConfig, 0644)
	if err != nil {
		panic(err)
	}
}

/*
██   ██  █████  ███    ██ ██████  ██      ███████ ██████
██   ██ ██   ██ ████   ██ ██   ██ ██      ██      ██   ██
███████ ███████ ██ ██  ██ ██   ██ ██      █████   ██████
██   ██ ██   ██ ██  ██ ██ ██   ██ ██      ██      ██   ██
██   ██ ██   ██ ██   ████ ██████  ███████ ███████ ██   ██
*/

func RootHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	w.WriteHeader(http.StatusOK)
}

func NewUserHandler(w http.ResponseWriter, r *http.Request) {
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	hashedpw, err := HashPassword(password)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Println(username, hashedpw)
	// TODO: Check if user exist in database
	// TODO: IF NOT Create new user and write to database
}

func UserHandler(w http.ResponseWriter, r *http.Request) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about all accounts of %s\n", username, password, string(variables["user_id"]))
	if username != string(variables["user_id"]) {
		w.WriteHeader(http.StatusForbidden)
	}
	// TODO: Check if user exists, if and if password correct, give him info
}

func AccountHandler(w http.ResponseWriter, r *http.Request) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about account %s from %s\n", username, password, string(variables["account"]), string(variables["user_id"]))
}

/*
 ██████ ██████  ██    ██ ██████  ████████
██      ██   ██  ██  ██  ██   ██    ██
██      ██████    ████   ██████     ██
██      ██   ██    ██    ██         ██
 ██████ ██   ██    ██    ██         ██
*/

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

/*
     ██ ███████  ██████  ███    ██
     ██ ██      ██    ██ ████   ██
     ██ ███████ ██    ██ ██ ██  ██
██   ██      ██ ██    ██ ██  ██ ██
 █████  ███████  ██████  ██   ████
*/

func fromjson(src string, v interface{}) error {
	return json.Unmarshal([]byte(src), v)
}

func tojson(v interface{}) ([]byte, error) {
	return json.Marshal(v)
}

func toprettyjson(v interface{}) ([]byte, error) {
	return json.MarshalIndent(v, "", "\t")
}

/*
██      ██  ██████ ███████ ███    ██ ███████ ███████
██      ██ ██      ██      ████   ██ ██      ██
██      ██ ██      █████   ██ ██  ██ ███████ █████
██      ██ ██      ██      ██  ██ ██      ██ ██
███████ ██  ██████ ███████ ██   ████ ███████ ███████
*/

const (
	muxlicense string = `Copyright (c) 2012 Rodrigo Moraes. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

	 * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
	 * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
	 * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
	trackmonlicense string = `BSD 2-Clause License

	Copyright (c) 2017, Paul Kramme
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	* Redistributions of source code must retain the above copyright notice, this
	  list of conditions and the following disclaimer.

	* Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
`
)
<commit_msg>Fix variable style on some variables<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/gorilla/mux"
	_ "github.com/lib/pq"
	"golang.org/x/crypto/bcrypt"
	"io/ioutil"
	"log"
	"net/http"
)

var (
	ServerVersion string = "pre version"
	APIVersion    string = "pre api"
)

func main() {
	fmt.Println("TRACKMON SERVER\nCopyright (c) 2017, Paul Kramme under BSD 2-Clause")
	fmt.Println("Please report bugs to https://github.com/trackmon/trackmon-server")

	// Configure flags
	CreateConfigFlag := flag.Bool("createconfig", false, "Creates a standard configuration and exits")
	ConfigLocation := flag.String("config", "./trackmonserv.conf", "Location of config file. Standard is ./trackmonserv")
	ShowLicenses := flag.Bool("licenses", false, "Shows licenses and exits")
	ShowVersion := flag.Bool("version", false, "Shows version and exits")
	ShowJsonVersion := flag.Bool("versionjson", false, "Shows version in json and exits")

	// Check flags
	flag.Parse()
	if *CreateConfigFlag == true {
		CreateConfig()
		return
	}
	if *ShowLicenses == true {
		fmt.Println("trackmon servers license\n")
		fmt.Print(trackmonlicense)
		fmt.Println("\n")

		fmt.Println("This project uses github.com/gorilla/mux\n")
		fmt.Print(muxlicense)
		fmt.Println("\n")

		return
	}
	if *ShowVersion == true {
		fmt.Printf("Server Version: %s\nAPI Version: %s\n", ServerVersion, APIVersion)
		return
	}
	if *ShowJsonVersion == true {
		fmt.Printf("{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	}

	// Load config
	var Config Configuration
	Configfile, err := ioutil.ReadFile(*ConfigLocation)
	if err != nil {
		fmt.Println("Couldn't find or open config file. Create one with -createconfig")
		panic(err)
	}
	err = fromjson(string(Configfile), &Config)
	if err != nil {
		panic(err)
	}

	// Setup database connection
	DatabaseConnectionString := fmt.Sprintf("dbname=trackmon_server_production user=trackmon host=%s password=%s", Config.DatabaseAddress, Config.DatabasePassword)
	db, err := sql.Open("postgres", DatabaseConnectionString)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	err = db.Ping()
	if err != nil {
		log.Fatal(err)
	}

	// Configure router and server
	r := mux.NewRouter()
	r.HandleFunc("/", RootHandler) // Returnes 200 OK, can be used for health checks
	r.HandleFunc("/version", VersionHandler)
	r.HandleFunc("/user/{user_id}", UserHandler)
	r.HandleFunc("/user/{user_id}/{account}", AccountHandler)
	r.HandleFunc("/signup", NewUserHandler)

	srv := &http.Server{
		Handler: r,
		Addr:    Config.ListeningAddress,
	}

	// Start the server
	log.Println("Initialization complete")
	srv.ListenAndServe()
}

/*
 ██████  ██████  ███    ██ ███████ ██  ██████
██      ██    ██ ████   ██ ██      ██ ██
██      ██    ██ ██ ██  ██ █████   ██ ██   ███
██      ██    ██ ██  ██ ██ ██      ██ ██    ██
 ██████  ██████  ██   ████ ██      ██  ██████
*/

type Configuration struct {
	ListeningAddress string
	DatabaseAddress  string
	DatabasePassword string
}

func CreateConfig() {
	var Config Configuration

	// Standard config
	Config.ListeningAddress = ":80"
	Config.DatabaseAddress = "localhost"
	Config.DatabasePassword = ""

	ByteJsonConfig, err := toprettyjson(Config)
	if err != nil {
		panic(err)
	}
	fmt.Println("Writing configuration to ./trackmonserv.conf")
	err = ioutil.WriteFile("./trackmonserv.conf", ByteJsonConfig, 0644)
	if err != nil {
		panic(err)
	}
}

/*
██   ██  █████  ███    ██ ██████  ██      ███████ ██████
██   ██ ██   ██ ████   ██ ██   ██ ██      ██      ██   ██
███████ ███████ ██ ██  ██ ██   ██ ██      █████   ██████
██   ██ ██   ██ ██  ██ ██ ██   ██ ██      ██      ██   ██
██   ██ ██   ██ ██   ████ ██████  ███████ ███████ ██   ██
*/

func RootHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func VersionHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "{\"serverversion\":\"%s\",\"apiversion\":\"%s\"}", ServerVersion, APIVersion)
	w.WriteHeader(http.StatusOK)
}

func NewUserHandler(w http.ResponseWriter, r *http.Request) {
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	}
	HashedPassword, err := HashPassword(password)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	log.Println(username, HashedPassword)
	// TODO: Check if user exist in database
	// TODO: IF NOT Create new user and write to database
}

func UserHandler(w http.ResponseWriter, r *http.Request) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about all accounts of %s\n", username, password, string(variables["user_id"]))
	if username != string(variables["user_id"]) {
		w.WriteHeader(http.StatusForbidden)
	}
	// TODO: Check if user exists, if and if password correct, give him info
}

func AccountHandler(w http.ResponseWriter, r *http.Request) {
	variables := mux.Vars(r)
	username, password, ok := r.BasicAuth()
	if ok != true {
		w.WriteHeader(http.StatusUnauthorized)
		return
	} // All requests below here have given basic auth
	log.Printf("User %s with pw %s wants info about account %s from %s\n", username, password, string(variables["account"]), string(variables["user_id"]))
}

/*
 ██████ ██████  ██    ██ ██████  ████████
██      ██   ██  ██  ██  ██   ██    ██
██      ██████    ████   ██████     ██
██      ██   ██    ██    ██         ██
 ██████ ██   ██    ██    ██         ██
*/

func HashPassword(password string) (string, error) {
	bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
	return string(bytes), err
}

func CheckPasswordHash(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

/*
     ██ ███████  ██████  ███    ██
     ██ ██      ██    ██ ████   ██
     ██ ███████ ██    ██ ██ ██  ██
██   ██      ██ ██    ██ ██  ██ ██
 █████  ███████  ██████  ██   ████
*/

func fromjson(src string, v interface{}) error {
	return json.Unmarshal([]byte(src), v)
}

func tojson(v interface{}) ([]byte, error) {
	return json.Marshal(v)
}

func toprettyjson(v interface{}) ([]byte, error) {
	return json.MarshalIndent(v, "", "\t")
}

/*
██      ██  ██████ ███████ ███    ██ ███████ ███████
██      ██ ██      ██      ████   ██ ██      ██
██      ██ ██      █████   ██ ██  ██ ███████ █████
██      ██ ██      ██      ██  ██ ██      ██ ██
███████ ██  ██████ ███████ ██   ████ ███████ ███████
*/

const (
	muxlicense string = `Copyright (c) 2012 Rodrigo Moraes. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

	 * Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
	 * Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
	 * Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.`
	trackmonlicense string = `BSD 2-Clause License

	Copyright (c) 2017, Paul Kramme
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	* Redistributions of source code must retain the above copyright notice, this
	  list of conditions and the following disclaimer.

	* Redistributions in binary form must reproduce the above copyright notice,
	  this list of conditions and the following disclaimer in the documentation
	  and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
	DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
	FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
	DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
	SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
	CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
	OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
`
)
<|endoftext|>


<commit_before>// Copyright 2011 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 runtime_test

import (
	"runtime"
	"testing"
)

func TestGcSys(t *testing.T) {
	if runtime.GOARCH != "amd64" {
		// TODO(adg): remove this when precise gc is implemented
		t.Logf("skipping on non-amd64 systems")
		return
	}
	memstats := new(runtime.MemStats)
	runtime.GC()
	runtime.ReadMemStats(memstats)
	sys := memstats.Sys

	runtime.MemProfileRate = 0 // disable profiler

	itercount := 1000000
	if testing.Short() {
		itercount = 100000
	}
	for i := 0; i < itercount; i++ {
		workthegc()
	}

	// Should only be using a few MB.
	runtime.ReadMemStats(memstats)
	if sys > memstats.Sys {
		sys = 0
	} else {
		sys = memstats.Sys - sys
	}
	t.Logf("used %d extra bytes", sys)
	if sys > 4<<20 {
		t.Fatalf("using too much memory: %d bytes", sys)
	}
}

func workthegc() []byte {
	return make([]byte, 1029)
}
<commit_msg>runtime: relax TestGcSys<commit_after>// Copyright 2011 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 runtime_test

import (
	"runtime"
	"testing"
)

func TestGcSys(t *testing.T) {
	memstats := new(runtime.MemStats)
	runtime.GC()
	runtime.ReadMemStats(memstats)
	sys := memstats.Sys

	runtime.MemProfileRate = 0 // disable profiler

	itercount := 1000000
	if testing.Short() {
		itercount = 100000
	}
	for i := 0; i < itercount; i++ {
		workthegc()
	}

	// Should only be using a few MB.
	// We allocated 100 MB or (if not short) 1 GB.
	runtime.ReadMemStats(memstats)
	if sys > memstats.Sys {
		sys = 0
	} else {
		sys = memstats.Sys - sys
	}
	t.Logf("used %d extra bytes", sys)
	if sys > 16<<20 {
		t.Fatalf("using too much memory: %d bytes", sys)
	}
}

func workthegc() []byte {
	return make([]byte, 1029)
}
<|endoftext|>


<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/limetext/text"
	"testing"
)

type caseTest struct {
	in_region []Region
	in        string
	exp       string
}

func runCaseTest(command string, testsuite *[]caseTest, t *testing.T) {
	ed := GetEditor()
	w := ed.NewWindow()
	defer w.Close()

	for i, test := range *testsuite {
		v := w.NewFile()
		defer func() {
			v.SetScratch(true)
			v.Close()
		}()

		e := v.BeginEdit()
		v.Insert(e, 0, test.in)
		v.EndEdit(e)

		v.Sel().Clear()
		if test.in_region != nil {
			for _, r := range test.in_region {
				v.Sel().Add(r)
			}
		}
		ed.CommandHandler().RunTextCommand(v, command, nil)
		sr := v.Buffer().Substr(Region{0, v.Buffer().Size()})
		if sr != test.exp {
			t.Errorf("%s test %d failed: %v, %+v", command, i, sr, test)
		}
	}
}

func TestTitleCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			// Please note the bizarre  capitalization of the first L in he'Ll...  This is due to a bug in go's strings
			// library.  I'm going to try to get them to fix it...  If not, maybe we'll have
			// to write our own Title Casing function.
			[]Region{{24, 51}},

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give a man a match, and He'Ll Be Warm For A Minute, but set him on fire, and he'll be warm for the rest of his life.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 17}, {52, 71}},

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give A Man A Match, and he'll be warm for a minute, But Set Him On Fire, and he'll be warm for the rest of his life.",
		},
		/*no selection*/
		{
			nil,

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
		},
		/*unicode*/
		{
			[]Region{{0, 12}},

			"ничего себе!",
			"Ничего Себе!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("title_case", &tests, t)
}

func TestSwapCase(t *testing.T) {
	tests := []caseTest{
		{
			[]Region{{0, 13}},

			"Hello, World!",
			"hELLO, wORLD!",
		},
		{
			[]Region{{0, 11}},

			"ПрИвЕт, МиР",
			"пРиВеТ, мИр",
		},
	}

	runCaseTest("swap_case", &tests, t)
}

func TestUpperCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			[]Region{{0, 76}},

			"Try not to become a man of success, but rather try to become a man of value.",
			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 20}, {74, 76}},

			"Try not to become a man of success, but rather try to become a man of value.",
			"TRY NOT TO BECOME A man of success, but rather try to become a man of valuE.",
		},
		/*no selection*/
		{
			nil,

			"Try not to become a man of success, but rather try to become a man of value.",
			"Try not to become a man of success, but rather try to become a man of value.",
		},
		/*unicode*/
		{
			[]Region{{0, 74}},

			"чем больше законов и постановлений, тем больше разбойников и преступлений!",
			"ЧЕМ БОЛЬШЕ ЗАКОНОВ И ПОСТАНОВЛЕНИЙ, ТЕМ БОЛЬШЕ РАЗБОЙНИКОВ И ПРЕСТУПЛЕНИЙ!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("upper_case", &tests, t)
}

func TestLowerCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			[]Region{{0, 76}},

			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
			"try not to become a man of success, but rather try to become a man of value.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 20}, {74, 76}},

			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
			"try not to become a MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUe.",
		},
		/*no selection*/
		{
			nil,

			"Try not to become a man of success, but rather try to become a man of value.",
			"Try not to become a man of success, but rather try to become a man of value.",
		},
		/*unicode*/
		{
			[]Region{{0, 74}},

			"ЧЕМ БОЛЬШЕ ЗАКОНОВ И ПОСТАНОВЛЕНИЙ, ТЕМ БОЛЬШЕ РАЗБОЙНИКОВ И ПРЕСТУПЛЕНИЙ!",
			"чем больше законов и постановлений, тем больше разбойников и преступлений!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("lower_case", &tests, t)
}
<commit_msg>add empty string test case in TestSwapCase in backend/commands/case_test.go<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package commands

import (
	. "github.com/limetext/lime/backend"
	. "github.com/limetext/text"
	"testing"
)

type caseTest struct {
	in_region []Region
	in        string
	exp       string
}

func runCaseTest(command string, testsuite *[]caseTest, t *testing.T) {
	ed := GetEditor()
	w := ed.NewWindow()
	defer w.Close()

	for i, test := range *testsuite {
		v := w.NewFile()
		defer func() {
			v.SetScratch(true)
			v.Close()
		}()

		e := v.BeginEdit()
		v.Insert(e, 0, test.in)
		v.EndEdit(e)

		v.Sel().Clear()
		if test.in_region != nil {
			for _, r := range test.in_region {
				v.Sel().Add(r)
			}
		}
		ed.CommandHandler().RunTextCommand(v, command, nil)
		sr := v.Buffer().Substr(Region{0, v.Buffer().Size()})
		if sr != test.exp {
			t.Errorf("%s test %d failed: %v, %+v", command, i, sr, test)
		}
	}
}

func TestTitleCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			// Please note the bizarre  capitalization of the first L in he'Ll...  This is due to a bug in go's strings
			// library.  I'm going to try to get them to fix it...  If not, maybe we'll have
			// to write our own Title Casing function.
			[]Region{{24, 51}},

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give a man a match, and He'Ll Be Warm For A Minute, but set him on fire, and he'll be warm for the rest of his life.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 17}, {52, 71}},

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give A Man A Match, and he'll be warm for a minute, But Set Him On Fire, and he'll be warm for the rest of his life.",
		},
		/*no selection*/
		{
			nil,

			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
			"Give a man a match, and he'll be warm for a minute, but set him on fire, and he'll be warm for the rest of his life.",
		},
		/*unicode*/
		{
			[]Region{{0, 12}},

			"ничего себе!",
			"Ничего Себе!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("title_case", &tests, t)
}

func TestSwapCase(t *testing.T) {
	tests := []caseTest{
		{
			[]Region{{0, 0}},

			"",
			"",
		},
		{
			[]Region{{0, 13}},

			"Hello, World!",
			"hELLO, wORLD!",
		},
		{
			[]Region{{0, 11}},

			"ПрИвЕт, МиР",
			"пРиВеТ, мИр",
		},
	}

	runCaseTest("swap_case", &tests, t)
}

func TestUpperCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			[]Region{{0, 76}},

			"Try not to become a man of success, but rather try to become a man of value.",
			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 20}, {74, 76}},

			"Try not to become a man of success, but rather try to become a man of value.",
			"TRY NOT TO BECOME A man of success, but rather try to become a man of valuE.",
		},
		/*no selection*/
		{
			nil,

			"Try not to become a man of success, but rather try to become a man of value.",
			"Try not to become a man of success, but rather try to become a man of value.",
		},
		/*unicode*/
		{
			[]Region{{0, 74}},

			"чем больше законов и постановлений, тем больше разбойников и преступлений!",
			"ЧЕМ БОЛЬШЕ ЗАКОНОВ И ПОСТАНОВЛЕНИЙ, ТЕМ БОЛЬШЕ РАЗБОЙНИКОВ И ПРЕСТУПЛЕНИЙ!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("upper_case", &tests, t)
}

func TestLowerCase(t *testing.T) {
	tests := []caseTest{
		/*single selection*/
		{
			[]Region{{0, 76}},

			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
			"try not to become a man of success, but rather try to become a man of value.",
		},
		/*multiple selection*/
		{
			[]Region{{0, 20}, {74, 76}},

			"TRY NOT TO BECOME A MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUE.",
			"try not to become a MAN OF SUCCESS, BUT RATHER TRY TO BECOME A MAN OF VALUe.",
		},
		/*no selection*/
		{
			nil,

			"Try not to become a man of success, but rather try to become a man of value.",
			"Try not to become a man of success, but rather try to become a man of value.",
		},
		/*unicode*/
		{
			[]Region{{0, 74}},

			"ЧЕМ БОЛЬШЕ ЗАКОНОВ И ПОСТАНОВЛЕНИЙ, ТЕМ БОЛЬШЕ РАЗБОЙНИКОВ И ПРЕСТУПЛЕНИЙ!",
			"чем больше законов и постановлений, тем больше разбойников и преступлений!",
		},
		/*asian characters*/
		{
			[]Region{{0, 9}},

			"千里之行﹐始于足下",
			"千里之行﹐始于足下",
		},
	}

	runCaseTest("lower_case", &tests, t)
}
<|endoftext|>


<commit_before>package jobs

import (
	"encoding/json"
	"fmt"
	"github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/structs"
	"log"
)

const (
	Restart = "NOMAD_RESTART_JOB"
)

type restart struct {
	action structs.Action
	client *api.Client
}

func NewRestart(action structs.Action, client *api.Client) *restart {
	return &restart{
		action: action,
		client: client,
	}
}

func (w *restart) Do() (*structs.Response, error) {
	origJob, _, err := w.client.Jobs().Info(w.action.Payload.(string), nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	// stop
	_, _, err = w.client.Jobs().Deregister(w.action.Payload.(string), false, nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	// start
	// do some json-dancing to modify the structure to make sure the job is in to-be-started-state
	jobJson, err := json.Marshal(origJob)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	var job_struct map[string]interface{}
	err = json.Unmarshal(jobJson, &job_struct)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	job_struct["Stop"] = false

	// dance backwards
	startJob := api.Job{}
	start_json, _ := json.Marshal(job_struct)
	log.Println(string(start_json))
	json.Unmarshal(start_json, &startJob)

	_, _, err = w.client.Jobs().Register(&startJob, nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	return structs.NewSuccessResponse("Successfully restarted origJob")
}

func (w *restart) Key() string {
	return fmt.Sprintf("/job/%s/restart", w.action.Payload.(string))
}

func (w *restart) IsMutable() bool {
	return true
}

func (w *restart) BackendType() string {
	return "nomad"
}
<commit_msg>Refactor restart<commit_after>package jobs

import (
	"fmt"
	"github.com/hashicorp/nomad/api"
	"github.com/jippi/hashi-ui/backend/structs"
)

const (
	Restart = "NOMAD_RESTART_JOB"
)

type restart struct {
	action structs.Action
	client *api.Client
}

func NewRestart(action structs.Action, client *api.Client) *restart {
	return &restart{
		action: action,
		client: client,
	}
}

// boolToPtr returns the pointer to a bool
func boolToPtr(b bool) *bool {
	return &b
}

func (w *restart) Do() (*structs.Response, error) {
	origJob, _, err := w.client.Jobs().Info(w.action.Payload.(string), nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	// stop
	_, _, err = w.client.Jobs().Deregister(w.action.Payload.(string), false, nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	// start
	origJob.Stop = boolToPtr(false) // enforce starting the job, even if it was stopped originally

	_, _, err = w.client.Jobs().Register(origJob, nil)
	if err != nil {
		return structs.NewErrorResponse(err)
	}

	return structs.NewSuccessResponse("Successfully restarted origJob")
}

func (w *restart) Key() string {
	return fmt.Sprintf("/job/%s/restart", w.action.Payload.(string))
}

func (w *restart) IsMutable() bool {
	return true
}

func (w *restart) BackendType() string {
	return "nomad"
}
<|endoftext|>