<commit_before>// Copyright 2011 The Go 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 driver defines interfaces to be implemented by database
// drivers as used by package sql.
//
// Most code should use package sql.
package driver

import "errors"

// Value is a value that drivers must be able to handle.
// It is either nil or an instance of one of these types:
//
//   int64
//   float64
//   bool
//   []byte
//   string   [*] everywhere except from Rows.Next.
//   time.Time
type Value interface{}

// Driver is the interface that must be implemented by a database
// driver.
type Driver interface {
	// Open returns a new connection to the database.
	// The name is a string in a driver-specific format.
	//
	// Open may return a cached connection (one previously
	// closed), but doing so is unnecessary; the sql package
	// maintains a pool of idle connections for efficient re-use.
	//
	// The returned connection is only used by one goroutine at a
	// time.
	Open(name string) (Conn, error)
}

// ErrSkip may be returned by some optional interfaces' methods to
// indicate at runtime that the fast path is unavailable and the sql
// package should continue as if the optional interface was not
// implemented. ErrSkip is only supported where explicitly
// documented.
var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")

// ErrBadConn should be returned by a driver to signal to the sql
// package that a driver.Conn is in a bad state (such as the server
// having earlier closed the connection) and the sql package should
// retry on a new connection.
//
// To prevent duplicate operations, ErrBadConn should NOT be returned
// if there's a possibility that the database server might have
// performed the operation. Even if the server sends back an error,
// you shouldn't return ErrBadConn.
var ErrBadConn = errors.New("driver: bad connection")

// Execer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Execer, the sql package's DB.Exec will
// first prepare a query, execute the statement, and then close the
// statement.
//
// Exec may return ErrSkip.
type Execer interface {
	Exec(query string, args []Value) (Result, error)
}

// Queryer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Queryer, the sql package's DB.Query will
// first prepare a query, execute the statement, and then close the
// statement.
//
// Query may return ErrSkip.
type Queryer interface {
	Query(query string, args []Value) (Rows, error)
}

// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
// Conn is assumed to be stateful.
type Conn interface {
	// Prepare returns a prepared statement, bound to this connection.
	Prepare(query string) (Stmt, error)

	// Close invalidates and potentially stops any current
	// prepared statements and transactions, marking this
	// connection as no longer in use.
	//
	// Because the sql package maintains a free pool of
	// connections and only calls Close when there's a surplus of
	// idle connections, it shouldn't be necessary for drivers to
	// do their own connection caching.
	Close() error

	// Begin starts and returns a new transaction.
	Begin() (Tx, error)
}

// Result is the result of a query execution.
type Result interface {
	// LastInsertId returns the database's auto-generated ID
	// after, for example, an INSERT into a table with primary
	// key.
	LastInsertId() (int64, error)

	// RowsAffected returns the number of rows affected by the
	// query.
	RowsAffected() (int64, error)
}

// Stmt is a prepared statement. It is bound to a Conn and not
// used by multiple goroutines concurrently.
type Stmt interface {
	// Close closes the statement.
	//
	// As of Go 1.1, a Stmt will not be closed if it's in use
	// by any queries.
	Close() error

	// NumInput returns the number of placeholder parameters.
	//
	// If NumInput returns >= 0, the sql package will sanity check
	// argument counts from callers and return errors to the caller
	// before the statement's Exec or Query methods are called.
	//
	// NumInput may also return -1, if the driver doesn't know
	// its number of placeholders. In that case, the sql package
	// will not sanity check Exec or Query argument counts.
	NumInput() int

	// Exec executes a query that doesn't return rows, such
	// as an INSERT or UPDATE.
	Exec(args []Value) (Result, error)

	// Exec executes a query that may return rows, such as a
	// SELECT.
	Query(args []Value) (Rows, error)
}

// ColumnConverter may be optionally implemented by Stmt if the
// statement is aware of its own columns' types and can convert from
// any type to a driver Value.
type ColumnConverter interface {
	// ColumnConverter returns a ValueConverter for the provided
	// column index.  If the type of a specific column isn't known
	// or shouldn't be handled specially, DefaultValueConverter
	// can be returned.
	ColumnConverter(idx int) ValueConverter
}

// Rows is an iterator over an executed query's results.
type Rows interface {
	// Columns returns the names of the columns. The number of
	// columns of the result is inferred from the length of the
	// slice.  If a particular column name isn't known, an empty
	// string should be returned for that entry.
	Columns() []string

	// Close closes the rows iterator.
	Close() error

	// Next is called to populate the next row of data into
	// the provided slice. The provided slice will be the same
	// size as the Columns() are wide.
	//
	// The dest slice may be populated only with
	// a driver Value type, but excluding string.
	// All string values must be converted to []byte.
	//
	// Next should return io.EOF when there are no more rows.
	Next(dest []Value) error
}

// Tx is a transaction.
type Tx interface {
	Commit() error
	Rollback() error
}

// RowsAffected implements Result for an INSERT or UPDATE operation
// which mutates a number of rows.
type RowsAffected int64

var _ Result = RowsAffected(0)

func (RowsAffected) LastInsertId() (int64, error) {
	return 0, errors.New("no LastInsertId available")
}

func (v RowsAffected) RowsAffected() (int64, error) {
	return int64(v), nil
}

// ResultNoRows is a pre-defined Result for drivers to return when a DDL
// command (such as a CREATE TABLE) succeeds. It returns an error for both
// LastInsertId and RowsAffected.
var ResultNoRows noRows

type noRows struct{}

var _ Result = noRows{}

func (noRows) LastInsertId() (int64, error) {
	return 0, errors.New("no LastInsertId available after DDL statement")
}

func (noRows) RowsAffected() (int64, error) {
	return 0, errors.New("no RowsAffected available after DDL statement")
}
<commit_msg>database/sql/driver: correct method name in comment<commit_after>// Copyright 2011 The Go 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 driver defines interfaces to be implemented by database
// drivers as used by package sql.
//
// Most code should use package sql.
package driver

import "errors"

// Value is a value that drivers must be able to handle.
// It is either nil or an instance of one of these types:
//
//   int64
//   float64
//   bool
//   []byte
//   string   [*] everywhere except from Rows.Next.
//   time.Time
type Value interface{}

// Driver is the interface that must be implemented by a database
// driver.
type Driver interface {
	// Open returns a new connection to the database.
	// The name is a string in a driver-specific format.
	//
	// Open may return a cached connection (one previously
	// closed), but doing so is unnecessary; the sql package
	// maintains a pool of idle connections for efficient re-use.
	//
	// The returned connection is only used by one goroutine at a
	// time.
	Open(name string) (Conn, error)
}

// ErrSkip may be returned by some optional interfaces' methods to
// indicate at runtime that the fast path is unavailable and the sql
// package should continue as if the optional interface was not
// implemented. ErrSkip is only supported where explicitly
// documented.
var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")

// ErrBadConn should be returned by a driver to signal to the sql
// package that a driver.Conn is in a bad state (such as the server
// having earlier closed the connection) and the sql package should
// retry on a new connection.
//
// To prevent duplicate operations, ErrBadConn should NOT be returned
// if there's a possibility that the database server might have
// performed the operation. Even if the server sends back an error,
// you shouldn't return ErrBadConn.
var ErrBadConn = errors.New("driver: bad connection")

// Execer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Execer, the sql package's DB.Exec will
// first prepare a query, execute the statement, and then close the
// statement.
//
// Exec may return ErrSkip.
type Execer interface {
	Exec(query string, args []Value) (Result, error)
}

// Queryer is an optional interface that may be implemented by a Conn.
//
// If a Conn does not implement Queryer, the sql package's DB.Query will
// first prepare a query, execute the statement, and then close the
// statement.
//
// Query may return ErrSkip.
type Queryer interface {
	Query(query string, args []Value) (Rows, error)
}

// Conn is a connection to a database. It is not used concurrently
// by multiple goroutines.
//
// Conn is assumed to be stateful.
type Conn interface {
	// Prepare returns a prepared statement, bound to this connection.
	Prepare(query string) (Stmt, error)

	// Close invalidates and potentially stops any current
	// prepared statements and transactions, marking this
	// connection as no longer in use.
	//
	// Because the sql package maintains a free pool of
	// connections and only calls Close when there's a surplus of
	// idle connections, it shouldn't be necessary for drivers to
	// do their own connection caching.
	Close() error

	// Begin starts and returns a new transaction.
	Begin() (Tx, error)
}

// Result is the result of a query execution.
type Result interface {
	// LastInsertId returns the database's auto-generated ID
	// after, for example, an INSERT into a table with primary
	// key.
	LastInsertId() (int64, error)

	// RowsAffected returns the number of rows affected by the
	// query.
	RowsAffected() (int64, error)
}

// Stmt is a prepared statement. It is bound to a Conn and not
// used by multiple goroutines concurrently.
type Stmt interface {
	// Close closes the statement.
	//
	// As of Go 1.1, a Stmt will not be closed if it's in use
	// by any queries.
	Close() error

	// NumInput returns the number of placeholder parameters.
	//
	// If NumInput returns >= 0, the sql package will sanity check
	// argument counts from callers and return errors to the caller
	// before the statement's Exec or Query methods are called.
	//
	// NumInput may also return -1, if the driver doesn't know
	// its number of placeholders. In that case, the sql package
	// will not sanity check Exec or Query argument counts.
	NumInput() int

	// Exec executes a query that doesn't return rows, such
	// as an INSERT or UPDATE.
	Exec(args []Value) (Result, error)

	// Query executes a query that may return rows, such as a
	// SELECT.
	Query(args []Value) (Rows, error)
}

// ColumnConverter may be optionally implemented by Stmt if the
// statement is aware of its own columns' types and can convert from
// any type to a driver Value.
type ColumnConverter interface {
	// ColumnConverter returns a ValueConverter for the provided
	// column index.  If the type of a specific column isn't known
	// or shouldn't be handled specially, DefaultValueConverter
	// can be returned.
	ColumnConverter(idx int) ValueConverter
}

// Rows is an iterator over an executed query's results.
type Rows interface {
	// Columns returns the names of the columns. The number of
	// columns of the result is inferred from the length of the
	// slice.  If a particular column name isn't known, an empty
	// string should be returned for that entry.
	Columns() []string

	// Close closes the rows iterator.
	Close() error

	// Next is called to populate the next row of data into
	// the provided slice. The provided slice will be the same
	// size as the Columns() are wide.
	//
	// The dest slice may be populated only with
	// a driver Value type, but excluding string.
	// All string values must be converted to []byte.
	//
	// Next should return io.EOF when there are no more rows.
	Next(dest []Value) error
}

// Tx is a transaction.
type Tx interface {
	Commit() error
	Rollback() error
}

// RowsAffected implements Result for an INSERT or UPDATE operation
// which mutates a number of rows.
type RowsAffected int64

var _ Result = RowsAffected(0)

func (RowsAffected) LastInsertId() (int64, error) {
	return 0, errors.New("no LastInsertId available")
}

func (v RowsAffected) RowsAffected() (int64, error) {
	return int64(v), nil
}

// ResultNoRows is a pre-defined Result for drivers to return when a DDL
// command (such as a CREATE TABLE) succeeds. It returns an error for both
// LastInsertId and RowsAffected.
var ResultNoRows noRows

type noRows struct{}

var _ Result = noRows{}

func (noRows) LastInsertId() (int64, error) {
	return 0, errors.New("no LastInsertId available after DDL statement")
}

