<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"koding/db/mongodb"
	"koding/kites/kloud/storage"
	"koding/tools/config"
	"log"
	"net/url"
	"os"

	"github.com/koding/kite"
	kiteconfig "github.com/koding/kite/config"
	"github.com/koding/kite/protocol"
	"github.com/koding/kloud"
	"github.com/koding/logging"
)

var (
	flagProfile  = flag.String("c", "", "Configuration profile from file")
	flagIP       = flag.String("ip", "", "Change public ip")
	flagPort     = flag.Int("port", 3000, "Change running port")
	flagRegion   = flag.String("r", "", "Change region")
	flagEnv      = flag.String("env", "", "Change environment")
	flagUniqueId = flag.String("id", "", "Start kloud with a uniqueId assignee name")

	flagVersion  = flag.Bool("version", false, "Show version and exit")
	flagDebug    = flag.Bool("debug", false, "Enable debug mode")
	flagProdMode = flag.Bool("prod", false, "Enable production mode")

	// Deployment related flags
	flagKontrolURL = flag.String("kontrol-url", "", "Kontrol URL to be connected")
	flagPublicKey  = flag.String("public-key", "", "Public RSA key of Kontrol")
	flagPrivateKey = flag.String("private-key", "", "Private RSA key of Kontrol")

	// Kontrol registiraiton related  flags
	flagLocal       = flag.Bool("local", true, "Start klient in local environment.")
	flagRegisterURL = flag.String("register-url", "", "Change register URL to kontrol")
	flagProxy       = flag.Bool("proxy", false, "Start klient behind a proxy")
)

func main() {
	flag.Parse()
	if *flagProfile == "" || *flagRegion == "" {
		log.Fatal("Please specify profile via -c and region via -r. Aborting.")
	}

	if *flagVersion {
		fmt.Println(kloud.VERSION)
		os.Exit(0)
	}

	k := newKite()

	registerURL := k.RegisterURL(*flagLocal)
	fmt.Printf("registerU %+v\n", registerURL)
	if *flagRegisterURL != "" {
		u, err := url.Parse(*flagRegisterURL)
		if err != nil {
			k.Log.Fatal("Couldn't parse register url: %s", err)
		}

		registerURL = u
	}

	if *flagProxy {
		k.Log.Info("Proxy mode is enabled")
		// Koding proxies in production only
		proxyQuery := &protocol.KontrolQuery{
			Username:    "koding",
			Environment: "production",
			Name:        "proxy",
		}

		k.Log.Info("Seaching proxy: %#v", proxyQuery)
		go k.RegisterToProxy(registerURL, proxyQuery)
	} else {
		if err := k.RegisterForever(registerURL); err != nil {
			k.Log.Fatal(err.Error())
		}
	}

	k.Run()
}

func newKite() *kite.Kite {
	k := kite.New(kloud.NAME, kloud.VERSION)
	k.Config = kiteconfig.MustGet()
	k.Config.Port = *flagPort

	if *flagRegion != "" {
		k.Config.Region = *flagRegion
	}

	if *flagEnv != "" {
		k.Config.Environment = *flagEnv
	} else {
		k.Config.Environment = config.MustConfig(*flagProfile).Environment
	}

	kld := newKloud(k)

	k.HandleFunc("build", kld.Build)
	k.HandleFunc("start", kld.Start)
	k.HandleFunc("stop", kld.Stop)
	k.HandleFunc("restart", kld.Restart)
	k.HandleFunc("info", kld.Info)
	k.HandleFunc("destroy", kld.Destroy)
	k.HandleFunc("event", kld.Event)

	return k
}

func newKloud(kloudKite *kite.Kite) *kloud.Kloud {
	id := uniqueId()
	if *flagUniqueId != "" {
		id = uniqueId()
	}

	conf := config.MustConfig(*flagProfile)

	mongodbStorage := &storage.MongoDB{
		Session:      mongodb.NewMongoDB(conf.Mongo),
		AssigneeName: id,
		Log:          logging.NewLogger("kloud-storage"),
	}

	if err := mongodbStorage.CleanupOldData(); err != nil {
		kloudKite.Log.Warning("Cleaning up mongodb err: %s", err.Error())
	}

	var kontrolURL string
	if *flagKontrolURL != "" {
		u, err := url.Parse(*flagKontrolURL)
		if err != nil {
			log.Fatalln(err)
		}

		kontrolURL = u.String()
	} else {
		// read kontrolURL from kite.key if it doesn't exist.
		kontrolURL = kiteconfig.MustGet().KontrolURL.String()
	}

	klientFolder := "klient/development/latest"
	if *flagProdMode {
		klientFolder = "klient/production/latest"
	}

	pubKeyPath := *flagPublicKey
	if *flagPublicKey == "" {
		pubKeyPath = conf.NewKontrol.PublicKeyFile
	}
	pubKey, err := ioutil.ReadFile(pubKeyPath)
	if err != nil {
		log.Fatalln(err)
	}
	publicKey := string(pubKey)

	privKeyPath := *flagPrivateKey
	if *flagPublicKey == "" {
		privKeyPath = conf.NewKontrol.PrivateKeyFile
	}
	privKey, err := ioutil.ReadFile(privKeyPath)
	if err != nil {
		log.Fatalln(err)
	}
	privateKey := string(privKey)

	deployer := &KodingDeploy{
		Kite:              kloudKite,
		Log:               logging.NewLogger("kloud-deploy"),
		KontrolURL:        kontrolURL,
		KontrolPrivateKey: privateKey,
		KontrolPublicKey:  publicKey,
		Bucket:            newBucket("koding-kites", klientFolder),
	}

	kld := kloud.NewKloud()
	kld.Storage = mongodbStorage
	kld.Deployer = deployer

	return kld
}

func uniqueId() string {
	// TODO: add a unique identifier, for letting multiple version of the same
	// worker work on the same hostname.
	hostname, err := os.Hostname()
	if err != nil {
		panic(err) // we should not let it start
	}

	return fmt.Sprintf("%s-%s", kloud.NAME, hostname)
}
<commit_msg>kloud: kontrolURL is a string now<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"koding/db/mongodb"
	"koding/kites/kloud/storage"
	"koding/tools/config"
	"log"
	"net/url"
	"os"

	"github.com/koding/kite"
	kiteconfig "github.com/koding/kite/config"
	"github.com/koding/kite/protocol"
	"github.com/koding/kloud"
	"github.com/koding/logging"
)

var (
	flagProfile  = flag.String("c", "", "Configuration profile from file")
	flagIP       = flag.String("ip", "", "Change public ip")
	flagPort     = flag.Int("port", 3000, "Change running port")
	flagRegion   = flag.String("r", "", "Change region")
	flagEnv      = flag.String("env", "", "Change environment")
	flagUniqueId = flag.String("id", "", "Start kloud with a uniqueId assignee name")

	flagVersion  = flag.Bool("version", false, "Show version and exit")
	flagDebug    = flag.Bool("debug", false, "Enable debug mode")
	flagProdMode = flag.Bool("prod", false, "Enable production mode")

	// Deployment related flags
	flagKontrolURL = flag.String("kontrol-url", "", "Kontrol URL to be connected")
	flagPublicKey  = flag.String("public-key", "", "Public RSA key of Kontrol")
	flagPrivateKey = flag.String("private-key", "", "Private RSA key of Kontrol")

	// Kontrol registiraiton related  flags
	flagLocal       = flag.Bool("local", true, "Start klient in local environment.")
	flagRegisterURL = flag.String("register-url", "", "Change register URL to kontrol")
	flagProxy       = flag.Bool("proxy", false, "Start klient behind a proxy")
)

func main() {
	flag.Parse()
	if *flagProfile == "" || *flagRegion == "" {
		log.Fatal("Please specify profile via -c and region via -r. Aborting.")
	}

	if *flagVersion {
		fmt.Println(kloud.VERSION)
		os.Exit(0)
	}

	k := newKite()

	registerURL := k.RegisterURL(*flagLocal)
	fmt.Printf("registerU %+v\n", registerURL)
	if *flagRegisterURL != "" {
		u, err := url.Parse(*flagRegisterURL)
		if err != nil {
			k.Log.Fatal("Couldn't parse register url: %s", err)
		}

		registerURL = u
	}

	if *flagProxy {
		k.Log.Info("Proxy mode is enabled")
		// Koding proxies in production only
		proxyQuery := &protocol.KontrolQuery{
			Username:    "koding",
			Environment: "production",
			Name:        "proxy",
		}

		k.Log.Info("Seaching proxy: %#v", proxyQuery)
		go k.RegisterToProxy(registerURL, proxyQuery)
	} else {
		if err := k.RegisterForever(registerURL); err != nil {
			k.Log.Fatal(err.Error())
		}
	}

	k.Run()
}

func newKite() *kite.Kite {
	k := kite.New(kloud.NAME, kloud.VERSION)
	k.Config = kiteconfig.MustGet()
	k.Config.Port = *flagPort

	if *flagRegion != "" {
		k.Config.Region = *flagRegion
	}

	if *flagEnv != "" {
		k.Config.Environment = *flagEnv
	} else {
		k.Config.Environment = config.MustConfig(*flagProfile).Environment
	}

	kld := newKloud(k)

	k.HandleFunc("build", kld.Build)
	k.HandleFunc("start", kld.Start)
	k.HandleFunc("stop", kld.Stop)
	k.HandleFunc("restart", kld.Restart)
	k.HandleFunc("info", kld.Info)
	k.HandleFunc("destroy", kld.Destroy)
	k.HandleFunc("event", kld.Event)

	return k
}

func newKloud(kloudKite *kite.Kite) *kloud.Kloud {
	id := uniqueId()
	if *flagUniqueId != "" {
		id = uniqueId()
	}

	conf := config.MustConfig(*flagProfile)

	mongodbStorage := &storage.MongoDB{
		Session:      mongodb.NewMongoDB(conf.Mongo),
		AssigneeName: id,
		Log:          logging.NewLogger("kloud-storage"),
	}

	if err := mongodbStorage.CleanupOldData(); err != nil {
		kloudKite.Log.Warning("Cleaning up mongodb err: %s", err.Error())
	}

	var kontrolURL string
	if *flagKontrolURL != "" {
		u, err := url.Parse(*flagKontrolURL)
		if err != nil {
			log.Fatalln(err)
		}

		kontrolURL = u.String()
	} else {
		// read kontrolURL from kite.key if it doesn't exist.
		kontrolURL = kiteconfig.MustGet().KontrolURL
	}

	klientFolder := "klient/development/latest"
	if *flagProdMode {
		klientFolder = "klient/production/latest"
	}

	pubKeyPath := *flagPublicKey
	if *flagPublicKey == "" {
		pubKeyPath = conf.NewKontrol.PublicKeyFile
	}
	pubKey, err := ioutil.ReadFile(pubKeyPath)
	if err != nil {
		log.Fatalln(err)
	}
	publicKey := string(pubKey)

	privKeyPath := *flagPrivateKey
	if *flagPublicKey == "" {
		privKeyPath = conf.NewKontrol.PrivateKeyFile
	}
	privKey, err := ioutil.ReadFile(privKeyPath)
	if err != nil {
		log.Fatalln(err)
	}
	privateKey := string(privKey)

	deployer := &KodingDeploy{
		Kite:              kloudKite,
		Log:               logging.NewLogger("kloud-deploy"),
		KontrolURL:        kontrolURL,
		KontrolPrivateKey: privateKey,
		KontrolPublicKey:  publicKey,
		Bucket:            newBucket("koding-kites", klientFolder),
	}

	kld := kloud.NewKloud()
	kld.Storage = mongodbStorage
	kld.Deployer = deployer

	return kld
}

func uniqueId() string {
	// TODO: add a unique identifier, for letting multiple version of the same
	// worker work on the same hostname.
	hostname, err := os.Hostname()
	if err != nil {
		panic(err) // we should not let it start
	}

	return fmt.Sprintf("%s-%s", kloud.NAME, hostname)
}
<|endoftext|>


