<commit_before>/*
Copyright 2016 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.
*/

// Tot vends (rations) incrementing numbers for use in builds.
// https://en.wikipedia.org/wiki/Rum_ration
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/pjutil"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
	"k8s.io/test-infra/prow/pod-utils/gcs"
)

type options struct {
	port        int
	storagePath string

	useFallback bool
	fallbackURI string

	configPath     string
	jobConfigPath  string
	fallbackBucket string
}

func gatherOptions() options {
	o := options{}
	flag.IntVar(&o.port, "port", 8888, "Port to listen on.")
	flag.StringVar(&o.storagePath, "storage", "tot.json", "Where to store the results.")

	flag.BoolVar(&o.useFallback, "fallback", false, "Fallback to GCS bucket for missing builds.")
	flag.StringVar(&o.fallbackURI, "fallback-url-template",
		"https://storage.googleapis.com/kubernetes-jenkins/logs/%s/latest-build.txt",
		"URL template to fallback to for jobs that lack a last vended build number.",
	)

	flag.StringVar(&o.configPath, "config-path", "", "Path to prow config.")
	flag.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
	flag.StringVar(&o.fallbackBucket, "fallback-bucket", "",
		"Fallback to top-level bucket for jobs that lack a last vended build number. The bucket layout is expected to follow https://github.com/kubernetes/test-infra/tree/master/gubernator#gcs-bucket-layout",
	)

	flag.Parse()
	return o
}

func (o *options) Validate() error {
	if o.configPath != "" && o.fallbackBucket == "" {
		return errors.New("you need to provide a bucket to fallback to when the prow config is specified")
	}
	if o.configPath == "" && o.fallbackBucket != "" {
		return errors.New("you need to provide the prow config when a fallback bucket is specified")
	}
	return nil
}

type store struct {
	Number       map[string]int // job name -> last vended build number
	mutex        sync.Mutex
	storagePath  string
	fallbackFunc func(string) int
}

func newStore(storagePath string) (*store, error) {
	s := &store{
		Number:      make(map[string]int),
		storagePath: storagePath,
	}
	buf, err := ioutil.ReadFile(storagePath)
	if err == nil {
		err = json.Unmarshal(buf, s)
		if err != nil {
			return nil, err
		}
	} else if !os.IsNotExist(err) {
		return nil, err
	}
	return s, nil
}

func (s *store) save() error {
	buf, err := json.Marshal(s)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(s.storagePath+".tmp", buf, 0644)
	if err != nil {
		return err
	}
	return os.Rename(s.storagePath+".tmp", s.storagePath)
}

func (s *store) vend(jobName string) int {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	n, ok := s.Number[jobName]
	if !ok && s.fallbackFunc != nil {
		n = s.fallbackFunc(jobName)
	}
	n++

	s.Number[jobName] = n

	err := s.save()
	if err != nil {
		logrus.Error(err)
	}

	return n
}

func (s *store) peek(jobName string) int {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.Number[jobName]
}

func (s *store) set(jobName string, n int) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.Number[jobName] = n

	err := s.save()
	if err != nil {
		logrus.Error(err)
	}
}

func (s *store) handle(w http.ResponseWriter, r *http.Request) {
	jobName := r.URL.Path[len("/vend/"):]
	switch r.Method {
	case "GET":
		n := s.vend(jobName)
		logrus.Infof("Vending %s number %d to %s.", jobName, n, r.RemoteAddr)
		fmt.Fprintf(w, "%d", n)
	case "HEAD":
		n := s.peek(jobName)
		logrus.Infof("Peeking %s number %d to %s.", jobName, n, r.RemoteAddr)
		fmt.Fprintf(w, "%d", n)
	case "POST":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			logrus.WithError(err).Error("Unable to read body.")
			return
		}
		n, err := strconv.Atoi(string(body))
		if err != nil {
			logrus.WithError(err).Error("Unable to parse number.")
			return
		}
		logrus.Infof("Setting %s to %d from %s.", jobName, n, r.RemoteAddr)
		s.set(jobName, n)
	}
}

type fallbackHandler struct {
	template string
	// in case a config agent is provided, tot will
	// determine the GCS path that it needs to use
	// based on the configured jobs in prow and
	// bucket.
	configAgent *config.Agent
	bucket      string
}

func (f fallbackHandler) get(jobName string) int {
	url := f.getURL(jobName)

	var body []byte

	for i := 0; i < 10; i++ {
		resp, err := http.Get(url)
		if err == nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				body, err = ioutil.ReadAll(resp.Body)
				if err == nil {
					break
				} else {
					logrus.WithError(err).Error("Failed to read response body.")
				}
			}
		} else {
			logrus.WithError(err).Errorf("Failed to GET %s.", url)
		}
		time.Sleep(2 * time.Second)
	}

	n, err := strconv.Atoi(strings.TrimSpace(string(body)))
	if err != nil {
		return 0
	}

	return n
}

func (f fallbackHandler) getURL(jobName string) string {
	if f.configAgent == nil {
		return fmt.Sprintf(f.template, jobName)
	}

	var spec *downwardapi.JobSpec
	cfg := f.configAgent.Config()

	for _, pre := range cfg.AllPresubmits(nil) {
		if jobName == pre.Name {
			spec = pjutil.PresubmitToJobSpec(pre)
			break
		}
	}
	if spec == nil {
		for _, post := range cfg.AllPostsubmits(nil) {
			if jobName == post.Name {
				spec = pjutil.PostsubmitToJobSpec(post)
				break
			}
		}
	}
	if spec == nil {
		for _, per := range cfg.AllPeriodics() {
			if jobName == per.Name {
				spec = pjutil.PeriodicToJobSpec(per)
				break
			}
		}
	}
	// If spec is still nil, we know nothing about the requested job.
	if spec == nil {
		logrus.Errorf("requested job is unknown to prow: %s", jobName)
		return ""
	}
	paths := gcs.LatestBuildForSpec(spec, nil)
	if len(paths) != 1 {
		logrus.Errorf("expected a single GCS path, got %v", paths)
		return ""
	}
	return fmt.Sprintf("%s/%s", strings.TrimSuffix(f.bucket, "/"), paths[0])
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.Fatalf("Invalid options: %v", err)
	}
	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "tot"}),
	)

	s, err := newStore(o.storagePath)
	if err != nil {
		logrus.WithError(err).Fatal("newStore failed")
	}

	if o.useFallback {
		var configAgent *config.Agent
		if o.configPath != "" {
			configAgent = &config.Agent{}
			if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil {
				logrus.WithError(err).Fatal("Error starting config agent.")
			}
		}

		s.fallbackFunc = fallbackHandler{
			template:    o.fallbackURI,
			configAgent: configAgent,
			bucket:      o.fallbackBucket,
		}.get
	}

	http.HandleFunc("/vend/", s.handle)

	logrus.Fatal(http.ListenAndServe(":"+strconv.Itoa(o.port), nil))
}
<commit_msg>Handle 404s in tot fallback better.<commit_after>/*
Copyright 2016 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.
*/

// Tot vends (rations) incrementing numbers for use in builds.
// https://en.wikipedia.org/wiki/Rum_ration
package main

import (
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/sirupsen/logrus"

	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/logrusutil"
	"k8s.io/test-infra/prow/pjutil"
	"k8s.io/test-infra/prow/pod-utils/downwardapi"
	"k8s.io/test-infra/prow/pod-utils/gcs"
)

type options struct {
	port        int
	storagePath string

	useFallback bool
	fallbackURI string

	configPath     string
	jobConfigPath  string
	fallbackBucket string
}

func gatherOptions() options {
	o := options{}
	flag.IntVar(&o.port, "port", 8888, "Port to listen on.")
	flag.StringVar(&o.storagePath, "storage", "tot.json", "Where to store the results.")

	flag.BoolVar(&o.useFallback, "fallback", false, "Fallback to GCS bucket for missing builds.")
	flag.StringVar(&o.fallbackURI, "fallback-url-template",
		"https://storage.googleapis.com/kubernetes-jenkins/logs/%s/latest-build.txt",
		"URL template to fallback to for jobs that lack a last vended build number.",
	)

	flag.StringVar(&o.configPath, "config-path", "", "Path to prow config.")
	flag.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
	flag.StringVar(&o.fallbackBucket, "fallback-bucket", "",
		"Fallback to top-level bucket for jobs that lack a last vended build number. The bucket layout is expected to follow https://github.com/kubernetes/test-infra/tree/master/gubernator#gcs-bucket-layout",
	)

	flag.Parse()
	return o
}

func (o *options) Validate() error {
	if o.configPath != "" && o.fallbackBucket == "" {
		return errors.New("you need to provide a bucket to fallback to when the prow config is specified")
	}
	if o.configPath == "" && o.fallbackBucket != "" {
		return errors.New("you need to provide the prow config when a fallback bucket is specified")
	}
	return nil
}

type store struct {
	Number       map[string]int // job name -> last vended build number
	mutex        sync.Mutex
	storagePath  string
	fallbackFunc func(string) int
}

func newStore(storagePath string) (*store, error) {
	s := &store{
		Number:      make(map[string]int),
		storagePath: storagePath,
	}
	buf, err := ioutil.ReadFile(storagePath)
	if err == nil {
		err = json.Unmarshal(buf, s)
		if err != nil {
			return nil, err
		}
	} else if !os.IsNotExist(err) {
		return nil, err
	}
	return s, nil
}

func (s *store) save() error {
	buf, err := json.Marshal(s)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(s.storagePath+".tmp", buf, 0644)
	if err != nil {
		return err
	}
	return os.Rename(s.storagePath+".tmp", s.storagePath)
}

func (s *store) vend(jobName string) int {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	n, ok := s.Number[jobName]
	if !ok && s.fallbackFunc != nil {
		n = s.fallbackFunc(jobName)
	}
	n++

	s.Number[jobName] = n

	err := s.save()
	if err != nil {
		logrus.Error(err)
	}

	return n
}

func (s *store) peek(jobName string) int {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	return s.Number[jobName]
}

func (s *store) set(jobName string, n int) {
	s.mutex.Lock()
	defer s.mutex.Unlock()
	s.Number[jobName] = n

	err := s.save()
	if err != nil {
		logrus.Error(err)
	}
}

