<commit_before>package prelude

const jsmapping = `
var $jsObjectPtr, $jsErrorPtr;

var $needsExternalization = function(t) {
  switch (t.kind) {
    case $kindBool:
    case $kindInt:
    case $kindInt8:
    case $kindInt16:
    case $kindInt32:
    case $kindUint:
    case $kindUint8:
    case $kindUint16:
    case $kindUint32:
    case $kindUintptr:
    case $kindFloat32:
    case $kindFloat64:
      return false;
    default:
      return t !== $jsObjectPtr;
  }
};

var $externalize = function(v, t) {
  if (t === $jsObjectPtr) {
    return v;
  }
  switch (t.kind) {
  case $kindBool:
  case $kindInt:
  case $kindInt8:
  case $kindInt16:
  case $kindInt32:
  case $kindUint:
  case $kindUint8:
  case $kindUint16:
  case $kindUint32:
  case $kindUintptr:
  case $kindFloat32:
  case $kindFloat64:
    return v;
  case $kindInt64:
  case $kindUint64:
    return $flatten64(v);
  case $kindArray:
    if ($needsExternalization(t.elem)) {
      return $mapArray(v, function(e) { return $externalize(e, t.elem); });
    }
    return v;
  case $kindFunc:
    if (v === $throwNilPointerError) {
      return null;
    }
    if (v.$externalizeWrapper === undefined) {
      $checkForDeadlock = false;
      var convert = false;
      for (var i = 0; i < t.params.length; i++) {
        convert = convert || (t.params[i] !== $jsObjectPtr);
      }
      for (var i = 0; i < t.results.length; i++) {
        convert = convert || $needsExternalization(t.results[i]);
      }
      v.$externalizeWrapper = v;
      if (convert) {
        v.$externalizeWrapper = function() {
          var args = [];
          for (var i = 0; i < t.params.length; i++) {
            if (t.variadic && i === t.params.length - 1) {
              var vt = t.params[i].elem, varargs = [];
              for (var j = i; j < arguments.length; j++) {
                varargs.push($internalize(arguments[j], vt));
              }
              args.push(new (t.params[i])(varargs));
              break;
            }
            args.push($internalize(arguments[i], t.params[i]));
          }
          var result = v.apply(this, args);
          switch (t.results.length) {
          case 0:
            return;
          case 1:
            return $externalize(result, t.results[0]);
          default:
            for (var i = 0; i < t.results.length; i++) {
              result[i] = $externalize(result[i], t.results[i]);
            }
            return result;
          }
        };
      }
    }
    return v.$externalizeWrapper;
  case $kindInterface:
    if (v === $ifaceNil) {
      return null;
    }
    if (v.constructor === $jsObjectPtr) {
      return v.$val.object;
    }
    return $externalize(v.$val, v.constructor);
  case $kindMap:
    var m = {};
    var keys = $keys(v);
    for (var i = 0; i < keys.length; i++) {
      var entry = v[keys[i]];
      m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
    }
    return m;
  case $kindPtr:
    if (v === t.nil) {
      return null;
    }
    return $externalize(v.$get(), t.elem);
  case $kindSlice:
    if ($needsExternalization(t.elem)) {
      return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
    }
    return $sliceToArray(v);
  case $kindString:
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "", r;
    for (var i = 0; i < v.length; i += r[1]) {
      r = $decodeRune(v, i);
      s += String.fromCharCode(r[0]);
    }
    return s;
  case $kindStruct:
    var timePkg = $packages["time"];
    if (timePkg && v.constructor === timePkg.Time.ptr) {
      var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
      return new Date($flatten64(milli));
    }

    var noJsObject = {};
    var searchJsObject = function(v, t) {
      if (t === $jsObjectPtr) {
        return v;
      }
      if (t.kind === $kindPtr && v !== t.nil) {
        var o = searchJsObject(v.$get(), t.elem);
        if (o !== noJsObject) {
          return o;
        }
      }
      if (t.kind === $kindStruct) {
        for (var i = 0; i < t.fields.length; i++) {
          var f = t.fields[i];
          var o = searchJsObject(v[f.prop], f.typ);
          if (o !== noJsObject) {
            return o;
          }
        }
      }
      return noJsObject;
    };
    var o = searchJsObject(v, t);
    if (o !== noJsObject) {
      return o;
    }

    o = {};
    for (var i = 0; i < t.fields.length; i++) {
      var f = t.fields[i];
      if (f.pkg !== "") { /* not exported */
        continue;
      }
      o[f.name] = $externalize(v[f.prop], f.typ);
    }
    return o;
  }
  $panic(new $String("cannot externalize " + t.string));
};

var $internalize = function(v, t, recv) {
  if (t === $jsObjectPtr) {
    return v;
  }
  switch (t.kind) {
  case $kindBool:
    return !!v;
  case $kindInt:
    return parseInt(v);
  case $kindInt8:
    return parseInt(v) << 24 >> 24;
  case $kindInt16:
    return parseInt(v) << 16 >> 16;
  case $kindInt32:
    return parseInt(v) >> 0;
  case $kindUint:
    return parseInt(v);
  case $kindUint8:
    return parseInt(v) << 24 >>> 24;
  case $kindUint16:
    return parseInt(v) << 16 >>> 16;
  case $kindUint32:
  case $kindUintptr:
    return parseInt(v) >>> 0;
  case $kindInt64:
  case $kindUint64:
    return new t(0, v);
  case $kindFloat32:
  case $kindFloat64:
    return parseFloat(v);
  case $kindArray:
    if (v.length !== t.len) {
      $throwRuntimeError("got array with wrong size from JavaScript native");
    }
    return $mapArray(v, function(e) { return $internalize(e, t.elem); });
  case $kindFunc:
    return function() {
      var args = [];
      for (var i = 0; i < t.params.length; i++) {
        if (t.variadic && i === t.params.length - 1) {
          var vt = t.params[i].elem, varargs = arguments[i];
          for (var j = 0; j < varargs.$length; j++) {
            args.push($externalize(varargs.$array[varargs.$offset + j], vt));
          }
          break;
        }
        args.push($externalize(arguments[i], t.params[i]));
      }
      var result = v.apply(recv, args);
      switch (t.results.length) {
      case 0:
        return;
      case 1:
        return $internalize(result, t.results[0]);
      default:
        for (var i = 0; i < t.results.length; i++) {
          result[i] = $internalize(result[i], t.results[i]);
        }
        return result;
      }
    };
  case $kindInterface:
    if (t.methods.length !== 0) {
      $panic(new $String("cannot internalize " + t.string));
    }
    if (v === null) {
      return $ifaceNil;
    }
    switch (v.constructor) {
    case Int8Array:
      return new ($sliceType($Int8))(v);
    case Int16Array:
      return new ($sliceType($Int16))(v);
    case Int32Array:
      return new ($sliceType($Int))(v);
    case Uint8Array:
      return new ($sliceType($Uint8))(v);
    case Uint16Array:
      return new ($sliceType($Uint16))(v);
    case Uint32Array:
      return new ($sliceType($Uint))(v);
    case Float32Array:
      return new ($sliceType($Float32))(v);
    case Float64Array:
      return new ($sliceType($Float64))(v);
    case Array:
      return $internalize(v, $sliceType($emptyInterface));
    case Boolean:
      return new $Bool(!!v);
    case Date:
      var timePkg = $packages["time"];
      if (timePkg) {
        return new timePkg.Time(timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000)));
      }
    case Function:
      var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true);
      return new funcType($internalize(v, funcType));
    case Number:
      return new $Float64(parseFloat(v));
    case String:
      return new $String($internalize(v, $String));
    default:
      if ($global.Node && v instanceof $global.Node) {
        return new $jsObjectPtr(v);
      }
      var mapType = $mapType($String, $emptyInterface);
      return new mapType($internalize(v, mapType));
    }
  case $kindMap:
    var m = new $Map();
    var keys = $keys(v);
    for (var i = 0; i < keys.length; i++) {
      var key = $internalize(keys[i], t.key);
      m[key.$key ? key.$key() : key] = { k: key, v: $internalize(v[keys[i]], t.elem) };
    }
    return m;
  case $kindPtr:
    if (t.elem.kind === $kindStruct) {
      return $internalize(v, t.elem);
    }
  case $kindSlice:
    return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
  case $kindString:
    v = String(v);
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "";
    for (var i = 0; i < v.length; i++) {
      s += $encodeRune(v.charCodeAt(i));
    }
    return s;
  case $kindStruct:
    var noJsObject = {};
    var searchJsObject = function(t) {
      if (t === $jsObjectPtr) {
        return v;
      }
      if (t.kind === $kindPtr && t.elem.kind === $kindStruct) {
        var o = searchJsObject(t.elem);
        if (o !== noJsObject) {
          return o;
        }
      }
      if (t.kind === $kindStruct) {
        for (var i = 0; i < t.fields.length; i++) {
          var f = t.fields[i];
          var o = searchJsObject(f.typ);
          if (o !== noJsObject) {
            var n = new t.ptr();
            n[f.prop] = o;
            return n;
          }
        }
      }
      return noJsObject;
    };
    var o = searchJsObject(t);
    if (o !== noJsObject) {
      return o;
    }
  }
  $panic(new $String("cannot internalize " + t.string));
};
`
<commit_msg>panic on internalization of js.Object (use *js.Object instead)<commit_after>package prelude

