<commit_before>package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "strings"
    "time"

    _ "github.com/mattn/go-sqlite3"
    _ "github.com/ziutek/mymysql/godrv"
)

// evo_categories
type Category struct {
    id        int64
    parent_id int64
    name      string
    url       string
}

// evo_items__item
type Post struct {
    id        int64
    id_sqlite int64
    date      time.Time
    body      string
    title     string
    url       string
}

// evo_comments
type Comment struct {
    postId        int64
    postId_sqlite int64
    authorId      sql.NullInt64
    author        sql.NullString
    authorEmail   sql.NullString
    authorUrl     sql.NullString
    authorIp      sql.NullString
    date          time.Time
    content       string
}

// evo_users
type User struct {
    id        int64
    id_sqlite int64
    login     string
    firstname string
    lastname  string
    nickname  string
    email     string
    url       string
    ip        string
}

type DbConfig map[string]string

func xferPosts(sconn, mconn *sql.DB) (posts []*Post, err error) {
    rows, err := mconn.Query(`select post_ID, post_datecreated, post_content,
                                     post_title, post_urltitle
                              from evo_items__item
                              where post_creator_user_ID=?`, 15)
    //posts := make([]*Post, 0, 10)
    for rows.Next() {
        var p Post
        err = rows.Scan(&p.id, &p.date, &p.body, &p.title, &p.url)
        if err != nil {
            fmt.Printf("err: %s\n" + err.Error())
        }
        posts = append(posts, &p)
    }
    fmt.Printf("#posts: %d\n", len(posts))
    xaction, err := sconn.Begin()
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, p := range posts {
        stmt, err := xaction.Prepare(`insert into post
                                      (author_id, title, date, url, body)
                                      values(?, ?, ?, ?, ?)`)
        if err != nil {
            fmt.Println(err)
            return posts, err
        }
        defer stmt.Close()
        result, err := stmt.Exec(1, p.title, p.date.Unix(), p.url, p.body)
        p.id_sqlite, _ = result.LastInsertId()
        //fmt.Printf("%+v\n", p)
        //fmt.Printf("%q | %q\n", p.title, p.url)
    }
    xaction.Commit()
    return posts, err
}

func xferComments(sconn, mconn *sql.DB, posts []*Post) {
    comms := make([]*Comment, 0, 10)
    for _, p := range posts {
        rows, err := mconn.Query(`select comment_post_ID, comment_author_ID,
                                         comment_author, comment_author_email,
                                         comment_author_url, comment_author_IP,
                                         comment_date, comment_content
                                  from evo_comments
                                  where comment_post_ID=?`, p.id)
        for rows.Next() {
            var c Comment
            err = rows.Scan(&c.postId, &c.authorId, &c.author, &c.authorEmail,
                &c.authorUrl, &c.authorIp, &c.date, &c.content)
            if err != nil {
                fmt.Printf("err: %s\n" + err.Error())
            }
            c.postId_sqlite = p.id_sqlite
            comms = append(comms, &c)
        }
    }
    fmt.Printf("#comms: %d\n", len(comms))
    xaction, err := sconn.Begin()
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, c := range comms {
        //fmt.Printf("%+v\n", c)
        authorId, err := getCommenterId(xaction, mconn, c)
        if err == sql.ErrNoRows {
            insertCommenter, _ := xaction.Prepare(`insert into commenter
                                                   (name, email, www, ip)
                                                   values (?, ?, ?, ?)`)
            defer insertCommenter.Close()
            ip := ""
            if c.authorIp.Valid {
                ip = c.authorIp.String
            }
            result, err := insertCommenter.Exec(c.author, c.authorEmail,
                c.authorUrl, ip)
            if err != nil {
                fmt.Println("Failed to insert commenter: " + err.Error())
            }
            authorId, err = result.LastInsertId()
            if err != nil {
                fmt.Println("Failed to insert commenter: " + err.Error())
            }
        } else if err != nil {
            fmt.Println("err: " + err.Error())
            continue
        }
        stmt, err := xaction.Prepare(`insert into comment
                                      (commenter_id, post_id, timestamp, body)
                                      values(?, ?, ?, ?)`)
        defer stmt.Close()
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        _, err = stmt.Exec(authorId, c.postId_sqlite, c.date.Unix(), c.content)
        if c.authorId.Int64 == 10 {
            //fmt.Printf("%+v\n", c)
        }
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
    }
    xaction.Commit()
}

func getCommenterId(xaction *sql.Tx, mconn *sql.DB, comment *Comment) (id int64, err error) {
    if comment.authorId.Valid {
        query, err := mconn.Prepare(`select user_nickname, user_email,
                                              user_url, user_ip
                                       from evo_users
                                       where user_ID=?`)
        defer query.Close()
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        err = query.QueryRow(comment.authorId.Int64).Scan(&comment.author,
            &comment.authorEmail,
            &comment.authorUrl,
            &comment.authorIp)
    }
    query, _ := xaction.Prepare(`select c.id from commenter as c
                                 where c.email like ?`)
    defer query.Close()
    err = query.QueryRow(comment.authorEmail.String).Scan(&id)
    return
}

func xferTags(sconn, mconn *sql.DB, posts []*Post) {
    for _, p := range posts {
        rows, err := mconn.Query(`select t.tag_name
                                  from evo_items__tag as t,
                                       evo_items__itemtag as it
                                  where t.tag_ID=it.itag_tag_ID
                                    and it.itag_itm_ID=?`, p.id)
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        for rows.Next() {
            var tag string
            err = rows.Scan(&tag)
            if err != nil {
                fmt.Printf("err: %s\n" + err.Error())
            }
            fixedTag := strings.Replace(tag, " ", "-", -1)
            row := sconn.QueryRow(`select id from tag where url=?`, fixedTag)
            var tagId int64
            err = row.Scan(&tagId)
            if err != nil {
                if err == sql.ErrNoRows {
                    stmt, err := sconn.Prepare(`insert into tag
                                                (name, url)
                                                values(?, ?)`)
                    if err != nil {
                        fmt.Println(err)
                        continue
                    }
                    defer stmt.Close()
                    result, err := stmt.Exec(strings.Title(tag), fixedTag)
                    tagId, _ = result.LastInsertId()
                } else {
                    fmt.Printf("err: %s\n", err.Error())
                }
            }
            stmt, err := sconn.Prepare(`insert into tagmap
                                        (tag_id, post_id)
                                        values(?, ?)`)
            if err != nil {
                fmt.Println(err)
                continue
            }
            defer stmt.Close()
            _, err = stmt.Exec(tagId, p.id_sqlite)
            if err != nil {
                fmt.Printf("err inserting tagmap: %s\n", err.Error())
            }
        }
    }
}

func readConf(path string) (db, uname, passwd string) {
    b, err := ioutil.ReadFile(path)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    var config DbConfig
    err = json.Unmarshal(b, &config)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    return config["db_name"], config["uname"], config["passwd"]
}

func importLegacyDb(sqliteFile, dbConf string) {
    db, uname, passwd := readConf(dbConf)
    mconn, err := sql.Open("mymysql", fmt.Sprintf("%s/%s/%s", db, uname, passwd))
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    defer mconn.Close()
    sconn, err := sql.Open("sqlite3", sqliteFile)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    defer sconn.Close()
    stmt, _ := sconn.Prepare("insert into author(id, disp_name, full_name, email, www) values(?, ?, ?, ?, ?)")
    defer stmt.Close()
    stmt.Exec(1, "rtfb", "Vytautas Šaltenis", "vytas@rtfb.lt", "http://rtfb.lt")
    row := mconn.QueryRow(`select blog_shortname, blog_name, blog_owner_user_ID
                           from evo_blogs where blog_ID=?`, 19)
    shortname := ""
    name := ""
    uid := 0
    err = row.Scan(&shortname, &name, &uid)
    if err != nil {
        fmt.Printf("err: " + err.Error())
    } else {
        fmt.Printf("shortname: %q, name: %q, id=%d\n", shortname, name, uid)
    }
    rows, err := mconn.Query(`select cat_ID, cat_parent_ID,
                                     cat_name, cat_urlname
                              from evo_categories
                              where cat_blog_ID=?`, 19)
    cat := make([]*Category, 0, 10)
    var id int64
    var parent_id sql.NullInt64
    var url string
    for rows.Next() {
        err = rows.Scan(&id, &parent_id, &name, &url)
        if err != nil {
            fmt.Printf("err: %s\n" + err.Error())
        }
        cat = append(cat, &Category{id, parent_id.Int64, name, url})
    }
    fmt.Printf("#categories: %d\n", len(cat))
    for _, c := range cat {
        fmt.Printf("%+v\n", c)
    }
    posts, err := xferPosts(sconn, mconn)
    if err != nil {
        fmt.Printf("err: " + err.Error())
    }
    xferComments(sconn, mconn, posts)
    xferTags(sconn, mconn, posts)
}
<commit_msg>dbtool: fixup bodies of legacy posts upon import<commit_after>package main

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "strings"
    "time"

    _ "github.com/mattn/go-sqlite3"
    _ "github.com/ziutek/mymysql/godrv"
)

// evo_categories
type Category struct {
    id        int64
    parent_id int64
    name      string
    url       string
}

