<commit_before>// trybot_updater is an application that updates a repo's buildbucket.config file.
package main

import (
	"bytes"
	"context"
	"flag"
	"sort"
	"strings"
	"text/template"
	"time"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/git"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
	"go.skia.org/infra/task_scheduler/go/specs"
)

const (
	// The format of this file is that of a gerrit extension config (not a proto).
	// The buildbucket extension parses the config like this:
	// https://chromium.googlesource.com/infra/gerrit-plugins/buildbucket/+/refs/heads/master/src/main/java/com/googlesource/chromium/plugins/buildbucket/GetConfig.java
	bbCfgFileName = "buildbucket.config"
	// Branch buildbucket.config lies in. Hopefully this will change one day, see b/38258213.
	bbCfgBranch = "refs/meta/config"
	// Template used to create buildbucket.config content.
	bbCfgTemplate = `
# This file is generated by trybot-updater from {{.TasksCfgFile}}. DO NOT EDIT.

{{range .EmptyBuckets}}[bucket "{{.}}"]{{end}}
[bucket "{{.BucketName}}"]
{{- range .Jobs}}
	builder = {{.}}
{{- end}}
`
)

var (
	// Flags.
	repoUrl       = flag.String("repo_url", common.REPO_SKIA, "Repo that needs buildbucket.config updated from it's tasks.json file.")
	bucketName    = flag.String("bucket_name", "luci.skia.skia.primary", "Name of the bucket to update in buildbucket.config.")
	emptyBuckets  = common.NewMultiStringFlag("empty_bucket", nil, "Empty buckets to specify in buildbucket.config. Eg: luci.chromium.try. See skbug.com/9639 for why these buckets are empty.")
	pollingPeriod = flag.Duration("polling_period", 10*time.Minute, "How often to poll tasks.json.")
	submit        = flag.Bool("submit", false, "If set, automatically submit the Gerrit change to update buildbucket.config")
	local         = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	promPort      = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")

	bbCfgTemplateParsed = template.Must(template.New("buildbucket_config").Parse(bbCfgTemplate))
)

// getBuildbucketCfgFromJobs reads tasks.json from the specified repository and returns
// contents of what the new buildbucket.config file should be.
func getBuildbucketCfgFromJobs(ctx context.Context, repo *gitiles.Repo) (string, error) {
	// Read tasks.json from the specified repository.
	tasksContents, err := repo.ReadFileAtRef(ctx, specs.TASKS_CFG_FILE, git.MasterBranch)
	if err != nil {
		return "", skerr.Fmt("Could not read %s: %s", specs.TASKS_CFG_FILE, err)
	}
	tasksCfg, err := specs.ParseTasksCfg(string(tasksContents))
	if err != nil {
		return "", skerr.Fmt("Could not parse %s: %s", specs.TASKS_CFG_FILE, err)
	}

	// Create a sorted slice of jobs.
	jobs := make([]string, 0, len(tasksCfg.Jobs))
	for j := range tasksCfg.Jobs {
		jobs = append(jobs, j)
	}
	sort.Strings(jobs)

	// Use jobs to create content of buildbucket.config.
	bbCfg := new(bytes.Buffer)
	if err := bbCfgTemplateParsed.Execute(bbCfg, struct {
		TasksCfgFile string
		EmptyBuckets []string
		BucketName   string
		Jobs         []string
	}{
		TasksCfgFile: specs.TASKS_CFG_FILE,
		EmptyBuckets: *emptyBuckets,
		BucketName:   *bucketName,
		Jobs:         jobs,
	}); err != nil {
		return "", skerr.Fmt("Failed to execute bbCfg template: %s", err)
	}
	return bbCfg.String(), nil
}

// getCurrentBuildbucketCfg returns the current contents of buildbucket.config for the
// specified repository.
func getCurrentBuildbucketCfg(ctx context.Context, repo *gitiles.Repo) (string, error) {
	contents, err := repo.ReadFileAtRef(ctx, bbCfgFileName, bbCfgBranch)
	if err != nil {
		return "", skerr.Fmt("Could not read %s: %s", bbCfgFileName, err)
	}
	return string(contents), nil
}

// updateBuildbucketCfg creates a Gerrit CL to update buildbucket.config. If submit flag is true then that CL
// is automatically self-approved and submitted.
func updateBuildbucketCfg(ctx context.Context, g *gerrit.Gerrit, repo *gitiles.Repo, cfgContents string) error {
	commitMsg := `Update buildbucket.config

Update is done by the trybot-updater bot.
Please contact the Skia Infra Gardener if this bot causes problems.
	`
	repoSplit := strings.Split(*repoUrl, "/")
	project := strings.TrimSuffix(repoSplit[len(repoSplit)-1], ".git")
	baseCommitInfo, err := repo.Details(ctx, bbCfgBranch)
	if err != nil {
		return skerr.Fmt("Could not get details of %s: %s", bbCfgBranch, err)
	}
	baseCommit := baseCommitInfo.Hash
	ci, err := gerrit.CreateAndEditChange(ctx, g, project, bbCfgBranch, commitMsg, baseCommit, func(ctx context.Context, g gerrit.GerritInterface, ci *gerrit.ChangeInfo) error {
		if err := g.EditFile(ctx, ci, bbCfgFileName, cfgContents); err != nil {
			return skerr.Fmt("Could not edit %s: %s", bbCfgFileName, err)
		}
		return nil
	})
	if err != nil {
		// If a change was created but had errors then abandon it.
		if ci != nil {
			if abandonErr := g.Abandon(ctx, ci, "Trybot updater CL creation had an error"); abandonErr != nil {
				sklog.Errorf("Error when abandoning change %s: %s", ci.Id, abandonErr)
			}
		}
		return skerr.Fmt("Could not create Gerrit change: %s", err)
	}
	sklog.Infof("Uploaded change https://skia-review.googlesource.com/c/%s/+/%d", project, ci.Issue)

	if *submit {
		// TODO(rmistry): Change reviewer to be the Infra Gardener after verifying that things work.
		reviewers := []string{"rmistry@google.com"}
		if err := g.SetReview(ctx, ci, "", gerrit.ConfigChromium.SelfApproveLabels, reviewers); err != nil {
			return abandonGerritChange(ctx, g, ci, err)
		}
		if err := g.Submit(ctx, ci); err != nil {
			return abandonGerritChange(ctx, g, ci, err)
		}
		sklog.Infof("Submitted change https://skia-review.googlesource.com/c/%s/+/%d", project, ci.Issue)
	}

	return nil
}

// abandonGerritChange abandons the specified CL and returns the specified err after abandoning.
// If abandoning fails then that error is wrapped with the specified err.
func abandonGerritChange(ctx context.Context, g *gerrit.Gerrit, issue *gerrit.ChangeInfo, err error) error {
	if abandonErr := g.Abandon(ctx, issue, ""); abandonErr != nil {
		return skerr.Wrapf(err, "failed to abandon CL with %s", abandonErr)
	}
	return err
}

func main() {
	common.InitWithMust("trybot_updater", common.PrometheusOpt(promPort), common.MetricsLoggingOpt())
	defer sklog.Flush()
	ctx := context.Background()

	// OAuth2.0 TokenSource.
	ts, err := auth.NewDefaultTokenSource(false, auth.SCOPE_USERINFO_EMAIL, auth.SCOPE_GERRIT)
	if err != nil {
		sklog.Fatal(err)
	}
	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Instantiate Gerrit.
	gUrl := strings.Split(*repoUrl, ".googlesource.com")[0] + "-review.googlesource.com"
	g, err := gerrit.NewGerrit(gUrl, httpClient)
	if err != nil {
		sklog.Fatal(err)
	}

	// Instantiate Gitiles using the specified repo URL.
	repo := gitiles.NewRepo(*repoUrl, httpClient)

	// TODO(rmistry): Use pubsub instead of polling.
	util.RepeatCtx(ctx, *pollingPeriod, func(ctx context.Context) {
		existingCfg, err := getCurrentBuildbucketCfg(ctx, repo)
		if err != nil {
			sklog.Errorf("Could not get contents of buildbucket.config from %s: %s", repo, err)
			return
		}

		newCfg, err := getBuildbucketCfgFromJobs(ctx, repo)
		if err != nil {
			sklog.Errorf("Could not get list of jobs from %s: %s", repo, err)
			return
		}

		// Only update buildbucket.config if the config is different.
		if newCfg != existingCfg {
			if err := updateBuildbucketCfg(ctx, g, repo, newCfg); err != nil {
				sklog.Errorf("Could not update buildbucket.config: %s", err)
				sklog.Info("Sleep for 10 mins since there was a error with Gerrit to give it time to recover.")
				time.Sleep(10 * time.Minute)
			}
		} else {
			sklog.Info("Config has not changed")
		}
	})
}
<commit_msg>[trybot-updater] Specify repoDefaultBranch<commit_after>// trybot_updater is an application that updates a repo's buildbucket.config file.
package main

import (
	"bytes"
	"context"
	"flag"
	"sort"
	"strings"
	"text/template"
	"time"

	"go.skia.org/infra/go/auth"
	"go.skia.org/infra/go/common"
	"go.skia.org/infra/go/gerrit"
	"go.skia.org/infra/go/git"
	"go.skia.org/infra/go/gitiles"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/skerr"
	"go.skia.org/infra/go/sklog"
	"go.skia.org/infra/go/util"
	"go.skia.org/infra/task_scheduler/go/specs"
)

