<commit_before>/*
Copyright 2020 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 kubetest2 is DEPRECATED. Please use https://github.com/kubernetes-sigs/kubetest2/tree/master/kubetest2-gke/deployer directly.
package kubetest2

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"knative.dev/test-infra/pkg/cmd"
	"knative.dev/test-infra/pkg/helpers"
	"knative.dev/test-infra/pkg/metautil"
	"knative.dev/test-infra/pkg/prow"
)

const (
	createCommandTmpl = "%s container clusters create --quiet --enable-autoscaling --min-nodes=%d --max-nodes=%d " +
		"--scopes=%s"
	clusterNamePrefix                  = "e2e-cls"
	boskosAcquireDefaultTimeoutSeconds = 1200
)

var (
	baseKubetest2Flags = []string{
		"gke",
		"--up",
		"--down",
		"--ignore-gcp-ssh-key=true",
		"--boskos-heartbeat-interval-seconds=60",
		"-v=1",
	}

	// If one of the error patterns below is matched, it would be recommended to
	// retry creating the cluster in a different region.
	// - stockout (https://github.com/knative/test-infra/issues/592)
	retryableErrorPatterns = `.*does not have enough resources available to fulfill.*` +
		`,.*only \d+ nodes out of \d+ have registered; this is likely due to Nodes failing to start correctly.*` +
		`,.*All cluster resources were brought up.+ but: component .+ from endpoint .+ is unhealthy.*`
)

// GKEClusterConfig are the supported configurations for creating a GKE cluster.
type GKEClusterConfig struct {
	GCPServiceAccount string
	GCPProjectID      string

	BoskosAcquireTimeoutSeconds int

	Environment  string
	CommandGroup string

	Name                              string
	Region                            string
	BackupRegions                     []string
	Machine                           string
	MinNodes                          int
	MaxNodes                          int
	Network                           string
	ReleaseChannel                    string
	Version                           string
	Scopes                            string
	Addons                            string
	ImageType                         string
	EnableWorkloadIdentity            bool
	PrivateClusterAccessLevel         string
	PrivateClusterMasterIPSubnetRange []string

	ExtraGcloudFlags string
}

// Run will run the `kubetest2 gke` command with the provided parameters,
// it will also handle the logic that is only used for Knative integration testing, like retrying cluster creation.
func Run(opts *Options, cc *GKEClusterConfig) error {
	kubetest2Flags := baseKubetest2Flags

	createCommand := fmt.Sprintf(createCommandTmpl, cc.CommandGroup, cc.MinNodes, cc.MaxNodes, cc.Scopes)
	if cc.ReleaseChannel != "" {
		createCommand += " --release-channel=" + cc.ReleaseChannel
	}
	kubetest2Flags = append(kubetest2Flags, "--version="+cc.Version)
	if cc.Addons != "" {
		createCommand += " --addons=" + cc.Addons
	}
	if cc.ExtraGcloudFlags != "" {
		createCommand += " " + cc.ExtraGcloudFlags
	}
	kubetest2Flags = append(kubetest2Flags, "--create-command="+createCommand)

	// If cluster name is not provided, generate a random name.
	if cc.Name == "" {
		cc.Name = helpers.AppendRandomString(clusterNamePrefix)
	}
	kubetest2Flags = append(kubetest2Flags, "--cluster-name="+cc.Name, "--environment="+cc.Environment,
		"--num-nodes="+strconv.Itoa(cc.MinNodes), "--machine-type="+cc.Machine, "--network="+cc.Network,
		"--image-type="+cc.ImageType)
	if cc.GCPServiceAccount != "" {
		kubetest2Flags = append(kubetest2Flags, "--gcp-service-account="+cc.GCPServiceAccount)
	}
	if cc.EnableWorkloadIdentity {
		kubetest2Flags = append(kubetest2Flags, "--enable-workload-identity")
	}

	if prow.IsCI() && cc.GCPProjectID == "" {
		log.Println("Will use boskos to provision the GCP project")
		timeout := cc.BoskosAcquireTimeoutSeconds
		if timeout == 0 {
			timeout = boskosAcquireDefaultTimeoutSeconds
		}
		kubetest2Flags = append(kubetest2Flags, "--boskos-acquire-timeout-seconds="+strconv.Itoa(timeout))
	} else {
		if cc.GCPProjectID == "" {
			return errors.New("GCP project must be provided in non-CI environment")
		}
		log.Printf("Will use the GCP project %q for creating the cluster", cc.GCPProjectID)
		kubetest2Flags = append(kubetest2Flags, "--project="+cc.GCPProjectID)
	}

	if cc.PrivateClusterAccessLevel != "" {
		kubetest2Flags = append(kubetest2Flags, "--private-cluster-access-level="+cc.PrivateClusterAccessLevel)
		kubetest2Flags = append(kubetest2Flags, "--private-cluster-master-ip-range="+strings.Join(cc.PrivateClusterMasterIPSubnetRange, ","))
	}

	regions := append([]string{cc.Region}, cc.BackupRegions...)
	kubetest2Flags = append(kubetest2Flags, "--region="+strings.Join(regions, ","))
	kubetest2Flags = append(kubetest2Flags, "--retryable-error-patterns='"+retryableErrorPatterns+"'")

	// Test command args must come last.
	if opts.TestCommand != "" {
		kubetest2Flags = append(kubetest2Flags, "--test=exec", "--")
		kubetest2Flags = append(kubetest2Flags, strings.Split(opts.TestCommand, " ")...)
	}

	log.Printf("Running kubetest2 with flags: %q", kubetest2Flags)

	command := exec.Command("kubetest2", kubetest2Flags...)
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	if err := command.Run(); err != nil {
		return err
	}

	// Only save the metadata if it's in CI environment and meta data is asked to be saved.
	if prow.IsCI() && opts.SaveMetaData {
		return saveMetaData(cc, strings.Join(regions, ","))
	}
	return nil
}

// saveMetaData will save the metadata with best effort.
func saveMetaData(cc *GKEClusterConfig, region string) error {
	cli, err := metautil.NewClient("")
	if err != nil {
		return fmt.Errorf("error creating the metautil client: %w", err)
	}
	cv, err := cmd.RunCommand("kubectl version --short=true")
	if err != nil {
		return fmt.Errorf("error getting the cluster version: %w", err)
	}

	// Set the metadata with best effort.
	cli.Set("E2E:Provider", "gke")
	cli.Set("E2E:Region", region)
	cli.Set("E2E:Machine", cc.Machine)
	cli.Set("E2E:Version", cv)
	cli.Set("E2E:MinNodes", strconv.Itoa(cc.MinNodes))
	cli.Set("E2E:MaxNodes", strconv.Itoa(cc.MaxNodes))
	return nil
}
<commit_msg>updates deprecated cluster --version flag (#3298)<commit_after>/*
Copyright 2020 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 kubetest2 is DEPRECATED. Please use https://github.com/kubernetes-sigs/kubetest2/tree/master/kubetest2-gke/deployer directly.
package kubetest2

import (
	"errors"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"knative.dev/test-infra/pkg/cmd"
	"knative.dev/test-infra/pkg/helpers"
	"knative.dev/test-infra/pkg/metautil"
	"knative.dev/test-infra/pkg/prow"
)

const (
	createCommandTmpl = "%s container clusters create --quiet --enable-autoscaling --min-nodes=%d --max-nodes=%d " +
		"--scopes=%s"
	clusterNamePrefix                  = "e2e-cls"
	boskosAcquireDefaultTimeoutSeconds = 1200
)

var (
	baseKubetest2Flags = []string{
		"gke",
		"--up",
		"--down",
		"--ignore-gcp-ssh-key=true",
		"--boskos-heartbeat-interval-seconds=60",
		"-v=1",
	}

	// If one of the error patterns below is matched, it would be recommended to
	// retry creating the cluster in a different region.
	// - stockout (https://github.com/knative/test-infra/issues/592)
	retryableErrorPatterns = `.*does not have enough resources available to fulfill.*` +
		`,.*only \d+ nodes out of \d+ have registered; this is likely due to Nodes failing to start correctly.*` +
		`,.*All cluster resources were brought up.+ but: component .+ from endpoint .+ is unhealthy.*`
)

// GKEClusterConfig are the supported configurations for creating a GKE cluster.
type GKEClusterConfig struct {
	GCPServiceAccount string
	GCPProjectID      string

	BoskosAcquireTimeoutSeconds int

	Environment  string
	CommandGroup string

	Name                              string
	Region                            string
	BackupRegions                     []string
	Machine                           string
	MinNodes                          int
	MaxNodes                          int
	Network                           string
	ReleaseChannel                    string
	Version                           string
	Scopes                            string
	Addons                            string
	ImageType                         string
	EnableWorkloadIdentity            bool
	PrivateClusterAccessLevel         string
	PrivateClusterMasterIPSubnetRange []string

	ExtraGcloudFlags string
}

// Run will run the `kubetest2 gke` command with the provided parameters,
// it will also handle the logic that is only used for Knative integration testing, like retrying cluster creation.
func Run(opts *Options, cc *GKEClusterConfig) error {
	kubetest2Flags := baseKubetest2Flags

	createCommand := fmt.Sprintf(createCommandTmpl, cc.CommandGroup, cc.MinNodes, cc.MaxNodes, cc.Scopes)
	if cc.ReleaseChannel != "" {
		createCommand += " --release-channel=" + cc.ReleaseChannel
	}
	kubetest2Flags = append(kubetest2Flags, "--cluster-version="+cc.Version)
	if cc.Addons != "" {
		createCommand += " --addons=" + cc.Addons
	}
	if cc.ExtraGcloudFlags != "" {
		createCommand += " " + cc.ExtraGcloudFlags
	}
	kubetest2Flags = append(kubetest2Flags, "--create-command="+createCommand)

	// If cluster name is not provided, generate a random name.
	if cc.Name == "" {
		cc.Name = helpers.AppendRandomString(clusterNamePrefix)
	}
	kubetest2Flags = append(kubetest2Flags, "--cluster-name="+cc.Name, "--environment="+cc.Environment,
		"--num-nodes="+strconv.Itoa(cc.MinNodes), "--machine-type="+cc.Machine, "--network="+cc.Network,
		"--image-type="+cc.ImageType)
	if cc.GCPServiceAccount != "" {
		kubetest2Flags = append(kubetest2Flags, "--gcp-service-account="+cc.GCPServiceAccount)
	}
	if cc.EnableWorkloadIdentity {
		kubetest2Flags = append(kubetest2Flags, "--enable-workload-identity")
	}

	if prow.IsCI() && cc.GCPProjectID == "" {
		log.Println("Will use boskos to provision the GCP project")
		timeout := cc.BoskosAcquireTimeoutSeconds
		if timeout == 0 {
			timeout = boskosAcquireDefaultTimeoutSeconds
		}
		kubetest2Flags = append(kubetest2Flags, "--boskos-acquire-timeout-seconds="+strconv.Itoa(timeout))
	} else {
		if cc.GCPProjectID == "" {
			return errors.New("GCP project must be provided in non-CI environment")
		}
		log.Printf("Will use the GCP project %q for creating the cluster", cc.GCPProjectID)
		kubetest2Flags = append(kubetest2Flags, "--project="+cc.GCPProjectID)
	}

	if cc.PrivateClusterAccessLevel != "" {
		kubetest2Flags = append(kubetest2Flags, "--private-cluster-access-level="+cc.PrivateClusterAccessLevel)
		kubetest2Flags = append(kubetest2Flags, "--private-cluster-master-ip-range="+strings.Join(cc.PrivateClusterMasterIPSubnetRange, ","))
	}

	regions := append([]string{cc.Region}, cc.BackupRegions...)
	kubetest2Flags = append(kubetest2Flags, "--region="+strings.Join(regions, ","))
	kubetest2Flags = append(kubetest2Flags, "--retryable-error-patterns='"+retryableErrorPatterns+"'")

	// Test command args must come last.
	if opts.TestCommand != "" {
		kubetest2Flags = append(kubetest2Flags, "--test=exec", "--")
		kubetest2Flags = append(kubetest2Flags, strings.Split(opts.TestCommand, " ")...)
	}

	log.Printf("Running kubetest2 with flags: %q", kubetest2Flags)

	command := exec.Command("kubetest2", kubetest2Flags...)
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	if err := command.Run(); err != nil {
		return err
	}

	// Only save the metadata if it's in CI environment and meta data is asked to be saved.
	if prow.IsCI() && opts.SaveMetaData {
		return saveMetaData(cc, strings.Join(regions, ","))
	}
	return nil
}

// saveMetaData will save the metadata with best effort.
func saveMetaData(cc *GKEClusterConfig, region string) error {
	cli, err := metautil.NewClient("")
	if err != nil {
		return fmt.Errorf("error creating the metautil client: %w", err)
	}
	cv, err := cmd.RunCommand("kubectl version --short=true")
	if err != nil {
		return fmt.Errorf("error getting the cluster version: %w", err)
	}

	// Set the metadata with best effort.
	cli.Set("E2E:Provider", "gke")
	cli.Set("E2E:Region", region)
	cli.Set("E2E:Machine", cc.Machine)
	cli.Set("E2E:Version", cv)
	cli.Set("E2E:MinNodes", strconv.Itoa(cc.MinNodes))
	cli.Set("E2E:MaxNodes", strconv.Itoa(cc.MaxNodes))
	return nil
}
<|endoftext|>


<commit_before>package renderer

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"strconv"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/middleware"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

type RenderOpts struct {
	Path    string
	Width   string
	Height  string
	Timeout string
	OrgId   int64
}

var rendererLog log.Logger = log.New("png-renderer")

func RenderToPng(params *RenderOpts) (string, error) {
	rendererLog.Info("Rendering", "path", params.Path)

	var executable = "phantomjs"
	if runtime.GOOS == "windows" {
		executable = executable + ".exe"
	}

	localDomain := "localhost"
	if setting.HttpAddr != setting.DEFAULT_HTTP_ADDR {
		localDomain = setting.HttpAddr
	}

	url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localDomain, setting.HttpPort, params.Path)

	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable))
	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
	pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20)))
	pngPath = pngPath + ".png"

	renderKey := middleware.AddRenderAuthKey(params.OrgId)
	defer middleware.RemoveRenderAuthKey(renderKey)

	cmdArgs := []string{
		"--ignore-ssl-errors=true",
		scriptPath,
		"url=" + url,
		"width=" + params.Width,
		"height=" + params.Height,
		"png=" + pngPath,
		"domain=" + localDomain,
		"renderKey=" + renderKey,
	}

	cmd := exec.Command(binPath, cmdArgs...)
	stdout, err := cmd.StdoutPipe()

	if err != nil {
		return "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stdout, stderr)

	done := make(chan error)
	go func() {
		cmd.Wait()
		close(done)
	}()

	timeout, err := strconv.Atoi(params.Timeout)
	if err != nil {
		timeout = 15
	}

	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			rendererLog.Error("failed to kill", "error", err)
		}
		return "", fmt.Errorf("PhantomRenderer::renderToPng timeout (>%vs)", timeout)
	case <-done:
	}

	rendererLog.Debug("Image rendered", "path", pngPath)
	return pngPath, nil
}
<commit_msg>Disable web-security for phantomjs rendering (#6469)<commit_after>package renderer

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"time"

	"strconv"

	"github.com/grafana/grafana/pkg/log"
	"github.com/grafana/grafana/pkg/middleware"
	"github.com/grafana/grafana/pkg/setting"
	"github.com/grafana/grafana/pkg/util"
)

type RenderOpts struct {
	Path    string
	Width   string
	Height  string
	Timeout string
	OrgId   int64
}

var rendererLog log.Logger = log.New("png-renderer")

func RenderToPng(params *RenderOpts) (string, error) {
	rendererLog.Info("Rendering", "path", params.Path)

	var executable = "phantomjs"
	if runtime.GOOS == "windows" {
		executable = executable + ".exe"
	}

	localDomain := "localhost"
	if setting.HttpAddr != setting.DEFAULT_HTTP_ADDR {
		localDomain = setting.HttpAddr
	}

	url := fmt.Sprintf("%s://%s:%s/%s", setting.Protocol, localDomain, setting.HttpPort, params.Path)

	binPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, executable))
	scriptPath, _ := filepath.Abs(filepath.Join(setting.PhantomDir, "render.js"))
	pngPath, _ := filepath.Abs(filepath.Join(setting.ImagesDir, util.GetRandomString(20)))
	pngPath = pngPath + ".png"

	renderKey := middleware.AddRenderAuthKey(params.OrgId)
	defer middleware.RemoveRenderAuthKey(renderKey)

	cmdArgs := []string{
		"--ignore-ssl-errors=true",
		"--web-security=false",
		scriptPath,
		"url=" + url,
		"width=" + params.Width,
		"height=" + params.Height,
		"png=" + pngPath,
		"domain=" + localDomain,
		"renderKey=" + renderKey,
	}

	cmd := exec.Command(binPath, cmdArgs...)
	stdout, err := cmd.StdoutPipe()

	if err != nil {
		return "", err
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		return "", err
	}

	err = cmd.Start()
	if err != nil {
		return "", err
	}

	go io.Copy(os.Stdout, stdout)
	go io.Copy(os.Stdout, stderr)

	done := make(chan error)
	go func() {
		cmd.Wait()
		close(done)
	}()

	timeout, err := strconv.Atoi(params.Timeout)
	if err != nil {
		timeout = 15
	}

	select {
	case <-time.After(time.Duration(timeout) * time.Second):
		if err := cmd.Process.Kill(); err != nil {
			rendererLog.Error("failed to kill", "error", err)
		}
		return "", fmt.Errorf("PhantomRenderer::renderToPng timeout (>%vs)", timeout)
	case <-done:
	}

	rendererLog.Debug("Image rendered", "path", pngPath)
	return pngPath, nil
}
<|endoftext|>


<commit_before>/*
 Copyright 2010 Jeremy Wall (jeremy@marzhillstudios.com)
 Use of this source code is governed by the Artistic License 2.0.
 That License is included in the LICENSE file.
*/
package transform

