<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"
	"text/template"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

type Suite string

const (
	E2E     Suite = "e2e"
	NodeE2E Suite = "node e2e"
)

var CurrentSuite Suite

// CommonImageWhiteList is the list of images used in common test. These images should be prepulled
// before a tests starts, so that the tests won't fail due image pulling flakes. Currently, this is
// only used by node e2e test.
// TODO(random-liu): Change the image puller pod to use similar mechanism.
var CommonImageWhiteList = sets.NewString(
	imageutils.GetE2EImage(imageutils.Agnhost),
	imageutils.GetE2EImage(imageutils.AuditProxy),
	imageutils.GetE2EImage(imageutils.BusyBox),
	imageutils.GetE2EImage(imageutils.EntrypointTester),
	imageutils.GetE2EImage(imageutils.IpcUtils),
	imageutils.GetE2EImage(imageutils.Mounttest),
	imageutils.GetE2EImage(imageutils.MounttestUser),
	imageutils.GetE2EImage(imageutils.Netexec),
	imageutils.GetE2EImage(imageutils.Nginx),
	imageutils.GetE2EImage(imageutils.ServeHostname),
	imageutils.GetE2EImage(imageutils.TestWebserver),
	imageutils.GetE2EImage(imageutils.VolumeNFSServer),
	imageutils.GetE2EImage(imageutils.VolumeGlusterServer),
	imageutils.GetE2EImage(imageutils.Net),
)

type testImagesStruct struct {
	AgnhostImage      string
	BusyBoxImage      string
	GBFrontendImage   string
	GBRedisSlaveImage string
	KittenImage       string
	MounttestImage    string
	NautilusImage     string
	NginxImage        string
	NginxNewImage     string
	PauseImage        string
	RedisImage        string
}

var testImages testImagesStruct

func init() {
	testImages = testImagesStruct{
		imageutils.GetE2EImage(imageutils.Agnhost),
		imageutils.GetE2EImage(imageutils.BusyBox),
		imageutils.GetE2EImage(imageutils.GBFrontend),
		imageutils.GetE2EImage(imageutils.GBRedisSlave),
		imageutils.GetE2EImage(imageutils.Kitten),
		imageutils.GetE2EImage(imageutils.Mounttest),
		imageutils.GetE2EImage(imageutils.Nautilus),
		imageutils.GetE2EImage(imageutils.Nginx),
		imageutils.GetE2EImage(imageutils.NginxNew),
		imageutils.GetE2EImage(imageutils.Pause),
		imageutils.GetE2EImage(imageutils.Redis),
	}
}

func SubstituteImageName(content string) string {
	contentWithImageName := new(bytes.Buffer)
	tmpl, err := template.New("imagemanifest").Parse(content)
	if err != nil {
		framework.Failf("Failed Parse the template: %v", err)
	}
	err = tmpl.Execute(contentWithImageName, testImages)
	if err != nil {
		framework.Failf("Failed executing template: %v", err)
	}
	return contentWithImageName.String()
}

func svcByName(name string, port int) *v1.Service {
	return &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.ServiceSpec{
			Type: v1.ServiceTypeNodePort,
			Selector: map[string]string{
				"name": name,
			},
			Ports: []v1.ServicePort{{
				Port:       int32(port),
				TargetPort: intstr.FromInt(port),
			}},
		},
	}
}

func NewSVCByName(c clientset.Interface, ns, name string) error {
	const testPort = 9376
	_, err := c.CoreV1().Services(ns).Create(svcByName(name, testPort))
	return err
}

// NewRCByName creates a replication controller with a selector by name of name.
func NewRCByName(c clientset.Interface, ns, name string, replicas int32, gracePeriod *int64) (*v1.ReplicationController, error) {
	ginkgo.By(fmt.Sprintf("creating replication controller %s", name))
	return c.CoreV1().ReplicationControllers(ns).Create(framework.RcByNamePort(
		name, replicas, framework.ServeHostnameImage, 9376, v1.ProtocolTCP, map[string]string{}, gracePeriod))
}

func RestartNodes(c clientset.Interface, nodes []v1.Node) error {
	// Build mapping from zone to nodes in that zone.
	nodeNamesByZone := make(map[string][]string)
	for i := range nodes {
		node := &nodes[i]
		zone := framework.TestContext.CloudConfig.Zone
		if z, ok := node.Labels[v1.LabelZoneFailureDomain]; ok {
			zone = z
		}
		nodeNamesByZone[zone] = append(nodeNamesByZone[zone], node.Name)
	}

	// Reboot the nodes.
	for zone, nodeNames := range nodeNamesByZone {
		args := []string{
			"compute",
			fmt.Sprintf("--project=%s", framework.TestContext.CloudConfig.ProjectID),
			"instances",
			"reset",
		}
		args = append(args, nodeNames...)
		args = append(args, fmt.Sprintf("--zone=%s", zone))
		stdout, stderr, err := framework.RunCmd("gcloud", args...)
		if err != nil {
			return fmt.Errorf("error restarting nodes: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
		}
	}

	// Wait for their boot IDs to change.
	for i := range nodes {
		node := &nodes[i]
		if err := wait.Poll(30*time.Second, framework.RestartNodeReadyAgainTimeout, func() (bool, error) {
			newNode, err := c.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
			if err != nil {
				return false, fmt.Errorf("error getting node info after reboot: %s", err)
			}
			return node.Status.NodeInfo.BootID != newNode.Status.NodeInfo.BootID, nil
		}); err != nil {
			return fmt.Errorf("error waiting for node %s boot ID to change: %s", node.Name, err)
		}
	}
	return nil
}
<commit_msg>fix golint failure in e2e/common/util.go<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"
	"text/template"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"
	imageutils "k8s.io/kubernetes/test/utils/image"

	"github.com/onsi/ginkgo"
)

// Suite represents test suite.
type Suite string

const (
	// E2E represents a test suite for e2e.
	E2E Suite = "e2e"
	// NodeE2E represents a test suite for node e2e.
	NodeE2E Suite = "node e2e"
)

// CurrentSuite represents current test suite.
var CurrentSuite Suite

// CommonImageWhiteList is the list of images used in common test. These images should be prepulled
// before a tests starts, so that the tests won't fail due image pulling flakes. Currently, this is
// only used by node e2e test.
// TODO(random-liu): Change the image puller pod to use similar mechanism.
var CommonImageWhiteList = sets.NewString(
	imageutils.GetE2EImage(imageutils.Agnhost),
	imageutils.GetE2EImage(imageutils.AuditProxy),
	imageutils.GetE2EImage(imageutils.BusyBox),
	imageutils.GetE2EImage(imageutils.EntrypointTester),
	imageutils.GetE2EImage(imageutils.IpcUtils),
	imageutils.GetE2EImage(imageutils.Mounttest),
	imageutils.GetE2EImage(imageutils.MounttestUser),
	imageutils.GetE2EImage(imageutils.Netexec),
	imageutils.GetE2EImage(imageutils.Nginx),
	imageutils.GetE2EImage(imageutils.ServeHostname),
	imageutils.GetE2EImage(imageutils.TestWebserver),
	imageutils.GetE2EImage(imageutils.VolumeNFSServer),
	imageutils.GetE2EImage(imageutils.VolumeGlusterServer),
	imageutils.GetE2EImage(imageutils.Net),
)

type testImagesStruct struct {
	AgnhostImage      string
	BusyBoxImage      string
	GBFrontendImage   string
	GBRedisSlaveImage string
	KittenImage       string
	MounttestImage    string
	NautilusImage     string
	NginxImage        string
	NginxNewImage     string
	PauseImage        string
	RedisImage        string
}

var testImages testImagesStruct

func init() {
	testImages = testImagesStruct{
		imageutils.GetE2EImage(imageutils.Agnhost),
		imageutils.GetE2EImage(imageutils.BusyBox),
		imageutils.GetE2EImage(imageutils.GBFrontend),
		imageutils.GetE2EImage(imageutils.GBRedisSlave),
		imageutils.GetE2EImage(imageutils.Kitten),
		imageutils.GetE2EImage(imageutils.Mounttest),
		imageutils.GetE2EImage(imageutils.Nautilus),
		imageutils.GetE2EImage(imageutils.Nginx),
		imageutils.GetE2EImage(imageutils.NginxNew),
		imageutils.GetE2EImage(imageutils.Pause),
		imageutils.GetE2EImage(imageutils.Redis),
	}
}

// SubstituteImageName replaces image name in content.
func SubstituteImageName(content string) string {
	contentWithImageName := new(bytes.Buffer)
	tmpl, err := template.New("imagemanifest").Parse(content)
	if err != nil {
		framework.Failf("Failed Parse the template: %v", err)
	}
	err = tmpl.Execute(contentWithImageName, testImages)
	if err != nil {
		framework.Failf("Failed executing template: %v", err)
	}
	return contentWithImageName.String()
}

func svcByName(name string, port int) *v1.Service {
	return &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
		Spec: v1.ServiceSpec{
			Type: v1.ServiceTypeNodePort,
			Selector: map[string]string{
				"name": name,
			},
			Ports: []v1.ServicePort{{
				Port:       int32(port),
				TargetPort: intstr.FromInt(port),
			}},
		},
	}
}

// NewSVCByName creates a service by name.
func NewSVCByName(c clientset.Interface, ns, name string) error {
	const testPort = 9376
	_, err := c.CoreV1().Services(ns).Create(svcByName(name, testPort))
	return err
}

// NewRCByName creates a replication controller with a selector by name of name.
func NewRCByName(c clientset.Interface, ns, name string, replicas int32, gracePeriod *int64) (*v1.ReplicationController, error) {
	ginkgo.By(fmt.Sprintf("creating replication controller %s", name))
	return c.CoreV1().ReplicationControllers(ns).Create(framework.RcByNamePort(
		name, replicas, framework.ServeHostnameImage, 9376, v1.ProtocolTCP, map[string]string{}, gracePeriod))
}