func (s *store) handle(w http.ResponseWriter, r *http.Request) {
	jobName := r.URL.Path[len("/vend/"):]
	switch r.Method {
	case "GET":
		n := s.vend(jobName)
		logrus.Infof("Vending %s number %d to %s.", jobName, n, r.RemoteAddr)
		fmt.Fprintf(w, "%d", n)
	case "HEAD":
		n := s.peek(jobName)
		logrus.Infof("Peeking %s number %d to %s.", jobName, n, r.RemoteAddr)
		fmt.Fprintf(w, "%d", n)
	case "POST":
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			logrus.WithError(err).Error("Unable to read body.")
			return
		}
		n, err := strconv.Atoi(string(body))
		if err != nil {
			logrus.WithError(err).Error("Unable to parse number.")
			return
		}
		logrus.Infof("Setting %s to %d from %s.", jobName, n, r.RemoteAddr)
		s.set(jobName, n)
	}
}

type fallbackHandler struct {
	template string
	// in case a config agent is provided, tot will
	// determine the GCS path that it needs to use
	// based on the configured jobs in prow and
	// bucket.
	configAgent *config.Agent
	bucket      string
}

func (f fallbackHandler) get(jobName string) int {
	url := f.getURL(jobName)

	var body []byte

	for i := 0; i < 10; i++ {
		resp, err := http.Get(url)
		if err == nil {
			defer resp.Body.Close()
			if resp.StatusCode == http.StatusOK {
				body, err = ioutil.ReadAll(resp.Body)
				if err == nil {
					break
				} else {
					logrus.WithError(err).Error("Failed to read response body.")
				}
			} else if resp.StatusCode == http.StatusNotFound {
				break
			}
		} else {
			logrus.WithError(err).Errorf("Failed to GET %s.", url)
		}
		time.Sleep(2 * time.Second)
	}

	n, err := strconv.Atoi(strings.TrimSpace(string(body)))
	if err != nil {
		return 0
	}

	return n
}

func (f fallbackHandler) getURL(jobName string) string {
	if f.configAgent == nil {
		return fmt.Sprintf(f.template, jobName)
	}

	var spec *downwardapi.JobSpec
	cfg := f.configAgent.Config()

	for _, pre := range cfg.AllPresubmits(nil) {
		if jobName == pre.Name {
			spec = pjutil.PresubmitToJobSpec(pre)
			break
		}
	}
	if spec == nil {
		for _, post := range cfg.AllPostsubmits(nil) {
			if jobName == post.Name {
				spec = pjutil.PostsubmitToJobSpec(post)
				break
			}
		}
	}
	if spec == nil {
		for _, per := range cfg.AllPeriodics() {
			if jobName == per.Name {
				spec = pjutil.PeriodicToJobSpec(per)
				break
			}
		}
	}
	// If spec is still nil, we know nothing about the requested job.
	if spec == nil {
		logrus.Errorf("requested job is unknown to prow: %s", jobName)
		return ""
	}
	paths := gcs.LatestBuildForSpec(spec, nil)
	if len(paths) != 1 {
		logrus.Errorf("expected a single GCS path, got %v", paths)
		return ""
	}
	return fmt.Sprintf("%s/%s", strings.TrimSuffix(f.bucket, "/"), paths[0])
}

func main() {
	o := gatherOptions()
	if err := o.Validate(); err != nil {
		logrus.Fatalf("Invalid options: %v", err)
	}
	logrus.SetFormatter(
		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "tot"}),
	)

	s, err := newStore(o.storagePath)
	if err != nil {
		logrus.WithError(err).Fatal("newStore failed")
	}

	if o.useFallback {
		var configAgent *config.Agent
		if o.configPath != "" {
			configAgent = &config.Agent{}
			if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil {
				logrus.WithError(err).Fatal("Error starting config agent.")
			}
		}

		s.fallbackFunc = fallbackHandler{
			template:    o.fallbackURI,
			configAgent: configAgent,
			bucket:      o.fallbackBucket,
		}.get
	}

	http.HandleFunc("/vend/", s.handle)

	logrus.Fatal(http.ListenAndServe(":"+strconv.Itoa(o.port), nil))
}
<|endoftext|>


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

package walk

import (
	"bytes"
	"strings"
)

import (
	"github.com/lxn/win"
)

const lineErrorPresenterWindowClass = `\o/ Walk_LineErrorPresenter_Class \o/`

func init() {
	MustRegisterWindowClass(lineErrorPresenterWindowClass)

	lineErrorPresenterBackground, _ = NewSolidColorBrush(RGB(255, 128, 128))
}

var lineErrorPresenterBackground Brush

type LineErrorPresenter struct {
	WidgetBase
	composite    *Composite
	label        *Label
	curWidget    Widget
	widget2error map[Widget]error
}

func NewLineErrorPresenter(parent Container) (*LineErrorPresenter, error) {
	lep := &LineErrorPresenter{widget2error: make(map[Widget]error)}

	if err := InitWidget(
		lep,
		parent,
		lineErrorPresenterWindowClass,
		win.WS_VISIBLE,
		win.WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			lep.Dispose()
		}
	}()

	var err error

	if lep.composite, err = newCompositeWithStyle(lep, 0); err != nil {
		return nil, err
	}

	lep.composite.applyFont(lep.Font())

	l := NewGridLayout()
	l.SetMargins(Margins{2, 2, 2, 2})

	if err = lep.composite.SetLayout(l); err != nil {
		return nil, err
	}

	if lep.label, err = NewLabel(lep.composite); err != nil {
		return nil, err
	}

	l.SetRange(lep.label, Rectangle{0, 0, 1, 1})

	focusCurWidget := func(x, y int, button MouseButton) {
		widget := lep.curWidget

		if button == LeftButton && widget != nil {
			widget.SetFocus()

			if textSel, ok := widget.(textSelectable); ok {
				textSel.SetTextSelection(0, -1)
			}
		}
	}

	lep.MouseDown().Attach(focusCurWidget)
	lep.composite.MouseDown().Attach(focusCurWidget)
	lep.label.MouseDown().Attach(focusCurWidget)

	succeeded = true

	return lep, nil
}

func (*LineErrorPresenter) LayoutFlags() LayoutFlags {
	return GrowableHorz | GreedyHorz
}

func (lep *LineErrorPresenter) MinSizeHint() Size {
	if lep.label == nil {
		return Size{}
	}

	text := lep.label.Text()
	if text == "" {
		text = "gM"
	}

	s := lep.label.calculateTextSizeImpl(text)

	return Size{s.Width + 8, s.Height + 8}
}

func (lep *LineErrorPresenter) SizeHint() Size {
	return lep.MinSizeHint()
}

func (lep *LineErrorPresenter) applyFont(font *Font) {
	lep.WidgetBase.applyFont(font)

	if lep.composite == nil {
		return
	}

	lep.composite.applyFont(font)
}

func (lep *LineErrorPresenter) PresentError(err error, widget Widget) {
	if err == nil {
		delete(lep.widget2error, widget)
	} else {
		lep.widget2error[widget] = err
	}

	var found bool
	walkDescendants(ancestor(widget).AsFormBase().clientComposite, func(w Window) bool {
		if found {
			return false
		}

		wt := w.(Widget)

		if e, ok := lep.widget2error[wt]; ok {
			err, widget, found = e, wt, true
		}

		return !found
	})

	if err != nil {
		lep.curWidget = widget
	} else {
		lep.curWidget = nil
	}

	var msg string
	var background Brush

	if err != nil {
		background = lineErrorPresenterBackground

		var labelText string
		if widget != nil {
			parent := widget.Parent()
			if parent != nil {
				children := parent.Children()

				i := children.Index(widget)
				if i > 0 {
					prev := children.At(i - 1)

					if label, ok := prev.(*Label); ok {
						labelText = label.Text()
					}
				}
			}
		}

		buf := new(bytes.Buffer)
		buf.WriteString(labelText)
		if labelText != "" && !strings.HasSuffix(labelText, ":") {
			buf.WriteString(":")
		}
		if labelText != "" {
			buf.WriteString(" ")
		}
		buf.WriteString(err.Error())

		msg = buf.String()
	}

	lep.SetBackground(background)
	lep.label.SetText(msg)

	if form := ancestor(lep); form != nil && form.Handle() != lep.hWnd {
		form.SetBounds(form.Bounds())
	}
}

func (lep *LineErrorPresenter) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_SIZE, win.WM_SIZING:
		if lep.composite != nil {
			b := lep.ClientBounds()
			lep.composite.SetBounds(Rectangle{b.X + 2, b.Y + 2, b.Width - 4, b.Height - 4})
		}
	}

	return lep.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<commit_msg>LineErrorPresenter: Fix font propagation<commit_after>// Copyright 2012 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package walk

import (
	"bytes"
	"strings"
)

import (
	"github.com/lxn/win"
)

const lineErrorPresenterWindowClass = `\o/ Walk_LineErrorPresenter_Class \o/`

func init() {
	MustRegisterWindowClass(lineErrorPresenterWindowClass)

	lineErrorPresenterBackground, _ = NewSolidColorBrush(RGB(255, 128, 128))
}

var lineErrorPresenterBackground Brush

type LineErrorPresenter struct {
	WidgetBase
	composite    *Composite
	label        *Label
	curWidget    Widget
	widget2error map[Widget]error
}

func NewLineErrorPresenter(parent Container) (*LineErrorPresenter, error) {
	lep := &LineErrorPresenter{widget2error: make(map[Widget]error)}

	if err := InitWidget(
		lep,
		parent,
		lineErrorPresenterWindowClass,
		win.WS_VISIBLE,
		win.WS_EX_CONTROLPARENT); err != nil {
		return nil, err
	}

	succeeded := false
	defer func() {
		if !succeeded {
			lep.Dispose()
		}
	}()

	var err error

	if lep.composite, err = newCompositeWithStyle(lep, 0); err != nil {
		return nil, err
	}

	l := NewGridLayout()
	l.SetMargins(Margins{2, 2, 2, 2})

	if err = lep.composite.SetLayout(l); err != nil {
		return nil, err
	}

	if lep.label, err = NewLabel(lep.composite); err != nil {
		return nil, err
	}

	l.SetRange(lep.label, Rectangle{0, 0, 1, 1})

	focusCurWidget := func(x, y int, button MouseButton) {
		widget := lep.curWidget

		if button == LeftButton && widget != nil {
			widget.SetFocus()

			if textSel, ok := widget.(textSelectable); ok {
				textSel.SetTextSelection(0, -1)
			}
		}
	}

	lep.MouseDown().Attach(focusCurWidget)
	lep.composite.MouseDown().Attach(focusCurWidget)
	lep.label.MouseDown().Attach(focusCurWidget)

	succeeded = true

	return lep, nil
}