import (
	"testing"
)

func assertTagType(t *testing.T, sel *Selector, typ string, msg string) {
	if sel.TagType != typ {
		t.Errorf(msg)
		t.Logf("TagType: [%s]", sel.TagType)
	}
}

func assertTagTypeAny(t *testing.T, sel *Selector) {
	assertTagType(t, sel, "*", "selector tagType not ANY")
}

func assertType(t *testing.T, sel *Selector, typ byte, msg string) {
	if sel.Type != typ {
		t.Errorf(msg)
		t.Logf("Type: [%s]", sel.Type)
	}
}

func assertKey(t *testing.T, sel *Selector, val string, msg string) {
	if sel.Key != val {
		t.Errorf(msg)
		t.Logf("Key: [%s]", sel.Val)
	}
}

func assertVal(t *testing.T, sel *Selector, val string, msg string) {
	if sel.Val != val {
		t.Errorf(msg)
		t.Logf("Val: [%s]", sel.Val)
	}
}

func TestNewAnyTagClassSelector(t *testing.T) {
	selString := ".foo"
	sel := newAnyTagClassOrIdSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestNewAnyTagSelector(t *testing.T) {
	selString := "*"
	sel := newAnyTagSelector(selString)
	assertType(t, sel, ANY,"selector type not ANY")
	assertTagTypeAny(t, sel)
}

func TestNewAnyTagAttrSelector(t *testing.T) {
	selString := "[foo=bar]"
	sel := newAnyTagAttrSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")
}

func TestTagNameSelector(t *testing.T) {
	selString := "a"
	sel := newTagNameSelector(selString)
	assertType(t, sel, TAGNAME, "selector type not TAGNAME")
	assertTagType(t, sel, "a", "selector TagType not a")
}

func TestTagNameWithAttr(t *testing.T) {
	selString := "a[foo=bar]"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")
}

func TestTagNameWithClass(t *testing.T) {
	selString := "a.foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestTagNameWithId(t *testing.T) {
	selString := "a#foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, ID, "selector type not ID")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestTagNameWithPseudo(t *testing.T) {
	selString := "a:foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, PSEUDO, "selector type not PSEUDO")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestNewSelector(t *testing.T) {
	selString := ".foo"
	sel := NewSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "*"
	sel = NewSelector(selString)
	assertType(t, sel, ANY,"selector type not ANY")
	assertTagTypeAny(t, sel)

	selString = "[foo=bar]"
	sel = NewSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")

	selString = "a"
	sel = NewSelector(selString)
	assertType(t, sel, TAGNAME, "selector type not TAGNAME")
	assertTagType(t, sel, "a", "selector TagType not a")

	selString = "a[foo=bar]"
	sel = NewSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")

	selString = "a.foo"
	sel = NewSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "a#foo"
	sel = NewSelector(selString)
	assertType(t, sel, ID, "selector type not ID")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "a:foo"
	sel = NewSelector(selString)
	assertType(t, sel, PSEUDO, "selector type not PSEUDO")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	// TODO(jwall): support combinators > + \S
}

// TODO(jwall): tests for NewSelectorQuery

func TestNewSelectorQuery(t *testing.T) {
	 NewSelectorQuery("a.foo", ".bar", "[id=foobar]")
	q := NewSelectorQuery("a.foo", ".bar", "[id=foobar]")
	sel := q.At(0).(*Selector)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	sel = q.At(1).(*Selector)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "bar", "selector val not foo")

	sel = q.At(2).(*Selector)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "id", "selector key not foo")
	assertVal(t, sel, "foobar", "selector val not bar")
}
<commit_msg>Using bitmasks for the selector types now.<commit_after>/*
 Copyright 2010 Jeremy Wall (jeremy@marzhillstudios.com)
 Use of this source code is governed by the Artistic License 2.0.
 That License is included in the LICENSE file.
*/
package transform