const (
	// The format of this file is that of a gerrit extension config (not a proto).
	// The buildbucket extension parses the config like this:
	// https://chromium.googlesource.com/infra/gerrit-plugins/buildbucket/+/refs/heads/master/src/main/java/com/googlesource/chromium/plugins/buildbucket/GetConfig.java
	bbCfgFileName = "buildbucket.config"
	// Branch buildbucket.config lies in. Hopefully this will change one day, see b/38258213.
	bbCfgBranch = "refs/meta/config"
	// Template used to create buildbucket.config content.
	bbCfgTemplate = `
# This file is generated by trybot-updater from {{.TasksCfgFile}}. DO NOT EDIT.

{{range .EmptyBuckets}}[bucket "{{.}}"]{{end}}
[bucket "{{.BucketName}}"]
{{- range .Jobs}}
	builder = {{.}}
{{- end}}
`
)

var (
	// Flags.
	repoUrl           = flag.String("repo_url", common.REPO_SKIA, "Repo that needs buildbucket.config updated from it's tasks.json file.")
	repoDefaultBranch = flag.String("branch", git.MasterBranch, "The default branch of the specified repo.")
	bucketName        = flag.String("bucket_name", "luci.skia.skia.primary", "Name of the bucket to update in buildbucket.config.")
	emptyBuckets      = common.NewMultiStringFlag("empty_bucket", nil, "Empty buckets to specify in buildbucket.config. Eg: luci.chromium.try. See skbug.com/9639 for why these buckets are empty.")
	pollingPeriod     = flag.Duration("polling_period", 10*time.Minute, "How often to poll tasks.json.")
	submit            = flag.Bool("submit", false, "If set, automatically submit the Gerrit change to update buildbucket.config")
	local             = flag.Bool("local", false, "Running locally if true. As opposed to in production.")
	promPort          = flag.String("prom_port", ":20000", "Metrics service address (e.g., ':20000')")

	bbCfgTemplateParsed = template.Must(template.New("buildbucket_config").Parse(bbCfgTemplate))
)

// getBuildbucketCfgFromJobs reads tasks.json from the specified repository and returns
// contents of what the new buildbucket.config file should be.
func getBuildbucketCfgFromJobs(ctx context.Context, repo *gitiles.Repo) (string, error) {
	// Read tasks.json from the specified repository.
	tasksContents, err := repo.ReadFileAtRef(ctx, specs.TASKS_CFG_FILE, *repoDefaultBranch)
	if err != nil {
		return "", skerr.Fmt("Could not read %s: %s", specs.TASKS_CFG_FILE, err)
	}
	tasksCfg, err := specs.ParseTasksCfg(string(tasksContents))
	if err != nil {
		return "", skerr.Fmt("Could not parse %s: %s", specs.TASKS_CFG_FILE, err)
	}

	// Create a sorted slice of jobs.
	jobs := make([]string, 0, len(tasksCfg.Jobs))
	for j := range tasksCfg.Jobs {
		jobs = append(jobs, j)
	}
	sort.Strings(jobs)

	// Use jobs to create content of buildbucket.config.
	bbCfg := new(bytes.Buffer)
	if err := bbCfgTemplateParsed.Execute(bbCfg, struct {
		TasksCfgFile string
		EmptyBuckets []string
		BucketName   string
		Jobs         []string
	}{
		TasksCfgFile: specs.TASKS_CFG_FILE,
		EmptyBuckets: *emptyBuckets,
		BucketName:   *bucketName,
		Jobs:         jobs,
	}); err != nil {
		return "", skerr.Fmt("Failed to execute bbCfg template: %s", err)
	}
	return bbCfg.String(), nil
}

// getCurrentBuildbucketCfg returns the current contents of buildbucket.config for the
// specified repository.
func getCurrentBuildbucketCfg(ctx context.Context, repo *gitiles.Repo) (string, error) {
	contents, err := repo.ReadFileAtRef(ctx, bbCfgFileName, bbCfgBranch)
	if err != nil {
		return "", skerr.Fmt("Could not read %s: %s", bbCfgFileName, err)
	}
	return string(contents), nil
}

// updateBuildbucketCfg creates a Gerrit CL to update buildbucket.config. If submit flag is true then that CL
// is automatically self-approved and submitted.
func updateBuildbucketCfg(ctx context.Context, g *gerrit.Gerrit, repo *gitiles.Repo, cfgContents string) error {
	commitMsg := `Update buildbucket.config

Update is done by the trybot-updater bot.
Please contact the Skia Infra Gardener if this bot causes problems.
	`
	repoSplit := strings.Split(*repoUrl, "/")
	project := strings.TrimSuffix(repoSplit[len(repoSplit)-1], ".git")
	baseCommitInfo, err := repo.Details(ctx, bbCfgBranch)
	if err != nil {
		return skerr.Fmt("Could not get details of %s: %s", bbCfgBranch, err)
	}
	baseCommit := baseCommitInfo.Hash
	ci, err := gerrit.CreateAndEditChange(ctx, g, project, bbCfgBranch, commitMsg, baseCommit, func(ctx context.Context, g gerrit.GerritInterface, ci *gerrit.ChangeInfo) error {
		if err := g.EditFile(ctx, ci, bbCfgFileName, cfgContents); err != nil {
			return skerr.Fmt("Could not edit %s: %s", bbCfgFileName, err)
		}
		return nil
	})
	if err != nil {
		// If a change was created but had errors then abandon it.
		if ci != nil {
			if abandonErr := g.Abandon(ctx, ci, "Trybot updater CL creation had an error"); abandonErr != nil {
				sklog.Errorf("Error when abandoning change %s: %s", ci.Id, abandonErr)
			}
		}
		return skerr.Fmt("Could not create Gerrit change: %s", err)
	}
	sklog.Infof("Uploaded change https://skia-review.googlesource.com/c/%s/+/%d", project, ci.Issue)

	if *submit {
		// TODO(rmistry): Change reviewer to be the Infra Gardener after verifying that things work.
		reviewers := []string{"rmistry@google.com"}
		if err := g.SetReview(ctx, ci, "", gerrit.ConfigChromium.SelfApproveLabels, reviewers); err != nil {
			return abandonGerritChange(ctx, g, ci, err)
		}
		if err := g.Submit(ctx, ci); err != nil {
			return abandonGerritChange(ctx, g, ci, err)
		}
		sklog.Infof("Submitted change https://skia-review.googlesource.com/c/%s/+/%d", project, ci.Issue)
	}

	return nil
}

// abandonGerritChange abandons the specified CL and returns the specified err after abandoning.
// If abandoning fails then that error is wrapped with the specified err.
func abandonGerritChange(ctx context.Context, g *gerrit.Gerrit, issue *gerrit.ChangeInfo, err error) error {
	if abandonErr := g.Abandon(ctx, issue, ""); abandonErr != nil {
		return skerr.Wrapf(err, "failed to abandon CL with %s", abandonErr)
	}
	return err
}

func main() {
	common.InitWithMust("trybot_updater", common.PrometheusOpt(promPort), common.MetricsLoggingOpt())
	defer sklog.Flush()
	ctx := context.Background()

	// OAuth2.0 TokenSource.
	ts, err := auth.NewDefaultTokenSource(false, auth.SCOPE_USERINFO_EMAIL, auth.SCOPE_GERRIT)
	if err != nil {
		sklog.Fatal(err)
	}
	// Authenticated HTTP client.
	httpClient := httputils.DefaultClientConfig().WithTokenSource(ts).With2xxOnly().Client()

	// Instantiate Gerrit.
	gUrl := strings.Split(*repoUrl, ".googlesource.com")[0] + "-review.googlesource.com"
	g, err := gerrit.NewGerrit(gUrl, httpClient)
	if err != nil {
		sklog.Fatal(err)
	}

	// Instantiate Gitiles using the specified repo URL.
	repo := gitiles.NewRepo(*repoUrl, httpClient)

	// TODO(rmistry): Use pubsub instead of polling.
	util.RepeatCtx(ctx, *pollingPeriod, func(ctx context.Context) {
		existingCfg, err := getCurrentBuildbucketCfg(ctx, repo)
		if err != nil {
			sklog.Errorf("Could not get contents of buildbucket.config from %s: %s", repo, err)
			return
		}

		newCfg, err := getBuildbucketCfgFromJobs(ctx, repo)
		if err != nil {
			sklog.Errorf("Could not get list of jobs from %s: %s", repo, err)
			return
		}

		// Only update buildbucket.config if the config is different.
		if newCfg != existingCfg {
			if err := updateBuildbucketCfg(ctx, g, repo, newCfg); err != nil {
				sklog.Errorf("Could not update buildbucket.config: %s", err)
				sklog.Info("Sleep for 10 mins since there was a error with Gerrit to give it time to recover.")
				time.Sleep(10 * time.Minute)
			}
		} else {
			sklog.Info("Config has not changed")
		}
	})
}
<|endoftext|>


<commit_before>package chap1

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
)

func fetchall(urls []string) {
	for _, url := range urls {
		resp, err := http.Get(url)
		if err != nil {
			fmt.Fprintf(os.Stdout, "fetch: %v\n", err)
			return
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
			return
		}
		fmt.Printf("%s", b)
	}
}
<commit_msg>fetchall<commit_after>package chap1

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"time"
)

func fetchall(urls []string) {
	start := time.Now()
	ch := make(chan string)

	for _, url := range urls {
	    println(url)
		go fetchOne(url, ch) // start a goroutine
	}

	for range urls {
		fmt.Println(<-ch)
	}
	fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
}

func fetchOne(url string, ch chan<- string) {
	start := time.Now()
	resp, err := http.Get(url)
	if err != nil {
		ch <- fmt.Sprint(err) // send to channel
		return
	}

	nbytes, err := io.Copy(ioutil.Discard, resp.Body) // dev/null
	resp.Body.Close()
	if err != nil {
		ch <- fmt.Sprintf("while reading %s: %v", url, err)
		return
	}
	secs := time.Since(start).Seconds()
	ch <- fmt.Sprintf("%.2fs  %7d  %s", secs, nbytes, url)
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"strconv"
	"strings"
	"text/template"
)

