<commit_before>// Copyright 2015 The Vanadium 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 testutil defines helpers for tests.
package testutil

import (
	"io/ioutil"
	"os"
	"reflect"
	"runtime/debug"
	"testing"

	"v.io/syncbase/v23/syncbase"
	"v.io/syncbase/v23/syncbase/nosql"
	"v.io/syncbase/v23/syncbase/util"
	"v.io/syncbase/x/ref/services/syncbase/server"
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/rpc"
	"v.io/v23/security"
	"v.io/v23/security/access"
	"v.io/x/lib/vlog"
	tsecurity "v.io/x/ref/test/testutil"
)

func Fatal(t *testing.T, args ...interface{}) {
	debug.PrintStack()
	t.Fatal(args...)
}

func Fatalf(t *testing.T, format string, args ...interface{}) {
	debug.PrintStack()
	t.Fatalf(format, args...)
}

func CreateApp(t *testing.T, ctx *context.T, s syncbase.Service, name string) syncbase.App {
	a := s.App(name)
	if err := a.Create(ctx, nil); err != nil {
		Fatalf(t, "a.Create() failed: %v", err)
	}
	return a
}

func CreateNoSQLDatabase(t *testing.T, ctx *context.T, a syncbase.App, name string) nosql.Database {
	d := a.NoSQLDatabase(name)
	if err := d.Create(ctx, nil); err != nil {
		Fatalf(t, "d.Create() failed: %v", err)
	}
	return d
}

func CreateTable(t *testing.T, ctx *context.T, d nosql.Database, name string) nosql.Table {
	if err := d.CreateTable(ctx, name, nil); err != nil {
		Fatalf(t, "d.CreateTable() failed: %v", err)
	}
	return d.Table(name)
}

func SetupOrDie(perms access.Permissions) (clientCtx *context.T, serverName string, cleanup func()) {
	ctx, sName, cleanup, _, _ := SetupOrDieCustom("client", "server", perms)
	return ctx, sName, cleanup
}

func SetupOrDieCustom(client, server string, perms access.Permissions) (clientCtx *context.T, serverName string, cleanup func(), sp security.Principal, ctx *context.T) {
	ctx, shutdown := v23.Init()
	sp = tsecurity.NewPrincipal(server)

	clientCtx = NewClient(client, server, ctx, sp)

	serverCtx, err := v23.WithPrincipal(ctx, sp)
	if err != nil {
		vlog.Fatal("v23.WithPrincipal() failed: ", err)
	}

	serverName, stopServer := newServer(serverCtx, perms)
	cleanup = func() {
		stopServer()
		shutdown()
	}
	return
}

func defaultPerms() access.Permissions {
	perms := access.Permissions{}
	for _, tag := range access.AllTypicalTags() {
		perms.Add(security.BlessingPattern("server/client"), string(tag))
	}
	return perms
}

func CheckScan(t *testing.T, ctx *context.T, tb nosql.Table, r nosql.RowRange, wantKeys []string, wantValues []interface{}) {
	if len(wantKeys) != len(wantValues) {
		panic("bad input args")
	}
	it := tb.Scan(ctx, r)
	gotKeys := []string{}
	for it.Advance() {
		gotKey := it.Key()
		gotKeys = append(gotKeys, gotKey)
		i := len(gotKeys) - 1
		if i >= len(wantKeys) {
			continue
		}
		// Check key.
		wantKey := wantKeys[i]
		if gotKey != wantKey {
			Fatalf(t, "Keys do not match: got %q, want %q", gotKey, wantKey)
		}
		// Check value.
		wantValue := wantValues[i]
		gotValue := reflect.Zero(reflect.TypeOf(wantValue)).Interface()
		if err := it.Value(&gotValue); err != nil {
			Fatalf(t, "it.Value() failed: %v", err)
		}
		if !reflect.DeepEqual(gotValue, wantValue) {
			Fatalf(t, "Values do not match: got %v, want %v", gotValue, wantValue)
		}
	}
	if err := it.Err(); err != nil {
		Fatalf(t, "tb.Scan() failed: %v", err)
	}
	if len(gotKeys) != len(wantKeys) {
		Fatalf(t, "Unmatched keys: got %v, want %v", gotKeys, wantKeys)
	}
}

////////////////////////////////////////
// Internal helpers

func getPermsOrDie(t *testing.T, ctx *context.T, ac util.AccessController) access.Permissions {
	perms, _, err := ac.GetPermissions(ctx)
	if err != nil {
		Fatalf(t, "GetPermissions failed: %v", err)
	}
	return perms
}

func newServer(ctx *context.T, perms access.Permissions) (string, func()) {
	s, err := v23.NewServer(ctx)
	if err != nil {
		vlog.Fatal("v23.NewServer() failed: ", err)
	}
	eps, err := s.Listen(rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}})
	if err != nil {
		vlog.Fatal("s.Listen() failed: ", err)
	}

	if perms == nil {
		perms = defaultPerms()
	}
	rootDir, err := ioutil.TempDir("", "syncbase")
	if err != nil {
		vlog.Fatal("ioutil.TempDir() failed: ", err)
	}
	service, err := server.NewService(nil, nil, server.ServiceOptions{
		Perms:   perms,
		RootDir: rootDir,
		// TODO(sadovsky): Switch to leveldb once Database.Delete actually deletes
		// the underlying storage engine data (or similar).
		Engine: "memstore",
	})
	if err != nil {
		vlog.Fatal("server.NewService() failed: ", err)
	}
	d := server.NewDispatcher(service)

	if err := s.ServeDispatcher("", d); err != nil {
		vlog.Fatal("s.ServeDispatcher() failed: ", err)
	}

	name := naming.JoinAddressName(eps[0].String(), "")
	return name, func() {
		s.Stop()
		os.RemoveAll(rootDir)
	}
}

func NewClient(client, server string, ctx *context.T, sp security.Principal) (clientCtx *context.T) {
	cp := tsecurity.NewPrincipal(client)

	// Have the server principal bless the client principal as "client".
	blessings, err := sp.Bless(cp.PublicKey(), sp.BlessingStore().Default(), client, security.UnconstrainedUse())
	if err != nil {
		vlog.Fatal("sp.Bless() failed: ", err)
	}

	// Have the client present its "client" blessing when talking to the server.
	if _, err := cp.BlessingStore().Set(blessings, security.BlessingPattern(server)); err != nil {
		vlog.Fatal("cp.BlessingStore().Set() failed: ", err)
	}

	// Have the client treat the server's public key as an authority on all
	// blessings that match the pattern "server".
	if err := cp.AddToRoots(blessings); err != nil {
		vlog.Fatal("cp.AddToRoots() failed: ", err)
	}

	clientCtx, err = v23.WithPrincipal(ctx, cp)
	if err != nil {
		vlog.Fatal("v23.WithPrincipal() failed: ", err)
	}

	return clientCtx
}
<commit_msg>server: make app.DeleteNoSQLDatabase actually delete the data<commit_after>// Copyright 2015 The Vanadium 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 testutil defines helpers for tests.
package testutil

import (
	"io/ioutil"
	"os"
	"reflect"
	"runtime/debug"
	"testing"

	"v.io/syncbase/v23/syncbase"
	"v.io/syncbase/v23/syncbase/nosql"
	"v.io/syncbase/v23/syncbase/util"
	"v.io/syncbase/x/ref/services/syncbase/server"
	"v.io/v23"
	"v.io/v23/context"
	"v.io/v23/naming"
	"v.io/v23/rpc"
	"v.io/v23/security"
	"v.io/v23/security/access"
	"v.io/x/lib/vlog"
	tsecurity "v.io/x/ref/test/testutil"
)

func Fatal(t *testing.T, args ...interface{}) {
	debug.PrintStack()
	t.Fatal(args...)
}

func Fatalf(t *testing.T, format string, args ...interface{}) {
	debug.PrintStack()
	t.Fatalf(format, args...)
}

func CreateApp(t *testing.T, ctx *context.T, s syncbase.Service, name string) syncbase.App {
	a := s.App(name)
	if err := a.Create(ctx, nil); err != nil {
		Fatalf(t, "a.Create() failed: %v", err)
	}
	return a
}

func CreateNoSQLDatabase(t *testing.T, ctx *context.T, a syncbase.App, name string) nosql.Database {
	d := a.NoSQLDatabase(name)
	if err := d.Create(ctx, nil); err != nil {
		Fatalf(t, "d.Create() failed: %v", err)
	}
	return d
}

func CreateTable(t *testing.T, ctx *context.T, d nosql.Database, name string) nosql.Table {
	if err := d.CreateTable(ctx, name, nil); err != nil {
		Fatalf(t, "d.CreateTable() failed: %v", err)
	}
	return d.Table(name)
}

func SetupOrDie(perms access.Permissions) (clientCtx *context.T, serverName string, cleanup func()) {
	ctx, sName, cleanup, _, _ := SetupOrDieCustom("client", "server", perms)
	return ctx, sName, cleanup
}

func SetupOrDieCustom(client, server string, perms access.Permissions) (clientCtx *context.T, serverName string, cleanup func(), sp security.Principal, ctx *context.T) {
	ctx, shutdown := v23.Init()
	sp = tsecurity.NewPrincipal(server)

	clientCtx = NewClient(client, server, ctx, sp)

	serverCtx, err := v23.WithPrincipal(ctx, sp)
	if err != nil {
		vlog.Fatal("v23.WithPrincipal() failed: ", err)
	}

	serverName, stopServer := newServer(serverCtx, perms)
	cleanup = func() {
		stopServer()
		shutdown()
	}
	return
}

func defaultPerms() access.Permissions {
	perms := access.Permissions{}
	for _, tag := range access.AllTypicalTags() {
		perms.Add(security.BlessingPattern("server/client"), string(tag))
	}
	return perms
}

