<commit_before>package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
)

var version = "master"

func getGrafanaPluginDir() string {
	currentOS := runtime.GOOS
	defaultNix := "/var/lib/grafana/plugins"

	if currentOS == "windows" {
		return "..\\data\\plugins"
	}

	pwd, err := os.Getwd()

	if err != nil {
		log.Error("Could not get current path. using default")
		return defaultNix
	}

	if isDevenvironment(pwd) {
		return "../data/plugins"
	}

	return defaultNix
}

func isDevenvironment(pwd string) bool {
	// if ../conf/default.ini exists, grafana is not installed as package
	_, err := os.Stat("../conf/default.ini")
	return err != nil
}

func main() {
	SetupLogging()

	app := cli.NewApp()
	app.Name = "Grafana cli"
	app.Usage = ""
	app.Author = "Grafana Project"
	app.Email = "https://github.com/grafana/grafana"
	app.Version = version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "pluginsDir",
			Usage:  "path to the grafana plugin directory",
			Value:  getGrafanaPluginDir(),
			EnvVar: "GF_PLUGIN_DIR",
		},
		cli.StringFlag{
			Name:   "repo",
			Usage:  "url to the plugin repository",
			Value:  "https://grafana.net/api/plugins",
			EnvVar: "GF_PLUGIN_REPO",
		},
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "enable debug logging",
		},
	}

	app.Commands = commands.Commands
	app.CommandNotFound = cmdNotFound

	if err := app.Run(os.Args); err != nil {
		log.Errorf("%v", err)
	}
}

func SetupLogging() {
	for _, f := range os.Args {
		if f == "-D" || f == "--debug" || f == "-debug" {
			log.SetDebug(true)
		}
	}
}

func cmdNotFound(c *cli.Context, command string) {
	fmt.Printf(
		"%s: '%s' is not a %s command. See '%s --help'.\n",
		c.App.Name,
		command,
		c.App.Name,
		os.Args[0],
	)
	os.Exit(1)
}
<commit_msg>Revert "feat(cli): detects plugin folder for dev env"<commit_after>package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/codegangsta/cli"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
	"strings"
)

var version = "master"

func getGrafanaPluginDir() string {
	currentOS := runtime.GOOS
	defaultNix := "/var/lib/grafana/plugins"

	if currentOS == "windows" {
		return "C:\\opt\\grafana\\plugins"
	}

	pwd, err := os.Getwd()

	if err != nil {
		log.Error("Could not get current path. using default")
		return defaultNix
	}

	if isDevenvironment(pwd) {
		return "../../../data/plugins"
	}

	return defaultNix
}

func isDevenvironment(pwd string) bool {
	// if grafana-cli is executed from the cmd folder we can assume
	// that its in development environment.
	return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli")
}

func main() {
	SetupLogging()

	app := cli.NewApp()
	app.Name = "Grafana cli"
	app.Usage = ""
	app.Author = "Grafana Project"
	app.Email = "https://github.com/grafana/grafana"
	app.Version = version
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "pluginsDir",
			Usage:  "path to the grafana plugin directory",
			Value:  getGrafanaPluginDir(),
			EnvVar: "GF_PLUGIN_DIR",
		},
		cli.StringFlag{
			Name:   "repo",
			Usage:  "url to the plugin repository",
			Value:  "https://grafana.net/api/plugins",
			EnvVar: "GF_PLUGIN_REPO",
		},
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "enable debug logging",
		},
	}

	app.Commands = commands.Commands
	app.CommandNotFound = cmdNotFound

	if err := app.Run(os.Args); err != nil {
		log.Errorf("%v", err)
	}
}

func SetupLogging() {
	for _, f := range os.Args {
		if f == "-D" || f == "--debug" || f == "-debug" {
			log.SetDebug(true)
		}
	}
}

func cmdNotFound(c *cli.Context, command string) {
	fmt.Printf(
		"%s: '%s' is not a %s command. See '%s --help'.\n",
		c.App.Name,
		command,
		c.App.Name,
		os.Args[0],
	)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
	"time"
)

const retrytime = 510 * time.Millisecond

func tcpSend(str, addr string) string {
	return tcpSendHelper(str, addr, 3)
}

func tcpSendHelper(str, addr string, retry int) string {
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	conn, err := net.DialTCP("tcp4", nil, tcpAddr)
	if err == nil {
		defer conn.Close()

		payloadBytes := []byte(fmt.Sprintf("%s\r\n\r\n", str))
		if _, err = conn.Write(payloadBytes); err != nil {
			log.Println(err)

			if retry > 0 {
				time.Sleep(retrytime)
				return tcpSendHelper(str, addr, retry-1)
			}
		}
		message, _ := bufio.NewReader(conn).ReadString('\t')
		return fmt.Sprint(message)
	}
	log.Println(err)

	if retry > 0 {
		time.Sleep(retrytime)
		return tcpSendHelper(str, addr, retry-1)
	}
	return ""
}

func main() {
	var host string = "google.de"
	var port string = "80"
	var request string = "GET /index.html"

	response := tcpSend(request, host+":"+port)
	fmt.Printf("response from %v:%v for the request \"%v\" is: %v\n", host, port, request, response)
}
<commit_msg>change from "read until \t or til EOF" to "read 65535 bytes or til EOF"<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"net"
	"time"
)

const retrytime = 510 * time.Millisecond

func tcpClient(str, addr string) string {
	return tcpClientHelper(str, addr, 3)
}

func tcpClientHelper(str, addr string, retry int) string {
	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	conn, err := net.DialTCP("tcp4", nil, tcpAddr)
	if err == nil {
		defer conn.Close()

		payloadBytes := []byte(fmt.Sprintf("%s\r\n\r\n", str))
		if _, err = conn.Write(payloadBytes); err != nil {
			log.Println(err)

			if retry > 0 {
				time.Sleep(retrytime)
				return tcpClientHelper(str, addr, retry-1)
			}
		}
		bytes := make([]byte, 65535)
		_, err := bufio.NewReader(conn).Read(bytes)
		if err == nil || err == io.EOF {
			return string(bytes)
		}
		return fmt.Sprint(err)
	}
	log.Println(err)

	if retry > 0 {
		time.Sleep(retrytime)
		return tcpClientHelper(str, addr, retry-1)
	}
	return ""
}

func main() {
	var host string = "google.de"
	var port string = "80"
	var request string = "GET /index.html"

	response := tcpClient(request, host+":"+port)
	fmt.Printf("response from %v:%v for the request \"%v\" is: \n%v\n", host, port, request, response)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.123"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.90", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<commit_msg>fnlb: 0.0.124 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.124"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.91", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.122"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.89", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<commit_msg>fnlb: 0.0.123 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.123"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.90", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// apimirror contains copies of Kubernetes APIs. This allows json serialization, without worrying about
// importing the massive 15mb+ Kubernetes API libraries.
// This is intended for import only by istio-agent. Any other binaries (Istiod) should likely import the
// upstream Kubernetes API instead.
package apimirror

import "k8s.io/apimachinery/pkg/util/intstr"

// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
	// Path to access on the HTTP server.
	// +optional
	Path string `json:"path,omitempty" protobuf:"bytes,1,opt,name=path"`
	// Name or number of the port to access on the container.
	// Number must be in the range 1 to 65535.
	// Name must be an IANA_SVC_NAME.
	Port intstr.IntOrString `json:"port" protobuf:"bytes,2,opt,name=port"`
	// Host name to connect to, defaults to the pod IP. You probably want to set
	// "Host" in httpHeaders instead.
	// +optional
	Host string `json:"host,omitempty" protobuf:"bytes,3,opt,name=host"`
	// Scheme to use for connecting to the host.
	// Defaults to HTTP.
	// +optional
	Scheme URIScheme `json:"scheme,omitempty" protobuf:"bytes,4,opt,name=scheme,casttype=URIScheme"`
	// Custom headers to set in the request. HTTP allows repeated headers.
	// +optional
	HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty" protobuf:"bytes,5,rep,name=httpHeaders"`
}

// URIScheme identifies the scheme used for connection to a host for Get actions
type URIScheme string

const (
	// URISchemeHTTP means that the scheme used will be http://
	URISchemeHTTP URIScheme = "HTTP"
	// URISchemeHTTPS means that the scheme used will be https://
	URISchemeHTTPS URIScheme = "HTTPS"
)

// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
	// The header field name
	Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
	// The header field value
	Value string `json:"value" protobuf:"bytes,2,opt,name=value"`
}
<commit_msg>Fix allows repeated headers (#31866)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// apimirror contains copies of Kubernetes APIs. This allows json serialization, without worrying about
// importing the massive 15mb+ Kubernetes API libraries.
// This is intended for import only by istio-agent. Any other binaries (Istiod) should likely import the
// upstream Kubernetes API instead.
package apimirror

import "k8s.io/apimachinery/pkg/util/intstr"

// HTTPGetAction describes an action based on HTTP Get requests.
type HTTPGetAction struct {
	// Path to access on the HTTP server.
	// +optional
	Path string `json:"path,omitempty" protobuf:"bytes,1,opt,name=path"`
	// Name or number of the port to access on the container.
	// Number must be in the range 1 to 65535.
	// Name must be an IANA_SVC_NAME.
	Port intstr.IntOrString `json:"port" protobuf:"bytes,2,opt,name=port"`
	// Host name to connect to, defaults to the pod IP. You probably want to set
	// "Host" in httpHeaders instead.
	// +optional
	Host string `json:"host,omitempty" protobuf:"bytes,3,opt,name=host"`
	// Scheme to use for connecting to the host.
	// Defaults to HTTP.
	// +optional
	Scheme URIScheme `json:"scheme,omitempty" protobuf:"bytes,4,opt,name=scheme,casttype=URIScheme"`
	// Custom headers to set in the request.
	// +optional
	HTTPHeaders []HTTPHeader `json:"httpHeaders,omitempty" protobuf:"bytes,5,rep,name=httpHeaders"`
}

// URIScheme identifies the scheme used for connection to a host for Get actions
type URIScheme string

const (
	// URISchemeHTTP means that the scheme used will be http://
	URISchemeHTTP URIScheme = "HTTP"
	// URISchemeHTTPS means that the scheme used will be https://
	URISchemeHTTPS URIScheme = "HTTPS"
)

// HTTPHeader describes a custom header to be used in HTTP probes
type HTTPHeader struct {
	// The header field name
	Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
	// The header field value
	Value string `json:"value" protobuf:"bytes,2,opt,name=value"`
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 image

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

	"github.com/cheggaaa/pb/v3"
	"github.com/docker/docker/client"
	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/daemon"
	"github.com/google/go-containerregistry/pkg/v1/mutate"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"github.com/google/go-containerregistry/pkg/v1/tarball"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/localpath"
)

var defaultPlatform = v1.Platform{
	Architecture: runtime.GOARCH,
	OS:           "linux",
}

// UseDaemon is if we should look in local daemon for image ref
var UseDaemon = true

// UseRemote is if we should look in remote registry for image ref
var UseRemote = true

// DigestByDockerLib uses client by docker lib to return image digest
// img.ID in as same as image digest
func DigestByDockerLib(imgClient *client.Client, imgName string) string {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()
	imgClient.NegotiateAPIVersion(ctx)
	img, _, err := imgClient.ImageInspectWithRaw(ctx, imgName)
	if err != nil && !client.IsErrNotFound(err) {
		klog.Infof("couldn't find image digest %s from local daemon: %v ", imgName, err)
		return ""
	}
	return img.ID
}

// DigestByGoLib gets image digest uses go-containerregistry lib
// which is 4s slower thabn local daemon per lookup https://github.com/google/go-containerregistry/issues/627
func DigestByGoLib(imgName string) string {
	ref, err := name.ParseReference(imgName, name.WeakValidation)
	if err != nil {
		klog.Infof("error parsing image name %s ref %v ", imgName, err)
		return ""
	}

	img, err := retrieveImage(ref)
	if err != nil {
		klog.Infof("error retrieve Image %s ref %v ", imgName, err)
		return ""
	}

	cf, err := img.ConfigName()
	if err != nil {
		klog.Infof("error getting Image config name %s %v ", imgName, err)
		return cf.Hex
	}
	return cf.Hex
}

// ExistsImageInDaemon if img exist in local docker daemon
func ExistsImageInDaemon(img string) bool {
	// Check if image exists locally
	klog.Infof("Checking for %s in local docker daemon", img)
	cmd := exec.Command("docker", "images", "--format", "{{.Repository}}:{{.Tag}}@{{.Digest}}")
	if output, err := cmd.Output(); err == nil {
		if strings.Contains(string(output), img) {
			klog.Infof("Found %s in local docker daemon, skipping pull", img)
			return true
		}
	}
	// Else, pull it
	return false
}

// LoadFromTarball checks if the image exists as a tarball and tries to load it to the local daemon
// TODO: Pass in if we are loading to docker or podman so this function can also be used for podman
func LoadFromTarball(binary, img string) error {
	p := filepath.Join(constants.ImageCacheDir, img)
	p = localpath.SanitizeCacheDir(p)

	switch binary {
	case driver.Podman:
		return fmt.Errorf("not yet implemented, see issue #8426")
	default:
		tag, err := name.NewTag(Tag(img))
		if err != nil {
			return errors.Wrap(err, "new tag")
		}

		i, err := tarball.ImageFromPath(p, &tag)
		if err != nil {
			return errors.Wrap(err, "tarball")
		}

		_, err = daemon.Write(tag, i)
		return err
	}

}

// Tag returns just the image with the tag
// eg image:tag@sha256:digest -> image:tag if there is an associated tag
// if not possible, just return the initial img
func Tag(img string) string {
	split := strings.Split(img, ":")
	if len(split) == 3 {
		tag := strings.Split(split[1], "@")[0]
		return fmt.Sprintf("%s:%s", split[0], tag)
	}
	return img
}

// WriteImageToDaemon write img to the local docker daemon
func WriteImageToDaemon(img string) error {
	// buffered channel
	c := make(chan v1.Update, 200)

	klog.Infof("Writing %s to local daemon", img)
	ref, err := name.ParseReference(img)
	if err != nil {
		return errors.Wrap(err, "parsing reference")
	}
	klog.V(3).Infof("Getting image %v", ref)
	i, err := remote.Image(ref)
	if err != nil {
		if strings.Contains(err.Error(), "GitHub Docker Registry needs login") {
			ErrGithubNeedsLogin = errors.New(err.Error())
			return ErrGithubNeedsLogin
		} else if strings.Contains(err.Error(), "UNAUTHORIZED") {
			ErrNeedsLogin = errors.New(err.Error())
			return ErrNeedsLogin
		}

		return errors.Wrap(err, "getting remote image")
	}
	klog.V(3).Infof("Writing image %v", ref)
	errchan := make(chan error)
	p := pb.Full.Start64(0)
	fn := strings.Split(ref.Name(), "@")[0]
	// abbreviate filename for progress
	maxwidth := 30 - len("...")
	if len(fn) > maxwidth {
		fn = fn[0:maxwidth] + "..."
	}
	p.Set("prefix", "    > "+fn+": ")
	p.Set(pb.Bytes, true)

	// Just a hair less than 80 (standard terminal width) for aesthetics & pasting into docs
	p.SetWidth(79)

	go func() {
		_, err = daemon.Write(ref, i, tarball.WithProgress(c))
		errchan <- err
	}()
	var update v1.Update
	for {
		select {
		case update = <-c:
			p.SetCurrent(update.Complete)
			p.SetTotal(update.Total)
		case err = <-errchan:
			p.Finish()
			if err != nil {
				return errors.Wrap(err, "writing daemon image")
			}
			return nil
		}
	}
}

func retrieveImage(ref name.Reference) (v1.Image, error) {
	var err error
	var img v1.Image

	klog.Infof("retrieving image: %+v", ref)
	if UseDaemon {
		img, err := daemon.Image(ref)
		if err == nil {
			klog.Infof("found %s locally: %+v", ref.Name(), img)
			return img, nil
		}
		// reference does not exist in the local daemon
		klog.Infof("daemon lookup for %+v: %v", ref, err)
	}

	if UseRemote {
		img, err = retrieveRemote(ref, defaultPlatform)
		if err != nil {
			return nil, err
		}
		return fixPlatform(ref, img, defaultPlatform)
	}

	return img, err
}

func retrieveRemote(ref name.Reference, p v1.Platform) (v1.Image, error) {
	img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithPlatform(p))
	if err == nil {
		return img, nil
	}

	klog.Warningf("authn lookup for %+v (trying anon): %+v", ref, err)
	img, err = remote.Image(ref, remote.WithPlatform(p))
	// reference does not exist in the remote registry
	if err != nil {
		klog.Infof("remote lookup for %+v: %v", ref, err)
	}
	return img, err
}

// See https://github.com/kubernetes/minikube/issues/10402
// check if downloaded image Architecture field matches the requested and fix it otherwise
func fixPlatform(ref name.Reference, img v1.Image, p v1.Platform) (v1.Image, error) {
	cfg, err := img.ConfigFile()
	if err != nil {
		klog.Warningf("failed to get config for %s: %v", ref, err)
		return img, err
	}

	if cfg.Architecture == p.Architecture {
		return img, nil
	}
	klog.Warningf("image %s arch mismatch: want %s got %s. fixing",
		ref, p.Architecture, cfg.Architecture)

	cfg.Architecture = p.Architecture
	img, err = mutate.ConfigFile(img, cfg)
	if err != nil {
		klog.Warningf("failed to change config for %s: %v", ref, err)
		return img, errors.Wrap(err, "failed to change image config")
	}
	return img, nil
}