// evo_items__item
type Post struct {
    id        int64
    id_sqlite int64
    date      time.Time
    body      string
    title     string
    url       string
}

// evo_comments
type Comment struct {
    postId        int64
    postId_sqlite int64
    authorId      sql.NullInt64
    author        sql.NullString
    authorEmail   sql.NullString
    authorUrl     sql.NullString
    authorIp      sql.NullString
    date          time.Time
    content       string
}

// evo_users
type User struct {
    id        int64
    id_sqlite int64
    login     string
    firstname string
    lastname  string
    nickname  string
    email     string
    url       string
    ip        string
}

type DbConfig map[string]string

func xferPosts(sconn, mconn *sql.DB) (posts []*Post, err error) {
    rows, err := mconn.Query(`select post_ID, post_datecreated, post_content,
                                     post_title, post_urltitle
                              from evo_items__item
                              where post_creator_user_ID=?`, 15)
    //posts := make([]*Post, 0, 10)
    for rows.Next() {
        var p Post
        err = rows.Scan(&p.id, &p.date, &p.body, &p.title, &p.url)
        if err != nil {
            fmt.Printf("err: %s\n" + err.Error())
        }
        posts = append(posts, &p)
    }
    fmt.Printf("#posts: %d\n", len(posts))
    xaction, err := sconn.Begin()
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, p := range posts {
        stmt, err := xaction.Prepare(`insert into post
                                      (author_id, title, date, url, body)
                                      values(?, ?, ?, ?, ?)`)
        if err != nil {
            fmt.Println(err)
            return posts, err
        }
        defer stmt.Close()
        newBody := fixupBody(p.body)
        result, err := stmt.Exec(1, p.title, p.date.Unix(), p.url, newBody)
        p.id_sqlite, _ = result.LastInsertId()
        //fmt.Printf("%+v\n", p)
        //fmt.Printf("%q | %q\n", p.title, p.url)
    }
    xaction.Commit()
    return posts, err
}

func fixupBody(obody string) (nbody string) {
    nbody = fixupPre(obody)
    nbody = fixupTt(nbody)
    nbody = fixupOl(nbody)
    return
}