func CheckScan(t *testing.T, ctx *context.T, tb nosql.Table, r nosql.RowRange, wantKeys []string, wantValues []interface{}) {
	if len(wantKeys) != len(wantValues) {
		panic("bad input args")
	}
	it := tb.Scan(ctx, r)
	gotKeys := []string{}
	for it.Advance() {
		gotKey := it.Key()
		gotKeys = append(gotKeys, gotKey)
		i := len(gotKeys) - 1
		if i >= len(wantKeys) {
			continue
		}
		// Check key.
		wantKey := wantKeys[i]
		if gotKey != wantKey {
			Fatalf(t, "Keys do not match: got %q, want %q", gotKey, wantKey)
		}
		// Check value.
		wantValue := wantValues[i]
		gotValue := reflect.Zero(reflect.TypeOf(wantValue)).Interface()
		if err := it.Value(&gotValue); err != nil {
			Fatalf(t, "it.Value() failed: %v", err)
		}
		if !reflect.DeepEqual(gotValue, wantValue) {
			Fatalf(t, "Values do not match: got %v, want %v", gotValue, wantValue)
		}
	}
	if err := it.Err(); err != nil {
		Fatalf(t, "tb.Scan() failed: %v", err)
	}
	if len(gotKeys) != len(wantKeys) {
		Fatalf(t, "Unmatched keys: got %v, want %v", gotKeys, wantKeys)
	}
}

////////////////////////////////////////
// Internal helpers

func getPermsOrDie(t *testing.T, ctx *context.T, ac util.AccessController) access.Permissions {
	perms, _, err := ac.GetPermissions(ctx)
	if err != nil {
		Fatalf(t, "GetPermissions failed: %v", err)
	}
	return perms
}

func newServer(ctx *context.T, perms access.Permissions) (string, func()) {
	s, err := v23.NewServer(ctx)
	if err != nil {
		vlog.Fatal("v23.NewServer() failed: ", err)
	}
	eps, err := s.Listen(rpc.ListenSpec{Addrs: rpc.ListenAddrs{{"tcp", "127.0.0.1:0"}}})
	if err != nil {
		vlog.Fatal("s.Listen() failed: ", err)
	}

	if perms == nil {
		perms = defaultPerms()
	}
	rootDir, err := ioutil.TempDir("", "syncbase")
	if err != nil {
		vlog.Fatal("ioutil.TempDir() failed: ", err)
	}
	service, err := server.NewService(nil, nil, server.ServiceOptions{
		Perms:   perms,
		RootDir: rootDir,
		Engine:  "leveldb",
	})
	if err != nil {
		vlog.Fatal("server.NewService() failed: ", err)
	}
	d := server.NewDispatcher(service)

	if err := s.ServeDispatcher("", d); err != nil {
		vlog.Fatal("s.ServeDispatcher() failed: ", err)
	}

	name := naming.JoinAddressName(eps[0].String(), "")
	return name, func() {
		s.Stop()
		os.RemoveAll(rootDir)
	}
}

func NewClient(client, server string, ctx *context.T, sp security.Principal) (clientCtx *context.T) {
	cp := tsecurity.NewPrincipal(client)

	// Have the server principal bless the client principal as "client".
	blessings, err := sp.Bless(cp.PublicKey(), sp.BlessingStore().Default(), client, security.UnconstrainedUse())
	if err != nil {
		vlog.Fatal("sp.Bless() failed: ", err)
	}

	// Have the client present its "client" blessing when talking to the server.
	if _, err := cp.BlessingStore().Set(blessings, security.BlessingPattern(server)); err != nil {
		vlog.Fatal("cp.BlessingStore().Set() failed: ", err)
	}

	// Have the client treat the server's public key as an authority on all
	// blessings that match the pattern "server".
	if err := cp.AddToRoots(blessings); err != nil {
		vlog.Fatal("cp.AddToRoots() failed: ", err)
	}

	clientCtx, err = v23.WithPrincipal(ctx, cp)
	if err != nil {
		vlog.Fatal("v23.WithPrincipal() failed: ", err)
	}

	return clientCtx
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"errors"
	//"fmt"
	"github.com/jmoiron/sqlx"
	"github.com/pborman/uuid"
	"net/http"
)

type Database struct {
	db *sqlx.DB
}

func (db *Database) readDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key FROM
            instrument
            WHERE plot = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT key from instruments)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readLatestDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH
        instruments as (
            SELECT i.key AS keys
            FROM instrument i
            WHERE plot = $1
        ),
        latest_measurement as (

            SELECT max(m.timestamp) as timestamp
            FROM measurement m, plot p
            WHERE m.timestamp >= p.start_time
            AND(p.end_time is null OR m.timestamp <= p.end_time)
            AND p.id = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        and m.timestamp = (select timestamp from latest_measurement)
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readHourlyDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key AS keys
            FROM instrument
            WHERE plot = $1
        )
        SELECT
            m.key,
            round(cast(avg(value) as numeric),0) AS value,
            m.timestamp::date::timestamp + make_interval(hours => DATE_PART('HOUR', m.timestamp)::integer) as timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        GROUP BY m.key, timestamp
        ORDER BY timestamp;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readMeasurements(user string, name string) ([]Measurement, error) {
	measurements := []Measurement{}

	var sql = `
        SELECT key, value, timestamp
        FROM measurement
        WHERE name = $1
        AND login = $2
        ORDER BY timestamp
    `

	err := db.db.Select(&measurements, sql, name, user)
	return measurements, err
}

func (db *Database) saveMeasurements(measurements []Measurement, user string) error {
	tx := db.db.MustBegin()
	var sql = `
        INSERT INTO measurement (key, value, timestamp, login)
        VALUES (:key, :value, :timestamp, :login)
    `
	for _, measurement := range measurements {
		measurement.Login = user
		tx.NamedExec(sql, &measurement)
	}

	err := tx.Commit()
	if err != nil {
		return err
	}
	return nil
}

func (db *Database) getPlots(user string) ([]Plot, error) {
	plots := []Plot{}

	var sql = `
        SELECT id, start_time, end_time, name
        FROM plot
        WHERE login = $1
        ORDER BY start_time DESC
    `

	err := db.db.Select(&plots, sql, user)
	return plots, err
}

func (db *Database) getInstruments(plotId int) ([]Instrument, error) {
	instruments := []Instrument{}

	var sql = `
        SELECT key, id, name, type
        FROM instrument
        WHERE plot = $1
    `

	err := db.db.Select(&instruments, sql, plotId)
	return instruments, err
}

func (db *Database) getPlot(id int, user string) (Plot, error) {
	plot := Plot{}

	var sql = `
        SELECT id, start_time, end_time, name
        FROM plot
        WHERE login = $1
        AND id = $2
    `

	err := db.db.Get(&plot, sql, user, id)
	return plot, err
}

func (db *Database) savePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        INSERT INTO plot (start_time, end_time, name, login) VALUES (:start_time, :end_time, :name, :login) RETURNING id
    `
	var id int
	rows, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, err
	}
	if rows.Next() {
		rows.Scan(&id)
	}
	plot.Id = id
	tx := db.db.MustBegin()

	var sql2 = `
        INSERT INTO instrument (key, name, type, plot)
        VALUES (:key, :name, :type, :plot)
    `
	for _, instrument := range plot.Instruments {
		instrument.Plot = plot.Id
		tx.NamedExec(sql2, &instrument)
	}
	tx.Commit()
	return plot, err
}

func (db *Database) getUser(r *http.Request) (string, error) {
	key := r.Header.Get("X-PYTILT-KEY")
	return db.getUserForKey(key)
}

func (db *Database) getUserForKey(key string) (string, error) {
	var id string
	err := db.db.Get(&id, "SELECT id FROM login WHERE key = $1", key)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown key")
	}
	return id, err
}

func (db *Database) getkeyForUser(user string) (string, error) {
	var key string
	err := db.db.Get(&key, "SELECT key FROM login WHERE id = $1", user)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown user")
	}
	return key, err
}

func (db *Database) userExists(id string) (bool, error) {
	var uid string
	if err := db.db.QueryRow("SELECT id FROM login WHERE id = $1", id).Scan(&uid); err == nil {
		return true, nil
	} else if err == sql.ErrNoRows {
		return false, nil
	} else {
		return false, err
	}

}

func (db *Database) createUser(id string, email string, name string) error {
	tx := db.db.MustBegin()
	key := uuid.New()
	var sql = `
        INSERT INTO login (id, first_name, email, key)
        VALUES ($1, $2, $3, $4)
    `
	tx.MustExec(sql, id, name, email, key)
	tx.Commit()
	return nil
}
<commit_msg>reverse ordering of measurements<commit_after>package main

import (
	"database/sql"
	"errors"
	//"fmt"
	"github.com/jmoiron/sqlx"
	"github.com/pborman/uuid"
	"net/http"
)

type Database struct {
	db *sqlx.DB
}

func (db *Database) readDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key FROM
            instrument
            WHERE plot = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT key from instruments)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readLatestDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH
        instruments as (
            SELECT i.key AS keys
            FROM instrument i
            WHERE plot = $1
        ),
        latest_measurement as (

            SELECT max(m.timestamp) as timestamp
            FROM measurement m, plot p
            WHERE m.timestamp >= p.start_time
            AND(p.end_time is null OR m.timestamp <= p.end_time)
            AND p.id = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        and m.timestamp = (select timestamp from latest_measurement)
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readHourlyDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key AS keys
            FROM instrument
            WHERE plot = $1
        )
        SELECT
            m.key,
            round(cast(avg(value) as numeric),0) AS value,
            m.timestamp::date::timestamp + make_interval(hours => DATE_PART('HOUR', m.timestamp)::integer) as timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        GROUP BY m.key, timestamp
        ORDER BY timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) readMeasurements(user string, name string) ([]Measurement, error) {
	measurements := []Measurement{}

	var sql = `
        SELECT key, value, timestamp
        FROM measurement
        WHERE name = $1
        AND login = $2
        ORDER BY timestamp
    `

	err := db.db.Select(&measurements, sql, name, user)
	return measurements, err
}

func (db *Database) saveMeasurements(measurements []Measurement, user string) error {
	tx := db.db.MustBegin()
	var sql = `
        INSERT INTO measurement (key, value, timestamp, login)
        VALUES (:key, :value, :timestamp, :login)
    `
	for _, measurement := range measurements {
		measurement.Login = user
		tx.NamedExec(sql, &measurement)
	}

	err := tx.Commit()
	if err != nil {
		return err
	}
	return nil
}

func (db *Database) getPlots(user string) ([]Plot, error) {
	plots := []Plot{}

	var sql = `
        SELECT id, start_time, end_time, name
        FROM plot
        WHERE login = $1
        ORDER BY start_time DESC
    `

	err := db.db.Select(&plots, sql, user)
	return plots, err
}

func (db *Database) getInstruments(plotId int) ([]Instrument, error) {
	instruments := []Instrument{}

	var sql = `
        SELECT key, id, name, type
        FROM instrument
        WHERE plot = $1
    `

	err := db.db.Select(&instruments, sql, plotId)
	return instruments, err
}

func (db *Database) getPlot(id int, user string) (Plot, error) {
	plot := Plot{}

	var sql = `
        SELECT id, start_time, end_time, name
        FROM plot
        WHERE login = $1
        AND id = $2
    `

	err := db.db.Get(&plot, sql, user, id)
	return plot, err
}

func (db *Database) savePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        INSERT INTO plot (start_time, end_time, name, login) VALUES (:start_time, :end_time, :name, :login) RETURNING id
    `
	var id int
	rows, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, err
	}
	if rows.Next() {
		rows.Scan(&id)
	}
	plot.Id = id
	tx := db.db.MustBegin()

	var sql2 = `
        INSERT INTO instrument (key, name, type, plot)
        VALUES (:key, :name, :type, :plot)
    `
	for _, instrument := range plot.Instruments {
		instrument.Plot = plot.Id
		tx.NamedExec(sql2, &instrument)
	}
	tx.Commit()
	return plot, err
}