<commit_before>package discovery

import (
	"flag"
	"fmt"
)

var replicationLowLag = flag.Uint("discovery_replication_low_lag", 30, "the replication lag in seconds that is considered low")

// FilterByReplicationLag filters the list of EndPointStats by EndPointStats.Stats.SecondsBehindMaster.
// The algorithm (EndPointStats that is non-serving or has error is ignored):
// - Return the list if there is 0 or 1 endpoint.
// - Return the list if all endpoints have <=30s lag.
// - Filter by replication lag: for each endpoint, if the mean value without it is more than 0.7 of the mean value across all endpoints, it is valid.
// For example, lags of (5s, 10s, 15s, 120s) return the first three;
// lags of (30m, 35m, 40m, 45m) return all.
func FilterByReplicationLag(epsList []*EndPointStats) []*EndPointStats {
	list := make([]*EndPointStats, 0, len(epsList))
	// filter non-serving endpoints
	for _, eps := range epsList {
		if !eps.Serving || eps.LastError != nil || eps.Stats == nil {
			continue
		}
		list = append(list, eps)
	}
	if len(list) <= 1 {
		return list
	}
	// if all have low replication lag (<=30s), return all endpoints.
	allLowLag := true
	for _, eps := range list {
		if eps.Stats.SecondsBehindMaster > uint32(*replicationLowLag) {
			allLowLag = false
			break
		}
	}
	if allLowLag {
		return list
	}
	// filter those affecting "mean" lag significantly
	// calculate mean for all endpoints
	res := make([]*EndPointStats, 0, len(list))
	m, _ := mean(list, -1)
	for i, eps := range list {
		// calculate mean by excluding ith endpoint
		mi, _ := mean(list, i)
		if float64(mi) > float64(m)*0.7 {
			res = append(res, eps)
		}
	}
	return res
}

// mean calculates the mean value over the given list,
// while excluding the item with the specified index.
func mean(epsList []*EndPointStats, idxExclude int) (uint64, error) {
	var sum uint64
	var count uint64
	for i, eps := range epsList {
		if i == idxExclude {
			continue
		}
		sum = sum + uint64(eps.Stats.SecondsBehindMaster)
		count++
	}
	if count == 0 {
		return 0, fmt.Errorf("empty list")
	}
	return sum / count, nil
}
<commit_msg>Update the low replication lag flag to duration<commit_after>package discovery

import (
	"flag"
	"fmt"
	"time"
)

var lowReplicationLag = flag.Duration("discovery_low_replication_lag", 30*time.Second, "the replication lag that is considered low enough to be healthy")

// FilterByReplicationLag filters the list of EndPointStats by EndPointStats.Stats.SecondsBehindMaster.
// The algorithm (EndPointStats that is non-serving or has error is ignored):
// - Return the list if there is 0 or 1 endpoint.
// - Return the list if all endpoints have <=30s lag.
// - Filter by replication lag: for each endpoint, if the mean value without it is more than 0.7 of the mean value across all endpoints, it is valid.
// For example, lags of (5s, 10s, 15s, 120s) return the first three;
// lags of (30m, 35m, 40m, 45m) return all.
func FilterByReplicationLag(epsList []*EndPointStats) []*EndPointStats {
	list := make([]*EndPointStats, 0, len(epsList))
	// filter non-serving endpoints
	for _, eps := range epsList {
		if !eps.Serving || eps.LastError != nil || eps.Stats == nil {
			continue
		}
		list = append(list, eps)
	}
	if len(list) <= 1 {
		return list
	}
	// if all have low replication lag (<=30s), return all endpoints.
	allLowLag := true
	for _, eps := range list {
		if float64(eps.Stats.SecondsBehindMaster) > lowReplicationLag.Seconds() {
			allLowLag = false
			break
		}
	}
	if allLowLag {
		return list
	}
	// filter those affecting "mean" lag significantly
	// calculate mean for all endpoints
	res := make([]*EndPointStats, 0, len(list))
	m, _ := mean(list, -1)
	for i, eps := range list {
		// calculate mean by excluding ith endpoint
		mi, _ := mean(list, i)
		if float64(mi) > float64(m)*0.7 {
			res = append(res, eps)
		}
	}
	return res
}

// mean calculates the mean value over the given list,
// while excluding the item with the specified index.
func mean(epsList []*EndPointStats, idxExclude int) (uint64, error) {
	var sum uint64
	var count uint64
	for i, eps := range epsList {
		if i == idxExclude {
			continue
		}
		sum = sum + uint64(eps.Stats.SecondsBehindMaster)
		count++
	}
	if count == 0 {
		return 0, fmt.Errorf("empty list")
	}
	return sum / count, nil
}
<|endoftext|>


<commit_before>// Copyright 2016, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package l2vtgate provides the core functionnality of a second-layer vtgate
// to route queries from an original vtgate to a subset of tablets.
package l2vtgate

import (
	"fmt"
	"io"

	log "github.com/golang/glog"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/tabletserver/queryservice"
	"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/vtgate/gateway"

	querypb "github.com/youtube/vitess/go/vt/proto/query"
	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

var (
	l2VTGate *L2VTGate
)

// L2VTGate implements queryservice.QueryService and forwards queries to
// the underlying gateway.
type L2VTGate struct {
	gateway gateway.Gateway
}

// RegisterL2VTGate defines the type of registration mechanism.
type RegisterL2VTGate func(queryservice.QueryService)

// RegisterL2VTGates stores register funcs for L2VTGate server.
var RegisterL2VTGates []RegisterL2VTGate

// Init creates the single L2VTGate with the provided parameters.
func Init(hc discovery.HealthCheck, topoServer topo.Server, serv topo.SrvTopoServer, cell string, retryCount int, tabletTypesToWait []topodatapb.TabletType) *L2VTGate {
	if l2VTGate != nil {
		log.Fatalf("L2VTGate already initialized")
	}

	gateway := gateway.GetCreator()(hc, topoServer, serv, cell, retryCount, tabletTypesToWait)
	l2VTGate = &L2VTGate{
		gateway: gateway,
	}
	servenv.OnRun(func() {
		for _, f := range RegisterL2VTGates {
			f(l2VTGate)
		}
	})
	return l2VTGate
}

// Begin is part of the queryservice.QueryService interface
func (l *L2VTGate) Begin(ctx context.Context, target *querypb.Target) (int64, error) {
	return l.gateway.Begin(ctx, target.Keyspace, target.Shard, target.TabletType)
}

// Commit is part of the queryservice.QueryService interface
func (l *L2VTGate) Commit(ctx context.Context, target *querypb.Target, transactionID int64) error {
	return l.gateway.Commit(ctx, target.Keyspace, target.Shard, target.TabletType, transactionID)
}

// Rollback is part of the queryservice.QueryService interface
func (l *L2VTGate) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) error {
	return l.gateway.Rollback(ctx, target.Keyspace, target.Shard, target.TabletType, transactionID)
}

// Execute is part of the queryservice.QueryService interface
func (l *L2VTGate) Execute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, transactionID int64) (*sqltypes.Result, error) {
	return l.gateway.Execute(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, transactionID)
}

// StreamExecute is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, sendReply func(*sqltypes.Result) error) error {
	stream, err := l.gateway.StreamExecute(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables)
	if err != nil {
		return err
	}
	for {
		r, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		if err := sendReply(r); err != nil {
			return err
		}
	}
}

// ExecuteBatch is part of the queryservice.QueryService interface
func (l *L2VTGate) ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, transactionID int64) ([]sqltypes.Result, error) {
	return l.gateway.ExecuteBatch(ctx, target.Keyspace, target.Shard, target.TabletType, queries, asTransaction, transactionID)
}

// SplitQuery is part of the queryservice.QueryService interface
func (l *L2VTGate) SplitQuery(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64) ([]querytypes.QuerySplit, error) {
	return l.gateway.SplitQuery(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, splitColumn, splitCount)
}

// SplitQueryV2 is part of the queryservice.QueryService interface
func (l *L2VTGate) SplitQueryV2(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
	return l.gateway.SplitQueryV2(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
}

// StreamHealthRegister is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamHealthRegister(chan<- *querypb.StreamHealthResponse) (int, error) {
	return -1, fmt.Errorf("L2VTGate does not provide health status")
}

// StreamHealthUnregister is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamHealthUnregister(int) error {
	return fmt.Errorf("L2VTGate does not provide health status")
}

// HandlePanic is part of the queryservice.QueryService interface
func (l *L2VTGate) HandlePanic(err *error) {
	if x := recover(); x != nil {
		*err = fmt.Errorf("uncaught panic: %v", x)
	}
}

// GetGatewayCacheStatus returns a displayable version of the Gateway cache.
func (l *L2VTGate) GetGatewayCacheStatus() gateway.TabletCacheStatusList {
	return l.gateway.CacheStatus()
}
<commit_msg>Adding extra accessor method.<commit_after>// Copyright 2016, Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package l2vtgate provides the core functionnality of a second-layer vtgate
// to route queries from an original vtgate to a subset of tablets.
package l2vtgate

import (
	"fmt"
	"io"

	log "github.com/golang/glog"
	"golang.org/x/net/context"

	"github.com/youtube/vitess/go/sqltypes"
	"github.com/youtube/vitess/go/vt/discovery"
	"github.com/youtube/vitess/go/vt/servenv"
	"github.com/youtube/vitess/go/vt/tabletserver/queryservice"
	"github.com/youtube/vitess/go/vt/tabletserver/querytypes"
	"github.com/youtube/vitess/go/vt/topo"
	"github.com/youtube/vitess/go/vt/vtgate/gateway"

	querypb "github.com/youtube/vitess/go/vt/proto/query"
	topodatapb "github.com/youtube/vitess/go/vt/proto/topodata"
)

var (
	l2VTGate *L2VTGate
)

// L2VTGate implements queryservice.QueryService and forwards queries to
// the underlying gateway.
type L2VTGate struct {
	gateway gateway.Gateway
}

// RegisterL2VTGate defines the type of registration mechanism.
type RegisterL2VTGate func(queryservice.QueryService)

// RegisterL2VTGates stores register funcs for L2VTGate server.
var RegisterL2VTGates []RegisterL2VTGate

// Init creates the single L2VTGate with the provided parameters.
func Init(hc discovery.HealthCheck, topoServer topo.Server, serv topo.SrvTopoServer, cell string, retryCount int, tabletTypesToWait []topodatapb.TabletType) *L2VTGate {
	if l2VTGate != nil {
		log.Fatalf("L2VTGate already initialized")
	}

	gateway := gateway.GetCreator()(hc, topoServer, serv, cell, retryCount, tabletTypesToWait)
	l2VTGate = &L2VTGate{
		gateway: gateway,
	}
	servenv.OnRun(func() {
		for _, f := range RegisterL2VTGates {
			f(l2VTGate)
		}
	})
	return l2VTGate
}

// Gateway returns this l2vtgate Gateway object (for tests mainly).
func (l *L2VTGate) Gateway() gateway.Gateway {
	return l.gateway
}

// Begin is part of the queryservice.QueryService interface
func (l *L2VTGate) Begin(ctx context.Context, target *querypb.Target) (int64, error) {
	return l.gateway.Begin(ctx, target.Keyspace, target.Shard, target.TabletType)
}

// Commit is part of the queryservice.QueryService interface
func (l *L2VTGate) Commit(ctx context.Context, target *querypb.Target, transactionID int64) error {
	return l.gateway.Commit(ctx, target.Keyspace, target.Shard, target.TabletType, transactionID)
}

// Rollback is part of the queryservice.QueryService interface
func (l *L2VTGate) Rollback(ctx context.Context, target *querypb.Target, transactionID int64) error {
	return l.gateway.Rollback(ctx, target.Keyspace, target.Shard, target.TabletType, transactionID)
}

