<commit_before>package parser

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/asdine/storm"
	"github.com/filebrowser/filebrowser"
	"github.com/filebrowser/filebrowser/bolt"
	"github.com/filebrowser/filebrowser/staticgen"
	"github.com/hacdias/fileutils"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

var databases = map[string]*storm.DB{}

// Parse ...
func Parse(c *caddy.Controller, plugin string) ([]*filebrowser.FileBrowser, error) {
	var (
		configs []*filebrowser.FileBrowser
		err     error
	)

	for c.Next() {
		u := &filebrowser.User{
			Locale:        "en",
			AllowCommands: true,
			AllowEdit:     true,
			AllowNew:      true,
			AllowPublish:  true,
			Commands:      []string{"git", "svn", "hg"},
			CSS:           "",
			ViewMode:      "mosaic",
			Rules: []*filebrowser.Rule{{
				Regex:  true,
				Allow:  false,
				Regexp: &filebrowser.Regexp{Raw: "\\/\\..+"},
			}},
		}

		baseURL := "/"
		scope := "."
		database := ""
		noAuth := false
		alterRecaptcha := false
		reCaptchaKey := ""
		reCaptchaSecret := ""

		if plugin != "" {
			baseURL = "/admin"
		}

		// Get the baseURL and scope
		args := c.RemainingArgs()

		if plugin == "" {
			if len(args) >= 1 {
				baseURL = args[0]
			}

			if len(args) > 1 {
				scope = args[1]
			}
		} else {
			if len(args) >= 1 {
				scope = args[0]
			}

			if len(args) > 1 {
				baseURL = args[1]
			}
		}

		for c.NextBlock() {
			switch c.Val() {
			case "database":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				database = c.Val()
			case "locale":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.Locale = c.Val()
			case "allow_commands":
				if !c.NextArg() {
					u.AllowCommands = true
					continue
				}

				u.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_edit":
				if !c.NextArg() {
					u.AllowEdit = true
					continue
				}

				u.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_new":
				if !c.NextArg() {
					u.AllowNew = true
					continue
				}

				u.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_publish":
				if !c.NextArg() {
					u.AllowPublish = true
					continue
				}

				u.AllowPublish, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "commands":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.Commands = strings.Split(c.Val(), " ")
			case "css":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				file := c.Val()
				css, err := ioutil.ReadFile(file)
				if err != nil {
					return nil, err
				}

				u.CSS = string(css)
			case "view_mode":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.ViewMode = c.Val()
				if u.ViewMode != filebrowser.MosaicViewMode && u.ViewMode != filebrowser.ListViewMode {
					return nil, c.ArgErr()
				}
			case "alternative_recaptcha":
				if !c.NextArg() {
					alterRecaptcha = true
					continue
				}

				alterRecaptcha, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "recaptcha_key":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				reCaptchaKey = c.Val()
			case "recaptcha_secret":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				reCaptchaSecret = c.Val()
			case "no_auth":
				if !c.NextArg() {
					noAuth = true
					continue
				}

				noAuth, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			}
		}

		caddyConf := httpserver.GetConfig(c)

		path := filepath.Join(caddy.AssetsPath(), "filemanager")
		err := os.MkdirAll(path, 0700)
		if err != nil {
			return nil, err
		}

		// if there is a database path and it is not absolute,
		// it will be relative to Caddy folder.
		if !filepath.IsAbs(database) && database != "" {
			database = filepath.Join(path, database)
		}

		// If there is no database path on the settings,
		// store one in .caddy/filemanager/name.db.
		if database == "" {
			// The name of the database is the hashed value of a string composed
			// by the host, address path and the baseurl of this File Manager
			// instance.
			hasher := md5.New()
			hasher.Write([]byte(caddyConf.Addr.Host + caddyConf.Addr.Path + baseURL))
			sha := hex.EncodeToString(hasher.Sum(nil))
			database = filepath.Join(path, sha+".db")

			fmt.Println("[WARNING] A database is going to be created for your File Manager instance at " + database +
				". It is highly recommended that you set the 'database' option to '" + sha + ".db'\n")
		}

		u.Scope = scope
		u.FileSystem = fileutils.Dir(scope)

		var db *storm.DB
		if stored, ok := databases[database]; ok {
			db = stored
		} else {
			db, err = storm.Open(database)
			databases[database] = db
		}

		if err != nil {
			return nil, err
		}

		recaptchaHost := "https://www.google.com"
		if alterRecaptcha {
			recaptchaHost = "https://recaptcha.net"
		}

		m := &filebrowser.FileBrowser{
			NoAuth:          noAuth,
			BaseURL:         "",
			PrefixURL:       "",
			ReCaptchaHost:   recaptchaHost,
			ReCaptchaKey:    reCaptchaKey,
			ReCaptchaSecret: reCaptchaSecret,
			DefaultUser:     u,
			Store: &filebrowser.Store{
				Config: bolt.ConfigStore{DB: db},
				Users:  bolt.UsersStore{DB: db},
				Share:  bolt.ShareStore{DB: db},
			},
			NewFS: func(scope string) filebrowser.FileSystem {
				return fileutils.Dir(scope)
			},
		}

		err = m.Setup()
		if err != nil {
			return nil, err
		}

		switch plugin {
		case "hugo":
			// Initialize the default settings for Hugo.
			hugo := &staticgen.Hugo{
				Root:        scope,
				Public:      filepath.Join(scope, "public"),
				Args:        []string{},
				CleanPublic: true,
			}

			// Attaches Hugo plugin to this file manager instance.
			err = m.Attach(hugo)
			if err != nil {
				return nil, err
			}
		case "jekyll":
			// Initialize the default settings for Jekyll.
			jekyll := &staticgen.Jekyll{
				Root:        scope,
				Public:      filepath.Join(scope, "_site"),
				Args:        []string{},
				CleanPublic: true,
			}

			// Attaches Hugo plugin to this file manager instance.
			err = m.Attach(jekyll)
			if err != nil {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		m.NoAuth = noAuth
		m.SetBaseURL(baseURL)
		m.SetPrefixURL(strings.TrimSuffix(caddyConf.Addr.Path, "/"))

		configs = append(configs, m)
	}

	return configs, nil
}
<commit_msg>fix: remove default rules (#455)<commit_after>package parser

import (
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/asdine/storm"
	"github.com/filebrowser/filebrowser"
	"github.com/filebrowser/filebrowser/bolt"
	"github.com/filebrowser/filebrowser/staticgen"
	"github.com/hacdias/fileutils"
	"github.com/mholt/caddy"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

var databases = map[string]*storm.DB{}

// Parse ...
func Parse(c *caddy.Controller, plugin string) ([]*filebrowser.FileBrowser, error) {
	var (
		configs []*filebrowser.FileBrowser
		err     error
	)

	for c.Next() {
		u := &filebrowser.User{
			Locale:        "en",
			AllowCommands: true,
			AllowEdit:     true,
			AllowNew:      true,
			AllowPublish:  true,
			Commands:      []string{"git", "svn", "hg"},
			CSS:           "",
			ViewMode:      "mosaic",
			Rules:         []*filebrowser.Rule{},
		}

		baseURL := "/"
		scope := "."
		database := ""
		noAuth := false
		alterRecaptcha := false
		reCaptchaKey := ""
		reCaptchaSecret := ""

		if plugin != "" {
			baseURL = "/admin"
		}

		// Get the baseURL and scope
		args := c.RemainingArgs()

		if plugin == "" {
			if len(args) >= 1 {
				baseURL = args[0]
			}

			if len(args) > 1 {
				scope = args[1]
			}
		} else {
			if len(args) >= 1 {
				scope = args[0]
			}

			if len(args) > 1 {
				baseURL = args[1]
			}
		}

		for c.NextBlock() {
			switch c.Val() {
			case "database":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				database = c.Val()
			case "locale":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.Locale = c.Val()
			case "allow_commands":
				if !c.NextArg() {
					u.AllowCommands = true
					continue
				}

				u.AllowCommands, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_edit":
				if !c.NextArg() {
					u.AllowEdit = true
					continue
				}

				u.AllowEdit, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_new":
				if !c.NextArg() {
					u.AllowNew = true
					continue
				}

				u.AllowNew, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "allow_publish":
				if !c.NextArg() {
					u.AllowPublish = true
					continue
				}

				u.AllowPublish, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "commands":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.Commands = strings.Split(c.Val(), " ")
			case "css":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				file := c.Val()
				css, err := ioutil.ReadFile(file)
				if err != nil {
					return nil, err
				}

				u.CSS = string(css)
			case "view_mode":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				u.ViewMode = c.Val()
				if u.ViewMode != filebrowser.MosaicViewMode && u.ViewMode != filebrowser.ListViewMode {
					return nil, c.ArgErr()
				}
			case "alternative_recaptcha":
				if !c.NextArg() {
					alterRecaptcha = true
					continue
				}

				alterRecaptcha, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			case "recaptcha_key":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				reCaptchaKey = c.Val()
			case "recaptcha_secret":
				if !c.NextArg() {
					return nil, c.ArgErr()
				}

				reCaptchaSecret = c.Val()
			case "no_auth":
				if !c.NextArg() {
					noAuth = true
					continue
				}

				noAuth, err = strconv.ParseBool(c.Val())
				if err != nil {
					return nil, err
				}
			}
		}

		caddyConf := httpserver.GetConfig(c)

		path := filepath.Join(caddy.AssetsPath(), "filemanager")
		err := os.MkdirAll(path, 0700)
		if err != nil {
			return nil, err
		}

		// if there is a database path and it is not absolute,
		// it will be relative to Caddy folder.
		if !filepath.IsAbs(database) && database != "" {
			database = filepath.Join(path, database)
		}

		// If there is no database path on the settings,
		// store one in .caddy/filemanager/name.db.
		if database == "" {
			// The name of the database is the hashed value of a string composed
			// by the host, address path and the baseurl of this File Manager
			// instance.
			hasher := md5.New()
			hasher.Write([]byte(caddyConf.Addr.Host + caddyConf.Addr.Path + baseURL))
			sha := hex.EncodeToString(hasher.Sum(nil))
			database = filepath.Join(path, sha+".db")

			fmt.Println("[WARNING] A database is going to be created for your File Manager instance at " + database +
				". It is highly recommended that you set the 'database' option to '" + sha + ".db'\n")
		}

		u.Scope = scope
		u.FileSystem = fileutils.Dir(scope)

		var db *storm.DB
		if stored, ok := databases[database]; ok {
			db = stored
		} else {
			db, err = storm.Open(database)
			databases[database] = db
		}

		if err != nil {
			return nil, err
		}

		recaptchaHost := "https://www.google.com"
		if alterRecaptcha {
			recaptchaHost = "https://recaptcha.net"
		}

		m := &filebrowser.FileBrowser{
			NoAuth:          noAuth,
			BaseURL:         "",
			PrefixURL:       "",
			ReCaptchaHost:   recaptchaHost,
			ReCaptchaKey:    reCaptchaKey,
			ReCaptchaSecret: reCaptchaSecret,
			DefaultUser:     u,
			Store: &filebrowser.Store{
				Config: bolt.ConfigStore{DB: db},
				Users:  bolt.UsersStore{DB: db},
				Share:  bolt.ShareStore{DB: db},
			},
			NewFS: func(scope string) filebrowser.FileSystem {
				return fileutils.Dir(scope)
			},
		}

		err = m.Setup()
		if err != nil {
			return nil, err
		}

		switch plugin {
		case "hugo":
			// Initialize the default settings for Hugo.
			hugo := &staticgen.Hugo{
				Root:        scope,
				Public:      filepath.Join(scope, "public"),
				Args:        []string{},
				CleanPublic: true,
			}

			// Attaches Hugo plugin to this file manager instance.
			err = m.Attach(hugo)
			if err != nil {
				return nil, err
			}
		case "jekyll":
			// Initialize the default settings for Jekyll.
			jekyll := &staticgen.Jekyll{
				Root:        scope,
				Public:      filepath.Join(scope, "_site"),
				Args:        []string{},
				CleanPublic: true,
			}

			// Attaches Hugo plugin to this file manager instance.
			err = m.Attach(jekyll)
			if err != nil {
				return nil, err
			}
		}

		if err != nil {
			return nil, err
		}

		m.NoAuth = noAuth
		m.SetBaseURL(baseURL)
		m.SetPrefixURL(strings.TrimSuffix(caddyConf.Addr.Path, "/"))

		configs = append(configs, m)
	}

	return configs, nil
}
<|endoftext|>


<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 (
	"bytes"
	"fmt"
	"strings"
	"time"

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("Kubelet", func() {
	f := framework.NewDefaultFramework("kubelet-test")
	var podClient *framework.PodClient
	BeforeEach(func() {
		podClient = f.PodClient()
	})
	Context("when scheduling a busybox command in a pod", func() {
		podName := "busybox-scheduling-" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, log output, default
			Description: By default the stdout and stderr from the process being executed in a pod MUST be sent to the pod's logs.
		*/
		framework.ConformanceIt("should print the output to logs [NodeConformance]", func() {
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"sh", "-c", "echo 'Hello World' ; sleep 240"},
						},
					},
				},
			})
			Eventually(func() string {
				sinceTime := metav1.NewTime(time.Now().Add(time.Duration(-1 * time.Hour)))
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{SinceTime: &sinceTime}).Stream()
				if err != nil {
					return ""
				}
				defer rc.Close()
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				return buf.String()
			}, time.Minute, time.Second*4).Should(Equal("Hello World\n"))
		})
	})
	Context("when scheduling a busybox command that always fails in a pod", func() {
		var podName string

		BeforeEach(func() {
			podName = "bin-false" + string(uuid.NewUUID())
			podClient.Create(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/false"},
						},
					},
				},
			})
		})

		/*
			Release : v1.13
			Testname: Kubelet, failed pod, terminated reason
			Description: Create a Pod with terminated state. Pod MUST have only one container. Container MUST be in terminated state and MUST have an terminated reason.
		*/
		framework.ConformanceIt("should have an terminated reason [NodeConformance]", func() {
			Eventually(func() error {
				podData, err := podClient.Get(podName, metav1.GetOptions{})
				if err != nil {
					return err
				}
				if len(podData.Status.ContainerStatuses) != 1 {
					return fmt.Errorf("expected only one container in the pod %q", podName)
				}
				contTerminatedState := podData.Status.ContainerStatuses[0].State.Terminated
				if contTerminatedState == nil {
					return fmt.Errorf("expected state to be terminated. Got pod status: %+v", podData.Status)
				}
				if contTerminatedState.ExitCode == 0 || contTerminatedState.Reason == "" {
					return fmt.Errorf("expected non-zero exitCode and non-empty terminated state reason. Got exitCode: %+v and terminated state reason: %+v", contTerminatedState.ExitCode, contTerminatedState.Reason)
				}
				return nil
			}, time.Minute, time.Second*4).Should(BeNil())
		})

		/*
			Release : v1.13
			Testname: Kubelet, failed pod, delete
			Description: Create a Pod with terminated state. This terminated pod MUST be able to be deleted.
		*/
		framework.ConformanceIt("should be possible to delete [NodeConformance]", func() {
			err := podClient.Delete(podName, &metav1.DeleteOptions{})
			Expect(err).To(BeNil(), fmt.Sprintf("Error deleting Pod %v", err))
		})
	})
	Context("when scheduling a busybox Pod with hostAliases", func() {
		podName := "busybox-host-aliases" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, hostAliases
			Description: Create a Pod with hostAliases and a container with command to output /etc/hosts entries. Pod's logs MUST have matching entries of specified hostAliases to the output of /etc/hosts entries.
		*/
		framework.ConformanceIt("should write entries to /etc/hosts [NodeConformance]", func() {
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/sh", "-c", "cat /etc/hosts; sleep 6000"},
						},
					},
					HostAliases: []v1.HostAlias{
						{
							IP:        "123.45.67.89",
							Hostnames: []string{"foo", "bar"},
						},
					},
				},
			})

			Eventually(func() error {
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{}).Stream()
				defer rc.Close()
				if err != nil {
					return err
				}
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				hostsFileContent := buf.String()

				if !strings.Contains(hostsFileContent, "123.45.67.89\tfoo\tbar") {
					return fmt.Errorf("expected hosts file to contain entries from HostAliases. Got:\n%+v", hostsFileContent)
				}

				return nil
			}, time.Minute, time.Second*4).Should(BeNil())
		})
	})
	Context("when scheduling a read only busybox container", func() {
		podName := "busybox-readonly-fs" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, pod with read only root file system
			Description: Create a Pod with security context set with ReadOnlyRootFileSystem set to true. The Pod then tries to write to the /file on the root, write operation to the root filesystem MUST fail as expected.
			This test is marked LinuxOnly since Windows does not support creating containers with read-only access.
		*/
		framework.ConformanceIt("should not write to root filesystem [LinuxOnly] [NodeConformance]", func() {
			isReadOnly := true
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/sh", "-c", "echo test > /file; sleep 240"},
							SecurityContext: &v1.SecurityContext{
								ReadOnlyRootFilesystem: &isReadOnly,
							},
						},
					},
				},
			})
			Eventually(func() string {
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{}).Stream()
				if err != nil {
					return ""
				}
				defer rc.Close()
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				return buf.String()
			}, time.Minute, time.Second*4).Should(Equal("/bin/sh: can't create /file: Read-only file system\n"))
		})
	})
})
<commit_msg>tests: Adds [LinuxOnly] tag to conformance test<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 (
	"bytes"
	"fmt"
	"strings"
	"time"

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

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = framework.KubeDescribe("Kubelet", func() {
	f := framework.NewDefaultFramework("kubelet-test")
	var podClient *framework.PodClient
	BeforeEach(func() {
		podClient = f.PodClient()
	})
	Context("when scheduling a busybox command in a pod", func() {
		podName := "busybox-scheduling-" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, log output, default
			Description: By default the stdout and stderr from the process being executed in a pod MUST be sent to the pod's logs.
		*/
		framework.ConformanceIt("should print the output to logs [NodeConformance]", func() {
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"sh", "-c", "echo 'Hello World' ; sleep 240"},
						},
					},
				},
			})
			Eventually(func() string {
				sinceTime := metav1.NewTime(time.Now().Add(time.Duration(-1 * time.Hour)))
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{SinceTime: &sinceTime}).Stream()
				if err != nil {
					return ""
				}
				defer rc.Close()
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				return buf.String()
			}, time.Minute, time.Second*4).Should(Equal("Hello World\n"))
		})
	})
	Context("when scheduling a busybox command that always fails in a pod", func() {
		var podName string

		BeforeEach(func() {
			podName = "bin-false" + string(uuid.NewUUID())
			podClient.Create(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/false"},
						},
					},
				},
			})
		})

		/*
			Release : v1.13
			Testname: Kubelet, failed pod, terminated reason
			Description: Create a Pod with terminated state. Pod MUST have only one container. Container MUST be in terminated state and MUST have an terminated reason.
		*/
		framework.ConformanceIt("should have an terminated reason [NodeConformance]", func() {
			Eventually(func() error {
				podData, err := podClient.Get(podName, metav1.GetOptions{})
				if err != nil {
					return err
				}
				if len(podData.Status.ContainerStatuses) != 1 {
					return fmt.Errorf("expected only one container in the pod %q", podName)
				}
				contTerminatedState := podData.Status.ContainerStatuses[0].State.Terminated
				if contTerminatedState == nil {
					return fmt.Errorf("expected state to be terminated. Got pod status: %+v", podData.Status)
				}
				if contTerminatedState.ExitCode == 0 || contTerminatedState.Reason == "" {
					return fmt.Errorf("expected non-zero exitCode and non-empty terminated state reason. Got exitCode: %+v and terminated state reason: %+v", contTerminatedState.ExitCode, contTerminatedState.Reason)
				}
				return nil
			}, time.Minute, time.Second*4).Should(BeNil())
		})

		/*
			Release : v1.13
			Testname: Kubelet, failed pod, delete
			Description: Create a Pod with terminated state. This terminated pod MUST be able to be deleted.
		*/
		framework.ConformanceIt("should be possible to delete [NodeConformance]", func() {
			err := podClient.Delete(podName, &metav1.DeleteOptions{})
			Expect(err).To(BeNil(), fmt.Sprintf("Error deleting Pod %v", err))
		})
	})
	Context("when scheduling a busybox Pod with hostAliases", func() {
		podName := "busybox-host-aliases" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, hostAliases
			Description: Create a Pod with hostAliases and a container with command to output /etc/hosts entries. Pod's logs MUST have matching entries of specified hostAliases to the output of /etc/hosts entries.
			Kubernetes mounts the /etc/hosts file into its containers, however, mounting individual files is not supported on Windows Containers. For this reason, this test is marked LinuxOnly.
		*/
		framework.ConformanceIt("should write entries to /etc/hosts [LinuxOnly] [NodeConformance]", func() {
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/sh", "-c", "cat /etc/hosts; sleep 6000"},
						},
					},
					HostAliases: []v1.HostAlias{
						{
							IP:        "123.45.67.89",
							Hostnames: []string{"foo", "bar"},
						},
					},
				},
			})

			Eventually(func() error {
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{}).Stream()
				defer rc.Close()
				if err != nil {
					return err
				}
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				hostsFileContent := buf.String()

				if !strings.Contains(hostsFileContent, "123.45.67.89\tfoo\tbar") {
					return fmt.Errorf("expected hosts file to contain entries from HostAliases. Got:\n%+v", hostsFileContent)
				}

				return nil
			}, time.Minute, time.Second*4).Should(BeNil())
		})
	})
	Context("when scheduling a read only busybox container", func() {
		podName := "busybox-readonly-fs" + string(uuid.NewUUID())

		/*
			Release : v1.13
			Testname: Kubelet, pod with read only root file system
			Description: Create a Pod with security context set with ReadOnlyRootFileSystem set to true. The Pod then tries to write to the /file on the root, write operation to the root filesystem MUST fail as expected.
			This test is marked LinuxOnly since Windows does not support creating containers with read-only access.
		*/
		framework.ConformanceIt("should not write to root filesystem [LinuxOnly] [NodeConformance]", func() {
			isReadOnly := true
			podClient.CreateSync(&v1.Pod{
				ObjectMeta: metav1.ObjectMeta{
					Name: podName,
				},
				Spec: v1.PodSpec{
					// Don't restart the Pod since it is expected to exit
					RestartPolicy: v1.RestartPolicyNever,
					Containers: []v1.Container{
						{
							Image:   framework.BusyBoxImage,
							Name:    podName,
							Command: []string{"/bin/sh", "-c", "echo test > /file; sleep 240"},
							SecurityContext: &v1.SecurityContext{
								ReadOnlyRootFilesystem: &isReadOnly,
							},
						},
					},
				},
			})
			Eventually(func() string {
				rc, err := podClient.GetLogs(podName, &v1.PodLogOptions{}).Stream()
				if err != nil {
					return ""
				}
				defer rc.Close()
				buf := new(bytes.Buffer)
				buf.ReadFrom(rc)
				return buf.String()
			}, time.Minute, time.Second*4).Should(Equal("/bin/sh: can't create /file: Read-only file system\n"))
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/oglematchers"
)

// AssertThat is identical to ExpectThat, except that in the event of failure
// it halts the currently running test immediately. It is thus useful for
// things like bounds checking:
//
//     someSlice := [...]
//     AssertEq(1, len(someSlice))  // Protects next line from panicking.
//     ExpectEq("taco", someSlice[0])
//
func AssertThat(
	x interface{},
	m oglematchers.Matcher,
	errorParts ...interface{}) {
	assertThat(x, m, errorParts...)
}

// Like expectThat, but returns an expectationResult.
func assertThat(
	x interface{},
	m oglematchers.Matcher,
	errorParts ...interface{}) expectationResult {
	res := expectThat(x, m, errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}

	return res
}

// assertThatError is a sentinel type that is used in a conspiracy between
// AssertThat and runTests. If runTests sees a *assertThatError as the value
// given to a panic() call, it will avoid printing the panic error.
type assertThatError struct {
}
<commit_msg>Fixed another such bug.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/oglematchers"
)

// AssertThat is identical to ExpectThat, except that in the event of failure
// it halts the currently running test immediately. It is thus useful for
// things like bounds checking:
//
//     someSlice := [...]
//     AssertEq(1, len(someSlice))  // Protects next line from panicking.
//     ExpectEq("taco", someSlice[0])
//
func AssertThat(
	x interface{},
	m oglematchers.Matcher,
	errorParts ...interface{}) {
	res := expectThat(x, m, errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// assertThatError is a sentinel type that is used in a conspiracy between
// AssertThat and runTests. If runTests sees a *assertThatError as the value
// given to a panic() call, it will avoid printing the panic error.
type assertThatError struct {
}
<|endoftext|>


<commit_before>package funnel

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"regexp"
	"sort"
	"strconv"
	"testing"
	"time"

	"vbom.ml/util/sortorder"
)

func TestRenameFileTimestamp(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	// Creating the active file
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		t.Fatal(err)
		return
	}

	// Rename the file
	_, err = renameFileTimestamp(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != 1 {
		t.Errorf("Incorrect no. of files created. Expected 1, Got %d", len(files))
	}
	for _, file := range files {
		regexStr := "[0-9]{2}_[0-9]{2}_[0-9]{2}.[0-9]{5}-[0-9]{4}_[0-9]{2}_[0-9]{2}.log"
		matched, err := regexp.MatchString(regexStr, file.Name())
		if err != nil {
			t.Fatal(err)
			return
		}
		if !matched {
			t.Errorf("Did not match. Expected \"%s\", Got \"%s\"", regexStr, file.Name())
		}
	}
}

func TestRenameFileSerial(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	// Create a whole lot of files
	numFiles := 13
	if err := populateFiles(cfg, numFiles); err != nil {
		t.Fatal(err)
		return
	}

	// Rename the files
	fileName, err := renameFileSerial(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}
	if fileName != cfg.ActiveFileName+".1" {
		t.Errorf("Incorrect active file name received. Expected %s, Got %s", cfg.ActiveFileName+".1", fileName)
	}
	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != numFiles+1 {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", numFiles+1, len(files))
	}
	var fileNames []string
	for _, file := range files {
		fileNames = append(fileNames, file.Name())
	}
	// Sorting the files in natural order
	sort.Sort(sortorder.Natural(fileNames))

	for i := 1; i <= len(fileNames); i++ {
		if fileNames[i-1] != cfg.ActiveFileName+"."+strconv.Itoa(i) {
			t.Errorf("Incorrect file created. Expected %s, Got %s", cfg.ActiveFileName+"."+strconv.Itoa(i), fileNames[i])
		}
	}
}

func TestRenameFileSerialGzip(t *testing.T) {
	cfg := setupRollupTest(t)
	cfg.Gzip = true
	defer os.RemoveAll(cfg.DirName)

	// Create a whole lot of files
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		t.Fatal(err)
		return
	}
	numFiles := 12
	for i := 1; i <= numFiles; i++ {
		err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz")).Run()
		if err != nil {
			t.Fatal(err)
			return
		}
	}

	// Rename the files
	fileName, err := renameFileSerial(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}
	if fileName != cfg.ActiveFileName+".1" {
		t.Errorf("Incorrect active file name received. Expected %s, Got %s", cfg.ActiveFileName+".1", fileName)
	}
	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != numFiles+1 {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", numFiles+1, len(files))
	}
	var fileNames []string
	for _, file := range files {
		fileNames = append(fileNames, file.Name())
	}
	// Sorting the files in natural order
	sort.Sort(sortorder.Natural(fileNames))

	for i := 2; i <= len(fileNames); i++ {
		if fileNames[i-1] != cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz" {
			t.Errorf("Incorrect file created. Expected %s, Got %s", cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz", fileNames[i])
		}
	}
}

func TestGzipFile(t *testing.T) {
	content := []byte("gzip test content")
	tmpfile, err := ioutil.TempFile("", "example")
	if err != nil {
		t.Fatal(err)
	}

	if _, err := tmpfile.Write(content); err != nil {
		t.Fatal(err)
	}
	if err := tmpfile.Close(); err != nil {
		t.Fatal(err)
	}

	gzipFile(tmpfile.Name())

	// check that the file is now deleted
	_, err = os.Open(tmpfile.Name())
	if os.IsExist(err) {
		t.Error(tmpfile.Name() + " exists. Expected to be deleted")
	}

	// check that a gzip file is there at the same place
	_, err = os.Open(tmpfile.Name() + ".gz")
	if os.IsNotExist(err) {
		t.Error(tmpfile.Name() + ".gz does not exist. Expected to exist")
	}
	os.Remove(tmpfile.Name() + ".gz")
}

func TestMaxFiles(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	if err := populateFiles(cfg, 13); err != nil {
		t.Fatal(err)
		return
	}

	deleteOldFiles(cfg)

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != cfg.MaxCount {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", cfg.MaxCount, len(files))
	}
}

func TestOldFiles(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)
	cfg.MaxCount = 100
	cfg.MaxAge = int64(2)

	if err := populateFiles(cfg, 13); err != nil {
		t.Fatal(err)
		return
	}

	// Sleeping for 5 seconds to let it exceed the max age
	time.Sleep(time.Second * 5)
	deleteOldFiles(cfg)

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != 1 {
		t.Errorf("Incorrect no. of files created. Expected 1, Got %d", len(files))
	}
}

// Internal helper functions
func setupRollupTest(t *testing.T) *Config {
	dir, err := ioutil.TempDir("", "test")
	if err != nil {
		t.Fatal(err)
		return nil
	}

	cfg := &Config{
		DirName:                  dir,
		ActiveFileName:           "out.log",
		RotationMaxLines:         40,
		RotationMaxBytes:         1000000,
		FlushingTimeIntervalSecs: 5,
		FileRenamePolicy:         "timestamp",
		MaxAge:                   int64(1 * 24 * 60 * 60),
		MaxCount:                 5,
	}
	return cfg
}

func populateFiles(cfg *Config, numFiles int) error {
	for i := 1; i <= numFiles; i++ {
		err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName+"."+strconv.Itoa(i))).Run()
		if err != nil {
			return err
		}
	}
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Minor reordering of file creation<commit_after>package funnel

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"regexp"
	"sort"
	"strconv"
	"testing"
	"time"

	"vbom.ml/util/sortorder"
)

