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

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	corev1 "k8s.io/client-go/1.4/kubernetes/typed/core/v1"
	typedv1beta1 "k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	_ "k8s.io/client-go/1.4/pkg/api/install"
	apiv1 "k8s.io/client-go/1.4/pkg/api/v1"
	_ "k8s.io/client-go/1.4/pkg/apis/extensions/install"
	extapiv1beta1 "k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/fields"
	"k8s.io/client-go/1.4/pkg/util/intstr"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/cache"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	glog.V(1).Infof("Listening on %s:%d", c.Bind.Host, c.Bind.Port)
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, &clientcmd.ConfigOverrides{})
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := typedv1beta1.NewForConfigOrDie(rcfg)
		client := corev1.NewForConfigOrDie(rcfg)

		// watch services
		services := cache.NewStore(cache.MetaNamespaceKeyFunc)
		lw := cache.NewListWatchFromClient(client, "services", "", fields.Everything())
		cache.NewReflector(lw, &apiv1.Service{}, services, time.Minute).Run()

		// watch ingresses
		updateTrigger := make(chan struct{}, 1)
		ingresses := map[string]*extapiv1beta1.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extapiv1beta1.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						if i != nil {
							glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						}
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()

				serverForBackend := func(ing *extapiv1beta1.Ingress, backend *extapiv1beta1.IngressBackend) (*Server, error) {
					obj, found, err := services.GetByKey(fmt.Sprintf("%s/%s", ing.Namespace, backend.ServiceName))
					if err != nil {
						return nil, err
					}
					if !found {
						return nil, fmt.Errorf("service %s/%s not found", ing.Namespace, backend.ServiceName)
					}
					svc := obj.(*apiv1.Service)
					var port int
					if backend.ServicePort.Type == intstr.String {
						for _, p := range svc.Spec.Ports {
							if p.Name == backend.ServicePort.StrVal {
								port = int(p.Port)
								break
							}
						}
						if port == 0 {
							return nil, fmt.Errorf("port %q of service %s/%s not found", backend.ServicePort.StrVal, svc.Namespace, svc.Name)
						}
					} else {
						port = int(backend.ServicePort.IntVal)
					}
					return &Server{
						Host: svc.Spec.ClusterIP,
						// TODO: support string values:
						Port: port,
					}, nil
				}

				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						s, err := serverForBackend(i, i.Spec.Backend)
						if err != nil {
							glog.Errorf("Ingress %s/%s error with default backend, skipping: %v", i.Namespace, i.Name, err)
							continue
						}
						s.Default = true
						c.Servers = append(c.Servers, *s)
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" && p.Path != "/" {
								glog.Errorf("Ingress %s/%s error with rule, skipping: %v", i.Namespace, i.Name, err)
								continue
							}
							s, err := serverForBackend(i, &p.Backend)
							if err != nil {
								glog.Errorf("Ingress %s/%s error with rule %q path %q, skipping: %v", i.Namespace, i.Name, r.Host, p.Path, err)
								continue
							}
							s.Names = []string{r.Host}
							c.Servers = append(c.Servers, *s)
						}
					}
				}
				lock.Unlock()

				glog.V(2).Infof("Updating proxy configuration")
				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				} else {
					glog.V(2).Infof("Proxy configuration update done")
				}

			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		glog.V(3).Infof(
			"%s -> %s",
			conn.RemoteAddr(),
			conn.LocalAddr(),
		)
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.Errorf("Error reading the configuration: %s", err)
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			conn.Close()
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			conn.Close()
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		conn.Close()
		return
	}
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		conn.Close()
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	defer client.Close()
	defer server.Close()

	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

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

package main

import (
	"fmt"
	"io"
	"net"
	"regexp"
	"sync"
	"time"

	"github.com/golang/glog"
	"github.com/paultag/sniff/parser"

	corev1 "k8s.io/client-go/1.4/kubernetes/typed/core/v1"
	typedv1beta1 "k8s.io/client-go/1.4/kubernetes/typed/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/api"
	_ "k8s.io/client-go/1.4/pkg/api/install"
	apiv1 "k8s.io/client-go/1.4/pkg/api/v1"
	_ "k8s.io/client-go/1.4/pkg/apis/extensions/install"
	extapiv1beta1 "k8s.io/client-go/1.4/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/1.4/pkg/fields"
	"k8s.io/client-go/1.4/pkg/util/intstr"
	"k8s.io/client-go/1.4/pkg/watch"
	"k8s.io/client-go/1.4/tools/cache"
	"k8s.io/client-go/1.4/tools/clientcmd"
)

const (
	// ingressClassKey picks a specific "class" for the Ingress. The controller
	// only processes Ingresses with this annotation either unset, or set
	// to either nginxIngressClass or the empty string.
	ingressClassKey = "kubernetes.io/ingress.class"
)

type ServerAndRegexp struct {
	Server *Server
	Regexp *regexp.Regexp
}

type Proxy struct {
	Lock       sync.RWMutex
	ServerList []ServerAndRegexp
	Default    *Server
}

func (c *Proxy) Get(host string) *Server {
	c.Lock.RLock()
	defer c.Lock.RUnlock()

	for _, tuple := range c.ServerList {
		if tuple.Regexp.MatchString(host) {
			return tuple.Server
		}
	}
	return c.Default
}

func (p *Proxy) Update(c *Config) error {
	servers := []ServerAndRegexp{}
	for i, server := range c.Servers {
		for _, hostname := range server.Names {
			var host_regexp *regexp.Regexp
			var err error
			if server.Regexp {
				host_regexp, err = regexp.Compile(hostname)
			} else {
				host_regexp, err = regexp.Compile("^" + regexp.QuoteMeta(hostname) + "$")
			}
			if err != nil {
				return fmt.Errorf("cannot update proxy due to invalid regex: %v", err)
			}
			tuple := ServerAndRegexp{&c.Servers[i], host_regexp}
			servers = append(servers, tuple)
		}
	}
	var def *Server
	for i, server := range c.Servers {
		if server.Default {
			def = &c.Servers[i]
			break
		}
	}

	p.Lock.Lock()
	defer p.Lock.Unlock()
	p.ServerList = servers
	p.Default = def

	return nil
}

func (c *Config) Serve() error {
	glog.V(1).Infof("Listening on %s:%d", c.Bind.Host, c.Bind.Port)
	listener, err := net.Listen("tcp", fmt.Sprintf(
		"%s:%d", c.Bind.Host, c.Bind.Port,
	))
	if err != nil {
		return err
	}

	proxy := Proxy{}
	err = proxy.Update(c)
	if err != nil {
		return err
	}

	if c.Kubernetes != nil {
		rules := clientcmd.NewDefaultClientConfigLoadingRules()
		if c.Kubernetes.Kubeconfig != "" {
			rules.ExplicitPath = c.Kubernetes.Kubeconfig
		}
		cmdcfg, err := rules.Load()
		if err != nil {
			return err
		}
		ccfg := clientcmd.NewDefaultClientConfig(*cmdcfg, &clientcmd.ConfigOverrides{})
		rcfg, err := ccfg.ClientConfig()
		if err != nil {
			return err
		}
		extclient := typedv1beta1.NewForConfigOrDie(rcfg)
		client := corev1.NewForConfigOrDie(rcfg)

		// watch services
		services := cache.NewStore(cache.MetaNamespaceKeyFunc)
		lw := cache.NewListWatchFromClient(client, "services", "", fields.Everything())
		cache.NewReflector(lw, &apiv1.Service{}, services, time.Minute).Run()

		// watch ingresses
		updateTrigger := make(chan struct{}, 1)
		ingresses := map[string]*extapiv1beta1.Ingress{}
		lock := sync.Mutex{}
		class := c.Kubernetes.IngressClass
		if class == "" {
			class = "sniff"
		}
		go func() {
			for {
				w, err := extclient.Ingresses("").Watch(api.ListOptions{})
				if err != nil {
					glog.Errorf("Ingress watch error: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
					continue
				}
				evs := w.ResultChan()

			EventLoop:
				for ev := range evs {
					i := ev.Object.(*extapiv1beta1.Ingress)
					if i != nil && i.Annotations[ingressClassKey] != class {
						continue
					}
					switch ev.Type {
					case watch.Added, watch.Modified:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						ingresses[i.Namespace+"/"+i.Name] = i
						lock.Unlock()
					case watch.Deleted:
						glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						lock.Lock()
						delete(ingresses, i.Namespace+"/"+i.Name)
						lock.Unlock()
					case watch.Error:
						if i != nil {
							glog.V(5).Infof("event %s for %s/%s", ev.Type, i.Namespace, i.Name)
						}
						w.Stop()
						break EventLoop
					}
					select {
					case updateTrigger <- struct{}{}:
					default:
					}
				}

				// TODO: add backoff logic
				time.Sleep(time.Second)
			}
		}()

		go func() {
			for range updateTrigger {
				lock.Lock()

				serverForBackend := func(ing *extapiv1beta1.Ingress, backend *extapiv1beta1.IngressBackend) (*Server, error) {
					obj, found, err := services.GetByKey(fmt.Sprintf("%s/%s", ing.Namespace, backend.ServiceName))
					if err != nil {
						return nil, err
					}
					if !found {
						return nil, fmt.Errorf("service %s/%s not found", ing.Namespace, backend.ServiceName)
					}
					svc := obj.(*apiv1.Service)
					var port int
					if backend.ServicePort.Type == intstr.String {
						for _, p := range svc.Spec.Ports {
							if p.Name == backend.ServicePort.StrVal {
								port = int(p.Port)
								break
							}
						}
						if port == 0 {
							return nil, fmt.Errorf("port %q of service %s/%s not found", backend.ServicePort.StrVal, svc.Namespace, svc.Name)
						}
					} else {
						port = int(backend.ServicePort.IntVal)
					}
					return &Server{
						Host: svc.Spec.ClusterIP,
						// TODO: support string values:
						Port: port,
					}, nil
				}

				for _, i := range ingresses {
					c.Servers = []Server{}
					if i.Spec.Backend != nil {
						s, err := serverForBackend(i, i.Spec.Backend)
						if err != nil {
							glog.Errorf("Ingress %s/%s error with default backend, skipping: %v", i.Namespace, i.Name, err)
							continue
						}
						s.Default = true
						glog.V(4).Infof("Adding default backend -> %s:%d", s.Host, s.Port)
						c.Servers = append(c.Servers, *s)
					}
					for _, r := range i.Spec.Rules {
						if r.HTTP == nil {
							continue
						}
						for _, p := range r.HTTP.Paths {
							if p.Path != "" && p.Path != "/" {
								glog.Errorf("Ingress %s/%s error with rule, skipping: %v", i.Namespace, i.Name, err)
								continue
							}
							s, err := serverForBackend(i, &p.Backend)
							if err != nil {
								glog.Errorf("Ingress %s/%s error with rule %q path %q, skipping: %v", i.Namespace, i.Name, r.Host, p.Path, err)
								continue
							}
							s.Names = []string{r.Host}
							glog.V(4).Infof("Adding backend %q -> %s:%d", r.Host, s.Host, s.Port)
							c.Servers = append(c.Servers, *s)
						}
					}
				}
				lock.Unlock()

				glog.V(2).Infof("Updating proxy configuration")
				err := proxy.Update(c)
				if err != nil {
					glog.Errorf("Error updating proxy: %v", err)
					// TODO: add backoff logic
					time.Sleep(time.Second)
				} else {
					glog.V(2).Infof("Proxy configuration update done")
				}

			}
		}()
	}

	for {
		conn, err := listener.Accept()
		if err != nil {
			return err
		}
		glog.V(3).Infof(
			"%s -> %s",
			conn.RemoteAddr(),
			conn.LocalAddr(),
		)
		go proxy.Handle(conn)
	}
}

func (s *Proxy) Handle(conn net.Conn) {
	data := make([]byte, 4096)

	length, err := conn.Read(data)
	if err != nil {
		glog.Errorf("Error reading the configuration: %s", err)
	}

	var proxy *Server
	hostname, hostname_err := parser.GetHostname(data[:])
	if hostname_err == nil {
		glog.V(6).Infof("Parsed hostname: %s", hostname)

		proxy = s.Get(hostname)
		if proxy == nil {
			glog.V(4).Infof("No proxy matched %s", hostname)
			conn.Close()
			return
		}
	} else {
		glog.V(6).Info("Parsed request without hostname")

		proxy = s.Default
		if proxy == nil {
			glog.V(4).Info("No default proxy")
			conn.Close()
			return
		}
	}

	clientConn, err := net.Dial("tcp", fmt.Sprintf(
		"%s:%d", proxy.Host, proxy.Port,
	))
	if err != nil {
		glog.Warningf("Error connecting to backend: %s", err)
		conn.Close()
		return
	}
	n, err := clientConn.Write(data[:length])
	glog.V(7).Infof("Wrote %d bytes", n)
	if err != nil {
		glog.V(7).Infof("Error sending data to backend: %s", err)
		conn.Close()
		clientConn.Close()
	}
	Copycat(clientConn, conn)
}

func Copycat(client, server net.Conn) {
	defer client.Close()
	defer server.Close()

	glog.V(6).Info("Entering copy routine")

	doCopy := func(s, c net.Conn, cancel chan<- bool) {
		io.Copy(s, c)
		cancel <- true
	}

	cancel := make(chan bool, 2)

	go doCopy(server, client, cancel)
	go doCopy(client, server, cancel)

	select {
	case <-cancel:
		glog.V(6).Info("Disconnected")
		return
	}

}

// vim: foldmethod=marker
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-ozzo/ozzo-dbx"
	"github.com/go-ozzo/ozzo-routing"
	"github.com/go-ozzo/ozzo-routing/auth"
	"github.com/go-ozzo/ozzo-routing/content"
	"github.com/go-ozzo/ozzo-routing/cors"
	_ "github.com/lib/pq"
	"github.com/Zhanat87/go/apis"
	"github.com/Zhanat87/go/app"
	"github.com/Zhanat87/go/daos"
	"github.com/Zhanat87/go/errors"
	"github.com/Zhanat87/go/services"
	"os"
)