func fixupPre(obody string) (nbody string) {
    ilines := strings.Split(obody, "\n")
    olines := make([]string, 0, len(ilines))
    inPre := false
    for _, line := range ilines {
        if strings.Contains(line, "
") {
            inPre = true
            line = strings.Replace(line, "
", "", -1)
        }
        if strings.Contains(line, "
") {
            inPre = false
            line = strings.Replace(line, "
", "", -1)
        }
        if inPre {
            olines = append(olines, "    " + line)
        } else {
            olines = append(olines, line)
        }
    }
    nbody = strings.Join(olines, "\n")
    return
}

func fixupTt(obody string) (nbody string) {
    nbody = strings.Replace(obody, "<tt>", "`", -1)
    nbody = strings.Replace(nbody, "</tt>", "`", -1)
    return
}

func fixupOl(obody string) (nbody string) {
    ilines := strings.Split(obody, "\n")
    olines := make([]string, 0, len(ilines))
    inList := false
    for _, line := range ilines {
        if strings.Contains(line, "<ol") {
            inList = true
        }
        if strings.Contains(line, "
") {
            inList = false
            s := strings.Replace(strings.TrimSpace(line), "
", "1. ", -1)
            s = strings.Replace(s, "
", "", -1)
            s = strings.Replace(s, "
", "", -1)
            olines = append(olines, s)
            continue
        }
        if inList && strings.TrimSpace(line) == "" {
            continue
        } else if inList {
            s := strings.Replace(strings.TrimSpace(line), "
", "1. ", -1)
            s = strings.Replace(s, "
", "", -1)
            s = strings.Replace(s, "
", "", -1)
            s = strings.Replace(s, "
", "", -1)
            s = strings.Replace(s, "
", "", -1)
            olines = append(olines, s)
        } else {
            olines = append(olines, line)
        }
    }
    nbody = strings.Join(olines, "\n")
    return
}

func xferComments(sconn, mconn *sql.DB, posts []*Post) {
    comms := make([]*Comment, 0, 10)
    for _, p := range posts {
        rows, err := mconn.Query(`select comment_post_ID, comment_author_ID,
                                         comment_author, comment_author_email,
                                         comment_author_url, comment_author_IP,
                                         comment_date, comment_content
                                  from evo_comments
                                  where comment_post_ID=?`, p.id)
        for rows.Next() {
            var c Comment
            err = rows.Scan(&c.postId, &c.authorId, &c.author, &c.authorEmail,
                &c.authorUrl, &c.authorIp, &c.date, &c.content)
            if err != nil {
                fmt.Printf("err: %s\n" + err.Error())
            }
            c.postId_sqlite = p.id_sqlite
            comms = append(comms, &c)
        }
    }
    fmt.Printf("#comms: %d\n", len(comms))
    xaction, err := sconn.Begin()
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, c := range comms {
        //fmt.Printf("%+v\n", c)
        authorId, err := getCommenterId(xaction, mconn, c)
        if err == sql.ErrNoRows {
            insertCommenter, _ := xaction.Prepare(`insert into commenter
                                                   (name, email, www, ip)
                                                   values (?, ?, ?, ?)`)
            defer insertCommenter.Close()
            ip := ""
            if c.authorIp.Valid {
                ip = c.authorIp.String
            }
            result, err := insertCommenter.Exec(c.author, c.authorEmail,
                c.authorUrl, ip)
            if err != nil {
                fmt.Println("Failed to insert commenter: " + err.Error())
            }
            authorId, err = result.LastInsertId()
            if err != nil {
                fmt.Println("Failed to insert commenter: " + err.Error())
            }
        } else if err != nil {
            fmt.Println("err: " + err.Error())
            continue
        }
        stmt, err := xaction.Prepare(`insert into comment
                                      (commenter_id, post_id, timestamp, body)
                                      values(?, ?, ?, ?)`)
        defer stmt.Close()
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        _, err = stmt.Exec(authorId, c.postId_sqlite, c.date.Unix(), c.content)
        if c.authorId.Int64 == 10 {
            //fmt.Printf("%+v\n", c)
        }
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
    }
    xaction.Commit()
}

func getCommenterId(xaction *sql.Tx, mconn *sql.DB, comment *Comment) (id int64, err error) {
    if comment.authorId.Valid {
        query, err := mconn.Prepare(`select user_nickname, user_email,
                                              user_url, user_ip
                                       from evo_users
                                       where user_ID=?`)
        defer query.Close()
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        err = query.QueryRow(comment.authorId.Int64).Scan(&comment.author,
            &comment.authorEmail,
            &comment.authorUrl,
            &comment.authorIp)
    }
    query, _ := xaction.Prepare(`select c.id from commenter as c
                                 where c.email like ?`)
    defer query.Close()
    err = query.QueryRow(comment.authorEmail.String).Scan(&id)
    return
}

func xferTags(sconn, mconn *sql.DB, posts []*Post) {
    for _, p := range posts {
        rows, err := mconn.Query(`select t.tag_name
                                  from evo_items__tag as t,
                                       evo_items__itemtag as it
                                  where t.tag_ID=it.itag_tag_ID
                                    and it.itag_itm_ID=?`, p.id)
        if err != nil {
            fmt.Printf("err: %s\n", err.Error())
        }
        for rows.Next() {
            var tag string
            err = rows.Scan(&tag)
            if err != nil {
                fmt.Printf("err: %s\n" + err.Error())
            }
            fixedTag := strings.Replace(tag, " ", "-", -1)
            row := sconn.QueryRow(`select id from tag where url=?`, fixedTag)
            var tagId int64
            err = row.Scan(&tagId)
            if err != nil {
                if err == sql.ErrNoRows {
                    stmt, err := sconn.Prepare(`insert into tag
                                                (name, url)
                                                values(?, ?)`)
                    if err != nil {
                        fmt.Println(err)
                        continue
                    }
                    defer stmt.Close()
                    result, err := stmt.Exec(strings.Title(tag), fixedTag)
                    tagId, _ = result.LastInsertId()
                } else {
                    fmt.Printf("err: %s\n", err.Error())
                }
            }
            stmt, err := sconn.Prepare(`insert into tagmap
                                        (tag_id, post_id)
                                        values(?, ?)`)
            if err != nil {
                fmt.Println(err)
                continue
            }
            defer stmt.Close()
            _, err = stmt.Exec(tagId, p.id_sqlite)
            if err != nil {
                fmt.Printf("err inserting tagmap: %s\n", err.Error())
            }
        }
    }
}

func readConf(path string) (db, uname, passwd string) {
    b, err := ioutil.ReadFile(path)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    var config DbConfig
    err = json.Unmarshal(b, &config)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    return config["db_name"], config["uname"], config["passwd"]
}

func importLegacyDb(sqliteFile, dbConf string) {
    db, uname, passwd := readConf(dbConf)
    mconn, err := sql.Open("mymysql", fmt.Sprintf("%s/%s/%s", db, uname, passwd))
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    defer mconn.Close()
    sconn, err := sql.Open("sqlite3", sqliteFile)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
    defer sconn.Close()
    stmt, _ := sconn.Prepare("insert into author(id, disp_name, full_name, email, www) values(?, ?, ?, ?, ?)")
    defer stmt.Close()
    stmt.Exec(1, "rtfb", "Vytautas Šaltenis", "vytas@rtfb.lt", "http://rtfb.lt")
    row := mconn.QueryRow(`select blog_shortname, blog_name, blog_owner_user_ID
                           from evo_blogs where blog_ID=?`, 19)
    shortname := ""
    name := ""
    uid := 0
    err = row.Scan(&shortname, &name, &uid)
    if err != nil {
        fmt.Printf("err: " + err.Error())
    } else {
        fmt.Printf("shortname: %q, name: %q, id=%d\n", shortname, name, uid)
    }
    rows, err := mconn.Query(`select cat_ID, cat_parent_ID,
                                     cat_name, cat_urlname
                              from evo_categories
                              where cat_blog_ID=?`, 19)
    cat := make([]*Category, 0, 10)
    var id int64
    var parent_id sql.NullInt64
    var url string
    for rows.Next() {
        err = rows.Scan(&id, &parent_id, &name, &url)
        if err != nil {
            fmt.Printf("err: %s\n" + err.Error())
        }
        cat = append(cat, &Category{id, parent_id.Int64, name, url})
    }
    fmt.Printf("#categories: %d\n", len(cat))
    for _, c := range cat {
        fmt.Printf("%+v\n", c)
    }
    posts, err := xferPosts(sconn, mconn)
    if err != nil {
        fmt.Printf("err: " + err.Error())
    }
    xferComments(sconn, mconn, posts)
    xferTags(sconn, mconn, posts)
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// 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 dbus

import (
	"errors"
	"time"

	"github.com/godbus/dbus"
)

const (
	cleanIgnoreInterval = int64(10 * time.Second)
	ignoreInterval      = int64(30 * time.Millisecond)
)

// Subscribe sets up this connection to subscribe to all systemd dbus events.
// This is required before calling SubscribeUnits. When the connection closes
// systemd will automatically stop sending signals so there is no need to
// explicitly call Unsubscribe().
func (c *Conn) Subscribe() error {
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")

	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) dispatch() {
	ch := make(chan *dbus.Signal, signalBuffer)

	c.sigconn.Signal(ch)

	go func() {
		for {
			signal, ok := <-ch
			if !ok {
				return
			}

			if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
				c.jobComplete(signal)
			}

			if c.subStateSubscriber.updateCh == nil &&
				c.propertiesSubscriber.updateCh == nil {
				continue
			}

			var unitPath dbus.ObjectPath
			switch signal.Name {
			case "org.freedesktop.systemd1.Manager.JobRemoved":
				unitName := signal.Body[2].(string)
				c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
			case "org.freedesktop.systemd1.Manager.UnitNew":
				unitPath = signal.Body[1].(dbus.ObjectPath)
			case "org.freedesktop.DBus.Properties.PropertiesChanged":
				if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
					unitPath = signal.Path

					if len(signal.Body) >= 2 {
						if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok {
							c.sendPropertiesUpdate(unitPath, changed)
						}
					}
				}
			}

			if unitPath == dbus.ObjectPath("") {
				continue
			}

			c.sendSubStateUpdate(unitPath)
		}
	}()
}

// Returns two unbuffered channels which will receive all changed units every
// interval.  Deleted units are sent as nil.
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
	return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
}

// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
// size of the channels, the comparison function for detecting changes and a filter
// function for cutting down on the noise that your channel receives.
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
	old := make(map[string]*UnitStatus)
	statusChan := make(chan map[string]*UnitStatus, buffer)
	errChan := make(chan error, buffer)

	go func() {
		for {
			timerChan := time.After(interval)

			units, err := c.ListUnits()
			if err == nil {
				cur := make(map[string]*UnitStatus)
				for i := range units {
					if filterUnit != nil && filterUnit(units[i].Name) {
						continue
					}
					cur[units[i].Name] = &units[i]
				}

				// add all new or changed units
				changed := make(map[string]*UnitStatus)
				for n, u := range cur {
					if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
						changed[n] = u
					}
					delete(old, n)
				}

				// add all deleted units
				for oldN := range old {
					changed[oldN] = nil
				}

				old = cur

				if len(changed) != 0 {
					statusChan <- changed
				}
			} else {
				errChan <- err
			}

			<-timerChan
		}
	}()

	return statusChan, errChan
}

type SubStateUpdate struct {
	UnitName string
	SubState string
}

// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
// Although this writes to updateCh on every state change, the reported state
// may be more recent than the change that generated it (due to an unavoidable
// race in the systemd dbus interface).  That is, this method provides a good
// way to keep a current view of all units' states, but is not guaranteed to
// show every state transition they go through.  Furthermore, state changes
// will only be written to the channel with non-blocking writes.  If updateCh
// is full, it attempts to write an error to errCh; if errCh is full, the error
// passes silently.
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
	c.subStateSubscriber.Lock()
	defer c.subStateSubscriber.Unlock()
	c.subStateSubscriber.updateCh = updateCh
	c.subStateSubscriber.errCh = errCh
}

func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
	c.subStateSubscriber.Lock()
	defer c.subStateSubscriber.Unlock()

	if c.subStateSubscriber.updateCh == nil {
		return
	}

	if c.shouldIgnore(unitPath) {
		return
	}

	info, err := c.GetUnitPathProperties(unitPath)
	if err != nil {
		select {
		case c.subStateSubscriber.errCh <- err:
		default:
		}
	}

	name := info["Id"].(string)
	substate := info["SubState"].(string)

	update := &SubStateUpdate{name, substate}
	select {
	case c.subStateSubscriber.updateCh <- update:
	default:
		select {
		case c.subStateSubscriber.errCh <- errors.New("update channel full!"):
		default:
		}
	}

	c.updateIgnore(unitPath, info)
}

// The ignore functions work around a wart in the systemd dbus interface.
// Requesting the properties of an unloaded unit will cause systemd to send a
// pair of UnitNew/UnitRemoved signals.  Because we need to get a unit's
// properties on UnitNew (as that's the only indication of a new unit coming up
// for the first time), we would enter an infinite loop if we did not attempt
// to detect and ignore these spurious signals.  The signal themselves are
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
// unloaded unit's signals for a short time after requesting its properties.
// This means that we will miss e.g. a transient unit being restarted
// *immediately* upon failure and also a transient unit being started
// immediately after requesting its status (with systemctl status, for example,
// because this causes a UnitNew signal to be sent which then causes us to fetch
// the properties).

func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
	t, ok := c.subStateSubscriber.ignore[path]
	return ok && t >= time.Now().UnixNano()
}

func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
	c.cleanIgnore()

	// unit is unloaded - it will trigger bad systemd dbus behavior
	if info["LoadState"].(string) == "not-found" {
		c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
	}
}

// without this, ignore would grow unboundedly over time
func (c *Conn) cleanIgnore() {
	now := time.Now().UnixNano()
	if c.subStateSubscriber.cleanIgnore < now {
		c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval

		for p, t := range c.subStateSubscriber.ignore {
			if t < now {
				delete(c.subStateSubscriber.ignore, p)
			}
		}
	}
}

// PropertiesUpdate holds a map of a unit's changed properties
type PropertiesUpdate struct {
	UnitName string
	Changed  map[string]dbus.Variant
}

// SetPropertiesSubscriber writes to updateCh when any unit's properties
// change. Every property change reported by systemd will be sent; that is, no
// transitions will be "missed" (as they might be with SetSubStateSubscriber).
// However, state changes will only be written to the channel with non-blocking
// writes.  If updateCh is full, it attempts to write an error to errCh; if
// errCh is full, the error passes silently.
func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) {
	c.propertiesSubscriber.Lock()
	defer c.propertiesSubscriber.Unlock()
	c.propertiesSubscriber.updateCh = updateCh
	c.propertiesSubscriber.errCh = errCh
}

// we don't need to worry about shouldIgnore() here because
// sendPropertiesUpdate doesn't call GetProperties()
func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) {
	c.propertiesSubscriber.Lock()
	defer c.propertiesSubscriber.Unlock()

	if c.propertiesSubscriber.updateCh == nil {
		return
	}

	update := &PropertiesUpdate{unitName(unitPath), changedProps}

	select {
	case c.propertiesSubscriber.updateCh <- update:
	default:
		select {
		case c.propertiesSubscriber.errCh <- errors.New("update channel is full"):
		default:
		}
		return
	}
}
<commit_msg>dbus: make SetSubStateSubscriber safer<commit_after>// Copyright 2015 CoreOS, Inc.
//
// 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 dbus

import (
	"errors"
	"log"
	"time"

	"github.com/godbus/dbus"
)

const (
	cleanIgnoreInterval = int64(10 * time.Second)
	ignoreInterval      = int64(30 * time.Millisecond)
)

// Subscribe sets up this connection to subscribe to all systemd dbus events.
// This is required before calling SubscribeUnits. When the connection closes
// systemd will automatically stop sending signals so there is no need to
// explicitly call Unsubscribe().
func (c *Conn) Subscribe() error {
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'")
	c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
		"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")

	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
	err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
	if err != nil {
		return err
	}

	return nil
}

