<commit_before>package main

import (
	"fmt"
	"log"

	"github.com/codegangsta/cli"
	"gopkg.in/resty.v0"
)

func registerPermissions(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			// permissions
			{
				Name:  "permissions",
				Usage: "SUBCOMMANDS for permissions",
				Subcommands: []cli.Command{
					{
						Name:  "category",
						Usage: "SUBCOMMANDS for permission categories",
						Subcommands: []cli.Command{
							{
								Name:   "add",
								Usage:  "Register a new permission category",
								Action: runtime(cmdPermissionCategoryAdd),
							},
							{
								Name:   "remove",
								Usage:  "Remove an existing permission category",
								Action: runtime(cmdPermissionCategoryDel),
							},
							{
								Name:   "list",
								Usage:  "List all permission categories",
								Action: runtime(cmdPermissionCategoryList),
							},
							{
								Name:   "show",
								Usage:  "Show details for a permission category",
								Action: runtime(cmdPermissionCategoryShow),
							},
						}, // end permissions type
					},
					{
						Name:   "add",
						Usage:  "Register a new permission",
						Action: runtime(cmdPermissionAdd),
					},
					{
						Name:   "remove",
						Usage:  "Remove a permission",
						Action: runtime(cmdPermissionDel),
					},
					{
						Name:   "list",
						Usage:  "List all permissions",
						Action: runtime(cmdPermissionList),
					},
					{
						Name:  "show",
						Usage: "SUBCOMMANDS for permission show",
						Subcommands: []cli.Command{
							{
								Name:   "user",
								Usage:  "Show permissions of a user",
								Action: runtime(cmdPermissionShowUser),
							},
							{
								Name:   "team",
								Usage:  "Show permissions of a team",
								Action: runtime(cmdPermissionShowTeam),
							},
							{
								Name:   "tool",
								Usage:  "Show permissions of a tool account",
								Action: runtime(cmdPermissionShowTool),
							},
							{
								Name:   "permission",
								Usage:  "Show details about a permission",
								Action: runtime(cmdPermissionShowPermission),
							},
						},
					}, // end permissions show
					{
						Name:   "audit",
						Usage:  "Show all limited permissions associated with a repository",
						Action: runtime(cmdPermissionAudit),
					},
					{
						Name:  "grant",
						Usage: "SUBCOMMANDS for permission grant",
						Subcommands: []cli.Command{
							{
								Name:   "enable",
								Usage:  "Enable a useraccount to receive GRANT permissions",
								Action: runtime(cmdPermissionGrantEnable),
							},
							{
								Name:   "global",
								Usage:  "Grant a global permission",
								Action: runtime(cmdPermissionGrantGlobal),
							},
							{
								Name:   "limited",
								Usage:  "Grant a limited permission",
								Action: runtime(cmdPermissionGrantLimited),
							},
							{
								Name:   "system",
								Usage:  "Grant a system permission",
								Action: runtime(cmdPermissionGrantSystem),
							},
						},
					}, // end permissions grant
				},
			}, // end permissions
		}...,
	)
	return &app
}

func cmdPermissionCategoryAdd(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	req := proto.NewCategoryRequest()
	req.Category.Name = c.Args().First()

	resp := utl.PostRequestWithBody(Client, req, `/category/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryDel(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/category/%s", c.Args().First())

	resp := utl.DeleteRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest(Client, `/category/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/category/%s", c.Args().First())

	resp := utl.GetRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionAdd(c *cli.Context) error {
	utl.ValidateCliMinArgumentCount(c, 3)
	multiple := []string{}
	unique := []string{`category`, `grants`}
	required := []string{`category`}

	opts := utl.ParseVariadicArguments(
		multiple,
		unique,
		required,
		c.Args().Tail())

	req := proto.NewPermissionRequest()
	req.Permission.Name = c.Args().First()
	req.Permission.Category = opts[`category`][0]
	if sl, ok := opts[`grants`]; ok && len(sl) > 0 {
		req.Permission.Grants = opts[`grants`][0]
	}

	resp := utl.PostRequestWithBody(Client, req, `/permission/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionDel(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/permission/%s", c.Args().First())

	resp := utl.DeleteRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest(Client, `/permission/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionShowGeneric(c *cli.Context, objType string) {
	url := Cfg.Run.SomaAPI
	var (
		objName string
		repo    string
		hasRepo bool
	)

	switch utl.GetCliArgumentCount(c) {
	case 1:
		hasRepo = false
	case 3:
		utl.ValidateCliArgument(c, 2, "repository")
		hasRepo = true
	default:
		log.Fatal("Syntax error, unexpected argument count")
	}

	objName = c.Args().Get(0)
	if hasRepo {
		repo = c.Args().Get(2)
		url.Path = fmt.Sprintf("/permissions/%s/%s/repository/%s",
			objType, objName, repo)
	} else {
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, objName)
	}

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list permissions
}

func cmdPermissionShowUser(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "user")
	return nil
}

func cmdPermissionShowTeam(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "team")
	return nil
}

func cmdPermissionShowTool(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "tool")
	return nil
}

func cmdPermissionShowPermission(c *cli.Context) error {
	url := Cfg.Run.SomaAPI

	utl.ValidateCliArgumentCount(c, 1)
	url.Path = fmt.Sprintf("/permissions/permission/%s", c.Args().Get(0))

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list users
	return nil
}

func cmdPermissionAudit(c *cli.Context) error {
	url := Cfg.Run.SomaAPI

	utl.ValidateCliArgumentCount(c, 1)
	url.Path = fmt.Sprintf("/permissions/repository/%s", c.Args().Get(0))

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list permissions
	return nil
}

func cmdPermissionGrantEnable(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)
	userId := utl.TryGetUserByUUIDOrName(Client, c.Args().First())
	path := fmt.Sprintf("/permissions/user/%s", userId)

	resp := utl.PatchRequestWithBody(Client, proto.Request{
		Grant: &proto.Grant{
			RecipientType: "user",
			RecipientId:   userId,
			Permission:    "global_grant_limited",
		},
	}, path,
	)
	fmt.Println(resp)
	return nil
}

func cmdPermissionGrantGlobal(c *cli.Context) error {
	/*
		url := getApiUrl()
		var objType string

		utl.ValidateCliArgumentCount(c, 3)
		switch c.Args().Get(1) {
		case "user":
			objType = "user"
		case "team":
			objType = "team"
		case "tool":
			objType = "tool"
		default:
			Slog.Fatal("Syntax error")
		}
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, c.Args().Get(2))

		var req somaproto.ProtoRequestPermission
		req.Permission = c.Args().Get(0)

		_, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Patch(url.String())
		if err != nil {
			Slog.Fatal(err)
		}
	*/
	return nil
}

func cmdPermissionGrantLimited(c *cli.Context) error {
	/*
		url := getApiUrl()
		keys := [...]string{"repository", "bucket", "group", "cluster"}
		keySlice := make([]string, 0)
		var objType string
		var req somaproto.ProtoRequestPermission
		req.Grant.GrantType = "limited"

		// the keys array is ordered towars increasing detail, ie. there can
		// not be a limited grant on a cluster without specifying the
		// repository
		// Also, slicing uses halfopen interval [a:b), ie `a` is part of the
		// resulting slice, but `b` is not
		switch utl.GetCliArgumentCount(c) {
		case 5:
			keySlice = keys[0:1]
		case 7:
			keySlice = keys[0:2]
		case 9:
			keySlice = keys[0:3]
		case 11:
			keySlice = keys[:]
		default:
			Slog.Fatal("Syntax error, unexpected argument count")
		}
		// Tail() skips the first argument 0, which is returned by First(),
		// thus contains arguments 1-n. The first 3 arguments 0-2 are fixed in
		// order, the variable parts are arguments 4+ (argv 3-10) -- element
		// 2-9 in the tail slice.
		argSlice := c.Args().Tail()[2:]
		options := *parseLimitedGrantArguments(keySlice, argSlice)

		switch c.Args().Get(1) {
		case "user":
			objType = "user"
		case "team":
			objType = "team"
		case "tool":
			objType = "tool"
		default:
			Slog.Fatal("Syntax error")
		}
		req.Permission = c.Args().Get(0)
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, c.Args().Get(2))

		for k, v := range options {
			switch k {
			case "repository":
				req.Grant.Repository = v
			case "bucket":
				req.Grant.Bucket = v
			case "group":
				req.Grant.Group = v
			case "cluster":
				req.Grant.Cluster = v
			}
		}

		_, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Patch(url.String())
		if err != nil {
			Slog.Fatal(err)
		}
	*/
	return nil
}

