<commit_before>// Copyright 2020 The Ebiten 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.

// +build example

package main

import (
	"bytes"
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"math"
	"time"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/audio"
	"github.com/hajimehoshi/ebiten/v2/audio/vorbis"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	raudio "github.com/hajimehoshi/ebiten/v2/examples/resources/audio"
	"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
)

const (
	screenWidth  = 640
	screenHeight = 480
	sampleRate   = 22050
)

var img *ebiten.Image

var audioContext = audio.NewContext(sampleRate)

type Game struct {
	player    *audio.Player
	panstream *StereoPanStream

	// panning goes from -1 to 1
	// -1: 100% left channel, 0% right channel
	// 0: 100% both channels
	// 1: 0% left channel, 100% right channel
	panning float64

	count int
	xpos  float64
}

func (g *Game) initAudio() {
	if g.player != nil {
		return
	}

	// Decode an Ogg file.
	// oggS is a decoded io.ReadCloser and io.Seeker.
	oggS, err := vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
	if err != nil {
		log.Fatal(err)
	}

	// Wrap the raw audio with the StereoPanStream
	g.panstream = NewStereoPanStreamFromReader(oggS)

	g.player, err = audio.NewPlayer(audioContext, g.panstream)
	if err != nil {
		log.Fatal(err)
	}

	// Play the infinite-length stream. This never ends.
	g.player.Play()
}

// time is whithin the 0 ... 1 range
func lerp(a, b, t float64) float64 {
	return a*(1-t) + b*t
}

func (g *Game) Update() error {
	g.initAudio()
	g.count++
	r := float64(g.count) * ((1.0 / 60.0) * 2 * math.Pi) * 0.1 // full cycle every 10 seconds
	g.xpos = (float64(screenWidth) / 2) + math.Cos(r)*(float64(screenWidth)/2)
	g.panning = lerp(-1, 1, g.xpos/float64(screenWidth))
	g.panstream.SetPan(g.panning)
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	pos := g.player.Current()
	if pos > 5*time.Second {
		pos = (g.player.Current()-5*time.Second)%(4*time.Second) + 5*time.Second
	}
	msg := fmt.Sprintf(`TPS: %0.2f
This is an example using
stereo audio panning.
Current: %0.2f[s]
Panning: %.2f`, ebiten.CurrentTPS(), float64(pos)/float64(time.Second), g.panning)
	ebitenutil.DebugPrint(screen, msg)

	// draw image to show where the sound is at related to the screen
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(g.xpos-float64(img.Bounds().Dx()/2), screenHeight/2)
	screen.DrawImage(img, op)
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight
}

func main() {
	// Decode image from a byte slice instead of a file so that
	// this example works in any working directory.
	// If you want to use a file, there are some options:
	// 1) Use os.Open and pass the file to the image decoder.
	//    This is a very regular way, but doesn't work on browsers.
	// 2) Use ebitenutil.OpenFile and pass the file to the image decoder.
	//    This works even on browsers.
	// 3) Use ebitenutil.NewImageFromFile to create an ebiten.Image directly from a file.
	//    This also works on browsers.
	rawimg, _, err := image.Decode(bytes.NewReader(images.Ebiten_png))
	if err != nil {
		log.Fatal(err)
	}
	img = ebiten.NewImageFromImage(rawimg)

	ebiten.SetWindowSize(screenWidth, screenHeight)
	ebiten.SetWindowTitle("Audio Panning Loop (Ebiten Demo)")
	g := &Game{}
	if err := ebiten.RunGame(g); err != nil {
		log.Fatal(err)
	}
}

// StereoPanStream is an audio buffer that changes the stereo channel's signal
// based on the Panning.
type StereoPanStream struct {
	io.ReadSeeker
	pan float64 // -1: left; 0: center; 1: right
}

func (s *StereoPanStream) Read(p []byte) (n int, err error) {
	n, err = s.ReadSeeker.Read(p)
	if err != nil {
		return
	}

	// This implementation uses a linear scale, ranging from -1 to 1, for stereo or mono sounds.
	// If pan = 0.0, the balance for the sound in each speaker is at 100% left and 100% right.
	// When pan is -1.0, only the left channel of the stereo sound is audible, when pan is 1.0,
	// only the right channel of the stereo sound is audible.
	// https://docs.unity3d.com/ScriptReference/AudioSource-panStereo.html
	ls := math.Min(s.pan*-1+1, 1)
	rs := math.Min(s.pan+1, 1)
	for i := 0; i < len(p); i += 4 {
		lc := int16(float64(int16(p[i])|int16(p[i+1])<<8) * ls)
		rc := int16(float64(int16(p[i+2])|int16(p[i+3])<<8) * rs)

		p[i] = byte(lc)
		p[i+1] = byte(lc >> 8)
		p[i+2] = byte(rc)
		p[i+3] = byte(rc >> 8)
	}
	return
}

func (s *StereoPanStream) SetPan(pan float64) {
	s.pan = math.Min(math.Max(-1, pan), 1)
}

func (s *StereoPanStream) Pan() float64 {
	return s.pan
}

// NewStereoPanStreamFromReader returns a new StereoPanStream with a buffered src.
//
// The src's format must be linear PCM (16bits little endian, 2 channel stereo)
// without a header (e.g. RIFF header). The sample rate must be same as that
// of the audio context.
func NewStereoPanStreamFromReader(src io.ReadSeeker) *StereoPanStream {
	return &StereoPanStream{
		ReadSeeker: src,
	}
}
<commit_msg>examples/audiopanning: Bug fix: Fix several issues<commit_after>// Copyright 2020 The Ebiten 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.

// +build example

package main

import (
	"bytes"
	"fmt"
	"image"
	_ "image/png"
	"io"
	"log"
	"math"
	"time"

	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/audio"
	"github.com/hajimehoshi/ebiten/v2/audio/vorbis"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
	raudio "github.com/hajimehoshi/ebiten/v2/examples/resources/audio"
	"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
)

const (
	screenWidth  = 640
	screenHeight = 480
	sampleRate   = 22050
)

var img *ebiten.Image

var audioContext = audio.NewContext(sampleRate)

type Game struct {
	player    *audio.Player
	panstream *StereoPanStream

	// panning goes from -1 to 1
	// -1: 100% left channel, 0% right channel
	// 0: 100% both channels
	// 1: 0% left channel, 100% right channel
	panning float64

	count int
	xpos  float64
}

func (g *Game) initAudio() {
	if g.player != nil {
		return
	}

	// Decode an Ogg file.
	// oggS is a decoded io.ReadCloser and io.Seeker.
	oggS, err := vorbis.Decode(audioContext, bytes.NewReader(raudio.Ragtime_ogg))
	if err != nil {
		log.Fatal(err)
	}

	// Wrap the raw audio with the StereoPanStream
	g.panstream = NewStereoPanStreamFromReader(audio.NewInfiniteLoop(oggS, oggS.Length()))

	g.player, err = audio.NewPlayer(audioContext, g.panstream)
	if err != nil {
		log.Fatal(err)
	}

	// Play the infinite-length stream. This never ends.
	g.player.Play()
}

// time is whithin the 0 ... 1 range
func lerp(a, b, t float64) float64 {
	return a*(1-t) + b*t
}

func (g *Game) Update() error {
	g.initAudio()
	g.count++
	r := float64(g.count) * ((1.0 / 60.0) * 2 * math.Pi) * 0.1 // full cycle every 10 seconds
	g.xpos = (float64(screenWidth) / 2) + math.Cos(r)*(float64(screenWidth)/2)
	g.panning = lerp(-1, 1, g.xpos/float64(screenWidth))
	g.panstream.SetPan(g.panning)
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	pos := g.player.Current()
	msg := fmt.Sprintf(`TPS: %0.2f
This is an example using
stereo audio panning.
Current: %0.2f[s]
Panning: %.2f`, ebiten.CurrentTPS(), float64(pos)/float64(time.Second), g.panning)
	ebitenutil.DebugPrint(screen, msg)

	// draw image to show where the sound is at related to the screen
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(g.xpos-float64(img.Bounds().Dx()/2), screenHeight/2)
	screen.DrawImage(img, op)
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight
}

func main() {
	// Decode image from a byte slice instead of a file so that
	// this example works in any working directory.
	// If you want to use a file, there are some options:
	// 1) Use os.Open and pass the file to the image decoder.
	//    This is a very regular way, but doesn't work on browsers.
	// 2) Use ebitenutil.OpenFile and pass the file to the image decoder.
	//    This works even on browsers.
	// 3) Use ebitenutil.NewImageFromFile to create an ebiten.Image directly from a file.
	//    This also works on browsers.
	rawimg, _, err := image.Decode(bytes.NewReader(images.Ebiten_png))
	if err != nil {
		log.Fatal(err)
	}
	img = ebiten.NewImageFromImage(rawimg)

	ebiten.SetWindowSize(screenWidth, screenHeight)
	ebiten.SetWindowTitle("Audio Panning Loop (Ebiten Demo)")
	g := &Game{}
	if err := ebiten.RunGame(g); err != nil {
		log.Fatal(err)
	}
}

// StereoPanStream is an audio buffer that changes the stereo channel's signal
// based on the Panning.
type StereoPanStream struct {
	io.ReadSeeker
	pan float64 // -1: left; 0: center; 1: right
}

func (s *StereoPanStream) Read(p []byte) (n int, err error) {
	n, err = s.ReadSeeker.Read(p)
	if err != nil {
		return
	}

	// This implementation uses a linear scale, ranging from -1 to 1, for stereo or mono sounds.
	// If pan = 0.0, the balance for the sound in each speaker is at 100% left and 100% right.
	// When pan is -1.0, only the left channel of the stereo sound is audible, when pan is 1.0,
	// only the right channel of the stereo sound is audible.
	// https://docs.unity3d.com/ScriptReference/AudioSource-panStereo.html
	ls := math.Min(s.pan*-1+1, 1)
	rs := math.Min(s.pan+1, 1)
	for i := 0; i < len(p); i += 4 {
		lc := int16(float64(int16(p[i])|int16(p[i+1])<<8) * ls)
		rc := int16(float64(int16(p[i+2])|int16(p[i+3])<<8) * rs)

		p[i] = byte(lc)
		p[i+1] = byte(lc >> 8)
		p[i+2] = byte(rc)
		p[i+3] = byte(rc >> 8)
	}
	return
}

func (s *StereoPanStream) SetPan(pan float64) {
	s.pan = math.Min(math.Max(-1, pan), 1)
}

func (s *StereoPanStream) Pan() float64 {
	return s.pan
}

// NewStereoPanStreamFromReader returns a new StereoPanStream with a buffered src.
//
// The src's format must be linear PCM (16bits little endian, 2 channel stereo)
// without a header (e.g. RIFF header). The sample rate must be same as that
// of the audio context.
func NewStereoPanStreamFromReader(src io.ReadSeeker) *StereoPanStream {
	return &StereoPanStream{
		ReadSeeker: src,
	}
}
<|endoftext|>


<commit_before>package auth_test

import (
	"github.com/synapse-garden/sg-proto/auth"
	. "gopkg.in/check.v1"
)

func (s *AuthSuite) TestErrMissingSessionError(c *C) {
	c.Check(
		auth.ErrMissingSession("hello").Error(),
		Equals,
		"no such session `hello`",
	)
}

func (s *AuthSuite) TestErrTokenExpiredError(c *C) {
	c.Check(
		auth.ErrTokenExpired("hello").Error(),
		Equals,
		"no such session `hello`",
	)
}

func (s *AuthSuite) TestCheckToken(c *C) {
	// If the token is not in the database, return errmissing.
	// If there was an unknown error, return it.
	// If the token's expiration is before now, return errexpired.
	// Otherwise (it's current and present) return nil.
}