func TestRenameFileTimestamp(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	// Creating the active file
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		t.Fatal(err)
		return
	}

	// Rename the file
	_, err = renameFileTimestamp(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != 1 {
		t.Errorf("Incorrect no. of files created. Expected 1, Got %d", len(files))
	}
	for _, file := range files {
		regexStr := "[0-9]{2}_[0-9]{2}_[0-9]{2}.[0-9]{5}-[0-9]{4}_[0-9]{2}_[0-9]{2}.log"
		matched, err := regexp.MatchString(regexStr, file.Name())
		if err != nil {
			t.Fatal(err)
			return
		}
		if !matched {
			t.Errorf("Did not match. Expected \"%s\", Got \"%s\"", regexStr, file.Name())
		}
	}
}

func TestRenameFileSerial(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	// Create a whole lot of files
	numFiles := 13
	if err := populateFiles(cfg, numFiles); err != nil {
		t.Fatal(err)
		return
	}

	// Rename the files
	fileName, err := renameFileSerial(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}
	if fileName != cfg.ActiveFileName+".1" {
		t.Errorf("Incorrect active file name received. Expected %s, Got %s", cfg.ActiveFileName+".1", fileName)
	}
	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != numFiles+1 {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", numFiles+1, len(files))
	}
	var fileNames []string
	for _, file := range files {
		fileNames = append(fileNames, file.Name())
	}
	// Sorting the files in natural order
	sort.Sort(sortorder.Natural(fileNames))

	for i := 1; i <= len(fileNames); i++ {
		if fileNames[i-1] != cfg.ActiveFileName+"."+strconv.Itoa(i) {
			t.Errorf("Incorrect file created. Expected %s, Got %s", cfg.ActiveFileName+"."+strconv.Itoa(i), fileNames[i])
		}
	}
}

