<commit_before>package main

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/broersa/lora"
	"github.com/broersa/semtech"
	"github.com/broersa/ttnbroker/bll"
	"github.com/broersa/ttnbroker/bllimpl"
	"github.com/broersa/ttnbroker/dalpsql"
	"github.com/gorilla/mux"

	// Database driver

	_ "github.com/lib/pq"
)

var b bll.Bll

func checkerror(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	log.Print("TTNBroker is ALIVE")
	c := os.Getenv("TTNBROKER_DB")
	//s, err := sql.Open("postgres", "postgres://user:password@server/ttn?sslmode=require")
	s, err := sql.Open("postgres", c)
	checkerror(err)
	d := dalpsql.New(s)
	/*d.BeginTransaction()
	var i int64
	i, err = d.AddApplication(&dal.Application{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(i)
	d.CommitTransaction()*/
	b = bllimpl.New(d)
	b.RegisterApplication(&bll.Application{})
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", Index)
	router.HandleFunc("/RegisterApplication", RegisterApplication).Methods("POST")
	router.HandleFunc("/HasApplication/{name}", HasApplication).Methods("GET")
	router.HandleFunc("/Message", MessageHandler).Methods("POST")
	//log.Fatal(http.ListenAndServeTLS(":4443", "server.pem", "server.key", router))
	log.Fatal(http.ListenAndServe(":4443", router))
}

// Index ...
func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, Andre")
}

// RegisterApplication ...
func RegisterApplication(w http.ResponseWriter, r *http.Request) {
	log.Println(r.Body)
}

// HasApplication ...
func HasApplication(w http.ResponseWriter, r *http.Request) {
	//vars := mux.Vars(r)
	//appeui := vars["appeui"]
	log.Println("OK")
	fmt.Fprintf(w, "OK")
}

// MessageHandler ...
func MessageHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	contents, err := ioutil.ReadAll(r.Body)
	checkerror(err)
	var message Message
	err = json.Unmarshal(contents, &message)
	checkerror(err)
	data, err := base64.StdEncoding.DecodeString(message.Package.Data)
	checkerror(err)
	mhdr, err := lora.NewMHDRFromByte(data[0])
	checkerror(err)
	if mhdr.IsJoinRequest() {
		appkey := []byte{0x15, 0x4f, 0x94, 0x7b, 0x41, 0xd0, 0x2f, 0x33, 0x96, 0xf9, 0xaf, 0x6b, 0x4d, 0xb1, 0x0d, 0x5f}
		_, err := lora.NewJoinRequestValidated(appkey, data)
		if err != nil {
			if _, ok := err.(*lora.ErrorMICValidationFailed); ok {
				log.Print(err)
				w.Write([]byte("{error: \"" + err.Error() + "\"}"))
			} else {
				checkerror(err)
			}
		} else {
			joinaccept, err := lora.NewJoinAccept(appkey, 0)
			checkerror(err)
			ja, err := joinaccept.Marshal(appkey)
			checkerror(err)
			responsemessage := &ResponseMessage{
				OriginUDPAddrNetwork: message.OriginUDPAddrNetwork,
				OriginUDPAddrString:  message.OriginUDPAddrString,
				Package: semtech.TXPK{
					Tmst: message.Package.Tmst + 5000000,
					Freq: message.Package.Freq,
					RFCh: message.Package.RFCh,
					Powe: 14,
					Modu: message.Package.Modu,
					DatR: message.Package.DatR,
					CodR: message.Package.CodR,
					IPol: true,
					Size: uint16(len(ja) - 4),
					Data: base64.StdEncoding.EncodeToString(ja)}}
			msg, err := json.Marshal(responsemessage)
			checkerror(err)
			w.Write(msg)
		}
	}
}
<commit_msg>rename to mybroker<commit_after>package main

import (
	"database/sql"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"

	"github.com/broersa/lora"
	"github.com/broersa/semtech"
	"github.com/broersa/ttnbroker/bll"
	"github.com/broersa/ttnbroker/bllimpl"
	"github.com/broersa/ttnbroker/dalpsql"
	"github.com/gorilla/mux"

	// Database driver

	_ "github.com/lib/pq"
)

var b bll.Bll

func checkerror(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	log.Print("MYBroker is ALIVE")
	c := os.Getenv("MYBROKER_DB")
	//s, err := sql.Open("postgres", "postgres://user:password@server/my?sslmode=require")
	s, err := sql.Open("postgres", c)
	checkerror(err)
	d := dalpsql.New(s)
	/*d.BeginTransaction()
	var i int64
	i, err = d.AddApplication(&dal.Application{})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(i)
	d.CommitTransaction()*/
	b = bllimpl.New(d)
	b.RegisterApplication(&bll.Application{})
	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", Index)
	router.HandleFunc("/RegisterApplication", RegisterApplication).Methods("POST")
	router.HandleFunc("/HasApplication/{name}", HasApplication).Methods("GET")
	router.HandleFunc("/Message", MessageHandler).Methods("POST")
	//log.Fatal(http.ListenAndServeTLS(":4443", "server.pem", "server.key", router))
	log.Fatal(http.ListenAndServe(":4443", router))
}

// Index ...
func Index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, Andre")
}

// RegisterApplication ...
func RegisterApplication(w http.ResponseWriter, r *http.Request) {
	log.Println(r.Body)
}

// HasApplication ...
func HasApplication(w http.ResponseWriter, r *http.Request) {
	//vars := mux.Vars(r)
	//appeui := vars["appeui"]
	log.Println("OK")
	fmt.Fprintf(w, "OK")
}

// MessageHandler ...
func MessageHandler(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()
	contents, err := ioutil.ReadAll(r.Body)
	checkerror(err)
	var message Message
	err = json.Unmarshal(contents, &message)
	checkerror(err)
	data, err := base64.StdEncoding.DecodeString(message.Package.Data)
	checkerror(err)
	mhdr, err := lora.NewMHDRFromByte(data[0])
	checkerror(err)
	if mhdr.IsJoinRequest() {
		appkey := []byte{0x15, 0x4f, 0x94, 0x7b, 0x41, 0xd0, 0x2f, 0x33, 0x96, 0xf9, 0xaf, 0x6b, 0x4d, 0xb1, 0x0d, 0x5f}
		_, err := lora.NewJoinRequestValidated(appkey, data)
		if err != nil {
			if _, ok := err.(*lora.ErrorMICValidationFailed); ok {
				log.Print(err)
				w.Write([]byte("{error: \"" + err.Error() + "\"}"))
			} else {
				checkerror(err)
			}
		} else {
			joinaccept, err := lora.NewJoinAccept(appkey, 0)
			checkerror(err)
			ja, err := joinaccept.Marshal(appkey)
			checkerror(err)
			responsemessage := &ResponseMessage{
				OriginUDPAddrNetwork: message.OriginUDPAddrNetwork,
				OriginUDPAddrString:  message.OriginUDPAddrString,
				Package: semtech.TXPK{
					Tmst: message.Package.Tmst + 5000000,
					Freq: message.Package.Freq,
					RFCh: message.Package.RFCh,
					Powe: 14,
					Modu: message.Package.Modu,
					DatR: message.Package.DatR,
					CodR: message.Package.CodR,
					IPol: true,
					Size: uint16(len(ja) - 4),
					Data: base64.StdEncoding.EncodeToString(ja)}}
			msg, err := json.Marshal(responsemessage)
			checkerror(err)
			w.Write(msg)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"

	"github.com/maniktaneja/perf/fetch"
	_ "net/http/pprof"
)

// send down a single random document
func randDocument(w http.ResponseWriter, r *http.Request) {

	// generate a random number
	docId := rand.Intn(docs.number)
	item := docs.docMap[docs.docList[docId]]

	// serialize the item before sending it out
	bytes, err := json.Marshal(item)
	if err != nil {
		fmt.Fprintf(w, "Failed to marshal document %v", err)
		return
	}
	fmt.Fprintf(w, string(bytes)) // send data to client side
}

// send a million documents
func million(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		item := docs.docMap[docs.docList[docId]]
		bytes, err := json.Marshal(item)
		if err != nil {
			fmt.Fprintf(w, "Failed to marshal document %v", err)
			return
		}
		fmt.Fprintf(w, string(bytes)) // send data to client side
		fmt.Fprintf(w, "\n\n")
	}
}

// send a million string documents
func millionstr(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		bytes := docs.docMapStr[docs.docList[docId]]
		fmt.Fprintf(w, bytes) // send data to client side
		fmt.Fprintf(w, "\n\n")
	}
}

var server = flag.String("server", "http://localhost:9000",
	"couchbase server URL")
var bucket = flag.String("bucket", "beer-sample", "bucket name")

type documents struct {
	docMap    map[string]interface{}
	docMapStr map[string]string
	docList   []string
	number    int
}

var docs *documents

func main() {

	flag.Parse()

	http.HandleFunc("/random", randDocument) // set router
	http.HandleFunc("/million", million)
	http.HandleFunc("/millionstr", millionstr)

	docMap := fetch.FetchDocs(*server, *bucket)
	if len(docMap) == 0 {
		log.Fatalf("Failed to fetch documents")
	}

	docs = &documents{docMap: docMap,
		docList:   make([]string, 0, len(docMap)),
		number:    len(docMap),
		docMapStr: make(map[string]string)}

	for dName, value := range docs.docMap {
		docs.docList = append(docs.docList, dName)
		item, _ := json.Marshal(value)
		docs.docMapStr[dName] = string(item)
	}

	err := http.ListenAndServe(":9090", nil) // set listen port
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>use buffer pool<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"runtime"

	"github.com/maniktaneja/perf/fetch"
	"github.com/oxtoacart/bpool"
	"github.com/pquerna/ffjson/ffjson"
	_ "net/http/pprof"
)

// send down a single random document
func randDocument(w http.ResponseWriter, r *http.Request) {

	// generate a random number
	docId := rand.Intn(docs.number)
	item := docs.docMap[docs.docList[docId]]

	// serialize the item before sending it out
	bytes, err := json.Marshal(item)
	if err != nil {
		fmt.Fprintf(w, "Failed to marshal document %v", err)
		return
	}
	fmt.Fprintf(w, string(bytes)) // send data to client side
}

// send a million documents
func million(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		item := docs.docMap[docs.docList[docId]]
		bytes, err := json.Marshal(item)
		if err != nil {
			fmt.Fprintf(w, "Failed to marshal document %v", err)
			return
		}
		fmt.Fprintf(w, string(bytes)) // send data to client side
		fmt.Fprintf(w, "\n\n")
	}
}

// send a million documents
func bufmillion(w http.ResponseWriter, r *http.Request) {

	bufpool := bpool.NewBufferPool(1024)

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		item := docs.docMap[docs.docList[docId]]

		pw := bufpool.Get()
		err := json.NewEncoder(pw).Encode(&item)
		if err != nil {
			log.Fatalf("Error %v", err)
		}

		fmt.Fprintf(w, string(pw.Bytes())) // send data to client side
		bufpool.Put(pw)
		fmt.Fprintf(w, "\n\n")
	}
}