func main() {
	// load application configurations
	if err := app.LoadConfig("./config"); err != nil {
		panic(fmt.Errorf("Invalid application configuration: %s", err))
	}

	// load error messages
	if err := errors.LoadMessages(app.Config.ErrorFile); err != nil {
		panic(fmt.Errorf("Failed to read the error message file: %s", err))
	}

	// create the logger
	logger := logrus.New()

	// connect to the database
	db, err := dbx.MustOpen("postgres", app.Config.GetDSN())
	if err != nil {
		var variables string
		for _, e := range os.Environ() {
			variables += e + "\r\n"
		}
		panic(err + "\r\ndsn: " + app.Config.GetDSN() + "\r\n" + variables)
	}
	db.LogFunc = logger.Infof

	// wire up API routing
	http.Handle("/", buildRouter(logger, db))

	// start the server
	address := fmt.Sprintf(":%v", app.Config.ServerPort)
	logger.Infof("server %v is started at %v\n", app.Version, address)
	panic(http.ListenAndServe(address, nil))
}

func buildRouter(logger *logrus.Logger, db *dbx.DB) *routing.Router {
	router := routing.New()

	router.To("GET,HEAD", "/ping", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("OK " + app.Version)
	})

	router.To("GET,HEAD", "/test", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("test5")
	})

	router.Use(
		app.Init(logger),
		content.TypeNegotiator(content.JSON),
		cors.Handler(cors.Options{
			AllowOrigins: "*",
			AllowHeaders: "*",
			AllowMethods: "*",
		}),
		app.Transactional(db),
	)

	rg := router.Group("/v1")

	rg.Post("/auth", apis.Auth(app.Config.JWTSigningKey))
	rg.Use(auth.JWT(app.Config.JWTVerificationKey, auth.JWTOptions{
		SigningMethod: app.Config.JWTSigningMethod,
		TokenHandler:  apis.JWTHandler,
	}))

	artistDAO := daos.NewArtistDAO()
	apis.ServeArtistResource(rg, services.NewArtistService(artistDAO))

	albumDAO := daos.NewAlbumDAO()
	apis.ServeAlbumResource(rg, services.NewAlbumService(albumDAO))

	// wire up more resource APIs here

	return router
}
<commit_msg>updates<commit_after>package main

import (
	"fmt"
	"net/http"

	"github.com/Sirupsen/logrus"
	"github.com/go-ozzo/ozzo-dbx"
	"github.com/go-ozzo/ozzo-routing"
	"github.com/go-ozzo/ozzo-routing/auth"
	"github.com/go-ozzo/ozzo-routing/content"
	"github.com/go-ozzo/ozzo-routing/cors"
	_ "github.com/lib/pq"
	"github.com/Zhanat87/go/apis"
	"github.com/Zhanat87/go/app"
	"github.com/Zhanat87/go/daos"
	"github.com/Zhanat87/go/errors"
	"github.com/Zhanat87/go/services"
	"os"
)

func main() {
	// load application configurations
	if err := app.LoadConfig("./config"); err != nil {
		panic(fmt.Errorf("Invalid application configuration: %s", err))
	}

	// load error messages
	if err := errors.LoadMessages(app.Config.ErrorFile); err != nil {
		panic(fmt.Errorf("Failed to read the error message file: %s", err))
	}

	// create the logger
	logger := logrus.New()

	// connect to the database
	db, err := dbx.MustOpen("postgres", app.Config.GetDSN())
	if err != nil {
		var variables string
		for _, e := range os.Environ() {
			variables += e + "\r\n"
		}
		panic(err + "\r\ndsn: " + app.Config.GetDSN() + "\r\n" + variables)
	}
	db.LogFunc = logger.Infof

	// wire up API routing
	http.Handle("/", buildRouter(logger, db))

	// start the server
	address := fmt.Sprintf(":%v", app.Config.ServerPort)
	logger.Infof("server %v is started at %v\n", app.Version, address)
	panic(http.ListenAndServe(address, nil))
}

func buildRouter(logger *logrus.Logger, db *dbx.DB) *routing.Router {
	router := routing.New()

	router.To("GET,HEAD", "/ping", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("OK " + app.Version)
	})

	router.To("GET,HEAD", "/test", func(c *routing.Context) error {
		c.Abort()  // skip all other middlewares/handlers
		return c.Write("test6")
	})

	router.Use(
		app.Init(logger),
		content.TypeNegotiator(content.JSON),
		cors.Handler(cors.Options{
			AllowOrigins: "*",
			AllowHeaders: "*",
			AllowMethods: "*",
		}),
		app.Transactional(db),
	)

	rg := router.Group("/v1")

	rg.Post("/auth", apis.Auth(app.Config.JWTSigningKey))
	rg.Use(auth.JWT(app.Config.JWTVerificationKey, auth.JWTOptions{
		SigningMethod: app.Config.JWTSigningMethod,
		TokenHandler:  apis.JWTHandler,
	}))

	artistDAO := daos.NewArtistDAO()
	apis.ServeArtistResource(rg, services.NewArtistService(artistDAO))

	albumDAO := daos.NewAlbumDAO()
	apis.ServeAlbumResource(rg, services.NewAlbumService(albumDAO))

	// wire up more resource APIs here

	return router
}
<|endoftext|>


<commit_before>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"spitz"
	"syscall"
)

type Env int

const (
	EnvProduction Env = iota
	EnvTesting
	EnvDevel
)

var ContentDir string
var Environment Env
var AfterErrorFunc func(Context, *AppError)

func init() {
	var envFlag string
	flag.StringVar(&envFlag, "e", "production", "environment to run in [devel|testing|production]")
	flag.StringVar(&ContentDir, "d", ".", "content root directory (where server can find 'templates/')")
	flag.Parse()

	switch envFlag {
	case "production":
		Environment = EnvProduction
	case "testing":
		Environment = EnvTesting
	case "devel":
		Environment = EnvDevel
	default:
		log.Fatalf("unknown environment %q", envFlag)
	}

	reloadTemplates := Environment != EnvProduction
	DefaultPool = spitz.New(ContentDir+"/templates", reloadTemplates)
	fmt.Printf("bingo content directory: %q\n", ContentDir)
	fmt.Printf("bingo reloading templates? %v\n", reloadTemplates)

	go trapSignals()
}

func trapSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL)
	for {
		x := <-c
		if x == syscall.SIGHUP {
			fmt.Printf("!!! signal %s trapped, reopening log files\n", x)
			logReload()
		} else {
			fmt.Printf("!!! signal %s trapped, exiting\n", x)
			cleanup()
			os.Exit(0)
		}
	}
}

func cleanup() {
	logCleanup()
}

func ListenAndServe(addr string) {
	http.ListenAndServe(addr, nil)
}
<commit_msg>fixed spitz import<commit_after>// Copyright (C) 2012 Numerotron Inc.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package bingo

import (
	"flag"
	"fmt"
	"github.com/stathat/spitz"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
)

type Env int

const (
	EnvProduction Env = iota
	EnvTesting
	EnvDevel
)

var ContentDir string
var Environment Env
var AfterErrorFunc func(Context, *AppError)

func init() {
	var envFlag string
	flag.StringVar(&envFlag, "e", "production", "environment to run in [devel|testing|production]")
	flag.StringVar(&ContentDir, "d", ".", "content root directory (where server can find 'templates/')")
	flag.Parse()

	switch envFlag {
	case "production":
		Environment = EnvProduction
	case "testing":
		Environment = EnvTesting
	case "devel":
		Environment = EnvDevel
	default:
		log.Fatalf("unknown environment %q", envFlag)
	}

	reloadTemplates := Environment != EnvProduction
	DefaultPool = spitz.New(ContentDir+"/templates", reloadTemplates)
	fmt.Printf("bingo content directory: %q\n", ContentDir)
	fmt.Printf("bingo reloading templates? %v\n", reloadTemplates)

	go trapSignals()
}

func trapSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, syscall.SIGINT, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGKILL)
	for {
		x := <-c
		if x == syscall.SIGHUP {
			fmt.Printf("!!! signal %s trapped, reopening log files\n", x)
			logReload()
		} else {
			fmt.Printf("!!! signal %s trapped, exiting\n", x)
			cleanup()
			os.Exit(0)
		}
	}
}

func cleanup() {
	logCleanup()
}

func ListenAndServe(addr string) {
	http.ListenAndServe(addr, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
)

const (
	forbiddenMsg = `{"message":"Forbidden"}`
	notFoundMsg  = `{"message":"Not Found"}`
	errMsg       = `{"message":"%s"}`
)

// RequestVars contain variables from the HTTP request. Variables from routing, json body decoding, and
// some headers are stored.
type RequestVars struct {
	Oid           string
	Size          int64
	User          string
	Password      string
	Repo          string
	Authorization string
	RequestID     string
}

// Meta is object metadata as seen by the object and metadata stores.
type Meta struct {
	Oid      string `json:"oid"`
	Size     int64  `json:"size"`
	existing bool
}

// Representation is object medata as seen by clients of harbour.
type Representation struct {
	Oid   string           `json:"oid"`
	Size  int64            `json:"size"`
	Links map[string]*link `json:"_links"`
}

// ObjectLink builds a URL linking to the object.
func (v *RequestVars) ObjectLink() string {
	path := fmt.Sprintf("/%s/%s/objects/%s", v.User, v.Repo, v.Oid)
	return fmt.Sprintf("%s://%s%s", Config.Scheme, Config.Host, path)
}

// link provides a structure used to build a hypermedia representation of an HTTP link.
type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

// App links a Router, ContentStore, and MetaStore to provide the LFS server.
type App struct {
	router       *Router
	contentStore *ContentStore
	metaStore    *MetaStore
}

// NewApp creates a new App using the ContentStore and MetaStore provided
func NewApp(content *ContentStore, meta *MetaStore) *App {
	app := &App{contentStore: content, metaStore: meta}

	r := NewRouter()

	s := r.Route("/{user}/{repo}/objects/{oid}")
	s.Get(contentMediaType, app.GetContentHandler)
	s.Head(contentMediaType, app.GetContentHandler)
	s.Get(metaMediaType, app.GetMetaHandler)
	s.Head(metaMediaType, app.GetMetaHandler)
	s.Put(contentMediaType, app.PutHandler)

	o := r.Route("/{user}/{repo}/objects")
	o.Post(metaMediaType, app.PostHandler)

	app.router = r

	return app
}

// Serve calls http.Serve with the provided Listener and the app's router
func (a *App) Serve(l net.Listener) error {
	return http.Serve(l, a.router)
}

// GetContentHandler gets the content from the content store
func (a *App) GetContentHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		w.WriteHeader(404)
		logRequest(r, 404)
		return
	}

	content, err := a.contentStore.Get(meta)
	if err != nil {
		w.WriteHeader(404)
		logRequest(r, 404)
		return
	}

	io.Copy(w, content)
	logRequest(r, 200)
}