func cleanImageCacheDir() error {
	err := filepath.Walk(constants.ImageCacheDir, func(path string, info os.FileInfo, err error) error {
		// If error is not nil, it's because the path was already deleted and doesn't exist
		// Move on to next path
		if err != nil {
			return nil
		}
		// Check if path is directory
		if !info.IsDir() {
			return nil
		}
		// If directory is empty, delete it
		entries, err := ioutil.ReadDir(path)
		if err != nil {
			return err
		}
		if len(entries) == 0 {
			if err = os.Remove(path); err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

// normalizeTagName automatically tag latest to image
// Example:
//  nginx -> nginx:latest
//  localhost:5000/nginx -> localhost:5000/nginx:latest
//  localhost:5000/nginx:latest -> localhost:5000/nginx:latest
//  docker.io/dotnet/core/sdk -> docker.io/dotnet/core/sdk:latest
func normalizeTagName(image string) string {
	base := image
	tag := "latest"

	// From google/go-containerregistry/pkg/name/tag.go
	parts := strings.Split(strings.TrimSpace(image), ":")
	if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], "/") {
		base = strings.Join(parts[:len(parts)-1], ":")
		tag = parts[len(parts)-1]
	}
	return base + ":" + tag
}
<commit_msg>Refactor: add daemon function matching remote<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 image

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

	"github.com/cheggaaa/pb/v3"
	"github.com/docker/docker/client"
	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/daemon"
	"github.com/google/go-containerregistry/pkg/v1/mutate"
	"github.com/google/go-containerregistry/pkg/v1/remote"
	"github.com/google/go-containerregistry/pkg/v1/tarball"

	"github.com/pkg/errors"
	"k8s.io/klog/v2"
	"k8s.io/minikube/pkg/minikube/constants"
	"k8s.io/minikube/pkg/minikube/driver"
	"k8s.io/minikube/pkg/minikube/localpath"
)

var defaultPlatform = v1.Platform{
	Architecture: runtime.GOARCH,
	OS:           "linux",
}

// UseDaemon is if we should look in local daemon for image ref
var UseDaemon = true

// UseRemote is if we should look in remote registry for image ref
var UseRemote = true

// DigestByDockerLib uses client by docker lib to return image digest
// img.ID in as same as image digest
func DigestByDockerLib(imgClient *client.Client, imgName string) string {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
	defer cancel()
	imgClient.NegotiateAPIVersion(ctx)
	img, _, err := imgClient.ImageInspectWithRaw(ctx, imgName)
	if err != nil && !client.IsErrNotFound(err) {
		klog.Infof("couldn't find image digest %s from local daemon: %v ", imgName, err)
		return ""
	}
	return img.ID
}

// DigestByGoLib gets image digest uses go-containerregistry lib
// which is 4s slower thabn local daemon per lookup https://github.com/google/go-containerregistry/issues/627
func DigestByGoLib(imgName string) string {
	ref, err := name.ParseReference(imgName, name.WeakValidation)
	if err != nil {
		klog.Infof("error parsing image name %s ref %v ", imgName, err)
		return ""
	}

	img, err := retrieveImage(ref)
	if err != nil {
		klog.Infof("error retrieve Image %s ref %v ", imgName, err)
		return ""
	}

	cf, err := img.ConfigName()
	if err != nil {
		klog.Infof("error getting Image config name %s %v ", imgName, err)
		return cf.Hex
	}
	return cf.Hex
}

// ExistsImageInDaemon if img exist in local docker daemon
func ExistsImageInDaemon(img string) bool {
	// Check if image exists locally
	klog.Infof("Checking for %s in local docker daemon", img)
	cmd := exec.Command("docker", "images", "--format", "{{.Repository}}:{{.Tag}}@{{.Digest}}")
	if output, err := cmd.Output(); err == nil {
		if strings.Contains(string(output), img) {
			klog.Infof("Found %s in local docker daemon, skipping pull", img)
			return true
		}
	}
	// Else, pull it
	return false
}

// LoadFromTarball checks if the image exists as a tarball and tries to load it to the local daemon
// TODO: Pass in if we are loading to docker or podman so this function can also be used for podman
func LoadFromTarball(binary, img string) error {
	p := filepath.Join(constants.ImageCacheDir, img)
	p = localpath.SanitizeCacheDir(p)

	switch binary {
	case driver.Podman:
		return fmt.Errorf("not yet implemented, see issue #8426")
	default:
		tag, err := name.NewTag(Tag(img))
		if err != nil {
			return errors.Wrap(err, "new tag")
		}

		i, err := tarball.ImageFromPath(p, &tag)
		if err != nil {
			return errors.Wrap(err, "tarball")
		}

		_, err = daemon.Write(tag, i)
		return err
	}

}

// Tag returns just the image with the tag
// eg image:tag@sha256:digest -> image:tag if there is an associated tag
// if not possible, just return the initial img
func Tag(img string) string {
	split := strings.Split(img, ":")
	if len(split) == 3 {
		tag := strings.Split(split[1], "@")[0]
		return fmt.Sprintf("%s:%s", split[0], tag)
	}
	return img
}

// WriteImageToDaemon write img to the local docker daemon
func WriteImageToDaemon(img string) error {
	// buffered channel
	c := make(chan v1.Update, 200)

	klog.Infof("Writing %s to local daemon", img)
	ref, err := name.ParseReference(img)
	if err != nil {
		return errors.Wrap(err, "parsing reference")
	}
	klog.V(3).Infof("Getting image %v", ref)
	i, err := remote.Image(ref)
	if err != nil {
		if strings.Contains(err.Error(), "GitHub Docker Registry needs login") {
			ErrGithubNeedsLogin = errors.New(err.Error())
			return ErrGithubNeedsLogin
		} else if strings.Contains(err.Error(), "UNAUTHORIZED") {
			ErrNeedsLogin = errors.New(err.Error())
			return ErrNeedsLogin
		}

		return errors.Wrap(err, "getting remote image")
	}
	klog.V(3).Infof("Writing image %v", ref)
	errchan := make(chan error)
	p := pb.Full.Start64(0)
	fn := strings.Split(ref.Name(), "@")[0]
	// abbreviate filename for progress
	maxwidth := 30 - len("...")
	if len(fn) > maxwidth {
		fn = fn[0:maxwidth] + "..."
	}
	p.Set("prefix", "    > "+fn+": ")
	p.Set(pb.Bytes, true)

	// Just a hair less than 80 (standard terminal width) for aesthetics & pasting into docs
	p.SetWidth(79)

	go func() {
		_, err = daemon.Write(ref, i, tarball.WithProgress(c))
		errchan <- err
	}()
	var update v1.Update
	for {
		select {
		case update = <-c:
			p.SetCurrent(update.Complete)
			p.SetTotal(update.Total)
		case err = <-errchan:
			p.Finish()
			if err != nil {
				return errors.Wrap(err, "writing daemon image")
			}
			return nil
		}
	}
}

func retrieveImage(ref name.Reference) (v1.Image, error) {
	var err error
	var img v1.Image

	klog.Infof("retrieving image: %+v", ref)
	if UseDaemon {
		img, err = retrieveDaemon(ref)
		if err != nil {
			return nil, err
		}
	}

	if UseRemote {
		img, err = retrieveRemote(ref, defaultPlatform)
		if err != nil {
			return nil, err
		}
		return fixPlatform(ref, img, defaultPlatform)
	}

	return img, err
}

func retrieveDaemon(ref name.Reference) (v1.Image, error) {
	img, err := daemon.Image(ref)
	if err == nil {
		klog.Infof("found %s locally: %+v", ref.Name(), img)
		return img, nil
	}
	// reference does not exist in the local daemon
	klog.Infof("daemon lookup for %+v: %v", ref, err)
	return img, err
}

func retrieveRemote(ref name.Reference, p v1.Platform) (v1.Image, error) {
	img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain), remote.WithPlatform(p))
	if err == nil {
		return img, nil
	}

	klog.Warningf("authn lookup for %+v (trying anon): %+v", ref, err)
	img, err = remote.Image(ref, remote.WithPlatform(p))
	// reference does not exist in the remote registry
	if err != nil {
		klog.Infof("remote lookup for %+v: %v", ref, err)
	}
	return img, err
}

// See https://github.com/kubernetes/minikube/issues/10402
// check if downloaded image Architecture field matches the requested and fix it otherwise
func fixPlatform(ref name.Reference, img v1.Image, p v1.Platform) (v1.Image, error) {
	cfg, err := img.ConfigFile()
	if err != nil {
		klog.Warningf("failed to get config for %s: %v", ref, err)
		return img, err
	}

	if cfg.Architecture == p.Architecture {
		return img, nil
	}
	klog.Warningf("image %s arch mismatch: want %s got %s. fixing",
		ref, p.Architecture, cfg.Architecture)

	cfg.Architecture = p.Architecture
	img, err = mutate.ConfigFile(img, cfg)
	if err != nil {
		klog.Warningf("failed to change config for %s: %v", ref, err)
		return img, errors.Wrap(err, "failed to change image config")
	}
	return img, nil
}

func cleanImageCacheDir() error {
	err := filepath.Walk(constants.ImageCacheDir, func(path string, info os.FileInfo, err error) error {
		// If error is not nil, it's because the path was already deleted and doesn't exist
		// Move on to next path
		if err != nil {
			return nil
		}
		// Check if path is directory
		if !info.IsDir() {
			return nil
		}
		// If directory is empty, delete it
		entries, err := ioutil.ReadDir(path)
		if err != nil {
			return err
		}
		if len(entries) == 0 {
			if err = os.Remove(path); err != nil {
				return err
			}
		}
		return nil
	})
	return err
}

// normalizeTagName automatically tag latest to image
// Example:
//  nginx -> nginx:latest
//  localhost:5000/nginx -> localhost:5000/nginx:latest
//  localhost:5000/nginx:latest -> localhost:5000/nginx:latest
//  docker.io/dotnet/core/sdk -> docker.io/dotnet/core/sdk:latest
func normalizeTagName(image string) string {
	base := image
	tag := "latest"

	// From google/go-containerregistry/pkg/name/tag.go
	parts := strings.Split(strings.TrimSpace(image), ":")
	if len(parts) > 1 && !strings.Contains(parts[len(parts)-1], "/") {
		base = strings.Join(parts[:len(parts)-1], ":")
		tag = parts[len(parts)-1]
	}
	return base + ":" + tag
}
<|endoftext|>


<commit_before>package wallet

import (
	"fmt"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/convert"
)

type Wallet struct {
	Type              string
	Currency          string
	Balance           float64
	UnsettledInterest float64
	BalanceAvailable  float64
}

type Update Wallet

type Snapshot struct {
	Snapshot []*Wallet
}

type transformerFn func(raw []interface{}) (w *Wallet, err error)

func FromRaw(raw []interface{}) (w *Wallet, err error) {
	if len(raw) < 4 {
		err = fmt.Errorf("data slice too short for wallet: %#v", raw)
		return
	}

	w = &Wallet{
		Type:              convert.SValOrEmpty(raw[0]),
		Currency:          convert.SValOrEmpty(raw[1]),
		Balance:           convert.F64ValOrZero(raw[2]),
		UnsettledInterest: convert.F64ValOrZero(raw[3]),
	}

	return
}

func FromWsRaw(raw []interface{}) (w *Wallet, err error) {
	if len(raw) < 5 {
		err = fmt.Errorf("data slice too short for wallet: %#v", raw)
		return
	}

	w = &Wallet{
		Type:              convert.SValOrEmpty(raw[0]),
		Currency:          convert.SValOrEmpty(raw[1]),
		Balance:           convert.F64ValOrZero(raw[2]),
		UnsettledInterest: convert.F64ValOrZero(raw[3]),
		BalanceAvailable:  convert.F64ValOrZero(raw[4]),
	}

	return
}

func SnapshotFromRaw(raw []interface{}, transformer transformerFn) (s *Snapshot, err error) {
	if len(raw) == 0 {
		return
	}

	ws := make([]*Wallet, 0)
	switch raw[0].(type) {
	case []interface{}:
		for _, v := range raw {
			if l, ok := v.([]interface{}); ok {
				w, err := transformer(l)
				if err != nil {
					return s, err
				}
				ws = append(ws, w)
			}
		}
	default:
		return s, fmt.Errorf("not an wallet snapshot")
	}
	s = &Snapshot{Snapshot: ws}

	return
}
<commit_msg>making sure we return error if data slice is invalid<commit_after>package wallet

import (
	"fmt"

	"github.com/bitfinexcom/bitfinex-api-go/pkg/convert"
)

type Wallet struct {
	Type              string
	Currency          string
	Balance           float64
	UnsettledInterest float64
	BalanceAvailable  float64
}

type Update Wallet