// send a million documents
func ffmillion(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		item := docs.docMap[docs.docList[docId]]
		bytes, err := ffjson.Marshal(item)
		if err != nil {
			fmt.Fprintf(w, "Failed to marshal document %v", err)
			return
		}
		fmt.Fprintf(w, string(bytes)) // send data to client side
		fmt.Fprintf(w, "\n\n")
		ffjson.Pool(bytes)
	}
}

// send a million string documents
func millionstr(w http.ResponseWriter, r *http.Request) {

	for i := 0; i < 10000000; i++ {
		docId := i % docs.number
		bytes := docs.docMapStr[docs.docList[docId]]
		fmt.Fprintf(w, bytes) // send data to client side
		fmt.Fprintf(w, "\n\n")
	}
}

var server = flag.String("server", "http://localhost:9000",
	"couchbase server URL")
var bucket = flag.String("bucket", "beer-sample", "bucket name")

type documents struct {
	docMap    map[string]interface{}
	docMapStr map[string]string
	docList   []string
	number    int
}

var docs *documents

func main() {

	flag.Parse()

	http.HandleFunc("/random", randDocument) // set router
	http.HandleFunc("/million", million)
	http.HandleFunc("/millionstr", millionstr)
	http.HandleFunc("/ffmillion", ffmillion)
	http.HandleFunc("/bufmillion", bufmillion)

	runtime.GOMAXPROCS(runtime.NumCPU())
	docMap := fetch.FetchDocs(*server, *bucket)
	if len(docMap) == 0 {
		log.Fatalf("Failed to fetch documents")
	}

	docs = &documents{docMap: docMap,
		docList:   make([]string, 0, len(docMap)),
		number:    len(docMap),
		docMapStr: make(map[string]string)}

	for dName, value := range docs.docMap {
		docs.docList = append(docs.docList, dName)
		item, _ := json.Marshal(value)
		docs.docMapStr[dName] = string(item)
	}

	err := http.ListenAndServe(":9090", nil) // set listen port
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package mysql

import (
	"database/sql"
	"fmt"
	"strings"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/migrator"
	"gorm.io/gorm/schema"
)

type Migrator struct {
	migrator.Migrator
	Dialector
}

func (m Migrator) FullDataTypeOf(field *schema.Field) clause.Expr {
	expr := m.Migrator.FullDataTypeOf(field)

	if value, ok := field.TagSettings["COMMENT"]; ok {
		expr.SQL += " COMMENT " + m.Dialector.Explain("?", value)
	}

	return expr
}

func (m Migrator) AlterColumn(value interface{}, field string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		if field := stmt.Schema.LookUpField(field); field != nil {
			return m.DB.Exec(
				"ALTER TABLE ? MODIFY COLUMN ? ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: field.DBName}, m.FullDataTypeOf(field),
			).Error
		}
		return fmt.Errorf("failed to look up field with name: %s", field)
	})
}

func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		if !m.Dialector.DontSupportRenameColumn {
			return m.Migrator.RenameColumn(value, oldName, newName)
		}

		var field *schema.Field
		if f := stmt.Schema.LookUpField(oldName); f != nil {
			oldName = f.DBName
			field = f
		}

		if f := stmt.Schema.LookUpField(newName); f != nil {
			newName = f.DBName
			field = f
		}

		if field != nil {
			return m.DB.Exec(
				"ALTER TABLE ? CHANGE ? ? ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: oldName},
				clause.Column{Name: newName}, m.FullDataTypeOf(field),
			).Error
		}

		return fmt.Errorf("failed to look up field with name: %s", newName)
	})
}

func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
	if !m.Dialector.DontSupportRenameIndex {
		return m.RunWithValue(value, func(stmt *gorm.Statement) error {
			return m.DB.Exec(
				"ALTER TABLE ? RENAME INDEX ? TO ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: oldName}, clause.Column{Name: newName},
			).Error
		})
	}

	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		err := m.DropIndex(value, oldName)
		if err != nil {
			return err
		}

		if idx := stmt.Schema.LookIndex(newName); idx == nil {
			if idx = stmt.Schema.LookIndex(oldName); idx != nil {
				opts := m.BuildIndexOptions(idx.Fields, stmt)
				values := []interface{}{clause.Column{Name: newName}, clause.Table{Name: stmt.Table}, opts}

				createIndexSQL := "CREATE "
				if idx.Class != "" {
					createIndexSQL += idx.Class + " "
				}
				createIndexSQL += "INDEX ? ON ??"

				if idx.Type != "" {
					createIndexSQL += " USING " + idx.Type
				}

				return m.DB.Exec(createIndexSQL, values...).Error
			}
		}

		return m.CreateIndex(value, newName)
	})

}

func (m Migrator) DropTable(values ...interface{}) error {
	values = m.ReorderModels(values, false)
	return m.DB.Connection(func(tx *gorm.DB) error {
		tx.Exec("SET FOREIGN_KEY_CHECKS = 0;")
		for i := len(values) - 1; i >= 0; i-- {
			if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
				return tx.Exec("DROP TABLE IF EXISTS ? CASCADE", clause.Table{Name: stmt.Table}).Error
			}); err != nil {
				return err
			}
		}
		return tx.Exec("SET FOREIGN_KEY_CHECKS = 1;").Error
	})
}

func (m Migrator) DropConstraint(value interface{}, name string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
		if chk != nil {
			return m.DB.Exec("ALTER TABLE ? DROP CHECK ?", clause.Table{Name: stmt.Table}, clause.Column{Name: chk.Name}).Error
		}
		if constraint != nil {
			name = constraint.Name
		}

		return m.DB.Exec(
			"ALTER TABLE ? DROP FOREIGN KEY ?", clause.Table{Name: table}, clause.Column{Name: name},
		).Error
	})
}

// ColumnTypes column types return columnTypes,error
func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
	columnTypes := make([]gorm.ColumnType, 0)
	err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
		var (
			currentDatabase = m.DB.Migrator().CurrentDatabase()
			columnTypeSQL   = "SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale "
			rows, err       = m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
		)

		if err != nil {
			return err
		}

		defer func() {
			err = rows.Close()
		}()

		rawColumnTypes, err := rows.ColumnTypes()

		if !m.DisableDatetimePrecision {
			columnTypeSQL += ", datetime_precision "
		}
		columnTypeSQL += "FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ORDINAL_POSITION"

		columns, rowErr := m.DB.Raw(columnTypeSQL, currentDatabase, stmt.Table).Rows()
		if rowErr != nil {
			return rowErr
		}

		defer columns.Close()

		for columns.Next() {
			var (
				column            migrator.ColumnType
				datetimePrecision sql.NullInt64
				extraValue        sql.NullString
				columnKey         sql.NullString
				values            = []interface{}{
					&column.NameValue, &column.DefaultValueValue, &column.NullableValue, &column.DataTypeValue, &column.LengthValue, &column.ColumnTypeValue, &columnKey, &extraValue, &column.CommentValue, &column.DecimalSizeValue, &column.ScaleValue,
				}
			)

			if !m.DisableDatetimePrecision {
				values = append(values, &datetimePrecision)
			}

			if scanErr := columns.Scan(values...); scanErr != nil {
				return scanErr
			}

			column.PrimaryKeyValue = sql.NullBool{Bool: false, Valid: true}
			column.UniqueValue = sql.NullBool{Bool: false, Valid: true}
			switch columnKey.String {
			case "PRI":
				column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
			case "UNI":
				column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
			}

			if strings.Contains(extraValue.String, "auto_increment") {
				column.AutoIncrementValue = sql.NullBool{Bool: true, Valid: true}
			}

			if datetimePrecision.Valid {
				column.DecimalSizeValue = datetimePrecision
			}

			for _, c := range rawColumnTypes {
				if c.Name() == column.NameValue.String {
					column.SQLColumnType = c
					break
				}
			}

			columnTypes = append(columnTypes, column)
		}

		return nil
	})

	return columnTypes, err
}

func (m Migrator) CurrentDatabase() (name string) {
	baseName := m.Migrator.CurrentDatabase()
	m.DB.Raw(
		"SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE ? ORDER BY SCHEMA_NAME=? DESC limit 1",
		baseName+"%", baseName).Scan(&name)
	return
}

func (m Migrator) GetTables() (tableList []string, err error) {
	err = m.DB.Raw("SELECT TABLE_NAME FROM information_schema.tables where TABLE_SCHEMA=?", m.CurrentDatabase()).
		Scan(&tableList).Error
	return
}
<commit_msg>trim default value to fix mariadb<commit_after>package mysql

import (
	"database/sql"
	"fmt"
	"strings"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/migrator"
	"gorm.io/gorm/schema"
)

type Migrator struct {
	migrator.Migrator
	Dialector
}

func (m Migrator) FullDataTypeOf(field *schema.Field) clause.Expr {
	expr := m.Migrator.FullDataTypeOf(field)

	if value, ok := field.TagSettings["COMMENT"]; ok {
		expr.SQL += " COMMENT " + m.Dialector.Explain("?", value)
	}

	return expr
}

func (m Migrator) AlterColumn(value interface{}, field string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		if field := stmt.Schema.LookUpField(field); field != nil {
			return m.DB.Exec(
				"ALTER TABLE ? MODIFY COLUMN ? ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: field.DBName}, m.FullDataTypeOf(field),
			).Error
		}
		return fmt.Errorf("failed to look up field with name: %s", field)
	})
}

func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		if !m.Dialector.DontSupportRenameColumn {
			return m.Migrator.RenameColumn(value, oldName, newName)
		}

		var field *schema.Field
		if f := stmt.Schema.LookUpField(oldName); f != nil {
			oldName = f.DBName
			field = f
		}

		if f := stmt.Schema.LookUpField(newName); f != nil {
			newName = f.DBName
			field = f
		}

		if field != nil {
			return m.DB.Exec(
				"ALTER TABLE ? CHANGE ? ? ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: oldName},
				clause.Column{Name: newName}, m.FullDataTypeOf(field),
			).Error
		}

		return fmt.Errorf("failed to look up field with name: %s", newName)
	})
}

func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error {
	if !m.Dialector.DontSupportRenameIndex {
		return m.RunWithValue(value, func(stmt *gorm.Statement) error {
			return m.DB.Exec(
				"ALTER TABLE ? RENAME INDEX ? TO ?",
				clause.Table{Name: stmt.Table}, clause.Column{Name: oldName}, clause.Column{Name: newName},
			).Error
		})
	}

	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		err := m.DropIndex(value, oldName)
		if err != nil {
			return err
		}

		if idx := stmt.Schema.LookIndex(newName); idx == nil {
			if idx = stmt.Schema.LookIndex(oldName); idx != nil {
				opts := m.BuildIndexOptions(idx.Fields, stmt)
				values := []interface{}{clause.Column{Name: newName}, clause.Table{Name: stmt.Table}, opts}

				createIndexSQL := "CREATE "
				if idx.Class != "" {
					createIndexSQL += idx.Class + " "
				}
				createIndexSQL += "INDEX ? ON ??"

				if idx.Type != "" {
					createIndexSQL += " USING " + idx.Type
				}

				return m.DB.Exec(createIndexSQL, values...).Error
			}
		}

		return m.CreateIndex(value, newName)
	})

}