// GetMetaHandler retrieves metadata about the object
func (a *App) GetMetaHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		if isAuthError(err) {
			w.WriteHeader(401)
			fmt.Fprintf(w, forbiddenMsg)
			logRequest(r, 401)
		} else {
			w.WriteHeader(404)
			fmt.Fprint(w, notFoundMsg)
			logRequest(r, 404)
		}
		return
	}

	w.Header().Set("Content-Type", metaMediaType)

	if r.Method == "GET" {
		enc := json.NewEncoder(w)
		enc.Encode(a.Represent(rv, meta, true, false))
	}

	logRequest(r, 200)
}

// PostHandler instructs the client how to upload data
func (a *App) PostHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Put(rv)
	if err != nil {
		if isAuthError(err) {
			w.WriteHeader(401)
			fmt.Fprint(w, forbiddenMsg)
			logRequest(r, 401)
		} else {
			w.WriteHeader(404)
			fmt.Fprint(w, notFoundMsg)
			logRequest(r, 404)
		}
		return
	}

	w.Header().Set("Content-Type", metaMediaType)

	sentStatus := 201
	if meta.existing && a.contentStore.Exists(meta) {
		sentStatus = 200
	}
	w.WriteHeader(sentStatus)

	enc := json.NewEncoder(w)
	enc.Encode(a.Represent(rv, meta, meta.existing, true))
	logRequest(r, sentStatus)
}

// PutHandler receives data from the client and puts it into the content store
func (a *App) PutHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		if isAuthError(err) {
			w.WriteHeader(401)
			fmt.Fprint(w, forbiddenMsg)
			logRequest(r, 401)
		} else {
			w.WriteHeader(404)
			fmt.Fprint(w, notFoundMsg)
			logRequest(r, 404)
		}
		return
	}

	if err := a.contentStore.Put(meta, r.Body); err != nil {
		w.WriteHeader(500)
		fmt.Fprintf(w, errMsg, err)
		return
	}

	logRequest(r, 200)
}

// Represent takes a RequestVars and Meta and turns it into a Representation suitable
// for json encoding
func (a *App) Represent(rv *RequestVars, meta *Meta, download, upload bool) *Representation {
	rep := &Representation{
		Oid:   meta.Oid,
		Size:  meta.Size,
		Links: make(map[string]*link),
	}

	if download {
		rep.Links["download"] = &link{Href: rv.ObjectLink(), Header: map[string]string{"Accept": contentMediaType}}
	}

	if upload {
		header := make(map[string]string)
		header["Accept"] = contentMediaType
		header["Authorization"] = rv.Authorization
		rep.Links["upload"] = &link{Href: rv.ObjectLink(), Header: header}
	}
	return rep
}

func unpack(r *http.Request) *RequestVars {
	vars := Vars(r)
	user, pass, _ := r.BasicAuth()

	rv := &RequestVars{
		User:          user,
		Password:      pass,
		Repo:          vars["repo"],
		Oid:           vars["oid"],
		Authorization: r.Header.Get("Authorization"),
		RequestID:     vars["request_id"],
	}

	if r.Method == "POST" { // Maybe also check if +json
		var p RequestVars
		dec := json.NewDecoder(r.Body)
		err := dec.Decode(&p)
		if err != nil {
			return rv
		}

		rv.Oid = p.Oid
		rv.Size = p.Size
	}

	return rv
}

func logRequest(r *http.Request, status int) {
	logger.Log(kv{"method": r.Method, "url": r.URL, "status": status, "request_id": Vars(r)["request_id"]})
}

func isAuthError(err error) bool {
	type autherror interface {
		AuthError() bool
	}
	if ae, ok := err.(autherror); ok {
		return ae.AuthError()
	}
	return false
}
<commit_msg>Clean up status writing<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net"
	"net/http"
)

const (
	forbiddenMsg = `{"message":"Forbidden"}`
	notFoundMsg  = `{"message":"Not Found"}`
	errMsg       = `{"message":"%s"}`
)

// RequestVars contain variables from the HTTP request. Variables from routing, json body decoding, and
// some headers are stored.
type RequestVars struct {
	Oid           string
	Size          int64
	User          string
	Password      string
	Repo          string
	Authorization string
	RequestID     string
}

// Meta is object metadata as seen by the object and metadata stores.
type Meta struct {
	Oid      string `json:"oid"`
	Size     int64  `json:"size"`
	existing bool
}

// Representation is object medata as seen by clients of harbour.
type Representation struct {
	Oid   string           `json:"oid"`
	Size  int64            `json:"size"`
	Links map[string]*link `json:"_links"`
}

// ObjectLink builds a URL linking to the object.
func (v *RequestVars) ObjectLink() string {
	path := fmt.Sprintf("/%s/%s/objects/%s", v.User, v.Repo, v.Oid)
	return fmt.Sprintf("%s://%s%s", Config.Scheme, Config.Host, path)
}

// link provides a structure used to build a hypermedia representation of an HTTP link.
type link struct {
	Href   string            `json:"href"`
	Header map[string]string `json:"header,omitempty"`
}

// App links a Router, ContentStore, and MetaStore to provide the LFS server.
type App struct {
	router       *Router
	contentStore *ContentStore
	metaStore    *MetaStore
}

// NewApp creates a new App using the ContentStore and MetaStore provided
func NewApp(content *ContentStore, meta *MetaStore) *App {
	app := &App{contentStore: content, metaStore: meta}

	r := NewRouter()

	s := r.Route("/{user}/{repo}/objects/{oid}")
	s.Get(contentMediaType, app.GetContentHandler)
	s.Head(contentMediaType, app.GetContentHandler)
	s.Get(metaMediaType, app.GetMetaHandler)
	s.Head(metaMediaType, app.GetMetaHandler)
	s.Put(contentMediaType, app.PutHandler)

	o := r.Route("/{user}/{repo}/objects")
	o.Post(metaMediaType, app.PostHandler)

	app.router = r

	return app
}

// Serve calls http.Serve with the provided Listener and the app's router
func (a *App) Serve(l net.Listener) error {
	return http.Serve(l, a.router)
}

// GetContentHandler gets the content from the content store
func (a *App) GetContentHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		writeStatus(w, r, 404, "")
		return
	}

	content, err := a.contentStore.Get(meta)
	if err != nil {
		writeStatus(w, r, 404, "")
		return
	}

	io.Copy(w, content)
	logRequest(r, 200)
}

// GetMetaHandler retrieves metadata about the object
func (a *App) GetMetaHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		if isAuthError(err) {
			writeStatus(w, r, 401, forbiddenMsg)
		} else {
			writeStatus(w, r, 404, notFoundMsg)
		}
		return
	}

	w.Header().Set("Content-Type", metaMediaType)

	if r.Method == "GET" {
		enc := json.NewEncoder(w)
		enc.Encode(a.Represent(rv, meta, true, false))
	}

	logRequest(r, 200)
}

// PostHandler instructs the client how to upload data
func (a *App) PostHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Put(rv)
	if err != nil {
		if isAuthError(err) {
			writeStatus(w, r, 401, forbiddenMsg)
		} else {
			writeStatus(w, r, 404, notFoundMsg)
		}
		return
	}

	w.Header().Set("Content-Type", metaMediaType)

	sentStatus := 201
	if meta.existing && a.contentStore.Exists(meta) {
		sentStatus = 200
	}
	w.WriteHeader(sentStatus)

	enc := json.NewEncoder(w)
	enc.Encode(a.Represent(rv, meta, meta.existing, true))
	logRequest(r, sentStatus)
}

// PutHandler receives data from the client and puts it into the content store
func (a *App) PutHandler(w http.ResponseWriter, r *http.Request) {
	rv := unpack(r)
	meta, err := a.metaStore.Get(rv)
	if err != nil {
		if isAuthError(err) {
			writeStatus(w, r, 401, forbiddenMsg)
		} else {
			writeStatus(w, r, 404, notFoundMsg)
		}
		return
	}

	if err := a.contentStore.Put(meta, r.Body); err != nil {
		writeStatus(w, r, 500, errMsg, err)
		return
	}

	logRequest(r, 200)
}

// Represent takes a RequestVars and Meta and turns it into a Representation suitable
// for json encoding
func (a *App) Represent(rv *RequestVars, meta *Meta, download, upload bool) *Representation {
	rep := &Representation{
		Oid:   meta.Oid,
		Size:  meta.Size,
		Links: make(map[string]*link),
	}

	if download {
		rep.Links["download"] = &link{Href: rv.ObjectLink(), Header: map[string]string{"Accept": contentMediaType}}
	}

	if upload {
		header := make(map[string]string)
		header["Accept"] = contentMediaType
		header["Authorization"] = rv.Authorization
		rep.Links["upload"] = &link{Href: rv.ObjectLink(), Header: header}
	}
	return rep
}

func unpack(r *http.Request) *RequestVars {
	vars := Vars(r)
	user, pass, _ := r.BasicAuth()

	rv := &RequestVars{
		User:          user,
		Password:      pass,
		Repo:          vars["repo"],
		Oid:           vars["oid"],
		Authorization: r.Header.Get("Authorization"),
		RequestID:     vars["request_id"],
	}

	if r.Method == "POST" { // Maybe also check if +json
		var p RequestVars
		dec := json.NewDecoder(r.Body)
		err := dec.Decode(&p)
		if err != nil {
			return rv
		}

		rv.Oid = p.Oid
		rv.Size = p.Size
	}

	return rv
}

func writeStatus(w http.ResponseWriter, r *http.Request, status int, format string, a ...interface{}) {
	w.WriteHeader(status)
	fmt.Fprintf(w, format, a)
	logRequest(r, status)
}

func logRequest(r *http.Request, status int) {
	logger.Log(kv{"method": r.Method, "url": r.URL, "status": status, "request_id": Vars(r)["request_id"]})
}

func isAuthError(err error) bool {
	type autherror interface {
		AuthError() bool
	}
	if ae, ok := err.(autherror); ok {
		return ae.AuthError()
	}
	return false
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"text/tabwriter"
	"time"

	log "github.com/Sirupsen/logrus"

	"golang.org/x/crypto/ssh"
)

var sessions = struct {
	mu   sync.RWMutex
	keys map[string][]*publicKey
}{
	keys: make(map[string][]*publicKey),
}

func serve(config *ssh.ServerConfig, nConn net.Conn) {
	// Before use, a handshake must be performed on the incoming net.Conn
	conn, chans, reqs, err := ssh.NewServerConn(nConn, config)
	if err != nil {
		log.Warnln("Failed to handshake:", err)
	}

	defer func() {
		sessions.mu.Lock()
		delete(sessions.keys, string(conn.SessionID()))
		sessions.mu.Unlock()
		conn.Close()
	}()

	// The incoming Request channel must be serviced
	go ssh.DiscardRequests(reqs)

	sessions.mu.RLock()
	keys := sessions.keys[string(conn.SessionID())]
	sessions.mu.RUnlock()

	// Service the incoming Channel channel
	for n := range chans {
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if n.ChannelType() != "session" {
			n.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := n.Accept()
		defer channel.Close()
		if err != nil {
			log.Warnln("Could not accept channel:", err)
		}

		agentFwd, x11 := false, false
		reqLock := &sync.Mutex{}
		reqLock.Lock()
		timeout := time.AfterFunc(30*time.Second, func() { reqLock.Unlock() })

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false
				switch req.Type {
				case "shell":
					fallthrough
				case "pty-req":
					ok = true

					// "auth-agent-req@openssh.com" and "x11-req" always arrive
					// before the "pty-req", so we can go ahead now
					if timeout.Stop() {
						reqLock.Unlock()
					}

				case "auth-agent-req@openssh.com":
					agentFwd = true
				case "x11-req":
					x11 = true
				}

				if req.WantReply {
					req.Reply(ok, nil)
				}
			}
		}(requests)

		markBlacklistedKeys(keys)

		channel.Write([]byte(welcomeMsg))

		var table bytes.Buffer
		tabWriter := new(tabwriter.Writer)
		tabWriter.Init(&table, 5, 2, 2, ' ', 0)
		// Note that using tabwriter, columns are tab-terminated,
		// not tab-delimited
		fmt.Fprint(tabWriter, "Bits\tType\tFingerprint\tIssues\n")

		var issues string
		var blacklisted, weak bool
		for _, k := range keys {
			issues = "No known issues"
			length, err := k.BitLen()

			if err != nil {
				log.Errorf("Failed to determine key length for %s key: %s", k.key.Type(), err)
			}

			if length < 2048 && k.key.Type() == ssh.KeyAlgoRSA {
				issues = "WEAK KEY LENGTH"
				weak = true
			}

			if k.blacklisted {
				// being blacklisted takes priority of any key length weaknesses
				issues = "BLACKLISTED"
				blacklisted = true
			}

			fmt.Fprintf(tabWriter, "%d\t%s\t%s\t%s\t\n", length, k.key.Type(), k.Fingerprint(), issues)
		}

		err = tabWriter.Flush()
		if err != nil {
			log.Errorln("Error when flushing tab writer:", err)
		}
		channel.Write([]byte(
			strings.Replace(table.String(), "\n", "\n\r", -1) +
				"\n\r"))

		if blacklisted {
			channel.Write([]byte(blacklistMsg))
		}

		if weak {
			channel.Write([]byte(weakMsg))
		}

		reqLock.Lock()
		if agentFwd {
			channel.Write([]byte(agentMsg))
		}
		if x11 {
			channel.Write([]byte(x11Msg))
		}

		// Explicitly close the channel to end the session
		channel.Close()
	}

}

func publicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
	sessions.mu.Lock()
	sessionID := string(conn.SessionID())
	sessions.keys[sessionID] = append(sessions.keys[sessionID], &publicKey{key: key})
	sessions.mu.Unlock()

	// Never succeed a key, or we might not see the next. See KeyboardInteractiveCallback.
	return nil, errors.New("")
}

func keyboardInteractiveCallback(ssh.ConnMetadata, ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
	// keyboard-interactive is tried when all public keys failed, and
	// since it's server-driven we can just pass without user
	// interaction to let the user in once we got all the public keys
	return nil, nil
}

var (
	agentMsg = strings.Replace(`CRITICAL: SSH agent forwarding is enabled; it is dangerous to enable agent forwarding
	  for servers you do not trust as it allows them to log in to other servers as you.

`, "\n", "\n\r", -1)

	blacklistMsg = strings.Replace(`CRITICAL: You are using blacklisted key(s) that are known to be insecure.
          You should replace them immediately.
          See: https://www.debian.org/security/2008/dsa-1576

`, "\n", "\n\r", -1)

	weakMsg = strings.Replace(`WARNING:  You are using RSA key(s) with a length of less than 2048 bits.
          Consider replacing them with a new key of 2048 bits or more.

`, "\n", "\n\r", -1)

	welcomeMsg = strings.Replace(`This server checks your SSH public keys for known or potential
security weaknesses.

For more information, please see:
https://github.com/mattbostock/sshkeycheck

The public keys presented by your SSH client are:

`, "\n", "\n\r", -1)

	x11Msg = strings.Replace(`CRITICAL: X11 forwarding is enabled; it is dangerous to allow X11 forwarding
	  for servers you do not trust as it allows them to access your desktop.

`, "\n", "\n\r", -1)
)
<commit_msg>Return immediately if handshake fails<commit_after>package main

import (
	"bytes"
	"errors"
	"fmt"
	"net"
	"strings"
	"sync"
	"text/tabwriter"
	"time"

	log "github.com/Sirupsen/logrus"

	"golang.org/x/crypto/ssh"
)

var sessions = struct {
	mu   sync.RWMutex
	keys map[string][]*publicKey
}{
	keys: make(map[string][]*publicKey),
}

func serve(config *ssh.ServerConfig, nConn net.Conn) {
	// Before use, a handshake must be performed on the incoming net.Conn
	conn, chans, reqs, err := ssh.NewServerConn(nConn, config)
	if err != nil {
		log.Warnln("Failed to handshake:", err)
		return
	}

	defer func() {
		sessions.mu.Lock()
		delete(sessions.keys, string(conn.SessionID()))
		sessions.mu.Unlock()
		conn.Close()
	}()

	// The incoming Request channel must be serviced
	go ssh.DiscardRequests(reqs)

	sessions.mu.RLock()
	keys := sessions.keys[string(conn.SessionID())]
	sessions.mu.RUnlock()

	// Service the incoming Channel channel
	for n := range chans {
		// Channels have a type, depending on the application level
		// protocol intended. In the case of a shell, the type is
		// "session" and ServerShell may be used to present a simple
		// terminal interface.
		if n.ChannelType() != "session" {
			n.Reject(ssh.UnknownChannelType, "unknown channel type")
			continue
		}

		channel, requests, err := n.Accept()
		defer channel.Close()
		if err != nil {
			log.Warnln("Could not accept channel:", err)
		}

		agentFwd, x11 := false, false
		reqLock := &sync.Mutex{}
		reqLock.Lock()
		timeout := time.AfterFunc(30*time.Second, func() { reqLock.Unlock() })

		go func(in <-chan *ssh.Request) {
			for req := range in {
				ok := false
				switch req.Type {
				case "shell":
					fallthrough
				case "pty-req":
					ok = true

					// "auth-agent-req@openssh.com" and "x11-req" always arrive
					// before the "pty-req", so we can go ahead now
					if timeout.Stop() {
						reqLock.Unlock()
					}

				case "auth-agent-req@openssh.com":
					agentFwd = true
				case "x11-req":
					x11 = true
				}

				if req.WantReply {
					req.Reply(ok, nil)
				}
			}
		}(requests)

		markBlacklistedKeys(keys)

		channel.Write([]byte(welcomeMsg))

		var table bytes.Buffer
		tabWriter := new(tabwriter.Writer)
		tabWriter.Init(&table, 5, 2, 2, ' ', 0)
		// Note that using tabwriter, columns are tab-terminated,
		// not tab-delimited
		fmt.Fprint(tabWriter, "Bits\tType\tFingerprint\tIssues\n")

		var issues string
		var blacklisted, weak bool
		for _, k := range keys {
			issues = "No known issues"
			length, err := k.BitLen()

			if err != nil {
				log.Errorf("Failed to determine key length for %s key: %s", k.key.Type(), err)
			}

			if length < 2048 && k.key.Type() == ssh.KeyAlgoRSA {
				issues = "WEAK KEY LENGTH"
				weak = true
			}

			if k.blacklisted {
				// being blacklisted takes priority of any key length weaknesses
				issues = "BLACKLISTED"
				blacklisted = true
			}

			fmt.Fprintf(tabWriter, "%d\t%s\t%s\t%s\t\n", length, k.key.Type(), k.Fingerprint(), issues)
		}

		err = tabWriter.Flush()
		if err != nil {
			log.Errorln("Error when flushing tab writer:", err)
		}
		channel.Write([]byte(
			strings.Replace(table.String(), "\n", "\n\r", -1) +
				"\n\r"))

		if blacklisted {
			channel.Write([]byte(blacklistMsg))
		}

		if weak {
			channel.Write([]byte(weakMsg))
		}

		reqLock.Lock()
		if agentFwd {
			channel.Write([]byte(agentMsg))
		}
		if x11 {
			channel.Write([]byte(x11Msg))
		}

		// Explicitly close the channel to end the session
		channel.Close()
	}

}

func publicKeyCallback(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
	sessions.mu.Lock()
	sessionID := string(conn.SessionID())
	sessions.keys[sessionID] = append(sessions.keys[sessionID], &publicKey{key: key})
	sessions.mu.Unlock()

	// Never succeed a key, or we might not see the next. See KeyboardInteractiveCallback.
	return nil, errors.New("")
}

func keyboardInteractiveCallback(ssh.ConnMetadata, ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) {
	// keyboard-interactive is tried when all public keys failed, and
	// since it's server-driven we can just pass without user
	// interaction to let the user in once we got all the public keys
	return nil, nil
}

var (
	agentMsg = strings.Replace(`CRITICAL: SSH agent forwarding is enabled; it is dangerous to enable agent forwarding
	  for servers you do not trust as it allows them to log in to other servers as you.

`, "\n", "\n\r", -1)

	blacklistMsg = strings.Replace(`CRITICAL: You are using blacklisted key(s) that are known to be insecure.
          You should replace them immediately.
          See: https://www.debian.org/security/2008/dsa-1576

`, "\n", "\n\r", -1)

	weakMsg = strings.Replace(`WARNING:  You are using RSA key(s) with a length of less than 2048 bits.
          Consider replacing them with a new key of 2048 bits or more.

`, "\n", "\n\r", -1)

	welcomeMsg = strings.Replace(`This server checks your SSH public keys for known or potential
security weaknesses.

For more information, please see:
https://github.com/mattbostock/sshkeycheck

The public keys presented by your SSH client are:

`, "\n", "\n\r", -1)

	x11Msg = strings.Replace(`CRITICAL: X11 forwarding is enabled; it is dangerous to allow X11 forwarding
	  for servers you do not trust as it allows them to access your desktop.

`, "\n", "\n\r", -1)
)
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/flynn/flynn-test/util"
	c "gopkg.in/check.v1"
)

func initApp(t *c.C, app string) string {
	dir := filepath.Join(t.MkDir(), "app")
	t.Assert(run(exec.Command("cp", "-r", filepath.Join("apps", app), dir)), Succeeds)
	t.Assert(git(dir, "init"), Succeeds)
	t.Assert(git(dir, "add", "."), Succeeds)
	t.Assert(git(dir, "commit", "-am", "init"), Succeeds)
	return dir
}

type appSuite struct {
	appDir string
}

func (s *appSuite) Flynn(args ...string) *CmdResult {
	return flynn(s.appDir, args...)
}

func (s *appSuite) Git(args ...string) *CmdResult {
	return git(s.appDir, args...)
}

type BasicSuite struct {
	appSuite
}

var _ = c.Suite(&BasicSuite{})

func (s *BasicSuite) SetUpSuite(t *c.C) {
	s.appDir = initApp(t, "basic")
}

func (s *BasicSuite) TestBasic(t *c.C) {
	name := util.RandomString()[:30]
	t.Assert(s.Flynn("create", name), Outputs, fmt.Sprintf("Created %s\n", name))

	push := s.Git("push", "flynn", "master")
	t.Assert(push, OutputContains, "Node.js app detected")
	t.Assert(push, OutputContains, "Downloading and installing node")
	t.Assert(push, OutputContains, "Installing dependencies")
	t.Assert(push, OutputContains, "Procfile declares types -> web")
	t.Assert(push, OutputContains, "Creating release")
	t.Assert(push, OutputContains, "Application deployed")
	t.Assert(push, OutputContains, "* [new branch]      master -> master")

	t.Assert(s.Flynn("scale", "web=3"), Succeeds)

	newRoute := s.Flynn("route-add-http", util.RandomString()+".dev")
	t.Assert(newRoute, Succeeds)

	t.Assert(s.Flynn("routes"), OutputContains, strings.TrimSpace(newRoute.Output))

	// TODO: add retry helper
	time.Sleep(5 * time.Second)

	ps := s.Flynn("ps")
	t.Assert(ps, Succeeds)
	psLines := strings.Split(strings.TrimSpace(ps.Output), "\n")
	t.Assert(len(psLines), c.Equals, 4)

	for _, l := range psLines[1:] {
		idType := regexp.MustCompile(`\s+`).Split(l, 2)
		t.Assert(idType[1], c.Equals, "web")
		log := s.Flynn("log", idType[0])
		t.Assert(log, OutputContains, "Listening on ")
	}

	// Make HTTP requests
}
<commit_msg>test: Use attempts in TestBasic<commit_after>package main

import (
	"fmt"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/flynn/flynn-test/util"
	"github.com/flynn/go-flynn/attempt"
	c "gopkg.in/check.v1"
)

func initApp(t *c.C, app string) string {
	dir := filepath.Join(t.MkDir(), "app")
	t.Assert(run(exec.Command("cp", "-r", filepath.Join("apps", app), dir)), Succeeds)
	t.Assert(git(dir, "init"), Succeeds)
	t.Assert(git(dir, "add", "."), Succeeds)
	t.Assert(git(dir, "commit", "-am", "init"), Succeeds)
	return dir
}

