<commit_before>//
// Copyright (c) 2016 Intel Corporation
//
// 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 main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"text/template"

	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
	"github.com/intel/tfortools"
)

var imageCommand = &command{
	SubCommands: map[string]subCommand{
		"add":    new(imageAddCommand),
		"show":   new(imageShowCommand),
		"list":   new(imageListCommand),
		"delete": new(imageDeleteCommand),
	},
}

type imageAddCommand struct {
	Flag       flag.FlagSet
	name       string
	id         string
	file       string
	template   string
	tags       string
	visibility string
}

const (
	internalImage images.ImageVisibility = "internal"
)

func (cmd *imageAddCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image add [flags]

Creates a new image

The add flags are:

`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\n%s", tfortools.GenerateUsageDecorated("f", images.Image{}, nil))
	os.Exit(2)
}

func (cmd *imageAddCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.name, "name", "", "Image Name")
	cmd.Flag.StringVar(&cmd.id, "id", "", "Image UUID")
	cmd.Flag.StringVar(&cmd.file, "file", "", "Image file to upload")
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.StringVar(&cmd.visibility, "visibility", string(images.ImageVisibilityPrivate),
		"Image visibility (internal,public,private)")
	cmd.Flag.StringVar(&cmd.tags, "tag", "", "Image tags (comma separated)")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageAddCommand) run(args []string) error {
	if cmd.name == "" {
		return errors.New("Missing required -name parameter")
	}

	if cmd.file == "" {
		return errors.New("Missing required -file parameter")
	}

	_, err := os.Stat(cmd.file)
	if err != nil {
		fatalf("Could not open %s [%s]\n", cmd.file, err)
	}

	imageVisibility := images.ImageVisibilityPrivate
	if cmd.visibility != "" {
		imageVisibility = images.ImageVisibility(cmd.visibility)
		switch imageVisibility {
		case images.ImageVisibilityPublic, images.ImageVisibilityPrivate, internalImage:
		default:
			fatalf("Invalid image visibility [%v]", imageVisibility)
		}
	}

	tags := strings.Split(cmd.tags, ",")

	opts := images.CreateOpts{
		Name:       cmd.name,
		ID:         cmd.id,
		Visibility: &imageVisibility,
		Tags:       tags,
	}

	b, err := json.Marshal(opts)
	if err != nil {
		fatalf(err.Error())
	}

	body := bytes.NewReader(b)
	url := buildImageURL("images")
	resp, err := sendHTTPRequest("POST", url, nil, body)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusCreated {
		fatalf("Image creation failed: %s", resp.Status)
	}

	image := new(images.Image)
	err = unmarshalHTTPResponse(resp, image)
	if err != nil {
		fatalf(err.Error())
	}

	uploadTenantImage(*tenantID, image.ID, cmd.file)

	url = buildImageURL("images/%s", image.ID)
	resp, err = sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}

	if resp.StatusCode != http.StatusOK {
		fatalf("Image get failed: %s", resp.Status)
	}

	err = unmarshalHTTPResponse(resp, image)
	if err != nil {
		fatalf(err.Error())
	}

	if cmd.template != "" {
		return tfortools.OutputToTemplate(os.Stdout, "image-add", cmd.template, image, nil)
	}

	fmt.Printf("Created image:\n")
	dumpImage(image)
	return nil
}

type imageShowCommand struct {
	Flag     flag.FlagSet
	image    string
	template string
}

func (cmd *imageShowCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image show

Show images
`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\n%s", tfortools.GenerateUsageDecorated("f", images.Image{}, nil))
	os.Exit(2)
}

func (cmd *imageShowCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.StringVar(&cmd.image, "image", "", "Image UUID")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageShowCommand) run(args []string) error {
	if cmd.image == "" {
		return errors.New("Missing required -image parameter")
	}

	url := buildImageURL("images/%s", cmd.image)
	resp, err := sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		fatalf("Image show failed: %s", resp.Status)
	}

	var i images.Image

	err = unmarshalHTTPResponse(resp, &i)
	if err != nil {
		fatalf(err.Error())
	}

	if cmd.template != "" {
		return tfortools.OutputToTemplate(os.Stdout, "image-show", cmd.template, i, nil)
	}

	dumpImage(&i)

	return nil
}

type imageListCommand struct {
	Flag     flag.FlagSet
	template string
}

func (cmd *imageListCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image list

List images
`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, `
The template passed to the -f option operates on a

%s

As images are retrieved in pages, the template may be applied multiple
times.  You can not therefore rely on the length of the slice passed
to the template to determine the total number of images.
`, tfortools.GenerateUsageUndecorated([]images.Image{}))
	fmt.Fprintln(os.Stderr, tfortools.TemplateFunctionHelp(nil))
	os.Exit(2)
}

func (cmd *imageListCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageListCommand) run(args []string) error {
	var t *template.Template
	var err error
	if cmd.template != "" {
		t, err = tfortools.CreateTemplate("image-list", cmd.template, nil)
		if err != nil {
			fatalf(err.Error())
		}
	}

	url := buildImageURL("images")
	resp, err := sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		fatalf("Image list failed: %s", resp.Status)
	}

	var images = struct {
		Images []images.Image
	}{}

	err = unmarshalHTTPResponse(resp, &images)
	if err != nil {
		fatalf(err.Error())
	}

	allImages := images.Images
	if t != nil {
		if err = t.Execute(os.Stdout, &allImages); err != nil {
			fatalf(err.Error())
		}
		return nil
	}

	for k, i := range allImages {
		fmt.Printf("Image #%d\n", k+1)
		dumpImage(&i)
		fmt.Printf("\n")
	}

	return err
}

type imageDeleteCommand struct {
	Flag  flag.FlagSet
	image string
}

func (cmd *imageDeleteCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image delete [flags]

Deletes an image

The delete flags are:

`)
	cmd.Flag.PrintDefaults()
	os.Exit(2)
}

func (cmd *imageDeleteCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.image, "image", "", "Image UUID")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageDeleteCommand) run(args []string) error {
	url := buildImageURL("images/%s", cmd.image)
	resp, err := sendHTTPRequest("DELETE", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}

	if resp.StatusCode != http.StatusNoContent {
		fatalf("Image delete failed: %s", resp.Status)
	}

	fmt.Printf("Deleted image %s\n", cmd.image)

	return nil
}

func uploadTenantImage(tenant, image, filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		fatalf("Could not open %s [%s]", filename, err)
	}
	defer file.Close()

	url := buildImageURL("images/%s/file", image)
	resp, err := sendHTTPRequestToken("PUT", url, nil, scopedToken, file, "octet-stream")
	defer func() { _ = resp.Body.Close() }()

	return err
}

func dumpImage(i *images.Image) {
	fmt.Printf("\tName             [%s]\n", i.Name)
	fmt.Printf("\tSize             [%d bytes]\n", i.SizeBytes)
	fmt.Printf("\tUUID             [%s]\n", i.ID)
	fmt.Printf("\tStatus           [%s]\n", i.Status)
	fmt.Printf("\tVisibility       [%s]\n", i.Visibility)
	fmt.Printf("\tTags             %v\n", i.Tags)
	fmt.Printf("\tCreatedAt        [%s]\n", i.CreatedAt)
}
<commit_msg>ciao-cli: Stop using gophercloud datatypes for image operations<commit_after>//
// Copyright (c) 2016 Intel Corporation
//
// 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 main

import (
	"bytes"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"net/http"
	"os"
	"strings"
	"text/template"

	"github.com/ciao-project/ciao/openstack/image"
	"github.com/intel/tfortools"
)

var imageCommand = &command{
	SubCommands: map[string]subCommand{
		"add":    new(imageAddCommand),
		"show":   new(imageShowCommand),
		"list":   new(imageListCommand),
		"delete": new(imageDeleteCommand),
	},
}

type imageAddCommand struct {
	Flag       flag.FlagSet
	name       string
	id         string
	file       string
	template   string
	tags       string
	visibility string
}

func (cmd *imageAddCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image add [flags]

Creates a new image

The add flags are:

`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\n%s", tfortools.GenerateUsageDecorated("f", image.DefaultResponse{}, nil))
	os.Exit(2)
}

func (cmd *imageAddCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.name, "name", "", "Image Name")
	cmd.Flag.StringVar(&cmd.id, "id", "", "Image UUID")
	cmd.Flag.StringVar(&cmd.file, "file", "", "Image file to upload")
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.StringVar(&cmd.visibility, "visibility", string(image.Private),
		"Image visibility (internal,public,private)")
	cmd.Flag.StringVar(&cmd.tags, "tag", "", "Image tags (comma separated)")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageAddCommand) run(args []string) error {
	if cmd.name == "" {
		return errors.New("Missing required -name parameter")
	}

	if cmd.file == "" {
		return errors.New("Missing required -file parameter")
	}

	_, err := os.Stat(cmd.file)
	if err != nil {
		fatalf("Could not open %s [%s]\n", cmd.file, err)
	}

	imageVisibility := image.Private
	if cmd.visibility != "" {
		imageVisibility = image.Visibility(cmd.visibility)
		switch imageVisibility {
		case image.Public, image.Private, image.Internal:
		default:
			fatalf("Invalid image visibility [%v]", imageVisibility)
		}
	}

	tags := strings.Split(cmd.tags, ",")

	opts := image.CreateImageRequest{
		Name:       cmd.name,
		ID:         cmd.id,
		Visibility: imageVisibility,
		Tags:       tags,
	}

	b, err := json.Marshal(opts)
	if err != nil {
		fatalf(err.Error())
	}

	body := bytes.NewReader(b)
	url := buildImageURL("images")
	resp, err := sendHTTPRequest("POST", url, nil, body)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusCreated {
		fatalf("Image creation failed: %s", resp.Status)
	}

	var image image.DefaultResponse
	err = unmarshalHTTPResponse(resp, &image)
	if err != nil {
		fatalf(err.Error())
	}

	uploadTenantImage(*tenantID, image.ID, cmd.file)

	url = buildImageURL("images/%s", image.ID)
	resp, err = sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}

	if resp.StatusCode != http.StatusOK {
		fatalf("Image get failed: %s", resp.Status)
	}

	err = unmarshalHTTPResponse(resp, &image)
	if err != nil {
		fatalf(err.Error())
	}

	if cmd.template != "" {
		return tfortools.OutputToTemplate(os.Stdout, "image-add", cmd.template, image, nil)
	}

	fmt.Printf("Created image:\n")
	dumpImage(&image)
	return nil
}

type imageShowCommand struct {
	Flag     flag.FlagSet
	image    string
	template string
}

func (cmd *imageShowCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image show

Show images
`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\n%s", tfortools.GenerateUsageDecorated("f", image.DefaultResponse{}, nil))
	os.Exit(2)
}

func (cmd *imageShowCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.StringVar(&cmd.image, "image", "", "Image UUID")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageShowCommand) run(args []string) error {
	if cmd.image == "" {
		return errors.New("Missing required -image parameter")
	}

	url := buildImageURL("images/%s", cmd.image)
	resp, err := sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		fatalf("Image show failed: %s", resp.Status)
	}

	var i image.DefaultResponse

	err = unmarshalHTTPResponse(resp, &i)
	if err != nil {
		fatalf(err.Error())
	}

	if cmd.template != "" {
		return tfortools.OutputToTemplate(os.Stdout, "image-show", cmd.template, i, nil)
	}

	dumpImage(&i)

	return nil
}