func (noRows) RowsAffected() (int64, error) {
	return 0, errors.New("no RowsAffected available after DDL statement")
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go 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 debug

import (
	"runtime"
	"testing"
	"time"
)

func TestReadGCStats(t *testing.T) {
	var stats GCStats
	var mstats runtime.MemStats
	var min, max time.Duration

	// First ReadGCStats will allocate, second should not,
	// especially if we follow up with an explicit garbage collection.
	stats.PauseQuantiles = make([]time.Duration, 10)
	ReadGCStats(&stats)
	runtime.GC()

	// Assume these will return same data: no GC during ReadGCStats.
	ReadGCStats(&stats)
	runtime.ReadMemStats(&mstats)

	if stats.NumGC != int64(mstats.NumGC) {
		t.Errorf("stats.NumGC = %d, but mstats.NumGC = %d", stats.NumGC, mstats.NumGC)
	}
	if stats.PauseTotal != time.Duration(mstats.PauseTotalNs) {
		t.Errorf("stats.PauseTotal = %d, but mstats.PauseTotalNs = %d", stats.PauseTotal, mstats.PauseTotalNs)
	}
	if stats.LastGC.UnixNano() != int64(mstats.LastGC) {
		t.Errorf("stats.LastGC.UnixNano = %d, but mstats.LastGC = %d", stats.LastGC.UnixNano(), mstats.LastGC)
	}
	n := int(mstats.NumGC)
	if n > len(mstats.PauseNs) {
		n = len(mstats.PauseNs)
	}
	if len(stats.Pause) != n {
		t.Errorf("len(stats.Pause) = %d, want %d", len(stats.Pause), n)
	} else {
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		for i := 0; i < n; i++ {
			dt := stats.Pause[i]
			if dt != time.Duration(mstats.PauseNs[off]) {
				t.Errorf("stats.Pause[%d] = %d, want %d", i, dt, mstats.PauseNs[off])
			}
			if max < dt {
				max = dt
			}
			if min > dt || i == 0 {
				min = dt
			}
			off = (off + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		}
	}

	q := stats.PauseQuantiles
	nq := len(q)
	if q[0] != min || q[nq-1] != max {
		t.Errorf("stats.PauseQuantiles = [%d, ..., %d], want [%d, ..., %d]", q[0], q[nq-1], min, max)
	}

	for i := 0; i < nq-1; i++ {
		if q[i] > q[i+1] {
			t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1])
		}
	}
}

var big []byte

func TestFreeOSMemory(t *testing.T) {
	var ms1, ms2 runtime.MemStats

	big = make([]byte, 1<<20)
	big = nil
	runtime.GC()
	runtime.ReadMemStats(&ms1)
	FreeOSMemory()
	runtime.ReadMemStats(&ms2)
	if ms1.HeapReleased >= ms2.HeapReleased {
		t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased)
	}
}

func TestSetGCPercent(t *testing.T) {
	// Test that the variable is being set and returned correctly.
	// Assume the percentage itself is implemented fine during GC,
	// which is harder to test.
	old := SetGCPercent(123)
	new := SetGCPercent(old)
	if new != 123 {
		t.Errorf("SetGCPercent(123); SetGCPercent(x) = %d, want 123", new)
	}
}
<commit_msg>runtime/debug: deflake TestFreeOSMemory This is followup to https://codereview.appspot.com/7319050/<commit_after>// Copyright 2013 The Go 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 debug

import (
	"runtime"
	"testing"
	"time"
)