func (*LineErrorPresenter) LayoutFlags() LayoutFlags {
	return GrowableHorz | GreedyHorz
}

func (lep *LineErrorPresenter) MinSizeHint() Size {
	if lep.label == nil {
		return Size{}
	}

	text := lep.label.Text()
	if text == "" {
		text = "gM"
	}

	s := lep.label.calculateTextSizeImpl(text)

	return Size{s.Width + 8, s.Height + 8}
}

func (lep *LineErrorPresenter) SizeHint() Size {
	return lep.MinSizeHint()
}

func (lep *LineErrorPresenter) applyFont(font *Font) {
	lep.WidgetBase.applyFont(font)

	if lep.composite == nil {
		return
	}

	// We have to call SetFont instead of applyFont here, because
	// LineErrorPresenter does not implement Container.
	lep.composite.SetFont(font)
}

func (lep *LineErrorPresenter) PresentError(err error, widget Widget) {
	if err == nil {
		delete(lep.widget2error, widget)
	} else {
		lep.widget2error[widget] = err
	}

	var found bool
	walkDescendants(ancestor(widget).AsFormBase().clientComposite, func(w Window) bool {
		if found {
			return false
		}

		wt := w.(Widget)

		if e, ok := lep.widget2error[wt]; ok {
			err, widget, found = e, wt, true
		}

		return !found
	})

	if err != nil {
		lep.curWidget = widget
	} else {
		lep.curWidget = nil
	}

	var msg string
	var background Brush

	if err != nil {
		background = lineErrorPresenterBackground

		var labelText string
		if widget != nil {
			parent := widget.Parent()
			if parent != nil {
				children := parent.Children()

				i := children.Index(widget)
				if i > 0 {
					prev := children.At(i - 1)

					if label, ok := prev.(*Label); ok {
						labelText = label.Text()
					}
				}
			}
		}

		buf := new(bytes.Buffer)
		buf.WriteString(labelText)
		if labelText != "" && !strings.HasSuffix(labelText, ":") {
			buf.WriteString(":")
		}
		if labelText != "" {
			buf.WriteString(" ")
		}
		buf.WriteString(err.Error())

		msg = buf.String()
	}

	lep.SetBackground(background)
	lep.label.SetText(msg)

	if form := ancestor(lep); form != nil && form.Handle() != lep.hWnd {
		form.SetBounds(form.Bounds())
	}
}

func (lep *LineErrorPresenter) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
	switch msg {
	case win.WM_SIZE, win.WM_SIZING:
		if lep.composite != nil {
			b := lep.ClientBounds()
			lep.composite.SetBounds(Rectangle{b.X + 2, b.Y + 2, b.Width - 4, b.Height - 4})
		}
	}

	return lep.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/types"
	"github.com/lxc/lxd/shared/api"
)

// Profiles returns a string list of profiles.
func (c *Cluster) Profiles() ([]string, error) {
	q := fmt.Sprintf("SELECT name FROM profiles")
	inargs := []interface{}{}
	var name string
	outfmt := []interface{}{name}
	result, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

	response := []string{}
	for _, r := range result {
		response = append(response, r[0].(string))
	}

	return response, nil
}

func (c *Cluster) ProfileGet(name string) (int64, *api.Profile, error) {
	id := int64(-1)
	description := sql.NullString{}

	q := "SELECT id, description FROM profiles WHERE name=?"
	arg1 := []interface{}{name}
	arg2 := []interface{}{&id, &description}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		return -1, nil, err
	}

	config, err := c.ProfileConfig(name)
	if err != nil {
		return -1, nil, err
	}

	devices, err := c.Devices(name, true)
	if err != nil {
		return -1, nil, err
	}

	profile := api.Profile{
		Name: name,
	}

	profile.Config = config
	profile.Description = description.String
	profile.Devices = devices

	return id, &profile, nil
}