type Snapshot struct {
	Snapshot []*Wallet
}

type transformerFn func(raw []interface{}) (w *Wallet, err error)

func FromRaw(raw []interface{}) (w *Wallet, err error) {
	if len(raw) < 4 {
		err = fmt.Errorf("data slice too short for wallet: %#v", raw)
		return
	}

	w = &Wallet{
		Type:              convert.SValOrEmpty(raw[0]),
		Currency:          convert.SValOrEmpty(raw[1]),
		Balance:           convert.F64ValOrZero(raw[2]),
		UnsettledInterest: convert.F64ValOrZero(raw[3]),
	}

	return
}

func FromWsRaw(raw []interface{}) (w *Wallet, err error) {
	if len(raw) < 5 {
		err = fmt.Errorf("data slice too short for wallet: %#v", raw)
		return
	}

	w = &Wallet{
		Type:              convert.SValOrEmpty(raw[0]),
		Currency:          convert.SValOrEmpty(raw[1]),
		Balance:           convert.F64ValOrZero(raw[2]),
		UnsettledInterest: convert.F64ValOrZero(raw[3]),
		BalanceAvailable:  convert.F64ValOrZero(raw[4]),
	}

	return
}

func SnapshotFromRaw(raw []interface{}, transformer transformerFn) (s *Snapshot, err error) {
	if len(raw) == 0 {
		return s, fmt.Errorf("data slice too short for wallet: %#v", raw)
	}

	ws := make([]*Wallet, 0)
	switch raw[0].(type) {
	case []interface{}:
		for _, v := range raw {
			if l, ok := v.([]interface{}); ok {
				w, err := transformer(l)
				if err != nil {
					return s, err
				}
				ws = append(ws, w)
			}
		}
	default:
		return s, fmt.Errorf("not an wallet snapshot")
	}
	s = &Snapshot{Snapshot: ws}

	return
}
<|endoftext|>


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

package mtd

import (
	"os"
	"testing"
)

func TestOpen(t *testing.T) {
	// If there's no such device then don't bother with the
	// test.
	if _, err := os.Stat(DevName); err != nil {
		t.Skip("No device to test")
	}
	m, err := NewDev(DevName)
	if err != nil {
		t.Fatal(err)
	}
	if err := m.Close(); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>pkg/mount/mtd: * Add tests for Dev struct of Flasher interface<commit_after>// Copyright 2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mtd

import (
	"bytes"
	"os"
	"testing"
)

var (
	testString = "This is just a test string"
)

func TestOpen(t *testing.T) {
	if _, err := os.Stat(DevName); err != nil {
		tmpDir := t.TempDir()
		testmtd, err := os.CreateTemp(tmpDir, "testmtd")
		if err != nil {
			t.Errorf(`os.Create(tmpDir, "testmtd")=file, %q, want file, nil`, err)
		}
		DevName = testmtd.Name()
	}

	m, err := NewDev(DevName)
	if err != nil {
		t.Fatal(err)
	}
	defer m.Close()

	if _, err := m.QueueWrite([]byte(testString), 0); err != nil {
		t.Errorf("m.QueueWrite([]byte(testString), 0)=-,%q, want _,nil", err)
	}
	buf := make([]byte, 26)
	if _, err := m.ReadAt(buf, 0); err != nil {
		t.Errorf("m.ReadAt([]byte(), 0)=-,%q, want _,nil", err)
	}
	if !bytes.Equal(buf, []byte(testString)) {
		t.Errorf("bytes.Equal(buf, []byte(testString))=false, want true")
	}
	if m.Name() != DevName {
		t.Errorf("want %s == %s, want m.DevName() == testmtd.Name()", m.Name(), DevName)
	}
}
<|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 util

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

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/klog/v2"
	extenderv1 "k8s.io/kube-scheduler/extender/v1"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
)

// GetPodFullName returns a name that uniquely identifies a pod.
func GetPodFullName(pod *v1.Pod) string {
	// Use underscore as the delimiter because it is not allowed in pod name
	// (DNS subdomain format).
	return pod.Name + "_" + pod.Namespace
}

// GetPodStartTime returns start time of the given pod or current timestamp
// if it hasn't started yet.
func GetPodStartTime(pod *v1.Pod) *metav1.Time {
	if pod.Status.StartTime != nil {
		return pod.Status.StartTime
	}
	// Assumed pods and bound pods that haven't started don't have a StartTime yet.
	return &metav1.Time{Time: time.Now()}
}

// GetEarliestPodStartTime returns the earliest start time of all pods that
// have the highest priority among all victims.
func GetEarliestPodStartTime(victims *extenderv1.Victims) *metav1.Time {
	if len(victims.Pods) == 0 {
		// should not reach here.
		klog.Errorf("victims.Pods is empty. Should not reach here.")
		return nil
	}

	earliestPodStartTime := GetPodStartTime(victims.Pods[0])
	maxPriority := podutil.GetPodPriority(victims.Pods[0])

	for _, pod := range victims.Pods {
		if podutil.GetPodPriority(pod) == maxPriority {
			if GetPodStartTime(pod).Before(earliestPodStartTime) {
				earliestPodStartTime = GetPodStartTime(pod)
			}
		} else if podutil.GetPodPriority(pod) > maxPriority {
			maxPriority = podutil.GetPodPriority(pod)
			earliestPodStartTime = GetPodStartTime(pod)
		}
	}

	return earliestPodStartTime
}

// MoreImportantPod return true when priority of the first pod is higher than
// the second one. If two pods' priorities are equal, compare their StartTime.
// It takes arguments of the type "interface{}" to be used with SortableList,
// but expects those arguments to be *v1.Pod.
func MoreImportantPod(pod1, pod2 *v1.Pod) bool {
	p1 := podutil.GetPodPriority(pod1)
	p2 := podutil.GetPodPriority(pod2)
	if p1 != p2 {
		return p1 > p2
	}
	return GetPodStartTime(pod1).Before(GetPodStartTime(pod2))
}

// GetPodAffinityTerms gets pod affinity terms by a pod affinity object.
func GetPodAffinityTerms(affinity *v1.Affinity) (terms []v1.PodAffinityTerm) {
	if affinity != nil && affinity.PodAffinity != nil {
		if len(affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
			terms = affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution
		}
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		//if len(affinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
		//	terms = append(terms, affinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution...)
		//}
	}
	return terms
}

// GetPodAntiAffinityTerms gets pod affinity terms by a pod anti-affinity.
func GetPodAntiAffinityTerms(affinity *v1.Affinity) (terms []v1.PodAffinityTerm) {
	if affinity != nil && affinity.PodAntiAffinity != nil {
		if len(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
			terms = affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution
		}
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		//if len(affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
		//	terms = append(terms, affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution...)
		//}
	}
	return terms
}

// PatchPod calculates the delta bytes change from <old> to <new>,
// and then submit a request to API server to patch the pod changes.
func PatchPod(cs kubernetes.Interface, old *v1.Pod, new *v1.Pod) error {
	oldData, err := json.Marshal(old)
	if err != nil {
		return err
	}

	newData, err := json.Marshal(new)
	if err != nil {
		return err
	}
	patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, &v1.Pod{})
	if err != nil {
		return fmt.Errorf("failed to create merge patch for pod %q/%q: %v", old.Namespace, old.Name, err)
	}
	_, err = cs.CoreV1().Pods(old.Namespace).Patch(context.TODO(), old.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, "status")
	return err
}

// GetUpdatedPod returns the latest version of <pod> from API server.
func GetUpdatedPod(cs kubernetes.Interface, pod *v1.Pod) (*v1.Pod, error) {
	return cs.CoreV1().Pods(pod.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{})
}

// DeletePod deletes the given <pod> from API server
func DeletePod(cs kubernetes.Interface, pod *v1.Pod) error {
	return cs.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
}

// ClearNominatedNodeName internally submit a patch request to API server
// to set each pods[*].Status.NominatedNodeName> to "".
func ClearNominatedNodeName(cs kubernetes.Interface, pods ...*v1.Pod) utilerrors.Aggregate {
	var errs []error
	for _, p := range pods {
		if len(p.Status.NominatedNodeName) == 0 {
			continue
		}
		podCopy := p.DeepCopy()
		podCopy.Status.NominatedNodeName = ""
		if err := PatchPod(cs, p, podCopy); err != nil {
			errs = append(errs, err)
		}
	}
	return utilerrors.NewAggregate(errs)
}
<commit_msg>Avoid the API call to update pod if nothing is changed<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 util

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

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/client-go/kubernetes"
	"k8s.io/klog/v2"
	extenderv1 "k8s.io/kube-scheduler/extender/v1"
	podutil "k8s.io/kubernetes/pkg/api/v1/pod"
)

// GetPodFullName returns a name that uniquely identifies a pod.
func GetPodFullName(pod *v1.Pod) string {
	// Use underscore as the delimiter because it is not allowed in pod name
	// (DNS subdomain format).
	return pod.Name + "_" + pod.Namespace
}

// GetPodStartTime returns start time of the given pod or current timestamp
// if it hasn't started yet.
func GetPodStartTime(pod *v1.Pod) *metav1.Time {
	if pod.Status.StartTime != nil {
		return pod.Status.StartTime
	}
	// Assumed pods and bound pods that haven't started don't have a StartTime yet.
	return &metav1.Time{Time: time.Now()}
}

// GetEarliestPodStartTime returns the earliest start time of all pods that
// have the highest priority among all victims.
func GetEarliestPodStartTime(victims *extenderv1.Victims) *metav1.Time {
	if len(victims.Pods) == 0 {
		// should not reach here.
		klog.Errorf("victims.Pods is empty. Should not reach here.")
		return nil
	}

	earliestPodStartTime := GetPodStartTime(victims.Pods[0])
	maxPriority := podutil.GetPodPriority(victims.Pods[0])

	for _, pod := range victims.Pods {
		if podutil.GetPodPriority(pod) == maxPriority {
			if GetPodStartTime(pod).Before(earliestPodStartTime) {
				earliestPodStartTime = GetPodStartTime(pod)
			}
		} else if podutil.GetPodPriority(pod) > maxPriority {
			maxPriority = podutil.GetPodPriority(pod)
			earliestPodStartTime = GetPodStartTime(pod)
		}
	}

	return earliestPodStartTime
}