func TestReadGCStats(t *testing.T) {
	var stats GCStats
	var mstats runtime.MemStats
	var min, max time.Duration

	// First ReadGCStats will allocate, second should not,
	// especially if we follow up with an explicit garbage collection.
	stats.PauseQuantiles = make([]time.Duration, 10)
	ReadGCStats(&stats)
	runtime.GC()

	// Assume these will return same data: no GC during ReadGCStats.
	ReadGCStats(&stats)
	runtime.ReadMemStats(&mstats)

	if stats.NumGC != int64(mstats.NumGC) {
		t.Errorf("stats.NumGC = %d, but mstats.NumGC = %d", stats.NumGC, mstats.NumGC)
	}
	if stats.PauseTotal != time.Duration(mstats.PauseTotalNs) {
		t.Errorf("stats.PauseTotal = %d, but mstats.PauseTotalNs = %d", stats.PauseTotal, mstats.PauseTotalNs)
	}
	if stats.LastGC.UnixNano() != int64(mstats.LastGC) {
		t.Errorf("stats.LastGC.UnixNano = %d, but mstats.LastGC = %d", stats.LastGC.UnixNano(), mstats.LastGC)
	}
	n := int(mstats.NumGC)
	if n > len(mstats.PauseNs) {
		n = len(mstats.PauseNs)
	}
	if len(stats.Pause) != n {
		t.Errorf("len(stats.Pause) = %d, want %d", len(stats.Pause), n)
	} else {
		off := (int(mstats.NumGC) + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		for i := 0; i < n; i++ {
			dt := stats.Pause[i]
			if dt != time.Duration(mstats.PauseNs[off]) {
				t.Errorf("stats.Pause[%d] = %d, want %d", i, dt, mstats.PauseNs[off])
			}
			if max < dt {
				max = dt
			}
			if min > dt || i == 0 {
				min = dt
			}
			off = (off + len(mstats.PauseNs) - 1) % len(mstats.PauseNs)
		}
	}

	q := stats.PauseQuantiles
	nq := len(q)
	if q[0] != min || q[nq-1] != max {
		t.Errorf("stats.PauseQuantiles = [%d, ..., %d], want [%d, ..., %d]", q[0], q[nq-1], min, max)
	}

	for i := 0; i < nq-1; i++ {
		if q[i] > q[i+1] {
			t.Errorf("stats.PauseQuantiles[%d]=%d > stats.PauseQuantiles[%d]=%d", i, q[i], i+1, q[i+1])
		}
	}
}

var big = make([]byte, 1<<20)

func TestFreeOSMemory(t *testing.T) {
	var ms1, ms2 runtime.MemStats

	if big == nil {
		t.Skip("test is not reliable when run multiple times")
	}
	big = nil
	runtime.GC()
	runtime.ReadMemStats(&ms1)
	FreeOSMemory()
	runtime.ReadMemStats(&ms2)
	if ms1.HeapReleased >= ms2.HeapReleased {
		t.Errorf("released before=%d; released after=%d; did not go up", ms1.HeapReleased, ms2.HeapReleased)
	}
}

func TestSetGCPercent(t *testing.T) {
	// Test that the variable is being set and returned correctly.
	// Assume the percentage itself is implemented fine during GC,
	// which is harder to test.
	old := SetGCPercent(123)
	new := SetGCPercent(old)
	if new != 123 {
		t.Errorf("SetGCPercent(123); SetGCPercent(x) = %d, want 123", new)
	}
}
<|endoftext|>


<commit_before>package catalog

import (
	installplanv1alpha1 "github.com/coreos-inc/alm/apis/installplan/v1alpha1"
	subscriptionv1alpha1 "github.com/coreos-inc/alm/apis/subscription/v1alpha1"
	"github.com/coreos-inc/operator-client/pkg/client"
	"github.com/coreos/go-semver/semver"

	"github.com/coreos-inc/alm/appcache"
)

// Subscription loop
//
//    A. Watches for Subscription objects
//         i. If no Installplan exists for the Subscription, creates it
//        ii. Checks CatalogSource for updates
//       iii. If newer version is available in the channel and is greater than current, creates an
//            Installplan for it.

// SubscriptionController to use for handling subscriptionv1alpha1.Subscription resource events
type SubscriptionController struct {
	catalog appcache.AppCache
	client  client.Interface
}

// TEMP - to be filled in with k8s and logic
type Installer interface {
	CheckForInstallPlan(namespace string, sub subscriptionv1alpha1.Subscription) (bool, error)
	CreateInstallPlan(namespace string, plan *installplanv1alpha1.InstallPlan) error
}

// installplanForSubscription is a helper method that fetches the install plan for a
// given Subscription from the catalog and installs it into the proper namespace
func (ctl *SubscriptionController) installplanForSubscription(sub subscriptionv1alpha1.Subscription) (*installplanv1alpha1.Installplan, error) {
	plan, err := ctl.catalog.FetchInstallplanForAppVersion(sub.Spec.AppType, sub.Status.CurrentVersion)
	if err != nil {
		return nil, err
	}
	return &plan, ctl.installer.CreateInstallplan(sub.GetNamespace(), planl)
}

// HandleSubscription is the handler in the subscriptionv1alpha1.Subscription controller that checks if an
// Installplan object exists in the namespace for a given Subscription and creates one if not
func (ctl *SubscriptionController) HandleNewSubscription(sub subscriptionv1alpha1.Subscription) error {
	ok, err := ctl.installer.CheckForInstallplan(sub.GetNamespace(), sub)
	if err != nil {
		return err
	}
	if ok {
		return nil
	}
	_, err = ctl.installplanForSubscription(sub)
	return err
}

// CheckCatalogForUpdate polls catalog for most recent version of an app and creates a new
// Installplan is a more recent version exists
func (ctl *SubscriptionController) CheckCatalogForUpdate(sub subscriptionv1alpha1.Subscription) error {
	versionStr, err := ctl.catalog.FetchLatestVersion(sub.Spec.AppType, sub.Spec.Channel)
	if err != nil {
		return err
	}
	ver := semver.New(versionStr)
	currVer := semver.New(sub.Status.CurrentVersion)
	if !currVer.LessThan(*ver) {
		return nil
	}
	_, err = ctl.installplanForSubscription(sub)
	return err
}
<commit_msg>wip(subscriptions): remove logic for now<commit_after>package catalog

import (
	"fmt"

	installplanv1alpha1 "github.com/coreos-inc/alm/apis/installplan/v1alpha1"
	subscriptionv1alpha1 "github.com/coreos-inc/alm/apis/subscription/v1alpha1"
	"github.com/coreos-inc/operator-client/pkg/client"
	//	"github.com/coreos/go-semver/semver"

	"github.com/coreos-inc/alm/appcache"
)

// Subscription loop
//
//    A. Watches for Subscription objects
//         i. If no Installplan exists for the Subscription, creates it
//        ii. Checks CatalogSource for updates
//       iii. If newer version is available in the channel and is greater than current, creates an
//            Installplan for it.

// SubscriptionController to use for handling subscriptionv1alpha1.Subscription resource events
type SubscriptionController struct {
	catalog   appcache.AppCache
	client    client.Interface
	installer Installer
}

// TEMP - to be filled in with k8s and logic
type Installer interface {
	CheckForInstallPlan(namespace string, sub subscriptionv1alpha1.Subscription) (bool, error)
	CreateInstallPlan(namespace string, plan *installplanv1alpha1.InstallPlan) error
}

// // installplanForSubscription is a helper method that fetches the install plan for a
// // given Subscription from the catalog and installs it into the proper namespace
// func (ctl *SubscriptionController) installplanForSubscription(sub subscriptionv1alpha1.Subscription) (*opverv1alpha1.OperatorVersion, error) {
// 	plan, err := ctl.catalog.FindCloudServiceVersionByName(fmt.Sprintf("%s:%s", sub.Spec.AppType, sub.Status.CurrentVersion))
// 	if err != nil {
// 		return nil, err
// 	}
// 	return &plan, ctl.installer.CreateInstallplan(sub.GetNamespace(), plan)
// }

// // HandleSubscription is the handler in the subscriptionv1alpha1.Subscription controller that checks if an
// // Installplan object exists in the namespace for a given Subscription and creates one if not
// func (ctl *SubscriptionController) HandleNewSubscription(sub subscriptionv1alpha1.Subscription) error {
// 	ok, err := ctl.installer.CheckForInstallplan(sub.GetNamespace(), sub)
// 	if err != nil {
// 		return err
// 	}
// 	if ok {
// 		return nil
// 	}
// 	_, err = ctl.installplanForSubscription(sub)
// 	return err
// }

// // CheckCatalogForUpdate polls catalog for most recent version of an app and creates a new
// // Installplan is a more recent version exists
// func (ctl *SubscriptionController) CheckCatalogForUpdate(sub subscriptionv1alpha1.Subscription) error {
// 	versionStr, err := ctl.catalog.FindCloudServiceVersionByName(
// 		fmt.Sprintf("%s:%s", sub.Spec.AppType, sub.Status.Channel))
// 	if err != nil {
// 		return err
// 	}
// 	ver := semver.New(versionStr)
// 	currVer := semver.New(sub.Status.CurrentVersion)
// 	if !currVer.LessThan(*ver) {
// 		return nil
// 	}
// 	_, err = ctl.installplanForSubscription(sub)
// 	return err
// }
<|endoftext|>


<commit_before>// Copyright 2015 The appc 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 types

import (
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/appc/spec/schema/common"
)

type MountPoint struct {
	Name     ACName `json:"name"`
	Path     string `json:"path"`
	ReadOnly bool   `json:"readOnly,omitempty"`
}

func (mount MountPoint) assertValid() error {
	if mount.Name.Empty() {
		return errors.New("name must be set")
	}
	if len(mount.Path) == 0 {
		return errors.New("path must be set")
	}
	return nil
}

// MountPointFromString takes a command line mountpoint parameter and returns a mountpoint
//
// It is useful for actool patch-manifest --mounts
//
// Example mountpoint parameters:
// 	database,path=/tmp,readOnly=true
func MountPointFromString(mp string) (*MountPoint, error) {
	var mount MountPoint

	mp = "name=" + mp
	mpQuery, err := common.MakeQueryString(mp)
	if err != nil {
		return nil, err
	}

	v, err := url.ParseQuery(mpQuery)
	if err != nil {
		return nil, err
	}
	for key, val := range v {
		if len(val) > 1 {
			return nil, fmt.Errorf("label %s with multiple values %q", key, val)
		}

		switch key {
		case "name":
			acn, err := NewACName(val[0])
			if err != nil {
				return nil, err
			}
			mount.Name = *acn
		case "path":
			mount.Path = val[0]
		case "readOnly":
			ro, err := strconv.ParseBool(val[0])
			if err != nil {
				return nil, err
			}
			mount.ReadOnly = ro
		default:
			return nil, fmt.Errorf("unknown mountpoint parameter %q", key)
		}
	}
	err = mount.assertValid()
	if err != nil {
		return nil, err
	}

	return &mount, nil
}
<commit_msg>CRI: Allow specifying a mount + volume independent of an App<commit_after>// Copyright 2015 The appc 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 types

import (
	"errors"
	"fmt"
	"net/url"
	"strconv"

	"github.com/appc/spec/schema/common"
)

// MountPoint is the application-side manifestation of a Volume.
// The Volume is optional. If missing, the pod-level Volume of the
// same name shall be used
type MountPoint struct {
	Name     ACName  `json:"name"`
	Path     string  `json:"path"`
	ReadOnly bool    `json:"readOnly,omitempty"`
	Volume   *Volume `json:"volume,omitempty"`
}

func (mount MountPoint) assertValid() error {
	if mount.Name.Empty() {
		return errors.New("name must be set")
	}
	if len(mount.Path) == 0 {
		return errors.New("path must be set")
	}
	return nil
}

// MountPointFromString takes a command line mountpoint parameter and returns a mountpoint
//
// It is useful for actool patch-manifest --mounts
//
// Example mountpoint parameters:
// 	database,path=/tmp,readOnly=true
func MountPointFromString(mp string) (*MountPoint, error) {
	var mount MountPoint

	mp = "name=" + mp
	mpQuery, err := common.MakeQueryString(mp)
	if err != nil {
		return nil, err
	}

	v, err := url.ParseQuery(mpQuery)
	if err != nil {
		return nil, err
	}
	for key, val := range v {
		if len(val) > 1 {
			return nil, fmt.Errorf("label %s with multiple values %q", key, val)
		}

		switch key {
		case "name":
			acn, err := NewACName(val[0])
			if err != nil {
				return nil, err
			}
			mount.Name = *acn
		case "path":
			mount.Path = val[0]
		case "readOnly":
			ro, err := strconv.ParseBool(val[0])
			if err != nil {
				return nil, err
			}
			mount.ReadOnly = ro
		default:
			return nil, fmt.Errorf("unknown mountpoint parameter %q", key)
		}
	}
	err = mount.assertValid()
	if err != nil {
		return nil, err
	}

	return &mount, nil
}
<|endoftext|>


<commit_before>package dalton

import (
	"database/sql"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/schools"
)

type term struct {
	ID     int    `json:"id"`
	TermID int    `json:"termId"`
	Name   string `json:"name"`
	UserID int    `json:"userId"`
}

type provider struct {
	schools.Provider
}

func getOffBlocksStartingBefore(db *sql.DB, before string, groupSQL string) ([]data.OffBlock, error) {
	// find the starts
	offBlockRows, err := db.Query("SELECT id, date, text, grade FROM dalton_announcements WHERE ("+groupSQL+") AND `type` = 2 AND `date` < ?", before)
	if err != nil {
		return nil, err
	}
	defer offBlockRows.Close()
	blocks := []data.OffBlock{}
	for offBlockRows.Next() {
		block := data.OffBlock{}
		offBlockRows.Scan(&block.StartID, &block.StartText, &block.Name, &block.Grade)
		blocks = append(blocks, block)
	}

	// find the matching ends
	for i, block := range blocks {
		offBlockEndRows, err := db.Query("SELECT date FROM dalton_announcements WHERE ("+groupSQL+") AND `type` = 3 AND `text` = ? AND `date` > ?", block.Name, block.StartText)
		if err != nil {
			return nil, err
		}
		defer offBlockEndRows.Close()
		if offBlockEndRows.Next() {
			offBlockEndRows.Scan(&blocks[i].EndText)
		}
	}

	// parse dates
	for i, block := range blocks {
		blocks[i].Start, err = time.Parse("2006-01-02", block.StartText)
		if err != nil {
			return nil, err
		}
		blocks[i].End, err = time.Parse("2006-01-02", block.EndText)
		if err != nil {
			return nil, err
		}
	}

	return blocks, err
}

func (p *provider) GetData(db *sql.DB, user *data.User, location *time.Location, startTime time.Time, endTime time.Time, dataType data.ProviderDataType) (data.ProviderData, error) {
	result := data.ProviderData{
		Announcements: nil,
		Events:        nil,
	}

	dayCount := int((endTime.Sub(startTime).Hours() / 24) + 0.5)

	school := (p.Provider.School).(*school)

	// get user's grade
	grade, err := getUserGrade(school.username)
	if err != nil {
		return data.ProviderData{}, err
	}

	// get user's announcement groups
	announcementGroups := getGradeAnnouncementGroups(grade)
	announcementGroupsSQL := getAnnouncementGroupSQL(announcementGroups)

	// get all rotation information for time period
	rotationRows, err := db.Query("SELECT * FROM dalton_rotations WHERE date >= ? AND date <= ?", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"))
	if err != nil {
		return data.ProviderData{}, err
	}
	rotations := []data.PlannerFriday{}
	for rotationRows.Next() {
		rotation := data.PlannerFriday{}
		rotationRows.Scan(&rotation.ID, &rotation.Date, &rotation.Index)
		rotations = append(rotations, rotation)
	}
	rotationRows.Close()

	// get announcements for time period
	announcementRows, err := db.Query("SELECT id, date, text, grade, `type` FROM dalton_announcements WHERE date >= ? AND date <= ? AND ("+announcementGroupsSQL+") AND type < 2", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"))
	if err != nil {
		return data.ProviderData{}, err
	}
	defer announcementRows.Close()
	announcements := []data.PlannerAnnouncement{}
	for announcementRows.Next() {
		resp := data.PlannerAnnouncement{}
		announcementRows.Scan(&resp.ID, &resp.Date, &resp.Text, &resp.Grade, &resp.Type)
		announcements = append(announcements, resp)
	}

	// get off blocks for time period
	offBlocks, err := getOffBlocksStartingBefore(db, endTime.Format("2006-01-02"), announcementGroupsSQL)
	if err != nil {
		return data.ProviderData{}, err
	}

	// generate list of all off days in time period
	offDays := []string{}

	for _, announcement := range announcements {
		if announcement.Type == data.AnnouncementTypeFullOff {
			offDays = append(offDays, announcement.Date)
		}
	}

	if dataType&data.ProviderDataAnnouncements != 0 {
		result.Announcements = []data.PlannerAnnouncement{}

		// add rotations as announcements
		for _, rotation := range rotations {
			dayName := "Friday "
			if CurrentMode == SchoolModeVirtual {
				dayName = "Wednesday "
			}

			rotationAnnouncement := data.PlannerAnnouncement{
				ID:    -1,
				Date:  rotation.Date,
				Text:  dayName + strconv.Itoa(rotation.Index),
				Grade: -1,
				Type:  0,
			}
			result.Announcements = append(result.Announcements, rotationAnnouncement)
		}

		// add exception day announcements
		currentDay := startTime
		for i := 0; i < dayCount; i++ {
			if i != 0 {
				currentDay = currentDay.Add(24 * time.Hour)
			}

			dayString := currentDay.Format("2006-01-02")

			overrideDay, isException := ExceptionDays[dayString]
			if isException {
				result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
					ID:    -1,
					Date:  dayString,
					Text:  overrideDay.String() + " Class Schedule",
					Grade: -1,
					Type:  0,
				})
			}
		}

		// add standard announcements
		result.Announcements = append(result.Announcements, announcements...)

		for _, offBlock := range offBlocks {
			offDayCount := int(math.Ceil(offBlock.End.Sub(offBlock.Start).Hours() / 24))
			offDay := offBlock.Start
			result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
				ID:    offBlock.StartID,
				Date:  offBlock.StartText,
				Text:  "Start of " + offBlock.Name,
				Grade: offBlock.Grade,
				Type:  data.AnnouncementTypeBreakStart,
			})
			for i := 0; i < offDayCount; i++ {
				if i != 0 {
					result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
						ID:    offBlock.StartID,
						Date:  offDay.Format("2006-01-02"),
						Text:  offBlock.Name,
						Grade: offBlock.Grade,
						Type:  data.AnnouncementTypeBreakStart,
					})
				}
				offDays = append(offDays, offDay.Format("2006-01-02"))
				offDay = offDay.Add(24 * time.Hour)
			}
			result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
				ID:    offBlock.EndID,
				Date:  offBlock.EndText,
				Text:  "End of " + offBlock.Name,
				Grade: offBlock.Grade,
				Type:  data.AnnouncementTypeBreakEnd,
			})
		}
	}

	if dataType&data.ProviderDataEvents != 0 {
		result.Events = []data.Event{}

		// get terms for user
		termRows, err := db.Query("SELECT id, termId, name, userId FROM dalton_terms WHERE userId = ? ORDER BY name ASC", user.ID)
		if err != nil {
			return data.ProviderData{}, err
		}
		defer termRows.Close()
		availableTerms := []term{}
		for termRows.Next() {
			term := term{}
			err = termRows.Scan(&term.ID, &term.TermID, &term.Name, &term.UserID)
			if err != nil {
				return data.ProviderData{}, err
			}
			availableTerms = append(availableTerms, term)
		}

		// get schedule events
		currentDay := startTime
		for i := 0; i < dayCount; i++ {
			if i != 0 {
				currentDay = currentDay.Add(24 * time.Hour)
			}

			dayString := currentDay.Format("2006-01-02")

			var currentTerm *term

			for _, termToCheck := range availableTerms {
				dates := TermMap[termToCheck.Name]
				if dates == nil {
					// this is an old schedule, just ignore it
					continue
				}

				startDate, endDate := dates[0], dates[1]
				if currentDay.Add(time.Second).After(startDate) && currentDay.Before(endDate) {
					// special exception: in normal mode, seniors stop having classes at a certain point
					if CurrentMode == SchoolModeVirtual {
						// if user is a senior, their classes end earlier
						if grade == 12 && currentDay.After(Day_SeniorEnd) {
							currentTerm = nil
							break
						}
					}

					currentTerm = &termToCheck
					break
				}
			}

			if currentTerm != nil {
				dayTime, _ := time.ParseInLocation("2006-01-02", dayString, location)
				dayOffset := int(dayTime.Unix())

				// check if it's an off day
				isOff := false

				for _, offDay := range offDays {
					if dayString == offDay {
						isOff = true
						break
					}
				}

				if isOff {
					continue
				}

				// calculate day index (1 = monday, 8 = rotation 4)
				dayNumber := int(dayTime.Weekday())

				overrideDay, isException := ExceptionDays[dayString]
				if isException {
					dayNumber = int(overrideDay)
				}

				rotationDay := time.Friday
				if CurrentMode == SchoolModeVirtual {
					rotationDay = time.Wednesday
				}

				if !isException && dayTime.Weekday() == rotationDay {
					rotationNumber := -1
					for _, rotation := range rotations {
						if dayString == rotation.Date {
							rotationNumber = rotation.Index
							break
						}
					}

					if rotationNumber == 1 {
						// the first rotation gets the normal day number
						// this is by default for friday, but needs to explicitly stated for virtual school, with wednesday rotations
						dayNumber = int(rotationDay)
					} else if rotationNumber != -1 {
						dayNumber = 4 + rotationNumber
					} else {
						continue
					}
				}

				if !isException && dayTime.Weekday() == time.Saturday || dayTime.Weekday() == time.Sunday {
					continue
				}

				rows, err := db.Query("SELECT dalton_periods.id, dalton_classes.termId, dalton_classes.sectionId, dalton_classes.`name`, dalton_classes.ownerId, dalton_classes.ownerName, dalton_periods.dayNumber, dalton_periods.block, dalton_periods.buildingName, dalton_periods.roomNumber, dalton_periods.`start`, dalton_periods.`end`, dalton_periods.userId FROM dalton_periods INNER JOIN dalton_classes ON dalton_periods.classId = dalton_classes.sectionId WHERE dalton_periods.userId = ? AND dalton_classes.userId = ? AND (dalton_classes.termId = ? OR dalton_classes.termId = -1) AND dalton_periods.dayNumber = ? GROUP BY dalton_periods.id, dalton_classes.termId, dalton_classes.name, dalton_classes.ownerId, dalton_classes.ownerName", user.ID, user.ID, currentTerm.TermID, dayNumber)
				if err != nil {
					return data.ProviderData{}, err
				}
				defer rows.Close()
				for rows.Next() {
					event := data.Event{
						Tags: map[data.EventTagType]interface{}{},
					}

					termID, classID, ownerID, ownerName, dayNumber, block, buildingName, roomNumber := -1, -1, -1, "", -1, "", "", ""

					rows.Scan(&event.ID, &termID, &classID, &event.Name, &ownerID, &ownerName, &dayNumber, &block, &buildingName, &roomNumber, &event.Start, &event.End, &event.UserID)

					event.UniqueID = strconv.Itoa(event.ID) + "-" + strconv.Itoa(classID) + "-" + dayString

					event.Tags[data.EventTagShortName] = strings.TrimSpace(strings.SplitN(event.Name, " - ", 2)[0])
					event.Tags[data.EventTagReadOnly] = true
					event.Tags[data.EventTagCancelable] = true
					event.Tags[data.EventTagTermID] = termID
					event.Tags[data.EventTagClassID] = classID
					event.Tags[data.EventTagOwnerID] = ownerID
					event.Tags[data.EventTagOwnerName] = ownerName
					event.Tags[data.EventTagDayNumber] = dayNumber
					event.Tags[data.EventTagBlock] = block
					event.Tags[data.EventTagBuildingName] = buildingName
					event.Tags[data.EventTagRoomNumber] = roomNumber

					event.Start += dayOffset
					event.End += dayOffset

					result.Events = append(result.Events, event)
				}

				if dayTime.Weekday() == time.Thursday {
					// special case: assembly
					for eventIndex, event := range result.Events {
						// check for an "HS House" event
						// starting 11:50, ending 12:50
						if strings.HasPrefix(event.Name, "HS House") && event.Start == int(dayTime.Unix())+42600 && event.End == int(dayTime.Unix())+46200 {
							// found it
							// now look up what type of assembly period it is this week
							assemblyType, foundType := AssemblyTypeList[dayTime.Format("2006-01-02")]

							if !foundType || assemblyType == AssemblyTypeAssembly {
								// set name to assembly and room to Theater
								result.Events[eventIndex].Name = "Assembly"
								result.Events[eventIndex].Tags[data.EventTagRoomNumber] = "Theater"
							} else if assemblyType == AssemblyTypeLongHouse {
								// set name to long house
								result.Events[eventIndex].Name = "Long House"
							} else if assemblyType == AssemblyTypeLab {
								// just remove it
								result.Events = append(result.Events[:eventIndex], result.Events[eventIndex+1:]...)
							}
						}
					}
				}
			}
		}
	}

	return result, nil
}
<commit_msg>dalton: catch some ignored errors<commit_after>package dalton