type pieTemplateData struct {
	Labels          string
	DisplayTitle    bool
	Title           string
	ChartType       string
	Data            string
	Colors          string
	TooltipTemplate string
}

var pieTemplate *template.Template

func init() {
	pieTemplateString := `{
    type: '{{ .ChartType }}',
    data: {
		labels: [{{ .Labels }}],
        datasets: [{
            data: [{{ .Data }}],
            backgroundColor: [{{ .Colors }}]
        }]
    },
    options: {
        title: {
            display: {{ .DisplayTitle }},
            text: '{{ .Title }}'
        },
        tooltips: {
            callbacks: {
                label: function(tti, data) {
                    var value = data.datasets[0].data[tti.index];
                    var total = data.datasets[0].data.reduce((a, b) => a + b, 0)
                    var label = data.labels[tti.index];
                    var percentage = Math.round(value / total * 100);
                    return {{ .TooltipTemplate }};
                }
            }
        }
    }
}`

	var err error
	pieTemplate, err = template.New("pie").Parse(pieTemplateString)
	if err != nil {
		log.Fatal(err)
	}
}

func setupPie(fss [][]float64, sss [][]string, title string, displayTitle bool) (string, error) {

	fmt.Println(fss, sss)

	var ds []string
	for _, fs := range fss {
		if len(fs) == 0 {
			return "", fmt.Errorf("Couldn't find values to plot.") //TODO this probably shouldn't happen
		}
		ds = append(ds, strconv.FormatFloat(fs[0], 'f', -1, 64))
	}

	var ls []string

	noLabels := len(sss) == 0
	for _, ss := range sss {
		if len(ss) == 0 {
			noLabels = true //TODO this probably shouldn't happen
			break
		}
		ls = append(ls, `"`+ss[0]+`"`)
	}

	stringData := strings.Join(ds, ",")
	stringLabels := strings.Join(ls, ",")

	var tooltipTemplate string
	if noLabels {
		tooltipTemplate = `percentage + '%'`
	} else {
		tooltipTemplate = `label + ': ' + percentage + '%'`
	}

	templateData := pieTemplateData{
		ChartType:       "pie",
		Data:            stringData,
		Labels:          stringLabels,
		Title:           title,
		DisplayTitle:    displayTitle,
		Colors:          colorFirstN(len(stringData)),
		TooltipTemplate: tooltipTemplate,
	}

	var b bytes.Buffer
	if err := pieTemplate.Execute(&b, templateData); err != nil {
		log.Fatal(err)
	}

	return b.String(), nil
}
<commit_msg>Removes debug line.<commit_after>package main

import (
	"bytes"
	"fmt"
	"log"
	"strconv"
	"strings"
	"text/template"
)

type pieTemplateData struct {
	Labels          string
	DisplayTitle    bool
	Title           string
	ChartType       string
	Data            string
	Colors          string
	TooltipTemplate string
}

var pieTemplate *template.Template

func init() {
	pieTemplateString := `{
    type: '{{ .ChartType }}',
    data: {
		labels: [{{ .Labels }}],
        datasets: [{
            data: [{{ .Data }}],
            backgroundColor: [{{ .Colors }}]
        }]
    },
    options: {
        title: {
            display: {{ .DisplayTitle }},
            text: '{{ .Title }}'
        },
        tooltips: {
            callbacks: {
                label: function(tti, data) {
                    var value = data.datasets[0].data[tti.index];
                    var total = data.datasets[0].data.reduce((a, b) => a + b, 0)
                    var label = data.labels[tti.index];
                    var percentage = Math.round(value / total * 100);
                    return {{ .TooltipTemplate }};
                }
            }
        }
    }
}`

	var err error
	pieTemplate, err = template.New("pie").Parse(pieTemplateString)
	if err != nil {
		log.Fatal(err)
	}
}

func setupPie(fss [][]float64, sss [][]string, title string, displayTitle bool) (string, error) {

	var ds []string
	for _, fs := range fss {
		if len(fs) == 0 {
			return "", fmt.Errorf("Couldn't find values to plot.") //TODO this probably shouldn't happen
		}
		ds = append(ds, strconv.FormatFloat(fs[0], 'f', -1, 64))
	}

	var ls []string

	noLabels := len(sss) == 0
	for _, ss := range sss {
		if len(ss) == 0 {
			noLabels = true //TODO this probably shouldn't happen
			break
		}
		ls = append(ls, `"`+ss[0]+`"`)
	}

	stringData := strings.Join(ds, ",")
	stringLabels := strings.Join(ls, ",")

	var tooltipTemplate string
	if noLabels {
		tooltipTemplate = `percentage + '%'`
	} else {
		tooltipTemplate = `label + ': ' + percentage + '%'`
	}

	templateData := pieTemplateData{
		ChartType:       "pie",
		Data:            stringData,
		Labels:          stringLabels,
		Title:           title,
		DisplayTitle:    displayTitle,
		Colors:          colorFirstN(len(stringData)),
		TooltipTemplate: tooltipTemplate,
	}

	var b bytes.Buffer
	if err := pieTemplate.Execute(&b, templateData); err != nil {
		log.Fatal(err)
	}

	return b.String(), nil
}
<|endoftext|>


<commit_before>package charts

import (
	"github.com/go-echarts/go-echarts/opts"
	"github.com/go-echarts/go-echarts/types"
)

// Chart3D is a chart in 3D coordinate.
type Chart3D struct {
	BaseConfiguration
	MultiSeries

	XAxis3D opts.XAxis3D
	YAxis3D opts.YAxis3D
	ZAxis3D opts.ZAxis3D
	Grid3D  opts.Grid3D

	xData interface{}
	yData interface{}
}

func WithXAxis3DOpts(opt opts.XAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.XAxis3D = opt
	}
}

func WithYAxis3DOpts(opt opts.YAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.YAxis3D = opt
	}
}

func WithZAxis3DOpts(opt opts.ZAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.ZAxis3D = opt
	}
}

func WithGrid3DOpts(opt opts.Grid3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.Grid3D = opt
	}
}

func (c *Chart3D) initChart3D() {
	c.JSAssets.Add("echarts-gl.min.js")
	c.Has3DAxis = true
}

// SetGlobalOptions sets options for the Chart3D instance.
func (c *Chart3D) SetGlobalOptions(opts ...GlobalOpts) *Chart3D {
	c.BaseConfiguration.setBaseGlobalOptions(opts...)
	return c
}

func (c *Chart3D) Validate() {
	// 确保 XY 轴数据项不会被抹除
	if c.XAxis3D.Data == nil {
		c.XAxis3D.Data = c.xData
	}
	if c.YAxis3D.Data == nil {
		c.YAxis3D.Data = c.yData
	}
	c.Assets.Validate(c.AssetsHost)
}

func (c *Chart3D) addZAxis(chartType, name string, zAxis interface{}, opts ...SeriesOpts) {
	series := SingleSeries{
		Name:        name,
		Type:        chartType,
		Data:        zAxis,
		CoordSystem: types.ChartCartesian3D,
	}
	series.configureSeriesOpts(opts...)
	c.MultiSeries = append(c.MultiSeries, series)
}
<commit_msg>remove unused property. (#93)<commit_after>package charts

import (
	"github.com/go-echarts/go-echarts/opts"
	"github.com/go-echarts/go-echarts/types"
)

// Chart3D is a chart in 3D coordinate.
type Chart3D struct {
	BaseConfiguration
	MultiSeries

	XAxis3D opts.XAxis3D
	YAxis3D opts.YAxis3D
	ZAxis3D opts.ZAxis3D

	xData interface{}
	yData interface{}
}

func WithXAxis3DOpts(opt opts.XAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.XAxis3D = opt
	}
}

func WithYAxis3DOpts(opt opts.YAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.YAxis3D = opt
	}
}

func WithZAxis3DOpts(opt opts.ZAxis3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.ZAxis3D = opt
	}
}

func WithGrid3DOpts(opt opts.Grid3D) GlobalOpts {
	return func(bc *BaseConfiguration) {
		bc.Grid3D = opt
	}
}

func (c *Chart3D) initChart3D() {
	c.JSAssets.Add("echarts-gl.min.js")
	c.Has3DAxis = true
}

// SetGlobalOptions sets options for the Chart3D instance.
func (c *Chart3D) SetGlobalOptions(opts ...GlobalOpts) *Chart3D {
	c.BaseConfiguration.setBaseGlobalOptions(opts...)
	return c
}

func (c *Chart3D) Validate() {
	// 确保 XY 轴数据项不会被抹除
	if c.XAxis3D.Data == nil {
		c.XAxis3D.Data = c.xData
	}
	if c.YAxis3D.Data == nil {
		c.YAxis3D.Data = c.yData
	}
	c.Assets.Validate(c.AssetsHost)
}

func (c *Chart3D) addZAxis(chartType, name string, zAxis interface{}, opts ...SeriesOpts) {
	series := SingleSeries{
		Name:        name,
		Type:        chartType,
		Data:        zAxis,
		CoordSystem: types.ChartCartesian3D,
	}
	series.configureSeriesOpts(opts...)
	c.MultiSeries = append(c.MultiSeries, series)
}
<|endoftext|>