func (db *Database) getUser(r *http.Request) (string, error) {
	key := r.Header.Get("X-PYTILT-KEY")
	return db.getUserForKey(key)
}

func (db *Database) getUserForKey(key string) (string, error) {
	var id string
	err := db.db.Get(&id, "SELECT id FROM login WHERE key = $1", key)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown key")
	}
	return id, err
}

func (db *Database) getkeyForUser(user string) (string, error) {
	var key string
	err := db.db.Get(&key, "SELECT key FROM login WHERE id = $1", user)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown user")
	}
	return key, err
}

func (db *Database) userExists(id string) (bool, error) {
	var uid string
	if err := db.db.QueryRow("SELECT id FROM login WHERE id = $1", id).Scan(&uid); err == nil {
		return true, nil
	} else if err == sql.ErrNoRows {
		return false, nil
	} else {
		return false, err
	}

}

func (db *Database) createUser(id string, email string, name string) error {
	tx := db.db.MustBegin()
	key := uuid.New()
	var sql = `
        INSERT INTO login (id, first_name, email, key)
        VALUES ($1, $2, $3, $4)
    `
	tx.MustExec(sql, id, name, email, key)
	tx.Commit()
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"github.com/jmoiron/sqlx"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
	"net/http"
	"strings"
	"time"
)

type Database struct {
	db *sqlx.DB
}

type Resolution int

const (
	All Resolution = iota
	Day
	Hour
	Minute
)

func (resolution Resolution) ToString() (string, error) {
	switch resolution {
	case All:
		return "All", nil
	case Minute:
		return "Minute", nil
	case Hour:
		return "Hour", nil
	case Day:
		return "Day", nil
	default:
		return "", errors.New("Unknown resolution")
	}
}

func (resolution Resolution) String() string {
	str, _ := resolution.ToString()
	return str
}

func ResolutionFromString(resolutionString string) (Resolution, error) {
	lowerCase := strings.ToLower(resolutionString)
	if lowerCase == strings.ToLower("All") {
		return All, nil
	} else if lowerCase == strings.ToLower("Minute") {
		return Minute, nil
	} else if lowerCase == strings.ToLower("Hour") {
		return Hour, nil
	} else if lowerCase == strings.ToLower("Day") {
		return Day, nil
	} else {
		return All, errors.New("Unknown resolution from string: " + resolutionString)
	}
}

func (db *Database) readDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time, resolution Resolution) ([]Measurement, error) {

	if resolution == All {
		return db.readAllDataFromPlot(user, plotId, startTime, endTime)
	} else {
		return db.readAggregatedDataFromPlot(user, plotId, startTime, endTime, resolution)
	}
}

func (db *Database) readAllDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key FROM
            instrument
            WHERE plot = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT key from instruments)
        AND p.id = $1
        AND p.login = $2
        AND m.timestamp >= $3
        AND m.timestamp <= $4
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user, startTime, endTime)
	return measurements, err
}

func (db *Database) readLatestDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH
        instruments as (
            SELECT i.key AS keys
            FROM instrument i
            WHERE plot = $1
        ),
        latest_measurement as (

            SELECT max(m.timestamp) as timestamp
            FROM measurement m, plot p
            WHERE m.timestamp >= p.start_time
            AND(p.end_time is null OR m.timestamp <= p.end_time)
            AND p.id = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        and m.timestamp = (select timestamp from latest_measurement)
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) getIntervalDefinition(resolution Resolution) (string, string, error) {
	switch resolution {
	case All:
		return "", "", errors.New("No interval definition for All")
	case Minute:
		return "mins", "1 minute", nil
	case Hour:
		return "hours", "1 hour", nil
	case Day:
		return "days", "1 day", nil
	default:
		return "", "", errors.New("Unknown resolution")
	}
}

func (db *Database) readAggregatedDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time, resolution Resolution) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key AS keys
            FROM instrument
            WHERE plot = $1
        ),
        intervals AS (
            SELECT start_time FROM
            generate_series(date_trunc($5, (select start_time from plot where id = $1)), NOW(), $6) as start_time
        )
        SELECT
            m.key,
            i.start_time as timestamp,
            AVG(m.value) as value
        FROM measurement m, plot p, intervals i
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT keys from instruments)
        AND p.id = $1
        AND p.login = $2
        AND m.timestamp > i.start_time
        AND m.timestamp < i.start_time + $6::interval
        AND m.timestamp >= $3
        AND m.timestamp <= $4
        GROUP BY m.key, i.start_time
        ORDER BY i.start_time desc
    `

	trunc, interval, err := db.getIntervalDefinition(resolution)
	if err != nil {
		return measurements, err
	}

	err = db.db.Select(&measurements, sql, plotId, user, startTime, endTime, trunc, interval)
	return measurements, err
}

func (db *Database) readMeasurements(user string, name string) ([]Measurement, error) {
	measurements := []Measurement{}

	var sql = `
        SELECT key, value, timestamp
        FROM measurement
        WHERE name = $1
        AND login = $2
        ORDER BY timestamp
    `

	err := db.db.Select(&measurements, sql, name, user)
	return measurements, err
}

func (db *Database) saveMeasurements(measurements []Measurement, user string) error {
	tx, err := db.db.Beginx()
	if err != nil {
		return errors.Wrap(err, "Unable to save measurement")
	}

	var sql = `
        INSERT INTO measurement (key, value, timestamp, login)
        VALUES (:key, :value, :timestamp, :login)
    `
	for _, measurement := range measurements {
		measurement.Login = user
		tx.NamedExec(sql, &measurement)
	}

	err = tx.Commit()
	if err != nil {
		return errors.Wrap(err, "Unable to save measurement")
	}
	return nil
}

func (db *Database) getPlots(user string) ([]Plot, error) {
	plots := []Plot{}

	var sql = `
        SELECT id, start_time, end_time, name, case when end_time IS null then true else false end as active
        FROM plot
        WHERE login = $1
        ORDER BY start_time DESC
    `

	err := db.db.Select(&plots, sql, user)
	return plots, err
}

func (db *Database) getInstruments(plotId int) ([]Instrument, error) {
	instruments := []Instrument{}

	var sql = `
        SELECT key, id, name, type
        FROM instrument
        WHERE plot = $1
    `

	err := db.db.Select(&instruments, sql, plotId)
	return instruments, err
}

func (db *Database) getPlot(id int, user string) (Plot, error) {
	plot := Plot{}

	var sql = `
        SELECT id, start_time, end_time, name, case when end_time IS null then true else false end as active
        FROM plot
        WHERE login = $1
        AND id = $2
    `

	err := db.db.Get(&plot, sql, user, id)
	return plot, err
}

func (db *Database) savePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        INSERT INTO plot (start_time, end_time, name, login) VALUES (:start_time, :end_time, :name, :login) RETURNING id
    `
	var id int
	rows, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, errors.Wrap(err, "Unable to save plot")
	}
	if rows.Next() {
		rows.Scan(&id)
	}
	plot.Id = id
	tx, err := db.db.Beginx()
	if err != nil {
		return plot, errors.Wrap(err, "Unable to save instruments for plot")
	}

	var sql2 = `
        INSERT INTO instrument (key, name, type, plot)
        VALUES (:key, :name, :type, :plot)
    `
	for _, instrument := range plot.Instruments {
		instrument.Plot = plot.Id
		tx.NamedExec(sql2, &instrument)
	}
	tx.Commit()
	return plot, err
}