import (
	"database/sql"
	"math"
	"strconv"
	"strings"
	"time"

	"github.com/MyHomeworkSpace/api-server/data"
	"github.com/MyHomeworkSpace/api-server/schools"
)

type term struct {
	ID     int    `json:"id"`
	TermID int    `json:"termId"`
	Name   string `json:"name"`
	UserID int    `json:"userId"`
}

type provider struct {
	schools.Provider
}

func getOffBlocksStartingBefore(db *sql.DB, before string, groupSQL string) ([]data.OffBlock, error) {
	// find the starts
	offBlockRows, err := db.Query("SELECT id, date, text, grade FROM dalton_announcements WHERE ("+groupSQL+") AND `type` = 2 AND `date` < ?", before)
	if err != nil {
		return nil, err
	}
	defer offBlockRows.Close()
	blocks := []data.OffBlock{}
	for offBlockRows.Next() {
		block := data.OffBlock{}
		err = offBlockRows.Scan(&block.StartID, &block.StartText, &block.Name, &block.Grade)
		if err != nil {
			return nil, err
		}
		blocks = append(blocks, block)
	}

	// find the matching ends
	for i, block := range blocks {
		offBlockEndRows, err := db.Query("SELECT date FROM dalton_announcements WHERE ("+groupSQL+") AND `type` = 3 AND `text` = ? AND `date` > ?", block.Name, block.StartText)
		if err != nil {
			return nil, err
		}
		defer offBlockEndRows.Close()
		if offBlockEndRows.Next() {
			err = offBlockEndRows.Scan(&blocks[i].EndText)
			if err != nil {
				return nil, err
			}
		}
	}

	// parse dates
	for i, block := range blocks {
		blocks[i].Start, err = time.Parse("2006-01-02", block.StartText)
		if err != nil {
			return nil, err
		}
		blocks[i].End, err = time.Parse("2006-01-02", block.EndText)
		if err != nil {
			return nil, err
		}
	}

	return blocks, err
}