type appSuite struct {
	appDir string
}

func (s *appSuite) Flynn(args ...string) *CmdResult {
	return flynn(s.appDir, args...)
}

func (s *appSuite) Git(args ...string) *CmdResult {
	return git(s.appDir, args...)
}

type BasicSuite struct {
	appSuite
}

var _ = c.Suite(&BasicSuite{})

func (s *BasicSuite) SetUpSuite(t *c.C) {
	s.appDir = initApp(t, "basic")
}

var Attempts = attempt.Strategy{
	Min:   5,
	Total: 10 * time.Second,
	Delay: 500 * time.Millisecond,
}

func (s *BasicSuite) TestBasic(t *c.C) {
	name := util.RandomString()[:30]
	t.Assert(s.Flynn("create", name), Outputs, fmt.Sprintf("Created %s\n", name))

	push := s.Git("push", "flynn", "master")
	t.Assert(push, OutputContains, "Node.js app detected")
	t.Assert(push, OutputContains, "Downloading and installing node")
	t.Assert(push, OutputContains, "Installing dependencies")
	t.Assert(push, OutputContains, "Procfile declares types -> web")
	t.Assert(push, OutputContains, "Creating release")
	t.Assert(push, OutputContains, "Application deployed")
	t.Assert(push, OutputContains, "* [new branch]      master -> master")

	t.Assert(s.Flynn("scale", "web=3"), Succeeds)

	newRoute := s.Flynn("route-add-http", util.RandomString()+".dev")
	t.Assert(newRoute, Succeeds)

	t.Assert(s.Flynn("routes"), OutputContains, strings.TrimSpace(newRoute.Output))

	// use Attempts to give the processes time to start
	if err := Attempts.Run(func() error {
		ps := s.Flynn("ps")
		if ps.Err != nil {
			return ps.Err
		}
		psLines := strings.Split(strings.TrimSpace(ps.Output), "\n")
		if len(psLines) != 4 {
			return fmt.Errorf("Expected 4 ps lines, got %d", len(psLines))
		}

		for _, l := range psLines[1:] {
			idType := regexp.MustCompile(`\s+`).Split(l, 2)
			if idType[1] != "web" {
				return fmt.Errorf("Expected web type, got %s", idType[1])
			}
			log := s.Flynn("log", idType[0])
			if !strings.Contains(log.Output, "Listening on ") {
				return fmt.Errorf("Expected \"%s\" to contain \"Listening on \"", log.Output)
			}
		}
		return nil
	}); err != nil {
		t.Error(err)
	}

	// Make HTTP requests
}
<|endoftext|>


<commit_before>package mods

import (
	"bytes"
	"context"
	"crypto/sha256"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/ngld/knossos/packages/api/common"
	"github.com/ngld/knossos/packages/libknossos/pkg/api"
	"github.com/ngld/knossos/packages/libknossos/pkg/downloader"
	"github.com/ngld/knossos/packages/libknossos/pkg/helpers"
	"github.com/ngld/knossos/packages/libknossos/pkg/storage"
	"github.com/rotisserie/eris"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
)

type modPackInfo struct {
	mod *common.ModIndex_Mod
	idx int
}

var errModNotFound = eris.New("remote mod not found")

func fetchRemoteMessage(ctx context.Context, messageName string, ref protoreflect.ProtoMessage) error {
	resp, err := helpers.CachedGet(ctx, api.SyncEndpoint+"/"+messageName)
	if err != nil {
		return eris.Wrapf(err, "failed to send modsync request to Nebula (%s)", messageName)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		return errModNotFound
	}

	if resp.StatusCode == 304 {
		return nil
	}

	if resp.StatusCode != 200 {
		return eris.Errorf("request to %s failed with status %d", api.SyncEndpoint+"/"+messageName, resp.StatusCode)
	}

	encoded, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return eris.Wrapf(err, "failed to read %s", messageName)
	}

	err = proto.Unmarshal(encoded, ref)
	if err != nil {
		return eris.Wrapf(err, "failed to parse response to %s", messageName)
	}

	return nil
}

func calcVersionsChecksum(versions []string) ([]byte, error) {
	// We use simple string sorting here instead of proper versioning sorting since it doesn't matter *how* the versions
	// are sorted as long as they appear in the same order on server and client.
	sort.Strings(versions)

	hasher := sha256.New()
	for _, version := range versions {
		_, err := hasher.Write([]byte(version))
		if err != nil {
			return nil, eris.Wrap(err, "failed to hash mod versions")
		}
	}

	return hasher.Sum(nil), nil
}

func UpdateRemoteModIndex(ctx context.Context) error {
	resyncNeeded := false
	for resyncNeeded {
		resyncNeeded = false
		err := storage.ImportRemoteMods(ctx, func(ctx context.Context, params storage.RemoteImportCallbackParams) error {
			curDates, err := storage.GetRemoteModsLastModifiedDates(ctx)
			if err != nil {
				return err
			}

			settings, err := storage.GetSettings(ctx)
			if err != nil {
				return err
			}

			api.Log(ctx, api.LogInfo, "Fetching remote index")
			var index common.ModIndex
			err = fetchRemoteMessage(ctx, "index", &index)
			if err != nil {
				return eris.Wrap(err, "failed to fetch index")
			}

			if len(index.Mods) == 0 {
				api.Log(ctx, api.LogInfo, "No changes since last check")
				api.SetProgress(ctx, 1, "Done")
				return nil
			}

			packs := make([]*downloader.QueueItem, 0)
			tmpFolder := filepath.Join(settings.LibraryPath, "temp")
			modEntries := make(map[string]modPackInfo)

			// If the mod index hasn't changed since the last time we fetched it, index will be the zero value of common.ModIndex
			// this means that index.Mods is an empty list which means we do nothing which is exactly what we want.
			for _, entry := range index.Mods {
				curModDates, found := curDates[entry.Modid]
				if !found || err != nil {
					curModDates = make([]time.Time, len(entry.PacksLastModified)+1)
				}

				if !curModDates[0].Equal(entry.LastModified.AsTime()) {
					key := "mod-" + entry.Modid
					modEntries[key] = modPackInfo{
						mod: entry,
						idx: -1,
					}
					packs = append(packs, &downloader.QueueItem{
						Key:      key,
						Filepath: filepath.Join(tmpFolder, "modsync-"+key),
						Mirrors:  []string{api.SyncEndpoint + "/m." + entry.Modid},
						Checksum: nil,
						Filesize: 0,
					})
				}

				for idx, modified := range entry.PacksLastModified {
					if len(curModDates) <= idx+1 || !curModDates[idx+1].Equal(modified.AsTime()) {
						key := fmt.Sprintf("pack-%s-%03d", entry.Modid, idx)
						modEntries[key] = modPackInfo{
							mod: entry,
							idx: idx,
						}
						packs = append(packs, &downloader.QueueItem{
							Key:      key,
							Filepath: filepath.Join(tmpFolder, "modsync-"+key),
							Mirrors:  []string{fmt.Sprintf("%s/m.%s.%03d", api.SyncEndpoint, entry.Modid, idx)},
							Checksum: nil,
							Filesize: 0,
						})
					}
				}
			}

			queue, err := downloader.NewQueue(ctx, packs)
			if err != nil {
				return eris.Wrap(err, "failed to initialise downloader")
			}

			// We're downloading a bunch of small files; it's fine to download many in parallel even on slow connections.
			queue.MaxParallel = 5
			go queue.Run(ctx)

			done := 0
			for queue.NextResult() {
				dlItem := queue.Result()
				entry := modEntries[dlItem.Key]
				api.SetProgress(ctx, float32(done)/float32(len(packs)), fmt.Sprintf("Processing mod %s (%d of %d)", entry.mod.Modid, done, len(packs)))

				encoded, err := os.ReadFile(dlItem.Filepath)
				if err != nil {
					return eris.Wrapf(err, "failed to open downloaded file %s", dlItem.Filepath)
				}

				if strings.HasPrefix(dlItem.Key, "mod-") {
					var modMeta common.ModMeta
					err = proto.Unmarshal(encoded, &modMeta)
					if err != nil {
						return eris.Wrapf(err, "failed to parse mod meta %s", dlItem.Key)
					}

					err = params.AddMod(&modMeta)
					if err != nil {
						return eris.Wrapf(err, "failed to save mod metadata for %s", entry.mod.Modid)
					}

					if _, ok := curDates[entry.mod.Modid]; !ok {
						curDates[entry.mod.Modid] = make([]time.Time, 1)
					}

					curDates[entry.mod.Modid][0] = entry.mod.LastModified.AsTime()
				} else {
					var releasePack common.ReleasePack
					err = proto.Unmarshal(encoded, &releasePack)
					if err != nil {
						return eris.Wrapf(err, "failed to parse mod release pack %s", dlItem.Key)
					}

					if releasePack.Modid != entry.mod.Modid || releasePack.Packnum != uint32(entry.idx) {
						return eris.Errorf("received wrong pack! expected %d for %s but got %d for %s", entry.idx, entry.mod.Modid, releasePack.Packnum, releasePack.Modid)
					}

					for _, rel := range releasePack.Releases {
						err := params.AddRelease(rel)
						if err != nil {
							return eris.Wrapf(err, "failed to import release %s for %s", rel.Version, entry.mod.Modid)
						}
					}

					modDates := curDates[entry.mod.Modid]
					for len(modDates) <= entry.idx+1 {
						modDates = append(modDates, time.Time{})
					}

					modDates[entry.idx+1] = entry.mod.PacksLastModified[entry.idx].AsTime()
					curDates[entry.mod.Modid] = modDates
				}

				done++
			}

			err = queue.Error()
			if err != nil {
				return eris.Wrap(err, "failed to download modsync files")
			}

			api.Log(ctx, api.LogInfo, "Looking for removed mods")
			releases, err := storage.RemoteMods.GetMods(ctx)
			if err != nil {
				return eris.Wrap(err, "failed to retrieve mod IDs")
			}

			seen := make(map[string]bool)
			for _, entry := range index.Mods {
				seen[entry.Modid] = true

				localVersions, err := storage.RemoteMods.GetVersionsForMod(ctx, entry.Modid)
				if err == nil {
					versionHash, err := calcVersionsChecksum(localVersions)
					if err != nil {
						return err
					}

					versionMismatch := !bytes.Equal(versionHash, entry.VersionChecksum)
					if versionMismatch {
						api.Log(ctx, api.LogInfo, "Version mismatch detected, clearing releases for %s.", entry.Modid)

						err = params.RemoveModReleases(entry.Modid)
						if err != nil {
							return err
						}

						delete(curDates, entry.Modid)
						resyncNeeded = true
					}
				}
			}

			for _, rel := range releases {
				if !seen[rel.Modid] {
					api.Log(ctx, api.LogInfo, "Removing %s", rel.Modid)
					err = params.RemoveMod(rel.Modid)
					if err != nil {
						return eris.Wrapf(err, "failed to remove mod %s", rel.Modid)
					}
				}
			}

			api.SetProgress(ctx, 1, "Finishing")

			err = storage.UpdateRemoteModsLastModifiedDates(ctx, curDates)
			if err == nil {
				api.Log(ctx, api.LogInfo, "Done")
			}

			return err
		})
		if err != nil {
			return err
		}

		if resyncNeeded {
			api.Log(ctx, api.LogInfo, "Found inconsistencies; will sync again.")
		}
	}

	return nil
}

func FetchModChecksums(ctx context.Context, modVersions map[string]string) (map[string]*common.ChecksumPack, error) {
	tempFolder, err := os.MkdirTemp("", "knossos-chk-dl")
	if err != nil {
		return nil, eris.Wrap(err, "failed to create temp folder for checksums")
	}
	defer os.RemoveAll(tempFolder)

	queueItems := make([]*downloader.QueueItem, 0, len(modVersions))
	for modID, version := range modVersions {
		queueItems = append(queueItems, &downloader.QueueItem{
			Key:      modID,
			Filepath: filepath.Join(tempFolder, fmt.Sprintf("c.%s.%s", modID, version)),
			Mirrors:  []string{fmt.Sprintf("%s/c.%s.%s", api.SyncEndpoint, modID, version)},
		})
	}

	queue, err := downloader.NewQueue(ctx, queueItems)
	if err != nil {
		return nil, eris.Wrap(err, "failed to init download queue")
	}

	err = queue.Run(ctx)
	if err != nil {
		return nil, eris.Wrap(err, "failed to download checksums")
	}

	result := make(map[string]*common.ChecksumPack)
	for _, item := range queueItems {
		encoded, err := os.ReadFile(item.Filepath)
		if err != nil {
			return nil, eris.Wrapf(err, "failed to read downloaded checksum file %s", item.Filepath)
		}

		data := new(common.ChecksumPack)
		err = proto.Unmarshal(encoded, data)
		if err != nil {
			return nil, eris.Wrapf(err, "failed to parse checksum file %s", item.Filepath)
		}

		result[item.Key] = data
	}

	return result, nil
}
<commit_msg>fix(libknossos): actually run modsync<commit_after>package mods