func (s *AuthSuite) TestNewSession(c *C) {
	// If an error occurred in store.Marshal, reset the values of
	// the given Session.

	// Otherwise, the new session with the new values should be
	// stored, and it should conform to the expected new values.
}

func (s *AuthSuite) TestNewToken(c *C) {
	// NewToken should generate a new v4 UUID dot Bytes for Bearer.
	// Otherwise, it should return nil.
}

func (s *AuthSuite) TestRefreshIfValid(c *C) {
	// If the given Refresh Token is not present in RefreshBucket,
	// return ErrMissingSession.  If an unknown error occurred,
	// return it.  Otherwise, call Refresh with the passed values.
}

func (s *AuthSuite) TestRefresh(c *C) {
	// Marshal the given Session into SessionBucket with the new
	// expiration and validFor, and the same Token.
}

func (s *AuthSuite) TestDeleteSession(c *C) {
	// DeleteSession should Delete the given Session from
	// SessionBucket, and the given Session's Refresh Token from
	// RefreshBucket.  If either value is not present, it should
	// not complain.
}
<commit_msg>Fix auth expired token error text<commit_after>package auth_test

import (
	"github.com/synapse-garden/sg-proto/auth"
	. "gopkg.in/check.v1"
)

func (s *AuthSuite) TestErrMissingSessionError(c *C) {
	c.Check(
		auth.ErrMissingSession("hello").Error(),
		Equals,
		"no such session `hello`",
	)
}

func (s *AuthSuite) TestErrTokenExpiredError(c *C) {
	c.Check(
		auth.ErrTokenExpired("hello").Error(),
		Equals,
		"session `hello` expired",
	)
}

func (s *AuthSuite) TestCheckToken(c *C) {
	// If the token is not in the database, return errmissing.
	// If there was an unknown error, return it.
	// If the token's expiration is before now, return errexpired.
	// Otherwise (it's current and present) return nil.
}

func (s *AuthSuite) TestNewSession(c *C) {
	// If an error occurred in store.Marshal, reset the values of
	// the given Session.

	// Otherwise, the new session with the new values should be
	// stored, and it should conform to the expected new values.
}

func (s *AuthSuite) TestNewToken(c *C) {
	// NewToken should generate a new v4 UUID dot Bytes for Bearer.
	// Otherwise, it should return nil.
}

func (s *AuthSuite) TestRefreshIfValid(c *C) {
	// If the given Refresh Token is not present in RefreshBucket,
	// return ErrMissingSession.  If an unknown error occurred,
	// return it.  Otherwise, call Refresh with the passed values.
}

func (s *AuthSuite) TestRefresh(c *C) {
	// Marshal the given Session into SessionBucket with the new
	// expiration and validFor, and the same Token.
}

func (s *AuthSuite) TestDeleteSession(c *C) {
	// DeleteSession should Delete the given Session from
	// SessionBucket, and the given Session's Refresh Token from
	// RefreshBucket.  If either value is not present, it should
	// not complain.
}
<|endoftext|>


<commit_before>package scripts

import (
	"github.com/salsita/salsaflow/config"
	"github.com/salsita/salsaflow/errs"
)

// Local configuration -------------------------------------------------------

type LocalConfig struct {
	Scripts struct {
		GetVersion string `yaml:"get_version"`
		SetVersion string `yaml:"set_version"`
	} `yaml:"scripts"`
}

// Configuration proxy ---------------------------------------------------------

type Config interface {
	GetVersionScriptRelativePath() string
	SetVersionScriptRelativePath() string
}

var configCache Config

func LoadConfig() (Config, error) {
	// Try the cache first.
	if configCache != nil {
		return configCache, nil
	}

	task := "Load scripts-related SalsaFlow config"

	// Parse the local config file.
	proxy := &configProxy{
		local: &LocalConfig{},
	}
	if err := config.UnmarshalLocalConfig(proxy.local); err != nil {
		return nil, errs.NewError(task, err, nil)
	}

	// Save the new instance into the cache and return.
	configCache = proxy
	return configCache, nil
}

type configProxy struct {
	local *LocalConfig
}

func (proxy *configProxy) GetVersionScriptRelativePath() string {
	return proxy.local.Scripts.GetVersion
}

func (proxy *configProxy) SetVersionScriptRelativePath() string {
	return proxy.local.Scripts.SetVersion
}
<commit_msg>scripts: Add validation to LocalConfig<commit_after>package scripts

import (
	"github.com/salsita/salsaflow/config"
	"github.com/salsita/salsaflow/errs"
)

// Local configuration -------------------------------------------------------

type LocalConfig struct {
	Scripts struct {
		GetVersion string `yaml:"get_version"`
		SetVersion string `yaml:"set_version"`
	} `yaml:"scripts"`
}

func (local *LocalConfig) validate() error {
	switch {
	case local.Scripts.GetVersion == "":
		return &config.ErrKeyNotSet{"scripts.get_version"}
	case local.Scripts.SetVersion == "":
		return &config.ErrKeyNotSet{"scripts.set_version"}
	default:
		return nil
	}
}

// Configuration proxy ---------------------------------------------------------

type Config interface {
	GetVersionScriptRelativePath() string
	SetVersionScriptRelativePath() string
}

var configCache Config

func LoadConfig() (Config, error) {
	// Try the cache first.
	if configCache != nil {
		return configCache, nil
	}

	task := "Load scripts-related SalsaFlow config"

	// Parse the local config file.
	proxy := &configProxy{
		local: &LocalConfig{},
	}
	if err := config.UnmarshalLocalConfig(proxy.local); err != nil {
		return nil, errs.NewError(task, err, nil)
	}
	if err := proxy.local.validate(); err != nil {
		return nil, err
	}

	// Save the new instance into the cache and return.
	configCache = proxy
	return configCache, nil
}

type configProxy struct {
	local *LocalConfig
}

func (proxy *configProxy) GetVersionScriptRelativePath() string {
	return proxy.local.Scripts.GetVersion
}

func (proxy *configProxy) SetVersionScriptRelativePath() string {
	return proxy.local.Scripts.SetVersion
}
<|endoftext|>


<commit_before>// Copyright 2013 Alexandre Fiori
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// autogzip provides on-the-fly gzip encoding for http servers.

package autogzip

import (
	"compress/gzip"
	"github.com/fiorix/go-web/http"
	"io"
	"strings"
)

type ResponseWriter struct {
	io.Writer
	http.ResponseWriter
}

func (w ResponseWriter) Write(b []byte) (int, error) {
	return w.Writer.Write(b)
}

// Handle provides on-the-fly gzip encoding for other handlers.
//
// Usage:
//
//	func IndexHandler(w http.ResponseWriter, req *http.Request) {
//		fmt.Fprintln(w, "Hello, world")
//	}
//
//	func main() {
//		http.HandleFunc("/", IndexHandler)
//		http.ListenAndServe(":8080", autogzip.Handle(http.DefaultServeMux))
//	}
func Handle(h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(ResponseWriter{Writer: gz, ResponseWriter: w}, r)
	}
}
<commit_msg>support for HandlerFunc<commit_after>// Copyright 2013 Alexandre Fiori
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// autogzip provides on-the-fly gzip encoding for http servers.

package autogzip

import (
	"compress/gzip"
	"github.com/fiorix/go-web/http"
	"io"
	"strings"
)

type ResponseWriter struct {
	io.Writer
	http.ResponseWriter
}

func (w ResponseWriter) Write(b []byte) (int, error) {
	return w.Writer.Write(b)
}

// Handle provides on-the-fly gzip encoding for other handlers.
//
// Usage:
//
//	func DL1Handler(w http.ResponseWriter, req *http.Request) {
//		fmt.Fprintln(w, "foobar")
//	}
//
//	func DL2Handler(w http.ResponseWriter, req *http.Request) {
//		fmt.Fprintln(w, "zzz")
//	}
//
//
//	func main() {
//		http.HandleFunc("/download1", DL1Handler)
//		http.HandleFunc("/download2", DL2Handler)
//		http.ListenAndServe(":8080", autogzip.Handle(http.DefaultServeMux))
//	}
func Handle(h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			h.ServeHTTP(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		h.ServeHTTP(ResponseWriter{Writer: gz, ResponseWriter: w}, r)
	}
}

// HandleFunc provides on-the-fly gzip encoding for other handler functions.
//
// Usage:
//
//	func IndexHandler(w http.ResponseWriter, req *http.Request) {
//		fmt.Fprintln(w, "Hello, world")
//	}
//
//	func DL1Handler(w http.ResponseWriter, req *http.Request) {
//		fmt.Fprintln(w, "foobar")
//	}
//
//	func main() {
//		http.HandleFunc("/", IndexHandler)
//		http.HandleFunc("/download1", autogzip.HandleFunc(DL1Handler))
//		http.ListenAndServe(":8080", nil)
//	}
func HandleFunc(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
			fn(w, r)
			return
		}
		w.Header().Set("Content-Encoding", "gzip")
		gz := gzip.NewWriter(w)
		defer gz.Close()
		fn(ResponseWriter{Writer: gz, ResponseWriter: w}, r)
	}
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go 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 main

import (
	"go/ast"
	"reflect"
)

type compositeLitFinder struct{}

func (f *compositeLitFinder) Visit(node interface{}) ast.Visitor {
	if outer, ok := node.(*ast.CompositeLit); ok {
		// array, slice, and map composite literals may be simplified
		var eltType ast.Expr
		switch typ := outer.Type.(type) {
		case *ast.ArrayType:
			eltType = typ.Elt
		case *ast.MapType:
			eltType = typ.Value
		}

		if eltType != nil {
			typ := reflect.NewValue(eltType)
			for _, x := range outer.Elts {
				// look at value of indexed/named elements
				if t, ok := x.(*ast.KeyValueExpr); ok {
					x = t.Value
				}
				simplify(x)
				// if the element is a composite literal and its literal type
				// matches the outer literal's element type exactly, the inner
				// literal type may be omitted
				if inner, ok := x.(*ast.CompositeLit); ok {
					if match(nil, typ, reflect.NewValue(inner.Type)) {
						inner.Type = nil
					}
				}
			}

			// node was simplified - stop walk
			return nil
		}
	}

	// not a composite literal or not simplified - continue walk
	return f
}

func simplify(node interface{}) {
	var f compositeLitFinder
	ast.Walk(&f, node)
}
<commit_msg>gofmt: simplify "x, _ = range y" to "x = range y"<commit_after>// Copyright 2010 The Go 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 main

import (
	"go/ast"
	"reflect"
)

type simplifier struct{}

func (s *simplifier) Visit(node interface{}) ast.Visitor {
	switch n := node.(type) {
	case *ast.CompositeLit:
		// array, slice, and map composite literals may be simplified
		outer := n
		var eltType ast.Expr
		switch typ := outer.Type.(type) {
		case *ast.ArrayType:
			eltType = typ.Elt
		case *ast.MapType:
			eltType = typ.Value
		}

		if eltType != nil {
			typ := reflect.NewValue(eltType)
			for _, x := range outer.Elts {
				// look at value of indexed/named elements
				if t, ok := x.(*ast.KeyValueExpr); ok {
					x = t.Value
				}
				simplify(x)
				// if the element is a composite literal and its literal type
				// matches the outer literal's element type exactly, the inner
				// literal type may be omitted
				if inner, ok := x.(*ast.CompositeLit); ok {
					if match(nil, typ, reflect.NewValue(inner.Type)) {
						inner.Type = nil
					}
				}
			}

			// node was simplified - stop walk (there are no subnodes to simplify)
			return nil
		}

	case *ast.RangeStmt:
		// range of the form: for x, _ = range v {...}
		// can be simplified to: for x = range v {...}
		if n.Value != nil {
			if ident, ok := n.Value.(*ast.Ident); ok && ident.Name == "_" {
				n.Value = nil
			}
		}
	}

	return s
}