func (m Migrator) DropTable(values ...interface{}) error {
	values = m.ReorderModels(values, false)
	return m.DB.Connection(func(tx *gorm.DB) error {
		tx.Exec("SET FOREIGN_KEY_CHECKS = 0;")
		for i := len(values) - 1; i >= 0; i-- {
			if err := m.RunWithValue(values[i], func(stmt *gorm.Statement) error {
				return tx.Exec("DROP TABLE IF EXISTS ? CASCADE", clause.Table{Name: stmt.Table}).Error
			}); err != nil {
				return err
			}
		}
		return tx.Exec("SET FOREIGN_KEY_CHECKS = 1;").Error
	})
}

func (m Migrator) DropConstraint(value interface{}, name string) error {
	return m.RunWithValue(value, func(stmt *gorm.Statement) error {
		constraint, chk, table := m.GuessConstraintAndTable(stmt, name)
		if chk != nil {
			return m.DB.Exec("ALTER TABLE ? DROP CHECK ?", clause.Table{Name: stmt.Table}, clause.Column{Name: chk.Name}).Error
		}
		if constraint != nil {
			name = constraint.Name
		}

		return m.DB.Exec(
			"ALTER TABLE ? DROP FOREIGN KEY ?", clause.Table{Name: table}, clause.Column{Name: name},
		).Error
	})
}

// ColumnTypes column types return columnTypes,error
func (m Migrator) ColumnTypes(value interface{}) ([]gorm.ColumnType, error) {
	columnTypes := make([]gorm.ColumnType, 0)
	err := m.RunWithValue(value, func(stmt *gorm.Statement) error {
		var (
			currentDatabase = m.DB.Migrator().CurrentDatabase()
			columnTypeSQL   = "SELECT column_name, column_default, is_nullable = 'YES', data_type, character_maximum_length, column_type, column_key, extra, column_comment, numeric_precision, numeric_scale "
			rows, err       = m.DB.Session(&gorm.Session{}).Table(stmt.Table).Limit(1).Rows()
		)

		if err != nil {
			return err
		}

		defer func() {
			err = rows.Close()
		}()

		rawColumnTypes, err := rows.ColumnTypes()

		if !m.DisableDatetimePrecision {
			columnTypeSQL += ", datetime_precision "
		}
		columnTypeSQL += "FROM information_schema.columns WHERE table_schema = ? AND table_name = ? ORDER BY ORDINAL_POSITION"

		columns, rowErr := m.DB.Raw(columnTypeSQL, currentDatabase, stmt.Table).Rows()
		if rowErr != nil {
			return rowErr
		}

		defer columns.Close()

		for columns.Next() {
			var (
				column            migrator.ColumnType
				datetimePrecision sql.NullInt64
				extraValue        sql.NullString
				columnKey         sql.NullString
				values            = []interface{}{
					&column.NameValue, &column.DefaultValueValue, &column.NullableValue, &column.DataTypeValue, &column.LengthValue, &column.ColumnTypeValue, &columnKey, &extraValue, &column.CommentValue, &column.DecimalSizeValue, &column.ScaleValue,
				}
			)

			if !m.DisableDatetimePrecision {
				values = append(values, &datetimePrecision)
			}

			if scanErr := columns.Scan(values...); scanErr != nil {
				return scanErr
			}

			column.PrimaryKeyValue = sql.NullBool{Bool: false, Valid: true}
			column.UniqueValue = sql.NullBool{Bool: false, Valid: true}
			switch columnKey.String {
			case "PRI":
				column.PrimaryKeyValue = sql.NullBool{Bool: true, Valid: true}
			case "UNI":
				column.UniqueValue = sql.NullBool{Bool: true, Valid: true}
			}

			if strings.Contains(extraValue.String, "auto_increment") {
				column.AutoIncrementValue = sql.NullBool{Bool: true, Valid: true}
			}

			column.DefaultValueValue.String = strings.Trim(column.DefaultValueValue.String, "'")

			if datetimePrecision.Valid {
				column.DecimalSizeValue = datetimePrecision
			}

			for _, c := range rawColumnTypes {
				if c.Name() == column.NameValue.String {
					column.SQLColumnType = c
					break
				}
			}

			columnTypes = append(columnTypes, column)
		}

		return nil
	})

	return columnTypes, err
}

func (m Migrator) CurrentDatabase() (name string) {
	baseName := m.Migrator.CurrentDatabase()
	m.DB.Raw(
		"SELECT SCHEMA_NAME from Information_schema.SCHEMATA where SCHEMA_NAME LIKE ? ORDER BY SCHEMA_NAME=? DESC limit 1",
		baseName+"%", baseName).Scan(&name)
	return
}

func (m Migrator) GetTables() (tableList []string, err error) {
	err = m.DB.Raw("SELECT TABLE_NAME FROM information_schema.tables where TABLE_SCHEMA=?", m.CurrentDatabase()).
		Scan(&tableList).Error
	return
}
<|endoftext|>


<commit_before>// Author: andre@freshest.me
// Date: 23.04.2015
// Version: 1

// configuration structs and global const.
package config

// Api configuration struct
type Config struct {
	Service struct {
		Username string
		Password string
		Listen   string
	}
}

// struct to login to api
type SignIn struct {
	DomainID    int
	SiteGuid    string
	LoginStatus int
	UserData    interface{}
}

// channellist
type ChannelList []struct {
	MediaID               interface{}
	MediaName             string
	MediaTypeID           interface{}
	MediaTypeName         interface{}
	Rating                interface{}
	ViewCounter           interface{}
	Description           interface{}
	CreationDate          interface{}
	LastWatchDate         interface{}
	StartDate             interface{}
	CatalogStartDate      interface{}
	PicURL                interface{}
	URL                   interface{}
	MediaWebLink          interface{}
	Duration              interface{}
	FileID                interface{}
	MediaDynamicData      interface{}
	SubDuration           interface{}
	SubFileFormat         interface{}
	SubFileID             interface{}
	SubURL                interface{}
	GeoBlock              interface{}
	TotalItems            interface{}
	like_counter          interface{}
	Tags                  interface{}
	AdvertisingParameters interface{}
	Files                 []struct {
		FileID          string
		URL             string
		Duration        string
		Format          string
		PreProvider     string
		PostProvider    string
		BreakProvider   string
		OverlayProvider string
		BreakPoints     string
		OverlayPoints   string
		Language        string
		IsDefaultLang   bool
		CoGuid          string
	}
	Pictures    interface{}
	ExternalIDs interface{}
}

// response of a licensed link
type LicensedLink struct {
	MainUrl string
	AltUrl  string
}

// global const to be used in code
const (
	GATEWAY              = "https://api-live.iptv.kabel-deutschland.de/v2_9/gateways/jsonpostgw.aspx"
	IOS_VERSION          = "8.1.2"
	APP_VERSION          = "1.1.5"
	METHOD_SIGNIN        = "SSOSignIn"
	METHOD_CHANNELLIST   = "GetChannelMediaList"
	METHOD_LICENSED_LINK = "GetLicensedLinks"
	INIT_OBJECT          = "eyJBcGlVc2VyIjoidHZwYXBpXzE4MSIsIlVESUQiOiJEMkFDNjMzQUZCNjQ0Q0YwQTY3NTA1MzcwNTc4Q0RFNSIsIkRvbWFpbklEIjozMTUzODQsIlNpdGVHdWlkIjo2Nzk4NzAsIlBsYXRmb3JtIjoiaVBhZCIsIkFwaVBhc3MiOiJhek5ETHpzblRCVHRBclZXMlNIUiIsIkxvY2FsZSI6eyJMb2NhbGVEZXZpY2UiOiJudWxsIiwiTG9jYWxlVXNlclN0YXRlIjoiVW5rbm93biIsIkxvY2FsZUNvdW50cnkiOiJudWxsIiwiTG9jYWxlTGFuZ3VhZ2UiOiJudWxsIn19"
	CHANNEL_OBJECT       = "\"orderBy\":\"None\",\"pageSize\":1000,\"picSize\":\"100X100\",\"ChannelID\":340758"
	M3U_HEAD             = "#EXTM3U\n"
	M3U_LINE             = "#EXTINF:-1,%s\n%s\n"

	QUALITY_LOW    = "CCURstream564000.m3u8"
	QUALITY_MEDIUM = "CCURstream1064000.m3u8"
	QUALITY_HIGH   = "CCURstream1664000.m3u8"

	CACHE_FILE     = "playlist_%s.m3u"
	CACHE_LIFETIME = 86400
)
<commit_msg>split Channel and ChannelList<commit_after>// Author: andre@freshest.me
// Date: 23.04.2015
// Version: 1

// configuration structs and global const.
package config

// Api configuration struct
type Config struct {
	Service struct {
		Username string
		Password string
		Listen   string
	}
}

// struct to login to api
type SignIn struct {
	DomainID    int
	SiteGuid    string
	LoginStatus int
	UserData    interface{}
}

type ChannelList []Channel

// channellist
// ToDo http://freshest.me/how-to-reverse-engineer-the-kabeldeutschland-tv-streaming-api/
type Channel struct {
	MediaID               interface{}
	MediaName             string
	MediaTypeID           interface{}
	MediaTypeName         interface{}
	Rating                interface{}
	ViewCounter           interface{}
	Description           interface{}
	CreationDate          interface{}
	LastWatchDate         interface{}
	StartDate             interface{}
	CatalogStartDate      interface{}
	PicURL                interface{}
	URL                   interface{}
	MediaWebLink          interface{}
	Duration              interface{}
	FileID                interface{}
	MediaDynamicData      interface{}
	SubDuration           interface{}
	SubFileFormat         interface{}
	SubFileID             interface{}
	SubURL                interface{}
	GeoBlock              interface{}
	TotalItems            interface{}
	like_counter          interface{}
	Tags                  interface{}
	AdvertisingParameters interface{}
	Files                 []struct {
		FileID          string
		URL             string
		Duration        string
		Format          string
		PreProvider     string
		PostProvider    string
		BreakProvider   string
		OverlayProvider string
		BreakPoints     string
		OverlayPoints   string
		Language        string
		IsDefaultLang   bool
		CoGuid          string
	}
	Pictures    interface{}
	ExternalIDs interface{}
}

// response of a licensed link
type LicensedLink struct {
	MainUrl string
	AltUrl  string
}