// Execute is part of the queryservice.QueryService interface
func (l *L2VTGate) Execute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, transactionID int64) (*sqltypes.Result, error) {
	return l.gateway.Execute(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, transactionID)
}

// StreamExecute is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, sendReply func(*sqltypes.Result) error) error {
	stream, err := l.gateway.StreamExecute(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables)
	if err != nil {
		return err
	}
	for {
		r, err := stream.Recv()
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		if err := sendReply(r); err != nil {
			return err
		}
	}
}

// ExecuteBatch is part of the queryservice.QueryService interface
func (l *L2VTGate) ExecuteBatch(ctx context.Context, target *querypb.Target, queries []querytypes.BoundQuery, asTransaction bool, transactionID int64) ([]sqltypes.Result, error) {
	return l.gateway.ExecuteBatch(ctx, target.Keyspace, target.Shard, target.TabletType, queries, asTransaction, transactionID)
}

// SplitQuery is part of the queryservice.QueryService interface
func (l *L2VTGate) SplitQuery(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64) ([]querytypes.QuerySplit, error) {
	return l.gateway.SplitQuery(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, splitColumn, splitCount)
}

// SplitQueryV2 is part of the queryservice.QueryService interface
func (l *L2VTGate) SplitQueryV2(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, splitColumns []string, splitCount int64, numRowsPerQueryPart int64, algorithm querypb.SplitQueryRequest_Algorithm) ([]querytypes.QuerySplit, error) {
	return l.gateway.SplitQueryV2(ctx, target.Keyspace, target.Shard, target.TabletType, sql, bindVariables, splitColumns, splitCount, numRowsPerQueryPart, algorithm)
}

// StreamHealthRegister is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamHealthRegister(chan<- *querypb.StreamHealthResponse) (int, error) {
	return -1, fmt.Errorf("L2VTGate does not provide health status")
}

// StreamHealthUnregister is part of the queryservice.QueryService interface
func (l *L2VTGate) StreamHealthUnregister(int) error {
	return fmt.Errorf("L2VTGate does not provide health status")
}

// HandlePanic is part of the queryservice.QueryService interface
func (l *L2VTGate) HandlePanic(err *error) {
	if x := recover(); x != nil {
		*err = fmt.Errorf("uncaught panic: %v", x)
	}
}

// GetGatewayCacheStatus returns a displayable version of the Gateway cache.
func (l *L2VTGate) GetGatewayCacheStatus() gateway.TabletCacheStatusList {
	return l.gateway.CacheStatus()
}
<|endoftext|>


<commit_before>package dns

import (
	"bytes"
	"fmt"
	"html/template"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	apps "k8s.io/client-go/pkg/apis/apps/v1beta1"
)

const (
	SERVICE_ACCOUNT    = "kube-dns"
	CONFIGMAP          = "kube-dns"
	DEFAULT_REPOSITORY = "gcr.io/google_containers"
	DEFAULT_VERSION    = "1.14.5"
	DEFAULT_DOMAIN     = "cluster.local"
	DEFAULT_CLUSTER_IP = "198.18.254.254"
)

type DeploymentOptions struct {
	Repository string
	Version    string
	Domain     string
}

type ServiceOptions struct {
	ClusterIP string
}

func SeedKubeDNS(client clientset.Interface, repository, version, domain, clusterIP string) error {
	if repository == "" {
		repository = DEFAULT_REPOSITORY
	}

	if version == "" {
		version = DEFAULT_VERSION
	}

	if domain == "" {
		domain = DEFAULT_DOMAIN
	}

	if clusterIP == "" {
		clusterIP = DEFAULT_CLUSTER_IP
	}

	if err := createKubeDNSServiceAccount(client); err != nil {
		return err
	}

	if err := createKubeDNSConfigMap(client); err != nil {
		return err
	}

	if err := createKubeDNSDeployment(client, repository, version, domain); err != nil {
		return err
	}

	if err := createKubeDNSService(client, clusterIP); err != nil {
		return err
	}

	return nil
}

func createKubeDNSServiceAccount(client clientset.Interface) error {
	return CreateOrUpdateServiceAccount(client, &v1.ServiceAccount{
		ObjectMeta: metav1.ObjectMeta{
			Name:      SERVICE_ACCOUNT,
			Namespace: metav1.NamespaceSystem,
			Labels: map[string]string{
				"kubernetes.io/cluster-service":   "true",
				"addonmanager.kubernetes.io/mode": "Reconcile",
			},
		},
	})
}

func createKubeDNSConfigMap(client clientset.Interface) error {
	return CreateOrUpdateConfigMap(client, &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      CONFIGMAP,
			Namespace: metav1.NamespaceSystem,
			Labels: map[string]string{
				"addonmanager.kubernetes.io/mode": "EnsureExists",
			},
		},
	})
}

func createKubeDNSDeployment(client clientset.Interface, repository, version, domain string) error {
	options := &DeploymentOptions{
		Repository: repository,
		Version:    version,
		Domain:     domain,
	}

	deployment, err := getKubeDNSDeployment(options)
	if err != nil {
		return err
	}

	if err := CreateOrUpdateDeployment(client, deployment); err != nil {
		return err
	}

	return nil
}

func createKubeDNSService(client clientset.Interface, clusterIP string) error {
	options := &ServiceOptions{
		ClusterIP: clusterIP,
	}

	service, err := getKubeDNSService(options)
	if err != nil {
		return err
	}

	if err := CreateOrUpdateService(client, service); err != nil {
		return err
	}

	return nil
}

func getKubeDNSDeployment(options *DeploymentOptions) (*apps.Deployment, error) {
	manifest := KubeDNSDeployment_v20171016
	deployment := &apps.Deployment{}

	template, err := RenderManifest(manifest, options)
	if err != nil {
		return nil, err
	}

	if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), template, deployment); err != nil {
		return nil, err
	}

	return deployment, nil
}

func getKubeDNSService(options *ServiceOptions) (*v1.Service, error) {
	manifest := KubeDNSService_v20171016
	service := &v1.Service{}

	template, err := RenderManifest(manifest, options)
	if err != nil {
		return nil, err
	}

	if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), template, service); err != nil {
		return nil, err
	}

	return service, nil
}

func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAccount) error {
	if _, err := client.CoreV1().ServiceAccounts(sa.ObjectMeta.Namespace).Create(sa); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create serviceaccount: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateDeployment(client clientset.Interface, deploy *apps.Deployment) error {
	if _, err := client.AppsV1beta1().Deployments(deploy.ObjectMeta.Namespace).Create(deploy); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create deployment: %v", err)
		}

		if _, err := client.AppsV1beta1().Deployments(deploy.ObjectMeta.Namespace).Update(deploy); err != nil {
			return fmt.Errorf("unable to update deployment: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateService(client clientset.Interface, service *v1.Service) error {
	if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Create(service); err != nil {
		if !apierrors.IsAlreadyExists(err) && !apierrors.IsInvalid(err) {
			return fmt.Errorf("unable to create a new kube-dns service: %v", err)
		}

		if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Update(service); err != nil {
			return fmt.Errorf("unable to create/update the kube-dns service: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateConfigMap(client clientset.Interface, configmap *v1.ConfigMap) error {
	if _, err := client.CoreV1().ConfigMaps(configmap.ObjectMeta.Namespace).Create(configmap); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create configmap: %v", err)
		}

		if _, err := client.CoreV1().ConfigMaps(configmap.ObjectMeta.Namespace).Update(configmap); err != nil {
			return fmt.Errorf("unable to update configmap: %v", err)
		}
	}
	return nil
}

func RenderManifest(strtmpl string, obj interface{}) ([]byte, error) {
	var buf bytes.Buffer
	tmpl, err := template.New("template").Parse(strtmpl)
	if err != nil {
		return nil, fmt.Errorf("error when parsing template: %v", err)
	}
	err = tmpl.Execute(&buf, obj)
	if err != nil {
		return nil, fmt.Errorf("error when executing template: %v", err)
	}
	return buf.Bytes(), nil
}
<commit_msg>Don’t fallthrough to update when create returns invalid<commit_after>package dns

import (
	"bytes"
	"fmt"
	"html/template"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/api/v1"
	apps "k8s.io/client-go/pkg/apis/apps/v1beta1"
)

const (
	SERVICE_ACCOUNT    = "kube-dns"
	CONFIGMAP          = "kube-dns"
	DEFAULT_REPOSITORY = "gcr.io/google_containers"
	DEFAULT_VERSION    = "1.14.5"
	DEFAULT_DOMAIN     = "cluster.local"
	DEFAULT_CLUSTER_IP = "198.18.254.254"
)

type DeploymentOptions struct {
	Repository string
	Version    string
	Domain     string
}

type ServiceOptions struct {
	ClusterIP string
}

func SeedKubeDNS(client clientset.Interface, repository, version, domain, clusterIP string) error {
	if repository == "" {
		repository = DEFAULT_REPOSITORY
	}

	if version == "" {
		version = DEFAULT_VERSION
	}

	if domain == "" {
		domain = DEFAULT_DOMAIN
	}

	if clusterIP == "" {
		clusterIP = DEFAULT_CLUSTER_IP
	}

	if err := createKubeDNSServiceAccount(client); err != nil {
		return err
	}

	if err := createKubeDNSConfigMap(client); err != nil {
		return err
	}

	if err := createKubeDNSDeployment(client, repository, version, domain); err != nil {
		return err
	}

	if err := createKubeDNSService(client, clusterIP); err != nil {
		return err
	}

	return nil
}

func createKubeDNSServiceAccount(client clientset.Interface) error {
	return CreateOrUpdateServiceAccount(client, &v1.ServiceAccount{
		ObjectMeta: metav1.ObjectMeta{
			Name:      SERVICE_ACCOUNT,
			Namespace: metav1.NamespaceSystem,
			Labels: map[string]string{
				"kubernetes.io/cluster-service":   "true",
				"addonmanager.kubernetes.io/mode": "Reconcile",
			},
		},
	})
}

func createKubeDNSConfigMap(client clientset.Interface) error {
	return CreateOrUpdateConfigMap(client, &v1.ConfigMap{
		ObjectMeta: metav1.ObjectMeta{
			Name:      CONFIGMAP,
			Namespace: metav1.NamespaceSystem,
			Labels: map[string]string{
				"addonmanager.kubernetes.io/mode": "EnsureExists",
			},
		},
	})
}

func createKubeDNSDeployment(client clientset.Interface, repository, version, domain string) error {
	options := &DeploymentOptions{
		Repository: repository,
		Version:    version,
		Domain:     domain,
	}

	deployment, err := getKubeDNSDeployment(options)
	if err != nil {
		return err
	}

	if err := CreateOrUpdateDeployment(client, deployment); err != nil {
		return err
	}

	return nil
}

func createKubeDNSService(client clientset.Interface, clusterIP string) error {
	options := &ServiceOptions{
		ClusterIP: clusterIP,
	}

	service, err := getKubeDNSService(options)
	if err != nil {
		return err
	}

	if err := CreateOrUpdateService(client, service); err != nil {
		return err
	}

	return nil
}

func getKubeDNSDeployment(options *DeploymentOptions) (*apps.Deployment, error) {
	manifest := KubeDNSDeployment_v20171016
	deployment := &apps.Deployment{}

	template, err := RenderManifest(manifest, options)
	if err != nil {
		return nil, err
	}

	if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), template, deployment); err != nil {
		return nil, err
	}

	return deployment, nil
}

func getKubeDNSService(options *ServiceOptions) (*v1.Service, error) {
	manifest := KubeDNSService_v20171016
	service := &v1.Service{}

	template, err := RenderManifest(manifest, options)
	if err != nil {
		return nil, err
	}

	if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), template, service); err != nil {
		return nil, err
	}

	return service, nil
}