func cmdPermissionGrantSystem(c *cli.Context) error {
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Vendor update: somaproto<commit_after>package main

import (
	"fmt"
	"log"

	"github.com/codegangsta/cli"
	"gopkg.in/resty.v0"
)

func registerPermissions(app cli.App) *cli.App {
	app.Commands = append(app.Commands,
		[]cli.Command{
			// permissions
			{
				Name:  "permissions",
				Usage: "SUBCOMMANDS for permissions",
				Subcommands: []cli.Command{
					{
						Name:  "category",
						Usage: "SUBCOMMANDS for permission categories",
						Subcommands: []cli.Command{
							{
								Name:   "add",
								Usage:  "Register a new permission category",
								Action: runtime(cmdPermissionCategoryAdd),
							},
							{
								Name:   "remove",
								Usage:  "Remove an existing permission category",
								Action: runtime(cmdPermissionCategoryDel),
							},
							{
								Name:   "list",
								Usage:  "List all permission categories",
								Action: runtime(cmdPermissionCategoryList),
							},
							{
								Name:   "show",
								Usage:  "Show details for a permission category",
								Action: runtime(cmdPermissionCategoryShow),
							},
						}, // end permissions type
					},
					{
						Name:   "add",
						Usage:  "Register a new permission",
						Action: runtime(cmdPermissionAdd),
					},
					{
						Name:   "remove",
						Usage:  "Remove a permission",
						Action: runtime(cmdPermissionDel),
					},
					{
						Name:   "list",
						Usage:  "List all permissions",
						Action: runtime(cmdPermissionList),
					},
					{
						Name:  "show",
						Usage: "SUBCOMMANDS for permission show",
						Subcommands: []cli.Command{
							{
								Name:   "user",
								Usage:  "Show permissions of a user",
								Action: runtime(cmdPermissionShowUser),
							},
							{
								Name:   "team",
								Usage:  "Show permissions of a team",
								Action: runtime(cmdPermissionShowTeam),
							},
							{
								Name:   "tool",
								Usage:  "Show permissions of a tool account",
								Action: runtime(cmdPermissionShowTool),
							},
							{
								Name:   "permission",
								Usage:  "Show details about a permission",
								Action: runtime(cmdPermissionShowPermission),
							},
						},
					}, // end permissions show
					{
						Name:   "audit",
						Usage:  "Show all limited permissions associated with a repository",
						Action: runtime(cmdPermissionAudit),
					},
					{
						Name:  "grant",
						Usage: "SUBCOMMANDS for permission grant",
						Subcommands: []cli.Command{
							{
								Name:   "enable",
								Usage:  "Enable a useraccount to receive GRANT permissions",
								Action: runtime(cmdPermissionGrantEnable),
							},
							{
								Name:   "global",
								Usage:  "Grant a global permission",
								Action: runtime(cmdPermissionGrantGlobal),
							},
							{
								Name:   "limited",
								Usage:  "Grant a limited permission",
								Action: runtime(cmdPermissionGrantLimited),
							},
							{
								Name:   "system",
								Usage:  "Grant a system permission",
								Action: runtime(cmdPermissionGrantSystem),
							},
						},
					}, // end permissions grant
				},
			}, // end permissions
		}...,
	)
	return &app
}

func cmdPermissionCategoryAdd(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	req := proto.NewCategoryRequest()
	req.Category.Name = c.Args().First()

	resp := utl.PostRequestWithBody(Client, req, `/category/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryDel(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/category/%s", c.Args().First())

	resp := utl.DeleteRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest(Client, `/category/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionCategoryShow(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/category/%s", c.Args().First())

	resp := utl.GetRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionAdd(c *cli.Context) error {
	utl.ValidateCliMinArgumentCount(c, 3)
	multiple := []string{}
	unique := []string{`category`, `grants`}
	required := []string{`category`}

	opts := utl.ParseVariadicArguments(
		multiple,
		unique,
		required,
		c.Args().Tail())

	req := proto.NewPermissionRequest()
	req.Permission.Name = c.Args().First()
	req.Permission.Category = opts[`category`][0]
	if sl, ok := opts[`grants`]; ok && len(sl) > 0 {
		req.Permission.Grants = opts[`grants`][0]
	}

	resp := utl.PostRequestWithBody(Client, req, `/permission/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionDel(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)

	path := fmt.Sprintf("/permission/%s", c.Args().First())

	resp := utl.DeleteRequest(Client, path)
	fmt.Println(resp)
	return nil
}

func cmdPermissionList(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 0)

	resp := utl.GetRequest(Client, `/permission/`)
	fmt.Println(resp)
	return nil
}

func cmdPermissionShowGeneric(c *cli.Context, objType string) {
	url := Cfg.Run.SomaAPI
	var (
		objName string
		repo    string
		hasRepo bool
	)

	switch utl.GetCliArgumentCount(c) {
	case 1:
		hasRepo = false
	case 3:
		utl.ValidateCliArgument(c, 2, "repository")
		hasRepo = true
	default:
		log.Fatal("Syntax error, unexpected argument count")
	}

	objName = c.Args().Get(0)
	if hasRepo {
		repo = c.Args().Get(2)
		url.Path = fmt.Sprintf("/permissions/%s/%s/repository/%s",
			objType, objName, repo)
	} else {
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, objName)
	}

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list permissions
}

func cmdPermissionShowUser(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "user")
	return nil
}

func cmdPermissionShowTeam(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "team")
	return nil
}

func cmdPermissionShowTool(c *cli.Context) error {
	cmdPermissionShowGeneric(c, "tool")
	return nil
}

func cmdPermissionShowPermission(c *cli.Context) error {
	url := Cfg.Run.SomaAPI

	utl.ValidateCliArgumentCount(c, 1)
	url.Path = fmt.Sprintf("/permissions/permission/%s", c.Args().Get(0))

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list users
	return nil
}

func cmdPermissionAudit(c *cli.Context) error {
	url := Cfg.Run.SomaAPI

	utl.ValidateCliArgumentCount(c, 1)
	url.Path = fmt.Sprintf("/permissions/repository/%s", c.Args().Get(0))

	_, err := resty.New().
		SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
		R().
		Get(url.String())
	if err != nil {
		log.Fatal(err)
	}
	// TODO list permissions
	return nil
}

func cmdPermissionGrantEnable(c *cli.Context) error {
	utl.ValidateCliArgumentCount(c, 1)
	userId := utl.TryGetUserByUUIDOrName(Client, c.Args().First())
	path := fmt.Sprintf("/permissions/user/%s", userId)

	resp := utl.PatchRequestWithBody(Client, proto.Request{
		Grant: &proto.Grant{
			RecipientType: "user",
			RecipientId:   userId,
			PermissionId:  "global_grant_limited",
		},
	}, path,
	)
	fmt.Println(resp)
	return nil
}

func cmdPermissionGrantGlobal(c *cli.Context) error {
	/*
		url := getApiUrl()
		var objType string

		utl.ValidateCliArgumentCount(c, 3)
		switch c.Args().Get(1) {
		case "user":
			objType = "user"
		case "team":
			objType = "team"
		case "tool":
			objType = "tool"
		default:
			Slog.Fatal("Syntax error")
		}
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, c.Args().Get(2))

		var req somaproto.ProtoRequestPermission
		req.Permission = c.Args().Get(0)

		_, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Patch(url.String())
		if err != nil {
			Slog.Fatal(err)
		}
	*/
	return nil
}

func cmdPermissionGrantLimited(c *cli.Context) error {
	/*
		url := getApiUrl()
		keys := [...]string{"repository", "bucket", "group", "cluster"}
		keySlice := make([]string, 0)
		var objType string
		var req somaproto.ProtoRequestPermission
		req.Grant.GrantType = "limited"

		// the keys array is ordered towars increasing detail, ie. there can
		// not be a limited grant on a cluster without specifying the
		// repository
		// Also, slicing uses halfopen interval [a:b), ie `a` is part of the
		// resulting slice, but `b` is not
		switch utl.GetCliArgumentCount(c) {
		case 5:
			keySlice = keys[0:1]
		case 7:
			keySlice = keys[0:2]
		case 9:
			keySlice = keys[0:3]
		case 11:
			keySlice = keys[:]
		default:
			Slog.Fatal("Syntax error, unexpected argument count")
		}
		// Tail() skips the first argument 0, which is returned by First(),
		// thus contains arguments 1-n. The first 3 arguments 0-2 are fixed in
		// order, the variable parts are arguments 4+ (argv 3-10) -- element
		// 2-9 in the tail slice.
		argSlice := c.Args().Tail()[2:]
		options := *parseLimitedGrantArguments(keySlice, argSlice)

		switch c.Args().Get(1) {
		case "user":
			objType = "user"
		case "team":
			objType = "team"
		case "tool":
			objType = "tool"
		default:
			Slog.Fatal("Syntax error")
		}
		req.Permission = c.Args().Get(0)
		url.Path = fmt.Sprintf("/permissions/%s/%s", objType, c.Args().Get(2))

		for k, v := range options {
			switch k {
			case "repository":
				req.Grant.Repository = v
			case "bucket":
				req.Grant.Bucket = v
			case "group":
				req.Grant.Group = v
			case "cluster":
				req.Grant.Cluster = v
			}
		}

		_, err := resty.New().
			SetRedirectPolicy(resty.FlexibleRedirectPolicy(3)).
			R().
			SetBody(req).
			Patch(url.String())
		if err != nil {
			Slog.Fatal(err)
		}
	*/
	return nil
}

func cmdPermissionGrantSystem(c *cli.Context) error {
	return nil
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>// Package prometheus provides Prometheus implementations for metrics.
// Individual metrics are mapped to their Prometheus counterparts, and
// (depending on the constructor used) may be automatically registered in the
// global Prometheus metrics registry.
package prometheus

import (
	"github.com/prometheus/client_golang/prometheus"

	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/internal/lv"
)

// Counter implements Counter, via a Prometheus CounterVec.
type Counter struct {
	cv  *prometheus.CounterVec
	lvs lv.LabelValues
}

// NewCounterFrom constructs and registers a Prometheus CounterVec,
// and returns a usable Counter object.
func NewCounterFrom(opts prometheus.CounterOpts, labelNames []string) *Counter {
	cv := prometheus.NewCounterVec(opts, labelNames)
	prometheus.MustRegister(cv)
	return NewCounter(cv)
}

// NewCounter wraps the CounterVec and returns a usable Counter object.
func NewCounter(cv *prometheus.CounterVec) *Counter {
	return &Counter{
		cv: cv,
	}
}

// With implements Counter.
func (c *Counter) With(labelValues ...string) metrics.Counter {
	return &Counter{
		cv:  c.cv,
		lvs: c.lvs.With(labelValues...),
	}
}

// Add implements Counter.
func (c *Counter) Add(delta float64) {
	c.cv.With(makeLabels(c.lvs...)).Add(delta)
}

// Gauge implements Gauge, via a Prometheus GaugeVec.
type Gauge struct {
	gv  *prometheus.GaugeVec
	lvs lv.LabelValues
}

// NewGaugeFrom construts and registers a Prometheus GaugeVec,
// and returns a usable Gauge object.
func NewGaugeFrom(opts prometheus.GaugeOpts, labelNames []string) *Gauge {
	gv := prometheus.NewGaugeVec(opts, labelNames)
	prometheus.MustRegister(gv)
	return NewGauge(gv)
}

// NewGauge wraps the GaugeVec and returns a usable Gauge object.
func NewGauge(gv *prometheus.GaugeVec) *Gauge {
	return &Gauge{
		gv: gv,
	}
}

// With implements Gauge.
func (g *Gauge) With(labelValues ...string) metrics.Gauge {
	return &Gauge{
		gv:  g.gv,
		lvs: g.lvs.With(labelValues...),
	}
}

// Set implements Gauge.
func (g *Gauge) Set(value float64) {
	g.gv.With(makeLabels(g.lvs...)).Set(value)
}

// Add is supported by Prometheus GaugeVecs.
func (g *Gauge) Add(delta float64) {
	g.gv.With(makeLabels(g.lvs...)).Add(delta)
}

// Summary implements Histogram, via a Prometheus SummaryVec. The difference
// between a Summary and a Histogram is that Summaries don't require predefined
// quantile buckets, but cannot be statistically aggregated.
type Summary struct {
	sv  *prometheus.SummaryVec
	lvs lv.LabelValues
}

// NewSummaryFrom constructs and registers a Prometheus SummaryVec,
// and returns a usable Summary object.
func NewSummaryFrom(opts prometheus.SummaryOpts, labelNames []string) *Summary {
	sv := prometheus.NewSummaryVec(opts, labelNames)
	prometheus.MustRegister(sv)
	return NewSummary(sv)
}

// NewSummary wraps the SummaryVec and returns a usable Summary object.
func NewSummary(sv *prometheus.SummaryVec) *Summary {
	return &Summary{
		sv: sv,
	}
}

// With implements Histogram.
func (s *Summary) With(labelValues ...string) metrics.Histogram {
	return &Summary{
		sv:  s.sv,
		lvs: s.lvs.With(labelValues...),
	}
}

// Observe implements Histogram.
func (s *Summary) Observe(value float64) {
	s.sv.With(makeLabels(s.lvs...)).Observe(value)
}

// Histogram implements Histogram via a Prometheus HistogramVec. The difference
// between a Histogram and a Summary is that Histograms require predefined
// quantile buckets, and can be statistically aggregated.
type Histogram struct {
	hv  *prometheus.HistogramVec
	lvs lv.LabelValues
}

// NewHistogramFrom constructs and registers a Prometheus HistogramVec,
// and returns a usable Histogram object.
func NewHistogramFrom(opts prometheus.HistogramOpts, labelNames []string) *Histogram {
	hv := prometheus.NewHistogramVec(opts, labelNames)
	prometheus.MustRegister(hv)
	return NewHistogram(hv)
}

// NewHistogram wraps the HistogramVec and returns a usable Histogram object.
func NewHistogram(hv *prometheus.HistogramVec) *Histogram {
	return &Histogram{
		hv: hv,
	}
}

// With implements Histogram.
func (h *Histogram) With(labelValues ...string) metrics.Histogram {
	return &Histogram{
		hv:  h.hv,
		lvs: h.lvs.With(labelValues...),
	}
}

// Observe implements Histogram.
func (h *Histogram) Observe(value float64) {
	h.hv.With(makeLabels(h.lvs...)).Observe(value)
}

func makeLabels(labelValues ...string) prometheus.Labels {
	labels := prometheus.Labels{}
	for i := 0; i < len(labelValues); i += 2 {
		labels[labelValues[i]] = labelValues[i+1]
	}
	return labels
}
<commit_msg>Fix typo: "construts" --> "constructs" (#925)<commit_after>// Package prometheus provides Prometheus implementations for metrics.
// Individual metrics are mapped to their Prometheus counterparts, and
// (depending on the constructor used) may be automatically registered in the
// global Prometheus metrics registry.
package prometheus

import (
	"github.com/prometheus/client_golang/prometheus"

	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/internal/lv"
)

// Counter implements Counter, via a Prometheus CounterVec.
type Counter struct {
	cv  *prometheus.CounterVec
	lvs lv.LabelValues
}

// NewCounterFrom constructs and registers a Prometheus CounterVec,
// and returns a usable Counter object.
func NewCounterFrom(opts prometheus.CounterOpts, labelNames []string) *Counter {
	cv := prometheus.NewCounterVec(opts, labelNames)
	prometheus.MustRegister(cv)
	return NewCounter(cv)
}

// NewCounter wraps the CounterVec and returns a usable Counter object.
func NewCounter(cv *prometheus.CounterVec) *Counter {
	return &Counter{
		cv: cv,
	}
}

// With implements Counter.
func (c *Counter) With(labelValues ...string) metrics.Counter {
	return &Counter{
		cv:  c.cv,
		lvs: c.lvs.With(labelValues...),
	}
}

// Add implements Counter.
func (c *Counter) Add(delta float64) {
	c.cv.With(makeLabels(c.lvs...)).Add(delta)
}

// Gauge implements Gauge, via a Prometheus GaugeVec.
type Gauge struct {
	gv  *prometheus.GaugeVec
	lvs lv.LabelValues
}

// NewGaugeFrom constructs and registers a Prometheus GaugeVec,
// and returns a usable Gauge object.
func NewGaugeFrom(opts prometheus.GaugeOpts, labelNames []string) *Gauge {
	gv := prometheus.NewGaugeVec(opts, labelNames)
	prometheus.MustRegister(gv)
	return NewGauge(gv)
}

// NewGauge wraps the GaugeVec and returns a usable Gauge object.
func NewGauge(gv *prometheus.GaugeVec) *Gauge {
	return &Gauge{
		gv: gv,
	}
}

// With implements Gauge.
func (g *Gauge) With(labelValues ...string) metrics.Gauge {
	return &Gauge{
		gv:  g.gv,
		lvs: g.lvs.With(labelValues...),
	}
}

// Set implements Gauge.
func (g *Gauge) Set(value float64) {
	g.gv.With(makeLabels(g.lvs...)).Set(value)
}

// Add is supported by Prometheus GaugeVecs.
func (g *Gauge) Add(delta float64) {
	g.gv.With(makeLabels(g.lvs...)).Add(delta)
}

// Summary implements Histogram, via a Prometheus SummaryVec. The difference
// between a Summary and a Histogram is that Summaries don't require predefined
// quantile buckets, but cannot be statistically aggregated.
type Summary struct {
	sv  *prometheus.SummaryVec
	lvs lv.LabelValues
}

// NewSummaryFrom constructs and registers a Prometheus SummaryVec,
// and returns a usable Summary object.
func NewSummaryFrom(opts prometheus.SummaryOpts, labelNames []string) *Summary {
	sv := prometheus.NewSummaryVec(opts, labelNames)
	prometheus.MustRegister(sv)
	return NewSummary(sv)
}

// NewSummary wraps the SummaryVec and returns a usable Summary object.
func NewSummary(sv *prometheus.SummaryVec) *Summary {
	return &Summary{
		sv: sv,
	}
}

// With implements Histogram.
func (s *Summary) With(labelValues ...string) metrics.Histogram {
	return &Summary{
		sv:  s.sv,
		lvs: s.lvs.With(labelValues...),
	}
}

// Observe implements Histogram.
func (s *Summary) Observe(value float64) {
	s.sv.With(makeLabels(s.lvs...)).Observe(value)
}

// Histogram implements Histogram via a Prometheus HistogramVec. The difference
// between a Histogram and a Summary is that Histograms require predefined
// quantile buckets, and can be statistically aggregated.
type Histogram struct {
	hv  *prometheus.HistogramVec
	lvs lv.LabelValues
}

// NewHistogramFrom constructs and registers a Prometheus HistogramVec,
// and returns a usable Histogram object.
func NewHistogramFrom(opts prometheus.HistogramOpts, labelNames []string) *Histogram {
	hv := prometheus.NewHistogramVec(opts, labelNames)
	prometheus.MustRegister(hv)
	return NewHistogram(hv)
}

// NewHistogram wraps the HistogramVec and returns a usable Histogram object.
func NewHistogram(hv *prometheus.HistogramVec) *Histogram {
	return &Histogram{
		hv: hv,
	}
}

// With implements Histogram.
func (h *Histogram) With(labelValues ...string) metrics.Histogram {
	return &Histogram{
		hv:  h.hv,
		lvs: h.lvs.With(labelValues...),
	}
}

// Observe implements Histogram.
func (h *Histogram) Observe(value float64) {
	h.hv.With(makeLabels(h.lvs...)).Observe(value)
}

func makeLabels(labelValues ...string) prometheus.Labels {
	labels := prometheus.Labels{}
	for i := 0; i < len(labelValues); i += 2 {
		labels[labelValues[i]] = labelValues[i+1]
	}
	return labels
}
<|endoftext|>


<commit_before>package game

import (
	"fmt"
	"github.com/dougfort/gocards"
)

// EnumerateMoves lists all possible legal moves in the current Tableau.
// The returned slice is in no patricular order
func (t Tableau) EnumerateMoves() []MoveType {
	var moves []MoveType

FROM_LOOP:
	for from := 0; from < TableauWidth; from++ {
		if len(t[from].Cards) == 0 {
			continue FROM_LOOP
		}

	TO_LOOP:
		for to := 0; to < TableauWidth; to++ {

			if to == from {
				continue TO_LOOP
			}

			// we could weed out a lot of these before validating,
			// but why bother, it would just make the code more confusing
		ROW_LOOP:
			for i := 0; i < len(t[from].Cards); i++ {
				row := t[from].HiddenCount + i
				move := MoveType{FromCol: from, FromRow: row, ToCol: to}
				if err := t.ValidateMove(move); err != nil {
					continue ROW_LOOP
				}
				moves = append(moves, move)
			}
		}
	}

	return moves
}

// ValidateMove returns nil if the move is valid in the Tableau
// A Move is valid if
// 1. The slice of Cards at 'From' is all of the same Suit
// 2. The slice of Cards at 'From' is in order by Rank from top to bottom
// 3. The bottom Card at 'To' is successor by Rank of the top Card moved
func (t Tableau) ValidateMove(m MoveType) error {
	var err error
	var s gocards.Cards

	if s, err = t.getSliceToMove(m); err != nil {
		return err
	}

	if !stackIndexValid(m.ToCol) || m.ToCol == m.FromCol {
		return fmt.Errorf("invalid ToCol: %d", m.FromCol)
	}

	if len(t[m.ToCol].Cards) > 0 {
		bottomCard := t.getBottomCard(m.ToCol)
		if s[0].Rank != bottomCard.Rank-1 {
			return fmt.Errorf("Rank of move slice top (%d) does not fit ToCol bottom (%d)",
				s[0].Rank, bottomCard.Rank)
		}
	}

	return nil
}

func stackIndexValid(index int) bool {
	return index >= 0 && index < TableauWidth
}

func (t Tableau) getSliceToMove(m MoveType) (gocards.Cards, error) {
	if !stackIndexValid(m.FromCol) {
		return nil, fmt.Errorf("m.FromCol invalid: %d", m.FromCol)
	}

	row := m.FromRow - t[m.FromCol].HiddenCount
	if !(row >= 0 && row < len(t[m.FromCol].Cards)) {
		return nil, fmt.Errorf("m.FromRow invalid: %d", m.FromRow)
	}

	s := t[m.FromCol].Cards[row:]

	var prev gocards.Card
	// interate from the top (highest Rank) card downto the bottom (lowest Rank)
	for i, card := range s {
		if i > 0 {
			if card.Suit != prev.Suit {
				return nil, fmt.Errorf("move slice not all the same Suit at %d", i)
			}
			if card.Rank != prev.Rank-1 {
				return nil, fmt.Errorf("move slice out of order %d %d at %d",
					prev.Rank, card.Rank, i)
			}
		}
		prev = card
	}

	return s, nil
}

func (t Tableau) getBottomCard(col int) gocards.Card {
	return t[col].Cards[len(t[col].Cards)-1]
}
<commit_msg>more replacements with Dave Cheny's 'errors'<commit_after>package game

import (
	"github.com/pkg/errors"

	"github.com/dougfort/gocards"
)

// EnumerateMoves lists all possible legal moves in the current Tableau.
// The returned slice is in no patricular order
func (t Tableau) EnumerateMoves() []MoveType {
	var moves []MoveType

FROM_LOOP:
	for from := 0; from < TableauWidth; from++ {
		if len(t[from].Cards) == 0 {
			continue FROM_LOOP
		}

	TO_LOOP:
		for to := 0; to < TableauWidth; to++ {

			if to == from {
				continue TO_LOOP
			}

			// we could weed out a lot of these before validating,
			// but why bother, it would just make the code more confusing
		ROW_LOOP:
			for i := 0; i < len(t[from].Cards); i++ {
				row := t[from].HiddenCount + i
				move := MoveType{FromCol: from, FromRow: row, ToCol: to}
				if err := t.ValidateMove(move); err != nil {
					continue ROW_LOOP
				}
				moves = append(moves, move)
			}
		}
	}

	return moves
}

// ValidateMove returns nil if the move is valid in the Tableau
// A Move is valid if
// 1. The slice of Cards at 'From' is all of the same Suit
// 2. The slice of Cards at 'From' is in order by Rank from top to bottom
// 3. The bottom Card at 'To' is successor by Rank of the top Card moved
func (t Tableau) ValidateMove(m MoveType) error {
	var err error
	var s gocards.Cards

	if s, err = t.getSliceToMove(m); err != nil {
		return err
	}

	if !stackIndexValid(m.ToCol) || m.ToCol == m.FromCol {
		return errors.Errorf("invalid ToCol: %d", m.FromCol)
	}

	if len(t[m.ToCol].Cards) > 0 {
		bottomCard := t.getBottomCard(m.ToCol)
		if s[0].Rank != bottomCard.Rank-1 {
			return errors.Errorf("Rank of move slice top (%d) does not fit ToCol bottom (%d)",
				s[0].Rank, bottomCard.Rank)
		}
	}

	return nil
}

func stackIndexValid(index int) bool {
	return index >= 0 && index < TableauWidth
}

func (t Tableau) getSliceToMove(m MoveType) (gocards.Cards, error) {
	if !stackIndexValid(m.FromCol) {
		return nil, errors.Errorf("m.FromCol invalid: %d", m.FromCol)
	}

	row := m.FromRow - t[m.FromCol].HiddenCount
	if !(row >= 0 && row < len(t[m.FromCol].Cards)) {
		return nil, errors.Errorf("m.FromRow invalid: %d", m.FromRow)
	}

	s := t[m.FromCol].Cards[row:]

	var prev gocards.Card
	// interate from the top (highest Rank) card downto the bottom (lowest Rank)
	for i, card := range s {
		if i > 0 {
			if card.Suit != prev.Suit {
				return nil, errors.Errorf("move slice not all the same Suit at %d", i)
			}
			if card.Rank != prev.Rank-1 {
				return nil, errors.Errorf("move slice out of order %d %d at %d",
					prev.Rank, card.Rank, i)
			}
		}
		prev = card
	}

	return s, nil
}

func (t Tableau) getBottomCard(col int) gocards.Card {
	return t[col].Cards[len(t[col].Cards)-1]
}
<|endoftext|>


<commit_before>package gat

import (
	"fmt"
	"io/ioutil"
	"os/exec"
	"strings"
	"syscall"
	"testing"

	. "github.com/smartystreets/goconvey/convey"

	"polydawn.net/danggit/lib/testutil"
)

func execGit(args ...string) (string, int) {
	cmd := exec.Command("git", args...)
	// ye standard attempts to armour git against bizzarotron system config and global effects
	cmd.Env = []string{
		"GIT_CONFIG_NOSYSTEM=true",
		"HOME=/dev/null",
		"GIT_ASKPASS=/bin/true",
	}
	// slurp all the outputs.  this isn't for purity and grace
	out, err := cmd.CombinedOutput()
	if err != nil {
		panic(err)
	}
	// have i mentioned how pleasant stdlib exec is at giving exit codes
	exit, err := cmd.Process.Wait()
	if err != nil {
		panic(err)
	}
	waitStatus := exit.Sys().(syscall.WaitStatus)
	if waitStatus.Exited() {
		return string(out), waitStatus.ExitStatus()
	} else {
		panic(waitStatus)
	}
	return string(out), 0
}

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}

func mustCmd(out string, exitCode int) string {
	if exitCode == 0 {
		return out
	}
	panic(fmt.Errorf(
		"expected command to pass; got code %d and message:\n\t%q",
		exitCode, out,
	))
}

func TestHello(t *testing.T) {
	Convey("Given a local git repo", t,
		testutil.WithTmpdir(func(c C) {
			mustCmd(execGit("init", "--", "repo-a"))
			var commitHash_1, commitHash_2 string
			testutil.UsingDir("repo-a", func() {
				mustCmd(execGit("commit", "--allow-empty", "-m", "testrepo-a initial commit"))
				commitHash_1 = strings.Trim(mustCmd(execGit("rev-parse", "HEAD")), "\n")
				ioutil.WriteFile("file-a", []byte("abcd"), 0644)
				mustCmd(execGit("add", "."))
				mustCmd(execGit("commit", "-m", "testrepo-a commit 1"))
				commitHash_2 = strings.Trim(mustCmd(execGit("rev-parse", "HEAD")), "\n")
			})

			Convey("Magic should happen", FailureContinues, func() {
			})
		}),
	)
}
<commit_msg>just... drop all this exec stuff for now.<commit_after>package gat

func maybePanic(err error) {
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package query

import (
	"reflect"
	"strings"
	"testing"

	turtle "github.com/gtfierro/hod/goraptor"
)

func TestOrClauseFlatten(t *testing.T) {
	for _, test := range []struct {
		orclause  OrClause
		flattened [][]Filter
	}{
		{
			OrClause{
				LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
			},
			[][]Filter{
				{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
			},
		},
	} {
		flattened := test.orclause.Flatten()
		if len(flattened) != len(test.flattened) {
			t.Errorf("Flatten() failed. Wanted\n%v\nbut got\n%v", test.flattened, flattened)
		}
		for idx := 0; idx < len(flattened); idx++ {
			if !compareFilterSliceAsSet(flattened[idx], test.flattened[idx]) {
				t.Errorf("Flatten() failed. Wanted\n%v\nbut got\n%v", test.flattened, flattened)
			}
		}

	}
}

func TestQueryParse(t *testing.T) {
	for _, test := range []struct {
		str          string
		selectClause SelectClause
		whereClause  WhereClause
	}{
		{
			"SELECT ?x WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x WHERE { ?x rdf:type brick:Room . } LIMIT 10;",
			SelectClause{Limit: 10, Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?x ?y brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("?y"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type rdf:type . ?x ?y brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{
					{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("?y"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")},
					{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("rdf:type")},
				},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x WHERE { ?x rdf:type+ brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type|rdfs:subClassOf ?x .} ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
						LeftOr: []OrClause{
							{
								RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
							},
						},
					},
				},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type|rdfs:subClassOf|rdf:isa ?x .} ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
						LeftOr: []OrClause{
							{
								RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
								LeftOr: []OrClause{
									{
										RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:isa"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
									},
								},
							},
						},
					},
				},
			},
		},
		{
			"SELECT ?x WHERE { {?x rdf:type brick:Room . OR ?x rdf:type brick:Floor .} };",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{{
					LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
					RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				}},
			},
		},
		{
			"SELECT ?x WHERE { ?y rdf:type brick:Building . { { ?x rdf:type brick:Room . ?x bf:isPartOf+ ?y .} OR { ?x rdf:type brick:Floor . ?x bf:isPartOf+ ?y .} } };",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Building")}},
				[]OrClause{{
					LeftTerms: []Filter{
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")},
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("bf:isPartOf"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("?y")},
					},
					RightTerms: []Filter{
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")},
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("bf:isPartOf"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("?y")},
					},
				}},
			},
		},
		{
			"SELECT ?x[link1] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: false, Links: []Link{{Name: "link1"}}}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x[link1, link2] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: false, Links: []Link{{Name: "link1"}, {Name: "link2"}}}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x[*] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: true}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
	} {
		r := strings.NewReader(test.str)
		q, e := Parse(r)
		if e != nil {
			t.Errorf("Error on query: %s", test.str, e)
			continue
		}
		if !reflect.DeepEqual(q.Select, test.selectClause) {
			t.Errorf("Query %s got select\n %+v\nexpected\n %+v", test.str, q.Select, test.selectClause)
			return
		}
		if !compareFilterSliceAsSet(q.Where.Filters, test.whereClause.Filters) {
			t.Errorf("Query %s got where Filters\n %v\nexpected\n %v", test.str, q.Where.Filters, test.whereClause.Filters)
			return
		}
		if !compareOrClauseLists(q.Where.Ors, test.whereClause.Ors) {
			t.Errorf("Query %s got where Ors\n %+v\nexpected\n %+v", test.str, q.Where.Ors, test.whereClause.Ors)
			return
		}
	}
}

func BenchmarkQueryParseShort(b *testing.B) {
	for i := 0; i < b.N; i++ {
		r := strings.NewReader("SELECT ?x WHERE { ?x rdf:type brick:Room . } ;")
		Parse(r)
	}
}

func BenchmarkQueryParseLong(b *testing.B) {
	for i := 0; i < b.N; i++ {
		r := strings.NewReader("SELECT ?x WHERE { ?y rdf:type brick:Building . { { ?x rdf:type brick:Room . ?x bf:isPartOf+ ?y .} OR { ?x rdf:type brick:Floor . ?x bf:isPartOf+ ?y .} } };")
		Parse(r)
	}
}

func BenchmarkOrClauseFlattenShort(b *testing.B) {
	oc := OrClause{
		LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
		RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
	}
	for i := 0; i < b.N; i++ {
		oc.Flatten()
	}
}
func BenchmarkOrClauseFlattenLong(b *testing.B) {
	oc := OrClause{
		RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
		LeftOr: []OrClause{
			{
				RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
				LeftOr: []OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:isa"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
					},
				},
			},
		},
	}
	for i := 0; i < b.N; i++ {
		oc.Flatten()
	}
}
<commit_msg>add parse test for variable predicate<commit_after>package query

import (
	"reflect"
	"strings"
	"testing"

	turtle "github.com/gtfierro/hod/goraptor"
)

func TestOrClauseFlatten(t *testing.T) {
	for _, test := range []struct {
		orclause  OrClause
		flattened [][]Filter
	}{
		{
			OrClause{
				LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
			},
			[][]Filter{
				{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
			},
		},
	} {
		flattened := test.orclause.Flatten()
		if len(flattened) != len(test.flattened) {
			t.Errorf("Flatten() failed. Wanted\n%v\nbut got\n%v", test.flattened, flattened)
		}
		for idx := 0; idx < len(flattened); idx++ {
			if !compareFilterSliceAsSet(flattened[idx], test.flattened[idx]) {
				t.Errorf("Flatten() failed. Wanted\n%v\nbut got\n%v", test.flattened, flattened)
			}
		}

	}
}

func TestQueryParse(t *testing.T) {
	for _, test := range []struct {
		str          string
		selectClause SelectClause
		whereClause  WhereClause
	}{
		{
			"SELECT ?x WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x WHERE { ?x ?y brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("?y"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x WHERE { ?x rdf:type brick:Room . } LIMIT 10;",
			SelectClause{Limit: 10, Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?x ?y brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("?y"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type rdf:type . ?x ?y brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{
					{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("?y"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")},
					{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("rdf:type")},
				},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x WHERE { ?x rdf:type+ brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type|rdfs:subClassOf ?x .} ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
						LeftOr: []OrClause{
							{
								RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
							},
						},
					},
				},
			},
		},
		{
			"SELECT ?x ?y WHERE { ?y rdf:type|rdfs:subClassOf|rdf:isa ?x .} ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}, {Var: turtle.ParseURI("?y")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
						LeftOr: []OrClause{
							{
								RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
								LeftOr: []OrClause{
									{
										RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:isa"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
									},
								},
							},
						},
					},
				},
			},
		},
		{
			"SELECT ?x WHERE { {?x rdf:type brick:Room . OR ?x rdf:type brick:Floor .} };",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{},
				[]OrClause{{
					LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
					RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				}},
			},
		},
		{
			"SELECT ?x WHERE { ?y rdf:type brick:Building . { { ?x rdf:type brick:Room . ?x bf:isPartOf+ ?y .} OR { ?x rdf:type brick:Floor . ?x bf:isPartOf+ ?y .} } };",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x")}}, HasLinks: false},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Building")}},
				[]OrClause{{
					LeftTerms: []Filter{
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")},
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("bf:isPartOf"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("?y")},
					},
					RightTerms: []Filter{
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")},
						{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("bf:isPartOf"), PATTERN_ONE_PLUS}}, Object: turtle.ParseURI("?y")},
					},
				}},
			},
		},
		{
			"SELECT ?x[link1] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: false, Links: []Link{{Name: "link1"}}}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x[link1, link2] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: false, Links: []Link{{Name: "link1"}, {Name: "link2"}}}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
		{
			"SELECT ?x[*] WHERE { ?x rdf:type brick:Room . } ;",
			SelectClause{Variables: []SelectVar{{Var: turtle.ParseURI("?x"), AllLinks: true}}, HasLinks: true},
			WhereClause{
				[]Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
				[]OrClause{},
			},
		},
	} {
		r := strings.NewReader(test.str)
		q, e := Parse(r)
		if e != nil {
			t.Errorf("Error on query: %s", test.str, e)
			continue
		}
		if !reflect.DeepEqual(q.Select, test.selectClause) {
			t.Errorf("Query %s got select\n %+v\nexpected\n %+v", test.str, q.Select, test.selectClause)
			return
		}
		if !compareFilterSliceAsSet(q.Where.Filters, test.whereClause.Filters) {
			t.Errorf("Query %s got where Filters\n %v\nexpected\n %v", test.str, q.Where.Filters, test.whereClause.Filters)
			return
		}
		if !compareOrClauseLists(q.Where.Ors, test.whereClause.Ors) {
			t.Errorf("Query %s got where Ors\n %+v\nexpected\n %+v", test.str, q.Where.Ors, test.whereClause.Ors)
			return
		}
	}
}

func BenchmarkQueryParseShort(b *testing.B) {
	for i := 0; i < b.N; i++ {
		r := strings.NewReader("SELECT ?x WHERE { ?x rdf:type brick:Room . } ;")
		Parse(r)
	}
}

func BenchmarkQueryParseLong(b *testing.B) {
	for i := 0; i < b.N; i++ {
		r := strings.NewReader("SELECT ?x WHERE { ?y rdf:type brick:Building . { { ?x rdf:type brick:Room . ?x bf:isPartOf+ ?y .} OR { ?x rdf:type brick:Floor . ?x bf:isPartOf+ ?y .} } };")
		Parse(r)
	}
}

func BenchmarkOrClauseFlattenShort(b *testing.B) {
	oc := OrClause{
		LeftTerms:  []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Room")}},
		RightTerms: []Filter{{Subject: turtle.ParseURI("?x"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("brick:Floor")}},
	}
	for i := 0; i < b.N; i++ {
		oc.Flatten()
	}
}
func BenchmarkOrClauseFlattenLong(b *testing.B) {
	oc := OrClause{
		RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:type"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
		LeftOr: []OrClause{
			{
				RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdfs:subClassOf"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
				LeftOr: []OrClause{
					{
						RightTerms: []Filter{{Subject: turtle.ParseURI("?y"), Path: []PathPattern{PathPattern{turtle.ParseURI("rdf:isa"), PATTERN_SINGLE}}, Object: turtle.ParseURI("?x")}},
					},
				},
			},
		},
	}
	for i := 0; i < b.N; i++ {
		oc.Flatten()
	}
}
<|endoftext|>


<commit_before>package commands

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

	"github.com/StackExchange/dnscontrol/v2/models"
	"github.com/StackExchange/dnscontrol/v2/pkg/acme"
	"github.com/StackExchange/dnscontrol/v2/pkg/normalize"
	"github.com/StackExchange/dnscontrol/v2/pkg/printer"
	"github.com/urfave/cli"
)

var _ = cmd(catUtils, func() *cli.Command {
	var args GetCertsArgs
	return &cli.Command{
		Name:  "get-certs",
		Usage: "Issue certificates via Let's Encrypt",
		Action: func(c *cli.Context) error {
			return exit(GetCerts(args))
		},
		Flags: args.flags(),
	}
}())

type GetCertsArgs struct {
	GetDNSConfigArgs
	GetCredentialsArgs

	ACMEServer     string
	CertsFile      string
	RenewUnderDays int
	CertDirectory  string
	Email          string
	AgreeTOS       bool
	Verbose        bool
	Vault          bool
	VaultPath      string
	Only           string

	Notify bool

	IgnoredProviders string
}

func (args *GetCertsArgs) flags() []cli.Flag {
	flags := args.GetDNSConfigArgs.flags()
	flags = append(flags, args.GetCredentialsArgs.flags()...)

	flags = append(flags, cli.StringFlag{
		Name:        "acme",
		Destination: &args.ACMEServer,
		Value:       "live",
		Usage:       `ACME server to issue against. Give full directory endpoint. Can also use 'staging' or 'live' for standard Let's Encrpyt endpoints.`,
	})
	flags = append(flags, cli.IntFlag{
		Name:        "renew",
		Destination: &args.RenewUnderDays,
		Value:       15,
		Usage:       `Renew certs with less than this many days remaining`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "dir",
		Destination: &args.CertDirectory,
		Value:       ".",
		Usage:       `Directory to store certificates and other data`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "certConfig",
		Destination: &args.CertsFile,
		Value:       "certs.json",
		Usage:       `Json file containing list of certificates to issue`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "email",
		Destination: &args.Email,
		Value:       "",
		Usage:       `Email to register with let's encrypt`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "agreeTOS",
		Destination: &args.AgreeTOS,
		Usage:       `Must provide this to agree to Let's Encrypt terms of service`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "vault",
		Destination: &args.Vault,
		Usage:       `Store certificates as secrets in hashicorp vault instead of on disk.`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "vaultPath",
		Destination: &args.VaultPath,
		Value:       "/secret/certs",
		Usage:       `Path in vault to store certificates`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "skip",
		Destination: &args.IgnoredProviders,
		Value:       "",
		Usage:       `Provider names to not use for challenges (comma separated)`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "verbose",
		Destination: &args.Verbose,
		Usage:       "Enable detailed logging (deprecated: use the global -v flag)",
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "notify",
		Destination: &args.Notify,
		Usage:       `set to true to send notifications to configured destinations`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "only",
		Destination: &args.Only,
		Usage:       `Only check a single cert. Provide cert name.`,
	})
	return flags
}

func GetCerts(args GetCertsArgs) error {
	fmt.Println(args.JSFile)
	// check agree flag
	if !args.AgreeTOS {
		return fmt.Errorf("You must agree to the Let's Encrypt Terms of Service by using -agreeTOS")
	}
	if args.Email == "" {
		return fmt.Errorf("Must provide email to use for Let's Encrypt registration")
	}

	// load dns config
	cfg, err := GetDNSConfig(args.GetDNSConfigArgs)
	if err != nil {
		return err
	}
	errs := normalize.NormalizeAndValidateConfig(cfg)
	if PrintValidationErrors(errs) {
		return fmt.Errorf("Exiting due to validation errors")
	}
	notifier, err := InitializeProviders(args.CredsFile, cfg, args.Notify)
	if err != nil {
		return err
	}

	for _, skip := range strings.Split(args.IgnoredProviders, ",") {
		acme.IgnoredProviders[skip] = true
	}

	// load cert list
	certList := []*acme.CertConfig{}
	f, err := os.Open(args.CertsFile)
	if err != nil {
		return err
	}
	defer f.Close()
	dec := json.NewDecoder(f)
	err = dec.Decode(&certList)
	if err != nil {
		return err
	}
	if len(certList) == 0 {
		return fmt.Errorf("Must provide at least one certificate to issue in cert configuration")
	}
	if err = validateCertificateList(certList, cfg); err != nil {
		return err
	}

	acmeServer := args.ACMEServer
	if acmeServer == "live" {
		acmeServer = acme.LetsEncryptLive
	} else if acmeServer == "staging" {
		acmeServer = acme.LetsEncryptStage
	}

	var client acme.Client

	if args.Vault {
		client, err = acme.NewVault(cfg, args.VaultPath, args.Email, acmeServer, notifier)
	} else {
		client, err = acme.New(cfg, args.CertDirectory, args.Email, acmeServer, notifier)
	}
	if err != nil {
		return err
	}
	var manyerr error
	for _, cert := range certList {
		if args.Only != "" && cert.CertName != args.Only {
			continue
		}
		v := args.Verbose || printer.DefaultPrinter.Verbose
		issued, err := client.IssueOrRenewCert(cert, args.RenewUnderDays, v)
		if issued || err != nil {
			notifier.Notify(cert.CertName, "certificate", "Issued new certificate", err, false)
		}
		if err != nil {
			if manyerr == nil {
				manyerr = err
			} else {
				manyerr = fmt.Errorf("%w; %v", manyerr, err)
			}
		}
	}
	notifier.Done()
	return manyerr
}

var validCertNamesRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_\-]*$`)

func validateCertificateList(certs []*acme.CertConfig, cfg *models.DNSConfig) error {
	for _, cert := range certs {
		name := cert.CertName
		if !validCertNamesRegex.MatchString(name) {
			return fmt.Errorf("'%s' is not a valid certificate name. Only alphanumerics, - and _ allowed", name)
		}
		sans := cert.Names
		if len(sans) > 100 {
			return fmt.Errorf("certificate '%s' has too many SANs. Max of 100", name)
		}
		if len(sans) == 0 {
			return fmt.Errorf("certificate '%s' needs at least one SAN", name)
		}
		for _, san := range sans {
			d := cfg.DomainContainingFQDN(san)
			if d == nil {
				return fmt.Errorf("DNS config has no domain that matches SAN '%s'", san)
			}
		}
	}
	return nil
}
<commit_msg>linting<commit_after>package commands

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

	"github.com/StackExchange/dnscontrol/v2/models"
	"github.com/StackExchange/dnscontrol/v2/pkg/acme"
	"github.com/StackExchange/dnscontrol/v2/pkg/normalize"
	"github.com/StackExchange/dnscontrol/v2/pkg/printer"
	"github.com/urfave/cli"
)

var _ = cmd(catUtils, func() *cli.Command {
	var args GetCertsArgs
	return &cli.Command{
		Name:  "get-certs",
		Usage: "Issue certificates via Let's Encrypt",
		Action: func(c *cli.Context) error {
			return exit(GetCerts(args))
		},
		Flags: args.flags(),
	}
}())

// GetCertsArgs stores the flags and arguments common to cert commands
type GetCertsArgs struct {
	GetDNSConfigArgs
	GetCredentialsArgs

	ACMEServer     string
	CertsFile      string
	RenewUnderDays int
	CertDirectory  string
	Email          string
	AgreeTOS       bool
	Verbose        bool
	Vault          bool
	VaultPath      string
	Only           string

	Notify bool

	IgnoredProviders string
}

func (args *GetCertsArgs) flags() []cli.Flag {
	flags := args.GetDNSConfigArgs.flags()
	flags = append(flags, args.GetCredentialsArgs.flags()...)

	flags = append(flags, cli.StringFlag{
		Name:        "acme",
		Destination: &args.ACMEServer,
		Value:       "live",
		Usage:       `ACME server to issue against. Give full directory endpoint. Can also use 'staging' or 'live' for standard Let's Encrpyt endpoints.`,
	})
	flags = append(flags, cli.IntFlag{
		Name:        "renew",
		Destination: &args.RenewUnderDays,
		Value:       15,
		Usage:       `Renew certs with less than this many days remaining`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "dir",
		Destination: &args.CertDirectory,
		Value:       ".",
		Usage:       `Directory to store certificates and other data`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "certConfig",
		Destination: &args.CertsFile,
		Value:       "certs.json",
		Usage:       `Json file containing list of certificates to issue`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "email",
		Destination: &args.Email,
		Value:       "",
		Usage:       `Email to register with let's encrypt`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "agreeTOS",
		Destination: &args.AgreeTOS,
		Usage:       `Must provide this to agree to Let's Encrypt terms of service`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "vault",
		Destination: &args.Vault,
		Usage:       `Store certificates as secrets in hashicorp vault instead of on disk.`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "vaultPath",
		Destination: &args.VaultPath,
		Value:       "/secret/certs",
		Usage:       `Path in vault to store certificates`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "skip",
		Destination: &args.IgnoredProviders,
		Value:       "",
		Usage:       `Provider names to not use for challenges (comma separated)`,
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "verbose",
		Destination: &args.Verbose,
		Usage:       "Enable detailed logging (deprecated: use the global -v flag)",
	})
	flags = append(flags, cli.BoolFlag{
		Name:        "notify",
		Destination: &args.Notify,
		Usage:       `set to true to send notifications to configured destinations`,
	})
	flags = append(flags, cli.StringFlag{
		Name:        "only",
		Destination: &args.Only,
		Usage:       `Only check a single cert. Provide cert name.`,
	})
	return flags
}