const jsmapping = `
var $jsObjectPtr, $jsErrorPtr;

var $needsExternalization = function(t) {
  switch (t.kind) {
    case $kindBool:
    case $kindInt:
    case $kindInt8:
    case $kindInt16:
    case $kindInt32:
    case $kindUint:
    case $kindUint8:
    case $kindUint16:
    case $kindUint32:
    case $kindUintptr:
    case $kindFloat32:
    case $kindFloat64:
      return false;
    default:
      return t !== $jsObjectPtr;
  }
};

var $externalize = function(v, t) {
  if (t === $jsObjectPtr) {
    return v;
  }
  switch (t.kind) {
  case $kindBool:
  case $kindInt:
  case $kindInt8:
  case $kindInt16:
  case $kindInt32:
  case $kindUint:
  case $kindUint8:
  case $kindUint16:
  case $kindUint32:
  case $kindUintptr:
  case $kindFloat32:
  case $kindFloat64:
    return v;
  case $kindInt64:
  case $kindUint64:
    return $flatten64(v);
  case $kindArray:
    if ($needsExternalization(t.elem)) {
      return $mapArray(v, function(e) { return $externalize(e, t.elem); });
    }
    return v;
  case $kindFunc:
    if (v === $throwNilPointerError) {
      return null;
    }
    if (v.$externalizeWrapper === undefined) {
      $checkForDeadlock = false;
      var convert = false;
      for (var i = 0; i < t.params.length; i++) {
        convert = convert || (t.params[i] !== $jsObjectPtr);
      }
      for (var i = 0; i < t.results.length; i++) {
        convert = convert || $needsExternalization(t.results[i]);
      }
      v.$externalizeWrapper = v;
      if (convert) {
        v.$externalizeWrapper = function() {
          var args = [];
          for (var i = 0; i < t.params.length; i++) {
            if (t.variadic && i === t.params.length - 1) {
              var vt = t.params[i].elem, varargs = [];
              for (var j = i; j < arguments.length; j++) {
                varargs.push($internalize(arguments[j], vt));
              }
              args.push(new (t.params[i])(varargs));
              break;
            }
            args.push($internalize(arguments[i], t.params[i]));
          }
          var result = v.apply(this, args);
          switch (t.results.length) {
          case 0:
            return;
          case 1:
            return $externalize(result, t.results[0]);
          default:
            for (var i = 0; i < t.results.length; i++) {
              result[i] = $externalize(result[i], t.results[i]);
            }
            return result;
          }
        };
      }
    }
    return v.$externalizeWrapper;
  case $kindInterface:
    if (v === $ifaceNil) {
      return null;
    }
    if (v.constructor === $jsObjectPtr) {
      return v.$val.object;
    }
    return $externalize(v.$val, v.constructor);
  case $kindMap:
    var m = {};
    var keys = $keys(v);
    for (var i = 0; i < keys.length; i++) {
      var entry = v[keys[i]];
      m[$externalize(entry.k, t.key)] = $externalize(entry.v, t.elem);
    }
    return m;
  case $kindPtr:
    if (v === t.nil) {
      return null;
    }
    return $externalize(v.$get(), t.elem);
  case $kindSlice:
    if ($needsExternalization(t.elem)) {
      return $mapArray($sliceToArray(v), function(e) { return $externalize(e, t.elem); });
    }
    return $sliceToArray(v);
  case $kindString:
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "", r;
    for (var i = 0; i < v.length; i += r[1]) {
      r = $decodeRune(v, i);
      s += String.fromCharCode(r[0]);
    }
    return s;
  case $kindStruct:
    var timePkg = $packages["time"];
    if (timePkg && v.constructor === timePkg.Time.ptr) {
      var milli = $div64(v.UnixNano(), new $Int64(0, 1000000));
      return new Date($flatten64(milli));
    }

    var noJsObject = {};
    var searchJsObject = function(v, t) {
      if (t === $jsObjectPtr) {
        return v;
      }
      if (t.kind === $kindPtr && v !== t.nil) {
        var o = searchJsObject(v.$get(), t.elem);
        if (o !== noJsObject) {
          return o;
        }
      }
      if (t.kind === $kindStruct) {
        for (var i = 0; i < t.fields.length; i++) {
          var f = t.fields[i];
          var o = searchJsObject(v[f.prop], f.typ);
          if (o !== noJsObject) {
            return o;
          }
        }
      }
      return noJsObject;
    };
    var o = searchJsObject(v, t);
    if (o !== noJsObject) {
      return o;
    }

    o = {};
    for (var i = 0; i < t.fields.length; i++) {
      var f = t.fields[i];
      if (f.pkg !== "") { /* not exported */
        continue;
      }
      o[f.name] = $externalize(v[f.prop], f.typ);
    }
    return o;
  }
  $panic(new $String("cannot externalize " + t.string));
};

var $internalize = function(v, t, recv) {
  if (t === $jsObjectPtr) {
    return v;
  }
  if (t === $jsObjectPtr.elem) {
    $panic(new $String("cannot internalize js.Object, use *js.Object instead"));
  }
  switch (t.kind) {
  case $kindBool:
    return !!v;
  case $kindInt:
    return parseInt(v);
  case $kindInt8:
    return parseInt(v) << 24 >> 24;
  case $kindInt16:
    return parseInt(v) << 16 >> 16;
  case $kindInt32:
    return parseInt(v) >> 0;
  case $kindUint:
    return parseInt(v);
  case $kindUint8:
    return parseInt(v) << 24 >>> 24;
  case $kindUint16:
    return parseInt(v) << 16 >>> 16;
  case $kindUint32:
  case $kindUintptr:
    return parseInt(v) >>> 0;
  case $kindInt64:
  case $kindUint64:
    return new t(0, v);
  case $kindFloat32:
  case $kindFloat64:
    return parseFloat(v);
  case $kindArray:
    if (v.length !== t.len) {
      $throwRuntimeError("got array with wrong size from JavaScript native");
    }
    return $mapArray(v, function(e) { return $internalize(e, t.elem); });
  case $kindFunc:
    return function() {
      var args = [];
      for (var i = 0; i < t.params.length; i++) {
        if (t.variadic && i === t.params.length - 1) {
          var vt = t.params[i].elem, varargs = arguments[i];
          for (var j = 0; j < varargs.$length; j++) {
            args.push($externalize(varargs.$array[varargs.$offset + j], vt));
          }
          break;
        }
        args.push($externalize(arguments[i], t.params[i]));
      }
      var result = v.apply(recv, args);
      switch (t.results.length) {
      case 0:
        return;
      case 1:
        return $internalize(result, t.results[0]);
      default:
        for (var i = 0; i < t.results.length; i++) {
          result[i] = $internalize(result[i], t.results[i]);
        }
        return result;
      }
    };
  case $kindInterface:
    if (t.methods.length !== 0) {
      $panic(new $String("cannot internalize " + t.string));
    }
    if (v === null) {
      return $ifaceNil;
    }
    switch (v.constructor) {
    case Int8Array:
      return new ($sliceType($Int8))(v);
    case Int16Array:
      return new ($sliceType($Int16))(v);
    case Int32Array:
      return new ($sliceType($Int))(v);
    case Uint8Array:
      return new ($sliceType($Uint8))(v);
    case Uint16Array:
      return new ($sliceType($Uint16))(v);
    case Uint32Array:
      return new ($sliceType($Uint))(v);
    case Float32Array:
      return new ($sliceType($Float32))(v);
    case Float64Array:
      return new ($sliceType($Float64))(v);
    case Array:
      return $internalize(v, $sliceType($emptyInterface));
    case Boolean:
      return new $Bool(!!v);
    case Date:
      var timePkg = $packages["time"];
      if (timePkg) {
        return new timePkg.Time(timePkg.Unix(new $Int64(0, 0), new $Int64(0, v.getTime() * 1000000)));
      }
    case Function:
      var funcType = $funcType([$sliceType($emptyInterface)], [$jsObjectPtr], true);
      return new funcType($internalize(v, funcType));
    case Number:
      return new $Float64(parseFloat(v));
    case String:
      return new $String($internalize(v, $String));
    default:
      if ($global.Node && v instanceof $global.Node) {
        return new $jsObjectPtr(v);
      }
      var mapType = $mapType($String, $emptyInterface);
      return new mapType($internalize(v, mapType));
    }
  case $kindMap:
    var m = new $Map();
    var keys = $keys(v);
    for (var i = 0; i < keys.length; i++) {
      var key = $internalize(keys[i], t.key);
      m[key.$key ? key.$key() : key] = { k: key, v: $internalize(v[keys[i]], t.elem) };
    }
    return m;
  case $kindPtr:
    if (t.elem.kind === $kindStruct) {
      return $internalize(v, t.elem);
    }
  case $kindSlice:
    return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
  case $kindString:
    v = String(v);
    if (v.search(/^[\x00-\x7F]*$/) !== -1) {
      return v;
    }
    var s = "";
    for (var i = 0; i < v.length; i++) {
      s += $encodeRune(v.charCodeAt(i));
    }
    return s;
  case $kindStruct:
    var noJsObject = {};
    var searchJsObject = function(t) {
      if (t === $jsObjectPtr) {
        return v;
      }
      if (t === $jsObjectPtr.elem) {
        $panic(new $String("cannot internalize js.Object, use *js.Object instead"));
      }
      if (t.kind === $kindPtr && t.elem.kind === $kindStruct) {
        var o = searchJsObject(t.elem);
        if (o !== noJsObject) {
          return o;
        }
      }
      if (t.kind === $kindStruct) {
        for (var i = 0; i < t.fields.length; i++) {
          var f = t.fields[i];
          var o = searchJsObject(f.typ);
          if (o !== noJsObject) {
            var n = new t.ptr();
            n[f.prop] = o;
            return n;
          }
        }
      }
      return noJsObject;
    };
    var o = searchJsObject(t);
    if (o !== noJsObject) {
      return o;
    }
  }
  $panic(new $String("cannot internalize " + t.string));
};
`
<|endoftext|>


<commit_before>package healthcheck

import (
	log "github.com/Sirupsen/logrus"
	"os/exec"
)

func init() {
	RegisterHealthcheck("command", CommandConstructor)
}

type CommandHealthCheck struct {
	Destination string
	Command string
}

func (h CommandHealthCheck) Healthcheck() bool {
	args := []string{"-c", "1", h.Destination}
	contextLogger := log.WithFields(log.Fields{
		"destination": h.Destination,
		"command": h.Command,
	})
	contextLogger.Debug("Run command")
	if err := exec.Command(h.Command, args...).Run(); err != nil {
		contextLogger.WithFields(log.Fields{"err": err.Error()}).Debug("command healthcheck failed")
		return false
	}
	contextLogger.Debug("command OK")
	return true
}

func CommandConstructor(h Healthcheck) (HealthChecker, error) {
	command := "ping"
	return CommandHealthCheck{
		Destination: h.Destination,
		Command: command,
	}, nil
}
<commit_msg>Getting there<commit_after>package healthcheck

import (
	log "github.com/Sirupsen/logrus"
	"os/exec"
	"strings"
)

func init() {
	RegisterHealthcheck("command", CommandConstructor)
}

type CommandHealthCheck struct {
	Destination string
	Command     string
	Arguments   []string
}

func (h CommandHealthCheck) Healthcheck() bool {
	contextLogger := log.WithFields(log.Fields{
		"destination": h.Destination,
		"command":     h.Command,
		"arguments":   strings.Join(h.Arguments, ", "),
	})
	contextLogger.Debug("Run command")
	if err := exec.Command(h.Command, h.Arguments...).Run(); err != nil {
		contextLogger.WithFields(log.Fields{"err": err.Error()}).Debug("command healthcheck failed")
		return false
	}
	contextLogger.Debug("command OK")
	return true
}

func CommandConstructor(h Healthcheck) (HealthChecker, error) {
	command := "ping"
	args := []string{"-c", "1"}
	return CommandHealthCheck{
		Destination: h.Destination,
		Command:     command,
		Arguments:   args,
	}, nil
}
<|endoftext|>


<commit_before>package keyshareserver

import (
	"database/sql"
	"encoding/json"
	"time"

	_ "github.com/jackc/pgx/stdlib"
	"github.com/privacybydesign/irmago/internal/common"
)

// postgresDB provides a postgres-backed implementation of KeyshareDB
// database access is done through the database/sql mechanisms, using
// pgx as database driver

type keysharePostgresDatabase struct {
	db *sql.DB
}