func TestRenameFileSerialGzip(t *testing.T) {
	cfg := setupRollupTest(t)
	cfg.Gzip = true
	defer os.RemoveAll(cfg.DirName)

	// Create a whole lot of files
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		t.Fatal(err)
		return
	}
	numFiles := 12
	for i := 1; i <= numFiles; i++ {
		err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz")).Run()
		if err != nil {
			t.Fatal(err)
			return
		}
	}

	// Rename the files
	fileName, err := renameFileSerial(cfg)
	if err != nil {
		t.Fatal(err)
		return
	}
	if fileName != cfg.ActiveFileName+".1" {
		t.Errorf("Incorrect active file name received. Expected %s, Got %s", cfg.ActiveFileName+".1", fileName)
	}
	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != numFiles+1 {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", numFiles+1, len(files))
	}
	var fileNames []string
	for _, file := range files {
		fileNames = append(fileNames, file.Name())
	}
	// Sorting the files in natural order
	sort.Sort(sortorder.Natural(fileNames))

	for i := 2; i <= len(fileNames); i++ {
		if fileNames[i-1] != cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz" {
			t.Errorf("Incorrect file created. Expected %s, Got %s", cfg.ActiveFileName+"."+strconv.Itoa(i)+".gz", fileNames[i])
		}
	}
}