func simplify(node interface{}) {
	var s simplifier
	ast.Walk(&s, node)
}
<|endoftext|>


<commit_before>package awsiam

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"

	"code.cloudfoundry.org/lager"
	awsarn "github.com/aws/aws-sdk-go/aws/arn"
	iampolicy "github.com/minio/minio/pkg/iam/policy"
	"github.com/minio/minio/pkg/madmin"
	"github.com/pkg/errors"
	"github.com/sethvargo/go-password/password"
)

type MinioUser struct {
	madmClnt *madmin.AdminClient
	logger   lager.Logger
	// preserved for subsequent connections (generating user-centric access keys)
	endpoint        string
	accessKey       string
	secretKey       string
	secure          bool
	customTransport http.RoundTripper
}

func NewMinioUser(
	logger lager.Logger,
	endpoint string,
	accessKey string,
	secretKey string,
	secure bool,
	customTransport http.RoundTripper,
) *MinioUser {
	madmClnt, err := newMinioAdminClient(endpoint, accessKey, secretKey, secure, customTransport)
	if err != nil {
		logger.Fatal("unable to create MinIO admin client", err)
	}

	return &MinioUser{
		madmClnt:        madmClnt,
		logger:          logger.Session("minio-user"),
		endpoint:        endpoint,
		accessKey:       accessKey,
		secretKey:       secretKey,
		secure:          secure,
		customTransport: customTransport,
	}
}

func newMinioAdminClient(endpoint, accessKey, secretKey string, secure bool, customTransport http.RoundTripper) (*madmin.AdminClient, error) {
	madmClnt, err := madmin.New(endpoint, accessKey, secretKey, secure)
	if err != nil {
		return nil, fmt.Errorf("Unable to connect to MinIO: %s", err)
	}
	madmClnt.SetCustomTransport(customTransport)
	return madmClnt, nil
}

func (i *MinioUser) Describe(userName string) (UserDetails, error) {
	// We simply echo data back; note that presence of the user is tested...
	userDetails := UserDetails{
		UserName: userName,
		UserARN:  toUserARN(userName),
		UserID:   userName,
	}

	i.logger.Debug("get-user", lager.Data{"input": userName})

	_, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return userDetails, err
	}

	i.logger.Debug("get-user", lager.Data{"output": userDetails})

	return userDetails, nil
}

func (i *MinioUser) Create(userName, iamPath string) (string, error) {
	i.logger.Debug("create-user", lager.Data{"userName": userName, "iamPath": iamPath})

	secretKey, err := password.Generate(64, 10, 0, false, true)
	if err != nil {
		i.logger.Error("password-generator", err)
		return "", err
	}

	err = i.madmClnt.AddUser(context.Background(), userName, secretKey)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", err
	}
	i.logger.Debug("create-user", lager.Data{"output": userName})

	return toUserARN(userName), nil
}

func (i *MinioUser) Delete(userName string) error {
	i.logger.Debug("delete-user", lager.Data{"userName": userName})

	err := i.madmClnt.RemoveUser(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("delete-user", lager.Data{"output": userName})

	return nil
}

func (i *MinioUser) createUserClient(userName string) (*madmin.AdminClient, error) {
	// UserInfo does not appear to respond with actual SecretKey
	// ... so we just set it again so we can auth to it and create a key!
	secretKey, err := password.Generate(64, 10, 0, false, true)
	if err != nil {
		i.logger.Error("createUserClient/password-generator", err)
		return nil, err
	}

	err = i.madmClnt.SetUser(context.Background(), userName, secretKey, madmin.AccountEnabled)
	if err != nil {
		i.logger.Error("createUserClient/minio-error", err)
		return nil, err
	}

	// Connect as alternate user
	return newMinioAdminClient(i.endpoint, userName, secretKey, i.secure, i.customTransport)
}

func (i *MinioUser) ListAccessKeys(userName string) ([]string, error) {
	i.logger.Debug("list-access-keys", lager.Data{"input": userName})

	var accessKeys []string

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return accessKeys, err
	}

	accounts, err := userClient.ListServiceAccounts(context.Background())
	if err != nil {
		i.logger.Error("minio-error", err)
		return accessKeys, err
	}

	i.logger.Debug("list-access-keys", lager.Data{"output": accounts.Accounts})

	return accounts.Accounts, nil
}

func (i *MinioUser) CreateAccessKey(userName string) (string, string, error) {
	i.logger.Debug("create-access-key", lager.Data{"input": userName})

	info, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	var policy *iampolicy.Policy
	if info.PolicyName != "" {
		policy, err = i.madmClnt.InfoCannedPolicy(context.Background(), info.PolicyName)
		if err != nil {
			i.logger.Error("minio-error", err)
			return "", "", err
		}
	}

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	creds, err := userClient.AddServiceAccount(context.Background(), policy)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	i.logger.Debug("create-access-key", lager.Data{"output": creds.AccessKey})

	return creds.AccessKey, creds.SecretKey, nil
}

func (i *MinioUser) DeleteAccessKey(userName, accessKeyID string) error {
	i.logger.Debug("delete-access-key", lager.Data{"input": userName})

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	err = userClient.DeleteServiceAccount(context.Background(), accessKeyID)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("delete-access-key", lager.Data{"output": "N/A"})

	return nil
}

func (i *MinioUser) CreatePolicy(policyName, iamPath, policyTemplate string, resources []string) (string, error) {
	i.logger.Debug("create-policy", lager.Data{"policyName": policyName, "iamPath": iamPath, "policyTemplate": policyTemplate, "resources": resources})

	tmpl, err := template.New("policy").Funcs(template.FuncMap{
		"resources": func(suffix string) string {
			resourcePaths := make([]string, len(resources))
			for idx, resource := range resources {
				resourcePaths[idx] = resource + suffix
			}
			marshaled, _ := json.Marshal(resourcePaths)
			return string(marshaled)
		},
	}).Parse(policyTemplate)
	if err != nil {
		i.logger.Error("parse-error", err)
		return "", err
	}
	policy := bytes.Buffer{}
	err = tmpl.Execute(&policy, map[string]interface{}{
		"Resource":  resources[0],
		"Resources": resources,
	})
	if err != nil {
		i.logger.Error("template-error", err)
		return "", err
	}

	policyInput := iampolicy.Policy{}
	err = json.Unmarshal(policy.Bytes(), &policyInput)
	if err != nil {
		i.logger.Error("unmarshal-error", err)
		return "", err
	}
	i.logger.Debug("create-policy", lager.Data{"input": policyInput})

	err = i.madmClnt.AddCannedPolicy(context.Background(), policyName, &policyInput)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", err
	}

	i.logger.Debug("create-policy", lager.Data{"output": "created"})

	return toPolicyARN(policyName), nil
}

func (i *MinioUser) DeletePolicy(policyARN string) error {
	i.logger.Debug("delete-policy", lager.Data{"input": policyARN})

	policy, err := fromPolicyARN(policyARN)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	err = i.madmClnt.RemoveCannedPolicy(context.Background(), policy)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("delete-policy", lager.Data{"output": "removed"})

	return nil
}

func (i *MinioUser) ListAttachedUserPolicies(userName, iamPath string) ([]string, error) {
	var userPolicies []string

	i.logger.Debug("list-attached-user-policies", lager.Data{"userName": userName, "iamPath": iamPath})

	userInfo, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return userPolicies, err
	}
	i.logger.Debug("list-attached-user-policies", lager.Data{"output": userInfo.PolicyName})

	userPolicies = append(userPolicies, userInfo.PolicyName)
	return userPolicies, nil
}

func (i *MinioUser) AttachUserPolicy(userName, policyARN string) error {
	i.logger.Debug("attach-user-policy", lager.Data{"userName": userName, "policyARN": policyARN})

	policy, err := fromPolicyARN(policyARN)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	err = i.madmClnt.SetPolicy(context.Background(), policy, userName, false)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("attach-user-policy", lager.Data{"output": "success"})

	return nil
}

func (i *MinioUser) DetachUserPolicy(userName, policyARN string) error {
	i.logger.Debug("detach-user-policy", lager.Data{"userName": userName, "policyARN": policyARN})

	err := i.madmClnt.SetPolicy(context.Background(), "", userName, false)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("detach-user-policy", lager.Data{"output": "success"})

	return nil
}

func toUserARN(userName string) string {
	return toARN("user", userName)
}
func toPolicyARN(policyName string) string {
	return toARN("policy", policyName)
}
func toARN(resourceType, resource string) string {
	return fmt.Sprintf("arn:aws:iam:::%s/%s", resourceType, resource)
}

func fromPolicyARN(policyARN string) (string, error) {
	return fromARN("policy", policyARN)
}
func fromARN(resourceType, theARN string) (string, error) {
	arn, err := awsarn.Parse(theARN)
	if err != nil {
		return "", nil
	}

	prefix := fmt.Sprintf("%s/", resourceType)
	if !strings.HasPrefix(arn.Resource, prefix) {
		return "", errors.Errorf("Not a %s ARN: %s", resourceType, theARN)
	}

	return strings.ReplaceAll(arn.Resource, prefix, ""), nil
}
<commit_msg>tweaking error log messages; testing for an ARN when decoding one<commit_after>package awsiam

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"text/template"

	"code.cloudfoundry.org/lager"
	awsarn "github.com/aws/aws-sdk-go/aws/arn"
	iampolicy "github.com/minio/minio/pkg/iam/policy"
	"github.com/minio/minio/pkg/madmin"
	"github.com/pkg/errors"
	"github.com/sethvargo/go-password/password"
)

type MinioUser struct {
	madmClnt *madmin.AdminClient
	logger   lager.Logger
	// preserved for subsequent connections (generating user-centric access keys)
	endpoint        string
	accessKey       string
	secretKey       string
	secure          bool
	customTransport http.RoundTripper
}

func NewMinioUser(
	logger lager.Logger,
	endpoint string,
	accessKey string,
	secretKey string,
	secure bool,
	customTransport http.RoundTripper,
) *MinioUser {
	madmClnt, err := newMinioAdminClient(endpoint, accessKey, secretKey, secure, customTransport)
	if err != nil {
		logger.Fatal("unable to create MinIO admin client", err)
	}

	return &MinioUser{
		madmClnt:        madmClnt,
		logger:          logger.Session("minio-user"),
		endpoint:        endpoint,
		accessKey:       accessKey,
		secretKey:       secretKey,
		secure:          secure,
		customTransport: customTransport,
	}
}

func newMinioAdminClient(endpoint, accessKey, secretKey string, secure bool, customTransport http.RoundTripper) (*madmin.AdminClient, error) {
	madmClnt, err := madmin.New(endpoint, accessKey, secretKey, secure)
	if err != nil {
		return nil, fmt.Errorf("Unable to connect to MinIO: %s", err)
	}
	madmClnt.SetCustomTransport(customTransport)
	return madmClnt, nil
}

func (i *MinioUser) Describe(userName string) (UserDetails, error) {
	// We simply echo data back; note that presence of the user is tested...
	userDetails := UserDetails{
		UserName: userName,
		UserARN:  toUserARN(userName),
		UserID:   userName,
	}

	i.logger.Debug("get-user", lager.Data{"input": userName})

	_, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return userDetails, err
	}

	i.logger.Debug("get-user", lager.Data{"output": userDetails})

	return userDetails, nil
}