import (
	"bytes"
	"context"
	"crypto/sha256"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"github.com/ngld/knossos/packages/api/common"
	"github.com/ngld/knossos/packages/libknossos/pkg/api"
	"github.com/ngld/knossos/packages/libknossos/pkg/downloader"
	"github.com/ngld/knossos/packages/libknossos/pkg/helpers"
	"github.com/ngld/knossos/packages/libknossos/pkg/storage"
	"github.com/rotisserie/eris"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
)

type modPackInfo struct {
	mod *common.ModIndex_Mod
	idx int
}

var errModNotFound = eris.New("remote mod not found")

func fetchRemoteMessage(ctx context.Context, messageName string, ref protoreflect.ProtoMessage) error {
	resp, err := helpers.CachedGet(ctx, api.SyncEndpoint+"/"+messageName)
	if err != nil {
		return eris.Wrapf(err, "failed to send modsync request to Nebula (%s)", messageName)
	}
	defer resp.Body.Close()

	if resp.StatusCode == 404 {
		return errModNotFound
	}

	if resp.StatusCode == 304 {
		return nil
	}

	if resp.StatusCode != 200 {
		return eris.Errorf("request to %s failed with status %d", api.SyncEndpoint+"/"+messageName, resp.StatusCode)
	}

	encoded, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return eris.Wrapf(err, "failed to read %s", messageName)
	}

	err = proto.Unmarshal(encoded, ref)
	if err != nil {
		return eris.Wrapf(err, "failed to parse response to %s", messageName)
	}

	return nil
}

func calcVersionsChecksum(versions []string) ([]byte, error) {
	// We use simple string sorting here instead of proper versioning sorting since it doesn't matter *how* the versions
	// are sorted as long as they appear in the same order on server and client.
	sort.Strings(versions)

	hasher := sha256.New()
	for _, version := range versions {
		_, err := hasher.Write([]byte(version))
		if err != nil {
			return nil, eris.Wrap(err, "failed to hash mod versions")
		}
	}

	return hasher.Sum(nil), nil
}

func UpdateRemoteModIndex(ctx context.Context) error {
	resyncNeeded := true
	for resyncNeeded {
		resyncNeeded = false
		err := storage.ImportRemoteMods(ctx, func(ctx context.Context, params storage.RemoteImportCallbackParams) error {
			curDates, err := storage.GetRemoteModsLastModifiedDates(ctx)
			if err != nil {
				return err
			}

			settings, err := storage.GetSettings(ctx)
			if err != nil {
				return err
			}

			api.Log(ctx, api.LogInfo, "Fetching remote index")
			var index common.ModIndex
			err = fetchRemoteMessage(ctx, "index", &index)
			if err != nil {
				return eris.Wrap(err, "failed to fetch index")
			}

			if len(index.Mods) == 0 {
				api.Log(ctx, api.LogInfo, "No changes since last check")
				api.SetProgress(ctx, 1, "Done")
				return nil
			}

			packs := make([]*downloader.QueueItem, 0)
			tmpFolder := filepath.Join(settings.LibraryPath, "temp")
			modEntries := make(map[string]modPackInfo)

			// If the mod index hasn't changed since the last time we fetched it, index will be the zero value of common.ModIndex
			// this means that index.Mods is an empty list which means we do nothing which is exactly what we want.
			for _, entry := range index.Mods {
				curModDates, found := curDates[entry.Modid]
				if !found || err != nil {
					curModDates = make([]time.Time, len(entry.PacksLastModified)+1)
				}

				if !curModDates[0].Equal(entry.LastModified.AsTime()) {
					key := "mod-" + entry.Modid
					modEntries[key] = modPackInfo{
						mod: entry,
						idx: -1,
					}
					packs = append(packs, &downloader.QueueItem{
						Key:      key,
						Filepath: filepath.Join(tmpFolder, "modsync-"+key),
						Mirrors:  []string{api.SyncEndpoint + "/m." + entry.Modid},
						Checksum: nil,
						Filesize: 0,
					})
				}

				for idx, modified := range entry.PacksLastModified {
					if len(curModDates) <= idx+1 || !curModDates[idx+1].Equal(modified.AsTime()) {
						key := fmt.Sprintf("pack-%s-%03d", entry.Modid, idx)
						modEntries[key] = modPackInfo{
							mod: entry,
							idx: idx,
						}
						packs = append(packs, &downloader.QueueItem{
							Key:      key,
							Filepath: filepath.Join(tmpFolder, "modsync-"+key),
							Mirrors:  []string{fmt.Sprintf("%s/m.%s.%03d", api.SyncEndpoint, entry.Modid, idx)},
							Checksum: nil,
							Filesize: 0,
						})
					}
				}
			}

			queue, err := downloader.NewQueue(ctx, packs)
			if err != nil {
				return eris.Wrap(err, "failed to initialise downloader")
			}

			// We're downloading a bunch of small files; it's fine to download many in parallel even on slow connections.
			queue.MaxParallel = 5
			go queue.Run(ctx)

			done := 0
			for queue.NextResult() {
				dlItem := queue.Result()
				entry := modEntries[dlItem.Key]
				api.SetProgress(ctx, float32(done)/float32(len(packs)), fmt.Sprintf("Processing mod %s (%d of %d)", entry.mod.Modid, done, len(packs)))

				encoded, err := os.ReadFile(dlItem.Filepath)
				if err != nil {
					return eris.Wrapf(err, "failed to open downloaded file %s", dlItem.Filepath)
				}

				if strings.HasPrefix(dlItem.Key, "mod-") {
					var modMeta common.ModMeta
					err = proto.Unmarshal(encoded, &modMeta)
					if err != nil {
						return eris.Wrapf(err, "failed to parse mod meta %s", dlItem.Key)
					}

					err = params.AddMod(&modMeta)
					if err != nil {
						return eris.Wrapf(err, "failed to save mod metadata for %s", entry.mod.Modid)
					}

					if _, ok := curDates[entry.mod.Modid]; !ok {
						curDates[entry.mod.Modid] = make([]time.Time, 1)
					}

					curDates[entry.mod.Modid][0] = entry.mod.LastModified.AsTime()
				} else {
					var releasePack common.ReleasePack
					err = proto.Unmarshal(encoded, &releasePack)
					if err != nil {
						return eris.Wrapf(err, "failed to parse mod release pack %s", dlItem.Key)
					}

					if releasePack.Modid != entry.mod.Modid || releasePack.Packnum != uint32(entry.idx) {
						return eris.Errorf("received wrong pack! expected %d for %s but got %d for %s", entry.idx, entry.mod.Modid, releasePack.Packnum, releasePack.Modid)
					}

					for _, rel := range releasePack.Releases {
						err := params.AddRelease(rel)
						if err != nil {
							return eris.Wrapf(err, "failed to import release %s for %s", rel.Version, entry.mod.Modid)
						}
					}

					modDates := curDates[entry.mod.Modid]
					for len(modDates) <= entry.idx+1 {
						modDates = append(modDates, time.Time{})
					}

					modDates[entry.idx+1] = entry.mod.PacksLastModified[entry.idx].AsTime()
					curDates[entry.mod.Modid] = modDates
				}

				done++
			}

			err = queue.Error()
			if err != nil {
				return eris.Wrap(err, "failed to download modsync files")
			}

			api.Log(ctx, api.LogInfo, "Looking for removed mods")
			releases, err := storage.RemoteMods.GetMods(ctx)
			if err != nil {
				return eris.Wrap(err, "failed to retrieve mod IDs")
			}

			seen := make(map[string]bool)
			for _, entry := range index.Mods {
				seen[entry.Modid] = true

				localVersions, err := storage.RemoteMods.GetVersionsForMod(ctx, entry.Modid)
				if err == nil {
					versionHash, err := calcVersionsChecksum(localVersions)
					if err != nil {
						return err
					}

					versionMismatch := !bytes.Equal(versionHash, entry.VersionChecksum)
					if versionMismatch {
						api.Log(ctx, api.LogInfo, "Version mismatch detected, clearing releases for %s.", entry.Modid)

						err = params.RemoveModReleases(entry.Modid)
						if err != nil {
							return err
						}

						delete(curDates, entry.Modid)
						resyncNeeded = true
					}
				}
			}

			for _, rel := range releases {
				if !seen[rel.Modid] {
					api.Log(ctx, api.LogInfo, "Removing %s", rel.Modid)
					err = params.RemoveMod(rel.Modid)
					if err != nil {
						return eris.Wrapf(err, "failed to remove mod %s", rel.Modid)
					}
				}
			}

			api.SetProgress(ctx, 1, "Finishing")

			err = storage.UpdateRemoteModsLastModifiedDates(ctx, curDates)
			if err == nil {
				api.Log(ctx, api.LogInfo, "Done")
			}

			return err
		})
		if err != nil {
			return err
		}

		if resyncNeeded {
			api.Log(ctx, api.LogInfo, "Found inconsistencies; will sync again.")
		}
	}

	return nil
}

func FetchModChecksums(ctx context.Context, modVersions map[string]string) (map[string]*common.ChecksumPack, error) {
	tempFolder, err := os.MkdirTemp("", "knossos-chk-dl")
	if err != nil {
		return nil, eris.Wrap(err, "failed to create temp folder for checksums")
	}
	defer os.RemoveAll(tempFolder)

	queueItems := make([]*downloader.QueueItem, 0, len(modVersions))
	for modID, version := range modVersions {
		queueItems = append(queueItems, &downloader.QueueItem{
			Key:      modID,
			Filepath: filepath.Join(tempFolder, fmt.Sprintf("c.%s.%s", modID, version)),
			Mirrors:  []string{fmt.Sprintf("%s/c.%s.%s", api.SyncEndpoint, modID, version)},
		})
	}

	queue, err := downloader.NewQueue(ctx, queueItems)
	if err != nil {
		return nil, eris.Wrap(err, "failed to init download queue")
	}

	err = queue.Run(ctx)
	if err != nil {
		return nil, eris.Wrap(err, "failed to download checksums")
	}

	result := make(map[string]*common.ChecksumPack)
	for _, item := range queueItems {
		encoded, err := os.ReadFile(item.Filepath)
		if err != nil {
			return nil, eris.Wrapf(err, "failed to read downloaded checksum file %s", item.Filepath)
		}

		data := new(common.ChecksumPack)
		err = proto.Unmarshal(encoded, data)
		if err != nil {
			return nil, eris.Wrapf(err, "failed to parse checksum file %s", item.Filepath)
		}

		result[item.Key] = data
	}

	return result, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	"path/filepath"
	"runtime"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/kubelet/qos"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/master/ports"
	kruntime "k8s.io/kubernetes/pkg/runtime"
)

const (
	defaultRootDir = "/var/lib/kubelet"

	// When these values are updated, also update test/e2e/framework/util.go
	defaultPodInfraContainerImageName    = "gcr.io/google_containers/pause"
	defaultPodInfraContainerImageVersion = "3.0"
	defaultPodInfraContainerImage        = defaultPodInfraContainerImageName +
		"-" + runtime.GOARCH + ":" +
		defaultPodInfraContainerImageVersion

	// From pkg/kubelet/rkt/rkt.go to avoid circular import
	defaultRktAPIServiceEndpoint = "localhost:15441"

	AutoDetectCloudProvider = "auto-detect"

	defaultIPTablesMasqueradeBit = 14
	defaultIPTablesDropBit       = 15
)

var zeroDuration = unversioned.Duration{}