func TestGzipFile(t *testing.T) {
	content := []byte("gzip test content")
	tmpfile, err := ioutil.TempFile("", "example")
	if err != nil {
		t.Fatal(err)
	}

	if _, err := tmpfile.Write(content); err != nil {
		t.Fatal(err)
	}
	if err := tmpfile.Close(); err != nil {
		t.Fatal(err)
	}

	gzipFile(tmpfile.Name())

	// check that the file is now deleted
	_, err = os.Open(tmpfile.Name())
	if os.IsExist(err) {
		t.Error(tmpfile.Name() + " exists. Expected to be deleted")
	}

	// check that a gzip file is there at the same place
	_, err = os.Open(tmpfile.Name() + ".gz")
	if os.IsNotExist(err) {
		t.Error(tmpfile.Name() + ".gz does not exist. Expected to exist")
	}
	os.Remove(tmpfile.Name() + ".gz")
}

func TestMaxFiles(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)

	if err := populateFiles(cfg, 13); err != nil {
		t.Fatal(err)
		return
	}

	deleteOldFiles(cfg)

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != cfg.MaxCount {
		t.Errorf("Incorrect no. of files created. Expected %d, Got %d", cfg.MaxCount, len(files))
	}
}

func TestOldFiles(t *testing.T) {
	cfg := setupRollupTest(t)
	defer os.RemoveAll(cfg.DirName)
	cfg.MaxCount = 100
	cfg.MaxAge = int64(2)

	if err := populateFiles(cfg, 13); err != nil {
		t.Fatal(err)
		return
	}

	// Sleeping for 5 seconds to let it exceed the max age
	time.Sleep(time.Second * 5)
	deleteOldFiles(cfg)

	files, err := ioutil.ReadDir(cfg.DirName)
	if err != nil {
		t.Fatal(err)
		return
	}
	if len(files) != 1 {
		t.Errorf("Incorrect no. of files created. Expected 1, Got %d", len(files))
	}
}