// RestartNodes restarts specific nodes.
func RestartNodes(c clientset.Interface, nodes []v1.Node) error {
	// Build mapping from zone to nodes in that zone.
	nodeNamesByZone := make(map[string][]string)
	for i := range nodes {
		node := &nodes[i]
		zone := framework.TestContext.CloudConfig.Zone
		if z, ok := node.Labels[v1.LabelZoneFailureDomain]; ok {
			zone = z
		}
		nodeNamesByZone[zone] = append(nodeNamesByZone[zone], node.Name)
	}

	// Reboot the nodes.
	for zone, nodeNames := range nodeNamesByZone {
		args := []string{
			"compute",
			fmt.Sprintf("--project=%s", framework.TestContext.CloudConfig.ProjectID),
			"instances",
			"reset",
		}
		args = append(args, nodeNames...)
		args = append(args, fmt.Sprintf("--zone=%s", zone))
		stdout, stderr, err := framework.RunCmd("gcloud", args...)
		if err != nil {
			return fmt.Errorf("error restarting nodes: %s\nstdout: %s\nstderr: %s", err, stdout, stderr)
		}
	}

	// Wait for their boot IDs to change.
	for i := range nodes {
		node := &nodes[i]
		if err := wait.Poll(30*time.Second, framework.RestartNodeReadyAgainTimeout, func() (bool, error) {
			newNode, err := c.CoreV1().Nodes().Get(node.Name, metav1.GetOptions{})
			if err != nil {
				return false, fmt.Errorf("error getting node info after reboot: %s", err)
			}
			return node.Status.NodeInfo.BootID != newNode.Status.NodeInfo.BootID, nil
		}); err != nil {
			return fmt.Errorf("error waiting for node %s boot ID to change: %s", node.Name, err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package bot

import "github.com/nlopes/slack"
import "strings"
import "math/rand"

func postResponse(bot *BaseBot, channel string, emoji string, name string, response string) {
	bot.PostMessage(channel, response, slack.PostMessageParameters{
		AsUser:    false,
		IconEmoji: emoji,
		Username:  name,
	})
}

func randomResponse(bot *BaseBot, channel string, emoji string, name string, responses []string) {
	response := responses[rand.Intn(len(responses))]
	postResponse(bot, channel, emoji, name, response)
}

var (
	anduinresp []string = []string{
		"안녕하세요!",
		"감사합니다!",
		"이게... 아닌데...",
		"빛이 당신을 태울 것입니다!",
		"정말 잘하셨어요.",
		"죄송합니다.",
	}
)

func specialResponses(bot *BaseBot, e *slack.MessageEvent) {
	if strings.Contains(e.Text, "72") || strings.Contains(e.Text, "치하야") || strings.Contains(e.Text, "큿") {
		postResponse(bot, e.Channel, ":kutt:", "치하야", "큿")
	}
	if strings.Contains(e.Text, "크킄") {
		postResponse(bot, e.Channel, ":chuni:", "흑염룡", "흐콰한다")
	}
	if strings.Contains(e.Text, "안두인") {
		randomResponse(bot, e.Channel, ":anduin:", "안두인", anduinresp)
	}
}
<commit_msg>ignore bot<commit_after>package bot

import "github.com/nlopes/slack"
import "strings"
import "math/rand"

func postResponse(bot *BaseBot, channel string, emoji string, name string, response string) {
	bot.PostMessage(channel, response, slack.PostMessageParameters{
		AsUser:    false,
		IconEmoji: emoji,
		Username:  name,
	})
}

func randomResponse(bot *BaseBot, channel string, emoji string, name string, responses []string) {
	response := responses[rand.Intn(len(responses))]
	postResponse(bot, channel, emoji, name, response)
}

var (
	anduinresp []string = []string{
		"안녕하세요!",
		"감사합니다!",
		"이게... 아닌데...",
		"빛이 당신을 태울 것입니다!",
		"정말 잘하셨어요.",
		"죄송합니다.",
	}
)

func specialResponses(bot *BaseBot, e *slack.MessageEvent) {
	// ignore all bot_message
	if e.SubType == "bot_message" {
		return
	}

	if strings.Contains(e.Text, "72") || strings.Contains(e.Text, "치하야") || strings.Contains(e.Text, "큿") {
		postResponse(bot, e.Channel, ":kutt:", "치하야", "큿")
	}
	if strings.Contains(e.Text, "크킄") {
		postResponse(bot, e.Channel, ":chuni:", "흑염룡", "흐콰한다")
	}
	if strings.Contains(e.Text, "안두인") {
		randomResponse(bot, e.Channel, ":anduin:", "안두인", anduinresp)
	}
}
<|endoftext|>


<commit_before>package integration

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"os/exec"
	"strings"
	"testing"

	"github.com/sclevine/spec"
	"github.com/stretchr/testify/require"
)

var _ = suite("compute/image/create", func(t *testing.T, when spec.G, it spec.S) {
	var (
		expect *require.Assertions
		server *httptest.Server
	)

	it.Before(func() {
		expect = require.New(t)
		server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			switch req.URL.Path {
			case "/v2/images":
				auth := req.Header.Get("Authorization")
				if auth != "Bearer some-magic-token" {
					w.WriteHeader(http.StatusUnauthorized)
					return
				}
				w.Write([]byte(imageCreateResponse))
			default:
				dump, err := httputil.DumpRequest(req, true)
				if err != nil {
					t.Fatal("failed to dump request")
				}

				t.Fatalf("received unknown request: %s", dump)
			}
		}))
	})

	when("all the required flags are passed", func() {
		it("creates an image", func() {
			cmd := exec.Command(builtBinaryPath,
				"-t", "some-magic-token",
				"-u", server.URL,
				"compute",
				"image",
				"create", "ubuntu-18.04-minimal",
				"--image-url", "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img",
				"--region", "nyc3",
			)

			output, err := cmd.CombinedOutput()
			expect.NoError(err, fmt.Sprintf("received unexpected error: %s", output))
			expect.Equal(strings.TrimSpace(imageCreateOutput), strings.TrimSpace(string(output)))
		})
	})

	when("required arguments are missing", func() {
		base := []string{
			"-t", "some-magic-token",
			"-u", "https://www.example.com",
			"compute",
			"image",
			"create", "test-image",
		}

		baseErr := `Error: (image.create%s) command is missing required arguments`

		var cases = []struct {
			desc string
			err  string
			args []string
		}{
			{
				"missing all",
				fmt.Sprintf(baseErr, ".image-url"),
				base,
			},
			{
				"missing region",
				fmt.Sprintf(baseErr, ".region"),
				append(base, []string{
					"--image-description", "an ubuntu custom minimal image",
					"--image-name", "ubuntu-18.04-minimal",
					"--image-url", "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img",
				}...),
			},
			{
				"missing image url",
				fmt.Sprintf(baseErr, ".image-url"),
				append(base, []string{
					"--image-description", "an ubuntu custom minimal image",
					"--image-name", "ubuntu-18.04-minimal",
					"--region", "nyc3",
				}...),
			},
		}

		for _, c := range cases {
			commandArgs := c.args
			expectedErr := c.err

			when(c.desc, func() {
				it("returns an error", func() {
					cmd := exec.Command(builtBinaryPath, commandArgs...)

					output, err := cmd.CombinedOutput()
					expect.Error(err)
					expect.Contains(string(output), expectedErr)
				})
			})
		}
	})
})

const imageCreateResponse = `{
	"image": {
	  "created_at": "2018-09-20T19:28:00Z",
	  "description": "Cloud-optimized image w/ small footprint",
	  "distribution": "Ubuntu",
	  "error_message": "",
	  "id": 38413969,
	  "name": "ubuntu-18.04-minimal",
	  "regions": [],
	  "type": "custom",
	  "tags": [
		"base-image",
		"prod"
	  ],
	  "status": "NEW"
	}
  }`

const imageCreateOutput = `
ID          Name                    Type      Distribution    Slug    Public    Min Disk
38413969    ubuntu-18.04-minimal    custom    Ubuntu                  false     0
`
<commit_msg>adds check of method to image create<commit_after>package integration

import (
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"os/exec"
	"strings"
	"testing"

	"github.com/sclevine/spec"
	"github.com/stretchr/testify/require"
)

var _ = suite("compute/image/create", func(t *testing.T, when spec.G, it spec.S) {
	var (
		expect *require.Assertions
		server *httptest.Server
	)

	it.Before(func() {
		expect = require.New(t)
		server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
			switch req.URL.Path {
			case "/v2/images":
				auth := req.Header.Get("Authorization")
				if auth != "Bearer some-magic-token" {
					w.WriteHeader(http.StatusUnauthorized)
					return
				}

				if req.Method != "POST" {
					w.WriteHeader(http.StatusMethodNotAllowed)
					return
				}

				w.Write([]byte(imageCreateResponse))
			default:
				dump, err := httputil.DumpRequest(req, true)
				if err != nil {
					t.Fatal("failed to dump request")
				}

				t.Fatalf("received unknown request: %s", dump)
			}
		}))
	})

	when("all the required flags are passed", func() {
		it("creates an image", func() {
			cmd := exec.Command(builtBinaryPath,
				"-t", "some-magic-token",
				"-u", server.URL,
				"compute",
				"image",
				"create", "ubuntu-18.04-minimal",
				"--image-url", "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img",
				"--region", "nyc3",
			)

			output, err := cmd.CombinedOutput()
			expect.NoError(err, fmt.Sprintf("received unexpected error: %s", output))
			expect.Equal(strings.TrimSpace(imageCreateOutput), strings.TrimSpace(string(output)))
		})
	})

	when("required arguments are missing", func() {
		base := []string{
			"-t", "some-magic-token",
			"-u", "https://www.example.com",
			"compute",
			"image",
			"create", "test-image",
		}

		baseErr := `Error: (image.create%s) command is missing required arguments`

		var cases = []struct {
			desc string
			err  string
			args []string
		}{
			{
				"missing all",
				fmt.Sprintf(baseErr, ".image-url"),
				base,
			},
			{
				"missing region",
				fmt.Sprintf(baseErr, ".region"),
				append(base, []string{
					"--image-description", "an ubuntu custom minimal image",
					"--image-name", "ubuntu-18.04-minimal",
					"--image-url", "http://cloud-images.ubuntu.com/minimal/releases/bionic/release/ubuntu-18.04-minimal-cloudimg-amd64.img",
				}...),
			},
			{
				"missing image url",
				fmt.Sprintf(baseErr, ".image-url"),
				append(base, []string{
					"--image-description", "an ubuntu custom minimal image",
					"--image-name", "ubuntu-18.04-minimal",
					"--region", "nyc3",
				}...),
			},
		}

		for _, c := range cases {
			commandArgs := c.args
			expectedErr := c.err

			when(c.desc, func() {
				it("returns an error", func() {
					cmd := exec.Command(builtBinaryPath, commandArgs...)

					output, err := cmd.CombinedOutput()
					expect.Error(err)
					expect.Contains(string(output), expectedErr)
				})
			})
		}
	})
})

