<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxd_client

import (
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/juju/errors"
	"github.com/lxc/lxd/shared"
)

func (client *Client) addInstance(spec InstanceSpec) error {
	remote := ""
	//remote := client.remote
	//remote := spec.Remote
	imageAlias := "ubuntu" // TODO(ericsnow) Do not hard-code.
	//image := spec.Image
	var profiles *[]string
	if len(spec.Profiles) > 0 {
		profiles = &spec.Profiles
	}

	// TODO(ericsnow) Copy the image first?

	resp, err := client.raw.Init(spec.Name, remote, imageAlias, profiles, spec.Ephemeral)
	if err != nil {
		return errors.Trace(err)
	}

	// Init is an async operation, since the tar -xvf (or whatever) might
	// take a while; the result is an LXD operation id, which we can just
	// wait on until it is finished.
	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	if err := client.initInstanceConfig(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after configuring it failed", spec.Name)
		}
		return errors.Trace(err)
	}

	// TODO(ericsnow) Only do this if it's a state server...
	if err := client.exposeHostAPI(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after exposing the API sock failed", spec.Name)
		}
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) initInstanceConfig(spec InstanceSpec) error {
	config := spec.config()
	for key, value := range config {
		err := client.raw.SetContainerConfig(spec.Name, key, value)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

func (client *Client) exposeHostAPI(spec InstanceSpec) error {
	// lxc config device add juju-container lxdsock disk \
	// source=/var/lib/lxd/unix.socket path=var/lib/lxd/unix.socket
	const apiDevName = "lxdsock"
	const devType = "disk"
	const filename = "/var/lib/lxd/unix.socket"
	props := []string{
		// TODO(ericsnow) hard-coded, unix-centric...
		"source=/var/lib/lxd/unix.socket",
		"path=var/lib/lxd/unix.socket",
	}
	resp, err := client.raw.ContainerDeviceAdd(spec.Name, apiDevName, devType, props)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) chown(spec InstanceSpec, filename, user, group string) error {
	cmd := []string{
		"/bin/chown",
		fmt.Sprintf("%s:%s", user, group),
		filename,
	}

	var env map[string]string

	// TODO(ericsnow) We *should* be able to use bytes.Buffer instead...
	stdin, err := ioutil.TempFile("", "")
	if err != nil {
		return errors.Trace(err)
	}
	stdout, err := ioutil.TempFile("", "")
	if err != nil {
		return errors.Trace(err)
	}
	stderr := stdout

	fmt.Println("running", strings.Join(cmd, " "))

	rc, err := client.raw.Exec(spec.Name, cmd, env, stdin, stdout, stderr)
	if err != nil {
		return errors.Trace(err)
	} else if rc != 0 {
		msg := "<reason unknown>"
		if _, err := stdout.Seek(0, 0); err == nil {
			data, err := ioutil.ReadAll(stdout)
			if err == nil {
				msg = string(data)
			}
		}
		return errors.Errorf("got non-zero code from chowning API sock: (%d) %s", rc, msg)
	}

	return nil
}

func (client *Client) startInstance(spec InstanceSpec) error {
	timeout := -1
	force := false
	resp, err := client.raw.Action(spec.Name, shared.Start, timeout, force)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// AddInstance creates a new instance based on the spec's data and
// returns it. The instance will be created using the client.
func (client *Client) AddInstance(spec InstanceSpec) (*Instance, error) {
	if err := client.addInstance(spec); err != nil {
		return nil, errors.Trace(err)
	}

	if err := client.startInstance(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after starting it failed", spec.Name)
		}
		return nil, errors.Trace(err)
	}

	// TODO(ericsnow) This is a hack tied to exposeHostAPI().
	const filename = "/var/lib/lxd/unix.socket"
	if err := client.chown(spec, filename, "root", "root"); err != nil {
		fmt.Println("---- ", err)
		//return errors.Trace(err)
	}

	inst, err := client.Instance(spec.Name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	inst.spec = &spec

	return inst, nil
}

// Instance gets the up-to-date info about the given instance
// and returns it.
func (client *Client) Instance(name string) (*Instance, error) {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return nil, errors.Trace(err)
	}

	inst := newInstance(info, nil)
	return inst, nil
}

// Instances sends a request to the API for a list of all instances
// (in the Client's namespace) for which the name starts with the
// provided prefix. The result is also limited to those instances with
// one of the specified statuses (if any).
func (client *Client) Instances(prefix string, statuses ...string) ([]Instance, error) {
	infos, err := client.raw.ListContainers()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var insts []Instance
	for _, info := range infos {
		name := info.State.Name
		if prefix != "" && !strings.HasPrefix(name, prefix) {
			continue
		}
		if len(statuses) > 0 && !checkStatus(info, statuses) {
			continue
		}

		inst := newInstance(&info.State, nil)
		insts = append(insts, *inst)
	}
	return insts, nil
}

func checkStatus(info shared.ContainerInfo, statuses []string) bool {
	for _, status := range statuses {
		statusCode := allStatuses[status]
		if info.State.Status.StatusCode == statusCode {
			return true
		}
	}
	return false
}

// removeInstance sends a request to the API to remove the instance
// with the provided ID. The call blocks until the instance is removed
// (or the request fails).
func (client *Client) removeInstance(name string) error {
	resp, err := client.raw.Delete(name)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// RemoveInstances sends a request to the API to terminate all
// instances (in the Client's namespace) that match one of the
// provided IDs. If a prefix is provided, only IDs that start with the
// prefix will be considered. The call blocks until all the instances
// are removed or the request fails.
func (client *Client) RemoveInstances(prefix string, names ...string) error {
	if len(names) == 0 {
		return nil
	}

	instances, err := client.Instances(prefix)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", names)
	}

	var failed []string
	for _, name := range names {
		if !checkInstanceName(name, instances) {
			// We ignore unknown instance names.
			continue
		}

		if err := client.removeInstance(name); err != nil {
			failed = append(failed, name)
			logger.Errorf("while removing instance %q: %v", name, err)
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func checkInstanceName(name string, instances []Instance) bool {
	for _, inst := range instances {
		if inst.Name == name {
			return true
		}
	}
	return false
}
<commit_msg>Factor out Client.exec.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxd_client

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/juju/errors"
	"github.com/lxc/lxd/shared"
)

func (client *Client) addInstance(spec InstanceSpec) error {
	remote := ""
	//remote := client.remote
	//remote := spec.Remote
	imageAlias := "ubuntu" // TODO(ericsnow) Do not hard-code.
	//image := spec.Image
	var profiles *[]string
	if len(spec.Profiles) > 0 {
		profiles = &spec.Profiles
	}

	// TODO(ericsnow) Copy the image first?

	resp, err := client.raw.Init(spec.Name, remote, imageAlias, profiles, spec.Ephemeral)
	if err != nil {
		return errors.Trace(err)
	}

	// Init is an async operation, since the tar -xvf (or whatever) might
	// take a while; the result is an LXD operation id, which we can just
	// wait on until it is finished.
	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	if err := client.initInstanceConfig(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after configuring it failed", spec.Name)
		}
		return errors.Trace(err)
	}

	// TODO(ericsnow) Only do this if it's a state server...
	if err := client.exposeHostAPI(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after exposing the API sock failed", spec.Name)
		}
		return errors.Trace(err)
	}

	return nil
}

func (client *Client) initInstanceConfig(spec InstanceSpec) error {
	config := spec.config()
	for key, value := range config {
		err := client.raw.SetContainerConfig(spec.Name, key, value)
		if err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

func (client *Client) exposeHostAPI(spec InstanceSpec) error {
	// lxc config device add juju-container lxdsock disk \
	// source=/var/lib/lxd/unix.socket path=var/lib/lxd/unix.socket
	const apiDevName = "lxdsock"
	const devType = "disk"
	const filename = "/var/lib/lxd/unix.socket"
	props := []string{
		// TODO(ericsnow) hard-coded, unix-centric...
		"source=/var/lib/lxd/unix.socket",
		"path=var/lib/lxd/unix.socket",
	}
	resp, err := client.raw.ContainerDeviceAdd(spec.Name, apiDevName, devType, props)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

type execFailure struct {
	cmd    string
	code   int
	stderr string
}

// Error returns the string representation of the error.
func (err execFailure) Error() string {
	return fmt.Sprintf("got non-zero code from %q: (%d) %s", err.cmd, err.code, err.stderr)
}

func (client *Client) exec(spec InstanceSpec, cmd []string) error {
	var env map[string]string

	stdin, stdout, stderr, err := ioFiles()
	if err != nil {
		return errors.Trace(err)
	}

	cmdStr := strings.Join(cmd, " ")
	fmt.Println("running", cmdStr)

	rc, err := client.raw.Exec(spec.Name, cmd, env, stdin, stdout, stderr)
	if err != nil {
		return errors.Trace(err)
	} else if rc != 0 {
		msg := "<reason unknown>"
		if _, err := stdout.Seek(0, 0); err == nil {
			data, err := ioutil.ReadAll(stdout)
			if err == nil {
				msg = string(data)
			}
		}
		err := &execFailure{
			cmd:    cmdStr,
			code:   rc,
			stderr: msg,
		}
		return errors.Trace(err)
	}

	return nil
}

// TODO(ericsnow) We *should* be able to use bytes.Buffer instead...
func ioFiles() (*os.File, *os.File, *os.File, error) {
	infile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	outfile, err := ioutil.TempFile("", "")
	if err != nil {
		return nil, nil, nil, errors.Trace(err)
	}

	// We combine stdout and stderr...
	return infile, outfile, outfile, nil
}

func (client *Client) chown(spec InstanceSpec, filename, user, group string) error {
	cmd := []string{
		"/bin/chown",
		fmt.Sprintf("%s:%s", user, group),
		filename,
	}

	if err := client.exec(spec, cmd); err != nil {
		return errors.Trace(err)
	}
	return nil
}

func (client *Client) startInstance(spec InstanceSpec) error {
	timeout := -1
	force := false
	resp, err := client.raw.Action(spec.Name, shared.Start, timeout, force)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// AddInstance creates a new instance based on the spec's data and
// returns it. The instance will be created using the client.
func (client *Client) AddInstance(spec InstanceSpec) (*Instance, error) {
	if err := client.addInstance(spec); err != nil {
		return nil, errors.Trace(err)
	}

	if err := client.startInstance(spec); err != nil {
		if err := client.removeInstance(spec.Name); err != nil {
			logger.Errorf("could not remove container %q after starting it failed", spec.Name)
		}
		return nil, errors.Trace(err)
	}

	// TODO(ericsnow) This is a hack tied to exposeHostAPI().
	const filename = "/var/lib/lxd/unix.socket"
	if err := client.chown(spec, filename, "root", "root"); err != nil {
		fmt.Println("---- ", err)
		//return errors.Trace(err)
	}

	inst, err := client.Instance(spec.Name)
	if err != nil {
		return nil, errors.Trace(err)
	}
	inst.spec = &spec

	return inst, nil
}

// Instance gets the up-to-date info about the given instance
// and returns it.
func (client *Client) Instance(name string) (*Instance, error) {
	info, err := client.raw.ContainerStatus(name)
	if err != nil {
		return nil, errors.Trace(err)
	}

	inst := newInstance(info, nil)
	return inst, nil
}

// Instances sends a request to the API for a list of all instances
// (in the Client's namespace) for which the name starts with the
// provided prefix. The result is also limited to those instances with
// one of the specified statuses (if any).
func (client *Client) Instances(prefix string, statuses ...string) ([]Instance, error) {
	infos, err := client.raw.ListContainers()
	if err != nil {
		return nil, errors.Trace(err)
	}

	var insts []Instance
	for _, info := range infos {
		name := info.State.Name
		if prefix != "" && !strings.HasPrefix(name, prefix) {
			continue
		}
		if len(statuses) > 0 && !checkStatus(info, statuses) {
			continue
		}

		inst := newInstance(&info.State, nil)
		insts = append(insts, *inst)
	}
	return insts, nil
}

func checkStatus(info shared.ContainerInfo, statuses []string) bool {
	for _, status := range statuses {
		statusCode := allStatuses[status]
		if info.State.Status.StatusCode == statusCode {
			return true
		}
	}
	return false
}

// removeInstance sends a request to the API to remove the instance
// with the provided ID. The call blocks until the instance is removed
// (or the request fails).
func (client *Client) removeInstance(name string) error {
	resp, err := client.raw.Delete(name)
	if err != nil {
		return errors.Trace(err)
	}

	if err := client.raw.WaitForSuccess(resp.Operation); err != nil {
		// TODO(ericsnow) Handle different failures (from the async
		// operation) differently?
		return errors.Trace(err)
	}

	return nil
}

// RemoveInstances sends a request to the API to terminate all
// instances (in the Client's namespace) that match one of the
// provided IDs. If a prefix is provided, only IDs that start with the
// prefix will be considered. The call blocks until all the instances
// are removed or the request fails.
func (client *Client) RemoveInstances(prefix string, names ...string) error {
	if len(names) == 0 {
		return nil
	}

	instances, err := client.Instances(prefix)
	if err != nil {
		return errors.Annotatef(err, "while removing instances %v", names)
	}

	var failed []string
	for _, name := range names {
		if !checkInstanceName(name, instances) {
			// We ignore unknown instance names.
			continue
		}

		if err := client.removeInstance(name); err != nil {
			failed = append(failed, name)
			logger.Errorf("while removing instance %q: %v", name, err)
		}
	}
	if len(failed) != 0 {
		return errors.Errorf("some instance removals failed: %v", failed)
	}
	return nil
}

func checkInstanceName(name string, instances []Instance) bool {
	for _, inst := range instances {
		if inst.Name == name {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bitbucket.org/gdamore/mangos"
	"bitbucket.org/gdamore/mangos/protocol/rep"
	"bitbucket.org/gdamore/mangos/transport/all"
	"fmt"
	"github.com/ugorji/go/codec"
	"reflect"
	"time"
)

var (
	mh codec.MsgpackHandle
	b  []byte
)

func decode(buf []byte) (error, map[string][]string) {

	doc := map[string][]string(nil)
	dec := codec.NewDecoderBytes(buf, &mh)
	err := dec.Decode(&doc)

	if err != nil {
		return err, nil
	}
	return nil, doc
}
func string2int(in map[string][]string) (error, map[string]int) {
	out := make(map[string]int)

	return nil, out
}

func main() {
	url := "tcp://127.0.0.1:8000"

	responseServerReady := make(chan struct{})
	responseServer, err := rep.NewSocket()
	defer responseServer.Close()

	all.AddTransports(responseServer)
	if err != nil {
		fmt.Println("Error connecting: ", err)
		return
	}

	fmt.Println("Consumer ready")

	go func() {
		var err error
		var serverMsg *mangos.Message

		if err = responseServer.Listen(url); err != nil {
			fmt.Printf("\nServer listen failed: %v", err)
			return
		}

		close(responseServerReady)
		mh.MapType = reflect.TypeOf(map[string][]string(nil))

		for {
			if serverMsg, err = responseServer.RecvMsg(); err != nil {
				fmt.Printf("\nServer receive failed: %v", err)
			}

			err, d := decode(serverMsg.Body)
			fmt.Println("------ server msg ------ ")
			for k, v := range d {
				fmt.Println(k, v)
			}
			fmt.Println("------ server msg ------ ")

			serverMsg.Body = []byte("OK")

			if err = responseServer.SendMsg(serverMsg); err != nil {
				fmt.Printf("\nServer send failed: %v", err)
				return
			}
		}
		fmt.Println("Listening")
	}()

	for {
		time.Sleep(10 * time.Second)
	}
}
<commit_msg>organize consumer.go<commit_after>package main

import (
	"bitbucket.org/gdamore/mangos"
	"bitbucket.org/gdamore/mangos/protocol/rep"
	"bitbucket.org/gdamore/mangos/transport/all"
	"fmt"
	"github.com/ugorji/go/codec"
	"reflect"
	"time"
)

var (
	mh codec.MsgpackHandle
	b  []byte
)

func boomerangMetrics(map[string][]string) {}

func jsMetrics(map[string]string) {}

func decode(buf []byte) (error, map[string][]string) {

	doc := map[string][]string(nil)
	dec := codec.NewDecoderBytes(buf, &mh)
	err := dec.Decode(&doc)

	if err != nil {
		return err, nil
	}
	return nil, doc
}
func string2int(in map[string][]string) (error, map[string]int) {
	out := make(map[string]int)

	return nil, out
}

func main() {
	// consumer --type boomerang --remote tcp://127.0.0.1:8000 --statsd 192.168.33.20:8125

	url := "tcp://127.0.0.1:8000"

	responseServerReady := make(chan struct{})
	responseServer, err := rep.NewSocket()
	defer responseServer.Close()

	all.AddTransports(responseServer)
	if err != nil {
		fmt.Println("Error connecting: ", err)
		return
	}

	fmt.Println("Consumer ready")

	go func() {
		var err error
		var serverMsg *mangos.Message

		if err = responseServer.Listen(url); err != nil {
			fmt.Printf("\nServer listen failed: %v", err)
			return
		}

		close(responseServerReady)
		mh.MapType = reflect.TypeOf(map[string][]string(nil))

		for {
			if serverMsg, err = responseServer.RecvMsg(); err != nil {
				fmt.Printf("\nServer receive failed: %v", err)
			}

			err, d := decode(serverMsg.Body)
			fmt.Println("------ server msg ------ ")
			for k, v := range d {
				fmt.Println(k, v)
			}
			fmt.Println("------ server msg ------ ")

			serverMsg.Body = []byte("OK")

			if err = responseServer.SendMsg(serverMsg); err != nil {
				fmt.Printf("\nServer send failed: %v", err)
				return
			}
		}
		fmt.Println("Listening")
	}()

	for {
		time.Sleep(10 * time.Second)
	}
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 main

// [START firestore_deps]
import (
	"context"
	"fmt"

	"cloud.google.com/go/firestore"
)

// [END firestore_deps]

func prepareQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_dataset]
	cities := []struct {
		id string
		c  City
	}{
		{
			id: "SF",
			c: City{Name: "San Francisco", State: "CA", Country: "USA",
				Capital: false, Population: 860000,
				Regions: []string{"west_coast", "norcal"}},
		},
		{
			id: "LA",
			c: City{Name: "Los Angeles", State: "CA", Country: "USA",
				Capital: false, Population: 3900000,
				Regions: []string{"west_coast", "socal"}},
		},
		{
			id: "DC",
			c: City{Name: "Washington D.C.", Country: "USA",
				Capital: false, Population: 680000,
				Regions: []string{"east_coast"}},
		},
		{
			id: "TOK",
			c: City{Name: "Tokyo", Country: "Japan",
				Capital: true, Population: 9000000,
				Regions: []string{"kanto", "honshu"}},
		},
		{
			id: "BJ",
			c: City{Name: "Beijing", Country: "China",
				Capital: true, Population: 21500000,
				Regions: []string{"jingjinji", "hebei"}},
		},
	}
	for _, c := range cities {
		if _, err := client.Collection("cities").Doc(c.id).Set(ctx, c.c); err != nil {
			return err
		}
	}
	// [END firestore_query_filter_dataset]
	return nil
}

func createQuery(client *firestore.Client) {
	// [START firestore_query_filter_eq_boolean]
	query := client.Collection("cities").Where("capital", "==", true)
	// [END firestore_query_filter_eq_boolean]
	_ = query
}

func createQueryTwo(client *firestore.Client) {
	// [START firestore_query_filter_eq_string]
	query := client.Collection("cities").Where("state", "==", "CA")
	// [END firestore_query_filter_eq_string]
	_ = query
}

func createSimpleQueries(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_single_examples]
	countryQuery := cities.Where("state", "==", "CA")
	popQuery := cities.Where("population", "<", 1000000)
	cityQuery := cities.Where("name", ">=", "San Francisco")
	// [END firestore_query_filter_single_examples]

	_ = countryQuery
	_ = popQuery
	_ = cityQuery
}

func createChainedQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_compound_multi_eq]
	denverQuery := cities.Where("name", "==", "Denver").Where("state", "==", "CO")
	caliQuery := cities.Where("state", "==", "CA").Where("population", "<=", 1000000)
	// [END firestore_query_filter_compound_multi_eq]

	_ = denverQuery
	_ = caliQuery
}

func createInvalidChainedQuery(client *firestore.Client) {
	// Note: this is an instance of a currently unsupported chained query
	cities := client.Collection("cities")
	// [START firestore_query_filter_compound_multi_eq]
	query := cities.Where("country", "==", "USA").Where("population", ">", 5000000)
	// [END firestore_query_filter_compound_multi_eq]

	_ = query
}

func createRangeQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_range_valid]
	stateQuery := cities.Where("state", ">=", "CA").Where("state", "<", "IN")
	populationQuery := cities.Where("state", "==", "CA").Where("population", ">", 1000000)
	// [END firestore_query_filter_range_valid]

	_ = stateQuery
	_ = populationQuery
}

func createInvalidRangeQuery(client *firestore.Client) {
	// Note: This is an invalid range query: range operators
	// are limited to a single field.
	cities := client.Collection("cities")
	// [START firestore_query_filter_range_invalid]
	query := cities.Where("state", ">=", "CA").Where("population", ">", 1000000)
	// [END firestore_query_filter_range_invalid]

	_ = query
}

func createOrderByNameLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit]
	query := cities.OrderBy("name", firestore.Asc).Limit(3)
	// [END firestore_query_order_limit]

	_ = query
}

func createOrderByNameLimitToLastQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit]
	query := cities.OrderBy("name", firestore.Asc).LimitToLast(3)
	// [END firestore_query_order_limit]

	_ = query
}

func createOrderByNameDescLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_desc_limit]
	query := cities.OrderBy("name", firestore.Desc).Limit(3)
	// [END firestore_query_order_desc_limit]

	_ = query
}

func createMultipleOrderByQuery(client *firestore.Client) {
	// [START firestore_query_order_multi]
	query := client.Collection("cities").OrderBy("state", firestore.Asc).OrderBy("population", firestore.Desc)
	// [END firestore_query_order_multi]
	_ = query
}

func createRangeWithOrderByAndLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit_field_valid]
	query := cities.Where("population", ">", 2500000).OrderBy("population", firestore.Desc).Limit(2)
	// [END firestore_query_order_limit_field_valid]

	_ = query
}

func createRangeWithOrderByQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_with_filter]
	query := cities.Where("population", ">", 2500000).OrderBy("population", firestore.Asc)
	// [END firestore_query_order_with_filter]

	_ = query
}

func createInvalidRangeWithOrderByQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_field_invalid]
	// Note: This is an invalid query. It violates the constraint that range
	// and order by are required to be on the same field.
	query := cities.Where("population", ">", 2500000).OrderBy("country", firestore.Asc)
	// [END firestore_query_order_field_invalid]

	_ = query
}

func createSimpleStartAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_start_at_field_value_single]
	query := client.Collection("cities").OrderBy("population", firestore.Asc).StartAt(1000000)
	// [END firestore_query_cursor_start_at_field_value_single]
	_ = query
}

func createSimpleEndtAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_end_at_field_value_single]
	query := client.Collection("cities").OrderBy("population", firestore.Asc).EndAt(1000000)
	// [END firestore_query_cursor_end_at_field_value_single]
	_ = query
}

func paginateCursor(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_cursor_pagination]
	cities := client.Collection("cities")

	// Get the first 25 cities, ordered by population.
	firstPage := cities.OrderBy("population", firestore.Asc).Limit(25).Documents(ctx)
	docs, err := firstPage.GetAll()
	if err != nil {
		return err
	}

	// Get the last document.
	lastDoc := docs[len(docs)-1]

	// Construct a new query to get the next 25 cities.
	secondPage := cities.OrderBy("population", firestore.Asc).
		StartAfter(lastDoc.Data()["population"]).
		Limit(25)

	// ...
	// [END firestore_query_cursor_pagination]
	_ = secondPage
	return nil
}

func createMultipleStartAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_start_at_field_value_multi]
	// Will return all Springfields.
	client.Collection("cities").
		OrderBy("name", firestore.Asc).
		OrderBy("state", firestore.Asc).
		StartAt("Springfield")

	// Will return Springfields where state comes after Wisconsin.
	client.Collection("cities").
		OrderBy("name", firestore.Asc).
		OrderBy("state", firestore.Asc).
		StartAt("Springfield", "Wisconsin")
	// [END firestore_query_cursor_start_at_field_value_multi]
}

func createInQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_in]
	cities := client.Collection("cities")
	query := cities.Where("country", "in", []string{"USA", "Japan"}).Documents(ctx)
	// [END firestore_query_filter_in]

	_ = query
	return nil
}

func createInQueryWithArray(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_in_with_array]
	cities := client.Collection("cities")
	query := cities.Where("regions", "in", [][]string{{"west_coast"}, {"east_coast"}}).Documents(ctx)
	// [END firestore_query_filter_in_with_array]

	_ = query
	return nil
}

func createArrayContainsQuery(ctx context.Context, client *firestore.Client) error {
	cities := client.Collection("cities")
	// [START firestore_query_filter_array_contains]
	query := cities.Where("regions", "array-contains", "west_coast").Documents(ctx)
	// [END firestore_query_filter_array_contains]

	_ = query
	return nil
}

func createArrayContainsAnyQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_array_contains_any]
	cities := client.Collection("cities")
	query := cities.Where("regions", "array-contains-any", []string{"west_coast", "east_coast"}).Documents(ctx)
	// [END firestore_query_filter_array_contains_any]

	_ = query
	return nil
}

func createStartAtDocSnapshotQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_cursor_start_at_document]
	cities := client.Collection("cities")
	dsnap, err := cities.Doc("SF").Get(ctx)
	if err != nil {
		fmt.Println(err)
	}
	query := cities.OrderBy("population", firestore.Asc).StartAt(dsnap.Data()["population"]).Documents(ctx)
	// [END firestore_query_cursor_start_at_document]

	_ = query
	return nil
}
<commit_msg>docs(firestore): minor correction to Firestore sample data #2147<commit_after>// Copyright 2019 Google LLC
//
// 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
//
//     https://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 main

// [START firestore_deps]
import (
	"context"
	"fmt"

	"cloud.google.com/go/firestore"
)

// [END firestore_deps]

func prepareQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_dataset]
	cities := []struct {
		id string
		c  City
	}{
		{
			id: "SF",
			c: City{Name: "San Francisco", State: "CA", Country: "USA",
				Capital: false, Population: 860000,
				Regions: []string{"west_coast", "norcal"}},
		},
		{
			id: "LA",
			c: City{Name: "Los Angeles", State: "CA", Country: "USA",
				Capital: false, Population: 3900000,
				Regions: []string{"west_coast", "socal"}},
		},
		{
			id: "DC",
			c: City{Name: "Washington D.C.", Country: "USA",
				Capital: true, Population: 680000,
				Regions: []string{"east_coast"}},
		},
		{
			id: "TOK",
			c: City{Name: "Tokyo", Country: "Japan",
				Capital: true, Population: 9000000,
				Regions: []string{"kanto", "honshu"}},
		},
		{
			id: "BJ",
			c: City{Name: "Beijing", Country: "China",
				Capital: true, Population: 21500000,
				Regions: []string{"jingjinji", "hebei"}},
		},
	}
	for _, c := range cities {
		if _, err := client.Collection("cities").Doc(c.id).Set(ctx, c.c); err != nil {
			return err
		}
	}
	// [END firestore_query_filter_dataset]
	return nil
}

func createQuery(client *firestore.Client) {
	// [START firestore_query_filter_eq_boolean]
	query := client.Collection("cities").Where("capital", "==", true)
	// [END firestore_query_filter_eq_boolean]
	_ = query
}

func createQueryTwo(client *firestore.Client) {
	// [START firestore_query_filter_eq_string]
	query := client.Collection("cities").Where("state", "==", "CA")
	// [END firestore_query_filter_eq_string]
	_ = query
}

func createSimpleQueries(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_single_examples]
	countryQuery := cities.Where("state", "==", "CA")
	popQuery := cities.Where("population", "<", 1000000)
	cityQuery := cities.Where("name", ">=", "San Francisco")
	// [END firestore_query_filter_single_examples]

	_ = countryQuery
	_ = popQuery
	_ = cityQuery
}

func createChainedQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_compound_multi_eq]
	denverQuery := cities.Where("name", "==", "Denver").Where("state", "==", "CO")
	caliQuery := cities.Where("state", "==", "CA").Where("population", "<=", 1000000)
	// [END firestore_query_filter_compound_multi_eq]

	_ = denverQuery
	_ = caliQuery
}

func createInvalidChainedQuery(client *firestore.Client) {
	// Note: this is an instance of a currently unsupported chained query
	cities := client.Collection("cities")
	// [START firestore_query_filter_compound_multi_eq]
	query := cities.Where("country", "==", "USA").Where("population", ">", 5000000)
	// [END firestore_query_filter_compound_multi_eq]

	_ = query
}

func createRangeQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_filter_range_valid]
	stateQuery := cities.Where("state", ">=", "CA").Where("state", "<", "IN")
	populationQuery := cities.Where("state", "==", "CA").Where("population", ">", 1000000)
	// [END firestore_query_filter_range_valid]

	_ = stateQuery
	_ = populationQuery
}

func createInvalidRangeQuery(client *firestore.Client) {
	// Note: This is an invalid range query: range operators
	// are limited to a single field.
	cities := client.Collection("cities")
	// [START firestore_query_filter_range_invalid]
	query := cities.Where("state", ">=", "CA").Where("population", ">", 1000000)
	// [END firestore_query_filter_range_invalid]

	_ = query
}

func createOrderByNameLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit]
	query := cities.OrderBy("name", firestore.Asc).Limit(3)
	// [END firestore_query_order_limit]

	_ = query
}

func createOrderByNameLimitToLastQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit]
	query := cities.OrderBy("name", firestore.Asc).LimitToLast(3)
	// [END firestore_query_order_limit]

	_ = query
}

func createOrderByNameDescLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_desc_limit]
	query := cities.OrderBy("name", firestore.Desc).Limit(3)
	// [END firestore_query_order_desc_limit]

	_ = query
}

func createMultipleOrderByQuery(client *firestore.Client) {
	// [START firestore_query_order_multi]
	query := client.Collection("cities").OrderBy("state", firestore.Asc).OrderBy("population", firestore.Desc)
	// [END firestore_query_order_multi]
	_ = query
}

func createRangeWithOrderByAndLimitQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_limit_field_valid]
	query := cities.Where("population", ">", 2500000).OrderBy("population", firestore.Desc).Limit(2)
	// [END firestore_query_order_limit_field_valid]

	_ = query
}

func createRangeWithOrderByQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_with_filter]
	query := cities.Where("population", ">", 2500000).OrderBy("population", firestore.Asc)
	// [END firestore_query_order_with_filter]

	_ = query
}

func createInvalidRangeWithOrderByQuery(client *firestore.Client) {
	cities := client.Collection("cities")
	// [START firestore_query_order_field_invalid]
	// Note: This is an invalid query. It violates the constraint that range
	// and order by are required to be on the same field.
	query := cities.Where("population", ">", 2500000).OrderBy("country", firestore.Asc)
	// [END firestore_query_order_field_invalid]

	_ = query
}

func createSimpleStartAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_start_at_field_value_single]
	query := client.Collection("cities").OrderBy("population", firestore.Asc).StartAt(1000000)
	// [END firestore_query_cursor_start_at_field_value_single]
	_ = query
}

func createSimpleEndtAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_end_at_field_value_single]
	query := client.Collection("cities").OrderBy("population", firestore.Asc).EndAt(1000000)
	// [END firestore_query_cursor_end_at_field_value_single]
	_ = query
}

func paginateCursor(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_cursor_pagination]
	cities := client.Collection("cities")

	// Get the first 25 cities, ordered by population.
	firstPage := cities.OrderBy("population", firestore.Asc).Limit(25).Documents(ctx)
	docs, err := firstPage.GetAll()
	if err != nil {
		return err
	}

	// Get the last document.
	lastDoc := docs[len(docs)-1]

	// Construct a new query to get the next 25 cities.
	secondPage := cities.OrderBy("population", firestore.Asc).
		StartAfter(lastDoc.Data()["population"]).
		Limit(25)

	// ...
	// [END firestore_query_cursor_pagination]
	_ = secondPage
	return nil
}

func createMultipleStartAtQuery(client *firestore.Client) {
	// [START firestore_query_cursor_start_at_field_value_multi]
	// Will return all Springfields.
	client.Collection("cities").
		OrderBy("name", firestore.Asc).
		OrderBy("state", firestore.Asc).
		StartAt("Springfield")

	// Will return Springfields where state comes after Wisconsin.
	client.Collection("cities").
		OrderBy("name", firestore.Asc).
		OrderBy("state", firestore.Asc).
		StartAt("Springfield", "Wisconsin")
	// [END firestore_query_cursor_start_at_field_value_multi]
}

func createInQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_in]
	cities := client.Collection("cities")
	query := cities.Where("country", "in", []string{"USA", "Japan"}).Documents(ctx)
	// [END firestore_query_filter_in]

	_ = query
	return nil
}

func createInQueryWithArray(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_in_with_array]
	cities := client.Collection("cities")
	query := cities.Where("regions", "in", [][]string{{"west_coast"}, {"east_coast"}}).Documents(ctx)
	// [END firestore_query_filter_in_with_array]

	_ = query
	return nil
}