import (
	"testing"
)

func assertTagType(t *testing.T, sel *Selector, typ string, msg string) {
	if sel.TagType != typ {
		t.Errorf(msg)
		t.Logf("TagType: [%s]", sel.TagType)
	}
}

func assertTagTypeAny(t *testing.T, sel *Selector) {
	assertTagType(t, sel, "*", "selector tagType not ANY")
}

func assertType(t *testing.T, sel *Selector, typ byte, msg string) {
	if (sel.Type | typ) != typ {
		result := sel.Type | typ != typ
		t.Errorf(msg)
		t.Logf("%s MaskResult: [%s], Type: [%s]", result,
			sel.Type & typ, typ)
	}
}

func assertKey(t *testing.T, sel *Selector, val string, msg string) {
	if sel.Key != val {
		t.Errorf(msg)
		t.Logf("Key: [%s]", sel.Val)
	}
}

func assertVal(t *testing.T, sel *Selector, val string, msg string) {
	if sel.Val != val {
		t.Errorf(msg)
		t.Logf("Val: [%s]", sel.Val)
	}
}

func TestNewAnyTagClassSelector(t *testing.T) {
	selString := ".foo"
	sel := newAnyTagClassOrIdSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestNewAnyTagSelector(t *testing.T) {
	selString := "*"
	sel := newAnyTagSelector(selString)
	assertType(t, sel, ANY,"selector type not ANY")
	assertTagTypeAny(t, sel)
}

func TestNewAnyTagAttrSelector(t *testing.T) {
	selString := "[foo=bar]"
	sel := newAnyTagAttrSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")
}

func TestTagNameSelector(t *testing.T) {
	selString := "a"
	sel := newTagNameSelector(selString)
	assertType(t, sel, TAGNAME, "selector type not TAGNAME")
	assertTagType(t, sel, "a", "selector TagType not a")
}

func TestTagNameWithAttr(t *testing.T) {
	selString := "a[foo=bar]"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")
}

func TestTagNameWithClass(t *testing.T) {
	selString := "a.foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestTagNameWithId(t *testing.T) {
	selString := "a#foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, ID, "selector type not ID")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestTagNameWithPseudo(t *testing.T) {
	selString := "a:foo"
        sel := newTagNameWithConstraints(selString, 1)
	assertType(t, sel, PSEUDO, "selector type not PSEUDO")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")
}

func TestNewSelector(t *testing.T) {
	selString := ".foo"
	sel := NewSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "*"
	sel = NewSelector(selString)
	assertType(t, sel, ANY,"selector type not ANY")
	assertTagTypeAny(t, sel)

	selString = "[foo=bar]"
	sel = NewSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")

	selString = "a"
	sel = NewSelector(selString)
	assertType(t, sel, TAGNAME, "selector type not TAGNAME")
	assertTagType(t, sel, "a", "selector TagType not a")

	selString = "a[foo=bar]"
	sel = NewSelector(selString)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertKey(t, sel, "foo", "selector key not foo")
	assertVal(t, sel, "bar", "selector val not bar")

	selString = "a.foo"
	sel = NewSelector(selString)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "a#foo"
	sel = NewSelector(selString)
	assertType(t, sel, ID, "selector type not ID")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	selString = "a:foo"
	sel = NewSelector(selString)
	assertType(t, sel, PSEUDO, "selector type not PSEUDO")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	// TODO(jwall): support combinators > + \S
}

// TODO(jwall): tests for NewSelectorQuery

func TestNewSelectorQuery(t *testing.T) {
	 NewSelectorQuery("a.foo", ".bar", "[id=foobar]")
	q := NewSelectorQuery("a.foo", ".bar", "[id=foobar]")
	sel := q.At(0).(*Selector)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagType(t, sel, "a", "selector TagType not a")
	assertVal(t, sel, "foo", "selector val not foo")

	sel = q.At(1).(*Selector)
	assertType(t, sel, CLASS, "selector type not CLASS")
	assertTagTypeAny(t, sel)
	assertVal(t, sel, "bar", "selector val not foo")

	sel = q.At(2).(*Selector)
	assertType(t, sel, ATTR, "selector type not ATTR")
	assertTagTypeAny(t, sel)
	assertKey(t, sel, "id", "selector key not foo")
	assertVal(t, sel, "foobar", "selector val not bar")
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package cmd

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/genericclioptions/printers"
	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	api "k8s.io/kubernetes/pkg/apis/core"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
	"k8s.io/kubernetes/pkg/kubectl/scheme"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

type ClusterInfoDumpOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	OutputDir     string
	AllNamespaces bool
	Namespaces    []string

	Timeout          time.Duration
	AppsClient       appsv1client.AppsV1Interface
	CoreClient       corev1client.CoreV1Interface
	Namespace        string
	RESTClientGetter genericclioptions.RESTClientGetter
	LogsForObject    polymorphichelpers.LogsForObjectFunc

	genericclioptions.IOStreams
}

// NewCmdCreateSecret groups subcommands to create various types of secrets
func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := &ClusterInfoDumpOptions{
		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme).WithDefaultOutput("json"),

		IOStreams: ioStreams,
	}

	cmd := &cobra.Command{
		Use:     "dump",
		Short:   i18n.T("Dump lots of relevant info for debugging and diagnosis"),
		Long:    dumpLong,
		Example: dumpExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files.  If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
	cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
	cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If true, dump all namespaces.  If true, --namespaces is ignored.")
	cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
	return cmd
}