func (p *provider) GetData(db *sql.DB, user *data.User, location *time.Location, startTime time.Time, endTime time.Time, dataType data.ProviderDataType) (data.ProviderData, error) {
	result := data.ProviderData{
		Announcements: nil,
		Events:        nil,
	}

	dayCount := int((endTime.Sub(startTime).Hours() / 24) + 0.5)

	school := (p.Provider.School).(*school)

	// get user's grade
	grade, err := getUserGrade(school.username)
	if err != nil {
		return data.ProviderData{}, err
	}

	// get user's announcement groups
	announcementGroups := getGradeAnnouncementGroups(grade)
	announcementGroupsSQL := getAnnouncementGroupSQL(announcementGroups)

	// get all rotation information for time period
	rotationRows, err := db.Query("SELECT * FROM dalton_rotations WHERE date >= ? AND date <= ?", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"))
	if err != nil {
		return data.ProviderData{}, err
	}
	rotations := []data.PlannerFriday{}
	for rotationRows.Next() {
		rotation := data.PlannerFriday{}
		err = rotationRows.Scan(&rotation.ID, &rotation.Date, &rotation.Index)
		if err != nil {
			return data.ProviderData{}, err
		}
		rotations = append(rotations, rotation)
	}
	rotationRows.Close()

	// get announcements for time period
	announcementRows, err := db.Query("SELECT id, date, text, grade, `type` FROM dalton_announcements WHERE date >= ? AND date <= ? AND ("+announcementGroupsSQL+") AND type < 2", startTime.Format("2006-01-02"), endTime.Format("2006-01-02"))
	if err != nil {
		return data.ProviderData{}, err
	}
	defer announcementRows.Close()
	announcements := []data.PlannerAnnouncement{}
	for announcementRows.Next() {
		resp := data.PlannerAnnouncement{}
		err = announcementRows.Scan(&resp.ID, &resp.Date, &resp.Text, &resp.Grade, &resp.Type)
		if err != nil {
			return data.ProviderData{}, err
		}
		announcements = append(announcements, resp)
	}

	// get off blocks for time period
	offBlocks, err := getOffBlocksStartingBefore(db, endTime.Format("2006-01-02"), announcementGroupsSQL)
	if err != nil {
		return data.ProviderData{}, err
	}

	// generate list of all off days in time period
	offDays := []string{}

	for _, announcement := range announcements {
		if announcement.Type == data.AnnouncementTypeFullOff {
			offDays = append(offDays, announcement.Date)
		}
	}

	if dataType&data.ProviderDataAnnouncements != 0 {
		result.Announcements = []data.PlannerAnnouncement{}

		// add rotations as announcements
		for _, rotation := range rotations {
			dayName := "Friday "
			if CurrentMode == SchoolModeVirtual {
				dayName = "Wednesday "
			}

			rotationAnnouncement := data.PlannerAnnouncement{
				ID:    -1,
				Date:  rotation.Date,
				Text:  dayName + strconv.Itoa(rotation.Index),
				Grade: -1,
				Type:  0,
			}
			result.Announcements = append(result.Announcements, rotationAnnouncement)
		}

		// add exception day announcements
		currentDay := startTime
		for i := 0; i < dayCount; i++ {
			if i != 0 {
				currentDay = currentDay.Add(24 * time.Hour)
			}

			dayString := currentDay.Format("2006-01-02")

			overrideDay, isException := ExceptionDays[dayString]
			if isException {
				result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
					ID:    -1,
					Date:  dayString,
					Text:  overrideDay.String() + " Class Schedule",
					Grade: -1,
					Type:  0,
				})
			}
		}

		// add standard announcements
		result.Announcements = append(result.Announcements, announcements...)

		for _, offBlock := range offBlocks {
			offDayCount := int(math.Ceil(offBlock.End.Sub(offBlock.Start).Hours() / 24))
			offDay := offBlock.Start
			result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
				ID:    offBlock.StartID,
				Date:  offBlock.StartText,
				Text:  "Start of " + offBlock.Name,
				Grade: offBlock.Grade,
				Type:  data.AnnouncementTypeBreakStart,
			})
			for i := 0; i < offDayCount; i++ {
				if i != 0 {
					result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
						ID:    offBlock.StartID,
						Date:  offDay.Format("2006-01-02"),
						Text:  offBlock.Name,
						Grade: offBlock.Grade,
						Type:  data.AnnouncementTypeBreakStart,
					})
				}
				offDays = append(offDays, offDay.Format("2006-01-02"))
				offDay = offDay.Add(24 * time.Hour)
			}
			result.Announcements = append(result.Announcements, data.PlannerAnnouncement{
				ID:    offBlock.EndID,
				Date:  offBlock.EndText,
				Text:  "End of " + offBlock.Name,
				Grade: offBlock.Grade,
				Type:  data.AnnouncementTypeBreakEnd,
			})
		}
	}

	if dataType&data.ProviderDataEvents != 0 {
		result.Events = []data.Event{}

		// get terms for user
		termRows, err := db.Query("SELECT id, termId, name, userId FROM dalton_terms WHERE userId = ? ORDER BY name ASC", user.ID)
		if err != nil {
			return data.ProviderData{}, err
		}
		defer termRows.Close()
		availableTerms := []term{}
		for termRows.Next() {
			term := term{}
			err = termRows.Scan(&term.ID, &term.TermID, &term.Name, &term.UserID)
			if err != nil {
				return data.ProviderData{}, err
			}
			availableTerms = append(availableTerms, term)
		}

		// get schedule events
		currentDay := startTime
		for i := 0; i < dayCount; i++ {
			if i != 0 {
				currentDay = currentDay.Add(24 * time.Hour)
			}

			dayString := currentDay.Format("2006-01-02")

			var currentTerm *term

			for _, termToCheck := range availableTerms {
				dates := TermMap[termToCheck.Name]
				if dates == nil {
					// this is an old schedule, just ignore it
					continue
				}

				startDate, endDate := dates[0], dates[1]
				if currentDay.Add(time.Second).After(startDate) && currentDay.Before(endDate) {
					// special exception: in normal mode, seniors stop having classes at a certain point
					if CurrentMode == SchoolModeVirtual {
						// if user is a senior, their classes end earlier
						if grade == 12 && currentDay.After(Day_SeniorEnd) {
							currentTerm = nil
							break
						}
					}

					currentTerm = &termToCheck
					break
				}
			}

			if currentTerm != nil {
				dayTime, _ := time.ParseInLocation("2006-01-02", dayString, location)
				dayOffset := int(dayTime.Unix())

				// check if it's an off day
				isOff := false

				for _, offDay := range offDays {
					if dayString == offDay {
						isOff = true
						break
					}
				}

				if isOff {
					continue
				}

				// calculate day index (1 = monday, 8 = rotation 4)
				dayNumber := int(dayTime.Weekday())

				overrideDay, isException := ExceptionDays[dayString]
				if isException {
					dayNumber = int(overrideDay)
				}

				rotationDay := time.Friday
				if CurrentMode == SchoolModeVirtual {
					rotationDay = time.Wednesday
				}

				if !isException && dayTime.Weekday() == rotationDay {
					rotationNumber := -1
					for _, rotation := range rotations {
						if dayString == rotation.Date {
							rotationNumber = rotation.Index
							break
						}
					}

					if rotationNumber == 1 {
						// the first rotation gets the normal day number
						// this is by default for friday, but needs to explicitly stated for virtual school, with wednesday rotations
						dayNumber = int(rotationDay)
					} else if rotationNumber != -1 {
						dayNumber = 4 + rotationNumber
					} else {
						continue
					}
				}

				if !isException && dayTime.Weekday() == time.Saturday || dayTime.Weekday() == time.Sunday {
					continue
				}

				rows, err := db.Query("SELECT dalton_periods.id, dalton_classes.termId, dalton_classes.sectionId, dalton_classes.`name`, dalton_classes.ownerId, dalton_classes.ownerName, dalton_periods.dayNumber, dalton_periods.block, dalton_periods.buildingName, dalton_periods.roomNumber, dalton_periods.`start`, dalton_periods.`end`, dalton_periods.userId FROM dalton_periods INNER JOIN dalton_classes ON dalton_periods.classId = dalton_classes.sectionId WHERE dalton_periods.userId = ? AND dalton_classes.userId = ? AND (dalton_classes.termId = ? OR dalton_classes.termId = -1) AND dalton_periods.dayNumber = ? GROUP BY dalton_periods.id, dalton_classes.termId, dalton_classes.name, dalton_classes.ownerId, dalton_classes.ownerName", user.ID, user.ID, currentTerm.TermID, dayNumber)
				if err != nil {
					return data.ProviderData{}, err
				}
				defer rows.Close()
				for rows.Next() {
					event := data.Event{
						Tags: map[data.EventTagType]interface{}{},
					}

					termID, classID, ownerID, ownerName, dayNumber, block, buildingName, roomNumber := -1, -1, -1, "", -1, "", "", ""

					err = rows.Scan(&event.ID, &termID, &classID, &event.Name, &ownerID, &ownerName, &dayNumber, &block, &buildingName, &roomNumber, &event.Start, &event.End, &event.UserID)
					if err != nil {
						return data.ProviderData{}, err
					}

					event.UniqueID = strconv.Itoa(event.ID) + "-" + strconv.Itoa(classID) + "-" + dayString

					event.Tags[data.EventTagShortName] = strings.TrimSpace(strings.SplitN(event.Name, " - ", 2)[0])
					event.Tags[data.EventTagReadOnly] = true
					event.Tags[data.EventTagCancelable] = true
					event.Tags[data.EventTagTermID] = termID
					event.Tags[data.EventTagClassID] = classID
					event.Tags[data.EventTagOwnerID] = ownerID
					event.Tags[data.EventTagOwnerName] = ownerName
					event.Tags[data.EventTagDayNumber] = dayNumber
					event.Tags[data.EventTagBlock] = block
					event.Tags[data.EventTagBuildingName] = buildingName
					event.Tags[data.EventTagRoomNumber] = roomNumber

					event.Start += dayOffset
					event.End += dayOffset

					result.Events = append(result.Events, event)
				}

				if dayTime.Weekday() == time.Thursday {
					// special case: assembly
					for eventIndex, event := range result.Events {
						// check for an "HS House" event
						// starting 11:50, ending 12:50
						if strings.HasPrefix(event.Name, "HS House") && event.Start == int(dayTime.Unix())+42600 && event.End == int(dayTime.Unix())+46200 {
							// found it
							// now look up what type of assembly period it is this week
							assemblyType, foundType := AssemblyTypeList[dayTime.Format("2006-01-02")]

							if !foundType || assemblyType == AssemblyTypeAssembly {
								// set name to assembly and room to Theater
								result.Events[eventIndex].Name = "Assembly"
								result.Events[eventIndex].Tags[data.EventTagRoomNumber] = "Theater"
							} else if assemblyType == AssemblyTypeLongHouse {
								// set name to long house
								result.Events[eventIndex].Name = "Long House"
							} else if assemblyType == AssemblyTypeLab {
								// just remove it
								result.Events = append(result.Events[:eventIndex], result.Events[eventIndex+1:]...)
							}
						}
					}
				}
			}
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>package database

import (
	"fmt"
	"labix.org/v2/mgo/bson"
	"mud/utils"
	"strings"
)

type Coordinate struct {
	X int
	Y int
	Z int
}

type User struct {
	Id           bson.ObjectId `bson:"_id,omitempty"`
	Name         string
	CharacterIds []bson.ObjectId `bson:"characterids,omitempty"`
}

func newUser(name string) User {
	var user User
	user.Id = bson.NewObjectId()
	user.Name = name
	return user
}

type Character struct {
	Id     bson.ObjectId `bson:"_id"`
	Name   string
	RoomId bson.ObjectId `bson:"roomid"`
}

func newCharacter(name string) Character {
	var character Character
	character.Id = bson.NewObjectId()
	character.Name = name
	character.RoomId = ""
	return character
}

type Room struct {
	Id          bson.ObjectId `bson:"_id"`
	Title       string
	Description string
	Location    Coordinate
	ExitNorth   bool
	ExitEast    bool
	ExitSouth   bool
	ExitWest    bool
	ExitUp      bool
	ExitDown    bool
	Default     bool
}

func newRoom() Room {
	var room Room
	room.Id = ""
	room.Title = "The Void"
	room.Description = "You are floating in the blackness of space. Complete darkness surrounds " +
		"you in all directions. There is no escape, there is no hope, just the emptiness. " +
		"You are likely to be eaten by a grue."

	room.ExitNorth = false
	room.ExitEast = false
	room.ExitSouth = false
	room.ExitWest = false
	room.ExitUp = false
	room.ExitDown = false

	room.Location = Coordinate{0, 0, 0}

	room.Default = false

	return room
}

type ExitDirection int

const (
	DirectionNone  ExitDirection = iota
	DirectionNorth ExitDirection = iota
	DirectionEast  ExitDirection = iota
	DirectionSouth ExitDirection = iota
	DirectionWest  ExitDirection = iota
	DirectionUp    ExitDirection = iota
	DirectionDown  ExitDirection = iota
)

type PrintMode int

const (
	ReadMode PrintMode = iota
	EditMode PrintMode = iota
)

func directionToExitString(direction ExitDirection) string {
	switch direction {
	case DirectionNorth:
		return "[N]orth"
	case DirectionEast:
		return "[E]ast"
	case DirectionSouth:
		return "[S]outh"
	case DirectionWest:
		return "[W]est"
	case DirectionUp:
		return "[U]p"
	case DirectionDown:
		return "[D]own"
	case DirectionNone:
		return "None"
	}

	panic("Unexpected code path")
}

func (self *Room) ToString(mode PrintMode) string {

	var str string
	if mode == ReadMode {
		str = fmt.Sprintf("\n >>> %v <<< (%v %v %v)\n\n %v \n\n Exits: ",
			self.Title,
			self.Location.X,
			self.Location.Y,
			self.Location.Z,
			self.Description)
	} else {
		str = fmt.Sprintf("\n [1] %v \n\n [2] %v \n\n [3] Exits: ", self.Title, self.Description)
	}

	var exitList []string

	appendIfExists := func(direction ExitDirection) {
		if self.HasExit(direction) {
			exitList = append(exitList, directionToExitString(direction))
		}
	}

	appendIfExists(DirectionNorth)
	appendIfExists(DirectionEast)
	appendIfExists(DirectionSouth)
	appendIfExists(DirectionWest)
	appendIfExists(DirectionUp)
	appendIfExists(DirectionDown)

	if len(exitList) == 0 {
		str = str + "None"
	} else {
		str = str + strings.Join(exitList, " ")
	}

	str = str + "\n"

	return str
}

func (self *Room) HasExit(dir ExitDirection) bool {
	switch dir {
	case DirectionNorth:
		return self.ExitNorth
	case DirectionEast:
		return self.ExitEast
	case DirectionSouth:
		return self.ExitSouth
	case DirectionWest:
		return self.ExitWest
	case DirectionUp:
		return self.ExitUp
	case DirectionDown:
		return self.ExitDown
	}

	panic("Unexpected code path")
}

func (self *Room) SetExitEnabled(dir ExitDirection, enabled bool) {
	switch dir {
	case DirectionNorth:
		self.ExitNorth = enabled
	case DirectionEast:
		self.ExitEast = enabled
	case DirectionSouth:
		self.ExitSouth = enabled
	case DirectionWest:
		self.ExitWest = enabled
	case DirectionUp:
		self.ExitUp = enabled
	case DirectionDown:
		self.ExitDown = enabled
	}
}

func (self *Character) PrettyName() string {
	return utils.FormatName(self.Name)
}

func (self *User) PrettyName() string {
	return utils.FormatName(self.Name)
}

func StringToDirection(str string) ExitDirection {
	dirStr := strings.ToLower(str)
	switch dirStr {
	case "n":
		return DirectionNorth
	case "s":
		return DirectionSouth
	case "e":
		return DirectionEast
	case "w":
		return DirectionWest
	case "u":
		return DirectionUp
	case "d":
		return DirectionDown
	}

	return DirectionNone
}

// vim: nocindent
<commit_msg>Fixed newRoom()<commit_after>package database

import (
	"fmt"
	"labix.org/v2/mgo/bson"
	"mud/utils"
	"strings"
)

type Coordinate struct {
	X int
	Y int
	Z int
}

type User struct {
	Id           bson.ObjectId `bson:"_id,omitempty"`
	Name         string
	CharacterIds []bson.ObjectId `bson:"characterids,omitempty"`
}

func newUser(name string) User {
	var user User
	user.Id = bson.NewObjectId()
	user.Name = name
	return user
}

type Character struct {
	Id     bson.ObjectId `bson:"_id"`
	Name   string
	RoomId bson.ObjectId `bson:"roomid"`
}

func newCharacter(name string) Character {
	var character Character
	character.Id = bson.NewObjectId()
	character.Name = name
	character.RoomId = ""
	return character
}

type Room struct {
	Id          bson.ObjectId `bson:"_id"`
	Title       string
	Description string
	Location    Coordinate
	ExitNorth   bool
	ExitEast    bool
	ExitSouth   bool
	ExitWest    bool
	ExitUp      bool
	ExitDown    bool
	Default     bool
}

func newRoom() Room {
	var room Room
	room.Id = bson.NewObjectId()
	room.Title = "The Void"
	room.Description = "You are floating in the blackness of space. Complete darkness surrounds " +
		"you in all directions. There is no escape, there is no hope, just the emptiness. " +
		"You are likely to be eaten by a grue."

	room.ExitNorth = false
	room.ExitEast = false
	room.ExitSouth = false
	room.ExitWest = false
	room.ExitUp = false
	room.ExitDown = false

	room.Location = Coordinate{0, 0, 0}

	room.Default = false

	return room
}

type ExitDirection int

const (
	DirectionNone  ExitDirection = iota
	DirectionNorth ExitDirection = iota
	DirectionEast  ExitDirection = iota
	DirectionSouth ExitDirection = iota
	DirectionWest  ExitDirection = iota
	DirectionUp    ExitDirection = iota
	DirectionDown  ExitDirection = iota
)

type PrintMode int

const (
	ReadMode PrintMode = iota
	EditMode PrintMode = iota
)

func directionToExitString(direction ExitDirection) string {
	switch direction {
	case DirectionNorth:
		return "[N]orth"
	case DirectionEast:
		return "[E]ast"
	case DirectionSouth:
		return "[S]outh"
	case DirectionWest:
		return "[W]est"
	case DirectionUp:
		return "[U]p"
	case DirectionDown:
		return "[D]own"
	case DirectionNone:
		return "None"
	}

	panic("Unexpected code path")
}

func (self *Room) ToString(mode PrintMode) string {

	var str string
	if mode == ReadMode {
		str = fmt.Sprintf("\n >>> %v <<< (%v %v %v)\n\n %v \n\n Exits: ",
			self.Title,
			self.Location.X,
			self.Location.Y,
			self.Location.Z,
			self.Description)
	} else {
		str = fmt.Sprintf("\n [1] %v \n\n [2] %v \n\n [3] Exits: ", self.Title, self.Description)
	}

	var exitList []string

	appendIfExists := func(direction ExitDirection) {
		if self.HasExit(direction) {
			exitList = append(exitList, directionToExitString(direction))
		}
	}

	appendIfExists(DirectionNorth)
	appendIfExists(DirectionEast)
	appendIfExists(DirectionSouth)
	appendIfExists(DirectionWest)
	appendIfExists(DirectionUp)
	appendIfExists(DirectionDown)

	if len(exitList) == 0 {
		str = str + "None"
	} else {
		str = str + strings.Join(exitList, " ")
	}

	str = str + "\n"

	return str
}

func (self *Room) HasExit(dir ExitDirection) bool {
	switch dir {
	case DirectionNorth:
		return self.ExitNorth
	case DirectionEast:
		return self.ExitEast
	case DirectionSouth:
		return self.ExitSouth
	case DirectionWest:
		return self.ExitWest
	case DirectionUp:
		return self.ExitUp
	case DirectionDown:
		return self.ExitDown
	}

	panic("Unexpected code path")
}

func (self *Room) SetExitEnabled(dir ExitDirection, enabled bool) {
	switch dir {
	case DirectionNorth:
		self.ExitNorth = enabled
	case DirectionEast:
		self.ExitEast = enabled
	case DirectionSouth:
		self.ExitSouth = enabled
	case DirectionWest:
		self.ExitWest = enabled
	case DirectionUp:
		self.ExitUp = enabled
	case DirectionDown:
		self.ExitDown = enabled
	}
}

func (self *Character) PrettyName() string {
	return utils.FormatName(self.Name)
}

func (self *User) PrettyName() string {
	return utils.FormatName(self.Name)
}

func StringToDirection(str string) ExitDirection {
	dirStr := strings.ToLower(str)
	switch dirStr {
	case "n":
		return DirectionNorth
	case "s":
		return DirectionSouth
	case "e":
		return DirectionEast
	case "w":
		return DirectionWest
	case "u":
		return DirectionUp
	case "d":
		return DirectionDown
	}

	return DirectionNone
}

// vim: nocindent
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"

	"github.com/bruston/handlers/gzipped"
)

func main() {
	listen := flag.String("listen", ":8090", "The host and port to listen on.")
	flag.Parse()
	http.HandleFunc("/headers", headers)
	http.HandleFunc("/status/", status)
	http.HandleFunc("/ip", ip)
	http.HandleFunc("/get", get)
	http.Handle("/gzip", gzipped.New(http.HandlerFunc(gzippedResponse)))
	log.Fatal(http.ListenAndServe(*listen, nil))
}

func writeJSON(w http.ResponseWriter, data interface{}, code int) error {
	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(code)
	return json.NewEncoder(w).Encode(data)
}

func headers(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, r.Header, http.StatusOK)
}