const imageCreateResponse = `{
	"image": {
	  "created_at": "2018-09-20T19:28:00Z",
	  "description": "Cloud-optimized image w/ small footprint",
	  "distribution": "Ubuntu",
	  "error_message": "",
	  "id": 38413969,
	  "name": "ubuntu-18.04-minimal",
	  "regions": [],
	  "type": "custom",
	  "tags": [
		"base-image",
		"prod"
	  ],
	  "status": "NEW"
	}
  }`

const imageCreateOutput = `
ID          Name                    Type      Distribution    Slug    Public    Min Disk
38413969    ubuntu-18.04-minimal    custom    Ubuntu                  false     0
`
<|endoftext|>


<commit_before>package shimmie

import (
	"io"
	"strings"
	"time"
)

const (
	imageRatingSafe         = "Safe"
	imageRatingQuestionable = "Questionable"
	imageRatingExplicit     = "Explicit"
)

// ImageRating converts rating letters to full words.
//
//		s -> Safe
//		q -> Questionable
//		e -> Explicit
//
// If another value except (s, q, e) is given, then it returns that value as it
// is.
func ImageRating(rating string) string {
	switch rating {
	case "s":
		return imageRatingSafe
	case "q":
		return imageRatingQuestionable
	case "e":
		return imageRatingExplicit
	}
	return rating
}

// Shimmie represents an installed shimmie2 project.
type Shimmie struct {
	ImagePath string
	ThumbPath string
	Store
}

// New creates a new Shimmie by providing a store with the database
// configuration and the paths of the images and thumbs.
func New(imgPath, thumbPath string, s Store) *Shimmie {
	return &Shimmie{ImagePath: imgPath, ThumbPath: thumbPath, Store: s}
}

// Store describes all the operations that need to access database storage.
type Store interface {
	// GetUser gets a user by unique username.
	GetUser(username string) (*User, error)

	// GetConfig gets shimmie config values.
	GetConfig(keys ...string) (map[string]string, error)

	// GetCommon gets common configuration values.
	GetCommon() (*Common, error)

	// GetRatedImages returns all the images that have been rated as safe
	// ignoring the ones from username.
	GetRatedImages(username string) ([]RatedImage, error)
	// GetImage gets a shimmie Image metadata (not it's bytes).
	GetImage(id int) (*Image, error)
	// RateImage sets the rating for an image.
	RateImage(id int, rating string) error
	// WriteImageFile reads a shimmie image file (image or thumb) which exists
	// under a path and has a hash and then writes to w.
	WriteImageFile(w io.Writer, path, hash string) error

	// Log stores a message on score_log table.
	Log(section, username, address string, priority int, message string) (*SCoreLog, error)
	// LogRating logs when an image rating is set.
	LogRating(imgID int, rating, username, userIP string) error
}

// SCoreLog represents a log message in the shimmie log that is stored in the
// table "score_log".
type SCoreLog struct {
	ID       int64
	DateSent *time.Time
	Section  string
	Username string
	Address  string
	Priority int
	Message  string
}

// RatedImage represents a shimmie image that also carries information about
// who rated it and when.
type RatedImage struct {
	Image
	Rater    string
	RaterIP  string
	RateDate *time.Time
}

// Image represents a shimmie image.
type Image struct {
	ID           int
	OwnerID      int
	OwnerIP      string
	Filename     string
	Filesize     int
	Hash         string
	Ext          string
	Source       string
	Width        int
	Height       int
	Posted       *time.Time
	Locked       string
	NumericScore int
	Rating       string
	Favorites    int
	ParentID     int64
	HasChildren  bool
	Author       string
	Notes        int
}

// User represents a shimmie user.
type User struct {
	ID       int
	Name     string
	Pass     string
	JoinDate *time.Time
	Admin    string
	Email    string
	Class    string
}

// Common holds common configuration values.
type Common struct {
	Title       string
	AnalyticsID string
	Description string
	Keywords    string
}

// SiteTitle returns the Title capitalized.
func (c Common) SiteTitle() string {
	return strings.Title(c.Title)
}
<commit_msg>Add RateDate format<commit_after>package shimmie

import (
	"io"
	"strings"
	"time"
)

const (
	imageRatingSafe         = "Safe"
	imageRatingQuestionable = "Questionable"
	imageRatingExplicit     = "Explicit"
)

// ImageRating converts rating letters to full words.
//
//		s -> Safe
//		q -> Questionable
//		e -> Explicit
//
// If another value except (s, q, e) is given, then it returns that value as it
// is.
func ImageRating(rating string) string {
	switch rating {
	case "s":
		return imageRatingSafe
	case "q":
		return imageRatingQuestionable
	case "e":
		return imageRatingExplicit
	}
	return rating
}

// Shimmie represents an installed shimmie2 project.
type Shimmie struct {
	ImagePath string
	ThumbPath string
	Store
}

// New creates a new Shimmie by providing a store with the database
// configuration and the paths of the images and thumbs.
func New(imgPath, thumbPath string, s Store) *Shimmie {
	return &Shimmie{ImagePath: imgPath, ThumbPath: thumbPath, Store: s}
}

// Store describes all the operations that need to access database storage.
type Store interface {
	// GetUser gets a user by unique username.
	GetUser(username string) (*User, error)

	// GetConfig gets shimmie config values.
	GetConfig(keys ...string) (map[string]string, error)

	// GetCommon gets common configuration values.
	GetCommon() (*Common, error)

	// GetRatedImages returns all the images that have been rated as safe
	// ignoring the ones from username.
	GetRatedImages(username string) ([]RatedImage, error)
	// GetImage gets a shimmie Image metadata (not it's bytes).
	GetImage(id int) (*Image, error)
	// RateImage sets the rating for an image.
	RateImage(id int, rating string) error
	// WriteImageFile reads a shimmie image file (image or thumb) which exists
	// under a path and has a hash and then writes to w.
	WriteImageFile(w io.Writer, path, hash string) error

	// Log stores a message on score_log table.
	Log(section, username, address string, priority int, message string) (*SCoreLog, error)
	// LogRating logs when an image rating is set.
	LogRating(imgID int, rating, username, userIP string) error
}

// SCoreLog represents a log message in the shimmie log that is stored in the
// table "score_log".
type SCoreLog struct {
	ID       int64
	DateSent *time.Time
	Section  string
	Username string
	Address  string
	Priority int
	Message  string
}

// RatedImage represents a shimmie image that also carries information about
// who rated it and when.
type RatedImage struct {
	Image
	Rater    string
	RaterIP  string
	RateDate *time.Time
}

func (ri RatedImage) RateDateFormat() string {
	return ri.RateDate.UTC().Format("Mon 02 Jan 2006 15:04:05 MST")
}

// Image represents a shimmie image.
type Image struct {
	ID           int
	OwnerID      int
	OwnerIP      string
	Filename     string
	Filesize     int
	Hash         string
	Ext          string
	Source       string
	Width        int
	Height       int
	Posted       *time.Time
	Locked       string
	NumericScore int
	Rating       string
	Favorites    int
	ParentID     int64
	HasChildren  bool
	Author       string
	Notes        int
}

// User represents a shimmie user.
type User struct {
	ID       int
	Name     string
	Pass     string
	JoinDate *time.Time
	Admin    string
	Email    string
	Class    string
}

// Common holds common configuration values.
type Common struct {
	Title       string
	AnalyticsID string
	Description string
	Keywords    string
}

// SiteTitle returns the Title capitalized.
func (c Common) SiteTitle() string {
	return strings.Title(c.Title)
}
<|endoftext|>


<commit_before>package windowsipam

import (
	"net"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/ipamapi"
	"github.com/docker/libnetwork/types"
)

const (
	localAddressSpace  = "LocalDefault"
	globalAddressSpace = "GlobalDefault"
)

var (
	defaultPool, _ = types.ParseCIDR("0.0.0.0/0")
)

type allocator struct {
}

// GetInit registers the built-in ipam service with libnetwork
func GetInit(ipamName string) func(ic ipamapi.Callback, l, g interface{}) error {
	return func(ic ipamapi.Callback, l, g interface{}) error {
		return ic.RegisterIpamDriver(ipamName, &allocator{})
	}
}

func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
	return localAddressSpace, globalAddressSpace, nil
}

// RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the
// subnet user asked and does not validate anything. Doesnt support subpool allocation
func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
	log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6)
	if subPool != "" || v6 {
		return "", nil, nil, types.InternalErrorf("This request is not supported by null ipam driver")
	}

	var ipNet *net.IPNet
	var err error

	if pool != "" {
		_, ipNet, err = net.ParseCIDR(pool)
		if err != nil {
			return "", nil, nil, err
		}
	} else {
		ipNet = defaultPool
	}

	return ipNet.String(), ipNet, nil, nil
}

// ReleasePool releases the address pool - always succeeds
func (a *allocator) ReleasePool(poolID string) error {
	log.Debugf("ReleasePool(%s)", poolID)
	return nil
}

// RequestAddress returns an address from the specified pool ID.
// Always allocate the 0.0.0.0/32 ip if no preferred address was specified
func (a *allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
	log.Debugf("RequestAddress(%s, %v, %v) %s", poolID, prefAddress, opts, opts["RequestAddressType"])
	_, ipNet, err := net.ParseCIDR(poolID)

	if err != nil {
		return nil, nil, err
	}
	if prefAddress == nil {
		return ipNet, nil, nil
	}
	return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil
}

// ReleaseAddress releases the address - always succeeds
func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
	log.Debugf("ReleaseAddress(%s, %v)", poolID, address)
	return nil
}
<commit_msg>Fix cross compilation breakage<commit_after>package windowsipam

import (
	"net"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/libnetwork/discoverapi"
	"github.com/docker/libnetwork/ipamapi"
	"github.com/docker/libnetwork/types"
)

const (
	localAddressSpace  = "LocalDefault"
	globalAddressSpace = "GlobalDefault"
)

var (
	defaultPool, _ = types.ParseCIDR("0.0.0.0/0")
)

type allocator struct {
}

// GetInit registers the built-in ipam service with libnetwork
func GetInit(ipamName string) func(ic ipamapi.Callback, l, g interface{}) error {
	return func(ic ipamapi.Callback, l, g interface{}) error {
		return ic.RegisterIpamDriver(ipamName, &allocator{})
	}
}