func (c *Conn) dispatch() {
	ch := make(chan *dbus.Signal, signalBuffer)

	c.sigconn.Signal(ch)

	go func() {
		for {
			signal, ok := <-ch
			if !ok {
				return
			}

			if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" {
				c.jobComplete(signal)
			}

			if c.subStateSubscriber.updateCh == nil &&
				c.propertiesSubscriber.updateCh == nil {
				continue
			}

			var unitPath dbus.ObjectPath
			switch signal.Name {
			case "org.freedesktop.systemd1.Manager.JobRemoved":
				unitName := signal.Body[2].(string)
				c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath)
			case "org.freedesktop.systemd1.Manager.UnitNew":
				unitPath = signal.Body[1].(dbus.ObjectPath)
			case "org.freedesktop.DBus.Properties.PropertiesChanged":
				if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
					unitPath = signal.Path

					if len(signal.Body) >= 2 {
						if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok {
							c.sendPropertiesUpdate(unitPath, changed)
						}
					}
				}
			}

			if unitPath == dbus.ObjectPath("") {
				continue
			}

			c.sendSubStateUpdate(unitPath)
		}
	}()
}

// Returns two unbuffered channels which will receive all changed units every
// interval.  Deleted units are sent as nil.
func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) {
	return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil)
}

// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer
// size of the channels, the comparison function for detecting changes and a filter
// function for cutting down on the noise that your channel receives.
func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) {
	old := make(map[string]*UnitStatus)
	statusChan := make(chan map[string]*UnitStatus, buffer)
	errChan := make(chan error, buffer)

	go func() {
		for {
			timerChan := time.After(interval)

			units, err := c.ListUnits()
			if err == nil {
				cur := make(map[string]*UnitStatus)
				for i := range units {
					if filterUnit != nil && filterUnit(units[i].Name) {
						continue
					}
					cur[units[i].Name] = &units[i]
				}

				// add all new or changed units
				changed := make(map[string]*UnitStatus)
				for n, u := range cur {
					if oldU, ok := old[n]; !ok || isChanged(oldU, u) {
						changed[n] = u
					}
					delete(old, n)
				}

				// add all deleted units
				for oldN := range old {
					changed[oldN] = nil
				}

				old = cur

				if len(changed) != 0 {
					statusChan <- changed
				}
			} else {
				errChan <- err
			}

			<-timerChan
		}
	}()

	return statusChan, errChan
}

type SubStateUpdate struct {
	UnitName string
	SubState string
}

// SetSubStateSubscriber writes to updateCh when any unit's substate changes.
// Although this writes to updateCh on every state change, the reported state
// may be more recent than the change that generated it (due to an unavoidable
// race in the systemd dbus interface).  That is, this method provides a good
// way to keep a current view of all units' states, but is not guaranteed to
// show every state transition they go through.  Furthermore, state changes
// will only be written to the channel with non-blocking writes.  If updateCh
// is full, it attempts to write an error to errCh; if errCh is full, the error
// passes silently.
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
	if c == nil {
		msg := "nil receiver"
		select {
		case errCh <- errors.New(msg):
		default:
			log.Printf("full error channel while reporting: %s\n", msg)
		}
		return
	}

	c.subStateSubscriber.Lock()
	defer c.subStateSubscriber.Unlock()
	c.subStateSubscriber.updateCh = updateCh
	c.subStateSubscriber.errCh = errCh
}

func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
	c.subStateSubscriber.Lock()
	defer c.subStateSubscriber.Unlock()

	if c.subStateSubscriber.updateCh == nil {
		return
	}

	isIgnored := c.shouldIgnore(unitPath)
	defer c.cleanIgnore()
	if isIgnored {
		return
	}

	info, err := c.GetUnitPathProperties(unitPath)
	if err != nil {
		select {
		case c.subStateSubscriber.errCh <- err:
		default:
			log.Printf("full error channel while reporting: %s\n", err)
		}
		return
	}
	defer c.updateIgnore(unitPath, info)

	name, ok := info["Id"].(string)
	if !ok {
		msg := "failed to cast info.Id"
		select {
		case c.subStateSubscriber.errCh <- errors.New(msg):
		default:
			log.Printf("full error channel while reporting: %s\n", err)
		}
		return
	}
	substate, ok := info["SubState"].(string)
	if !ok {
		msg := "failed to cast info.SubState"
		select {
		case c.subStateSubscriber.errCh <- errors.New(msg):
		default:
			log.Printf("full error channel while reporting: %s\n", msg)
		}
		return
	}

	update := &SubStateUpdate{name, substate}
	select {
	case c.subStateSubscriber.updateCh <- update:
	default:
		msg := "update channel is full"
		select {
		case c.subStateSubscriber.errCh <- errors.New(msg):
		default:
			log.Printf("full error channel while reporting: %s\n", msg)
		}
		return
	}
}

// The ignore functions work around a wart in the systemd dbus interface.
// Requesting the properties of an unloaded unit will cause systemd to send a
// pair of UnitNew/UnitRemoved signals.  Because we need to get a unit's
// properties on UnitNew (as that's the only indication of a new unit coming up
// for the first time), we would enter an infinite loop if we did not attempt
// to detect and ignore these spurious signals.  The signal themselves are
// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an
// unloaded unit's signals for a short time after requesting its properties.
// This means that we will miss e.g. a transient unit being restarted
// *immediately* upon failure and also a transient unit being started
// immediately after requesting its status (with systemctl status, for example,
// because this causes a UnitNew signal to be sent which then causes us to fetch
// the properties).

func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
	t, ok := c.subStateSubscriber.ignore[path]
	return ok && t >= time.Now().UnixNano()
}

func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
	loadState, ok := info["LoadState"].(string)
	if !ok {
		return
	}

	// unit is unloaded - it will trigger bad systemd dbus behavior
	if loadState == "not-found" {
		c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
	}
}

// without this, ignore would grow unboundedly over time
func (c *Conn) cleanIgnore() {
	now := time.Now().UnixNano()
	if c.subStateSubscriber.cleanIgnore < now {
		c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval

		for p, t := range c.subStateSubscriber.ignore {
			if t < now {
				delete(c.subStateSubscriber.ignore, p)
			}
		}
	}
}

// PropertiesUpdate holds a map of a unit's changed properties
type PropertiesUpdate struct {
	UnitName string
	Changed  map[string]dbus.Variant
}

// SetPropertiesSubscriber writes to updateCh when any unit's properties
// change. Every property change reported by systemd will be sent; that is, no
// transitions will be "missed" (as they might be with SetSubStateSubscriber).
// However, state changes will only be written to the channel with non-blocking
// writes.  If updateCh is full, it attempts to write an error to errCh; if
// errCh is full, the error passes silently.
func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) {
	c.propertiesSubscriber.Lock()
	defer c.propertiesSubscriber.Unlock()
	c.propertiesSubscriber.updateCh = updateCh
	c.propertiesSubscriber.errCh = errCh
}

// we don't need to worry about shouldIgnore() here because
// sendPropertiesUpdate doesn't call GetProperties()
func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) {
	c.propertiesSubscriber.Lock()
	defer c.propertiesSubscriber.Unlock()

	if c.propertiesSubscriber.updateCh == nil {
		return
	}

	update := &PropertiesUpdate{unitName(unitPath), changedProps}

	select {
	case c.propertiesSubscriber.updateCh <- update:
	default:
		msg := "update channel is full"
		select {
		case c.propertiesSubscriber.errCh <- errors.New(msg):
		default:
			log.Printf("full error channel while reporting: %s\n", msg)
		}
		return
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Cockroach 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.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package sqlbase

import (
	"testing"

	"github.com/cockroachdb/cockroach/keys"
	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/sql/privilege"
	"github.com/cockroachdb/cockroach/util/leaktest"
)

func TestPrivilege(t *testing.T) {
	defer leaktest.AfterTest(t)()
	descriptor := NewDefaultPrivilegeDescriptor()

	testCases := []struct {
		grantee       string // User to grant/revoke privileges on.
		grant, revoke privilege.List
		show          []UserPrivilegeString
	}{
		{"", nil, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{security.RootUser, privilege.List{privilege.ALL}, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{security.RootUser, privilege.List{privilege.INSERT, privilege.DROP}, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{"foo", privilege.List{privilege.INSERT, privilege.DROP}, nil,
			[]UserPrivilegeString{{"foo", "DROP,INSERT"}, {security.RootUser, "ALL"}},
		},
		{"bar", nil, privilege.List{privilege.INSERT, privilege.ALL},
			[]UserPrivilegeString{{"foo", "DROP,INSERT"}, {security.RootUser, "ALL"}},
		},
		{"foo", privilege.List{privilege.ALL}, nil,
			[]UserPrivilegeString{{"foo", "ALL"}, {security.RootUser, "ALL"}},
		},
		{"foo", nil, privilege.List{privilege.SELECT, privilege.INSERT},
			[]UserPrivilegeString{{"foo", "CREATE,DELETE,DROP,GRANT,UPDATE"}, {security.RootUser, "ALL"}},
		},
		{"foo", nil, privilege.List{privilege.ALL},
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		// Validate checks that root still has ALL privileges, but we do not call it here.
		{security.RootUser, nil, privilege.List{privilege.ALL},
			[]UserPrivilegeString{},
		},
	}

	for tcNum, tc := range testCases {
		if tc.grantee != "" {
			if tc.grant != nil {
				descriptor.Grant(tc.grantee, tc.grant)
			}
			if tc.revoke != nil {
				descriptor.Revoke(tc.grantee, tc.revoke)
			}
		}
		show := descriptor.Show()
		if len(show) != len(tc.show) {
			t.Fatalf("#%d: show output for descriptor %+v differs, got: %+v, expected %+v",
				tcNum, descriptor, show, tc.show)
		}
		for i := 0; i < len(show); i++ {
			if show[i].User != tc.show[i].User || show[i].Privileges != tc.show[i].Privileges {
				t.Fatalf("#%d: show output for descriptor %+v differs, got: %+v, expected %+v",
					tcNum, descriptor, show, tc.show)
			}
		}
	}
}

func TestCheckPrivilege(t *testing.T) {
	defer leaktest.AfterTest(t)()

	testCases := []struct {
		pd   *PrivilegeDescriptor
		user string
		priv privilege.Kind
		exp  bool
	}{
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.CREATE, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"bar", privilege.CREATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"bar", privilege.DROP, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.DROP, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.ALL}),
			"foo", privilege.CREATE, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.ALL, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.ALL}),
			"foo", privilege.ALL, true},
		{NewPrivilegeDescriptor("foo", privilege.List{}),
			"foo", privilege.ALL, false},
		{NewPrivilegeDescriptor("foo", privilege.List{}),
			"foo", privilege.CREATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"foo", privilege.UPDATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"foo", privilege.DROP, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.ALL}),
			"foo", privilege.DROP, true},
	}

	for tcNum, tc := range testCases {
		if found := tc.pd.CheckPrivilege(tc.user, tc.priv); found != tc.exp {
			t.Errorf("#%d: CheckPrivilege(%s, %v) for descriptor %+v = %t, expected %t",
				tcNum, tc.user, tc.priv, tc.pd, found, tc.exp)
		}
	}
}