// MoreImportantPod return true when priority of the first pod is higher than
// the second one. If two pods' priorities are equal, compare their StartTime.
// It takes arguments of the type "interface{}" to be used with SortableList,
// but expects those arguments to be *v1.Pod.
func MoreImportantPod(pod1, pod2 *v1.Pod) bool {
	p1 := podutil.GetPodPriority(pod1)
	p2 := podutil.GetPodPriority(pod2)
	if p1 != p2 {
		return p1 > p2
	}
	return GetPodStartTime(pod1).Before(GetPodStartTime(pod2))
}

// GetPodAffinityTerms gets pod affinity terms by a pod affinity object.
func GetPodAffinityTerms(affinity *v1.Affinity) (terms []v1.PodAffinityTerm) {
	if affinity != nil && affinity.PodAffinity != nil {
		if len(affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
			terms = affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution
		}
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		//if len(affinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
		//	terms = append(terms, affinity.PodAffinity.RequiredDuringSchedulingRequiredDuringExecution...)
		//}
	}
	return terms
}

// GetPodAntiAffinityTerms gets pod affinity terms by a pod anti-affinity.
func GetPodAntiAffinityTerms(affinity *v1.Affinity) (terms []v1.PodAffinityTerm) {
	if affinity != nil && affinity.PodAntiAffinity != nil {
		if len(affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution) != 0 {
			terms = affinity.PodAntiAffinity.RequiredDuringSchedulingIgnoredDuringExecution
		}
		// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
		//if len(affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution) != 0 {
		//	terms = append(terms, affinity.PodAntiAffinity.RequiredDuringSchedulingRequiredDuringExecution...)
		//}
	}
	return terms
}

// PatchPod calculates the delta bytes change from <old> to <new>,
// and then submit a request to API server to patch the pod changes.
func PatchPod(cs kubernetes.Interface, old *v1.Pod, new *v1.Pod) error {
	oldData, err := json.Marshal(old)
	if err != nil {
		return err
	}

	newData, err := json.Marshal(new)
	if err != nil {
		return err
	}
	patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, &v1.Pod{})
	if err != nil {
		return fmt.Errorf("failed to create merge patch for pod %q/%q: %v", old.Namespace, old.Name, err)
	}

	if "{}" == string(patchBytes) {
		return nil
	}

	_, err = cs.CoreV1().Pods(old.Namespace).Patch(context.TODO(), old.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}, "status")
	return err
}

// GetUpdatedPod returns the latest version of <pod> from API server.
func GetUpdatedPod(cs kubernetes.Interface, pod *v1.Pod) (*v1.Pod, error) {
	return cs.CoreV1().Pods(pod.Namespace).Get(context.TODO(), pod.Name, metav1.GetOptions{})
}

// DeletePod deletes the given <pod> from API server
func DeletePod(cs kubernetes.Interface, pod *v1.Pod) error {
	return cs.CoreV1().Pods(pod.Namespace).Delete(context.TODO(), pod.Name, metav1.DeleteOptions{})
}

// ClearNominatedNodeName internally submit a patch request to API server
// to set each pods[*].Status.NominatedNodeName> to "".
func ClearNominatedNodeName(cs kubernetes.Interface, pods ...*v1.Pod) utilerrors.Aggregate {
	var errs []error
	for _, p := range pods {
		if len(p.Status.NominatedNodeName) == 0 {
			continue
		}
		podCopy := p.DeepCopy()
		podCopy.Status.NominatedNodeName = ""
		if err := PatchPod(cs, p, podCopy); err != nil {
			errs = append(errs, err)
		}
	}
	return utilerrors.NewAggregate(errs)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold 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 deploy

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type HelmDeployer struct {
	*v1alpha2.HelmDeploy

	kubeContext string
	namespace   string
}

// NewHelmDeployer returns a new HelmDeployer for a DeployConfig filled
// with the needed configuration for `helm`
func NewHelmDeployer(cfg *v1alpha2.HelmDeploy, kubeContext string, namespace string) *HelmDeployer {
	return &HelmDeployer{
		HelmDeploy:  cfg,
		kubeContext: kubeContext,
		namespace:   namespace,
	}
}

func (h *HelmDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "helm",
	}
}

func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]Artifact, error) {
	deployResults := []Artifact{}
	for _, r := range h.Releases {
		results, err := h.deployRelease(ctx, out, r, builds)
		if err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return deployResults, errors.Wrapf(err, "deploying %s", releaseName)
		}
		deployResults = append(deployResults, results...)
	}
	return deployResults, nil
}

func (h *HelmDeployer) Dependencies() ([]string, error) {
	var deps []string
	for _, release := range h.Releases {
		deps = append(deps, release.ValuesFilePath)
		filepath.Walk(release.ChartPath, func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() {
				deps = append(deps, path)
			}
			return nil
		})
	}
	return deps, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	for _, r := range h.Releases {
		if err := h.deleteRelease(ctx, out, r); err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return errors.Wrapf(err, "deploying %s", releaseName)
		}
	}
	return nil
}

func (h *HelmDeployer) helm(ctx context.Context, out io.Writer, arg ...string) error {
	args := append([]string{"--kube-context", h.kubeContext}, arg...)

	cmd := exec.CommandContext(ctx, "helm", args...)
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r v1alpha2.HelmRelease, builds []build.Artifact) ([]Artifact, error) {
	isInstalled := true

	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the release name template")
	}
	if err := h.helm(ctx, out, "get", releaseName); err != nil {
		color.Red.Fprintf(out, "Helm release %s not installed. Installing...\n", releaseName)
		isInstalled = false
	}
	params, err := joinTagsToBuildResult(builds, r.Values)
	if err != nil {
		return nil, errors.Wrap(err, "matching build results to chart values")
	}

	var setOpts []string
	for k, v := range params {
		setOpts = append(setOpts, "--set")
		if r.ImageStrategy.HelmImageConfig.HelmConventionConfig != nil {
			tagSplit := strings.Split(v.Tag, ":")
			imageRepositoryTag := fmt.Sprintf("%s.repository=%s,%s.tag=%s", k, tagSplit[0], k, tagSplit[1])
			setOpts = append(setOpts, imageRepositoryTag)
		} else {
			setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v.Tag))
		}
	}

	// First build dependencies.
	logrus.Infof("Building helm dependencies...")
	if err := h.helm(ctx, out, "dep", "build", r.ChartPath); err != nil {
		return nil, errors.Wrap(err, "building helm dependencies")
	}

	var args []string
	if !isInstalled {
		args = append(args, "install", "--name", releaseName)
	} else {
		args = append(args, "upgrade", releaseName)
	}

	// There are 2 strategies:
	// 1) Deploy chart directly from filesystem path or from repository
	//    (like stable/kubernetes-dashboard). Version only applies to a
	//    chart from repository.
	// 2) Package chart into a .tgz archive with specific version and then deploy
	//    that packaged chart. This way user can apply any version and appVersion
	//    for the chart.
	if r.Packaged == nil {
		if r.Version != "" {
			args = append(args, "--version", r.Version)
		}
		args = append(args, r.ChartPath)
	} else {
		chartPath, err := h.packageChart(ctx, r)
		if err != nil {
			return nil, errors.WithMessage(err, "cannot package chart")
		}
		args = append(args, chartPath)
	}

	var ns string
	if h.namespace != "" {
		ns = h.namespace
	} else if r.Namespace != "" {
		ns = r.Namespace
	}
	if ns != "" {
		args = append(args, "--namespace", ns)
	}
	if len(r.Overrides) != 0 {
		overrides, err := yaml.Marshal(r.Overrides)
		if err != nil {
			return nil, errors.Wrap(err, "cannot marshal overrides to create overrides values.yaml")
		}
		overridesFile, err := os.Create(constants.HelmOverridesFilename)
		if err != nil {
			return nil, errors.Wrapf(err, "cannot create file %s", constants.HelmOverridesFilename)
		}
		defer func() {
			overridesFile.Close()
			os.Remove(constants.HelmOverridesFilename)
		}()
		if _, err := overridesFile.WriteString(string(overrides)); err != nil {
			return nil, errors.Wrapf(err, "failed to write file %s", constants.HelmOverridesFilename)
		}
		args = append(args, "-f", constants.HelmOverridesFilename)
	}
	if r.ValuesFilePath != "" {
		args = append(args, "-f", r.ValuesFilePath)
	}

	setValues := r.SetValues
	if setValues == nil {
		setValues = map[string]string{}
	}
	if len(r.SetValueTemplates) != 0 {
		envMap := map[string]string{}
		for idx, b := range builds {
			suffix := ""
			if idx > 0 {
				suffix = strconv.Itoa(idx + 1)
			}
			m := tag.CreateEnvVarMap(b.ImageName, extractTag(b.Tag))
			for k, v := range m {
				envMap[k+suffix] = v
			}
			color.Default.Fprintf(out, "EnvVarMap: %#v\n", envMap)
		}
		for k, v := range r.SetValueTemplates {
			t, err := util.ParseEnvTemplate(v)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse setValueTemplates")
			}
			result, err := util.ExecuteEnvTemplate(t, envMap)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to generate setValueTemplates")
			}
			setValues[k] = result
		}
	}
	for k, v := range setValues {
		setOpts = append(setOpts, "--set")
		setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v))
	}
	if r.Wait {
		args = append(args, "--wait")
	}
	args = append(args, setOpts...)

	helmErr := h.helm(ctx, out, args...)
	return h.getDeployResults(ctx, ns, releaseName), helmErr
}

// imageName if the given string includes a fully qualified docker image name then lets trim just the tag part out
func extractTag(imageName string) string {
	idx := strings.LastIndex(imageName, "/")
	if idx < 0 {
		return imageName
	}
	tag := imageName[idx+1:]
	idx = strings.Index(tag, ":")
	if idx > 0 {
		return tag[idx+1:]
	}
	return tag
}