const MAX_PIN_TRIES = 3         // Number of tries allowed on pin before we start with exponential backoff
const EMAIL_TOKEN_VALIDITY = 24 // Ammount of time user's email validation token is valid (in hours)

// Initial ammount of time user is forced to back off when having multiple pin failures (in seconds).
// var so that tests may change it.
var BACKOFF_START int64 = 30

func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
	db, err := sql.Open("pgx", connstring)
	if err != nil {
		return nil, err
	}
	return &keysharePostgresDatabase{
		db: db,
	}, nil
}

func (db *keysharePostgresDatabase) NewUser(user *KeyshareUser) error {
	res, err := db.db.Query("INSERT INTO irma.users (username, language, coredata, last_seen, pin_counter, pin_block_date) VALUES ($1, $2, $3, $4, 0, 0) RETURNING id",
		user.Username,
		user.Language,
		user.Coredata[:],
		time.Now().Unix())
	if err != nil {
		return err
	}
	defer common.Close(res)
	if !res.Next() {
		return ErrUserAlreadyExists
	}
	var id int64
	err = res.Scan(&id)
	if err != nil {
		return err
	}
	user.id = id
	return nil
}

func (db *keysharePostgresDatabase) User(username string) (*KeyshareUser, error) {
	rows, err := db.db.Query("SELECT id, username, language, coredata FROM irma.users WHERE username = $1 AND coredata IS NOT NULL", username)
	if err != nil {
		return nil, err
	}
	defer common.Close(rows)
	if !rows.Next() {
		return nil, ErrUserNotFound
	}
	var result KeyshareUser
	var ep []byte
	err = rows.Scan(&result.id, &result.Username, &result.Language, &ep)
	if err != nil {
		return nil, err
	}
	if len(ep) != len(result.Coredata[:]) {
		return nil, ErrInvalidRecord
	}
	copy(result.Coredata[:], ep)
	return &result, nil
}

func (db *keysharePostgresDatabase) UpdateUser(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET username=$1, language=$2, coredata=$3 WHERE id=$4",
		user.Username,
		user.Language,
		user.Coredata[:],
		user.id,
	)
}

func (db *keysharePostgresDatabase) ReservePincheck(user *KeyshareUser) (bool, int, int64, error) {
	// Check that account is not blocked already, and if not,
	//  update pinCounter and pinBlockDate
	uprows, err := db.db.Query(`
		UPDATE irma.users
		SET pin_counter = pin_counter+1,
			pin_block_date = $1+$2*2^GREATEST(0, pin_counter-$3)
		WHERE id=$4 AND pin_block_date<=$5 AND coredata IS NOT NULL
		RETURNING pin_counter, pin_block_date`,
		time.Now().Unix()-1-BACKOFF_START, // Grace time of 2 seconds on pinBlockDate set
		BACKOFF_START,
		MAX_PIN_TRIES-2,
		user.id,
		time.Now().Unix())
	if err != nil {
		return false, 0, 0, err
	}
	defer common.Close(uprows)

	var (
		allowed bool
		wait    int64
		tries   int
	)
	if !uprows.Next() {
		// if no results, then account either does not exist (which would be weird here) or is blocked
		// so request wait timeout
		pinrows, err := db.db.Query("SELECT pin_block_date FROM irma.users WHERE id=$1 AND coredata IS NOT NULL", user.id)
		if err != nil {
			return false, 0, 0, err
		}
		defer common.Close(pinrows)
		if !pinrows.Next() {
			return false, 0, 0, ErrUserNotFound
		}
		err = pinrows.Scan(&wait)
		if err != nil {
			return false, 0, 0, err
		}
	} else {
		// Pin check is allowed (implied since there is a result, so pinBlockDate <= now)
		//  calculate tries remaining and wait time
		allowed = true
		err = uprows.Scan(&tries, &wait)
		if err != nil {
			return false, 0, 0, err
		}
		tries = MAX_PIN_TRIES - tries
		if tries < 0 {
			tries = 0
		}
	}

	wait = wait - time.Now().Unix()
	if wait < 0 {
		wait = 0
	}
	return allowed, tries, wait, nil
}

func (db *keysharePostgresDatabase) ClearPincheck(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET pin_counter=0, pin_block_date=0 WHERE id=$1",
		user.id,
	)
}

func (db *keysharePostgresDatabase) SetSeen(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET last_seen = $1 WHERE id = $2",
		time.Now().Unix(),
		user.id,
	)
}

func (db *keysharePostgresDatabase) updateUser(query string, args ...interface{}) error {
	res, err := db.db.Exec(query, args...)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) AddLog(user *KeyshareUser, eventType LogEntryType, param interface{}) error {
	var encodedParamString *string
	if param != nil {
		encodedParam, err := json.Marshal(param)
		if err != nil {
			return err
		}
		encodedParams := string(encodedParam)
		encodedParamString = &encodedParams
	}

	_, err := db.db.Exec("INSERT INTO irma.log_entry_records (time, event, param, user_id) VALUES ($1, $2, $3, $4)",
		time.Now().Unix(),
		eventType,
		encodedParamString,
		user.id)
	return err
}

func (db *keysharePostgresDatabase) AddEmailVerification(user *KeyshareUser, emailAddress, token string) error {
	_, err := db.db.Exec("INSERT INTO irma.email_verification_tokens (token, email, user_id, expiry) VALUES ($1, $2, $3, $4)",
		token,
		emailAddress,
		user.id,
		time.Now().Add(EMAIL_TOKEN_VALIDITY*time.Hour).Unix())
	return err
}
<commit_msg>refactor: simplify keyshare pin reservation query by making a case distinction explicit<commit_after>package keyshareserver

import (
	"database/sql"
	"encoding/json"
	"time"

	_ "github.com/jackc/pgx/stdlib"
	"github.com/privacybydesign/irmago/internal/common"
)

// postgresDB provides a postgres-backed implementation of KeyshareDB
// database access is done through the database/sql mechanisms, using
// pgx as database driver

type keysharePostgresDatabase struct {
	db *sql.DB
}

const MAX_PIN_TRIES = 3         // Number of tries allowed on pin before we start with exponential backoff
const EMAIL_TOKEN_VALIDITY = 24 // Ammount of time user's email validation token is valid (in hours)

// Initial ammount of time user is forced to back off when having multiple pin failures (in seconds).
// var so that tests may change it.
var BACKOFF_START int64 = 30

func NewPostgresDatabase(connstring string) (KeyshareDB, error) {
	db, err := sql.Open("pgx", connstring)
	if err != nil {
		return nil, err
	}
	return &keysharePostgresDatabase{
		db: db,
	}, nil
}

func (db *keysharePostgresDatabase) NewUser(user *KeyshareUser) error {
	res, err := db.db.Query("INSERT INTO irma.users (username, language, coredata, last_seen, pin_counter, pin_block_date) VALUES ($1, $2, $3, $4, 0, 0) RETURNING id",
		user.Username,
		user.Language,
		user.Coredata[:],
		time.Now().Unix())
	if err != nil {
		return err
	}
	defer common.Close(res)
	if !res.Next() {
		return ErrUserAlreadyExists
	}
	var id int64
	err = res.Scan(&id)
	if err != nil {
		return err
	}
	user.id = id
	return nil
}

func (db *keysharePostgresDatabase) User(username string) (*KeyshareUser, error) {
	rows, err := db.db.Query("SELECT id, username, language, coredata FROM irma.users WHERE username = $1 AND coredata IS NOT NULL", username)
	if err != nil {
		return nil, err
	}
	defer common.Close(rows)
	if !rows.Next() {
		return nil, ErrUserNotFound
	}
	var result KeyshareUser
	var ep []byte
	err = rows.Scan(&result.id, &result.Username, &result.Language, &ep)
	if err != nil {
		return nil, err
	}
	if len(ep) != len(result.Coredata[:]) {
		return nil, ErrInvalidRecord
	}
	copy(result.Coredata[:], ep)
	return &result, nil
}

func (db *keysharePostgresDatabase) UpdateUser(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET username=$1, language=$2, coredata=$3 WHERE id=$4",
		user.Username,
		user.Language,
		user.Coredata[:],
		user.id,
	)
}

func (db *keysharePostgresDatabase) ReservePincheck(user *KeyshareUser) (bool, int, int64, error) {
	// Check that account is not blocked already, and if not,
	//  update pinCounter and pinBlockDate
	uprows, err := db.db.Query(`
		UPDATE irma.users
		SET pin_counter = pin_counter+1,
			pin_block_date = $1 + CASE WHEN pin_counter-$3 < 0 THEN 0
			                           ELSE $2*2^GREATEST(0, pin_counter-$3)
			                      END
		WHERE id=$4 AND pin_block_date<=$1 AND coredata IS NOT NULL
		RETURNING pin_counter, pin_block_date`,
		time.Now().Unix(),
		BACKOFF_START,
		MAX_PIN_TRIES-1,
		user.id)
	if err != nil {
		return false, 0, 0, err
	}
	defer common.Close(uprows)

	var (
		allowed bool
		wait    int64
		tries   int
	)
	if !uprows.Next() {
		// if no results, then account either does not exist (which would be weird here) or is blocked
		// so request wait timeout
		pinrows, err := db.db.Query("SELECT pin_block_date FROM irma.users WHERE id=$1 AND coredata IS NOT NULL", user.id)
		if err != nil {
			return false, 0, 0, err
		}
		defer common.Close(pinrows)
		if !pinrows.Next() {
			return false, 0, 0, ErrUserNotFound
		}
		err = pinrows.Scan(&wait)
		if err != nil {
			return false, 0, 0, err
		}
	} else {
		// Pin check is allowed (implied since there is a result, so pinBlockDate <= now)
		//  calculate tries remaining and wait time
		allowed = true
		err = uprows.Scan(&tries, &wait)
		if err != nil {
			return false, 0, 0, err
		}
		tries = MAX_PIN_TRIES - tries
		if tries < 0 {
			tries = 0
		}
	}

	wait = wait - time.Now().Unix()
	if wait < 0 {
		wait = 0
	}
	return allowed, tries, wait, nil
}

func (db *keysharePostgresDatabase) ClearPincheck(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET pin_counter=0, pin_block_date=0 WHERE id=$1",
		user.id,
	)
}

func (db *keysharePostgresDatabase) SetSeen(user *KeyshareUser) error {
	return db.updateUser(
		"UPDATE irma.users SET last_seen = $1 WHERE id = $2",
		time.Now().Unix(),
		user.id,
	)
}

func (db *keysharePostgresDatabase) updateUser(query string, args ...interface{}) error {
	res, err := db.db.Exec(query, args...)
	if err != nil {
		return err
	}
	c, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if c == 0 {
		return ErrUserNotFound
	}
	return nil
}