// Internal helper functions
func setupRollupTest(t *testing.T) *Config {
	dir, err := ioutil.TempDir("", "test")
	if err != nil {
		t.Fatal(err)
		return nil
	}

	cfg := &Config{
		DirName:                  dir,
		ActiveFileName:           "out.log",
		RotationMaxLines:         40,
		RotationMaxBytes:         1000000,
		FlushingTimeIntervalSecs: 5,
		FileRenamePolicy:         "timestamp",
		MaxAge:                   int64(1 * 24 * 60 * 60),
		MaxCount:                 5,
	}
	return cfg
}

func populateFiles(cfg *Config, numFiles int) error {
	for i := numFiles; i > 0; i-- {
		err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName+"."+strconv.Itoa(i))).Run()
		if err != nil {
			return err
		}
	}
	err := exec.Command("touch", path.Join(cfg.DirName, cfg.ActiveFileName)).Run()
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package goberry_test

import (
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/EconomistDigitalSolutions/goberry"
	"github.com/gorilla/mux"
)

type Response struct {
	Message string
}

var (
	router   *mux.Router
	response Response
)

func TestMain(m *testing.M) {
	router = goberry.NewRouter("api.raml", "", "")
	retCode := m.Run()
	os.Exit(retCode)
}

func TestRootEndpoint(t *testing.T) {
	req, _ := http.NewRequest("GET", "/", nil)
	res := httptest.NewRecorder()

	router.ServeHTTP(res, req)
	json.NewDecoder(res.Body).Decode(&response)
	if response.Message != "RootGET" {
		t.Fatalf("expected RootGET as message, got %s\n", response.Message)
	}
}
<commit_msg>fix test<commit_after>package goberry_test

import (
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

	"github.com/EconomistDigitalSolutions/goberry"
	"github.com/gorilla/mux"
)

type Response struct {
	Message string
}

var (
	router   *mux.Router
	response Response
)

func TestMain(m *testing.M) {
	router = goberry.NewRouter("api.raml", "", "")
	retCode := m.Run()
	os.Exit(retCode)
}

func TestRootEndpoint(t *testing.T) {
	req, _ := http.NewRequest("GET", "/", nil)
	res := httptest.NewRecorder()

	router.ServeHTTP(res, req)
	if res.Code != http.StatusOK {
		t.Fatalf("expected 200 response got %d\n", res.Code)
	}
}
<|endoftext|>


<commit_before>package rpcc

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
)

var (
	// ErrStreamClosing indicates that the operation is illegal because
	// the stream is closing and there are no pending messages.
	ErrStreamClosing = errors.New("rpcc: the stream is closing")
)

type messageBuffer struct {
	ch    chan []byte
	mu    sync.Mutex
	queue [][]byte
}

func (b *messageBuffer) store(m []byte) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.ch) == 0 {
		select {
		case b.ch <- m:
			return
		default:
		}
	}
	b.queue = append(b.queue, m)
}

func (b *messageBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.queue) > 0 {
		select {
		case b.ch <- b.queue[0]:
			// Pop from queue and ensure references are freed.
			copied := copy(b.queue, b.queue[1:])
			b.queue[copied] = nil
			b.queue = b.queue[:copied]
		default:
		}
	}
}

func (b *messageBuffer) get() <-chan []byte {
	return b.ch
}

// Stream represents a stream of notifications for a certain method.
type Stream interface {
	// RecvMsg unmarshals pending messages onto m. Blocks until the
	// next message is received, context is canceled or stream is
	// closed.
	RecvMsg(m interface{}) error
	// Close closes the stream and no new messages will be received.
	// RecvMsg will return ErrStreamClosing once all pending messages
	// have been received.
	Close() error
}

// NewStream creates a new stream that listens to notifications from the
// RPC server. This function is called by generated code.
func NewStream(ctx context.Context, method string, conn *Conn) (Stream, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	s := &streamClient{userCtx: ctx, done: make(chan struct{})}
	s.msgBuf.ch = make(chan []byte, 1)
	s.ctx, s.cancel = context.WithCancel(context.Background())

	var err error
	s.remove, err = conn.listen(method, s)
	if err != nil {
		return nil, err
	}

	go func() {
		select {
		case <-s.ctx.Done():
		case <-conn.ctx.Done():
			s.close(ErrConnClosing)
		case <-ctx.Done():
			s.close(ctx.Err())
		}
	}()

	return s, nil
}

type streamClient struct {
	userCtx context.Context
	ctx     context.Context
	cancel  context.CancelFunc

	// msgBuf stores all incoming messages
	// until they are ready to be received.
	msgBuf messageBuffer

	mu     sync.Mutex // Protects following.
	remove func()     // Unsubscribes from messages.

	done chan struct{} // Protects err.
	err  error
}

func (s *streamClient) RecvMsg(m interface{}) (err error) {
	var data []byte

	userCancelled := func() bool {
		select {
		case <-s.userCtx.Done():
			return true
		default:
			return false
		}
	}

	select {
	case <-s.userCtx.Done():
		return s.userCtx.Err()
	case <-s.ctx.Done():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}

		// Send all messages before returning error.
		select {
		case data = <-s.msgBuf.get():
		default:
			<-s.done
			return s.err
		}
	case data = <-s.msgBuf.get():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}
	}

	// Preload the next message.
	s.msgBuf.load()

	return json.Unmarshal(data, m)
}

// Close closes the stream client.
func (s *streamClient) close(err error) error {
	s.mu.Lock()
	remove := s.remove
	s.remove = nil
	s.mu.Unlock()

	if remove == nil {
		return errors.New("rpcc: the stream is already closed")
	}

	if err == nil {
		err = ErrStreamClosing
	}

	// Unsubscribe first to prevent incoming messages.
	remove()
	s.cancel()
	s.err = err
	close(s.done)

	return nil
}

// Close closes the stream client.
func (s *streamClient) Close() error {
	return s.close(nil)
}

// streamClients handles multiple instances of streamClient and
// enables sending of the same message to multiple clients.
type streamClients struct {
	mu      sync.Mutex
	seq     uint64
	clients map[uint64]*streamClient
}

func newStreamService() *streamClients {
	return &streamClients{
		clients: make(map[uint64]*streamClient),
	}
}

func (s *streamClients) add(client *streamClient) (seq uint64) {
	s.mu.Lock()
	seq = s.seq
	s.seq++
	s.clients[seq] = client
	s.mu.Unlock()
	return seq
}

func (s *streamClients) remove(seq uint64) {
	s.mu.Lock()
	delete(s.clients, seq)
	s.mu.Unlock()
}

func (s *streamClients) send(args []byte) {
	s.mu.Lock()
	for _, client := range s.clients {
		client.msgBuf.store(args)
	}
	s.mu.Unlock()
}
<commit_msg>rpcc: Simplify messageBuffer load<commit_after>package rpcc