func CreateOrUpdateServiceAccount(client clientset.Interface, sa *v1.ServiceAccount) error {
	if _, err := client.CoreV1().ServiceAccounts(sa.ObjectMeta.Namespace).Create(sa); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create serviceaccount: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateDeployment(client clientset.Interface, deploy *apps.Deployment) error {
	if _, err := client.AppsV1beta1().Deployments(deploy.ObjectMeta.Namespace).Create(deploy); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create deployment: %v", err)
		}

		if _, err := client.AppsV1beta1().Deployments(deploy.ObjectMeta.Namespace).Update(deploy); err != nil {
			return fmt.Errorf("unable to update deployment: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateService(client clientset.Interface, service *v1.Service) error {
	if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Create(service); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create a new kube-dns service: %v", err)
		}

		if _, err := client.CoreV1().Services(metav1.NamespaceSystem).Update(service); err != nil {
			return fmt.Errorf("unable to create/update the kube-dns service: %v", err)
		}
	}
	return nil
}

func CreateOrUpdateConfigMap(client clientset.Interface, configmap *v1.ConfigMap) error {
	if _, err := client.CoreV1().ConfigMaps(configmap.ObjectMeta.Namespace).Create(configmap); err != nil {
		if !apierrors.IsAlreadyExists(err) {
			return fmt.Errorf("unable to create configmap: %v", err)
		}

		if _, err := client.CoreV1().ConfigMaps(configmap.ObjectMeta.Namespace).Update(configmap); err != nil {
			return fmt.Errorf("unable to update configmap: %v", err)
		}
	}
	return nil
}

func RenderManifest(strtmpl string, obj interface{}) ([]byte, error) {
	var buf bytes.Buffer
	tmpl, err := template.New("template").Parse(strtmpl)
	if err != nil {
		return nil, fmt.Errorf("error when parsing template: %v", err)
	}
	err = tmpl.Execute(&buf, obj)
	if err != nil {
		return nil, fmt.Errorf("error when executing template: %v", err)
	}
	return buf.Bytes(), nil
}
<|endoftext|>


<commit_before>package docker_registry

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

type gitHubApi struct{}

func newGitHubApi() gitHubApi {
	return gitHubApi{}
}

type githubApiUser struct {
	Login             string    `json:"login"`
	Id                int       `json:"id"`
	NodeId            string    `json:"node_id"`
	AvatarUrl         string    `json:"avatar_url"`
	GravatarId        string    `json:"gravatar_id"`
	Url               string    `json:"url"`
	HtmlUrl           string    `json:"html_url"`
	FollowersUrl      string    `json:"followers_url"`
	FollowingUrl      string    `json:"following_url"`
	GistsUrl          string    `json:"gists_url"`
	StarredUrl        string    `json:"starred_url"`
	SubscriptionsUrl  string    `json:"subscriptions_url"`
	OrganizationsUrl  string    `json:"organizations_url"`
	ReposUrl          string    `json:"repos_url"`
	EventsUrl         string    `json:"events_url"`
	ReceivedEventsUrl string    `json:"received_events_url"`
	Type              string    `json:"type"`
	SiteAdmin         bool      `json:"site_admin"`
	Name              string    `json:"name"`
	Company           string    `json:"company"`
	Blog              string    `json:"blog"`
	Location          string    `json:"location"`
	Email             string    `json:"email"`
	Hireable          bool      `json:"hireable"`
	Bio               string    `json:"bio"`
	TwitterUsername   string    `json:"twitter_username"`
	PublicRepos       int       `json:"public_repos"`
	PublicGists       int       `json:"public_gists"`
	Followers         int       `json:"followers"`
	Following         int       `json:"following"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
	Plan              struct {
		Name          string `json:"name"`
		Space         int    `json:"space"`
		Collaborators int    `json:"collaborators"`
		PrivateRepos  int    `json:"private_repos"`
	} `json:"plan"`
}

func (api *gitHubApi) getUser(ctx context.Context, username, token string) (githubApiUser, *http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/users/%s", username)
	resp, respBody, err := doRequest(ctx, http.MethodGet, url, nil, doRequestOptions{
		Headers: map[string]string{
			"Accept":        "application/vnd.github.v3+json",
			"Authorization": fmt.Sprintf("Bearer %s", token),
		},
		AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
	})
	if err != nil {
		return githubApiUser{}, resp, err
	}

	var user githubApiUser
	if err := json.Unmarshal(respBody, &user); err != nil {
		return githubApiUser{}, resp, fmt.Errorf("unexpected body %s", string(respBody))
	}

	return user, nil, nil
}

func (api *gitHubApi) deleteOrgContainerPackage(ctx context.Context, orgName, packageName, token string) (*http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s",
		orgName, packageName,
	)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) deleteOrgContainerPackageVersion(ctx context.Context, orgName, packageName, packageVersionId, token string) (*http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s/versions/%s",
		orgName, packageName, packageVersionId,
	)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) getOrgContainerPackageVersionId(ctx context.Context, orgName, packageName, tag, token string) (string, *http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s/versions",
		orgName,
		packageName,
	)
	return api.getContainerPackageVersionId(ctx, url, tag, token)
}

func (api *gitHubApi) deleteUserContainerPackage(ctx context.Context, packageName, token string) (*http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s", packageName)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) deleteUserContainerPackageVersion(ctx context.Context, packageName, packageVersionId, token string) (*http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s/versions/%s", packageName, packageVersionId)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) getUserContainerPackageVersionId(ctx context.Context, packageName, tag, token string) (string, *http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s/versions", packageName)
	return api.getContainerPackageVersionId(ctx, url, tag, token)
}

type githubApiVersion struct {
	Id             int       `json:"id"`
	Name           string    `json:"name"`
	Url            string    `json:"url"`
	PackageHtmlUrl string    `json:"package_html_url"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
	HtmlUrl        string    `json:"html_url"`
	Metadata       struct {
		PackageType string `json:"package_type"`
		Container   struct {
			Tags []string `json:"tags"`
		} `json:"container"`
	} `json:"metadata"`
}

func (api *gitHubApi) getContainerPackageVersionId(ctx context.Context, url, tag, token string) (string, *http.Response, error) {
	for page := 0; true; page++ {
		pageUrl := url + fmt.Sprintf("?page=%d&per_page=100", page)
		resp, respBody, err := doRequest(ctx, http.MethodGet, pageUrl, nil, doRequestOptions{
			Headers: map[string]string{
				"Accept":        "application/vnd.github.v3+json",
				"Authorization": fmt.Sprintf("Bearer %s", token),
			},
			AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
		})
		if err != nil {
			return "", resp, err
		}

		var pageVersionList []githubApiVersion
		if err := json.Unmarshal(respBody, &pageVersionList); err != nil {
			return "", resp, fmt.Errorf("unexpected body %s", string(respBody))
		}

		if len(pageVersionList) == 0 {
			break
		}

		for _, version := range pageVersionList {
			for _, t := range version.Metadata.Container.Tags {
				if t == tag {
					return fmt.Sprint(version.Id), nil, nil
				}
			}
		}
	}

	return "", nil, fmt.Errorf("container package version id for tag %q not found", tag)
}

func (api *gitHubApi) deleteContainerPackage(ctx context.Context, url, token string) (*http.Response, error) {
	resp, _, err := doRequest(ctx, http.MethodDelete, url, nil, doRequestOptions{
		Headers: map[string]string{
			"Accept":        "application/vnd.github.v3+json",
			"Authorization": fmt.Sprintf("Bearer %s", token),
		},
		AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
	})
	if err != nil {
		return resp, err
	}

	return nil, nil
}
<commit_msg>feat(container_registry): handle secondary rate limit error deleting tags from ghcr.io<commit_after>package docker_registry

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"time"

	"github.com/werf/logboek"
)

type gitHubApi struct{}

func newGitHubApi() gitHubApi {
	return gitHubApi{}
}