func (db *Database) updatePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        UPDATE plot SET start_time = :start_time, end_time = :end_time, name = :name WHERE id = :id and login = :login
    `
	_, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, err
	}
	return plot, err
}

func (db *Database) getUser(r *http.Request) (string, error) {
	key := r.Header.Get("X-PYTILT-KEY")
	return db.getUserForKey(key)
}

func (db *Database) getUserForKey(key string) (string, error) {
	var id string
	err := db.db.Get(&id, "SELECT id FROM login WHERE key = $1", key)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown key")
	}
	return id, err
}

func (db *Database) getkeyForUser(user string) (string, error) {
	var key string
	err := db.db.Get(&key, "SELECT key FROM login WHERE id = $1", user)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown user")
	}
	return key, err
}

func (db *Database) userExists(id string) (bool, error) {
	var uid string
	if err := db.db.QueryRow("SELECT id FROM login WHERE id = $1", id).Scan(&uid); err == nil {
		return true, nil
	} else if err == sql.ErrNoRows {
		return false, nil
	} else {
		return false, errors.Wrap(err, "Unable to check if user exists")
	}

}

func (db *Database) createUser(id string, email string, name string) error {
	tx, error := db.db.Beginx()
	if error != nil {
		return errors.New("Unable to connect to database.")
	}

	key := uuid.New()
	var sql = `
        INSERT INTO login (id, name, email, key)
        VALUES ($1, $2, $3, $4)
    `
	_, error = tx.Exec(sql, id, name, email, key)
	if error != nil {
		return errors.New("Unable to create new user")
	}

	tx.Commit()
	return nil
}
<commit_msg>Respect start and end times in sql to make aggregation faster.<commit_after>package main

import (
	"database/sql"
	"github.com/jmoiron/sqlx"
	"github.com/pborman/uuid"
	"github.com/pkg/errors"
	"net/http"
	"strings"
	"time"
)

type Database struct {
	db *sqlx.DB
}

type Resolution int

const (
	All Resolution = iota
	Day
	Hour
	Minute
)

func (resolution Resolution) ToString() (string, error) {
	switch resolution {
	case All:
		return "All", nil
	case Minute:
		return "Minute", nil
	case Hour:
		return "Hour", nil
	case Day:
		return "Day", nil
	default:
		return "", errors.New("Unknown resolution")
	}
}

func (resolution Resolution) String() string {
	str, _ := resolution.ToString()
	return str
}

func ResolutionFromString(resolutionString string) (Resolution, error) {
	lowerCase := strings.ToLower(resolutionString)
	if lowerCase == strings.ToLower("All") {
		return All, nil
	} else if lowerCase == strings.ToLower("Minute") {
		return Minute, nil
	} else if lowerCase == strings.ToLower("Hour") {
		return Hour, nil
	} else if lowerCase == strings.ToLower("Day") {
		return Day, nil
	} else {
		return All, errors.New("Unknown resolution from string: " + resolutionString)
	}
}

func (db *Database) readDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time, resolution Resolution) ([]Measurement, error) {

	if resolution == All {
		return db.readAllDataFromPlot(user, plotId, startTime, endTime)
	} else {
		return db.readAggregatedDataFromPlot(user, plotId, startTime, endTime, resolution)
	}
}

func (db *Database) readAllDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key FROM
            instrument
            WHERE plot = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT key from instruments)
        AND p.id = $1
        AND p.login = $2
        AND m.timestamp >= $3
        AND m.timestamp <= $4
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user, startTime, endTime)
	return measurements, err
}

func (db *Database) readLatestDataFromPlot(user string, plotId int) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH
        instruments as (
            SELECT i.key AS keys
            FROM instrument i
            WHERE plot = $1
        ),
        latest_measurement as (

            SELECT max(m.timestamp) as timestamp
            FROM measurement m, plot p
            WHERE m.timestamp >= p.start_time
            AND(p.end_time is null OR m.timestamp <= p.end_time)
            AND p.id = $1
        )
        SELECT m.key, m.value, m.timestamp
        FROM measurement m, plot p
        WHERE m.timestamp >= p.start_time
        and m.timestamp = (select timestamp from latest_measurement)
        AND m.key IN (SELECT i.keys from instruments i)
        AND p.id = $1
        AND p.login = $2
        ORDER BY m.timestamp desc;
    `
	err := db.db.Select(&measurements, sql, plotId, user)
	return measurements, err
}

func (db *Database) getIntervalDefinition(resolution Resolution) (string, string, error) {
	switch resolution {
	case All:
		return "", "", errors.New("No interval definition for All")
	case Minute:
		return "mins", "1 minute", nil
	case Hour:
		return "hours", "1 hour", nil
	case Day:
		return "days", "1 day", nil
	default:
		return "", "", errors.New("Unknown resolution")
	}
}

func (db *Database) readAggregatedDataFromPlot(user string, plotId int, startTime time.Time, endTime time.Time, resolution Resolution) ([]Measurement, error) {
	measurements := []Measurement{}
	var sql = `
        WITH instruments as (
            SELECT key AS keys
            FROM instrument
            WHERE plot = $1
        ),
        intervals AS (
            SELECT start_time FROM
            generate_series(date_trunc($5, GREATEST($3, (select start_time from plot where id = $1))),
                            LEAST($4, NOW()),
                            $6) as start_time
        )
        SELECT
            m.key,
            i.start_time as timestamp,
            AVG(m.value) as value
        FROM measurement m, plot p, intervals i
        WHERE m.timestamp >= p.start_time
        AND(p.end_time is null OR m.timestamp <= p.end_time)
        AND m.key IN (SELECT keys from instruments)
        AND p.id = $1
        AND p.login = $2
        AND m.timestamp > i.start_time
        AND m.timestamp < i.start_time + $6::interval
        AND m.timestamp >= $3
        AND m.timestamp <= $4
        GROUP BY m.key, i.start_time
        ORDER BY i.start_time desc
    `

	trunc, interval, err := db.getIntervalDefinition(resolution)
	if err != nil {
		return measurements, err
	}

	err = db.db.Select(&measurements, sql, plotId, user, startTime, endTime, trunc, interval)
	return measurements, err
}

func (db *Database) readMeasurements(user string, name string) ([]Measurement, error) {
	measurements := []Measurement{}

	var sql = `
        SELECT key, value, timestamp
        FROM measurement
        WHERE name = $1
        AND login = $2
        ORDER BY timestamp
    `

	err := db.db.Select(&measurements, sql, name, user)
	return measurements, err
}

func (db *Database) saveMeasurements(measurements []Measurement, user string) error {
	tx, err := db.db.Beginx()
	if err != nil {
		return errors.Wrap(err, "Unable to save measurement")
	}

	var sql = `
        INSERT INTO measurement (key, value, timestamp, login)
        VALUES (:key, :value, :timestamp, :login)
    `
	for _, measurement := range measurements {
		measurement.Login = user
		tx.NamedExec(sql, &measurement)
	}

	err = tx.Commit()
	if err != nil {
		return errors.Wrap(err, "Unable to save measurement")
	}
	return nil
}

func (db *Database) getPlots(user string) ([]Plot, error) {
	plots := []Plot{}

	var sql = `
        SELECT id, start_time, end_time, name, case when end_time IS null then true else false end as active
        FROM plot
        WHERE login = $1
        ORDER BY start_time DESC
    `

	err := db.db.Select(&plots, sql, user)
	return plots, err
}

func (db *Database) getInstruments(plotId int) ([]Instrument, error) {
	instruments := []Instrument{}

	var sql = `
        SELECT key, id, name, type
        FROM instrument
        WHERE plot = $1
    `

	err := db.db.Select(&instruments, sql, plotId)
	return instruments, err
}

func (db *Database) getPlot(id int, user string) (Plot, error) {
	plot := Plot{}

	var sql = `
        SELECT id, start_time, end_time, name, case when end_time IS null then true else false end as active
        FROM plot
        WHERE login = $1
        AND id = $2
    `

	err := db.db.Get(&plot, sql, user, id)
	return plot, err
}