var (
	dumpLong = templates.LongDesc(i18n.T(`
    Dumps cluster info out suitable for debugging and diagnosing cluster problems.  By default, dumps everything to
    stdout. You can optionally specify a directory with --output-directory.  If you specify a directory, kubernetes will
    build a set of files in that directory.  By default only dumps things in the 'kube-system' namespace, but you can
    switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.

    The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
    based on namespace and pod name.`))

	dumpExample = templates.Examples(i18n.T(`
    # Dump current cluster state to stdout
    kubectl cluster-info dump

    # Dump current cluster state to /path/to/cluster-state
    kubectl cluster-info dump --output-directory=/path/to/cluster-state

    # Dump all namespaces to stdout
    kubectl cluster-info dump --all-namespaces

    # Dump a set of namespaces to /path/to/cluster-state
    kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
)

func setupOutputWriter(dir string, defaultWriter io.Writer, filename string) io.Writer {
	if len(dir) == 0 || dir == "-" {
		return defaultWriter
	}
	fullFile := path.Join(dir, filename)
	parent := path.Dir(fullFile)
	cmdutil.CheckErr(os.MkdirAll(parent, 0755))

	file, err := os.Create(path.Join(dir, filename))
	cmdutil.CheckErr(err)
	return file
}

func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	o.PrintObj = printer.PrintObj

	config, err := f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CoreClient, err = corev1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.AppsClient, err = appsv1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	// TODO this should eventually just be the completed kubeconfigflag struct
	o.RESTClientGetter = f
	o.LogsForObject = polymorphichelpers.LogsForObjectFn

	return nil
}

func (o *ClusterInfoDumpOptions) Run() error {
	nodes, err := o.CoreClient.Nodes().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes.json")); err != nil {
		return err
	}

	var namespaces []string
	if o.AllNamespaces {
		namespaceList, err := o.CoreClient.Namespaces().List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		for ix := range namespaceList.Items {
			namespaces = append(namespaces, namespaceList.Items[ix].Name)
		}
	} else {
		if len(o.Namespaces) == 0 {
			namespaces = []string{
				metav1.NamespaceSystem,
				o.Namespace,
			}
		}
	}
	for _, namespace := range namespaces {
		// TODO: this is repetitive in the extreme.  Use reflection or
		// something to make this a for loop.
		events, err := o.CoreClient.Events(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events.json"))); err != nil {
			return err
		}

		rcs, err := o.CoreClient.ReplicationControllers(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
			return err
		}

		svcs, err := o.CoreClient.Services(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services.json"))); err != nil {
			return err
		}

		sets, err := o.AppsClient.DaemonSets(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
			return err
		}

		deps, err := o.AppsClient.Deployments(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
			return err
		}

		rps, err := o.AppsClient.ReplicaSets(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
			return err
		}

		pods, err := o.CoreClient.Pods(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}

		if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods.json"))); err != nil {
			return err
		}

		printContainer := func(writer io.Writer, container corev1.Container, pod *corev1.Pod) {
			writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
			defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))

			requests, err := o.LogsForObject(o.RESTClientGetter, pod, &api.PodLogOptions{Container: container.Name}, timeout, false)
			if err != nil {
				// Print error and return.
				writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
				return
			}

			for _, request := range requests {
				data, err := request.DoRaw()
				if err != nil {
					// Print error and return.
					writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
					return
				}
				writer.Write(data)
			}
		}

		for ix := range pods.Items {
			pod := &pods.Items[ix]
			containers := pod.Spec.Containers
			writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs.txt"))

			for i := range containers {
				printContainer(writer, containers[i], pod)
			}
		}
	}

	dest := o.OutputDir
	if len(dest) == 0 {
		dest = "standard output"
	}
	if dest != "-" {
		fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dest)
	}
	return nil
}
<commit_msg>Small fix to remove dependency on internal version of resource<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 cmd

import (
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/genericclioptions/printers"
	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/polymorphichelpers"
	"k8s.io/kubernetes/pkg/kubectl/scheme"
	"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)

type ClusterInfoDumpOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	OutputDir     string
	AllNamespaces bool
	Namespaces    []string

	Timeout          time.Duration
	AppsClient       appsv1client.AppsV1Interface
	CoreClient       corev1client.CoreV1Interface
	Namespace        string
	RESTClientGetter genericclioptions.RESTClientGetter
	LogsForObject    polymorphichelpers.LogsForObjectFunc

	genericclioptions.IOStreams
}

// NewCmdCreateSecret groups subcommands to create various types of secrets
func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := &ClusterInfoDumpOptions{
		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme).WithDefaultOutput("json"),

		IOStreams: ioStreams,
	}

	cmd := &cobra.Command{
		Use:     "dump",
		Short:   i18n.T("Dump lots of relevant info for debugging and diagnosis"),
		Long:    dumpLong,
		Example: dumpExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files.  If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
	cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
	cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If true, dump all namespaces.  If true, --namespaces is ignored.")
	cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
	return cmd
}

var (
	dumpLong = templates.LongDesc(i18n.T(`
    Dumps cluster info out suitable for debugging and diagnosing cluster problems.  By default, dumps everything to
    stdout. You can optionally specify a directory with --output-directory.  If you specify a directory, kubernetes will
    build a set of files in that directory.  By default only dumps things in the 'kube-system' namespace, but you can
    switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.

    The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
    based on namespace and pod name.`))

	dumpExample = templates.Examples(i18n.T(`
    # Dump current cluster state to stdout
    kubectl cluster-info dump

    # Dump current cluster state to /path/to/cluster-state
    kubectl cluster-info dump --output-directory=/path/to/cluster-state

    # Dump all namespaces to stdout
    kubectl cluster-info dump --all-namespaces

    # Dump a set of namespaces to /path/to/cluster-state
    kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
)

func setupOutputWriter(dir string, defaultWriter io.Writer, filename string) io.Writer {
	if len(dir) == 0 || dir == "-" {
		return defaultWriter
	}
	fullFile := path.Join(dir, filename)
	parent := path.Dir(fullFile)
	cmdutil.CheckErr(os.MkdirAll(parent, 0755))

	file, err := os.Create(path.Join(dir, filename))
	cmdutil.CheckErr(err)
	return file
}

func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	o.PrintObj = printer.PrintObj

	config, err := f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CoreClient, err = corev1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.AppsClient, err = appsv1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	// TODO this should eventually just be the completed kubeconfigflag struct
	o.RESTClientGetter = f
	o.LogsForObject = polymorphichelpers.LogsForObjectFn

	return nil
}

func (o *ClusterInfoDumpOptions) Run() error {
	nodes, err := o.CoreClient.Nodes().List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes.json")); err != nil {
		return err
	}

	var namespaces []string
	if o.AllNamespaces {
		namespaceList, err := o.CoreClient.Namespaces().List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		for ix := range namespaceList.Items {
			namespaces = append(namespaces, namespaceList.Items[ix].Name)
		}
	} else {
		if len(o.Namespaces) == 0 {
			namespaces = []string{
				metav1.NamespaceSystem,
				o.Namespace,
			}
		}
	}
	for _, namespace := range namespaces {
		// TODO: this is repetitive in the extreme.  Use reflection or
		// something to make this a for loop.
		events, err := o.CoreClient.Events(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events.json"))); err != nil {
			return err
		}

		rcs, err := o.CoreClient.ReplicationControllers(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
			return err
		}

		svcs, err := o.CoreClient.Services(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services.json"))); err != nil {
			return err
		}

		sets, err := o.AppsClient.DaemonSets(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
			return err
		}

		deps, err := o.AppsClient.Deployments(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
			return err
		}

		rps, err := o.AppsClient.ReplicaSets(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
			return err
		}

		pods, err := o.CoreClient.Pods(namespace).List(metav1.ListOptions{})
		if err != nil {
			return err
		}

		if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods.json"))); err != nil {
			return err
		}

		printContainer := func(writer io.Writer, container corev1.Container, pod *corev1.Pod) {
			writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
			defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))

			requests, err := o.LogsForObject(o.RESTClientGetter, pod, &corev1.PodLogOptions{Container: container.Name}, timeout, false)
			if err != nil {
				// Print error and return.
				writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
				return
			}

			for _, request := range requests {
				data, err := request.DoRaw()
				if err != nil {
					// Print error and return.
					writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
					return
				}
				writer.Write(data)
			}
		}

		for ix := range pods.Items {
			pod := &pods.Items[ix]
			containers := pod.Spec.Containers
			writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs.txt"))

			for i := range containers {
				printContainer(writer, containers[i], pod)
			}
		}
	}

	dest := o.OutputDir
	if len(dest) == 0 {
		dest = "standard output"
	}
	if dest != "-" {
		fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dest)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package kubelet

import (
	"testing"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	testingclock "k8s.io/utils/clock/testing"
)

// mockPodStatusProvider returns the status on the specified pod
type mockPodStatusProvider struct {
	pods []*v1.Pod
}

// GetPodStatus returns the status on the associated pod with matching uid (if found)
func (m *mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
	for _, pod := range m.pods {
		if pod.UID == uid {
			return pod.Status, true
		}
	}
	return v1.PodStatus{}, false
}

// TestActiveDeadlineHandler verifies the active deadline handler functions as expected.
func TestActiveDeadlineHandler(t *testing.T) {
	pods := newTestPods(4)
	fakeClock := testingclock.NewFakeClock(time.Now())
	podStatusProvider := &mockPodStatusProvider{pods: pods}
	fakeRecorder := &record.FakeRecorder{}
	handler, err := newActiveDeadlineHandler(podStatusProvider, fakeRecorder, fakeClock)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	now := metav1.Now()
	startTime := metav1.NewTime(now.Time.Add(-1 * time.Minute))

	// this pod has exceeded its active deadline
	exceededActiveDeadlineSeconds := int64(30)
	pods[0].Status.StartTime = &startTime
	pods[0].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds

	// this pod has not exceeded its active deadline
	notYetActiveDeadlineSeconds := int64(120)
	pods[1].Status.StartTime = &startTime
	pods[1].Spec.ActiveDeadlineSeconds = &notYetActiveDeadlineSeconds

	// this pod has no deadline
	pods[2].Status.StartTime = &startTime
	pods[2].Spec.ActiveDeadlineSeconds = nil

	testCases := []struct {
		pod      *v1.Pod
		expected bool
	}{{pods[0], true}, {pods[1], false}, {pods[2], false}, {pods[3], false}}

	for i, testCase := range testCases {
		if actual := handler.ShouldSync(testCase.pod); actual != testCase.expected {
			t.Errorf("[%d] ShouldSync expected %#v, got %#v", i, testCase.expected, actual)
		}
		actual := handler.ShouldEvict(testCase.pod)
		if actual.Evict != testCase.expected {
			t.Errorf("[%d] ShouldEvict.Evict expected %#v, got %#v", i, testCase.expected, actual.Evict)
		}
		if testCase.expected {
			if actual.Reason != reason {
				t.Errorf("[%d] ShouldEvict.Reason expected %#v, got %#v", i, message, actual.Reason)
			}
			if actual.Message != message {
				t.Errorf("[%d] ShouldEvict.Message expected %#v, got %#v", i, message, actual.Message)
			}
		}
	}
}
<commit_msg>Add unit tests for active_deadline.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 kubelet

import (
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	"k8s.io/kubernetes/pkg/kubelet/status"
	"k8s.io/utils/clock"
	testingclock "k8s.io/utils/clock/testing"
)

// mockPodStatusProvider returns the status on the specified pod
type mockPodStatusProvider struct {
	pods []*v1.Pod
}

// GetPodStatus returns the status on the associated pod with matching uid (if found)
func (m *mockPodStatusProvider) GetPodStatus(uid types.UID) (v1.PodStatus, bool) {
	for _, pod := range m.pods {
		if pod.UID == uid {
			return pod.Status, true
		}
	}
	return v1.PodStatus{}, false
}

func TestNewActiveDeadlineHandler(t *testing.T) {
	pods := newTestPods(1)
	podStatusProvider := &mockPodStatusProvider{pods: pods}
	fakeRecorder := &record.FakeRecorder{}
	fakeClock := testingclock.NewFakeClock(time.Now())

	testCases := []struct {
		podStatusProvider status.PodStatusProvider
		recorder          record.EventRecorder
		clock             clock.Clock
	}{
		{podStatusProvider, fakeRecorder, fakeClock},
		{podStatusProvider, fakeRecorder, nil},
		{podStatusProvider, nil, fakeClock},
		{podStatusProvider, nil, nil},
		{nil, fakeRecorder, fakeClock},
		{nil, fakeRecorder, nil},
		{nil, nil, fakeClock},
		{nil, nil, nil},
	}

	for i, testCase := range testCases {
		actual, err := newActiveDeadlineHandler(testCase.podStatusProvider, testCase.recorder, testCase.clock)

		if i == 0 {
			expected := &activeDeadlineHandler{
				clock:             fakeClock,
				podStatusProvider: podStatusProvider,
				recorder:          fakeRecorder,
			}
			if !reflect.DeepEqual(actual, expected) {
				t.Errorf("[%d] newActiveDeadlineHandler expected %#v, got %#v", i, expected, actual)
			}
			assert.NoError(t, err)

			continue
		}

		assert.Error(t, err)
	}
}