import (
	"context"
	"encoding/json"
	"errors"
	"sync"
)

var (
	// ErrStreamClosing indicates that the operation is illegal because
	// the stream is closing and there are no pending messages.
	ErrStreamClosing = errors.New("rpcc: the stream is closing")
)

type messageBuffer struct {
	ch    chan []byte
	mu    sync.Mutex
	queue [][]byte
}

func (b *messageBuffer) store(m []byte) {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.ch) == 0 {
		select {
		case b.ch <- m:
			return
		default:
		}
	}
	b.queue = append(b.queue, m)
}

func (b *messageBuffer) load() {
	b.mu.Lock()
	defer b.mu.Unlock()
	if len(b.queue) > 0 {
		select {
		case b.ch <- b.queue[0]:
			b.queue[0] = nil // Remove reference from underlying array.
			b.queue = b.queue[1:]
		default:
		}
	}
}

func (b *messageBuffer) get() <-chan []byte {
	return b.ch
}

// Stream represents a stream of notifications for a certain method.
type Stream interface {
	// RecvMsg unmarshals pending messages onto m. Blocks until the
	// next message is received, context is canceled or stream is
	// closed.
	RecvMsg(m interface{}) error
	// Close closes the stream and no new messages will be received.
	// RecvMsg will return ErrStreamClosing once all pending messages
	// have been received.
	Close() error
}

// NewStream creates a new stream that listens to notifications from the
// RPC server. This function is called by generated code.
func NewStream(ctx context.Context, method string, conn *Conn) (Stream, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	s := &streamClient{userCtx: ctx, done: make(chan struct{})}
	s.msgBuf.ch = make(chan []byte, 1)
	s.ctx, s.cancel = context.WithCancel(context.Background())

	var err error
	s.remove, err = conn.listen(method, s)
	if err != nil {
		return nil, err
	}

	go func() {
		select {
		case <-s.ctx.Done():
		case <-conn.ctx.Done():
			s.close(ErrConnClosing)
		case <-ctx.Done():
			s.close(ctx.Err())
		}
	}()

	return s, nil
}

type streamClient struct {
	userCtx context.Context
	ctx     context.Context
	cancel  context.CancelFunc

	// msgBuf stores all incoming messages
	// until they are ready to be received.
	msgBuf messageBuffer

	mu     sync.Mutex // Protects following.
	remove func()     // Unsubscribes from messages.

	done chan struct{} // Protects err.
	err  error
}

func (s *streamClient) RecvMsg(m interface{}) (err error) {
	var data []byte

	userCancelled := func() bool {
		select {
		case <-s.userCtx.Done():
			return true
		default:
			return false
		}
	}

	select {
	case <-s.userCtx.Done():
		return s.userCtx.Err()
	case <-s.ctx.Done():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}

		// Send all messages before returning error.
		select {
		case data = <-s.msgBuf.get():
		default:
			<-s.done
			return s.err
		}
	case data = <-s.msgBuf.get():
		// Give precedence for user cancellation.
		if userCancelled() {
			return s.userCtx.Err()
		}
	}

	// Preload the next message.
	s.msgBuf.load()

	return json.Unmarshal(data, m)
}

// Close closes the stream client.
func (s *streamClient) close(err error) error {
	s.mu.Lock()
	remove := s.remove
	s.remove = nil
	s.mu.Unlock()

	if remove == nil {
		return errors.New("rpcc: the stream is already closed")
	}

	if err == nil {
		err = ErrStreamClosing
	}

	// Unsubscribe first to prevent incoming messages.
	remove()
	s.cancel()
	s.err = err
	close(s.done)

	return nil
}

// Close closes the stream client.
func (s *streamClient) Close() error {
	return s.close(nil)
}

// streamClients handles multiple instances of streamClient and
// enables sending of the same message to multiple clients.
type streamClients struct {
	mu      sync.Mutex
	seq     uint64
	clients map[uint64]*streamClient
}

func newStreamService() *streamClients {
	return &streamClients{
		clients: make(map[uint64]*streamClient),
	}
}

func (s *streamClients) add(client *streamClient) (seq uint64) {
	s.mu.Lock()
	seq = s.seq
	s.seq++
	s.clients[seq] = client
	s.mu.Unlock()
	return seq
}

func (s *streamClients) remove(seq uint64) {
	s.mu.Lock()
	delete(s.clients, seq)
	s.mu.Unlock()
}

func (s *streamClients) send(args []byte) {
	s.mu.Lock()
	for _, client := range s.clients {
		client.msgBuf.store(args)
	}
	s.mu.Unlock()
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSPolicyAttachment_basic(t *testing.T) {
	var out iam.ListEntitiesForPolicyOutput

	user1 := fmt.Sprintf("test-user-%d", acctest.RandInt())
	user2 := fmt.Sprintf("test-user-%d", acctest.RandInt())
	user3 := fmt.Sprintf("test-user-%d", acctest.RandInt())

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSPolicyAttachmentDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPolicyAttachConfig(user1),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-attach", 3, &out),
					testAccCheckAWSPolicyAttachmentAttributes([]string{user1}, []string{"test-role"}, []string{"test-group"}, &out),
				),
			},
			resource.TestStep{
				Config: testAccAWSPolicyAttachConfigUpdate(user1, user2, user3),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-attach", 6, &out),
					testAccCheckAWSPolicyAttachmentAttributes([]string{user3, user3}, []string{"test-role2", "test-role3"}, []string{"test-group2", "test-group3"}, &out),
				),
			},
		},
	})
}

func TestAccAWSPolicyAttachment_paginatedEntities(t *testing.T) {
	var out iam.ListEntitiesForPolicyOutput

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSPolicyAttachmentDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPolicyPaginatedAttachConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-paginated-attach", 101, &out),
				),
			},
		},
	})
}

func testAccCheckAWSPolicyAttachmentDestroy(s *terraform.State) error {
	return nil
}

func testAccCheckAWSPolicyAttachmentExists(n string, c int64, out *iam.ListEntitiesForPolicyOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No policy name is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).iamconn
		arn := rs.Primary.Attributes["policy_arn"]

		resp, err := conn.GetPolicy(&iam.GetPolicyInput{
			PolicyArn: aws.String(arn),
		})
		if err != nil {
			return fmt.Errorf("Error: Policy (%s) not found", n)
		}
		if c != *resp.Policy.AttachmentCount {
			return fmt.Errorf("Error: Policy (%s) has wrong number of entities attached on initial creation", n)
		}
		resp2, err := conn.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{
			PolicyArn: aws.String(arn),
		})
		if err != nil {
			return fmt.Errorf("Error: Failed to get entities for Policy (%s)", arn)
		}

		*out = *resp2
		return nil
	}
}

func testAccCheckAWSPolicyAttachmentAttributes(users []string, roles []string, groups []string, out *iam.ListEntitiesForPolicyOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		uc := len(users)
		rc := len(roles)
		gc := len(groups)

		for _, u := range users {
			for _, pu := range out.PolicyUsers {
				if u == *pu.UserName {
					uc--
				}
			}
		}
		for _, r := range roles {
			for _, pr := range out.PolicyRoles {
				if r == *pr.RoleName {
					rc--
				}
			}
		}
		for _, g := range groups {
			for _, pg := range out.PolicyGroups {
				if g == *pg.GroupName {
					gc--
				}
			}
		}
		if uc != 0 || rc != 0 || gc != 0 {
			return fmt.Errorf("Error: Number of attached users, roles, or groups was incorrect:\n expected %d users and found %d\nexpected %d roles and found %d\nexpected %d groups and found %d", len(users), len(users)-uc, len(roles), len(roles)-rc, len(groups), len(groups)-gc)
		}
		return nil
	}
}

func testAccAWSPolicyAttachConfig(u1 string) string {
	return fmt.Sprintf(`
resource "aws_iam_user" "user" {
    name = "%s"
}
resource "aws_iam_role" "role" {
    name = "test-role"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
resource "aws_iam_group" "group" {
    name = "test-group"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-attach" {
    name = "test-attachment"
    users = ["${aws_iam_user.user.name}"]
    roles = ["${aws_iam_role.role.name}"]
    groups = ["${aws_iam_group.group.name}"]
    policy_arn = "${aws_iam_policy.policy.arn}"
}`, u1)
}

