<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"runtime"

	"github.com/fiorix/go-web/autogzip"
	"github.com/gorilla/mux"
	"go.skia.org/infra/debugger/go/instances"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/iap"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

// flags
var (
	local        = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	port         = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
	promPort     = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':10110')")
	resourcesDir = flag.String("resources_dir", "", "The directory to find templates, JS, and CSS files. If blank the current directory will be used.")
)

var (
	templates *template.Template

	// repo is the Skia checkout.
	repo *gitinfo.GitInfo

	// build is responsible to building the LKGR of skiaserve periodically.
	build *buildskia.ContinuousBuilder

	// co handles proxying requests to skiaserve instances which is spins up and down.
	co *instances.Instances
)

func loadTemplates() {
	templates = template.Must(template.New("").ParseFiles(
		filepath.Join(*resourcesDir, "templates/index.html"),
		filepath.Join(*resourcesDir, "templates/admin.html"),
	))
}

func templateHandler(name string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if *local {
			loadTemplates()
		}
		if err := templates.ExecuteTemplate(w, name, struct{}{}); err != nil {
			sklog.Errorln("Failed to expand template:", err)
		}
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}
	if err := templates.ExecuteTemplate(w, "index.html", nil); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

func adminHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}
	if err := templates.ExecuteTemplate(w, "admin.html", co.DescribeAll()); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// loadHandler allows an SKP available on the open web to be downloaded into
// skiaserve for debugging.
//
// Expects a single query parameter of "url" that contains the URL of the SKP
// to download.
func loadHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}

	// Load the SKP from the given query parameter.
	client := httputils.NewTimeoutClient()
	resp, err := client.Get(r.FormValue("url"))
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to retrieve the SKP.")
		return
	}
	if resp.StatusCode != 200 {
		httputils.ReportError(w, r, err, "Failed to retrieve the SKP, bad status code.")
		return
	}
	defer util.Close(r.Body)
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to read body.")
		return
	}

	// Now package that SKP up in the multipart/form-file that skiaserve expects.
	body := &bytes.Buffer{}
	multipartWriter := multipart.NewWriter(body)
	formFile, err := multipartWriter.CreateFormFile("file", "file.skp")
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to create new multipart/form-file object to pass to skiaserve.")
		return
	}
	if _, err := formFile.Write(b); err != nil {
		httputils.ReportError(w, r, err, "Failed to copy SKP into multipart/form-file object to pass to skiaserve.")
		return
	}
	if err := multipartWriter.Close(); err != nil {
		httputils.ReportError(w, r, err, "Failed to close new multipart/form-file object to pass to skiaserve.")
		return
	}

	// POST the image down to skiaserve.
	instanceID := instances.NewInstanceID()
	req, err := http.NewRequest("POST", fmt.Sprintf("/%s/new", instanceID), body)
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to create new request object to pass to skiaserve.")
		return
	}
	// Copy over cookies so the request is authenticated.
	for _, c := range r.Cookies() {
		req.AddCookie(c)
	}
	req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", multipartWriter.Boundary()))
	rec := httptest.NewRecorder()
	co.ServeHTTP(rec, req)
	if rec.Code >= 400 {
		httputils.ReportError(w, r, fmt.Errorf("Bad status from SKP upload: Status %d Body %q", rec.Code, rec.Body.String()), "Failed to upload SKP.")
	} else {
		http.Redirect(w, r, fmt.Sprintf("/%s/", instanceID), 303)
	}
}

func Init() {
	if *resourcesDir == "" {
		_, filename, _, _ := runtime.Caller(0)
		*resourcesDir = filepath.Join(filepath.Dir(filename), "../..")
	}
	loadTemplates()
}

func makeResourceHandler() func(http.ResponseWriter, *http.Request) {
	fileServer := http.FileServer(http.Dir(*resourcesDir))
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", "max-age=300")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		fileServer.ServeHTTP(w, r)
	}
}

func main() {
	defer common.LogPanic()
	common.InitWithMust(
		"debugger",
		common.PrometheusOpt(promPort),
	)
	co = instances.New()

	Init()

	router := mux.NewRouter()
	router.PathPrefix("/res/").HandlerFunc(autogzip.HandleFunc(makeResourceHandler()))
	router.HandleFunc("/", mainHandler)
	router.HandleFunc("/admin", adminHandler)
	router.HandleFunc("/loadfrom", loadHandler)

	// All URLs that we don't understand will be routed to be handled by
	// skiaserve.
	router.NotFoundHandler = co

	h := httputils.LoggingRequestResponse(router)
	if !*local {
		h = iap.None(h)
	}
	http.Handle("/", h)

	sklog.Infoln("Ready to serve.")
	sklog.Fatal(http.ListenAndServe(*port, nil))
}
<commit_msg>[debugger] Add /healthz handler.<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"path/filepath"
	"runtime"

	"github.com/fiorix/go-web/autogzip"
	"github.com/gorilla/mux"
	"go.skia.org/infra/debugger/go/instances"
	"go.skia.org/infra/go/buildskia"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/git/gitinfo"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/iap"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
)

// flags
var (
	local        = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	port         = flag.String("port", ":8000", "HTTP service address (e.g., ':8000')")
	promPort     = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':10110')")
	resourcesDir = flag.String("resources_dir", "", "The directory to find templates, JS, and CSS files. If blank the current directory will be used.")
)

var (
	templates *template.Template

	// repo is the Skia checkout.
	repo *gitinfo.GitInfo

	// build is responsible to building the LKGR of skiaserve periodically.
	build *buildskia.ContinuousBuilder

	// co handles proxying requests to skiaserve instances which is spins up and down.
	co *instances.Instances
)

func loadTemplates() {
	templates = template.Must(template.New("").ParseFiles(
		filepath.Join(*resourcesDir, "templates/index.html"),
		filepath.Join(*resourcesDir, "templates/admin.html"),
	))
}

func templateHandler(name string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/html")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if *local {
			loadTemplates()
		}
		if err := templates.ExecuteTemplate(w, name, struct{}{}); err != nil {
			sklog.Errorln("Failed to expand template:", err)
		}
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}
	if err := templates.ExecuteTemplate(w, "index.html", nil); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

func adminHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}
	if err := templates.ExecuteTemplate(w, "admin.html", co.DescribeAll()); err != nil {
		sklog.Errorf("Failed to expand template: %s", err)
	}
}

// loadHandler allows an SKP available on the open web to be downloaded into
// skiaserve for debugging.
//
// Expects a single query parameter of "url" that contains the URL of the SKP
// to download.
func loadHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	if *local {
		loadTemplates()
	}

	// Load the SKP from the given query parameter.
	client := httputils.NewTimeoutClient()
	resp, err := client.Get(r.FormValue("url"))
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to retrieve the SKP.")
		return
	}
	if resp.StatusCode != 200 {
		httputils.ReportError(w, r, err, "Failed to retrieve the SKP, bad status code.")
		return
	}
	defer util.Close(r.Body)
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to read body.")
		return
	}

	// Now package that SKP up in the multipart/form-file that skiaserve expects.
	body := &bytes.Buffer{}
	multipartWriter := multipart.NewWriter(body)
	formFile, err := multipartWriter.CreateFormFile("file", "file.skp")
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to create new multipart/form-file object to pass to skiaserve.")
		return
	}
	if _, err := formFile.Write(b); err != nil {
		httputils.ReportError(w, r, err, "Failed to copy SKP into multipart/form-file object to pass to skiaserve.")
		return
	}
	if err := multipartWriter.Close(); err != nil {
		httputils.ReportError(w, r, err, "Failed to close new multipart/form-file object to pass to skiaserve.")
		return
	}

	// POST the image down to skiaserve.
	instanceID := instances.NewInstanceID()
	req, err := http.NewRequest("POST", fmt.Sprintf("/%s/new", instanceID), body)
	if err != nil {
		httputils.ReportError(w, r, err, "Failed to create new request object to pass to skiaserve.")
		return
	}
	// Copy over cookies so the request is authenticated.
	for _, c := range r.Cookies() {
		req.AddCookie(c)
	}
	req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", multipartWriter.Boundary()))
	rec := httptest.NewRecorder()
	co.ServeHTTP(rec, req)
	if rec.Code >= 400 {
		httputils.ReportError(w, r, fmt.Errorf("Bad status from SKP upload: Status %d Body %q", rec.Code, rec.Body.String()), "Failed to upload SKP.")
	} else {
		http.Redirect(w, r, fmt.Sprintf("/%s/", instanceID), 303)
	}
}

func Init() {
	if *resourcesDir == "" {
		_, filename, _, _ := runtime.Caller(0)
		*resourcesDir = filepath.Join(filepath.Dir(filename), "../..")
	}
	loadTemplates()
}

func makeResourceHandler() func(http.ResponseWriter, *http.Request) {
	fileServer := http.FileServer(http.Dir(*resourcesDir))
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", "max-age=300")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		fileServer.ServeHTTP(w, r)
	}
}

func main() {
	defer common.LogPanic()
	common.InitWithMust(
		"debugger",
		common.PrometheusOpt(promPort),
	)
	co = instances.New()

	Init()

	router := mux.NewRouter()
	router.PathPrefix("/res/").HandlerFunc(autogzip.HandleFunc(makeResourceHandler()))
	router.HandleFunc("/", mainHandler)
	router.HandleFunc("/healthz", httputils.HealthCheckHandler).Methods("GET")
	router.HandleFunc("/admin", adminHandler)
	router.HandleFunc("/loadfrom", loadHandler)

	// All URLs that we don't understand will be routed to be handled by
	// skiaserve.
	router.NotFoundHandler = co

	h := httputils.LoggingRequestResponse(router)
	if !*local {
		h = iap.None(h)
	}
	http.Handle("/", h)

	sklog.Infoln("Ready to serve.")
	sklog.Fatal(http.ListenAndServe(*port, nil))
}
<|endoftext|>