func createArrayContainsQuery(ctx context.Context, client *firestore.Client) error {
	cities := client.Collection("cities")
	// [START firestore_query_filter_array_contains]
	query := cities.Where("regions", "array-contains", "west_coast").Documents(ctx)
	// [END firestore_query_filter_array_contains]

	_ = query
	return nil
}

func createArrayContainsAnyQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_filter_array_contains_any]
	cities := client.Collection("cities")
	query := cities.Where("regions", "array-contains-any", []string{"west_coast", "east_coast"}).Documents(ctx)
	// [END firestore_query_filter_array_contains_any]

	_ = query
	return nil
}

func createStartAtDocSnapshotQuery(ctx context.Context, client *firestore.Client) error {
	// [START firestore_query_cursor_start_at_document]
	cities := client.Collection("cities")
	dsnap, err := cities.Doc("SF").Get(ctx)
	if err != nil {
		fmt.Println(err)
	}
	query := cities.OrderBy("population", firestore.Asc).StartAt(dsnap.Data()["population"]).Documents(ctx)
	// [END firestore_query_cursor_start_at_document]

	_ = query
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018-2019 Authors of Cilium
//
// 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 k8sTest

import (
	"context"
	"fmt"
	"time"

	. "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"

	. "github.com/onsi/gomega"
)

// This tests the Istio 1.2.2 integration, following the configuration
// instructions specified in the Istio Getting Started Guide in
// Documentation/gettingstarted/istio.rst.
// Changes to the Getting Started Guide may require re-generating or copying
// the following manifests:
// - istio-crds.yaml
// - istio-cilium.yaml
// - bookinfo-v1-istio.yaml
// - bookinfo-v2-istio.yaml
// Cf. the comments below for each manifest.
var _ = Describe("K8sIstioTest", func() {

	var (
		// istioSystemNamespace is the default namespace into which Istio is
		// installed.
		istioSystemNamespace = "istio-system"

		// istioCRDYAMLPath is the file generated from istio-init during a
		// step in Documentation/gettingstarted/istio.rst to setup
		// Istio 1.2.2. In the GSG the file is directly piped to kubectl.
		istioCRDYAMLPath = helpers.ManifestGet("istio-crds.yaml")

		// istioYAMLPath is the istio-cilium.yaml file generated following the
		// instructions in Documentation/gettingstarted/istio.rst to setup
		// Istio 1.2.2. mTLS is enabled.
		istioYAMLPath = helpers.ManifestGet("istio-cilium.yaml")

		// istioServiceNames is the subset of Istio services in the Istio
		// namespace that are accessed from sidecar proxies.
		istioServiceNames = []string{
			// All the services created by Istio are listed here, but only
			// those that we care about are uncommented.
			// "istio-citadel",
			// "istio-galley",
			// "istio-egressgateway",
			"istio-ingressgateway",
			"istio-pilot",
			// "istio-policy",
			// "istio-telemetry",
			// "prometheus",
		}

		// wgetCommand is the command used in this test because the Istio apps
		// do not provide curl.
		wgetCommand = fmt.Sprintf("wget --tries=2 --connect-timeout %d", helpers.CurlConnectTimeout)

		kubectl          *helpers.Kubectl
		microscopeCancel = func() error { return nil }
		uptimeCancel     context.CancelFunc

		teardownTimeout = 10 * time.Minute
	)

	BeforeAll(func() {
		Skip("Istio test is unstable, disable for now until we find the cause")

		k8sVersion := helpers.GetCurrentK8SEnv()
		switch k8sVersion {
		case "1.7", "1.8", "1.9":
			Skip(fmt.Sprintf("Istio doesn't support K8S %s", k8sVersion))
		}

		kubectl = helpers.CreateKubectl(helpers.K8s1VMName(), logger)
		DeployCiliumAndDNS(kubectl)

		By("Creating the istio-system namespace")
		res := kubectl.NamespaceCreate(istioSystemNamespace)
		res.ExpectSuccess("unable to create namespace %q", istioSystemNamespace)

		By("Creating the Istio resources")

		res = kubectl.Apply(istioCRDYAMLPath)
		res.ExpectSuccess("unable to create Istio CRDs")

		By("Waiting for Istio CRDs to be ready")
		err := kubectl.WaitForCRDCount("istio.io|certmanager.k8s.io", 23, helpers.HelperTimeout)
		Expect(err).To(BeNil(),
			"Istio CRDs are not ready after timeout")

		res = kubectl.Apply(istioYAMLPath)
		res.ExpectSuccess("unable to create Istio resources")
	})

	AfterAll(func() {
		By("Deleting the Istio resources")
		_ = kubectl.Delete(istioYAMLPath)

		By("Deleting the Istio CRDs")
		_ = kubectl.Delete(istioCRDYAMLPath)

		By("Deleting the istio-system namespace")
		_ = kubectl.NamespaceDelete(istioSystemNamespace)

		kubectl.WaitCleanAllTerminatingPods(teardownTimeout)

		kubectl.CloseSSHClient()
	})

	JustBeforeEach(func() {
		var err error
		err, microscopeCancel = kubectl.MicroscopeStart()
		Expect(err).To(BeNil(), "Microscope cannot be started")

		uptimeCancel, err = kubectl.BackgroundReport("uptime")
		Expect(err).To(BeNil(), "Cannot start background report process")
	})

	JustAfterEach(func() {
		Expect(microscopeCancel()).To(BeNil(), "Cannot stop microscope")
		uptimeCancel()

		kubectl.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
	})

	AfterFailed(func() {
		kubectl.CiliumReport(helpers.KubeSystemNamespace,
			"cilium endpoint list",
			"cilium bpf proxy list")
	})

	waitIstioReady := func() {
		// Ignore one-time jobs and Prometheus. All other pods in the
		// namespaces have an "istio" label.
		By("Waiting for Istio pods to be ready")
		err := kubectl.WaitforPods(istioSystemNamespace, "-l istio", helpers.HelperTimeout)
		ExpectWithOffset(1, err).To(BeNil(),
			"Istio pods are not ready after timeout in namespace %q", istioSystemNamespace)

		for _, name := range istioServiceNames {
			By("Waiting for Istio service %q to be ready", name)
			err = kubectl.WaitForServiceEndpoints(
				istioSystemNamespace, "", name, helpers.HelperTimeout)
			ExpectWithOffset(1, err).Should(BeNil(), "Service %q is not ready after timeout", name)
		}

		for _, name := range istioServiceNames {
			By("Waiting for DNS to resolve Istio service %q", name)
			err = kubectl.WaitForKubeDNSEntry(name, istioSystemNamespace)
			ExpectWithOffset(1, err).To(BeNil(), "DNS entry is not ready after timeout")
		}
	}

	// This is a subset of Services's "Bookinfo Demo" test suite, with the pods
	// injected with Istio sidecar proxies and Istio mTLS enabled.
	Context("Istio Bookinfo Demo", func() {

		var (
			resourceYAMLPaths []string
			policyPaths       []string
		)

		AfterEach(func() {
			for _, resourcePath := range resourceYAMLPaths {
				By("Deleting resource in file %q", resourcePath)
				// Explicitly do not check result to avoid having assertions in AfterEach.
				_ = kubectl.Delete(resourcePath)
			}

			for _, policyPath := range policyPaths {
				By("Deleting policy in file %q", policyPath)
				// Explicitly do not check result to avoid having assertions in AfterEach.
				_ = kubectl.Delete(policyPath)
			}
		})

		// shouldConnect checks that srcPod can connect to dstURI.
		shouldConnect := func(srcPod, dstURI string) bool {
			By("Checking that %q can connect to %q", srcPod, dstURI)
			res := kubectl.ExecPodCmd(
				helpers.DefaultNamespace, srcPod, fmt.Sprintf("%s %s", wgetCommand, dstURI))
			if !res.WasSuccessful() {
				GinkgoPrint("Unable to connect from %q to %q: %s", srcPod, dstURI, res.OutputPrettyPrint())
				return false
			}
			return true
		}

		// shouldNotConnect checks that srcPod cannot connect to dstURI.
		shouldNotConnect := func(srcPod, dstURI string) bool {
			By("Checking that %q cannot connect to %q", srcPod, dstURI)
			res := kubectl.ExecPodCmd(
				helpers.DefaultNamespace, srcPod, fmt.Sprintf("%s %s", wgetCommand, dstURI))
			if res.WasSuccessful() {
				GinkgoPrint("Was able to connect from %q to %q, but expected no connection: %s", srcPod, dstURI, res.OutputPrettyPrint())
				return false
			}
			return true
		}

		// formatLabelArgument formats the provided key-value pairs as labels for use in
		// querying Kubernetes.
		formatLabelArgument := func(firstKey, firstValue string, nextLabels ...string) string {
			baseString := fmt.Sprintf("-l %s=%s", firstKey, firstValue)
			if nextLabels == nil {
				return baseString
			} else if len(nextLabels)%2 != 0 {
				Fail("must provide even number of arguments for label key-value pairings")
			} else {
				for i := 0; i < len(nextLabels); i += 2 {
					baseString = fmt.Sprintf("%s,%s=%s", baseString, nextLabels[i], nextLabels[i+1])
				}
			}
			return baseString
		}

		// formatAPI is a helper function which formats a URI to access.
		formatAPI := func(service, port, resource string) string {
			target := fmt.Sprintf(
				"%s.%s.svc.cluster.local:%s",
				service, helpers.DefaultNamespace, port)
			if resource != "" {
				return fmt.Sprintf("%s/%s", target, resource)
			}
			return target
		}

		It("Tests bookinfo inter-service connectivity", func() {
			var err error
			version := "version"
			v1 := "v1"

			productPage := "productpage"
			reviews := "reviews"
			ratings := "ratings"
			details := "details"
			dnsChecks := []string{productPage, reviews, ratings, details}
			app := "app"
			health := "health"
			ratingsPath := "ratings/0"
			apiPort := "9080"
			podNameFilter := "{.items[*].metadata.name}"

			// Those YAML files are the bookinfo-v1.yaml and bookinfo-v2.yaml
			// manifests injected with Istio sidecars using those commands:
			// istioctl kube-inject -f bookinfo-v1.yaml > bookinfo-v1-istio.yaml
			// istioctl kube-inject -f bookinfo-v2.yaml > bookinfo-v2-istio.yaml
			bookinfoV1YAML := helpers.ManifestGet("bookinfo-v1-istio.yaml")
			bookinfoV2YAML := helpers.ManifestGet("bookinfo-v2-istio.yaml")
			l7PolicyPath := helpers.ManifestGet("cnp-specs.yaml")

			waitIstioReady()

			// Create the L7 policy before creating the pods, in order to test
			// that the sidecar proxy mode doesn't deadlock on endpoint
			// creation in this case.
			policyPaths = []string{l7PolicyPath}
			for _, policyPath := range policyPaths {
				By("Creating policy in file %q", policyPath)
				_, err := kubectl.CiliumPolicyAction(helpers.DefaultNamespace, policyPath, helpers.KubectlApply, helpers.HelperTimeout)
				Expect(err).Should(BeNil(), "Unable to create policy %q", policyPath)
			}

			resourceYAMLPaths = []string{bookinfoV2YAML, bookinfoV1YAML}
			for _, resourcePath := range resourceYAMLPaths {
				By("Creating resources in file %q", resourcePath)
				res := kubectl.Create(resourcePath)
				res.ExpectSuccess("Unable to create resource %q", resourcePath)
			}

			// Wait for pods and endpoints to be ready before creating the
			// next resources to reduce the load on the next pod creations,
			// in order to reduce the probability of regeneration timeout.
			By("Waiting for Bookinfo pods to be ready")
			err = kubectl.WaitforPods(helpers.DefaultNamespace, "-l zgroup=bookinfo", helpers.HelperTimeout)
			Expect(err).Should(BeNil(), "Pods are not ready after timeout")

			By("Waiting for Bookinfo endpoints to be ready")
			err = kubectl.CiliumEndpointWaitReady()
			Expect(err).Should(BeNil(), "Endpoints are not ready after timeout")

			for _, service := range []string{details, ratings, reviews, productPage} {
				By("Waiting for Bookinfo service %q to be ready", service)
				err = kubectl.WaitForServiceEndpoints(
					helpers.DefaultNamespace, "", service,
					helpers.HelperTimeout)
				Expect(err).Should(BeNil(), "Service %q is not ready after timeout", service)
			}

			for _, name := range dnsChecks {
				By("Waiting for DNS to resolve Bookinfo service %q", name)
				err = kubectl.WaitForKubeDNSEntry(name, helpers.DefaultNamespace)
				Expect(err).To(BeNil(), "DNS entry is not ready after timeout")
			}

			By("Testing L7 filtering")
			reviewsPodV1, err := kubectl.GetPods(helpers.DefaultNamespace, formatLabelArgument(app, reviews, version, v1)).Filter(podNameFilter)
			Expect(err).Should(BeNil(), "Cannot get reviewsV1 pods")
			productpagePodV1, err := kubectl.GetPods(helpers.DefaultNamespace, formatLabelArgument(app, productPage, version, v1)).Filter(podNameFilter)
			Expect(err).Should(BeNil(), "Cannot get productpageV1 pods")

			// Connectivity checks often need to be repeated because Pilot
			// is eventually consistent, i.e. it may take some time for a
			// sidecar proxy to get updated with the configuration for another
			// new endpoint and it rejects egress traffic with 503s in the
			// meantime.
			err = helpers.WithTimeout(func() bool {
				allGood := true

				allGood = shouldConnect(reviewsPodV1.String(), formatAPI(ratings, apiPort, health)) && allGood
				allGood = shouldNotConnect(reviewsPodV1.String(), formatAPI(ratings, apiPort, ratingsPath)) && allGood

				allGood = shouldConnect(productpagePodV1.String(), formatAPI(details, apiPort, health)) && allGood

				allGood = shouldNotConnect(productpagePodV1.String(), formatAPI(ratings, apiPort, health)) && allGood
				allGood = shouldNotConnect(productpagePodV1.String(), formatAPI(ratings, apiPort, ratingsPath)) && allGood

				return allGood
			}, "Istio sidecar proxies are not configured", &helpers.TimeoutConfig{Timeout: helpers.HelperTimeout})
			Expect(err).Should(BeNil(), "Cannot configure Istio sidecar proxies")
		})
	})
})
<commit_msg>Revert "test: Temporarily disable Istio CI test"<commit_after>// Copyright 2018-2019 Authors of Cilium
//
// 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 k8sTest

import (
	"context"
	"fmt"
	"time"

	. "github.com/cilium/cilium/test/ginkgo-ext"
	"github.com/cilium/cilium/test/helpers"

	. "github.com/onsi/gomega"
)

// This tests the Istio 1.2.2 integration, following the configuration
// instructions specified in the Istio Getting Started Guide in
// Documentation/gettingstarted/istio.rst.
// Changes to the Getting Started Guide may require re-generating or copying
// the following manifests:
// - istio-crds.yaml
// - istio-cilium.yaml
// - bookinfo-v1-istio.yaml
// - bookinfo-v2-istio.yaml
// Cf. the comments below for each manifest.
var _ = Describe("K8sIstioTest", func() {

	var (
		// istioSystemNamespace is the default namespace into which Istio is
		// installed.
		istioSystemNamespace = "istio-system"

		// istioCRDYAMLPath is the file generated from istio-init during a
		// step in Documentation/gettingstarted/istio.rst to setup
		// Istio 1.2.2. In the GSG the file is directly piped to kubectl.
		istioCRDYAMLPath = helpers.ManifestGet("istio-crds.yaml")

		// istioYAMLPath is the istio-cilium.yaml file generated following the
		// instructions in Documentation/gettingstarted/istio.rst to setup
		// Istio 1.2.2. mTLS is enabled.
		istioYAMLPath = helpers.ManifestGet("istio-cilium.yaml")

		// istioServiceNames is the subset of Istio services in the Istio
		// namespace that are accessed from sidecar proxies.
		istioServiceNames = []string{
			// All the services created by Istio are listed here, but only
			// those that we care about are uncommented.
			// "istio-citadel",
			// "istio-galley",
			// "istio-egressgateway",
			"istio-ingressgateway",
			"istio-pilot",
			// "istio-policy",
			// "istio-telemetry",
			// "prometheus",
		}

		// wgetCommand is the command used in this test because the Istio apps
		// do not provide curl.
		wgetCommand = fmt.Sprintf("wget --tries=2 --connect-timeout %d", helpers.CurlConnectTimeout)

		kubectl          *helpers.Kubectl
		microscopeCancel = func() error { return nil }
		uptimeCancel     context.CancelFunc

		teardownTimeout = 10 * time.Minute
	)

	BeforeAll(func() {
		k8sVersion := helpers.GetCurrentK8SEnv()
		switch k8sVersion {
		case "1.7", "1.8", "1.9":
			Skip(fmt.Sprintf("Istio doesn't support K8S %s", k8sVersion))
		}

		kubectl = helpers.CreateKubectl(helpers.K8s1VMName(), logger)
		DeployCiliumAndDNS(kubectl)

		By("Creating the istio-system namespace")
		res := kubectl.NamespaceCreate(istioSystemNamespace)
		res.ExpectSuccess("unable to create namespace %q", istioSystemNamespace)

		By("Creating the Istio resources")

		res = kubectl.Apply(istioCRDYAMLPath)
		res.ExpectSuccess("unable to create Istio CRDs")

		By("Waiting for Istio CRDs to be ready")
		err := kubectl.WaitForCRDCount("istio.io|certmanager.k8s.io", 23, helpers.HelperTimeout)
		Expect(err).To(BeNil(),
			"Istio CRDs are not ready after timeout")

		res = kubectl.Apply(istioYAMLPath)
		res.ExpectSuccess("unable to create Istio resources")
	})

	AfterAll(func() {
		By("Deleting the Istio resources")
		_ = kubectl.Delete(istioYAMLPath)

		By("Deleting the Istio CRDs")
		_ = kubectl.Delete(istioCRDYAMLPath)

		By("Deleting the istio-system namespace")
		_ = kubectl.NamespaceDelete(istioSystemNamespace)

		kubectl.WaitCleanAllTerminatingPods(teardownTimeout)

		kubectl.CloseSSHClient()
	})

	JustBeforeEach(func() {
		var err error
		err, microscopeCancel = kubectl.MicroscopeStart()
		Expect(err).To(BeNil(), "Microscope cannot be started")

		uptimeCancel, err = kubectl.BackgroundReport("uptime")
		Expect(err).To(BeNil(), "Cannot start background report process")
	})

	JustAfterEach(func() {
		Expect(microscopeCancel()).To(BeNil(), "Cannot stop microscope")
		uptimeCancel()

		kubectl.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
	})

	AfterFailed(func() {
		kubectl.CiliumReport(helpers.KubeSystemNamespace,
			"cilium endpoint list",
			"cilium bpf proxy list")
	})

	waitIstioReady := func() {
		// Ignore one-time jobs and Prometheus. All other pods in the
		// namespaces have an "istio" label.
		By("Waiting for Istio pods to be ready")
		err := kubectl.WaitforPods(istioSystemNamespace, "-l istio", helpers.HelperTimeout)
		ExpectWithOffset(1, err).To(BeNil(),
			"Istio pods are not ready after timeout in namespace %q", istioSystemNamespace)

		for _, name := range istioServiceNames {
			By("Waiting for Istio service %q to be ready", name)
			err = kubectl.WaitForServiceEndpoints(
				istioSystemNamespace, "", name, helpers.HelperTimeout)
			ExpectWithOffset(1, err).Should(BeNil(), "Service %q is not ready after timeout", name)
		}

		for _, name := range istioServiceNames {
			By("Waiting for DNS to resolve Istio service %q", name)
			err = kubectl.WaitForKubeDNSEntry(name, istioSystemNamespace)
			ExpectWithOffset(1, err).To(BeNil(), "DNS entry is not ready after timeout")
		}
	}

	// This is a subset of Services's "Bookinfo Demo" test suite, with the pods
	// injected with Istio sidecar proxies and Istio mTLS enabled.
	Context("Istio Bookinfo Demo", func() {

		var (
			resourceYAMLPaths []string
			policyPaths       []string
		)

		AfterEach(func() {
			for _, resourcePath := range resourceYAMLPaths {
				By("Deleting resource in file %q", resourcePath)
				// Explicitly do not check result to avoid having assertions in AfterEach.
				_ = kubectl.Delete(resourcePath)
			}

			for _, policyPath := range policyPaths {
				By("Deleting policy in file %q", policyPath)
				// Explicitly do not check result to avoid having assertions in AfterEach.
				_ = kubectl.Delete(policyPath)
			}
		})

		// shouldConnect checks that srcPod can connect to dstURI.
		shouldConnect := func(srcPod, dstURI string) bool {
			By("Checking that %q can connect to %q", srcPod, dstURI)
			res := kubectl.ExecPodCmd(
				helpers.DefaultNamespace, srcPod, fmt.Sprintf("%s %s", wgetCommand, dstURI))
			if !res.WasSuccessful() {
				GinkgoPrint("Unable to connect from %q to %q: %s", srcPod, dstURI, res.OutputPrettyPrint())
				return false
			}
			return true
		}

		// shouldNotConnect checks that srcPod cannot connect to dstURI.
		shouldNotConnect := func(srcPod, dstURI string) bool {
			By("Checking that %q cannot connect to %q", srcPod, dstURI)
			res := kubectl.ExecPodCmd(
				helpers.DefaultNamespace, srcPod, fmt.Sprintf("%s %s", wgetCommand, dstURI))
			if res.WasSuccessful() {
				GinkgoPrint("Was able to connect from %q to %q, but expected no connection: %s", srcPod, dstURI, res.OutputPrettyPrint())
				return false
			}
			return true
		}

		// formatLabelArgument formats the provided key-value pairs as labels for use in
		// querying Kubernetes.
		formatLabelArgument := func(firstKey, firstValue string, nextLabels ...string) string {
			baseString := fmt.Sprintf("-l %s=%s", firstKey, firstValue)
			if nextLabels == nil {
				return baseString
			} else if len(nextLabels)%2 != 0 {
				Fail("must provide even number of arguments for label key-value pairings")
			} else {
				for i := 0; i < len(nextLabels); i += 2 {
					baseString = fmt.Sprintf("%s,%s=%s", baseString, nextLabels[i], nextLabels[i+1])
				}
			}
			return baseString
		}

		// formatAPI is a helper function which formats a URI to access.
		formatAPI := func(service, port, resource string) string {
			target := fmt.Sprintf(
				"%s.%s.svc.cluster.local:%s",
				service, helpers.DefaultNamespace, port)
			if resource != "" {
				return fmt.Sprintf("%s/%s", target, resource)
			}
			return target
		}

		It("Tests bookinfo inter-service connectivity", func() {
			var err error
			version := "version"
			v1 := "v1"

			productPage := "productpage"
			reviews := "reviews"
			ratings := "ratings"
			details := "details"
			dnsChecks := []string{productPage, reviews, ratings, details}
			app := "app"
			health := "health"
			ratingsPath := "ratings/0"
			apiPort := "9080"
			podNameFilter := "{.items[*].metadata.name}"

			// Those YAML files are the bookinfo-v1.yaml and bookinfo-v2.yaml
			// manifests injected with Istio sidecars using those commands:
			// istioctl kube-inject -f bookinfo-v1.yaml > bookinfo-v1-istio.yaml
			// istioctl kube-inject -f bookinfo-v2.yaml > bookinfo-v2-istio.yaml
			bookinfoV1YAML := helpers.ManifestGet("bookinfo-v1-istio.yaml")
			bookinfoV2YAML := helpers.ManifestGet("bookinfo-v2-istio.yaml")
			l7PolicyPath := helpers.ManifestGet("cnp-specs.yaml")

			waitIstioReady()

			// Create the L7 policy before creating the pods, in order to test
			// that the sidecar proxy mode doesn't deadlock on endpoint
			// creation in this case.
			policyPaths = []string{l7PolicyPath}
			for _, policyPath := range policyPaths {
				By("Creating policy in file %q", policyPath)
				_, err := kubectl.CiliumPolicyAction(helpers.DefaultNamespace, policyPath, helpers.KubectlApply, helpers.HelperTimeout)
				Expect(err).Should(BeNil(), "Unable to create policy %q", policyPath)
			}

			resourceYAMLPaths = []string{bookinfoV2YAML, bookinfoV1YAML}
			for _, resourcePath := range resourceYAMLPaths {
				By("Creating resources in file %q", resourcePath)
				res := kubectl.Create(resourcePath)
				res.ExpectSuccess("Unable to create resource %q", resourcePath)
			}

			// Wait for pods and endpoints to be ready before creating the
			// next resources to reduce the load on the next pod creations,
			// in order to reduce the probability of regeneration timeout.
			By("Waiting for Bookinfo pods to be ready")
			err = kubectl.WaitforPods(helpers.DefaultNamespace, "-l zgroup=bookinfo", helpers.HelperTimeout)
			Expect(err).Should(BeNil(), "Pods are not ready after timeout")

			By("Waiting for Bookinfo endpoints to be ready")
			err = kubectl.CiliumEndpointWaitReady()
			Expect(err).Should(BeNil(), "Endpoints are not ready after timeout")

			for _, service := range []string{details, ratings, reviews, productPage} {
				By("Waiting for Bookinfo service %q to be ready", service)
				err = kubectl.WaitForServiceEndpoints(
					helpers.DefaultNamespace, "", service,
					helpers.HelperTimeout)
				Expect(err).Should(BeNil(), "Service %q is not ready after timeout", service)
			}

			for _, name := range dnsChecks {
				By("Waiting for DNS to resolve Bookinfo service %q", name)
				err = kubectl.WaitForKubeDNSEntry(name, helpers.DefaultNamespace)
				Expect(err).To(BeNil(), "DNS entry is not ready after timeout")
			}

			By("Testing L7 filtering")
			reviewsPodV1, err := kubectl.GetPods(helpers.DefaultNamespace, formatLabelArgument(app, reviews, version, v1)).Filter(podNameFilter)
			Expect(err).Should(BeNil(), "Cannot get reviewsV1 pods")
			productpagePodV1, err := kubectl.GetPods(helpers.DefaultNamespace, formatLabelArgument(app, productPage, version, v1)).Filter(podNameFilter)
			Expect(err).Should(BeNil(), "Cannot get productpageV1 pods")

			// Connectivity checks often need to be repeated because Pilot
			// is eventually consistent, i.e. it may take some time for a
			// sidecar proxy to get updated with the configuration for another
			// new endpoint and it rejects egress traffic with 503s in the
			// meantime.
			err = helpers.WithTimeout(func() bool {
				allGood := true

				allGood = shouldConnect(reviewsPodV1.String(), formatAPI(ratings, apiPort, health)) && allGood
				allGood = shouldNotConnect(reviewsPodV1.String(), formatAPI(ratings, apiPort, ratingsPath)) && allGood

				allGood = shouldConnect(productpagePodV1.String(), formatAPI(details, apiPort, health)) && allGood

				allGood = shouldNotConnect(productpagePodV1.String(), formatAPI(ratings, apiPort, health)) && allGood
				allGood = shouldNotConnect(productpagePodV1.String(), formatAPI(ratings, apiPort, ratingsPath)) && allGood

				return allGood
			}, "Istio sidecar proxies are not configured", &helpers.TimeoutConfig{Timeout: helpers.HelperTimeout})
			Expect(err).Should(BeNil(), "Cannot configure Istio sidecar proxies")
		})
	})
})
<|endoftext|>


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

import (
	"mime"
	"net/http"
	"strconv"
	"strings"

	"bitbucket.org/ww/goautoneg"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// MediaTypesForSerializer returns a list of media and stream media types for the server.
func MediaTypesForSerializer(ns runtime.NegotiatedSerializer) (mediaTypes, streamMediaTypes []string) {
	for _, info := range ns.SupportedMediaTypes() {
		mediaTypes = append(mediaTypes, info.MediaType)
		if info.StreamSerializer != nil {
			// stream=watch is the existing mime-type parameter for watch
			streamMediaTypes = append(streamMediaTypes, info.MediaType+";stream=watch")
		}
	}
	return mediaTypes, streamMediaTypes
}

func NegotiateOutputMediaType(req *http.Request, ns runtime.NegotiatedSerializer, restrictions EndpointRestrictions) (MediaTypeOptions, runtime.SerializerInfo, error) {
	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), restrictions)
	if !ok {
		supported, _ := MediaTypesForSerializer(ns)
		return mediaType, runtime.SerializerInfo{}, NewNotAcceptableError(supported)
	}
	// TODO: move into resthandler
	info := mediaType.Accepted.Serializer
	if (mediaType.Pretty || isPrettyPrint(req)) && info.PrettySerializer != nil {
		info.Serializer = info.PrettySerializer
	}
	return mediaType, info, nil
}

func NegotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	_, info, err := NegotiateOutputMediaType(req, ns, DefaultEndpointRestrictions)
	return info, err
}

func NegotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), DefaultEndpointRestrictions)
	if !ok || mediaType.Accepted.Serializer.StreamSerializer == nil {
		_, supported := MediaTypesForSerializer(ns)
		return runtime.SerializerInfo{}, NewNotAcceptableError(supported)
	}
	return mediaType.Accepted.Serializer, nil
}

func NegotiateInputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	mediaTypes := ns.SupportedMediaTypes()
	mediaType := req.Header.Get("Content-Type")
	if len(mediaType) == 0 {
		mediaType = mediaTypes[0].MediaType
	}
	mediaType, _, err := mime.ParseMediaType(mediaType)
	if err != nil {
		_, supported := MediaTypesForSerializer(ns)
		return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(supported)
	}

	for _, info := range mediaTypes {
		if info.MediaType != mediaType {
			continue
		}
		return info, nil
	}

	_, supported := MediaTypesForSerializer(ns)
	return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(supported)
}

// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
// matches known "human" clients.
func isPrettyPrint(req *http.Request) bool {
	// DEPRECATED: should be part of the content type
	if req.URL != nil {
		pp := req.URL.Query().Get("pretty")
		if len(pp) > 0 {
			pretty, _ := strconv.ParseBool(pp)
			return pretty
		}
	}
	userAgent := req.UserAgent()
	// This covers basic all browers and cli http tools
	if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") {
		return true
	}
	return false
}