// GetCerts implements the get-certs command.
func GetCerts(args GetCertsArgs) error {
	fmt.Println(args.JSFile)
	// check agree flag
	if !args.AgreeTOS {
		return fmt.Errorf("You must agree to the Let's Encrypt Terms of Service by using -agreeTOS")
	}
	if args.Email == "" {
		return fmt.Errorf("Must provide email to use for Let's Encrypt registration")
	}

	// load dns config
	cfg, err := GetDNSConfig(args.GetDNSConfigArgs)
	if err != nil {
		return err
	}
	errs := normalize.NormalizeAndValidateConfig(cfg)
	if PrintValidationErrors(errs) {
		return fmt.Errorf("Exiting due to validation errors")
	}
	notifier, err := InitializeProviders(args.CredsFile, cfg, args.Notify)
	if err != nil {
		return err
	}

	for _, skip := range strings.Split(args.IgnoredProviders, ",") {
		acme.IgnoredProviders[skip] = true
	}

	// load cert list
	certList := []*acme.CertConfig{}
	f, err := os.Open(args.CertsFile)
	if err != nil {
		return err
	}
	defer f.Close()
	dec := json.NewDecoder(f)
	err = dec.Decode(&certList)
	if err != nil {
		return err
	}
	if len(certList) == 0 {
		return fmt.Errorf("Must provide at least one certificate to issue in cert configuration")
	}
	if err = validateCertificateList(certList, cfg); err != nil {
		return err
	}

	acmeServer := args.ACMEServer
	if acmeServer == "live" {
		acmeServer = acme.LetsEncryptLive
	} else if acmeServer == "staging" {
		acmeServer = acme.LetsEncryptStage
	}

	var client acme.Client

	if args.Vault {
		client, err = acme.NewVault(cfg, args.VaultPath, args.Email, acmeServer, notifier)
	} else {
		client, err = acme.New(cfg, args.CertDirectory, args.Email, acmeServer, notifier)
	}
	if err != nil {
		return err
	}
	var manyerr error
	for _, cert := range certList {
		if args.Only != "" && cert.CertName != args.Only {
			continue
		}
		v := args.Verbose || printer.DefaultPrinter.Verbose
		issued, err := client.IssueOrRenewCert(cert, args.RenewUnderDays, v)
		if issued || err != nil {
			notifier.Notify(cert.CertName, "certificate", "Issued new certificate", err, false)
		}
		if err != nil {
			if manyerr == nil {
				manyerr = err
			} else {
				manyerr = fmt.Errorf("%w; %v", manyerr, err)
			}
		}
	}
	notifier.Done()
	return manyerr
}