// global const to be used in code
const (
	GATEWAY              = "https://api-live.iptv.kabel-deutschland.de/v2_9/gateways/jsonpostgw.aspx"
	IOS_VERSION          = "8.1.2"
	APP_VERSION          = "1.1.5"
	METHOD_SIGNIN        = "SSOSignIn"
	METHOD_CHANNELLIST   = "GetChannelMediaList"
	METHOD_LICENSED_LINK = "GetLicensedLinks"
	INIT_OBJECT          = "eyJBcGlVc2VyIjoidHZwYXBpXzE4MSIsIlVESUQiOiJEMkFDNjMzQUZCNjQ0Q0YwQTY3NTA1MzcwNTc4Q0RFNSIsIkRvbWFpbklEIjozMTUzODQsIlNpdGVHdWlkIjo2Nzk4NzAsIlBsYXRmb3JtIjoiaVBhZCIsIkFwaVBhc3MiOiJhek5ETHpzblRCVHRBclZXMlNIUiIsIkxvY2FsZSI6eyJMb2NhbGVEZXZpY2UiOiJudWxsIiwiTG9jYWxlVXNlclN0YXRlIjoiVW5rbm93biIsIkxvY2FsZUNvdW50cnkiOiJudWxsIiwiTG9jYWxlTGFuZ3VhZ2UiOiJudWxsIn19"
	CHANNEL_OBJECT       = "\"orderBy\":\"None\",\"pageSize\":1000,\"picSize\":\"100X100\",\"ChannelID\":340758"
	M3U_HEAD             = "#EXTM3U\n"
	M3U_LINE             = "#EXTINF:-1,%s\n%s\n"

	QUALITY_LOW    = "CCURstream564000.m3u8"
	QUALITY_MEDIUM = "CCURstream1064000.m3u8"
	QUALITY_HIGH   = "CCURstream1664000.m3u8"

	CACHE_FILE     = "playlist_%s.m3u"
	CACHE_LIFETIME = 86400
)
<|endoftext|>


<commit_before>// Package go-oui provides functions to work with MAC and OUI's
package ouidb

import (
	"bufio"
	"bytes"
	"errors"
	"os"
	"regexp"
	"strconv"
)

// https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf
// Bigger than we need, not too big to worry about overflow
const big = 0xFFFFFF

var ErrInvalidMACAddress = errors.New("invalid MAC address")

// Hexadecimal to integer starting at &s[i0].
// Returns number, new offset, success.
func xtoi(s string, i0 int) (n int, i int, ok bool) {
	n = 0
	for i = i0; i < len(s); i++ {
		if '0' <= s[i] && s[i] <= '9' {
			n *= 16
			n += int(s[i] - '0')
		} else if 'a' <= s[i] && s[i] <= 'f' {
			n *= 16
			n += int(s[i]-'a') + 10
		} else if 'A' <= s[i] && s[i] <= 'F' {
			n *= 16
			n += int(s[i]-'A') + 10
		} else {
			break
		}
		if n >= big {
			return 0, i, false
		}
	}
	if i == i0 {
		return 0, i, false
	}
	return n, i, true
}

// xtoi2 converts the next two hex digits of s into a byte.
// If s is longer than 2 bytes then the third byte must be e.
// If the first two bytes of s are not hex digits or the third byte
// does not match e, false is returned.
func xtoi2(s string, e byte) (byte, bool) {
	if len(s) > 2 && s[2] != e {
		return 0, false
	}
	n, ei, ok := xtoi(s[:2], 0)
	return byte(n), ok && ei == 2
}

const hexDigit = "0123456789abcdef"

type HardwareAddr []byte

func (a HardwareAddr) String() string {
	if len(a) == 0 {
		return ""
	}
	buf := make([]byte, 0, len(a)*3-1)
	for i, b := range a {
		if i > 0 {
			buf = append(buf, ':')
		}
		buf = append(buf, hexDigit[b>>4])
		buf = append(buf, hexDigit[b&0xF])
	}
	return string(buf)
}

// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
// following formats:
//   01:23:45:67:89:ab
//   01:23:45:67:89:ab:cd:ef
//   01-23-45-67-89-ab
//   01-23-45-67-89-ab-cd-ef
//   0123.4567.89ab
//   0123.4567.89ab.cdef
func ParseOUI(s string, size int) (hw HardwareAddr, err error) {
	if s[2] == ':' || s[2] == '-' {
		if (len(s)+1)%3 != 0 {
			goto error
		}

		n := (len(s) + 1) / 3

		hw = make(HardwareAddr, size)
		for x, i := 0, 0; i < n; i++ {
			var ok bool
			if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
				goto error
			}
			x += 3
		}
	} else {
		goto error
	}
	return hw, nil

error:
	return nil, ErrInvalidMACAddress
}

// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
// following formats:
//   01:23:45:67:89:ab
//   01:23:45:67:89:ab:cd:ef
//   01-23-45-67-89-ab
//   01-23-45-67-89-ab-cd-ef
//   0123.4567.89ab
//   0123.4567.89ab.cdef
func ParseMAC(s string) (hw HardwareAddr, err error) {
	if len(s) < 14 {
		goto error
	}

	if s[2] == ':' || s[2] == '-' {
		if (len(s)+1)%3 != 0 {
			goto error
		}
		n := (len(s) + 1) / 3

		hw = make(HardwareAddr, n)
		for x, i := 0, 0; i < n; i++ {
			var ok bool
			if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
				goto error
			}
			x += 3
		}
	} else if s[4] == '.' {
		if (len(s)+1)%5 != 0 {
			goto error
		}
		n := 2 * (len(s) + 1) / 5
		if n != 6 && n != 8 {
			goto error
		}
		hw = make(HardwareAddr, n)
		for x, i := 0, 0; i < n; i += 2 {
			var ok bool
			if hw[i], ok = xtoi2(s[x:x+2], 0); !ok {
				goto error
			}
			if hw[i+1], ok = xtoi2(s[x+2:], s[4]); !ok {
				goto error
			}
			x += 5
		}
	} else {
		goto error
	}
	return hw, nil

error:
	return nil, errors.New("invalid MAC address: " + s)
}

// Mask returns the result of masking the address with mask.
func (address HardwareAddr) Mask(mask []byte) []byte {
	n := len(address)
	if n != len(mask) {
		return nil
	}
	out := make([]byte, n)
	for i := 0; i < n; i++ {
		out[i] = address[i] & mask[i]
	}
	return out
}

type t2 struct {
	T3    map[byte]t2
	Block *AddressBlock
}

type OuiDb struct {
	hw   [6]byte
	mask int

	dict   [][]byte
	Blocks []AddressBlock

	t map[int]t2
}

// New returns a new OUI database loaded from the specified file.
func New(file string) *OuiDb {
	db := &OuiDb{}
	if err := db.Load(file); err != nil {
		return nil
	}
	return db
}

// Lookup finds the OUI the address belongs to
func (m *OuiDb) Lookup(address HardwareAddr) *AddressBlock {
	for _, block := range m.Blocks {
		if block.Contains(address) {
			return &block
		}
	}

	return nil
}

// VendorLookup obtains the vendor organization name from the MAC address s.
func (m *OuiDb) VendorLookup(s string) (string, error) {
	addr, err := ParseMAC(s)
	if err != nil {
		return "", err
	}
	block := m.Lookup(addr)
	if block == nil {
		return "", ErrInvalidMACAddress
	}
	return block.Organization, nil
}

func byteIndex(s string, c byte) int {
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			return i
		}
	}
	return -1
}

func (m *OuiDb) Load(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return (err)
	}

	fieldsRe := regexp.MustCompile(`^(\S+)\t+(\S+)(\s+#\s+(\S.*))?`)

	re := regexp.MustCompile(`((?:(?:[0-9a-zA-Z]{2})[-:]){2,5}(?:[0-9a-zA-Z]{2}))(?:/(\w{1,2}))?`)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		text := scanner.Text()
		if text == "" || text[0] == '#' || text[0] == '\t' {
			continue
		}

		block := AddressBlock{}

		// Split input text into address, short organization name
		// and full organization name
		fields := fieldsRe.FindAllStringSubmatch(text, -1)
		addr := fields[0][1]
		if fields[0][4] != "" {
			block.Organization = fields[0][4]
		} else {
			block.Organization = fields[0][2]
		}

		matches := re.FindAllStringSubmatch(addr, -1)
		if len(matches) == 0 {
			continue
		}

		s := matches[0][1]

		i := byteIndex(s, '/')

		if i == -1 {
			block.Oui, err = ParseOUI(s, 6)
			block.Mask = 24 // len(block.Oui) * 8
		} else {
			block.Oui, err = ParseOUI(s[:i], 6)
			block.Mask, err = strconv.Atoi(s[i+1:])
		}

		//fmt.Println("OUI:", block.Oui, block.Mask, err)

		m.Blocks = append(m.Blocks, block)

		// create smart map
		for i := len(block.Oui) - 1; i >= 0; i-- {
			_ = block.Oui[i]

		}

		// fmt.Printf("BLA %v %v ALB", m.hw, m.mask)
	}

	if err := scanner.Err(); err != nil {
		return (err)
	}

	return (nil)
}

func CIDRMask(ones, bits int) []byte {
	l := bits / 8
	m := make([]byte, l)

	n := uint(ones)
	for i := 0; i < l; i++ {
		if n >= 8 {
			m[i] = 0xff
			n -= 8
			continue
		}
		m[i] = ^byte(0xff >> n)
		n = 0
	}

	return (m)
}

// oui, mask, organization
type AddressBlock struct {
	Oui          HardwareAddr
	Mask         int
	Organization string
}

// Contains reports whether the mac address belongs to the OUI
func (b *AddressBlock) Contains(address HardwareAddr) bool {
	//fmt.Println("%v %v %v %v", b.Oui, len(b.Oui), address.Mask(CIDRMask(b.Mask, len(b.Oui)*8)), CIDRMask(b.Mask, len(b.Oui)*8))

	return (bytes.Equal(address.Mask(CIDRMask(b.Mask, len(b.Oui)*8)), b.Oui))
}
<commit_msg>Delete duplicate HardwareAddr struct and methods<commit_after>// Package go-oui provides functions to work with MAC and OUI's
package ouidb

import (
	"bufio"
	"bytes"
	"errors"
	"net"
	"os"
	"regexp"
	"strconv"
)

// https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf
// Bigger than we need, not too big to worry about overflow
const big = 0xFFFFFF

var ErrInvalidMACAddress = errors.New("invalid MAC address")

// Hexadecimal to integer starting at &s[i0].
// Returns number, new offset, success.
func xtoi(s string, i0 int) (n int, i int, ok bool) {
	n = 0
	for i = i0; i < len(s); i++ {
		if '0' <= s[i] && s[i] <= '9' {
			n *= 16
			n += int(s[i] - '0')
		} else if 'a' <= s[i] && s[i] <= 'f' {
			n *= 16
			n += int(s[i]-'a') + 10
		} else if 'A' <= s[i] && s[i] <= 'F' {
			n *= 16
			n += int(s[i]-'A') + 10
		} else {
			break
		}
		if n >= big {
			return 0, i, false
		}
	}
	if i == i0 {
		return 0, i, false
	}
	return n, i, true
}