// negotiate the most appropriate content type given the accept header and a list of
// alternatives.
func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) {
	alternates := make([][]string, 0, len(alternatives))
	for _, alternate := range alternatives {
		alternates = append(alternates, strings.SplitN(alternate, "/", 2))
	}
	for _, clause := range goautoneg.ParseAccept(header) {
		for _, alternate := range alternates {
			if clause.Type == alternate[0] && clause.SubType == alternate[1] {
				return clause, true
			}
			if clause.Type == alternate[0] && clause.SubType == "*" {
				clause.SubType = alternate[1]
				return clause, true
			}
			if clause.Type == "*" && clause.SubType == "*" {
				clause.Type = alternate[0]
				clause.SubType = alternate[1]
				return clause, true
			}
		}
	}
	return goautoneg.Accept{}, false
}

// EndpointRestrictions is an interface that allows content-type negotiation
// to verify server support for specific options
type EndpointRestrictions interface {
	// AllowsConversion should return true if the specified group version kind
	// is an allowed target object.
	AllowsConversion(schema.GroupVersionKind) bool
	// AllowsServerVersion should return true if the specified version is valid
	// for the server group.
	AllowsServerVersion(version string) bool
	// AllowsStreamSchema should return true if the specified stream schema is
	// valid for the server group.
	AllowsStreamSchema(schema string) bool
}

var DefaultEndpointRestrictions = emptyEndpointRestrictions{}

type emptyEndpointRestrictions struct{}

func (emptyEndpointRestrictions) AllowsConversion(schema.GroupVersionKind) bool { return false }
func (emptyEndpointRestrictions) AllowsServerVersion(string) bool               { return false }
func (emptyEndpointRestrictions) AllowsStreamSchema(s string) bool              { return s == "watch" }

// AcceptedMediaType contains information about a valid media type that the
// server can serialize.
type AcceptedMediaType struct {
	// Type is the first part of the media type ("application")
	Type string
	// SubType is the second part of the media type ("json")
	SubType string
	// Serializer is the serialization info this object accepts
	Serializer runtime.SerializerInfo
}

// MediaTypeOptions describes information for a given media type that may alter
// the server response
type MediaTypeOptions struct {
	// pretty is true if the requested representation should be formatted for human
	// viewing
	Pretty bool

	// stream, if set, indicates that a streaming protocol variant of this encoding
	// is desired. The only currently supported value is watch which returns versioned
	// events. In the future, this may refer to other stream protocols.
	Stream string

	// convert is a request to alter the type of object returned by the server from the
	// normal response
	Convert *schema.GroupVersionKind
	// useServerVersion is an optional version for the server group
	UseServerVersion string

	// export is true if the representation requested should exclude fields the server
	// has set
	Export bool

	// unrecognized is a list of all unrecognized keys
	Unrecognized []string

	// the accepted media type from the client
	Accepted *AcceptedMediaType
}