<commit_before>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013 Hajime Nakagami

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 firebirdsql

import (
    "database/sql/driver"
    )

type _result struct {
}
func (r *_result) LastInsertId() (int64, error) {
    return 0, nil
}

func (r *_result) RowsAffected() (int64, error) {
    return 0, nil
}

type firebirdsqlStmt struct {
    wp *wireProtocol
    stmtHandle int32
    tx *firebirdsqlTx
    xsqlda []xSQLVAR
    blr []byte
    stmtType int32
}

func (stmt *firebirdsqlStmt) Close() (err error) {
    stmt.wp.opFreeStatement(stmt.stmtHandle, 2)     // DSQL_drop
    return
}

func (stmt *firebirdsqlStmt) NumInput() int {
    return -1
}

func (stmt *firebirdsqlStmt) Exec(args []driver.Value) (result driver.Result, err error) {
    stmt.wp.opExecute(stmt.stmtHandle, stmt.tx.transHandle, args)
    _, _, _, err = stmt.wp.opResponse()

    result = new(_result)
    return
}

func (stmt *firebirdsqlStmt) Query(args []driver.Value) (rows driver.Rows, err error) {
    stmt.wp.opExecute(stmt.stmtHandle, stmt.tx.transHandle, args)
    _, _, _, err = stmt.wp.opResponse()
    rows = newFirebirdsqlRows(stmt)
    return
}