func (c *Cluster) ProfileCreate(profile string, description string, config map[string]string,
	devices types.Devices) (int64, error) {

	var id int64
	err := c.Transaction(func(tx *ClusterTx) error {
		result, err := tx.tx.Exec("INSERT INTO profiles (name, description) VALUES (?, ?)", profile, description)
		if err != nil {
			return err
		}
		id, err = result.LastInsertId()
		if err != nil {
			return err
		}

		err = ProfileConfigAdd(tx.tx, id, config)
		if err != nil {
			return err
		}

		err = DevicesAdd(tx.tx, "profile", id, devices)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		id = -1
	}

	return id, nil
}

func (c *Cluster) ProfileCreateDefault() error {
	id, _, _ := c.ProfileGet("default")

	if id != -1 {
		// default profile already exists
		return nil
	}

	_, err := c.ProfileCreate("default", "Default LXD profile", map[string]string{}, types.Devices{})
	if err != nil {
		return err
	}

	return nil
}

// Get the profile configuration map from the DB
func (c *Cluster) ProfileConfig(name string) (map[string]string, error) {
	var key, value string
	query := `
        SELECT
            key, value
        FROM profiles_config
        JOIN profiles ON profiles_config.profile_id=profiles.id
		WHERE name=?`
	inargs := []interface{}{name}
	outfmt := []interface{}{key, value}
	results, err := queryScan(c.db, query, inargs, outfmt)
	if err != nil {
		return nil, fmt.Errorf("Failed to get profile '%s'", name)
	}

	if len(results) == 0 {
		/*
		 * If we didn't get any rows here, let's check to make sure the
		 * profile really exists; if it doesn't, let's send back a 404.
		 */
		query := "SELECT id FROM profiles WHERE name=?"
		var id int
		results, err := queryScan(c.db, query, []interface{}{name}, []interface{}{id})
		if err != nil {
			return nil, err
		}

		if len(results) == 0 {
			return nil, ErrNoSuchObject
		}
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

func (c *Cluster) ProfileDelete(name string) error {
	id, _, err := c.ProfileGet(name)
	if err != nil {
		return err
	}

	err = exec(c.db, "DELETE FROM profiles WHERE id=?", id)
	if err != nil {
		return err
	}

	return nil
}

func (c *Cluster) ProfileUpdate(name string, newName string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		_, err := tx.tx.Exec("UPDATE profiles SET name=? WHERE name=?", newName, name)
		return err
	})
	return err
}

func ProfileDescriptionUpdate(tx *sql.Tx, id int64, description string) error {
	_, err := tx.Exec("UPDATE profiles SET description=? WHERE id=?", description, id)
	return err
}

func ProfileConfigClear(tx *sql.Tx, id int64) error {
	_, err := tx.Exec("DELETE FROM profiles_config WHERE profile_id=?", id)
	if err != nil {
		return err
	}

	_, err = tx.Exec(`DELETE FROM profiles_devices_config WHERE id IN
		(SELECT profiles_devices_config.id
		 FROM profiles_devices_config JOIN profiles_devices
		 ON profiles_devices_config.profile_device_id=profiles_devices.id
		 WHERE profiles_devices.profile_id=?)`, id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM profiles_devices WHERE profile_id=?", id)
	if err != nil {
		return err
	}
	return nil
}

func ProfileConfigAdd(tx *sql.Tx, id int64, config map[string]string) error {
	str := fmt.Sprintf("INSERT INTO profiles_config (profile_id, key, value) VALUES(?, ?, ?)")
	stmt, err := tx.Prepare(str)
	defer stmt.Close()
	if err != nil {
		return err
	}

	for k, v := range config {
		_, err = stmt.Exec(id, k, v)
		if err != nil {
			return err
		}
	}

	return nil
}

func (c *Cluster) ProfileContainersGet(profile string) ([]string, error) {
	q := `SELECT containers.name FROM containers JOIN containers_profiles
		ON containers.id == containers_profiles.container_id
		JOIN profiles ON containers_profiles.profile_id == profiles.id
		WHERE profiles.name == ?`

	results := []string{}
	inargs := []interface{}{profile}
	var name string
	outfmt := []interface{}{name}

	output, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return results, err
	}

	for _, r := range output {
		results = append(results, r[0].(string))
	}

	return results, nil
}

func (c *Cluster) ProfileCleanupLeftover() error {
	stmt := `
DELETE FROM profiles_config WHERE profile_id NOT IN (SELECT id FROM profiles);
DELETE FROM profiles_devices WHERE profile_id NOT IN (SELECT id FROM profiles);
DELETE FROM profiles_devices_config WHERE profile_device_id NOT IN (SELECT id FROM profiles_devices);
`
	err := exec(c.db, stmt)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fix lints in lxd/db/profiles.go<commit_after>package db

import (
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/types"
	"github.com/lxc/lxd/shared/api"
)

// Profiles returns a string list of profiles.
func (c *Cluster) Profiles() ([]string, error) {
	q := fmt.Sprintf("SELECT name FROM profiles")
	inargs := []interface{}{}
	var name string
	outfmt := []interface{}{name}
	result, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

	response := []string{}
	for _, r := range result {
		response = append(response, r[0].(string))
	}

	return response, nil
}

// ProfileGet returns the profile with the given name.
func (c *Cluster) ProfileGet(name string) (int64, *api.Profile, error) {
	id := int64(-1)
	description := sql.NullString{}

	q := "SELECT id, description FROM profiles WHERE name=?"
	arg1 := []interface{}{name}
	arg2 := []interface{}{&id, &description}
	err := dbQueryRowScan(c.db, q, arg1, arg2)
	if err != nil {
		return -1, nil, err
	}

	config, err := c.ProfileConfig(name)
	if err != nil {
		return -1, nil, err
	}

	devices, err := c.Devices(name, true)
	if err != nil {
		return -1, nil, err
	}

	profile := api.Profile{
		Name: name,
	}

	profile.Config = config
	profile.Description = description.String
	profile.Devices = devices

	return id, &profile, nil
}

// ProfileCreate creates a new profile.
func (c *Cluster) ProfileCreate(profile string, description string, config map[string]string,
	devices types.Devices) (int64, error) {

	var id int64
	err := c.Transaction(func(tx *ClusterTx) error {
		result, err := tx.tx.Exec("INSERT INTO profiles (name, description) VALUES (?, ?)", profile, description)
		if err != nil {
			return err
		}
		id, err = result.LastInsertId()
		if err != nil {
			return err
		}

		err = ProfileConfigAdd(tx.tx, id, config)
		if err != nil {
			return err
		}

		err = DevicesAdd(tx.tx, "profile", id, devices)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		id = -1
	}

	return id, nil
}

// ProfileCreateDefault creates the default profile.
func (c *Cluster) ProfileCreateDefault() error {
	id, _, _ := c.ProfileGet("default")

	if id != -1 {
		// default profile already exists
		return nil
	}

	_, err := c.ProfileCreate("default", "Default LXD profile", map[string]string{}, types.Devices{})
	if err != nil {
		return err
	}

	return nil
}

// ProfileConfig gets the profile configuration map from the DB.
func (c *Cluster) ProfileConfig(name string) (map[string]string, error) {
	var key, value string
	query := `
        SELECT
            key, value
        FROM profiles_config
        JOIN profiles ON profiles_config.profile_id=profiles.id
		WHERE name=?`
	inargs := []interface{}{name}
	outfmt := []interface{}{key, value}
	results, err := queryScan(c.db, query, inargs, outfmt)
	if err != nil {
		return nil, fmt.Errorf("Failed to get profile '%s'", name)
	}

	if len(results) == 0 {
		/*
		 * If we didn't get any rows here, let's check to make sure the
		 * profile really exists; if it doesn't, let's send back a 404.
		 */
		query := "SELECT id FROM profiles WHERE name=?"
		var id int
		results, err := queryScan(c.db, query, []interface{}{name}, []interface{}{id})
		if err != nil {
			return nil, err
		}

		if len(results) == 0 {
			return nil, ErrNoSuchObject
		}
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

// ProfileDelete deletes the profile with the given name.
func (c *Cluster) ProfileDelete(name string) error {
	id, _, err := c.ProfileGet(name)
	if err != nil {
		return err
	}

	err = exec(c.db, "DELETE FROM profiles WHERE id=?", id)
	if err != nil {
		return err
	}

	return nil
}

// ProfileUpdate renames the profile with the given name to the given new name.
func (c *Cluster) ProfileUpdate(name string, newName string) error {
	err := c.Transaction(func(tx *ClusterTx) error {
		_, err := tx.tx.Exec("UPDATE profiles SET name=? WHERE name=?", newName, name)
		return err
	})
	return err
}

// ProfileDescriptionUpdate updates the description of the profile with the given ID.
func ProfileDescriptionUpdate(tx *sql.Tx, id int64, description string) error {
	_, err := tx.Exec("UPDATE profiles SET description=? WHERE id=?", description, id)
	return err
}

// ProfileConfigClear resets the config of the profile with the given ID.
func ProfileConfigClear(tx *sql.Tx, id int64) error {
	_, err := tx.Exec("DELETE FROM profiles_config WHERE profile_id=?", id)
	if err != nil {
		return err
	}

	_, err = tx.Exec(`DELETE FROM profiles_devices_config WHERE id IN
		(SELECT profiles_devices_config.id
		 FROM profiles_devices_config JOIN profiles_devices
		 ON profiles_devices_config.profile_device_id=profiles_devices.id
		 WHERE profiles_devices.profile_id=?)`, id)
	if err != nil {
		return err
	}
	_, err = tx.Exec("DELETE FROM profiles_devices WHERE profile_id=?", id)
	if err != nil {
		return err
	}
	return nil
}

// ProfileConfigAdd adds a config to the profile with the given ID.
func ProfileConfigAdd(tx *sql.Tx, id int64, config map[string]string) error {
	str := fmt.Sprintf("INSERT INTO profiles_config (profile_id, key, value) VALUES(?, ?, ?)")
	stmt, err := tx.Prepare(str)
	defer stmt.Close()
	if err != nil {
		return err
	}

	for k, v := range config {
		_, err = stmt.Exec(id, k, v)
		if err != nil {
			return err
		}
	}

	return nil
}

// ProfileContainersGet gets the names of the containers associated with the
// profile with the given name.
func (c *Cluster) ProfileContainersGet(profile string) ([]string, error) {
	q := `SELECT containers.name FROM containers JOIN containers_profiles
		ON containers.id == containers_profiles.container_id
		JOIN profiles ON containers_profiles.profile_id == profiles.id
		WHERE profiles.name == ?`

	results := []string{}
	inargs := []interface{}{profile}
	var name string
	outfmt := []interface{}{name}

	output, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return results, err
	}

	for _, r := range output {
		results = append(results, r[0].(string))
	}

	return results, nil
}

// ProfileCleanupLeftover removes unreferenced profiles.
func (c *Cluster) ProfileCleanupLeftover() error {
	stmt := `
DELETE FROM profiles_config WHERE profile_id NOT IN (SELECT id FROM profiles);
DELETE FROM profiles_devices WHERE profile_id NOT IN (SELECT id FROM profiles);
DELETE FROM profiles_devices_config WHERE profile_device_id NOT IN (SELECT id FROM profiles_devices);
`
	err := exec(c.db, stmt)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


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

// Package listener implements kubernetes API specific
// helper functions.
package listener

import (
	"fmt"
	"strings"
	"sync"

	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	"github.com/romana/core/common/log/trace"
	log "github.com/romana/rlog"

	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

type PolicyTranslator interface {
	Init(*client.Client, string, string)

	// Translates kubernetes policy into romana format.
	Kube2Romana(v1beta1.NetworkPolicy) (api.Policy, error)

	// Translates number of kubernetes policies into romana format.
	// Returns a list of translated policies, list of original policies
	// that failed to translate and an error.
	Kube2RomanaBulk([]v1beta1.NetworkPolicy) ([]api.Policy, []v1beta1.NetworkPolicy, error)
}

type Translator struct {
	listener         *KubeListener
	client           *client.Client
	tenantsCache     []TenantCacheEntry
	cacheMu          *sync.Mutex
	segmentLabelName string
	tenantLabelName  string
}

func (t *Translator) Init(client *client.Client, segmentLabelName, tenantLabelName string) {
	t.cacheMu = &sync.Mutex{}
	t.client = client
	t.segmentLabelName = segmentLabelName
	t.tenantLabelName = tenantLabelName
}

func (t Translator) GetClient() *client.Client {
	return t.client
}

// Kube2Romana reserved for future use.
func (t Translator) Kube2Romana(kubePolicy v1beta1.NetworkPolicy) (api.Policy, error) {
	return api.Policy{}, nil
}

// Kube2RomanaBulk attempts to translate a list of kubernetes policies into
// romana representation, returns a list of translated policies and a list
// of policies that can't be translated in original format.
func (t Translator) Kube2RomanaBulk(kubePolicies []v1beta1.NetworkPolicy) ([]api.Policy, []v1beta1.NetworkPolicy, error) {
	log.Info("In Kube2RomanaBulk")
	var returnRomanaPolicy []api.Policy
	var returnKubePolicy []v1beta1.NetworkPolicy

	for kubePolicyNumber, _ := range kubePolicies {
		romanaPolicy, err := t.translateNetworkPolicy(&kubePolicies[kubePolicyNumber])
		if err != nil {
			log.Errorf("Error during policy translation %s", err)
			returnKubePolicy = append(returnKubePolicy, kubePolicies[kubePolicyNumber])
		} else {
			returnRomanaPolicy = append(returnRomanaPolicy, romanaPolicy)
		}
	}

	return returnRomanaPolicy, returnKubePolicy, nil

}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see api.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *Translator) translateNetworkPolicy(kubePolicy *v1beta1.NetworkPolicy) (api.Policy, error) {
	policyID := getPolicyID(*kubePolicy)
	romanaPolicy := &api.Policy{Direction: api.PolicyDirectionIngress, ID: policyID}

	// Prepare translate group with original kubernetes policy and empty romana policy.
	translateGroup := &TranslateGroup{kubePolicy, romanaPolicy, TranslateGroupStartIndex}

	// Fill in AppliedTo field of romana policy.
	err := translateGroup.translateTarget(l)
	if err != nil {
		return *translateGroup.romanaPolicy, TranslatorError{ErrorTranslatingPolicyTarget, err}
	}

	// For each Ingress field in kubernetes policy, create Peer and Rule fields in
	// romana policy.
	for {
		err := translateGroup.translateNextIngress(l)
		if _, ok := err.(NoMoreIngressEntities); ok {
			break
		}

		if err != nil {
			return *translateGroup.romanaPolicy, TranslatorError{ErrorTranslatingPolicyIngress, err}
		}
	}

	return *translateGroup.romanaPolicy, nil
}

type TenantCacheEntry struct {
	Tenant api.Tenant
	//Segments []api.Segment
}

type TranslatorError struct {
	Code    TranslatorErrorType
	Details error
}

func (t TranslatorError) Error() string {
	return fmt.Sprintf("Translator error code %d, %s", t.Code, t.Details)
}

type TranslatorErrorType int

const (
	ErrorCacheUpdate TranslatorErrorType = iota
	ErrorTenantNotInCache
	ErrorTranslatingPolicyTarget
	ErrorTranslatingPolicyIngress
)

// TranslateGroup represent a state of translation of kubernetes policy
// into romana policy.
type TranslateGroup struct {
	kubePolicy   *v1beta1.NetworkPolicy
	romanaPolicy *api.Policy
	ingressIndex int
}

const TranslateGroupStartIndex = 0

// translateTarget analizes kubePolicy and fills romanaPolicy.AppliedTo field.
func (tg *TranslateGroup) translateTarget(translator *Translator) error {

	var targetEndpoint api.Endpoint

	// Translate kubernetes namespace into romana tenant. Must be defined.
	tenantID := GetTenantIDFromNamespaceName(tg.kubePolicy.ObjectMeta.Namespace)
	targetEndpoint.TenantID = tenantID

	// Empty PodSelector means policy applied to the entire namespace.
	if len(tg.kubePolicy.Spec.PodSelector.MatchLabels) == 0 {
		tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}

		log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy)
		return nil
	}

	// If PodSelector is not empty then segment label must be defined.
	kubeSegmentID, ok := tg.kubePolicy.Spec.PodSelector.MatchLabels[translator.segmentLabelName]
	if !ok || kubeSegmentID == "" {
		tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}
		log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy)
		return nil
	}

	targetEndpoint.SegmentID = kubeSegmentID

	tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}

	return nil
}