func (db *keysharePostgresDatabase) AddLog(user *KeyshareUser, eventType LogEntryType, param interface{}) error {
	var encodedParamString *string
	if param != nil {
		encodedParam, err := json.Marshal(param)
		if err != nil {
			return err
		}
		encodedParams := string(encodedParam)
		encodedParamString = &encodedParams
	}

	_, err := db.db.Exec("INSERT INTO irma.log_entry_records (time, event, param, user_id) VALUES ($1, $2, $3, $4)",
		time.Now().Unix(),
		eventType,
		encodedParamString,
		user.id)
	return err
}

func (db *keysharePostgresDatabase) AddEmailVerification(user *KeyshareUser, emailAddress, token string) error {
	_, err := db.db.Exec("INSERT INTO irma.email_verification_tokens (token, email, user_id, expiry) VALUES ($1, $2, $3, $4)",
		token,
		emailAddress,
		user.id,
		time.Now().Add(EMAIL_TOKEN_VALIDITY*time.Hour).Unix())
	return err
}
<|endoftext|>


<commit_before>package isolated

import (
	"fmt"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("delete-orphaned-routes command", func() {
	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("Not logged in. Use 'cf login' to log in."))
			})
		})

		Context("when there no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No org targeted, use 'cf target -o ORG' to target an org."))
			})
		})

		Context("when there no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no space targeted error message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No space targeted, use 'cf target -s SPACE' to target a space"))
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		var (
			orgName    string
			spaceName  string
			domainName string
			appName    string
			domain     helpers.Domain
		)

		BeforeEach(func() {
			orgName = helpers.NewOrgName()
			spaceName = helpers.NewSpaceName()
			domainName = helpers.DomainName()
			appName = helpers.PrefixedRandomName("APP")

			setupCF(orgName, spaceName)
			domain = helpers.NewDomain(orgName, domainName)
			domain.Create()
		})

		Context("when there are orphaned routes", func() {
			var (
				orphanedRoute1 helpers.Route
				orphanedRoute2 helpers.Route
			)

			BeforeEach(func() {
				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, domainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()
			})

			It("deletes all the orphaned routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", domainName)),
					Say("OK"),
				))
			})
		})

		Context("when there are orphaned routes and bound routes", func() {
			var (
				orphanedRoute1 helpers.Route
				orphanedRoute2 helpers.Route
				boundRoute     helpers.Route
			)

			BeforeEach(func() {
				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, domainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()

				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0))
				})
				Eventually(helpers.CF("apps")).Should(And(Exit(0), Say(fmt.Sprintf("%s\\s+stopped\\s+0/1\\s+%s\\s+%s", appName, helpers.DefaultMemoryLimit, helpers.DefaultDiskLimit))))

				boundRoute = helpers.NewRoute(spaceName, domainName, "bound-1", "path-3")
				boundRoute.Create()
				helpers.BindRouteToApplication(appName, boundRoute.Domain, boundRoute.Host, boundRoute.Path)
			})

			It("deletes only the orphaned routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", domainName)),
					Not(Say(fmt.Sprintf("Deleting route bound-1.%s/path-3...", domainName))),
					Say("OK"),
				))
			})
		})

		Context("when there are more than one page of routes", func() {
			BeforeEach(func() {
				var orphanedRoute helpers.Route
				for i := 0; i < 51; i++ {
					orphanedRoute = helpers.NewRoute(spaceName, domainName, fmt.Sprintf("orphan-multi-page-%d", i), "")
					orphanedRoute.Create()
				}
			})
			It("deletes all the orphaned routes", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(0))

				for i := 0; i < 51; i++ {
					Expect(session.Out).To(Say(fmt.Sprintf("Deleting route orphan-multi-page-%d.%s...", i, domainName)))
				}
			})
		})

		Context("when the force flag is not given", func() {
			var buffer *Buffer
			BeforeEach(func() {
				orphanedRoute := helpers.NewRoute(spaceName, domainName, "orphan", "path")
				orphanedRoute.Create()
			})

			Context("when the user inputs y", func() {
				BeforeEach(func() {
					buffer = NewBuffer()
					buffer.Write([]byte("y\n"))
				})

				It("deletes the orphaned routes", func() {
					session := helpers.CFWithStdin(buffer, "delete-orphaned-routes")
					Eventually(session).Should(Say("Really delete orphaned routes?"))
					Eventually(session).Should(SatisfyAll(
						Exit(0),
						Say("Getting routes as"),
						Say(fmt.Sprintf("Deleting route orphan.%s/path...", domainName)),
						Say("OK"),
					))
				})
			})

			Context("when the user inputs n", func() {
				BeforeEach(func() {
					buffer = NewBuffer()
					buffer.Write([]byte("n\n"))
				})

				It("exits without deleting the orphaned routes", func() {
					session := helpers.CFWithStdin(buffer, "delete-orphaned-routes")
					Eventually(session).Should(Say("Really delete orphaned routes?"))
					Eventually(session).Should(SatisfyAll(
						Exit(0),
						Not(Say("Getting routes as")),
						Not(Say(fmt.Sprintf("Deleting route orphan.%s/path...", domainName))),
						Not(Say("OK")),
					))
				})
			})
		})

		Context("when there are no orphaned routes", func() {
			var (
				boundRoute helpers.Route
			)

			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0))
				})
				Eventually(helpers.CF("apps")).Should(And(Exit(0), Say(fmt.Sprintf("%s\\s+stopped\\s+0/1\\s+%s\\s+%s", appName, helpers.DefaultMemoryLimit, helpers.DefaultDiskLimit))))

				boundRoute = helpers.NewRoute(spaceName, domainName, "bound-route", "bound-path")
				boundRoute.Create()
				helpers.BindRouteToApplication(appName, boundRoute.Domain, boundRoute.Host, boundRoute.Path)
			})

			It("displays OK without deleting any routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Not(Say(fmt.Sprintf("Deleting route bound-route.%s/bound-path...", domainName))),
					Say("OK"),
				))
			})
		})

		Context("when the orphaned routes are attached to both shared and private domains", func() {
			var (
				orphanedRoute1   helpers.Route
				orphanedRoute2   helpers.Route
				sharedDomainName string
			)

			BeforeEach(func() {
				sharedDomainName = helpers.DomainName()
				sharedDomain := helpers.NewDomain(orgName, sharedDomainName)
				sharedDomain.Create()
				sharedDomain.Share()

				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, sharedDomainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()
			})

			It("deletes both the routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", sharedDomainName)),
					Say("OK"),
				))
			})
		})
	})
})
<commit_msg>do not test memory and disk in delete orphaned routes test<commit_after>package isolated

import (
	"fmt"

	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("delete-orphaned-routes command", func() {
	Context("when the environment is not setup correctly", func() {
		Context("when no API endpoint is set", func() {
			BeforeEach(func() {
				helpers.UnsetAPI()
			})

			It("fails with no API endpoint set message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No API endpoint set. Use 'cf login' or 'cf api' to target an endpoint."))
			})
		})

		Context("when not logged in", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
			})

			It("fails with not logged in message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("Not logged in. Use 'cf login' to log in."))
			})
		})

		Context("when there no org set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
			})

			It("fails with no targeted org error message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No org targeted, use 'cf target -o ORG' to target an org."))
			})
		})

		Context("when there no space set", func() {
			BeforeEach(func() {
				helpers.LogoutCF()
				helpers.LoginCF()
				helpers.TargetOrg(ReadOnlyOrg)
			})

			It("fails with no space targeted error message", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(1))
				Expect(session.Out).To(Say("FAILED"))
				Expect(session.Err).To(Say("No space targeted, use 'cf target -s SPACE' to target a space"))
			})
		})
	})

	Context("when the environment is setup correctly", func() {
		var (
			orgName    string
			spaceName  string
			domainName string
			appName    string
			domain     helpers.Domain
		)

		BeforeEach(func() {
			orgName = helpers.NewOrgName()
			spaceName = helpers.NewSpaceName()
			domainName = helpers.DomainName()
			appName = helpers.PrefixedRandomName("APP")

			setupCF(orgName, spaceName)
			domain = helpers.NewDomain(orgName, domainName)
			domain.Create()
		})

		Context("when there are orphaned routes", func() {
			var (
				orphanedRoute1 helpers.Route
				orphanedRoute2 helpers.Route
			)

			BeforeEach(func() {
				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, domainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()
			})

			It("deletes all the orphaned routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", domainName)),
					Say("OK"),
				))
			})
		})

		Context("when there are orphaned routes and bound routes", func() {
			var (
				orphanedRoute1 helpers.Route
				orphanedRoute2 helpers.Route
				boundRoute     helpers.Route
			)

			BeforeEach(func() {
				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, domainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()

				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0))
				})
				Eventually(helpers.CF("apps")).Should(And(Exit(0), Say(fmt.Sprintf("%s\\s+stopped\\s+0/1", appName))))

				boundRoute = helpers.NewRoute(spaceName, domainName, "bound-1", "path-3")
				boundRoute.Create()
				helpers.BindRouteToApplication(appName, boundRoute.Domain, boundRoute.Host, boundRoute.Path)
			})

			It("deletes only the orphaned routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", domainName)),
					Not(Say(fmt.Sprintf("Deleting route bound-1.%s/path-3...", domainName))),
					Say("OK"),
				))
			})
		})

		Context("when there are more than one page of routes", func() {
			BeforeEach(func() {
				var orphanedRoute helpers.Route
				for i := 0; i < 51; i++ {
					orphanedRoute = helpers.NewRoute(spaceName, domainName, fmt.Sprintf("orphan-multi-page-%d", i), "")
					orphanedRoute.Create()
				}
			})
			It("deletes all the orphaned routes", func() {
				session := helpers.CF("delete-orphaned-routes", "-f")
				Eventually(session).Should(Exit(0))

				for i := 0; i < 51; i++ {
					Expect(session.Out).To(Say(fmt.Sprintf("Deleting route orphan-multi-page-%d.%s...", i, domainName)))
				}
			})
		})

		Context("when the force flag is not given", func() {
			var buffer *Buffer
			BeforeEach(func() {
				orphanedRoute := helpers.NewRoute(spaceName, domainName, "orphan", "path")
				orphanedRoute.Create()
			})

			Context("when the user inputs y", func() {
				BeforeEach(func() {
					buffer = NewBuffer()
					buffer.Write([]byte("y\n"))
				})

				It("deletes the orphaned routes", func() {
					session := helpers.CFWithStdin(buffer, "delete-orphaned-routes")
					Eventually(session).Should(Say("Really delete orphaned routes?"))
					Eventually(session).Should(SatisfyAll(
						Exit(0),
						Say("Getting routes as"),
						Say(fmt.Sprintf("Deleting route orphan.%s/path...", domainName)),
						Say("OK"),
					))
				})
			})

			Context("when the user inputs n", func() {
				BeforeEach(func() {
					buffer = NewBuffer()
					buffer.Write([]byte("n\n"))
				})

				It("exits without deleting the orphaned routes", func() {
					session := helpers.CFWithStdin(buffer, "delete-orphaned-routes")
					Eventually(session).Should(Say("Really delete orphaned routes?"))
					Eventually(session).Should(SatisfyAll(
						Exit(0),
						Not(Say("Getting routes as")),
						Not(Say(fmt.Sprintf("Deleting route orphan.%s/path...", domainName))),
						Not(Say("OK")),
					))
				})
			})
		})

		Context("when there are no orphaned routes", func() {
			var (
				boundRoute helpers.Route
			)

			BeforeEach(func() {
				helpers.WithHelloWorldApp(func(appDir string) {
					Eventually(helpers.CF("push", appName, "--no-start", "-p", appDir, "-b", "staticfile_buildpack", "--no-route")).Should(Exit(0))
				})
				Eventually(helpers.CF("apps")).Should(And(Exit(0), Say(fmt.Sprintf("%s\\s+stopped\\s+0/1", appName))))

				boundRoute = helpers.NewRoute(spaceName, domainName, "bound-route", "bound-path")
				boundRoute.Create()
				helpers.BindRouteToApplication(appName, boundRoute.Domain, boundRoute.Host, boundRoute.Path)
			})

			It("displays OK without deleting any routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Not(Say(fmt.Sprintf("Deleting route bound-route.%s/bound-path...", domainName))),
					Say("OK"),
				))
			})
		})

		Context("when the orphaned routes are attached to both shared and private domains", func() {
			var (
				orphanedRoute1   helpers.Route
				orphanedRoute2   helpers.Route
				sharedDomainName string
			)

			BeforeEach(func() {
				sharedDomainName = helpers.DomainName()
				sharedDomain := helpers.NewDomain(orgName, sharedDomainName)
				sharedDomain.Create()
				sharedDomain.Share()

				orphanedRoute1 = helpers.NewRoute(spaceName, domainName, "orphan-1", "path-1")
				orphanedRoute2 = helpers.NewRoute(spaceName, sharedDomainName, "orphan-2", "path-2")
				orphanedRoute1.Create()
				orphanedRoute2.Create()
			})

			It("deletes both the routes", func() {
				Eventually(helpers.CF("delete-orphaned-routes", "-f")).Should(SatisfyAll(
					Exit(0),
					Say("Getting routes as"),
					Say(fmt.Sprintf("Deleting route orphan-1.%s/path-1...", domainName)),
					Say(fmt.Sprintf("Deleting route orphan-2.%s/path-2...", sharedDomainName)),
					Say("OK"),
				))
			})
		})
	})
})
<|endoftext|>


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