// TestActiveDeadlineHandler verifies the active deadline handler functions as expected.
func TestActiveDeadlineHandler(t *testing.T) {
	pods := newTestPods(5)
	fakeClock := testingclock.NewFakeClock(time.Now())
	podStatusProvider := &mockPodStatusProvider{pods: pods}
	fakeRecorder := &record.FakeRecorder{}
	handler, err := newActiveDeadlineHandler(podStatusProvider, fakeRecorder, fakeClock)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	now := metav1.Now()
	startTime := metav1.NewTime(now.Time.Add(-1 * time.Minute))

	// this pod has exceeded its active deadline
	exceededActiveDeadlineSeconds := int64(30)
	pods[0].Status.StartTime = &startTime
	pods[0].Spec.ActiveDeadlineSeconds = &exceededActiveDeadlineSeconds

	// this pod has not exceeded its active deadline
	notYetActiveDeadlineSeconds := int64(120)
	pods[1].Status.StartTime = &startTime
	pods[1].Spec.ActiveDeadlineSeconds = &notYetActiveDeadlineSeconds

	// this pod has no deadline
	pods[2].Status.StartTime = &startTime
	pods[2].Spec.ActiveDeadlineSeconds = nil

	// this pod has no start time
	pods[3].Status.StartTime = nil
	pods[3].Spec.ActiveDeadlineSeconds = &notYetActiveDeadlineSeconds

	testCases := []struct {
		pod      *v1.Pod
		expected bool
	}{{pods[0], true}, {pods[1], false}, {pods[2], false}, {pods[3], false}, {pods[4], false}}

	for i, testCase := range testCases {
		if actual := handler.ShouldSync(testCase.pod); actual != testCase.expected {
			t.Errorf("[%d] ShouldSync expected %#v, got %#v", i, testCase.expected, actual)
		}
		actual := handler.ShouldEvict(testCase.pod)
		if actual.Evict != testCase.expected {
			t.Errorf("[%d] ShouldEvict.Evict expected %#v, got %#v", i, testCase.expected, actual.Evict)
		}
		if testCase.expected {
			if actual.Reason != reason {
				t.Errorf("[%d] ShouldEvict.Reason expected %#v, got %#v", i, message, actual.Reason)
			}
			if actual.Message != message {
				t.Errorf("[%d] ShouldEvict.Message expected %#v, got %#v", i, message, actual.Message)
			}
		}
	}
}
<|endoftext|>


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

import (
	"fmt"

	"k8s.io/klog"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
	"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
)

type OpenstackModelContext struct {
	*model.KopsModelContext
}

func (c *OpenstackModelContext) GetNetworkName() (string, error) {
	if c.Cluster.Spec.NetworkID == "" {
		return c.ClusterName(), nil
	}

	tags := make(map[string]string)
	tags[openstack.TagClusterName] = c.ClusterName()
	osCloud, err := openstack.NewOpenstackCloud(tags, &c.Cluster.Spec)
	if err != nil {
		return "", fmt.Errorf("error loading cloud: %v", err)
	}

	network, err := osCloud.GetNetwork(c.Cluster.Spec.NetworkID)
	if err != nil {
		return "", err
	}
	return network.Name, nil
}

func (c *OpenstackModelContext) findSubnetClusterSpec(subnet string) (string, error) {
	for _, sp := range c.Cluster.Spec.Subnets {
		if sp.Name == subnet {
			name, err := c.findSubnetNameByID(sp.ProviderID, sp.Name)
			if err != nil {
				return "", err
			}
			return name, nil
		}
	}
	return "", fmt.Errorf("Could not find subnet %s from clusterSpec", subnet)
}

func (c *OpenstackModelContext) findSubnetNameByID(subnetID string, subnetName string) (string, error) {
	if subnetID == "" {
		return subnetName + "." + c.ClusterName(), nil
	}

	tags := make(map[string]string)
	tags[openstack.TagClusterName] = c.ClusterName()
	osCloud, err := openstack.NewOpenstackCloud(tags, &c.Cluster.Spec)
	if err != nil {
		return "", fmt.Errorf("error loading cloud: %v", err)
	}

	subnet, err := osCloud.GetSubnet(subnetID)
	if err != nil {
		return "", err
	}
	return subnet.Name, nil
}

func (c *OpenstackModelContext) LinkToNetwork() *openstacktasks.Network {
	netName, err := c.GetNetworkName()
	if err != nil {
		klog.Infof("Could not find networkname")
		return nil
	}
	return &openstacktasks.Network{Name: s(netName)}
}

func (c *OpenstackModelContext) LinkToRouter(name *string) *openstacktasks.Router {
	return &openstacktasks.Router{Name: name}
}

func (c *OpenstackModelContext) LinkToSubnet(name *string) *openstacktasks.Subnet {
	return &openstacktasks.Subnet{Name: name}
}

func (c *OpenstackModelContext) LinkToPort(name *string) *openstacktasks.Port {
	return &openstacktasks.Port{Name: name}
}