func status(w http.ResponseWriter, r *http.Request) {
	code, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil {
		http.Error(w, "status code must be an integer", http.StatusBadRequest)
		return
	}
	w.WriteHeader(code)
}

type origin struct {
	IP           string `json:"ip"`
	ForwardedFor string `json:"forwarded_for,omitempty"`
}

func getOrigin(r *http.Request) origin {
	host, _, _ := net.SplitHostPort(r.RemoteAddr)
	return origin{host, r.Header.Get("X-Forwarded-For")}
}

func ip(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, getOrigin(r), http.StatusOK)
}

type request struct {
	Args    url.Values  `json:"args"`
	Gzipped bool        `json:"gzipped,omitempty"`
	Headers http.Header `json:"headers"`
	Origin  origin      `json:"origin"`
	URL     string      `json:"url"`
}

func rawURL(r *http.Request) string {
	var scheme string
	if r.TLS == nil {
		scheme = "http"
	} else {
		scheme = "https"
	}
	return scheme + "://" + r.Host + r.URL.String()
}

func getReq(r *http.Request) request {
	return request{
		Args:    r.URL.Query(),
		Headers: r.Header,
		Origin:  getOrigin(r),
		URL:     rawURL(r),
	}
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
		return
	}
	req := getReq(r)
	writeJSON(w, req, http.StatusOK)
}

func gzippedResponse(w http.ResponseWriter, r *http.Request) {
	req := getReq(r)
	if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
		req.Gzipped = true
	}
	writeJSON(w, req, http.StatusOK)
}
<commit_msg>Do a better check to see if a response will be gzipped<commit_after>package main

import (
	"encoding/json"
	"flag"
	"log"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"

	"github.com/bruston/handlers/gzipped"
)

func main() {
	listen := flag.String("listen", ":8090", "The host and port to listen on.")
	flag.Parse()
	http.HandleFunc("/headers", headers)
	http.HandleFunc("/status/", status)
	http.HandleFunc("/ip", ip)
	http.HandleFunc("/get", get)
	http.Handle("/gzip", gzipped.New(http.HandlerFunc(gzippedResponse)))
	log.Fatal(http.ListenAndServe(*listen, nil))
}

func writeJSON(w http.ResponseWriter, data interface{}, code int) error {
	w.Header().Set("Content-type", "application/json")
	w.WriteHeader(code)
	return json.NewEncoder(w).Encode(data)
}

func headers(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, r.Header, http.StatusOK)
}

func status(w http.ResponseWriter, r *http.Request) {
	code, err := strconv.Atoi(path.Base(r.URL.Path))
	if err != nil {
		http.Error(w, "status code must be an integer", http.StatusBadRequest)
		return
	}
	w.WriteHeader(code)
}

type origin struct {
	IP           string `json:"ip"`
	ForwardedFor string `json:"forwarded_for,omitempty"`
}

func getOrigin(r *http.Request) origin {
	host, _, _ := net.SplitHostPort(r.RemoteAddr)
	return origin{host, r.Header.Get("X-Forwarded-For")}
}

func ip(w http.ResponseWriter, r *http.Request) {
	writeJSON(w, getOrigin(r), http.StatusOK)
}

type request struct {
	Args    url.Values  `json:"args"`
	Gzipped bool        `json:"gzipped,omitempty"`
	Headers http.Header `json:"headers"`
	Origin  origin      `json:"origin"`
	URL     string      `json:"url"`
}

func rawURL(r *http.Request) string {
	var scheme string
	if r.TLS == nil {
		scheme = "http"
	} else {
		scheme = "https"
	}
	return scheme + "://" + r.Host + r.URL.String()
}

func getReq(r *http.Request) request {
	return request{
		Args:    r.URL.Query(),
		Headers: r.Header,
		Origin:  getOrigin(r),
		URL:     rawURL(r),
	}
}

func get(w http.ResponseWriter, r *http.Request) {
	if r.Method != "GET" {
		http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
		return
	}
	req := getReq(r)
	writeJSON(w, req, http.StatusOK)
}

func gzippedResponse(w http.ResponseWriter, r *http.Request) {
	req := getReq(r)
	if _, ok := w.(gzipped.GzipResponseWriter); ok {
		req.Gzipped = true
	}
	writeJSON(w, req, http.StatusOK)
}
<|endoftext|>


<commit_before>package factom

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"

	"github.com/FactomProject/btcutil/base58"
	ed "github.com/FactomProject/ed25519"
)