// feeder polls the sumdb log and pushes the results to a generic witness.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"time"

	"github.com/golang/glog"
	"github.com/google/trillian-examples/formats/log"
	"github.com/google/trillian-examples/sumdbaudit/client"
	"github.com/google/trillian-examples/witness/golang/client/http"
	"github.com/google/trillian/merkle/compact"
	"github.com/google/trillian/merkle/rfc6962"
	"golang.org/x/mod/sumdb/note"
	"golang.org/x/mod/sumdb/tlog"
)

var (
	vkey         = flag.String("k", "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8", "key")
	witness      = flag.String("w", "", "The endpoint of the witness HTTP REST API")
	witnessKey   = flag.String("wk", "", "The public key of the witness")
	logID        = flag.String("lid", "", "The ID of the log within the witness")
	pollInterval = flag.Duration("poll", 10*time.Second, "How quickly to poll the sumdb to get updates")

	rf = &compact.RangeFactory{
		Hash: rfc6962.DefaultHasher.HashChildren,
	}
)

const (
	tileHeight    = 8
	leavesPerTile = 1 << tileHeight
)

func main() {
	flag.Parse()
	ctx := context.Background()
	sdb := client.NewSumDB(tileHeight, *vkey)
	var w http.Witness
	if wURL, err := url.Parse(*witness); err != nil {
		glog.Exitf("Failed to parse witness URL: %v", err)
	} else {
		w = http.Witness{
			URL:      wURL,
			Verifier: mustCreateVerifier(*witnessKey),
		}
	}

	wcp := &log.Checkpoint{}
	if wcpRaw, err := w.GetLatestCheckpoint(ctx, *logID); err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			glog.Exitf("Failed to get witness checkpoint: %v", err)
		}
	} else {
		n, err := note.Open(wcpRaw, note.VerifierList(w.Verifier))
		if err != nil {
			glog.Exitf("Failed to open CP: %v", err)
		}

		_, err = wcp.Unmarshal([]byte(n.Text))
		if err != nil {
			glog.Exitf("Failed to unmarshal CP: %v", err)
		}
	}

	feeder := feeder{
		wcp: wcp,
		sdb: sdb,
		w:   w,
	}

	tik := time.NewTicker(*pollInterval)
	for {
		glog.V(2).Infof("Tick: start feedOnce (witness size %d)", feeder.wcp.Size)
		if err := feeder.feedOnce(ctx); err != nil {
			glog.Exitf("Failed to feed: %v", err)
		}
		glog.V(2).Infof("Tick: feedOnce complete (witness size %d)", feeder.wcp.Size)

		select {
		case <-ctx.Done():
			return
		case <-tik.C:
		}
	}
}

// feeder encapsulates the main logic and state of the feeder.
// The residual logic outside of this should simply be initialization and
// orchestration with a timer.
// Note that feeder maintains its own state that represents the witness state.
// This is optimized for the case where this is the only feeder for this log to
// the witness. If the witness state is updated by another entity, then the
// number of successful updates from this feeder will drop. On the other hand,
// if this is the only feeder then it avoids making requests to get the latest
// checkpoint from the witness when it isn't changing.
type feeder struct {
	wcp *log.Checkpoint
	sdb *client.SumDBClient
	w   http.Witness
}

// feedOnce gets the latest checkpoint from the SumDB server, and if this is more
// recent than the witness state then it will construct a compact range proof by
// requesting the minimal set of tiles, and then update the witness with the new
// checkpoint. Finally, it will update the feeder's view of the witness state.
func (f *feeder) feedOnce(ctx context.Context) error {
	sdbcp, err := f.sdb.LatestCheckpoint()
	if err != nil {
		return fmt.Errorf("failed to get latest checkpoint: %v", err)
	}
	if int64(f.wcp.Size) >= sdbcp.N {
		glog.V(1).Infof("Witness size %d >= SumDB size %d - nothing to do", f.wcp.Size, sdbcp.N)
		return nil
	}

	glog.Infof("Updating witness from size %d to %d", f.wcp.Size, sdbcp.N)

	broker := newTileBroker(sdbcp.N, f.sdb.TileHashes)

	required := compact.RangeNodes(f.wcp.Size, uint64(sdbcp.N))
	proof := make([][]byte, 0, len(required))
	for _, n := range required {
		i, r := convertToSumDBTiles(n)
		t, err := broker.tile(i)
		if err != nil {
			return fmt.Errorf("failed to lookup %s: %v", i, err)
		}
		h := t.hash(r)
		proof = append(proof, h)
	}

	wcpRaw, err := f.w.Update(ctx, *logID, sdbcp.Raw, proof)

	if err != nil && !errors.Is(err, http.ErrCheckpointTooOld) {
		return fmt.Errorf("failed to update checkpoint: %v", err)
	}

	// An optimization would be to immediately retry the Update if we get
	// http.ErrCheckpointTooOld. For now, we'll update the local state and
	// retry only the next time this method is called.

	n, err := note.Open(wcpRaw, note.VerifierList(f.w.Verifier))
	if err != nil {
		return fmt.Errorf("failed to open CP: %v", err)
	}
	_, err = f.wcp.Unmarshal([]byte(n.Text))
	if err != nil {
		return fmt.Errorf("failed to unmarshal CP: %v", err)
	}
	return nil
}

// convertToSumDBTiles takes a NodeID pointing to a node within the overall log,
// and returns the tile coordinate that it will be found in, along with the nodeID
// that references the node within that tile.
func convertToSumDBTiles(n compact.NodeID) (tileIndex, compact.NodeID) {
	sdbLevel := n.Level / tileHeight
	rLevel := uint(n.Level % tileHeight)

	// The last leaf that n commits to.
	lastLeaf := (1<<n.Level)*(n.Index+1) - 1
	// How many proper leaves are committed to by a tile at sdbLevel.
	tileLeaves := 1 << ((1 + sdbLevel) * tileHeight)

	sdbOffset := lastLeaf / uint64(tileLeaves)
	rLeaves := lastLeaf % uint64(tileLeaves)
	rIndex := rLeaves / (1 << n.Level)

	return tileIndex{
		level:  int(sdbLevel),
		offset: int(sdbOffset),
	}, compact.NewNodeID(rLevel, rIndex)
}

// tileIndex is the location of a tile.
// We could have used compact.NodeID, but that would overload its meaning;
// with this usage tileIndex points to a tile, and NodeID points to a node.
type tileIndex struct {
	level, offset int
}

func (i tileIndex) String() string {
	return fmt.Sprintf("T(%d,%d)", i.level, i.offset)
}

type tile struct {
	leaves [][]byte
}

func newTile(hs []tlog.Hash) tile {
	leaves := make([][]byte, len(hs))
	for i, h := range hs {
		h := h
		leaves[i] = h[:]
	}
	return tile{
		leaves: leaves,
	}
}

// hash returns the hash of the subtree within this tile identified by n.
// Note that the coordinate system starts with the bottom left of the tile
// being (0,0), no matter where this tile appears within the overall log.
func (t tile) hash(n compact.NodeID) []byte {
	r := rf.NewEmptyRange(0)

	left := n.Index << uint64(n.Level)
	right := (n.Index + 1) << uint64(n.Level)

	if len(t.leaves) < int(right) {
		panic(fmt.Sprintf("index %d out of range of %d leaves", right, len(t.leaves)))
	}
	for _, l := range t.leaves[left:right] {
		r.Append(l[:], nil)
	}
	root, err := r.GetRootHash(nil)
	if err != nil {
		panic(err)
	}
	return root
}

// tileBroker takes requests for tiles and returns the appropriate tile for the tree size.
// This will cache results for the lifetime of the broker, and it will ensure that requests
// for incomplete tiles are requested as partial tiles.
type tileBroker struct {
	pts    map[tileIndex]int
	ts     map[tileIndex]tile
	lookup func(level, offset, partial int) ([]tlog.Hash, error)
}

func newTileBroker(size int64, lookup func(level, offset, partial int) ([]tlog.Hash, error)) tileBroker {
	pts := make(map[tileIndex]int)
	l := 0
	t := size / leavesPerTile
	o := size % leavesPerTile
	for {
		i := tileIndex{l, int(t)}
		pts[i] = int(o)
		if t == 0 {
			break
		}
		o = t % leavesPerTile
		t = t / leavesPerTile
		l++
	}
	return tileBroker{
		pts:    pts,
		ts:     make(map[tileIndex]tile),
		lookup: lookup,
	}
}