// packageChart packages the chart and returns path to the chart archive file.
// If this function returns an error, it will always be wrapped.
func (h *HelmDeployer) packageChart(ctx context.Context, r v1alpha2.HelmRelease) (string, error) {
	tmp := os.TempDir()
	packageArgs := []string{"package", r.ChartPath, "--destination", tmp}
	if r.Packaged.Version != "" {
		v, err := concretize(r.Packaged.Version)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.version" template`)
		}
		packageArgs = append(packageArgs, "--version", v)
	}
	if r.Packaged.AppVersion != "" {
		av, err := concretize(r.Packaged.AppVersion)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.appVersion" template`)
		}
		packageArgs = append(packageArgs, "--app-version", av)
	}

	buf := &bytes.Buffer{}
	err := h.helm(ctx, buf, packageArgs...)
	output := strings.TrimSpace(buf.String())
	if err != nil {
		return "", errors.Wrapf(err, "package chart into a .tgz archive (%s)", output)
	}

	fpath, err := extractChartFilename(output, tmp)
	if err != nil {
		return "", err
	}

	return filepath.Join(tmp, fpath), nil
}

func (h *HelmDeployer) getReleaseInfo(ctx context.Context, release string) (*bufio.Reader, error) {
	var releaseInfo bytes.Buffer
	if err := h.helm(ctx, &releaseInfo, "get", release); err != nil {
		return nil, fmt.Errorf("error retrieving helm deployment info: %s", releaseInfo.String())
	}
	return bufio.NewReader(&releaseInfo), nil
}

// Retrieve info about all releases using helm get
// Skaffold labels will be applied to each deployed k8s object
// Since helm isn't always consistent with retrieving results, don't return errors here
func (h *HelmDeployer) getDeployResults(ctx context.Context, namespace string, release string) []Artifact {
	b, err := h.getReleaseInfo(ctx, release)
	if err != nil {
		logrus.Warnf(err.Error())
		return nil
	}
	return parseReleaseInfo(namespace, b)
}

func (h *HelmDeployer) deleteRelease(ctx context.Context, out io.Writer, r v1alpha2.HelmRelease) error {
	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return errors.Wrap(err, "cannot parse the release name template")
	}

	if err := h.helm(ctx, out, "delete", releaseName, "--purge"); err != nil {
		logrus.Debugf("deleting release %s: %v\n", releaseName, err)
	}

	return nil
}

func evaluateReleaseName(nameTemplate string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(nameTemplate)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	return util.ExecuteEnvTemplate(tmpl, nil)
}

// concretize parses and executes template s with OS environment variables.
// If s is not a template but a simple string, returns unchanged s.
func concretize(s string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(s)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	tmpl.Option("missingkey=error")
	return util.ExecuteEnvTemplate(tmpl, nil)
}