func (db *Database) savePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        INSERT INTO plot (start_time, end_time, name, login) VALUES (:start_time, :end_time, :name, :login) RETURNING id
    `
	var id int
	rows, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, errors.Wrap(err, "Unable to save plot")
	}
	if rows.Next() {
		rows.Scan(&id)
	}
	plot.Id = id
	tx, err := db.db.Beginx()
	if err != nil {
		return plot, errors.Wrap(err, "Unable to save instruments for plot")
	}

	var sql2 = `
        INSERT INTO instrument (key, name, type, plot)
        VALUES (:key, :name, :type, :plot)
    `
	for _, instrument := range plot.Instruments {
		instrument.Plot = plot.Id
		tx.NamedExec(sql2, &instrument)
	}
	tx.Commit()
	return plot, err
}

func (db *Database) updatePlot(plot Plot, user string) (Plot, error) {

	plot.Login = user

	var sql = `
        UPDATE plot SET start_time = :start_time, end_time = :end_time, name = :name WHERE id = :id and login = :login
    `
	_, err := db.db.NamedQuery(sql, plot)
	if err != nil {
		return plot, err
	}
	return plot, err
}

func (db *Database) getUser(r *http.Request) (string, error) {
	key := r.Header.Get("X-PYTILT-KEY")
	return db.getUserForKey(key)
}

func (db *Database) getUserForKey(key string) (string, error) {
	var id string
	err := db.db.Get(&id, "SELECT id FROM login WHERE key = $1", key)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown key")
	}
	return id, err
}

func (db *Database) getkeyForUser(user string) (string, error) {
	var key string
	err := db.db.Get(&key, "SELECT key FROM login WHERE id = $1", user)

	if err == sql.ErrNoRows {
		return "", errors.New("unknown user")
	}
	return key, err
}

func (db *Database) userExists(id string) (bool, error) {
	var uid string
	if err := db.db.QueryRow("SELECT id FROM login WHERE id = $1", id).Scan(&uid); err == nil {
		return true, nil
	} else if err == sql.ErrNoRows {
		return false, nil
	} else {
		return false, errors.Wrap(err, "Unable to check if user exists")
	}

}

func (db *Database) createUser(id string, email string, name string) error {
	tx, error := db.db.Beginx()
	if error != nil {
		return errors.New("Unable to connect to database.")
	}

	key := uuid.New()
	var sql = `
        INSERT INTO login (id, name, email, key)
        VALUES ($1, $2, $3, $4)
    `
	_, error = tx.Exec(sql, id, name, email, key)
	if error != nil {
		return errors.New("Unable to create new user")
	}

	tx.Commit()
	return nil
}
<|endoftext|>


<commit_before>package main

// TODO: Make this less bad, all of it

import (
	"github.com/boltdb/bolt"
)

type Database struct {
	db *bolt.DB
}

func NewDatabase() *Database {
	db, err := bolt.Open("mirror.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	return &Database{db}
}

func (d *Database) Path() string {
	return d.db.Path()
}

func (d *Database) Close() {
	d.db.Close()
}

func (d *Database) StoreMirror(downstreamID int, upstreamID int) error {

	// Store the upstream->downstream id
	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("up2down"))
		err := b.Put([]byte(upstreamID), []byte(downstreamID))
		return err
	})

	// Store the upstream->downstream id
	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("down2up"))
		err := b.Put([]byte(downstreamID), []byte(upstreamID))
		return err
	})

	return nil
}

func (d *Database) GetDownstreamID(upstreamID int) []byte {
	var retval = []byte{0}
	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("up2down"))
		v := b.Get([]byte(upstreamID))
		copy(retval, v)
		return nil
	})
	return retval
}

func (d *Database) GetUpstreamID(downstreamID int) []byte {
	var retval = []byte{0}
	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("down2up"))
		v := b.Get([]byte(downstreamID))
		copy(retval, v)
		return nil
	})
	return retval
}
<commit_msg>BYTES BYTES BYTES<commit_after>package main

// TODO: Make this less bad, all of it

import (
	"github.com/boltdb/bolt"
)

type Database struct {
	db *bolt.DB
}

func NewDatabase() *Database {
	db, err := bolt.Open("mirror.db", 0600, nil)
	if err != nil {
		log.Fatal(err)
	}

	return &Database{db}
}

func (d *Database) Path() string {
	return d.db.Path()
}

func (d *Database) Close() {
	d.db.Close()
}

func (d *Database) StoreMirror(downstreamID byte, upstreamID byte) error {

	// Store the upstream->downstream id
	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("up2down"))
		err := b.Put([]byte(upstreamID), []byte(downstreamID))
		return err
	})

	// Store the upstream->downstream id
	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("down2up"))
		err := b.Put([]byte(downstreamID), []byte(upstreamID))
		return err
	})

	return nil
}

func (d *Database) GetDownstreamID(upstreamID byte) []byte {
	var retval = []byte{0}
	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("up2down"))
		v := b.Get([]byte(upstreamID))
		copy(retval, v)
		return nil
	})
	return retval
}

func (d *Database) GetUpstreamID(downstreamID byte) []byte {
	var retval = []byte{0}
	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("down2up"))
		v := b.Get([]byte(downstreamID))
		copy(retval, v)
		return nil
	})
	return retval
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-xorm/xorm"
	_ "github.com/mattn/go-sqlite3"
)

var orm *xorm.Engine

// InitDatabase sets up the database and xorm
func InitDatabase() error {
	var err error

	// connect to our database
	orm, err = xorm.NewEngine("sqlite3", "/tmp/requests.db")

	if err != nil {
		return err
	}

	err = orm.Sync(new(ClientRequest))
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Connect to mysql, if it's available<commit_after>package main

import (
	"os"
	"strings"

	"github.com/go-xorm/xorm"
	_ "github.com/mattn/go-sqlite3"
)

var orm *xorm.Engine

// InitDatabase sets up the database and xorm
func InitDatabase() error {
	var err error

	// connect to our database
	if os.Getenv("OPENSHIFT_MYSQL_DB_URL") != "" {
		openshiftURL := os.Getenv("OPENSHIFT_MYSQL_DB_URL")
		mysql := strings.TrimPrefix(openshiftURL, "mysql://")
		orm, err = xorm.NewEngine("mysql", mysql)
	} else {
		orm, err = xorm.NewEngine("sqlite3", ":memory:")
	}

	if err != nil {
		return err
	}

	// Migrate our database if needed
	err = orm.Sync(new(ClientRequest))
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"net/http"
	"net/url"
	"strings"

	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/util/proxy"
)

type RemoteService struct {
	cluster   *v3.Cluster
	transport *http.Transport
	url       *url.URL
	auth      string
}

var (
	er = &errorResponder{}
)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(cluster *v3.Cluster, factory dialer.Factory) (*RemoteService, error) {
	transport := &http.Transport{}

	if factory != nil {
		d, err := factory.ClusterDialer(cluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	if cluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(cluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	u, err := url.Parse(cluster.Status.APIEndpoint)
	if err != nil {
		return nil, err
	}

	return &RemoteService{
		cluster:   cluster,
		transport: transport,
		url:       u,
		auth:      "Bearer " + cluster.Status.ServiceAccountToken,
	}, nil
}

func (r *RemoteService) Close() {
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u := *r.url
	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	req.Header.Set("Authorization", r.auth)

	httpProxy := proxy.NewUpgradeAwareHandler(&u, r.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}
<commit_msg>Fix kubectl regression<commit_after>package proxy

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/base64"
	"net/http"
	"net/url"
	"strings"

	"github.com/rancher/types/apis/management.cattle.io/v3"
	"github.com/rancher/types/config/dialer"
	"k8s.io/apimachinery/pkg/util/proxy"
)

type RemoteService struct {
	cluster   *v3.Cluster
	transport *http.Transport
	url       *url.URL
	auth      string
}

var (
	er = &errorResponder{}
)

type errorResponder struct {
}

func (e *errorResponder) Error(w http.ResponseWriter, req *http.Request, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte(err.Error()))
}

func prefix(cluster *v3.Cluster) string {
	return "/k8s/clusters/" + cluster.Name
}

func New(cluster *v3.Cluster, factory dialer.Factory) (*RemoteService, error) {
	transport := &http.Transport{}

	if factory != nil {
		d, err := factory.ClusterDialer(cluster.Name)
		if err != nil {
			return nil, err
		}
		transport.Dial = d
	}

	if cluster.Status.CACert != "" {
		certBytes, err := base64.StdEncoding.DecodeString(cluster.Status.CACert)
		if err != nil {
			return nil, err
		}
		certs := x509.NewCertPool()
		certs.AppendCertsFromPEM(certBytes)
		transport.TLSClientConfig = &tls.Config{
			RootCAs: certs,
		}
	}

	u, err := url.Parse(cluster.Status.APIEndpoint)
	if err != nil {
		return nil, err
	}

	return &RemoteService{
		cluster:   cluster,
		transport: transport,
		url:       u,
		auth:      "Bearer " + cluster.Status.ServiceAccountToken,
	}, nil
}

func (r *RemoteService) Close() {
}

func (r *RemoteService) Handler() http.Handler {
	return r
}

func (r *RemoteService) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	u := *r.url
	u.Path = strings.TrimPrefix(req.URL.Path, prefix(r.cluster))
	u.RawQuery = req.URL.RawQuery

	proto := req.Header.Get("X-Forwarded-Proto")
	if proto != "" {
		req.URL.Scheme = proto
	} else if req.TLS == nil {
		req.URL.Scheme = "http"
	} else {
		req.URL.Scheme = "https"
	}

	req.URL.Host = req.Host
	req.Header.Set("Authorization", r.auth)

	httpProxy := proxy.NewUpgradeAwareHandler(&u, r.transport, true, false, er)
	httpProxy.ServeHTTP(rw, req)
}

func (r *RemoteService) Cluster() *v3.Cluster {
	return r.cluster
}
<|endoftext|>


<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.
*/

package clusterinfo

import (
	"context"
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/polymorphichelpers"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

const (
	defaultPodLogsTimeout = 20 * time.Second
	timeout               = 5 * time.Minute
)

type ClusterInfoDumpOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	OutputDir     string
	AllNamespaces bool
	Namespaces    []string

	Timeout          time.Duration
	AppsClient       appsv1client.AppsV1Interface
	CoreClient       corev1client.CoreV1Interface
	Namespace        string
	RESTClientGetter genericclioptions.RESTClientGetter
	LogsForObject    polymorphichelpers.LogsForObjectFunc

	genericclioptions.IOStreams
}

func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := &ClusterInfoDumpOptions{
		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme).WithDefaultOutput("json"),

		IOStreams: ioStreams,
	}

	cmd := &cobra.Command{
		Use:     "dump",
		Short:   i18n.T("Dump lots of relevant info for debugging and diagnosis"),
		Long:    dumpLong,
		Example: dumpExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files.  If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
	cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
	cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If true, dump all namespaces.  If true, --namespaces is ignored.")
	cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
	return cmd
}

var (
	dumpLong = templates.LongDesc(i18n.T(`
    Dumps cluster info out suitable for debugging and diagnosing cluster problems.  By default, dumps everything to
    stdout. You can optionally specify a directory with --output-directory.  If you specify a directory, kubernetes will
    build a set of files in that directory.  By default only dumps things in the 'kube-system' namespace, but you can
    switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.

    The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
    based on namespace and pod name.`))

	dumpExample = templates.Examples(i18n.T(`
    # Dump current cluster state to stdout
    kubectl cluster-info dump

    # Dump current cluster state to /path/to/cluster-state
    kubectl cluster-info dump --output-directory=/path/to/cluster-state

    # Dump all namespaces to stdout
    kubectl cluster-info dump --all-namespaces

    # Dump a set of namespaces to /path/to/cluster-state
    kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
)

func setupOutputWriter(dir string, defaultWriter io.Writer, filename string, fileExtension string) io.Writer {
	if len(dir) == 0 || dir == "-" {
		return defaultWriter
	}
	fullFile := path.Join(dir, filename) + fileExtension
	parent := path.Dir(fullFile)
	cmdutil.CheckErr(os.MkdirAll(parent, 0755))

	file, err := os.Create(fullFile)
	cmdutil.CheckErr(err)
	return file
}

func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	o.PrintObj = printer.PrintObj

	config, err := f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CoreClient, err = corev1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.AppsClient, err = appsv1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	// TODO this should eventually just be the completed kubeconfigflag struct
	o.RESTClientGetter = f
	o.LogsForObject = polymorphichelpers.LogsForObjectFn

	return nil
}