// xtoi2 converts the next two hex digits of s into a byte.
// If s is longer than 2 bytes then the third byte must be e.
// If the first two bytes of s are not hex digits or the third byte
// does not match e, false is returned.
func xtoi2(s string, e byte) (byte, bool) {
	if len(s) > 2 && s[2] != e {
		return 0, false
	}
	n, ei, ok := xtoi(s[:2], 0)
	return byte(n), ok && ei == 2
}

const hexDigit = "0123456789abcdef"

type HardwareAddr net.HardwareAddr

// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, or EUI-64 using one of the
// following formats:
//   01:23:45:67:89:ab
//   01:23:45:67:89:ab:cd:ef
//   01-23-45-67-89-ab
//   01-23-45-67-89-ab-cd-ef
//   0123.4567.89ab
//   0123.4567.89ab.cdef
func ParseOUI(s string, size int) (hw HardwareAddr, err error) {
	if s[2] == ':' || s[2] == '-' {
		if (len(s)+1)%3 != 0 {
			goto error
		}

		n := (len(s) + 1) / 3

		hw = make(HardwareAddr, size)
		for x, i := 0, 0; i < n; i++ {
			var ok bool
			if hw[i], ok = xtoi2(s[x:], s[2]); !ok {
				goto error
			}
			x += 3
		}
	} else {
		goto error
	}
	return hw, nil

error:
	return nil, ErrInvalidMACAddress
}

// Mask returns the result of masking the address with mask.
func (address HardwareAddr) Mask(mask []byte) []byte {
	n := len(address)
	if n != len(mask) {
		return nil
	}
	out := make([]byte, n)
	for i := 0; i < n; i++ {
		out[i] = address[i] & mask[i]
	}
	return out
}

type t2 struct {
	T3    map[byte]t2
	Block *AddressBlock
}

type OuiDb struct {
	hw   [6]byte
	mask int

	dict   [][]byte
	Blocks []AddressBlock

	t map[int]t2
}

// New returns a new OUI database loaded from the specified file.
func New(file string) *OuiDb {
	db := &OuiDb{}
	if err := db.Load(file); err != nil {
		return nil
	}
	return db
}

// Lookup finds the OUI the address belongs to
func (m *OuiDb) Lookup(address HardwareAddr) *AddressBlock {
	for _, block := range m.Blocks {
		if block.Contains(address) {
			return &block
		}
	}

	return nil
}

// VendorLookup obtains the vendor organization name from the MAC address s.
func (m *OuiDb) VendorLookup(s string) (string, error) {
	addr, err := net.ParseMAC(s)
	if err != nil {
		return "", err
	}
	block := m.Lookup(HardwareAddr(addr))
	if block == nil {
		return "", ErrInvalidMACAddress
	}
	return block.Organization, nil
}

func byteIndex(s string, c byte) int {
	for i := 0; i < len(s); i++ {
		if s[i] == c {
			return i
		}
	}
	return -1
}

func (m *OuiDb) Load(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return (err)
	}

	fieldsRe := regexp.MustCompile(`^(\S+)\t+(\S+)(\s+#\s+(\S.*))?`)

	re := regexp.MustCompile(`((?:(?:[0-9a-zA-Z]{2})[-:]){2,5}(?:[0-9a-zA-Z]{2}))(?:/(\w{1,2}))?`)

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		text := scanner.Text()
		if text == "" || text[0] == '#' || text[0] == '\t' {
			continue
		}

		block := AddressBlock{}

		// Split input text into address, short organization name
		// and full organization name
		fields := fieldsRe.FindAllStringSubmatch(text, -1)
		addr := fields[0][1]
		if fields[0][4] != "" {
			block.Organization = fields[0][4]
		} else {
			block.Organization = fields[0][2]
		}

		matches := re.FindAllStringSubmatch(addr, -1)
		if len(matches) == 0 {
			continue
		}

		s := matches[0][1]

		i := byteIndex(s, '/')

		if i == -1 {
			block.Oui, err = ParseOUI(s, 6)
			block.Mask = 24 // len(block.Oui) * 8
		} else {
			block.Oui, err = ParseOUI(s[:i], 6)
			block.Mask, err = strconv.Atoi(s[i+1:])
		}

		//fmt.Println("OUI:", block.Oui, block.Mask, err)

		m.Blocks = append(m.Blocks, block)

		// create smart map
		for i := len(block.Oui) - 1; i >= 0; i-- {
			_ = block.Oui[i]

		}

		// fmt.Printf("BLA %v %v ALB", m.hw, m.mask)
	}

	if err := scanner.Err(); err != nil {
		return (err)
	}

	return (nil)
}

func CIDRMask(ones, bits int) []byte {
	l := bits / 8
	m := make([]byte, l)

	n := uint(ones)
	for i := 0; i < l; i++ {
		if n >= 8 {
			m[i] = 0xff
			n -= 8
			continue
		}
		m[i] = ^byte(0xff >> n)
		n = 0
	}

	return (m)
}

// oui, mask, organization
type AddressBlock struct {
	Oui          HardwareAddr
	Mask         int
	Organization string
}

// Contains reports whether the mac address belongs to the OUI
func (b *AddressBlock) Contains(address HardwareAddr) bool {
	//fmt.Println("%v %v %v %v", b.Oui, len(b.Oui), address.Mask(CIDRMask(b.Mask, len(b.Oui)*8)), CIDRMask(b.Mask, len(b.Oui)*8))

	return (bytes.Equal(address.Mask(CIDRMask(b.Mask, len(b.Oui)*8)), b.Oui))
}
<|endoftext|>


<commit_before>package terraform

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"sort"
	"sync"

	"github.com/hashicorp/terraform/config"
)

// The format byte is prefixed into the state file format so that we have
// the ability in the future to change the file format if we want for any
// reason.
const (
	stateFormatMagic        = "tfstate"
	stateFormatVersion byte = 1
)

// StateV1 is used to represent the state of Terraform files before
// 0.3. It is automatically upgraded to a modern State representation
// on start.
type StateV1 struct {
	Outputs   map[string]string
	Resources map[string]*ResourceState
	Tainted   map[string]struct{}

	once sync.Once
}

func (s *StateV1) init() {
	s.once.Do(func() {
		if s.Resources == nil {
			s.Resources = make(map[string]*ResourceState)
		}

		if s.Tainted == nil {
			s.Tainted = make(map[string]struct{})
		}
	})
}

func (s *StateV1) deepcopy() *State {
	result := new(State)
	result.init()
	if s != nil {
		for k, v := range s.Resources {
			result.Resources[k] = v
		}
		for k, v := range s.Tainted {
			result.Tainted[k] = v
		}
	}

	return result
}

// prune is a helper that removes any empty IDs from the state
// and cleans it up in general.
func (s *StateV1) prune() {
	for k, v := range s.Resources {
		if v.ID == "" {
			delete(s.Resources, k)
		}
	}
}

// Orphans returns a list of keys of resources that are in the State
// but aren't present in the configuration itself. Hence, these keys
// represent the state of resources that are orphans.
func (s *StateV1) Orphans(c *config.Config) []string {
	keys := make(map[string]struct{})
	for k, _ := range s.Resources {
		keys[k] = struct{}{}
	}

	for _, r := range c.Resources {
		delete(keys, r.Id())

		// Mark all the counts as not orphans.
		for i := 0; i < r.Count; i++ {
			delete(keys, fmt.Sprintf("%s.%d", r.Id(), i))
		}
	}

	result := make([]string, 0, len(keys))
	for k, _ := range keys {
		result = append(result, k)
	}

	return result
}

func (s *StateV1) String() string {
	if len(s.Resources) == 0 {
		return "<no state>"
	}

	var buf bytes.Buffer

	names := make([]string, 0, len(s.Resources))
	for name, _ := range s.Resources {
		names = append(names, name)
	}
	sort.Strings(names)

	for _, k := range names {
		rs := s.Resources[k]
		id := rs.ID
		if id == "" {
			id = "<not created>"
		}

		taintStr := ""
		if _, ok := s.Tainted[k]; ok {
			taintStr = " (tainted)"
		}

		buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr))
		buf.WriteString(fmt.Sprintf("  ID = %s\n", id))

		attrKeys := make([]string, 0, len(rs.Attributes))
		for ak, _ := range rs.Attributes {
			if ak == "id" {
				continue
			}

			attrKeys = append(attrKeys, ak)
		}
		sort.Strings(attrKeys)

		for _, ak := range attrKeys {
			av := rs.Attributes[ak]
			buf.WriteString(fmt.Sprintf("  %s = %s\n", ak, av))
		}

		if len(rs.Dependencies) > 0 {
			buf.WriteString(fmt.Sprintf("\n  Dependencies:\n"))
			for _, dep := range rs.Dependencies {
				buf.WriteString(fmt.Sprintf("    %s\n", dep.ID))
			}
		}
	}

	if len(s.Outputs) > 0 {
		buf.WriteString("\nOutputs:\n\n")

		ks := make([]string, 0, len(s.Outputs))
		for k, _ := range s.Outputs {
			ks = append(ks, k)
		}
		sort.Strings(ks)

		for _, k := range ks {
			v := s.Outputs[k]
			buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
		}
	}

	return buf.String()
}

/// ResourceState holds the state of a resource that is used so that
// a provider can find and manage an existing resource as well as for
// storing attributes that are uesd to populate variables of child
// resources.
//
// Attributes has attributes about the created resource that are
// queryable in interpolation: "${type.id.attr}"
//
// Extra is just extra data that a provider can return that we store
// for later, but is not exposed in any way to the user.
type ResourceStateV1 struct {
	// This is filled in and managed by Terraform, and is the resource
	// type itself such as "mycloud_instance". If a resource provider sets
	// this value, it won't be persisted.
	Type string

	// The attributes below are all meant to be filled in by the
	// resource providers themselves. Documentation for each are above
	// each element.

	// A unique ID for this resource. This is opaque to Terraform
	// and is only meant as a lookup mechanism for the providers.
	ID string

	// Attributes are basic information about the resource. Any keys here
	// are accessible in variable format within Terraform configurations:
	// ${resourcetype.name.attribute}.
	Attributes map[string]string

	// ConnInfo is used for the providers to export information which is
	// used to connect to the resource for provisioning. For example,
	// this could contain SSH or WinRM credentials.
	ConnInfo map[string]string

	// Extra information that the provider can store about a resource.
	// This data is opaque, never shown to the user, and is sent back to
	// the provider as-is for whatever purpose appropriate.
	Extra map[string]interface{}

	// Dependencies are a list of things that this resource relies on
	// existing to remain intact. For example: an AWS instance might
	// depend on a subnet (which itself might depend on a VPC, and so
	// on).
	//
	// Terraform uses this information to build valid destruction
	// orders and to warn the user if they're destroying a resource that
	// another resource depends on.
	//
	// Things can be put into this list that may not be managed by
	// Terraform. If Terraform doesn't find a matching ID in the
	// overall state, then it assumes it isn't managed and doesn't
	// worry about it.
	Dependencies []ResourceDependency
}