func (a *allocator) GetDefaultAddressSpaces() (string, string, error) {
	return localAddressSpace, globalAddressSpace, nil
}

// RequestPool returns an address pool along with its unique id. This is a null ipam driver. It allocates the
// subnet user asked and does not validate anything. Doesnt support subpool allocation
func (a *allocator) RequestPool(addressSpace, pool, subPool string, options map[string]string, v6 bool) (string, *net.IPNet, map[string]string, error) {
	log.Debugf("RequestPool(%s, %s, %s, %v, %t)", addressSpace, pool, subPool, options, v6)
	if subPool != "" || v6 {
		return "", nil, nil, types.InternalErrorf("This request is not supported by null ipam driver")
	}

	var ipNet *net.IPNet
	var err error

	if pool != "" {
		_, ipNet, err = net.ParseCIDR(pool)
		if err != nil {
			return "", nil, nil, err
		}
	} else {
		ipNet = defaultPool
	}

	return ipNet.String(), ipNet, nil, nil
}

// ReleasePool releases the address pool - always succeeds
func (a *allocator) ReleasePool(poolID string) error {
	log.Debugf("ReleasePool(%s)", poolID)
	return nil
}

// RequestAddress returns an address from the specified pool ID.
// Always allocate the 0.0.0.0/32 ip if no preferred address was specified
func (a *allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
	log.Debugf("RequestAddress(%s, %v, %v) %s", poolID, prefAddress, opts, opts["RequestAddressType"])
	_, ipNet, err := net.ParseCIDR(poolID)

	if err != nil {
		return nil, nil, err
	}
	if prefAddress == nil {
		return ipNet, nil, nil
	}
	return &net.IPNet{IP: prefAddress, Mask: ipNet.Mask}, nil, nil
}

// ReleaseAddress releases the address - always succeeds
func (a *allocator) ReleaseAddress(poolID string, address net.IP) error {
	log.Debugf("ReleaseAddress(%s, %v)", poolID, address)
	return nil
}

// DiscoverNew informs the allocator about a new global scope datastore
func (a *allocator) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
	return nil
}

// DiscoverDelete is a notification of no interest for the allocator
func (a *allocator) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
	return nil
}
<|endoftext|>


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

// Code generated by client-gen. DO NOT EDIT.

package versioned

import (
	"fmt"
	"net/http"

	discovery "k8s.io/client-go/discovery"
	rest "k8s.io/client-go/rest"
	flowcontrol "k8s.io/client-go/util/flowcontrol"
	metricsv1alpha1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1"
	metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
)

type Interface interface {
	Discovery() discovery.DiscoveryInterface
	MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface
	MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface
}

// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
	*discovery.DiscoveryClient
	metricsV1alpha1 *metricsv1alpha1.MetricsV1alpha1Client
	metricsV1beta1  *metricsv1beta1.MetricsV1beta1Client
}

// MetricsV1alpha1 retrieves the MetricsV1alpha1Client
func (c *Clientset) MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface {
	return c.metricsV1alpha1
}

// MetricsV1beta1 retrieves the MetricsV1beta1Client
func (c *Clientset) MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface {
	return c.metricsV1beta1
}

// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
	if c == nil {
		return nil
	}
	return c.DiscoveryClient
}

// NewForConfig creates a new Clientset for the given config.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfig will generate a rate-limiter in configShallowCopy.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*Clientset, error) {
	configShallowCopy := *c

	// share the transport between all clients
	httpClient, err := rest.HTTPClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return NewForConfigAndClient(&configShallowCopy, httpClient)
}

// NewForConfigAndClient creates a new Clientset for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.
func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		if configShallowCopy.Burst <= 0 {
			return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
		}
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}

	var cs Clientset
	var err error
	cs.metricsV1alpha1, err = metricsv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}
	cs.metricsV1beta1, err = metricsv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}

	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}
	return &cs, nil
}

// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
	cs, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return cs
}

// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
	var cs Clientset
	cs.metricsV1alpha1 = metricsv1alpha1.New(c)
	cs.metricsV1beta1 = metricsv1beta1.New(c)

	cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
	return &cs
}
<commit_msg>client-go: update generated<commit_after>/*
Copyright 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.
*/

// Code generated by client-gen. DO NOT EDIT.

package versioned

import (
	"fmt"
	"net/http"

	discovery "k8s.io/client-go/discovery"
	rest "k8s.io/client-go/rest"
	flowcontrol "k8s.io/client-go/util/flowcontrol"
	metricsv1alpha1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1alpha1"
	metricsv1beta1 "k8s.io/metrics/pkg/client/clientset/versioned/typed/metrics/v1beta1"
)

type Interface interface {
	Discovery() discovery.DiscoveryInterface
	MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface
	MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface
}

// Clientset contains the clients for groups. Each group has exactly one
// version included in a Clientset.
type Clientset struct {
	*discovery.DiscoveryClient
	metricsV1alpha1 *metricsv1alpha1.MetricsV1alpha1Client
	metricsV1beta1  *metricsv1beta1.MetricsV1beta1Client
}

// MetricsV1alpha1 retrieves the MetricsV1alpha1Client
func (c *Clientset) MetricsV1alpha1() metricsv1alpha1.MetricsV1alpha1Interface {
	return c.metricsV1alpha1
}

// MetricsV1beta1 retrieves the MetricsV1beta1Client
func (c *Clientset) MetricsV1beta1() metricsv1beta1.MetricsV1beta1Interface {
	return c.metricsV1beta1
}

// Discovery retrieves the DiscoveryClient
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
	if c == nil {
		return nil
	}
	return c.DiscoveryClient
}

// NewForConfig creates a new Clientset for the given config.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfig will generate a rate-limiter in configShallowCopy.
// NewForConfig is equivalent to NewForConfigAndClient(c, httpClient),
// where httpClient was generated with rest.HTTPClientFor(c).
func NewForConfig(c *rest.Config) (*Clientset, error) {
	configShallowCopy := *c

	if configShallowCopy.UserAgent == "" {
		configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent()
	}

	// share the transport between all clients
	httpClient, err := rest.HTTPClientFor(&configShallowCopy)
	if err != nil {
		return nil, err
	}

	return NewForConfigAndClient(&configShallowCopy, httpClient)
}

// NewForConfigAndClient creates a new Clientset for the given config and http client.
// Note the http client provided takes precedence over the configured transport values.
// If config's RateLimiter is not set and QPS and Burst are acceptable,
// NewForConfigAndClient will generate a rate-limiter in configShallowCopy.
func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) {
	configShallowCopy := *c
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
		if configShallowCopy.Burst <= 0 {
			return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
		}
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
	}

	var cs Clientset
	var err error
	cs.metricsV1alpha1, err = metricsv1alpha1.NewForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}
	cs.metricsV1beta1, err = metricsv1beta1.NewForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}

	cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient)
	if err != nil {
		return nil, err
	}
	return &cs, nil
}

// NewForConfigOrDie creates a new Clientset for the given config and
// panics if there is an error in the config.
func NewForConfigOrDie(c *rest.Config) *Clientset {
	cs, err := NewForConfig(c)
	if err != nil {
		panic(err)
	}
	return cs
}

// New creates a new Clientset for the given RESTClient.
func New(c rest.Interface) *Clientset {
	var cs Clientset
	cs.metricsV1alpha1 = metricsv1alpha1.New(c)
	cs.metricsV1beta1 = metricsv1beta1.New(c)

	cs.DiscoveryClient = discovery.NewDiscoveryClient(c)
	return &cs
}
<|endoftext|>


<commit_before>package validation

import (
	"reflect"
	"testing"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
	"github.com/openshift/origin/pkg/image/api"
)

func TestValidateImageOK(t *testing.T) {
	errs := ValidateImage(&api.Image{
		ObjectMeta:           kapi.ObjectMeta{Name: "foo"},
		DockerImageReference: "openshift/ruby-19-centos",
	})
	if len(errs) > 0 {
		t.Errorf("Unexpected non-empty error list: %#v", errs)
	}
}

func TestValidateImageMissingFields(t *testing.T) {
	errorCases := map[string]struct {
		I api.Image
		T fielderrors.ValidationErrorType
		F string
	}{
		"missing Name": {
			api.Image{DockerImageReference: "ref"},
			fielderrors.ValidationErrorTypeRequired,
			"metadata.name",
		},
		"no slash in Name": {
			api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo/bar"}},
			fielderrors.ValidationErrorTypeInvalid,
			"metadata.name",
		},
		"missing DockerImageReference": {
			api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo"}},
			fielderrors.ValidationErrorTypeRequired,
			"dockerImageReference",
		},
	}

	for k, v := range errorCases {
		errs := ValidateImage(&v.I)
		if len(errs) == 0 {
			t.Errorf("Expected failure for %s", k)
			continue
		}
		match := false
		for i := range errs {
			if errs[i].(*fielderrors.ValidationError).Type == v.T && errs[i].(*fielderrors.ValidationError).Field == v.F {
				match = true
				break
			}
		}
		if !match {
			t.Errorf("%s: expected errors to have field %s and type %s: %v", k, v.F, v.T, errs)
		}
	}
}

func TestValidateImageStreamMappingNotOK(t *testing.T) {
	errorCases := map[string]struct {
		I api.ImageStreamMapping
		T fielderrors.ValidationErrorType
		F string
	}{
		"missing DockerImageRepository": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"dockerImageRepository",
		},
		"missing Name": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"name",
		},
		"missing Tag": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "openshift/ruby-19-centos",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"tag",
		},
		"missing image name": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "openshift/ruby-19-centos",
				Tag: "latest",
				Image: api.Image{
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"image.metadata.name",
		},
		"invalid repository pull spec": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "registry/extra/openshift/ruby-19-centos",
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeInvalid,
			"dockerImageRepository",
		},
	}

	for k, v := range errorCases {
		errs := ValidateImageStreamMapping(&v.I)
		if len(errs) == 0 {
			t.Errorf("Expected failure for %s", k)
			continue
		}
		match := false
		for i := range errs {
			if errs[i].(*fielderrors.ValidationError).Type == v.T && errs[i].(*fielderrors.ValidationError).Field == v.F {
				match = true
				break
			}
		}
		if !match {
			t.Errorf("%s: expected errors to have field %s and type %s: %v", k, v.F, v.T, errs)
		}
	}
}