<commit_before>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(volConfig map[string]string, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, VolName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<commit_msg>lxd/storage/drivers/interface: Modifies ValidateVolume definition<commit_after>package drivers

import (
	"io"

	"github.com/lxc/lxd/lxd/migration"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/api"
)

// driver is the extended internal interface.
type driver interface {
	Driver

	init(state *state.State, name string, config map[string]string, volIDFunc func(volType VolumeType, volName string) (int64, error), commonRulesFunc func() map[string]func(string) error)
}

// Driver represents a low-level storage driver.
type Driver interface {
	// Internal.
	Info() Info
	HasVolume(volType VolumeType, volName string) bool

	// Pool.
	Create() error
	Delete(op *operations.Operation) error
	Mount() (bool, error)
	Unmount() (bool, error)
	GetResources() (*api.ResourcesStoragePool, error)

	// Volumes.
	ValidateVolume(vol Volume, removeUnknownKeys bool) error
	CreateVolume(vol Volume, filler func(path string) error, op *operations.Operation) error
	CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots bool, op *operations.Operation) error
	DeleteVolume(volType VolumeType, volName string, op *operations.Operation) error
	RenameVolume(volType VolumeType, volName string, newName string, op *operations.Operation) error

	// MountVolume mounts a storage volume, returns true if we caused a new mount, false if
	// already mounted.
	MountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// MountVolumeSnapshot mounts a storage volume snapshot as readonly, returns true if we
	// caused a new mount, false if already mounted.
	MountVolumeSnapshot(volType VolumeType, VolName, snapshotName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume, returns true if unmounted, false if was not
	// mounted.
	UnmountVolume(volType VolumeType, volName string, op *operations.Operation) (bool, error)

	// UnmountVolume unmounts a storage volume snapshot, returns true if unmounted, false if was
	// not mounted.
	UnmountVolumeSnapshot(VolumeType VolumeType, volName, snapshotName string, op *operations.Operation) (bool, error)

	CreateVolumeSnapshot(volType VolumeType, volName string, newSnapshotName string, op *operations.Operation) error
	DeleteVolumeSnapshot(volType VolumeType, volName string, snapshotName string, op *operations.Operation) error
	RenameVolumeSnapshot(volType VolumeType, volName string, snapshotName string, newSnapshotName string, op *operations.Operation) error
	VolumeSnapshots(volType VolumeType, volName string, op *operations.Operation) ([]string, error)

	// Migration.
	MigrationTypes(contentType ContentType) []migration.Type
	MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs migration.VolumeSourceArgs, op *operations.Operation) error
	CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, volTargetArgs migration.VolumeTargetArgs, op *operations.Operation) error
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
)

func storagePoolUpdate(state *state.State, name, newDescription string, newConfig map[string]string, withDB bool) error {
	pool, err := storagePools.GetPoolByName(state, name)
	if err != nil {
		return err
	}

	return pool.Update(!withDB, newDescription, newConfig, nil)
}

// storagePoolDBCreate creates a storage pool DB entry and returns the created Pool ID.
func storagePoolDBCreate(s *state.State, poolName, poolDescription string, driver string, config map[string]string) (int64, error) {
	// Check that the storage pool does not already exist.
	_, err := s.Cluster.GetStoragePoolID(poolName)
	if err == nil {
		return -1, fmt.Errorf("The storage pool already exists")
	}

	// Make sure that we don't pass a nil to the next function.
	if config == nil {
		config = map[string]string{}
	}
	err = storagePoolValidate(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Fill in the defaults.
	err = storagePoolFillDefault(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Create the database entry for the storage pool.
	id, err := dbStoragePoolCreateAndUpdateCache(s, poolName, poolDescription, driver, config)
	if err != nil {
		return -1, fmt.Errorf("Error inserting %s into database: %s", poolName, err)
	}

	return id, nil
}

func storagePoolValidate(poolName string, driverName string, config map[string]string) error {
	// Check if the storage pool name is valid.
	err := storagePools.ValidName(poolName)
	if err != nil {
		return err
	}

	// Validate the requested storage pool configuration.
	err = storagePoolValidateConfig(poolName, driverName, config, nil)
	if err != nil {
		return err
	}

	return nil
}

func storagePoolCreateGlobal(state *state.State, req api.StoragePoolsPost) error {
	// Create the database entry.
	id, err := storagePoolDBCreate(state, req.Name, req.Description, req.Driver, req.Config)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	tryUndo := true
	defer func() {
		if !tryUndo {
			return
		}

		dbStoragePoolDeleteAndUpdateCache(state, req.Name)
	}()

	_, err = storagePoolCreateLocal(state, id, req, false)
	if err != nil {
		return err
	}

	tryUndo = false
	return nil
}

// This performs local pool setup and updates DB record if config was changed during pool setup.
// Returns resulting config.
func storagePoolCreateLocal(state *state.State, id int64, req api.StoragePoolsPost, isNotification bool) (map[string]string, error) {
	// Setup revert.
	revert := revert.New()
	defer revert.Fail()

	// Make a copy of the req for later diff.
	var updatedReq api.StoragePoolsPost
	shared.DeepCopy(&req, &updatedReq)

	// Fill in the node specific defaults.
	err := storagePoolFillDefault(updatedReq.Name, updatedReq.Driver, updatedReq.Config)
	if err != nil {
		return nil, err
	}

	configDiff, _ := storagePools.ConfigDiff(req.Config, updatedReq.Config)
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, updatedReq.Config)
		if err != nil {
			return nil, errors.Wrapf(err, "Error updating storage pool config after local fill defaults for %q", req.Name)
		}
	}

	// Load pool record.
	pool, err := storagePools.GetPoolByName(state, updatedReq.Name)
	if err != nil {
		return nil, err
	}

	if pool.LocalStatus() == api.NetworkStatusCreated {
		logger.Debug("Skipping storage pool create as already created locally", log.Ctx{"pool": pool.Name()})

		return pool.Driver().Config(), nil
	}

	// Create the pool.
	err = pool.Create(isNotification, nil)
	if err != nil {
		return nil, err
	}

	revert.Add(func() { pool.Delete(isNotification, nil) })

	// Mount the pool.
	_, err = pool.Mount()
	if err != nil {
		return nil, err
	}

	// In case the storage pool config was changed during the pool creation, we need to update the database to
	// reflect this change. This can e.g. happen, when we create a loop file image. This means we append ".img"
	// to the path the user gave us and update the config in the storage callback. So diff the config here to
	// see if something like this has happened.
	configDiff, _ = storagePools.ConfigDiff(updatedReq.Config, pool.Driver().Config())
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, pool.Driver().Config())
		if err != nil {
			return nil, errors.Wrapf(err, "Error updating storage pool config after local create for %q", req.Name)
		}
	}

	// Set storage pool node to storagePoolCreated.
	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.StoragePoolNodeCreated(id)
	})
	if err != nil {
		return nil, err
	}
	logger.Debug("Marked storage pool local status as created", log.Ctx{"pool": req.Name})

	revert.Success()
	return pool.Driver().Config(), nil
}

// Helper around the low-level DB API, which also updates the driver names cache.
func dbStoragePoolCreateAndUpdateCache(s *state.State, poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
	id, err := s.Cluster.CreateStoragePool(poolName, poolDescription, poolDriver, poolConfig)
	if err != nil {
		return id, err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return id, nil
}

// Helper around the low-level DB API, which also updates the driver names
// cache.
func dbStoragePoolDeleteAndUpdateCache(s *state.State, poolName string) error {
	_, err := s.Cluster.RemoveStoragePool(poolName)
	if err != nil {
		return err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return err
}
<commit_msg>lxd/storage/pools/utils: Removes unused storagePoolUpdate<commit_after>package main

import (
	"fmt"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
)

// storagePoolDBCreate creates a storage pool DB entry and returns the created Pool ID.
func storagePoolDBCreate(s *state.State, poolName, poolDescription string, driver string, config map[string]string) (int64, error) {
	// Check that the storage pool does not already exist.
	_, err := s.Cluster.GetStoragePoolID(poolName)
	if err == nil {
		return -1, fmt.Errorf("The storage pool already exists")
	}

	// Make sure that we don't pass a nil to the next function.
	if config == nil {
		config = map[string]string{}
	}
	err = storagePoolValidate(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Fill in the defaults.
	err = storagePoolFillDefault(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Create the database entry for the storage pool.
	id, err := dbStoragePoolCreateAndUpdateCache(s, poolName, poolDescription, driver, config)
	if err != nil {
		return -1, fmt.Errorf("Error inserting %s into database: %s", poolName, err)
	}

	return id, nil
}

func storagePoolValidate(poolName string, driverName string, config map[string]string) error {
	// Check if the storage pool name is valid.
	err := storagePools.ValidName(poolName)
	if err != nil {
		return err
	}

	// Validate the requested storage pool configuration.
	err = storagePoolValidateConfig(poolName, driverName, config, nil)
	if err != nil {
		return err
	}

	return nil
}

func storagePoolCreateGlobal(state *state.State, req api.StoragePoolsPost) error {
	// Create the database entry.
	id, err := storagePoolDBCreate(state, req.Name, req.Description, req.Driver, req.Config)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	tryUndo := true
	defer func() {
		if !tryUndo {
			return
		}

		dbStoragePoolDeleteAndUpdateCache(state, req.Name)
	}()

	_, err = storagePoolCreateLocal(state, id, req, false)
	if err != nil {
		return err
	}

	tryUndo = false
	return nil
}

// This performs local pool setup and updates DB record if config was changed during pool setup.
// Returns resulting config.
func storagePoolCreateLocal(state *state.State, id int64, req api.StoragePoolsPost, isNotification bool) (map[string]string, error) {
	// Setup revert.
	revert := revert.New()
	defer revert.Fail()

	// Make a copy of the req for later diff.
	var updatedReq api.StoragePoolsPost
	shared.DeepCopy(&req, &updatedReq)

	// Fill in the node specific defaults.
	err := storagePoolFillDefault(updatedReq.Name, updatedReq.Driver, updatedReq.Config)
	if err != nil {
		return nil, err
	}

	configDiff, _ := storagePools.ConfigDiff(req.Config, updatedReq.Config)
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, updatedReq.Config)
		if err != nil {
			return nil, errors.Wrapf(err, "Error updating storage pool config after local fill defaults for %q", req.Name)
		}
	}

	// Load pool record.
	pool, err := storagePools.GetPoolByName(state, updatedReq.Name)
	if err != nil {
		return nil, err
	}

	if pool.LocalStatus() == api.NetworkStatusCreated {
		logger.Debug("Skipping storage pool create as already created locally", log.Ctx{"pool": pool.Name()})

		return pool.Driver().Config(), nil
	}

	// Create the pool.
	err = pool.Create(isNotification, nil)
	if err != nil {
		return nil, err
	}

	revert.Add(func() { pool.Delete(isNotification, nil) })

	// Mount the pool.
	_, err = pool.Mount()
	if err != nil {
		return nil, err
	}

	// In case the storage pool config was changed during the pool creation, we need to update the database to
	// reflect this change. This can e.g. happen, when we create a loop file image. This means we append ".img"
	// to the path the user gave us and update the config in the storage callback. So diff the config here to
	// see if something like this has happened.
	configDiff, _ = storagePools.ConfigDiff(updatedReq.Config, pool.Driver().Config())
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, pool.Driver().Config())
		if err != nil {
			return nil, errors.Wrapf(err, "Error updating storage pool config after local create for %q", req.Name)
		}
	}

	// Set storage pool node to storagePoolCreated.
	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.StoragePoolNodeCreated(id)
	})
	if err != nil {
		return nil, err
	}
	logger.Debug("Marked storage pool local status as created", log.Ctx{"pool": req.Name})

	revert.Success()
	return pool.Driver().Config(), nil
}