func (i *MinioUser) Create(userName, iamPath string) (string, error) {
	i.logger.Debug("create-user", lager.Data{"userName": userName, "iamPath": iamPath})

	secretKey, err := password.Generate(64, 10, 0, false, true)
	if err != nil {
		i.logger.Error("password-generator", err)
		return "", err
	}

	err = i.madmClnt.AddUser(context.Background(), userName, secretKey)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", err
	}
	i.logger.Debug("create-user", lager.Data{"output": userName})

	return toUserARN(userName), nil
}

func (i *MinioUser) Delete(userName string) error {
	i.logger.Debug("delete-user", lager.Data{"userName": userName})

	err := i.madmClnt.RemoveUser(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("delete-user", lager.Data{"output": userName})

	return nil
}

func (i *MinioUser) createUserClient(userName string) (*madmin.AdminClient, error) {
	// UserInfo does not appear to respond with actual SecretKey
	// ... so we just set it again so we can auth to it and create a key!
	secretKey, err := password.Generate(64, 10, 0, false, true)
	if err != nil {
		i.logger.Error("createUserClient/password-generator", err)
		return nil, err
	}

	err = i.madmClnt.SetUser(context.Background(), userName, secretKey, madmin.AccountEnabled)
	if err != nil {
		i.logger.Error("createUserClient/minio-error", err)
		return nil, err
	}

	// Connect as alternate user
	return newMinioAdminClient(i.endpoint, userName, secretKey, i.secure, i.customTransport)
}

func (i *MinioUser) ListAccessKeys(userName string) ([]string, error) {
	i.logger.Debug("list-access-keys", lager.Data{"input": userName})

	var accessKeys []string

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return accessKeys, err
	}

	accounts, err := userClient.ListServiceAccounts(context.Background())
	if err != nil {
		i.logger.Error("minio-error", err)
		return accessKeys, err
	}

	i.logger.Debug("list-access-keys", lager.Data{"output": accounts.Accounts})

	return accounts.Accounts, nil
}

func (i *MinioUser) CreateAccessKey(userName string) (string, string, error) {
	i.logger.Debug("create-access-key", lager.Data{"input": userName})

	info, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	var policy *iampolicy.Policy
	if info.PolicyName != "" {
		policy, err = i.madmClnt.InfoCannedPolicy(context.Background(), info.PolicyName)
		if err != nil {
			i.logger.Error("minio-error", err)
			return "", "", err
		}
	}

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	creds, err := userClient.AddServiceAccount(context.Background(), policy)
	if err != nil {
		i.logger.Error("minio-error", err)
		return "", "", err
	}

	i.logger.Debug("create-access-key", lager.Data{"output": creds.AccessKey})

	return creds.AccessKey, creds.SecretKey, nil
}