func TestValidateImageStream(t *testing.T) {
	tests := map[string]struct {
		namespace             string
		name                  string
		dockerImageRepository string
		specTags              map[string]api.TagReference
		statusTags            map[string]api.TagEventList
		expected              fielderrors.ValidationErrorList
	}{
		"missing name": {
			namespace: "foo",
			name:      "",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("metadata.name"),
			},
		},
		"missing namespace": {
			namespace: "",
			name:      "foo",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("metadata.namespace"),
			},
		},
		"invalid namespace": {
			namespace: "!$",
			name:      "foo",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("metadata.namespace", "!$", `must have at most 253 characters and match regex [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*`),
			},
		},
		"invalid dockerImageRepository": {
			namespace: "namespace",
			name:      "foo",
			dockerImageRepository: "a-|///bbb",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("spec.dockerImageRepository", "a-|///bbb", "the docker pull spec \"a-|///bbb\" must be two or three segments separated by slashes"),
			},
		},
		"both dockerImageReference and from set": {
			namespace: "namespace",
			name:      "foo",
			specTags: map[string]api.TagReference{
				"tag": {
					DockerImageReference: "abc",
					From:                 &kapi.ObjectReference{},
				},
			},
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("spec.tags[tag]", "", "only 1 of dockerImageReference or from may be set"),
			},
		},
		"status tag missing dockerImageReference": {
			namespace: "namespace",
			name:      "foo",
			statusTags: map[string]api.TagEventList{
				"tag": {
					Items: []api.TagEvent{
						{DockerImageReference: ""},
						{DockerImageReference: "foo/bar:latest"},
						{DockerImageReference: ""},
					},
				},
			},
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("status.tags[tag].Items[0].dockerImageReference"),
				fielderrors.NewFieldRequired("status.tags[tag].Items[2].dockerImageReference"),
			},
		},
		"valid": {
			namespace: "namespace",
			name:      "foo",
			specTags: map[string]api.TagReference{
				"tag": {
					DockerImageReference: "abc",
				},
				"other": {
					From: &kapi.ObjectReference{
						Kind: "ImageStreamTag",
						Name: "other:latest",
					},
				},
			},
			statusTags: map[string]api.TagEventList{
				"tag": {
					Items: []api.TagEvent{
						{DockerImageReference: "foo/bar:latest"},
					},
				},
			},
			expected: fielderrors.ValidationErrorList{},
		},
	}

	for name, test := range tests {
		stream := api.ImageStream{
			ObjectMeta: kapi.ObjectMeta{
				Namespace: test.namespace,
				Name:      test.name,
			},
			Spec: api.ImageStreamSpec{
				DockerImageRepository: test.dockerImageRepository,
				Tags: test.specTags,
			},
			Status: api.ImageStreamStatus{
				Tags: test.statusTags,
			},
		}

		errs := ValidateImageStream(&stream)
		if e, a := test.expected, errs; !reflect.DeepEqual(e, a) {
			t.Errorf("%s: unexpected errors: %s", name, util.ObjectDiff(e, a))
		}
	}
}
<commit_msg>more tests<commit_after>package validation

import (
	"reflect"
	"testing"

	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
	"github.com/openshift/origin/pkg/image/api"
)

func TestValidateImageOK(t *testing.T) {
	errs := ValidateImage(&api.Image{
		ObjectMeta:           kapi.ObjectMeta{Name: "foo"},
		DockerImageReference: "openshift/ruby-19-centos",
	})
	if len(errs) > 0 {
		t.Errorf("Unexpected non-empty error list: %#v", errs)
	}
}

func TestValidateImageMissingFields(t *testing.T) {
	errorCases := map[string]struct {
		I api.Image
		T fielderrors.ValidationErrorType
		F string
	}{
		"missing Name": {
			api.Image{DockerImageReference: "ref"},
			fielderrors.ValidationErrorTypeRequired,
			"metadata.name",
		},
		"no slash in Name": {
			api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo/bar"}},
			fielderrors.ValidationErrorTypeInvalid,
			"metadata.name",
		},
		"no percent in Name": {
			api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo%%bar"}},
			fielderrors.ValidationErrorTypeInvalid,
			"metadata.name",
		},
		"missing DockerImageReference": {
			api.Image{ObjectMeta: kapi.ObjectMeta{Name: "foo"}},
			fielderrors.ValidationErrorTypeRequired,
			"dockerImageReference",
		},
	}

	for k, v := range errorCases {
		errs := ValidateImage(&v.I)
		if len(errs) == 0 {
			t.Errorf("Expected failure for %s", k)
			continue
		}
		match := false
		for i := range errs {
			if errs[i].(*fielderrors.ValidationError).Type == v.T && errs[i].(*fielderrors.ValidationError).Field == v.F {
				match = true
				break
			}
		}
		if !match {
			t.Errorf("%s: expected errors to have field %s and type %s: %v", k, v.F, v.T, errs)
		}
	}
}

func TestValidateImageStreamMappingNotOK(t *testing.T) {
	errorCases := map[string]struct {
		I api.ImageStreamMapping
		T fielderrors.ValidationErrorType
		F string
	}{
		"missing DockerImageRepository": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"dockerImageRepository",
		},
		"missing Name": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"name",
		},
		"missing Tag": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "openshift/ruby-19-centos",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"tag",
		},
		"missing image name": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "openshift/ruby-19-centos",
				Tag: "latest",
				Image: api.Image{
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeRequired,
			"image.metadata.name",
		},
		"invalid repository pull spec": {
			api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Namespace: "default",
				},
				DockerImageRepository: "registry/extra/openshift/ruby-19-centos",
				Tag: "latest",
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name:      "foo",
						Namespace: "default",
					},
					DockerImageReference: "openshift/ruby-19-centos",
				},
			},
			fielderrors.ValidationErrorTypeInvalid,
			"dockerImageRepository",
		},
	}

	for k, v := range errorCases {
		errs := ValidateImageStreamMapping(&v.I)
		if len(errs) == 0 {
			t.Errorf("Expected failure for %s", k)
			continue
		}
		match := false
		for i := range errs {
			if errs[i].(*fielderrors.ValidationError).Type == v.T && errs[i].(*fielderrors.ValidationError).Field == v.F {
				match = true
				break
			}
		}
		if !match {
			t.Errorf("%s: expected errors to have field %s and type %s: %v", k, v.F, v.T, errs)
		}
	}
}

func TestValidateImageStream(t *testing.T) {
	tests := map[string]struct {
		namespace             string
		name                  string
		dockerImageRepository string
		specTags              map[string]api.TagReference
		statusTags            map[string]api.TagEventList
		expected              fielderrors.ValidationErrorList
	}{
		"missing name": {
			namespace: "foo",
			name:      "",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("metadata.name"),
			},
		},
		"no slash in Name": {
			namespace: "foo",
			name:      "foo/bar",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("metadata.name", "foo/bar", `may not contain "/"`),
			},
		},
		"no percent in Name": {
			namespace: "foo",
			name:      "foo%%bar",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("metadata.name", "foo%%bar", `may not contain "%"`),
			},
		},
		"missing namespace": {
			namespace: "",
			name:      "foo",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("metadata.namespace"),
			},
		},
		"invalid namespace": {
			namespace: "!$",
			name:      "foo",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("metadata.namespace", "!$", `must have at most 253 characters and match regex [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*`),
			},
		},
		"invalid dockerImageRepository": {
			namespace: "namespace",
			name:      "foo",
			dockerImageRepository: "a-|///bbb",
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("spec.dockerImageRepository", "a-|///bbb", "the docker pull spec \"a-|///bbb\" must be two or three segments separated by slashes"),
			},
		},
		"both dockerImageReference and from set": {
			namespace: "namespace",
			name:      "foo",
			specTags: map[string]api.TagReference{
				"tag": {
					DockerImageReference: "abc",
					From:                 &kapi.ObjectReference{},
				},
			},
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldInvalid("spec.tags[tag]", "", "only 1 of dockerImageReference or from may be set"),
			},
		},
		"status tag missing dockerImageReference": {
			namespace: "namespace",
			name:      "foo",
			statusTags: map[string]api.TagEventList{
				"tag": {
					Items: []api.TagEvent{
						{DockerImageReference: ""},
						{DockerImageReference: "foo/bar:latest"},
						{DockerImageReference: ""},
					},
				},
			},
			expected: fielderrors.ValidationErrorList{
				fielderrors.NewFieldRequired("status.tags[tag].Items[0].dockerImageReference"),
				fielderrors.NewFieldRequired("status.tags[tag].Items[2].dockerImageReference"),
			},
		},
		"valid": {
			namespace: "namespace",
			name:      "foo",
			specTags: map[string]api.TagReference{
				"tag": {
					DockerImageReference: "abc",
				},
				"other": {
					From: &kapi.ObjectReference{
						Kind: "ImageStreamTag",
						Name: "other:latest",
					},
				},
			},
			statusTags: map[string]api.TagEventList{
				"tag": {
					Items: []api.TagEvent{
						{DockerImageReference: "foo/bar:latest"},
					},
				},
			},
			expected: fielderrors.ValidationErrorList{},
		},
	}

	for name, test := range tests {
		stream := api.ImageStream{
			ObjectMeta: kapi.ObjectMeta{
				Namespace: test.namespace,
				Name:      test.name,
			},
			Spec: api.ImageStreamSpec{
				DockerImageRepository: test.dockerImageRepository,
				Tags: test.specTags,
			},
			Status: api.ImageStreamStatus{
				Tags: test.statusTags,
			},
		}

		errs := ValidateImageStream(&stream)
		if e, a := test.expected, errs; !reflect.DeepEqual(e, a) {
			t.Errorf("%s: unexpected errors: %s", name, util.ObjectDiff(e, a))
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

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

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

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

package revision

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"

	"k8s.io/apimachinery/pkg/util/sets"

	"github.com/google/go-containerregistry/pkg/authn/k8schain"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"k8s.io/client-go/kubernetes"
)

type digestResolver struct {
	client    kubernetes.Interface
	transport http.RoundTripper
}

const (
	// Kubernetes CA certificate bundle is mounted into the pod here, see:
	// https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/#trusting-tls-in-a-cluster
	k8sCertPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)

// newResolverTransport returns an http.Transport that appends the certs bundle
// at path to the system cert pool.
//
// Use this with k8sCertPath to trust the same certs as the cluster.
func newResolverTransport(path string) (*http.Transport, error) {
	pool, err := x509.SystemCertPool()
	if err != nil {
		pool = x509.NewCertPool()
	}

	if crt, err := ioutil.ReadFile(path); err != nil {
		return nil, err
	} else if ok := pool.AppendCertsFromPEM(crt); !ok {
		return nil, errors.New("failed to append k8s cert bundle to cert pool")
	}

	return &http.Transport{
		TLSClientConfig: &tls.Config{
			RootCAs: pool,
		},
	}, nil
}

// Resolve resolves the image references that use tags to digests.
func (r *digestResolver) Resolve(
	image string,
	opt k8schain.Options,
	registriesToSkip sets.String) (string, error) {
	kc, err := k8schain.New(r.client, opt)
	if err != nil {
		return "", err
	}

	if _, err := name.NewDigest(image, name.WeakValidation); err == nil {
		// Already a digest
		return image, nil
	}

	tag, err := name.NewTag(image, name.WeakValidation)
	if err != nil {
		return "", err
	}

	if registriesToSkip.Has(tag.Registry.RegistryStr()) {
		return "", nil
	}

	img, err := remote.Image(tag, remote.WithTransport(r.transport), remote.WithAuthFromKeychain(kc))
	if err != nil {
		return "", err
	}
	digest, err := img.Digest()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s@%s", tag.Repository.String(), digest), nil
}
<commit_msg>Construct a better resolver transport (#3409)<commit_after>/*
Copyright 2018 The Knative Authors

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

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

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

package revision

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"

	"github.com/google/go-containerregistry/pkg/authn/k8schain"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/client-go/kubernetes"
)

type digestResolver struct {
	client    kubernetes.Interface
	transport http.RoundTripper
}

const (
	// Kubernetes CA certificate bundle is mounted into the pod here, see:
	// https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/#trusting-tls-in-a-cluster
	k8sCertPath = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
)

// newResolverTransport returns an http.Transport that appends the certs bundle
// at path to the system cert pool.
//
// Use this with k8sCertPath to trust the same certs as the cluster.
func newResolverTransport(path string) (*http.Transport, error) {
	pool, err := x509.SystemCertPool()
	if err != nil {
		pool = x509.NewCertPool()
	}

	if crt, err := ioutil.ReadFile(path); err != nil {
		return nil, err
	} else if ok := pool.AppendCertsFromPEM(crt); !ok {
		return nil, errors.New("failed to append k8s cert bundle to cert pool")
	}

	// Copied from https://github.com/golang/go/blob/release-branch.go1.12/src/net/http/transport.go#L42-L53
	// We want to use the DefaultTransport but change its TLSClientConfig. There
	// isn't a clean way to do this yet: https://github.com/golang/go/issues/26013
	return &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
			DualStack: true,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ResponseHeaderTimeout: 10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
		// Use the cert pool with k8s cert bundle appended.
		TLSClientConfig: &tls.Config{
			RootCAs: pool,
		},
	}, nil
}