// MergeDiff takes a ResourceDiff and merges the attributes into
// this resource state in order to generate a new state. This new
// state can be used to provide updated attribute lookups for
// variable interpolation.
//
// If the diff attribute requires computing the value, and hence
// won't be available until apply, the value is replaced with the
// computeID.
func (s *ResourceStateV1) MergeDiff(d *ResourceDiff) *ResourceStateV1 {
	var result ResourceStateV1
	if s != nil {
		result = *s
	}

	result.Attributes = make(map[string]string)
	if s != nil {
		for k, v := range s.Attributes {
			result.Attributes[k] = v
		}
	}
	if d != nil {
		for k, diff := range d.Attributes {
			if diff.NewRemoved {
				delete(result.Attributes, k)
				continue
			}
			if diff.NewComputed {
				result.Attributes[k] = config.UnknownVariableValue
				continue
			}

			result.Attributes[k] = diff.New
		}
	}

	return &result
}

func (s *ResourceStateV1) GoString() string {
	return fmt.Sprintf("*%#v", *s)
}

// ResourceDependency maps a resource to another resource that it
// depends on to remain intact and uncorrupted.
type ResourceDependency struct {
	// ID of the resource that we depend on. This ID should map
	// directly to another ResourceState's ID.
	ID string
}

// ReadStateV1 reads a state structure out of a reader in the format that
// was written by WriteState.
func ReadStateV1(src io.Reader) (*StateV1, error) {
	var result *StateV1
	var err error
	n := 0

	// Verify the magic bytes
	magic := make([]byte, len(stateFormatMagic))
	for n < len(magic) {
		n, err = src.Read(magic[n:])
		if err != nil {
			return nil, fmt.Errorf("error while reading magic bytes: %s", err)
		}
	}
	if string(magic) != stateFormatMagic {
		return nil, fmt.Errorf("not a valid state file")
	}

	// Verify the version is something we can read
	var formatByte [1]byte
	n, err = src.Read(formatByte[:])
	if err != nil {
		return nil, err
	}
	if n != len(formatByte) {
		return nil, errors.New("failed to read state version byte")
	}

	if formatByte[0] != stateFormatVersion {
		return nil, fmt.Errorf("unknown state file version: %d", formatByte[0])
	}

	// Decode
	dec := gob.NewDecoder(src)
	if err := dec.Decode(&result); err != nil {
		return nil, err
	}

	return result, nil
}
<commit_msg>terraform: Fixing type references<commit_after>package terraform

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io"
	"sort"
	"sync"

	"github.com/hashicorp/terraform/config"
)

// The format byte is prefixed into the state file format so that we have
// the ability in the future to change the file format if we want for any
// reason.
const (
	stateFormatMagic        = "tfstate"
	stateFormatVersion byte = 1
)

// StateV1 is used to represent the state of Terraform files before
// 0.3. It is automatically upgraded to a modern State representation
// on start.
type StateV1 struct {
	Outputs   map[string]string
	Resources map[string]*ResourceStateV1
	Tainted   map[string]struct{}

	once sync.Once
}

func (s *StateV1) init() {
	s.once.Do(func() {
		if s.Resources == nil {
			s.Resources = make(map[string]*ResourceStateV1)
		}

		if s.Tainted == nil {
			s.Tainted = make(map[string]struct{})
		}
	})
}

func (s *StateV1) deepcopy() *StateV1 {
	result := new(StateV1)
	result.init()
	if s != nil {
		for k, v := range s.Resources {
			result.Resources[k] = v
		}
		for k, v := range s.Tainted {
			result.Tainted[k] = v
		}
	}

	return result
}

// prune is a helper that removes any empty IDs from the state
// and cleans it up in general.
func (s *StateV1) prune() {
	for k, v := range s.Resources {
		if v.ID == "" {
			delete(s.Resources, k)
		}
	}
}

// Orphans returns a list of keys of resources that are in the State
// but aren't present in the configuration itself. Hence, these keys
// represent the state of resources that are orphans.
func (s *StateV1) Orphans(c *config.Config) []string {
	keys := make(map[string]struct{})
	for k, _ := range s.Resources {
		keys[k] = struct{}{}
	}

	for _, r := range c.Resources {
		delete(keys, r.Id())

		// Mark all the counts as not orphans.
		for i := 0; i < r.Count; i++ {
			delete(keys, fmt.Sprintf("%s.%d", r.Id(), i))
		}
	}

	result := make([]string, 0, len(keys))
	for k, _ := range keys {
		result = append(result, k)
	}

	return result
}

func (s *StateV1) String() string {
	if len(s.Resources) == 0 {
		return "<no state>"
	}

	var buf bytes.Buffer

	names := make([]string, 0, len(s.Resources))
	for name, _ := range s.Resources {
		names = append(names, name)
	}
	sort.Strings(names)

	for _, k := range names {
		rs := s.Resources[k]
		id := rs.ID
		if id == "" {
			id = "<not created>"
		}

		taintStr := ""
		if _, ok := s.Tainted[k]; ok {
			taintStr = " (tainted)"
		}

		buf.WriteString(fmt.Sprintf("%s:%s\n", k, taintStr))
		buf.WriteString(fmt.Sprintf("  ID = %s\n", id))

		attrKeys := make([]string, 0, len(rs.Attributes))
		for ak, _ := range rs.Attributes {
			if ak == "id" {
				continue
			}

			attrKeys = append(attrKeys, ak)
		}
		sort.Strings(attrKeys)

		for _, ak := range attrKeys {
			av := rs.Attributes[ak]
			buf.WriteString(fmt.Sprintf("  %s = %s\n", ak, av))
		}

		if len(rs.Dependencies) > 0 {
			buf.WriteString(fmt.Sprintf("\n  Dependencies:\n"))
			for _, dep := range rs.Dependencies {
				buf.WriteString(fmt.Sprintf("    %s\n", dep.ID))
			}
		}
	}

	if len(s.Outputs) > 0 {
		buf.WriteString("\nOutputs:\n\n")

		ks := make([]string, 0, len(s.Outputs))
		for k, _ := range s.Outputs {
			ks = append(ks, k)
		}
		sort.Strings(ks)

		for _, k := range ks {
			v := s.Outputs[k]
			buf.WriteString(fmt.Sprintf("%s = %s\n", k, v))
		}
	}

	return buf.String()
}

/// ResourceState holds the state of a resource that is used so that
// a provider can find and manage an existing resource as well as for
// storing attributes that are uesd to populate variables of child
// resources.
//
// Attributes has attributes about the created resource that are
// queryable in interpolation: "${type.id.attr}"
//
// Extra is just extra data that a provider can return that we store
// for later, but is not exposed in any way to the user.
type ResourceStateV1 struct {
	// This is filled in and managed by Terraform, and is the resource
	// type itself such as "mycloud_instance". If a resource provider sets
	// this value, it won't be persisted.
	Type string

	// The attributes below are all meant to be filled in by the
	// resource providers themselves. Documentation for each are above
	// each element.

	// A unique ID for this resource. This is opaque to Terraform
	// and is only meant as a lookup mechanism for the providers.
	ID string

	// Attributes are basic information about the resource. Any keys here
	// are accessible in variable format within Terraform configurations:
	// ${resourcetype.name.attribute}.
	Attributes map[string]string

	// ConnInfo is used for the providers to export information which is
	// used to connect to the resource for provisioning. For example,
	// this could contain SSH or WinRM credentials.
	ConnInfo map[string]string

	// Extra information that the provider can store about a resource.
	// This data is opaque, never shown to the user, and is sent back to
	// the provider as-is for whatever purpose appropriate.
	Extra map[string]interface{}

	// Dependencies are a list of things that this resource relies on
	// existing to remain intact. For example: an AWS instance might
	// depend on a subnet (which itself might depend on a VPC, and so
	// on).
	//
	// Terraform uses this information to build valid destruction
	// orders and to warn the user if they're destroying a resource that
	// another resource depends on.
	//
	// Things can be put into this list that may not be managed by
	// Terraform. If Terraform doesn't find a matching ID in the
	// overall state, then it assumes it isn't managed and doesn't
	// worry about it.
	Dependencies []ResourceDependency
}

// MergeDiff takes a ResourceDiff and merges the attributes into
// this resource state in order to generate a new state. This new
// state can be used to provide updated attribute lookups for
// variable interpolation.
//
// If the diff attribute requires computing the value, and hence
// won't be available until apply, the value is replaced with the
// computeID.
func (s *ResourceStateV1) MergeDiff(d *ResourceDiff) *ResourceStateV1 {
	var result ResourceStateV1
	if s != nil {
		result = *s
	}

	result.Attributes = make(map[string]string)
	if s != nil {
		for k, v := range s.Attributes {
			result.Attributes[k] = v
		}
	}
	if d != nil {
		for k, diff := range d.Attributes {
			if diff.NewRemoved {
				delete(result.Attributes, k)
				continue
			}
			if diff.NewComputed {
				result.Attributes[k] = config.UnknownVariableValue
				continue
			}

			result.Attributes[k] = diff.New
		}
	}

	return &result
}

func (s *ResourceStateV1) GoString() string {
	return fmt.Sprintf("*%#v", *s)
}

// ResourceDependency maps a resource to another resource that it
// depends on to remain intact and uncorrupted.
type ResourceDependency struct {
	// ID of the resource that we depend on. This ID should map
	// directly to another ResourceState's ID.
	ID string
}

// ReadStateV1 reads a state structure out of a reader in the format that
// was written by WriteState.
func ReadStateV1(src io.Reader) (*StateV1, error) {
	var result *StateV1
	var err error
	n := 0

	// Verify the magic bytes
	magic := make([]byte, len(stateFormatMagic))
	for n < len(magic) {
		n, err = src.Read(magic[n:])
		if err != nil {
			return nil, fmt.Errorf("error while reading magic bytes: %s", err)
		}
	}
	if string(magic) != stateFormatMagic {
		return nil, fmt.Errorf("not a valid state file")
	}

	// Verify the version is something we can read
	var formatByte [1]byte
	n, err = src.Read(formatByte[:])
	if err != nil {
		return nil, err
	}
	if n != len(formatByte) {
		return nil, errors.New("failed to read state version byte")
	}

	if formatByte[0] != stateFormatVersion {
		return nil, fmt.Errorf("unknown state file version: %d", formatByte[0])
	}

	// Decode
	dec := gob.NewDecoder(src)
	if err := dec.Decode(&result); err != nil {
		return nil, err
	}

	return result, nil
}
<|endoftext|>


<commit_before>package setting

import (
	"net/http"

	"encoding/json"
	"fmt"

	"github.com/rancher/norman/api/handler"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	"github.com/rancher/types/client/management/v3"
	"github.com/sirupsen/logrus"
)

func Formatter(apiContext *types.APIContext, resource *types.RawResource) {
	v, ok := resource.Values["value"]
	if !ok || v == "" {
		resource.Values["value"] = resource.Values["default"]
		resource.Values["customized"] = false
	} else {
		resource.Values["customized"] = true
	}
}