func (o *ClusterInfoDumpOptions) Run() error {
	nodes, err := o.CoreClient.Nodes().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return err
	}

	fileExtension := ".txt"
	if o.PrintFlags.OutputFormat != nil {
		switch *o.PrintFlags.OutputFormat {
		case "json":
			fileExtension = ".json"
		case "yaml":
			fileExtension = ".yaml"
		}
	}

	if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes", fileExtension)); err != nil {
		return err
	}

	var namespaces []string
	if o.AllNamespaces {
		namespaceList, err := o.CoreClient.Namespaces().List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		for ix := range namespaceList.Items {
			namespaces = append(namespaces, namespaceList.Items[ix].Name)
		}
	} else {
		if len(o.Namespaces) == 0 {
			namespaces = []string{
				metav1.NamespaceSystem,
				o.Namespace,
			}
		}
	}
	for _, namespace := range namespaces {
		// TODO: this is repetitive in the extreme.  Use reflection or
		// something to make this a for loop.
		events, err := o.CoreClient.Events(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events"), fileExtension)); err != nil {
			return err
		}

		rcs, err := o.CoreClient.ReplicationControllers(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers"), fileExtension)); err != nil {
			return err
		}

		svcs, err := o.CoreClient.Services(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services"), fileExtension)); err != nil {
			return err
		}

		sets, err := o.AppsClient.DaemonSets(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets"), fileExtension)); err != nil {
			return err
		}

		deps, err := o.AppsClient.Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments"), fileExtension)); err != nil {
			return err
		}

		rps, err := o.AppsClient.ReplicaSets(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets"), fileExtension)); err != nil {
			return err
		}

		pods, err := o.CoreClient.Pods(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}

		if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods"), fileExtension)); err != nil {
			return err
		}

		printContainer := func(writer io.Writer, container corev1.Container, pod *corev1.Pod) {
			writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
			defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))

			requests, err := o.LogsForObject(o.RESTClientGetter, pod, &corev1.PodLogOptions{Container: container.Name}, timeout, false)
			if err != nil {
				// Print error and return.
				writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
				return
			}

			for _, request := range requests {
				data, err := request.DoRaw(context.TODO())
				if err != nil {
					// Print error and return.
					writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
					return
				}
				writer.Write(data)
			}
		}

		for ix := range pods.Items {
			pod := &pods.Items[ix]
			containers := pod.Spec.Containers
			writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs"), ".txt")

			for i := range containers {
				printContainer(writer, containers[i], pod)
			}
		}
	}

	dest := o.OutputDir
	if len(dest) == 0 {
		dest = "standard output"
	}
	if dest != "-" {
		fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dest)
	}
	return nil
}
<commit_msg>Add init containers to dump info<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.
*/

package clusterinfo

import (
	"context"
	"fmt"
	"io"
	"os"
	"path"
	"time"

	"github.com/spf13/cobra"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/polymorphichelpers"
	"k8s.io/kubectl/pkg/scheme"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"
)

const (
	defaultPodLogsTimeout = 20 * time.Second
	timeout               = 5 * time.Minute
)

type ClusterInfoDumpOptions struct {
	PrintFlags *genericclioptions.PrintFlags
	PrintObj   printers.ResourcePrinterFunc

	OutputDir     string
	AllNamespaces bool
	Namespaces    []string

	Timeout          time.Duration
	AppsClient       appsv1client.AppsV1Interface
	CoreClient       corev1client.CoreV1Interface
	Namespace        string
	RESTClientGetter genericclioptions.RESTClientGetter
	LogsForObject    polymorphichelpers.LogsForObjectFunc

	genericclioptions.IOStreams
}