// Resolve resolves the image references that use tags to digests.
func (r *digestResolver) Resolve(
	image string,
	opt k8schain.Options,
	registriesToSkip sets.String) (string, error) {
	kc, err := k8schain.New(r.client, opt)
	if err != nil {
		return "", err
	}

	if _, err := name.NewDigest(image, name.WeakValidation); err == nil {
		// Already a digest
		return image, nil
	}

	tag, err := name.NewTag(image, name.WeakValidation)
	if err != nil {
		return "", err
	}

	if registriesToSkip.Has(tag.Registry.RegistryStr()) {
		return "", nil
	}

	img, err := remote.Image(tag, remote.WithTransport(r.transport), remote.WithAuthFromKeychain(kc))
	if err != nil {
		return "", err
	}
	digest, err := img.Digest()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s@%s", tag.Repository.String(), digest), nil
}
<|endoftext|>


<commit_before>package notifiers

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const PUSHOVER_ENDPOINT = "https://api.pushover.net/1/messages.json"

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pushover",
		Name:        "Pushover",
		Description: "Sends HTTP POST request to the Pushover API",
		Factory:     NewPushoverNotifier,
		OptionsTemplate: `

Pushover settings

        API Token
        <input type="text" class="gf-form-input" required placeholder="Application token" ng-model="ctrl.model.settings.apiToken"></input>

        User key(s)
        <input type="text" class="gf-form-input" required placeholder="comma-separated list" ng-model="ctrl.model.settings.userKey"></input>

        Device(s) (optional)
        <input type="text" class="gf-form-input" placeholder="comma-separated list; leave empty to send to all devices" ng-model="ctrl.model.settings.device"></input>

        Priority
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.priority" ng-options="v as k for (k, v) in {
          Emergency: '2',
          High:      '1',
          Normal:    '0',
          Low:      '-1',
          Lowest:   '-2'
        }" ng-init="ctrl.model.settings.priority=ctrl.model.settings.priority||'0'"></select>

        Retry
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="minimum 30 seconds" ng-model="ctrl.model.settings.retry" ng-init="ctrl.model.settings.retry=ctrl.model.settings.retry||'60'></input>

        Expire
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="maximum 86400 seconds" ng-model="ctrl.model.settings.expire" ng-init="ctrl.model.settings.expire=ctrl.model.settings.expire||'3600'"></input>

        Sound
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.sound" ng-options="s for s in [
          'default',
          'pushover',
          'bike',
          'bugle',
          'cashregister',
          'classical',
          'cosmic',
          'falling',
          'gamelan',
          'incoming',
          'intermission',
          'magic',
          'mechanical',
          'pianobar',
          'siren',
          'spacealarm',
          'tugboat',
          'alien',
          'climb',
          'persistent',
          'echo',
          'updown',
          'none'
        ]" ng-init="ctrl.model.settings.sound=ctrl.model.settings.sound||'default'"></select>

    `,
	})
}

func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	userKey := model.Settings.Get("userKey").MustString()
	apiToken := model.Settings.Get("apiToken").MustString()
	device := model.Settings.Get("device").MustString()
	priority, _ := strconv.Atoi(model.Settings.Get("priority").MustString())
	retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
	expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
	sound := model.Settings.Get("sound").MustString()

	if userKey == "" {
		return nil, alerting.ValidationError{Reason: "User key not given"}
	}
	if apiToken == "" {
		return nil, alerting.ValidationError{Reason: "API token not given"}
	}
	return &PushoverNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		UserKey:      userKey,
		ApiToken:     apiToken,
		Priority:     priority,
		Retry:        retry,
		Expire:       expire,
		Device:       device,
		Sound:        sound,
		log:          log.New("alerting.notifier.pushover"),
	}, nil
}

type PushoverNotifier struct {
	NotifierBase
	UserKey  string
	ApiToken string
	Priority int
	Retry    int
	Expire   int
	Device   string
	Sound    string
	log      log.Logger
}

func (this *PushoverNotifier) ShouldNotify(context *alerting.EvalContext) bool {
	return defaultShouldNotify(context)
}

func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}

	message := evalContext.Rule.Message
	for idx, evt := range evalContext.EvalMatches {
		message += fmt.Sprintf("\n%s: %v", evt.Metric, evt.Value)
		if idx > 4 {
			break
		}
	}
	if evalContext.Error != nil {
		message += fmt.Sprintf("\nError message: %s", evalContext.Error.Error())
	}
	if evalContext.ImagePublicUrl != "" {
		message += fmt.Sprintf("\nShow graph image", evalContext.ImagePublicUrl)
	}
	if message == "" {
		message = "Nothing to see here! (Set a notification message to replace this text.)"
	}

	q := url.Values{}
	q.Add("user", this.UserKey)
	q.Add("token", this.ApiToken)
	q.Add("priority", strconv.Itoa(this.Priority))
	if this.Priority == 2 {
		q.Add("retry", strconv.Itoa(this.Retry))
		q.Add("expire", strconv.Itoa(this.Expire))
	}
	if this.Device != "" {
		q.Add("device", this.Device)
	}
	if this.Sound != "default" {
		q.Add("sound", this.Sound)
	}
	q.Add("title", evalContext.GetNotificationTitle())
	q.Add("url", ruleUrl)
	q.Add("url_title", "Show dashboard with alert")
	q.Add("message", message)
	q.Add("html", "1")

	cmd := &m.SendWebhookSync{
		Url:        PUSHOVER_ENDPOINT,
		HttpMethod: "POST",
		HttpHeader: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
		Body:       q.Encode(),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send pushover notification", "error", err, "webhook", this.Name)
		return err
	}

	return nil
}
<commit_msg>pushover: update default message<commit_after>package notifiers

import (
	"fmt"
	"net/url"
	"strconv"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

const PUSHOVER_ENDPOINT = "https://api.pushover.net/1/messages.json"

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "pushover",
		Name:        "Pushover",
		Description: "Sends HTTP POST request to the Pushover API",
		Factory:     NewPushoverNotifier,
		OptionsTemplate: `

Pushover settings

        API Token
        <input type="text" class="gf-form-input" required placeholder="Application token" ng-model="ctrl.model.settings.apiToken"></input>

        User key(s)
        <input type="text" class="gf-form-input" required placeholder="comma-separated list" ng-model="ctrl.model.settings.userKey"></input>

        Device(s) (optional)
        <input type="text" class="gf-form-input" placeholder="comma-separated list; leave empty to send to all devices" ng-model="ctrl.model.settings.device"></input>

        Priority
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.priority" ng-options="v as k for (k, v) in {
          Emergency: '2',
          High:      '1',
          Normal:    '0',
          Low:      '-1',
          Lowest:   '-2'
        }" ng-init="ctrl.model.settings.priority=ctrl.model.settings.priority||'0'"></select>

        Retry
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="minimum 30 seconds" ng-model="ctrl.model.settings.retry" ng-init="ctrl.model.settings.retry=ctrl.model.settings.retry||'60'></input>

        Expire
        <input type="text" class="gf-form-input max-width-14" ng-required="ctrl.model.settings.priority == '2'" placeholder="maximum 86400 seconds" ng-model="ctrl.model.settings.expire" ng-init="ctrl.model.settings.expire=ctrl.model.settings.expire||'3600'"></input>

        Sound
        <select class="gf-form-input max-width-14" ng-model="ctrl.model.settings.sound" ng-options="s for s in [
          'default',
          'pushover',
          'bike',
          'bugle',
          'cashregister',
          'classical',
          'cosmic',
          'falling',
          'gamelan',
          'incoming',
          'intermission',
          'magic',
          'mechanical',
          'pianobar',
          'siren',
          'spacealarm',
          'tugboat',
          'alien',
          'climb',
          'persistent',
          'echo',
          'updown',
          'none'
        ]" ng-init="ctrl.model.settings.sound=ctrl.model.settings.sound||'default'"></select>

    `,
	})
}

func NewPushoverNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	userKey := model.Settings.Get("userKey").MustString()
	apiToken := model.Settings.Get("apiToken").MustString()
	device := model.Settings.Get("device").MustString()
	priority, _ := strconv.Atoi(model.Settings.Get("priority").MustString())
	retry, _ := strconv.Atoi(model.Settings.Get("retry").MustString())
	expire, _ := strconv.Atoi(model.Settings.Get("expire").MustString())
	sound := model.Settings.Get("sound").MustString()

	if userKey == "" {
		return nil, alerting.ValidationError{Reason: "User key not given"}
	}
	if apiToken == "" {
		return nil, alerting.ValidationError{Reason: "API token not given"}
	}
	return &PushoverNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		UserKey:      userKey,
		ApiToken:     apiToken,
		Priority:     priority,
		Retry:        retry,
		Expire:       expire,
		Device:       device,
		Sound:        sound,
		log:          log.New("alerting.notifier.pushover"),
	}, nil
}

type PushoverNotifier struct {
	NotifierBase
	UserKey  string
	ApiToken string
	Priority int
	Retry    int
	Expire   int
	Device   string
	Sound    string
	log      log.Logger
}

func (this *PushoverNotifier) ShouldNotify(context *alerting.EvalContext) bool {
	return defaultShouldNotify(context)
}

func (this *PushoverNotifier) Notify(evalContext *alerting.EvalContext) error {
	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}

	message := evalContext.Rule.Message
	for idx, evt := range evalContext.EvalMatches {
		message += fmt.Sprintf("\n%s: %v", evt.Metric, evt.Value)
		if idx > 4 {
			break
		}
	}
	if evalContext.Error != nil {
		message += fmt.Sprintf("\nError message: %s", evalContext.Error.Error())
	}
	if evalContext.ImagePublicUrl != "" {
		message += fmt.Sprintf("\nShow graph image", evalContext.ImagePublicUrl)
	}
	if message == "" {
		message = "Notification message missing (Set a notification message to replace this text.)"
	}

	q := url.Values{}
	q.Add("user", this.UserKey)
	q.Add("token", this.ApiToken)
	q.Add("priority", strconv.Itoa(this.Priority))
	if this.Priority == 2 {
		q.Add("retry", strconv.Itoa(this.Retry))
		q.Add("expire", strconv.Itoa(this.Expire))
	}
	if this.Device != "" {
		q.Add("device", this.Device)
	}
	if this.Sound != "default" {
		q.Add("sound", this.Sound)
	}
	q.Add("title", evalContext.GetNotificationTitle())
	q.Add("url", ruleUrl)
	q.Add("url_title", "Show dashboard with alert")
	q.Add("message", message)
	q.Add("html", "1")

	cmd := &m.SendWebhookSync{
		Url:        PUSHOVER_ENDPOINT,
		HttpMethod: "POST",
		HttpHeader: map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
		Body:       q.Encode(),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send pushover notification", "error", err, "webhook", this.Name)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"testing"

	keybase1 "github.com/keybase/client/go/protocol"
)

func TestPGPKeyGen(t *testing.T) {
	tc := SetupEngineTest(t, "pgpkeygen")
	defer tc.Cleanup()

	u := CreateAndSignupFakeUser(tc, "pgp")
	pgpUI := &TestPgpUI{ShouldPush: true}
	ctx := &Context{
		LogUI:    tc.G.UI.GetLogUI(),
		SecretUI: u.NewSecretUI(),
		PgpUI:    pgpUI,
	}
	arg := keybase1.PGPKeyGenDefaultArg{
		CreateUids: keybase1.PGPCreateUids{
			UseDefault: true,
			Ids: []keybase1.PGPIdentity{
				{Username: u.Username, Email: u.Email},
			},
		},
	}
	eng := NewPGPKeyGen(tc.G, arg)
	if err := RunEngine(eng, ctx); err != nil {
		t.Fatal(err)
	}

	xarg := keybase1.PGPExportArg{
		Options: keybase1.PGPQuery{
			Secret: true,
			Query:  pgpUI.Generated.Key.Fingerprint,
		},
	}
	xe := NewPGPKeyExportEngine(xarg, tc.G)
	if err := RunEngine(xe, ctx); err != nil {
		t.Fatal(err)
	}
	if len(xe.Results()) != 1 {
		t.Errorf("result keys: %d, expected 1", len(xe.Results()))
	}
}
<commit_msg>Add failing test<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package engine

import (
	"testing"

	keybase1 "github.com/keybase/client/go/protocol"
)

func TestPGPKeyGenPush(t *testing.T) {
	tc := SetupEngineTest(t, "pgpkeygen")
	defer tc.Cleanup()

	u := CreateAndSignupFakeUser(tc, "pgp")
	pgpUI := &TestPgpUI{ShouldPush: true}
	ctx := &Context{
		LogUI:    tc.G.UI.GetLogUI(),
		SecretUI: u.NewSecretUI(),
		PgpUI:    pgpUI,
	}
	arg := keybase1.PGPKeyGenDefaultArg{
		CreateUids: keybase1.PGPCreateUids{
			UseDefault: true,
			Ids: []keybase1.PGPIdentity{
				{Username: u.Username, Email: u.Email},
			},
		},
	}
	eng := NewPGPKeyGen(tc.G, arg)
	if err := RunEngine(eng, ctx); err != nil {
		t.Fatal(err)
	}

	xarg := keybase1.PGPExportArg{
		Options: keybase1.PGPQuery{
			Secret: true,
			Query:  pgpUI.Generated.Key.Fingerprint,
		},
	}
	xe := NewPGPKeyExportEngine(xarg, tc.G)
	if err := RunEngine(xe, ctx); err != nil {
		t.Fatal(err)
	}
	if len(xe.Results()) != 1 {
		t.Errorf("result keys: %d, expected 1", len(xe.Results()))
	}
}

func TestPGPKeyGenNoPush(t *testing.T) {
	tc := SetupEngineTest(t, "pgpkeygen")
	defer tc.Cleanup()

	u := CreateAndSignupFakeUser(tc, "pgp")
	pgpUI := &TestPgpUI{ShouldPush: false}
	ctx := &Context{
		LogUI:    tc.G.UI.GetLogUI(),
		SecretUI: u.NewSecretUI(),
		PgpUI:    pgpUI,
	}
	arg := keybase1.PGPKeyGenDefaultArg{
		CreateUids: keybase1.PGPCreateUids{
			UseDefault: true,
			Ids: []keybase1.PGPIdentity{
				{Username: u.Username, Email: u.Email},
			},
		},
	}
	eng := NewPGPKeyGen(tc.G, arg)
	if err := RunEngine(eng, ctx); err != nil {
		t.Fatal(err)
	}

	xarg := keybase1.PGPExportArg{
		Options: keybase1.PGPQuery{
			Secret: true,
			Query:  pgpUI.Generated.Key.Fingerprint,
		},
	}
	xe := NewPGPKeyExportEngine(xarg, tc.G)
	if err := RunEngine(xe, ctx); err != nil {
		t.Fatal(err)
	}
	if len(xe.Results()) != 1 {
		t.Errorf("result keys: %d, expected 1", len(xe.Results()))
	}
}
<|endoftext|>


<commit_before>package ingestion

import (
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"testing"
	"time"

	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/gcs/gcs_testutils"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/testutils/unittest"
)

const (
	// TEST_GCS_DIR is the directory from where to fetch GCS test data.
	TEST_GCS_DIR = "ingest-testdata/dm-json-v1"

	// TEST_DATA_DIR  is the directory with data used for local ingest.
	TEST_DATA_DIR = "./testdata/local-ingest-test"

	// TEST_DATA_STORAGE_PATH is the folder in the test data bucket.
	// See go/testutils for details.
	TEST_DATA_STORAGE_PATH = "ingest-testdata/local-new-ingestion.tar.gz"

	// TEST_DATA_FILE_INDEX contains the list of files contained in the locally
	// ingested file and the Google storage bucket.
	TEST_DATA_INDEX_FILE = "./testdata/local_ingest_files.txt"
)

var (
	BEGINNING_OF_TIME = time.Date(2015, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()
	END_OF_TIME       = time.Date(2015, time.October, 30, 0, 0, 0, 0, time.UTC).Unix()
	START_TIME        = time.Date(2015, time.October, 1, 0, 0, 0, 0, time.UTC).Unix()
	END_TIME          = time.Date(2015, time.October, 1, 23, 59, 59, 0, time.UTC).Unix()
)

func TestGoogleStorageSource(t *testing.T) {
	unittest.LargeTest(t)

	src, err := NewGoogleStorageSource("gs-test-src", gcs_testutils.TEST_DATA_BUCKET, TEST_GCS_DIR, http.DefaultClient, nil)
	assert.NoError(t, err)
	testSource(t, src)
}

func TestFileSystemResultFileLocations(t *testing.T) {
	unittest.LargeTest(t)

	err := gcs_testutils.DownloadTestDataArchive(t, gcs_testutils.TEST_DATA_BUCKET, TEST_DATA_STORAGE_PATH, TEST_DATA_DIR)
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, TEST_DATA_DIR)

	src, err := NewFileSystemSource("test-fs-source", TEST_DATA_DIR)
	assert.NoError(t, err)
	testSource(t, src)
}

func TestCompareSources(t *testing.T) {
	unittest.LargeTest(t)

	gsSource, err := NewGoogleStorageSource("gs-test-src", gcs_testutils.TEST_DATA_BUCKET, TEST_GCS_DIR, http.DefaultClient, nil)
	assert.NoError(t, err)

	err = gcs_testutils.DownloadTestDataArchive(t, gcs_testutils.TEST_DATA_BUCKET, TEST_DATA_STORAGE_PATH, TEST_DATA_DIR)
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, TEST_DATA_DIR)

	fsSource, err := NewFileSystemSource("test-fs-source", TEST_DATA_DIR)
	assert.NoError(t, err)

	gsResults := drainPollChannel(gsSource.Poll(START_TIME, END_TIME))
	fsResults := drainPollChannel(fsSource.Poll(START_TIME, END_TIME))

	assert.Equal(t, len(gsResults), len(fsResults))
	sort.Sort(rflSlice(gsResults))
	sort.Sort(rflSlice(fsResults))

	// Only compare a subset of all files to limit the runtime of this test.
	gsResults = gsResults[:10]
	fsResults = fsResults[:10]

	for idx, result := range gsResults {
		// Check if the MD5 from GS and the file system match.
		assert.Equal(t, result.MD5(), fsResults[idx].MD5())

		// Check if the content is the same for both sources.
		file, err := result.Open()
		assert.NoError(t, err)
		c1, err := ioutil.ReadAll(file)
		assert.NoError(t, err)
		file, err = fsResults[idx].Open()
		c2, err := ioutil.ReadAll(file)
		assert.NoError(t, err)
		assert.Equal(t, string(c1), string(c2))
	}
}