func newFirebirdsqlStmt(fc *firebirdsqlConn, query string) (stmt *firebirdsqlStmt, err error) {
    stmt = new(firebirdsqlStmt)
    stmt.wp = fc.wp
    stmt.tx = fc.tx
    fc.wp.opAllocateStatement()
    stmt.stmtHandle, _, _, err = fc.wp.opResponse()
    if err != nil {
        return
    }
    fc.wp.opPrepareStatement(stmt.stmtHandle, stmt.tx.transHandle, query)
    _, _, buf, err := fc.wp.opResponse()

    stmt.stmtType, stmt.xsqlda, err = fc.wp.parse_xsqlda(buf, stmt.stmtHandle)
    stmt.blr = calcBlr(stmt.xsqlda)

    return
}
<commit_msg>statement handle not free adhoc<commit_after>/*******************************************************************************
The MIT License (MIT)

Copyright (c) 2013 Hajime Nakagami

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 firebirdsql

import (
    "database/sql/driver"
    )

type _result struct {
}
func (r *_result) LastInsertId() (int64, error) {
    return 0, nil
}

func (r *_result) RowsAffected() (int64, error) {
    return 0, nil
}

type firebirdsqlStmt struct {
    wp *wireProtocol
    stmtHandle int32
    tx *firebirdsqlTx
    xsqlda []xSQLVAR
    blr []byte
    stmtType int32
}

func (stmt *firebirdsqlStmt) Close() (err error) {
//    stmt.wp.opFreeStatement(stmt.stmtHandle, 2)     // DSQL_drop
    return
}

func (stmt *firebirdsqlStmt) NumInput() int {
    return -1
}

func (stmt *firebirdsqlStmt) Exec(args []driver.Value) (result driver.Result, err error) {
    stmt.wp.opExecute(stmt.stmtHandle, stmt.tx.transHandle, args)
    _, _, _, err = stmt.wp.opResponse()

    result = new(_result)
    return
}

func (stmt *firebirdsqlStmt) Query(args []driver.Value) (rows driver.Rows, err error) {
    stmt.wp.opExecute(stmt.stmtHandle, stmt.tx.transHandle, args)
    _, _, _, err = stmt.wp.opResponse()
    rows = newFirebirdsqlRows(stmt)
    return
}

func newFirebirdsqlStmt(fc *firebirdsqlConn, query string) (stmt *firebirdsqlStmt, err error) {
    stmt = new(firebirdsqlStmt)
    stmt.wp = fc.wp
    stmt.tx = fc.tx
    fc.wp.opAllocateStatement()
    stmt.stmtHandle, _, _, err = fc.wp.opResponse()
    if err != nil {
        return
    }
    fc.wp.opPrepareStatement(stmt.stmtHandle, stmt.tx.transHandle, query)
    _, _, buf, err := fc.wp.opResponse()

    stmt.stmtType, stmt.xsqlda, err = fc.wp.parse_xsqlda(buf, stmt.stmtHandle)
    stmt.blr = calcBlr(stmt.xsqlda)

    return
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestWalkName(t *testing.T) {
	// ;; ANSWER SECTION:
	// www.facebook.com.			2256	IN	CNAME	star-mini.c10r.facebook.com.
	// star-mini.c10r.facebook.com. 25		IN	A	31.13.77.36
	b := []byte{
		0xe0, 0xba, 0x81, 0x80, 0x0, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
		// pointers
		// 12 0x0c: www.facebook.com
		0x03, 0x77, 0x77, 0x77,
		// 16 0x10: faceboo.com
		0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b,
		// 25 0x19: com
		0x03, 0x63, 0x6f, 0x6d,
		// termination
		0x00,

		0x00, 0x01,
		0x00, 0x01,

		// 34
		0xc0, 0x0c, // offset to 12 = 0x0c
		0x00, 0x05, // Type CNAME
		0x00, 0x01, // Class IN
		0x00, 0x00, 0x08, 0xd0, // TTL 2256
		0x00, 0x11, // RDLength 17

		// 46
		0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, // star-mini
		0x04, 0x63, 0x31, 0x30, 0x72, // c10r
		0xc0, 0x10, // offset to 16 = 0x10 "facebook.com"

		// 63
		0xc0, 0x2e, // offset
		0x00, 0x01, // Type A
		0x00, 0x01, // Class IN
		0x00, 0x00, 0x00, 0xc, // TTL
		0x00, 0x04, // RDLength
		0x1f, 0xd, 0x4d, 0x24, 0x00, // RData
	}

	for _, tt := range []struct {
		off int
		out string
	}{
		{12, "www.facebook.com."},
		{16, "facebook.com."},
		{25, "com."},
	} {
		if labels(b, tt.off) != tt.out {
			t.Errorf("labels(b, %v) did not match %v", tt.off, tt.out)
		}
	}

	for _, tt := range []struct {
		off0 int
		out  string
		off  int
		next byte
	}{
		{12, "www.facebook.com.", 30, 0x00},
		{46, "star-mini.c10r.facebook.com.", 63, 0xc0},
	} {
		out, off := decompName(b, tt.off0)
		if out != tt.out {
			t.Errorf("walkName(b, %v) expected output %v but got %v)", tt.off0, tt.out, out)
		}
		if off != tt.off {
			t.Errorf("walkName(b, %v) expected offset %v but got %v)", tt.off0, tt.off, off)
		}
		if b[off] != tt.next {
			t.Errorf("walkName(b, %v) expected next byte was %v but got %v)", tt.off0, tt.next, b[off])
		}
	}
}
<commit_msg>func name typo in test error messages<commit_after>package main

import "testing"

func TestWalkName(t *testing.T) {
	// ;; ANSWER SECTION:
	// www.facebook.com.			2256	IN	CNAME	star-mini.c10r.facebook.com.
	// star-mini.c10r.facebook.com. 25		IN	A	31.13.77.36
	b := []byte{
		0xe0, 0xba, 0x81, 0x80, 0x0, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
		// pointers
		// 12 0x0c: www.facebook.com
		0x03, 0x77, 0x77, 0x77,
		// 16 0x10: faceboo.com
		0x08, 0x66, 0x61, 0x63, 0x65, 0x62, 0x6f, 0x6f, 0x6b,
		// 25 0x19: com
		0x03, 0x63, 0x6f, 0x6d,
		// termination
		0x00,

		0x00, 0x01,
		0x00, 0x01,

		// 34
		0xc0, 0x0c, // offset to 12 = 0x0c
		0x00, 0x05, // Type CNAME
		0x00, 0x01, // Class IN
		0x00, 0x00, 0x08, 0xd0, // TTL 2256
		0x00, 0x11, // RDLength 17

		// 46
		0x09, 0x73, 0x74, 0x61, 0x72, 0x2d, 0x6d, 0x69, 0x6e, 0x69, // star-mini
		0x04, 0x63, 0x31, 0x30, 0x72, // c10r
		0xc0, 0x10, // offset to 16 = 0x10 "facebook.com"

		// 63
		0xc0, 0x2e, // offset
		0x00, 0x01, // Type A
		0x00, 0x01, // Class IN
		0x00, 0x00, 0x00, 0xc, // TTL
		0x00, 0x04, // RDLength
		0x1f, 0xd, 0x4d, 0x24, 0x00, // RData
	}

	for _, tt := range []struct {
		off int
		out string
	}{
		{12, "www.facebook.com."},
		{16, "facebook.com."},
		{25, "com."},
	} {
		if labels(b, tt.off) != tt.out {
			t.Errorf("labels(b, %v) did not match %v", tt.off, tt.out)
		}
	}

	for _, tt := range []struct {
		off0 int
		out  string
		off  int
		next byte
	}{
		{12, "www.facebook.com.", 30, 0x00},
		{46, "star-mini.c10r.facebook.com.", 63, 0xc0},
	} {
		out, off := decompName(b, tt.off0)
		if out != tt.out {
			t.Errorf("deompName(b, %v) expected output %v but got %v)", tt.off0, tt.out, out)
		}
		if off != tt.off {
			t.Errorf("deompName(b, %v) expected offset %v but got %v)", tt.off0, tt.off, off)
		}
		if b[off] != tt.next {
			t.Errorf("deompName(b, %v) expected next byte was %v but got %v)", tt.off0, tt.next, b[off])
		}
	}
}
<|endoftext|>


<commit_before>package statsdurl

import (
	"net/url"
	"os"

	"github.com/quipo/statsd"
)

func Connect(prefix string) (*statsd.StatsdClient, error) {
	return ConnectToURL(os.Getenv("STATSD_URL"))
}

func ConnectToURL(s string, prefix string) (c *statsd.StatsdClient, err error) {
	statsdUrl, err := url.Parse(s)

	if err != nil {
		return nil, err
	}

	c = statsd.NewStatsdClient(statsdUrl.Host, prefix)
	err = c.CreateSocket()
	return c, err
}
<commit_msg>Double derp<commit_after>package statsdurl

import (
	"net/url"
	"os"

	"github.com/quipo/statsd"
)

func Connect(prefix string) (*statsd.StatsdClient, error) {
	return ConnectToURL(os.Getenv("STATSD_URL"), prefix)
}

func ConnectToURL(s string, prefix string) (c *statsd.StatsdClient, err error) {
	statsdUrl, err := url.Parse(s)

	if err != nil {
		return nil, err
	}

	c = statsd.NewStatsdClient(statsdUrl.Host, prefix)
	err = c.CreateSocket()
	return c, err
}
<|endoftext|>


<commit_before>package auth

import (
	"fmt"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/mux"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

const (
	loginRequest      = "{\"username\": \"dummy\", \"password\": \"dummy\"}"
	dummySignedToken  = "dummy-token"
	dummyCookieHeader = "access_token=dummy-token; Path=/api; HttpOnly; Secure"
)

var (
	dummyToken *jwt.Token = jwt.New(jwt.SigningMethodHS256)
)

func TestLoginSuccess(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextLogin = dummyToken
	svc.nextSign = dummySignedToken

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(loginRequest))
	if err != nil {
		t.Error(err)
	}

	if resp.StatusCode != 204 {
		t.Errorf("Should return 204 on successful login.  Got: %v", resp.StatusCode)
	}

	header := resp.Header.Get("Set-Cookie")
	if len(header) == 0 {
		t.Fatalf("Didn't find Set-Cookie header on response: %v", err)
	}

	if header != dummyCookieHeader {
		t.Fatalf("Set-Cookie header didn't match expected.\nGot: %v\nExpected: %v", header, dummyCookieHeader)
	}
}

func TestLoginFailure(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextError = fmt.Errorf("fail")

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(loginRequest))
	if err != nil {
		t.Error(err)
	}

	if resp.StatusCode != 403 {
		t.Errorf("Should return 403 on failed login.  Got: %v", resp.StatusCode)
	}

}

func TestLoginWithNoBody(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(""))
	if err != nil {
		t.Fatal(err)
	}

	if resp.StatusCode != 400 {
		t.Errorf("Should return 400 for bad request.  Got %v.", resp.StatusCode)
	}
}

func createServer(service Service) *httptest.Server {
	r := mux.NewRouter()
	AddRoutes(r, service)
	return httptest.NewServer(r)
}

type dummyService struct {
	nextLogin *jwt.Token
	nextSign  string
	nextParse *jwt.Token
	nextError error
}

func (s *dummyService) Login(r LoginRequest) (*jwt.Token, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return nil, err
	}

	token := s.nextLogin
	s.nextLogin = nil
	return token, nil
}

func (s *dummyService) Sign(*jwt.Token) (string, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return "", err
	}

	ss := s.nextSign
	s.nextSign = ""
	return ss, nil
}

func (s *dummyService) Parse(ss string) (*jwt.Token, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return nil, err
	}

	token := s.nextParse
	s.nextParse = nil
	return token, nil
}
<commit_msg>Add further auth http tests<commit_after>package auth

import (
	"fmt"
	jwt "github.com/dgrijalva/jwt-go"
	"github.com/gorilla/mux"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
)

const (
	loginRequest      = "{\"username\": \"dummy\", \"password\": \"dummy\"}"
	dummySignedToken  = "dummy-token"
	dummyCookieHeader = "access_token=dummy-token; Path=/api; HttpOnly; Secure"
)

var (
	dummyCookie *http.Cookie = &http.Cookie{
		Name:  tokenCookieName,
		Value: "dummy-token",
	}

	dummyToken *jwt.Token = jwt.New(jwt.SigningMethodHS256)

	userInfoClaims = &Claims{
		jwt.StandardClaims{
			Subject: "test-user",
		},
	}
	dummyTokenWithUserInfo *jwt.Token = jwt.NewWithClaims(jwt.SigningMethodHS256, userInfoClaims)

	badClaims = &badClaimsType{
		Extra: "unexpected-data",
		StandardClaims: jwt.StandardClaims{
			Subject: "test-user",
		},
	}
	dummyTokenWithBadClaims *jwt.Token = jwt.NewWithClaims(jwt.SigningMethodHS256, badClaims)
)

func TestLoginSuccess(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextLogin = dummyToken
	svc.nextSign = dummySignedToken

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(loginRequest))
	if err != nil {
		t.Error(err)
	}

	if resp.StatusCode != 204 {
		t.Errorf("Should return 204 on successful login.  Got: %v", resp.StatusCode)
	}

	header := resp.Header.Get("Set-Cookie")
	if len(header) == 0 {
		t.Fatalf("Didn't find Set-Cookie header on response: %v", err)
	}

	if header != dummyCookieHeader {
		t.Fatalf("Set-Cookie header didn't match expected.\nGot: %v\nExpected: %v", header, dummyCookieHeader)
	}
}

func TestLoginFailure(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextError = fmt.Errorf("fail")

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(loginRequest))
	if err != nil {
		t.Error(err)
	}

	if resp.StatusCode != 403 {
		t.Errorf("Should return 403 on failed login.  Got: %v", resp.StatusCode)
	}

}

func TestLoginWithNoBody(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	resp, err := http.Post(svr.URL+"/login", "application/json", strings.NewReader(""))
	if err != nil {
		t.Fatal(err)
	}

	if resp.StatusCode != 400 {
		t.Errorf("Should return 400 for bad request.  Got %v.", resp.StatusCode)
	}
}

func TestUserInfoSuccess(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextParse = dummyTokenWithUserInfo

	req, err := http.NewRequest("GET", svr.URL+"/user", nil)
	if err != nil {
		t.Fatal(err)
	}

	req.AddCookie(dummyCookie)
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal(err)
	}

	if resp.StatusCode != 200 {
		t.Errorf("Should return 200 for successful user info request.  Got %v.", resp.StatusCode)
	}
}

func TestUserInfoNoCookie(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	req, err := http.NewRequest("GET", svr.URL+"/user", nil)
	if err != nil {
		t.Fatal(err)
	}

	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal(err)
	}

	if resp.StatusCode != 403 {
		t.Errorf("Should return 403 when no token cookie provided.  Got %v.", resp.StatusCode)
	}
}

func TestUserInfoBadCookie(t *testing.T) {
	svc := &dummyService{}
	svr := createServer(svc)
	defer svr.Close()

	svc.nextParse = dummyTokenWithBadClaims

	req, err := http.NewRequest("GET", svr.URL+"/user", nil)
	if err != nil {
		t.Fatal(err)
	}

	req.AddCookie(dummyCookie)
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		t.Fatal(err)
	}

	if resp.StatusCode != 403 {
		t.Errorf("Should return 403 for invalid token.  Got %v.", resp.StatusCode)
	}
}

func createServer(service Service) *httptest.Server {
	r := mux.NewRouter()
	AddRoutes(r, service)
	return httptest.NewServer(r)
}

type dummyService struct {
	nextLogin *jwt.Token
	nextSign  string
	nextParse *jwt.Token
	nextError error
}

func (s *dummyService) Login(r LoginRequest) (*jwt.Token, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return nil, err
	}

	token := s.nextLogin
	s.nextLogin = nil
	return token, nil
}

func (s *dummyService) Sign(*jwt.Token) (string, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return "", err
	}

	ss := s.nextSign
	s.nextSign = ""
	return ss, nil
}

func (s *dummyService) Parse(ss string) (*jwt.Token, error) {
	if s.nextError != nil {
		err := s.nextError
		s.nextError = nil
		return nil, err
	}

	token := s.nextParse
	s.nextParse = nil
	return token, nil
}

type badClaimsType struct {
	Extra string
	jwt.StandardClaims
}
<|endoftext|>


<commit_before>package organisations

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

	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/go-logger"
	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/service-status-go/gtg"
	"github.com/Financial-Times/transactionid-utils-go"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

type HTTPClient interface {
	Do(req *http.Request) (resp *http.Response, err error)
}

type OrganisationsHandler struct {
	client      HTTPClient
	conceptsURL string
}

// OrganisationDriver for cypher queries
var CacheControlHeader string

const (
	validUUID           = "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
	ontologyPrefix      = "http://www.ft.com/ontology"
	organisationSuffix  = "/organisation/Organisation"
	publicCompanySuffix = "/company/PublicCompany"
	relatedQueryParam   = "?showRelationship=related"
	isParentPredicate   = "/parentOrganisationOf"
	hasParentPredicate  = "/subOrganisationOf"
	issuedPredicate     = "/issued"
	thingsApiUrl        = "http://api.ft.com/things/"
	ftThing             = "http://www.ft.com/thing/"
)

func NewHandler(client HTTPClient, conceptsURL string) OrganisationsHandler {
	return OrganisationsHandler{
		client,
		conceptsURL,
	}
}

func (h *OrganisationsHandler) RegisterHandlers(router *mux.Router) {
	logger.Info("Registering handlers")
	mh := handlers.MethodHandler{
		"GET": http.HandlerFunc(h.GetOrganisation),
	}

	path := "/organisations/{uuid}"
	router.Handle(path, mh)
	router.HandleFunc(path, h.MethodNotAllowedHandler)
}

// HealthCheck does something
func (h *OrganisationsHandler) HealthCheck() fthealth.Check {
	return fthealth.Check{
		ID:               "public-concepts-api-check",
		BusinessImpact:   "Unable to respond to Public Organisations api requests",
		Name:             "Check connectivity to public-concepts-api",
		PanicGuide:       "https://dewey.ft.com/public-org-api.html",
		Severity:         2,
		TechnicalSummary: "Not being able to communicate with public-concepts-api means that requests for organisations cannot be performed.",
		Checker:          h.Checker,
	}
}

// Checker does more stuff
func (h *OrganisationsHandler) Checker() (string, error) {
	req, err := http.NewRequest("GET", h.conceptsURL+"/__gtg", nil)
	if err != nil {
		return "", err
	}

	req.Header.Add("User-Agent", "UPP public-organisations-api")

	resp, err := h.client.Do(req)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("health check returned a non-200 HTTP status: %v", resp.StatusCode)
	}
	return "Public Concepts API is healthy", nil

}

// Ping says pong
func Ping(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "pong")
}

// BuildInfoHandler - This is a stop gap and will be added to when we can define what we should display here
func (h *OrganisationsHandler) BuildInfoHandler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "build-info")
}

// MethodNotAllowedHandler does stuff
func (h *OrganisationsHandler) MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	return
}

// GetOrganisation is the public API
func (h *OrganisationsHandler) GetOrganisation(w http.ResponseWriter, r *http.Request) {
	uuidMatcher := regexp.MustCompile(validUUID)
	vars := mux.Vars(r)
	uuid := vars["uuid"]
	transID := transactionidutils.GetTransactionIDFromRequest(r)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	if uuid == "" || !uuidMatcher.MatchString(uuid) {
		msg := fmt.Sprintf(`uuid '%s' is either missing or invalid`, uuid)
		logger.WithTransactionID(transID).WithUUID(uuid).Error(msg)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "` + msg + `"}`))
		return
	}

	organisation, found, err := h.getOrganisationViaConceptsAPI(uuid, transID)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"message": "failed to return organisation"}`))
		return
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`{"message": "organisation not found"}`))
		return
	}
	//if the request was not made for the canonical, but an alternate uuid: redirect
	if !strings.Contains(organisation.ID, uuid) {
		validRegexp := regexp.MustCompile(validUUID)
		canonicalUUID := validRegexp.FindString(organisation.ID)
		redirectURL := strings.Replace(r.RequestURI, uuid, canonicalUUID, 1)
		w.Header().Set("Location", redirectURL)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	w.Header().Set("Cache-Control", CacheControlHeader)
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(organisation)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"message":"Organisation could not be marshelled, err=` + err.Error() + `"}`))
	}
}

//GoodToGo returns a 503 if the healthcheck fails - suitable for use from varnish to check availability of a node
func (h *OrganisationsHandler) GTG() gtg.Status {
	statusCheck := func() gtg.Status {
		return gtgCheck(h.Checker)
	}
	return gtg.FailFastParallelCheck([]gtg.StatusChecker{statusCheck})()
}

func gtgCheck(handler func() (string, error)) gtg.Status {
	if _, err := handler(); err != nil {
		return gtg.Status{GoodToGo: false, Message: err.Error()}
	}
	return gtg.Status{GoodToGo: true}
}

func (h *OrganisationsHandler) getOrganisationViaConceptsAPI(uuid string, transID string) (organisation Organisation, found bool, err error) {
	org := Organisation{}

	reqURL := h.conceptsURL + "/concepts/" + uuid + relatedQueryParam

	request, err := http.NewRequest("GET", reqURL, nil)

	if err != nil {
		msg := fmt.Sprintf("failed to create request to %s", reqURL)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}

	request.Header.Set("X-Request-Id", transID)
	resp, err := h.client.Do(request)
	if err != nil {
		msg := fmt.Sprintf("request to %s returned status: %d", reqURL, resp.StatusCode)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}
	if resp.StatusCode == http.StatusNotFound {
		return org, false, nil
	}

	conceptsApiResponse := ConceptApiResponse{}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		msg := fmt.Sprintf("failed to read response body: %v", resp.Body)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}
	if err = json.Unmarshal(body, &conceptsApiResponse); err != nil {
		msg := fmt.Sprintf("failed to unmarshal response body: %v", body)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}

	if conceptsApiResponse.Type != ontologyPrefix+organisationSuffix && conceptsApiResponse.Type != ontologyPrefix+publicCompanySuffix {
		logger.WithTransactionID(transID).WithUUID(uuid).Info("requested concept is not a organisation")
		return org, false, nil
	}

	org.ID = convertID(conceptsApiResponse.ID)
	org.APIURL = convertApiUrl(conceptsApiResponse.ApiURL, "organisations")
	org.PrefLabel = conceptsApiResponse.PrefLabel
	org.Types = mapper.FullTypeHierarchy(conceptsApiResponse.Type)
	org.DirectType = conceptsApiResponse.Type
	org.PostalCode = conceptsApiResponse.PostalCode
	org.CountryCode = conceptsApiResponse.CountryCode
	org.CountryOfIncorporation = conceptsApiResponse.CountryOfIncorporation
	org.LegalEntityIdentifier = conceptsApiResponse.LeiCode
	org.YearFounded = conceptsApiResponse.YearFounded
	org.IsDeprecated = conceptsApiResponse.IsDeprecated

	formerNames := []string{}
	m := make(map[string]bool)
	uniqLabel := []string{}
	for _, label := range conceptsApiResponse.AlternativeLabels {
		compare := func(expected string) bool {
			return strings.TrimPrefix(label.Type, ontologyPrefix) == expected
		}
		switch {
		case compare("/properName"):
			org.ProperName = label.Value
		case compare("/shortName"):
			org.ShortName = label.Value
		case compare("/hiddenLabel"):
			org.HiddenLabel = label.Value
		case compare("/formerName"):
			formerNames = append(formerNames, label.Value)
		}

		if !m[label.Value] {
			m[label.Value] = true
			uniqLabel = append(uniqLabel, label.Value)
		}
	}
	if len(formerNames) > 0 {
		org.FormerNames = formerNames
	}
	if len(uniqLabel) > 0 {
		org.Labels = uniqLabel
	}

	var subsidiaries = []Subsidiary{}
	for _, item := range conceptsApiResponse.Related {
		c := item.Concept
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == hasParentPredicate {
			parent := &Parent{}
			parent.ID = convertID(c.ID)
			parent.APIURL = convertApiUrl(c.ApiURL, "organisations")
			parent.PrefLabel = c.PrefLabel
			parent.DirectType = c.Type
			parent.Types = mapper.FullTypeHierarchy(c.Type)
			org.Parent = parent
		}
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == isParentPredicate {
			subsidiary := Subsidiary{}
			subsidiary.ID = convertID(c.ID)
			subsidiary.APIURL = convertApiUrl(c.ApiURL, "organisations")
			subsidiary.PrefLabel = c.PrefLabel
			subsidiary.DirectType = c.Type
			subsidiary.Types = mapper.FullTypeHierarchy(c.Type)
			subsidiaries = append(subsidiaries, subsidiary)
		}
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == issuedPredicate {
			f := &FinancialInstrument{}
			f.ID = convertID(c.ID)
			f.APIURL = convertApiUrl(c.ApiURL, "things")
			f.PrefLabel = c.PrefLabel
			f.DirectType = c.Type
			f.Types = mapper.FullTypeHierarchy(c.Type)
			f.Figi = c.Figi
			org.FinancialInstrument = f
		}
	}
	if len(subsidiaries) > 0 {
		org.Subsidiaries = subsidiaries
	}

	return org, true, nil
}

func convertApiUrl(conceptsApiUrl string, desired string) string {
	return strings.Replace(conceptsApiUrl, "concepts", desired, 1)
}

func convertID(conceptsApiID string) string {
	return strings.Replace(conceptsApiID, ftThing, thingsApiUrl, 1)
}
<commit_msg>Fixing memory leak.<commit_after>package organisations

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

	fthealth "github.com/Financial-Times/go-fthealth/v1_1"
	"github.com/Financial-Times/go-logger"
	"github.com/Financial-Times/neo-model-utils-go/mapper"
	"github.com/Financial-Times/service-status-go/gtg"
	"github.com/Financial-Times/transactionid-utils-go"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

type HTTPClient interface {
	Do(req *http.Request) (resp *http.Response, err error)
}

type OrganisationsHandler struct {
	client      HTTPClient
	conceptsURL string
}

// OrganisationDriver for cypher queries
var CacheControlHeader string

const (
	validUUID           = "([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$"
	ontologyPrefix      = "http://www.ft.com/ontology"
	organisationSuffix  = "/organisation/Organisation"
	publicCompanySuffix = "/company/PublicCompany"
	relatedQueryParam   = "?showRelationship=related"
	isParentPredicate   = "/parentOrganisationOf"
	hasParentPredicate  = "/subOrganisationOf"
	issuedPredicate     = "/issued"
	thingsApiUrl        = "http://api.ft.com/things/"
	ftThing             = "http://www.ft.com/thing/"
)

func NewHandler(client HTTPClient, conceptsURL string) OrganisationsHandler {
	return OrganisationsHandler{
		client,
		conceptsURL,
	}
}

func (h *OrganisationsHandler) RegisterHandlers(router *mux.Router) {
	logger.Info("Registering handlers")
	mh := handlers.MethodHandler{
		"GET": http.HandlerFunc(h.GetOrganisation),
	}

	path := "/organisations/{uuid}"
	router.Handle(path, mh)
	router.HandleFunc(path, h.MethodNotAllowedHandler)
}

// HealthCheck does something
func (h *OrganisationsHandler) HealthCheck() fthealth.Check {
	return fthealth.Check{
		ID:               "public-concepts-api-check",
		BusinessImpact:   "Unable to respond to Public Organisations api requests",
		Name:             "Check connectivity to public-concepts-api",
		PanicGuide:       "https://dewey.ft.com/public-org-api.html",
		Severity:         2,
		TechnicalSummary: "Not being able to communicate with public-concepts-api means that requests for organisations cannot be performed.",
		Checker:          h.Checker,
	}
}

// Checker does more stuff
func (h *OrganisationsHandler) Checker() (string, error) {
	req, err := http.NewRequest("GET", h.conceptsURL+"/__gtg", nil)
	if err != nil {
		return "", err
	}

	req.Header.Add("User-Agent", "UPP public-organisations-api")

	resp, err := h.client.Do(req)
	if err != nil {
		return "", err
	}

	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("health check returned a non-200 HTTP status: %v", resp.StatusCode)
	}
	return "Public Concepts API is healthy", nil

}

// Ping says pong
func Ping(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "pong")
}

// BuildInfoHandler - This is a stop gap and will be added to when we can define what we should display here
func (h *OrganisationsHandler) BuildInfoHandler(w http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(w, "build-info")
}

// MethodNotAllowedHandler does stuff
func (h *OrganisationsHandler) MethodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusMethodNotAllowed)
	return
}

// GetOrganisation is the public API
func (h *OrganisationsHandler) GetOrganisation(w http.ResponseWriter, r *http.Request) {
	uuidMatcher := regexp.MustCompile(validUUID)
	vars := mux.Vars(r)
	uuid := vars["uuid"]
	transID := transactionidutils.GetTransactionIDFromRequest(r)

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	if uuid == "" || !uuidMatcher.MatchString(uuid) {
		msg := fmt.Sprintf(`uuid '%s' is either missing or invalid`, uuid)
		logger.WithTransactionID(transID).WithUUID(uuid).Error(msg)
		w.WriteHeader(http.StatusBadRequest)
		w.Write([]byte(`{"message": "` + msg + `"}`))
		return
	}

	organisation, found, err := h.getOrganisationViaConceptsAPI(uuid, transID)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"message": "failed to return organisation"}`))
		return
	}
	if !found {
		w.WriteHeader(http.StatusNotFound)
		w.Write([]byte(`{"message": "organisation not found"}`))
		return
	}
	//if the request was not made for the canonical, but an alternate uuid: redirect
	if !strings.Contains(organisation.ID, uuid) {
		validRegexp := regexp.MustCompile(validUUID)
		canonicalUUID := validRegexp.FindString(organisation.ID)
		redirectURL := strings.Replace(r.RequestURI, uuid, canonicalUUID, 1)
		w.Header().Set("Location", redirectURL)
		w.WriteHeader(http.StatusMovedPermanently)
		return
	}

	w.Header().Set("Cache-Control", CacheControlHeader)
	w.WriteHeader(http.StatusOK)
	err = json.NewEncoder(w).Encode(organisation)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte(`{"message":"Organisation could not be marshelled, err=` + err.Error() + `"}`))
	}
}

//GoodToGo returns a 503 if the healthcheck fails - suitable for use from varnish to check availability of a node
func (h *OrganisationsHandler) GTG() gtg.Status {
	statusCheck := func() gtg.Status {
		return gtgCheck(h.Checker)
	}
	return gtg.FailFastParallelCheck([]gtg.StatusChecker{statusCheck})()
}

func gtgCheck(handler func() (string, error)) gtg.Status {
	if _, err := handler(); err != nil {
		return gtg.Status{GoodToGo: false, Message: err.Error()}
	}
	return gtg.Status{GoodToGo: true}
}

func (h *OrganisationsHandler) getOrganisationViaConceptsAPI(uuid string, transID string) (organisation Organisation, found bool, err error) {
	org := Organisation{}

	reqURL := h.conceptsURL + "/concepts/" + uuid + relatedQueryParam

	request, err := http.NewRequest("GET", reqURL, nil)

	if err != nil {
		msg := fmt.Sprintf("failed to create request to %s", reqURL)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}

	request.Header.Set("X-Request-Id", transID)
	resp, err := h.client.Do(request)
	if err != nil {
		msg := fmt.Sprintf("request to %s was unsuccessful", reqURL)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}
	if resp.StatusCode == http.StatusNotFound {
		return org, false, nil
	}

	conceptsApiResponse := ConceptApiResponse{}
	body, err := ioutil.ReadAll(resp.Body)
	defer resp.Body.Close()
	if err != nil {
		msg := fmt.Sprintf("failed to read response body: %v", resp.Body)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}
	if err = json.Unmarshal(body, &conceptsApiResponse); err != nil {
		msg := fmt.Sprintf("failed to unmarshal response body: %v", body)
		logger.WithError(err).WithUUID(uuid).WithTransactionID(transID).Error(msg)
		return org, false, err
	}

	if conceptsApiResponse.Type != ontologyPrefix+organisationSuffix && conceptsApiResponse.Type != ontologyPrefix+publicCompanySuffix {
		logger.WithTransactionID(transID).WithUUID(uuid).Info("requested concept is not a organisation")
		return org, false, nil
	}

	org.ID = convertID(conceptsApiResponse.ID)
	org.APIURL = convertApiUrl(conceptsApiResponse.ApiURL, "organisations")
	org.PrefLabel = conceptsApiResponse.PrefLabel
	org.Types = mapper.FullTypeHierarchy(conceptsApiResponse.Type)
	org.DirectType = conceptsApiResponse.Type
	org.PostalCode = conceptsApiResponse.PostalCode
	org.CountryCode = conceptsApiResponse.CountryCode
	org.CountryOfIncorporation = conceptsApiResponse.CountryOfIncorporation
	org.LegalEntityIdentifier = conceptsApiResponse.LeiCode
	org.YearFounded = conceptsApiResponse.YearFounded
	org.IsDeprecated = conceptsApiResponse.IsDeprecated

	formerNames := []string{}
	m := make(map[string]bool)
	uniqLabel := []string{}
	for _, label := range conceptsApiResponse.AlternativeLabels {
		compare := func(expected string) bool {
			return strings.TrimPrefix(label.Type, ontologyPrefix) == expected
		}
		switch {
		case compare("/properName"):
			org.ProperName = label.Value
		case compare("/shortName"):
			org.ShortName = label.Value
		case compare("/hiddenLabel"):
			org.HiddenLabel = label.Value
		case compare("/formerName"):
			formerNames = append(formerNames, label.Value)
		}

		if !m[label.Value] {
			m[label.Value] = true
			uniqLabel = append(uniqLabel, label.Value)
		}
	}
	if len(formerNames) > 0 {
		org.FormerNames = formerNames
	}
	if len(uniqLabel) > 0 {
		org.Labels = uniqLabel
	}

	var subsidiaries = []Subsidiary{}
	for _, item := range conceptsApiResponse.Related {
		c := item.Concept
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == hasParentPredicate {
			parent := &Parent{}
			parent.ID = convertID(c.ID)
			parent.APIURL = convertApiUrl(c.ApiURL, "organisations")
			parent.PrefLabel = c.PrefLabel
			parent.DirectType = c.Type
			parent.Types = mapper.FullTypeHierarchy(c.Type)
			org.Parent = parent
		}
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == isParentPredicate {
			subsidiary := Subsidiary{}
			subsidiary.ID = convertID(c.ID)
			subsidiary.APIURL = convertApiUrl(c.ApiURL, "organisations")
			subsidiary.PrefLabel = c.PrefLabel
			subsidiary.DirectType = c.Type
			subsidiary.Types = mapper.FullTypeHierarchy(c.Type)
			subsidiaries = append(subsidiaries, subsidiary)
		}
		if strings.TrimPrefix(item.Predicate, ontologyPrefix) == issuedPredicate {
			f := &FinancialInstrument{}
			f.ID = convertID(c.ID)
			f.APIURL = convertApiUrl(c.ApiURL, "things")
			f.PrefLabel = c.PrefLabel
			f.DirectType = c.Type
			f.Types = mapper.FullTypeHierarchy(c.Type)
			f.Figi = c.Figi
			org.FinancialInstrument = f
		}
	}
	if len(subsidiaries) > 0 {
		org.Subsidiaries = subsidiaries
	}

	return org, true, nil
}

func convertApiUrl(conceptsApiUrl string, desired string) string {
	return strings.Replace(conceptsApiUrl, "concepts", desired, 1)
}

func convertID(conceptsApiID string) string {
	return strings.Replace(conceptsApiID, ftThing, thingsApiUrl, 1)
}
<|endoftext|>


<commit_before>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package semtech

import (
	"fmt"
	"net"

	"github.com/TheThingsNetwork/ttn/core"
	. "github.com/TheThingsNetwork/ttn/core/errors"
	"github.com/TheThingsNetwork/ttn/semtech"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/stats"
	"github.com/apex/log"
)

// Adapter represents a semtech adapter which sends and receives packet via UDP in respect of the
// semtech forwarder protocol
type Adapter struct {
	ctx  log.Interface // Just a logger
	conn chan udpMsg   // Channel used to manage response transmissions made by multiple goroutines
	next chan rxpkMsg  // Incoming valid RXPK packets are pushed to this channel
}

// udpMsg type materializes response messages transmitted towards existing recipients (commonly,
// gateways).
type udpMsg struct {
	conn *net.UDPConn // Provide if you intent to change the current adapter connection
	addr *net.UDPAddr // The target recipient address
	raw  []byte       // The raw byte sequence that has to be sent
}

// rxpkMsg type materializes valid uplink messages coming from a given recipient
type rxpkMsg struct {
	rxpk      semtech.RXPK   // The actual RXPK message
	recipient core.Recipient // The address and id of the source emitter
}

// NewAdapter constructs and allocates a new semtech adapter
func NewAdapter(port uint, ctx log.Interface) (*Adapter, error) {
	a := Adapter{
		ctx:  ctx,
		conn: make(chan udpMsg),
		next: make(chan rxpkMsg),
	}

	// Create the udp connection and start listening with a goroutine
	var udpConn *net.UDPConn
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("0.0.0.0:%d", port))
	a.ctx.WithField("port", port).Info("Starting Server")
	if udpConn, err = net.ListenUDP("udp", addr); err != nil {
		a.ctx.WithError(err).Error("Unable to start server")
		return nil, errors.New(ErrInvalidStructure, fmt.Sprintf("Invalid port %v", port))
	}

	go a.monitorConnection()
	a.conn <- udpMsg{conn: udpConn}
	go a.listen(udpConn) // Terminates when the connection is closed

	return &a, nil
}

// Send implements the core.Adapter interface. Not implemented for the semtech adapter.
func (a *Adapter) Send(p core.Packet, r ...core.Recipient) (core.Packet, error) {
	return core.Packet{}, errors.New(ErrNotSupported, "Send not supported on semtech adapter")
}

// Next implements the core.Adapter interface
func (a *Adapter) Next() (core.Packet, core.AckNacker, error) {
	msg := <-a.next
	packet, err := core.ConvertRXPK(msg.rxpk)
	if err != nil {
		a.ctx.Debug("Received invalid packet")
		return core.Packet{}, nil, errors.New(ErrInvalidStructure, err)
	}
	return packet, semtechAckNacker{recipient: msg.recipient, conn: a.conn}, nil
}

// NextRegistration implements the core.Adapter interface
func (a *Adapter) NextRegistration() (core.Registration, core.AckNacker, error) {
	return core.Registration{}, nil, errors.New(ErrNotSupported, "NextRegistration not supported on semtech adapter")
}

// listen Handle incoming packets and forward them
func (a *Adapter) listen(conn *net.UDPConn) {
	defer conn.Close()
	a.ctx.WithField("address", conn.LocalAddr()).Debug("Starting accept loop")
	for {
		// The Semtech packet forwarder uses a buffer size of ((540 * NB_PKT_MAX) + 30 + STATUS_SIZE)
		// Where NB_PKT_MAX = 8 and STATUS_SIZE = 200
		// We are collecting statistics to see if we can reduce the buffer size (see below).
		buf := make([]byte, 4550)

		n, addr, err := conn.ReadFromUDP(buf)
		if err != nil { // Problem with the connection
			a.ctx.WithError(err).Error("Connection error")
			continue
		}
		a.ctx.Debug("Incoming datagram")

		// Collect statistics for buffer size reduction
		stats.UpdateHistogram("semtech_adapter.push_data.size", int64(len(buf)))

		pkt := new(semtech.Packet)
		err = pkt.UnmarshalBinary(buf[:n])
		if err != nil {
			a.ctx.WithError(err).Warn("Invalid packet")
			continue
		}

		switch pkt.Identifier {
		case semtech.PULL_DATA: // PULL_DATA -> Respond to the recipient with an ACK
			stats.MarkMeter("semtech_adapter.pull_data")
			pullAck, err := semtech.Packet{
				Version:    semtech.VERSION,
				Token:      pkt.Token,
				Identifier: semtech.PULL_ACK,
			}.MarshalBinary()
			if err != nil {
				a.ctx.WithError(err).Error("Unexpected error while marshaling PULL_ACK")
				continue
			}
			a.ctx.WithField("recipient", addr).Debug("Sending PULL_ACK")
			a.conn <- udpMsg{addr: addr, raw: pullAck}
		case semtech.PUSH_DATA: // PUSH_DATA -> Transfer all RXPK to the component
			stats.MarkMeter("semtech_adapter.push_data")
			pushAck, err := semtech.Packet{
				Version:    semtech.VERSION,
				Token:      pkt.Token,
				Identifier: semtech.PUSH_ACK,
			}.MarshalBinary()
			if err != nil {
				a.ctx.WithError(err).Error("Unexpected error while marshaling PUSH_ACK")
				continue
			}
			a.ctx.WithField("Recipient", addr).Debug("Sending PUSH_ACK")
			a.conn <- udpMsg{addr: addr, raw: pushAck}

			if pkt.Payload == nil {
				a.ctx.WithField("packet", pkt).Warn("Invalid PUSH_DATA packet")
				continue
			}
			for _, rxpk := range pkt.Payload.RXPK {
				a.next <- rxpkMsg{
					rxpk:      rxpk,
					recipient: core.Recipient{Address: addr, Id: pkt.GatewayId},
				}
			}
		default:
			a.ctx.WithField("packet", pkt).Debug("Ignoring unexpected packet")
			continue
		}
	}
}

// monitorConnection manages udpConnection of the adapter and send message through that connection
//
// That function executes into a single goroutine and is the only one allowed to write UDP messages.
// Doing this makes sure that only 1 goroutine is interacting with the connection. It thereby allows
// the connection to be replaced at any moment (in case of failure for instance) without disturbing
// the ongoing process.
func (a *Adapter) monitorConnection() {
	var udpConn *net.UDPConn
	for msg := range a.conn {
		if msg.conn != nil { // Change the connection
			if udpConn != nil {
				a.ctx.Debug("Define new UDP connection")
				udpConn.Close()
			}
			udpConn = msg.conn
		}

		if udpConn != nil && msg.raw != nil { // Send the given udp message
			if _, err := udpConn.WriteToUDP(msg.raw, msg.addr); err != nil {
				a.ctx.WithError(err).Error("Error while sending UDP message")
			}
		}
	}
	if udpConn != nil {
		udpConn.Close() // Make sure we close the connection before leaving if we dare ever leave.
	}
}
<commit_msg>Get the actual size of the datagram<commit_after>// Copyright © 2015 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package semtech

import (
	"fmt"
	"net"

	"github.com/TheThingsNetwork/ttn/core"
	. "github.com/TheThingsNetwork/ttn/core/errors"
	"github.com/TheThingsNetwork/ttn/semtech"
	"github.com/TheThingsNetwork/ttn/utils/errors"
	"github.com/TheThingsNetwork/ttn/utils/stats"
	"github.com/apex/log"
)

// Adapter represents a semtech adapter which sends and receives packet via UDP in respect of the
// semtech forwarder protocol
type Adapter struct {
	ctx  log.Interface // Just a logger
	conn chan udpMsg   // Channel used to manage response transmissions made by multiple goroutines
	next chan rxpkMsg  // Incoming valid RXPK packets are pushed to this channel
}

// udpMsg type materializes response messages transmitted towards existing recipients (commonly,
// gateways).
type udpMsg struct {
	conn *net.UDPConn // Provide if you intent to change the current adapter connection
	addr *net.UDPAddr // The target recipient address
	raw  []byte       // The raw byte sequence that has to be sent
}

// rxpkMsg type materializes valid uplink messages coming from a given recipient
type rxpkMsg struct {
	rxpk      semtech.RXPK   // The actual RXPK message
	recipient core.Recipient // The address and id of the source emitter
}

// NewAdapter constructs and allocates a new semtech adapter
func NewAdapter(port uint, ctx log.Interface) (*Adapter, error) {
	a := Adapter{
		ctx:  ctx,
		conn: make(chan udpMsg),
		next: make(chan rxpkMsg),
	}

	// Create the udp connection and start listening with a goroutine
	var udpConn *net.UDPConn
	addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("0.0.0.0:%d", port))
	a.ctx.WithField("port", port).Info("Starting Server")
	if udpConn, err = net.ListenUDP("udp", addr); err != nil {
		a.ctx.WithError(err).Error("Unable to start server")
		return nil, errors.New(ErrInvalidStructure, fmt.Sprintf("Invalid port %v", port))
	}

	go a.monitorConnection()
	a.conn <- udpMsg{conn: udpConn}
	go a.listen(udpConn) // Terminates when the connection is closed

	return &a, nil
}

// Send implements the core.Adapter interface. Not implemented for the semtech adapter.
func (a *Adapter) Send(p core.Packet, r ...core.Recipient) (core.Packet, error) {
	return core.Packet{}, errors.New(ErrNotSupported, "Send not supported on semtech adapter")
}

// Next implements the core.Adapter interface
func (a *Adapter) Next() (core.Packet, core.AckNacker, error) {
	msg := <-a.next
	packet, err := core.ConvertRXPK(msg.rxpk)
	if err != nil {
		a.ctx.Debug("Received invalid packet")
		return core.Packet{}, nil, errors.New(ErrInvalidStructure, err)
	}
	return packet, semtechAckNacker{recipient: msg.recipient, conn: a.conn}, nil
}

// NextRegistration implements the core.Adapter interface
func (a *Adapter) NextRegistration() (core.Registration, core.AckNacker, error) {
	return core.Registration{}, nil, errors.New(ErrNotSupported, "NextRegistration not supported on semtech adapter")
}

// listen Handle incoming packets and forward them
func (a *Adapter) listen(conn *net.UDPConn) {
	defer conn.Close()
	a.ctx.WithField("address", conn.LocalAddr()).Debug("Starting accept loop")
	for {
		// The Semtech packet forwarder uses a buffer size of ((540 * NB_PKT_MAX) + 30 + STATUS_SIZE)
		// Where NB_PKT_MAX = 8 and STATUS_SIZE = 200
		// We are collecting statistics to see if we can reduce the buffer size (see below).
		buf := make([]byte, 4550)

		n, addr, err := conn.ReadFromUDP(buf)
		if err != nil { // Problem with the connection
			a.ctx.WithError(err).Error("Connection error")
			continue
		}
		a.ctx.Debug("Incoming datagram")

		// Collect statistics for buffer size reduction
		stats.UpdateHistogram("semtech_adapter.push_data.size", int64(n))

		pkt := new(semtech.Packet)
		err = pkt.UnmarshalBinary(buf[:n])
		if err != nil {
			a.ctx.WithError(err).Warn("Invalid packet")
			continue
		}

		switch pkt.Identifier {
		case semtech.PULL_DATA: // PULL_DATA -> Respond to the recipient with an ACK
			stats.MarkMeter("semtech_adapter.pull_data")
			pullAck, err := semtech.Packet{
				Version:    semtech.VERSION,
				Token:      pkt.Token,
				Identifier: semtech.PULL_ACK,
			}.MarshalBinary()
			if err != nil {
				a.ctx.WithError(err).Error("Unexpected error while marshaling PULL_ACK")
				continue
			}
			a.ctx.WithField("recipient", addr).Debug("Sending PULL_ACK")
			a.conn <- udpMsg{addr: addr, raw: pullAck}
		case semtech.PUSH_DATA: // PUSH_DATA -> Transfer all RXPK to the component
			stats.MarkMeter("semtech_adapter.push_data")
			pushAck, err := semtech.Packet{
				Version:    semtech.VERSION,
				Token:      pkt.Token,
				Identifier: semtech.PUSH_ACK,
			}.MarshalBinary()
			if err != nil {
				a.ctx.WithError(err).Error("Unexpected error while marshaling PUSH_ACK")
				continue
			}
			a.ctx.WithField("Recipient", addr).Debug("Sending PUSH_ACK")
			a.conn <- udpMsg{addr: addr, raw: pushAck}

			if pkt.Payload == nil {
				a.ctx.WithField("packet", pkt).Warn("Invalid PUSH_DATA packet")
				continue
			}
			for _, rxpk := range pkt.Payload.RXPK {
				a.next <- rxpkMsg{
					rxpk:      rxpk,
					recipient: core.Recipient{Address: addr, Id: pkt.GatewayId},
				}
			}
		default:
			a.ctx.WithField("packet", pkt).Debug("Ignoring unexpected packet")
			continue
		}
	}
}

// monitorConnection manages udpConnection of the adapter and send message through that connection
//
// That function executes into a single goroutine and is the only one allowed to write UDP messages.
// Doing this makes sure that only 1 goroutine is interacting with the connection. It thereby allows
// the connection to be replaced at any moment (in case of failure for instance) without disturbing
// the ongoing process.
func (a *Adapter) monitorConnection() {
	var udpConn *net.UDPConn
	for msg := range a.conn {
		if msg.conn != nil { // Change the connection
			if udpConn != nil {
				a.ctx.Debug("Define new UDP connection")
				udpConn.Close()
			}
			udpConn = msg.conn
		}

		if udpConn != nil && msg.raw != nil { // Send the given udp message
			if _, err := udpConn.WriteToUDP(msg.raw, msg.addr); err != nil {
				a.ctx.WithError(err).Error("Error while sending UDP message")
			}
		}
	}
	if udpConn != nil {
		udpConn.Close() // Make sure we close the connection before leaving if we dare ever leave.
	}
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"fmt"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/pkg/errors"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t profiles.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e profile names
//go:generate mapper stmt -p db -e profile names-by-Project
//go:generate mapper stmt -p db -e profile names-by-Project-and-Name
//go:generate mapper stmt -p db -e profile objects
//go:generate mapper stmt -p db -e profile objects-by-Project
//go:generate mapper stmt -p db -e profile objects-by-Project-and-Name
//go:generate mapper stmt -p db -e profile config-ref
//go:generate mapper stmt -p db -e profile config-ref-by-Project
//go:generate mapper stmt -p db -e profile config-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile devices-ref
//go:generate mapper stmt -p db -e profile devices-ref-by-Project
//go:generate mapper stmt -p db -e profile devices-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile used-by-ref
//go:generate mapper stmt -p db -e profile used-by-ref-by-Project
//go:generate mapper stmt -p db -e profile used-by-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile id
//go:generate mapper stmt -p db -e profile create struct=Profile
//go:generate mapper stmt -p db -e profile create-config-ref
//go:generate mapper stmt -p db -e profile create-devices-ref
//go:generate mapper stmt -p db -e profile rename
//go:generate mapper stmt -p db -e profile delete-by-Project-and-Name
//go:generate mapper stmt -p db -e profile delete-config-ref
//go:generate mapper stmt -p db -e profile delete-devices-ref
//go:generate mapper stmt -p db -e profile update struct=Profile
//
//go:generate mapper method -p db -e profile URIs
//go:generate mapper method -p db -e profile List
//go:generate mapper method -p db -e profile Get
//go:generate mapper method -p db -e profile Exists struct=Profile
//go:generate mapper method -p db -e profile ID struct=Profile
//go:generate mapper method -p db -e profile ConfigRef
//go:generate mapper method -p db -e profile DevicesRef
//go:generate mapper method -p db -e profile UsedByRef
//go:generate mapper method -p db -e profile Create struct=Profile
//go:generate mapper method -p db -e profile Rename
//go:generate mapper method -p db -e profile DeleteOne
//go:generate mapper method -p db -e profile Update struct=Profile

// Profile is a value object holding db-related details about a profile.
type Profile struct {
	ID          int
	Project     string `db:"primary=yes&join=projects.name"`
	Name        string `db:"primary=yes"`
	Description string `db:"coalesce=''"`
	Config      map[string]string
	Devices     map[string]map[string]string
	UsedBy      []string
}

// ProfileToAPI is a convenience to convert a Profile db struct into
// an API profile struct.
func ProfileToAPI(profile *Profile) *api.Profile {
	p := &api.Profile{
		Name:   profile.Name,
		UsedBy: profile.UsedBy,
	}
	p.Description = profile.Description
	p.Config = profile.Config
	p.Devices = profile.Devices

	return p
}

// ProfileFilter specifies potential query parameter fields.
type ProfileFilter struct {
	Project string
	Name    string
}

// GetProfileNames returns the names of all profiles in the given project.
func (c *Cluster) GetProfileNames(project string) ([]string, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has profiles")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	q := fmt.Sprintf(`
SELECT profiles.name
 FROM profiles
 JOIN projects ON projects.id = profiles.project_id
WHERE projects.name = ?
`)
	inargs := []interface{}{project}
	var name string
	outfmt := []interface{}{name}
	result, err := queryScan(c, q, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

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

	return response, nil
}

// GetProfile returns the profile with the given name.
func (c *Cluster) GetProfile(project, name string) (int64, *api.Profile, error) {
	var result *api.Profile
	var id int64

	err := c.Transaction(func(tx *ClusterTx) error {
		var err error
		id, result, err = tx.getProfile(project, name)
		return err
	})
	if err != nil {
		return -1, nil, err
	}

	return id, result, nil
}

// Returns the profile with the given name.
func (c *ClusterTx) getProfile(project, name string) (int64, *api.Profile, error) {
	var result *api.Profile
	var id int64

	enabled, err := c.ProjectHasProfiles(project)
	if err != nil {
		return -1, nil, errors.Wrap(err, "Check if project has profiles")
	}
	if !enabled {
		project = "default"
	}

	profile, err := c.GetProfile(project, name)
	if err != nil {
		return -1, nil, err
	}

	result = ProfileToAPI(profile)
	id = int64(profile.ID)

	return id, result, nil
}

// GetProfiles returns the profiles with the given names in the given project.
func (c *Cluster) GetProfiles(projectName string, profileNames []string) ([]api.Profile, error) {
	profiles := make([]api.Profile, len(profileNames))

	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(projectName)
		if err != nil {
			return errors.Wrapf(err, "Failed checking if project %q has profiles", projectName)
		}

		if !enabled {
			projectName = "default"
		}

		for i, profileName := range profileNames {
			profile, err := tx.GetProfile(projectName, profileName)
			if err != nil {
				return errors.Wrapf(err, "Failed loading profile %q", profileName)
			}

			profiles[i] = *ProfileToAPI(profile)
		}

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

	return profiles, nil
}

// GetInstancesWithProfile gets the names of the instance associated with the
// profile with the given name in the given project.
func (c *Cluster) GetInstancesWithProfile(project, profile string) (map[string][]string, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has profiles")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	q := `SELECT instances.name, projects.name FROM instances
		JOIN instances_profiles ON instances.id == instances_profiles.instance_id
		JOIN projects ON projects.id == instances.project_id
		WHERE instances_profiles.profile_id ==
		  (SELECT profiles.id FROM profiles
		   JOIN projects ON projects.id == profiles.project_id
		   WHERE profiles.name=? AND projects.name=?)`

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

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

	for _, r := range output {
		if results[r[1].(string)] == nil {
			results[r[1].(string)] = []string{}
		}

		results[r[1].(string)] = append(results[r[1].(string)], r[0].(string))
	}

	return results, nil
}

// RemoveUnreferencedProfiles removes unreferenced profiles.
func (c *Cluster) RemoveUnreferencedProfiles() 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, stmt)
	if err != nil {
		return err
	}

	return nil
}

// ExpandInstanceConfig expands the given instance config with the config
// values of the given profiles.
func ExpandInstanceConfig(config map[string]string, profiles []api.Profile) map[string]string {
	expandedConfig := map[string]string{}

	// Apply all the profiles
	profileConfigs := make([]map[string]string, len(profiles))
	for i, profile := range profiles {
		profileConfigs[i] = profile.Config
	}

	for i := range profileConfigs {
		for k, v := range profileConfigs[i] {
			expandedConfig[k] = v
		}
	}

	// Stick the given config on top
	for k, v := range config {
		expandedConfig[k] = v
	}

	return expandedConfig
}

// ExpandInstanceDevices expands the given instance devices with the devices
// defined in the given profiles.
func ExpandInstanceDevices(devices deviceConfig.Devices, profiles []api.Profile) deviceConfig.Devices {
	expandedDevices := deviceConfig.Devices{}

	// Apply all the profiles
	profileDevices := make([]deviceConfig.Devices, len(profiles))
	for i, profile := range profiles {
		profileDevices[i] = deviceConfig.NewDevices(profile.Devices)
	}
	for i := range profileDevices {
		for k, v := range profileDevices[i] {
			expandedDevices[k] = v
		}
	}

	// Stick the given devices on top
	for k, v := range devices {
		expandedDevices[k] = v
	}

	return expandedDevices
}
<commit_msg>lxd/db/profiles: Adds GetProjectProfileNames function<commit_after>//go:build linux && cgo && !agent
// +build linux,cgo,!agent

package db

import (
	"fmt"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/shared/api"
	"github.com/pkg/errors"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t profiles.mapper.go
//go:generate mapper reset
//
//go:generate mapper stmt -p db -e profile names
//go:generate mapper stmt -p db -e profile names-by-Project
//go:generate mapper stmt -p db -e profile names-by-Project-and-Name
//go:generate mapper stmt -p db -e profile objects
//go:generate mapper stmt -p db -e profile objects-by-Project
//go:generate mapper stmt -p db -e profile objects-by-Project-and-Name
//go:generate mapper stmt -p db -e profile config-ref
//go:generate mapper stmt -p db -e profile config-ref-by-Project
//go:generate mapper stmt -p db -e profile config-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile devices-ref
//go:generate mapper stmt -p db -e profile devices-ref-by-Project
//go:generate mapper stmt -p db -e profile devices-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile used-by-ref
//go:generate mapper stmt -p db -e profile used-by-ref-by-Project
//go:generate mapper stmt -p db -e profile used-by-ref-by-Project-and-Name
//go:generate mapper stmt -p db -e profile id
//go:generate mapper stmt -p db -e profile create struct=Profile
//go:generate mapper stmt -p db -e profile create-config-ref
//go:generate mapper stmt -p db -e profile create-devices-ref
//go:generate mapper stmt -p db -e profile rename
//go:generate mapper stmt -p db -e profile delete-by-Project-and-Name
//go:generate mapper stmt -p db -e profile delete-config-ref
//go:generate mapper stmt -p db -e profile delete-devices-ref
//go:generate mapper stmt -p db -e profile update struct=Profile
//
//go:generate mapper method -p db -e profile URIs
//go:generate mapper method -p db -e profile List
//go:generate mapper method -p db -e profile Get
//go:generate mapper method -p db -e profile Exists struct=Profile
//go:generate mapper method -p db -e profile ID struct=Profile
//go:generate mapper method -p db -e profile ConfigRef
//go:generate mapper method -p db -e profile DevicesRef
//go:generate mapper method -p db -e profile UsedByRef
//go:generate mapper method -p db -e profile Create struct=Profile
//go:generate mapper method -p db -e profile Rename
//go:generate mapper method -p db -e profile DeleteOne
//go:generate mapper method -p db -e profile Update struct=Profile

// Profile is a value object holding db-related details about a profile.
type Profile struct {
	ID          int
	Project     string `db:"primary=yes&join=projects.name"`
	Name        string `db:"primary=yes"`
	Description string `db:"coalesce=''"`
	Config      map[string]string
	Devices     map[string]map[string]string
	UsedBy      []string
}

// ProfileToAPI is a convenience to convert a Profile db struct into
// an API profile struct.
func ProfileToAPI(profile *Profile) *api.Profile {
	p := &api.Profile{
		Name:   profile.Name,
		UsedBy: profile.UsedBy,
	}
	p.Description = profile.Description
	p.Config = profile.Config
	p.Devices = profile.Devices

	return p
}

// ProfileFilter specifies potential query parameter fields.
type ProfileFilter struct {
	Project string
	Name    string
}

// GetProjectProfileNames returns slice of profile names keyed on the project they belong to.
func (c *ClusterTx) GetProjectProfileNames() (map[string][]string, error) {
	query := `
	SELECT projects.name, profiles.name
	FROM profiles
	JOIN projects ON projects.id = profiles.project_id
	`

	rows, err := c.tx.Query(query)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	res := make(map[string][]string)

	for rows.Next() {
		var projectName, profileName string

		err = rows.Scan(&projectName, &profileName)
		if err != nil {
			return nil, err
		}

		if res[projectName] == nil {
			res[projectName] = []string{profileName}
		} else {
			res[projectName] = append(res[projectName], profileName)
		}
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return res, nil
}

// GetProfileNames returns the names of all profiles in the given project.
func (c *Cluster) GetProfileNames(project string) ([]string, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has profiles")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	q := fmt.Sprintf(`
SELECT profiles.name
 FROM profiles
 JOIN projects ON projects.id = profiles.project_id
WHERE projects.name = ?
`)
	inargs := []interface{}{project}
	var name string
	outfmt := []interface{}{name}
	result, err := queryScan(c, q, inargs, outfmt)
	if err != nil {
		return []string{}, err
	}

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

	return response, nil
}

// GetProfile returns the profile with the given name.
func (c *Cluster) GetProfile(project, name string) (int64, *api.Profile, error) {
	var result *api.Profile
	var id int64

	err := c.Transaction(func(tx *ClusterTx) error {
		var err error
		id, result, err = tx.getProfile(project, name)
		return err
	})
	if err != nil {
		return -1, nil, err
	}

	return id, result, nil
}

// Returns the profile with the given name.
func (c *ClusterTx) getProfile(project, name string) (int64, *api.Profile, error) {
	var result *api.Profile
	var id int64

	enabled, err := c.ProjectHasProfiles(project)
	if err != nil {
		return -1, nil, errors.Wrap(err, "Check if project has profiles")
	}
	if !enabled {
		project = "default"
	}

	profile, err := c.GetProfile(project, name)
	if err != nil {
		return -1, nil, err
	}

	result = ProfileToAPI(profile)
	id = int64(profile.ID)

	return id, result, nil
}

// GetProfiles returns the profiles with the given names in the given project.
func (c *Cluster) GetProfiles(projectName string, profileNames []string) ([]api.Profile, error) {
	profiles := make([]api.Profile, len(profileNames))

	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(projectName)
		if err != nil {
			return errors.Wrapf(err, "Failed checking if project %q has profiles", projectName)
		}

		if !enabled {
			projectName = "default"
		}

		for i, profileName := range profileNames {
			profile, err := tx.GetProfile(projectName, profileName)
			if err != nil {
				return errors.Wrapf(err, "Failed loading profile %q", profileName)
			}

			profiles[i] = *ProfileToAPI(profile)
		}

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

	return profiles, nil
}

// GetInstancesWithProfile gets the names of the instance associated with the
// profile with the given name in the given project.
func (c *Cluster) GetInstancesWithProfile(project, profile string) (map[string][]string, error) {
	err := c.Transaction(func(tx *ClusterTx) error {
		enabled, err := tx.ProjectHasProfiles(project)
		if err != nil {
			return errors.Wrap(err, "Check if project has profiles")
		}
		if !enabled {
			project = "default"
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	q := `SELECT instances.name, projects.name FROM instances
		JOIN instances_profiles ON instances.id == instances_profiles.instance_id
		JOIN projects ON projects.id == instances.project_id
		WHERE instances_profiles.profile_id ==
		  (SELECT profiles.id FROM profiles
		   JOIN projects ON projects.id == profiles.project_id
		   WHERE profiles.name=? AND projects.name=?)`

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

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

	for _, r := range output {
		if results[r[1].(string)] == nil {
			results[r[1].(string)] = []string{}
		}

		results[r[1].(string)] = append(results[r[1].(string)], r[0].(string))
	}

	return results, nil
}

// RemoveUnreferencedProfiles removes unreferenced profiles.
func (c *Cluster) RemoveUnreferencedProfiles() 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, stmt)
	if err != nil {
		return err
	}

	return nil
}

// ExpandInstanceConfig expands the given instance config with the config
// values of the given profiles.
func ExpandInstanceConfig(config map[string]string, profiles []api.Profile) map[string]string {
	expandedConfig := map[string]string{}

	// Apply all the profiles
	profileConfigs := make([]map[string]string, len(profiles))
	for i, profile := range profiles {
		profileConfigs[i] = profile.Config
	}

	for i := range profileConfigs {
		for k, v := range profileConfigs[i] {
			expandedConfig[k] = v
		}
	}

	// Stick the given config on top
	for k, v := range config {
		expandedConfig[k] = v
	}

	return expandedConfig
}

// ExpandInstanceDevices expands the given instance devices with the devices
// defined in the given profiles.
func ExpandInstanceDevices(devices deviceConfig.Devices, profiles []api.Profile) deviceConfig.Devices {
	expandedDevices := deviceConfig.Devices{}

	// Apply all the profiles
	profileDevices := make([]deviceConfig.Devices, len(profiles))
	for i, profile := range profiles {
		profileDevices[i] = deviceConfig.NewDevices(profile.Devices)
	}
	for i := range profileDevices {
		for k, v := range profileDevices[i] {
			expandedDevices[k] = v
		}
	}

	// Stick the given devices on top
	for k, v := range devices {
		expandedDevices[k] = v
	}

	return expandedDevices
}
<|endoftext|>