// An Identity is an array of names and a hierarchy of keys. It can assign/receive
// Attributes as JSON objects and rotate/replace its currently valid keys.
type Identity struct {
	ChainID string
	Name    []string
	Keys    []*IdentityKey
}

type IdentityAttribute struct {
	Key   interface{} `json:"key"`
	Value interface{} `json:"value"`
}

// GetIdentityChainID takes an identity name and returns its corresponding ChainID
func GetIdentityChainID(name []string) string {
	hs := sha256.New()
	for _, part := range name {
		h := sha256.Sum256([]byte(part))
		hs.Write(h[:])
	}
	return hex.EncodeToString(hs.Sum(nil))
}

// NewIdentityChain creates an returns a Chain struct for a new identity. Publish it to the
// blockchain using the usual factom.CommitChain(...) and factom.RevealChain(...) calls.
func NewIdentityChain(name []string, keys []string) (*Chain, error) {
	e := &Entry{}
	for _, part := range name {
		e.ExtIDs = append(e.ExtIDs, []byte(part))
	}

	var publicKeys []string
	for _, key := range keys {
		if IdentityKeyStringType(key) != IDPub {
			return nil, fmt.Errorf("provided key %s is not a valid identity public key", key)
		}
		publicKeys = append(publicKeys, key)
	}
	keysMap := map[string]interface{}{"identity-version": 1, "keys": publicKeys}
	keysJSON, _ := json.Marshal(keysMap)
	e.Content = keysJSON
	c := NewChain(e)
	return c, nil
}

// GetIdentityKeysAtCurrentHeight returns the identity's public keys that were/are valid at the highest saved block height
func GetIdentityKeysAtCurrentHeight(chainID string) ([]string, error) {
	heights, err := GetHeights()
	if err != nil {
		return nil, err
	}
	return GetIdentityKeysAtHeight(chainID, heights.DirectoryBlockHeight)
}

// GetIdentityKeysAtHeight returns the identity's public keys that were valid at the specified block height
func GetIdentityKeysAtHeight(chainID string, height int64) ([]string, error) {
	if !ChainExists(chainID) {
		return nil, fmt.Errorf("chain does not exist")
	}

	entries, err := GetAllChainEntriesAtHeight(chainID, height)
	if err != nil {
		return nil, err
	} else if len(entries) == 0 {
		return nil, fmt.Errorf("chain did not yet exist at height %d", height)
	}

	var identityInfo struct {
		Version     int      `json:"identity-version"`
		InitialKeys []string `json:"keys"`
	}
	initialKeysJSON := entries[0].Content
	err = json.Unmarshal(initialKeysJSON, &identityInfo)
	if err != nil {
		return nil, fmt.Errorf("no identity found at chain ID: %s", chainID)
	}

	var validKeys []*IdentityKey
	for _, pubString := range identityInfo.InitialKeys {
		if IdentityKeyStringType(pubString) != IDPub {
			return nil, fmt.Errorf("invalid identity public key string in first entry: %s", pubString)
		}
		pub := base58.Decode(pubString)
		k := NewIdentityKey()
		copy(k.Pub[:], pub[IDKeyPrefixLength:IDKeyBodyLength])
		validKeys = append(validKeys, k)
	}

	for _, e := range entries {
		if len(e.ExtIDs) < 5 || bytes.Compare(e.ExtIDs[0], []byte("ReplaceKey")) != 0 {
			continue
		}
		if len(e.ExtIDs[1]) != 55 || len(e.ExtIDs[2]) != 55 || len(e.ExtIDs[3]) != ed.SignatureSize {
			continue
		}

		var oldKey [ed.PublicKeySize]byte
		oldPubString := string(e.ExtIDs[1])
		if IdentityKeyStringType(oldPubString) != IDPub {
			continue
		}
		b := base58.Decode(oldPubString)
		copy(oldKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

		var newKey [ed.PublicKeySize]byte
		newPubString := string(e.ExtIDs[2])
		if IdentityKeyStringType(newPubString) != IDPub {
			continue
		}
		b = base58.Decode(newPubString)
		copy(newKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

		var signature [ed.SignatureSize]byte
		copy(signature[:], e.ExtIDs[3])
		signerPubString := string(e.ExtIDs[4])

		levelToReplace := -1
		for level, key := range validKeys {
			if bytes.Compare(oldKey[:], key.PubBytes()) == 0 {
				levelToReplace = level
			}
		}
		if levelToReplace == -1 {
			// oldkey not in the set of valid keys when this entry was published
			continue
		}

		message := []byte(oldPubString + newPubString)
		for level, key := range validKeys {
			if level > levelToReplace {
				// low priority key trying to replace high priority key, disregard
				break
			}
			if key.PubString() == signerPubString && ed.Verify(key.Pub, message, &signature) {
				validKeys[levelToReplace].Pub = &newKey
				break
			}
		}
	}

	var resp []string
	for _, k := range validKeys {
		resp = append(resp, k.PubString())
	}
	return resp, nil
}

// NewIdentityKeyReplacementEntry creates and returns a new Entry struct for the key replacement. Publish it to the
// blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityKeyReplacementEntry(chainID string, oldKey string, newKey string, signerKey *IdentityKey) (*Entry, error) {
	if IdentityKeyStringType(oldKey) != IDPub {
		return nil, fmt.Errorf("provided key %s is not a valid identity public key", oldKey)
	}
	if IdentityKeyStringType(newKey) != IDPub {
		return nil, fmt.Errorf("provided key %s is not a valid identity public key", newKey)
	}
	message := []byte(oldKey + newKey)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = chainID
	e.ExtIDs = [][]byte{
		[]byte("ReplaceKey"),
		[]byte(oldKey),
		[]byte(newKey),
		signature[:],
		[]byte(signerKey.String()),
	}
	return &e, nil
}

// NewIdentityAttributeEntry creates and returns an Entry struct that assigns an attribute JSON object to a given
// identity. Publish it to the blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityAttributeEntry(receiverChainID string, destinationChainID string, attributesJSON string, signerKey *IdentityKey, signerChainID string) *Entry {
	message := []byte(receiverChainID + destinationChainID)
	attributeHash := sha256.Sum256([]byte(attributesJSON))
	message = append(message, attributeHash[:]...)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = destinationChainID
	e.ExtIDs = [][]byte{
		[]byte("IdentityAttribute"),
		[]byte(receiverChainID),
		signature[:],
		[]byte(signerKey.String()),
		[]byte(signerChainID),
	}
	e.Content = []byte(attributesJSON)
	return &e
}

// NewIdentityAttributeEndorsementEntry creates and returns an Entry struct that agrees with or recognizes a given
// attribute. Publish it to the blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityAttributeEndorsementEntry(destinationChainID string, attributeEntryHash string, signerKey *IdentityKey, signerChainID string) *Entry {
	message := []byte(destinationChainID + attributeEntryHash)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = destinationChainID
	e.ExtIDs = [][]byte{
		[]byte("IdentityAttributeEndorsement"),
		signature[:],
		[]byte(signerKey.String()),
		[]byte(signerChainID),
	}
	e.Content = []byte(attributeEntryHash)
	return &e
}