func (tb *tileBroker) tile(i tileIndex) (tile, error) {
	if t, ok := tb.ts[i]; ok {
		return t, nil
	}
	partial := tb.pts[i]
	glog.V(2).Infof("Looking up %s (partial=%d) from remote", i, partial)
	hs, err := tb.lookup(i.level, i.offset, partial)
	if err != nil {
		return tile{}, fmt.Errorf("lookup failed: %v", err)
	}
	t := newTile(hs)
	tb.ts[i] = t
	return t, nil
}

func mustCreateVerifier(pub string) note.Verifier {
	v, err := note.NewVerifier(pub)
	if err != nil {
		glog.Exitf("Failed to create signature verifier from %q: %v", pub, err)
	}
	return v
}
<commit_msg>Make the feeder warn instead of exiting on failure (#462)<commit_after>// Copyright 2021 Google LLC. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// feeder polls the sumdb log and pushes the results to a generic witness.
package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"net/url"
	"os"
	"time"

	"github.com/golang/glog"
	"github.com/google/trillian-examples/formats/log"
	"github.com/google/trillian-examples/sumdbaudit/client"
	"github.com/google/trillian-examples/witness/golang/client/http"
	"github.com/google/trillian/merkle/compact"
	"github.com/google/trillian/merkle/rfc6962"
	"golang.org/x/mod/sumdb/note"
	"golang.org/x/mod/sumdb/tlog"
)

var (
	vkey         = flag.String("k", "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8", "key")
	witness      = flag.String("w", "", "The endpoint of the witness HTTP REST API")
	witnessKey   = flag.String("wk", "", "The public key of the witness")
	logID        = flag.String("lid", "", "The ID of the log within the witness")
	pollInterval = flag.Duration("poll", 10*time.Second, "How quickly to poll the sumdb to get updates")

	rf = &compact.RangeFactory{
		Hash: rfc6962.DefaultHasher.HashChildren,
	}
)

const (
	tileHeight    = 8
	leavesPerTile = 1 << tileHeight
)

func main() {
	flag.Parse()
	ctx := context.Background()
	sdb := client.NewSumDB(tileHeight, *vkey)
	var w http.Witness
	if wURL, err := url.Parse(*witness); err != nil {
		glog.Exitf("Failed to parse witness URL: %v", err)
	} else {
		w = http.Witness{
			URL:      wURL,
			Verifier: mustCreateVerifier(*witnessKey),
		}
	}

	wcp := &log.Checkpoint{}
	if wcpRaw, err := w.GetLatestCheckpoint(ctx, *logID); err != nil {
		if !errors.Is(err, os.ErrNotExist) {
			glog.Exitf("Failed to get witness checkpoint: %v", err)
		}
	} else {
		n, err := note.Open(wcpRaw, note.VerifierList(w.Verifier))
		if err != nil {
			glog.Exitf("Failed to open CP: %v", err)
		}

		_, err = wcp.Unmarshal([]byte(n.Text))
		if err != nil {
			glog.Exitf("Failed to unmarshal CP: %v", err)
		}
	}

	feeder := feeder{
		wcp: wcp,
		sdb: sdb,
		w:   w,
	}

	tik := time.NewTicker(*pollInterval)
	for {
		glog.V(2).Infof("Tick: start feedOnce (witness size %d)", feeder.wcp.Size)
		if err := feeder.feedOnce(ctx); err != nil {
			glog.Warningf("Failed to feed: %v", err)
		}
		glog.V(2).Infof("Tick: feedOnce complete (witness size %d)", feeder.wcp.Size)

		select {
		case <-ctx.Done():
			return
		case <-tik.C:
		}
	}
}

// feeder encapsulates the main logic and state of the feeder.
// The residual logic outside of this should simply be initialization and
// orchestration with a timer.
// Note that feeder maintains its own state that represents the witness state.
// This is optimized for the case where this is the only feeder for this log to
// the witness. If the witness state is updated by another entity, then the
// number of successful updates from this feeder will drop. On the other hand,
// if this is the only feeder then it avoids making requests to get the latest
// checkpoint from the witness when it isn't changing.
type feeder struct {
	wcp *log.Checkpoint
	sdb *client.SumDBClient
	w   http.Witness
}

// feedOnce gets the latest checkpoint from the SumDB server, and if this is more
// recent than the witness state then it will construct a compact range proof by
// requesting the minimal set of tiles, and then update the witness with the new
// checkpoint. Finally, it will update the feeder's view of the witness state.
func (f *feeder) feedOnce(ctx context.Context) error {
	sdbcp, err := f.sdb.LatestCheckpoint()
	if err != nil {
		return fmt.Errorf("failed to get latest checkpoint: %v", err)
	}
	if int64(f.wcp.Size) >= sdbcp.N {
		glog.V(1).Infof("Witness size %d >= SumDB size %d - nothing to do", f.wcp.Size, sdbcp.N)
		return nil
	}

	glog.Infof("Updating witness from size %d to %d", f.wcp.Size, sdbcp.N)

	broker := newTileBroker(sdbcp.N, f.sdb.TileHashes)

	required := compact.RangeNodes(f.wcp.Size, uint64(sdbcp.N))
	proof := make([][]byte, 0, len(required))
	for _, n := range required {
		i, r := convertToSumDBTiles(n)
		t, err := broker.tile(i)
		if err != nil {
			return fmt.Errorf("failed to lookup %s: %v", i, err)
		}
		h := t.hash(r)
		proof = append(proof, h)
	}

	wcpRaw, err := f.w.Update(ctx, *logID, sdbcp.Raw, proof)

	if err != nil && !errors.Is(err, http.ErrCheckpointTooOld) {
		return fmt.Errorf("failed to update checkpoint: %v", err)
	}

	// An optimization would be to immediately retry the Update if we get
	// http.ErrCheckpointTooOld. For now, we'll update the local state and
	// retry only the next time this method is called.

	n, err := note.Open(wcpRaw, note.VerifierList(f.w.Verifier))
	if err != nil {
		return fmt.Errorf("failed to open CP: %v", err)
	}
	_, err = f.wcp.Unmarshal([]byte(n.Text))
	if err != nil {
		return fmt.Errorf("failed to unmarshal CP: %v", err)
	}
	return nil
}

// convertToSumDBTiles takes a NodeID pointing to a node within the overall log,
// and returns the tile coordinate that it will be found in, along with the nodeID
// that references the node within that tile.
func convertToSumDBTiles(n compact.NodeID) (tileIndex, compact.NodeID) {
	sdbLevel := n.Level / tileHeight
	rLevel := uint(n.Level % tileHeight)

	// The last leaf that n commits to.
	lastLeaf := (1<<n.Level)*(n.Index+1) - 1
	// How many proper leaves are committed to by a tile at sdbLevel.
	tileLeaves := 1 << ((1 + sdbLevel) * tileHeight)

	sdbOffset := lastLeaf / uint64(tileLeaves)
	rLeaves := lastLeaf % uint64(tileLeaves)
	rIndex := rLeaves / (1 << n.Level)

	return tileIndex{
		level:  int(sdbLevel),
		offset: int(sdbOffset),
	}, compact.NewNodeID(rLevel, rIndex)
}

// tileIndex is the location of a tile.
// We could have used compact.NodeID, but that would overload its meaning;
// with this usage tileIndex points to a tile, and NodeID points to a node.
type tileIndex struct {
	level, offset int
}

func (i tileIndex) String() string {
	return fmt.Sprintf("T(%d,%d)", i.level, i.offset)
}

type tile struct {
	leaves [][]byte
}

func newTile(hs []tlog.Hash) tile {
	leaves := make([][]byte, len(hs))
	for i, h := range hs {
		h := h
		leaves[i] = h[:]
	}
	return tile{
		leaves: leaves,
	}
}

// hash returns the hash of the subtree within this tile identified by n.
// Note that the coordinate system starts with the bottom left of the tile
// being (0,0), no matter where this tile appears within the overall log.
func (t tile) hash(n compact.NodeID) []byte {
	r := rf.NewEmptyRange(0)

	left := n.Index << uint64(n.Level)
	right := (n.Index + 1) << uint64(n.Level)

	if len(t.leaves) < int(right) {
		panic(fmt.Sprintf("index %d out of range of %d leaves", right, len(t.leaves)))
	}
	for _, l := range t.leaves[left:right] {
		r.Append(l[:], nil)
	}
	root, err := r.GetRootHash(nil)
	if err != nil {
		panic(err)
	}
	return root
}

// tileBroker takes requests for tiles and returns the appropriate tile for the tree size.
// This will cache results for the lifetime of the broker, and it will ensure that requests
// for incomplete tiles are requested as partial tiles.
type tileBroker struct {
	pts    map[tileIndex]int
	ts     map[tileIndex]tile
	lookup func(level, offset, partial int) ([]tlog.Hash, error)
}

func newTileBroker(size int64, lookup func(level, offset, partial int) ([]tlog.Hash, error)) tileBroker {
	pts := make(map[tileIndex]int)
	l := 0
	t := size / leavesPerTile
	o := size % leavesPerTile
	for {
		i := tileIndex{l, int(t)}
		pts[i] = int(o)
		if t == 0 {
			break
		}
		o = t % leavesPerTile
		t = t / leavesPerTile
		l++
	}
	return tileBroker{
		pts:    pts,
		ts:     make(map[tileIndex]tile),
		lookup: lookup,
	}
}

func (tb *tileBroker) tile(i tileIndex) (tile, error) {
	if t, ok := tb.ts[i]; ok {
		return t, nil
	}
	partial := tb.pts[i]
	glog.V(2).Infof("Looking up %s (partial=%d) from remote", i, partial)
	hs, err := tb.lookup(i.level, i.offset, partial)
	if err != nil {
		return tile{}, fmt.Errorf("lookup failed: %v", err)
	}
	t := newTile(hs)
	tb.ts[i] = t
	return t, nil
}

func mustCreateVerifier(pub string) note.Verifier {
	v, err := note.NewVerifier(pub)
	if err != nil {
		glog.Exitf("Failed to create signature verifier from %q: %v", pub, err)
	}
	return v
}
<|endoftext|>


<commit_before>package gocbcore

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
)

// HttpRequest contains the description of an HTTP request to perform.
type HttpRequest struct {
	Service  ServiceType
	Method   string
	Endpoint string
	Path     string
	Username string
	Password string
	Body     []byte
}

// HttpResponse encapsulates the response from an HTTP request.
type HttpResponse struct {
	Endpoint   string
	StatusCode int
	Body       io.ReadCloser
}

func injectJsonCreds(body []byte, creds []UserPassPair) []byte {
	var props map[string]json.RawMessage
	err := json.Unmarshal(body, &props)
	if err == nil {
		if _, ok := props["creds"]; ok {
			// Early out if the user has already passed a set of credentials.
			return body
		}

		jsonCreds, err := json.Marshal(creds)
		if err == nil {
			props["creds"] = json.RawMessage(jsonCreds)

			newBody, err := json.Marshal(props)
			if err == nil {
				return newBody
			}
		}
	}

	return body
}