type imageListCommand struct {
	Flag     flag.FlagSet
	template string
}

func (cmd *imageListCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image list

List images
`)
	cmd.Flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, `
The template passed to the -f option operates on a

%s

As images are retrieved in pages, the template may be applied multiple
times.  You can not therefore rely on the length of the slice passed
to the template to determine the total number of images.
`, tfortools.GenerateUsageUndecorated([]image.DefaultResponse{}))
	fmt.Fprintln(os.Stderr, tfortools.TemplateFunctionHelp(nil))
	os.Exit(2)
}

func (cmd *imageListCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.template, "f", "", "Template used to format output")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageListCommand) run(args []string) error {
	var t *template.Template
	var err error
	if cmd.template != "" {
		t, err = tfortools.CreateTemplate("image-list", cmd.template, nil)
		if err != nil {
			fatalf(err.Error())
		}
	}

	url := buildImageURL("images")
	resp, err := sendHTTPRequest("GET", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		fatalf("Image list failed: %s", resp.Status)
	}

	var images = struct {
		Images []image.DefaultResponse
	}{}

	err = unmarshalHTTPResponse(resp, &images)
	if err != nil {
		fatalf(err.Error())
	}

	if t != nil {
		if err = t.Execute(os.Stdout, &images.Images); err != nil {
			fatalf(err.Error())
		}
		return nil
	}

	for k, i := range images.Images {
		fmt.Printf("Image #%d\n", k+1)
		dumpImage(&i)
		fmt.Printf("\n")
	}

	return err
}

type imageDeleteCommand struct {
	Flag  flag.FlagSet
	image string
}

func (cmd *imageDeleteCommand) usage(...string) {
	fmt.Fprintf(os.Stderr, `usage: ciao-cli [options] image delete [flags]

Deletes an image

The delete flags are:

`)
	cmd.Flag.PrintDefaults()
	os.Exit(2)
}

func (cmd *imageDeleteCommand) parseArgs(args []string) []string {
	cmd.Flag.StringVar(&cmd.image, "image", "", "Image UUID")
	cmd.Flag.Usage = func() { cmd.usage() }
	cmd.Flag.Parse(args)
	return cmd.Flag.Args()
}

func (cmd *imageDeleteCommand) run(args []string) error {
	url := buildImageURL("images/%s", cmd.image)
	resp, err := sendHTTPRequest("DELETE", url, nil, nil)
	if err != nil {
		fatalf(err.Error())
	}

	if resp.StatusCode != http.StatusNoContent {
		fatalf("Image delete failed: %s", resp.Status)
	}

	fmt.Printf("Deleted image %s\n", cmd.image)

	return nil
}

func uploadTenantImage(tenant, image, filename string) error {
	file, err := os.Open(filename)
	if err != nil {
		fatalf("Could not open %s [%s]", filename, err)
	}
	defer file.Close()

	url := buildImageURL("images/%s/file", image)
	resp, err := sendHTTPRequestToken("PUT", url, nil, scopedToken, file, "octet-stream")
	defer func() { _ = resp.Body.Close() }()

	return err
}

func dumpImage(i *image.DefaultResponse) {
	fmt.Printf("\tName             [%s]\n", *i.Name)
	fmt.Printf("\tSize             [%d bytes]\n", i.Size)
	fmt.Printf("\tUUID             [%s]\n", i.ID)
	fmt.Printf("\tStatus           [%s]\n", i.Status)
	fmt.Printf("\tVisibility       [%s]\n", i.Visibility)
	fmt.Printf("\tTags             %v\n", i.Tags)
	fmt.Printf("\tCreatedAt        [%s]\n", i.CreatedAt)
}
<|endoftext|>


<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

/*
 * Copyright (c) 2011 Alexander Færøy <ahf@0x90.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
    "net"
)

func LookupAddress(ip string) chan string {
    r := make(chan string)

    go func() {
        names, error := net.LookupAddr(ip)

        if error != nil {
            // Error? Use the IP.
            r<-ip
        }

        if len(names) > 0 {
            // Use the first value.
            r<-names[0]
        } else {
            // Use the IP
            r<-ip
        }
    }()

    return r
}
<commit_msg>Strip leading "." from rDNS lookups, if they are there.<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

/*
 * Copyright (c) 2011 Alexander Færøy <ahf@0x90.dk>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package main

import (
    "net"
)

func LookupAddress(ip string) chan string {
    r := make(chan string)

    go func() {
        names, error := net.LookupAddr(ip)

        if error != nil {
            // Error? Use the IP.
            r<-ip
        }

        if len(names) > 0 {
            // Use the first value, but strip the leading ".".
            host := names[0]
            l := len(host)

            if host[l - 1] == '.' {
                r<-host[0:l - 1]
            } else {
                r<-host
            }
        } else {
            // Use the IP
            r<-ip
        }
    }()

    return r
}
<|endoftext|>


<commit_before>// Copyright 2013 Sonia Keys.
// Licensed under MIT license.  See "license" file in this source tree.

// Quant provides an interface for image color quantizers.
package quant

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// Quantizer defines a color quantizer for images.
type Quantizer interface {
	// Image quantizes an image and returns a paletted image
	Image(image.Image) *image.Paletted
	// Palette quantizes an image and returns a Palette.  Note the type is
	// the Palette of this package and not image.Palette.
	Palette(image.Image) Palette
}

// Palette is a palette of color.Colors, just as color.Pallete of the standard
// library.
//
// It is defined as an interface here to allow more general implementations
// of Index, presumably ones that maintain some data structure to achieve
// performance advantages over linear search.
type Palette interface {
	Convert(color.Color) color.Color
	Index(color.Color) int
	ColorPalette() color.Palette
}

// LinearPalette implements the Palette interface with color.Palette
// and has no optimizations.
type LinearPalette struct {
	color.Palette
}

func (p LinearPalette) ColorPalette() color.Palette {
	return p.Palette
}

type Dither211 struct{}

// Dither211 satisfies draw.Drawer
var _ draw.Drawer = Dither211{}

func (d Dither211) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
	pd, ok := dst.(*image.Paletted)
	if !ok {
		// dither211 currently requires a palette
		draw.Draw(dst, r, src, sp, draw.Src)
		return
	}
	// intersect r with both dst and src bounds, fix up sp.
	ir := r.Intersect(pd.Bounds()).
		Intersect(src.Bounds().Add(r.Min.Sub(sp)))
	if ir.Empty() {
		return // no work to do.
	}
	sp = ir.Min.Sub(r.Min)
	// get subimage of src
	sr := ir.Add(sp)
	if !sr.Eq(src.Bounds()) {
		s, ok := src.(interface {
			SubImage(image.Rectangle) image.Image
		})
		if !ok {
			// dither211 currently works on whole images
			draw.Draw(dst, r, src, sp, draw.Src)
			return
		}
		src = s.SubImage(sr)
	}
	// dither211 currently returns a new image, or nil if dithering not
	// possible.
	if s := dither211(src, pd.Palette); s != nil {
		src = s
	}
	draw.Draw(dst, r, src, image.Point{}, draw.Src)
}

// signed color type, no alpha.  signed to represent color deltas as well as
// color values 0-ffff as with colorRGBA64
type sRGB struct{ r, g, b int32 }

// a linear palette, but with signed values.  while the fields hold uint32s,
// values must be restricted to 0-ffff as with color.RGBA64.  values are signed
// here to facilitate arithmetic, not to represent some new color space.
type sPalette []sRGB

// like PaletteIndex method
func (p sPalette) index(c sRGB) int {
	// still the awful linear search
	i, min := 0, int64(math.MaxInt64)
	for j, pc := range p {
		d := int64(c.r - pc.r)
		s := d * d
		d = int64(c.g - pc.g)
		s += d * d
		d = int64(c.b - pc.b)
		s += d * d
		if s < min {
			min = s
			i = j
		}
	}
	return i
}

// currently this is strictly a helper function for Dither211.Draw, so
// not generalized to use Palette from this package.
func dither211(i0 image.Image, cp color.Palette) *image.Paletted {
	if len(cp) > 256 {
		// representation limit of image.Paletted.  a little sketchy to return
		// nil, but unworkable results are always better than wrong results.
		return nil
	}
	b := i0.Bounds()
	pi := image.NewPaletted(b, cp)
	if b.Empty() {
		return pi // no work to do
	}
	p64 := make([]color.RGBA64, len(cp))
	sp := make(sPalette, len(cp))
	for i, c := range cp {
		r, g, b, _ := c.RGBA()
		p64[i] = color.RGBA64{uint16(r), uint16(g), uint16(b), 0xffff}
		sp[i] = sRGB{int32(r), int32(g), int32(b)}
	}
	// rt, dn hold diffused errors.
	var rt sRGB
	dn := make([]sRGB, b.Dx()+1)
	for y := b.Min.Y; y < b.Max.Y; y++ {
		rt = dn[1]
		dn[0] = sRGB{}
		for x := b.Min.X; x < b.Max.X; x++ {
			// full color from original image
			r0, g0, b0, _ := i0.At(x, y).RGBA()
			// adjusted full color = diffused err + original color
			rt.r += int32(r0)
			rt.g += int32(g0)
			rt.b += int32(b0)
			// nearest palette entry
			i := sp.index(rt)
			// set pixel in destination image
			pi.SetColorIndex(x, y, uint8(i))
			// error to be diffused = full color - palette color.
			e := rt
			pc := sp[i]
			e.r -= pc.r
			e.g -= pc.g
			e.b -= pc.b
			// half of error goes right
			rt.r = e.r / 2
			rt.g = e.g / 2
			rt.b = e.b / 2
			// the other half goes down
			e.r -= rt.r
			e.g -= rt.g
			e.b -= rt.b
			dx := x - b.Min.X
			dn[dx+1].r = e.r / 2
			dn[dx+1].g = e.g / 2
			dn[dx+1].b = e.b / 2
			dn[dx].r += e.r - dn[dx+1].r
			dn[dx].g += e.g - dn[dx+1].g
			dn[dx].b += e.b - dn[dx+1].b
		}
	}
	return pi
}
<commit_msg>something that actually works.  plus credit sierra.<commit_after>// Copyright 2013 Sonia Keys.
// Licensed under MIT license.  See "license" file in this source tree.

// Quant provides an interface for image color quantizers.
package quant

import (
	"image"
	"image/color"
	"image/draw"
	"math"
)

// Quantizer defines a color quantizer for images.
type Quantizer interface {
	// Image quantizes an image and returns a paletted image
	Image(image.Image) *image.Paletted
	// Palette quantizes an image and returns a Palette.  Note the type is
	// the Palette of this package and not image.Palette.
	Palette(image.Image) Palette
}

// Palette is a palette of color.Colors, just as color.Pallete of the standard
// library.
//
// It is defined as an interface here to allow more general implementations
// of Index, presumably ones that maintain some data structure to achieve
// performance advantages over linear search.
type Palette interface {
	Convert(color.Color) color.Color
	Index(color.Color) int
	ColorPalette() color.Palette
}

// LinearPalette implements the Palette interface with color.Palette
// and has no optimizations.
type LinearPalette struct {
	color.Palette
}

func (p LinearPalette) ColorPalette() color.Palette {
	return p.Palette
}

type Sierra24A struct{}

// Sierra24A satisfies draw.Drawer
var _ draw.Drawer = Sierra24A{}

// Dithering filter by Frankie Sierra.
func (d Sierra24A) Draw(dst draw.Image, r image.Rectangle, src image.Image, sp image.Point) {
	pd, ok := dst.(*image.Paletted)
	if !ok {
		// dither211 currently requires a palette
		draw.Draw(dst, r, src, sp, draw.Src)
		return
	}
	// intersect r with both dst and src bounds, fix up sp.
	ir := r.Intersect(pd.Bounds()).
		Intersect(src.Bounds().Add(r.Min.Sub(sp)))
	if ir.Empty() {
		return // no work to do.
	}
	sp = ir.Min.Sub(r.Min)
	// get subimage of src
	sr := ir.Add(sp)
	if !sr.Eq(src.Bounds()) {
		s, ok := src.(interface {
			SubImage(image.Rectangle) image.Image
		})
		if !ok {
			// dither211 currently works on whole images
			draw.Draw(dst, r, src, sp, draw.Src)
			return
		}
		src = s.SubImage(sr)
	}
	// dither211 currently returns a new image, or nil if dithering not
	// possible.
	if s := dither211(src, pd.Palette); s != nil {
		src = s
	}
	// this avoids any problem of src dst overlap but it would usually
	// work to render directly into dst.  todo.
	draw.Draw(dst, r, src, image.Point{}, draw.Src)
}

// signed color type, no alpha.  signed to represent color deltas as well as
// color values 0-ffff as with colorRGBA64
type sRGB struct{ r, g, b int32 }
type sPalette []sRGB

func (p sPalette) index(c sRGB) int {
	// still the awful linear search
	i, min := 0, int64(math.MaxInt64)
	for j, pc := range p {
		d := int64(c.r) - int64(pc.r)
		s := d * d
		d = int64(c.g) - int64(pc.g)
		s += d * d
		d = int64(c.b) - int64(pc.b)
		s += d * d
		if s < min {
			min = s
			i = j
		}
	}
	return i
}

// currently this is strictly a helper function for Dither211.Draw, so
// not generalized to use Palette from this package.
func dither211(i0 image.Image, cp color.Palette) *image.Paletted {
	if len(cp) > 256 {
		// representation limit of image.Paletted.  a little sketchy to return
		// nil, but unworkable results are always better than wrong results.
		return nil
	}
	b := i0.Bounds()
	pi := image.NewPaletted(b, cp)
	if b.Empty() {
		return pi // no work to do
	}
	sp := make(sPalette, len(cp))
	for i, c := range cp {
		r, g, b, _ := c.RGBA()
		sp[i] = sRGB{int32(r), int32(g), int32(b)}
	}
	// afc is adjustd full color.  e, rt, dn hold diffused errors.
	var afc, e, rt sRGB
	dn := make([]sRGB, b.Dx()+1)
	for y := b.Min.Y; y < b.Max.Y; y++ {
		rt = dn[0]
		dn[0] = sRGB{}
		for x := b.Min.X; x < b.Max.X; x++ {
			// full color from original image
			r0, g0, b0, _ := i0.At(x, y).RGBA()
			// adjusted full color = original color + diffused error
			afc.r = int32(r0) + rt.r>>2
			afc.g = int32(g0) + rt.g>>2
			afc.b = int32(b0) + rt.b>>2
			// clipping or clamping is usually explained as necessary
			// to avoid integer overflow but with palettes that do not
			// represent the full color space of the image, it is needed
			// to keep areas of excess color from saturating at palette
			// limits and bleeding into neighboring areas.
			if afc.r < 0 {
				afc.r = 0
			} else if afc.r > 0xffff {
				afc.r = 0xffff
			}
			if afc.g < 0 {
				afc.g = 0
			} else if afc.g > 0xffff {
				afc.g = 0xffff
			}
			if afc.b < 0 {
				afc.b = 0
			} else if afc.b > 0xffff {
				afc.b = 0xffff
			}
			// nearest palette entry
			i := sp.index(afc)
			// set pixel in destination image
			pi.SetColorIndex(x, y, uint8(i))
			// error to be diffused = full color - palette color.
			pc := sp[i]
			e.r = afc.r - pc.r
			e.g = afc.g - pc.g
			e.b = afc.b - pc.b
			// half of error*4 goes right
			dx := x - b.Min.X + 1
			rt.r = dn[dx].r + e.r*2
			rt.g = dn[dx].g + e.g*2
			rt.b = dn[dx].b + e.b*2
			// the other half goes down
			dn[dx] = e
			dn[dx-1].r += e.r
			dn[dx-1].g += e.g
			dn[dx-1].b += e.b
		}
	}
	return pi
}
<|endoftext|>


<commit_before>package pq

import (
	"container/heap"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
)

var ErrQueueNotStarted = fmt.Errorf("Queue not started or closed")
var ErrQueueAlreadyStarted = fmt.Errorf("Queue already started")

type Queue struct {
	numWorkers  int
	pq          priorityQueue
	work        chan *item
	cond        *sync.Cond
	wg          sync.WaitGroup
	working     bool
	taskRunning int32
}

// Starts work. You can add tasks only after starting queue
func (q *Queue) Start(numWorkers int) (err error) {
	if q.cond == nil {
		q.cond = sync.NewCond(&sync.Mutex{})
	}
	q.cond.L.Lock()
	defer q.cond.L.Unlock()
	if q.working {
		return ErrQueueAlreadyStarted
	}
	q.numWorkers = numWorkers
	q.pq = make(priorityQueue, 0)
	q.work = make(chan *item)
	if q.numWorkers <= 0 {
		q.numWorkers = runtime.NumCPU()
	}
	q.runWorkers()
	go q.dispatcher()
	return
}

// Add func() to queue
func (q *Queue) AddFunc(f func() error, priority int) (err error) {
	task := &funcTask{
		f: f,
		p: priority,
	}
	return q.AddTask(task)
}

// Add func() to queue and wait while tasks will be done
func (q *Queue) WaitFunc(f func() error, priority int) (err error) {
	task := &funcTask{
		f: f,
		p: priority,
	}
	return q.WaitTask(task)
}

// Just add group of tasks
func (q *Queue) AddGroup(tasks []Task) (err error) {
	for _, t := range tasks {
		if err = q.AddTask(t); err != nil {
			return
		}
	}
	return
}

// Add group of tasks and waits while all tasks will be done
func (q *Queue) WaitGroup(tasks []Task) (err error) {
	if len(tasks) == 0 {
		return
	}

	ctrl := &itemCtrl{count: int32(len(tasks)), m: new(sync.Mutex), done: make(chan error)}

	go func() {
		for _, t := range tasks {
			it := &item{task: t, ctrl: ctrl}
			if err = q.addItem(it); err != nil {
				ctrl.done <- err
				return
			}
		}
	}()
	err = <-ctrl.done
	return
}

// Add single task to queue
func (q *Queue) AddTask(task Task) (err error) {
	it := &item{task: task}
	return q.addItem(it)
}

// Add single task to queue and waits while task will be done
func (q *Queue) WaitTask(task Task) (err error) {
	return q.WaitGroup([]Task{task})
}

// Size of queue
func (q *Queue) Len() int {
	return len(q.pq)
}

// How much workers do work at this moment
func (q *Queue) TaskRunning() int {
	return int(atomic.LoadInt32(&q.taskRunning))
}

func (q *Queue) addItem(it *item) (err error) {
	q.cond.L.Lock()
	if !q.working {
		return ErrQueueNotStarted
		q.cond.L.Unlock()
	}
	heap.Push(&q.pq, it)
	q.cond.L.Unlock()
	q.cond.Signal()
	return
}

func (q *Queue) runWorkers() {
	for i := 0; i < q.numWorkers; i++ {
		go q.worker()
	}
	q.working = true
}

func (q *Queue) dispatcher() {
	for {
		q.cond.L.Lock()
		for q.pq.Len() == 0 {
			q.cond.Wait()
		}
		if !q.working {
			q.cond.L.Unlock()
			break
		}
		it := heap.Pop(&q.pq)
		q.work <- it.(*item)
		q.cond.L.Unlock()
	}
}

func (q *Queue) worker() {
	q.wg.Add(1)
	for it := range q.work {
		q.runTask(it)
	}
	q.wg.Done()
}

func (q *Queue) runTask(it *item) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("PQ. Panic while executing task: %v", r)
		}
		it.done(err)
	}()

	atomic.AddInt32(&q.taskRunning, 1)
	defer atomic.AddInt32(&q.taskRunning, -1)
	if it.can() {
		err = it.task.Run()
	}
	return
}

// Stopping queue. Wait while all workers finish current tasks
func (q *Queue) Stop() {
	q.cond.L.Lock()
	defer q.cond.L.Unlock()
	close(q.work)
	q.working = false
	q.wg.Wait()
}
<commit_msg>Make sure to unlock properly before returning an error<commit_after>package pq

import (
	"container/heap"
	"fmt"
	"runtime"
	"sync"
	"sync/atomic"
)

var ErrQueueNotStarted = fmt.Errorf("Queue not started or closed")
var ErrQueueAlreadyStarted = fmt.Errorf("Queue already started")

type Queue struct {
	numWorkers  int
	pq          priorityQueue
	work        chan *item
	cond        *sync.Cond
	wg          sync.WaitGroup
	working     bool
	taskRunning int32
}

// Starts work. You can add tasks only after starting queue
func (q *Queue) Start(numWorkers int) (err error) {
	if q.cond == nil {
		q.cond = sync.NewCond(&sync.Mutex{})
	}
	q.cond.L.Lock()
	defer q.cond.L.Unlock()
	if q.working {
		return ErrQueueAlreadyStarted
	}
	q.numWorkers = numWorkers
	q.pq = make(priorityQueue, 0)
	q.work = make(chan *item)
	if q.numWorkers <= 0 {
		q.numWorkers = runtime.NumCPU()
	}
	q.runWorkers()
	go q.dispatcher()
	return
}

// Add func() to queue
func (q *Queue) AddFunc(f func() error, priority int) (err error) {
	task := &funcTask{
		f: f,
		p: priority,
	}
	return q.AddTask(task)
}

// Add func() to queue and wait while tasks will be done
func (q *Queue) WaitFunc(f func() error, priority int) (err error) {
	task := &funcTask{
		f: f,
		p: priority,
	}
	return q.WaitTask(task)
}

// Just add group of tasks
func (q *Queue) AddGroup(tasks []Task) (err error) {
	for _, t := range tasks {
		if err = q.AddTask(t); err != nil {
			return
		}
	}
	return
}

// Add group of tasks and waits while all tasks will be done
func (q *Queue) WaitGroup(tasks []Task) (err error) {
	if len(tasks) == 0 {
		return
	}

	ctrl := &itemCtrl{count: int32(len(tasks)), m: new(sync.Mutex), done: make(chan error)}

	go func() {
		for _, t := range tasks {
			it := &item{task: t, ctrl: ctrl}
			if err = q.addItem(it); err != nil {
				ctrl.done <- err
				return
			}
		}
	}()
	err = <-ctrl.done
	return
}

// Add single task to queue
func (q *Queue) AddTask(task Task) (err error) {
	it := &item{task: task}
	return q.addItem(it)
}

// Add single task to queue and waits while task will be done
func (q *Queue) WaitTask(task Task) (err error) {
	return q.WaitGroup([]Task{task})
}

// Size of queue
func (q *Queue) Len() int {
	return len(q.pq)
}

// How much workers do work at this moment
func (q *Queue) TaskRunning() int {
	return int(atomic.LoadInt32(&q.taskRunning))
}

func (q *Queue) addItem(it *item) (err error) {
	q.cond.L.Lock()
	if !q.working {
		q.cond.L.Unlock()
		return ErrQueueNotStarted
	}
	heap.Push(&q.pq, it)
	q.cond.L.Unlock()
	q.cond.Signal()
	return
}

func (q *Queue) runWorkers() {
	for i := 0; i < q.numWorkers; i++ {
		go q.worker()
	}
	q.working = true
}

func (q *Queue) dispatcher() {
	for {
		q.cond.L.Lock()
		for q.pq.Len() == 0 {
			q.cond.Wait()
		}
		if !q.working {
			q.cond.L.Unlock()
			break
		}
		it := heap.Pop(&q.pq)
		q.work <- it.(*item)
		q.cond.L.Unlock()
	}
}

func (q *Queue) worker() {
	q.wg.Add(1)
	for it := range q.work {
		q.runTask(it)
	}
	q.wg.Done()
}

func (q *Queue) runTask(it *item) {
	var err error
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("PQ. Panic while executing task: %v", r)
		}
		it.done(err)
	}()

	atomic.AddInt32(&q.taskRunning, 1)
	defer atomic.AddInt32(&q.taskRunning, -1)
	if it.can() {
		err = it.task.Run()
	}
	return
}

// Stopping queue. Wait while all workers finish current tasks
func (q *Queue) Stop() {
	q.cond.L.Lock()
	defer q.cond.L.Unlock()
	close(q.work)
	q.working = false
	q.wg.Wait()
}
<|endoftext|>


<commit_before>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tests

import (
	"fmt"
	"math/rand"
	"net/http"
	"testing"

	"github.com/google/go-github/github"
)

func TestRepositories_CRUD(t *testing.T) {
	// get authenticated user
	me, _, err := client.Users.Get("")
	if err != nil {
		t.Fatalf("Users.Get('') returned error: %v", err)
	}

	// create random repo name that does not currently exist
	var repoName string
	for {
		repoName = fmt.Sprintf("test-%d", rand.Int())
		_, resp, err := client.Repositories.Get(*me.Login, repoName)
		if err != nil {
			if resp.StatusCode == http.StatusNotFound {
				// found a non-existant repo, perfect
				break
			}
			t.Fatalf("Repositories.Get() returned error: %v", err)
		}
	}

	// create the repository
	repo, _, err := client.Repositories.Create("", &github.Repository{Name: github.String(repoName)})
	if err != nil {
		t.Fatalf("Repositories.Create() returned error: %v", err)
	}

	// update the repository description
	repo.Description = github.String("description")
	repo.DefaultBranch = nil // FIXME: this shouldn't be necessary
	_, _, err = client.Repositories.Edit(*repo.Owner.Login, *repo.Name, repo)
	if err != nil {
		t.Fatalf("Repositories.Edit() returned error: %v", err)
	}

	// delete the repository
	_, err = client.Repositories.Delete(*repo.Owner.Login, *repo.Name)
	if err != nil {
		t.Fatalf("Repositories.Delete() returned error: %v", err)
	}

	// verify that the repository was deleted
	_, resp, err := client.Repositories.Get(*repo.Owner.Login, *repo.Name)
	if err == nil {
		t.Fatalf("Test repository still exists after deleting it.")
	}
	if err != nil && resp.StatusCode != http.StatusNotFound {
		t.Fatalf("Repositories.Get() returned error: %v", err)
	}
}

func TestRepositories_BranchesTags(t *testing.T) {
	// branches
	branches, _, err := client.Repositories.ListBranches("git", "git")
	if err != nil {
		t.Fatalf("Repositories.ListBranches() returned error: %v", err)
	}

	if len(branches) == 0 {
		t.Fatalf("Repositories.ListBranches('git', 'git') returned no branches")
	}

	_, _, err = client.Repositories.GetBranch("git", "git", *branches[0].Name)
	if err != nil {
		t.Fatalf("Repositories.GetBranch() returned error: %v", err)
	}

	// tags
	tags, _, err := client.Repositories.ListTags("git", "git")
	if err != nil {
		t.Fatalf("Repositories.ListTags() returned error: %v", err)
	}

	if len(tags) == 0 {
		t.Fatalf("Repositories.ListTags('git', 'git') returned no tags")
	}
}
<commit_msg>add auth check to TestRepositories_CRUD<commit_after>// Copyright 2014 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tests

import (
	"fmt"
	"math/rand"
	"net/http"
	"testing"

	"github.com/google/go-github/github"
)

func TestRepositories_CRUD(t *testing.T) {
	if !checkAuth("TestRepositories_CRUD") {
		return
	}

	// get authenticated user
	me, _, err := client.Users.Get("")
	if err != nil {
		t.Fatalf("Users.Get('') returned error: %v", err)
	}

	// create random repo name that does not currently exist
	var repoName string
	for {
		repoName = fmt.Sprintf("test-%d", rand.Int())
		_, resp, err := client.Repositories.Get(*me.Login, repoName)
		if err != nil {
			if resp.StatusCode == http.StatusNotFound {
				// found a non-existant repo, perfect
				break
			}
			t.Fatalf("Repositories.Get() returned error: %v", err)
		}
	}

	// create the repository
	repo, _, err := client.Repositories.Create("", &github.Repository{Name: github.String(repoName)})
	if err != nil {
		t.Fatalf("Repositories.Create() returned error: %v", err)
	}

	// update the repository description
	repo.Description = github.String("description")
	repo.DefaultBranch = nil // FIXME: this shouldn't be necessary
	_, _, err = client.Repositories.Edit(*repo.Owner.Login, *repo.Name, repo)
	if err != nil {
		t.Fatalf("Repositories.Edit() returned error: %v", err)
	}

	// delete the repository
	_, err = client.Repositories.Delete(*repo.Owner.Login, *repo.Name)
	if err != nil {
		t.Fatalf("Repositories.Delete() returned error: %v", err)
	}

	// verify that the repository was deleted
	_, resp, err := client.Repositories.Get(*repo.Owner.Login, *repo.Name)
	if err == nil {
		t.Fatalf("Test repository still exists after deleting it.")
	}
	if err != nil && resp.StatusCode != http.StatusNotFound {
		t.Fatalf("Repositories.Get() returned error: %v", err)
	}
}

func TestRepositories_BranchesTags(t *testing.T) {
	// branches
	branches, _, err := client.Repositories.ListBranches("git", "git")
	if err != nil {
		t.Fatalf("Repositories.ListBranches() returned error: %v", err)
	}

	if len(branches) == 0 {
		t.Fatalf("Repositories.ListBranches('git', 'git') returned no branches")
	}

	_, _, err = client.Repositories.GetBranch("git", "git", *branches[0].Name)
	if err != nil {
		t.Fatalf("Repositories.GetBranch() returned error: %v", err)
	}

	// tags
	tags, _, err := client.Repositories.ListTags("git", "git")
	if err != nil {
		t.Fatalf("Repositories.ListTags() returned error: %v", err)
	}

	if len(tags) == 0 {
		t.Fatalf("Repositories.ListTags('git', 'git') returned no tags")
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Hari haran. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/hariharan-uno/recon/blockdevice"
	"github.com/hariharan-uno/recon/cpu"
	"github.com/hariharan-uno/recon/languages"
	"github.com/hariharan-uno/recon/lsb"
	"github.com/hariharan-uno/recon/memory"
)

func main() {
	log.SetPrefix("recon: ")

	lsbdata, err := lsb.CollectData()
	if err != nil {
		log.Println(err)
	}
	memdata, err := memory.CollectData()
	if err != nil {
		log.Println(err)
	}
	cpudata, err := cpu.CollectData()
	if err != nil {
		log.Println(err)
	}
	blockdevicedata, err := blockdevice.CollectData()
	if err != nil {
		log.Println(err)
	}
	langsdata, err := languages.CollectData()
	if err != nil {
		log.Println(err)
	}

	data := map[string]interface{}{
		"lsb":          lsbdata,
		"memory":       memdata,
		"cpu":          cpudata,
		"block_device": blockdevicedata,
		"languages":    langsdata,
		"recon_time":   time.Now(),
	}

	b, err := json.MarshalIndent(data, "", "\t")
	if err != nil {
		fmt.Println("error:", err)
	}

	fmt.Printf("%s\n", b)
}
<commit_msg>Add uptime data in main<commit_after>// Copyright 2015 Hari haran. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/hariharan-uno/recon/blockdevice"
	"github.com/hariharan-uno/recon/cpu"
	"github.com/hariharan-uno/recon/languages"
	"github.com/hariharan-uno/recon/lsb"
	"github.com/hariharan-uno/recon/memory"
	"github.com/hariharan-uno/recon/uptime"
)

func main() {
	log.SetPrefix("recon: ")

	lsbdata, err := lsb.CollectData()
	if err != nil {
		log.Println(err)
	}
	memdata, err := memory.CollectData()
	if err != nil {
		log.Println(err)
	}
	cpudata, err := cpu.CollectData()
	if err != nil {
		log.Println(err)
	}
	blockdevicedata, err := blockdevice.CollectData()
	if err != nil {
		log.Println(err)
	}
	langsdata, err := languages.CollectData()
	if err != nil {
		log.Println(err)
	}
	uptimedata, err := uptime.CollectData()
	if err != nil {
		log.Println(err)
	}
	data := map[string]interface{}{
		"lsb":          lsbdata,
		"memory":       memdata,
		"cpu":          cpudata,
		"block_device": blockdevicedata,
		"languages":    langsdata,
		"recon_time":   time.Now(),
	}
	copyMap(uptimedata, data) // uptime Data is not namespaced.

	b, err := json.MarshalIndent(data, "", "\t")
	if err != nil {
		fmt.Println("error:", err)
	}

	fmt.Printf("%s\n", b)
}

func copyMap(from, to map[string]interface{}) {
	for k, v := range from {
		to[k] = v
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/uber/tchannel-go/relay"

	"github.com/uber-go/atomic"
)

// _maxRelayTombs is the maximum number of tombs we'll accumulate in a single
// relayItems.
const _maxRelayTombs = 1e4

// _relayTombTTL is the length of time we'll keep a tomb before GC'ing it.
const _relayTombTTL = time.Second

type relayItem struct {
	*time.Timer

	stats       relay.CallStats
	remapID     uint32
	destination *Relayer
	tomb        bool
	span        Span
}

type relayItems struct {
	sync.RWMutex

	logger Logger
	tombs  uint64
	items  map[uint32]relayItem
}

func newRelayItems(logger Logger) *relayItems {
	return &relayItems{
		items:  make(map[uint32]relayItem),
		logger: logger,
	}
}

// Count returns the number of non-tombstone items in the relay.
func (r *relayItems) Count() int {
	r.RLock()
	n := len(r.items) - int(r.tombs)
	r.RUnlock()
	return n
}

// Get checks for a relay item by ID, returning the item and a bool indicating
// whether the item was found.
func (r *relayItems) Get(id uint32) (relayItem, bool) {
	r.RLock()
	item, ok := r.items[id]
	r.RUnlock()

	return item, ok
}

// Add adds a relay item.
func (r *relayItems) Add(id uint32, item relayItem) {
	r.Lock()
	r.items[id] = item
	r.Unlock()
}

// Delete removes a relayItem completely (without leaving a tombstone). It
// returns the deleted item, along with a bool indicating whether we completed a
// relayed call.
func (r *relayItems) Delete(id uint32) (relayItem, bool) {
	r.Lock()
	item, ok := r.items[id]
	if !ok {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Attempted to delete non-existent relay item.")
		return item, false
	}
	delete(r.items, id)
	if item.tomb {
		r.tombs--
	}
	r.Unlock()

	item.Stop()
	return item, !item.tomb
}

// Entomb sets the tomb bit on a relayItem and schedules a garbage collection. It
// returns the entombed item, along with a bool indicating whether we completed
// a relayed call.
func (r *relayItems) Entomb(id uint32, deleteAfter time.Duration) (relayItem, bool) {
	r.Lock()
	if r.tombs > _maxRelayTombs {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Too many tombstones, deleting relay item immediately.")
		return r.Delete(id)
	}
	item, ok := r.items[id]
	if !ok {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Can't find relay item to entomb.")
		return item, false
	}
	if item.tomb {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Re-entombing a tombstone.")
		return item, false
	}
	r.tombs++
	item.tomb = true
	r.items[id] = item
	r.Unlock()

	// TODO: We should be clearing these out in batches, rather than creating
	// individual timers for each item.
	time.AfterFunc(deleteAfter, func() { r.Delete(id) })
	return item, true
}

type frameType int

const (
	requestFrame  frameType = 0
	responseFrame frameType = 1
)

// A Relayer forwards frames.
type Relayer struct {
	stats relay.Stats
	hosts relay.Hosts

	// outbound is the remapping for requests that originated on this
	// connection, and are outbound towards some other connection.
	// It stores remappings for all request frames read on this connection.
	outbound *relayItems

	// inbound is the remapping for requests that originated on some other
	// connection which was directed to this connection.
	// It stores remappings for all response frames read on this connection.
	inbound *relayItems

	peers   *PeerList
	conn    *Connection
	logger  Logger
	pending atomic.Uint32
}

// NewRelayer constructs a Relayer.
func NewRelayer(ch *Channel, conn *Connection) *Relayer {
	return &Relayer{
		stats:    ch.relayStats,
		hosts:    ch.RelayHosts(),
		outbound: newRelayItems(ch.Logger().WithFields(LogField{"relay", "outbound"})),
		inbound:  newRelayItems(ch.Logger().WithFields(LogField{"relay", "inbound"})),
		peers:    ch.Peers(),
		conn:     conn,
		logger:   conn.log,
	}
}

// Hosts returns the RelayHosts guiding peer selection.
func (r *Relayer) Hosts() relay.Hosts {
	return r.hosts
}

// Relay is called for each frame that is read on the connection.
func (r *Relayer) Relay(f *Frame) error {
	if f.messageType() != messageTypeCallReq {
		return r.handleNonCallReq(f)
	}
	return r.handleCallReq(newLazyCallReq(f))
}

// Receive receives frames intended for this connection.
func (r *Relayer) Receive(f *Frame, fType frameType) {
	// If we receive a response frame, we expect to find that ID in our outbound.
	// If we receive a request frame, we expect to find that ID in our inbound.
	items := r.receiverItems(fType)

	item, ok := items.Get(f.Header.ID)
	if !ok {
		r.logger.WithFields(
			LogField{"ID", f.Header.ID},
		).Warn("Received a frame without a RelayItem.")
		return
	}

	// call res frames don't include the OK bit, so we can't wait until the last
	// frame of a relayed RPC to determine if the call succeeded.
	if fType == responseFrame {
		// If we've gotten a response frame, we're the originating relayer and
		// should handle stats.
		if succeeded, failMsg := determinesCallSuccess(f); succeeded {
			item.stats.Succeeded()
		} else if len(failMsg) > 0 {
			item.stats.Failed(failMsg)
		}
	}

	// TODO: Add some sort of timeout here to avoid blocking forever on a
	// stalled connection.
	r.conn.sendCh <- f
	if finishesCall(f) {
		items := r.receiverItems(fType)
		r.finishRelayItem(items, f.Header.ID)
	}
}

func (r *Relayer) canHandleNewCall() bool {
	var canHandle bool
	r.conn.withStateRLock(func() error {
		canHandle = r.conn.state == connectionActive
		if canHandle {
			r.pending.Inc()
		}
		return nil
	})
	return canHandle
}

func (r *Relayer) getDestination(f lazyCallReq, cs relay.CallStats) (*Connection, bool, error) {
	if _, ok := r.outbound.Get(f.Header.ID); ok {
		r.logger.WithFields(LogField{"id", f.Header.ID}).Warn("received duplicate callReq")
		cs.Failed("relay-" + ErrCodeProtocol.MetricsKey())
		// TODO: this is a protocol error, kill the connection.
		return nil, false, errors.New("callReq with already active ID")
	}

	// Get the destination
	selectedPeer, err := r.hosts.Get(f)
	if err == nil && selectedPeer.HostPort == "" {
		err = errInvalidPeerForGroup(f.Service())
	}
	if err != nil {
		if _, ok := err.(SystemError); !ok {
			err = NewSystemError(ErrCodeDeclined, err.Error())
		}
		cs.Failed("relay-" + GetSystemErrorCode(err).MetricsKey())
		r.conn.SendSystemError(f.Header.ID, f.Span(), err)
		return nil, false, nil
	}

	cs.SetPeer(selectedPeer)
	peer := r.peers.GetOrAdd(selectedPeer.HostPort)

	// TODO: Should connections use the call timeout? Or a separate timeout?
	remoteConn, err := peer.getConnectionTimeout(f.TTL())
	if err != nil {
		r.logger.WithFields(
			ErrField(err),
			LogField{"selectedPeer", selectedPeer},
		).Warn("Failed to connect to relay host.")
		cs.Failed("relay-connection-failed")
		r.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeNetwork, err))
		return nil, false, nil
	}

	return remoteConn, true, nil
}

func (r *Relayer) handleCallReq(f lazyCallReq) error {
	callStats := r.stats.Begin(f)
	if !r.canHandleNewCall() {
		callStats.Failed("relay-channel-closed")
		callStats.End()
		return ErrChannelClosed
	}

	// Get a remote connection and check whether it can handle this call.
	remoteConn, ok, err := r.getDestination(f, callStats)
	if err == nil && ok {
		if !remoteConn.relay.canHandleNewCall() {
			err = NewSystemError(ErrCodeNetwork, "selected closed connection, retry")
			callStats.Failed("relay-connection-closed")
		}
	}
	if err != nil || !ok {
		// Failed to get a remote connection, or the connection is not in the right
		// state to handle this call. Since we already incremented pending on
		// the current relay, we need to decrement it.
		r.decrementPending()
		callStats.End()
		return err
	}

	destinationID := remoteConn.NextMessageID()
	ttl := f.TTL()
	span := f.Span()
	// The remote side of the relay doesn't need to track stats.
	remoteConn.relay.addRelayItem(false /* isOriginator */, destinationID, f.Header.ID, r, ttl, span, nil)
	relayToDest := r.addRelayItem(true /* isOriginator */, f.Header.ID, destinationID, remoteConn.relay, ttl, span, callStats)

	f.Header.ID = destinationID
	relayToDest.destination.Receive(f.Frame, requestFrame)
	return nil
}

// Handle all frames except messageTypeCallReq.
func (r *Relayer) handleNonCallReq(f *Frame) error {
	frameType := frameTypeFor(f)

	// If we read a request frame, we need to use the outbound map to decide
	// the destination. Otherwise, we use the inbound map.
	items := r.outbound
	if frameType == responseFrame {
		items = r.inbound
	}

	item, ok := items.Get(f.Header.ID)
	if !ok {
		return errors.New("non-callReq for inactive ID")
	}
	if item.tomb {
		// Call timed out, ignore this frame. (We've already handled stats.)
		// TODO: metrics for late-arriving frames.
		return nil
	}
	originalID := f.Header.ID
	f.Header.ID = item.remapID
	item.destination.Receive(f, frameType)

	if finishesCall(f) {
		r.finishRelayItem(items, originalID)
	}
	return nil
}

// addRelayItem adds a relay item to either outbound or inbound.
func (r *Relayer) addRelayItem(isOriginator bool, id, remapID uint32, destination *Relayer, ttl time.Duration, span Span, cs relay.CallStats) relayItem {
	item := relayItem{
		stats:       cs,
		remapID:     remapID,
		destination: destination,
		span:        span,
	}

	items := r.inbound
	if isOriginator {
		items = r.outbound
	}
	item.Timer = time.AfterFunc(ttl, func() { r.timeoutRelayItem(isOriginator, items, id) })
	items.Add(id, item)
	return item
}

func (r *Relayer) timeoutRelayItem(isOriginator bool, items *relayItems, id uint32) {
	item, ok := items.Entomb(id, _relayTombTTL)
	if !ok {
		return
	}
	if isOriginator {
		r.conn.SendSystemError(id, item.span, ErrTimeout)
		item.stats.Failed("timeout")
		item.stats.End()
	}

	r.decrementPending()
}

func (r *Relayer) finishRelayItem(items *relayItems, id uint32) {
	item, ok := items.Delete(id)
	if !ok {
		return
	}
	if item.stats != nil {
		item.stats.End()
	}
	r.decrementPending()
}

func (r *Relayer) decrementPending() {
	r.pending.Dec()
	r.conn.checkExchanges()
}

func (r *Relayer) canClose() bool {
	if r == nil {
		return true
	}
	return r.countPending() == 0
}

func (r *Relayer) countPending() uint32 {
	return r.pending.Load()
}

func (r *Relayer) receiverItems(fType frameType) *relayItems {
	if fType == requestFrame {
		return r.inbound
	}
	return r.outbound
}

func frameTypeFor(f *Frame) frameType {
	switch t := f.Header.messageType; t {
	case messageTypeCallRes, messageTypeCallResContinue, messageTypeError, messageTypePingRes:
		return responseFrame
	case messageTypeCallReq, messageTypeCallReqContinue, messageTypePingReq:
		return requestFrame
	default:
		panic(fmt.Sprintf("unsupported frame type: %v", t))
	}
}

func errInvalidPeerForGroup(group []byte) error {
	return NewSystemError(ErrCodeDeclined, "invalid peer for %q", group)
}

func determinesCallSuccess(f *Frame) (succeeded bool, failMsg string) {
	switch f.messageType() {
	case messageTypeError:
		msg := newLazyError(f).Code().MetricsKey()
		return false, msg
	case messageTypeCallRes:
		if newLazyCallRes(f).OK() {
			return true, ""
		}
		return false, "application-error"
	default:
		return false, ""
	}
}
<commit_msg>relay: Precalculate frame properties before writing to sendCh<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package tchannel

import (
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/uber/tchannel-go/relay"

	"github.com/uber-go/atomic"
)

// _maxRelayTombs is the maximum number of tombs we'll accumulate in a single
// relayItems.
const _maxRelayTombs = 1e4

// _relayTombTTL is the length of time we'll keep a tomb before GC'ing it.
const _relayTombTTL = time.Second

type relayItem struct {
	*time.Timer

	stats       relay.CallStats
	remapID     uint32
	destination *Relayer
	tomb        bool
	span        Span
}

type relayItems struct {
	sync.RWMutex

	logger Logger
	tombs  uint64
	items  map[uint32]relayItem
}

func newRelayItems(logger Logger) *relayItems {
	return &relayItems{
		items:  make(map[uint32]relayItem),
		logger: logger,
	}
}

// Count returns the number of non-tombstone items in the relay.
func (r *relayItems) Count() int {
	r.RLock()
	n := len(r.items) - int(r.tombs)
	r.RUnlock()
	return n
}

// Get checks for a relay item by ID, returning the item and a bool indicating
// whether the item was found.
func (r *relayItems) Get(id uint32) (relayItem, bool) {
	r.RLock()
	item, ok := r.items[id]
	r.RUnlock()

	return item, ok
}

// Add adds a relay item.
func (r *relayItems) Add(id uint32, item relayItem) {
	r.Lock()
	r.items[id] = item
	r.Unlock()
}

// Delete removes a relayItem completely (without leaving a tombstone). It
// returns the deleted item, along with a bool indicating whether we completed a
// relayed call.
func (r *relayItems) Delete(id uint32) (relayItem, bool) {
	r.Lock()
	item, ok := r.items[id]
	if !ok {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Attempted to delete non-existent relay item.")
		return item, false
	}
	delete(r.items, id)
	if item.tomb {
		r.tombs--
	}
	r.Unlock()

	item.Stop()
	return item, !item.tomb
}

// Entomb sets the tomb bit on a relayItem and schedules a garbage collection. It
// returns the entombed item, along with a bool indicating whether we completed
// a relayed call.
func (r *relayItems) Entomb(id uint32, deleteAfter time.Duration) (relayItem, bool) {
	r.Lock()
	if r.tombs > _maxRelayTombs {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Too many tombstones, deleting relay item immediately.")
		return r.Delete(id)
	}
	item, ok := r.items[id]
	if !ok {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Can't find relay item to entomb.")
		return item, false
	}
	if item.tomb {
		r.Unlock()
		r.logger.WithFields(LogField{"id", id}).Warn("Re-entombing a tombstone.")
		return item, false
	}
	r.tombs++
	item.tomb = true
	r.items[id] = item
	r.Unlock()

	// TODO: We should be clearing these out in batches, rather than creating
	// individual timers for each item.
	time.AfterFunc(deleteAfter, func() { r.Delete(id) })
	return item, true
}

type frameType int

const (
	requestFrame  frameType = 0
	responseFrame frameType = 1
)

// A Relayer forwards frames.
type Relayer struct {
	stats relay.Stats
	hosts relay.Hosts

	// outbound is the remapping for requests that originated on this
	// connection, and are outbound towards some other connection.
	// It stores remappings for all request frames read on this connection.
	outbound *relayItems

	// inbound is the remapping for requests that originated on some other
	// connection which was directed to this connection.
	// It stores remappings for all response frames read on this connection.
	inbound *relayItems

	peers   *PeerList
	conn    *Connection
	logger  Logger
	pending atomic.Uint32
}

// NewRelayer constructs a Relayer.
func NewRelayer(ch *Channel, conn *Connection) *Relayer {
	return &Relayer{
		stats:    ch.relayStats,
		hosts:    ch.RelayHosts(),
		outbound: newRelayItems(ch.Logger().WithFields(LogField{"relay", "outbound"})),
		inbound:  newRelayItems(ch.Logger().WithFields(LogField{"relay", "inbound"})),
		peers:    ch.Peers(),
		conn:     conn,
		logger:   conn.log,
	}
}

// Hosts returns the RelayHosts guiding peer selection.
func (r *Relayer) Hosts() relay.Hosts {
	return r.hosts
}

// Relay is called for each frame that is read on the connection.
func (r *Relayer) Relay(f *Frame) error {
	if f.messageType() != messageTypeCallReq {
		return r.handleNonCallReq(f)
	}
	return r.handleCallReq(newLazyCallReq(f))
}

// Receive receives frames intended for this connection.
func (r *Relayer) Receive(f *Frame, fType frameType) {
	id := f.Header.ID

	// If we receive a response frame, we expect to find that ID in our outbound.
	// If we receive a request frame, we expect to find that ID in our inbound.
	items := r.receiverItems(fType)

	item, ok := items.Get(id)
	if !ok {
		r.logger.WithFields(
			LogField{"id", id},
		).Warn("Received a frame without a RelayItem.")
		return
	}

	// call res frames don't include the OK bit, so we can't wait until the last
	// frame of a relayed RPC to determine if the call succeeded.
	if fType == responseFrame {
		// If we've gotten a response frame, we're the originating relayer and
		// should handle stats.
		if succeeded, failMsg := determinesCallSuccess(f); succeeded {
			item.stats.Succeeded()
		} else if len(failMsg) > 0 {
			item.stats.Failed(failMsg)
		}
	}

	// When we write the frame to sendCh, we lose ownership of the frame, and it
	// may be released to the frame pool at any point.
	finished := finishesCall(f)

	// TODO: Add some sort of timeout here to avoid blocking forever on a
	// stalled connection.
	r.conn.sendCh <- f

	if finished {
		items := r.receiverItems(fType)
		r.finishRelayItem(items, id)
	}
}

func (r *Relayer) canHandleNewCall() bool {
	var canHandle bool
	r.conn.withStateRLock(func() error {
		canHandle = r.conn.state == connectionActive
		if canHandle {
			r.pending.Inc()
		}
		return nil
	})
	return canHandle
}

func (r *Relayer) getDestination(f lazyCallReq, cs relay.CallStats) (*Connection, bool, error) {
	if _, ok := r.outbound.Get(f.Header.ID); ok {
		r.logger.WithFields(LogField{"id", f.Header.ID}).Warn("received duplicate callReq")
		cs.Failed("relay-" + ErrCodeProtocol.MetricsKey())
		// TODO: this is a protocol error, kill the connection.
		return nil, false, errors.New("callReq with already active ID")
	}

	// Get the destination
	selectedPeer, err := r.hosts.Get(f)
	if err == nil && selectedPeer.HostPort == "" {
		err = errInvalidPeerForGroup(f.Service())
	}
	if err != nil {
		if _, ok := err.(SystemError); !ok {
			err = NewSystemError(ErrCodeDeclined, err.Error())
		}
		cs.Failed("relay-" + GetSystemErrorCode(err).MetricsKey())
		r.conn.SendSystemError(f.Header.ID, f.Span(), err)
		return nil, false, nil
	}

	cs.SetPeer(selectedPeer)
	peer := r.peers.GetOrAdd(selectedPeer.HostPort)

	// TODO: Should connections use the call timeout? Or a separate timeout?
	remoteConn, err := peer.getConnectionTimeout(f.TTL())
	if err != nil {
		r.logger.WithFields(
			ErrField(err),
			LogField{"selectedPeer", selectedPeer},
		).Warn("Failed to connect to relay host.")
		cs.Failed("relay-connection-failed")
		r.conn.SendSystemError(f.Header.ID, f.Span(), NewWrappedSystemError(ErrCodeNetwork, err))
		return nil, false, nil
	}

	return remoteConn, true, nil
}

func (r *Relayer) handleCallReq(f lazyCallReq) error {
	callStats := r.stats.Begin(f)
	if !r.canHandleNewCall() {
		callStats.Failed("relay-channel-closed")
		callStats.End()
		return ErrChannelClosed
	}

	// Get a remote connection and check whether it can handle this call.
	remoteConn, ok, err := r.getDestination(f, callStats)
	if err == nil && ok {
		if !remoteConn.relay.canHandleNewCall() {
			err = NewSystemError(ErrCodeNetwork, "selected closed connection, retry")
			callStats.Failed("relay-connection-closed")
		}
	}
	if err != nil || !ok {
		// Failed to get a remote connection, or the connection is not in the right
		// state to handle this call. Since we already incremented pending on
		// the current relay, we need to decrement it.
		r.decrementPending()
		callStats.End()
		return err
	}

	destinationID := remoteConn.NextMessageID()
	ttl := f.TTL()
	span := f.Span()
	// The remote side of the relay doesn't need to track stats.
	remoteConn.relay.addRelayItem(false /* isOriginator */, destinationID, f.Header.ID, r, ttl, span, nil)
	relayToDest := r.addRelayItem(true /* isOriginator */, f.Header.ID, destinationID, remoteConn.relay, ttl, span, callStats)

	f.Header.ID = destinationID
	relayToDest.destination.Receive(f.Frame, requestFrame)
	return nil
}

// Handle all frames except messageTypeCallReq.
func (r *Relayer) handleNonCallReq(f *Frame) error {
	frameType := frameTypeFor(f)

	// If we read a request frame, we need to use the outbound map to decide
	// the destination. Otherwise, we use the inbound map.
	items := r.outbound
	if frameType == responseFrame {
		items = r.inbound
	}

	item, ok := items.Get(f.Header.ID)
	if !ok {
		return errors.New("non-callReq for inactive ID")
	}
	if item.tomb {
		// Call timed out, ignore this frame. (We've already handled stats.)
		// TODO: metrics for late-arriving frames.
		return nil
	}
	originalID := f.Header.ID
	f.Header.ID = item.remapID
	item.destination.Receive(f, frameType)

	if finishesCall(f) {
		r.finishRelayItem(items, originalID)
	}
	return nil
}

// addRelayItem adds a relay item to either outbound or inbound.
func (r *Relayer) addRelayItem(isOriginator bool, id, remapID uint32, destination *Relayer, ttl time.Duration, span Span, cs relay.CallStats) relayItem {
	item := relayItem{
		stats:       cs,
		remapID:     remapID,
		destination: destination,
		span:        span,
	}

	items := r.inbound
	if isOriginator {
		items = r.outbound
	}
	item.Timer = time.AfterFunc(ttl, func() { r.timeoutRelayItem(isOriginator, items, id) })
	items.Add(id, item)
	return item
}

func (r *Relayer) timeoutRelayItem(isOriginator bool, items *relayItems, id uint32) {
	item, ok := items.Entomb(id, _relayTombTTL)
	if !ok {
		return
	}
	if isOriginator {
		r.conn.SendSystemError(id, item.span, ErrTimeout)
		item.stats.Failed("timeout")
		item.stats.End()
	}

	r.decrementPending()
}

func (r *Relayer) finishRelayItem(items *relayItems, id uint32) {
	item, ok := items.Delete(id)
	if !ok {
		return
	}
	if item.stats != nil {
		item.stats.End()
	}
	r.decrementPending()
}

func (r *Relayer) decrementPending() {
	r.pending.Dec()
	r.conn.checkExchanges()
}

func (r *Relayer) canClose() bool {
	if r == nil {
		return true
	}
	return r.countPending() == 0
}

func (r *Relayer) countPending() uint32 {
	return r.pending.Load()
}

func (r *Relayer) receiverItems(fType frameType) *relayItems {
	if fType == requestFrame {
		return r.inbound
	}
	return r.outbound
}

func frameTypeFor(f *Frame) frameType {
	switch t := f.Header.messageType; t {
	case messageTypeCallRes, messageTypeCallResContinue, messageTypeError, messageTypePingRes:
		return responseFrame
	case messageTypeCallReq, messageTypeCallReqContinue, messageTypePingReq:
		return requestFrame
	default:
		panic(fmt.Sprintf("unsupported frame type: %v", t))
	}
}

func errInvalidPeerForGroup(group []byte) error {
	return NewSystemError(ErrCodeDeclined, "invalid peer for %q", group)
}

func determinesCallSuccess(f *Frame) (succeeded bool, failMsg string) {
	switch f.messageType() {
	case messageTypeError:
		msg := newLazyError(f).Code().MetricsKey()
		return false, msg
	case messageTypeCallRes:
		if newLazyCallRes(f).OK() {
			return true, ""
		}
		return false, "application-error"
	default:
		return false, ""
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The go9p Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package go9p

import (
	"fmt"
	"os"
	"path"
	"strings"
	"syscall"
	"time"
)

func atime(fi os.FileInfo) time.Time {
	return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
}

// IsBlock reports if the file is a block device
func isBlock(d os.FileInfo) bool {
	// stat := d.Sys().(*syscall.Stat_t)
	// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
	return true
}

// IsChar reports if the file is a character device
func isChar(d os.FileInfo) bool {
	// stat := d.Sys().(*syscall.Stat_t)
	// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
	return true
}

func dir2Qid(d os.FileInfo) *Qid {
	var qid Qid

	qid.Path = uint64(d.Sys().(*syscall.Win32FileAttributeData).FileSizeLow)
	qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
	qid.Type = dir2QidType(d)

	return &qid
}

func dir2Dir(path string, d os.FileInfo, dotu bool, upool Users) (*Dir, error) {
	if r := recover(); r != nil {
		fmt.Print("stat failed: ", r)
		return nil, &os.PathError{"dir2Dir", path, nil}
	}
	sysif := d.Sys()
	if sysif == nil {
		return nil, &os.PathError{"dir2Dir: sysif is nil", path, nil}
	}
	sysMode := sysif.(*syscall.Win32FileAttributeData)

	dir := new(ufsDir)
	dir.Qid = *dir2Qid(d)
	dir.Mode = dir2Npmode(d, dotu)
	// dir.Atime = uint32(0 /*atime(sysMode).Unix()*/)
	// dir.Mtime = uint32(d.ModTime().Unix())
	dir.Length = uint64(d.Size())
	dir.Name = path[strings.LastIndex(path, "/")+1:]

	if dotu {
		dir.dotu(path, d, upool, sysMode)
		return &dir.Dir, nil
	}

	// unixUid := int(sysMode.Uid)
	// unixGid := int(sysMode.Gid)
	// dir.Uid = strconv.Itoa(unixUid)
	// dir.Gid = strconv.Itoa(unixGid)

	dir.Uid = "none"
	dir.Gid = "none"
	dir.Muid = "none"

	// BUG(akumar): LookupId will never find names for
	// groups, as it only operates on user ids.
	// u, err := user.LookupId(dir.Uid)
	// if err == nil {
	// 	dir.Uid = u.Username
	// }
	// g, err := user.LookupId(dir.Gid)
	// if err == nil {
	// 	dir.Gid = g.Username
	// }

	var err error
	/* For Akaros, we use the Muid as the link value. */
	if *Akaros && (d.Mode()&os.ModeSymlink != 0) {
		dir.Muid, err = os.Readlink(path)
		if err == nil {
			dir.Mode |= DMSYMLINK
		}
	}
	return &dir.Dir, nil
}

func (dir *ufsDir) dotu(path string, d os.FileInfo, upool Users, sysMode *syscall.Win32FileAttributeData) {
	// u := upool.Uid2User(int(sysMode.Uid))
	// g := upool.Gid2Group(int(sysMode.Gid))
	// dir.Uid = u.Name()
	// if dir.Uid == "" {
	// 	dir.Uid = "none"
	// }

	// dir.Gid = g.Name()
	// if dir.Gid == "" {
	// 	dir.Gid = "none"
	// }
	// dir.Muid = "none"
	// dir.Ext = ""
	// dir.Uidnum = uint32(u.Id())
	// dir.Gidnum = uint32(g.Id())
	// dir.Muidnum = NOUID
	// if d.Mode()&os.ModeSymlink != 0 {
	// 	var err error
	// 	dir.Ext, err = os.Readlink(path)
	// 	if err != nil {
	// 		dir.Ext = ""
	// 	}
	// } else if isBlock(d) {
	// 	dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
	// } else if isChar(d) {
	// 	dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
	// }
	dir.Uid = "none"
	dir.Gid = "none"
	dir.Muid = "none"
	dir.Uidnum = 0
	dir.Gidnum = 0
	dir.Muidnum = NOUID
	dir.Ext = ""
}

func (u *Ufs) Wstat(req *SrvReq) {
	fid := req.Fid.Aux.(*ufsFid)
	err := fid.stat()
	if err != nil {
		req.RespondError(err)
		return
	}

	dir := &req.Tc.Dir
	if dir.Mode != 0xFFFFFFFF {
		mode := dir.Mode & 0777
		if req.Conn.Dotu {
			if dir.Mode&DMSETUID > 0 {
				mode |= syscall.S_ISUID
			}
			if dir.Mode&DMSETGID > 0 {
				mode |= syscall.S_ISGID
			}
		}
		e := os.Chmod(fid.path, os.FileMode(mode))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	uid, gid := NOUID, NOUID
	if req.Conn.Dotu {
		uid = dir.Uidnum
		gid = dir.Gidnum
	}

	// Try to find local uid, gid by name.
	if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
		uid, err = lookup(dir.Uid, false)
		if err != nil {
			req.RespondError(err)
			return
		}

		// BUG(akumar): Lookup will never find gids
		// corresponding to group names, because
		// it only operates on user names.
		gid, err = lookup(dir.Gid, true)
		if err != nil {
			req.RespondError(err)
			return
		}
	}

	if uid != NOUID || gid != NOUID {
		e := os.Chown(fid.path, int(uid), int(gid))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	if dir.Name != "" {
		fmt.Printf("Rename %s to %s\n", fid.path, dir.Name)
		// if first char is / it is relative to root, else relative to
		// cwd.
		var destpath string
		if dir.Name[0] == '/' {
			destpath = path.Join(u.Root, dir.Name)
			fmt.Printf("/ results in %s\n", destpath)
		} else {
			fiddir, _ := path.Split(fid.path)
			destpath = path.Join(fiddir, dir.Name)
			fmt.Printf("rel  results in %s\n", destpath)
		}
		err := os.Rename(fid.path, destpath)
		fmt.Printf("rename %s to %s gets %v\n", fid.path, destpath, err)
		if err != nil {
			req.RespondError(toError(err))
			return
		}
		fid.path = destpath
	}

	if dir.Length != 0xFFFFFFFFFFFFFFFF {
		e := os.Truncate(fid.path, int64(dir.Length))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	// If either mtime or atime need to be changed, then
	// we must change both.
	// if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
	// 	mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
	// 	if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
	// 		st, e := os.Stat(fid.path)
	// 		if e != nil {
	// 			req.RespondError(toError(e))
	// 			return
	// 		}
	// 		switch cmt {
	// 		case true:
	// 			mt = st.ModTime()
	// 		default:
	// 			//at = time.Time(0)//atime(st.Sys().(*syscall.Stat_t))
	// 		}
	// 	}
	// 	e := os.Chtimes(fid.path, at, mt)
	// 	if e != nil {
	// 		req.RespondError(toError(e))
	// 		return
	// 	}
	// }

	req.RespondRwstat()
}
<commit_msg>Issue #3044 - set timestamps of mounted files with windows host.<commit_after>// Copyright 2009 The go9p Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package go9p

import (
	"fmt"
	"os"
	"path"
	"strings"
	"syscall"
	"time"
)

func atime(fi os.FileInfo) time.Time {
	return time.Unix(0, fi.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds())
}

// IsBlock reports if the file is a block device
func isBlock(d os.FileInfo) bool {
	// stat := d.Sys().(*syscall.Stat_t)
	// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFBLK
	return true
}

// IsChar reports if the file is a character device
func isChar(d os.FileInfo) bool {
	// stat := d.Sys().(*syscall.Stat_t)
	// return (stat.Mode & syscall.S_IFMT) == syscall.S_IFCHR
	return true
}

func dir2Qid(d os.FileInfo) *Qid {
	var qid Qid

	qid.Path = uint64(d.Sys().(*syscall.Win32FileAttributeData).FileSizeLow)
	qid.Version = uint32(d.ModTime().UnixNano() / 1000000)
	qid.Type = dir2QidType(d)

	return &qid
}

func dir2Dir(path string, d os.FileInfo, dotu bool, upool Users) (*Dir, error) {
	if r := recover(); r != nil {
		fmt.Print("stat failed: ", r)
		return nil, &os.PathError{"dir2Dir", path, nil}
	}
	sysif := d.Sys()
	if sysif == nil {
		return nil, &os.PathError{"dir2Dir: sysif is nil", path, nil}
	}
	sysMode := sysif.(*syscall.Win32FileAttributeData)

	dir := new(ufsDir)
	dir.Qid = *dir2Qid(d)
	dir.Mode = dir2Npmode(d, dotu)
	dir.Atime = uint32(atime(d).Unix())
	dir.Mtime = uint32(d.ModTime().Unix())
	dir.Length = uint64(d.Size())
	dir.Name = path[strings.LastIndex(path, "/")+1:]

	if dotu {
		dir.dotu(path, d, upool, sysMode)
		return &dir.Dir, nil
	}

	// unixUid := int(sysMode.Uid)
	// unixGid := int(sysMode.Gid)
	// dir.Uid = strconv.Itoa(unixUid)
	// dir.Gid = strconv.Itoa(unixGid)

	dir.Uid = "none"
	dir.Gid = "none"
	dir.Muid = "none"

	// BUG(akumar): LookupId will never find names for
	// groups, as it only operates on user ids.
	// u, err := user.LookupId(dir.Uid)
	// if err == nil {
	// 	dir.Uid = u.Username
	// }
	// g, err := user.LookupId(dir.Gid)
	// if err == nil {
	// 	dir.Gid = g.Username
	// }

	var err error
	/* For Akaros, we use the Muid as the link value. */
	if *Akaros && (d.Mode()&os.ModeSymlink != 0) {
		dir.Muid, err = os.Readlink(path)
		if err == nil {
			dir.Mode |= DMSYMLINK
		}
	}
	return &dir.Dir, nil
}

func (dir *ufsDir) dotu(path string, d os.FileInfo, upool Users, sysMode *syscall.Win32FileAttributeData) {
	// u := upool.Uid2User(int(sysMode.Uid))
	// g := upool.Gid2Group(int(sysMode.Gid))
	// dir.Uid = u.Name()
	// if dir.Uid == "" {
	// 	dir.Uid = "none"
	// }

	// dir.Gid = g.Name()
	// if dir.Gid == "" {
	// 	dir.Gid = "none"
	// }
	// dir.Muid = "none"
	// dir.Ext = ""
	// dir.Uidnum = uint32(u.Id())
	// dir.Gidnum = uint32(g.Id())
	// dir.Muidnum = NOUID
	// if d.Mode()&os.ModeSymlink != 0 {
	// 	var err error
	// 	dir.Ext, err = os.Readlink(path)
	// 	if err != nil {
	// 		dir.Ext = ""
	// 	}
	// } else if isBlock(d) {
	// 	dir.Ext = fmt.Sprintf("b %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
	// } else if isChar(d) {
	// 	dir.Ext = fmt.Sprintf("c %d %d", sysMode.Rdev>>24, sysMode.Rdev&0xFFFFFF)
	// }
	dir.Uid = "none"
	dir.Gid = "none"
	dir.Muid = "none"
	dir.Uidnum = 0
	dir.Gidnum = 0
	dir.Muidnum = NOUID
	dir.Ext = ""
}

func (u *Ufs) Wstat(req *SrvReq) {
	fid := req.Fid.Aux.(*ufsFid)
	err := fid.stat()
	if err != nil {
		req.RespondError(err)
		return
	}

	dir := &req.Tc.Dir
	if dir.Mode != 0xFFFFFFFF {
		mode := dir.Mode & 0777
		if req.Conn.Dotu {
			if dir.Mode&DMSETUID > 0 {
				mode |= syscall.S_ISUID
			}
			if dir.Mode&DMSETGID > 0 {
				mode |= syscall.S_ISGID
			}
		}
		e := os.Chmod(fid.path, os.FileMode(mode))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	uid, gid := NOUID, NOUID
	if req.Conn.Dotu {
		uid = dir.Uidnum
		gid = dir.Gidnum
	}

	// Try to find local uid, gid by name.
	if (dir.Uid != "" || dir.Gid != "") && !req.Conn.Dotu {
		uid, err = lookup(dir.Uid, false)
		if err != nil {
			req.RespondError(err)
			return
		}

		// BUG(akumar): Lookup will never find gids
		// corresponding to group names, because
		// it only operates on user names.
		gid, err = lookup(dir.Gid, true)
		if err != nil {
			req.RespondError(err)
			return
		}
	}

	if uid != NOUID || gid != NOUID {
		e := os.Chown(fid.path, int(uid), int(gid))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	if dir.Name != "" {
		fmt.Printf("Rename %s to %s\n", fid.path, dir.Name)
		// if first char is / it is relative to root, else relative to
		// cwd.
		var destpath string
		if dir.Name[0] == '/' {
			destpath = path.Join(u.Root, dir.Name)
			fmt.Printf("/ results in %s\n", destpath)
		} else {
			fiddir, _ := path.Split(fid.path)
			destpath = path.Join(fiddir, dir.Name)
			fmt.Printf("rel  results in %s\n", destpath)
		}
		err := os.Rename(fid.path, destpath)
		fmt.Printf("rename %s to %s gets %v\n", fid.path, destpath, err)
		if err != nil {
			req.RespondError(toError(err))
			return
		}
		fid.path = destpath
	}

	if dir.Length != 0xFFFFFFFFFFFFFFFF {
		e := os.Truncate(fid.path, int64(dir.Length))
		if e != nil {
			req.RespondError(toError(e))
			return
		}
	}

	// If either mtime or atime need to be changed, then
	// we must change both.
	// if dir.Mtime != ^uint32(0) || dir.Atime != ^uint32(0) {
	// 	mt, at := time.Unix(int64(dir.Mtime), 0), time.Unix(int64(dir.Atime), 0)
	// 	if cmt, cat := (dir.Mtime == ^uint32(0)), (dir.Atime == ^uint32(0)); cmt || cat {
	// 		st, e := os.Stat(fid.path)
	// 		if e != nil {
	// 			req.RespondError(toError(e))
	// 			return
	// 		}
	// 		switch cmt {
	// 		case true:
	// 			mt = st.ModTime()
	// 		default:
	// 			//at = time.Time(0)//atime(st.Sys().(*syscall.Stat_t))
	// 		}
	// 	}
	// 	e := os.Chtimes(fid.path, at, mt)
	// 	if e != nil {
	// 		req.RespondError(toError(e))
	// 		return
	// 	}
	// }

	req.RespondRwstat()
}
<|endoftext|>


<commit_before>// Copyright Istio 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 authz

import (
	tcppb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
	httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/networking"
	"istio.io/istio/pilot/pkg/networking/plugin"
	"istio.io/istio/pilot/pkg/security/authz/builder"
	"istio.io/istio/pilot/pkg/security/trustdomain"
	"istio.io/istio/pkg/spiffe"
	"istio.io/pkg/log"
)

var authzLog = log.RegisterScope("authorization", "Istio Authorization Policy", 0)

type ActionType int

const (
	// Local for action ALLOW, DENY and AUDIT and is enforced by Envoy RBAC filter.
	Local ActionType = iota
	// Custom action is enforced by Envoy ext_authz filter.
	Custom
)

// Plugin implements Istio Authorization
type Plugin struct {
	actionType ActionType
}

// NewPlugin returns an instance of the authorization plugin
func NewPlugin(actionType ActionType) plugin.Plugin {
	return Plugin{actionType: actionType}
}

// OnOutboundListener is called whenever a new outbound listener is added to the LDS output for a given service
// Can be used to add additional filters on the outbound path
func (p Plugin) OnOutboundListener(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.Router {
		// Only care about router.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

// OnInboundListener is called whenever a new listener is added to the LDS output for a given service
// Can be used to add additional filters or add more stuff to the HTTP connection manager
// on the inbound path
func (p Plugin) OnInboundListener(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.SidecarProxy {
		// Only care about sidecar.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

func (p Plugin) buildFilter(in *plugin.InputParams, mutable *networking.MutableObjects) {
	if in.Push == nil || in.Push.AuthzPolicies == nil {
		authzLog.Debugf("No authorization policy for %s", in.Node.ID)
		return
	}

	// TODO: Get trust domain from MeshConfig instead.
	// https://github.com/istio/istio/issues/17873
	tdBundle := trustdomain.NewBundle(spiffe.GetTrustDomain(), in.Push.Mesh.TrustDomainAliases)
	option := builder.Option{
		IsCustomBuilder: p.actionType == Custom,
		Logger:          &builder.AuthzLogger{},
	}
	defer option.Logger.Report(in)
	b := builder.New(tdBundle, in, option)
	if b == nil {
		return
	}

	// We will lazily build filters for tcp/http as needed
	httpBuilt := false
	tcpBuilt := false
	var httpFilters []*httppb.HttpFilter
	var tcpFilters []*tcppb.Filter

	for cnum := range mutable.FilterChains {
		switch mutable.FilterChains[cnum].ListenerProtocol {
		case networking.ListenerProtocolTCP:
			if !tcpBuilt {
				tcpFilters = b.BuildTCP()
				tcpBuilt = true
			}
			option.Logger.AppendDebugf("added %d TCP filters to filter chain %d", len(tcpFilters), cnum)
			mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, tcpFilters...)
		case networking.ListenerProtocolHTTP:
			if !httpBuilt {
				httpFilters = b.BuildHTTP()
				httpBuilt = true
			}
			option.Logger.AppendDebugf("added %d HTTP filters to filter chain %d", len(httpFilters), cnum)
			mutable.FilterChains[cnum].HTTP = append(mutable.FilterChains[cnum].HTTP, httpFilters...)
		}
	}
}

// OnInboundPassthrough is called whenever a new passthrough filter chain is added to the LDS output.
func (p Plugin) OnInboundPassthrough(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.SidecarProxy {
		// Only care about sidecar.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

func (p Plugin) InboundMTLSConfiguration(in *plugin.InputParams, passthrough bool) []plugin.MTLSSettings {
	return nil
}
<commit_msg>authz-plugin: get trust domain from MeshConfig to build filters (#32448)<commit_after>// Copyright Istio 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 authz

import (
	tcppb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
	httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"

	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/networking"
	"istio.io/istio/pilot/pkg/networking/plugin"
	"istio.io/istio/pilot/pkg/security/authz/builder"
	"istio.io/istio/pilot/pkg/security/trustdomain"
	"istio.io/pkg/log"
)

var authzLog = log.RegisterScope("authorization", "Istio Authorization Policy", 0)

type ActionType int

const (
	// Local for action ALLOW, DENY and AUDIT and is enforced by Envoy RBAC filter.
	Local ActionType = iota
	// Custom action is enforced by Envoy ext_authz filter.
	Custom
)

// Plugin implements Istio Authorization
type Plugin struct {
	actionType ActionType
}

// NewPlugin returns an instance of the authorization plugin
func NewPlugin(actionType ActionType) plugin.Plugin {
	return Plugin{actionType: actionType}
}

// OnOutboundListener is called whenever a new outbound listener is added to the LDS output for a given service
// Can be used to add additional filters on the outbound path
func (p Plugin) OnOutboundListener(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.Router {
		// Only care about router.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

// OnInboundListener is called whenever a new listener is added to the LDS output for a given service
// Can be used to add additional filters or add more stuff to the HTTP connection manager
// on the inbound path
func (p Plugin) OnInboundListener(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.SidecarProxy {
		// Only care about sidecar.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

func (p Plugin) buildFilter(in *plugin.InputParams, mutable *networking.MutableObjects) {
	if in.Push == nil || in.Push.AuthzPolicies == nil {
		authzLog.Debugf("No authorization policy for %s", in.Node.ID)
		return
	}

	meshConfig := in.Push.Mesh
	tdBundle := trustdomain.NewBundle(meshConfig.TrustDomain, meshConfig.TrustDomainAliases)
	option := builder.Option{
		IsCustomBuilder: p.actionType == Custom,
		Logger:          &builder.AuthzLogger{},
	}
	defer option.Logger.Report(in)
	b := builder.New(tdBundle, in, option)
	if b == nil {
		return
	}

	// We will lazily build filters for tcp/http as needed
	httpBuilt := false
	tcpBuilt := false
	var httpFilters []*httppb.HttpFilter
	var tcpFilters []*tcppb.Filter

	for cnum := range mutable.FilterChains {
		switch mutable.FilterChains[cnum].ListenerProtocol {
		case networking.ListenerProtocolTCP:
			if !tcpBuilt {
				tcpFilters = b.BuildTCP()
				tcpBuilt = true
			}
			option.Logger.AppendDebugf("added %d TCP filters to filter chain %d", len(tcpFilters), cnum)
			mutable.FilterChains[cnum].TCP = append(mutable.FilterChains[cnum].TCP, tcpFilters...)
		case networking.ListenerProtocolHTTP:
			if !httpBuilt {
				httpFilters = b.BuildHTTP()
				httpBuilt = true
			}
			option.Logger.AppendDebugf("added %d HTTP filters to filter chain %d", len(httpFilters), cnum)
			mutable.FilterChains[cnum].HTTP = append(mutable.FilterChains[cnum].HTTP, httpFilters...)
		}
	}
}

// OnInboundPassthrough is called whenever a new passthrough filter chain is added to the LDS output.
func (p Plugin) OnInboundPassthrough(in *plugin.InputParams, mutable *networking.MutableObjects) error {
	if in.Node.Type != model.SidecarProxy {
		// Only care about sidecar.
		return nil
	}

	p.buildFilter(in, mutable)
	return nil
}

func (p Plugin) InboundMTLSConfiguration(in *plugin.InputParams, passthrough bool) []plugin.MTLSSettings {
	return nil
}
<|endoftext|>