func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
	o := &ClusterInfoDumpOptions{
		PrintFlags: genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme).WithDefaultOutput("json"),

		IOStreams: ioStreams,
	}

	cmd := &cobra.Command{
		Use:     "dump",
		Short:   i18n.T("Dump lots of relevant info for debugging and diagnosis"),
		Long:    dumpLong,
		Example: dumpExample,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Complete(f, cmd))
			cmdutil.CheckErr(o.Run())
		},
	}

	o.PrintFlags.AddFlags(cmd)

	cmd.Flags().StringVar(&o.OutputDir, "output-directory", o.OutputDir, i18n.T("Where to output the files.  If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
	cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", o.Namespaces, "A comma separated list of namespaces to dump.")
	cmd.Flags().BoolVarP(&o.AllNamespaces, "all-namespaces", "A", o.AllNamespaces, "If true, dump all namespaces.  If true, --namespaces is ignored.")
	cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
	return cmd
}

var (
	dumpLong = templates.LongDesc(i18n.T(`
    Dumps cluster info out suitable for debugging and diagnosing cluster problems.  By default, dumps everything to
    stdout. You can optionally specify a directory with --output-directory.  If you specify a directory, kubernetes will
    build a set of files in that directory.  By default only dumps things in the 'kube-system' namespace, but you can
    switch to a different namespace with the --namespaces flag, or specify --all-namespaces to dump all namespaces.

    The command also dumps the logs of all of the pods in the cluster, these logs are dumped into different directories
    based on namespace and pod name.`))

	dumpExample = templates.Examples(i18n.T(`
    # Dump current cluster state to stdout
    kubectl cluster-info dump

    # Dump current cluster state to /path/to/cluster-state
    kubectl cluster-info dump --output-directory=/path/to/cluster-state

    # Dump all namespaces to stdout
    kubectl cluster-info dump --all-namespaces

    # Dump a set of namespaces to /path/to/cluster-state
    kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
)

func setupOutputWriter(dir string, defaultWriter io.Writer, filename string, fileExtension string) io.Writer {
	if len(dir) == 0 || dir == "-" {
		return defaultWriter
	}
	fullFile := path.Join(dir, filename) + fileExtension
	parent := path.Dir(fullFile)
	cmdutil.CheckErr(os.MkdirAll(parent, 0755))

	file, err := os.Create(fullFile)
	cmdutil.CheckErr(err)
	return file
}

func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
	printer, err := o.PrintFlags.ToPrinter()
	if err != nil {
		return err
	}

	o.PrintObj = printer.PrintObj

	config, err := f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CoreClient, err = corev1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.AppsClient, err = appsv1client.NewForConfig(config)
	if err != nil {
		return err
	}

	o.Timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
	if err != nil {
		return err
	}

	o.Namespace, _, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}
	// TODO this should eventually just be the completed kubeconfigflag struct
	o.RESTClientGetter = f
	o.LogsForObject = polymorphichelpers.LogsForObjectFn

	return nil
}

func (o *ClusterInfoDumpOptions) Run() error {
	nodes, err := o.CoreClient.Nodes().List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		return err
	}

	fileExtension := ".txt"
	if o.PrintFlags.OutputFormat != nil {
		switch *o.PrintFlags.OutputFormat {
		case "json":
			fileExtension = ".json"
		case "yaml":
			fileExtension = ".yaml"
		}
	}

	if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes", fileExtension)); err != nil {
		return err
	}

	var namespaces []string
	if o.AllNamespaces {
		namespaceList, err := o.CoreClient.Namespaces().List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		for ix := range namespaceList.Items {
			namespaces = append(namespaces, namespaceList.Items[ix].Name)
		}
	} else {
		if len(o.Namespaces) == 0 {
			namespaces = []string{
				metav1.NamespaceSystem,
				o.Namespace,
			}
		}
	}
	for _, namespace := range namespaces {
		// TODO: this is repetitive in the extreme.  Use reflection or
		// something to make this a for loop.
		events, err := o.CoreClient.Events(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events"), fileExtension)); err != nil {
			return err
		}

		rcs, err := o.CoreClient.ReplicationControllers(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers"), fileExtension)); err != nil {
			return err
		}

		svcs, err := o.CoreClient.Services(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services"), fileExtension)); err != nil {
			return err
		}

		sets, err := o.AppsClient.DaemonSets(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets"), fileExtension)); err != nil {
			return err
		}

		deps, err := o.AppsClient.Deployments(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments"), fileExtension)); err != nil {
			return err
		}

		rps, err := o.AppsClient.ReplicaSets(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}
		if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets"), fileExtension)); err != nil {
			return err
		}

		pods, err := o.CoreClient.Pods(namespace).List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			return err
		}

		if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods"), fileExtension)); err != nil {
			return err
		}

		printContainer := func(writer io.Writer, container corev1.Container, pod *corev1.Pod) {
			writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
			defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))

			requests, err := o.LogsForObject(o.RESTClientGetter, pod, &corev1.PodLogOptions{Container: container.Name}, timeout, false)
			if err != nil {
				// Print error and return.
				writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
				return
			}

			for _, request := range requests {
				data, err := request.DoRaw(context.TODO())
				if err != nil {
					// Print error and return.
					writer.Write([]byte(fmt.Sprintf("Request log error: %s\n", err.Error())))
					return
				}
				writer.Write(data)
			}
		}

		for ix := range pods.Items {
			pod := &pods.Items[ix]
			initcontainers := pod.Spec.InitContainers
			containers := pod.Spec.Containers
			writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs"), ".txt")

			for i := range initcontainers {
				printContainer(writer, initcontainers[i], pod)
			}
			for i := range containers {
				printContainer(writer, containers[i], pod)
			}
		}
	}

	dest := o.OutputDir
	if len(dest) == 0 {
		dest = "standard output"
	}
	if dest != "-" {
		fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dest)
	}
	return nil
}
<|endoftext|>


<commit_before>// +build !linux

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package nsenter

import (
	"k8s.io/utils/exec"
)

// Nsenter is part of experimental support for running the kubelet
// in a container.
type Nsenter struct {
	// a map of commands to their paths on the host filesystem
	Paths map[string]string
}

// NewNsenter constructs a new instance of Nsenter
func NewNsenter() *Nsenter {
	return &Nsenter{}
}

// Exec executes nsenter commands in hostProcMountNsPath mount namespace
func (ne *Nsenter) Exec(args ...string) exec.Cmd {
	return nil
}

// AbsHostPath returns the absolute runnable path for a specified command
func (ne *Nsenter) AbsHostPath(command string) string {
	return ""
}

// SupportsSystemd checks whether command systemd-run exists
func (ne *Nsenter) SupportsSystemd() (string, bool) {
	return "", false
}
<commit_msg>Fix nsenter on Mac<commit_after>// +build !linux

/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package nsenter

import (
	"k8s.io/utils/exec"
)

// Nsenter is part of experimental support for running the kubelet
// in a container.
type Nsenter struct {
	// a map of commands to their paths on the host filesystem
	Paths map[string]string
}

// NewNsenter constructs a new instance of Nsenter
func NewNsenter() *Nsenter {
	return &Nsenter{}
}

// Exec executes nsenter commands in hostProcMountNsPath mount namespace
func (ne *Nsenter) Exec(cmd string, args []string) exec.Cmd {
	return nil
}

// AbsHostPath returns the absolute runnable path for a specified command
func (ne *Nsenter) AbsHostPath(command string) string {
	return ""
}

// SupportsSystemd checks whether command systemd-run exists
func (ne *Nsenter) SupportsSystemd() (string, bool) {
	return "", false
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/mcuadros/go-version"
	"io"
	"log"
	"mime/multipart"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

type Config struct {
	Host               string
	Port               int
	RequestCacheSize   int
	Logfile            string
	RepoLocation       string
	TmpDir             string
	RepoRebuildCommand string
	Token              []Token
}

type Token struct {
	Value string
	Owner string
	Repo  []Repo
}

type Repo struct {
	Name string
}

func main() {
	var config Config
	if _, err := toml.DecodeFile("/etc/deb-drop/deb-drop.toml", &config); err != nil {
		fmt.Println("Failed to parse config file", err.Error())
		os.Exit(1)
	}

	logfile, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		fmt.Println("Can not open logfile", config.Logfile, err)
		os.Exit(1)
	}
	lg := log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	// We need to validate config a bit before we run server
	for _, token := range config.Token {
		for _, repo := range token.Repo {
			err = validateRepos(lg, config.RepoLocation, []string{repo.Name})
			if err != nil {
				lg.Println("Found invalid repo. Next time will refuse to run", err)
			}
		}

	}

	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
	if err != nil {
		lg.Println("Error:", err)
	}

	http.HandleFunc("/", makeHandler(lg, &config, mainHandler))
	err = fcgi.Serve(l, nil)

	if err != nil {
		lg.Println("Error:", err)
	}
}

func makeHandler(lg *log.Logger, config *Config, fn func(http.ResponseWriter, *http.Request, *Config, *log.Logger)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, config, lg)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, config *Config, lg *log.Logger) {

	if r.Method == "HEAD" {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, "Hello healthcheck")
		return
	}

	repos := strings.Split(r.FormValue("repos"), ",")
	err := validateRepos(lg, config.RepoLocation, repos)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	err = validateToken(lg, config, r.FormValue("token"), repos)
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	//Check if old packages should be removed
	keepVersions, err := strconv.Atoi(r.FormValue("versions"))
	if err != nil || keepVersions < 1 {
		keepVersions = 5
	}

	var content multipart.File
	var packageName string

	// We can get package name from FORM or from parameter. It depends if there is an upload or copy/get
	if r.FormValue("package") != "" {
		packageName = r.FormValue("package")
	} else {
		// This is upload
		header := new(multipart.FileHeader)
		content, header, err = r.FormFile("package")
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}
		defer content.Close()
		packageName = header.Filename
	}

	if r.Method == "GET" {
		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName, false)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		if len(repos) != 1 {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println("You should pass exactly 1 repo")
			fmt.Fprintln(w, "You should pass exactly 1 repo")
			return
		}

		pattern := config.RepoLocation + "/" + repos[0] + "/" + packageName + "*"
		matches := getPackagesByPattern(pattern)
		if len(matches) == 0 {
			w.WriteHeader(http.StatusNotFound)
			lg.Println(pattern + " is not found")
			return
		} else {
			w.WriteHeader(http.StatusOK)
			for i := 0; i < keepVersions; i++ {
				element := len(matches) - 1 - i
				if element < 0 {
					break
				}
				fmt.Fprintln(w, path.Base(matches[element]))
			}
			return
		}
	} else if r.Method == "POST" {
		// Allow caching of up to <amount> in memory before buffering to disk. In MB
		err = r.ParseMultipartForm(int64(config.RequestCacheSize * 1024))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName, true)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		repositories := repos

		if r.FormValue("package") != "" {
			// This is used when package is passed as name, which means it is copy action

			// Open original file
			content, err = os.Open(config.RepoLocation + "/" + repos[0] + "/" + packageName)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println(err)
				fmt.Fprintf(w, "Can not find original package %s in %s", packageName, repos[0])
				return
			}
			defer content.Close()

			// We need at least 2 repos to copy package between
			if len(repos) < 2 {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println("You should pass at least 2 repo")
				fmt.Fprintln(w, "You should pass at least 2 repo")
				return
			}
			repositories = repos[1:]
		}

		err = addToRepos(lg, config, content, repositories, packageName)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		err = removeOldPackages(lg, config, repos, packageName, keepVersions)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
		fmt.Fprintln(w, "Unsupported method "+r.Method)
		return
	}

	err = generateRepos(lg, config, repos)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, err)
		return
	}
}

func validateToken(lg *log.Logger, config *Config, token string, repos []string) error {
	// Going over all tokens in configuration to find requested
	if token == "" {
		lg.Printf("Attempt to access %s without token", repos)
		return fmt.Errorf("%s", "You must specify token")
	}

	var foundToken bool
	for _, configToken := range config.Token {
		if configToken.Value == token {
			foundToken = true
			// Checking all requested repos to be allowed for this token
			for _, requestedRepo := range repos {
				var foundRepo bool
				for _, configRepo := range configToken.Repo {
					if configRepo.Name == requestedRepo {
						foundRepo = true
						break
					}
				}
				if !foundRepo {
					lg.Println("Use of valid token with not listed repo " + requestedRepo)
					return fmt.Errorf("%s", "Token is not allowed to use on one or more of the specified repos")
				}
			}
			return nil
		}
	}

	if !foundToken {
		lg.Printf("Attempt to access %s with invalid token\n", repos)
		return fmt.Errorf("%s", "Token is not allowed to use one or more of the specified repos")
	}

	return nil
}

func validateRepos(lg *log.Logger, repoLocation string, repos []string) error {
	if len(repos) == 0 {
		lg.Println("You should pass at least 1 repo")
		return fmt.Errorf("%s", "You should pass at least 1 repo")
	}

	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		if len(parts) != 3 {
			lg.Println("Repo has invalid format")
			return fmt.Errorf("%s", "Repo has invalid format")
		}

		stat, err := os.Stat(repoLocation + "/" + repo)
		if err != nil {
			lg.Println("Repository does not exist", err)
			return fmt.Errorf("%s", "Repository does not exist")
		}

		if !stat.IsDir() {
			lg.Println("Specified repository location exists but is not a directory")
			return fmt.Errorf("%s", "Specified repository location exists but is not a directory")
		}
	}
	return nil
}

func validatePackageName(lg *log.Logger, name string, strict bool) error {
	r := new(regexp.Regexp)
	if strict {
		r = regexp.MustCompile("^([-0-9A-Za-z.]+_){2}[-0-9A-Za-z]+.deb$")
	} else {
		r = regexp.MustCompile("^([-0-9A-Za-z._]*)$")
	}

	lg.Println(name)
	if !r.MatchString(name) {
		lg.Println("Somebody tried to pass invalid package name", name)
		return fmt.Errorf("%s", "Invalid package name")
	}
	return nil
}

func writeStreamToTmpFile(lg *log.Logger, content io.Reader, tmpFilePath string) error {
	tmpDir := filepath.Dir(tmpFilePath)
	stat, err := os.Stat(tmpDir)
	if err != nil {
		lg.Printf("%s does not exist. Creating...\n", tmpDir)
		err = os.Mkdir(tmpDir, os.ModePerm)
		if err != nil {
			lg.Println(err)
			return err
		}
	} else if !stat.IsDir() {
		lg.Printf("%s exists, but it is not a directory\n", tmpDir)
		return fmt.Errorf("%s exists, but it is not a directory", tmpDir)
	}

	tmpFile, err := os.Create(tmpFilePath)
	if err != nil {
		lg.Println(err)
		return err
	}
	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, content)
	if err != nil {
		lg.Printf("Can not save data from POST to %s\n", tmpFilePath)
		return err
	}
	return nil

}

func addToRepos(lg *log.Logger, config *Config, content io.Reader, repos []string, packageName string) error {
	tmpFilePath := fmt.Sprintf("%s/%s", config.TmpDir, packageName)
	err := writeStreamToTmpFile(lg, content, tmpFilePath)
	if err != nil {
		return err
	}
	defer os.Remove(tmpFilePath)

	for _, repo := range repos {
		fileInRepo := config.RepoLocation + "/" + repo + "/" + packageName
		err := os.Link(tmpFilePath, fileInRepo)
		if err != nil {
			lg.Printf("Can not link package %s to %s", tmpFilePath, fileInRepo)
			return err
		}
	}
	return nil
}

func getPackagesByPattern(pattern string) []string {
	matches, _ := filepath.Glob(pattern)
	version.Sort(matches)
	return matches
}

func removeOldPackages(lg *log.Logger, config *Config, repos []string, fileName string, keepVersions int) error {
	packageName := strings.Split(fileName, "_")[0]
	for _, repo := range repos {
		matches := getPackagesByPattern(config.RepoLocation + "/" + repo + "/" + packageName + "_*")
		if len(matches) > keepVersions {
			to_remove := len(matches) - keepVersions
			for _, file := range matches[:to_remove] {
				lg.Println("Removing", file)
				err := os.Remove(file)
				if err != nil {
					lg.Println("Could remove package '", file, "' from Repo: '", err, "'")
					return fmt.Errorf("%s", "Cleanup of old packages has failed")
				}
			}
		}

	}
	return nil
}

func generateRepos(lg *log.Logger, config *Config, repos []string) error {
	// Rebuild repositories only once
	names := make(map[string]string)
	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		names[parts[0]] = repo
	}

	for name, repo := range names {
		var cmd *exec.Cmd
		lg.Println("running", config.RepoRebuildCommand, repo)
		parts := strings.Fields(config.RepoRebuildCommand)
		head := parts[0]
		parts = parts[1:]
		parts = append(parts, repo)
		cmd = exec.Command(head, parts...)
		err := cmd.Run()
		if err != nil {
			lg.Println("Could not generate metadata for", name, ":", err)
			return fmt.Errorf("Could not generate metadata for %s : %v", name, err)
		}
	}
	return nil
}
<commit_msg>Removed debug message<commit_after>package main

import (
	"fmt"
	"github.com/BurntSushi/toml"
	"github.com/mcuadros/go-version"
	"io"
	"log"
	"mime/multipart"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

type Config struct {
	Host               string
	Port               int
	RequestCacheSize   int
	Logfile            string
	RepoLocation       string
	TmpDir             string
	RepoRebuildCommand string
	Token              []Token
}

type Token struct {
	Value string
	Owner string
	Repo  []Repo
}

type Repo struct {
	Name string
}

func main() {
	var config Config
	if _, err := toml.DecodeFile("/etc/deb-drop/deb-drop.toml", &config); err != nil {
		fmt.Println("Failed to parse config file", err.Error())
		os.Exit(1)
	}

	logfile, err := os.OpenFile(config.Logfile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
	if err != nil {
		fmt.Println("Can not open logfile", config.Logfile, err)
		os.Exit(1)
	}
	lg := log.New(logfile, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)

	// We need to validate config a bit before we run server
	for _, token := range config.Token {
		for _, repo := range token.Repo {
			err = validateRepos(lg, config.RepoLocation, []string{repo.Name})
			if err != nil {
				lg.Println("Found invalid repo. Next time will refuse to run", err)
			}
		}

	}

	l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Host, config.Port))
	if err != nil {
		lg.Println("Error:", err)
	}

	http.HandleFunc("/", makeHandler(lg, &config, mainHandler))
	err = fcgi.Serve(l, nil)

	if err != nil {
		lg.Println("Error:", err)
	}
}

func makeHandler(lg *log.Logger, config *Config, fn func(http.ResponseWriter, *http.Request, *Config, *log.Logger)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		fn(w, r, config, lg)
	}
}

func mainHandler(w http.ResponseWriter, r *http.Request, config *Config, lg *log.Logger) {

	if r.Method == "HEAD" {
		w.WriteHeader(http.StatusOK)
		fmt.Fprintln(w, "Hello healthcheck")
		return
	}

	repos := strings.Split(r.FormValue("repos"), ",")
	err := validateRepos(lg, config.RepoLocation, repos)
	if err != nil {
		w.WriteHeader(http.StatusNotFound)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	err = validateToken(lg, config, r.FormValue("token"), repos)
	if err != nil {
		w.WriteHeader(http.StatusForbidden)
		lg.Println(err)
		fmt.Fprintln(w, err)
		return
	}

	//Check if old packages should be removed
	keepVersions, err := strconv.Atoi(r.FormValue("versions"))
	if err != nil || keepVersions < 1 {
		keepVersions = 5
	}

	var content multipart.File
	var packageName string

	// We can get package name from FORM or from parameter. It depends if there is an upload or copy/get
	if r.FormValue("package") != "" {
		packageName = r.FormValue("package")
	} else {
		// This is upload
		header := new(multipart.FileHeader)
		content, header, err = r.FormFile("package")
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}
		defer content.Close()
		packageName = header.Filename
	}

	if r.Method == "GET" {
		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName, false)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		if len(repos) != 1 {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println("You should pass exactly 1 repo")
			fmt.Fprintln(w, "You should pass exactly 1 repo")
			return
		}

		pattern := config.RepoLocation + "/" + repos[0] + "/" + packageName + "*"
		matches := getPackagesByPattern(pattern)
		if len(matches) == 0 {
			w.WriteHeader(http.StatusNotFound)
			lg.Println(pattern + " is not found")
			return
		} else {
			w.WriteHeader(http.StatusOK)
			for i := 0; i < keepVersions; i++ {
				element := len(matches) - 1 - i
				if element < 0 {
					break
				}
				fmt.Fprintln(w, path.Base(matches[element]))
			}
			return
		}
	} else if r.Method == "POST" {
		// Allow caching of up to <amount> in memory before buffering to disk. In MB
		err = r.ParseMultipartForm(int64(config.RequestCacheSize * 1024))
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		// Package name needs to be validated only when we are making changes
		err = validatePackageName(lg, packageName, true)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		repositories := repos

		if r.FormValue("package") != "" {
			// This is used when package is passed as name, which means it is copy action

			// Open original file
			content, err = os.Open(config.RepoLocation + "/" + repos[0] + "/" + packageName)
			if err != nil {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println(err)
				fmt.Fprintf(w, "Can not find original package %s in %s", packageName, repos[0])
				return
			}
			defer content.Close()

			// We need at least 2 repos to copy package between
			if len(repos) < 2 {
				w.WriteHeader(http.StatusBadRequest)
				lg.Println("You should pass at least 2 repo")
				fmt.Fprintln(w, "You should pass at least 2 repo")
				return
			}
			repositories = repos[1:]
		}

		err = addToRepos(lg, config, content, repositories, packageName)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			lg.Println(err)
			fmt.Fprintln(w, err)
			return
		}

		err = removeOldPackages(lg, config, repos, packageName, keepVersions)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
			return
		}
	} else {
		w.WriteHeader(http.StatusMethodNotAllowed)
		fmt.Fprintln(w, "Unsupported method "+r.Method)
		return
	}

	err = generateRepos(lg, config, repos)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, err)
		return
	}
}

func validateToken(lg *log.Logger, config *Config, token string, repos []string) error {
	// Going over all tokens in configuration to find requested
	if token == "" {
		lg.Printf("Attempt to access %s without token", repos)
		return fmt.Errorf("%s", "You must specify token")
	}

	var foundToken bool
	for _, configToken := range config.Token {
		if configToken.Value == token {
			foundToken = true
			// Checking all requested repos to be allowed for this token
			for _, requestedRepo := range repos {
				var foundRepo bool
				for _, configRepo := range configToken.Repo {
					if configRepo.Name == requestedRepo {
						foundRepo = true
						break
					}
				}
				if !foundRepo {
					lg.Println("Use of valid token with not listed repo " + requestedRepo)
					return fmt.Errorf("%s", "Token is not allowed to use on one or more of the specified repos")
				}
			}
			return nil
		}
	}

	if !foundToken {
		lg.Printf("Attempt to access %s with invalid token\n", repos)
		return fmt.Errorf("%s", "Token is not allowed to use one or more of the specified repos")
	}

	return nil
}

func validateRepos(lg *log.Logger, repoLocation string, repos []string) error {
	if len(repos) == 0 {
		lg.Println("You should pass at least 1 repo")
		return fmt.Errorf("%s", "You should pass at least 1 repo")
	}

	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		if len(parts) != 3 {
			lg.Println("Repo has invalid format")
			return fmt.Errorf("%s", "Repo has invalid format")
		}

		stat, err := os.Stat(repoLocation + "/" + repo)
		if err != nil {
			lg.Println("Repository does not exist", err)
			return fmt.Errorf("%s", "Repository does not exist")
		}

		if !stat.IsDir() {
			lg.Println("Specified repository location exists but is not a directory")
			return fmt.Errorf("%s", "Specified repository location exists but is not a directory")
		}
	}
	return nil
}

func validatePackageName(lg *log.Logger, name string, strict bool) error {
	r := new(regexp.Regexp)
	if strict {
		r = regexp.MustCompile("^([-0-9A-Za-z.]+_){2}[-0-9A-Za-z]+.deb$")
	} else {
		r = regexp.MustCompile("^([-0-9A-Za-z._]*)$")
	}

	if !r.MatchString(name) {
		lg.Println("Somebody tried to pass invalid package name", name)
		return fmt.Errorf("%s", "Invalid package name")
	}
	return nil
}

func writeStreamToTmpFile(lg *log.Logger, content io.Reader, tmpFilePath string) error {
	tmpDir := filepath.Dir(tmpFilePath)
	stat, err := os.Stat(tmpDir)
	if err != nil {
		lg.Printf("%s does not exist. Creating...\n", tmpDir)
		err = os.Mkdir(tmpDir, os.ModePerm)
		if err != nil {
			lg.Println(err)
			return err
		}
	} else if !stat.IsDir() {
		lg.Printf("%s exists, but it is not a directory\n", tmpDir)
		return fmt.Errorf("%s exists, but it is not a directory", tmpDir)
	}

	tmpFile, err := os.Create(tmpFilePath)
	if err != nil {
		lg.Println(err)
		return err
	}
	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, content)
	if err != nil {
		lg.Printf("Can not save data from POST to %s\n", tmpFilePath)
		return err
	}
	return nil

}

func addToRepos(lg *log.Logger, config *Config, content io.Reader, repos []string, packageName string) error {
	tmpFilePath := fmt.Sprintf("%s/%s", config.TmpDir, packageName)
	err := writeStreamToTmpFile(lg, content, tmpFilePath)
	if err != nil {
		return err
	}
	defer os.Remove(tmpFilePath)

	for _, repo := range repos {
		fileInRepo := config.RepoLocation + "/" + repo + "/" + packageName
		err := os.Link(tmpFilePath, fileInRepo)
		if err != nil {
			lg.Printf("Can not link package %s to %s", tmpFilePath, fileInRepo)
			return err
		}
	}
	return nil
}

func getPackagesByPattern(pattern string) []string {
	matches, _ := filepath.Glob(pattern)
	version.Sort(matches)
	return matches
}

func removeOldPackages(lg *log.Logger, config *Config, repos []string, fileName string, keepVersions int) error {
	packageName := strings.Split(fileName, "_")[0]
	for _, repo := range repos {
		matches := getPackagesByPattern(config.RepoLocation + "/" + repo + "/" + packageName + "_*")
		if len(matches) > keepVersions {
			to_remove := len(matches) - keepVersions
			for _, file := range matches[:to_remove] {
				lg.Println("Removing", file)
				err := os.Remove(file)
				if err != nil {
					lg.Println("Could remove package '", file, "' from Repo: '", err, "'")
					return fmt.Errorf("%s", "Cleanup of old packages has failed")
				}
			}
		}

	}
	return nil
}

func generateRepos(lg *log.Logger, config *Config, repos []string) error {
	// Rebuild repositories only once
	names := make(map[string]string)
	for _, repo := range repos {
		parts := strings.Split(repo, "-")
		names[parts[0]] = repo
	}

	for name, repo := range names {
		var cmd *exec.Cmd
		lg.Println("running", config.RepoRebuildCommand, repo)
		parts := strings.Fields(config.RepoRebuildCommand)
		head := parts[0]
		parts = parts[1:]
		parts = append(parts, repo)
		cmd = exec.Command(head, parts...)
		err := cmd.Run()
		if err != nil {
			lg.Println("Could not generate metadata for", name, ":", err)
			return fmt.Errorf("Could not generate metadata for %s : %v", name, err)
		}
	}
	return nil
}
<|endoftext|>