func (i *MinioUser) DeleteAccessKey(userName, accessKeyID string) error {
	i.logger.Debug("delete-access-key", lager.Data{"input": userName})

	// Connect as alternate user
	userClient, err := i.createUserClient(userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	err = userClient.DeleteServiceAccount(context.Background(), accessKeyID)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("delete-access-key", lager.Data{"output": "N/A"})

	return nil
}

func (i *MinioUser) CreatePolicy(policyName, iamPath, policyTemplate string, resources []string) (string, error) {
	i.logger.Debug("create-policy", lager.Data{"policyName": policyName, "iamPath": iamPath, "policyTemplate": policyTemplate, "resources": resources})

	tmpl, err := template.New("policy").Funcs(template.FuncMap{
		"resources": func(suffix string) string {
			resourcePaths := make([]string, len(resources))
			for idx, resource := range resources {
				resourcePaths[idx] = resource + suffix
			}
			marshaled, _ := json.Marshal(resourcePaths)
			return string(marshaled)
		},
	}).Parse(policyTemplate)
	if err != nil {
		i.logger.Error("parse-error", err)
		return "", err
	}
	policy := bytes.Buffer{}
	err = tmpl.Execute(&policy, map[string]interface{}{
		"Resource":  resources[0],
		"Resources": resources,
	})
	if err != nil {
		i.logger.Error("template-error", err)
		return "", err
	}

	policyInput := iampolicy.Policy{}
	err = json.Unmarshal(policy.Bytes(), &policyInput)
	if err != nil {
		i.logger.Error("unmarshal-error", err)
		return "", err
	}
	i.logger.Debug("create-policy", lager.Data{"input": policyInput})

	err = i.madmClnt.AddCannedPolicy(context.Background(), policyName, &policyInput)
	if err != nil {
		i.logger.Error("minio-add-policy-error", err)
		return "", err
	}

	i.logger.Debug("create-policy", lager.Data{"output": "created"})

	return toPolicyARN(policyName), nil
}

func (i *MinioUser) DeletePolicy(policyARN string) error {
	i.logger.Debug("delete-policy", lager.Data{"input": policyARN})

	policy, err := fromPolicyARN(policyARN)
	if err != nil {
		i.logger.Error("minio-arn-error", err)
		return err
	}

	err = i.madmClnt.RemoveCannedPolicy(context.Background(), policy)
	if err != nil {
		i.logger.Error("minio-remove-policy-error", err)
		return err
	}

	i.logger.Debug("delete-policy", lager.Data{"output": "removed"})

	return nil
}

func (i *MinioUser) ListAttachedUserPolicies(userName, iamPath string) ([]string, error) {
	var userPolicies []string

	i.logger.Debug("list-attached-user-policies", lager.Data{"userName": userName, "iamPath": iamPath})

	userInfo, err := i.madmClnt.GetUserInfo(context.Background(), userName)
	if err != nil {
		i.logger.Error("minio-error", err)
		return userPolicies, err
	}
	i.logger.Debug("list-attached-user-policies", lager.Data{"output": userInfo.PolicyName})

	userPolicies = append(userPolicies, userInfo.PolicyName)
	return userPolicies, nil
}

func (i *MinioUser) AttachUserPolicy(userName, policyARN string) error {
	i.logger.Debug("attach-user-policy", lager.Data{"userName": userName, "policyARN": policyARN})

	policy, err := fromPolicyARN(policyARN)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	err = i.madmClnt.SetPolicy(context.Background(), policy, userName, false)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("attach-user-policy", lager.Data{"output": "success"})

	return nil
}

func (i *MinioUser) DetachUserPolicy(userName, policyARN string) error {
	i.logger.Debug("detach-user-policy", lager.Data{"userName": userName, "policyARN": policyARN})

	err := i.madmClnt.SetPolicy(context.Background(), "", userName, false)
	if err != nil {
		i.logger.Error("minio-error", err)
		return err
	}

	i.logger.Debug("detach-user-policy", lager.Data{"output": "success"})

	return nil
}

func toUserARN(userName string) string {
	return toARN("user", userName)
}
func toPolicyARN(policyName string) string {
	return toARN("policy", policyName)
}
func toARN(resourceType, resource string) string {
	return fmt.Sprintf("arn:aws:iam:::%s/%s", resourceType, resource)
}

func fromPolicyARN(policyARN string) (string, error) {
	return fromARN("policy", policyARN)
}
func fromARN(resourceType, theARN string) (string, error) {
	// Short-circuit in case we get non-ARN's...
	if !awsarn.IsARN(theARN) {
		return theARN, nil
	}

	arn, err := awsarn.Parse(theARN)
	if err != nil {
		return "", err
	}

	prefix := fmt.Sprintf("%s/", resourceType)
	if !strings.HasPrefix(arn.Resource, prefix) {
		return "", errors.Errorf("Not a %s ARN: %s", resourceType, theARN)
	}

	return strings.ReplaceAll(arn.Resource, prefix, ""), nil
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2017] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package project

import (
	"time"
	"github.com/satori/go.uuid"
)

type Project struct {
	ID        uuid.UUID `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Created     time.Time `json:"created"`
	Updated     time.Time `json:"updated"`
}

type ProjectList []Project
<commit_msg>Fix project logic<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2017] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package project

import (
	"github.com/satori/go.uuid"
	"time"
)

type Project struct {
	ID          uuid.UUID `json:"id"`
	Name        string    `json:"name"`
	Description string    `json:"description"`
	Created     time.Time `json:"created"`
	Updated     time.Time `json:"updated"`
}

type ProjectList []Project
<|endoftext|>


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

import (
	"strings"

	"k8s.io/apiserver/pkg/authentication/user"
)

// NewDefaultNodeIdentifier returns a default NodeIdentifier implementation,
// which returns isNode=true if the user groups contain the system:nodes group
// and the user name matches the format system:node:<nodeName>, and populates
// nodeName if isNode is true
func NewDefaultNodeIdentifier() NodeIdentifier {
	return defaultNodeIdentifier{}
}

// defaultNodeIdentifier implements NodeIdentifier
type defaultNodeIdentifier struct{}

// nodeUserNamePrefix is the prefix for usernames in the form `system:node:<nodeName>`
const nodeUserNamePrefix = "system:node:"

// NodeIdentity returns isNode=true if the user groups contain the system:nodes
// group and the user name matches the format system:node:<nodeName>, and
// populates nodeName if isNode is true
func (defaultNodeIdentifier) NodeIdentity(u user.Info) (string, bool) {
	// Make sure we're a node, and can parse the node name
	if u == nil {
		return "", false
	}

	userName := u.GetName()
	if !strings.HasPrefix(userName, nodeUserNamePrefix) {
		return "", false
	}

	nodeName := strings.TrimPrefix(userName, nodeUserNamePrefix)

	isNode := false
	for _, g := range u.GetGroups() {
		if g == user.NodesGroup {
			isNode = true
			break
		}
	}
	if !isNode {
		return "", false
	}

	return nodeName, isNode
}
<commit_msg>Only do string trim when it's necessary<commit_after>/*
Copyright 2017 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 nodeidentifier

import (
	"strings"

	"k8s.io/apiserver/pkg/authentication/user"
)

// NewDefaultNodeIdentifier returns a default NodeIdentifier implementation,
// which returns isNode=true if the user groups contain the system:nodes group
// and the user name matches the format system:node:<nodeName>, and populates
// nodeName if isNode is true
func NewDefaultNodeIdentifier() NodeIdentifier {
	return defaultNodeIdentifier{}
}

// defaultNodeIdentifier implements NodeIdentifier
type defaultNodeIdentifier struct{}

// nodeUserNamePrefix is the prefix for usernames in the form `system:node:<nodeName>`
const nodeUserNamePrefix = "system:node:"

// NodeIdentity returns isNode=true if the user groups contain the system:nodes
// group and the user name matches the format system:node:<nodeName>, and
// populates nodeName if isNode is true
func (defaultNodeIdentifier) NodeIdentity(u user.Info) (string, bool) {
	// Make sure we're a node, and can parse the node name
	if u == nil {
		return "", false
	}

	userName := u.GetName()
	if !strings.HasPrefix(userName, nodeUserNamePrefix) {
		return "", false
	}

	isNode := false
	for _, g := range u.GetGroups() {
		if g == user.NodesGroup {
			isNode = true
			break
		}
	}
	if !isNode {
		return "", false
	}

	nodeName := strings.TrimPrefix(userName, nodeUserNamePrefix)
	return nodeName, true
}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 downloader

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"

	"helm.sh/helm/v3/internal/experimental/registry"
	"helm.sh/helm/v3/internal/fileutil"
	"helm.sh/helm/v3/internal/urlutil"
	"helm.sh/helm/v3/pkg/getter"
	"helm.sh/helm/v3/pkg/helmpath"
	"helm.sh/helm/v3/pkg/provenance"
	"helm.sh/helm/v3/pkg/repo"
)

// VerificationStrategy describes a strategy for determining whether to verify a chart.
type VerificationStrategy int

const (
	// VerifyNever will skip all verification of a chart.
	VerifyNever VerificationStrategy = iota
	// VerifyIfPossible will attempt a verification, it will not error if verification
	// data is missing. But it will not stop processing if verification fails.
	VerifyIfPossible
	// VerifyAlways will always attempt a verification, and will fail if the
	// verification fails.
	VerifyAlways
	// VerifyLater will fetch verification data, but not do any verification.
	// This is to accommodate the case where another step of the process will
	// perform verification.
	VerifyLater
)

// ErrNoOwnerRepo indicates that a given chart URL can't be found in any repos.
var ErrNoOwnerRepo = errors.New("could not find a repo containing the given URL")

// ChartDownloader handles downloading a chart.
//
// It is capable of performing verifications on charts as well.
type ChartDownloader struct {
	// Out is the location to write warning and info messages.
	Out io.Writer
	// Verify indicates what verification strategy to use.
	Verify VerificationStrategy
	// Keyring is the keyring file used for verification.
	Keyring string
	// Getter collection for the operation
	Getters getter.Providers
	// Options provide parameters to be passed along to the Getter being initialized.
	Options          []getter.Option
	RegistryClient   *registry.Client
	RepositoryConfig string
	RepositoryCache  string
}

// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
//
// If Verify is set to VerifyNever, the verification will be nil.
// If Verify is set to VerifyIfPossible, this will return a verification (or nil on failure), and print a warning on failure.
// If Verify is set to VerifyAlways, this will return a verification or an error if the verification fails.
// If Verify is set to VerifyLater, this will download the prov file (if it exists), but not verify it.
//
// For VerifyNever and VerifyIfPossible, the Verification may be empty.
//
// Returns a string path to the location where the file was downloaded and a verification
// (if provenance was verified), or an error if something bad happened.
func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) {
	u, err := c.ResolveChartVersion(ref, version)
	if err != nil {
		return "", nil, err
	}

	g, err := c.Getters.ByScheme(u.Scheme)
	if err != nil {
		return "", nil, err
	}

	data, err := g.Get(u.String(), c.Options...)
	if err != nil {
		return "", nil, err
	}

	name := filepath.Base(u.Path)
	if u.Scheme == "oci" {
		name = fmt.Sprintf("%s-%s.tgz", name, version)
	}

	destfile := filepath.Join(dest, name)
	if err := fileutil.AtomicWriteFile(destfile, data, 0644); err != nil {
		return destfile, nil, err
	}

	// If provenance is requested, verify it.
	ver := &provenance.Verification{}
	if c.Verify > VerifyNever {
		body, err := g.Get(u.String() + ".prov")
		if err != nil {
			if c.Verify == VerifyAlways {
				return destfile, ver, errors.Errorf("failed to fetch provenance %q", u.String()+".prov")
			}
			fmt.Fprintf(c.Out, "WARNING: Verification not found for %s: %s\n", ref, err)
			return destfile, ver, nil
		}
		provfile := destfile + ".prov"
		if err := fileutil.AtomicWriteFile(provfile, body, 0644); err != nil {
			return destfile, nil, err
		}

		if c.Verify != VerifyLater {
			ver, err = VerifyChart(destfile, c.Keyring)
			if err != nil {
				// Fail always in this case, since it means the verification step
				// failed.
				return destfile, ver, err
			}
		}
	}
	return destfile, ver, nil
}

// ResolveChartVersion resolves a chart reference to a URL.
//
// It returns the URL and sets the ChartDownloader's Options that can fetch
// the URL using the appropriate Getter.
//
// A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path.
//
// A version is a SemVer string (1.2.3-beta.1+f334a6789).
//
//	- For fully qualified URLs, the version will be ignored (since URLs aren't versioned)
//	- For a chart reference
//		* If version is non-empty, this will return the URL for that version
//		* If version is empty, this will return the URL for the latest version
//		* If no version can be found, an error is returned
func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) {
	u, err := url.Parse(ref)
	if err != nil {
		return nil, errors.Errorf("invalid chart URL format: %s", ref)
	}
	c.Options = append(c.Options, getter.WithURL(ref))

	rf, err := loadRepoConfig(c.RepositoryConfig)
	if err != nil {
		return u, err
	}

	if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 {
		// In this case, we have to find the parent repo that contains this chart
		// URL. And this is an unfortunate problem, as it requires actually going
		// through each repo cache file and finding a matching URL. But basically
		// we want to find the repo in case we have special SSL cert config
		// for that repo.

		rc, err := c.scanReposForURL(ref, rf)
		if err != nil {
			// If there is no special config, return the default HTTP client and
			// swallow the error.
			if err == ErrNoOwnerRepo {
				return u, nil
			}
			return u, err
		}

		// If we get here, we don't need to go through the next phase of looking
		// up the URL. We have it already. So we just set the parameters and return.
		c.Options = append(
			c.Options,
			getter.WithURL(rc.URL),
		)
		if rc.CertFile != "" || rc.KeyFile != "" || rc.CAFile != "" {
			c.Options = append(c.Options, getter.WithTLSClientConfig(rc.CertFile, rc.KeyFile, rc.CAFile))
		}
		if rc.Username != "" && rc.Password != "" {
			c.Options = append(
				c.Options,
				getter.WithBasicAuth(rc.Username, rc.Password),
				getter.WithPassCredentialsAll(rc.PassCredentialsAll),
			)
		}
		return u, nil
	}

	// See if it's of the form: repo/path_to_chart
	p := strings.SplitN(u.Path, "/", 2)
	if len(p) < 2 {
		return u, errors.Errorf("non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
	}

	repoName := p[0]
	chartName := p[1]
	rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories)

	if err != nil {
		return u, err
	}

	r, err := repo.NewChartRepository(rc, c.Getters)
	if err != nil {
		return u, err
	}

	if r != nil && r.Config != nil {
		if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" {
			c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile))
		}
		if r.Config.Username != "" && r.Config.Password != "" {
			c.Options = append(c.Options,
				getter.WithBasicAuth(r.Config.Username, r.Config.Password),
				getter.WithPassCredentialsAll(r.Config.PassCredentialsAll),
			)
		}
	}

	// Next, we need to load the index, and actually look up the chart.
	idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
	i, err := repo.LoadIndexFile(idxFile)
	if err != nil {
		return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
	}

	cv, err := i.Get(chartName, version)
	if err != nil {
		return u, errors.Wrapf(err, "chart %q matching %s not found in %s index. (try 'helm repo update')", chartName, version, r.Config.Name)
	}

	if len(cv.URLs) == 0 {
		return u, errors.Errorf("chart %q has no downloadable URLs", ref)
	}

	// TODO: Seems that picking first URL is not fully correct
	u, err = url.Parse(cv.URLs[0])
	if err != nil {
		return u, errors.Errorf("invalid chart URL format: %s", ref)
	}

	// If the URL is relative (no scheme), prepend the chart repo's base URL
	if !u.IsAbs() {
		repoURL, err := url.Parse(rc.URL)
		if err != nil {
			return repoURL, err
		}
		q := repoURL.Query()
		// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
		repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
		u = repoURL.ResolveReference(u)
		u.RawQuery = q.Encode()
		// TODO add user-agent
		if _, err := getter.NewHTTPGetter(getter.WithURL(rc.URL)); err != nil {
			return repoURL, err
		}
		return u, err
	}

	// TODO add user-agent
	return u, nil
}

// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
//
// It assumes that a chart archive file is accompanied by a provenance file whose
// name is the archive file name plus the ".prov" extension.
func VerifyChart(path, keyring string) (*provenance.Verification, error) {
	// For now, error out if it's not a tar file.
	switch fi, err := os.Stat(path); {
	case err != nil:
		return nil, err
	case fi.IsDir():
		return nil, errors.New("unpacked charts cannot be verified")
	case !isTar(path):
		return nil, errors.New("chart must be a tgz file")
	}

	provfile := path + ".prov"
	if _, err := os.Stat(provfile); err != nil {
		return nil, errors.Wrapf(err, "could not load provenance file %s", provfile)
	}

	sig, err := provenance.NewFromKeyring(keyring, "")
	if err != nil {
		return nil, errors.Wrap(err, "failed to load keyring")
	}
	return sig.Verify(path, provfile)
}

// isTar tests whether the given file is a tar file.
//
// Currently, this simply checks extension, since a subsequent function will
// untar the file and validate its binary format.
func isTar(filename string) bool {
	return strings.EqualFold(filepath.Ext(filename), ".tgz")
}

func pickChartRepositoryConfigByName(name string, cfgs []*repo.Entry) (*repo.Entry, error) {
	for _, rc := range cfgs {
		if rc.Name == name {
			if rc.URL == "" {
				return nil, errors.Errorf("no URL found for repository %s", name)
			}
			return rc, nil
		}
	}
	return nil, errors.Errorf("repo %s not found", name)
}

// scanReposForURL scans all repos to find which repo contains the given URL.
//
// This will attempt to find the given URL in all of the known repositories files.
//
// If the URL is found, this will return the repo entry that contained that URL.
//
// If all of the repos are checked, but the URL is not found, an ErrNoOwnerRepo
// error is returned.
//
// Other errors may be returned when repositories cannot be loaded or searched.
//
// Technically, the fact that a URL is not found in a repo is not a failure indication.
// Charts are not required to be included in an index before they are valid. So
// be mindful of this case.
//
// The same URL can technically exist in two or more repositories. This algorithm
// will return the first one it finds. Order is determined by the order of repositories
// in the repositories.yaml file.
func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, error) {
	// FIXME: This is far from optimal. Larger installations and index files will
	// incur a performance hit for this type of scanning.
	for _, rc := range rf.Repositories {
		r, err := repo.NewChartRepository(rc, c.Getters)
		if err != nil {
			return nil, err
		}

		idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
		i, err := repo.LoadIndexFile(idxFile)
		if err != nil {
			return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
		}

		for _, entry := range i.Entries {
			for _, ver := range entry {
				for _, dl := range ver.URLs {
					if urlutil.Equal(u, dl) {
						return rc, nil
					}
				}
			}
		}
	}
	// This means that there is no repo file for the given URL.
	return nil, ErrNoOwnerRepo
}

func loadRepoConfig(file string) (*repo.File, error) {
	r, err := repo.LoadFile(file)
	if err != nil && !os.IsNotExist(errors.Cause(err)) {
		return nil, err
	}
	return r, nil
}
<commit_msg>Fix the url being set by WithURL on the getters<commit_after>/*
Copyright The Helm 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 downloader

import (
	"fmt"
	"io"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/pkg/errors"

	"helm.sh/helm/v3/internal/experimental/registry"
	"helm.sh/helm/v3/internal/fileutil"
	"helm.sh/helm/v3/internal/urlutil"
	"helm.sh/helm/v3/pkg/getter"
	"helm.sh/helm/v3/pkg/helmpath"
	"helm.sh/helm/v3/pkg/provenance"
	"helm.sh/helm/v3/pkg/repo"
)

// VerificationStrategy describes a strategy for determining whether to verify a chart.
type VerificationStrategy int

const (
	// VerifyNever will skip all verification of a chart.
	VerifyNever VerificationStrategy = iota
	// VerifyIfPossible will attempt a verification, it will not error if verification
	// data is missing. But it will not stop processing if verification fails.
	VerifyIfPossible
	// VerifyAlways will always attempt a verification, and will fail if the
	// verification fails.
	VerifyAlways
	// VerifyLater will fetch verification data, but not do any verification.
	// This is to accommodate the case where another step of the process will
	// perform verification.
	VerifyLater
)

// ErrNoOwnerRepo indicates that a given chart URL can't be found in any repos.
var ErrNoOwnerRepo = errors.New("could not find a repo containing the given URL")

// ChartDownloader handles downloading a chart.
//
// It is capable of performing verifications on charts as well.
type ChartDownloader struct {
	// Out is the location to write warning and info messages.
	Out io.Writer
	// Verify indicates what verification strategy to use.
	Verify VerificationStrategy
	// Keyring is the keyring file used for verification.
	Keyring string
	// Getter collection for the operation
	Getters getter.Providers
	// Options provide parameters to be passed along to the Getter being initialized.
	Options          []getter.Option
	RegistryClient   *registry.Client
	RepositoryConfig string
	RepositoryCache  string
}

// DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file.
//
// If Verify is set to VerifyNever, the verification will be nil.
// If Verify is set to VerifyIfPossible, this will return a verification (or nil on failure), and print a warning on failure.
// If Verify is set to VerifyAlways, this will return a verification or an error if the verification fails.
// If Verify is set to VerifyLater, this will download the prov file (if it exists), but not verify it.
//
// For VerifyNever and VerifyIfPossible, the Verification may be empty.
//
// Returns a string path to the location where the file was downloaded and a verification
// (if provenance was verified), or an error if something bad happened.
func (c *ChartDownloader) DownloadTo(ref, version, dest string) (string, *provenance.Verification, error) {
	u, err := c.ResolveChartVersion(ref, version)
	if err != nil {
		return "", nil, err
	}

	g, err := c.Getters.ByScheme(u.Scheme)
	if err != nil {
		return "", nil, err
	}

	data, err := g.Get(u.String(), c.Options...)
	if err != nil {
		return "", nil, err
	}

	name := filepath.Base(u.Path)
	if u.Scheme == "oci" {
		name = fmt.Sprintf("%s-%s.tgz", name, version)
	}

	destfile := filepath.Join(dest, name)
	if err := fileutil.AtomicWriteFile(destfile, data, 0644); err != nil {
		return destfile, nil, err
	}

	// If provenance is requested, verify it.
	ver := &provenance.Verification{}
	if c.Verify > VerifyNever {
		body, err := g.Get(u.String() + ".prov")
		if err != nil {
			if c.Verify == VerifyAlways {
				return destfile, ver, errors.Errorf("failed to fetch provenance %q", u.String()+".prov")
			}
			fmt.Fprintf(c.Out, "WARNING: Verification not found for %s: %s\n", ref, err)
			return destfile, ver, nil
		}
		provfile := destfile + ".prov"
		if err := fileutil.AtomicWriteFile(provfile, body, 0644); err != nil {
			return destfile, nil, err
		}

		if c.Verify != VerifyLater {
			ver, err = VerifyChart(destfile, c.Keyring)
			if err != nil {
				// Fail always in this case, since it means the verification step
				// failed.
				return destfile, ver, err
			}
		}
	}
	return destfile, ver, nil
}

// ResolveChartVersion resolves a chart reference to a URL.
//
// It returns the URL and sets the ChartDownloader's Options that can fetch
// the URL using the appropriate Getter.
//
// A reference may be an HTTP URL, a 'reponame/chartname' reference, or a local path.
//
// A version is a SemVer string (1.2.3-beta.1+f334a6789).
//
//	- For fully qualified URLs, the version will be ignored (since URLs aren't versioned)
//	- For a chart reference
//		* If version is non-empty, this will return the URL for that version
//		* If version is empty, this will return the URL for the latest version
//		* If no version can be found, an error is returned
func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, error) {
	u, err := url.Parse(ref)
	if err != nil {
		return nil, errors.Errorf("invalid chart URL format: %s", ref)
	}

	rf, err := loadRepoConfig(c.RepositoryConfig)
	if err != nil {
		return u, err
	}

	if u.IsAbs() && len(u.Host) > 0 && len(u.Path) > 0 {
		// In this case, we have to find the parent repo that contains this chart
		// URL. And this is an unfortunate problem, as it requires actually going
		// through each repo cache file and finding a matching URL. But basically
		// we want to find the repo in case we have special SSL cert config
		// for that repo.

		rc, err := c.scanReposForURL(ref, rf)
		if err != nil {
			// If there is no special config, return the default HTTP client and
			// swallow the error.
			if err == ErrNoOwnerRepo {
				// Make sure to add the ref URL as the URL for the getter
				c.Options = append(c.Options, getter.WithURL(ref))
				return u, nil
			}
			return u, err
		}

		// If we get here, we don't need to go through the next phase of looking
		// up the URL. We have it already. So we just set the parameters and return.
		c.Options = append(
			c.Options,
			getter.WithURL(rc.URL),
		)
		if rc.CertFile != "" || rc.KeyFile != "" || rc.CAFile != "" {
			c.Options = append(c.Options, getter.WithTLSClientConfig(rc.CertFile, rc.KeyFile, rc.CAFile))
		}
		if rc.Username != "" && rc.Password != "" {
			c.Options = append(
				c.Options,
				getter.WithBasicAuth(rc.Username, rc.Password),
				getter.WithPassCredentialsAll(rc.PassCredentialsAll),
			)
		}
		return u, nil
	}

	// See if it's of the form: repo/path_to_chart
	p := strings.SplitN(u.Path, "/", 2)
	if len(p) < 2 {
		return u, errors.Errorf("non-absolute URLs should be in form of repo_name/path_to_chart, got: %s", u)
	}

	repoName := p[0]
	chartName := p[1]
	rc, err := pickChartRepositoryConfigByName(repoName, rf.Repositories)

	if err != nil {
		return u, err
	}

	// Now that we have the chart repository information we can use that URL
	// to set the URL for the getter.
	c.Options = append(c.Options, getter.WithURL(rc.URL))

	r, err := repo.NewChartRepository(rc, c.Getters)
	if err != nil {
		return u, err
	}

	if r != nil && r.Config != nil {
		if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" {
			c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile))
		}
		if r.Config.Username != "" && r.Config.Password != "" {
			c.Options = append(c.Options,
				getter.WithBasicAuth(r.Config.Username, r.Config.Password),
				getter.WithPassCredentialsAll(r.Config.PassCredentialsAll),
			)
		}
	}

	// Next, we need to load the index, and actually look up the chart.
	idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
	i, err := repo.LoadIndexFile(idxFile)
	if err != nil {
		return u, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
	}

	cv, err := i.Get(chartName, version)
	if err != nil {
		return u, errors.Wrapf(err, "chart %q matching %s not found in %s index. (try 'helm repo update')", chartName, version, r.Config.Name)
	}

	if len(cv.URLs) == 0 {
		return u, errors.Errorf("chart %q has no downloadable URLs", ref)
	}

	// TODO: Seems that picking first URL is not fully correct
	u, err = url.Parse(cv.URLs[0])
	if err != nil {
		return u, errors.Errorf("invalid chart URL format: %s", ref)
	}

	// If the URL is relative (no scheme), prepend the chart repo's base URL
	if !u.IsAbs() {
		repoURL, err := url.Parse(rc.URL)
		if err != nil {
			return repoURL, err
		}
		q := repoURL.Query()
		// We need a trailing slash for ResolveReference to work, but make sure there isn't already one
		repoURL.Path = strings.TrimSuffix(repoURL.Path, "/") + "/"
		u = repoURL.ResolveReference(u)
		u.RawQuery = q.Encode()
		// TODO add user-agent
		if _, err := getter.NewHTTPGetter(getter.WithURL(rc.URL)); err != nil {
			return repoURL, err
		}
		return u, err
	}

	// TODO add user-agent
	return u, nil
}

// VerifyChart takes a path to a chart archive and a keyring, and verifies the chart.
//
// It assumes that a chart archive file is accompanied by a provenance file whose
// name is the archive file name plus the ".prov" extension.
func VerifyChart(path, keyring string) (*provenance.Verification, error) {
	// For now, error out if it's not a tar file.
	switch fi, err := os.Stat(path); {
	case err != nil:
		return nil, err
	case fi.IsDir():
		return nil, errors.New("unpacked charts cannot be verified")
	case !isTar(path):
		return nil, errors.New("chart must be a tgz file")
	}

	provfile := path + ".prov"
	if _, err := os.Stat(provfile); err != nil {
		return nil, errors.Wrapf(err, "could not load provenance file %s", provfile)
	}

	sig, err := provenance.NewFromKeyring(keyring, "")
	if err != nil {
		return nil, errors.Wrap(err, "failed to load keyring")
	}
	return sig.Verify(path, provfile)
}

// isTar tests whether the given file is a tar file.
//
// Currently, this simply checks extension, since a subsequent function will
// untar the file and validate its binary format.
func isTar(filename string) bool {
	return strings.EqualFold(filepath.Ext(filename), ".tgz")
}

func pickChartRepositoryConfigByName(name string, cfgs []*repo.Entry) (*repo.Entry, error) {
	for _, rc := range cfgs {
		if rc.Name == name {
			if rc.URL == "" {
				return nil, errors.Errorf("no URL found for repository %s", name)
			}
			return rc, nil
		}
	}
	return nil, errors.Errorf("repo %s not found", name)
}

// scanReposForURL scans all repos to find which repo contains the given URL.
//
// This will attempt to find the given URL in all of the known repositories files.
//
// If the URL is found, this will return the repo entry that contained that URL.
//
// If all of the repos are checked, but the URL is not found, an ErrNoOwnerRepo
// error is returned.
//
// Other errors may be returned when repositories cannot be loaded or searched.
//
// Technically, the fact that a URL is not found in a repo is not a failure indication.
// Charts are not required to be included in an index before they are valid. So
// be mindful of this case.
//
// The same URL can technically exist in two or more repositories. This algorithm
// will return the first one it finds. Order is determined by the order of repositories
// in the repositories.yaml file.
func (c *ChartDownloader) scanReposForURL(u string, rf *repo.File) (*repo.Entry, error) {
	// FIXME: This is far from optimal. Larger installations and index files will
	// incur a performance hit for this type of scanning.
	for _, rc := range rf.Repositories {
		r, err := repo.NewChartRepository(rc, c.Getters)
		if err != nil {
			return nil, err
		}

		idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
		i, err := repo.LoadIndexFile(idxFile)
		if err != nil {
			return nil, errors.Wrap(err, "no cached repo found. (try 'helm repo update')")
		}

		for _, entry := range i.Entries {
			for _, ver := range entry {
				for _, dl := range ver.URLs {
					if urlutil.Equal(u, dl) {
						return rc, nil
					}
				}
			}
		}
	}
	// This means that there is no repo file for the given URL.
	return nil, ErrNoOwnerRepo
}

func loadRepoConfig(file string) (*repo.File, error) {
	r, err := repo.LoadFile(file)
	if err != nil && !os.IsNotExist(errors.Cause(err)) {
		return nil, err
	}
	return r, nil
}
<|endoftext|>


<commit_before>// Package captcha provides an easy to use, unopinionated API for captcha generation
package captcha

import (
	"bytes"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"math/rand"
	"strconv"
	"time"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
)

const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
var ttfFont *truetype.Font

// Options manage captcha generation details.
type Options struct {
	// BackgroundColor is captcha image's background color.
	// It defaults to color.Transparent.
	BackgroundColor color.Color
	// CharPreset decides what text will be on captcha image.
	// It defaults to digit 0-9 and all English alphabet.
	// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
	CharPreset string
	// TextLength is the length of captcha text.
	// It defaults to 4.
	TextLength int
	// CurveNumber is the number of curves to draw on captcha image.
	// It defaults to 2.
	CurveNumber int
	// FontDPI controls DPI (dots per inch) of font.
	// The default is 72.0.
	FontDPI float64
	// FontScale controls the scale of font.
	// The default is 1.0.
	FontScale float64
	// Noise controls the number of noise drawn.
	// A noise dot is drawn for every 28 pixel by default.
	// The default is 1.0.
	Noise float64

	width  int
	height int
}

func newDefaultOption(width, height int) *Options {
	return &Options{
		BackgroundColor: color.Transparent,
		CharPreset:      charPreset,
		TextLength:      4,
		CurveNumber:     2,
		FontDPI:         72.0,
		FontScale:       1.0,
		Noise:           1.0,
		width:           width,
		height:          height,
	}
}

// SetOption is a function that can be used to modify default options.
type SetOption func(*Options)

// Data is the result of captcha generation.
// It has a `Text` field and a private `img` field that will
// be used in `WriteImage` receiver.
type Data struct {
	// Text is captcha solution.
	Text string

	img *image.NRGBA
}

// WriteImage encodes image data and writes to an io.Writer.
// It returns possible error from PNG encoding.
func (data *Data) WriteImage(w io.Writer) error {
	return png.Encode(w, data.img)
}

// WriteJPG encodes image data in JPEG format and writes to an io.Writer.
// It returns possible error from JPEG encoding.
func (data *Data) WriteJPG(w io.Writer, o *jpeg.Options) error {
	return jpeg.Encode(w, data.img, o)
}

// WriteGIF encodes image data in GIF format and writes to an io.Writer.
// It returns possible error from GIF encoding.
func (data *Data) WriteGIF(w io.Writer, o *gif.Options) error {
	return gif.Encode(w, data.img, o)
}

func init() {
	ttfFont, _ = freetype.ParseFont(ttf)
}

// LoadFont let you load an external font.
func LoadFont(fontData []byte) error {
	var err error
	ttfFont, err = freetype.ParseFont(fontData)
	return err
}

// LoadFontFromReader load an external font from an io.Reader interface.
func LoadFontFromReader(reader io.Reader) error {
	var buf bytes.Buffer
	if _, err := io.Copy(&buf, reader); err != nil {
		return err
	}

	return LoadFont(buf.Bytes())
}

// New creates a new captcha.
// It returns captcha data and any freetype drawing error encountered.
func New(width int, height int, option ...SetOption) (*Data, error) {
	options := newDefaultOption(width, height)
	for _, setOption := range option {
		setOption(options)
	}

	text := randomText(options)
	img := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
	drawNoise(img, options)
	drawCurves(img, options)
	err := drawText(text, img, options)
	if err != nil {
		return nil, err
	}

	return &Data{Text: text, img: img}, nil
}

// NewMathExpr creates a new captcha.
// It will generate a image with a math expression like `1 + 2`.
func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
	options := newDefaultOption(width, height)
	for _, setOption := range option {
		setOption(options)
	}

	text, equation := randomEquation()
	img := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
	drawNoise(img, options)
	drawCurves(img, options)
	err := drawText(equation, img, options)
	if err != nil {
		return nil, err
	}

	return &Data{Text: text, img: img}, nil
}

func randomText(opts *Options) (text string) {
	n := len(opts.CharPreset)
	for i := 0; i < opts.TextLength; i++ {
		text += string(opts.CharPreset[rng.Intn(n)])
	}

	return text
}

func drawNoise(img *image.NRGBA, opts *Options) {
	noiseCount := (opts.width * opts.height) / int(28.0/opts.Noise)
	for i := 0; i < noiseCount; i++ {
		x := rng.Intn(opts.width)
		y := rng.Intn(opts.height)
		img.Set(x, y, randomColor())
	}
}

func randomColor() color.RGBA {
	red := rng.Intn(255)
	green := rng.Intn(255)
	blue := rng.Intn(255)

	return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}

func drawCurves(img *image.NRGBA, opts *Options) {
	for i := 0; i < opts.CurveNumber; i++ {
		drawSineCurve(img, opts)
	}
}

// Ideally we want to draw bezier curves
// For now sine curves will do the job
func drawSineCurve(img *image.NRGBA, opts *Options) {
	var xStart, xEnd int
	if opts.width <= 40 {
		xStart, xEnd = 1, opts.width-1
	} else {
		xStart = rng.Intn(opts.width/10) + 1
		xEnd = opts.width - rng.Intn(opts.width/10) - 1
	}
	curveHeight := float64(rng.Intn(opts.height/6) + opts.height/6)
	yStart := rng.Intn(opts.height*2/3) + opts.height/6
	angle := 1.0 + rng.Float64()
	flip := rng.Intn(2) == 0
	yFlip := 1.0
	if flip {
		yFlip = -1.0
	}
	curveColor := randomInvertColor(opts.BackgroundColor)

	for x1 := xStart; x1 <= xEnd; x1++ {
		y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
		img.Set(x1, int(y)+yStart, curveColor)
	}
}

func drawText(text string, img *image.NRGBA, opts *Options) error {
	ctx := freetype.NewContext()
	ctx.SetDPI(opts.FontDPI)
	ctx.SetClip(img.Bounds())
	ctx.SetDst(img)
	ctx.SetHinting(font.HintingFull)
	ctx.SetFont(ttfFont)

	fontSpacing := opts.width / len(text)
	fontOffset := rng.Intn(fontSpacing / 2)

	for idx, char := range text {
		fontScale := 0.8 + rng.Float64()*0.4
		fontSize := float64(opts.height) / fontScale * opts.FontScale
		ctx.SetFontSize(fontSize)
		ctx.SetSrc(image.NewUniform(randomInvertColor(opts.BackgroundColor)))
		x := fontSpacing*idx + fontOffset
		y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
		pt := freetype.Pt(x, y)
		if _, err := ctx.DrawString(string(char), pt); err != nil {
			return err
		}
	}

	return nil
}

func randomInvertColor(base color.Color) color.Color {
	baseLightness := getLightness(base)
	var value float64
	if baseLightness >= 0.5 {
		value = baseLightness - 0.3 - rng.Float64()*0.2
	} else {
		value = baseLightness + 0.3 + rng.Float64()*0.2
	}
	hue := float64(rng.Intn(361)) / 360
	saturation := 0.6 + rng.Float64()*0.2

	return hsva{h: hue, s: saturation, v: value, a: uint8(255)}
}

func getLightness(colour color.Color) float64 {
	r, g, b, a := colour.RGBA()
	// transparent
	if a == 0 {
		return 1.0
	}
	max := maxColor(r, g, b)
	min := minColor(r, g, b)

	l := (float64(max) + float64(min)) / (2 * 255)

	return l
}

func maxColor(numList ...uint32) (max uint32) {
	for _, num := range numList {
		colorVal := num & 255
		if colorVal > max {
			max = colorVal
		}
	}

	return max
}

func minColor(numList ...uint32) (min uint32) {
	min = 255
	for _, num := range numList {
		colorVal := num & 255
		if colorVal < min {
			min = colorVal
		}
	}

	return min
}

func randomEquation() (text string, equation string) {
	left := 1 + rng.Intn(9)
	right := 1 + rng.Intn(9)
	text = strconv.Itoa(left + right)
	equation = strconv.Itoa(left) + "+" + strconv.Itoa(right)

	return text, equation
}
<commit_msg>Simplify drawSineCurve function<commit_after>// Package captcha provides an easy to use, unopinionated API for captcha generation
package captcha

import (
	"bytes"
	"image"
	"image/color"
	"image/draw"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"math"
	"math/rand"
	"strconv"
	"time"

	"github.com/golang/freetype"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
)

const charPreset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
var ttfFont *truetype.Font

// Options manage captcha generation details.
type Options struct {
	// BackgroundColor is captcha image's background color.
	// It defaults to color.Transparent.
	BackgroundColor color.Color
	// CharPreset decides what text will be on captcha image.
	// It defaults to digit 0-9 and all English alphabet.
	// ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789
	CharPreset string
	// TextLength is the length of captcha text.
	// It defaults to 4.
	TextLength int
	// CurveNumber is the number of curves to draw on captcha image.
	// It defaults to 2.
	CurveNumber int
	// FontDPI controls DPI (dots per inch) of font.
	// The default is 72.0.
	FontDPI float64
	// FontScale controls the scale of font.
	// The default is 1.0.
	FontScale float64
	// Noise controls the number of noise drawn.
	// A noise dot is drawn for every 28 pixel by default.
	// The default is 1.0.
	Noise float64

	width  int
	height int
}

func newDefaultOption(width, height int) *Options {
	return &Options{
		BackgroundColor: color.Transparent,
		CharPreset:      charPreset,
		TextLength:      4,
		CurveNumber:     2,
		FontDPI:         72.0,
		FontScale:       1.0,
		Noise:           1.0,
		width:           width,
		height:          height,
	}
}

// SetOption is a function that can be used to modify default options.
type SetOption func(*Options)

// Data is the result of captcha generation.
// It has a `Text` field and a private `img` field that will
// be used in `WriteImage` receiver.
type Data struct {
	// Text is captcha solution.
	Text string

	img *image.NRGBA
}

// WriteImage encodes image data and writes to an io.Writer.
// It returns possible error from PNG encoding.
func (data *Data) WriteImage(w io.Writer) error {
	return png.Encode(w, data.img)
}

// WriteJPG encodes image data in JPEG format and writes to an io.Writer.
// It returns possible error from JPEG encoding.
func (data *Data) WriteJPG(w io.Writer, o *jpeg.Options) error {
	return jpeg.Encode(w, data.img, o)
}

// WriteGIF encodes image data in GIF format and writes to an io.Writer.
// It returns possible error from GIF encoding.
func (data *Data) WriteGIF(w io.Writer, o *gif.Options) error {
	return gif.Encode(w, data.img, o)
}

func init() {
	ttfFont, _ = freetype.ParseFont(ttf)
}

// LoadFont let you load an external font.
func LoadFont(fontData []byte) error {
	var err error
	ttfFont, err = freetype.ParseFont(fontData)
	return err
}

// LoadFontFromReader load an external font from an io.Reader interface.
func LoadFontFromReader(reader io.Reader) error {
	var buf bytes.Buffer
	if _, err := io.Copy(&buf, reader); err != nil {
		return err
	}

	return LoadFont(buf.Bytes())
}

// New creates a new captcha.
// It returns captcha data and any freetype drawing error encountered.
func New(width int, height int, option ...SetOption) (*Data, error) {
	options := newDefaultOption(width, height)
	for _, setOption := range option {
		setOption(options)
	}

	text := randomText(options)
	img := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
	drawNoise(img, options)
	drawCurves(img, options)
	err := drawText(text, img, options)
	if err != nil {
		return nil, err
	}

	return &Data{Text: text, img: img}, nil
}

// NewMathExpr creates a new captcha.
// It will generate a image with a math expression like `1 + 2`.
func NewMathExpr(width int, height int, option ...SetOption) (*Data, error) {
	options := newDefaultOption(width, height)
	for _, setOption := range option {
		setOption(options)
	}

	text, equation := randomEquation()
	img := image.NewNRGBA(image.Rect(0, 0, width, height))
	draw.Draw(img, img.Bounds(), &image.Uniform{options.BackgroundColor}, image.ZP, draw.Src)
	drawNoise(img, options)
	drawCurves(img, options)
	err := drawText(equation, img, options)
	if err != nil {
		return nil, err
	}

	return &Data{Text: text, img: img}, nil
}

func randomText(opts *Options) (text string) {
	n := len(opts.CharPreset)
	for i := 0; i < opts.TextLength; i++ {
		text += string(opts.CharPreset[rng.Intn(n)])
	}

	return text
}

func drawNoise(img *image.NRGBA, opts *Options) {
	noiseCount := (opts.width * opts.height) / int(28.0/opts.Noise)
	for i := 0; i < noiseCount; i++ {
		x := rng.Intn(opts.width)
		y := rng.Intn(opts.height)
		img.Set(x, y, randomColor())
	}
}

func randomColor() color.RGBA {
	red := rng.Intn(255)
	green := rng.Intn(255)
	blue := rng.Intn(255)

	return color.RGBA{R: uint8(red), G: uint8(green), B: uint8(blue), A: uint8(255)}
}

func drawCurves(img *image.NRGBA, opts *Options) {
	for i := 0; i < opts.CurveNumber; i++ {
		drawSineCurve(img, opts)
	}
}

// Ideally we want to draw bezier curves
// For now sine curves will do the job
func drawSineCurve(img *image.NRGBA, opts *Options) {
	var xStart, xEnd int
	if opts.width <= 40 {
		xStart, xEnd = 1, opts.width-1
	} else {
		xStart = rng.Intn(opts.width/10) + 1
		xEnd = opts.width - rng.Intn(opts.width/10) - 1
	}
	curveHeight := float64(rng.Intn(opts.height/6) + opts.height/6)
	yStart := rng.Intn(opts.height*2/3) + opts.height/6
	angle := 1.0 + rng.Float64()
	yFlip := 1.0
	if rng.Intn(2) == 0 {
		yFlip = -1.0
	}
	curveColor := randomInvertColor(opts.BackgroundColor)

	for x1 := xStart; x1 <= xEnd; x1++ {
		y := math.Sin(math.Pi*angle*float64(x1)/float64(opts.width)) * curveHeight * yFlip
		img.Set(x1, int(y)+yStart, curveColor)
	}
}

func drawText(text string, img *image.NRGBA, opts *Options) error {
	ctx := freetype.NewContext()
	ctx.SetDPI(opts.FontDPI)
	ctx.SetClip(img.Bounds())
	ctx.SetDst(img)
	ctx.SetHinting(font.HintingFull)
	ctx.SetFont(ttfFont)

	fontSpacing := opts.width / len(text)
	fontOffset := rng.Intn(fontSpacing / 2)

	for idx, char := range text {
		fontScale := 0.8 + rng.Float64()*0.4
		fontSize := float64(opts.height) / fontScale * opts.FontScale
		ctx.SetFontSize(fontSize)
		ctx.SetSrc(image.NewUniform(randomInvertColor(opts.BackgroundColor)))
		x := fontSpacing*idx + fontOffset
		y := opts.height/6 + rng.Intn(opts.height/3) + int(fontSize/2)
		pt := freetype.Pt(x, y)
		if _, err := ctx.DrawString(string(char), pt); err != nil {
			return err
		}
	}

	return nil
}

func randomInvertColor(base color.Color) color.Color {
	baseLightness := getLightness(base)
	var value float64
	if baseLightness >= 0.5 {
		value = baseLightness - 0.3 - rng.Float64()*0.2
	} else {
		value = baseLightness + 0.3 + rng.Float64()*0.2
	}
	hue := float64(rng.Intn(361)) / 360
	saturation := 0.6 + rng.Float64()*0.2

	return hsva{h: hue, s: saturation, v: value, a: uint8(255)}
}

func getLightness(colour color.Color) float64 {
	r, g, b, a := colour.RGBA()
	// transparent
	if a == 0 {
		return 1.0
	}
	max := maxColor(r, g, b)
	min := minColor(r, g, b)

	l := (float64(max) + float64(min)) / (2 * 255)

	return l
}

func maxColor(numList ...uint32) (max uint32) {
	for _, num := range numList {
		colorVal := num & 255
		if colorVal > max {
			max = colorVal
		}
	}

	return max
}

func minColor(numList ...uint32) (min uint32) {
	min = 255
	for _, num := range numList {
		colorVal := num & 255
		if colorVal < min {
			min = colorVal
		}
	}

	return min
}

func randomEquation() (text string, equation string) {
	left := 1 + rng.Intn(9)
	right := 1 + rng.Intn(9)
	text = strconv.Itoa(left + right)
	equation = strconv.Itoa(left) + "+" + strconv.Itoa(right)

	return text, equation
}
<|endoftext|>


<commit_before>package store

import (
	"context"
	"fmt"
	"strings"

	"github.com/grafana/grafana-plugin-sdk-go/data"
	"github.com/grafana/grafana/pkg/infra/filestorage"
	"gocloud.dev/blob"
)

const rootStorageTypeDisk = "disk"

var _ storageRuntime = &rootStorageDisk{}

type rootStorageDisk struct {
	settings *StorageLocalDiskConfig
	meta     RootStorageMeta
	store    filestorage.FileStorage
}

func newDiskStorage(meta RootStorageMeta, scfg RootStorageConfig) *rootStorageDisk {
	cfg := scfg.Disk
	if cfg == nil {
		cfg = &StorageLocalDiskConfig{}
		scfg.Disk = cfg
	}
	scfg.Type = rootStorageTypeDisk
	meta.Config = scfg
	if scfg.Prefix == "" {
		meta.Notice = append(meta.Notice, data.Notice{
			Severity: data.NoticeSeverityError,
			Text:     "Missing prefix",
		})
	}
	if cfg.Path == "" {
		meta.Notice = append(meta.Notice, data.Notice{
			Severity: data.NoticeSeverityError,
			Text:     "Missing path configuration",
		})
	}

	s := &rootStorageDisk{
		settings: cfg,
	}

	if meta.Notice == nil {
		path := fmt.Sprintf("file://%s", cfg.Path)
		bucket, err := blob.OpenBucket(context.Background(), path)
		if err != nil {
			grafanaStorageLogger.Warn("error loading storage", "prefix", scfg.Prefix, "err", err)
			meta.Notice = append(meta.Notice, data.Notice{
				Severity: data.NoticeSeverityError,
				Text:     "Failed to initialize storage",
			})
		} else {
			s.store = filestorage.NewCdkBlobStorage(grafanaStorageLogger,
				bucket, "",
				filestorage.NewPathFilter(cfg.Roots, nil, nil, nil))

			meta.Ready = true // exists!
		}
	}

	s.meta = meta
	return s
}

func (s *rootStorageDisk) Meta() RootStorageMeta {
	return s.meta
}

func (s *rootStorageDisk) Store() filestorage.FileStorage {
	return s.store
}

func (s *rootStorageDisk) Sync() error {
	return nil // already in sync
}

// with local disk user metadata and messages are lost
func (s *rootStorageDisk) Write(ctx context.Context, cmd *WriteValueRequest) (*WriteValueResponse, error) {
	byteAray := []byte(cmd.Body)

	path := cmd.Path
	if !strings.HasPrefix(path, filestorage.Delimiter) {
		path = filestorage.Delimiter + path
	}
	err := s.store.Upsert(ctx, &filestorage.UpsertFileCommand{
		Path:     path,
		Contents: byteAray,
	})
	if err != nil {
		return nil, err
	}
	return &WriteValueResponse{Code: 200}, nil
}
<commit_msg> Storage: Fix initialization on windows (#57504)<commit_after>package store

import (
	"context"
	"strings"

	"github.com/grafana/grafana-plugin-sdk-go/data"
	"github.com/grafana/grafana/pkg/infra/filestorage"
	"gocloud.dev/blob"
)

const rootStorageTypeDisk = "disk"

var _ storageRuntime = &rootStorageDisk{}

type rootStorageDisk struct {
	settings *StorageLocalDiskConfig
	meta     RootStorageMeta
	store    filestorage.FileStorage
}

func newDiskStorage(meta RootStorageMeta, scfg RootStorageConfig) *rootStorageDisk {
	cfg := scfg.Disk
	if cfg == nil {
		cfg = &StorageLocalDiskConfig{}
		scfg.Disk = cfg
	}
	scfg.Type = rootStorageTypeDisk
	meta.Config = scfg
	if scfg.Prefix == "" {
		meta.Notice = append(meta.Notice, data.Notice{
			Severity: data.NoticeSeverityError,
			Text:     "Missing prefix",
		})
	}
	if cfg.Path == "" {
		meta.Notice = append(meta.Notice, data.Notice{
			Severity: data.NoticeSeverityError,
			Text:     "Missing path configuration",
		})
	}

	s := &rootStorageDisk{
		settings: cfg,
	}

	if meta.Notice == nil {
		protocol := "file:///"
		path := protocol + cfg.Path
		bucket, err := blob.OpenBucket(context.Background(), path)
		if err != nil {
			grafanaStorageLogger.Warn("error loading storage", "prefix", scfg.Prefix, "err", err)
			meta.Notice = append(meta.Notice, data.Notice{
				Severity: data.NoticeSeverityError,
				Text:     "Failed to initialize storage",
			})
		} else {
			s.store = filestorage.NewCdkBlobStorage(grafanaStorageLogger,
				bucket, "",
				filestorage.NewPathFilter(cfg.Roots, nil, nil, nil))

			meta.Ready = true // exists!
		}
	}

	s.meta = meta
	return s
}

func (s *rootStorageDisk) Meta() RootStorageMeta {
	return s.meta
}

func (s *rootStorageDisk) Store() filestorage.FileStorage {
	return s.store
}

func (s *rootStorageDisk) Sync() error {
	return nil // already in sync
}

// with local disk user metadata and messages are lost
func (s *rootStorageDisk) Write(ctx context.Context, cmd *WriteValueRequest) (*WriteValueResponse, error) {
	byteAray := []byte(cmd.Body)

	path := cmd.Path
	if !strings.HasPrefix(path, filestorage.Delimiter) {
		path = filestorage.Delimiter + path
	}
	err := s.store.Upsert(ctx, &filestorage.UpsertFileCommand{
		Path:     path,
		Contents: byteAray,
	})
	if err != nil {
		return nil, err
	}
	return &WriteValueResponse{Code: 200}, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The etcd 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 transport

import (
	"net"
	"net/http"
	"time"
)

// NewTimeoutTransport returns a transport created using the given TLS info.
// If read/write on the created connection blocks longer than its time limit,
// it will return timeout error.
// If read/write timeout is set, transport will not be able to reuse connection.
func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.Duration) (*http.Transport, error) {
	tr, err := NewTransport(info, dialtimeoutd)
	if err != nil {
		return nil, err
	}

	if rdtimeoutd != 0 || wtimeoutd != 0 {
		// the timed out connection will timeout soon after it is idle.
		// it should not be put back to http transport as an idle connection for future usage.
		tr.MaxIdleConnsPerHost = -1
	} else {
		// allow more idle connections between peers to avoid unncessary port allocation.
		tr.MaxIdleConnsPerHost = 1024
	}

	tr.Dial = (&rwTimeoutDialer{
		Dialer: net.Dialer{
			Timeout:   dialtimeoutd,
			KeepAlive: 30 * time.Second,
		},
		rdtimeoutd: rdtimeoutd,
		wtimeoutd:  wtimeoutd,
	}).Dial
	return tr, nil
}
<commit_msg>pkg/transport: fix minor typo<commit_after>// Copyright 2015 The etcd 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 transport

import (
	"net"
	"net/http"
	"time"
)

// NewTimeoutTransport returns a transport created using the given TLS info.
// If read/write on the created connection blocks longer than its time limit,
// it will return timeout error.
// If read/write timeout is set, transport will not be able to reuse connection.
func NewTimeoutTransport(info TLSInfo, dialtimeoutd, rdtimeoutd, wtimeoutd time.Duration) (*http.Transport, error) {
	tr, err := NewTransport(info, dialtimeoutd)
	if err != nil {
		return nil, err
	}

	if rdtimeoutd != 0 || wtimeoutd != 0 {
		// the timed out connection will timeout soon after it is idle.
		// it should not be put back to http transport as an idle connection for future usage.
		tr.MaxIdleConnsPerHost = -1
	} else {
		// allow more idle connections between peers to avoid unnecessary port allocation.
		tr.MaxIdleConnsPerHost = 1024
	}

	tr.Dial = (&rwTimeoutDialer{
		Dialer: net.Dialer{
			Timeout:   dialtimeoutd,
			KeepAlive: 30 * time.Second,
		},
		rdtimeoutd: rdtimeoutd,
		wtimeoutd:  wtimeoutd,
	}).Dial
	return tr, nil
}
<|endoftext|>