func (agent *Agent) getMgmtEp() (string, error) {
	mgmtEps := agent.MgmtEps()
	if len(mgmtEps) == 0 {
		return "", ErrNoMgmtService
	}
	return mgmtEps[rand.Intn(len(mgmtEps))], nil
}

func (agent *Agent) getCapiEp() (string, error) {
	capiEps := agent.CapiEps()
	if len(capiEps) == 0 {
		return "", ErrNoMgmtService
	}
	return capiEps[rand.Intn(len(capiEps))], nil
}

func (agent *Agent) getN1qlEp() (string, error) {
	n1qlEps := agent.N1qlEps()
	if len(n1qlEps) == 0 {
		return "", ErrNoN1qlService
	}
	return n1qlEps[rand.Intn(len(n1qlEps))], nil
}

func (agent *Agent) getFtsEp() (string, error) {
	ftsEps := agent.FtsEps()
	if len(ftsEps) == 0 {
		return "", ErrNoFtsService
	}
	return ftsEps[rand.Intn(len(ftsEps))], nil
}

// DoHttpRequest will perform an HTTP request against one of the HTTP
// services which are available within the SDK.
func (agent *Agent) DoHttpRequest(req *HttpRequest) (*HttpResponse, error) {
	if req.Service == MemdService {
		return nil, ErrInvalidService
	}

	// Identify an endpoint to use for the request
	endpoint := req.Endpoint
	if endpoint == "" {
		var err error
		switch req.Service {
		case MgmtService:
			endpoint, err = agent.getMgmtEp()
		case CapiService:
			endpoint, err = agent.getCapiEp()
		case N1qlService:
			endpoint, err = agent.getN1qlEp()
		case FtsService:
			endpoint, err = agent.getFtsEp()
		}
		if err != nil {
			return nil, err
		}
	}

	// Generate a request URI
	reqUri := endpoint + req.Path

	// Create a new request
	hreq, err := http.NewRequest(req.Method, reqUri, nil)
	if err != nil {
		return nil, err
	}

	body := req.Body

	// Inject credentials into the request
	if req.Username != "" || req.Password != "" {
		hreq.SetBasicAuth(req.Username, req.Password)
	} else {
		if req.Service == N1qlService || req.Service == CbasService ||
			req.Service == FtsService {
			// Handle service which support multi-bucket authentication using
			// injection into the body of the request.

			creds, err := agent.auth.Credentials(AuthCredsRequest{
				Service:  req.Service,
				Endpoint: endpoint,
			})
			if err != nil {
				return nil, err
			}

			if len(creds) == 1 {
				hreq.SetBasicAuth(creds[0].Username, creds[0].Password)
			} else {
				body = injectJsonCreds(body, creds)
			}
		} else {
			// Handle normal services which are only able to deal with a single
			// username and password for authentication.  Errors if the auth
			// provider returns multiple credentials.

			creds, err := agent.auth.Credentials(AuthCredsRequest{
				Service:  req.Service,
				Endpoint: endpoint,
			})
			if err != nil {
				return nil, err
			}

			if len(creds) != 1 {
				return nil, ErrInvalidCredentials
			}

			hreq.SetBasicAuth(creds[0].Username, creds[0].Password)
		}
	}

	hreq.Body = ioutil.NopCloser(bytes.NewReader(body))

	hresp, err := agent.httpCli.Do(hreq)
	if err != nil {
		return nil, err
	}

	respOut := HttpResponse{
		Endpoint:   endpoint,
		StatusCode: hresp.StatusCode,
		Body:       hresp.Body,
	}

	return &respOut, nil
}
<commit_msg>Cleanup duplicated HTTP authentication handling.<commit_after>package gocbcore

import (
	"bytes"
	"encoding/json"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
)

// HttpRequest contains the description of an HTTP request to perform.
type HttpRequest struct {
	Service  ServiceType
	Method   string
	Endpoint string
	Path     string
	Username string
	Password string
	Body     []byte
}

// HttpResponse encapsulates the response from an HTTP request.
type HttpResponse struct {
	Endpoint   string
	StatusCode int
	Body       io.ReadCloser
}

func injectJsonCreds(body []byte, creds []UserPassPair) []byte {
	var props map[string]json.RawMessage
	err := json.Unmarshal(body, &props)
	if err == nil {
		if _, ok := props["creds"]; ok {
			// Early out if the user has already passed a set of credentials.
			return body
		}

		jsonCreds, err := json.Marshal(creds)
		if err == nil {
			props["creds"] = json.RawMessage(jsonCreds)

			newBody, err := json.Marshal(props)
			if err == nil {
				return newBody
			}
		}
	}

	return body
}

func (agent *Agent) getMgmtEp() (string, error) {
	mgmtEps := agent.MgmtEps()
	if len(mgmtEps) == 0 {
		return "", ErrNoMgmtService
	}
	return mgmtEps[rand.Intn(len(mgmtEps))], nil
}

func (agent *Agent) getCapiEp() (string, error) {
	capiEps := agent.CapiEps()
	if len(capiEps) == 0 {
		return "", ErrNoMgmtService
	}
	return capiEps[rand.Intn(len(capiEps))], nil
}

func (agent *Agent) getN1qlEp() (string, error) {
	n1qlEps := agent.N1qlEps()
	if len(n1qlEps) == 0 {
		return "", ErrNoN1qlService
	}
	return n1qlEps[rand.Intn(len(n1qlEps))], nil
}

func (agent *Agent) getFtsEp() (string, error) {
	ftsEps := agent.FtsEps()
	if len(ftsEps) == 0 {
		return "", ErrNoFtsService
	}
	return ftsEps[rand.Intn(len(ftsEps))], nil
}

// DoHttpRequest will perform an HTTP request against one of the HTTP
// services which are available within the SDK.
func (agent *Agent) DoHttpRequest(req *HttpRequest) (*HttpResponse, error) {
	if req.Service == MemdService {
		return nil, ErrInvalidService
	}

	// Identify an endpoint to use for the request
	endpoint := req.Endpoint
	if endpoint == "" {
		var err error
		switch req.Service {
		case MgmtService:
			endpoint, err = agent.getMgmtEp()
		case CapiService:
			endpoint, err = agent.getCapiEp()
		case N1qlService:
			endpoint, err = agent.getN1qlEp()
		case FtsService:
			endpoint, err = agent.getFtsEp()
		}
		if err != nil {
			return nil, err
		}
	}

	// Generate a request URI
	reqUri := endpoint + req.Path

	// Create a new request
	hreq, err := http.NewRequest(req.Method, reqUri, nil)
	if err != nil {
		return nil, err
	}

	body := req.Body

	// Inject credentials into the request
	if req.Username != "" || req.Password != "" {
		hreq.SetBasicAuth(req.Username, req.Password)
	} else {
		creds, err := agent.auth.Credentials(AuthCredsRequest{
			Service:  req.Service,
			Endpoint: endpoint,
		})
		if err != nil {
			return nil, err
		}

		if req.Service == N1qlService || req.Service == CbasService ||
			req.Service == FtsService {
			// Handle service which support multi-bucket authentication using
			// injection into the body of the request.
			if len(creds) == 1 {
				hreq.SetBasicAuth(creds[0].Username, creds[0].Password)
			} else {
				body = injectJsonCreds(body, creds)
			}
		} else {
			if len(creds) != 1 {
				return nil, ErrInvalidCredentials
			}

			hreq.SetBasicAuth(creds[0].Username, creds[0].Password)
		}
	}

	hreq.Body = ioutil.NopCloser(bytes.NewReader(body))

	hresp, err := agent.httpCli.Do(hreq)
	if err != nil {
		return nil, err
	}

	respOut := HttpResponse{
		Endpoint:   endpoint,
		StatusCode: hresp.StatusCode,
		Body:       hresp.Body,
	}

	return &respOut, nil
}
<|endoftext|>


<commit_before>package sensu

import "fmt"

// GetAggregates Return the list of Aggregates
func (s *Sensu) GetAggregates() ([]interface{}, error) {
	return s.getList("aggregates", 0, 0)
}

// GetAggregatesSlice Return a slice in the current list of Aggregates
func (s *Sensu) GetAggregatesSlice(limit int, offset int) ([]interface{}, error) {
	return s.getList("aggregates", limit, offset)
}

// GetAggregate Return Aggregate info
func (s *Sensu) GetAggregate(check string, age int) ([]interface{}, error) {
	fmt.Printf("FIXME GetAgregate Not handling age %d", age)
	return s.getList(fmt.Sprintf("aggregate/%s", check), 0, 0)
}

// GetAggregateIssued Return Aggregate history
func (s *Sensu) GetAggregateIssued(check string, issued string, summarize bool, result bool) (map[string]interface{}, error) {
	// FIXME
	fmt.Printf("FIXME Aggregate Not handling summarize/result")
	return s.get(fmt.Sprintf("aggregate/%s/%s", check, issued))
}

// DeleteAggregate Return the list of Aggregates
func (s *Sensu) DeleteAggregate(aggregate string) error {
	return s.delete(fmt.Sprintf("aggregate/%s", aggregate))
}
<commit_msg>Do not print warnings for aggregates<commit_after>package sensu

import "fmt"

// GetAggregates Return the list of Aggregates
func (s *Sensu) GetAggregates() ([]interface{}, error) {
	return s.getList("aggregates", 0, 0)
}

// GetAggregatesSlice Return a slice in the current list of Aggregates
func (s *Sensu) GetAggregatesSlice(limit int, offset int) ([]interface{}, error) {
	return s.getList("aggregates", limit, offset)
}

// GetAggregate Return Aggregate info
func (s *Sensu) GetAggregate(check string, age int) ([]interface{}, error) {
	// FIXME GetAgregate Not handling age
	return s.getList(fmt.Sprintf("aggregate/%s", check), 0, 0)
}

// GetAggregateIssued Return Aggregate history
func (s *Sensu) GetAggregateIssued(check string, issued string, summarize bool, result bool) (map[string]interface{}, error) {
	// FIXME Aggregate Not handling summarize/result
	return s.get(fmt.Sprintf("aggregate/%s/%s", check, issued))
}

// DeleteAggregate Return the list of Aggregates
func (s *Sensu) DeleteAggregate(aggregate string) error {
	return s.delete(fmt.Sprintf("aggregate/%s", aggregate))
}
<|endoftext|>


<commit_before>package apiserver