/// makeNextIngressPeer analyzes current Ingress rule and adds new Peer to romanaPolicy.Peers.
func (tg *TranslateGroup) makeNextIngressPeer(translator *Translator) error {
	ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex]
	// romanaIngress := tg.romanaPolicy.Ingress[tg.ingressIndex]

	for _, fromEntry := range ingress.From {
		var sourceEndpoint api.Endpoint

		// This ingress field matching a namespace which will be our source tenant.
		if fromEntry.NamespaceSelector != nil {
			tenantID := GetTenantIDFromNamespaceName(fromEntry.NamespaceSelector.MatchLabels[translator.tenantLabelName])
			if tenantID == "" {
				log.Errorf("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
				return common.NewError("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
			}

			// Found a source tenant, let's register it as romana Peer.
			sourceEndpoint.TenantID = tenantID
		}

		// if source tenant not specified assume same as target tenant.
		if sourceEndpoint.TenantID == "" {
			sourceEndpoint.TenantID = GetTenantIDFromNamespaceName(tg.kubePolicy.ObjectMeta.Namespace)
		}

		// This ingress field matches a either one segment or all segments.
		if fromEntry.PodSelector != nil {

			// Get segment name from podSelector.
			kubeSegmentID, ok := fromEntry.PodSelector.MatchLabels[translator.segmentLabelName]
			if ok {
				// Register source tenant/segment as a romana Peer.
				sourceEndpoint.SegmentID = kubeSegmentID
			}
		}

		tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, sourceEndpoint)

	}

	// kubernetes policy with empty Ingress with empty From field matches traffic
	// from all sources.
	if len(ingress.From) == 0 {
		tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, api.Endpoint{Peer: api.Wildcard})

		log.Debugf("Translating empty From as Peer:any %v", tg.romanaPolicy.Ingress[tg.ingressIndex].Peers)
	}

	log.Debugf("Translating From %+v as Peer %+v", ingress.From, tg.romanaPolicy.Ingress[tg.ingressIndex].Peers)

	return nil
}

// makeNextRule analizes current ingress rule and adds a new Rule to romanaPolicy.Rules.
func (tg *TranslateGroup) makeNextRule(translator *Translator) error {
	ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex]

	for _, toPort := range ingress.Ports {
		proto := strings.ToLower(string(*toPort.Protocol))
		ports := []uint{uint(toPort.Port.IntValue())}
		rule := api.Rule{Protocol: proto, Ports: ports}
		tg.romanaPolicy.Ingress[tg.ingressIndex].Rules = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Rules, rule)
	}

	// treat policy with no rules as policy that targets all traffic.
	if len(ingress.Ports) == 0 {
		rule := api.Rule{Protocol: api.Wildcard}
		tg.romanaPolicy.Ingress[tg.ingressIndex].Rules = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Rules, rule)
	}

	return nil
}

// translateNextIngress translates next Ingress object from kubePolicy into romanaPolicy
// Peer and Rule fields.
func (tg *TranslateGroup) translateNextIngress(translator *Translator) error {

	if tg.ingressIndex > len(tg.kubePolicy.Spec.Ingress)-1 {
		return NoMoreIngressEntities{}
	}

	tg.romanaPolicy.Ingress = append(tg.romanaPolicy.Ingress, api.RomanaIngress{})

	// Translate Ingress.From into romanaPolicy.ToPorts.
	err := tg.makeNextIngressPeer(translator)
	if err != nil {
		return err
	}

	// Translate Ingress.Ports into romanaPolicy.Rules.
	err = tg.makeNextRule(translator)
	if err != nil {
		return err
	}

	tg.ingressIndex++

	return nil
}

// NoMoreIngressEntities is an error that indicates that translateNextIngress
// went through all Ingress entries in TranslateGroup.kubePolicy.
type NoMoreIngressEntities struct{}

func (e NoMoreIngressEntities) Error() string {
	return "Done translating"
}
<commit_msg>Remove extra logging<commit_after>// Copyright (c) 2016 Pani Networks
// All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package listener implements kubernetes API specific
// helper functions.
package listener

import (
	"fmt"
	"strings"
	"sync"

	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	"github.com/romana/core/common/log/trace"
	log "github.com/romana/rlog"

	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
)

type PolicyTranslator interface {
	Init(*client.Client, string, string)

	// Translates kubernetes policy into romana format.
	Kube2Romana(v1beta1.NetworkPolicy) (api.Policy, error)

	// Translates number of kubernetes policies into romana format.
	// Returns a list of translated policies, list of original policies
	// that failed to translate and an error.
	Kube2RomanaBulk([]v1beta1.NetworkPolicy) ([]api.Policy, []v1beta1.NetworkPolicy, error)
}

type Translator struct {
	listener         *KubeListener
	client           *client.Client
	tenantsCache     []TenantCacheEntry
	cacheMu          *sync.Mutex
	segmentLabelName string
	tenantLabelName  string
}

func (t *Translator) Init(client *client.Client, segmentLabelName, tenantLabelName string) {
	t.cacheMu = &sync.Mutex{}
	t.client = client
	t.segmentLabelName = segmentLabelName
	t.tenantLabelName = tenantLabelName
}

func (t Translator) GetClient() *client.Client {
	return t.client
}

// Kube2Romana reserved for future use.
func (t Translator) Kube2Romana(kubePolicy v1beta1.NetworkPolicy) (api.Policy, error) {
	return api.Policy{}, nil
}

// Kube2RomanaBulk attempts to translate a list of kubernetes policies into
// romana representation, returns a list of translated policies and a list
// of policies that can't be translated in original format.
func (t Translator) Kube2RomanaBulk(kubePolicies []v1beta1.NetworkPolicy) ([]api.Policy, []v1beta1.NetworkPolicy, error) {
	log.Info("In Kube2RomanaBulk")
	var returnRomanaPolicy []api.Policy
	var returnKubePolicy []v1beta1.NetworkPolicy

	for kubePolicyNumber, _ := range kubePolicies {
		romanaPolicy, err := t.translateNetworkPolicy(&kubePolicies[kubePolicyNumber])
		if err != nil {
			log.Errorf("Error during policy translation %s", err)
			returnKubePolicy = append(returnKubePolicy, kubePolicies[kubePolicyNumber])
		} else {
			returnRomanaPolicy = append(returnRomanaPolicy, romanaPolicy)
		}
	}

	return returnRomanaPolicy, returnKubePolicy, nil

}

// translateNetworkPolicy translates a Kubernetes policy into
// Romana policy (see api.Policy) with the following rules:
// 1. Kubernetes Namespace corresponds to Romana Tenant
// 2. If Romana Tenant does not exist it is an error (a tenant should
//    automatically have been created when the namespace was added)
func (l *Translator) translateNetworkPolicy(kubePolicy *v1beta1.NetworkPolicy) (api.Policy, error) {
	policyID := getPolicyID(*kubePolicy)
	romanaPolicy := &api.Policy{Direction: api.PolicyDirectionIngress, ID: policyID}

	// Prepare translate group with original kubernetes policy and empty romana policy.
	translateGroup := &TranslateGroup{kubePolicy, romanaPolicy, TranslateGroupStartIndex}

	// Fill in AppliedTo field of romana policy.
	err := translateGroup.translateTarget(l)
	if err != nil {
		return *translateGroup.romanaPolicy, TranslatorError{ErrorTranslatingPolicyTarget, err}
	}

	// For each Ingress field in kubernetes policy, create Peer and Rule fields in
	// romana policy.
	for {
		err := translateGroup.translateNextIngress(l)
		if _, ok := err.(NoMoreIngressEntities); ok {
			break
		}

		if err != nil {
			return *translateGroup.romanaPolicy, TranslatorError{ErrorTranslatingPolicyIngress, err}
		}
	}

	return *translateGroup.romanaPolicy, nil
}

type TenantCacheEntry struct {
	Tenant api.Tenant
	//Segments []api.Segment
}

type TranslatorError struct {
	Code    TranslatorErrorType
	Details error
}

func (t TranslatorError) Error() string {
	return fmt.Sprintf("Translator error code %d, %s", t.Code, t.Details)
}

type TranslatorErrorType int

const (
	ErrorCacheUpdate TranslatorErrorType = iota
	ErrorTenantNotInCache
	ErrorTranslatingPolicyTarget
	ErrorTranslatingPolicyIngress
)

// TranslateGroup represent a state of translation of kubernetes policy
// into romana policy.
type TranslateGroup struct {
	kubePolicy   *v1beta1.NetworkPolicy
	romanaPolicy *api.Policy
	ingressIndex int
}

const TranslateGroupStartIndex = 0

// translateTarget analizes kubePolicy and fills romanaPolicy.AppliedTo field.
func (tg *TranslateGroup) translateTarget(translator *Translator) error {

	var targetEndpoint api.Endpoint

	// Translate kubernetes namespace into romana tenant. Must be defined.
	tenantID := GetTenantIDFromNamespaceName(tg.kubePolicy.ObjectMeta.Namespace)
	targetEndpoint.TenantID = tenantID

	// Empty PodSelector means policy applied to the entire namespace.
	if len(tg.kubePolicy.Spec.PodSelector.MatchLabels) == 0 {
		tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}

		log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy)
		return nil
	}

	// If PodSelector is not empty then segment label must be defined.
	kubeSegmentID, ok := tg.kubePolicy.Spec.PodSelector.MatchLabels[translator.segmentLabelName]
	if !ok || kubeSegmentID == "" {
		tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}
		log.Tracef(trace.Inside, "Segment was not specified in policy %v, assuming target is a namespace", tg.kubePolicy)
		return nil
	}

	targetEndpoint.SegmentID = kubeSegmentID

	tg.romanaPolicy.AppliedTo = []api.Endpoint{targetEndpoint}

	return nil
}