// IsValidAttribute returns true if the entry is a properly formatted attribute with a verifiable signature.
// Note: does not check that the signer key was valid for the signer identity at the time of publishing.
func IsValidAttribute(e *Entry) bool {
	// Check ExtIDs for valid formatting, then process them
	if len(e.ExtIDs) < 5 || bytes.Compare(e.ExtIDs[0], []byte("IdentityAttribute")) != 0 {
		return false
	}
	receiverChainID := string(e.ExtIDs[1])
	signerChainID := string(e.ExtIDs[4])
	if len(receiverChainID) != 64 || len(signerChainID) != 64 {
		return false
	}
	var signature [ed.SignatureSize]byte
	copy(signature[:], e.ExtIDs[2])
	var signerKey [ed.PublicKeySize]byte
	signerPubString := string(e.ExtIDs[3])
	if IdentityKeyStringType(signerPubString) != IDPub {
		return false
	}
	b := base58.Decode(signerPubString)
	copy(signerKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

	// Message that was signed = ReceiverChainID + DestinationChainID + AttributesJSON
	msg := []byte(receiverChainID + e.ChainID)
	attributesHash := sha256.Sum256(e.Content)
	msg = append(msg, attributesHash[:]...)
	return ed.Verify(&signerKey, msg, &signature)
}

// IsValidEndorsement returns true if the Entry is a properly formatted attribute endorsement with a verifiable signature.
// Note: does not check that the signer key was valid for the signer identity at the time of publishing.
func IsValidEndorsement(e *Entry) bool {
	// Check ExtIDs for valid formatting, then process them
	if len(e.ExtIDs) < 4 || string(e.ExtIDs[0]) != "IdentityAttributeEndorsement" {
		return false
	}

	signerChainID := string(e.ExtIDs[3])
	if len(signerChainID) != 64 {
		return false
	}
	var signature [ed.SignatureSize]byte
	copy(signature[:], e.ExtIDs[1])
	var signerKey [ed.PublicKeySize]byte
	signerPubString := string(e.ExtIDs[2])
	if IdentityKeyStringType(signerPubString) != IDPub {
		return false
	}
	b := base58.Decode(signerPubString)
	copy(signerKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

	// Message that was signed = DestinationChainID + AttributeEntryHash
	msg := e.ChainID + string(e.Content)
	return ed.Verify(&signerKey, []byte(msg), &signature)
}
<commit_msg>Added IdentityChain as the first extid of each identity chain<commit_after>package factom

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"

	"github.com/FactomProject/btcutil/base58"
	ed "github.com/FactomProject/ed25519"
)

// An Identity is an array of names and a hierarchy of keys. It can assign/receive
// Attributes as JSON objects and rotate/replace its currently valid keys.
type Identity struct {
	ChainID string
	Name    []string
	Keys    []*IdentityKey
}

type IdentityAttribute struct {
	Key   interface{} `json:"key"`
	Value interface{} `json:"value"`
}

// GetIdentityChainID takes an identity name and returns its corresponding ChainID
func GetIdentityChainID(name []string) string {
	hs := sha256.New()
	for _, part := range name {
		h := sha256.Sum256([]byte(part))
		hs.Write(h[:])
	}
	return hex.EncodeToString(hs.Sum(nil))
}

// NewIdentityChain creates an returns a Chain struct for a new identity. Publish it to the
// blockchain using the usual factom.CommitChain(...) and factom.RevealChain(...) calls.
func NewIdentityChain(name []string, keys []string) (*Chain, error) {
	e := &Entry{}
	e.ExtIDs = append(e.ExtIDs, []byte("IdentityChain"))
	for _, part := range name {
		e.ExtIDs = append(e.ExtIDs, []byte(part))
	}

	var publicKeys []string
	for _, key := range keys {
		if IdentityKeyStringType(key) != IDPub {
			return nil, fmt.Errorf("provided key %s is not a valid identity public key", key)
		}
		publicKeys = append(publicKeys, key)
	}
	keysMap := map[string]interface{}{"identity-version": 1, "keys": publicKeys}
	keysJSON, _ := json.Marshal(keysMap)
	e.Content = keysJSON
	c := NewChain(e)
	return c, nil
}

// GetIdentityKeysAtCurrentHeight returns the identity's public keys that were/are valid at the highest saved block height
func GetIdentityKeysAtCurrentHeight(chainID string) ([]string, error) {
	heights, err := GetHeights()
	if err != nil {
		return nil, err
	}
	return GetIdentityKeysAtHeight(chainID, heights.DirectoryBlockHeight)
}

// GetIdentityKeysAtHeight returns the identity's public keys that were valid at the specified block height
func GetIdentityKeysAtHeight(chainID string, height int64) ([]string, error) {
	if !ChainExists(chainID) {
		return nil, fmt.Errorf("chain does not exist")
	}

	entries, err := GetAllChainEntriesAtHeight(chainID, height)
	if err != nil {
		return nil, err
	} else if len(entries) == 0 {
		return nil, fmt.Errorf("chain did not yet exist at height %d", height)
	} else if len(entries[0].ExtIDs) == 0 || bytes.Compare(entries[0].ExtIDs[0], []byte("IdentityChain")) != 0 {
		return nil, fmt.Errorf("no identity found at chain ID: %s", chainID)
	}

	var identityInfo struct {
		Version     int      `json:"identity-version"`
		InitialKeys []string `json:"keys"`
	}
	initialKeysJSON := entries[0].Content
	err = json.Unmarshal(initialKeysJSON, &identityInfo)
	if err != nil {
		return nil, fmt.Errorf("no identity found at chain ID: %s", chainID)
	}

	var validKeys []*IdentityKey
	for _, pubString := range identityInfo.InitialKeys {
		if IdentityKeyStringType(pubString) != IDPub {
			return nil, fmt.Errorf("invalid identity public key string in first entry: %s", pubString)
		}
		pub := base58.Decode(pubString)
		k := NewIdentityKey()
		copy(k.Pub[:], pub[IDKeyPrefixLength:IDKeyBodyLength])
		validKeys = append(validKeys, k)
	}

	for _, e := range entries {
		if len(e.ExtIDs) < 5 || bytes.Compare(e.ExtIDs[0], []byte("ReplaceKey")) != 0 {
			continue
		}
		if len(e.ExtIDs[1]) != 55 || len(e.ExtIDs[2]) != 55 || len(e.ExtIDs[3]) != ed.SignatureSize {
			continue
		}

		var oldKey [ed.PublicKeySize]byte
		oldPubString := string(e.ExtIDs[1])
		if IdentityKeyStringType(oldPubString) != IDPub {
			continue
		}
		b := base58.Decode(oldPubString)
		copy(oldKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

		var newKey [ed.PublicKeySize]byte
		newPubString := string(e.ExtIDs[2])
		if IdentityKeyStringType(newPubString) != IDPub {
			continue
		}
		b = base58.Decode(newPubString)
		copy(newKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

		var signature [ed.SignatureSize]byte
		copy(signature[:], e.ExtIDs[3])
		signerPubString := string(e.ExtIDs[4])

		levelToReplace := -1
		for level, key := range validKeys {
			if bytes.Compare(oldKey[:], key.PubBytes()) == 0 {
				levelToReplace = level
			}
		}
		if levelToReplace == -1 {
			// oldkey not in the set of valid keys when this entry was published
			continue
		}

		message := []byte(oldPubString + newPubString)
		for level, key := range validKeys {
			if level > levelToReplace {
				// low priority key trying to replace high priority key, disregard
				break
			}
			if key.PubString() == signerPubString && ed.Verify(key.Pub, message, &signature) {
				validKeys[levelToReplace].Pub = &newKey
				break
			}
		}
	}

	var resp []string
	for _, k := range validKeys {
		resp = append(resp, k.PubString())
	}
	return resp, nil
}

// NewIdentityKeyReplacementEntry creates and returns a new Entry struct for the key replacement. Publish it to the
// blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityKeyReplacementEntry(chainID string, oldKey string, newKey string, signerKey *IdentityKey) (*Entry, error) {
	if IdentityKeyStringType(oldKey) != IDPub {
		return nil, fmt.Errorf("provided key %s is not a valid identity public key", oldKey)
	}
	if IdentityKeyStringType(newKey) != IDPub {
		return nil, fmt.Errorf("provided key %s is not a valid identity public key", newKey)
	}
	message := []byte(oldKey + newKey)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = chainID
	e.ExtIDs = [][]byte{
		[]byte("ReplaceKey"),
		[]byte(oldKey),
		[]byte(newKey),
		signature[:],
		[]byte(signerKey.String()),
	}
	return &e, nil
}

// NewIdentityAttributeEntry creates and returns an Entry struct that assigns an attribute JSON object to a given
// identity. Publish it to the blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityAttributeEntry(receiverChainID string, destinationChainID string, attributesJSON string, signerKey *IdentityKey, signerChainID string) *Entry {
	message := []byte(receiverChainID + destinationChainID)
	attributeHash := sha256.Sum256([]byte(attributesJSON))
	message = append(message, attributeHash[:]...)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = destinationChainID
	e.ExtIDs = [][]byte{
		[]byte("IdentityAttribute"),
		[]byte(receiverChainID),
		signature[:],
		[]byte(signerKey.String()),
		[]byte(signerChainID),
	}
	e.Content = []byte(attributesJSON)
	return &e
}

// NewIdentityAttributeEndorsementEntry creates and returns an Entry struct that agrees with or recognizes a given
// attribute. Publish it to the blockchain using the usual factom.CommitEntry(...) and factom.RevealEntry(...) calls.
func NewIdentityAttributeEndorsementEntry(destinationChainID string, attributeEntryHash string, signerKey *IdentityKey, signerChainID string) *Entry {
	message := []byte(destinationChainID + attributeEntryHash)
	signature := signerKey.Sign(message)

	e := Entry{}
	e.ChainID = destinationChainID
	e.ExtIDs = [][]byte{
		[]byte("IdentityAttributeEndorsement"),
		signature[:],
		[]byte(signerKey.String()),
		[]byte(signerChainID),
	}
	e.Content = []byte(attributeEntryHash)
	return &e
}

// IsValidAttribute returns true if the entry is a properly formatted attribute with a verifiable signature.
// Note: does not check that the signer key was valid for the signer identity at the time of publishing.
func IsValidAttribute(e *Entry) bool {
	// Check ExtIDs for valid formatting, then process them
	if len(e.ExtIDs) < 5 || bytes.Compare(e.ExtIDs[0], []byte("IdentityAttribute")) != 0 {
		return false
	}
	receiverChainID := string(e.ExtIDs[1])
	signerChainID := string(e.ExtIDs[4])
	if len(receiverChainID) != 64 || len(signerChainID) != 64 {
		return false
	}
	var signature [ed.SignatureSize]byte
	copy(signature[:], e.ExtIDs[2])
	var signerKey [ed.PublicKeySize]byte
	signerPubString := string(e.ExtIDs[3])
	if IdentityKeyStringType(signerPubString) != IDPub {
		return false
	}
	b := base58.Decode(signerPubString)
	copy(signerKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

	// Message that was signed = ReceiverChainID + DestinationChainID + AttributesJSON
	msg := []byte(receiverChainID + e.ChainID)
	attributesHash := sha256.Sum256(e.Content)
	msg = append(msg, attributesHash[:]...)
	return ed.Verify(&signerKey, msg, &signature)
}

// IsValidEndorsement returns true if the Entry is a properly formatted attribute endorsement with a verifiable signature.
// Note: does not check that the signer key was valid for the signer identity at the time of publishing.
func IsValidEndorsement(e *Entry) bool {
	// Check ExtIDs for valid formatting, then process them
	if len(e.ExtIDs) < 4 || string(e.ExtIDs[0]) != "IdentityAttributeEndorsement" {
		return false
	}

	signerChainID := string(e.ExtIDs[3])
	if len(signerChainID) != 64 {
		return false
	}
	var signature [ed.SignatureSize]byte
	copy(signature[:], e.ExtIDs[1])
	var signerKey [ed.PublicKeySize]byte
	signerPubString := string(e.ExtIDs[2])
	if IdentityKeyStringType(signerPubString) != IDPub {
		return false
	}
	b := base58.Decode(signerPubString)
	copy(signerKey[:], b[IDKeyPrefixLength:IDKeyBodyLength])

	// Message that was signed = DestinationChainID + AttributeEntryHash
	msg := e.ChainID + string(e.Content)
	return ed.Verify(&signerKey, []byte(msg), &signature)
}
<|endoftext|>


<commit_before>package resources

import (
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/rebuy-de/aws-nuke/pkg/types"
)

func init() {
	register("CloudFormationStack", ListCloudFormationStacks)
}

func ListCloudFormationStacks(sess *session.Session) ([]Resource, error) {
	svc := cloudformation.New(sess)

	params := &cloudformation.DescribeStacksInput{}
	resources := make([]Resource, 0)

	for {
		resp, err := svc.DescribeStacks(params)
		if err != nil {
			return nil, err
		}
		for _, stack := range resp.Stacks {
			resources = append(resources, &CloudFormationStack{
				svc:   svc,
				stack: stack,
			})
		}

		if resp.NextToken == nil {
			break
		}

		params.NextToken = resp.NextToken
	}

	return resources, nil
}

type CloudFormationStack struct {
	svc   *cloudformation.CloudFormation
	stack *cloudformation.Stack
}

func (cfs *CloudFormationStack) Remove() error {
	retainableResources, err := cfs.svc.ListStackResources(&cloudformation.ListStackResourcesInput{
		StackName: cfs.stack.StackName,
	})

	retain := make([]*string, 0)
	for _, r := range retainableResources.StackResourceSummaries {
		retain = append(retain, r.LogicalResourceId)
	}

	cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{
		StackName:       cfs.stack.StackName,
		RetainResources: retain,
	})

	return err
}

func (cfs *CloudFormationStack) Properties() types.Properties {
	properties := types.NewProperties()
	properties.Set("Name", cfs.stack.StackName)
	for _, tagValue := range cfs.stack.Tags {
		properties.SetTag(tagValue.Key, tagValue.Value)
	}

	return properties
}

func (cfs *CloudFormationStack) String() string {
	return *cfs.stack.StackName
}
<commit_msg>Revert "Retain all CloudFormation resources in Deletion of CloudFormationStack"<commit_after>package resources

import (
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/rebuy-de/aws-nuke/pkg/types"
)

func init() {
	register("CloudFormationStack", ListCloudFormationStacks)
}

func ListCloudFormationStacks(sess *session.Session) ([]Resource, error) {
	svc := cloudformation.New(sess)

	params := &cloudformation.DescribeStacksInput{}
	resources := make([]Resource, 0)

	for {
		resp, err := svc.DescribeStacks(params)
		if err != nil {
			return nil, err
		}
		for _, stack := range resp.Stacks {
			resources = append(resources, &CloudFormationStack{
				svc:   svc,
				stack: stack,
			})
		}

		if resp.NextToken == nil {
			break
		}

		params.NextToken = resp.NextToken
	}

	return resources, nil
}

type CloudFormationStack struct {
	svc   *cloudformation.CloudFormation
	stack *cloudformation.Stack
}

func (cfs *CloudFormationStack) Remove() error {
	_, err := cfs.svc.DeleteStack(&cloudformation.DeleteStackInput{
		StackName: cfs.stack.StackName,
	})
	return err
}

func (cfs *CloudFormationStack) Properties() types.Properties {
	properties := types.NewProperties()
	properties.Set("Name", cfs.stack.StackName)
	for _, tagValue := range cfs.stack.Tags {
		properties.SetTag(tagValue.Key, tagValue.Value)
	}

	return properties
}

func (cfs *CloudFormationStack) String() string {
	return *cfs.stack.StackName
}
<|endoftext|>