func addDefaultingFuncs(scheme *kruntime.Scheme) error {
	return scheme.AddDefaultingFuncs(
		SetDefaults_KubeProxyConfiguration,
		SetDefaults_KubeSchedulerConfiguration,
		SetDefaults_LeaderElectionConfiguration,
		SetDefaults_KubeletConfiguration,
	)
}

func SetDefaults_KubeProxyConfiguration(obj *KubeProxyConfiguration) {
	if obj.BindAddress == "" {
		obj.BindAddress = "0.0.0.0"
	}
	if obj.HealthzPort == 0 {
		obj.HealthzPort = 10249
	}
	if obj.HealthzBindAddress == "" {
		obj.HealthzBindAddress = "127.0.0.1"
	}
	if obj.OOMScoreAdj == nil {
		temp := int32(qos.KubeProxyOOMScoreAdj)
		obj.OOMScoreAdj = &temp
	}
	if obj.ResourceContainer == "" {
		obj.ResourceContainer = "/kube-proxy"
	}
	if obj.IPTablesSyncPeriod.Duration == 0 {
		obj.IPTablesSyncPeriod = unversioned.Duration{Duration: 30 * time.Second}
	}
	zero := unversioned.Duration{}
	if obj.UDPIdleTimeout == zero {
		obj.UDPIdleTimeout = unversioned.Duration{Duration: 250 * time.Millisecond}
	}
	// If ConntrackMax is set, respect it.
	if obj.ConntrackMax == 0 {
		// If ConntrackMax is *not* set, use per-core scaling.
		if obj.ConntrackMaxPerCore == 0 {
			obj.ConntrackMaxPerCore = 32 * 1024
		}
	}
	if obj.IPTablesMasqueradeBit == nil {
		temp := int32(14)
		obj.IPTablesMasqueradeBit = &temp
	}
	if obj.ConntrackTCPEstablishedTimeout == zero {
		obj.ConntrackTCPEstablishedTimeout = unversioned.Duration{Duration: 24 * time.Hour} // 1 day (1/5 default)
	}
}

func SetDefaults_KubeSchedulerConfiguration(obj *KubeSchedulerConfiguration) {
	if obj.Port == 0 {
		obj.Port = ports.SchedulerPort
	}
	if obj.Address == "" {
		obj.Address = "0.0.0.0"
	}
	if obj.AlgorithmProvider == "" {
		obj.AlgorithmProvider = "DefaultProvider"
	}
	if obj.ContentType == "" {
		obj.ContentType = "application/vnd.kubernetes.protobuf"
	}
	if obj.KubeAPIQPS == 0 {
		obj.KubeAPIQPS = 50.0
	}
	if obj.KubeAPIBurst == 0 {
		obj.KubeAPIBurst = 100
	}
	if obj.SchedulerName == "" {
		obj.SchedulerName = api.DefaultSchedulerName
	}
	if obj.HardPodAffinitySymmetricWeight == 0 {
		obj.HardPodAffinitySymmetricWeight = api.DefaultHardPodAffinitySymmetricWeight
	}
	if obj.FailureDomains == "" {
		obj.FailureDomains = api.DefaultFailureDomains
	}
}

func SetDefaults_LeaderElectionConfiguration(obj *LeaderElectionConfiguration) {
	zero := unversioned.Duration{}
	if obj.LeaseDuration == zero {
		obj.LeaseDuration = unversioned.Duration{Duration: 15 * time.Second}
	}
	if obj.RenewDeadline == zero {
		obj.RenewDeadline = unversioned.Duration{Duration: 10 * time.Second}
	}
	if obj.RetryPeriod == zero {
		obj.RetryPeriod = unversioned.Duration{Duration: 2 * time.Second}
	}
}

func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) {
	if obj.Address == "" {
		obj.Address = "0.0.0.0"
	}
	if obj.CloudProvider == "" {
		obj.CloudProvider = AutoDetectCloudProvider
	}
	if obj.CAdvisorPort == 0 {
		obj.CAdvisorPort = 4194
	}
	if obj.VolumeStatsAggPeriod == zeroDuration {
		obj.VolumeStatsAggPeriod = unversioned.Duration{Duration: time.Minute}
	}
	if obj.CertDirectory == "" {
		obj.CertDirectory = "/var/run/kubernetes"
	}
	if obj.ConfigureCBR0 == nil {
		obj.ConfigureCBR0 = boolVar(false)
	}
	if obj.CgroupsPerQOS == nil {
		obj.CgroupsPerQOS = boolVar(false)
	}
	if obj.ContainerRuntime == "" {
		obj.ContainerRuntime = "docker"
	}
	if obj.RuntimeRequestTimeout == zeroDuration {
		obj.RuntimeRequestTimeout = unversioned.Duration{Duration: 2 * time.Minute}
	}
	if obj.CPUCFSQuota == nil {
		obj.CPUCFSQuota = boolVar(true)
	}
	if obj.DockerExecHandlerName == "" {
		obj.DockerExecHandlerName = "native"
	}
	if obj.DockerEndpoint == "" {
		obj.DockerEndpoint = "unix:///var/run/docker.sock"
	}
	if obj.EventBurst == 0 {
		obj.EventBurst = 10
	}
	if obj.EventRecordQPS == nil {
		temp := int32(5)
		obj.EventRecordQPS = &temp
	}
	if obj.EnableControllerAttachDetach == nil {
		obj.EnableControllerAttachDetach = boolVar(true)
	}
	if obj.EnableDebuggingHandlers == nil {
		obj.EnableDebuggingHandlers = boolVar(true)
	}
	if obj.EnableServer == nil {
		obj.EnableServer = boolVar(true)
	}
	if obj.FileCheckFrequency == zeroDuration {
		obj.FileCheckFrequency = unversioned.Duration{Duration: 20 * time.Second}
	}
	if obj.HealthzBindAddress == "" {
		obj.HealthzBindAddress = "127.0.0.1"
	}
	if obj.HealthzPort == 0 {
		obj.HealthzPort = 10248
	}
	if obj.HostNetworkSources == nil {
		obj.HostNetworkSources = []string{kubetypes.AllSource}
	}
	if obj.HostPIDSources == nil {
		obj.HostPIDSources = []string{kubetypes.AllSource}
	}
	if obj.HostIPCSources == nil {
		obj.HostIPCSources = []string{kubetypes.AllSource}
	}
	if obj.HTTPCheckFrequency == zeroDuration {
		obj.HTTPCheckFrequency = unversioned.Duration{Duration: 20 * time.Second}
	}
	if obj.ImageMinimumGCAge == zeroDuration {
		obj.ImageMinimumGCAge = unversioned.Duration{Duration: 2 * time.Minute}
	}
	if obj.ImageGCHighThresholdPercent == nil {
		temp := int32(90)
		obj.ImageGCHighThresholdPercent = &temp
	}
	if obj.ImageGCLowThresholdPercent == nil {
		temp := int32(80)
		obj.ImageGCLowThresholdPercent = &temp
	}
	if obj.LowDiskSpaceThresholdMB == 0 {
		obj.LowDiskSpaceThresholdMB = 256
	}
	if obj.MasterServiceNamespace == "" {
		obj.MasterServiceNamespace = api.NamespaceDefault
	}
	if obj.MaxContainerCount == nil {
		temp := int32(-1)
		obj.MaxContainerCount = &temp
	}
	if obj.MaxPerPodContainerCount == 0 {
		obj.MaxPerPodContainerCount = 1
	}
	if obj.MaxOpenFiles == 0 {
		obj.MaxOpenFiles = 1000000
	}
	if obj.MaxPods == 0 {
		obj.MaxPods = 110
	}
	if obj.MinimumGCAge == zeroDuration {
		obj.MinimumGCAge = unversioned.Duration{Duration: 0}
	}
	if obj.NonMasqueradeCIDR == "" {
		obj.NonMasqueradeCIDR = "10.0.0.0/8"
	}
	if obj.VolumePluginDir == "" {
		obj.VolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
	}
	if obj.NodeStatusUpdateFrequency == zeroDuration {
		obj.NodeStatusUpdateFrequency = unversioned.Duration{Duration: 10 * time.Second}
	}
	if obj.OOMScoreAdj == nil {
		temp := int32(qos.KubeletOOMScoreAdj)
		obj.OOMScoreAdj = &temp
	}
	if obj.PodInfraContainerImage == "" {
		obj.PodInfraContainerImage = defaultPodInfraContainerImage
	}
	if obj.Port == 0 {
		obj.Port = ports.KubeletPort
	}
	if obj.ReadOnlyPort == 0 {
		obj.ReadOnlyPort = ports.KubeletReadOnlyPort
	}
	if obj.RegisterNode == nil {
		obj.RegisterNode = boolVar(true)
	}
	if obj.RegisterSchedulable == nil {
		obj.RegisterSchedulable = boolVar(true)
	}
	if obj.RegistryBurst == 0 {
		obj.RegistryBurst = 10
	}
	if obj.RegistryPullQPS == nil {
		temp := int32(5)
		obj.RegistryPullQPS = &temp
	}
	if obj.ResolverConfig == "" {
		obj.ResolverConfig = kubetypes.ResolvConfDefault
	}
	if obj.RktAPIEndpoint == "" {
		obj.RktAPIEndpoint = defaultRktAPIServiceEndpoint
	}
	if obj.RootDirectory == "" {
		obj.RootDirectory = defaultRootDir
	}
	if obj.SerializeImagePulls == nil {
		obj.SerializeImagePulls = boolVar(true)
	}
	if obj.SeccompProfileRoot == "" {
		filepath.Join(defaultRootDir, "seccomp")
	}
	if obj.StreamingConnectionIdleTimeout == zeroDuration {
		obj.StreamingConnectionIdleTimeout = unversioned.Duration{Duration: 4 * time.Hour}
	}
	if obj.SyncFrequency == zeroDuration {
		obj.SyncFrequency = unversioned.Duration{Duration: 1 * time.Minute}
	}
	if obj.ReconcileCIDR == nil {
		obj.ReconcileCIDR = boolVar(true)
	}
	if obj.ContentType == "" {
		obj.ContentType = "application/vnd.kubernetes.protobuf"
	}
	if obj.KubeAPIQPS == nil {
		temp := int32(5)
		obj.KubeAPIQPS = &temp
	}
	if obj.KubeAPIBurst == 0 {
		obj.KubeAPIBurst = 10
	}
	if obj.OutOfDiskTransitionFrequency == zeroDuration {
		obj.OutOfDiskTransitionFrequency = unversioned.Duration{Duration: 5 * time.Minute}
	}
	if string(obj.HairpinMode) == "" {
		obj.HairpinMode = PromiscuousBridge
	}
	if obj.EvictionHard == nil {
		temp := "memory.available<100Mi"
		obj.EvictionHard = &temp
	}
	if obj.EvictionPressureTransitionPeriod == zeroDuration {
		obj.EvictionPressureTransitionPeriod = unversioned.Duration{Duration: 5 * time.Minute}
	}
	if obj.SystemReserved == nil {
		obj.SystemReserved = make(map[string]string)
	}
	if obj.KubeReserved == nil {
		obj.KubeReserved = make(map[string]string)
	}
	if obj.MakeIPTablesUtilChains == nil {
		obj.MakeIPTablesUtilChains = boolVar(true)
	}
	if obj.IPTablesMasqueradeBit == nil {
		temp := int32(defaultIPTablesMasqueradeBit)
		obj.IPTablesMasqueradeBit = &temp
	}
	if obj.IPTablesDropBit == nil {
		temp := int32(defaultIPTablesDropBit)
		obj.IPTablesDropBit = &temp
	}
}

func boolVar(b bool) *bool {
	return &b
}