// acceptMediaTypeOptions returns an options object that matches the provided media type params. If
// it returns false, the provided options are not allowed and the media type must be skipped.  These
// parameters are unversioned and may not be changed.
func acceptMediaTypeOptions(params map[string]string, accepts *AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
	var options MediaTypeOptions

	// extract all known parameters
	for k, v := range params {
		switch k {

		// controls transformation of the object when returned
		case "as":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Kind = v
		case "g":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Group = v
		case "v":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Version = v

		// controls the streaming schema
		case "stream":
			if len(v) > 0 && (accepts.Serializer.StreamSerializer == nil || !endpoint.AllowsStreamSchema(v)) {
				return MediaTypeOptions{}, false
			}
			options.Stream = v

		// controls the version of the server API group used
		// for generic output
		case "sv":
			if len(v) > 0 && !endpoint.AllowsServerVersion(v) {
				return MediaTypeOptions{}, false
			}
			options.UseServerVersion = v

		// if specified, the server should transform the returned
		// output and remove fields that are always server specified,
		// or which fit the default behavior.
		case "export":
			options.Export = v == "1"

		// if specified, the pretty serializer will be used
		case "pretty":
			options.Pretty = v == "1"

		default:
			options.Unrecognized = append(options.Unrecognized, k)
		}
	}

	if options.Convert != nil && !endpoint.AllowsConversion(*options.Convert) {
		return MediaTypeOptions{}, false
	}

	options.Accepted = accepts
	return options, true
}

// NegotiateMediaTypeOptions returns the most appropriate content type given the accept header and
// a list of alternatives along with the accepted media type parameters.
func NegotiateMediaTypeOptions(header string, accepted []AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
	if len(header) == 0 && len(accepted) > 0 {
		return MediaTypeOptions{
			Accepted: &accepted[0],
		}, true
	}

	clauses := goautoneg.ParseAccept(header)
	for _, clause := range clauses {
		for i := range accepted {
			accepts := &accepted[i]
			switch {
			case clause.Type == accepts.Type && clause.SubType == accepts.SubType,
				clause.Type == accepts.Type && clause.SubType == "*",
				clause.Type == "*" && clause.SubType == "*":
				// TODO: should we prefer the first type with no unrecognized options?  Do we need to ignore unrecognized
				// parameters.
				return acceptMediaTypeOptions(clause.Params, accepts, endpoint)
			}
		}
	}
	return MediaTypeOptions{}, false
}