/// makeNextIngressPeer analyzes current Ingress rule and adds new Peer to romanaPolicy.Peers.
func (tg *TranslateGroup) makeNextIngressPeer(translator *Translator) error {
	ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex]
	// romanaIngress := tg.romanaPolicy.Ingress[tg.ingressIndex]

	for _, fromEntry := range ingress.From {
		var sourceEndpoint api.Endpoint

		// This ingress field matching a namespace which will be our source tenant.
		if fromEntry.NamespaceSelector != nil {
			tenantID := GetTenantIDFromNamespaceName(fromEntry.NamespaceSelector.MatchLabels[translator.tenantLabelName])
			if tenantID == "" {
				log.Errorf("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
				return common.NewError("Expected tenant name to be specified in NamespaceSelector field with a key %s", translator.tenantLabelName)
			}

			// Found a source tenant, let's register it as romana Peer.
			sourceEndpoint.TenantID = tenantID
		}

		// if source tenant not specified assume same as target tenant.
		if sourceEndpoint.TenantID == "" {
			sourceEndpoint.TenantID = GetTenantIDFromNamespaceName(tg.kubePolicy.ObjectMeta.Namespace)
		}

		// This ingress field matches a either one segment or all segments.
		if fromEntry.PodSelector != nil {

			// Get segment name from podSelector.
			kubeSegmentID, ok := fromEntry.PodSelector.MatchLabels[translator.segmentLabelName]
			if ok {
				// Register source tenant/segment as a romana Peer.
				sourceEndpoint.SegmentID = kubeSegmentID
			}
		}

		tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, sourceEndpoint)

	}

	// kubernetes policy with empty Ingress with empty From field matches traffic
	// from all sources.
	if len(ingress.From) == 0 {
		tg.romanaPolicy.Ingress[tg.ingressIndex].Peers = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Peers, api.Endpoint{Peer: api.Wildcard})

	}

	return nil
}

// makeNextRule analizes current ingress rule and adds a new Rule to romanaPolicy.Rules.
func (tg *TranslateGroup) makeNextRule(translator *Translator) error {
	ingress := tg.kubePolicy.Spec.Ingress[tg.ingressIndex]

	for _, toPort := range ingress.Ports {
		proto := strings.ToLower(string(*toPort.Protocol))
		ports := []uint{uint(toPort.Port.IntValue())}
		rule := api.Rule{Protocol: proto, Ports: ports}
		tg.romanaPolicy.Ingress[tg.ingressIndex].Rules = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Rules, rule)
	}

	// treat policy with no rules as policy that targets all traffic.
	if len(ingress.Ports) == 0 {
		rule := api.Rule{Protocol: api.Wildcard}
		tg.romanaPolicy.Ingress[tg.ingressIndex].Rules = append(tg.romanaPolicy.Ingress[tg.ingressIndex].Rules, rule)
	}

	return nil
}

// translateNextIngress translates next Ingress object from kubePolicy into romanaPolicy
// Peer and Rule fields.
func (tg *TranslateGroup) translateNextIngress(translator *Translator) error {

	if tg.ingressIndex > len(tg.kubePolicy.Spec.Ingress)-1 {
		return NoMoreIngressEntities{}
	}

	tg.romanaPolicy.Ingress = append(tg.romanaPolicy.Ingress, api.RomanaIngress{})

	// Translate Ingress.From into romanaPolicy.ToPorts.
	err := tg.makeNextIngressPeer(translator)
	if err != nil {
		return err
	}

	// Translate Ingress.Ports into romanaPolicy.Rules.
	err = tg.makeNextRule(translator)
	if err != nil {
		return err
	}

	tg.ingressIndex++

	return nil
}

// NoMoreIngressEntities is an error that indicates that translateNextIngress
// went through all Ingress entries in TranslateGroup.kubePolicy.
type NoMoreIngressEntities struct{}

func (e NoMoreIngressEntities) Error() string {
	return "Done translating"
}
<|endoftext|>


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

package main

import (
	"encoding/xml"
	"flag"
	"log"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"

	libvirt "github.com/libvirt/libvirt-go"

	"./libvirt_schema"
)

var (
	libvirtUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "", "up"),
		"Whether scraping libvirt's metrics was successful.",
		nil,
		nil)

	libvirtBlockRdBytesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_bytes_total"),
		"Number of bytes read from a block device, in bytes.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockRdReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_requests_total"),
		"Number of read requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockRdTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_seconds_total"),
		"Amount of time spent reading from a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrBytesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_bytes_total"),
		"Number of bytes written from a block device, in bytes.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_requests_total"),
		"Number of write requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_seconds_total"),
		"Amount of time spent writing from a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockFlushReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "flush_requests_total"),
		"Number of flush requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockFlushTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "flush_seconds_total"),
		"Amount of time spent flushing of a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
)

// CollectDomain extracts Prometheus metrics from a libvirt domain.
func CollectDomain(ch chan<- prometheus.Metric, domain *libvirt.Domain) error {
	domainName, err := domain.GetName()
	if err != nil {
		return err
	}

	xmlDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		return err
	}

	var desc libvirt_schema.Domain
	err = xml.Unmarshal([]byte(xmlDesc), &desc)
	if err != nil {
		return err
	}

	// Report block device statistics.
	for _, disk := range desc.Devices.Disks {
		blockStats, err := domain.BlockStats(disk.Target.Device)
		if err != nil {
			return err
		}
		if blockStats.RdBytesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdBytesDesc,
				prometheus.CounterValue,
				float64(blockStats.RdBytes),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.RdReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdReqDesc,
				prometheus.CounterValue,
				float64(blockStats.RdReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.RdTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.RdTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrBytesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrBytesDesc,
				prometheus.CounterValue,
				float64(blockStats.WrBytes),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrReqDesc,
				prometheus.CounterValue,
				float64(blockStats.WrReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.WrTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.FlushReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockFlushReqDesc,
				prometheus.CounterValue,
				float64(blockStats.FlushReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.FlushTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockFlushTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.FlushTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		// Skip "Errs", as the documentation does not clearly
		// explain what this means.
	}

	return nil
}

// CollectFromLibvirt obtains Prometheus metrics from all domains in a
// libvirt setup.
func CollectFromLibvirt(ch chan<- prometheus.Metric, uri string) error {
	conn, err := libvirt.NewConnect(uri)
	if err != nil {
		return err
	}
	defer conn.Close()

	// First attempt to get a list of active domains using
	// ListAllDomains(). If this fails, the remote side is using a version
	// of libvirt older than 0.9.13. In that case, fall back to using
	// ListDomains() in combination with LookupDomainById().
	domains, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
	if err == nil {
		for _, domain := range domains {
			defer domain.Free()
		}
		for _, domain := range domains {
			err = CollectDomain(ch, &domain)
			if err != nil {
				return err
			}
		}
	} else {
		domainIds, err := conn.ListDomains()
		if err != nil {
			return err
		}
		for _, id := range domainIds {
			domain, err := conn.LookupDomainById(id)
			if err == nil {
				err = CollectDomain(ch, domain)
				domain.Free()
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// LibvirtExporter implements a Prometheus exporter for libvirt state.
type LibvirtExporter struct {
	uri string
}

// NewLibvirtExporter creates a new Prometheus exporter for libvirt.
func NewLibvirtExporter(uri string) (*LibvirtExporter, error) {
	return &LibvirtExporter{
		uri: uri,
	}, nil
}

// Describe returns metadata for all Prometheus metrics that may be exported.
func (e *LibvirtExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- libvirtUpDesc

	ch <- libvirtBlockRdBytesDesc
	ch <- libvirtBlockRdReqDesc
	ch <- libvirtBlockRdTotalTimesDesc
	ch <- libvirtBlockWrBytesDesc
	ch <- libvirtBlockWrReqDesc
	ch <- libvirtBlockWrTotalTimesDesc
	ch <- libvirtBlockFlushReqDesc
	ch <- libvirtBlockFlushTotalTimesDesc
}

// Collect scrapes Prometheus metrics from libvirt.
func (e *LibvirtExporter) Collect(ch chan<- prometheus.Metric) {
	err := CollectFromLibvirt(ch, e.uri)
	if err == nil {
		ch <- prometheus.MustNewConstMetric(
			libvirtUpDesc,
			prometheus.GaugeValue,
			1.0)
	} else {
		log.Printf("Failed to scrape metrics: %s", err)
		ch <- prometheus.MustNewConstMetric(
			libvirtUpDesc,
			prometheus.GaugeValue,
			0.0)
	}
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9167", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		libvirtURI    = flag.String("libvirt.uri", "qemu:///system", "Libvirt URI from which to extract metrics.")
	)
	flag.Parse()

	exporter, err := NewLibvirtExporter(*libvirtURI)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>Libvirt Exporter</title>

Libvirt Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<commit_msg>Use an officially allocated port number.<commit_after>// Copyright 2017 Kumina, https://kumina.nl/
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"encoding/xml"
	"flag"
	"log"
	"net/http"

	"github.com/prometheus/client_golang/prometheus"

	libvirt "github.com/libvirt/libvirt-go"

	"./libvirt_schema"
)

var (
	libvirtUpDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "", "up"),
		"Whether scraping libvirt's metrics was successful.",
		nil,
		nil)

	libvirtBlockRdBytesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_bytes_total"),
		"Number of bytes read from a block device, in bytes.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockRdReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_requests_total"),
		"Number of read requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockRdTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "read_seconds_total"),
		"Amount of time spent reading from a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrBytesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_bytes_total"),
		"Number of bytes written from a block device, in bytes.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_requests_total"),
		"Number of write requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockWrTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "write_seconds_total"),
		"Amount of time spent writing from a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockFlushReqDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "flush_requests_total"),
		"Number of flush requests from a block device.",
		[]string{"domain", "source_file", "target_device"},
		nil)
	libvirtBlockFlushTotalTimesDesc = prometheus.NewDesc(
		prometheus.BuildFQName("libvirt", "block_stats", "flush_seconds_total"),
		"Amount of time spent flushing of a block device, in seconds.",
		[]string{"domain", "source_file", "target_device"},
		nil)
)