type githubApiUser struct {
	Login             string    `json:"login"`
	Id                int       `json:"id"`
	NodeId            string    `json:"node_id"`
	AvatarUrl         string    `json:"avatar_url"`
	GravatarId        string    `json:"gravatar_id"`
	Url               string    `json:"url"`
	HtmlUrl           string    `json:"html_url"`
	FollowersUrl      string    `json:"followers_url"`
	FollowingUrl      string    `json:"following_url"`
	GistsUrl          string    `json:"gists_url"`
	StarredUrl        string    `json:"starred_url"`
	SubscriptionsUrl  string    `json:"subscriptions_url"`
	OrganizationsUrl  string    `json:"organizations_url"`
	ReposUrl          string    `json:"repos_url"`
	EventsUrl         string    `json:"events_url"`
	ReceivedEventsUrl string    `json:"received_events_url"`
	Type              string    `json:"type"`
	SiteAdmin         bool      `json:"site_admin"`
	Name              string    `json:"name"`
	Company           string    `json:"company"`
	Blog              string    `json:"blog"`
	Location          string    `json:"location"`
	Email             string    `json:"email"`
	Hireable          bool      `json:"hireable"`
	Bio               string    `json:"bio"`
	TwitterUsername   string    `json:"twitter_username"`
	PublicRepos       int       `json:"public_repos"`
	PublicGists       int       `json:"public_gists"`
	Followers         int       `json:"followers"`
	Following         int       `json:"following"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
	Plan              struct {
		Name          string `json:"name"`
		Space         int    `json:"space"`
		Collaborators int    `json:"collaborators"`
		PrivateRepos  int    `json:"private_repos"`
	} `json:"plan"`
}

func (api *gitHubApi) getUser(ctx context.Context, username, token string) (githubApiUser, *http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/users/%s", username)
	resp, respBody, err := doRequest(ctx, http.MethodGet, url, nil, doRequestOptions{
		Headers: map[string]string{
			"Accept":        "application/vnd.github.v3+json",
			"Authorization": fmt.Sprintf("Bearer %s", token),
		},
		AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
	})
	if err != nil {
		return githubApiUser{}, resp, err
	}

	var user githubApiUser
	if err := json.Unmarshal(respBody, &user); err != nil {
		return githubApiUser{}, resp, fmt.Errorf("unexpected body %s", string(respBody))
	}

	return user, nil, nil
}

func (api *gitHubApi) deleteOrgContainerPackage(ctx context.Context, orgName, packageName, token string) (*http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s",
		orgName, packageName,
	)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) deleteOrgContainerPackageVersion(ctx context.Context, orgName, packageName, packageVersionId, token string) (*http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s/versions/%s",
		orgName, packageName, packageVersionId,
	)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) getOrgContainerPackageVersionId(ctx context.Context, orgName, packageName, tag, token string) (string, *http.Response, error) {
	url := fmt.Sprintf(
		"https://api.github.com/orgs/%s/packages/container/%s/versions",
		orgName,
		packageName,
	)
	return api.getContainerPackageVersionId(ctx, url, tag, token)
}

func (api *gitHubApi) deleteUserContainerPackage(ctx context.Context, packageName, token string) (*http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s", packageName)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) deleteUserContainerPackageVersion(ctx context.Context, packageName, packageVersionId, token string) (*http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s/versions/%s", packageName, packageVersionId)
	return api.deleteContainerPackage(ctx, url, token)
}

func (api *gitHubApi) getUserContainerPackageVersionId(ctx context.Context, packageName, tag, token string) (string, *http.Response, error) {
	url := fmt.Sprintf("https://api.github.com/user/packages/container/%s/versions", packageName)
	return api.getContainerPackageVersionId(ctx, url, tag, token)
}

type githubApiVersion struct {
	Id             int       `json:"id"`
	Name           string    `json:"name"`
	Url            string    `json:"url"`
	PackageHtmlUrl string    `json:"package_html_url"`
	CreatedAt      time.Time `json:"created_at"`
	UpdatedAt      time.Time `json:"updated_at"`
	HtmlUrl        string    `json:"html_url"`
	Metadata       struct {
		PackageType string `json:"package_type"`
		Container   struct {
			Tags []string `json:"tags"`
		} `json:"container"`
	} `json:"metadata"`
}

func (api *gitHubApi) getContainerPackageVersionId(ctx context.Context, url, tag, token string) (string, *http.Response, error) {
	for page := 0; true; page++ {
		pageUrl := url + fmt.Sprintf("?page=%d&per_page=100", page)
		resp, respBody, err := doRequest(ctx, http.MethodGet, pageUrl, nil, doRequestOptions{
			Headers: map[string]string{
				"Accept":        "application/vnd.github.v3+json",
				"Authorization": fmt.Sprintf("Bearer %s", token),
			},
			AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
		})
		if err != nil {
			return "", resp, err
		}

		var pageVersionList []githubApiVersion
		if err := json.Unmarshal(respBody, &pageVersionList); err != nil {
			return "", resp, fmt.Errorf("unexpected body %s", string(respBody))
		}

		if len(pageVersionList) == 0 {
			break
		}

		for _, version := range pageVersionList {
			for _, t := range version.Metadata.Container.Tags {
				if t == tag {
					return fmt.Sprint(version.Id), nil, nil
				}
			}
		}
	}

	return "", nil, fmt.Errorf("container package version id for tag %q not found", tag)
}

func (api *gitHubApi) deleteContainerPackage(ctx context.Context, url, token string) (*http.Response, error) {
	resp, _, err := doRequest(ctx, http.MethodDelete, url, nil, doRequestOptions{
		Headers: map[string]string{
			"Accept":        "application/vnd.github.v3+json",
			"Authorization": fmt.Sprintf("Bearer %s", token),
		},
		AcceptedCodes: []int{http.StatusOK, http.StatusAccepted, http.StatusNoContent},
	})
	if err != nil {
		if resp != nil && resp.StatusCode == http.StatusForbidden {
			secondsString := resp.Header.Get("Retry-After")
			seconds, err := strconv.Atoi(secondsString)
			if err == nil {
				logboek.Context(ctx).Warn().LogF(
					"WARNING: Secondary rate limit error occurred. Waiting for %d before retrying package delete request...\n",
					seconds,
				)
				time.Sleep(time.Second * time.Duration(seconds))
				return api.deleteContainerPackage(ctx, url, token)
			}
		}

		return resp, err
	}

	return nil, nil
}
<|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 configmap

import (
	"fmt"
	"time"

	"k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
	corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/kubelet/util/manager"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/watch"
)

type Manager interface {
	// Get configmap by configmap namespace and name.
	GetConfigMap(namespace, name string) (*v1.ConfigMap, error)

	// WARNING: Register/UnregisterPod functions should be efficient,
	// i.e. should not block on network operations.

	// RegisterPod registers all configmaps from a given pod.
	RegisterPod(pod *v1.Pod)

	// UnregisterPod unregisters configmaps from a given pod that are not
	// used by any other registered pod.
	UnregisterPod(pod *v1.Pod)
}

// simpleConfigMapManager implements ConfigMap Manager interface with
// simple operations to apiserver.
type simpleConfigMapManager struct {
	kubeClient clientset.Interface
}

func NewSimpleConfigMapManager(kubeClient clientset.Interface) Manager {
	return &simpleConfigMapManager{kubeClient: kubeClient}
}

func (s *simpleConfigMapManager) GetConfigMap(namespace, name string) (*v1.ConfigMap, error) {
	return s.kubeClient.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{})
}

func (s *simpleConfigMapManager) RegisterPod(pod *v1.Pod) {
}

func (s *simpleConfigMapManager) UnregisterPod(pod *v1.Pod) {
}

// configMapManager keeps a cache of all configmaps necessary
// for registered pods. Different implementation of the store
// may result in different semantics for freshness of configmaps
// (e.g. ttl-based implementation vs watch-based implementation).
type configMapManager struct {
	manager manager.Manager
}

func (c *configMapManager) GetConfigMap(namespace, name string) (*v1.ConfigMap, error) {
	object, err := c.manager.GetObject(namespace, name)
	if err != nil {
		return nil, err
	}
	if configmap, ok := object.(*v1.ConfigMap); ok {
		return configmap, nil
	}
	return nil, fmt.Errorf("unexpected object type: %v", object)
}

func (c *configMapManager) RegisterPod(pod *v1.Pod) {
	c.manager.RegisterPod(pod)
}

func (c *configMapManager) UnregisterPod(pod *v1.Pod) {
	c.manager.UnregisterPod(pod)
}

func getConfigMapNames(pod *v1.Pod) sets.String {
	result := sets.NewString()
	podutil.VisitPodConfigmapNames(pod, func(name string) bool {
		result.Insert(name)
		return true
	})
	return result
}

const (
	defaultTTL = time.Minute
)

// NewCachingConfigMapManager creates a manager that keeps a cache of all configmaps
// necessary for registered pods.
// It implement the following logic:
// - whenever a pod is create or updated, the cached versions of all configmaps
//   are invalidated
// - every GetObject() call tries to fetch the value from local cache; if it is
//   not there, invalidated or too old, we fetch it from apiserver and refresh the
//   value in cache; otherwise it is just fetched from cache
func NewCachingConfigMapManager(kubeClient clientset.Interface, getTTL manager.GetObjectTTLFunc) Manager {
	getConfigMap := func(namespace, name string, opts metav1.GetOptions) (runtime.Object, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).Get(name, opts)
	}
	configMapStore := manager.NewObjectStore(getConfigMap, clock.RealClock{}, getTTL, defaultTTL)
	return &configMapManager{
		manager: manager.NewCacheBasedManager(configMapStore, getConfigMapNames),
	}
}

// NewWatchingConfigMapManager creates a manager that keeps a cache of all configmaps
// necessary for registered pods.
// It implements the following logic:
// - whenever a pod is created or updated, we start inidvidual watches for all
//   referenced objects that aren't referenced from other registered pods
// - every GetObject() returns a value from local cache propagated via watches
func NewWatchingConfigMapManager(kubeClient clientset.Interface) Manager {
	listConfigMap := func(namespace string, opts metav1.ListOptions) (runtime.Object, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).List(opts)
	}
	watchConfigMap := func(namespace string, opts metav1.ListOptions) (watch.Interface, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).Watch(opts)
	}
	newConfigMap := func() runtime.Object {
		return &v1.ConfigMap{}
	}
	gr := corev1.Resource("configmap")
	return &configMapManager{
		manager: manager.NewWatchBasedManager(listConfigMap, watchConfigMap, newConfigMap, gr, getConfigMapNames),
	}
}
<commit_msg>fix typo: "inidvidual"-> "individual"<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 configmap

import (
	"fmt"
	"time"

	"k8s.io/api/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
	corev1 "k8s.io/kubernetes/pkg/apis/core/v1"
	"k8s.io/kubernetes/pkg/kubelet/util/manager"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/watch"
)

type Manager interface {
	// Get configmap by configmap namespace and name.
	GetConfigMap(namespace, name string) (*v1.ConfigMap, error)

	// WARNING: Register/UnregisterPod functions should be efficient,
	// i.e. should not block on network operations.

	// RegisterPod registers all configmaps from a given pod.
	RegisterPod(pod *v1.Pod)

	// UnregisterPod unregisters configmaps from a given pod that are not
	// used by any other registered pod.
	UnregisterPod(pod *v1.Pod)
}

// simpleConfigMapManager implements ConfigMap Manager interface with
// simple operations to apiserver.
type simpleConfigMapManager struct {
	kubeClient clientset.Interface
}

func NewSimpleConfigMapManager(kubeClient clientset.Interface) Manager {
	return &simpleConfigMapManager{kubeClient: kubeClient}
}

func (s *simpleConfigMapManager) GetConfigMap(namespace, name string) (*v1.ConfigMap, error) {
	return s.kubeClient.CoreV1().ConfigMaps(namespace).Get(name, metav1.GetOptions{})
}

func (s *simpleConfigMapManager) RegisterPod(pod *v1.Pod) {
}

func (s *simpleConfigMapManager) UnregisterPod(pod *v1.Pod) {
}

// configMapManager keeps a cache of all configmaps necessary
// for registered pods. Different implementation of the store
// may result in different semantics for freshness of configmaps
// (e.g. ttl-based implementation vs watch-based implementation).
type configMapManager struct {
	manager manager.Manager
}

func (c *configMapManager) GetConfigMap(namespace, name string) (*v1.ConfigMap, error) {
	object, err := c.manager.GetObject(namespace, name)
	if err != nil {
		return nil, err
	}
	if configmap, ok := object.(*v1.ConfigMap); ok {
		return configmap, nil
	}
	return nil, fmt.Errorf("unexpected object type: %v", object)
}

func (c *configMapManager) RegisterPod(pod *v1.Pod) {
	c.manager.RegisterPod(pod)
}

func (c *configMapManager) UnregisterPod(pod *v1.Pod) {
	c.manager.UnregisterPod(pod)
}

func getConfigMapNames(pod *v1.Pod) sets.String {
	result := sets.NewString()
	podutil.VisitPodConfigmapNames(pod, func(name string) bool {
		result.Insert(name)
		return true
	})
	return result
}

const (
	defaultTTL = time.Minute
)

// NewCachingConfigMapManager creates a manager that keeps a cache of all configmaps
// necessary for registered pods.
// It implement the following logic:
// - whenever a pod is create or updated, the cached versions of all configmaps
//   are invalidated
// - every GetObject() call tries to fetch the value from local cache; if it is
//   not there, invalidated or too old, we fetch it from apiserver and refresh the
//   value in cache; otherwise it is just fetched from cache
func NewCachingConfigMapManager(kubeClient clientset.Interface, getTTL manager.GetObjectTTLFunc) Manager {
	getConfigMap := func(namespace, name string, opts metav1.GetOptions) (runtime.Object, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).Get(name, opts)
	}
	configMapStore := manager.NewObjectStore(getConfigMap, clock.RealClock{}, getTTL, defaultTTL)
	return &configMapManager{
		manager: manager.NewCacheBasedManager(configMapStore, getConfigMapNames),
	}
}

// NewWatchingConfigMapManager creates a manager that keeps a cache of all configmaps
// necessary for registered pods.
// It implements the following logic:
// - whenever a pod is created or updated, we start individual watches for all
//   referenced objects that aren't referenced from other registered pods
// - every GetObject() returns a value from local cache propagated via watches
func NewWatchingConfigMapManager(kubeClient clientset.Interface) Manager {
	listConfigMap := func(namespace string, opts metav1.ListOptions) (runtime.Object, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).List(opts)
	}
	watchConfigMap := func(namespace string, opts metav1.ListOptions) (watch.Interface, error) {
		return kubeClient.CoreV1().ConfigMaps(namespace).Watch(opts)
	}
	newConfigMap := func() runtime.Object {
		return &v1.ConfigMap{}
	}
	gr := corev1.Resource("configmap")
	return &configMapManager{
		manager: manager.NewWatchBasedManager(listConfigMap, watchConfigMap, newConfigMap, gr, getConfigMapNames),
	}
}
<|endoftext|>


<commit_before>package notifiers

import (
	"encoding/json"
	"strconv"
	"strings"

	"fmt"

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

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "hipchat",
		Name:        "HipChat",
		Description: "Sends notifications uto a HipChat Room",
		Factory:     NewHipChatNotifier,
		OptionsTemplate: `

HipChat settings

			        Hip Chat Url
			        <input type="text" required class="gf-form-input max-width-30" ng-model="ctrl.model.settings.url" placeholder="HipChat URL (ex https://grafana.hipchat.com)"></input>

        API Key
        <input type="text" required class="gf-form-input max-width-30" ng-model="ctrl.model.settings.apikey" placeholder="HipChat API Key"></input>

        Room ID
        <input type="text"
          class="gf-form-input max-width-30"
          ng-model="ctrl.model.settings.roomid"
          data-placement="right">
        </input>

    `,
	})

}

const (
	maxFieldCount int = 4
)

func NewHipChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if strings.HasSuffix(url, "/") {
		url = url[:len(url)-1]
	}
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	apikey := model.Settings.Get("apikey").MustString()
	roomId := model.Settings.Get("roomid").MustString()

	return &HipChatNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Url:          url,
		ApiKey:       apikey,
		RoomId:       roomId,
		log:          log.New("alerting.notifier.hipchat"),
	}, nil
}

type HipChatNotifier struct {
	NotifierBase
	Url    string
	ApiKey string
	RoomId string
	log    log.Logger
}

func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)

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

	attributes := make([]map[string]interface{}, 0)
	for index, evt := range evalContext.EvalMatches {
		attributes = append(attributes, map[string]interface{}{
			"label": evt.Metric,
			"value": map[string]interface{}{
				"label": strconv.FormatFloat(evt.Value.Float64, 'f', -1, 64),
			},
		})
		if index > maxFieldCount {
			break
		}
	}

	if evalContext.Error != nil {
		attributes = append(attributes, map[string]interface{}{
			"label": "Error message",
			"value": map[string]interface{}{
				"label": evalContext.Error.Error(),
			},
		})
	}

	message := ""
	if evalContext.Rule.State != models.AlertStateOK { //dont add message when going back to alert state ok.
		message += " " + evalContext.Rule.Message
	}
	//HipChat has a set list of colors
	var color string
	switch evalContext.Rule.State {
	case models.AlertStateOK:
		color = "green"
	case models.AlertStateNoData:
		color = "grey"
	case models.AlertStateAlerting:
		color = "red"
	}

	// Add a card with link to the dashboard
	card := map[string]interface{}{
		"style":       "application",
		"url":         ruleUrl,
		"id":          "1",
		"title":       evalContext.GetNotificationTitle(),
		"description": message,
		"icon": map[string]interface{}{
			"url": "https://grafana.com/assets/img/fav32.png",
		},
		"date":       evalContext.EndTime.Unix(),
		"attributes": attributes,
	}

	body := map[string]interface{}{
		"message":        message,
		"notify":         "true",
		"message_format": "html",
		"color":          color,
		"card":           card,
	}

	hipUrl := fmt.Sprintf("%s/v2/room/%s/notification?auth_token=%s", this.Url, this.RoomId, this.ApiKey)
	data, _ := json.Marshal(&body)
	cmd := &models.SendWebhookSync{Url: hipUrl, Body: string(data)}

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

	return nil
}
<commit_msg>Add thumbnail to card<commit_after>package notifiers

import (
	"encoding/json"
	"strconv"
	"strings"

	"fmt"

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

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "hipchat",
		Name:        "HipChat",
		Description: "Sends notifications uto a HipChat Room",
		Factory:     NewHipChatNotifier,
		OptionsTemplate: `

HipChat settings

			        Hip Chat Url
			        <input type="text" required class="gf-form-input max-width-30" ng-model="ctrl.model.settings.url" placeholder="HipChat URL (ex https://grafana.hipchat.com)"></input>

        API Key
        <input type="text" required class="gf-form-input max-width-30" ng-model="ctrl.model.settings.apikey" placeholder="HipChat API Key"></input>

        Room ID
        <input type="text"
          class="gf-form-input max-width-30"
          ng-model="ctrl.model.settings.roomid"
          data-placement="right">
        </input>

    `,
	})

}

const (
	maxFieldCount int = 4
)

func NewHipChatNotifier(model *models.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if strings.HasSuffix(url, "/") {
		url = url[:len(url)-1]
	}
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	apikey := model.Settings.Get("apikey").MustString()
	roomId := model.Settings.Get("roomid").MustString()

	return &HipChatNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Url:          url,
		ApiKey:       apikey,
		RoomId:       roomId,
		log:          log.New("alerting.notifier.hipchat"),
	}, nil
}

type HipChatNotifier struct {
	NotifierBase
	Url    string
	ApiKey string
	RoomId string
	log    log.Logger
}

func (this *HipChatNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Executing hipchat notification", "ruleId", evalContext.Rule.Id, "notification", this.Name)

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

	attributes := make([]map[string]interface{}, 0)
	for index, evt := range evalContext.EvalMatches {
		attributes = append(attributes, map[string]interface{}{
			"label": evt.Metric,
			"value": map[string]interface{}{
				"label": strconv.FormatFloat(evt.Value.Float64, 'f', -1, 64),
			},
		})
		if index > maxFieldCount {
			break
		}
	}

	if evalContext.Error != nil {
		attributes = append(attributes, map[string]interface{}{
			"label": "Error message",
			"value": map[string]interface{}{
				"label": evalContext.Error.Error(),
			},
		})
	}

	message := ""
	if evalContext.Rule.State != models.AlertStateOK { //dont add message when going back to alert state ok.
		message += " " + evalContext.Rule.Message
	}
	//HipChat has a set list of colors
	var color string
	switch evalContext.Rule.State {
	case models.AlertStateOK:
		color = "green"
	case models.AlertStateNoData:
		color = "grey"
	case models.AlertStateAlerting:
		color = "red"
	}

	// Add a card with link to the dashboard
	card := map[string]interface{}{
		"style":       "application",
		"url":         ruleUrl,
		"id":          "1",
		"title":       evalContext.GetNotificationTitle(),
		"description": message,
		"icon": map[string]interface{}{
			"url": "https://grafana.com/assets/img/fav32.png",
		},
		"date":       evalContext.EndTime.Unix(),
		"attributes": attributes,
	}
	if len(evalContext.ImagePublicUrl) > 0 {
		card["thumbnail"] = map[string]interface{}{
			"url":    evalContext.ImagePublicUrl,
			"url@2x": evalContext.ImagePublicUrl,
			"width":  1193,
			"height": 564,
		}
	}

	body := map[string]interface{}{
		"message":        message,
		"notify":         "true",
		"message_format": "html",
		"color":          color,
		"card":           card,
	}

	hipUrl := fmt.Sprintf("%s/v2/room/%s/notification?auth_token=%s", this.Url, this.RoomId, this.ApiKey)
	data, _ := json.Marshal(&body)
	cmd := &models.SendWebhookSync{Url: hipUrl, Body: string(data)}

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

	return nil
}
<|endoftext|>


<commit_before>package astar

import (
	"container/heap"
	//"fmt"
)

// astar is an A* pathfinding implementation.

// Pather is an interface which allows A* searching on arbitrary objects which
// can represent a weighted graph.
type Pather interface {
	// PathNeighbors returns the direct neighboring nodes of this node which
	// can be pathed to.
	PathNeighbors() []Pather
	// PathNeighborCost calculates the exact movement cost to neighbor nodes.
	PathNeighborCost(to Pather) float64
	// PathEstimatedCost is a heuristic method for estimating movement costs
	// between non-adjacent nodes.
	PathEstimatedCost(to Pather) float64
}

// node is a wrapper to store A* data for a Pather node.
type node struct {
	pather Pather
	cost   float64
	rank   float64
	parent *node
	open   bool
	closed bool
	index  int
}

// nodeMap is a collection of nodes keyed by Pather nodes for quick reference.
type nodeMap map[Pather]*node

// get gets the Pather object wrapped in a node, instantiating if required.
func (nm nodeMap) get_node_from_pather(p Pather) *node {
	n, ok := nm[p]
	if !ok {
		n = &node{
			pather: p,
		}
		nm[p] = n
	}
	return n
}

func expand(nm nodeMap, nq *priorityQueue, curnode *node, dest Pather) {
	for _, neighbor := range curnode.pather.PathNeighbors() {

		cost := curnode.cost + curnode.pather.PathNeighborCost(neighbor)
		neighborNode := nm.get_node_from_pather(neighbor)

		if cost < neighborNode.cost {
			if neighborNode.open {
				heap.Remove(nq, neighborNode.index)
			}
			neighborNode.open = false
			neighborNode.closed = false
		}
		if !neighborNode.open && !neighborNode.closed {
			neighborNode.cost = cost
			neighborNode.open = true
			neighborNode.rank = cost + neighbor.PathEstimatedCost(dest)
			neighborNode.parent = curnode
			heap.Push(nq, neighborNode)
		}
	}
}

// Path calculates a short path and the distance between the two Pather nodes.
//
// If no path is found, found will be false.
func Path(from Pather, to Pather) (path []Pather, distance float64, found bool) {
	fwd_nodemap := nodeMap{}
	fwd_nq := &priorityQueue{} // fwd priq

	heap.Init(fwd_nq)

	fromNode := fwd_nodemap.get_node_from_pather(from)
	fromNode.open = true

	heap.Push(fwd_nq, fromNode)

	for {
		if fwd_nq.Len() == 0 {
			// There's no path, return found false.
			return
		}

		fwd_curnode := heap.Pop(fwd_nq).(*node)
		fwd_curnode.open = false
		fwd_curnode.closed = true
		fwd_pather := fwd_curnode.pather
		//fmt.Println(fwd_curnode)

		if fwd_pather == to {
			// Found a path to the goal.
			//fmt.Println("RES:", fwd_pather)

			p := []Pather{}

			curr := fwd_curnode
			for curr != nil {
				p = append(p, curr.pather)
				//fmt.Println(curr.pather)
				curr = curr.parent
			}

			return p, fwd_curnode.cost, true
		}

		expand(fwd_nodemap, fwd_nq, fwd_curnode, to)

	}
}

// Path calculates a short path and the distance between the two Pather nodes.
//
// If no path is found, found will be false.
func PathBidir(from Pather, to Pather) (path []Pather, distance float64, found bool) {
	fwd_nodemap := nodeMap{}
	fwd_nq := &priorityQueue{} // fwd priq

	rev_nodemap := nodeMap{}
	rev_nq := &priorityQueue{} // rev priq

	heap.Init(fwd_nq)
	heap.Init(rev_nq)

	fromNode := fwd_nodemap.get_node_from_pather(from)
	fromNode.open = true

	toNode := rev_nodemap.get_node_from_pather(to)
	toNode.open = true

	heap.Push(fwd_nq, fromNode)
	heap.Push(rev_nq, toNode)

	for {
		if fwd_nq.Len() == 0 || rev_nq.Len() == 0 {
			// There's no path, return found false.
			return
		}

		fwd_curnode := heap.Pop(fwd_nq).(*node)
		fwd_curnode.open = false
		fwd_curnode.closed = true
		fwd_pather := fwd_curnode.pather
		//fmt.Println(fwd_curnode)

		rev_curnode := heap.Pop(rev_nq).(*node)
		rev_curnode.open = false
		rev_curnode.closed = true
		//rev_pather := rev_curnode.pather
		//fmt.Println(rev_curnode)

		fwd_node_in_rev_map := rev_nodemap.get_node_from_pather(fwd_pather)
		if fwd_node_in_rev_map.closed || fwd_node_in_rev_map.open || fwd_pather == to {
			// Found a path to the goal.
			//fmt.Println("RES:", fwd_node_in_rev_map.closed, fwd_node_in_rev_map.open, fwd_pather) //, rev_pather)

			rp := []Pather{}
			curr := fwd_node_in_rev_map
			for curr != nil {
				rp = append(rp, curr.pather)
				curr = curr.parent
			}

			// reverse the path from touchpoint to "to"
			p := []Pather{}
			for i := len(rp) - 1; i >= 0; i = i - 1 {
				p = append(p, rp[i])
				//fmt.Println(rp[i])
			}
			//fmt.Println("revdone")

			curr = fwd_curnode.parent
			for curr != nil {
				p = append(p, curr.pather)
				//fmt.Println(curr.pather)
				curr = curr.parent
			}

			return p, fwd_curnode.parent.cost + fwd_node_in_rev_map.cost, true
		}

		expand(fwd_nodemap, fwd_nq, fwd_curnode, to)
		expand(rev_nodemap, rev_nq, rev_curnode, from)

	}
}
<commit_msg>bidir cost bugfix<commit_after>package astar

import (
	"container/heap"
	//"fmt"
)

// astar is an A* pathfinding implementation.

// Pather is an interface which allows A* searching on arbitrary objects which
// can represent a weighted graph.
type Pather interface {
	// PathNeighbors returns the direct neighboring nodes of this node which
	// can be pathed to.
	PathNeighbors() []Pather
	// PathNeighborCost calculates the exact movement cost to neighbor nodes.
	PathNeighborCost(to Pather) float64
	// PathEstimatedCost is a heuristic method for estimating movement costs
	// between non-adjacent nodes.
	PathEstimatedCost(to Pather) float64
}

// node is a wrapper to store A* data for a Pather node.
type node struct {
	pather Pather
	cost   float64
	rank   float64
	parent *node
	open   bool
	closed bool
	index  int
}

// nodeMap is a collection of nodes keyed by Pather nodes for quick reference.
type nodeMap map[Pather]*node

// get gets the Pather object wrapped in a node, instantiating if required.
func (nm nodeMap) get_node_from_pather(p Pather) *node {
	n, ok := nm[p]
	if !ok {
		n = &node{
			pather: p,
		}
		nm[p] = n
	}
	return n
}

func expand(nm nodeMap, nq *priorityQueue, curnode *node, dest Pather) {
	for _, neighbor := range curnode.pather.PathNeighbors() {

		cost := curnode.cost + curnode.pather.PathNeighborCost(neighbor)
		neighborNode := nm.get_node_from_pather(neighbor)

		if cost < neighborNode.cost {
			if neighborNode.open {
				heap.Remove(nq, neighborNode.index)
			}
			neighborNode.open = false
			neighborNode.closed = false
		}
		if !neighborNode.open && !neighborNode.closed {
			neighborNode.cost = cost
			neighborNode.open = true
			neighborNode.rank = cost + neighbor.PathEstimatedCost(dest)
			neighborNode.parent = curnode
			heap.Push(nq, neighborNode)
		}
	}
}

// Path calculates a short path and the distance between the two Pather nodes.
//
// If no path is found, found will be false.
func Path(from Pather, to Pather) (path []Pather, distance float64, found bool) {
	fwd_nodemap := nodeMap{}
	fwd_nq := &priorityQueue{} // fwd priq

	heap.Init(fwd_nq)

	fromNode := fwd_nodemap.get_node_from_pather(from)
	fromNode.open = true

	heap.Push(fwd_nq, fromNode)

	for {
		if fwd_nq.Len() == 0 {
			// There's no path, return found false.
			return
		}

		fwd_curnode := heap.Pop(fwd_nq).(*node)
		fwd_curnode.open = false
		fwd_curnode.closed = true
		fwd_pather := fwd_curnode.pather
		//fmt.Println(fwd_curnode)

		if fwd_pather == to {
			// Found a path to the goal.
			//fmt.Println("RES:", fwd_pather)

			p := []Pather{}

			curr := fwd_curnode
			for curr != nil {
				p = append(p, curr.pather)
				//fmt.Println(curr.pather)
				curr = curr.parent
			}

			return p, fwd_curnode.cost, true
		}

		expand(fwd_nodemap, fwd_nq, fwd_curnode, to)

	}
}

// Path calculates a short path and the distance between the two Pather nodes.
//
// If no path is found, found will be false.
func PathBidir(from Pather, to Pather) (path []Pather, distance float64, found bool) {
	fwd_nodemap := nodeMap{}
	fwd_nq := &priorityQueue{} // fwd priq

	rev_nodemap := nodeMap{}
	rev_nq := &priorityQueue{} // rev priq

	heap.Init(fwd_nq)
	heap.Init(rev_nq)

	fromNode := fwd_nodemap.get_node_from_pather(from)
	fromNode.open = true

	toNode := rev_nodemap.get_node_from_pather(to)
	toNode.open = true

	heap.Push(fwd_nq, fromNode)
	heap.Push(rev_nq, toNode)

	for {
		if fwd_nq.Len() == 0 || rev_nq.Len() == 0 {
			// There's no path, return found false.
			return
		}

		fwd_curnode := heap.Pop(fwd_nq).(*node)
		fwd_curnode.open = false
		fwd_curnode.closed = true
		fwd_pather := fwd_curnode.pather
		//fmt.Println(fwd_curnode)

		rev_curnode := heap.Pop(rev_nq).(*node)
		rev_curnode.open = false
		rev_curnode.closed = true
		//rev_pather := rev_curnode.pather
		//fmt.Println(rev_curnode)

		fwd_node_in_rev_map := rev_nodemap.get_node_from_pather(fwd_pather)
		if fwd_node_in_rev_map.closed || fwd_node_in_rev_map.open || fwd_pather == to {
			// Found a path to the goal.
			//fmt.Println("RES:", fwd_node_in_rev_map.closed, fwd_node_in_rev_map.open, fwd_pather) //, rev_pather)

			rp := []Pather{}
			curr := fwd_node_in_rev_map
			for curr != nil {
				rp = append(rp, curr.pather)
				curr = curr.parent
			}

			// reverse the path from touchpoint to "to"
			p := []Pather{}
			for i := len(rp) - 1; i >= 0; i = i - 1 {
				p = append(p, rp[i])
				//fmt.Println(rp[i])
			}
			//fmt.Println("revdone")

			curr = fwd_curnode.parent
			for curr != nil {
				p = append(p, curr.pather)
				//fmt.Println(curr.pather)
				curr = curr.parent
			}

			return p, fwd_curnode.cost + fwd_node_in_rev_map.cost, true
		}

		expand(fwd_nodemap, fwd_nq, fwd_curnode, to)
		expand(rev_nodemap, rev_nq, rev_curnode, from)

	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2019, The Decred developers
// See LICENSE for details.

// Package piclient handles the http requests made to Politeia APIs.
package piclient

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"

	pitypes "github.com/decred/dcrdata/v4/gov/politeia/types"
	piapi "github.com/decred/politeia/politeiawww/api/v1"
)

// HandleGetRequests accepts a http client and API URL path as arguments. If the
// parameters are valid, a GET request is made to the API URL passed. The body
// returned is decoded into []byte and returned.
func HandleGetRequests(client *http.Client, URLPath string) ([]byte, error) {
	if client == nil {
		return nil, fmt.Errorf("invalid http client was passed")
	}

	if URLPath == "" {
		return nil, fmt.Errorf("empty API URL is not supported")
	}

	response, err := client.Get(URLPath)
	if err != nil {
		return nil, err
	}

	defer response.Body.Close()

	return ioutil.ReadAll(response.Body)
}

// DropURLRegex replaces "{token:[A-z0-9]{64}}" in a URL with provided the parameter.
func DropURLRegex(URLPath, param string) string {
	r := regexp.MustCompile(`\{token:\[A-z0-9]\{64\}}`)
	return r.ReplaceAllLiteralString(URLPath, param)
}

// RetrieveAllProposals returns a list of Proposals whose maximum count is defined
// by piapi.ProposalListPageSize. Data returned is queried from Politeia API.
func RetrieveAllProposals(client *http.Client, APIRootPath, URLParams string) (
	*pitypes.Proposals, error) {
	// Constructs the full vetted proposals API URL
	URLpath := APIRootPath + piapi.RouteAllVetted + URLParams
	data, err := HandleGetRequests(client, URLpath)
	if err != nil {
		return nil, err
	}

	var publicProposals pitypes.Proposals
	err = json.Unmarshal(data, &publicProposals)
	if err != nil || len(publicProposals.Data) == 0 {
		return nil, err
	}

	// Constructs the full vote status API URL
	URLpath = APIRootPath + piapi.RouteAllVoteStatus + URLParams
	data, err = HandleGetRequests(client, URLpath)
	if err != nil {
		return nil, err
	}

	var votesInfo pitypes.Votes
	err = json.Unmarshal(data, &votesInfo)
	if err != nil {
		return nil, err
	}

	// Append the votes status information to the respective proposals if it exists.
	for _, val := range publicProposals.Data {
		for k := range votesInfo.Data {
			if val.Censorship.Token == votesInfo.Data[k].Token {
				val.ProposalVotes = votesInfo.Data[k]
				// exits the second loop after finding a match.
				break
			}
		}
	}

	return &publicProposals, nil
}

// RetrieveProposalByToken returns a single proposal identified by the token
// hash provided if it exists. Data returned is queried from Politeia API.
func RetrieveProposalByToken(client *http.Client, APIRootPath, token string) (*pitypes.ProposalInfo, error) {
	// Constructs the full proposal's URl and fetch is data.
	proposalRoute := APIRootPath + DropURLRegex(piapi.RouteProposalDetails, token)
	data, err := HandleGetRequests(client, proposalRoute)
	if err != nil {
		return nil, fmt.Errorf("retrieving %s proposal details failed: %v", token, err)
	}

	var proposal pitypes.ProposalInfo
	err = json.Unmarshal(data, &proposal)
	if err != nil {
		return nil, err
	}

	// Constructs the full votes status URL and fetch its data.
	votesStatusRoute := APIRootPath + DropURLRegex(piapi.RouteVoteStatus, token)
	data, err = HandleGetRequests(client, votesStatusRoute)
	if err != nil {
		return nil, fmt.Errorf("retrieving %s proposal vote status failed: %v", token, err)
	}

	err = json.Unmarshal(data, &proposal.ProposalVotes)
	if err != nil {
		return nil, err
	}

	return &proposal, nil
}
<commit_msg>Check for valid htp status code<commit_after>// Copyright (c) 2019, The Decred developers
// See LICENSE for details.

// Package piclient handles the http requests made to Politeia APIs.
package piclient

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"

	pitypes "github.com/decred/dcrdata/v4/gov/politeia/types"
	piapi "github.com/decred/politeia/politeiawww/api/v1"
)

// HandleGetRequests accepts a http client and API URL path as arguments. If the
// parameters are valid, a GET request is made to the API URL passed. The body
// returned is decoded into []byte and returned.
func HandleGetRequests(client *http.Client, URLPath string) ([]byte, error) {
	if client == nil {
		return nil, fmt.Errorf("invalid http client was passed")
	}

	if URLPath == "" {
		return nil, fmt.Errorf("empty API URL is not supported")
	}

	response, err := client.Get(URLPath)
	if err != nil || response == nil {
		return nil, fmt.Errorf("request failed: %v", err)
	}

	// Check if valid status code (200 Ok) was returned.
	if response.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("request (%s) failed with status code: %s",
			URLPath, response.Status)
	}

	defer response.Body.Close()

	return ioutil.ReadAll(response.Body)
}

// DropURLRegex replaces "{token:[A-z0-9]{64}}" in a URL with provided the parameter.
func DropURLRegex(URLPath, param string) string {
	r := regexp.MustCompile(`\{token:\[A-z0-9]\{64\}}`)
	return r.ReplaceAllLiteralString(URLPath, param)
}

// RetrieveAllProposals returns a list of Proposals whose maximum count is defined
// by piapi.ProposalListPageSize. Data returned is queried from Politeia API.
func RetrieveAllProposals(client *http.Client, APIRootPath, URLParams string) (
	*pitypes.Proposals, error) {
	// Constructs the full vetted proposals API URL
	URLpath := APIRootPath + piapi.RouteAllVetted + URLParams
	data, err := HandleGetRequests(client, URLpath)
	if err != nil {
		return nil, err
	}

	var publicProposals pitypes.Proposals
	err = json.Unmarshal(data, &publicProposals)
	if err != nil || len(publicProposals.Data) == 0 {
		return nil, err
	}

	// Constructs the full vote status API URL
	URLpath = APIRootPath + piapi.RouteAllVoteStatus + URLParams
	data, err = HandleGetRequests(client, URLpath)
	if err != nil {
		return nil, err
	}

	var votesInfo pitypes.Votes
	err = json.Unmarshal(data, &votesInfo)
	if err != nil {
		return nil, err
	}

	// Append the votes status information to the respective proposals if it exists.
	for _, val := range publicProposals.Data {
		for k := range votesInfo.Data {
			if val.Censorship.Token == votesInfo.Data[k].Token {
				val.ProposalVotes = votesInfo.Data[k]
				// exits the second loop after finding a match.
				break
			}
		}
	}

	return &publicProposals, nil
}

// RetrieveProposalByToken returns a single proposal identified by the token
// hash provided if it exists. Data returned is queried from Politeia API.
func RetrieveProposalByToken(client *http.Client, APIRootPath, token string) (*pitypes.ProposalInfo, error) {
	// Constructs the full proposal's URl and fetch is data.
	proposalRoute := APIRootPath + DropURLRegex(piapi.RouteProposalDetails, token)
	data, err := HandleGetRequests(client, proposalRoute)
	if err != nil {
		return nil, fmt.Errorf("retrieving %s proposal details failed: %v", token, err)
	}

	var proposal pitypes.ProposalInfo
	err = json.Unmarshal(data, &proposal)
	if err != nil {
		return nil, err
	}

	// Constructs the full votes status URL and fetch its data.
	votesStatusRoute := APIRootPath + DropURLRegex(piapi.RouteVoteStatus, token)
	data, err = HandleGetRequests(client, votesStatusRoute)
	if err != nil {
		return nil, fmt.Errorf("retrieving %s proposal vote status failed: %v", token, err)
	}

	err = json.Unmarshal(data, &proposal.ProposalVotes)
	if err != nil {
		return nil, err
	}

	return &proposal, nil
}
<|endoftext|>


<commit_before>package ams

import (
	"context"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
)

type AssetOption int

const (
	OptionNone                        = 0
	OptionStorageEncrypted            = 1
	OptionCommonEncryptionProtected   = 2
	OptionEnvelopeEncryptionProtected = 4
)

type Asset struct {
	ID                 string `json:"Id"`
	State              int    `json:"State"`
	Created            string `json:"Created"`
	LastModified       string `json:"LastModified"`
	Name               string `json:"Name"`
	Options            int    `json:"Options"`
	FormatOption       int    `json:"FormatOption"`
	URI                string `json:"Uri"`
	StorageAccountName string `json:"StorageAccountName"`
}

func (a *Asset) toResource() string {
	return fmt.Sprintf("Assets('%s')", a.ID)
}

func (c *Client) GetAssetWithContext(ctx context.Context, assetID string) (*Asset, error) {
	req, err := c.newRequest(ctx, http.MethodGet, fmt.Sprintf("Assets('%s')", assetID), nil)
	if err != nil {
		return nil, err
	}

	var out Asset
	if err := c.do(req, http.StatusOK, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

func (c *Client) GetAssetsWithContext(ctx context.Context) ([]Asset, error) {
	req, err := c.newRequest(ctx, http.MethodGet, "Assets", nil)
	if err != nil {
		return nil, errors.Wrap(err, "get assets request build failed")
	}
	var out struct {
		Assets []Asset `json:"value"`
	}
	if err := c.do(req, http.StatusOK, &out); err != nil {
		return nil, errors.Wrap(err, "get assets request failed")
	}
	return out.Assets, nil
}

func (c *Client) CreateAssetWithContext(ctx context.Context, name string) (*Asset, error) {
	params := map[string]interface{}{
		"Name": name,
	}
	body, err := encodeParams(params)
	if err != nil {
		return nil, errors.Wrap(err, "create asset request parameter encode failed")
	}
	req, err := c.newRequest(ctx, http.MethodPost, "Assets", body)
	if err != nil {
		return nil, errors.Wrap(err, "create asset request build failed")
	}
	var out Asset
	if err := c.do(req, http.StatusCreated, &out); err != nil {
		return nil, errors.Wrap(err, "create asset request failed")
	}
	return &out, nil
}

func (c *Client) buildAssetURI(asset *Asset) string {
	return c.buildURI(asset.toResource())
}
<commit_msg>feat: implement GetAssetFilesWithContext<commit_after>package ams

import (
	"context"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
)

type AssetOption int

const (
	OptionStorageEncrypted = 1 << iota
	OptionCommonEncryptionProtected
	OptionEnvelopeEncryptionProtected
	OptionNone = 0
)

type Asset struct {
	ID                 string `json:"Id"`
	State              int    `json:"State"`
	Created            string `json:"Created"`
	LastModified       string `json:"LastModified"`
	Name               string `json:"Name"`
	Options            int    `json:"Options"`
	FormatOption       int    `json:"FormatOption"`
	URI                string `json:"Uri"`
	StorageAccountName string `json:"StorageAccountName"`
}

func (a *Asset) toResource() string {
	return fmt.Sprintf("Assets('%s')", a.ID)
}

func (c *Client) GetAssetWithContext(ctx context.Context, assetID string) (*Asset, error) {
	req, err := c.newRequest(ctx, http.MethodGet, fmt.Sprintf("Assets('%s')", assetID), nil)
	if err != nil {
		return nil, err
	}

	var out Asset
	if err := c.do(req, http.StatusOK, &out); err != nil {
		return nil, err
	}
	return &out, nil
}

func (c *Client) GetAssetsWithContext(ctx context.Context) ([]Asset, error) {
	req, err := c.newRequest(ctx, http.MethodGet, "Assets", nil)
	if err != nil {
		return nil, errors.Wrap(err, "get assets request build failed")
	}
	var out struct {
		Assets []Asset `json:"value"`
	}
	if err := c.do(req, http.StatusOK, &out); err != nil {
		return nil, errors.Wrap(err, "get assets request failed")
	}
	return out.Assets, nil
}

func (c *Client) CreateAssetWithContext(ctx context.Context, name string) (*Asset, error) {
	params := map[string]interface{}{
		"Name": name,
	}
	body, err := encodeParams(params)
	if err != nil {
		return nil, errors.Wrap(err, "create asset request parameter encode failed")
	}
	req, err := c.newRequest(ctx, http.MethodPost, "Assets", body)
	if err != nil {
		return nil, errors.Wrap(err, "create asset request build failed")
	}
	var out Asset
	if err := c.do(req, http.StatusCreated, &out); err != nil {
		return nil, errors.Wrap(err, "create asset request failed")
	}
	return &out, nil
}

func (c *Client) GetAssetFilesWithContext(ctx context.Context, asset *Asset) ([]AssetFile, error) {
	endpoint := asset.toResource() + "/Files"
	req, err := c.newRequest(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, errors.Wrap(err, "get asset files request build failed")
	}
	var out struct {
		AssetFiles []AssetFile `json:"value"`
	}
	if err := c.do(req, http.StatusOK, &out); err != nil {
		return nil, errors.Wrap(err, "get asset files request failed")
	}
	return out.AssetFiles, nil
}

func (c *Client) buildAssetURI(asset *Asset) string {
	return c.buildURI(asset.toResource())
}
<|endoftext|>


<commit_before>package async

import (
	"fmt"
	"reflect"
)

// Type used for callback
type Callback func(...interface{}) error

// Type used as a list of functions
type Functions []interface{}

// funcs is the struct used to control the stack
// of functions to be executed.
type funcs struct {
	Stack []reflect.Value
}

// executeNext executes recursively each function of
// the stack until it reachs the bottom of the stack or
// it is interrupted by an error or isn't called by one of the
// functions of the stack
func (f *funcs) executeNext(args ...interface{}) error {
	// end of stack, no need to proceed
	if len(f.Stack) == 0 {
		return nil
	}

	var (
		// true if function has the last argument of type `Callback`
		expectCallback bool
		// Prepare callback that will be passed to function if `expectCallback` is true
		next Callback = Callback(f.executeNext)
		// Get function to be executed
		fn reflect.Value = f.Stack[0]
		// Get type of the function to be executed
		fnt    reflect.Type = fn.Type()
		inArgs []reflect.Value
	)

	// If function expect any argument
	// and length of arguments is less than the expected
	if l := fnt.NumIn(); l > 0 && len(args) < l {
		var cb Callback
		// Get last argument of the function
		lastArg := fnt.In(l - 1)

		// Check if the last argument is a Callback
		expectCallback = lastArg.AssignableTo(reflect.TypeOf(cb))
	}

	// Transform arguments from interface{} to reflect.Value
	for i := 0; i < len(args); i++ {
		arg := args[i]

		inArgs = append(inArgs, reflect.ValueOf(arg))
	}

	if expectCallback {
		inArgs = append(inArgs, reflect.ValueOf(next))
	}

	f.Stack = f.Stack[1:len(f.Stack)]

	resArgs := fn.Call(inArgs)

	if lr := len(resArgs); lr > 0 {
		if e, ok := resArgs[lr-1].Interface().(error); ok {
			return e
		}
	}
	return nil
}

// Waterfall executes every function sequencially.
// The execution flow may be interrupted by not calling the  callback or returning an error.
// `firstArgs` is a slice of parameters to be passed to the first function of the stack.
func Waterfall(stack Functions, firstArgs ...interface{}) error {
	// Init stack of functions
	f := &funcs{}
	// Checks if arguments passed are valid functions.
	// If so, appends functions to `f.Stack`.
	for i := 0; i < len(stack); i++ {
		v := reflect.Indirect(reflect.ValueOf(stack[i]))

		if v.Kind() != reflect.Func {
			return fmt.Errorf("%T must be a Function ", v)
		}

		f.Stack = append(f.Stack, v)
	}

	return f.executeNext(firstArgs...)
}
<commit_msg>Improved some lines<commit_after>package async

import (
	"fmt"
	"reflect"
)

// Type used for callback
type Callback func(...interface{}) error

// Type used as a list of functions
type Functions []interface{}

// funcs is the struct used to control the stack
// of functions to be executed.
type funcs struct {
	Stack []reflect.Value
}

// executeNext executes recursively each function of
// the stack until it reachs the bottom of the stack or
// it is interrupted by an error or isn't called by one of the
// functions of the stack
func (f *funcs) executeNext(args ...interface{}) error {
	// end of stack, no need to proceed
	if len(f.Stack) == 0 {
		return nil
	}

	var (
		// true if function has the last argument of type `Callback`
		expectCallback bool
		// Prepare callback that will be passed to function if `expectCallback` is true
		next Callback = Callback(f.executeNext)
		// Get function to be executed
		fn reflect.Value = f.Stack[0]
		// Get type of the function to be executed
		fnt    reflect.Type = fn.Type()
                // Arguments to be sent to the function
		inArgs []reflect.Value
	)

	// If function expect any argument
	if l := fnt.NumIn(); l > 0 {
		// Get last argument of the function
		lastArg := fnt.In(l - 1)

		// Check if the last argument is a Callback
		expectCallback = lastArg.AssignableTo(reflect.TypeOf(next))
	}

	// Transform arguments from interface{} to reflect.Value
	for i := 0; i < len(args); i++ {
		inArgs = append(inArgs, reflect.ValueOf(args[i]))
	}

        // If function is expecting a `Callback`, append next to the function arguments
	if expectCallback {
		inArgs = append(inArgs, reflect.ValueOf(next))
	}

        // Remove current function from the stack
	f.Stack = f.Stack[1:len(f.Stack)]

	resArgs := fn.Call(inArgs)

	if lr := len(resArgs); lr > 0 {
		if e, ok := resArgs[lr-1].Interface().(error); ok {
			return e
		}
	}
	return nil
}

// Waterfall executes every function sequencially.
// The execution flow may be interrupted by not calling the  callback or returning an error.
// `firstArgs` is a slice of parameters to be passed to the first function of the stack.
func Waterfall(stack Functions, firstArgs ...interface{}) error {
	// Init stack of functions
	f := &funcs{}
	// Checks if arguments passed are valid functions.
	// If so, appends functions to `f.Stack`.
	for i := 0; i < len(stack); i++ {
		v := reflect.Indirect(reflect.ValueOf(stack[i]))

		if v.Kind() != reflect.Func {
			return fmt.Errorf("%T must be a Function ", v)
		}

		f.Stack = append(f.Stack, v)
	}

	return f.executeNext(firstArgs...)
}
<|endoftext|>