type Handler struct{}

func (h *Handler) UpdateHandler(apiContext *types.APIContext, next types.RequestHandler) error {
	if apiContext.ID != "cluster-defaults" {
		return handler.UpdateHandler(apiContext, next)
	}

	store := apiContext.Schema.Store
	if store == nil {
		return httperror.NewAPIError(httperror.NotFound, "no store found")
	}

	data, err := handler.ParseAndValidateBody(apiContext, false)
	if err != nil {
		return err
	}

	value := convert.ToString(data["value"])
	if value != "" {
		spec := v3.ClusterSpec{}
		err = json.Unmarshal([]byte(value), &spec)
		if err != nil {
			return fmt.Errorf("unmarshal error %v", err)
		}

		dataMap := map[string]interface{}{}
		err = json.Unmarshal([]byte(value), &dataMap)
		if err != nil {
			return fmt.Errorf("unmarshal error %v", err)
		}

		clusterSchema := apiContext.Schemas.Schema(&managementschema.Version, client.ClusterType)
		modify(clusterSchema, dataMap, apiContext.Schemas)
	}

	data, err = store.Update(apiContext, apiContext.Schema, data, apiContext.ID)
	if err != nil {
		return err
	}

	apiContext.WriteResponse(http.StatusOK, data)
	return nil
}

func modify(schema *types.Schema, data map[string]interface{}, schemas *types.Schemas) {
	for name, value := range data {
		if field, ok := schema.ResourceFields[name]; ok {
			checkSchema := schemas.Schema(&managementschema.Version, field.Type)
			if checkSchema != nil {
				modify(checkSchema, convert.ToMapInterface(value), schemas)
			} else {
				field.Default = value
				schema.ResourceFields[name] = field
			}
		}
	}
}

func ModifySchema(schema *types.Schema, schemas *types.Schemas) {
	data := settings.ClusterDefaults.Get()
	if data != "" {
		dataMap := map[string]interface{}{}
		err := json.Unmarshal([]byte(data), &dataMap)
		if err != nil {
			return
		}
		modify(schema, dataMap, schemas)
	}
}

func SetClusterDefaults(schema *types.Schema, schemas *types.Schemas) {
	ans, err := json.Marshal(getClusterSpec(schema, schemas))
	if err != nil {
		logrus.Warnf("error setting cluster defaults %v", err)
	}
	settings.ClusterDefaults.Set(string(ans))
}

func getClusterSpec(schema *types.Schema, schemas *types.Schemas) map[string]interface{} {
	data := map[string]interface{}{}
	for name, field := range schema.ResourceFields {
		checkSchema := schemas.Schema(&managementschema.Version, field.Type)
		if checkSchema != nil {
			value := getClusterSpec(checkSchema, schemas)
			if len(value) > 0 {
				data[name] = value
			}
		} else {
			if field.Default != nil {
				data[name] = field.Default
			}
		}
	}
	return data
}
<commit_msg>add fixes for cluster-defaults setting<commit_after>package setting

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/rancher/norman/api/handler"
	"github.com/rancher/norman/httperror"
	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/rancher/types/apis/management.cattle.io/v3"
	managementschema "github.com/rancher/types/apis/management.cattle.io/v3/schema"
	"github.com/rancher/types/client/management/v3"
	"github.com/sirupsen/logrus"
)

func Formatter(apiContext *types.APIContext, resource *types.RawResource) {
	v, ok := resource.Values["value"]
	if !ok || v == "" {
		resource.Values["value"] = resource.Values["default"]
		resource.Values["customized"] = false
	} else {
		resource.Values["customized"] = true
	}
}

type Handler struct{}

func (h *Handler) UpdateHandler(apiContext *types.APIContext, next types.RequestHandler) error {
	if apiContext.ID != "cluster-defaults" {
		return handler.UpdateHandler(apiContext, next)
	}

	store := apiContext.Schema.Store
	if store == nil {
		return httperror.NewAPIError(httperror.NotFound, "no store found")
	}

	data, err := handler.ParseAndValidateBody(apiContext, false)
	if err != nil {
		return err
	}

	value := convert.ToString(data["value"])
	clusterSchema := apiContext.Schemas.Schema(&managementschema.Version, client.ClusterType)
	if value == "" {
		SetClusterDefaults(clusterSchema, apiContext.Schemas)
	} else {
		spec := v3.ClusterSpec{}
		err = json.Unmarshal([]byte(value), &spec)
		if err != nil {
			return fmt.Errorf("unmarshal error %v", err)
		}

		dataMap := map[string]interface{}{}
		err = json.Unmarshal([]byte(value), &dataMap)
		if err != nil {
			return fmt.Errorf("unmarshal error %v", err)
		}
		modify(clusterSchema, dataMap, apiContext.Schemas, getIgnoredFields(apiContext.Schemas))
	}

	data, err = store.Update(apiContext, apiContext.Schema, data, apiContext.ID)
	if err != nil {
		return err
	}

	apiContext.WriteResponse(http.StatusOK, data)
	return nil
}

func modify(schema *types.Schema, data map[string]interface{}, schemas *types.Schemas, toIgnore map[string]bool) {
	for name, value := range data {
		if _, ok := toIgnore[name]; ok {
			continue
		}
		if field, ok := schema.ResourceFields[name]; ok {
			checkSchema := schemas.Schema(&managementschema.Version, field.Type)
			if checkSchema != nil {
				modify(checkSchema, convert.ToMapInterface(value), schemas, toIgnore)
			} else {
				field.Default = value
				schema.ResourceFields[name] = field
			}
		}
	}
}

func ModifySchema(schema *types.Schema, schemas *types.Schemas) {
	data := settings.ClusterDefaults.Get()
	if data != "" {
		dataMap := map[string]interface{}{}
		err := json.Unmarshal([]byte(data), &dataMap)
		if err != nil {
			return
		}
		modify(schema, dataMap, schemas, getIgnoredFields(schemas))
	}
}

func SetClusterDefaults(schema *types.Schema, schemas *types.Schemas) {
	ans, err := json.Marshal(getClusterSpec(schema, schemas, getIgnoredFields(schemas)))
	if err != nil {
		logrus.Warnf("error setting cluster defaults %v", err)
	}
	settings.ClusterDefaults.Set(string(ans))
}

func getClusterSpec(schema *types.Schema, schemas *types.Schemas, toIgnore map[string]bool) map[string]interface{} {
	data := map[string]interface{}{}
	for name, field := range schema.ResourceFields {
		if _, ok := toIgnore[name]; ok {
			continue
		}
		checkSchema := schemas.Schema(&managementschema.Version, field.Type)
		if checkSchema != nil {
			value := getClusterSpec(checkSchema, schemas, toIgnore)
			if len(value) > 0 {
				data[name] = value
			}
		} else {
			data[name] = field.Default
		}
	}
	return data
}

func getIgnoredFields(schemas *types.Schemas) map[string]bool {
	ignored := map[string]bool{}
	clusterSchema := schemas.Schema(&managementschema.Version, client.ClusterType)
	specSchema := schemas.Schema(&managementschema.Version, client.ClusterSpecType)
	statusSchema := schemas.Schema(&managementschema.Version, client.ClusterStatusType)
	for name := range statusSchema.ResourceFields {
		ignored[name] = true
	}
	for name := range clusterSchema.ResourceFields {
		if strings.HasSuffix(name, "Config") && !strings.HasPrefix(name, "rancher") {
			ignored[name] = true
		}
		if _, ok := specSchema.ResourceFields[name]; !ok {
			ignored[name] = true
		}
	}
	ignored["clusterName"] = true
	return ignored
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton 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 v1beta1

import (
	"context"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1"
)

// ParamSpec defines arbitrary parameters needed beyond typed inputs (such as
// resources). Parameter values are provided by users as inputs on a TaskRun
// or PipelineRun.
type ParamSpec struct {
	// Name declares the name by which a parameter is referenced.
	Name string `json:"name"`
	// Type is the user-specified type of the parameter. The possible types
	// are currently "string" and "array", and "string" is the default.
	// +optional
	Type ParamType `json:"type,omitempty"`
	// Description is a user-facing description of the parameter that may be
	// used to populate a UI.
	// +optional
	Description string `json:"description,omitempty"`
	// Default is the value a parameter takes if no input value is supplied. If
	// default is set, a Task may be executed without a supplied value for the
	// parameter.
	// +optional
	Default *ArrayOrString `json:"default,omitempty"`
}

func (pp *ParamSpec) SetDefaults(ctx context.Context) {
	if pp != nil && pp.Type == "" {
		if pp.Default != nil {
			// propagate the parsed ArrayOrString's type to the parent ParamSpec's type
			pp.Type = pp.Default.Type
		} else {
			// ParamTypeString is the default value (when no type can be inferred from the default value)
			pp.Type = ParamTypeString
		}
	}
}

// ResourceParam declares a string value to use for the parameter called Name, and is used in
// the specific context of PipelineResources.
type ResourceParam = resource.ResourceParam

// Param declares an ArrayOrString to use for the parameter called name.
type Param struct {
	Name  string        `json:"name"`
	Value ArrayOrString `json:"value"`
}

// ParamType indicates the type of an input parameter;
// Used to distinguish between a single string and an array of strings.
type ParamType string

// Valid ParamTypes:
const (
	ParamTypeString ParamType = "string"
	ParamTypeArray  ParamType = "array"
)

// AllParamTypes can be used for ParamType validation.
var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray}

// ArrayOrString is modeled after IntOrString in kubernetes/apimachinery:

// ArrayOrString is a type that can hold a single string or string array.
// Used in JSON unmarshalling so that a single JSON field can accept
// either an individual string or an array of strings.
type ArrayOrString struct {
	Type      ParamType // Represents the stored type of ArrayOrString.
	StringVal string
	ArrayVal  []string
}

// UnmarshalJSON implements the json.Unmarshaller interface.
func (arrayOrString *ArrayOrString) UnmarshalJSON(value []byte) error {
	if value[0] == '"' {
		arrayOrString.Type = ParamTypeString
		return json.Unmarshal(value, &arrayOrString.StringVal)
	}
	arrayOrString.Type = ParamTypeArray
	return json.Unmarshal(value, &arrayOrString.ArrayVal)
}

// MarshalJSON implements the json.Marshaller interface.
func (arrayOrString ArrayOrString) MarshalJSON() ([]byte, error) {
	switch arrayOrString.Type {
	case ParamTypeString:
		return json.Marshal(arrayOrString.StringVal)
	case ParamTypeArray:
		return json.Marshal(arrayOrString.ArrayVal)
	default:
		return []byte{}, fmt.Errorf("impossible ArrayOrString.Type: %q", arrayOrString.Type)
	}
}