func extractChartFilename(s, tmp string) (string, error) {
	s = strings.TrimSpace(s)
	idx := strings.Index(s, tmp)
	if idx == -1 {
		return "", errors.New("cannot locate packaged chart archive")
	}

	return s[idx+len(tmp):], nil
}
<commit_msg>Exclude helm dependency chart packages from watched files<commit_after>/*
Copyright 2018 The Skaffold 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 deploy

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/tag"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/color"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	yaml "gopkg.in/yaml.v2"
)

type HelmDeployer struct {
	*v1alpha2.HelmDeploy

	kubeContext string
	namespace   string
}

// NewHelmDeployer returns a new HelmDeployer for a DeployConfig filled
// with the needed configuration for `helm`
func NewHelmDeployer(cfg *v1alpha2.HelmDeploy, kubeContext string, namespace string) *HelmDeployer {
	return &HelmDeployer{
		HelmDeploy:  cfg,
		kubeContext: kubeContext,
		namespace:   namespace,
	}
}

func (h *HelmDeployer) Labels() map[string]string {
	return map[string]string{
		constants.Labels.Deployer: "helm",
	}
}

func (h *HelmDeployer) Deploy(ctx context.Context, out io.Writer, builds []build.Artifact) ([]Artifact, error) {
	deployResults := []Artifact{}
	for _, r := range h.Releases {
		results, err := h.deployRelease(ctx, out, r, builds)
		if err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return deployResults, errors.Wrapf(err, "deploying %s", releaseName)
		}
		deployResults = append(deployResults, results...)
	}
	return deployResults, nil
}

func (h *HelmDeployer) Dependencies() ([]string, error) {
	var deps []string
	for _, release := range h.Releases {
		deps = append(deps, release.ValuesFilePath)
		chart_deps_dir := filepath.Join(release.ChartPath, "charts")
		filepath.Walk(release.ChartPath, func(path string, info os.FileInfo, err error) error {
			if !info.IsDir() && !strings.HasPrefix(path, chart_deps_dir) {
				deps = append(deps, path)
			}
			return nil
		})
	}
	return deps, nil
}

// Cleanup deletes what was deployed by calling Deploy.
func (h *HelmDeployer) Cleanup(ctx context.Context, out io.Writer) error {
	for _, r := range h.Releases {
		if err := h.deleteRelease(ctx, out, r); err != nil {
			releaseName, _ := evaluateReleaseName(r.Name)
			return errors.Wrapf(err, "deploying %s", releaseName)
		}
	}
	return nil
}

func (h *HelmDeployer) helm(ctx context.Context, out io.Writer, arg ...string) error {
	args := append([]string{"--kube-context", h.kubeContext}, arg...)

	cmd := exec.CommandContext(ctx, "helm", args...)
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}

func (h *HelmDeployer) deployRelease(ctx context.Context, out io.Writer, r v1alpha2.HelmRelease, builds []build.Artifact) ([]Artifact, error) {
	isInstalled := true

	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return nil, errors.Wrap(err, "cannot parse the release name template")
	}
	if err := h.helm(ctx, out, "get", releaseName); err != nil {
		color.Red.Fprintf(out, "Helm release %s not installed. Installing...\n", releaseName)
		isInstalled = false
	}
	params, err := joinTagsToBuildResult(builds, r.Values)
	if err != nil {
		return nil, errors.Wrap(err, "matching build results to chart values")
	}

	var setOpts []string
	for k, v := range params {
		setOpts = append(setOpts, "--set")
		if r.ImageStrategy.HelmImageConfig.HelmConventionConfig != nil {
			tagSplit := strings.Split(v.Tag, ":")
			imageRepositoryTag := fmt.Sprintf("%s.repository=%s,%s.tag=%s", k, tagSplit[0], k, tagSplit[1])
			setOpts = append(setOpts, imageRepositoryTag)
		} else {
			setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v.Tag))
		}
	}

	// First build dependencies.
	logrus.Infof("Building helm dependencies...")
	if err := h.helm(ctx, out, "dep", "build", r.ChartPath); err != nil {
		return nil, errors.Wrap(err, "building helm dependencies")
	}

	var args []string
	if !isInstalled {
		args = append(args, "install", "--name", releaseName)
	} else {
		args = append(args, "upgrade", releaseName)
	}

	// There are 2 strategies:
	// 1) Deploy chart directly from filesystem path or from repository
	//    (like stable/kubernetes-dashboard). Version only applies to a
	//    chart from repository.
	// 2) Package chart into a .tgz archive with specific version and then deploy
	//    that packaged chart. This way user can apply any version and appVersion
	//    for the chart.
	if r.Packaged == nil {
		if r.Version != "" {
			args = append(args, "--version", r.Version)
		}
		args = append(args, r.ChartPath)
	} else {
		chartPath, err := h.packageChart(ctx, r)
		if err != nil {
			return nil, errors.WithMessage(err, "cannot package chart")
		}
		args = append(args, chartPath)
	}

	var ns string
	if h.namespace != "" {
		ns = h.namespace
	} else if r.Namespace != "" {
		ns = r.Namespace
	}
	if ns != "" {
		args = append(args, "--namespace", ns)
	}
	if len(r.Overrides) != 0 {
		overrides, err := yaml.Marshal(r.Overrides)
		if err != nil {
			return nil, errors.Wrap(err, "cannot marshal overrides to create overrides values.yaml")
		}
		overridesFile, err := os.Create(constants.HelmOverridesFilename)
		if err != nil {
			return nil, errors.Wrapf(err, "cannot create file %s", constants.HelmOverridesFilename)
		}
		defer func() {
			overridesFile.Close()
			os.Remove(constants.HelmOverridesFilename)
		}()
		if _, err := overridesFile.WriteString(string(overrides)); err != nil {
			return nil, errors.Wrapf(err, "failed to write file %s", constants.HelmOverridesFilename)
		}
		args = append(args, "-f", constants.HelmOverridesFilename)
	}
	if r.ValuesFilePath != "" {
		args = append(args, "-f", r.ValuesFilePath)
	}

	setValues := r.SetValues
	if setValues == nil {
		setValues = map[string]string{}
	}
	if len(r.SetValueTemplates) != 0 {
		envMap := map[string]string{}
		for idx, b := range builds {
			suffix := ""
			if idx > 0 {
				suffix = strconv.Itoa(idx + 1)
			}
			m := tag.CreateEnvVarMap(b.ImageName, extractTag(b.Tag))
			for k, v := range m {
				envMap[k+suffix] = v
			}
			color.Default.Fprintf(out, "EnvVarMap: %#v\n", envMap)
		}
		for k, v := range r.SetValueTemplates {
			t, err := util.ParseEnvTemplate(v)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to parse setValueTemplates")
			}
			result, err := util.ExecuteEnvTemplate(t, envMap)
			if err != nil {
				return nil, errors.Wrapf(err, "failed to generate setValueTemplates")
			}
			setValues[k] = result
		}
	}
	for k, v := range setValues {
		setOpts = append(setOpts, "--set")
		setOpts = append(setOpts, fmt.Sprintf("%s=%s", k, v))
	}
	if r.Wait {
		args = append(args, "--wait")
	}
	args = append(args, setOpts...)

	helmErr := h.helm(ctx, out, args...)
	return h.getDeployResults(ctx, ns, releaseName), helmErr
}

// imageName if the given string includes a fully qualified docker image name then lets trim just the tag part out
func extractTag(imageName string) string {
	idx := strings.LastIndex(imageName, "/")
	if idx < 0 {
		return imageName
	}
	tag := imageName[idx+1:]
	idx = strings.Index(tag, ":")
	if idx > 0 {
		return tag[idx+1:]
	}
	return tag
}

// packageChart packages the chart and returns path to the chart archive file.
// If this function returns an error, it will always be wrapped.
func (h *HelmDeployer) packageChart(ctx context.Context, r v1alpha2.HelmRelease) (string, error) {
	tmp := os.TempDir()
	packageArgs := []string{"package", r.ChartPath, "--destination", tmp}
	if r.Packaged.Version != "" {
		v, err := concretize(r.Packaged.Version)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.version" template`)
		}
		packageArgs = append(packageArgs, "--version", v)
	}
	if r.Packaged.AppVersion != "" {
		av, err := concretize(r.Packaged.AppVersion)
		if err != nil {
			return "", errors.Wrap(err, `concretize "packaged.appVersion" template`)
		}
		packageArgs = append(packageArgs, "--app-version", av)
	}

	buf := &bytes.Buffer{}
	err := h.helm(ctx, buf, packageArgs...)
	output := strings.TrimSpace(buf.String())
	if err != nil {
		return "", errors.Wrapf(err, "package chart into a .tgz archive (%s)", output)
	}

	fpath, err := extractChartFilename(output, tmp)
	if err != nil {
		return "", err
	}

	return filepath.Join(tmp, fpath), nil
}

func (h *HelmDeployer) getReleaseInfo(ctx context.Context, release string) (*bufio.Reader, error) {
	var releaseInfo bytes.Buffer
	if err := h.helm(ctx, &releaseInfo, "get", release); err != nil {
		return nil, fmt.Errorf("error retrieving helm deployment info: %s", releaseInfo.String())
	}
	return bufio.NewReader(&releaseInfo), nil
}

// Retrieve info about all releases using helm get
// Skaffold labels will be applied to each deployed k8s object
// Since helm isn't always consistent with retrieving results, don't return errors here
func (h *HelmDeployer) getDeployResults(ctx context.Context, namespace string, release string) []Artifact {
	b, err := h.getReleaseInfo(ctx, release)
	if err != nil {
		logrus.Warnf(err.Error())
		return nil
	}
	return parseReleaseInfo(namespace, b)
}

func (h *HelmDeployer) deleteRelease(ctx context.Context, out io.Writer, r v1alpha2.HelmRelease) error {
	releaseName, err := evaluateReleaseName(r.Name)
	if err != nil {
		return errors.Wrap(err, "cannot parse the release name template")
	}

	if err := h.helm(ctx, out, "delete", releaseName, "--purge"); err != nil {
		logrus.Debugf("deleting release %s: %v\n", releaseName, err)
	}

	return nil
}

func evaluateReleaseName(nameTemplate string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(nameTemplate)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	return util.ExecuteEnvTemplate(tmpl, nil)
}

// concretize parses and executes template s with OS environment variables.
// If s is not a template but a simple string, returns unchanged s.
func concretize(s string) (string, error) {
	tmpl, err := util.ParseEnvTemplate(s)
	if err != nil {
		return "", errors.Wrap(err, "parsing template")
	}

	tmpl.Option("missingkey=error")
	return util.ExecuteEnvTemplate(tmpl, nil)
}

func extractChartFilename(s, tmp string) (string, error) {
	s = strings.TrimSpace(s)
	idx := strings.Index(s, tmp)
	if idx == -1 {
		return "", errors.New("cannot locate packaged chart archive")
	}

	return s[idx+len(tmp):], nil
}
<|endoftext|>


<commit_before>package joinserver

import (
	"encoding/hex"
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
)

type serverItem struct {
	joinEUI lorawan.EUI64
	client  backend.Client
}

var (
	servers []serverItem
	keks    map[string][]byte

	netID               lorawan.NetID
	resolveJoinEUI      bool
	resolveDomainSuffix string
	defaultServer       string
	defaultCACert       string
	defaultTLSCert      string
	defaultTLSKey       string
	defaultRedisClient  redis.UniversalClient
	defaultAsyncTimeout time.Duration
)

// Setup sets up the joinserver backend.
func Setup(c config.Config) error {
	conf := c.JoinServer
	keks = make(map[string][]byte)

	netID = c.NetworkServer.NetID
	defaultServer = c.JoinServer.Default.Server
	defaultCACert = c.JoinServer.Default.CACert
	defaultTLSCert = c.JoinServer.Default.TLSCert
	defaultTLSKey = c.JoinServer.Default.TLSKey
	resolveJoinEUI = c.JoinServer.ResolveJoinEUI
	resolveDomainSuffix = c.JoinServer.ResolveDomainSuffix
	if c.JoinServer.Default.Async {
		defaultRedisClient = storage.RedisClient()
	}
	defaultAsyncTimeout = c.JoinServer.Default.AsyncTimeout

	for _, s := range conf.Servers {
		var joinEUI lorawan.EUI64
		if err := joinEUI.UnmarshalText([]byte(s.JoinEUI)); err != nil {
			return errors.Wrap(err, "decode joineui error")
		}

		if s.Server == "" {
			s.Server = joinEUIToServer(joinEUI, resolveDomainSuffix)
		}

		var redisClient redis.UniversalClient
		if s.Async {
			redisClient = storage.RedisClient()
		}

		fmt.Println("FOOOO")
		client, err := backend.NewClient(backend.ClientConfig{
			Logger:       log.StandardLogger(),
			SenderID:     c.NetworkServer.NetID.String(),
			ReceiverID:   joinEUI.String(),
			Server:       s.Server,
			CACert:       s.CACert,
			TLSCert:      s.TLSCert,
			TLSKey:       s.TLSKey,
			RedisClient:  redisClient,
			AsyncTimeout: s.AsyncTimeout,
		})
		if err != nil {
			return errors.Wrap(err, "new backend client error")
		}

		servers = append(servers, serverItem{
			joinEUI: joinEUI,
			client:  client,
		})
	}

	for _, k := range conf.KEK.Set {
		kek, err := hex.DecodeString(k.KEK)
		if err != nil {
			return errors.Wrap(err, "decode kek error")
		}

		keks[k.Label] = kek
	}

	return nil
}

// GetClientForJoinEUI returns the backend client for the given JoinEUI.
func GetClientForJoinEUI(joinEUI lorawan.EUI64) (backend.Client, error) {
	// Pre-configured join-servers.
	for _, s := range servers {
		if s.joinEUI == joinEUI {
			return s.client, nil
		}
	}

	// If DNS resolving is enabled, try to resolve the join-server through DNS.
	if resolveJoinEUI {
		client, err := resolveClient(joinEUI)
		if err == nil {
			return client, nil
		}

		log.WithFields(log.Fields{
			"join_eui": joinEUI,
		}).Warning("joinserver: resolving JoinEUI failed, returning default join-server client")
	}

	// Default join-server.
	return getDefaultClient(joinEUI)
}

func resolveClient(joinEUI lorawan.EUI64) (backend.Client, error) {
	server := joinEUIToServer(joinEUI, resolveDomainSuffix)
	serverParsed, err := url.Parse(server)
	if err != nil {
		return nil, errors.Wrap(err, "parse url error")
	}

	_, err = net.LookupIP(serverParsed.Host)
	if err != nil {
		return nil, errors.Wrap(err, "lookup ip error")
	}

	client, err := backend.NewClient(backend.ClientConfig{
		Logger:       log.StandardLogger(),
		SenderID:     netID.String(),
		ReceiverID:   joinEUI.String(),
		Server:       server,
		CACert:       defaultCACert,
		TLSCert:      defaultTLSCert,
		TLSKey:       defaultTLSKey,
		RedisClient:  defaultRedisClient,
		AsyncTimeout: defaultAsyncTimeout,
	})
	if err != nil {
		return nil, errors.Wrap(err, "joinserver: new client error")
	}

	return client, nil
}

func getDefaultClient(joinEUI lorawan.EUI64) (backend.Client, error) {
	defaultClient, err := backend.NewClient(backend.ClientConfig{
		Logger:       log.StandardLogger(),
		SenderID:     netID.String(),
		ReceiverID:   joinEUI.String(),
		Server:       defaultServer,
		CACert:       defaultCACert,
		TLSCert:      defaultTLSCert,
		TLSKey:       defaultTLSKey,
		RedisClient:  defaultRedisClient,
		AsyncTimeout: defaultAsyncTimeout,
	})
	if err != nil {
		return nil, errors.Wrap(err, "joinserver: new default client error")
	}

	return defaultClient, nil
}

// GetKEKKey returns the KEK key for the given label.
func GetKEKKey(label string) ([]byte, error) {
	kek, ok := keks[label]
	if !ok {
		return nil, fmt.Errorf("kek label '%s' is not configured", label)
	}
	return kek, nil
}

func joinEUIToServer(joinEUI lorawan.EUI64, domain string) string {
	nibbles := strings.Split(joinEUI.String(), "")

	for i, j := 0, len(nibbles)-1; i < j; i, j = i+1, j-1 {
		nibbles[i], nibbles[j] = nibbles[j], nibbles[i]
	}

	return "https://" + strings.Join(nibbles, ".") + domain
}
<commit_msg>Remove debug comment.<commit_after>package joinserver

import (
	"encoding/hex"
	"fmt"
	"net"
	"net/url"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
)

type serverItem struct {
	joinEUI lorawan.EUI64
	client  backend.Client
}

var (
	servers []serverItem
	keks    map[string][]byte

	netID               lorawan.NetID
	resolveJoinEUI      bool
	resolveDomainSuffix string
	defaultServer       string
	defaultCACert       string
	defaultTLSCert      string
	defaultTLSKey       string
	defaultRedisClient  redis.UniversalClient
	defaultAsyncTimeout time.Duration
)

// Setup sets up the joinserver backend.
func Setup(c config.Config) error {
	conf := c.JoinServer
	keks = make(map[string][]byte)

	netID = c.NetworkServer.NetID
	defaultServer = c.JoinServer.Default.Server
	defaultCACert = c.JoinServer.Default.CACert
	defaultTLSCert = c.JoinServer.Default.TLSCert
	defaultTLSKey = c.JoinServer.Default.TLSKey
	resolveJoinEUI = c.JoinServer.ResolveJoinEUI
	resolveDomainSuffix = c.JoinServer.ResolveDomainSuffix
	if c.JoinServer.Default.Async {
		defaultRedisClient = storage.RedisClient()
	}
	defaultAsyncTimeout = c.JoinServer.Default.AsyncTimeout

	for _, s := range conf.Servers {
		var joinEUI lorawan.EUI64
		if err := joinEUI.UnmarshalText([]byte(s.JoinEUI)); err != nil {
			return errors.Wrap(err, "decode joineui error")
		}

		if s.Server == "" {
			s.Server = joinEUIToServer(joinEUI, resolveDomainSuffix)
		}

		var redisClient redis.UniversalClient
		if s.Async {
			redisClient = storage.RedisClient()
		}

		client, err := backend.NewClient(backend.ClientConfig{
			Logger:       log.StandardLogger(),
			SenderID:     c.NetworkServer.NetID.String(),
			ReceiverID:   joinEUI.String(),
			Server:       s.Server,
			CACert:       s.CACert,
			TLSCert:      s.TLSCert,
			TLSKey:       s.TLSKey,
			RedisClient:  redisClient,
			AsyncTimeout: s.AsyncTimeout,
		})
		if err != nil {
			return errors.Wrap(err, "new backend client error")
		}

		servers = append(servers, serverItem{
			joinEUI: joinEUI,
			client:  client,
		})
	}

	for _, k := range conf.KEK.Set {
		kek, err := hex.DecodeString(k.KEK)
		if err != nil {
			return errors.Wrap(err, "decode kek error")
		}

		keks[k.Label] = kek
	}

	return nil
}

// GetClientForJoinEUI returns the backend client for the given JoinEUI.
func GetClientForJoinEUI(joinEUI lorawan.EUI64) (backend.Client, error) {
	// Pre-configured join-servers.
	for _, s := range servers {
		if s.joinEUI == joinEUI {
			return s.client, nil
		}
	}

	// If DNS resolving is enabled, try to resolve the join-server through DNS.
	if resolveJoinEUI {
		client, err := resolveClient(joinEUI)
		if err == nil {
			return client, nil
		}

		log.WithFields(log.Fields{
			"join_eui": joinEUI,
		}).Warning("joinserver: resolving JoinEUI failed, returning default join-server client")
	}

	// Default join-server.
	return getDefaultClient(joinEUI)
}

func resolveClient(joinEUI lorawan.EUI64) (backend.Client, error) {
	server := joinEUIToServer(joinEUI, resolveDomainSuffix)
	serverParsed, err := url.Parse(server)
	if err != nil {
		return nil, errors.Wrap(err, "parse url error")
	}

	_, err = net.LookupIP(serverParsed.Host)
	if err != nil {
		return nil, errors.Wrap(err, "lookup ip error")
	}

	client, err := backend.NewClient(backend.ClientConfig{
		Logger:       log.StandardLogger(),
		SenderID:     netID.String(),
		ReceiverID:   joinEUI.String(),
		Server:       server,
		CACert:       defaultCACert,
		TLSCert:      defaultTLSCert,
		TLSKey:       defaultTLSKey,
		RedisClient:  defaultRedisClient,
		AsyncTimeout: defaultAsyncTimeout,
	})
	if err != nil {
		return nil, errors.Wrap(err, "joinserver: new client error")
	}

	return client, nil
}

func getDefaultClient(joinEUI lorawan.EUI64) (backend.Client, error) {
	defaultClient, err := backend.NewClient(backend.ClientConfig{
		Logger:       log.StandardLogger(),
		SenderID:     netID.String(),
		ReceiverID:   joinEUI.String(),
		Server:       defaultServer,
		CACert:       defaultCACert,
		TLSCert:      defaultTLSCert,
		TLSKey:       defaultTLSKey,
		RedisClient:  defaultRedisClient,
		AsyncTimeout: defaultAsyncTimeout,
	})
	if err != nil {
		return nil, errors.Wrap(err, "joinserver: new default client error")
	}

	return defaultClient, nil
}

// GetKEKKey returns the KEK key for the given label.
func GetKEKKey(label string) ([]byte, error) {
	kek, ok := keks[label]
	if !ok {
		return nil, fmt.Errorf("kek label '%s' is not configured", label)
	}
	return kek, nil
}

func joinEUIToServer(joinEUI lorawan.EUI64, domain string) string {
	nibbles := strings.Split(joinEUI.String(), "")

	for i, j := 0, len(nibbles)-1; i < j; i, j = i+1, j-1 {
		nibbles[i], nibbles[j] = nibbles[j], nibbles[i]
	}

	return "https://" + strings.Join(nibbles, ".") + domain
}
<|endoftext|>


<commit_before>package backend

import (
	gw "github.com/cvmfs/gateway/internal/gateway"
	"github.com/pkg/errors"
)

// LeaseReturn is the response type of lease queries, handed
// back to the HTTP frontend
type LeaseReturn struct {
	KeyID     string `json:"key_id,omitempty"`
	LeasePath string `json:"path,omitempty"`
	Expires   string `json:"expires,omitempty"`
}

// NewLease for the specified path, using keyID
func NewLease(s *Services, keyID, leasePath string) (string, error) {
	repoName, subPath, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		return "", errors.Wrap(err, "could not parse lease path")
	}

	// Check if keyID is allowed to request a lease in the repository
	// at the specified subpath
	if err := s.Access.Check(keyID, subPath, repoName); err != nil {
		return "", err
	}

	// Generate a new token for the lease
	token, err := NewLeaseToken(leasePath, s.Config.MaxLeaseTime)
	if err != nil {
		return "", errors.Wrap(err, "could not generate session token")
	}

	if err := s.Leases.NewLease(keyID, leasePath, *token); err != nil {
		return "", err
	}

	return token.TokenStr, nil
}

// GetLeases returns all active and valid leases
func GetLeases(s *Services) (map[string]LeaseReturn, error) {
	leases, err := s.Leases.GetLeases()
	if err != nil {
		return nil, err
	}
	ret := make(map[string]LeaseReturn)
	for k, v := range leases {
		if err := CheckToken(v.Token.TokenStr, v.Token.Secret); err == nil {
			ret[k] = LeaseReturn{KeyID: v.KeyID, Expires: v.Token.Expiration.String()}
		}
	}
	return ret, nil
}

// GetLease returns the lease associated with a token
func GetLease(s *Services, tokenStr string) (*LeaseReturn, error) {
	leasePath, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return nil, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		return nil, err
	}

	ret := &LeaseReturn{
		KeyID:     lease.KeyID,
		LeasePath: leasePath,
		Expires:   lease.Token.Expiration.String(),
	}
	return ret, nil
}

// CancelLease associated with the token (transaction rollback)
func CancelLease(s *Services, tokenStr string) error {
	_, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			return err
		}
	}

	if err := s.Leases.CancelLease(tokenStr); err != nil {
		return err
	}

	return nil
}

// CommitLease associated with the token (transaction commit)
func CommitLease(s *Services, tokenStr, oldRootHash, newRootHash string, tag gw.RepositoryTag) error {
	leasePath, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			return err
		}
	}

	if err := s.Pool.CommitLease(leasePath, oldRootHash, newRootHash, tag); err != nil {
		return err
	}

	return nil
}
<commit_msg>Remove lease after successful commit<commit_after>package backend

import (
	gw "github.com/cvmfs/gateway/internal/gateway"
	"github.com/pkg/errors"
)

// LeaseReturn is the response type of lease queries, handed
// back to the HTTP frontend
type LeaseReturn struct {
	KeyID     string `json:"key_id,omitempty"`
	LeasePath string `json:"path,omitempty"`
	Expires   string `json:"expires,omitempty"`
}

// NewLease for the specified path, using keyID
func NewLease(s *Services, keyID, leasePath string) (string, error) {
	repoName, subPath, err := gw.SplitLeasePath(leasePath)
	if err != nil {
		return "", errors.Wrap(err, "could not parse lease path")
	}

	// Check if keyID is allowed to request a lease in the repository
	// at the specified subpath
	if err := s.Access.Check(keyID, subPath, repoName); err != nil {
		return "", err
	}

	// Generate a new token for the lease
	token, err := NewLeaseToken(leasePath, s.Config.MaxLeaseTime)
	if err != nil {
		return "", errors.Wrap(err, "could not generate session token")
	}

	if err := s.Leases.NewLease(keyID, leasePath, *token); err != nil {
		return "", err
	}

	return token.TokenStr, nil
}

// GetLeases returns all active and valid leases
func GetLeases(s *Services) (map[string]LeaseReturn, error) {
	leases, err := s.Leases.GetLeases()
	if err != nil {
		return nil, err
	}
	ret := make(map[string]LeaseReturn)
	for k, v := range leases {
		if err := CheckToken(v.Token.TokenStr, v.Token.Secret); err == nil {
			ret[k] = LeaseReturn{KeyID: v.KeyID, Expires: v.Token.Expiration.String()}
		}
	}
	return ret, nil
}

// GetLease returns the lease associated with a token
func GetLease(s *Services, tokenStr string) (*LeaseReturn, error) {
	leasePath, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return nil, err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		return nil, err
	}

	ret := &LeaseReturn{
		KeyID:     lease.KeyID,
		LeasePath: leasePath,
		Expires:   lease.Token.Expiration.String(),
	}
	return ret, nil
}

// CancelLease associated with the token (transaction rollback)
func CancelLease(s *Services, tokenStr string) error {
	_, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			return err
		}
	}

	if err := s.Leases.CancelLease(tokenStr); err != nil {
		return err
	}

	return nil
}

// CommitLease associated with the token (transaction commit)
func CommitLease(s *Services, tokenStr, oldRootHash, newRootHash string, tag gw.RepositoryTag) error {
	leasePath, lease, err := s.Leases.GetLease(tokenStr)
	if err != nil {
		return err
	}

	if err := CheckToken(tokenStr, lease.Token.Secret); err != nil {
		// Allow an expired token to be used to cancel a lease
		if _, ok := err.(ExpiredTokenError); !ok {
			return err
		}
	}

	if err := s.Pool.CommitLease(leasePath, oldRootHash, newRootHash, tag); err != nil {
		return err
	}

	if err := s.Leases.CancelLease(tokenStr); err != nil {
		return err
	}

	return nil
}
<|endoftext|>