func testAccAWSPolicyAttachConfigUpdate(u1, u2, u3 string) string {
	return fmt.Sprintf(`
resource "aws_iam_user" "user" {
    name = "%s"
}
resource "aws_iam_user" "user2" {
    name = "%s"
}
resource "aws_iam_user" "user3" {
    name = "%s"
}
resource "aws_iam_role" "role" {
    name = "test-role"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role" "role2" {
    name = "test-role2"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

}
resource "aws_iam_role" "role3" {
    name = "test-role3"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

}
resource "aws_iam_group" "group" {
    name = "test-group"
}
resource "aws_iam_group" "group2" {
    name = "test-group2"
}
resource "aws_iam_group" "group3" {
    name = "test-group3"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-attach" {
    name = "test-attachment"
    users = [
        "${aws_iam_user.user2.name}",
        "${aws_iam_user.user3.name}"
    ]
    roles = [
        "${aws_iam_role.role2.name}",
        "${aws_iam_role.role3.name}"
    ]
    groups = [
        "${aws_iam_group.group2.name}",
        "${aws_iam_group.group3.name}"
    ]
    policy_arn = "${aws_iam_policy.policy.arn}"
}`, u1, u2, u3)
}

const testAccAWSPolicyPaginatedAttachConfig = `
resource "aws_iam_user" "user" {
    count = 101
    name = "${format("paged-test-user-%d", count.index + 1)}"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-paginated-attach" {
    name = "test-attachment"
    users = ["${aws_iam_user.user.*.name}"]
    policy_arn = "${aws_iam_policy.policy.arn}"
}
`
<commit_msg>add randomness to iam policy attachments<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/iam"
	randomprovider "github.com/hashicorp/terraform/builtin/providers/random"
	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

func TestAccAWSPolicyAttachment_basic(t *testing.T) {
	var out iam.ListEntitiesForPolicyOutput

	user1 := fmt.Sprintf("test-user-%d", acctest.RandInt())
	user2 := fmt.Sprintf("test-user-%d", acctest.RandInt())
	user3 := fmt.Sprintf("test-user-%d", acctest.RandInt())

	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAWSPolicyAttachmentDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPolicyAttachConfig(user1),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-attach", 3, &out),
					testAccCheckAWSPolicyAttachmentAttributes([]string{user1}, []string{"test-role"}, []string{"test-group"}, &out),
				),
			},
			resource.TestStep{
				Config: testAccAWSPolicyAttachConfigUpdate(user1, user2, user3),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-attach", 6, &out),
					testAccCheckAWSPolicyAttachmentAttributes([]string{user3, user3}, []string{"test-role2", "test-role3"}, []string{"test-group2", "test-group3"}, &out),
				),
			},
		},
	})
}

func TestAccAWSPolicyAttachment_paginatedEntities(t *testing.T) {
	var out iam.ListEntitiesForPolicyOutput

	resource.Test(t, resource.TestCase{
		PreCheck: func() { testAccPreCheck(t) },
		Providers: map[string]terraform.ResourceProvider{
			"aws":    testAccProvider,
			"random": randomprovider.Provider(),
		},
		CheckDestroy: testAccCheckAWSPolicyAttachmentDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAWSPolicyPaginatedAttachConfig,
				Check: resource.ComposeTestCheckFunc(
					testAccCheckAWSPolicyAttachmentExists("aws_iam_policy_attachment.test-paginated-attach", 101, &out),
				),
			},
		},
	})
}

func testAccCheckAWSPolicyAttachmentDestroy(s *terraform.State) error {
	return nil
}

func testAccCheckAWSPolicyAttachmentExists(n string, c int64, out *iam.ListEntitiesForPolicyOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No policy name is set")
		}

		conn := testAccProvider.Meta().(*AWSClient).iamconn
		arn := rs.Primary.Attributes["policy_arn"]

		resp, err := conn.GetPolicy(&iam.GetPolicyInput{
			PolicyArn: aws.String(arn),
		})
		if err != nil {
			return fmt.Errorf("Error: Policy (%s) not found", n)
		}
		if c != *resp.Policy.AttachmentCount {
			return fmt.Errorf("Error: Policy (%s) has wrong number of entities attached on initial creation", n)
		}
		resp2, err := conn.ListEntitiesForPolicy(&iam.ListEntitiesForPolicyInput{
			PolicyArn: aws.String(arn),
		})
		if err != nil {
			return fmt.Errorf("Error: Failed to get entities for Policy (%s)", arn)
		}

		*out = *resp2
		return nil
	}
}

func testAccCheckAWSPolicyAttachmentAttributes(users []string, roles []string, groups []string, out *iam.ListEntitiesForPolicyOutput) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		uc := len(users)
		rc := len(roles)
		gc := len(groups)

		for _, u := range users {
			for _, pu := range out.PolicyUsers {
				if u == *pu.UserName {
					uc--
				}
			}
		}
		for _, r := range roles {
			for _, pr := range out.PolicyRoles {
				if r == *pr.RoleName {
					rc--
				}
			}
		}
		for _, g := range groups {
			for _, pg := range out.PolicyGroups {
				if g == *pg.GroupName {
					gc--
				}
			}
		}
		if uc != 0 || rc != 0 || gc != 0 {
			return fmt.Errorf("Error: Number of attached users, roles, or groups was incorrect:\n expected %d users and found %d\nexpected %d roles and found %d\nexpected %d groups and found %d", len(users), len(users)-uc, len(roles), len(roles)-rc, len(groups), len(groups)-gc)
		}
		return nil
	}
}

func testAccAWSPolicyAttachConfig(u1 string) string {
	return fmt.Sprintf(`
resource "aws_iam_user" "user" {
    name = "%s"
}
resource "aws_iam_role" "role" {
    name = "test-role"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}
resource "aws_iam_group" "group" {
    name = "test-group"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-attach" {
    name = "test-attachment"
    users = ["${aws_iam_user.user.name}"]
    roles = ["${aws_iam_role.role.name}"]
    groups = ["${aws_iam_group.group.name}"]
    policy_arn = "${aws_iam_policy.policy.arn}"
}`, u1)
}

func testAccAWSPolicyAttachConfigUpdate(u1, u2, u3 string) string {
	return fmt.Sprintf(`
resource "aws_iam_user" "user" {
    name = "%s"
}
resource "aws_iam_user" "user2" {
    name = "%s"
}
resource "aws_iam_user" "user3" {
    name = "%s"
}
resource "aws_iam_role" "role" {
    name = "test-role"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

resource "aws_iam_role" "role2" {
    name = "test-role2"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

}
resource "aws_iam_role" "role3" {
    name = "test-role3"
	  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF

}
resource "aws_iam_group" "group" {
    name = "test-group"
}
resource "aws_iam_group" "group2" {
    name = "test-group2"
}
resource "aws_iam_group" "group3" {
    name = "test-group3"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-attach" {
    name = "test-attachment"
    users = [
        "${aws_iam_user.user2.name}",
        "${aws_iam_user.user3.name}"
    ]
    roles = [
        "${aws_iam_role.role2.name}",
        "${aws_iam_role.role3.name}"
    ]
    groups = [
        "${aws_iam_group.group2.name}",
        "${aws_iam_group.group3.name}"
    ]
    policy_arn = "${aws_iam_policy.policy.arn}"
}`, u1, u2, u3)
}

const testAccAWSPolicyPaginatedAttachConfig = `
resource "random_id" "user_id" {
  byte_length = 10
}

resource "aws_iam_user" "user" {
    count = 101
    name = "${format("paged-test-user-${random_id.user_id.hex}-%d", count.index + 1)}"
}

resource "aws_iam_policy" "policy" {
    name = "test-policy"
    description = "A test policy"
    policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "iam:ChangePassword"
      ],
      "Resource": "*",
      "Effect": "Allow"
    }
  ]
}
EOF
}

resource "aws_iam_policy_attachment" "test-paginated-attach" {
    name = "test-attachment"
    users = ["${aws_iam_user.user.*.name}"]
    policy_arn = "${aws_iam_policy.policy.arn}"
}
`
<|endoftext|>


<commit_before>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event
// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<commit_msg>Added doc<commit_after>package gochecks

import (
	"fmt"
	"net"
	"strings"
	"time"

	"io/ioutil"
	"net/http"
	"net/url"

	"github.com/streadway/amqp"
	"github.com/tatsushid/go-fastping"
)

import (
	"database/sql"

	_ "github.com/go-sql-driver/mysql"
)

const (
	sysName     = "1.3.6.1.2.1.1.5.0"
	maxPingTime = 4 * time.Second
)

// CheckFunction type for a function that return a event
type CheckFunction func() Event
// MultiCheckFunction type for a function that return an array of events
type MultiCheckFunction func() []Event