// TestPrivilegeValidate exercises validation for non-system descriptors.
func TestPrivilegeValidate(t *testing.T) {
	defer leaktest.AfterTest(t)()
	id := ID(keys.MaxReservedDescID + 1)
	descriptor := NewDefaultPrivilegeDescriptor()
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Grant("foo", privilege.List{privilege.ALL})
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Grant(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Revoke(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
	// TODO(marc): validate fails here because we do not aggregate
	// privileges into ALL when all are set.
	descriptor.Grant(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
	descriptor.Revoke(security.RootUser, privilege.List{privilege.ALL})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
}

// TestSystemPrivilegeValidate exercises validation for system config
// descriptors. We use 1 (the system database ID).
func TestSystemPrivilegeValidate(t *testing.T) {
	defer leaktest.AfterTest(t)()
	id := ID(1)
	allowedPrivileges := systemConfigAllowedPrivileges[id]

	hasPrivilege := func(pl privilege.List, p privilege.Kind) bool {
		for _, i := range pl {
			if i == p {
				return true
			}
		}
		return false
	}

	// Exhaustively grant/revoke all privileges.
	// Due to the way validation is done after Grant/Revoke,
	// we need to revert the just-performed change after errors.
	descriptor := NewPrivilegeDescriptor(security.RootUser, allowedPrivileges)
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	for _, p := range privilege.ByValue {
		if hasPrivilege(allowedPrivileges, p) {
			// Grant allowed privileges. Either they are already
			// on (noop), or they're accepted.
			descriptor.Grant(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err != nil {
				t.Fatal(err)
			}
			descriptor.Grant("foo", privilege.List{p})
			if err := descriptor.Validate(id); err != nil {
				t.Fatal(err)
			}

			// Remove allowed privileges. This fails for root,
			// but passes for other users.
			descriptor.Revoke(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Grant(security.RootUser, privilege.List{p})
		} else {
			// Granting non-allowed privileges always.
			descriptor.Grant(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Revoke(security.RootUser, privilege.List{p})
			descriptor.Grant(security.RootUser, allowedPrivileges)

			descriptor.Grant("foo", privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Revoke("foo", privilege.List{p})
			descriptor.Grant("foo", allowedPrivileges)

			// Revoking non-allowed privileges always succeeds,
			// except when removing ALL for root.
			if p == privilege.ALL {
				// We need to reset privileges as Revoke(ALL) will clear
				// all bits.
				descriptor.Revoke(security.RootUser, privilege.List{p})
				if err := descriptor.Validate(id); err == nil {
					t.Fatal("unexpected success")
				}
				descriptor.Grant(security.RootUser, allowedPrivileges)
			} else {
				descriptor.Revoke(security.RootUser, privilege.List{p})
				if err := descriptor.Validate(id); err != nil {
					t.Fatal(err)
				}
			}
		}

		// We can always revoke anything from non-root users.
		descriptor.Revoke("foo", privilege.List{p})
		if err := descriptor.Validate(id); err != nil {
			t.Fatal(err)
		}
	}
}
<commit_msg>sqlbase: Add unit test for PrivilegeDescriptor.AnyPrivilege<commit_after>// Copyright 2015 The Cockroach 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.
//
// Author: Marc Berhault (marc@cockroachlabs.com)

package sqlbase

import (
	"testing"

	"github.com/cockroachdb/cockroach/keys"
	"github.com/cockroachdb/cockroach/security"
	"github.com/cockroachdb/cockroach/sql/privilege"
	"github.com/cockroachdb/cockroach/util/leaktest"
)

func TestPrivilege(t *testing.T) {
	defer leaktest.AfterTest(t)()
	descriptor := NewDefaultPrivilegeDescriptor()

	testCases := []struct {
		grantee       string // User to grant/revoke privileges on.
		grant, revoke privilege.List
		show          []UserPrivilegeString
	}{
		{"", nil, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{security.RootUser, privilege.List{privilege.ALL}, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{security.RootUser, privilege.List{privilege.INSERT, privilege.DROP}, nil,
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		{"foo", privilege.List{privilege.INSERT, privilege.DROP}, nil,
			[]UserPrivilegeString{{"foo", "DROP,INSERT"}, {security.RootUser, "ALL"}},
		},
		{"bar", nil, privilege.List{privilege.INSERT, privilege.ALL},
			[]UserPrivilegeString{{"foo", "DROP,INSERT"}, {security.RootUser, "ALL"}},
		},
		{"foo", privilege.List{privilege.ALL}, nil,
			[]UserPrivilegeString{{"foo", "ALL"}, {security.RootUser, "ALL"}},
		},
		{"foo", nil, privilege.List{privilege.SELECT, privilege.INSERT},
			[]UserPrivilegeString{{"foo", "CREATE,DELETE,DROP,GRANT,UPDATE"}, {security.RootUser, "ALL"}},
		},
		{"foo", nil, privilege.List{privilege.ALL},
			[]UserPrivilegeString{{security.RootUser, "ALL"}},
		},
		// Validate checks that root still has ALL privileges, but we do not call it here.
		{security.RootUser, nil, privilege.List{privilege.ALL},
			[]UserPrivilegeString{},
		},
	}

	for tcNum, tc := range testCases {
		if tc.grantee != "" {
			if tc.grant != nil {
				descriptor.Grant(tc.grantee, tc.grant)
			}
			if tc.revoke != nil {
				descriptor.Revoke(tc.grantee, tc.revoke)
			}
		}
		show := descriptor.Show()
		if len(show) != len(tc.show) {
			t.Fatalf("#%d: show output for descriptor %+v differs, got: %+v, expected %+v",
				tcNum, descriptor, show, tc.show)
		}
		for i := 0; i < len(show); i++ {
			if show[i].User != tc.show[i].User || show[i].Privileges != tc.show[i].Privileges {
				t.Fatalf("#%d: show output for descriptor %+v differs, got: %+v, expected %+v",
					tcNum, descriptor, show, tc.show)
			}
		}
	}
}

func TestCheckPrivilege(t *testing.T) {
	defer leaktest.AfterTest(t)()

	testCases := []struct {
		pd   *PrivilegeDescriptor
		user string
		priv privilege.Kind
		exp  bool
	}{
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.CREATE, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"bar", privilege.CREATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"bar", privilege.DROP, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.DROP, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.ALL}),
			"foo", privilege.CREATE, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", privilege.ALL, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.ALL}),
			"foo", privilege.ALL, true},
		{NewPrivilegeDescriptor("foo", privilege.List{}),
			"foo", privilege.ALL, false},
		{NewPrivilegeDescriptor("foo", privilege.List{}),
			"foo", privilege.CREATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"foo", privilege.UPDATE, false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"foo", privilege.DROP, true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.ALL}),
			"foo", privilege.DROP, true},
	}

	for tcNum, tc := range testCases {
		if found := tc.pd.CheckPrivilege(tc.user, tc.priv); found != tc.exp {
			t.Errorf("#%d: CheckPrivilege(%s, %v) for descriptor %+v = %t, expected %t",
				tcNum, tc.user, tc.priv, tc.pd, found, tc.exp)
		}
	}
}

func TestAnyPrivilege(t *testing.T) {
	defer leaktest.AfterTest(t)()

	testCases := []struct {
		pd   *PrivilegeDescriptor
		user string
		exp  bool
	}{
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"foo", true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE}),
			"bar", false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.ALL}),
			"foo", true},
		{NewPrivilegeDescriptor("foo", privilege.List{}),
			"foo", false},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"foo", true},
		{NewPrivilegeDescriptor("foo", privilege.List{privilege.CREATE, privilege.DROP}),
			"bar", false},
	}

	for tcNum, tc := range testCases {
		if found := tc.pd.AnyPrivilege(tc.user); found != tc.exp {
			t.Errorf("#%d: AnyPrivilege(%s) for descriptor %+v = %t, expected %t",
				tcNum, tc.user, tc.pd, found, tc.exp)
		}
	}
}