var (
	defaultCfg = KubeletConfiguration{}
)
<commit_msg>UPSTREAM: 36375: Fix default Seccomp profile directory<commit_after>/*
Copyright 2015 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
	"path/filepath"
	"runtime"
	"time"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/kubelet/qos"
	kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
	"k8s.io/kubernetes/pkg/master/ports"
	kruntime "k8s.io/kubernetes/pkg/runtime"
)

const (
	defaultRootDir = "/var/lib/kubelet"

	// When these values are updated, also update test/e2e/framework/util.go
	defaultPodInfraContainerImageName    = "gcr.io/google_containers/pause"
	defaultPodInfraContainerImageVersion = "3.0"
	defaultPodInfraContainerImage        = defaultPodInfraContainerImageName +
		"-" + runtime.GOARCH + ":" +
		defaultPodInfraContainerImageVersion

	// From pkg/kubelet/rkt/rkt.go to avoid circular import
	defaultRktAPIServiceEndpoint = "localhost:15441"

	AutoDetectCloudProvider = "auto-detect"

	defaultIPTablesMasqueradeBit = 14
	defaultIPTablesDropBit       = 15
)

var zeroDuration = unversioned.Duration{}

func addDefaultingFuncs(scheme *kruntime.Scheme) error {
	return scheme.AddDefaultingFuncs(
		SetDefaults_KubeProxyConfiguration,
		SetDefaults_KubeSchedulerConfiguration,
		SetDefaults_LeaderElectionConfiguration,
		SetDefaults_KubeletConfiguration,
	)
}

func SetDefaults_KubeProxyConfiguration(obj *KubeProxyConfiguration) {
	if obj.BindAddress == "" {
		obj.BindAddress = "0.0.0.0"
	}
	if obj.HealthzPort == 0 {
		obj.HealthzPort = 10249
	}
	if obj.HealthzBindAddress == "" {
		obj.HealthzBindAddress = "127.0.0.1"
	}
	if obj.OOMScoreAdj == nil {
		temp := int32(qos.KubeProxyOOMScoreAdj)
		obj.OOMScoreAdj = &temp
	}
	if obj.ResourceContainer == "" {
		obj.ResourceContainer = "/kube-proxy"
	}
	if obj.IPTablesSyncPeriod.Duration == 0 {
		obj.IPTablesSyncPeriod = unversioned.Duration{Duration: 30 * time.Second}
	}
	zero := unversioned.Duration{}
	if obj.UDPIdleTimeout == zero {
		obj.UDPIdleTimeout = unversioned.Duration{Duration: 250 * time.Millisecond}
	}
	// If ConntrackMax is set, respect it.
	if obj.ConntrackMax == 0 {
		// If ConntrackMax is *not* set, use per-core scaling.
		if obj.ConntrackMaxPerCore == 0 {
			obj.ConntrackMaxPerCore = 32 * 1024
		}
	}
	if obj.IPTablesMasqueradeBit == nil {
		temp := int32(14)
		obj.IPTablesMasqueradeBit = &temp
	}
	if obj.ConntrackTCPEstablishedTimeout == zero {
		obj.ConntrackTCPEstablishedTimeout = unversioned.Duration{Duration: 24 * time.Hour} // 1 day (1/5 default)
	}
}

func SetDefaults_KubeSchedulerConfiguration(obj *KubeSchedulerConfiguration) {
	if obj.Port == 0 {
		obj.Port = ports.SchedulerPort
	}
	if obj.Address == "" {
		obj.Address = "0.0.0.0"
	}
	if obj.AlgorithmProvider == "" {
		obj.AlgorithmProvider = "DefaultProvider"
	}
	if obj.ContentType == "" {
		obj.ContentType = "application/vnd.kubernetes.protobuf"
	}
	if obj.KubeAPIQPS == 0 {
		obj.KubeAPIQPS = 50.0
	}
	if obj.KubeAPIBurst == 0 {
		obj.KubeAPIBurst = 100
	}
	if obj.SchedulerName == "" {
		obj.SchedulerName = api.DefaultSchedulerName
	}
	if obj.HardPodAffinitySymmetricWeight == 0 {
		obj.HardPodAffinitySymmetricWeight = api.DefaultHardPodAffinitySymmetricWeight
	}
	if obj.FailureDomains == "" {
		obj.FailureDomains = api.DefaultFailureDomains
	}
}

func SetDefaults_LeaderElectionConfiguration(obj *LeaderElectionConfiguration) {
	zero := unversioned.Duration{}
	if obj.LeaseDuration == zero {
		obj.LeaseDuration = unversioned.Duration{Duration: 15 * time.Second}
	}
	if obj.RenewDeadline == zero {
		obj.RenewDeadline = unversioned.Duration{Duration: 10 * time.Second}
	}
	if obj.RetryPeriod == zero {
		obj.RetryPeriod = unversioned.Duration{Duration: 2 * time.Second}
	}
}

func SetDefaults_KubeletConfiguration(obj *KubeletConfiguration) {
	if obj.Address == "" {
		obj.Address = "0.0.0.0"
	}
	if obj.CloudProvider == "" {
		obj.CloudProvider = AutoDetectCloudProvider
	}
	if obj.CAdvisorPort == 0 {
		obj.CAdvisorPort = 4194
	}
	if obj.VolumeStatsAggPeriod == zeroDuration {
		obj.VolumeStatsAggPeriod = unversioned.Duration{Duration: time.Minute}
	}
	if obj.CertDirectory == "" {
		obj.CertDirectory = "/var/run/kubernetes"
	}
	if obj.ConfigureCBR0 == nil {
		obj.ConfigureCBR0 = boolVar(false)
	}
	if obj.CgroupsPerQOS == nil {
		obj.CgroupsPerQOS = boolVar(false)
	}
	if obj.ContainerRuntime == "" {
		obj.ContainerRuntime = "docker"
	}
	if obj.RuntimeRequestTimeout == zeroDuration {
		obj.RuntimeRequestTimeout = unversioned.Duration{Duration: 2 * time.Minute}
	}
	if obj.CPUCFSQuota == nil {
		obj.CPUCFSQuota = boolVar(true)
	}
	if obj.DockerExecHandlerName == "" {
		obj.DockerExecHandlerName = "native"
	}
	if obj.DockerEndpoint == "" {
		obj.DockerEndpoint = "unix:///var/run/docker.sock"
	}
	if obj.EventBurst == 0 {
		obj.EventBurst = 10
	}
	if obj.EventRecordQPS == nil {
		temp := int32(5)
		obj.EventRecordQPS = &temp
	}
	if obj.EnableControllerAttachDetach == nil {
		obj.EnableControllerAttachDetach = boolVar(true)
	}
	if obj.EnableDebuggingHandlers == nil {
		obj.EnableDebuggingHandlers = boolVar(true)
	}
	if obj.EnableServer == nil {
		obj.EnableServer = boolVar(true)
	}
	if obj.FileCheckFrequency == zeroDuration {
		obj.FileCheckFrequency = unversioned.Duration{Duration: 20 * time.Second}
	}
	if obj.HealthzBindAddress == "" {
		obj.HealthzBindAddress = "127.0.0.1"
	}
	if obj.HealthzPort == 0 {
		obj.HealthzPort = 10248
	}
	if obj.HostNetworkSources == nil {
		obj.HostNetworkSources = []string{kubetypes.AllSource}
	}
	if obj.HostPIDSources == nil {
		obj.HostPIDSources = []string{kubetypes.AllSource}
	}
	if obj.HostIPCSources == nil {
		obj.HostIPCSources = []string{kubetypes.AllSource}
	}
	if obj.HTTPCheckFrequency == zeroDuration {
		obj.HTTPCheckFrequency = unversioned.Duration{Duration: 20 * time.Second}
	}
	if obj.ImageMinimumGCAge == zeroDuration {
		obj.ImageMinimumGCAge = unversioned.Duration{Duration: 2 * time.Minute}
	}
	if obj.ImageGCHighThresholdPercent == nil {
		temp := int32(90)
		obj.ImageGCHighThresholdPercent = &temp
	}
	if obj.ImageGCLowThresholdPercent == nil {
		temp := int32(80)
		obj.ImageGCLowThresholdPercent = &temp
	}
	if obj.LowDiskSpaceThresholdMB == 0 {
		obj.LowDiskSpaceThresholdMB = 256
	}
	if obj.MasterServiceNamespace == "" {
		obj.MasterServiceNamespace = api.NamespaceDefault
	}
	if obj.MaxContainerCount == nil {
		temp := int32(-1)
		obj.MaxContainerCount = &temp
	}
	if obj.MaxPerPodContainerCount == 0 {
		obj.MaxPerPodContainerCount = 1
	}
	if obj.MaxOpenFiles == 0 {
		obj.MaxOpenFiles = 1000000
	}
	if obj.MaxPods == 0 {
		obj.MaxPods = 110
	}
	if obj.MinimumGCAge == zeroDuration {
		obj.MinimumGCAge = unversioned.Duration{Duration: 0}
	}
	if obj.NonMasqueradeCIDR == "" {
		obj.NonMasqueradeCIDR = "10.0.0.0/8"
	}
	if obj.VolumePluginDir == "" {
		obj.VolumePluginDir = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
	}
	if obj.NodeStatusUpdateFrequency == zeroDuration {
		obj.NodeStatusUpdateFrequency = unversioned.Duration{Duration: 10 * time.Second}
	}
	if obj.OOMScoreAdj == nil {
		temp := int32(qos.KubeletOOMScoreAdj)
		obj.OOMScoreAdj = &temp
	}
	if obj.PodInfraContainerImage == "" {
		obj.PodInfraContainerImage = defaultPodInfraContainerImage
	}
	if obj.Port == 0 {
		obj.Port = ports.KubeletPort
	}
	if obj.ReadOnlyPort == 0 {
		obj.ReadOnlyPort = ports.KubeletReadOnlyPort
	}
	if obj.RegisterNode == nil {
		obj.RegisterNode = boolVar(true)
	}
	if obj.RegisterSchedulable == nil {
		obj.RegisterSchedulable = boolVar(true)
	}
	if obj.RegistryBurst == 0 {
		obj.RegistryBurst = 10
	}
	if obj.RegistryPullQPS == nil {
		temp := int32(5)
		obj.RegistryPullQPS = &temp
	}
	if obj.ResolverConfig == "" {
		obj.ResolverConfig = kubetypes.ResolvConfDefault
	}
	if obj.RktAPIEndpoint == "" {
		obj.RktAPIEndpoint = defaultRktAPIServiceEndpoint
	}
	if obj.RootDirectory == "" {
		obj.RootDirectory = defaultRootDir
	}
	if obj.SerializeImagePulls == nil {
		obj.SerializeImagePulls = boolVar(true)
	}
	if obj.SeccompProfileRoot == "" {
		obj.SeccompProfileRoot = filepath.Join(defaultRootDir, "seccomp")
	}
	if obj.StreamingConnectionIdleTimeout == zeroDuration {
		obj.StreamingConnectionIdleTimeout = unversioned.Duration{Duration: 4 * time.Hour}
	}
	if obj.SyncFrequency == zeroDuration {
		obj.SyncFrequency = unversioned.Duration{Duration: 1 * time.Minute}
	}
	if obj.ReconcileCIDR == nil {
		obj.ReconcileCIDR = boolVar(true)
	}
	if obj.ContentType == "" {
		obj.ContentType = "application/vnd.kubernetes.protobuf"
	}
	if obj.KubeAPIQPS == nil {
		temp := int32(5)
		obj.KubeAPIQPS = &temp
	}
	if obj.KubeAPIBurst == 0 {
		obj.KubeAPIBurst = 10
	}
	if obj.OutOfDiskTransitionFrequency == zeroDuration {
		obj.OutOfDiskTransitionFrequency = unversioned.Duration{Duration: 5 * time.Minute}
	}
	if string(obj.HairpinMode) == "" {
		obj.HairpinMode = PromiscuousBridge
	}
	if obj.EvictionHard == nil {
		temp := "memory.available<100Mi"
		obj.EvictionHard = &temp
	}
	if obj.EvictionPressureTransitionPeriod == zeroDuration {
		obj.EvictionPressureTransitionPeriod = unversioned.Duration{Duration: 5 * time.Minute}
	}
	if obj.SystemReserved == nil {
		obj.SystemReserved = make(map[string]string)
	}
	if obj.KubeReserved == nil {
		obj.KubeReserved = make(map[string]string)
	}
	if obj.MakeIPTablesUtilChains == nil {
		obj.MakeIPTablesUtilChains = boolVar(true)
	}
	if obj.IPTablesMasqueradeBit == nil {
		temp := int32(defaultIPTablesMasqueradeBit)
		obj.IPTablesMasqueradeBit = &temp
	}
	if obj.IPTablesDropBit == nil {
		temp := int32(defaultIPTablesDropBit)
		obj.IPTablesDropBit = &temp
	}
}

func boolVar(b bool) *bool {
	return &b
}

var (
	defaultCfg = KubeletConfiguration{}
)
<|endoftext|>