var validCertNamesRegex = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_\-]*$`)

func validateCertificateList(certs []*acme.CertConfig, cfg *models.DNSConfig) error {
	for _, cert := range certs {
		name := cert.CertName
		if !validCertNamesRegex.MatchString(name) {
			return fmt.Errorf("'%s' is not a valid certificate name. Only alphanumerics, - and _ allowed", name)
		}
		sans := cert.Names
		if len(sans) > 100 {
			return fmt.Errorf("certificate '%s' has too many SANs. Max of 100", name)
		}
		if len(sans) == 0 {
			return fmt.Errorf("certificate '%s' needs at least one SAN", name)
		}
		for _, san := range sans {
			d := cfg.DomainContainingFQDN(san)
			if d == nil {
				return fmt.Errorf("DNS config has no domain that matches SAN '%s'", san)
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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.

// +build generate windows

// Package dism provides an interface to the Deployment Image Servicing and Management (DISM).
//
// Reference: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/deployment-image-servicing-and-management--dism--api
package dism

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"
)

// API Constants
// Ref https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dism-api-constants
const (
	DISM_ONLINE_IMAGE = "DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}"

	DISM_MOUNT_READWRITE       = 0x00000000
	DISM_MOUNT_READONLY        = 0x00000001
	DISM_MOUNT_OPTIMIZE        = 0x00000002
	DISM_MOUNT_CHECK_INTEGRITY = 0x00000004
)

// DismPackageIdentifier specifies whether a package is identified by name or by file path.
type DismPackageIdentifier uint32

const (
	// DismPackageNone indicates that no package is specified.
	DismPackageNone DismPackageIdentifier = iota
	// DismPackageName indicates that the package is identified by its name.
	DismPackageName
	// DismPackagePath indicates that the package is specified by its path.
	DismPackagePath
)

// Session holds a dism session. You must call Close() to free up the session upon completion.
type Session struct {
	Handle uint32
}

// AddPackage adds Windows packages(s) to an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismaddpackage-function
func (s Session) AddPackage(
	packagePath string,
	ignoreCheck bool,
	preventPending bool,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismAddPackage(s.Handle, toUint16(packagePath), ignoreCheck, preventPending, cancelEvent, progressCallback, nil)
}

// DisableFeature disables Windows Feature(s).
//
// To disable multiple features, separate each feature name with a semicolon.
//
// May return the error windows.ERROR_SUCCESS_REBOOT_REQUIRED if a reboot is required to complete the operation.
//
// Example, disabling a feature:
//   s.DisableFeature("SMB1Protocol", "", nil, nil)
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismdisablefeature-function
func (s Session) DisableFeature(
	feature string,
	optPackageName string,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismDisableFeature(s.Handle, toUint16(feature), toUint16(optPackageName), false, cancelEvent, progressCallback, nil)
}

// EnableFeature enables Windows Feature(s).
//
// To enable multiple features, separate each feature name with a semicolon.
//
// May return the error windows.ERROR_SUCCESS_REBOOT_REQUIRED if a reboot is required to complete the operation.
//
// Example, enabling a feature, including all dependencies:
//   s.EnableFeature("SMB1Protocol", "", nil, true, nil, nil)
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismenablefeature-function
func (s Session) EnableFeature(
	feature string,
	optIdentifier string,
	optPackageIdentifier *DismPackageIdentifier,
	enableAll bool,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismEnableFeature(s.Handle, toUint16(feature), toUint16(optIdentifier), optPackageIdentifier, false, nil, 0, enableAll, cancelEvent, progressCallback, nil)
}

// RemovePackage removes Windows packages(s) from an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismremovepackage-function
func (s Session) RemovePackage(
	identifier string,
	packageIdentifier *DismPackageIdentifier,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismRemovePackage(s.Handle, toUint16(identifier), packageIdentifier, cancelEvent, progressCallback, nil)
}

// Close closes the session and shuts down dism. This must be called prior to exiting.
func (s Session) Close() error {
	if err := DismCloseSession(s.Handle); err != nil {
		return err
	}
	return DismShutdown()
}

// DismLogLevel specifies the kind of information that is reported in the log file.
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismloglevel-enumeration
type DismLogLevel uint32

const (
	// DismLogErrors logs only errors.
	DismLogErrors DismLogLevel = 0
	// DismLogErrorsWarnings logs errors and warnings.
	DismLogErrorsWarnings DismLogLevel = 1
	// DismLogErrorsWarningsInfo logs errors, warnings, and additional information.
	DismLogErrorsWarningsInfo DismLogLevel = 2
)

func toUint16(in string) (out *uint16) {
	if in != "" {
		out = windows.StringToUTF16Ptr(in)
	}
	return
}

// OpenSession opens a DISM session. The session can be used for subsequent DISM calls.
//
// Don't forget to call Close() on the returned Session object.
//
// Example, modifying the online image:
//		dism.OpenSession(dism.DISM_ONLINE_IMAGE, "", "", dism.DismLogErrorsWarningsInfo, "", "")
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/disminitialize-function
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismopensession-function
func OpenSession(imagePath, optWindowsDir, optSystemDrive string, logLevel DismLogLevel, optLogFilePath, optScratchDir string) (Session, error) {
	s := Session{}

	if err := DismInitialize(logLevel, toUint16(optLogFilePath), toUint16(optScratchDir)); err != nil {
		return s, fmt.Errorf("DismInitialize: %w", err)
	}

	if err := DismOpenSession(toUint16(imagePath), toUint16(optWindowsDir), toUint16(optSystemDrive), &s.Handle); err != nil {
		return s, fmt.Errorf("DismOpenSession: %w", err)
	}

	return s, nil
}

//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zdism.go dism.go
//sys DismAddCapability(Session uint32, Name *uint16, LimitAccess bool, SourcePaths **uint16,  SourcePathCount uint32, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismAddCapability
//sys DismAddDriver(Session uint32, DriverPath *uint16, ForceUnsigned bool) (e error) = DismAPI.DismAddDriver
//sys DismAddPackage(Session uint32, PackagePath *uint16, IgnoreCheck bool, PreventPending bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismAddPackage
//sys DismApplyUnattend(Session uint32, UnattendFile *uint16, SingleSession bool) (e error) = DismAPI.DismApplyUnattend
//sys DismCloseSession(Session uint32) (e error) = DismAPI.DismCloseSession
//sys DismInitialize(LogLevel DismLogLevel, LogFilePath *uint16, ScratchDirectory *uint16) (e error) = DismAPI.DismInitialize
//sys DismDisableFeature(Session uint32, FeatureName *uint16, PackageName *uint16, RemovePayload bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismDisableFeature
//sys DismEnableFeature(Session uint32, FeatureName *uint16, Identifier *uint16, PackageIdentifier *DismPackageIdentifier, LimitAccess bool, SourcePaths *string, SourcePathCount uint32, EnableAll bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismEnableFeature
//sys DismOpenSession(ImagePath *uint16, WindowsDirectory *uint16, SystemDrive *uint16, Session *uint32) (e error) = DismAPI.DismOpenSession
//sys DismRemoveCapability(Session uint32, Name *uint16, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismRemoveCapability
//sys DismRemoveDriver(Session uint32, DriverPath *uint16) (e error) = DismAPI.DismRemoveDriver
//sys DismRemovePackage(Session uint32, Identifier *uint16, PackageIdentifier *DismPackageIdentifier, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismRemovePackage
//sys DismShutdown() (e error) = DismAPI.DismShutdown
<commit_msg>Add dism.AddCapability and dism.RemoveCapability<commit_after>// Copyright 2021 Google LLC
//
// 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.

// +build generate windows

// Package dism provides an interface to the Deployment Image Servicing and Management (DISM).
//
// Reference: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/deployment-image-servicing-and-management--dism--api
package dism

import (
	"fmt"
	"unsafe"

	"golang.org/x/sys/windows"
)

// API Constants
// Ref https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dism-api-constants
const (
	DISM_ONLINE_IMAGE = "DISM_{53BFAE52-B167-4E2F-A258-0A37B57FF845}"

	DISM_MOUNT_READWRITE       = 0x00000000
	DISM_MOUNT_READONLY        = 0x00000001
	DISM_MOUNT_OPTIMIZE        = 0x00000002
	DISM_MOUNT_CHECK_INTEGRITY = 0x00000004
)

// DismPackageIdentifier specifies whether a package is identified by name or by file path.
type DismPackageIdentifier uint32

const (
	// DismPackageNone indicates that no package is specified.
	DismPackageNone DismPackageIdentifier = iota
	// DismPackageName indicates that the package is identified by its name.
	DismPackageName
	// DismPackagePath indicates that the package is specified by its path.
	DismPackagePath
)

// Session holds a dism session. You must call Close() to free up the session upon completion.
type Session struct {
	Handle uint32
}

// AddCapability adds a Windows capability from an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismaddcapability
func (s Session) AddCapability(
	name string,
	limitAccess bool,
	sourcePaths string,
	sourcePathsCount uint32,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	var sp **uint16
	if p := toUint16(sourcePaths); p != nil {
		sp = &p
	}
	return DismAddCapability(s.Handle, toUint16(name), limitAccess, sp, sourcePathsCount, cancelEvent, progressCallback, nil)
}

// AddPackage adds Windows packages(s) to an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismaddpackage-function
func (s Session) AddPackage(
	packagePath string,
	ignoreCheck bool,
	preventPending bool,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismAddPackage(s.Handle, toUint16(packagePath), ignoreCheck, preventPending, cancelEvent, progressCallback, nil)
}

// DisableFeature disables Windows Feature(s).
//
// To disable multiple features, separate each feature name with a semicolon.
//
// May return the error windows.ERROR_SUCCESS_REBOOT_REQUIRED if a reboot is required to complete the operation.
//
// Example, disabling a feature:
//   s.DisableFeature("SMB1Protocol", "", nil, nil)
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismdisablefeature-function
func (s Session) DisableFeature(
	feature string,
	optPackageName string,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismDisableFeature(s.Handle, toUint16(feature), toUint16(optPackageName), false, cancelEvent, progressCallback, nil)
}

// EnableFeature enables Windows Feature(s).
//
// To enable multiple features, separate each feature name with a semicolon.
//
// May return the error windows.ERROR_SUCCESS_REBOOT_REQUIRED if a reboot is required to complete the operation.
//
// Example, enabling a feature, including all dependencies:
//   s.EnableFeature("SMB1Protocol", "", nil, true, nil, nil)
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismenablefeature-function
func (s Session) EnableFeature(
	feature string,
	optIdentifier string,
	optPackageIdentifier *DismPackageIdentifier,
	enableAll bool,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismEnableFeature(s.Handle, toUint16(feature), toUint16(optIdentifier), optPackageIdentifier, false, nil, 0, enableAll, cancelEvent, progressCallback, nil)
}

// RemoveCapability removes a Windows capability from an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismremovecapability
func (s Session) RemoveCapability(
	name string,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismRemoveCapability(s.Handle, toUint16(name), cancelEvent, progressCallback, nil)
}

// RemovePackage removes Windows packages(s) from an image.
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismremovepackage-function
func (s Session) RemovePackage(
	identifier string,
	packageIdentifier *DismPackageIdentifier,
	cancelEvent *windows.Handle,
	progressCallback unsafe.Pointer,
) error {
	return DismRemovePackage(s.Handle, toUint16(identifier), packageIdentifier, cancelEvent, progressCallback, nil)
}

// Close closes the session and shuts down dism. This must be called prior to exiting.
func (s Session) Close() error {
	if err := DismCloseSession(s.Handle); err != nil {
		return err
	}
	return DismShutdown()
}

// DismLogLevel specifies the kind of information that is reported in the log file.
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismloglevel-enumeration
type DismLogLevel uint32

const (
	// DismLogErrors logs only errors.
	DismLogErrors DismLogLevel = 0
	// DismLogErrorsWarnings logs errors and warnings.
	DismLogErrorsWarnings DismLogLevel = 1
	// DismLogErrorsWarningsInfo logs errors, warnings, and additional information.
	DismLogErrorsWarningsInfo DismLogLevel = 2
)

func toUint16(in string) (out *uint16) {
	if in != "" {
		out = windows.StringToUTF16Ptr(in)
	}
	return
}

// OpenSession opens a DISM session. The session can be used for subsequent DISM calls.
//
// Don't forget to call Close() on the returned Session object.
//
// Example, modifying the online image:
//		dism.OpenSession(dism.DISM_ONLINE_IMAGE, "", "", dism.DismLogErrorsWarningsInfo, "", "")
//
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/disminitialize-function
// Ref: https://docs.microsoft.com/en-us/windows-hardware/manufacture/desktop/dism/dismopensession-function
func OpenSession(imagePath, optWindowsDir, optSystemDrive string, logLevel DismLogLevel, optLogFilePath, optScratchDir string) (Session, error) {
	s := Session{}

	if err := DismInitialize(logLevel, toUint16(optLogFilePath), toUint16(optScratchDir)); err != nil {
		return s, fmt.Errorf("DismInitialize: %w", err)
	}

	if err := DismOpenSession(toUint16(imagePath), toUint16(optWindowsDir), toUint16(optSystemDrive), &s.Handle); err != nil {
		return s, fmt.Errorf("DismOpenSession: %w", err)
	}

	return s, nil
}

//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zdism.go dism.go
//sys DismAddCapability(Session uint32, Name *uint16, LimitAccess bool, SourcePaths **uint16,  SourcePathCount uint32, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismAddCapability
//sys DismAddDriver(Session uint32, DriverPath *uint16, ForceUnsigned bool) (e error) = DismAPI.DismAddDriver
//sys DismAddPackage(Session uint32, PackagePath *uint16, IgnoreCheck bool, PreventPending bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismAddPackage
//sys DismApplyUnattend(Session uint32, UnattendFile *uint16, SingleSession bool) (e error) = DismAPI.DismApplyUnattend
//sys DismCloseSession(Session uint32) (e error) = DismAPI.DismCloseSession
//sys DismInitialize(LogLevel DismLogLevel, LogFilePath *uint16, ScratchDirectory *uint16) (e error) = DismAPI.DismInitialize
//sys DismDisableFeature(Session uint32, FeatureName *uint16, PackageName *uint16, RemovePayload bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismDisableFeature
//sys DismEnableFeature(Session uint32, FeatureName *uint16, Identifier *uint16, PackageIdentifier *DismPackageIdentifier, LimitAccess bool, SourcePaths *string, SourcePathCount uint32, EnableAll bool, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismEnableFeature
//sys DismOpenSession(ImagePath *uint16, WindowsDirectory *uint16, SystemDrive *uint16, Session *uint32) (e error) = DismAPI.DismOpenSession
//sys DismRemoveCapability(Session uint32, Name *uint16, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismRemoveCapability
//sys DismRemoveDriver(Session uint32, DriverPath *uint16) (e error) = DismAPI.DismRemoveDriver
//sys DismRemovePackage(Session uint32, Identifier *uint16, PackageIdentifier *DismPackageIdentifier, CancelEvent *windows.Handle, Progress unsafe.Pointer, UserData unsafe.Pointer) (e error) = DismAPI.DismRemovePackage
//sys DismShutdown() (e error) = DismAPI.DismShutdown
<|endoftext|>


<commit_before>package goactor

import (
	"fmt"
	"testing"
)

type AnActor struct {
	Actor
	outbox chan string
}

func (this AnActor) Act(message Any) {
	response := fmt.Sprintf("Got '%s'", message)
	this.outbox <- response
}

func expectResponseToEq(t *testing.T, outbox chan string, expected string) {
	if actual := <-outbox; actual != expected {
		t.Errorf("Expected to receive '%s' but received '%s'", expected, actual)
	}
}

func TestHandlesInboxMessages(t *testing.T) {
	outbox := make(chan string)
	anActor := AnActor{
		Actor:  NewActor(),
		outbox: outbox,
	}
	Go(anActor, "String Actor")

	Send(anActor, "hello, world")
	expectResponseToEq(t, outbox, "Got 'hello, world'")

	Send(anActor, "hello, goworld")
	expectResponseToEq(t, outbox, "Got 'hello, goworld'")
}

func TestClosedInbox(t *testing.T) {
	anActor := AnActor{
		Actor: NewActor(),
	}
	Go(anActor, "String Actor")

	close(anActor.Inbox())
}

type AnIntegerActor struct {
	Actor
	outbox chan int
}

func (this AnIntegerActor) Act(message Any) {
	integerMessage, ok := message.(int)
	if !ok {
		return
	}

	response := integerMessage + 1
	this.outbox <- response
}

func expectIntegerResponseToEq(t *testing.T, outbox chan int, expected int) {
	if actual := <-outbox; actual != expected {
		t.Errorf("Expected to receive '%d' but received '%d'", expected, actual)
	}
}

func TestWorksWithDifferentType(t *testing.T) {
	outbox := make(chan int)
	anActor := AnIntegerActor{
		Actor:  NewActor(),
		outbox: outbox,
	}
	Go(anActor, "Integer Actor")

	Send(anActor, 41)
	expectIntegerResponseToEq(t, outbox, 42)
}
<commit_msg>[#4] [RDD] Adjust specs<commit_after>package goactor

import (
	"fmt"
	"testing"
)

type AnActor struct {
	Actor
	outbox chan string
}

func (this *AnActor) Act(message Any) {
	response := fmt.Sprintf("Got '%s'", message)
	this.outbox <- response
}

func expectResponseToEq(t *testing.T, outbox chan string, expected string) {
	if actual := <-outbox; actual != expected {
		t.Errorf("Expected to receive '%s' but received '%s'", expected, actual)
	}
}

func TestHandlesInboxMessages(t *testing.T) {
	outbox := make(chan string)
	anActor := AnActor{
		Actor:  NewActor(),
		outbox: outbox,
	}
	Go(anActor, "String Actor")

	Send(anActor, "hello, world")
	expectResponseToEq(t, outbox, "Got 'hello, world'")

	Send(anActor, "hello, goworld")
	expectResponseToEq(t, outbox, "Got 'hello, goworld'")
}

func TestClosedInbox(t *testing.T) {
	anActor := AnActor{
		Actor: NewActor(),
	}
	Go(anActor, "String Actor")

	close(anActor.Inbox())
}

type AnIntegerActor struct {
	Actor
	outbox chan int
}

func (this *AnIntegerActor) Act(message Any) {
	integerMessage, ok := message.(int)
	if !ok {
		return
	}

	response := integerMessage + 1
	this.outbox <- response
}

func expectIntegerResponseToEq(t *testing.T, outbox chan int, expected int) {
	if actual := <-outbox; actual != expected {
		t.Errorf("Expected to receive '%d' but received '%d'", expected, actual)
	}
}

func TestWorksWithDifferentType(t *testing.T) {
	outbox := make(chan int)
	anActor := AnIntegerActor{
		Actor:  NewActor(),
		outbox: outbox,
	}
	Go(anActor, "Integer Actor")

	Send(anActor, 41)
	expectIntegerResponseToEq(t, outbox, 42)
}
<|endoftext|>


<commit_before>package gob

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"testing"
)

var _ = fmt.Printf

type Data struct {
	N int
	S []byte
	A []byte
}

func TestGobEncoding(t *testing.T) {
	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	enc.Encode(putData)

	getData := &Data{}
	dec := gob.NewDecoder(m)
	dec.Decode(&getData)

	if getData.N != putData.N ||
		bytes.Compare(getData.S, putData.S) != 0 ||
		bytes.Compare(getData.A, putData.A) != 0 {
		t.Fatal("Gob encoding failed!")
	}
}

func BenchmarkMars(b *testing.B) {
	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	for i := 0; i < b.N; i++ {
		enc.Encode(putData)
	}
}

func BenchmarkUnma(b *testing.B) {

	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	enc.Encode(putData)

	getData := &Data{}
	dec := gob.NewDecoder(m)
	for i := 0; i < b.N; i++ {
		dec.Decode(&getData)
	}
}
<commit_msg>fix stream decode in gob bench<commit_after>package gob

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"testing"
)

var _ = fmt.Printf

type Data struct {
	N int
	S []byte
	A []byte
}

func TestGobEncoding(t *testing.T) {
	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	enc.Encode(putData)

	getData := &Data{}
	dec := gob.NewDecoder(m)
	dec.Decode(&getData)

	if getData.N != putData.N ||
		bytes.Compare(getData.S, putData.S) != 0 ||
		bytes.Compare(getData.A, putData.A) != 0 {
		t.Fatal("Gob encoding failed!")
	}
}

func BenchmarkMars(b *testing.B) {
	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	for i := 0; i < b.N; i++ {
		enc.Encode(putData)
	}
}

func BenchmarkUnma(b *testing.B) {

	length := 100
	a := make([]byte, length, length)
	for i := 0; i < length; i++ {
		a[i] = byte(i)
	}

	putData := &Data{N: 1, S: []byte{'a', 'b', 'c'}, A: a}

	m := new(bytes.Buffer)
	enc := gob.NewEncoder(m)
	for i := 0; i < b.N; i++ {
		enc.Encode(putData)
	}

	b.ResetTimer()

	getData := &Data{}
	dec := gob.NewDecoder(m)
	for i := 0; i < b.N; i++ {
		dec.Decode(&getData)
	}
}
<|endoftext|>


<commit_before>package redlot

import "github.com/syndtr/goleveldb/leveldb/util"

// KeyRange will return key range.
func KeyRange() string {
	kr := "key_range.kv\n\t"
	iter := db.NewIterator(&util.Range{Start: []byte{0x6b, 0x00}}, nil)
	iter.Next()
	kr += "\"" + string(decodeKvKey(iter.Key())) + "\" - "

	iter.Last()
	iter.Prev()
	kr += "\"" + string(decodeKvKey(iter.Key())) + "\"\n"

	return kr
}
<commit_msg>Add hash key range.<commit_after>package redlot

import "github.com/syndtr/goleveldb/leveldb/util"

// KeyRange will return key range.
func KeyRange() string {
	kr := "key_range.kv\n\t"
	iter := db.NewIterator(&util.Range{Start: []byte{0x6b, 0x00}}, nil)
	iter.Next()
	kr += "\"" + string(decodeKvKey(iter.Key())) + "\" - "

	iter.Last()
	iter.Prev()
	kr += "\"" + string(decodeKvKey(iter.Key())) + "\"\n"

	kr += "hash_range.kv\n\t"
	iter = db.NewIterator(&util.Range{Start: []byte{0x48, 0x00}}, nil)
	iter.Next()
	kr += "\"" + string(decodeHsizeKey(iter.Key())) + "\" - "

	iter.Seek([]byte{0x48, 0xff})
	iter.Prev()
	kr += "\"" + string(decodeHsizeKey(iter.Key())) + "\"\n"

	return kr
}
<|endoftext|>