// TestPrivilegeValidate exercises validation for non-system descriptors.
func TestPrivilegeValidate(t *testing.T) {
	defer leaktest.AfterTest(t)()
	id := ID(keys.MaxReservedDescID + 1)
	descriptor := NewDefaultPrivilegeDescriptor()
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Grant("foo", privilege.List{privilege.ALL})
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Grant(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	descriptor.Revoke(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
	// TODO(marc): validate fails here because we do not aggregate
	// privileges into ALL when all are set.
	descriptor.Grant(security.RootUser, privilege.List{privilege.SELECT})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
	descriptor.Revoke(security.RootUser, privilege.List{privilege.ALL})
	if err := descriptor.Validate(id); err == nil {
		t.Fatal("unexpected success")
	}
}

// TestSystemPrivilegeValidate exercises validation for system config
// descriptors. We use 1 (the system database ID).
func TestSystemPrivilegeValidate(t *testing.T) {
	defer leaktest.AfterTest(t)()
	id := ID(1)
	allowedPrivileges := systemConfigAllowedPrivileges[id]

	hasPrivilege := func(pl privilege.List, p privilege.Kind) bool {
		for _, i := range pl {
			if i == p {
				return true
			}
		}
		return false
	}

	// Exhaustively grant/revoke all privileges.
	// Due to the way validation is done after Grant/Revoke,
	// we need to revert the just-performed change after errors.
	descriptor := NewPrivilegeDescriptor(security.RootUser, allowedPrivileges)
	if err := descriptor.Validate(id); err != nil {
		t.Fatal(err)
	}
	for _, p := range privilege.ByValue {
		if hasPrivilege(allowedPrivileges, p) {
			// Grant allowed privileges. Either they are already
			// on (noop), or they're accepted.
			descriptor.Grant(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err != nil {
				t.Fatal(err)
			}
			descriptor.Grant("foo", privilege.List{p})
			if err := descriptor.Validate(id); err != nil {
				t.Fatal(err)
			}

			// Remove allowed privileges. This fails for root,
			// but passes for other users.
			descriptor.Revoke(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Grant(security.RootUser, privilege.List{p})
		} else {
			// Granting non-allowed privileges always.
			descriptor.Grant(security.RootUser, privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Revoke(security.RootUser, privilege.List{p})
			descriptor.Grant(security.RootUser, allowedPrivileges)

			descriptor.Grant("foo", privilege.List{p})
			if err := descriptor.Validate(id); err == nil {
				t.Fatal("unexpected success")
			}
			descriptor.Revoke("foo", privilege.List{p})
			descriptor.Grant("foo", allowedPrivileges)

			// Revoking non-allowed privileges always succeeds,
			// except when removing ALL for root.
			if p == privilege.ALL {
				// We need to reset privileges as Revoke(ALL) will clear
				// all bits.
				descriptor.Revoke(security.RootUser, privilege.List{p})
				if err := descriptor.Validate(id); err == nil {
					t.Fatal("unexpected success")
				}
				descriptor.Grant(security.RootUser, allowedPrivileges)
			} else {
				descriptor.Revoke(security.RootUser, privilege.List{p})
				if err := descriptor.Validate(id); err != nil {
					t.Fatal(err)
				}
			}
		}

		// We can always revoke anything from non-root users.
		descriptor.Revoke("foo", privilege.List{p})
		if err := descriptor.Validate(id); err != nil {
			t.Fatal(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
)

type DateTimeType struct {
	Date struct {
		Day   int `xml:"day,attr"`
		Month int `xml:"month,attr"`
		Year  int `xml:"year,attr"`
	} `xml:"itdDate"`

	Time struct {
		Hour   int `xml:"hour,attr"`
		Minute int `xml:"minute,attr"`
	} `xml:"itdTime"`
}

type Line struct {
	Number    string `xml:"number,attr"`
	Direction string `xml:"direction,attr"`
}

type Departure struct {
	Countdown int    `xml:"countdown,attr"`
	Platform  string `xml:"platform,attr"`

	DateTime    DateTimeType `xml:"itdDateTime"`
	ServingLine Line         `xml:"itdServingLine"`
}

type StopInfo struct {
	State string `xml:"state,attr"`

	IdfdStop struct {
		StopName  string `xml:",chardata"`
		MatchQlty int    `xml:"matchQuality,attr"`
		StopID    int    `xml:"stopID,attr"`
	} `xml:"odvNameElem"`
}

type XmlResult struct {
	Stop       StopInfo    `xml:"itdDepartureMonitorRequest>itdOdv>itdOdvName"`
	Departures []Departure `xml:"itdDepartureMonitorRequest>itdDepartureList>itdDeparture"`
}

func main() {
	station_id := flag.String("stop", "Königsplatz", "id or (part of the) stop name")
	max_results := flag.Int("results", 5, "how many results to show")
	flag.Parse()

	baseURL := "http://efa.avv-augsburg.de/avv/"
	endpoint := "XML_DM_REQUEST"

	params := url.Values{
		"type_dm":              {"stop"},
		"name_dm":              {*station_id},
		"useRealtime":          {"1"},
		"locationServerActive": {"1"},
		"dmLineSelection":      {"all"},
		"limit":                {strconv.Itoa(*max_results)},
		"mode":                 {"direct"},
	}

	resp, err := http.PostForm(baseURL+endpoint, params)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var result XmlResult
	decoder := xml.NewDecoder(resp.Body)
	decoder.CharsetReader = charset.NewReader
	if err = decoder.Decode(&result); err != nil {
		panic(err)
	}
	//fmt.Printf("%+v", result)

	if result.Stop.State != "identified" {
		fmt.Println("stop does not exist or name is not unique!")
		return
	}
	fmt.Println("selected stop: " + result.Stop.IdfdStop.StopName + " (" + strconv.Itoa(result.Stop.IdfdStop.StopID) + ")\n")

	for _, departure := range result.Departures {

		plu := ""
		if departure.Countdown != 1 {
			plu = "s"
		}

		fmt.Printf("route %-5s due in %-2s minute%s --> %s\n",
			departure.ServingLine.Number,
			strconv.Itoa(departure.Countdown),
			plu,
			departure.ServingLine.Direction)
	}

}
<commit_msg>* Captionize.<commit_after>package main

import (
	"encoding/xml"
	"flag"
	"fmt"
	"net/http"
	"net/url"
	"strconv"

	"code.google.com/p/go-charset/charset"
	_ "code.google.com/p/go-charset/data"
)

type DateTimeType struct {
	Date struct {
		Day   int `xml:"day,attr"`
		Month int `xml:"month,attr"`
		Year  int `xml:"year,attr"`
	} `xml:"itdDate"`

	Time struct {
		Hour   int `xml:"hour,attr"`
		Minute int `xml:"minute,attr"`
	} `xml:"itdTime"`
}

type Line struct {
	Number    string `xml:"number,attr"`
	Direction string `xml:"direction,attr"`
}

type Departure struct {
	Countdown int    `xml:"countdown,attr"`
	Platform  string `xml:"platform,attr"`

	DateTime    DateTimeType `xml:"itdDateTime"`
	ServingLine Line         `xml:"itdServingLine"`
}

type StopInfo struct {
	State string `xml:"state,attr"`

	IdfdStop struct {
		StopName  string `xml:",chardata"`
		MatchQlty int    `xml:"matchQuality,attr"`
		StopID    int    `xml:"stopID,attr"`
	} `xml:"odvNameElem"`
}

type XmlResult struct {
	Stop       StopInfo    `xml:"itdDepartureMonitorRequest>itdOdv>itdOdvName"`
	Departures []Departure `xml:"itdDepartureMonitorRequest>itdDepartureList>itdDeparture"`
}

func main() {
	station_id := flag.String("stop", "Königsplatz", "id or (part of the) stop name")
	max_results := flag.Int("results", 5, "how many results to show")
	flag.Parse()

	baseURL := "http://efa.avv-augsburg.de/avv/"
	endpoint := "XML_DM_REQUEST"

	params := url.Values{
		"type_dm":              {"stop"},
		"name_dm":              {*station_id},
		"useRealtime":          {"1"},
		"locationServerActive": {"1"},
		"dmLineSelection":      {"all"},
		"limit":                {strconv.Itoa(*max_results)},
		"mode":                 {"direct"},
	}

	resp, err := http.PostForm(baseURL+endpoint, params)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	var result XmlResult
	decoder := xml.NewDecoder(resp.Body)
	decoder.CharsetReader = charset.NewReader
	if err = decoder.Decode(&result); err != nil {
		panic(err)
	}
	//fmt.Printf("%+v", result)

	if result.Stop.State != "identified" {
		fmt.Println("Stop does not exist or name is not unique!")
		return
	}
	fmt.Println("Selected stop: " + result.Stop.IdfdStop.StopName + " (" + strconv.Itoa(result.Stop.IdfdStop.StopID) + ")\n")

	for _, departure := range result.Departures {
		plu := ""
		if departure.Countdown != 1 {
			plu = "s"
		}

		fmt.Printf("Route %-5s due in %-2s minute%s --> %s\n",
			departure.ServingLine.Number,
			strconv.Itoa(departure.Countdown),
			plu,
			departure.ServingLine.Direction)
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2017 Ankyra

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 core

import (
	"fmt"
)

type DependencyConfig struct {
	ReleaseId     string                 `json:"release_id" yaml:"release_id"`
	BuildMapping  map[string]interface{} `json:"build_mapping" yaml:"build_mapping"`
	DeployMapping map[string]interface{} `json:"deploy_mapping" yaml:"deploy_mapping"`
	Consumes      map[string]string      `json:"consumes" yaml:"consumes"`
	Scopes        []string               `json:"scopes" yaml:"scopes"`
}

func NewDependencyConfig(releaseId string) *DependencyConfig {
	return &DependencyConfig{
		ReleaseId:     releaseId,
		BuildMapping:  map[string]interface{}{},
		DeployMapping: map[string]interface{}{},
		Scopes:        []string{"build", "deploy"},
		Consumes:      map[string]string{},
	}
}

func (d *DependencyConfig) Validate(m *ReleaseMetadata) error {
	if d.BuildMapping == nil {
		d.BuildMapping = map[string]interface{}{}
	}
	if d.DeployMapping == nil {
		d.DeployMapping = map[string]interface{}{}
	}
	if d.Scopes == nil || len(d.Scopes) == 0 {
		d.Scopes = []string{"build", "deploy"}
	}
	return nil
}

func (d *DependencyConfig) InScope(scopes ...string) bool {
	for _, s := range d.Scopes {
		for _, scope := range scopes {
			if s == scope {
				return true
			}
		}
	}
	return false
}

func NewDependencyConfigFromMap(dep map[interface{}]interface{}) (*DependencyConfig, error) {
	var releaseId string
	buildMapping := map[string]interface{}{}
	deployMapping := map[string]interface{}{}
	consumes := map[string]string{}
	scopes := []string{}
	for key, val := range dep {
		keyStr, ok := key.(string)
		if !ok {
			return nil, fmt.Errorf("Expecting string key in dependency")
		}
		if keyStr == "release_id" {
			valString, ok := val.(string)
			if !ok {
				return nil, fmt.Errorf("Expecting string for dependency 'release_id' got '%T'", val)
			}
			releaseId = valString
		} else if key == "mapping" { // backwards compatibility with release metadata <= 6
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'mapping'")
				}
				buildMapping[kStr] = v
				deployMapping[kStr] = v
			}
		} else if key == "build_mapping" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'build_mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'build_mapping'")
				}
				buildMapping[kStr] = v
			}
		} else if key == "deploy_mapping" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'deploy_mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'deploy_mapping'")
				}
				deployMapping[kStr] = v
			}
		} else if key == "consumes" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'consumes' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency consumer mapping")
				}
				vStr, ok := v.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string value in dependency consumer mapping")
				}
				consumes[kStr] = vStr
			}
		} else if key == "scopes" {
			s, err := parseScopesFromInterface(val)
			if err != nil {
				return nil, err
			}
			scopes = s
		}
	}
	if releaseId == "" {
		return nil, fmt.Errorf("Missing 'release_id' in dependency")
	}
	cfg := NewDependencyConfig(releaseId)
	cfg.BuildMapping = buildMapping
	cfg.DeployMapping = deployMapping
	cfg.Scopes = scopes
	cfg.Consumes = consumes
	return cfg, nil
}
<commit_msg>Add helper methods on DependencyConfig<commit_after>/*
Copyright 2017 Ankyra

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 core

import (
	"fmt"
)

type DependencyConfig struct {
	ReleaseId     string                 `json:"release_id" yaml:"release_id"`
	BuildMapping  map[string]interface{} `json:"build_mapping" yaml:"build_mapping"`
	DeployMapping map[string]interface{} `json:"deploy_mapping" yaml:"deploy_mapping"`
	Consumes      map[string]string      `json:"consumes" yaml:"consumes"`
	Scopes        []string               `json:"scopes" yaml:"scopes"`
}

func NewDependencyConfig(releaseId string) *DependencyConfig {
	return &DependencyConfig{
		ReleaseId:     releaseId,
		BuildMapping:  map[string]interface{}{},
		DeployMapping: map[string]interface{}{},
		Scopes:        []string{"build", "deploy"},
		Consumes:      map[string]string{},
	}
}

func (d *DependencyConfig) Validate(m *ReleaseMetadata) error {
	if d.BuildMapping == nil {
		d.BuildMapping = map[string]interface{}{}
	}
	if d.DeployMapping == nil {
		d.DeployMapping = map[string]interface{}{}
	}
	if d.Scopes == nil || len(d.Scopes) == 0 {
		d.Scopes = []string{"build", "deploy"}
	}
	return nil
}

func (d *DependencyConfig) GetMapping(scope string) map[string]interface{} {
	if scope == "build" {
		return d.BuildMapping
	}
	if scope == "deploy" {
		return d.DeployMapping
	}
	return nil
}

func (d *DependencyConfig) AddVariableMapping(scopes []string, id, key string) {
	for _, scope := range scopes {
		mapping := d.GetMapping(scope)
		if mapping != nil {
			_, found := mapping[id]
			if !found {
				mapping[id] = key
			}
		}
	}
}

func (d *DependencyConfig) InScope(scope string) bool {
	for _, s := range d.Scopes {
		if s == scope {
			return true
		}
	}
	return false
}

func NewDependencyConfigFromMap(dep map[interface{}]interface{}) (*DependencyConfig, error) {
	var releaseId string
	buildMapping := map[string]interface{}{}
	deployMapping := map[string]interface{}{}
	consumes := map[string]string{}
	scopes := []string{}
	for key, val := range dep {
		keyStr, ok := key.(string)
		if !ok {
			return nil, fmt.Errorf("Expecting string key in dependency")
		}
		if keyStr == "release_id" {
			valString, ok := val.(string)
			if !ok {
				return nil, fmt.Errorf("Expecting string for dependency 'release_id' got '%T'", val)
			}
			releaseId = valString
		} else if key == "mapping" { // backwards compatibility with release metadata <= 6
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'mapping'")
				}
				buildMapping[kStr] = v
				deployMapping[kStr] = v
			}
		} else if key == "build_mapping" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'build_mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'build_mapping'")
				}
				buildMapping[kStr] = v
			}
		} else if key == "deploy_mapping" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'deploy_mapping' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency 'deploy_mapping'")
				}
				deployMapping[kStr] = v
			}
		} else if key == "consumes" {
			valMap, ok := val.(map[interface{}]interface{})
			if !ok {
				return nil, fmt.Errorf("Expecting dict for dependency 'consumes' got '%T'", val)
			}
			for k, v := range valMap {
				kStr, ok := k.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string key in dependency consumer mapping")
				}
				vStr, ok := v.(string)
				if !ok {
					return nil, fmt.Errorf("Expecting string value in dependency consumer mapping")
				}
				consumes[kStr] = vStr
			}
		} else if key == "scopes" {
			s, err := parseScopesFromInterface(val)
			if err != nil {
				return nil, err
			}
			scopes = s
		}
	}
	if releaseId == "" {
		return nil, fmt.Errorf("Missing 'release_id' in dependency")
	}
	cfg := NewDependencyConfig(releaseId)
	cfg.BuildMapping = buildMapping
	cfg.DeployMapping = deployMapping
	cfg.Scopes = scopes
	cfg.Consumes = consumes
	return cfg, nil
}
<|endoftext|>


<commit_before>package main

import (
    //"fmt"
    "strconv"
    "net/http"
    "html/template"
    "log"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    // "errors"
)

type Data struct {
    Id int
    Content string
}

const (
    DB_HOST = "tcp(127.0.0.1:3306)"
    DB_NAME = "sphere"
    DB_USER = "root"
    DB_PASS = "gaojingwen"
    DB_DATA_TABLE = "dataSet"
    DB_DATA_TABLE_COLUMN_CONTENT = "content"
)

var db *sql.DB

// save data
func (data *Data) save() error {
    stmt1, err := db.Prepare("INSERT INTO " + DB_DATA_TABLE +
        "(id, " + DB_DATA_TABLE_COLUMN_CONTENT + ") VALUES(?, ?)" +
        " ON DUPLICATE KEY UPDATE " + DB_DATA_TABLE_COLUMN_CONTENT + "= ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt1.Close()

    _, err = stmt1.Exec(data.Id, data.Content, data.Content)
    if err != nil {
        log.Fatal(err)
    }
    return nil
}

// var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func loadData(id int) (*Data, error) {
    var content string
    err := db.QueryRow("SELECT " + DB_DATA_TABLE_COLUMN_CONTENT +
        " FROM " + DB_DATA_TABLE + " where id = ?", id).Scan(&content)
    return &Data{Id : id, Content : content}, err
}

func loadAllData() (*[]Data, error) {
    rows, err := db.Query("SELECT * FROM " + DB_DATA_TABLE)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var dataSet []Data
    var id int
    var content string
    for rows.Next() {
        err := rows.Scan(&id, &content)
        if err != nil {
            log.Fatal(err)
        }
        dataSet = append(dataSet, Data{Id: id, Content: content})
    }
    err = rows.Err()
    if err != nil {
        log.Fatal(err)
    }

    return &dataSet, nil
}

func renderTemplateShowAll(w http.ResponseWriter, tmpl string, dataSet *[]Data) {
    t, err := template.ParseFiles(tmpl + ".html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    err = t.Execute(w, dataSet)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func renderTemplate(w http.ResponseWriter, tmpl string, data *Data) {
    t, err := template.ParseFiles(tmpl + ".html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    err = t.Execute(w, data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "/view/", http.StatusFound)
}

func addHandler(w http.ResponseWriter, r *http.Request) {
    var cnt int
    _ = db.QueryRow("SELECT COUNT(*) FROM " + DB_DATA_TABLE).Scan(&cnt)

    nextId := strconv.Itoa(cnt+1)
    http.Redirect(w, r, "/edit/" + nextId, http.StatusFound)
}

// edit the Content and store in database
func editHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/edit/"):]
    id, err := strconv.Atoi(idstring)
    if err != nil {
        renderTemplate(w, "empty", nil)
    } else {
        data, err := loadData(id)
        if err == nil || err == sql.ErrNoRows {
            renderTemplate(w, "edit", data)
        } else {
            renderTemplate(w, "empty", nil)
        }
    }
}

// view data from database
func viewHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/view/"):]

    // view specific data
    if idstring != "" {
        id, err := strconv.Atoi(idstring)
        if err != nil {
            renderTemplate(w, "empty", nil)
            return
        }
        data, err := loadData(id)
        if err == nil {
            renderTemplate(w, "view", data)
        } else if err == sql.ErrNoRows {
            renderTemplate(w, "not-found", data)
        } else {
            renderTemplate(w, "empty", nil)
        }
    // view data set
    } else {
        dataSet, _ := loadAllData()
        renderTemplateShowAll(w, "viewAll", dataSet)
    }
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/save/"):]
    id, err := strconv.Atoi(idstring)
    if err != nil {
        renderTemplate(w, "empty", nil)
        return
    }
    content := r.FormValue("content")
    data := &Data{Id: id, Content: content}
    err = data.save()
    if err != nil {
        renderTemplate(w, "not-found", data)
    } else {
        http.Redirect(w, r, "/view/" + idstring, http.StatusFound)
    }
}

func main() {
    var err error
    dns := DB_USER + ":" + DB_PASS + "@" + DB_HOST + "/"
    db, err = sql.Open("mysql", dns)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec("CREATE DATABASE IF NOT EXISTS "+ DB_NAME)
    if err != nil {
        log.Fatal(err)
    }

    _, err = db.Exec("USE " + DB_NAME)
    if err != nil {
        log.Fatal(err)
    }

    _, err = db.Exec("CREATE TABLE IF NOT EXISTS " + DB_DATA_TABLE +
        " (id int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), content TEXT NOT NULL)")
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/", handler)
    http.HandleFunc("/view/", viewHandler)
    http.HandleFunc("/edit/", editHandler)
    http.HandleFunc("/add/", addHandler)
    http.HandleFunc("/save/", saveHandler)
    http.ListenAndServe(":8080", nil)
}<commit_msg>change port<commit_after>package main

import (
    //"fmt"
    "strconv"
    "net/http"
    "html/template"
    "log"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    // "errors"
)

type Data struct {
    Id int
    Content string
}

const (
    DB_HOST = "tcp(127.0.0.1:3306)"
    DB_NAME = "sphere"
    DB_USER = "root"
    DB_PASS = "sphere"
    DB_DATA_TABLE = "dataSet"
    DB_DATA_TABLE_COLUMN_CONTENT = "content"
)

var db *sql.DB

// save data
func (data *Data) save() error {
    stmt1, err := db.Prepare("INSERT INTO " + DB_DATA_TABLE +
        "(id, " + DB_DATA_TABLE_COLUMN_CONTENT + ") VALUES(?, ?)" +
        " ON DUPLICATE KEY UPDATE " + DB_DATA_TABLE_COLUMN_CONTENT + "= ?")
    if err != nil {
        log.Fatal(err)
    }
    defer stmt1.Close()

    _, err = stmt1.Exec(data.Id, data.Content, data.Content)
    if err != nil {
        log.Fatal(err)
    }
    return nil
}

// var templates = template.Must(template.ParseFiles("edit.html", "view.html"))

func loadData(id int) (*Data, error) {
    var content string
    err := db.QueryRow("SELECT " + DB_DATA_TABLE_COLUMN_CONTENT +
        " FROM " + DB_DATA_TABLE + " where id = ?", id).Scan(&content)
    return &Data{Id : id, Content : content}, err
}

func loadAllData() (*[]Data, error) {
    rows, err := db.Query("SELECT * FROM " + DB_DATA_TABLE)
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    var dataSet []Data
    var id int
    var content string
    for rows.Next() {
        err := rows.Scan(&id, &content)
        if err != nil {
            log.Fatal(err)
        }
        dataSet = append(dataSet, Data{Id: id, Content: content})
    }
    err = rows.Err()
    if err != nil {
        log.Fatal(err)
    }

    return &dataSet, nil
}

func renderTemplateShowAll(w http.ResponseWriter, tmpl string, dataSet *[]Data) {
    t, err := template.ParseFiles(tmpl + ".html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    err = t.Execute(w, dataSet)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func renderTemplate(w http.ResponseWriter, tmpl string, data *Data) {
    t, err := template.ParseFiles(tmpl + ".html")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    err = t.Execute(w, data)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    http.Redirect(w, r, "/view/", http.StatusFound)
}

func addHandler(w http.ResponseWriter, r *http.Request) {
    var cnt int
    _ = db.QueryRow("SELECT COUNT(*) FROM " + DB_DATA_TABLE).Scan(&cnt)

    nextId := strconv.Itoa(cnt+1)
    http.Redirect(w, r, "/edit/" + nextId, http.StatusFound)
}

// edit the Content and store in database
func editHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/edit/"):]
    id, err := strconv.Atoi(idstring)
    if err != nil {
        renderTemplate(w, "empty", nil)
    } else {
        data, err := loadData(id)
        if err == nil || err == sql.ErrNoRows {
            renderTemplate(w, "edit", data)
        } else {
            renderTemplate(w, "empty", nil)
        }
    }
}

// view data from database
func viewHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/view/"):]

    // view specific data
    if idstring != "" {
        id, err := strconv.Atoi(idstring)
        if err != nil {
            renderTemplate(w, "empty", nil)
            return
        }
        data, err := loadData(id)
        if err == nil {
            renderTemplate(w, "view", data)
        } else if err == sql.ErrNoRows {
            renderTemplate(w, "not-found", data)
        } else {
            renderTemplate(w, "empty", nil)
        }
    // view data set
    } else {
        dataSet, _ := loadAllData()
        renderTemplateShowAll(w, "viewAll", dataSet)
    }
}

func saveHandler(w http.ResponseWriter, r *http.Request) {
    idstring := r.URL.Path[len("/save/"):]
    id, err := strconv.Atoi(idstring)
    if err != nil {
        renderTemplate(w, "empty", nil)
        return
    }
    content := r.FormValue("content")
    data := &Data{Id: id, Content: content}
    err = data.save()
    if err != nil {
        renderTemplate(w, "not-found", data)
    } else {
        http.Redirect(w, r, "/view/" + idstring, http.StatusFound)
    }
}

func main() {
    var err error
    dns := DB_USER + ":" + DB_PASS + "@" + DB_HOST + "/"
    db, err = sql.Open("mysql", dns)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()

    _, err = db.Exec("CREATE DATABASE IF NOT EXISTS "+ DB_NAME)
    if err != nil {
        log.Fatal(err)
    }

    _, err = db.Exec("USE " + DB_NAME)
    if err != nil {
        log.Fatal(err)
    }

    _, err = db.Exec("CREATE TABLE IF NOT EXISTS " + DB_DATA_TABLE +
        " (id int(11) NOT NULL AUTO_INCREMENT, PRIMARY KEY (id), content TEXT NOT NULL)")
    if err != nil {
        log.Fatal(err)
    }

    http.HandleFunc("/", handler)
    http.HandleFunc("/view/", viewHandler)
    http.HandleFunc("/edit/", editHandler)
    http.HandleFunc("/add/", addHandler)
    http.HandleFunc("/save/", saveHandler)
    http.ListenAndServe("0.0.0.0:8080", nil)
}<|endoftext|>


<commit_before>package hwaflib

func (ctx *Context) Version() string {
	version := "20130911"
	return version
}

func (ctx *Context) Revision() string {
	revision := "4f32e27"
	return revision
}

// EOF

<commit_msg>version: 20130912<commit_after>package hwaflib

func (ctx *Context) Version() string {
	version := "20130912"
	return version
}

func (ctx *Context) Revision() string {
	revision := "d0b70d1"
	return revision
}

// EOF

<|endoftext|>