func (c *OpenstackModelContext) LinkToSecurityGroup(name string) *openstacktasks.SecurityGroup {
	return &openstacktasks.SecurityGroup{Name: fi.String(name)}
}
<commit_msg>return fatal if no network found<commit_after>/*
Copyright 2019 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 openstackmodel

import (
	"fmt"

	"k8s.io/klog"
	"k8s.io/kops/pkg/model"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/openstack"
	"k8s.io/kops/upup/pkg/fi/cloudup/openstacktasks"
)

type OpenstackModelContext struct {
	*model.KopsModelContext
}

func (c *OpenstackModelContext) GetNetworkName() (string, error) {
	if c.Cluster.Spec.NetworkID == "" {
		return c.ClusterName(), nil
	}

	tags := make(map[string]string)
	tags[openstack.TagClusterName] = c.ClusterName()
	osCloud, err := openstack.NewOpenstackCloud(tags, &c.Cluster.Spec)
	if err != nil {
		return "", fmt.Errorf("error loading cloud: %v", err)
	}

	network, err := osCloud.GetNetwork(c.Cluster.Spec.NetworkID)
	if err != nil {
		return "", err
	}
	return network.Name, nil
}

func (c *OpenstackModelContext) findSubnetClusterSpec(subnet string) (string, error) {
	for _, sp := range c.Cluster.Spec.Subnets {
		if sp.Name == subnet {
			name, err := c.findSubnetNameByID(sp.ProviderID, sp.Name)
			if err != nil {
				return "", err
			}
			return name, nil
		}
	}
	return "", fmt.Errorf("Could not find subnet %s from clusterSpec", subnet)
}

func (c *OpenstackModelContext) findSubnetNameByID(subnetID string, subnetName string) (string, error) {
	if subnetID == "" {
		return subnetName + "." + c.ClusterName(), nil
	}

	tags := make(map[string]string)
	tags[openstack.TagClusterName] = c.ClusterName()
	osCloud, err := openstack.NewOpenstackCloud(tags, &c.Cluster.Spec)
	if err != nil {
		return "", fmt.Errorf("error loading cloud: %v", err)
	}

	subnet, err := osCloud.GetSubnet(subnetID)
	if err != nil {
		return "", err
	}
	return subnet.Name, nil
}

func (c *OpenstackModelContext) LinkToNetwork() *openstacktasks.Network {
	netName, err := c.GetNetworkName()
	if err != nil {
		klog.Fatalf("Could not find networkname")
		return nil
	}
	return &openstacktasks.Network{Name: s(netName)}
}

func (c *OpenstackModelContext) LinkToRouter(name *string) *openstacktasks.Router {
	return &openstacktasks.Router{Name: name}
}

func (c *OpenstackModelContext) LinkToSubnet(name *string) *openstacktasks.Subnet {
	return &openstacktasks.Subnet{Name: name}
}

func (c *OpenstackModelContext) LinkToPort(name *string) *openstacktasks.Port {
	return &openstacktasks.Port{Name: name}
}

func (c *OpenstackModelContext) LinkToSecurityGroup(name string) *openstacktasks.SecurityGroup {
	return &openstacktasks.SecurityGroup{Name: fi.String(name)}
}
<|endoftext|>


<commit_before>package backendplugin

import (
	"context"
	"os/exec"

	"github.com/grafana/grafana-plugin-sdk-go/backend/plugin"

	"github.com/grafana/grafana/pkg/infra/log"

	datasourceV1 "github.com/grafana/grafana-plugin-model/go/datasource"
	rendererV1 "github.com/grafana/grafana-plugin-model/go/renderer"
	"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
	goplugin "github.com/hashicorp/go-plugin"
)

const (
	// DefaultProtocolVersion is the protocol version assumed for legacy clients that don't specify
	// a particular version or version 1 during their handshake. This is currently the version used
	// since Grafana launched support for backend plugins.
	DefaultProtocolVersion = 1
)

// Handshake is the HandshakeConfig used to configure clients and servers.
var handshake = goplugin.HandshakeConfig{
	// The ProtocolVersion is the version that must match between Grafana core
	// and Grafana plugins. This should be bumped whenever a (breaking) change
	// happens in one or the other that makes it so that they can't safely communicate.
	ProtocolVersion: DefaultProtocolVersion,

	// The magic cookie values should NEVER be changed.
	MagicCookieKey:   plugin.MagicCookieKey,
	MagicCookieValue: plugin.MagicCookieValue,
}

func newClientConfig(executablePath string, logger log.Logger, versionedPlugins map[int]goplugin.PluginSet) *goplugin.ClientConfig {
	return &goplugin.ClientConfig{
		Cmd:              exec.Command(executablePath),
		HandshakeConfig:  handshake,
		VersionedPlugins: versionedPlugins,
		Logger:           logWrapper{Logger: logger},
		AllowedProtocols: []goplugin.Protocol{goplugin.ProtocolGRPC},
	}
}

// LegacyStartFunc callback function called when a plugin with old plugin protocol is started.
type LegacyStartFunc func(pluginID string, client *LegacyClient, logger log.Logger) error

// StartFunc callback function called when a plugin with current plugin protocol version is started.
type StartFunc func(pluginID string, client *Client, logger log.Logger) error

// PluginStartFuncs functions called for plugin when started.
type PluginStartFuncs struct {
	OnLegacyStart LegacyStartFunc
	OnStart       StartFunc
}

// PluginDescriptor descriptor used for registering backend plugins.
type PluginDescriptor struct {
	pluginID         string
	executablePath   string
	managed          bool
	versionedPlugins map[int]goplugin.PluginSet
	startFns         PluginStartFuncs
}

// NewBackendPluginDescriptor creates a new backend plugin descriptor
// used for registering a backend datasource plugin.
func NewBackendPluginDescriptor(pluginID, executablePath string, startFns PluginStartFuncs) PluginDescriptor {
	return PluginDescriptor{
		pluginID:       pluginID,
		executablePath: executablePath,
		managed:        true,
		versionedPlugins: map[int]goplugin.PluginSet{
			DefaultProtocolVersion: {
				pluginID: &datasourceV1.DatasourcePluginImpl{},
			},
			plugin.ProtocolVersion: {
				"diagnostics": &plugin.DiagnosticsGRPCPlugin{},
				"backend":     &plugin.CoreGRPCPlugin{},
				"transform":   &plugin.TransformGRPCPlugin{},
			},
		},
		startFns: startFns,
	}
}

// NewRendererPluginDescriptor creates a new renderer plugin descriptor
// used for registering a backend renderer plugin.
func NewRendererPluginDescriptor(pluginID, executablePath string, startFns PluginStartFuncs) PluginDescriptor {
	return PluginDescriptor{
		pluginID:       pluginID,
		executablePath: executablePath,
		managed:        false,
		versionedPlugins: map[int]goplugin.PluginSet{
			DefaultProtocolVersion: {
				pluginID: &rendererV1.RendererPluginImpl{},
			},
		},
		startFns: startFns,
	}
}

type DiagnosticsPlugin interface {
	CollectMetrics(ctx context.Context, req *pluginv2.CollectMetrics_Request) (*pluginv2.CollectMetrics_Response, error)
	CheckHealth(ctx context.Context, req *pluginv2.CheckHealth_Request) (*pluginv2.CheckHealth_Response, error)
}

type DatasourcePlugin interface {
	DataQuery(ctx context.Context, req *pluginv2.DataQueryRequest) (*pluginv2.DataQueryResponse, error)
}

type CorePlugin interface {
	CallResource(ctx context.Context, req *pluginv2.CallResource_Request) (*pluginv2.CallResource_Response, error)
	DatasourcePlugin
}

type TransformCallBack interface {
	DataQuery(ctx context.Context, req *pluginv2.DataQueryRequest) (*pluginv2.DataQueryResponse, error)
}

type TransformPlugin interface {
	DataQuery(ctx context.Context, req *pluginv2.DataQueryRequest, callback TransformCallBack) (*pluginv2.DataQueryResponse, error)
}

// LegacyClient client for communicating with a plugin using the old plugin protocol.
type LegacyClient struct {
	DatasourcePlugin datasourceV1.DatasourcePlugin
	RendererPlugin   rendererV1.RendererPlugin
}

// Client client for communicating with a plugin using the current plugin protocol.
type Client struct {
	DatasourcePlugin DatasourcePlugin
	TransformPlugin  TransformPlugin
}
<commit_msg>Backend Plugins: make transform work again (#22078)<commit_after>package backendplugin

import (
	"context"
	"os/exec"

	"github.com/grafana/grafana-plugin-sdk-go/backend/plugin"

	"github.com/grafana/grafana/pkg/infra/log"

	datasourceV1 "github.com/grafana/grafana-plugin-model/go/datasource"
	rendererV1 "github.com/grafana/grafana-plugin-model/go/renderer"
	"github.com/grafana/grafana-plugin-sdk-go/genproto/pluginv2"
	goplugin "github.com/hashicorp/go-plugin"
)

const (
	// DefaultProtocolVersion is the protocol version assumed for legacy clients that don't specify
	// a particular version or version 1 during their handshake. This is currently the version used
	// since Grafana launched support for backend plugins.
	DefaultProtocolVersion = 1
)

// Handshake is the HandshakeConfig used to configure clients and servers.
var handshake = goplugin.HandshakeConfig{
	// The ProtocolVersion is the version that must match between Grafana core
	// and Grafana plugins. This should be bumped whenever a (breaking) change
	// happens in one or the other that makes it so that they can't safely communicate.
	ProtocolVersion: DefaultProtocolVersion,

	// The magic cookie values should NEVER be changed.
	MagicCookieKey:   plugin.MagicCookieKey,
	MagicCookieValue: plugin.MagicCookieValue,
}

func newClientConfig(executablePath string, logger log.Logger, versionedPlugins map[int]goplugin.PluginSet) *goplugin.ClientConfig {
	return &goplugin.ClientConfig{
		Cmd:              exec.Command(executablePath),
		HandshakeConfig:  handshake,
		VersionedPlugins: versionedPlugins,
		Logger:           logWrapper{Logger: logger},
		AllowedProtocols: []goplugin.Protocol{goplugin.ProtocolGRPC},
	}
}

// LegacyStartFunc callback function called when a plugin with old plugin protocol is started.
type LegacyStartFunc func(pluginID string, client *LegacyClient, logger log.Logger) error

// StartFunc callback function called when a plugin with current plugin protocol version is started.
type StartFunc func(pluginID string, client *Client, logger log.Logger) error

// PluginStartFuncs functions called for plugin when started.
type PluginStartFuncs struct {
	OnLegacyStart LegacyStartFunc
	OnStart       StartFunc
}

// PluginDescriptor descriptor used for registering backend plugins.
type PluginDescriptor struct {
	pluginID         string
	executablePath   string
	managed          bool
	versionedPlugins map[int]goplugin.PluginSet
	startFns         PluginStartFuncs
}

// NewBackendPluginDescriptor creates a new backend plugin descriptor
// used for registering a backend datasource plugin.
func NewBackendPluginDescriptor(pluginID, executablePath string, startFns PluginStartFuncs) PluginDescriptor {
	return PluginDescriptor{
		pluginID:       pluginID,
		executablePath: executablePath,
		managed:        true,
		versionedPlugins: map[int]goplugin.PluginSet{
			DefaultProtocolVersion: {
				pluginID: &datasourceV1.DatasourcePluginImpl{},
			},
			plugin.ProtocolVersion: {
				"diagnostics": &plugin.DiagnosticsGRPCPlugin{},
				"backend":     &plugin.CoreGRPCPlugin{},
				"transform":   &plugin.TransformGRPCPlugin{},
			},
		},
		startFns: startFns,
	}
}

// NewRendererPluginDescriptor creates a new renderer plugin descriptor
// used for registering a backend renderer plugin.
func NewRendererPluginDescriptor(pluginID, executablePath string, startFns PluginStartFuncs) PluginDescriptor {
	return PluginDescriptor{
		pluginID:       pluginID,
		executablePath: executablePath,
		managed:        false,
		versionedPlugins: map[int]goplugin.PluginSet{
			DefaultProtocolVersion: {
				pluginID: &rendererV1.RendererPluginImpl{},
			},
		},
		startFns: startFns,
	}
}

type DiagnosticsPlugin interface {
	CollectMetrics(ctx context.Context, req *pluginv2.CollectMetrics_Request) (*pluginv2.CollectMetrics_Response, error)
	CheckHealth(ctx context.Context, req *pluginv2.CheckHealth_Request) (*pluginv2.CheckHealth_Response, error)
}

type DatasourcePlugin interface {
	DataQuery(ctx context.Context, req *pluginv2.DataQueryRequest) (*pluginv2.DataQueryResponse, error)
}

type CorePlugin interface {
	CallResource(ctx context.Context, req *pluginv2.CallResource_Request) (*pluginv2.CallResource_Response, error)
	DatasourcePlugin
}

type TransformPlugin interface {
	DataQuery(ctx context.Context, req *pluginv2.DataQueryRequest, callback plugin.TransformCallBack) (*pluginv2.DataQueryResponse, error)
}

// LegacyClient client for communicating with a plugin using the old plugin protocol.
type LegacyClient struct {
	DatasourcePlugin datasourceV1.DatasourcePlugin
	RendererPlugin   rendererV1.RendererPlugin
}

// Client client for communicating with a plugin using the current plugin protocol.
type Client struct {
	DatasourcePlugin DatasourcePlugin
	TransformPlugin  TransformPlugin
}
<|endoftext|>


<commit_before>package main

import "fmt"

func add(args ...int) int {
    total := 0
    for _, v := range args {
        total += v
    }
    return total
}

func main() {
    fmt.Println(add(1,2,3))
}

<commit_msg>Println takes any number of values of any type<commit_after>package main

import "fmt"

func add(args ...int) int {
    total := 0
    for _, v := range args {
        total += v
    }
    return total
}

func main() {
    fmt.Println(add(1,2,3))

    xs := [] int{1, 2, 3}

    fmt.Println("\nsum of xs = ")
    fmt.Println(add(xs...))
}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"time"
)

func main() {
	keyword, count := args()
	urls := fetchUrls(keyword, count)
	html := generateHtml(urls)
	openHtml(html)
}

func args() (keyword string, count int) {
	flag.StringVar(&keyword, "k", "yuyushiki", "keyword")
	flag.IntVar(&count, "c", 8, "count")
	flag.Parse()

	return keyword, count
}

func fetchUrls(keyword string, count int) (urls []string) {
	page := 1
	var _urls []string
	for len(urls) <= count {
		_urls = search(page, keyword)
		for _, url := range _urls {
			urls = append(urls, url)
		}
		page += 1
	}

	return urls
}

func generateHtml(urls []string) (html string) {
	html = "<!DOCTYPE HTML>"
	for _, url := range urls {
		html = html + ""
	}
	html = html + ""
	return html
}

func openHtml(html string) {
	file, err := ioutil.TempFile(os.TempDir(), "animegif")
	perror(err)
	ioutil.WriteFile(file.Name(), []byte(html), 0644)
	exec.Command("open", file.Name()).Output()
	perror(err)
	time.Sleep(time.Second * 1)

	defer os.Remove(file.Name())
}

func perror(err error) {
	if err != nil {
		panic(err)
	}
}

type ResultType struct {
	Url string `json:"url"`
}

type ResponseDataType struct {
	Results []ResultType `json:"results"`
}

type ResponseType struct {
	ResponseData ResponseDataType `json:"responseData"`
}

func search(page int, keyword string) (urls []string) {
	perPage := 8
	base := "http://ajax.googleapis.com/ajax/services/search/images?"
	start := (page-1)*perPage + 1

	params := url.Values{}
	params.Add("q", keyword)
	params.Add("rsz", fmt.Sprint(perPage))
	params.Add("safe", "off")
	params.Add("v", "1.0")
	params.Add("as_filetype", "gif")
	params.Add("imgsz", "large")
	params.Add("start", fmt.Sprint(start))
	params.Add("as_sitesearch", "tumblr.com")

	body := openUrl(base + params.Encode())

	var response ResponseType
	err := json.Unmarshal(body, &response)
	perror(err)
	for _, value := range response.ResponseData.Results {
		urls = append(urls, value.Url)
	}
	return urls
}

func openUrl(req string) (body []byte) {
	res, err := http.Get(req)
	perror(err)
	defer res.Body.Close()

	body, err = ioutil.ReadAll(res.Body)
	perror(err)
	return body
}
<commit_msg>Remove unnecessary code<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"time"
)

func main() {
	keyword, count := args()
	urls := fetchUrls(keyword, count)
	html := generateHtml(urls)
	openHtml(html)
}

func args() (keyword string, count int) {
	flag.StringVar(&keyword, "k", "yuyushiki", "keyword")
	flag.IntVar(&count, "c", 8, "count")
	flag.Parse()

	return keyword, count
}

func fetchUrls(keyword string, count int) (urls []string) {
	page := 1
	var _urls []string
	for len(urls) <= count {
		_urls = search(page, keyword)
		for _, url := range _urls {
			urls = append(urls, url)
		}
		page += 1
	}

	return urls
}

func generateHtml(urls []string) (html string) {
	html = "<!DOCTYPE HTML>"
	for _, url := range urls {
		html = html + ""
	}
	html = html + ""
	return html
}

func openHtml(html string) {
	file, err := ioutil.TempFile(os.TempDir(), "animegif")
	perror(err)
	ioutil.WriteFile(file.Name(), []byte(html), 0644)
	exec.Command("open", file.Name()).Start()
	time.Sleep(time.Second * 1)

	defer os.Remove(file.Name())
}

func perror(err error) {
	if err != nil {
		panic(err)
	}
}

type ResultType struct {
	Url string `json:"url"`
}

type ResponseDataType struct {
	Results []ResultType `json:"results"`
}

type ResponseType struct {
	ResponseData ResponseDataType `json:"responseData"`
}

func search(page int, keyword string) (urls []string) {
	perPage := 8
	base := "http://ajax.googleapis.com/ajax/services/search/images?"
	start := (page-1)*perPage + 1

	params := url.Values{}
	params.Add("q", keyword)
	params.Add("rsz", fmt.Sprint(perPage))
	params.Add("safe", "off")
	params.Add("v", "1.0")
	params.Add("as_filetype", "gif")
	params.Add("imgsz", "large")
	params.Add("start", fmt.Sprint(start))
	params.Add("as_sitesearch", "tumblr.com")

	body := openUrl(base + params.Encode())

	var response ResponseType
	err := json.Unmarshal(body, &response)
	perror(err)
	for _, value := range response.ResponseData.Results {
		urls = append(urls, value.Url)
	}
	return urls
}

func openUrl(req string) (body []byte) {
	res, err := http.Get(req)
	perror(err)
	defer res.Body.Close()

	body, err = ioutil.ReadAll(res.Body)
	perror(err)
	return body
}
<|endoftext|>


<commit_before>// Copyright 2017 The LUCI 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 cloud

import (
	"fmt"
	"net/http"
	"os"
	"strconv"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/runtime/paniccatcher"

	cloudLogging "cloud.google.com/go/logging"

	"golang.org/x/net/context"
)

// HandlerFunc is a callback to handle the HTTP request.
type HandlerFunc func(context.Context, http.ResponseWriter)

// ScopedRequestHandler offers a middleware functionality that adds
// AppEngine-like handling to individual requests.
//
// ScopedRequestHandler is not required to use the cloud implementation. If
// used, a ScopedRequestHandler's Handle method should be called during
// processing of individual HTTP requests to add pre- and post-request
// processing to these requests.
//
// AppEngine has the behavior of grouping logs emitted during the processing of
// an individual request underneath of a top-level log for that request,
// allowing a user to visually isolate messages associated with a request.
// AppEngine Flex does not offer the same functionality by default, requiring
// users to manually emit the appropriate log messages to achieve a similar
// per-request grouping effect.
//
// The construction used by ScopedRequestHandler works as follows: a "debug log"
// Logger is installed into the handler Context by Use, causing all logs
// generated by the Handler to be associated with the request's Trace ID. After
// the request is completed, ScopedRequestHandler emits a single "request log"
// representing the status of the request's handling and referencing the same
// Trace ID as the debug logs. A user viewing the request log stream would see
// one log entry per request; however, when that log entry is expanded, the
// viewer populates it with the debug logs that share its Trace ID, creating the
// effect of per-request logs.
type ScopedRequestHandler struct {
	// CapturePanics, if true, instructs the ScopedRequestHandler to capture and
	// handle any panics that occur during its request processing. IF false,
	// panics will not be caught or logged by ScopedRequestHandler.
	//
	// If a panic is caught, it will be logged to the installed logger. Handle
	// will return an error indicating that a panic was observed.
	CapturePanics bool
}

// Handle is a middleware function that wraps an HTTP request. It sets up a
// preprocessing environment, invokes the supplied Handler, and operates on the
// output after the Handler has finished.
//
// If a panic occurs during Handle, and CapturePanics is true, the panic will
// be logged and consumed, and Handle will return an error. The panic's metadata
// will be available in the error by calling ScopedRequestPanic.
//
// Handle requires a Flex GAE environment to be installed in the supplied
// Context (see Use).
func (sr *ScopedRequestHandler) Handle(c context.Context, rw http.ResponseWriter, fn HandlerFunc) (err error) {
	rs := currentRequestState(c)
	if rs == nil {
		panic("no request state is installed in the Context")
	}

	// Wrap the ResponseWriter so we can track as much of the response properties
	// as we can.
	crw := capturingResponseWriter{Inner: rw}

	// Finalize on deferred so we can handle panic scenarios.
	defer func() {
		if derr := sr.finalize(c, rs, &crw); derr != nil {
			logging.WithError(derr).Warningf(c, "Failed to finalize scoped request.")
			if err != nil {
				err = derr
			}
		}
	}()

	// If we are handling panics, handle them before we finalize.
	if sr.CapturePanics {
		defer paniccatcher.Catch(func(p *paniccatcher.Panic) {
			uri := "UNKNOWN"
			if rs.HTTPRequest != nil {
				uri = rs.HTTPRequest.RequestURI
			}

			// Log the panic to STDERR (flex).
			fmt.Fprintf(os.Stderr, "Caught panic during handling of %q: %s\n%s\n", uri, p.Reason, p.Stack)

			// Log the reason before the stack in case the stack gets truncated
			// due to size limitations.
			logging.Fields{
				"panic.error": p.Reason,
			}.Errorf(c, "Caught panic during handling of %q: %s\n%s", uri, p.Reason, p.Stack)

			// Escalate observed log level to Critical.
			rs.SeverityTracker.Observe(cloudLogging.Critical)

			// Note: it may be too late to send HTTP 500 if the ResponseWriter has
			// already sent headers. But there's nothing else we can do at this point
			// anyway.
			http.Error(&crw, "Internal Server Error. See logs.", http.StatusInternalServerError)

			// Record the panic as an error.
			if err == nil {
				panicTag := makeScopedRequestPanicTag(p)
				err = errors.Reason("panic caught during handler: %v", p.Reason).Tag(panicTag).Err()
			}
		})
	}

	// Invoke the actual handler.
	fn(c, &crw)
	return nil
}

func (*ScopedRequestHandler) finalize(c context.Context, rs *requestState, crw *capturingResponseWriter) error {
	// If a request log name is supplied, emit a request logging entry.
	if rs.cfg.RequestLogger != nil {
		now := clock.Now(c)
		latency := now.Sub(rs.StartTime)

		// Build a logging HTTPRequest.
		httpRequest := cloudLogging.HTTPRequest{
			Request:      rs.HTTPRequest,
			Status:       crw.Status,
			ResponseSize: crw.Bytes,
			Latency:      latency,
			LocalIP:      rs.LocalAddr,
		}
		if req := rs.HTTPRequest; req != nil {
			httpRequest.RemoteIP = req.RemoteAddr
		}

		// The payload is copied from observed "nginx" payload logging in Flex
		// enviornment.
		payload := struct {
			LatencySeconds string `json:"latencySeconds,omitempty"`
			Trace          string `json:"trace,omitempty"`
		}{
			LatencySeconds: strconv.FormatFloat(latency.Seconds(), 'f', 3, 64),
			Trace:          rs.TraceID,
		}

		labels := make(map[string]string, 2)
		if rs.cfg.InstanceID != "" {
			labels["appengine.googleapis.com/instance_name"] = rs.cfg.InstanceID
		}
		labels[TraceIDLogLabel] = rs.TraceID

		rs.cfg.RequestLogger.Log(cloudLogging.Entry{
			Timestamp:   now,
			Severity:    rs.SeverityTracker.HighestSeverity(),
			Payload:     payload,
			Labels:      labels,
			InsertID:    rs.insertIDGenerator.Next(),
			HTTPRequest: &httpRequest,
		})
	}
	return nil
}

////////////////////////////////////////////////////////////////////////////////
// capturingResponseWriter
////////////////////////////////////////////////////////////////////////////////

// capturingResponseWriter wraps an http.ResponseWriter, capturing metadata
// related to the response.
type capturingResponseWriter struct {
	// Inner is the inner ResponseWriter.
	Inner http.ResponseWriter

	// Bytes is the total number of response bytes written.
	Bytes int64

	// HasStatus is true if an HTTP status has been formally set.
	HasStatus bool
	// Status is the value of the set HTTP status. It is only valid if HasStatus
	// is true.
	Status int
}

func (crw *capturingResponseWriter) Header() http.Header { return crw.Inner.Header() }

func (crw *capturingResponseWriter) Write(p []byte) (int, error) {
	crw.maybeRecordStatus(http.StatusOK)
	amt, err := crw.Inner.Write(p)
	crw.Bytes += int64(amt)
	return amt, err
}

func (crw *capturingResponseWriter) WriteHeader(status int) {
	crw.maybeRecordStatus(status)
	crw.Inner.WriteHeader(status)
}

func (crw *capturingResponseWriter) maybeRecordStatus(s int) {
	if !crw.HasStatus {
		crw.Status, crw.HasStatus = s, true
	}
}

////////////////////////////////////////////////////////////////////////////////
// scopedRequestPanicTag
////////////////////////////////////////////////////////////////////////////////

var scopedRequestPanicTagKey = errors.NewTagKey("the panic reason that was captured by a ScoepdRequest Handler")

type scopedRequestPanicTag struct {
	p *paniccatcher.Panic
}

func makeScopedRequestPanicTag(p *paniccatcher.Panic) errors.TagValueGenerator {
	return &scopedRequestPanicTag{p}
}

func (t *scopedRequestPanicTag) GenerateErrorTagValue() errors.TagValue {
	return errors.TagValue{Key: scopedRequestPanicTagKey, Value: t.p}
}

// ScopedRequestPanic returns the panic that a ScopedRequestHandler error hasl
// been tagged with.
//
// If the error was not triggered by a captured Panic, ScopedRequestPanic will
// return nil.
func ScopedRequestPanic(err error) *paniccatcher.Panic {
	if v, ok := errors.TagValueIn(scopedRequestPanicTagKey, err); ok {
		return v.(*paniccatcher.Panic)
	}
	return nil
}
<commit_msg>[gae] Implement http.Flusher for capturingResponseWriter<commit_after>// Copyright 2017 The LUCI 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 cloud

import (
	"fmt"
	"net/http"
	"os"
	"strconv"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/runtime/paniccatcher"

	cloudLogging "cloud.google.com/go/logging"

	"golang.org/x/net/context"
)

// HandlerFunc is a callback to handle the HTTP request.
type HandlerFunc func(context.Context, http.ResponseWriter)

// ScopedRequestHandler offers a middleware functionality that adds
// AppEngine-like handling to individual requests.
//
// ScopedRequestHandler is not required to use the cloud implementation. If
// used, a ScopedRequestHandler's Handle method should be called during
// processing of individual HTTP requests to add pre- and post-request
// processing to these requests.
//
// AppEngine has the behavior of grouping logs emitted during the processing of
// an individual request underneath of a top-level log for that request,
// allowing a user to visually isolate messages associated with a request.
// AppEngine Flex does not offer the same functionality by default, requiring
// users to manually emit the appropriate log messages to achieve a similar
// per-request grouping effect.
//
// The construction used by ScopedRequestHandler works as follows: a "debug log"
// Logger is installed into the handler Context by Use, causing all logs
// generated by the Handler to be associated with the request's Trace ID. After
// the request is completed, ScopedRequestHandler emits a single "request log"
// representing the status of the request's handling and referencing the same
// Trace ID as the debug logs. A user viewing the request log stream would see
// one log entry per request; however, when that log entry is expanded, the
// viewer populates it with the debug logs that share its Trace ID, creating the
// effect of per-request logs.
type ScopedRequestHandler struct {
	// CapturePanics, if true, instructs the ScopedRequestHandler to capture and
	// handle any panics that occur during its request processing. IF false,
	// panics will not be caught or logged by ScopedRequestHandler.
	//
	// If a panic is caught, it will be logged to the installed logger. Handle
	// will return an error indicating that a panic was observed.
	CapturePanics bool
}

// Handle is a middleware function that wraps an HTTP request. It sets up a
// preprocessing environment, invokes the supplied Handler, and operates on the
// output after the Handler has finished.
//
// If a panic occurs during Handle, and CapturePanics is true, the panic will
// be logged and consumed, and Handle will return an error. The panic's metadata
// will be available in the error by calling ScopedRequestPanic.
//
// Handle requires a Flex GAE environment to be installed in the supplied
// Context (see Use).
func (sr *ScopedRequestHandler) Handle(c context.Context, rw http.ResponseWriter, fn HandlerFunc) (err error) {
	rs := currentRequestState(c)
	if rs == nil {
		panic("no request state is installed in the Context")
	}

	// Wrap the ResponseWriter so we can track as much of the response properties
	// as we can.
	crw := capturingResponseWriter{Inner: rw}

	// Finalize on deferred so we can handle panic scenarios.
	defer func() {
		if derr := sr.finalize(c, rs, &crw); derr != nil {
			logging.WithError(derr).Warningf(c, "Failed to finalize scoped request.")
			if err != nil {
				err = derr
			}
		}
	}()

	// If we are handling panics, handle them before we finalize.
	if sr.CapturePanics {
		defer paniccatcher.Catch(func(p *paniccatcher.Panic) {
			uri := "UNKNOWN"
			if rs.HTTPRequest != nil {
				uri = rs.HTTPRequest.RequestURI
			}

			// Log the panic to STDERR (flex).
			fmt.Fprintf(os.Stderr, "Caught panic during handling of %q: %s\n%s\n", uri, p.Reason, p.Stack)

			// Log the reason before the stack in case the stack gets truncated
			// due to size limitations.
			logging.Fields{
				"panic.error": p.Reason,
			}.Errorf(c, "Caught panic during handling of %q: %s\n%s", uri, p.Reason, p.Stack)

			// Escalate observed log level to Critical.
			rs.SeverityTracker.Observe(cloudLogging.Critical)

			// Note: it may be too late to send HTTP 500 if the ResponseWriter has
			// already sent headers. But there's nothing else we can do at this point
			// anyway.
			http.Error(&crw, "Internal Server Error. See logs.", http.StatusInternalServerError)

			// Record the panic as an error.
			if err == nil {
				panicTag := makeScopedRequestPanicTag(p)
				err = errors.Reason("panic caught during handler: %v", p.Reason).Tag(panicTag).Err()
			}
		})
	}

	// Invoke the actual handler.
	fn(c, &crw)
	return nil
}

func (*ScopedRequestHandler) finalize(c context.Context, rs *requestState, crw *capturingResponseWriter) error {
	// If a request log name is supplied, emit a request logging entry.
	if rs.cfg.RequestLogger != nil {
		now := clock.Now(c)
		latency := now.Sub(rs.StartTime)

		// Build a logging HTTPRequest.
		httpRequest := cloudLogging.HTTPRequest{
			Request:      rs.HTTPRequest,
			Status:       crw.Status,
			ResponseSize: crw.Bytes,
			Latency:      latency,
			LocalIP:      rs.LocalAddr,
		}
		if req := rs.HTTPRequest; req != nil {
			httpRequest.RemoteIP = req.RemoteAddr
		}

		// The payload is copied from observed "nginx" payload logging in Flex
		// enviornment.
		payload := struct {
			LatencySeconds string `json:"latencySeconds,omitempty"`
			Trace          string `json:"trace,omitempty"`
		}{
			LatencySeconds: strconv.FormatFloat(latency.Seconds(), 'f', 3, 64),
			Trace:          rs.TraceID,
		}

		labels := make(map[string]string, 2)
		if rs.cfg.InstanceID != "" {
			labels["appengine.googleapis.com/instance_name"] = rs.cfg.InstanceID
		}
		labels[TraceIDLogLabel] = rs.TraceID

		rs.cfg.RequestLogger.Log(cloudLogging.Entry{
			Timestamp:   now,
			Severity:    rs.SeverityTracker.HighestSeverity(),
			Payload:     payload,
			Labels:      labels,
			InsertID:    rs.insertIDGenerator.Next(),
			HTTPRequest: &httpRequest,
		})
	}
	return nil
}

////////////////////////////////////////////////////////////////////////////////
// capturingResponseWriter
////////////////////////////////////////////////////////////////////////////////

// capturingResponseWriter wraps an http.ResponseWriter, capturing metadata
// related to the response.
type capturingResponseWriter struct {
	// Inner is the inner ResponseWriter.
	Inner http.ResponseWriter

	// Bytes is the total number of response bytes written.
	Bytes int64

	// HasStatus is true if an HTTP status has been formally set.
	HasStatus bool
	// Status is the value of the set HTTP status. It is only valid if HasStatus
	// is true.
	Status int
}

func (crw *capturingResponseWriter) Header() http.Header { return crw.Inner.Header() }

func (crw *capturingResponseWriter) Write(p []byte) (int, error) {
	crw.maybeRecordStatus(http.StatusOK)
	amt, err := crw.Inner.Write(p)
	crw.Bytes += int64(amt)
	return amt, err
}

func (crw *capturingResponseWriter) WriteHeader(status int) {
	crw.maybeRecordStatus(status)
	crw.Inner.WriteHeader(status)
}

func (crw *capturingResponseWriter) maybeRecordStatus(s int) {
	if !crw.HasStatus {
		crw.Status, crw.HasStatus = s, true
	}
}

// Flush implements http.Flusher by passing through the flush call if the underlying
// ResponseWriter implements it.
func (crw *capturingResponseWriter) Flush() {
	if f, ok := crw.Inner.(http.Flusher); ok {
		f.Flush()
	}
}

////////////////////////////////////////////////////////////////////////////////
// scopedRequestPanicTag
////////////////////////////////////////////////////////////////////////////////

var scopedRequestPanicTagKey = errors.NewTagKey("the panic reason that was captured by a ScoepdRequest Handler")

type scopedRequestPanicTag struct {
	p *paniccatcher.Panic
}

func makeScopedRequestPanicTag(p *paniccatcher.Panic) errors.TagValueGenerator {
	return &scopedRequestPanicTag{p}
}

func (t *scopedRequestPanicTag) GenerateErrorTagValue() errors.TagValue {
	return errors.TagValue{Key: scopedRequestPanicTagKey, Value: t.p}
}

// ScopedRequestPanic returns the panic that a ScopedRequestHandler error hasl
// been tagged with.
//
// If the error was not triggered by a captured Panic, ScopedRequestPanic will
// return nil.
func ScopedRequestPanic(err error) *paniccatcher.Panic {
	if v, ok := errors.TagValueIn(scopedRequestPanicTagKey, err); ok {
		return v.(*paniccatcher.Panic)
	}
	return nil
}
<|endoftext|>