// CollectDomain extracts Prometheus metrics from a libvirt domain.
func CollectDomain(ch chan<- prometheus.Metric, domain *libvirt.Domain) error {
	domainName, err := domain.GetName()
	if err != nil {
		return err
	}

	xmlDesc, err := domain.GetXMLDesc(0)
	if err != nil {
		return err
	}

	var desc libvirt_schema.Domain
	err = xml.Unmarshal([]byte(xmlDesc), &desc)
	if err != nil {
		return err
	}

	// Report block device statistics.
	for _, disk := range desc.Devices.Disks {
		blockStats, err := domain.BlockStats(disk.Target.Device)
		if err != nil {
			return err
		}
		if blockStats.RdBytesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdBytesDesc,
				prometheus.CounterValue,
				float64(blockStats.RdBytes),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.RdReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdReqDesc,
				prometheus.CounterValue,
				float64(blockStats.RdReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.RdTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockRdTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.RdTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrBytesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrBytesDesc,
				prometheus.CounterValue,
				float64(blockStats.WrBytes),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrReqDesc,
				prometheus.CounterValue,
				float64(blockStats.WrReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.WrTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockWrTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.WrTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.FlushReqSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockFlushReqDesc,
				prometheus.CounterValue,
				float64(blockStats.FlushReq),
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		if blockStats.FlushTotalTimesSet {
			ch <- prometheus.MustNewConstMetric(
				libvirtBlockFlushTotalTimesDesc,
				prometheus.CounterValue,
				float64(blockStats.FlushTotalTimes) / 1e9,
				domainName,
				disk.Source.File,
				disk.Target.Device)
		}
		// Skip "Errs", as the documentation does not clearly
		// explain what this means.
	}

	return nil
}

// CollectFromLibvirt obtains Prometheus metrics from all domains in a
// libvirt setup.
func CollectFromLibvirt(ch chan<- prometheus.Metric, uri string) error {
	conn, err := libvirt.NewConnect(uri)
	if err != nil {
		return err
	}
	defer conn.Close()

	// First attempt to get a list of active domains using
	// ListAllDomains(). If this fails, the remote side is using a version
	// of libvirt older than 0.9.13. In that case, fall back to using
	// ListDomains() in combination with LookupDomainById().
	domains, err := conn.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_ACTIVE)
	if err == nil {
		for _, domain := range domains {
			defer domain.Free()
		}
		for _, domain := range domains {
			err = CollectDomain(ch, &domain)
			if err != nil {
				return err
			}
		}
	} else {
		domainIds, err := conn.ListDomains()
		if err != nil {
			return err
		}
		for _, id := range domainIds {
			domain, err := conn.LookupDomainById(id)
			if err == nil {
				err = CollectDomain(ch, domain)
				domain.Free()
				if err != nil {
					return err
				}
			}
		}
	}

	return nil
}

// LibvirtExporter implements a Prometheus exporter for libvirt state.
type LibvirtExporter struct {
	uri string
}

// NewLibvirtExporter creates a new Prometheus exporter for libvirt.
func NewLibvirtExporter(uri string) (*LibvirtExporter, error) {
	return &LibvirtExporter{
		uri: uri,
	}, nil
}

// Describe returns metadata for all Prometheus metrics that may be exported.
func (e *LibvirtExporter) Describe(ch chan<- *prometheus.Desc) {
	ch <- libvirtUpDesc

	ch <- libvirtBlockRdBytesDesc
	ch <- libvirtBlockRdReqDesc
	ch <- libvirtBlockRdTotalTimesDesc
	ch <- libvirtBlockWrBytesDesc
	ch <- libvirtBlockWrReqDesc
	ch <- libvirtBlockWrTotalTimesDesc
	ch <- libvirtBlockFlushReqDesc
	ch <- libvirtBlockFlushTotalTimesDesc
}

// Collect scrapes Prometheus metrics from libvirt.
func (e *LibvirtExporter) Collect(ch chan<- prometheus.Metric) {
	err := CollectFromLibvirt(ch, e.uri)
	if err == nil {
		ch <- prometheus.MustNewConstMetric(
			libvirtUpDesc,
			prometheus.GaugeValue,
			1.0)
	} else {
		log.Printf("Failed to scrape metrics: %s", err)
		ch <- prometheus.MustNewConstMetric(
			libvirtUpDesc,
			prometheus.GaugeValue,
			0.0)
	}
}

func main() {
	var (
		listenAddress = flag.String("web.listen-address", ":9177", "Address to listen on for web interface and telemetry.")
		metricsPath   = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		libvirtURI    = flag.String("libvirt.uri", "qemu:///system", "Libvirt URI from which to extract metrics.")
	)
	flag.Parse()

	exporter, err := NewLibvirtExporter(*libvirtURI)
	if err != nil {
		panic(err)
	}
	prometheus.MustRegister(exporter)

	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`

			<title>Libvirt Exporter</title>

Libvirt Exporter

<a href='` + *metricsPath + `'>Metrics

			`))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}
<|endoftext|>


<commit_before>package main

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

	"github.com/Songmu/gitconfig"
)

type LocalRepository struct {
	FullPath  string
	RelPath   string
	RootPath  string
	PathParts []string

	repoPath   string
	vcsBackend *VCSBackend
}

func (repo *LocalRepository) RepoPath() string {
	if repo.repoPath != "" {
		return repo.repoPath
	}
	return repo.FullPath
}

func LocalRepositoryFromFullPath(fullPath string, backend *VCSBackend) (*LocalRepository, error) {
	var relPath string

	roots, err := localRepositoryRoots()
	if err != nil {
		return nil, err
	}
	var root string
	for _, root = range roots {
		if !strings.HasPrefix(fullPath, root) {
			continue
		}

		var err error
		relPath, err = filepath.Rel(root, fullPath)
		if err == nil {
			break
		}
	}

	if relPath == "" {
		return nil, fmt.Errorf("no local repository found for: %s", fullPath)
	}

	pathParts := strings.Split(relPath, string(filepath.Separator))

	return &LocalRepository{
		FullPath:   fullPath,
		RelPath:    filepath.ToSlash(relPath),
		RootPath:   root,
		PathParts:  pathParts,
		vcsBackend: backend,
	}, nil
}

func LocalRepositoryFromURL(remoteURL *url.URL) (*LocalRepository, error) {
	pathParts := append(
		[]string{remoteURL.Hostname()}, strings.Split(remoteURL.Path, "/")...,
	)
	relPath := strings.TrimSuffix(filepath.Join(pathParts...), ".git")
	pathParts[len(pathParts)-1] = strings.TrimSuffix(pathParts[len(pathParts)-1], ".git")

	var localRepository *LocalRepository

	// Find existing local repository first
	if err := walkLocalRepositories(func(repo *LocalRepository) {
		if repo.RelPath == relPath {
			localRepository = repo
		}
	}); err != nil {
		return nil, err
	}

	if localRepository != nil {
		return localRepository, nil
	}

	prim, err := primaryLocalRepositoryRoot()
	if err != nil {
		return nil, err
	}
	// No local repository found, returning new one
	return &LocalRepository{
		FullPath:  filepath.Join(prim, relPath),
		RelPath:   relPath,
		RootPath:  prim,
		PathParts: pathParts,
	}, nil
}

// Subpaths returns lists of tail parts of relative path from the root directory (shortest first)
// for example, {"ghq", "motemen/ghq", "github.com/motemen/ghq"} for $root/github.com/motemen/ghq.
func (repo *LocalRepository) Subpaths() []string {
	tails := make([]string, len(repo.PathParts))

	for i := range repo.PathParts {
		tails[i] = strings.Join(repo.PathParts[len(repo.PathParts)-(i+1):], "/")
	}

	return tails
}

func (repo *LocalRepository) NonHostPath() string {
	return strings.Join(repo.PathParts[1:], "/")
}

// list as bellow
// - "$GHQ_ROOT/github.com/motemen/ghq/cmdutil" // repo.FullPath
// - "$GHQ_ROOT/github.com/motemen/ghq"
// - "$GHQ_ROOT/github.com/motemen
func (repo *LocalRepository) repoRootCandidates() []string {
	hostRoot := filepath.Join(repo.RootPath, repo.PathParts[0])
	nonHostParts := repo.PathParts[1:]
	candidates := make([]string, len(nonHostParts))
	for i := 0; i < len(nonHostParts); i++ {
		candidates[i] = filepath.Join(append(
			[]string{hostRoot}, nonHostParts[0:len(nonHostParts)-i]...)...)
	}
	return candidates
}

func (repo *LocalRepository) IsUnderPrimaryRoot() bool {
	prim, err := primaryLocalRepositoryRoot()
	if err != nil {
		return false
	}
	return strings.HasPrefix(repo.FullPath, prim)
}

// Matches checks if any subpath of the local repository equals the query.
func (repo *LocalRepository) Matches(pathQuery string) bool {
	for _, p := range repo.Subpaths() {
		if p == pathQuery {
			return true
		}
	}

	return false
}

func (repo *LocalRepository) VCS() (*VCSBackend, string) {
	if repo.vcsBackend == nil {
		for _, dir := range repo.repoRootCandidates() {
			backend := findVCSBackend(dir)
			if backend != nil {
				repo.vcsBackend = backend
				repo.repoPath = dir
				break
			}
		}
	}
	return repo.vcsBackend, repo.RepoPath()
}

var vcsContentsMap = map[string]*VCSBackend{
	".git/svn":       GitsvnBackend,
	".git":           GitBackend,
	".svn":           SubversionBackend,
	".hg":            MercurialBackend,
	"_darcs":         DarcsBackend,
	".fslckout":      FossilBackend, // file
	"_FOSSIL_":       FossilBackend, // file
	"CVS/Repository": cvsDummyBackend,
	".bzr":           BazaarBackend,
}

var vcsContents = make([]string, 0, len(vcsContentsMap))

func init() {
	for k := range vcsContentsMap {
		vcsContents = append(vcsContents, k)
	}
	// Sort in order of length.
	// This is to check git/svn before git.
	sort.Slice(vcsContents, func(i, j int) bool {
		return len(vcsContents[i]) > len(vcsContents[j])
	})
}

func findVCSBackend(fpath string) *VCSBackend {
	for _, d := range vcsContents {
		if _, err := os.Stat(filepath.Join(fpath, d)); err == nil {
			return vcsContentsMap[d]
		}
	}
	return nil
}

func walkLocalRepositories(callback func(*LocalRepository)) error {
	roots, err := localRepositoryRoots()
	if err != nil {
		return err
	}
	for _, root := range roots {
		if err := filepath.Walk(root, func(fpath string, fi os.FileInfo, err error) error {
			isSymlink := false
			if err != nil || fi == nil {
				if err == nil || os.IsNotExist(err) {
					return nil
				}
				if os.IsPermission(err) && filepath.Base(fpath)[0] == '.' {
					return nil
				}
				return err
			}
			if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
				isSymlink = true
				realpath, err := filepath.EvalSymlinks(fpath)
				if err != nil {
					return nil
				}
				fi, err = os.Stat(realpath)
				if err != nil {
					return nil
				}
			}
			if !fi.IsDir() {
				return nil
			}
			vcsBackend := findVCSBackend(fpath)
			if vcsBackend == nil {
				return nil
			}

			repo, err := LocalRepositoryFromFullPath(fpath, vcsBackend)
			if err != nil || repo == nil {
				return nil
			}
			callback(repo)

			if isSymlink {
				return nil
			}
			return filepath.SkipDir
		}); err != nil {
			return err
		}
	}
	return nil
}