// AcceptedMediaTypesForEndpoint returns an array of structs that are used to efficiently check which
// allowed media types the server exposes.
func AcceptedMediaTypesForEndpoint(ns runtime.NegotiatedSerializer) []AcceptedMediaType {
	var acceptedMediaTypes []AcceptedMediaType
	for _, info := range ns.SupportedMediaTypes() {
		segments := strings.SplitN(info.MediaType, "/", 2)
		if len(segments) == 1 {
			segments = append(segments, "*")
		}
		t := AcceptedMediaType{
			Type:       segments[0],
			SubType:    segments[1],
			Serializer: info,
		}
		acceptedMediaTypes = append(acceptedMediaTypes, t)
	}
	return acceptedMediaTypes
}
<commit_msg>Add a negotiate method media type for use in explicit contexts<commit_after>/*
Copyright 2015 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 negotiation

import (
	"mime"
	"net/http"
	"strconv"
	"strings"

	"bitbucket.org/ww/goautoneg"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
)

// MediaTypesForSerializer returns a list of media and stream media types for the server.
func MediaTypesForSerializer(ns runtime.NegotiatedSerializer) (mediaTypes, streamMediaTypes []string) {
	for _, info := range ns.SupportedMediaTypes() {
		mediaTypes = append(mediaTypes, info.MediaType)
		if info.StreamSerializer != nil {
			// stream=watch is the existing mime-type parameter for watch
			streamMediaTypes = append(streamMediaTypes, info.MediaType+";stream=watch")
		}
	}
	return mediaTypes, streamMediaTypes
}

// NegotiateOutputMediaType negotiates the output structured media type and a serializer, or
// returns an error.
func NegotiateOutputMediaType(req *http.Request, ns runtime.NegotiatedSerializer, restrictions EndpointRestrictions) (MediaTypeOptions, runtime.SerializerInfo, error) {
	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), restrictions)
	if !ok {
		supported, _ := MediaTypesForSerializer(ns)
		return mediaType, runtime.SerializerInfo{}, NewNotAcceptableError(supported)
	}
	// TODO: move into resthandler
	info := mediaType.Accepted.Serializer
	if (mediaType.Pretty || isPrettyPrint(req)) && info.PrettySerializer != nil {
		info.Serializer = info.PrettySerializer
	}
	return mediaType, info, nil
}

// NegotiateOutputSerializer returns a serializer for the output.
func NegotiateOutputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	_, info, err := NegotiateOutputMediaType(req, ns, DefaultEndpointRestrictions)
	return info, err
}

// NegotiateOutputStreamSerializer returns a stream serializer for the given request.
func NegotiateOutputStreamSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	mediaType, ok := NegotiateMediaTypeOptions(req.Header.Get("Accept"), AcceptedMediaTypesForEndpoint(ns), DefaultEndpointRestrictions)
	if !ok || mediaType.Accepted.Serializer.StreamSerializer == nil {
		_, supported := MediaTypesForSerializer(ns)
		return runtime.SerializerInfo{}, NewNotAcceptableError(supported)
	}
	return mediaType.Accepted.Serializer, nil
}

// NegotiateInputSerializer returns the input serializer for the provided request.
func NegotiateInputSerializer(req *http.Request, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	mediaType := req.Header.Get("Content-Type")
	return NegotiateInputSerializerForMediaType(mediaType, ns)
}

// NegotiateInputSerializerForMediaType returns the appropriate serializer for the given media type or an error.
func NegotiateInputSerializerForMediaType(mediaType string, ns runtime.NegotiatedSerializer) (runtime.SerializerInfo, error) {
	mediaTypes := ns.SupportedMediaTypes()
	if len(mediaType) == 0 {
		mediaType = mediaTypes[0].MediaType
	}
	mediaType, _, err := mime.ParseMediaType(mediaType)
	if err != nil {
		_, supported := MediaTypesForSerializer(ns)
		return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(supported)
	}

	for _, info := range mediaTypes {
		if info.MediaType != mediaType {
			continue
		}
		return info, nil
	}

	_, supported := MediaTypesForSerializer(ns)
	return runtime.SerializerInfo{}, NewUnsupportedMediaTypeError(supported)
}

// isPrettyPrint returns true if the "pretty" query parameter is true or if the User-Agent
// matches known "human" clients.
func isPrettyPrint(req *http.Request) bool {
	// DEPRECATED: should be part of the content type
	if req.URL != nil {
		pp := req.URL.Query().Get("pretty")
		if len(pp) > 0 {
			pretty, _ := strconv.ParseBool(pp)
			return pretty
		}
	}
	userAgent := req.UserAgent()
	// This covers basic all browers and cli http tools
	if strings.HasPrefix(userAgent, "curl") || strings.HasPrefix(userAgent, "Wget") || strings.HasPrefix(userAgent, "Mozilla/5.0") {
		return true
	}
	return false
}

// negotiate the most appropriate content type given the accept header and a list of
// alternatives.
func negotiate(header string, alternatives []string) (goautoneg.Accept, bool) {
	alternates := make([][]string, 0, len(alternatives))
	for _, alternate := range alternatives {
		alternates = append(alternates, strings.SplitN(alternate, "/", 2))
	}
	for _, clause := range goautoneg.ParseAccept(header) {
		for _, alternate := range alternates {
			if clause.Type == alternate[0] && clause.SubType == alternate[1] {
				return clause, true
			}
			if clause.Type == alternate[0] && clause.SubType == "*" {
				clause.SubType = alternate[1]
				return clause, true
			}
			if clause.Type == "*" && clause.SubType == "*" {
				clause.Type = alternate[0]
				clause.SubType = alternate[1]
				return clause, true
			}
		}
	}
	return goautoneg.Accept{}, false
}

// EndpointRestrictions is an interface that allows content-type negotiation
// to verify server support for specific options
type EndpointRestrictions interface {
	// AllowsConversion should return true if the specified group version kind
	// is an allowed target object.
	AllowsConversion(schema.GroupVersionKind) bool
	// AllowsServerVersion should return true if the specified version is valid
	// for the server group.
	AllowsServerVersion(version string) bool
	// AllowsStreamSchema should return true if the specified stream schema is
	// valid for the server group.
	AllowsStreamSchema(schema string) bool
}

var DefaultEndpointRestrictions = emptyEndpointRestrictions{}

type emptyEndpointRestrictions struct{}

func (emptyEndpointRestrictions) AllowsConversion(schema.GroupVersionKind) bool { return false }
func (emptyEndpointRestrictions) AllowsServerVersion(string) bool               { return false }
func (emptyEndpointRestrictions) AllowsStreamSchema(s string) bool              { return s == "watch" }

// AcceptedMediaType contains information about a valid media type that the
// server can serialize.
type AcceptedMediaType struct {
	// Type is the first part of the media type ("application")
	Type string
	// SubType is the second part of the media type ("json")
	SubType string
	// Serializer is the serialization info this object accepts
	Serializer runtime.SerializerInfo
}

// MediaTypeOptions describes information for a given media type that may alter
// the server response
type MediaTypeOptions struct {
	// pretty is true if the requested representation should be formatted for human
	// viewing
	Pretty bool

	// stream, if set, indicates that a streaming protocol variant of this encoding
	// is desired. The only currently supported value is watch which returns versioned
	// events. In the future, this may refer to other stream protocols.
	Stream string

	// convert is a request to alter the type of object returned by the server from the
	// normal response
	Convert *schema.GroupVersionKind
	// useServerVersion is an optional version for the server group
	UseServerVersion string

	// export is true if the representation requested should exclude fields the server
	// has set
	Export bool

	// unrecognized is a list of all unrecognized keys
	Unrecognized []string

	// the accepted media type from the client
	Accepted *AcceptedMediaType
}

// acceptMediaTypeOptions returns an options object that matches the provided media type params. If
// it returns false, the provided options are not allowed and the media type must be skipped.  These
// parameters are unversioned and may not be changed.
func acceptMediaTypeOptions(params map[string]string, accepts *AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
	var options MediaTypeOptions

	// extract all known parameters
	for k, v := range params {
		switch k {

		// controls transformation of the object when returned
		case "as":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Kind = v
		case "g":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Group = v
		case "v":
			if options.Convert == nil {
				options.Convert = &schema.GroupVersionKind{}
			}
			options.Convert.Version = v

		// controls the streaming schema
		case "stream":
			if len(v) > 0 && (accepts.Serializer.StreamSerializer == nil || !endpoint.AllowsStreamSchema(v)) {
				return MediaTypeOptions{}, false
			}
			options.Stream = v

		// controls the version of the server API group used
		// for generic output
		case "sv":
			if len(v) > 0 && !endpoint.AllowsServerVersion(v) {
				return MediaTypeOptions{}, false
			}
			options.UseServerVersion = v

		// if specified, the server should transform the returned
		// output and remove fields that are always server specified,
		// or which fit the default behavior.
		case "export":
			options.Export = v == "1"

		// if specified, the pretty serializer will be used
		case "pretty":
			options.Pretty = v == "1"

		default:
			options.Unrecognized = append(options.Unrecognized, k)
		}
	}

	if options.Convert != nil && !endpoint.AllowsConversion(*options.Convert) {
		return MediaTypeOptions{}, false
	}

	options.Accepted = accepts
	return options, true
}

// NegotiateMediaTypeOptions returns the most appropriate content type given the accept header and
// a list of alternatives along with the accepted media type parameters.
func NegotiateMediaTypeOptions(header string, accepted []AcceptedMediaType, endpoint EndpointRestrictions) (MediaTypeOptions, bool) {
	if len(header) == 0 && len(accepted) > 0 {
		return MediaTypeOptions{
			Accepted: &accepted[0],
		}, true
	}

	clauses := goautoneg.ParseAccept(header)
	for _, clause := range clauses {
		for i := range accepted {
			accepts := &accepted[i]
			switch {
			case clause.Type == accepts.Type && clause.SubType == accepts.SubType,
				clause.Type == accepts.Type && clause.SubType == "*",
				clause.Type == "*" && clause.SubType == "*":
				// TODO: should we prefer the first type with no unrecognized options?  Do we need to ignore unrecognized
				// parameters.
				return acceptMediaTypeOptions(clause.Params, accepts, endpoint)
			}
		}
	}
	return MediaTypeOptions{}, false
}

// AcceptedMediaTypesForEndpoint returns an array of structs that are used to efficiently check which
// allowed media types the server exposes.
func AcceptedMediaTypesForEndpoint(ns runtime.NegotiatedSerializer) []AcceptedMediaType {
	var acceptedMediaTypes []AcceptedMediaType
	for _, info := range ns.SupportedMediaTypes() {
		segments := strings.SplitN(info.MediaType, "/", 2)
		if len(segments) == 1 {
			segments = append(segments, "*")
		}
		t := AcceptedMediaType{
			Type:       segments[0],
			SubType:    segments[1],
			Serializer: info,
		}
		acceptedMediaTypes = append(acceptedMediaTypes, t)
	}
	return acceptedMediaTypes
}
<|endoftext|>


<commit_before>package sawyer

import (
	"github.com/bmizerany/assert"
	"net/url"
	"testing"
)

var endpoints = map[string]map[string]string{
	"http://api.github.com": map[string]string{
		"user":                "http://api.github.com/user",
		"/user":               "http://api.github.com/user",
		"http://api.com/user": "http://api.com/user",
	},
	"http://api.github.com/api/v1": map[string]string{
		"user":                "http://api.github.com/api/v1/user",
		"/user":               "http://api.github.com/user",
		"http://api.com/user": "http://api.com/user",
	},
}

func TestResolve(t *testing.T) {
	for endpoint, tests := range endpoints {
		client, err := NewFromString(endpoint, nil)
		if err != nil {
			t.Fatal(err.Error())
		}

		for relative, result := range tests {
			u, err := url.Parse(relative)
			if err != nil {
				t.Error(err.Error())
				break
			}

			abs := client.ResolveReference(u)
			if absurl := abs.String(); result != absurl {
				t.Errorf("Bad absolute URL %s for %s + %s == %s", absurl, endpoint, relative, result)
			}
		}
	}
}

func TestResolveWithNoHeader(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	req, _ := client.NewRequest("")
	assert.Equal(t, 0, len(req.Header))

	req.Header.Set("Cache-Control", "private")
	assert.Equal(t, 1, len(req.Header))
	assert.Equal(t, 0, len(client.Header))
}

func TestResolveWithHeader(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	client.Header.Set("Cache-Control", "private")

	req, _ := client.NewRequest("")
	assert.Equal(t, 1, len(req.Header))
	assert.Equal(t, "private", req.Header.Get("Cache-Control"))
}

func TestResolveClientQuery(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	u, err := client.ResolveReferenceString("/foo?a=1")
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "http://api.github.com/foo?a=1", u)
}

func TestResolveClientQueryWithClientQuery(t *testing.T) {
	client, err := NewFromString("http://api.github.com?a=1&b=1", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "1", client.Query.Get("a"))
	assert.Equal(t, "1", client.Query.Get("b"))

	client.Query.Set("b", "2")
	client.Query.Set("c", "3")
	u, err := client.ResolveReferenceString("/foo?d=4")
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "http://api.github.com/foo?a=1&b=2&c=3&d=4", u)
}
<commit_msg>Make sure we resolve relative dependencies properly.<commit_after>package sawyer

import (
	"github.com/bmizerany/assert"
	"net/url"
	"testing"
)

var endpoints = map[string]map[string]string{
	"http://api.github.com": map[string]string{
		"user":                "http://api.github.com/user",
		"/user":               "http://api.github.com/user",
		"http://api.com/user": "http://api.com/user",
	},
	"http://api.github.com/api/v1": map[string]string{
		"user":                "http://api.github.com/api/v1/user",
		"/user":               "http://api.github.com/user",
		"http://api.com/user": "http://api.com/user",
	},
}

func TestResolve(t *testing.T) {
	for endpoint, tests := range endpoints {
		client, err := NewFromString(endpoint, nil)
		if err != nil {
			t.Fatal(err.Error())
		}

		for relative, result := range tests {
			u, err := url.Parse(relative)
			if err != nil {
				t.Error(err.Error())
				break
			}

			abs := client.ResolveReference(u)
			if absurl := abs.String(); result != absurl {
				t.Errorf("Bad absolute URL %s for %s + %s == %s", absurl, endpoint, relative, result)
			}
		}
	}
}

func TestResolveWithNoHeader(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	req, _ := client.NewRequest("")
	assert.Equal(t, 0, len(req.Header))

	req.Header.Set("Cache-Control", "private")
	assert.Equal(t, 1, len(req.Header))
	assert.Equal(t, 0, len(client.Header))
}

func TestResolveWithHeader(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	client.Header.Set("Cache-Control", "private")

	req, _ := client.NewRequest("")
	assert.Equal(t, 1, len(req.Header))
	assert.Equal(t, "private", req.Header.Get("Cache-Control"))
}

func TestResolveClientQuery(t *testing.T) {
	client, err := NewFromString("http://api.github.com", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	u, err := client.ResolveReferenceString("/foo?a=1")
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "http://api.github.com/foo?a=1", u)
}

func TestResolveClientQueryWithClientQuery(t *testing.T) {
	client, err := NewFromString("http://api.github.com?a=1&b=1", nil)
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "1", client.Query.Get("a"))
	assert.Equal(t, "1", client.Query.Get("b"))

	client.Query.Set("b", "2")
	client.Query.Set("c", "3")
	u, err := client.ResolveReferenceString("/foo?d=4")
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "http://api.github.com/foo?a=1&b=2&c=3&d=4", u)
}

func TestResolveClientRelativeReference(t *testing.T) {
	client, err := NewFromString("http://github.enterprise.com/api/v3/", nil)
	if err != nil {
		t.Fatal(err.Error())
	}
	u, err := client.ResolveReferenceString("users")
	if err != nil {
		t.Fatal(err.Error())
	}

	assert.Equal(t, "http://github.enterprise.com/api/v3/users", u)
}
<|endoftext|>


<commit_before>package imagecontroller

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"strconv"

	"github.com/jademcosta/melanite/config"
	"github.com/jademcosta/melanite/converter"
	"github.com/jademcosta/melanite/resizer"
)

type ImageController struct {
	config config.Config
}

func New(config config.Config) *ImageController {
	return &ImageController{config: config}
}

func (controller *ImageController) ServeHTTP(rw http.ResponseWriter,
	r *http.Request) {

	filePath := r.URL.Path
	if filePath == "/" {
		rw.WriteHeader(http.StatusBadRequest)
		return
	}

	url := fmt.Sprintf("%s%s", controller.config.ImageSource, filePath)

	response, err := getImage(&url)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	if externalImageNotFound(response) {
		rw.WriteHeader(http.StatusNotFound)
		return
	}

	defer response.Body.Close()

	imgAsBytes, err := decodeImageFromBody(&response.Body)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	if output, ok := r.URL.Query()["o"]; ok && len(output) > 0 {
		outputFormat := output[0]
		if !converter.IsValidImageEncoding(outputFormat) {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		imgAsBytes, err = converter.Convert(*imgAsBytes, outputFormat)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	if resizeParam, ok := r.URL.Query()["r"]; ok && len(resizeParam) > 0 {
		resizeDimensions := resizeParam[0]

		*imgAsBytes, err = resizer.Resize(*imgAsBytes, resizeDimensions)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	rw.Header().Add("Content-Length", strconv.Itoa(len(*imgAsBytes)))
	rw.Header().Add("Content-Type", http.DetectContentType(*imgAsBytes))
	rw.Write(*imgAsBytes)
}

func getImage(url *string) (*http.Response, error) {
	return http.Get(*url)
}

func decodeImageFromBody(body *io.ReadCloser) (*[]byte, error) {
	buf := &bytes.Buffer{}
	_, err := buf.ReadFrom(*body)

	if err != nil {
		return nil, err
	}

	b := buf.Bytes()
	return &b, nil
}

func externalImageNotFound(response *http.Response) bool {
	return response.StatusCode == http.StatusNotFound
}
<commit_msg>Return the status code of upstream if it is not 200<commit_after>package imagecontroller

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"strconv"

	"github.com/jademcosta/melanite/config"
	"github.com/jademcosta/melanite/converter"
	"github.com/jademcosta/melanite/resizer"
)

type ImageController struct {
	config config.Config
}

func New(config config.Config) *ImageController {
	return &ImageController{config: config}
}

func (controller *ImageController) ServeHTTP(rw http.ResponseWriter,
	r *http.Request) {

	filePath := r.URL.Path
	emptyFilePath := "/"
	if filePath == emptyFilePath {
		rw.WriteHeader(http.StatusBadRequest)
		return
	}

	url := fmt.Sprintf("%s%s", controller.config.ImageSource, filePath)

	response, err := getImage(&url)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	if response.StatusCode != http.StatusOK {
		rw.WriteHeader(response.StatusCode)
		return
	}

	defer response.Body.Close()

	imgAsBytes, err := decodeImageFromBody(&response.Body)
	if err != nil {
		rw.WriteHeader(http.StatusInternalServerError)
		return
	}

	if output, ok := r.URL.Query()["o"]; ok && len(output) > 0 {
		outputFormat := output[0]
		if !converter.IsValidImageEncoding(outputFormat) {
			rw.WriteHeader(http.StatusBadRequest)
			return
		}

		imgAsBytes, err = converter.Convert(*imgAsBytes, outputFormat)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	if resizeParam, ok := r.URL.Query()["r"]; ok && len(resizeParam) > 0 {
		resizeDimensions := resizeParam[0]

		*imgAsBytes, err = resizer.Resize(*imgAsBytes, resizeDimensions)
		if err != nil {
			rw.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	rw.Header().Add("Content-Length", strconv.Itoa(len(*imgAsBytes)))
	rw.Header().Add("Content-Type", http.DetectContentType(*imgAsBytes))
	rw.Write(*imgAsBytes)
}

func getImage(url *string) (*http.Response, error) {
	return http.Get(*url)
}

func decodeImageFromBody(body *io.ReadCloser) (*[]byte, error) {
	buf := &bytes.Buffer{}
	_, err := buf.ReadFrom(*body)

	if err != nil {
		return nil, err
	}

	b := buf.Bytes()
	return &b, nil
}
<|endoftext|>