// Tags  returns a new check function that adds the given tags to the result
// generated by the initial check function
func (f CheckFunction) Tags(tags ...string) CheckFunction {
	return func() Event {
		result := f()
		result.Tags = tags
		return result
	}
}

// Attributtes  returns a new check function that adds the attributes map to the result
// generated by the initial check function
func (f CheckFunction) Attributes(attributes map[string]string) CheckFunction {
	return func() Event {
		result := f()
		result.Attributes = attributes
		return result
	}
}

// Attributtes  returns a new check function that adds the given TTL time (in seconds) to the result
// generated by the initial check function
func (f CheckFunction) TTL(ttl float32) CheckFunction {
	return func() Event {
		result := f()
		result.TTL = ttl
		return result
	}
}

// Retry returns a new check funciont that execute the given function up to a given retry times or
// until the first execution that returns a ok (whichever comes first). The new function will return
// the event of the last execution
func (f CheckFunction) Retry(times int, sleep time.Duration) CheckFunction {
	return func() Event {
		var result Event
		for i := 0; i < times; i++ {
			result = f()
			if result.State == "ok" {
				return result
			}
			time.Sleep(sleep)
		}
		return result
	}
}

// NewPingChecker returns a check function that can check if a host answer to a ICMP Ping
func NewPingChecker(host, service, ip string) CheckFunction {
	return func() Event {
		var retRtt time.Duration
		var result = Event{Host: host, Service: service, State: "critical"}

		p := fastping.NewPinger()
		p.MaxRTT = maxPingTime
		ra, err := net.ResolveIPAddr("ip4:icmp", ip)
		if err != nil {
			result.Description = err.Error()
		}

		p.AddIPAddr(ra)
		p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
			result.State = "ok"
			result.Metric = float32(retRtt.Nanoseconds() / 1e6)
		}

		err = p.Run()
		if err != nil {
			result.Description = err.Error()
		}
		return result
	}
}

// NewTCPPortChecker returns a check function that can check if a host have a tcp port open
func NewTCPPortChecker(host, service, ip string, port int, timeout time.Duration) CheckFunction {
	return func() Event {
		var err error
		var conn net.Conn

		var t1 = time.Now()
		conn, err = net.DialTimeout("tcp", fmt.Sprintf("%s:%d", ip, port), timeout)
		if err == nil {
			conn.Close()
			milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
			return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "critical"}
	}
}

// ValidateHTTPResponseFunction function type that should validate a http response and return the state (ok, critical, warning) and error description for a check. (Used with NewGenericHTTPChecker)
type ValidateHTTPResponseFunction func(resp *http.Response) (state, description string)

func BodyGreaterThan(minLength int) ValidateHTTPResponseFunction {
	return func(httpResp *http.Response) (state, description string) {
		if httpResp.StatusCode != 200 {
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		}
		if httpResp.Body == nil {
			return "critical", fmt.Sprintf("Empty body")
		}
		body, err := ioutil.ReadAll(httpResp.Body)
		if err != nil {
			return "critical", fmt.Sprintf("Error geting body")
		}
		if len(body) < minLength {
			return "critical", fmt.Sprintf("Obtained %d bytes, expected more than %d", len(body), minLength)
		}
		return "ok", ""
	}
}

func NewGenericHTTPChecker(host, service, url string, validationFunc ValidateHTTPResponseFunction) CheckFunction {
	return func() Event {
		var t1 = time.Now()

		response, err := http.Get(url)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		result := Event{Host: host, Service: service, State: "critical", Metric: milliseconds}
		if err != nil {
			result.Description = err.Error()
		} else {
			if response.Body != nil {
				defer response.Body.Close()
			}
			result.State, result.Description = validationFunc(response)
		}
		return result
	}
}

// NewHTTPChecker returns a check function that get a given url and validate if the return code is the expected one
func NewHTTPChecker(host, service, url string, expectedStatusCode int) CheckFunction {
	return NewGenericHTTPChecker(host, service, url,
		func(httpResp *http.Response) (string, string) {
			if httpResp.StatusCode == expectedStatusCode {
				return "ok", ""
			}
			return "critical", fmt.Sprintf("Response %d", httpResp.StatusCode)
		})
}

type SnmpCheckerConf struct {
	retries    int
	timeout    time.Duration
	oidToCheck string
}

var DefaultSnmpCheckConf = SnmpCheckerConf{
	retries:    1,
	timeout:    1 * time.Second,
	oidToCheck: sysName,
}

// NewSnmpChecker returns a check function that check if a host respond to a snmp get query
func NewSnmpChecker(host, service, ip, community string, conf SnmpCheckerConf) CheckFunction {
	return func() Event {

		_, err := snmpGet(ip, community, []string{conf.oidToCheck}, conf.timeout, conf.retries)
		if err == nil {
			return Event{Host: host, Service: service, State: "ok", Description: err.Error()}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

// NewC4CMTSTempChecker returns a check function that check if any of the slot of a Arris C4 CMTS have a temperature above a given max
func NewC4CMTSTempChecker(host, service, ip, community string, maxAllowedTemp int) CheckFunction {
	return func() Event {

		result, err := snmpWalk(ip, community, "1.3.6.1.4.1.4998.1.1.10.1.4.2.1.29", 2*time.Second, 1)

		if err == nil {
			max := 0
			for _, r := range result {
				if r.Value.(int) != 999 && r.Value.(int) > max {
					max = r.Value.(int)
				}
			}
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func getMaxValueFromSnmpWalk(oid, ip, community string) (uint, error) {
	result, err := snmpWalk(ip, community, oid, 2*time.Second, 1)
	if err == nil {
		max := uint(0)
		for _, r := range result {
			if r.Value.(uint) > max {
				max = r.Value.(uint)
			}
		}
		return max, nil
	}
	return 0, err
}

func NewJuniperTempChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.7", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewJuniperCPUChecker(host, service, ip, community string, maxAllowedTemp uint) CheckFunction {
	return func() Event {
		max, err := getMaxValueFromSnmpWalk("1.3.6.1.4.1.2636.3.1.13.1.8", ip, community)
		if err == nil {
			var state = "critical"
			if max < maxAllowedTemp {
				state = "ok"
			}
			return Event{Host: host, Service: service, State: state, Metric: float32(max)}
		}
		return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
	}
}

func NewRabbitMQQueueLenCheck(host, service, amqpuri, queue string, max int) CheckFunction {
	return func() Event {
		result := Event{Host: host, Service: service}

		conn, err := amqp.Dial(amqpuri)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		ch, err := conn.Channel()
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}
		defer ch.Close()
		defer conn.Close()

		queueInfo, err := ch.QueueInspect(queue)
		if err != nil {
			result.State = "critical"
			result.Description = err.Error()
			return result
		}

		var state = "critical"
		if queueInfo.Messages <= max {
			state = "ok"
		}
		return Event{Host: host, Service: service, State: state, Metric: float32(queueInfo.Messages)}
	}
}

func NewMysqlConnectionCheck(host, service, mysqluri string) CheckFunction {
	return func() Event {
		u, err := url.Parse(mysqluri)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}

		if u.User == nil {
			return Event{Host: host, Service: service, State: "critical", Description: "No user defined"}
		}
		password, hasPassword := u.User.Password()
		if !hasPassword {
			return Event{Host: host, Service: service, State: "critical", Description: "No password defined"}
		}
		hostAndPort := u.Host
		if !strings.Contains(hostAndPort, ":") {
			hostAndPort = hostAndPort + ":3306"
		}
		var t1 = time.Now()
		con, err := sql.Open("mysql", u.User.Username()+":"+password+"@"+"tcp("+hostAndPort+")"+u.Path)
		defer con.Close()
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error()}
		}
		q := `select CURTIME()`
		row := con.QueryRow(q)
		var date string
		err = row.Scan(&date)
		milliseconds := float32((time.Now().Sub(t1)).Nanoseconds() / 1e6)
		if err != nil {
			return Event{Host: host, Service: service, State: "critical", Description: err.Error(), Metric: milliseconds}
		}
		return Event{Host: host, Service: service, State: "ok", Metric: milliseconds}
	}
}

type ObtainMetricFunction func() float32
type CalculateStateFunction func(float32) string

func NewGenericCheck(host, service string, metricFunc ObtainMetricFunction, stateFunc CalculateStateFunction) CheckFunction {
	return func() Event {
		value := metricFunc()
		var state = stateFunc(value)
		return Event{Host: host, Service: service, State: state, Metric: value}
	}
}
<|endoftext|>