// Helper around the low-level DB API, which also updates the driver names cache.
func dbStoragePoolCreateAndUpdateCache(s *state.State, poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
	id, err := s.Cluster.CreateStoragePool(poolName, poolDescription, poolDriver, poolConfig)
	if err != nil {
		return id, err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return id, nil
}

// Helper around the low-level DB API, which also updates the driver names
// cache.
func dbStoragePoolDeleteAndUpdateCache(s *state.State, poolName string) error {
	_, err := s.Cluster.RemoveStoragePool(poolName)
	if err != nil {
		return err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return err
}
<|endoftext|>


<commit_before>package hec

import (
	"io"
	"net/http"
)

type HEC interface {
	SetHTTPClient(client *http.Client)
	SetKeepAlive(enable bool)
	SetChannel(channel string)
	SetMaxRetry(retries int)
	SetMaxContentLength(size int)

	// WriteEvent writes single event via HEC json mode
	WriteEvent(event *Event) error

	// WriteBatch writes multiple events via HCE batch mode
	WriteBatch(events []*Event) error

	// WriteRaw writes raw data stream via HEC raw mode
	WriteRaw(reader io.ReadSeeker, metadata *EventMetadata) error
}
<commit_msg>Add WithContext methods to the HEC interface<commit_after>package hec

import (
	"context"
	"io"
	"net/http"
)

type HEC interface {
	SetHTTPClient(client *http.Client)
	SetKeepAlive(enable bool)
	SetChannel(channel string)
	SetMaxRetry(retries int)
	SetMaxContentLength(size int)

	// WriteEvent writes single event via HEC json mode
	WriteEvent(event *Event) error

	// WriteBatch writes multiple events via HCE batch mode
	WriteBatch(events []*Event) error

	// WriteBatchWithContext writes multiple events via HEC batch mode with a context for cancellation
	WriteBatchWithContext(ctx context.Context, events []*Event) error

	// WriteRaw writes raw data stream via HEC raw mode
	WriteRaw(reader io.ReadSeeker, metadata *EventMetadata) error

	// WriteRawWithContext writes raw data stream via HEC raw mode with a context for cancellation
	WriteRawWithContext(ctx context.Context, reader io.ReadSeeker, metadata *EventMetadata) error
}
<|endoftext|>


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

package vppcalls

import (
	"fmt"
	"time"

	"github.com/ligato/vpp-agent/plugins/vpp/binapi/nat"
)

// Num protocol representation
const (
	ICMP uint8 = 1
	TCP  uint8 = 6
	UDP  uint8 = 17
)

// NoInterface is sw-if-idx which means 'no interface'
const NoInterface uint32 = 0xffffffff

// StaticMappingContext groups common fields required for static mapping
type StaticMappingContext struct {
	Tag           string
	AddressOnly   bool
	LocalIP       []byte
	LocalPort     uint16
	ExternalIP    []byte
	ExternalPort  uint16
	ExternalIfIdx uint32
	Protocol      uint8
	Vrf           uint32
	TwiceNat      bool
	SelfTwiceNat  bool
}

// StaticMappingLbContext groups common fields required for static mapping with load balancer
type StaticMappingLbContext struct {
	Tag          string
	LocalIPs     []*LocalLbAddress
	ExternalIP   []byte
	ExternalPort uint16
	Protocol     uint8
	TwiceNat     bool
	SelfTwiceNat bool
}

// IdentityMappingContext groups common fields required for identity mapping
type IdentityMappingContext struct {
	Tag       string
	IPAddress []byte
	Protocol  uint8
	Port      uint16
	IfIdx     uint32
	Vrf       uint32
}

// LocalLbAddress represents one local IP and address entry
type LocalLbAddress struct {
	Vrf         uint32
	Tag         string
	LocalIP     []byte
	LocalPort   uint16
	Probability uint8
}

func (handler *natVppHandler) SetNat44Forwarding(enableFwd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44ForwardingEnableDisable{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44ForwardingEnableDisable{
		Enable: boolToUint(enableFwd),
	}

	reply := &nat.Nat44ForwardingEnableDisableReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to set/unset interface as NAT
func (handler *natVppHandler) handleNat44Interface(ifIdx uint32, isInside, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44InterfaceAddDelFeature{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44InterfaceAddDelFeature{
		SwIfIndex: ifIdx,
		IsInside:  boolToUint(isInside),
		IsAdd:     boolToUint(isAdd),
	}

	reply := &nat.Nat44InterfaceAddDelFeatureReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to set/unset interface as NAT with output feature
func (handler *natVppHandler) handleNat44InterfaceOutputFeature(ifIdx uint32, isInside, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44InterfaceAddDelOutputFeature{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44InterfaceAddDelOutputFeature{
		SwIfIndex: ifIdx,
		IsInside:  boolToUint(isInside),
		IsAdd:     boolToUint(isAdd),
	}

	reply := &nat.Nat44InterfaceAddDelOutputFeatureReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove address pool
func (handler *natVppHandler) handleNat44AddressPool(first, last []byte, vrf uint32, twiceNat, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelAddressRange{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelAddressRange{
		FirstIPAddress: first,
		LastIPAddress:  last,
		VrfID:          vrf,
		TwiceNat:       boolToUint(twiceNat),
		IsAdd:          boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelAddressRangeReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove static mapping
func (handler *natVppHandler) handleNat44StaticMapping(ctx *StaticMappingContext, isAdd, addrOnly bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelStaticMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelStaticMapping{
		Tag:               []byte(ctx.Tag),
		LocalIPAddress:    ctx.LocalIP,
		LocalPort:         ctx.LocalPort,
		ExternalIPAddress: ctx.ExternalIP,
		ExternalPort:      ctx.ExternalPort,
		Protocol:          ctx.Protocol,
		ExternalSwIfIndex: ctx.ExternalIfIdx,
		VrfID:             ctx.Vrf,
		TwiceNat:          boolToUint(ctx.TwiceNat),
		SelfTwiceNat:      boolToUint(ctx.SelfTwiceNat),
		Out2inOnly:        1,
		IsAdd:             boolToUint(isAdd),
	}
	if addrOnly {
		req.AddrOnly = 1
	} else {
		req.LocalPort = ctx.LocalPort
		req.ExternalPort = ctx.ExternalPort
	}

	reply := &nat.Nat44AddDelStaticMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove static mapping with load balancer
func (handler *natVppHandler) handleNat44StaticMappingLb(ctx *StaticMappingLbContext, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelLbStaticMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	// Transform local IP/Ports
	var localAddrPorts []nat.Nat44LbAddrPort
	for _, ctxLocal := range ctx.LocalIPs {
		localAddrPort := nat.Nat44LbAddrPort{
			Addr:        ctxLocal.LocalIP,
			Port:        ctxLocal.LocalPort,
			Probability: ctxLocal.Probability,
			VrfID:       ctxLocal.Vrf,
		}
		localAddrPorts = append(localAddrPorts, localAddrPort)
	}

	req := &nat.Nat44AddDelLbStaticMapping{
		Tag:          []byte(ctx.Tag),
		Locals:       localAddrPorts,
		LocalNum:     uint8(len(localAddrPorts)),
		ExternalAddr: ctx.ExternalIP,
		ExternalPort: ctx.ExternalPort,
		Protocol:     ctx.Protocol,
		TwiceNat:     boolToUint(ctx.TwiceNat),
		SelfTwiceNat: boolToUint(ctx.SelfTwiceNat),
		Out2inOnly:   1,
		IsAdd:        boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelLbStaticMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove identity mapping
func (handler *natVppHandler) handleNat44IdentityMapping(ctx *IdentityMappingContext, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelIdentityMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelIdentityMapping{
		Tag: []byte(ctx.Tag),
		AddrOnly: func(port uint16, ip []byte) uint8 {
			// Set addr only if port is set to zero
			if port == 0 || ip == nil {
				return 1
			}
			return 0
		}(ctx.Port, ctx.IPAddress),
		IPAddress: ctx.IPAddress,
		Port:      ctx.Port,
		Protocol:  ctx.Protocol,
		SwIfIndex: func(ifIdx uint32) uint32 {
			if ifIdx == 0 {
				return NoInterface
			}
			return ifIdx
		}(ctx.IfIdx),
		VrfID: ctx.Vrf,
		IsAdd: boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelIdentityMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

func (handler *natVppHandler) EnableNat44Interface(ifIdx uint32, isInside bool) error {
	return handler.handleNat44Interface(ifIdx, isInside, true)
}

func (handler *natVppHandler) DisableNat44Interface(ifIdx uint32, isInside bool) error {
	return handler.handleNat44Interface(ifIdx, isInside, false)
}

func (handler *natVppHandler) EnableNat44InterfaceOutput(ifIdx uint32, isInside bool) error {
	return handler.handleNat44InterfaceOutputFeature(ifIdx, isInside, true)
}

func (handler *natVppHandler) DisableNat44InterfaceOutput(ifIdx uint32, isInside bool) error {
	return handler.handleNat44InterfaceOutputFeature(ifIdx, isInside, false)
}

func (handler *natVppHandler) AddNat44AddressPool(first, last []byte, vrf uint32, twiceNat bool) error {
	return handler.handleNat44AddressPool(first, last, vrf, twiceNat, true)
}

func (handler *natVppHandler) DelNat44AddressPool(first, last []byte, vrf uint32, twiceNat bool) error {
	return handler.handleNat44AddressPool(first, last, vrf, twiceNat, false)
}

func (handler *natVppHandler) AddNat44IdentityMapping(ctx *IdentityMappingContext) error {
	return handler.handleNat44IdentityMapping(ctx, true)
}

func (handler *natVppHandler) DelNat44IdentityMapping(ctx *IdentityMappingContext) error {
	return handler.handleNat44IdentityMapping(ctx, false)
}

func (handler *natVppHandler) AddNat44StaticMapping(ctx *StaticMappingContext) error {
	if ctx.AddressOnly {
		return handler.handleNat44StaticMapping(ctx, true, true)
	}
	return handler.handleNat44StaticMapping(ctx, true, false)
}

func (handler *natVppHandler) DelNat44StaticMapping(ctx *StaticMappingContext) error {
	if ctx.AddressOnly {
		return handler.handleNat44StaticMapping(ctx, false, true)
	}
	return handler.handleNat44StaticMapping(ctx, false, false)
}

func (handler *natVppHandler) AddNat44StaticMappingLb(ctx *StaticMappingLbContext) error {
	return handler.handleNat44StaticMappingLb(ctx, true)
}

func (handler *natVppHandler) DelNat44StaticMappingLb(ctx *StaticMappingLbContext) error {
	return handler.handleNat44StaticMappingLb(ctx, false)
}
<commit_msg>Add error logs into NAT plugin<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package vppcalls

import (
	"fmt"
	"time"

	"github.com/ligato/vpp-agent/plugins/vpp/binapi/nat"
)

// Num protocol representation
const (
	ICMP uint8 = 1
	TCP  uint8 = 6
	UDP  uint8 = 17
)

// NoInterface is sw-if-idx which means 'no interface'
const NoInterface uint32 = 0xffffffff

// StaticMappingContext groups common fields required for static mapping
type StaticMappingContext struct {
	Tag           string
	AddressOnly   bool
	LocalIP       []byte
	LocalPort     uint16
	ExternalIP    []byte
	ExternalPort  uint16
	ExternalIfIdx uint32
	Protocol      uint8
	Vrf           uint32
	TwiceNat      bool
	SelfTwiceNat  bool
}

// StaticMappingLbContext groups common fields required for static mapping with load balancer
type StaticMappingLbContext struct {
	Tag          string
	LocalIPs     []*LocalLbAddress
	ExternalIP   []byte
	ExternalPort uint16
	Protocol     uint8
	TwiceNat     bool
	SelfTwiceNat bool
}

// IdentityMappingContext groups common fields required for identity mapping
type IdentityMappingContext struct {
	Tag       string
	IPAddress []byte
	Protocol  uint8
	Port      uint16
	IfIdx     uint32
	Vrf       uint32
}

// LocalLbAddress represents one local IP and address entry
type LocalLbAddress struct {
	Vrf         uint32
	Tag         string
	LocalIP     []byte
	LocalPort   uint16
	Probability uint8
}

func (handler *natVppHandler) SetNat44Forwarding(enableFwd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44ForwardingEnableDisable{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44ForwardingEnableDisable{
		Enable: boolToUint(enableFwd),
	}

	reply := &nat.Nat44ForwardingEnableDisableReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to set/unset interface as NAT
func (handler *natVppHandler) handleNat44Interface(ifIdx uint32, isInside, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44InterfaceAddDelFeature{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44InterfaceAddDelFeature{
		SwIfIndex: ifIdx,
		IsInside:  boolToUint(isInside),
		IsAdd:     boolToUint(isAdd),
	}

	reply := &nat.Nat44InterfaceAddDelFeatureReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to set/unset interface as NAT with output feature
func (handler *natVppHandler) handleNat44InterfaceOutputFeature(ifIdx uint32, isInside, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44InterfaceAddDelOutputFeature{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44InterfaceAddDelOutputFeature{
		SwIfIndex: ifIdx,
		IsInside:  boolToUint(isInside),
		IsAdd:     boolToUint(isAdd),
	}

	reply := &nat.Nat44InterfaceAddDelOutputFeatureReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove address pool
func (handler *natVppHandler) handleNat44AddressPool(first, last []byte, vrf uint32, twiceNat, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelAddressRange{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelAddressRange{
		FirstIPAddress: first,
		LastIPAddress:  last,
		VrfID:          vrf,
		TwiceNat:       boolToUint(twiceNat),
		IsAdd:          boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelAddressRangeReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove static mapping
func (handler *natVppHandler) handleNat44StaticMapping(ctx *StaticMappingContext, isAdd, addrOnly bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelStaticMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelStaticMapping{
		Tag:               []byte(ctx.Tag),
		LocalIPAddress:    ctx.LocalIP,
		LocalPort:         ctx.LocalPort,
		ExternalIPAddress: ctx.ExternalIP,
		ExternalPort:      ctx.ExternalPort,
		Protocol:          ctx.Protocol,
		ExternalSwIfIndex: ctx.ExternalIfIdx,
		VrfID:             ctx.Vrf,
		TwiceNat:          boolToUint(ctx.TwiceNat),
		SelfTwiceNat:      boolToUint(ctx.SelfTwiceNat),
		Out2inOnly:        1,
		IsAdd:             boolToUint(isAdd),
	}
	if addrOnly {
		req.AddrOnly = 1
	} else {
		req.LocalPort = ctx.LocalPort
		req.ExternalPort = ctx.ExternalPort
	}

	reply := &nat.Nat44AddDelStaticMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove static mapping with load balancer
func (handler *natVppHandler) handleNat44StaticMappingLb(ctx *StaticMappingLbContext, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelLbStaticMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	// Transform local IP/Ports
	var localAddrPorts []nat.Nat44LbAddrPort
	for _, ctxLocal := range ctx.LocalIPs {
		localAddrPort := nat.Nat44LbAddrPort{
			Addr:        ctxLocal.LocalIP,
			Port:        ctxLocal.LocalPort,
			Probability: ctxLocal.Probability,
			VrfID:       ctxLocal.Vrf,
		}
		localAddrPorts = append(localAddrPorts, localAddrPort)
	}

	req := &nat.Nat44AddDelLbStaticMapping{
		Tag:          []byte(ctx.Tag),
		Locals:       localAddrPorts,
		LocalNum:     uint8(len(localAddrPorts)),
		ExternalAddr: ctx.ExternalIP,
		ExternalPort: ctx.ExternalPort,
		Protocol:     ctx.Protocol,
		TwiceNat:     boolToUint(ctx.TwiceNat),
		SelfTwiceNat: boolToUint(ctx.SelfTwiceNat),
		Out2inOnly:   1,
		IsAdd:        boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelLbStaticMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

// Calls VPP binary API to add/remove identity mapping
func (handler *natVppHandler) handleNat44IdentityMapping(ctx *IdentityMappingContext, isAdd bool) error {
	defer func(t time.Time) {
		handler.stopwatch.TimeLog(nat.Nat44AddDelIdentityMapping{}).LogTimeEntry(time.Since(t))
	}(time.Now())

	req := &nat.Nat44AddDelIdentityMapping{
		Tag: []byte(ctx.Tag),
		AddrOnly: func(port uint16, ip []byte) uint8 {
			// Set addr only if port is set to zero
			if port == 0 || ip == nil {
				return 1
			}
			return 0
		}(ctx.Port, ctx.IPAddress),
		IPAddress: ctx.IPAddress,
		Port:      ctx.Port,
		Protocol:  ctx.Protocol,
		SwIfIndex: func(ifIdx uint32) uint32 {
			if ifIdx == 0 {
				return NoInterface
			}
			return ifIdx
		}(ctx.IfIdx),
		VrfID: ctx.Vrf,
		IsAdd: boolToUint(isAdd),
	}

	reply := &nat.Nat44AddDelIdentityMappingReply{}
	if err := handler.callsChannel.SendRequest(req).ReceiveReply(reply); err != nil {
		return err
	}
	if reply.Retval < 0 {
		fmt.Errorf("CONTIV DBG level=error: %s returned %d", reply.GetMessageName(), reply.Retval)
		return fmt.Errorf("%s returned %d", reply.GetMessageName(), reply.Retval)
	}

	return nil
}

func (handler *natVppHandler) EnableNat44Interface(ifIdx uint32, isInside bool) error {
	return handler.handleNat44Interface(ifIdx, isInside, true)
}

func (handler *natVppHandler) DisableNat44Interface(ifIdx uint32, isInside bool) error {
	return handler.handleNat44Interface(ifIdx, isInside, false)
}

func (handler *natVppHandler) EnableNat44InterfaceOutput(ifIdx uint32, isInside bool) error {
	return handler.handleNat44InterfaceOutputFeature(ifIdx, isInside, true)
}

func (handler *natVppHandler) DisableNat44InterfaceOutput(ifIdx uint32, isInside bool) error {
	return handler.handleNat44InterfaceOutputFeature(ifIdx, isInside, false)
}

func (handler *natVppHandler) AddNat44AddressPool(first, last []byte, vrf uint32, twiceNat bool) error {
	return handler.handleNat44AddressPool(first, last, vrf, twiceNat, true)
}

func (handler *natVppHandler) DelNat44AddressPool(first, last []byte, vrf uint32, twiceNat bool) error {
	return handler.handleNat44AddressPool(first, last, vrf, twiceNat, false)
}

func (handler *natVppHandler) AddNat44IdentityMapping(ctx *IdentityMappingContext) error {
	return handler.handleNat44IdentityMapping(ctx, true)
}

func (handler *natVppHandler) DelNat44IdentityMapping(ctx *IdentityMappingContext) error {
	return handler.handleNat44IdentityMapping(ctx, false)
}

func (handler *natVppHandler) AddNat44StaticMapping(ctx *StaticMappingContext) error {
	if ctx.AddressOnly {
		return handler.handleNat44StaticMapping(ctx, true, true)
	}
	return handler.handleNat44StaticMapping(ctx, true, false)
}

func (handler *natVppHandler) DelNat44StaticMapping(ctx *StaticMappingContext) error {
	if ctx.AddressOnly {
		return handler.handleNat44StaticMapping(ctx, false, true)
	}
	return handler.handleNat44StaticMapping(ctx, false, false)
}

func (handler *natVppHandler) AddNat44StaticMappingLb(ctx *StaticMappingLbContext) error {
	return handler.handleNat44StaticMappingLb(ctx, true)
}

func (handler *natVppHandler) DelNat44StaticMappingLb(ctx *StaticMappingLbContext) error {
	return handler.handleNat44StaticMappingLb(ctx, false)
}
<|endoftext|>


<commit_before>
package main
import (
	"fmt"
	"net"
	"./Crypt"
	"bufio"
)

const key = "HeiIS105"

func main() {
	var msg = Crypt.AesEncrypt([]byte("Møte fr 5.5 14:45 Flåklypa"), key)
}

func connectAndSend(key, message []byte){

	p :=  make([]byte, 2048)

	conn, err := net.Dial("udp", "158.37.63.180:8009")
	if err != nil {
		fmt.Printf("Some error %v", err)
		return
	}

	fmt.Fprintf(conn, string(message))
	_, err = bufio.NewReader(conn).Read(p)

	if err == nil {
		fmt.Printf("%s\n", p)
	} else {
		fmt.Printf("Some error %v\n", err)
	}
	conn.Close()
}<commit_msg>Update client.go<commit_after>// Copyright 2017 Gruppe 12 IS-105. All rights reserved.
package main
import (
	"fmt"
	"net"
	"./Crypt"
	"bufio"
)

const key = "HeiIS105"

func main() {
	var msg = Crypt.AesEncrypt([]byte("Møte fr 5.5 14:45 Flåklypa"), key)
}

func connectAndSend(key, message []byte){

	p :=  make([]byte, 2048)

	conn, err := net.Dial("udp", "158.37.63.180:8009")
	if err != nil {
		fmt.Printf("Some error %v", err)
		return
	}

	fmt.Fprintf(conn, string(message))
	_, err = bufio.NewReader(conn).Read(p)

	if err == nil {
		fmt.Printf("%s\n", p)
	} else {
		fmt.Printf("Some error %v\n", err)
	}
	conn.Close()
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "math/rand"
import "time"

var quotes = []string{
	"Chaos will reign",
	"One step closer to the end of the world",
	"The Sun shall be turned into darkness",
	"The whole land will be a burning waste of salt and sulfur; nothing planted, nothing sprouting, no vegetation growing on it",
	"There the fire will consume you, the sword will cut you down; they will devour you like a swarm of locusts",
	"All these are the beginning of sorrows",
	"And many false prophets will appear and will deceive many people",
	"Not even death can save you",
	"Beware! Beyond lies mortal danger for the likes of you!",
	"This holy place has been desecrated",
	"Turn back! I can tell that you need more experience to fight safely in the next wilderness",
	"We shall feast when darkness falls, sing until our jaws are broken",
	"Everyday’s like a war between the will to go on and a wish that the world would spiral into the sun",
	"As they dwell in darkness so shall I",
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())
	fmt.Printf(quotes[rand.Intn(len(quotes))] + "\n")
}
<commit_msg>Change and decay<commit_after>package main

import (
	"fmt"
	"math/rand"
	"time"
)

var quotes = []string{
	"Chaos will reign",
	"One step closer to the end of the world",
	"The Sun shall be turned into darkness",
	"The whole land will be a burning waste of salt and sulfur; nothing planted, nothing sprouting, no vegetation growing on it",
	"There the fire will consume you, the sword will cut you down; they will devour you like a swarm of locusts",
	"All these are the beginning of sorrows",
	"And many false prophets will appear and will deceive many people",
	"Not even death can save you",
	"Beware! Beyond lies mortal danger for the likes of you!",
	"This holy place has been desecrated",
	"Turn back! I can tell that you need more experience to fight safely in the next wilderness",
	"We shall feast when darkness falls, sing until our jaws are broken",
	"Everyday’s like a war between the will to go on and a wish that the world would spiral into the sun",
	"As they dwell in darkness so shall I",
	"Change and decay in all around I see...",
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())
	fmt.Printf(quotes[rand.Intn(len(quotes))] + "\n")
}
<|endoftext|>


<commit_before>package luar

import (
	"reflect"

	"github.com/yuin/gopher-lua"
)

func checkPtr(L *lua.LState, idx int) (ref reflect.Value, mt *Metatable) {
	ud := L.CheckUserData(idx)
	ref = reflect.ValueOf(ud.Value)
	kind := reflect.Ptr
	if ref.Kind() != kind {
		L.ArgError(idx, "expecting "+kind.String())
	}
	mt = &Metatable{LTable: ud.Metatable.(*lua.LTable)}
	return
}

func ptrIndex(L *lua.LState) int {
	ref, mt := checkPtr(L, 1)
	key := L.CheckString(2)

	if fn := mt.method(key); fn != nil {
		L.Push(fn)
		return 1
	}

	// fallback to non-pointer method
	ref = ref.Elem()
	mt = MT(L, ref.Interface())
	if fn := mt.method(key); fn != nil {
		L.Push(fn)
		return 1
	}

	return 0
}

func ptrPow(L *lua.LState) int {
	ref, _ := checkPtr(L, 1)
	val := L.CheckAny(2)

	if ref.IsNil() {
		L.RaiseError("cannot dereference nil pointer")
	}
	elem := ref.Elem()
	if !elem.CanSet() {
		L.RaiseError("unable to set pointer value")
	}
	value, err := lValueToReflect(L, val, elem.Type(), nil)
	if err != nil {
		L.ArgError(2, err.Error())
	}
	elem.Set(value)
	L.SetTop(1)
	return 1
}

func ptrUnm(L *lua.LState) int {
	ref, _ := checkPtr(L, 1)
	elem := ref.Elem()
	if !elem.CanInterface() {
		L.RaiseError("cannot interface pointer type " + elem.String())
	}
	L.Push(New(L, elem.Interface()))
	return 1
}

func ptrEq(L *lua.LState) int {
	ref1, _ := checkPtr(L, 1)
	ref2, _ := checkPtr(L, 2)

	L.Push(lua.LBool(ref1.Pointer() == ref2.Pointer()))
	return 1
}
<commit_msg>simplify checkPtr<commit_after>package luar

import (
	"reflect"

	"github.com/yuin/gopher-lua"
)

func checkPtr(L *lua.LState, idx int) (ref reflect.Value, mt *Metatable) {
	ud := L.CheckUserData(idx)
	ref = reflect.ValueOf(ud.Value)
	if expecting := reflect.Ptr; ref.Kind() != expecting {
		L.ArgError(idx, "expecting "+expecting.String())
	}
	mt = &Metatable{LTable: ud.Metatable.(*lua.LTable)}
	return
}

func ptrIndex(L *lua.LState) int {
	ref, mt := checkPtr(L, 1)
	key := L.CheckString(2)

	if fn := mt.method(key); fn != nil {
		L.Push(fn)
		return 1
	}

	// fallback to non-pointer method
	ref = ref.Elem()
	mt = MT(L, ref.Interface())
	if fn := mt.method(key); fn != nil {
		L.Push(fn)
		return 1
	}

	return 0
}

func ptrPow(L *lua.LState) int {
	ref, _ := checkPtr(L, 1)
	val := L.CheckAny(2)

	if ref.IsNil() {
		L.RaiseError("cannot dereference nil pointer")
	}
	elem := ref.Elem()
	if !elem.CanSet() {
		L.RaiseError("unable to set pointer value")
	}
	value, err := lValueToReflect(L, val, elem.Type(), nil)
	if err != nil {
		L.ArgError(2, err.Error())
	}
	elem.Set(value)
	L.SetTop(1)
	return 1
}

func ptrUnm(L *lua.LState) int {
	ref, _ := checkPtr(L, 1)
	elem := ref.Elem()
	if !elem.CanInterface() {
		L.RaiseError("cannot interface pointer type " + elem.String())
	}
	L.Push(New(L, elem.Interface()))
	return 1
}

func ptrEq(L *lua.LState) int {
	ref1, _ := checkPtr(L, 1)
	ref2, _ := checkPtr(L, 2)

	L.Push(lua.LBool(ref1.Pointer() == ref2.Pointer()))
	return 1
}
<|endoftext|>


<commit_before>// khan
// https://github.com/topfreegames/khan
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Top Free Games <backend@tfgco.com>

package util

// VERSION identifies Khan's current version
var VERSION = "4.3.5"
<commit_msg>Bump API version to 4.3.6<commit_after>// khan
// https://github.com/topfreegames/khan
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Top Free Games <backend@tfgco.com>

package util

// VERSION identifies Khan's current version
var VERSION = "4.3.6"
<|endoftext|>


<commit_before>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

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

type State struct {
	mode uint32
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Size.X), int(info.Size.Y), nil
}

// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
type passwordReader int

func (r passwordReader) Read(buf []byte) (int, error) {
	return windows.Read(windows.Handle(r), buf)
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer func() {
		windows.SetConsoleMode(windows.Handle(fd), old)
	}()

	return readPasswordLine(passwordReader(fd))
}
<commit_msg>ssh/terminal: handle non-ASCII characters when reading passwords<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build windows

// Package terminal provides support functions for dealing with terminals, as
// commonly found on UNIX systems.
//
// Putting a terminal into raw mode is the most common requirement:
//
// 	oldState, err := terminal.MakeRaw(0)
// 	if err != nil {
// 	        panic(err)
// 	}
// 	defer terminal.Restore(0, oldState)
package terminal

import (
	"os"

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

type State struct {
	mode uint32
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
	var st uint32
	err := windows.GetConsoleMode(windows.Handle(fd), &st)
	return err == nil
}

// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// GetState returns the current state of a terminal which may be useful to
// restore the terminal after a signal.
func GetState(fd int) (*State, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	return &State{st}, nil
}

// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func Restore(fd int, state *State) error {
	return windows.SetConsoleMode(windows.Handle(fd), state.mode)
}

// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
	var info windows.ConsoleScreenBufferInfo
	if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
		return 0, 0, err
	}
	return int(info.Size.X), int(info.Size.Y), nil
}

// ReadPassword reads a line of input from a terminal without local echo.  This
// is commonly used for inputting passwords and other sensitive data. The slice
// returned does not include the \n.
func ReadPassword(fd int) ([]byte, error) {
	var st uint32
	if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
		return nil, err
	}
	old := st

	st &^= (windows.ENABLE_ECHO_INPUT)
	st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
	if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
		return nil, err
	}

	defer func() {
		windows.SetConsoleMode(windows.Handle(fd), old)
	}()

	return readPasswordLine(os.NewFile(uintptr(fd), "stdin"))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/julienschmidt/httprouter"
	"log"
	"net/http"
)

func main() {

	router := httprouter.New()

	router.POST("/:topic/:username", subscribe)
	router.DELETE("/:topic/:username", unsubscribe)
	router.POST("/:topic", publish)
	router.GET("/:topic/:username", retrieve)

	http.Handle("/", router)

	log.Println("Listening...")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

func subscribe(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("subscribe from " + params.ByName("topic") + "/" + params.ByName("username"))
	//
}

func unsubscribe(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("unsubscribe from " + params.ByName("topic") + "/" + params.ByName("username"))
}

func publish(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("publish into " + params.ByName("topic"))
}

func retrieve(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("retrieve from " + params.ByName("topic") + "/" + params.ByName("username"))
}

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

func errLog(err error) {
	if err != nil {
		log.Print(err)
	}
}
<commit_msg>added the redis code and implemented the Channel, Post and Usr hashes<commit_after>package main

import (
	"fmt"
	"github.com/fzzy/radix/redis"
	"github.com/julienschmidt/httprouter"
	"io/ioutil"
	"log"
	"net/http"
)

var RedisClient redis.Client

//TODO abstract exist check
func main() {

	log.Println("               __    ___    ___")
	log.Println("      |__|    |__|  |__    |__")
	log.Println("      |  |TTP |  ub ___|ub ___|erver")
	log.Println("https://github.com/Charlesworth/pubSubServer")
	log.Println("--------------------------------------------")

	//**************    set up redis     ************************
	RedisClient, err := redis.Dial("tcp", "178.62.74.225:6379")
	errLog(err)
	defer RedisClient.Close()

	foo, err := RedisClient.Cmd("PING").Str()
	errLog(err)
	log.Println("Redis Connection Reply: " + foo + " (connection accepted)")

	_, err = RedisClient.Cmd("FLUSHALL").Str() //test code
	errLog(err)                                //test code

	//************** router set up       ************************
	router := httprouter.New()

	router.POST("/:topic/:username", subscribe)
	router.DELETE("/:topic/:username", unsubscribe)
	router.POST("/:topic", publish)
	router.GET("/:topic/:username", retrieve)

	http.Handle("/", router)

	log.Println("Listening...")
	log.Fatal(http.ListenAndServe(":3000", nil))
}

func subscribe(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("subscribe from " + params.ByName("topic") + "/" + params.ByName("username"))
	//check if usr already exists
	usrExists, _ := RedisClient.Cmd("EXISTS", params.ByName("username")).Int() //******double check exsists works with a hash
	if usrExists == 0 {
		//make the usr profile
		RedisClient.Cmd("HMSET", params.ByName("username"))
	}

	//check if channel exsists
	keyExists, _ := RedisClient.Cmd("EXISTS", params.ByName("topic")).Int()
	if keyExists == 0 {
		//no channel for that name
		log.Println("fail, no such channel exists")
		//**************************************Need code here for fail****************************
	} else {
		//get channel post no
		postNo, _ := RedisClient.Cmd("GET", params.ByName("topic")).Str()

		//add channel to usr profile with channel post no
		RedisClient.Cmd("HSETNX", params.ByName("username"), params.ByName("topic"), postNo)

		//increment usrCount on channel post n
		RedisClient.Cmd("HINCRBY", params.ByName("topic")+postNo, 1) //error possible here, post may not exists yet
		//may need to increment a channel waiting users int

		//return responce code 200
		w.WriteHeader(200)
	}

}

func unsubscribe(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("unsubscribe from " + params.ByName("topic") + "/" + params.ByName("username"))
	//check if channel exists
	usrExists, _ := RedisClient.Cmd("EXISTS", params.ByName("username")).Int() //******double check exsists works with a hash
	if usrExists == 0 {
		//profile doesn't exist, fail
		//**************************************Need code here for fail****************************
	}

	//check if channel is in usr profile
	//HEXISTS  yhash field1
	isSubscribed, _ := RedisClient.Cmd("HEXISTS", params.ByName("username"), params.ByName("topic")).Int()
	if isSubscribed == 0 {
		//subscription doesn't exsist
		w.WriteHeader(404)
	} else {
		//if it is get the post no
		postNo, _ := RedisClient.Cmd("HGET", params.ByName("username"), params.ByName("topic")).Str()

		//then remove the channel from usr profile
		RedisClient.Cmd("HDEL", params.ByName("username"), params.ByName("topic"))

		//decrement usr count on that post
		RedisClient.Cmd("HINCRBY", params.ByName("topic")+postNo, "usrCount", -1) //this may fail as post may
		//not exsist yet

		w.WriteHeader(200)
	}

	//*channelClean* go channelClean

}

func publish(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("Publish requested from " + r.RemoteAddr + " into Channel " + params.ByName("topic"))
	defer r.Body.Close()
	bodyByte, _ := ioutil.ReadAll(r.Body)
	body := string(bodyByte)
	log.Println(body)

	var postNo string
	//if channel !exists
	keyExists, _ := RedisClient.Cmd("EXISTS", params.ByName("topic")).Int()
	if keyExists == 0 {
		//make the channel with value 1 and use postNumber = 0
		RedisClient.Cmd("SET", params.ByName("topic"), "1")
		postNo = "0"
	} else {
		//else read channel postNumber int and increment the value
		postNo, _ = RedisClient.Cmd("GET", params.ByName("topic")).Str()
		RedisClient.Cmd("INCR", params.ByName("topic"), "1")
	}

	//make channeln hash with content=body and UsrNO=0
	RedisClient.Cmd("HMSET", params.ByName("topic")+postNo, "usrCount", "0", "Content", body)
	//return responce code 200
	w.WriteHeader(200)
}

func retrieve(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Println("request from " + r.RemoteAddr)
	log.Println("retrieve from " + params.ByName("topic") + "/" + params.ByName("username"))
	//check if subscribed
	isSubscribed, _ := RedisClient.Cmd("HEXISTS", params.ByName("username"), params.ByName("topic")).Int()
	if isSubscribed == 0 {
		//subscription doesn't exsist
		w.WriteHeader(404)
	} else {
		//if subscribed then look at channel post no on usr profile
		postNo, _ := RedisClient.Cmd("HGET", params.ByName("username"), params.ByName("topic")).Str()

		//check if there are any posts to read
		postExists, _ := RedisClient.Cmd("HEXISTS", params.ByName("username"), params.ByName("Content")).Int()
		if postExists == 0 {
			//if not then return 204
			w.WriteHeader(204)
		} else {
			//else if channel post no is not last, retrieve post content
			content, _ := RedisClient.Cmd("HGET", params.ByName("username"), params.ByName("Content")).Str()

			//then move usr count to next post (may need to make an empty post for this or make a "waiting users"
			//feild in the channel to carry users accross, in the case where they just read the last post)

			//increment the post number on the user profile
			RedisClient.Cmd("HINCRBY", params.ByName("username"), params.ByName("topic"), 1)

			fmt.Fprintf(w, content)
			w.WriteHeader(200)
		}

		//*cleanChannel*
	}
}

//clean the channel of any posts that have been read by everyone, kicked off after a usrCount decrement
//should run differently depending on retrieve and unsub: unsub will scan whole load until a usrCount is found,
//while retrieve will scan up to channelPost(N + 1)
func channelClean(channel string, postNo int) {
	//after a usr count decrement, clean the channel of any posts that have been read by everyone
}

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

func errLog(err error) {
	if err != nil {
		log.Print(err)
	}
}
<|endoftext|>