func (arrayOrString *ArrayOrString) ApplyReplacements(stringReplacements map[string]string, arrayReplacements map[string][]string) {
	if arrayOrString.Type == ParamTypeString {
		arrayOrString.StringVal = ApplyReplacements(arrayOrString.StringVal, stringReplacements)
	} else {
		var newArrayVal []string
		for _, v := range arrayOrString.ArrayVal {
			newArrayVal = append(newArrayVal, ApplyArrayReplacements(v, stringReplacements, arrayReplacements)...)
		}
		arrayOrString.ArrayVal = newArrayVal
	}
}

// ResultRef is a type that represents a reference to a task run result
type ResultRef struct {
	PipelineTask string
	Result       string
}

const (
	resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
	// ResultTaskPart Constant used to define the "tasks" part of a pipeline result reference
	ResultTaskPart = "tasks"
	// ResultResultPart Constant used to define the "results" part of a pipeline result reference
	ResultResultPart           = "results"
	variableSubstitutionFormat = `\$\([A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\)`
)

var variableSubstitutionRegex = regexp.MustCompile(variableSubstitutionFormat)

// NewResultRefs extracts all ResultReferences from param.
// If the ResultReference can be extracted, they are returned. Otherwise an error is returned
func NewResultRefs(param Param) ([]*ResultRef, error) {
	substitutionExpressions, ok := getVarSubstitutionExpressions(param)
	if !ok {
		return nil, fmt.Errorf("Invalid result reference expression: must contain variable substitution %q", resultExpressionFormat)
	}
	var resultRefs []*ResultRef
	for _, expression := range substitutionExpressions {
		pipelineTask, result, err := parseExpression(expression)
		if err != nil {
			return nil, fmt.Errorf("Invalid result reference expression: %v", err)
		}
		resultRefs = append(resultRefs, &ResultRef{
			PipelineTask: pipelineTask,
			Result:       result,
		})
	}
	return resultRefs, nil
}

// LooksLikeContainsResultRefs attempts to check if param looks like it contains any
// result references.
// This is useful if we want to make sure the param looks like a ResultReference before
// performing strict validation
func LooksLikeContainsResultRefs(param Param) bool {
	if param.Value.Type != ParamTypeString {
		return false
	}
	extractedExpressions, ok := getVarSubstitutionExpressions(param)
	if !ok {
		return false
	}
	for _, expression := range extractedExpressions {
		if looksLikeResultRef(expression) {
			return true
		}
	}
	return false
}

func looksLikeResultRef(expression string) bool {
	return strings.HasPrefix(expression, "task") && strings.Contains(expression, ".result")
}

// getVarSubstitutionExpressions extracts all the value between "$(" and ")""
func getVarSubstitutionExpressions(param Param) ([]string, bool) {
	if param.Value.Type != ParamTypeString {
		return nil, false
	}
	expressions := variableSubstitutionRegex.FindAllString(param.Value.StringVal, -1)
	if expressions == nil {
		return nil, false
	}
	var allExpressions []string
	for _, expression := range expressions {
		allExpressions = append(allExpressions, stripVarSubExpression(expression))
	}
	return allExpressions, true
}

func stripVarSubExpression(expression string) string {
	return strings.TrimSuffix(strings.TrimPrefix(expression, "$("), ")")
}

func parseExpression(substitutionExpression string) (string, string, error) {
	subExpressions := strings.Split(substitutionExpression, ".")
	if len(subExpressions) != 4 || subExpressions[0] != ResultTaskPart || subExpressions[2] != ResultResultPart {
		return "", "", fmt.Errorf("Must be of the form %q", resultExpressionFormat)
	}
	return subExpressions[1], subExpressions[3], nil
}
<commit_msg>Add a `NewArrayOrString` function 🆕<commit_after>/*
Copyright 2019 The Tekton 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 v1beta1

import (
	"context"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"

	resource "github.com/tektoncd/pipeline/pkg/apis/resource/v1alpha1"
)

// ParamSpec defines arbitrary parameters needed beyond typed inputs (such as
// resources). Parameter values are provided by users as inputs on a TaskRun
// or PipelineRun.
type ParamSpec struct {
	// Name declares the name by which a parameter is referenced.
	Name string `json:"name"`
	// Type is the user-specified type of the parameter. The possible types
	// are currently "string" and "array", and "string" is the default.
	// +optional
	Type ParamType `json:"type,omitempty"`
	// Description is a user-facing description of the parameter that may be
	// used to populate a UI.
	// +optional
	Description string `json:"description,omitempty"`
	// Default is the value a parameter takes if no input value is supplied. If
	// default is set, a Task may be executed without a supplied value for the
	// parameter.
	// +optional
	Default *ArrayOrString `json:"default,omitempty"`
}

func (pp *ParamSpec) SetDefaults(ctx context.Context) {
	if pp != nil && pp.Type == "" {
		if pp.Default != nil {
			// propagate the parsed ArrayOrString's type to the parent ParamSpec's type
			pp.Type = pp.Default.Type
		} else {
			// ParamTypeString is the default value (when no type can be inferred from the default value)
			pp.Type = ParamTypeString
		}
	}
}

// ResourceParam declares a string value to use for the parameter called Name, and is used in
// the specific context of PipelineResources.
type ResourceParam = resource.ResourceParam

// Param declares an ArrayOrString to use for the parameter called name.
type Param struct {
	Name  string        `json:"name"`
	Value ArrayOrString `json:"value"`
}

// ParamType indicates the type of an input parameter;
// Used to distinguish between a single string and an array of strings.
type ParamType string

// Valid ParamTypes:
const (
	ParamTypeString ParamType = "string"
	ParamTypeArray  ParamType = "array"
)

// AllParamTypes can be used for ParamType validation.
var AllParamTypes = []ParamType{ParamTypeString, ParamTypeArray}

// ArrayOrString is modeled after IntOrString in kubernetes/apimachinery:

// ArrayOrString is a type that can hold a single string or string array.
// Used in JSON unmarshalling so that a single JSON field can accept
// either an individual string or an array of strings.
type ArrayOrString struct {
	Type      ParamType // Represents the stored type of ArrayOrString.
	StringVal string
	ArrayVal  []string
}

// UnmarshalJSON implements the json.Unmarshaller interface.
func (arrayOrString *ArrayOrString) UnmarshalJSON(value []byte) error {
	if value[0] == '"' {
		arrayOrString.Type = ParamTypeString
		return json.Unmarshal(value, &arrayOrString.StringVal)
	}
	arrayOrString.Type = ParamTypeArray
	return json.Unmarshal(value, &arrayOrString.ArrayVal)
}

// MarshalJSON implements the json.Marshaller interface.
func (arrayOrString ArrayOrString) MarshalJSON() ([]byte, error) {
	switch arrayOrString.Type {
	case ParamTypeString:
		return json.Marshal(arrayOrString.StringVal)
	case ParamTypeArray:
		return json.Marshal(arrayOrString.ArrayVal)
	default:
		return []byte{}, fmt.Errorf("impossible ArrayOrString.Type: %q", arrayOrString.Type)
	}
}

func (arrayOrString *ArrayOrString) ApplyReplacements(stringReplacements map[string]string, arrayReplacements map[string][]string) {
	if arrayOrString.Type == ParamTypeString {
		arrayOrString.StringVal = ApplyReplacements(arrayOrString.StringVal, stringReplacements)
	} else {
		var newArrayVal []string
		for _, v := range arrayOrString.ArrayVal {
			newArrayVal = append(newArrayVal, ApplyArrayReplacements(v, stringReplacements, arrayReplacements)...)
		}
		arrayOrString.ArrayVal = newArrayVal
	}
}

// NewArrayOrString creates an ArrayOrString of type ParamTypeString or ParamTypeArray, based on
// how many inputs are given (>1 input will create an array, not string).
func NewArrayOrString(value string, values ...string) ArrayOrString {
	if len(values) > 0 {
		values = append([]string{value}, values...)
		return ArrayOrString{
			Type:     ParamTypeArray,
			ArrayVal: values,
		}
	}
	return ArrayOrString{
		Type:      ParamTypeString,
		StringVal: value,
	}
}

// ResultRef is a type that represents a reference to a task run result
type ResultRef struct {
	PipelineTask string
	Result       string
}

const (
	resultExpressionFormat = "tasks.<taskName>.results.<resultName>"
	// ResultTaskPart Constant used to define the "tasks" part of a pipeline result reference
	ResultTaskPart = "tasks"
	// ResultResultPart Constant used to define the "results" part of a pipeline result reference
	ResultResultPart           = "results"
	variableSubstitutionFormat = `\$\([A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\)`
)

var variableSubstitutionRegex = regexp.MustCompile(variableSubstitutionFormat)

// NewResultRefs extracts all ResultReferences from param.
// If the ResultReference can be extracted, they are returned. Otherwise an error is returned
func NewResultRefs(param Param) ([]*ResultRef, error) {
	substitutionExpressions, ok := getVarSubstitutionExpressions(param)
	if !ok {
		return nil, fmt.Errorf("Invalid result reference expression: must contain variable substitution %q", resultExpressionFormat)
	}
	var resultRefs []*ResultRef
	for _, expression := range substitutionExpressions {
		pipelineTask, result, err := parseExpression(expression)
		if err != nil {
			return nil, fmt.Errorf("Invalid result reference expression: %v", err)
		}
		resultRefs = append(resultRefs, &ResultRef{
			PipelineTask: pipelineTask,
			Result:       result,
		})
	}
	return resultRefs, nil
}

// LooksLikeContainsResultRefs attempts to check if param looks like it contains any
// result references.
// This is useful if we want to make sure the param looks like a ResultReference before
// performing strict validation
func LooksLikeContainsResultRefs(param Param) bool {
	if param.Value.Type != ParamTypeString {
		return false
	}
	extractedExpressions, ok := getVarSubstitutionExpressions(param)
	if !ok {
		return false
	}
	for _, expression := range extractedExpressions {
		if looksLikeResultRef(expression) {
			return true
		}
	}
	return false
}

func looksLikeResultRef(expression string) bool {
	return strings.HasPrefix(expression, "task") && strings.Contains(expression, ".result")
}

// getVarSubstitutionExpressions extracts all the value between "$(" and ")""
func getVarSubstitutionExpressions(param Param) ([]string, bool) {
	if param.Value.Type != ParamTypeString {
		return nil, false
	}
	expressions := variableSubstitutionRegex.FindAllString(param.Value.StringVal, -1)
	if expressions == nil {
		return nil, false
	}
	var allExpressions []string
	for _, expression := range expressions {
		allExpressions = append(allExpressions, stripVarSubExpression(expression))
	}
	return allExpressions, true
}

func stripVarSubExpression(expression string) string {
	return strings.TrimSuffix(strings.TrimPrefix(expression, "$("), ")")
}

func parseExpression(substitutionExpression string) (string, string, error) {
	subExpressions := strings.Split(substitutionExpression, ".")
	if len(subExpressions) != 4 || subExpressions[0] != ResultTaskPart || subExpressions[2] != ResultResultPart {
		return "", "", fmt.Errorf("Must be of the form %q", resultExpressionFormat)
	}
	return subExpressions[1], subExpressions[3], nil
}
<|endoftext|>