func testSource(t *testing.T, src Source) {
	testFilePaths := readTestFileNames(t)

	resultFileLocations := drainPollChannel(src.Poll(START_TIME, END_TIME))

	assert.Equal(t, len(testFilePaths), len(resultFileLocations))
	sort.Sort(rflSlice(resultFileLocations))

	for idx, result := range resultFileLocations {
		assert.True(t, strings.HasSuffix(result.Name(), testFilePaths[idx]))
	}

	// Make sure the narrow and wide time range produce the same result.
	allResultFileLocations := drainPollChannel(src.Poll(BEGINNING_OF_TIME, END_OF_TIME))
	sort.Sort(rflSlice(allResultFileLocations))

	assert.Equal(t, len(resultFileLocations), len(allResultFileLocations))
	for idx, result := range resultFileLocations {
		assert.Equal(t, result.MD5(), allResultFileLocations[idx].MD5())
		assert.Equal(t, result.Name(), allResultFileLocations[idx].Name())
	}
}

func readTestFileNames(t *testing.T) []string {
	// Read the expected list of files and compare them.
	content, err := ioutil.ReadFile("./testdata/filelist_2015_10_01.txt")
	assert.NoError(t, err)
	lines := strings.Split(strings.TrimSpace(string(content)), "\n")
	sort.Strings(lines)
	return lines
}

type rflSlice []ResultFileLocation

func (r rflSlice) Len() int           { return len(r) }
func (r rflSlice) Less(i, j int) bool { return r[i].Name() < r[j].Name() }
func (r rflSlice) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }

func drainPollChannel(ch <-chan ResultFileLocation) []ResultFileLocation {
	ret := []ResultFileLocation{}
	for rf := range ch {
		ret = append(ret, rf)
	}
	return ret
}
<commit_msg>Disable flaky test.<commit_after>package ingestion

import (
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"testing"
	"time"

	assert "github.com/stretchr/testify/require"
	"go.skia.org/infra/go/gcs/gcs_testutils"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/testutils/unittest"
)

const (
	// TEST_GCS_DIR is the directory from where to fetch GCS test data.
	TEST_GCS_DIR = "ingest-testdata/dm-json-v1"

	// TEST_DATA_DIR  is the directory with data used for local ingest.
	TEST_DATA_DIR = "./testdata/local-ingest-test"

	// TEST_DATA_STORAGE_PATH is the folder in the test data bucket.
	// See go/testutils for details.
	TEST_DATA_STORAGE_PATH = "ingest-testdata/local-new-ingestion.tar.gz"

	// TEST_DATA_FILE_INDEX contains the list of files contained in the locally
	// ingested file and the Google storage bucket.
	TEST_DATA_INDEX_FILE = "./testdata/local_ingest_files.txt"
)

var (
	BEGINNING_OF_TIME = time.Date(2015, time.June, 1, 0, 0, 0, 0, time.UTC).Unix()
	END_OF_TIME       = time.Date(2015, time.October, 30, 0, 0, 0, 0, time.UTC).Unix()
	START_TIME        = time.Date(2015, time.October, 1, 0, 0, 0, 0, time.UTC).Unix()
	END_TIME          = time.Date(2015, time.October, 1, 23, 59, 59, 0, time.UTC).Unix()
)

func TestGoogleStorageSource(t *testing.T) {
	unittest.LargeTest(t)

	src, err := NewGoogleStorageSource("gs-test-src", gcs_testutils.TEST_DATA_BUCKET, TEST_GCS_DIR, http.DefaultClient, nil)
	assert.NoError(t, err)
	testSource(t, src)
}

func TestFileSystemResultFileLocations(t *testing.T) {
	unittest.LargeTest(t)

	err := gcs_testutils.DownloadTestDataArchive(t, gcs_testutils.TEST_DATA_BUCKET, TEST_DATA_STORAGE_PATH, TEST_DATA_DIR)
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, TEST_DATA_DIR)

	src, err := NewFileSystemSource("test-fs-source", TEST_DATA_DIR)
	assert.NoError(t, err)
	testSource(t, src)
}

func TestCompareSources(t *testing.T) {
	// This test often times out after 5m on the Race bot, causing flaky failures.
	// Since https://bugs.chromium.org/p/skia/issues/detail?id=8692 is marked Low
	// Priority, I'm disabling the test until it's fixed or removed.
	unittest.ManualTest(t)

	gsSource, err := NewGoogleStorageSource("gs-test-src", gcs_testutils.TEST_DATA_BUCKET, TEST_GCS_DIR, http.DefaultClient, nil)
	assert.NoError(t, err)

	err = gcs_testutils.DownloadTestDataArchive(t, gcs_testutils.TEST_DATA_BUCKET, TEST_DATA_STORAGE_PATH, TEST_DATA_DIR)
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, TEST_DATA_DIR)

	fsSource, err := NewFileSystemSource("test-fs-source", TEST_DATA_DIR)
	assert.NoError(t, err)

	gsResults := drainPollChannel(gsSource.Poll(START_TIME, END_TIME))
	fsResults := drainPollChannel(fsSource.Poll(START_TIME, END_TIME))

	assert.Equal(t, len(gsResults), len(fsResults))
	sort.Sort(rflSlice(gsResults))
	sort.Sort(rflSlice(fsResults))

	// Only compare a subset of all files to limit the runtime of this test.
	gsResults = gsResults[:10]
	fsResults = fsResults[:10]

	for idx, result := range gsResults {
		// Check if the MD5 from GS and the file system match.
		assert.Equal(t, result.MD5(), fsResults[idx].MD5())

		// Check if the content is the same for both sources.
		file, err := result.Open()
		assert.NoError(t, err)
		c1, err := ioutil.ReadAll(file)
		assert.NoError(t, err)
		file, err = fsResults[idx].Open()
		c2, err := ioutil.ReadAll(file)
		assert.NoError(t, err)
		assert.Equal(t, string(c1), string(c2))
	}
}

func testSource(t *testing.T, src Source) {
	testFilePaths := readTestFileNames(t)

	resultFileLocations := drainPollChannel(src.Poll(START_TIME, END_TIME))

	assert.Equal(t, len(testFilePaths), len(resultFileLocations))
	sort.Sort(rflSlice(resultFileLocations))

	for idx, result := range resultFileLocations {
		assert.True(t, strings.HasSuffix(result.Name(), testFilePaths[idx]))
	}

	// Make sure the narrow and wide time range produce the same result.
	allResultFileLocations := drainPollChannel(src.Poll(BEGINNING_OF_TIME, END_OF_TIME))
	sort.Sort(rflSlice(allResultFileLocations))

	assert.Equal(t, len(resultFileLocations), len(allResultFileLocations))
	for idx, result := range resultFileLocations {
		assert.Equal(t, result.MD5(), allResultFileLocations[idx].MD5())
		assert.Equal(t, result.Name(), allResultFileLocations[idx].Name())
	}
}

func readTestFileNames(t *testing.T) []string {
	// Read the expected list of files and compare them.
	content, err := ioutil.ReadFile("./testdata/filelist_2015_10_01.txt")
	assert.NoError(t, err)
	lines := strings.Split(strings.TrimSpace(string(content)), "\n")
	sort.Strings(lines)
	return lines
}

type rflSlice []ResultFileLocation

func (r rflSlice) Len() int           { return len(r) }
func (r rflSlice) Less(i, j int) bool { return r[i].Name() < r[j].Name() }
func (r rflSlice) Swap(i, j int)      { r[i], r[j] = r[j], r[i] }

func drainPollChannel(ch <-chan ResultFileLocation) []ResultFileLocation {
	ret := []ResultFileLocation{}
	for rf := range ch {
		ret = append(ret, rf)
	}
	return ret
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

import (
	"github.com/longbai/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for _, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &p)
		} else {
			normalProblems = append(normalProblems, &p)
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if v == fileName {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnore(path) {
			checkFile(path)
		}
		return err
	})
}
<commit_msg>transfer owner<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for _, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &p)
		} else {
			normalProblems = append(normalProblems, &p)
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if v == fileName {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnore(path) {
			checkFile(path)
		}
		return err
	})
}
<|endoftext|>


<commit_before>package main

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

var tokens = make(chan struct{}, 20)

func crawl(url string) []string {
	fmt.Println(url)
	tokens <- struct{}{}
	list, err := Extract(url)
	<-tokens

	if err != nil {
		log.Print(err)
	}
	return list
}

func main() {
	worklist := make(chan []string)
	var n int

	n++
	go func() { worklist <- os.Args[1:] }()

	seen := make(map[string]bool)
	for ; n > 0; n-- {
		list := <-worklist
		for _, link := range list {
			if !seen[link] {
				seen[link] = true
				n++
				go func(link string) {
					worklist <- crawl(link)
				}(link)
			}
		}
	}
}
<commit_msg>[8.6] add link depth support for crawl<commit_after>package main

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

var tokens = make(chan struct{}, 20)

type link struct {
	url   string
	depth int
}

func crawl(l link, maxDepth int) []link {
	links := []link{}
	if l.depth >= maxDepth {
		return links
	}
	tokens <- struct{}{}
	list, err := Extract(l.url)
	<-tokens
	if err != nil {
		log.Print(err)
	}

	depth := l.depth + 1
	for _, url := range list {
		links = append(links, link{url, depth})
		fmt.Printf("[%d/%d]: %s\n", depth, maxDepth, url)
	}
	return links
}

func main() {
	worklist := make(chan []string)
	var n int

	n++
	go func() { worklist <- os.Args[1:] }()

	seen := make(map[string]bool)
	for ; n > 0; n-- {
		list := <-worklist
		for _, link := range list {
			if !seen[link] {
				seen[link] = true
				n++
				go func(link string) {
					worklist <- crawl(link)
				}(link)
			}
		}
	}
}
<|endoftext|>