import (
	"context"
	"fmt"
	"github.com/openshift/origin/test/extended/operators"
	"io/ioutil"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"net/http"
	"strings"
	"time"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	configv1 "github.com/openshift/api/config/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	types "k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	"github.com/openshift/origin/test/extended/scheme"
	"github.com/openshift/origin/test/extended/single_node"
	exutil "github.com/openshift/origin/test/extended/util"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = ginkgo.Describe("[Conformance][sig-sno][Serial] Cluster", func() {
	f := framework.NewDefaultFramework("cluster-resiliency")
	f.SkipNamespaceCreation = true
	f.SkipPrivilegedPSPBinding = true

	oc := exutil.NewCLIWithoutNamespace("cluster-resiliency")

	ginkgo.It("should allow a fast rollout of kube-apiserver with no pods restarts during API disruption", func() {
		controlPlaneTopology, _ := single_node.GetTopologies(f)

		if controlPlaneTopology != configv1.SingleReplicaTopologyMode {
			e2eskipper.Skipf("Test is only relevant for single replica topologies")
		}

		config, err := framework.LoadConfig()
		framework.ExpectNoError(err)

		setRESTConfigDefaults(config)
		restClient, err := rest.RESTClientFor(config)
		framework.ExpectNoError(err)

		httpClient := restClient.Client

		ginkgo.By("Making sure no previous rollout is in progress")
		clusterApiServer, err := oc.AdminOperatorClient().OperatorV1().KubeAPIServers().Get(context.Background(), "cluster", metav1.GetOptions{})
		framework.ExpectNoError(err)
		gomega.Expect(clusterApiServer.Status.NodeStatuses[0].TargetRevision).To(gomega.Equal(int32(0)))

		ginkgo.By("Initialize pods restart count")
		restartingContainers := make(map[operators.ContainerName]int)
		c, err := e2e.LoadClientset()
		gomega.Expect(err).NotTo(gomega.HaveOccurred())
		// This will just load the restartingContainers map with the current restart count
		// The current restart count is the baseline for validating that there was no restarts during the API rollout
		_ = GetRestartedPods(c, restartingContainers)

		ginkgo.By("Forcing API rollout")
		forceApiRollout(oc)

		ginkgo.By("Expecting API to become unavailable")
		wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
			ready, _, err := isApiReady(config, httpClient)
			return !ready, err
		})

		start := time.Now()

		ginkgo.By("Expecting API to become ready")
		wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
			ready, _, _ := isApiReady(config, httpClient)
			return ready, nil
		})

		end := time.Now()

		ginkgo.By("Measuring disruption duration time")
		disruptionDuration := end.Sub(start)
		// For more information: https://github.com/openshift/origin/pull/26337/files#r698435488
		gomega.Expect(disruptionDuration).To(gomega.BeNumerically("<", 40*time.Second),
			fmt.Sprintf("Total time of disruption is %v which is more than 40 seconds. ", disruptionDuration)+
				"Actual SLO for this is 60 seconds, yet we want to be notified about major regressions")

		ginkgo.By("with no pods restarts during API disruption")
		names := GetRestartedPods(c, restartingContainers)
		gomega.Expect(len(names)).To(gomega.Equal(0), "Some pods in got restarted during kube-apiserver rollout: %s", strings.Join(names, ", "))

		// Workaround for issues identified in https://bugzilla.redhat.com/show_bug.cgi?id=2059581
		time.Sleep(60 * time.Second)
	})

})

func GetRestartedPods(c *kubernetes.Clientset, restartingContainers map[operators.ContainerName]int) (names []string) {
	pods := operators.GetPodsWithFilter(c, []operators.PodFilter{operators.InCoreNamespaces, ignoreNamespaces})
	for _, pod := range pods {
		if pod.Status.Phase == corev1.PodSucceeded {
			continue
		}
		// This will just load the restartingContainers map with the current restart count
		if operators.HasExcessiveRestarts(pod, 1, restartingContainers) {
			key := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
			names = append(names, key)
		}
	}
	return names
}

func setRESTConfigDefaults(config *rest.Config) {
	if config.GroupVersion == nil {
		config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
	}

	if config.NegotiatedSerializer == nil {
		config.NegotiatedSerializer = scheme.Codecs
	}
}

func forceApiRollout(oc *exutil.CLI) {
	redeploymentReason := fmt.Sprintf(`{"spec":{"forceRedeploymentReason":"resiliency-test-%v"}}`, uuid.NewUUID())

	_, err := oc.AdminOperatorClient().OperatorV1().KubeAPIServers().Patch(context.Background(), "cluster", types.MergePatchType,
		[]byte(redeploymentReason), metav1.PatchOptions{})
	gomega.Expect(err).NotTo(gomega.HaveOccurred())
}

func isApiReady(clusterConfig *rest.Config, httpClient *http.Client) (ready bool, reason string, err error) {
	resp, err := httpClient.Get(clusterConfig.Host + "/readyz")
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return false, "client failed to make the request", err
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		body, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			return false, fmt.Sprintf("got status code %v from the server: %v", resp.Status, body), nil
		}

		return false, fmt.Sprintf("got status code %v from the server", resp.Status), err
	}

	return true, "kube-apiserver is ready", nil
}

func ignoreNamespaces(pod *corev1.Pod) bool {
	return !(strings.HasPrefix(pod.Namespace, "openshift-kube-apiserver") ||
		strings.HasPrefix(pod.Namespace, "openshift-kube-controller-manager")) // remove this once https://bugzilla.redhat.com/show_bug.cgi?id=2001330 is fixed
}
<commit_msg>Update test/extended/apiserver/resiliency.go<commit_after>package apiserver

import (
	"context"
	"fmt"
	"github.com/openshift/origin/test/extended/operators"
	"io/ioutil"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/client-go/kubernetes"
	"net/http"
	"strings"
	"time"

	"github.com/onsi/ginkgo"
	"github.com/onsi/gomega"
	configv1 "github.com/openshift/api/config/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	types "k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/uuid"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"

	"github.com/openshift/origin/test/extended/scheme"
	"github.com/openshift/origin/test/extended/single_node"
	exutil "github.com/openshift/origin/test/extended/util"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = ginkgo.Describe("[Conformance][sig-sno][Serial] Cluster", func() {
	f := framework.NewDefaultFramework("cluster-resiliency")
	f.SkipNamespaceCreation = true
	f.SkipPrivilegedPSPBinding = true

	oc := exutil.NewCLIWithoutNamespace("cluster-resiliency")

	ginkgo.It("should allow a fast rollout of kube-apiserver with no pods restarts during API disruption", func() {
		controlPlaneTopology, _ := single_node.GetTopologies(f)

		if controlPlaneTopology != configv1.SingleReplicaTopologyMode {
			e2eskipper.Skipf("Test is only relevant for single replica topologies")
		}

		config, err := framework.LoadConfig()
		framework.ExpectNoError(err)

		setRESTConfigDefaults(config)
		restClient, err := rest.RESTClientFor(config)
		framework.ExpectNoError(err)

		httpClient := restClient.Client

		ginkgo.By("Making sure no previous rollout is in progress")
		clusterApiServer, err := oc.AdminOperatorClient().OperatorV1().KubeAPIServers().Get(context.Background(), "cluster", metav1.GetOptions{})
		framework.ExpectNoError(err)
		gomega.Expect(clusterApiServer.Status.NodeStatuses[0].TargetRevision).To(gomega.Equal(int32(0)))

		ginkgo.By("Initialize pods restart count")
		restartingContainers := make(map[operators.ContainerName]int)
		c, err := e2e.LoadClientset()
		gomega.Expect(err).NotTo(gomega.HaveOccurred())
		// This will just load the restartingContainers map with the current restart count
		// The current restart count is the baseline for validating that there was no restarts during the API rollout
		_ = GetRestartedPods(c, restartingContainers)

		ginkgo.By("Forcing API rollout")
		forceApiRollout(oc)

		ginkgo.By("Expecting API to become unavailable")
		wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
			ready, _, err := isApiReady(config, httpClient)
			return !ready, err
		})

		start := time.Now()

		ginkgo.By("Expecting API to become ready")
		wait.PollImmediate(time.Second, time.Minute, func() (bool, error) {
			ready, _, _ := isApiReady(config, httpClient)
			return ready, nil
		})

		end := time.Now()

		ginkgo.By("Measuring disruption duration time")
		disruptionDuration := end.Sub(start)
		// For more information: https://github.com/openshift/origin/pull/26337/files#r698435488
		gomega.Expect(disruptionDuration).To(gomega.BeNumerically("<", 40*time.Second),
			fmt.Sprintf("Total time of disruption is %v which is more than 40 seconds. ", disruptionDuration)+
				"Actual SLO for this is 60 seconds, yet we want to be notified about major regressions")

		ginkgo.By("with no pods restarts during API disruption")
		names := GetRestartedPods(c, restartingContainers)
		gomega.Expect(len(names)).To(gomega.Equal(0), "Some pods in got restarted during kube-apiserver rollout: %s", strings.Join(names, ", "))

		// Workaround for issues identified in https://bugzilla.redhat.com/show_bug.cgi?id=2059581
		// TODO: Remove this sleep once that bug is resolved
		time.Sleep(60 * time.Second)
	})

})

func GetRestartedPods(c *kubernetes.Clientset, restartingContainers map[operators.ContainerName]int) (names []string) {
	pods := operators.GetPodsWithFilter(c, []operators.PodFilter{operators.InCoreNamespaces, ignoreNamespaces})
	for _, pod := range pods {
		if pod.Status.Phase == corev1.PodSucceeded {
			continue
		}
		// This will just load the restartingContainers map with the current restart count
		if operators.HasExcessiveRestarts(pod, 1, restartingContainers) {
			key := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name)
			names = append(names, key)
		}
	}
	return names
}

func setRESTConfigDefaults(config *rest.Config) {
	if config.GroupVersion == nil {
		config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"}
	}

	if config.NegotiatedSerializer == nil {
		config.NegotiatedSerializer = scheme.Codecs
	}
}

func forceApiRollout(oc *exutil.CLI) {
	redeploymentReason := fmt.Sprintf(`{"spec":{"forceRedeploymentReason":"resiliency-test-%v"}}`, uuid.NewUUID())

	_, err := oc.AdminOperatorClient().OperatorV1().KubeAPIServers().Patch(context.Background(), "cluster", types.MergePatchType,
		[]byte(redeploymentReason), metav1.PatchOptions{})
	gomega.Expect(err).NotTo(gomega.HaveOccurred())
}

func isApiReady(clusterConfig *rest.Config, httpClient *http.Client) (ready bool, reason string, err error) {
	resp, err := httpClient.Get(clusterConfig.Host + "/readyz")
	if resp != nil && resp.Body != nil {
		defer resp.Body.Close()
	}

	if err != nil {
		return false, "client failed to make the request", err
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 400 {
		body, err := ioutil.ReadAll(resp.Body)
		if err == nil {
			return false, fmt.Sprintf("got status code %v from the server: %v", resp.Status, body), nil
		}

		return false, fmt.Sprintf("got status code %v from the server", resp.Status), err
	}

	return true, "kube-apiserver is ready", nil
}

func ignoreNamespaces(pod *corev1.Pod) bool {
	return !(strings.HasPrefix(pod.Namespace, "openshift-kube-apiserver") ||
		strings.HasPrefix(pod.Namespace, "openshift-kube-controller-manager")) // remove this once https://bugzilla.redhat.com/show_bug.cgi?id=2001330 is fixed
}
<|endoftext|>