var _localRepositoryRoots []string

// localRepositoryRoots returns locally cloned repositories' root directories.
// The root dirs are determined as following:
//
//   - If GHQ_ROOT environment variable is nonempty, use it as the only root dir.
//   - Otherwise, use the result of `git config --get-all ghq.root` as the dirs.
//   - Otherwise, fallback to the default root, `~/.ghq`.
func localRepositoryRoots() ([]string, error) {
	if len(_localRepositoryRoots) != 0 {
		return _localRepositoryRoots, nil
	}

	envRoot := os.Getenv("GHQ_ROOT")
	if envRoot != "" {
		_localRepositoryRoots = filepath.SplitList(envRoot)
	} else {
		var err error
		_localRepositoryRoots, err = gitconfig.PathAll("ghq.root")
		if err != nil && !gitconfig.IsNotFound(err) {
			return nil, err
		}
	}

	if len(_localRepositoryRoots) == 0 {
		homeDir, err := os.UserHomeDir()
		if err != nil {
			return nil, err
		}
		_localRepositoryRoots = []string{filepath.Join(homeDir, ".ghq")}
	}

	for i, v := range _localRepositoryRoots {
		path := filepath.Clean(v)
		if _, err := os.Stat(path); err == nil {
			if path, err = filepath.EvalSymlinks(path); err != nil {
				return nil, err
			}
		}
		if !filepath.IsAbs(path) {
			var err error
			if path, err = filepath.Abs(path); err != nil {
				return nil, err
			}
		}
		_localRepositoryRoots[i] = path
	}

	return _localRepositoryRoots, nil
}

func primaryLocalRepositoryRoot() (string, error) {
	roots, err := localRepositoryRoots()
	if err != nil {
		return "", err
	}
	return roots[0], nil
}
<commit_msg>ditto<commit_after>package main

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

	"github.com/Songmu/gitconfig"
)

type LocalRepository struct {
	FullPath  string
	RelPath   string
	RootPath  string
	PathParts []string

	repoPath   string
	vcsBackend *VCSBackend
}

func (repo *LocalRepository) RepoPath() string {
	if repo.repoPath != "" {
		return repo.repoPath
	}
	return repo.FullPath
}

func LocalRepositoryFromFullPath(fullPath string, backend *VCSBackend) (*LocalRepository, error) {
	var relPath string

	roots, err := localRepositoryRoots()
	if err != nil {
		return nil, err
	}
	var root string
	for _, root = range roots {
		if !strings.HasPrefix(fullPath, root) {
			continue
		}

		var err error
		relPath, err = filepath.Rel(root, fullPath)
		if err == nil {
			break
		}
	}

	if relPath == "" {
		return nil, fmt.Errorf("no local repository found for: %s", fullPath)
	}

	pathParts := strings.Split(relPath, string(filepath.Separator))

	return &LocalRepository{
		FullPath:   fullPath,
		RelPath:    filepath.ToSlash(relPath),
		RootPath:   root,
		PathParts:  pathParts,
		vcsBackend: backend,
	}, nil
}

func LocalRepositoryFromURL(remoteURL *url.URL) (*LocalRepository, error) {
	pathParts := append(
		[]string{remoteURL.Hostname()}, strings.Split(remoteURL.Path, "/")...,
	)
	relPath := strings.TrimSuffix(filepath.Join(pathParts...), ".git")
	pathParts[len(pathParts)-1] = strings.TrimSuffix(pathParts[len(pathParts)-1], ".git")

	var localRepository *LocalRepository

	// Find existing local repository first
	if err := walkLocalRepositories(func(repo *LocalRepository) {
		if repo.RelPath == relPath {
			localRepository = repo
		}
	}); err != nil {
		return nil, err
	}

	if localRepository != nil {
		return localRepository, nil
	}

	prim, err := primaryLocalRepositoryRoot()
	if err != nil {
		return nil, err
	}
	// No local repository found, returning new one
	return &LocalRepository{
		FullPath:  filepath.Join(prim, relPath),
		RelPath:   filepath.ToSlash(relPath),
		RootPath:  prim,
		PathParts: pathParts,
	}, nil
}

// Subpaths returns lists of tail parts of relative path from the root directory (shortest first)
// for example, {"ghq", "motemen/ghq", "github.com/motemen/ghq"} for $root/github.com/motemen/ghq.
func (repo *LocalRepository) Subpaths() []string {
	tails := make([]string, len(repo.PathParts))

	for i := range repo.PathParts {
		tails[i] = strings.Join(repo.PathParts[len(repo.PathParts)-(i+1):], "/")
	}

	return tails
}

func (repo *LocalRepository) NonHostPath() string {
	return strings.Join(repo.PathParts[1:], "/")
}

// list as bellow
// - "$GHQ_ROOT/github.com/motemen/ghq/cmdutil" // repo.FullPath
// - "$GHQ_ROOT/github.com/motemen/ghq"
// - "$GHQ_ROOT/github.com/motemen
func (repo *LocalRepository) repoRootCandidates() []string {
	hostRoot := filepath.Join(repo.RootPath, repo.PathParts[0])
	nonHostParts := repo.PathParts[1:]
	candidates := make([]string, len(nonHostParts))
	for i := 0; i < len(nonHostParts); i++ {
		candidates[i] = filepath.Join(append(
			[]string{hostRoot}, nonHostParts[0:len(nonHostParts)-i]...)...)
	}
	return candidates
}

func (repo *LocalRepository) IsUnderPrimaryRoot() bool {
	prim, err := primaryLocalRepositoryRoot()
	if err != nil {
		return false
	}
	return strings.HasPrefix(repo.FullPath, prim)
}

// Matches checks if any subpath of the local repository equals the query.
func (repo *LocalRepository) Matches(pathQuery string) bool {
	for _, p := range repo.Subpaths() {
		if p == pathQuery {
			return true
		}
	}

	return false
}

func (repo *LocalRepository) VCS() (*VCSBackend, string) {
	if repo.vcsBackend == nil {
		for _, dir := range repo.repoRootCandidates() {
			backend := findVCSBackend(dir)
			if backend != nil {
				repo.vcsBackend = backend
				repo.repoPath = dir
				break
			}
		}
	}
	return repo.vcsBackend, repo.RepoPath()
}

var vcsContentsMap = map[string]*VCSBackend{
	".git/svn":       GitsvnBackend,
	".git":           GitBackend,
	".svn":           SubversionBackend,
	".hg":            MercurialBackend,
	"_darcs":         DarcsBackend,
	".fslckout":      FossilBackend, // file
	"_FOSSIL_":       FossilBackend, // file
	"CVS/Repository": cvsDummyBackend,
	".bzr":           BazaarBackend,
}

var vcsContents = make([]string, 0, len(vcsContentsMap))

func init() {
	for k := range vcsContentsMap {
		vcsContents = append(vcsContents, k)
	}
	// Sort in order of length.
	// This is to check git/svn before git.
	sort.Slice(vcsContents, func(i, j int) bool {
		return len(vcsContents[i]) > len(vcsContents[j])
	})
}

func findVCSBackend(fpath string) *VCSBackend {
	for _, d := range vcsContents {
		if _, err := os.Stat(filepath.Join(fpath, d)); err == nil {
			return vcsContentsMap[d]
		}
	}
	return nil
}

func walkLocalRepositories(callback func(*LocalRepository)) error {
	roots, err := localRepositoryRoots()
	if err != nil {
		return err
	}
	for _, root := range roots {
		if err := filepath.Walk(root, func(fpath string, fi os.FileInfo, err error) error {
			isSymlink := false
			if err != nil || fi == nil {
				if err == nil || os.IsNotExist(err) {
					return nil
				}
				if os.IsPermission(err) && filepath.Base(fpath)[0] == '.' {
					return nil
				}
				return err
			}
			if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
				isSymlink = true
				realpath, err := filepath.EvalSymlinks(fpath)
				if err != nil {
					return nil
				}
				fi, err = os.Stat(realpath)
				if err != nil {
					return nil
				}
			}
			if !fi.IsDir() {
				return nil
			}
			vcsBackend := findVCSBackend(fpath)
			if vcsBackend == nil {
				return nil
			}

			repo, err := LocalRepositoryFromFullPath(fpath, vcsBackend)
			if err != nil || repo == nil {
				return nil
			}
			callback(repo)

			if isSymlink {
				return nil
			}
			return filepath.SkipDir
		}); err != nil {
			return err
		}
	}
	return nil
}

var _localRepositoryRoots []string

// localRepositoryRoots returns locally cloned repositories' root directories.
// The root dirs are determined as following:
//
//   - If GHQ_ROOT environment variable is nonempty, use it as the only root dir.
//   - Otherwise, use the result of `git config --get-all ghq.root` as the dirs.
//   - Otherwise, fallback to the default root, `~/.ghq`.
func localRepositoryRoots() ([]string, error) {
	if len(_localRepositoryRoots) != 0 {
		return _localRepositoryRoots, nil
	}

	envRoot := os.Getenv("GHQ_ROOT")
	if envRoot != "" {
		_localRepositoryRoots = filepath.SplitList(envRoot)
	} else {
		var err error
		_localRepositoryRoots, err = gitconfig.PathAll("ghq.root")
		if err != nil && !gitconfig.IsNotFound(err) {
			return nil, err
		}
	}

	if len(_localRepositoryRoots) == 0 {
		homeDir, err := os.UserHomeDir()
		if err != nil {
			return nil, err
		}
		_localRepositoryRoots = []string{filepath.Join(homeDir, ".ghq")}
	}

	for i, v := range _localRepositoryRoots {
		path := filepath.Clean(v)
		if _, err := os.Stat(path); err == nil {
			if path, err = filepath.EvalSymlinks(path); err != nil {
				return nil, err
			}
		}
		if !filepath.IsAbs(path) {
			var err error
			if path, err = filepath.Abs(path); err != nil {
				return nil, err
			}
		}
		_localRepositoryRoots[i] = path
	}

	return _localRepositoryRoots, nil
}

func primaryLocalRepositoryRoot() (string, error) {
	roots, err := localRepositoryRoots()
	if err != nil {
		return "", err
	}
	return roots[0], nil
}
<|endoftext|>