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

package main

import (
	"bytes"
	"testing"
	"text/tabwriter"

	"strings"

	"github.com/golang/dep"
	"github.com/golang/dep/internal/gps"
)

func TestStatusFormatVersion(t *testing.T) {
	t.Parallel()

	tests := map[gps.Version]string{
		nil: "",
		gps.NewBranch("master"):        "branch master",
		gps.NewVersion("1.0.0"):        "1.0.0",
		gps.Revision("flooboofoobooo"): "flooboo",
	}
	for version, expected := range tests {
		str := formatVersion(version)
		if str != expected {
			t.Fatalf("expected '%v', got '%v'", expected, str)
		}
	}
}

func TestBasicLine(t *testing.T) {
	project := dep.Project{}
	aSemverConstraint, _ := gps.NewSemverConstraint("1.2.3")

	tests := []struct {
		name            string
		status          BasicStatus
		wantDotStatus   []string
		wantJSONStatus  []string
		wantTableStatus []string
	}{
		{
			name: "BasicStatus with ProjectRoot only",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar"];`},
			wantJSONStatus:  []string{`"Version":""`, `"Revision":""`},
			wantTableStatus: []string{`github.com/foo/bar                                         0`},
		},
		{
			name: "BasicStatus with Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Revision:    gps.Revision("flooboofoobooo"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\nflooboo"];`},
			wantJSONStatus:  []string{`"Version":""`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
			wantTableStatus: []string{`github.com/foo/bar                       flooboo           0`},
		},
		{
			name: "BasicStatus with Version and Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Version:     gps.NewVersion("1.0.0"),
				Revision:    gps.Revision("flooboofoobooo"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\n1.0.0"];`},
			wantJSONStatus:  []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
			wantTableStatus: []string{`github.com/foo/bar              1.0.0    flooboo           0`},
		},
		{
			name: "BasicStatus with Constraint, Version and Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Constraint:  aSemverConstraint,
				Version:     gps.NewVersion("1.0.0"),
				Revision:    gps.Revision("revxyz"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\n1.0.0"];`},
			wantJSONStatus:  []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`},
			wantTableStatus: []string{`github.com/foo/bar  1.2.3       1.0.0    revxyz            0`},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			var buf bytes.Buffer

			dotout := &dotOutput{
				p: &project,
				w: &buf,
			}
			dotout.BasicHeader()
			dotout.BasicLine(&test.status)
			dotout.BasicFooter()

			for _, wantStatus := range test.wantDotStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected node status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}

			buf.Reset()

			jsonout := &jsonOutput{w: &buf}

			jsonout.BasicHeader()
			jsonout.BasicLine(&test.status)
			jsonout.BasicFooter()

			for _, wantStatus := range test.wantJSONStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected JSON status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}

			buf.Reset()

			tabw := tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0)

			tableout := &tableOutput{w: tabw}

			tableout.BasicHeader()
			tableout.BasicLine(&test.status)
			tableout.BasicFooter()

			for _, wantStatus := range test.wantTableStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected Table status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}
		})
	}
}

func TestBasicStatusGetConsolidatedConstraint(t *testing.T) {
	aSemverConstraint, _ := gps.NewSemverConstraint("1.2.1")

	testCases := []struct {
		name           string
		basicStatus    BasicStatus
		wantConstraint string
	}{
		{
			name:           "empty BasicStatus",
			basicStatus:    BasicStatus{},
			wantConstraint: "",
		},
		{
			name: "BasicStatus with Any Constraint",
			basicStatus: BasicStatus{
				Constraint: gps.Any(),
			},
			wantConstraint: "*",
		},
		{
			name: "BasicStatus with Semver Constraint",
			basicStatus: BasicStatus{
				Constraint: aSemverConstraint,
			},
			wantConstraint: "1.2.1",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.basicStatus.getConsolidatedConstraint() != tc.wantConstraint {
				t.Errorf("unexpected consolidated constraint: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedConstraint(), tc.wantConstraint)
			}
		})
	}
}

func TestBasicStatusGetConsolidatedVersion(t *testing.T) {
	testCases := []struct {
		name        string
		basicStatus BasicStatus
		wantVersion string
	}{
		{
			name:        "empty BasicStatus",
			basicStatus: BasicStatus{},
			wantVersion: "",
		},
		{
			name: "BasicStatus with Version and Revision",
			basicStatus: BasicStatus{
				Version:  gps.NewVersion("1.0.0"),
				Revision: gps.Revision("revxyz"),
			},
			wantVersion: "1.0.0",
		},
		{
			name: "BasicStatus with only Revision",
			basicStatus: BasicStatus{
				Revision: gps.Revision("revxyz"),
			},
			wantVersion: "revxyz",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.basicStatus.getConsolidatedVersion() != tc.wantVersion {
				t.Errorf("unexpected consolidated version: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedVersion(), tc.wantVersion)
			}
		})
	}
}
<commit_msg>test(status): consolidated constraint with override<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"testing"
	"text/tabwriter"

	"strings"

	"github.com/golang/dep"
	"github.com/golang/dep/internal/gps"
)

func TestStatusFormatVersion(t *testing.T) {
	t.Parallel()

	tests := map[gps.Version]string{
		nil: "",
		gps.NewBranch("master"):        "branch master",
		gps.NewVersion("1.0.0"):        "1.0.0",
		gps.Revision("flooboofoobooo"): "flooboo",
	}
	for version, expected := range tests {
		str := formatVersion(version)
		if str != expected {
			t.Fatalf("expected '%v', got '%v'", expected, str)
		}
	}
}

func TestBasicLine(t *testing.T) {
	project := dep.Project{}
	aSemverConstraint, _ := gps.NewSemverConstraint("1.2.3")

	tests := []struct {
		name            string
		status          BasicStatus
		wantDotStatus   []string
		wantJSONStatus  []string
		wantTableStatus []string
	}{
		{
			name: "BasicStatus with ProjectRoot only",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar"];`},
			wantJSONStatus:  []string{`"Version":""`, `"Revision":""`},
			wantTableStatus: []string{`github.com/foo/bar                                         0`},
		},
		{
			name: "BasicStatus with Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Revision:    gps.Revision("flooboofoobooo"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\nflooboo"];`},
			wantJSONStatus:  []string{`"Version":""`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
			wantTableStatus: []string{`github.com/foo/bar                       flooboo           0`},
		},
		{
			name: "BasicStatus with Version and Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Version:     gps.NewVersion("1.0.0"),
				Revision:    gps.Revision("flooboofoobooo"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\n1.0.0"];`},
			wantJSONStatus:  []string{`"Version":"1.0.0"`, `"Revision":"flooboofoobooo"`, `"Constraint":""`},
			wantTableStatus: []string{`github.com/foo/bar              1.0.0    flooboo           0`},
		},
		{
			name: "BasicStatus with Constraint, Version and Revision",
			status: BasicStatus{
				ProjectRoot: "github.com/foo/bar",
				Constraint:  aSemverConstraint,
				Version:     gps.NewVersion("1.0.0"),
				Revision:    gps.Revision("revxyz"),
			},
			wantDotStatus:   []string{`[label="github.com/foo/bar\n1.0.0"];`},
			wantJSONStatus:  []string{`"Revision":"revxyz"`, `"Constraint":"1.2.3"`, `"Version":"1.0.0"`},
			wantTableStatus: []string{`github.com/foo/bar  1.2.3       1.0.0    revxyz            0`},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			var buf bytes.Buffer

			dotout := &dotOutput{
				p: &project,
				w: &buf,
			}
			dotout.BasicHeader()
			dotout.BasicLine(&test.status)
			dotout.BasicFooter()

			for _, wantStatus := range test.wantDotStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected node status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}

			buf.Reset()

			jsonout := &jsonOutput{w: &buf}

			jsonout.BasicHeader()
			jsonout.BasicLine(&test.status)
			jsonout.BasicFooter()

			for _, wantStatus := range test.wantJSONStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected JSON status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}

			buf.Reset()

			tabw := tabwriter.NewWriter(&buf, 0, 4, 2, ' ', 0)

			tableout := &tableOutput{w: tabw}

			tableout.BasicHeader()
			tableout.BasicLine(&test.status)
			tableout.BasicFooter()

			for _, wantStatus := range test.wantTableStatus {
				if ok := strings.Contains(buf.String(), wantStatus); !ok {
					t.Errorf("Did not find expected Table status: \n\t(GOT) %v \n\t(WNT) %v", buf.String(), wantStatus)
				}
			}
		})
	}
}

func TestBasicStatusGetConsolidatedConstraint(t *testing.T) {
	aSemverConstraint, _ := gps.NewSemverConstraint("1.2.1")

	testCases := []struct {
		name           string
		basicStatus    BasicStatus
		wantConstraint string
	}{
		{
			name:           "empty BasicStatus",
			basicStatus:    BasicStatus{},
			wantConstraint: "",
		},
		{
			name: "BasicStatus with Any Constraint",
			basicStatus: BasicStatus{
				Constraint: gps.Any(),
			},
			wantConstraint: "*",
		},
		{
			name: "BasicStatus with Semver Constraint",
			basicStatus: BasicStatus{
				Constraint: aSemverConstraint,
			},
			wantConstraint: "1.2.1",
		},
		{
			name: "BasicStatus with Override",
			basicStatus: BasicStatus{
				Constraint:  aSemverConstraint,
				hasOverride: true,
			},
			wantConstraint: "1.2.1 (override)",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.basicStatus.getConsolidatedConstraint() != tc.wantConstraint {
				t.Errorf("unexpected consolidated constraint: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedConstraint(), tc.wantConstraint)
			}
		})
	}
}

func TestBasicStatusGetConsolidatedVersion(t *testing.T) {
	testCases := []struct {
		name        string
		basicStatus BasicStatus
		wantVersion string
	}{
		{
			name:        "empty BasicStatus",
			basicStatus: BasicStatus{},
			wantVersion: "",
		},
		{
			name: "BasicStatus with Version and Revision",
			basicStatus: BasicStatus{
				Version:  gps.NewVersion("1.0.0"),
				Revision: gps.Revision("revxyz"),
			},
			wantVersion: "1.0.0",
		},
		{
			name: "BasicStatus with only Revision",
			basicStatus: BasicStatus{
				Revision: gps.Revision("revxyz"),
			},
			wantVersion: "revxyz",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			if tc.basicStatus.getConsolidatedVersion() != tc.wantVersion {
				t.Errorf("unexpected consolidated version: \n\t(GOT) %v \n\t(WNT) %v", tc.basicStatus.getConsolidatedVersion(), tc.wantVersion)
			}
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"errors"
	"io/ioutil"

	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/cmd/influx/internal"
	"github.com/influxdata/influxdb/v2/http"
	"github.com/influxdata/influxdb/v2/tenant"
	"github.com/spf13/cobra"
)

func cmdTelegraf(f *globalFlags, opts genericCLIOpts) *cobra.Command {
	return newCmdTelegrafBuilder(newTelegrafSVCs, f, opts).cmdTelegrafs()
}

type telegrafSVCsFn func() (influxdb.TelegrafConfigStore, influxdb.OrganizationService, error)

type cmdTelegrafBuilder struct {
	genericCLIOpts
	*globalFlags

	svcFn telegrafSVCsFn

	desc string
	file string
	id   string
	ids  []string
	name string
	org  organization
}

func newCmdTelegrafBuilder(svcFn telegrafSVCsFn, f *globalFlags, opts genericCLIOpts) *cmdTelegrafBuilder {
	return &cmdTelegrafBuilder{
		genericCLIOpts: opts,
		globalFlags:    f,
		svcFn:          svcFn,
	}
}

func (b *cmdTelegrafBuilder) cmdTelegrafs() *cobra.Command {
	cmd := b.newCmd("telegrafs", b.listRunE)
	cmd.Short = "List Telegraf configuration(s). Subcommands manage Telegraf configurations."
	cmd.Long = `
	List Telegraf configuration(s). Subcommands manage Telegraf configurations.

	Examples:
		# list all known Telegraf configurations
		influx telegrafs

		# list Telegraf configuration corresponding to specific ID
		influx telegrafs --id $ID

		# list Telegraf configuration corresponding to specific ID shorts
		influx telegrafs -i $ID
`

	b.org.register(cmd, false)
	cmd.Flags().StringVarP(&b.id, "id", "i", "", "Telegraf configuration ID to retrieve.")

	cmd.AddCommand(
		b.cmdCreate(),
		b.cmdRemove(),
		b.cmdUpdate(),
	)
	return cmd
}

func (b *cmdTelegrafBuilder) listRunE(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, _ := b.org.getID(orgSVC)
	if orgID == 0 && b.id == "" {
		return &influxdb.Error{
			Code: influxdb.EUnprocessableEntity,
			Msg:  "at least one of org, org-id, or id must be provided",
		}
	}

	var filter influxdb.UserResourceMappingFilter
	if b.id != "" {
		id, err := influxdb.IDFromString(b.id)
		if err != nil {
			return err
		}
		filter.ResourceID = *id
		filter.ResourceType = influxdb.TelegrafsResourceType
	}

	cfgs, _, err := svc.FindTelegrafConfigs(context.Background(), influxdb.TelegrafConfigFilter{
		OrgID:                     &orgID,
		UserResourceMappingFilter: filter,
	})
	if err != nil {
		return err
	}
	return b.writeTelegrafConfig(cfgs...)
}

func (b *cmdTelegrafBuilder) cmdCreate() *cobra.Command {
	cmd := b.newCmd("create", b.createRunEFn)
	cmd.Short = "Create a Telegraf configuration"
	cmd.Long = `
	The telegrafs create command creates a new Telegraf configuration.

	Examples:
		# create new Telegraf configuration
		influx telegrafs create --name $CFG_NAME --description $CFG_DESC --file $PATH_TO_TELE_CFG

		# create new Telegraf configuration using shorts
		influx telegrafs create -n $CFG_NAME -d $CFG_DESC -f $PATH_TO_TELE_CFG

		# create a new Telegraf config with a config provided via STDIN
		cat $CONFIG_FILE | influx telegrafs create -n $CFG_NAME -d $CFG_DESC
`

	b.org.register(cmd, false)
	b.registerTelegrafCfgFlags(cmd)

	return cmd
}

func (b *cmdTelegrafBuilder) createRunEFn(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, err := b.org.getID(orgSVC)
	if err != nil {
		return err
	}

	cfg, err := b.readConfig(b.file)
	if err != nil {
		return err
	}

	newTelegraf := influxdb.TelegrafConfig{
		OrgID:       orgID,
		Name:        b.name,
		Description: b.desc,
		Config:      cfg,
	}
	err = svc.CreateTelegrafConfig(context.Background(), &newTelegraf, 0)
	if err != nil {
		return err
	}

	return b.writeTelegrafConfig(&newTelegraf)
}

func (b *cmdTelegrafBuilder) cmdRemove() *cobra.Command {
	cmd := b.newCmd("rm", b.removeRunEFn)
	cmd.Aliases = []string{"remove"}
	cmd.Short = "Remove Telegraf configuration(s)"
	cmd.Long = `
	The telegrafs rm command removes Telegraf configuration(s).

	Examples:
		# remove a single Telegraf configuration
		influx telegrafs rm --id $ID

		# remove multiple Telegraf configurations
		influx telegrafs rm --id $ID1 --id $ID2

		# remove using short flags
		influx telegrafs rm -i $ID1
`

	cmd.Flags().StringArrayVarP(&b.ids, "id", "i", nil, "Telegraf configuration ID(s) to remove.")
	cmd.MarkFlagRequired("id")

	return cmd
}

func (b *cmdTelegrafBuilder) removeRunEFn(cmd *cobra.Command, args []string) error {
	svc, _, err := b.svcFn()
	if err != nil {
		return err
	}

	for _, rawID := range b.ids {
		id, err := influxdb.IDFromString(rawID)
		if err != nil {
			return err
		}

		err = svc.DeleteTelegrafConfig(context.Background(), *id)
		if err != nil && influxdb.ErrorCode(err) != influxdb.ENotFound {
			return err
		}
	}

	return nil
}

func (b *cmdTelegrafBuilder) cmdUpdate() *cobra.Command {
	cmd := b.newCmd("update", b.updateRunEFn)
	cmd.Short = "Update a Telegraf configuration"
	cmd.Long = `
	The telegrafs update command updates a Telegraf configuration to match the
	specified parameters. If a name or description is not provided, then are set
	to an empty string.

	Examples:
		# update new Telegraf configuration
		influx telegrafs update --id $ID --name $CFG_NAME --description $CFG_DESC --file $PATH_TO_TELE_CFG

		# update new Telegraf configuration using shorts
		influx telegrafs update -i $ID -n $CFG_NAME -d $CFG_DESC -f $PATH_TO_TELE_CFG

		# update a Telegraf config with a config provided via STDIN
		cat $CONFIG_FILE | influx telegrafs update -i $ID  -n $CFG_NAME -d $CFG_DESC
`

	b.org.register(cmd, false)
	b.registerTelegrafCfgFlags(cmd)
	cmd.Flags().StringVarP(&b.id, "id", "i", "", "Telegraf configuration id to update")
	cmd.MarkFlagRequired("id")

	return cmd
}

func (b *cmdTelegrafBuilder) updateRunEFn(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, err := b.org.getID(orgSVC)
	if err != nil {
		return err
	}

	cfg, err := b.readConfig(b.file)
	if err != nil {
		return err
	}

	id, err := influxdb.IDFromString(b.id)
	if err != nil {
		return err
	}

	teleCfg := influxdb.TelegrafConfig{
		ID:          *id,
		OrgID:       orgID,
		Name:        b.name,
		Description: b.desc,
		Config:      cfg,
	}
	updatedCfg, err := svc.UpdateTelegrafConfig(context.Background(), *id, &teleCfg, 0)
	if err != nil {
		return err
	}

	return b.writeTelegrafConfig(updatedCfg)
}

func (b *cmdTelegrafBuilder) writeTelegrafConfig(cfgs ...*influxdb.TelegrafConfig) error {
	if b.json {
		return b.writeJSON(cfgs)
	}

	tabW := b.newTabWriter()
	defer tabW.Flush()

	writeTelegrafRows(tabW, cfgs...)
	return nil
}

func writeTelegrafRows(tabW *internal.TabWriter, cfgs ...*influxdb.TelegrafConfig) {
	tabW.WriteHeaders("ID", "OrgID", "Name", "Description")
	for _, cfg := range cfgs {
		tabW.Write(map[string]interface{}{
			"ID":          cfg.ID,
			"OrgID":       cfg.OrgID,
			"Name":        cfg.Name,
			"Description": cfg.Description,
		})
	}
}

func (b *cmdTelegrafBuilder) registerTelegrafCfgFlags(cmd *cobra.Command) {
	cmd.Flags().StringVarP(&b.file, "file", "f", "", "Path to Telegraf configuration")
	cmd.Flags().StringVarP(&b.name, "name", "n", "", "Name of Telegraf configuration")
	cmd.Flags().StringVarP(&b.desc, "description", "d", "", "Description for Telegraf configuration")
}

func (b *cmdTelegrafBuilder) readConfig(file string) (string, error) {
	if file != "" {
		bb, err := ioutil.ReadFile(file)
		if err != nil {
			return "", err
		}

		return string(bb), nil
	}

	stdIn, err := inStdIn(b.in)
	if err != nil {
		return "", &influxdb.Error{
			Code: influxdb.EUnprocessableEntity,
			Err:  errors.New("a Telegraf config must be provided"),
		}
	}
	defer stdIn.Close()

	bb, err := ioutil.ReadAll(stdIn)
	if err != nil {
		return "", err
	}
	return string(bb), nil
}

func (b *cmdTelegrafBuilder) newCmd(use string, runE func(*cobra.Command, []string) error) *cobra.Command {
	cmd := b.genericCLIOpts.newCmd(use, runE, true)
	b.genericCLIOpts.registerPrintOptions(cmd)
	return cmd
}

func newTelegrafSVCs() (influxdb.TelegrafConfigStore, influxdb.OrganizationService, error) {
	httpClient, err := newHTTPClient()
	if err != nil {
		return nil, nil, err
	}

	orgSVC := &tenant.OrgClientService{
		Client: httpClient,
	}

	return http.NewTelegrafService(httpClient), orgSVC, nil
}
<commit_msg>fix(influx): register global flags on telegraf cmds<commit_after>package main

import (
	"context"
	"errors"
	"io/ioutil"

	"github.com/influxdata/influxdb/v2"
	"github.com/influxdata/influxdb/v2/cmd/influx/internal"
	"github.com/influxdata/influxdb/v2/http"
	"github.com/influxdata/influxdb/v2/tenant"
	"github.com/spf13/cobra"
)

func cmdTelegraf(f *globalFlags, opts genericCLIOpts) *cobra.Command {
	return newCmdTelegrafBuilder(newTelegrafSVCs, f, opts).cmdTelegrafs()
}

type telegrafSVCsFn func() (influxdb.TelegrafConfigStore, influxdb.OrganizationService, error)

type cmdTelegrafBuilder struct {
	genericCLIOpts
	*globalFlags

	svcFn telegrafSVCsFn

	desc string
	file string
	id   string
	ids  []string
	name string
	org  organization
}

func newCmdTelegrafBuilder(svcFn telegrafSVCsFn, f *globalFlags, opts genericCLIOpts) *cmdTelegrafBuilder {
	return &cmdTelegrafBuilder{
		genericCLIOpts: opts,
		globalFlags:    f,
		svcFn:          svcFn,
	}
}

func (b *cmdTelegrafBuilder) cmdTelegrafs() *cobra.Command {
	cmd := b.newCmd("telegrafs", b.listRunE)
	cmd.Short = "List Telegraf configuration(s). Subcommands manage Telegraf configurations."
	cmd.Long = `
	List Telegraf configuration(s). Subcommands manage Telegraf configurations.

	Examples:
		# list all known Telegraf configurations
		influx telegrafs

		# list Telegraf configuration corresponding to specific ID
		influx telegrafs --id $ID

		# list Telegraf configuration corresponding to specific ID shorts
		influx telegrafs -i $ID
`

	b.org.register(cmd, false)
	cmd.Flags().StringVarP(&b.id, "id", "i", "", "Telegraf configuration ID to retrieve.")

	cmd.AddCommand(
		b.cmdCreate(),
		b.cmdRemove(),
		b.cmdUpdate(),
	)
	return cmd
}

func (b *cmdTelegrafBuilder) listRunE(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, _ := b.org.getID(orgSVC)
	if orgID == 0 && b.id == "" {
		return &influxdb.Error{
			Code: influxdb.EUnprocessableEntity,
			Msg:  "at least one of org, org-id, or id must be provided",
		}
	}

	var filter influxdb.UserResourceMappingFilter
	if b.id != "" {
		id, err := influxdb.IDFromString(b.id)
		if err != nil {
			return err
		}
		filter.ResourceID = *id
		filter.ResourceType = influxdb.TelegrafsResourceType
	}

	cfgs, _, err := svc.FindTelegrafConfigs(context.Background(), influxdb.TelegrafConfigFilter{
		OrgID:                     &orgID,
		UserResourceMappingFilter: filter,
	})
	if err != nil {
		return err
	}
	return b.writeTelegrafConfig(cfgs...)
}

func (b *cmdTelegrafBuilder) cmdCreate() *cobra.Command {
	cmd := b.newCmd("create", b.createRunEFn)
	cmd.Short = "Create a Telegraf configuration"
	cmd.Long = `
	The telegrafs create command creates a new Telegraf configuration.

	Examples:
		# create new Telegraf configuration
		influx telegrafs create --name $CFG_NAME --description $CFG_DESC --file $PATH_TO_TELE_CFG

		# create new Telegraf configuration using shorts
		influx telegrafs create -n $CFG_NAME -d $CFG_DESC -f $PATH_TO_TELE_CFG

		# create a new Telegraf config with a config provided via STDIN
		cat $CONFIG_FILE | influx telegrafs create -n $CFG_NAME -d $CFG_DESC
`

	b.org.register(cmd, false)
	b.registerTelegrafCfgFlags(cmd)

	return cmd
}

func (b *cmdTelegrafBuilder) createRunEFn(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, err := b.org.getID(orgSVC)
	if err != nil {
		return err
	}

	cfg, err := b.readConfig(b.file)
	if err != nil {
		return err
	}

	newTelegraf := influxdb.TelegrafConfig{
		OrgID:       orgID,
		Name:        b.name,
		Description: b.desc,
		Config:      cfg,
	}
	err = svc.CreateTelegrafConfig(context.Background(), &newTelegraf, 0)
	if err != nil {
		return err
	}

	return b.writeTelegrafConfig(&newTelegraf)
}

func (b *cmdTelegrafBuilder) cmdRemove() *cobra.Command {
	cmd := b.newCmd("rm", b.removeRunEFn)
	cmd.Aliases = []string{"remove"}
	cmd.Short = "Remove Telegraf configuration(s)"
	cmd.Long = `
	The telegrafs rm command removes Telegraf configuration(s).

	Examples:
		# remove a single Telegraf configuration
		influx telegrafs rm --id $ID

		# remove multiple Telegraf configurations
		influx telegrafs rm --id $ID1 --id $ID2

		# remove using short flags
		influx telegrafs rm -i $ID1
`

	cmd.Flags().StringArrayVarP(&b.ids, "id", "i", nil, "Telegraf configuration ID(s) to remove.")
	cmd.MarkFlagRequired("id")

	return cmd
}

func (b *cmdTelegrafBuilder) removeRunEFn(cmd *cobra.Command, args []string) error {
	svc, _, err := b.svcFn()
	if err != nil {
		return err
	}

	for _, rawID := range b.ids {
		id, err := influxdb.IDFromString(rawID)
		if err != nil {
			return err
		}

		err = svc.DeleteTelegrafConfig(context.Background(), *id)
		if err != nil && influxdb.ErrorCode(err) != influxdb.ENotFound {
			return err
		}
	}

	return nil
}

func (b *cmdTelegrafBuilder) cmdUpdate() *cobra.Command {
	cmd := b.newCmd("update", b.updateRunEFn)
	cmd.Short = "Update a Telegraf configuration"
	cmd.Long = `
	The telegrafs update command updates a Telegraf configuration to match the
	specified parameters. If a name or description is not provided, then are set
	to an empty string.

	Examples:
		# update new Telegraf configuration
		influx telegrafs update --id $ID --name $CFG_NAME --description $CFG_DESC --file $PATH_TO_TELE_CFG

		# update new Telegraf configuration using shorts
		influx telegrafs update -i $ID -n $CFG_NAME -d $CFG_DESC -f $PATH_TO_TELE_CFG

		# update a Telegraf config with a config provided via STDIN
		cat $CONFIG_FILE | influx telegrafs update -i $ID  -n $CFG_NAME -d $CFG_DESC
`

	b.org.register(cmd, false)
	b.registerTelegrafCfgFlags(cmd)
	cmd.Flags().StringVarP(&b.id, "id", "i", "", "Telegraf configuration id to update")
	cmd.MarkFlagRequired("id")

	return cmd
}

func (b *cmdTelegrafBuilder) updateRunEFn(cmd *cobra.Command, args []string) error {
	svc, orgSVC, err := b.svcFn()
	if err != nil {
		return err
	}

	orgID, err := b.org.getID(orgSVC)
	if err != nil {
		return err
	}

	cfg, err := b.readConfig(b.file)
	if err != nil {
		return err
	}

	id, err := influxdb.IDFromString(b.id)
	if err != nil {
		return err
	}

	teleCfg := influxdb.TelegrafConfig{
		ID:          *id,
		OrgID:       orgID,
		Name:        b.name,
		Description: b.desc,
		Config:      cfg,
	}
	updatedCfg, err := svc.UpdateTelegrafConfig(context.Background(), *id, &teleCfg, 0)
	if err != nil {
		return err
	}

	return b.writeTelegrafConfig(updatedCfg)
}

func (b *cmdTelegrafBuilder) writeTelegrafConfig(cfgs ...*influxdb.TelegrafConfig) error {
	if b.json {
		return b.writeJSON(cfgs)
	}

	tabW := b.newTabWriter()
	defer tabW.Flush()

	writeTelegrafRows(tabW, cfgs...)
	return nil
}

func writeTelegrafRows(tabW *internal.TabWriter, cfgs ...*influxdb.TelegrafConfig) {
	tabW.WriteHeaders("ID", "OrgID", "Name", "Description")
	for _, cfg := range cfgs {
		tabW.Write(map[string]interface{}{
			"ID":          cfg.ID,
			"OrgID":       cfg.OrgID,
			"Name":        cfg.Name,
			"Description": cfg.Description,
		})
	}
}

func (b *cmdTelegrafBuilder) registerTelegrafCfgFlags(cmd *cobra.Command) {
	cmd.Flags().StringVarP(&b.file, "file", "f", "", "Path to Telegraf configuration")
	cmd.Flags().StringVarP(&b.name, "name", "n", "", "Name of Telegraf configuration")
	cmd.Flags().StringVarP(&b.desc, "description", "d", "", "Description for Telegraf configuration")
}

func (b *cmdTelegrafBuilder) readConfig(file string) (string, error) {
	if file != "" {
		bb, err := ioutil.ReadFile(file)
		if err != nil {
			return "", err
		}

		return string(bb), nil
	}

	stdIn, err := inStdIn(b.in)
	if err != nil {
		return "", &influxdb.Error{
			Code: influxdb.EUnprocessableEntity,
			Err:  errors.New("a Telegraf config must be provided"),
		}
	}
	defer stdIn.Close()

	bb, err := ioutil.ReadAll(stdIn)
	if err != nil {
		return "", err
	}
	return string(bb), nil
}

func (b *cmdTelegrafBuilder) newCmd(use string, runE func(*cobra.Command, []string) error) *cobra.Command {
	cmd := b.genericCLIOpts.newCmd(use, runE, true)
	b.genericCLIOpts.registerPrintOptions(cmd)
	b.globalFlags.registerFlags(cmd)
	return cmd
}

func newTelegrafSVCs() (influxdb.TelegrafConfigStore, influxdb.OrganizationService, error) {
	httpClient, err := newHTTPClient()
	if err != nil {
		return nil, nil, err
	}

	orgSVC := &tenant.OrgClientService{
		Client: httpClient,
	}

	return http.NewTelegrafService(httpClient), orgSVC, nil
}
<|endoftext|>


<commit_before>// Copyright 2020 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.

// Implements java/maven buildpack.
// The maven buildpack builds Maven applications.
package main

import (
	"fmt"
	"net/http"
	"os"
	"path/filepath"
	"strings"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/java"
	"github.com/buildpack/libbuildpack/layers"
)

const (
	// TODO(b/151198698): Automate Maven version updates.
	mavenVersion = "3.6.3"
	mavenURL     = "https://downloads.apache.org/maven/maven-3/%[1]s/binaries/apache-maven-%[1]s-bin.tar.gz"
	mavenLayer   = "maven"
	m2Layer      = "m2"
)

// mavenMetadata represents metadata stored for a maven layer.
type mavenMetadata struct {
	Version string `toml:"version"`
}

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) error {
	if !ctx.FileExists("pom.xml") {
		ctx.OptOut("pom.xml not found.")
	}
	return nil
}

func buildFn(ctx *gcp.Context) error {
	var repoMeta java.RepoMetadata
	m2CachedRepo := ctx.Layer(m2Layer)
	ctx.ReadMetadata(m2CachedRepo, &repoMeta)
	java.CheckCacheExpiration(ctx, &repoMeta, m2CachedRepo)

	var mvn string
	var err error
	if ctx.FileExists("mvnw") {
		mvn = "./mvnw"
	} else if mvnInstalled(ctx) {
		mvn = "mvn"
	} else {
		mvn, err = installMaven(ctx)
		if err != nil {
			return fmt.Errorf("installing Maven: %w", err)
		}
	}

	command := []string{mvn, "clean", "package", "--batch-mode", "-DskipTests", "-Dmaven.repo.local=" + m2CachedRepo.Root}

	if buildArgs := os.Getenv(env.BuildArgs); buildArgs != "" {
		if strings.Contains(buildArgs, "maven.repo.local") {
			ctx.Warnf("Detected maven.repo.local property set in GOOGLE_BUILD_ARGS. Maven caching may not work properly.")
		}
		command = append(command, buildArgs)
	}
	if !ctx.Debug() {
		command = append(command, "--quiet")
	}
	ctx.ExecUser(command)
	ctx.WriteMetadata(m2CachedRepo, &repoMeta, layers.Cache)

	return nil
}

func mvnInstalled(ctx *gcp.Context) bool {
	result := ctx.Exec([]string{"bash", "-c", "command -v mvn || true"})
	return result.Stdout != ""
}

// installMaven installs Maven and returns the path of the mvn binary
func installMaven(ctx *gcp.Context) (string, error) {
	mvnl := ctx.Layer(mavenLayer)

	// Check the metadata in the cache layer to determine if we need to proceed.
	var meta mavenMetadata
	ctx.ReadMetadata(mvnl, &meta)

	if mavenVersion == meta.Version {
		ctx.CacheHit(mavenLayer)
		ctx.Logf("Maven cache hit, skipping installation.")
		return filepath.Join(mvnl.Root, "bin", "mvn"), nil
	}
	ctx.CacheMiss(mavenLayer)
	ctx.ClearLayer(mvnl)

	// Download and install maven in layer.
	ctx.Logf("Installing Maven v%s", mavenVersion)
	archiveURL := fmt.Sprintf(mavenURL, mavenVersion)
	if code := ctx.HTTPStatus(archiveURL); code != http.StatusOK {
		return "", gcp.UserErrorf("Maven version %s does not exist at %s (status %d).", mavenVersion, archiveURL, code)
	}
	command := fmt.Sprintf("curl --fail --show-error --silent --location --retry 3 %s | tar xz --directory %s --strip-components=1", archiveURL, mvnl.Root)
	ctx.Exec([]string{"bash", "-c", command})

	meta.Version = mavenVersion
	ctx.WriteMetadata(mvnl, meta, layers.Cache)
	return filepath.Join(mvnl.Root, "bin", "mvn"), nil
}
<commit_msg>Make a symlink from the root of the Maven repo layer to ~/.m2. That means it becomes the default for Maven, and is also accessible to later buildpacks.<commit_after>// Copyright 2020 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.

// Implements java/maven buildpack.
// The maven buildpack builds Maven applications.
package main

import (
	"fmt"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
	gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
	"github.com/GoogleCloudPlatform/buildpacks/pkg/java"
	"github.com/buildpack/libbuildpack/layers"
)

const (
	// TODO(b/151198698): Automate Maven version updates.
	mavenVersion = "3.6.3"
	mavenURL     = "https://downloads.apache.org/maven/maven-3/%[1]s/binaries/apache-maven-%[1]s-bin.tar.gz"
	mavenLayer   = "maven"
	m2Layer      = "m2"
)

// mavenMetadata represents metadata stored for a maven layer.
type mavenMetadata struct {
	Version string `toml:"version"`
}

func main() {
	gcp.Main(detectFn, buildFn)
}

func detectFn(ctx *gcp.Context) error {
	if !ctx.FileExists("pom.xml") {
		ctx.OptOut("pom.xml not found.")
	}
	return nil
}

func buildFn(ctx *gcp.Context) error {
	var repoMeta java.RepoMetadata
	m2CachedRepo := ctx.Layer(m2Layer)
	ctx.ReadMetadata(m2CachedRepo, &repoMeta)
	java.CheckCacheExpiration(ctx, &repoMeta, m2CachedRepo)
	ctx.WriteMetadata(m2CachedRepo, &repoMeta, layers.Cache)

	usr, err := user.Current()
	if err != nil {
		return fmt.Errorf("getting current user: %v", err)
	}
	// TODO(b/157297290): just use os.Getenv("HOME") when that is consistent with /etc/passwd.
	homeM2 := filepath.Join(usr.HomeDir, ".m2")
	// Symlink the m2 layer into ~/.m2. If ~/.m2 already exists, delete it first.
	// If it exists as a symlink, RemoveAll will remove the link, not anything it's linked to.
	ctx.RemoveAll(homeM2)
	ctx.Symlink(m2CachedRepo.Root, homeM2)

	var mvn string
	if ctx.FileExists("mvnw") {
		mvn = "./mvnw"
	} else if mvnInstalled(ctx) {
		mvn = "mvn"
	} else {
		mvn, err = installMaven(ctx)
		if err != nil {
			return fmt.Errorf("installing Maven: %w", err)
		}
	}

	command := []string{mvn, "clean", "package", "--batch-mode", "-DskipTests"}

	if buildArgs := os.Getenv(env.BuildArgs); buildArgs != "" {
		if strings.Contains(buildArgs, "maven.repo.local") {
			ctx.Warnf("Detected maven.repo.local property set in GOOGLE_BUILD_ARGS. Maven caching may not work properly.")
		}
		command = append(command, buildArgs)
	}
	if !ctx.Debug() {
		command = append(command, "--quiet")
	}
	ctx.ExecUser(command)

	return nil
}

func mvnInstalled(ctx *gcp.Context) bool {
	result := ctx.Exec([]string{"bash", "-c", "command -v mvn || true"})
	return result.Stdout != ""
}

// installMaven installs Maven and returns the path of the mvn binary
func installMaven(ctx *gcp.Context) (string, error) {
	mvnl := ctx.Layer(mavenLayer)

	// Check the metadata in the cache layer to determine if we need to proceed.
	var meta mavenMetadata
	ctx.ReadMetadata(mvnl, &meta)

	if mavenVersion == meta.Version {
		ctx.CacheHit(mavenLayer)
		ctx.Logf("Maven cache hit, skipping installation.")
		return filepath.Join(mvnl.Root, "bin", "mvn"), nil
	}
	ctx.CacheMiss(mavenLayer)
	ctx.ClearLayer(mvnl)

	// Download and install maven in layer.
	ctx.Logf("Installing Maven v%s", mavenVersion)
	archiveURL := fmt.Sprintf(mavenURL, mavenVersion)
	if code := ctx.HTTPStatus(archiveURL); code != http.StatusOK {
		return "", gcp.UserErrorf("Maven version %s does not exist at %s (status %d).", mavenVersion, archiveURL, code)
	}
	command := fmt.Sprintf("curl --fail --show-error --silent --location --retry 3 %s | tar xz --directory %s --strip-components=1", archiveURL, mvnl.Root)
	ctx.Exec([]string{"bash", "-c", command})

	meta.Version = mavenVersion
	ctx.WriteMetadata(mvnl, meta, layers.Cache)
	return filepath.Join(mvnl.Root, "bin", "mvn"), nil
}
<|endoftext|>


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

// The main package for the Prometheus server executable.
package main

import (
	"fmt"
	"net"
	_ "net/http/pprof" // Comment this line to disable pprof endpoint.
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/asaskevich/govalidator"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"github.com/prometheus/common/version"
	"golang.org/x/net/context"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/prometheus/prometheus/config"
	"github.com/prometheus/prometheus/notifier"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/retrieval"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/storage/remote"
	"github.com/prometheus/prometheus/storage/tsdb"
	"github.com/prometheus/prometheus/web"
)

var (
	configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: "prometheus",
		Name:      "config_last_reload_successful",
		Help:      "Whether the last configuration reload attempt was successful.",
	})
	configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: "prometheus",
		Name:      "config_last_reload_success_timestamp_seconds",
		Help:      "Timestamp of the last successful configuration reload.",
	})
)

func init() {
	prometheus.MustRegister(version.NewCollector("prometheus"))
}

func main() {
	cfg := struct {
		printVersion bool
		configFile   string

		localStoragePath string
		notifier         notifier.Options
		notifierTimeout  model.Duration
		queryEngine      promql.EngineOptions
		web              web.Options
		tsdb             tsdb.Options
		lookbackDelta    model.Duration
		webTimeout       model.Duration
		queryTimeout     model.Duration

		prometheusURL string

		logFormat string
		logLevel  string
	}{
		notifier: notifier.Options{
			Registerer: prometheus.DefaultRegisterer,
		},
	}

	a := kingpin.New(filepath.Base(os.Args[0]), "The Prometheus monitoring server")

	a.Version(version.Print("prometheus"))

	a.HelpFlag.Short('h')

	a.Flag("log.level",
		"Only log messages with the given severity or above. One of: [debug, info, warn, error, fatal]").
		Default("info").StringVar(&cfg.logLevel)

	a.Flag("log.format",
		`Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`).
		Default("logger:stderr").StringVar(&cfg.logFormat)

	a.Flag("config.file", "Prometheus configuration file path.").
		Default("prometheus.yml").StringVar(&cfg.configFile)

	a.Flag("web.listen-address", "Address to listen on for UI, API, and telemtry.").
		Default("0.0.0.0:9090").StringVar(&cfg.web.ListenAddress)

	a.Flag("web.read-timeout",
		"Maximum duration before timing out read of the request, and closing idle connections.").
		Default("5m").SetValue(&cfg.webTimeout)

	a.Flag("web.max-connections", "Maximum number of simultaneous connections.").
		Default("512").IntVar(&cfg.web.MaxConnections)

	a.Flag("web.external-url",
		"The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically.").
		PlaceHolder("<URL>").StringVar(&cfg.prometheusURL)

	a.Flag("web.route-prefix",
		"Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.").
		PlaceHolder("<path>").StringVar(&cfg.web.RoutePrefix)

	a.Flag("web.user-assets", "Path to static asset directory, available at /user.").
		PlaceHolder("<path>").StringVar(&cfg.web.UserAssetsPath)

	a.Flag("web.enable-lifecycle", "Enable shutdown and reload via HTTP request.").
		Default("false").BoolVar(&cfg.web.EnableLifecycle)

	a.Flag("web.enable-admin-api", "Enables API endpoints for admin control actions.").
		Default("false").BoolVar(&cfg.web.EnableAdminAPI)

	a.Flag("web.console.templates", "Path to the console template directory, available at /consoles.").
		Default("consoles").StringVar(&cfg.web.ConsoleTemplatesPath)

	a.Flag("web.console.libraries", "Path to the console library directory.").
		Default("console_libraries").StringVar(&cfg.web.ConsoleLibrariesPath)

	a.Flag("storage.tsdb.path", "Base path for metrics storage.").
		Default("data/").StringVar(&cfg.localStoragePath)

	a.Flag("storage.tsdb.min-block-duration", "Minimum duration of a data block before being persisted.").
		Default("2h").SetValue(&cfg.tsdb.MinBlockDuration)

	a.Flag("storage.tsdb.max-block-duration",
		"Maximum duration compacted blocks may span. (Defaults to 10% of the retention period)").
		PlaceHolder("<duration>").SetValue(&cfg.tsdb.MaxBlockDuration)

	a.Flag("storage.tsdb.retention", "How long to retain samples in the storage.").
		Default("15d").SetValue(&cfg.tsdb.Retention)

	a.Flag("storage.tsdb.no-lockfile", "Do not create lockfile in data directory.").
		Default("false").BoolVar(&cfg.tsdb.NoLockfile)

	a.Flag("alertmanager.notification-queue-capacity", "The capacity of the queue for pending alert manager notifications.").
		Default("10000").IntVar(&cfg.notifier.QueueCapacity)

	a.Flag("alertmanager.timeout", "Timeout for sending alerts to Alertmanager.").
		Default("10s").SetValue(&cfg.notifierTimeout)

	a.Flag("query.lookback-delta", "The delta difference allowed for retrieving metrics during expression evaluations.").
		Default("5m").SetValue(&cfg.lookbackDelta)

	a.Flag("query.timeout", "Maximum time a query may take before being aborted.").
		Default("2m").SetValue(&cfg.queryTimeout)

	a.Flag("query.max-concurrency", "Maximum number of queries executed concurrently.").
		Default("20").IntVar(&cfg.queryEngine.MaxConcurrentQueries)

	_, err := a.Parse(os.Args[1:])
	if err != nil {
		a.Usage(os.Args[1:])
		os.Exit(2)
	}

	cfg.web.ExternalURL, err = computeExternalURL(cfg.prometheusURL, cfg.web.ListenAddress)
	if err != nil {
		fmt.Fprintln(os.Stderr, errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL))
		os.Exit(2)
	}

	cfg.web.ReadTimeout = time.Duration(cfg.webTimeout)
	// Default -web.route-prefix to path of -web.external-url.
	if cfg.web.RoutePrefix == "" {
		cfg.web.RoutePrefix = cfg.web.ExternalURL.Path
	}
	// RoutePrefix must always be at least '/'.
	cfg.web.RoutePrefix = "/" + strings.Trim(cfg.web.RoutePrefix, "/")

	if cfg.tsdb.MaxBlockDuration == 0 {
		cfg.tsdb.MaxBlockDuration = cfg.tsdb.Retention / 10
	}

	promql.LookbackDelta = time.Duration(cfg.lookbackDelta)

	cfg.queryEngine.Timeout = time.Duration(cfg.queryTimeout)

	logger := log.NewLogger(os.Stdout)
	logger.SetLevel(cfg.logLevel)
	logger.SetFormat(cfg.logFormat)

	logger.Infoln("Starting prometheus", version.Info())
	logger.Infoln("Build context", version.BuildContext())
	logger.Infoln("Host details", Uname())

	var (
		// sampleAppender = storage.Fanout{}
		reloadables []Reloadable
	)

	// Make sure that sighup handler is registered with a redirect to the channel before the potentially
	// long and synchronous tsdb init.
	hup := make(chan os.Signal)
	hupReady := make(chan bool)
	signal.Notify(hup, syscall.SIGHUP)
	logger.Infoln("Starting tsdb")
	localStorage, err := tsdb.Open(cfg.localStoragePath, prometheus.DefaultRegisterer, &cfg.tsdb)
	if err != nil {
		log.Errorf("Opening storage failed: %s", err)
		os.Exit(1)
	}
	logger.Infoln("tsdb started")

	remoteStorage := &remote.Storage{}
	reloadables = append(reloadables, remoteStorage)
	fanoutStorage := storage.NewFanout(tsdb.Adapter(localStorage), remoteStorage)

	cfg.queryEngine.Logger = logger
	var (
		notifier       = notifier.New(&cfg.notifier, logger)
		targetManager  = retrieval.NewTargetManager(fanoutStorage, logger)
		queryEngine    = promql.NewEngine(fanoutStorage, &cfg.queryEngine)
		ctx, cancelCtx = context.WithCancel(context.Background())
	)

	ruleManager := rules.NewManager(&rules.ManagerOptions{
		Appendable:  fanoutStorage,
		Notifier:    notifier,
		QueryEngine: queryEngine,
		Context:     ctx,
		ExternalURL: cfg.web.ExternalURL,
		Logger:      logger,
	})

	cfg.web.Context = ctx
	cfg.web.Storage = localStorage
	cfg.web.QueryEngine = queryEngine
	cfg.web.TargetManager = targetManager
	cfg.web.RuleManager = ruleManager
	cfg.web.Notifier = notifier

	cfg.web.Version = &web.PrometheusVersion{
		Version:   version.Version,
		Revision:  version.Revision,
		Branch:    version.Branch,
		BuildUser: version.BuildUser,
		BuildDate: version.BuildDate,
		GoVersion: version.GoVersion,
	}

	cfg.web.Flags = map[string]string{}
	for _, f := range a.Model().Flags {
		cfg.web.Flags[f.Name] = f.Value.String()
	}

	webHandler := web.New(&cfg.web)

	reloadables = append(reloadables, targetManager, ruleManager, webHandler, notifier)

	if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
		logger.Errorf("Error loading config: %s", err)
		os.Exit(1)
	}

	// Wait for reload or termination signals. Start the handler for SIGHUP as
	// early as possible, but ignore it until we are ready to handle reloading
	// our config.
	go func() {
		<-hupReady
		for {
			select {
			case <-hup:
				if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
					logger.Errorf("Error reloading config: %s", err)
				}
			case rc := <-webHandler.Reload():
				if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
					logger.Errorf("Error reloading config: %s", err)
					rc <- err
				} else {
					rc <- nil
				}
			}
		}
	}()

	// Start all components. The order is NOT arbitrary.
	defer func() {
		if err := fanoutStorage.Close(); err != nil {
			log.Errorln("Error stopping storage:", err)
		}
	}()

	// defer remoteStorage.Stop()

	prometheus.MustRegister(configSuccess)
	prometheus.MustRegister(configSuccessTime)

	// The notifier is a dependency of the rule manager. It has to be
	// started before and torn down afterwards.
	go notifier.Run()
	defer notifier.Stop()

	go ruleManager.Run()
	defer ruleManager.Stop()

	go targetManager.Run()
	defer targetManager.Stop()

	// Shutting down the query engine before the rule manager will cause pending queries
	// to be canceled and ensures a quick shutdown of the rule manager.
	defer cancelCtx()

	errc := make(chan error)
	go func() { errc <- webHandler.Run(ctx) }()

	// Wait for reload or termination signals.
	close(hupReady) // Unblock SIGHUP handler.

	// Set web server to ready.
	webHandler.Ready()
	log.Info("Server is Ready to receive requests.")

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)
	select {
	case <-term:
		logger.Warn("Received SIGTERM, exiting gracefully...")
	case <-webHandler.Quit():
		logger.Warn("Received termination request via web service, exiting gracefully...")
	case err := <-errc:
		logger.Errorln("Error starting web server, exiting gracefully:", err)
	}

	logger.Info("See you next time!")
}

// Reloadable things can change their internal state to match a new config
// and handle failure gracefully.
type Reloadable interface {
	ApplyConfig(*config.Config) error
}

func reloadConfig(filename string, logger log.Logger, rls ...Reloadable) (err error) {
	logger.Infof("Loading configuration file %s", filename)
	defer func() {
		if err == nil {
			configSuccess.Set(1)
			configSuccessTime.Set(float64(time.Now().Unix()))
		} else {
			configSuccess.Set(0)
		}
	}()

	conf, err := config.LoadFile(filename)
	if err != nil {
		return fmt.Errorf("couldn't load configuration (--config.file=%s): %v", filename, err)
	}

	failed := false
	for _, rl := range rls {
		if err := rl.ApplyConfig(conf); err != nil {
			logger.Error("Failed to apply configuration: ", err)
			failed = true
		}
	}
	if failed {
		return fmt.Errorf("one or more errors occurred while applying the new configuration (--config.file=%s)", filename)
	}
	return nil
}

// computeExternalURL computes a sanitized external URL from a raw input. It infers unset
// URL parts from the OS and the given listen address.
func computeExternalURL(u, listenAddr string) (*url.URL, error) {
	if u == "" {
		hostname, err := os.Hostname()
		if err != nil {
			return nil, err
		}
		_, port, err := net.SplitHostPort(listenAddr)
		if err != nil {
			return nil, err
		}
		u = fmt.Sprintf("http://%s:%s/", hostname, port)
	}

	if ok := govalidator.IsURL(u); !ok {
		return nil, fmt.Errorf("invalid external URL %q", u)
	}

	eu, err := url.Parse(u)
	if err != nil {
		return nil, err
	}

	ppref := strings.TrimRight(eu.Path, "/")
	if ppref != "" && !strings.HasPrefix(ppref, "/") {
		ppref = "/" + ppref
	}
	eu.Path = ppref

	return eu, nil
}
<commit_msg>cmd/prometheus: Fix capitalisation in log line (#3123)<commit_after>// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// The main package for the Prometheus server executable.
package main

import (
	"fmt"
	"net"
	_ "net/http/pprof" // Comment this line to disable pprof endpoint.
	"net/url"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/asaskevich/govalidator"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/log"
	"github.com/prometheus/common/model"
	"github.com/prometheus/common/version"
	"golang.org/x/net/context"
	"gopkg.in/alecthomas/kingpin.v2"

	"github.com/prometheus/prometheus/config"
	"github.com/prometheus/prometheus/notifier"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/retrieval"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/storage/remote"
	"github.com/prometheus/prometheus/storage/tsdb"
	"github.com/prometheus/prometheus/web"
)

var (
	configSuccess = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: "prometheus",
		Name:      "config_last_reload_successful",
		Help:      "Whether the last configuration reload attempt was successful.",
	})
	configSuccessTime = prometheus.NewGauge(prometheus.GaugeOpts{
		Namespace: "prometheus",
		Name:      "config_last_reload_success_timestamp_seconds",
		Help:      "Timestamp of the last successful configuration reload.",
	})
)

func init() {
	prometheus.MustRegister(version.NewCollector("prometheus"))
}

func main() {
	cfg := struct {
		printVersion bool
		configFile   string

		localStoragePath string
		notifier         notifier.Options
		notifierTimeout  model.Duration
		queryEngine      promql.EngineOptions
		web              web.Options
		tsdb             tsdb.Options
		lookbackDelta    model.Duration
		webTimeout       model.Duration
		queryTimeout     model.Duration

		prometheusURL string

		logFormat string
		logLevel  string
	}{
		notifier: notifier.Options{
			Registerer: prometheus.DefaultRegisterer,
		},
	}

	a := kingpin.New(filepath.Base(os.Args[0]), "The Prometheus monitoring server")

	a.Version(version.Print("prometheus"))

	a.HelpFlag.Short('h')

	a.Flag("log.level",
		"Only log messages with the given severity or above. One of: [debug, info, warn, error, fatal]").
		Default("info").StringVar(&cfg.logLevel)

	a.Flag("log.format",
		`Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`).
		Default("logger:stderr").StringVar(&cfg.logFormat)

	a.Flag("config.file", "Prometheus configuration file path.").
		Default("prometheus.yml").StringVar(&cfg.configFile)

	a.Flag("web.listen-address", "Address to listen on for UI, API, and telemtry.").
		Default("0.0.0.0:9090").StringVar(&cfg.web.ListenAddress)

	a.Flag("web.read-timeout",
		"Maximum duration before timing out read of the request, and closing idle connections.").
		Default("5m").SetValue(&cfg.webTimeout)

	a.Flag("web.max-connections", "Maximum number of simultaneous connections.").
		Default("512").IntVar(&cfg.web.MaxConnections)

	a.Flag("web.external-url",
		"The URL under which Prometheus is externally reachable (for example, if Prometheus is served via a reverse proxy). Used for generating relative and absolute links back to Prometheus itself. If the URL has a path portion, it will be used to prefix all HTTP endpoints served by Prometheus. If omitted, relevant URL components will be derived automatically.").
		PlaceHolder("<URL>").StringVar(&cfg.prometheusURL)

	a.Flag("web.route-prefix",
		"Prefix for the internal routes of web endpoints. Defaults to path of --web.external-url.").
		PlaceHolder("<path>").StringVar(&cfg.web.RoutePrefix)

	a.Flag("web.user-assets", "Path to static asset directory, available at /user.").
		PlaceHolder("<path>").StringVar(&cfg.web.UserAssetsPath)

	a.Flag("web.enable-lifecycle", "Enable shutdown and reload via HTTP request.").
		Default("false").BoolVar(&cfg.web.EnableLifecycle)

	a.Flag("web.enable-admin-api", "Enables API endpoints for admin control actions.").
		Default("false").BoolVar(&cfg.web.EnableAdminAPI)

	a.Flag("web.console.templates", "Path to the console template directory, available at /consoles.").
		Default("consoles").StringVar(&cfg.web.ConsoleTemplatesPath)

	a.Flag("web.console.libraries", "Path to the console library directory.").
		Default("console_libraries").StringVar(&cfg.web.ConsoleLibrariesPath)

	a.Flag("storage.tsdb.path", "Base path for metrics storage.").
		Default("data/").StringVar(&cfg.localStoragePath)

	a.Flag("storage.tsdb.min-block-duration", "Minimum duration of a data block before being persisted.").
		Default("2h").SetValue(&cfg.tsdb.MinBlockDuration)

	a.Flag("storage.tsdb.max-block-duration",
		"Maximum duration compacted blocks may span. (Defaults to 10% of the retention period)").
		PlaceHolder("<duration>").SetValue(&cfg.tsdb.MaxBlockDuration)

	a.Flag("storage.tsdb.retention", "How long to retain samples in the storage.").
		Default("15d").SetValue(&cfg.tsdb.Retention)

	a.Flag("storage.tsdb.no-lockfile", "Do not create lockfile in data directory.").
		Default("false").BoolVar(&cfg.tsdb.NoLockfile)

	a.Flag("alertmanager.notification-queue-capacity", "The capacity of the queue for pending alert manager notifications.").
		Default("10000").IntVar(&cfg.notifier.QueueCapacity)

	a.Flag("alertmanager.timeout", "Timeout for sending alerts to Alertmanager.").
		Default("10s").SetValue(&cfg.notifierTimeout)

	a.Flag("query.lookback-delta", "The delta difference allowed for retrieving metrics during expression evaluations.").
		Default("5m").SetValue(&cfg.lookbackDelta)

	a.Flag("query.timeout", "Maximum time a query may take before being aborted.").
		Default("2m").SetValue(&cfg.queryTimeout)

	a.Flag("query.max-concurrency", "Maximum number of queries executed concurrently.").
		Default("20").IntVar(&cfg.queryEngine.MaxConcurrentQueries)

	_, err := a.Parse(os.Args[1:])
	if err != nil {
		a.Usage(os.Args[1:])
		os.Exit(2)
	}

	cfg.web.ExternalURL, err = computeExternalURL(cfg.prometheusURL, cfg.web.ListenAddress)
	if err != nil {
		fmt.Fprintln(os.Stderr, errors.Wrapf(err, "parse external URL %q", cfg.prometheusURL))
		os.Exit(2)
	}

	cfg.web.ReadTimeout = time.Duration(cfg.webTimeout)
	// Default -web.route-prefix to path of -web.external-url.
	if cfg.web.RoutePrefix == "" {
		cfg.web.RoutePrefix = cfg.web.ExternalURL.Path
	}
	// RoutePrefix must always be at least '/'.
	cfg.web.RoutePrefix = "/" + strings.Trim(cfg.web.RoutePrefix, "/")

	if cfg.tsdb.MaxBlockDuration == 0 {
		cfg.tsdb.MaxBlockDuration = cfg.tsdb.Retention / 10
	}

	promql.LookbackDelta = time.Duration(cfg.lookbackDelta)

	cfg.queryEngine.Timeout = time.Duration(cfg.queryTimeout)

	logger := log.NewLogger(os.Stdout)
	logger.SetLevel(cfg.logLevel)
	logger.SetFormat(cfg.logFormat)

	logger.Infoln("Starting prometheus", version.Info())
	logger.Infoln("Build context", version.BuildContext())
	logger.Infoln("Host details", Uname())

	var (
		// sampleAppender = storage.Fanout{}
		reloadables []Reloadable
	)

	// Make sure that sighup handler is registered with a redirect to the channel before the potentially
	// long and synchronous tsdb init.
	hup := make(chan os.Signal)
	hupReady := make(chan bool)
	signal.Notify(hup, syscall.SIGHUP)
	logger.Infoln("Starting tsdb")
	localStorage, err := tsdb.Open(cfg.localStoragePath, prometheus.DefaultRegisterer, &cfg.tsdb)
	if err != nil {
		log.Errorf("Opening storage failed: %s", err)
		os.Exit(1)
	}
	logger.Infoln("tsdb started")

	remoteStorage := &remote.Storage{}
	reloadables = append(reloadables, remoteStorage)
	fanoutStorage := storage.NewFanout(tsdb.Adapter(localStorage), remoteStorage)

	cfg.queryEngine.Logger = logger
	var (
		notifier       = notifier.New(&cfg.notifier, logger)
		targetManager  = retrieval.NewTargetManager(fanoutStorage, logger)
		queryEngine    = promql.NewEngine(fanoutStorage, &cfg.queryEngine)
		ctx, cancelCtx = context.WithCancel(context.Background())
	)

	ruleManager := rules.NewManager(&rules.ManagerOptions{
		Appendable:  fanoutStorage,
		Notifier:    notifier,
		QueryEngine: queryEngine,
		Context:     ctx,
		ExternalURL: cfg.web.ExternalURL,
		Logger:      logger,
	})

	cfg.web.Context = ctx
	cfg.web.Storage = localStorage
	cfg.web.QueryEngine = queryEngine
	cfg.web.TargetManager = targetManager
	cfg.web.RuleManager = ruleManager
	cfg.web.Notifier = notifier

	cfg.web.Version = &web.PrometheusVersion{
		Version:   version.Version,
		Revision:  version.Revision,
		Branch:    version.Branch,
		BuildUser: version.BuildUser,
		BuildDate: version.BuildDate,
		GoVersion: version.GoVersion,
	}

	cfg.web.Flags = map[string]string{}
	for _, f := range a.Model().Flags {
		cfg.web.Flags[f.Name] = f.Value.String()
	}

	webHandler := web.New(&cfg.web)

	reloadables = append(reloadables, targetManager, ruleManager, webHandler, notifier)

	if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
		logger.Errorf("Error loading config: %s", err)
		os.Exit(1)
	}

	// Wait for reload or termination signals. Start the handler for SIGHUP as
	// early as possible, but ignore it until we are ready to handle reloading
	// our config.
	go func() {
		<-hupReady
		for {
			select {
			case <-hup:
				if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
					logger.Errorf("Error reloading config: %s", err)
				}
			case rc := <-webHandler.Reload():
				if err := reloadConfig(cfg.configFile, logger, reloadables...); err != nil {
					logger.Errorf("Error reloading config: %s", err)
					rc <- err
				} else {
					rc <- nil
				}
			}
		}
	}()

	// Start all components. The order is NOT arbitrary.
	defer func() {
		if err := fanoutStorage.Close(); err != nil {
			log.Errorln("Error stopping storage:", err)
		}
	}()

	// defer remoteStorage.Stop()

	prometheus.MustRegister(configSuccess)
	prometheus.MustRegister(configSuccessTime)

	// The notifier is a dependency of the rule manager. It has to be
	// started before and torn down afterwards.
	go notifier.Run()
	defer notifier.Stop()

	go ruleManager.Run()
	defer ruleManager.Stop()

	go targetManager.Run()
	defer targetManager.Stop()

	// Shutting down the query engine before the rule manager will cause pending queries
	// to be canceled and ensures a quick shutdown of the rule manager.
	defer cancelCtx()

	errc := make(chan error)
	go func() { errc <- webHandler.Run(ctx) }()

	// Wait for reload or termination signals.
	close(hupReady) // Unblock SIGHUP handler.

	// Set web server to ready.
	webHandler.Ready()
	log.Info("Server is ready to receive requests.")

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)
	select {
	case <-term:
		logger.Warn("Received SIGTERM, exiting gracefully...")
	case <-webHandler.Quit():
		logger.Warn("Received termination request via web service, exiting gracefully...")
	case err := <-errc:
		logger.Errorln("Error starting web server, exiting gracefully:", err)
	}

	logger.Info("See you next time!")
}

// Reloadable things can change their internal state to match a new config
// and handle failure gracefully.
type Reloadable interface {
	ApplyConfig(*config.Config) error
}

func reloadConfig(filename string, logger log.Logger, rls ...Reloadable) (err error) {
	logger.Infof("Loading configuration file %s", filename)
	defer func() {
		if err == nil {
			configSuccess.Set(1)
			configSuccessTime.Set(float64(time.Now().Unix()))
		} else {
			configSuccess.Set(0)
		}
	}()

	conf, err := config.LoadFile(filename)
	if err != nil {
		return fmt.Errorf("couldn't load configuration (--config.file=%s): %v", filename, err)
	}

	failed := false
	for _, rl := range rls {
		if err := rl.ApplyConfig(conf); err != nil {
			logger.Error("Failed to apply configuration: ", err)
			failed = true
		}
	}
	if failed {
		return fmt.Errorf("one or more errors occurred while applying the new configuration (--config.file=%s)", filename)
	}
	return nil
}

// computeExternalURL computes a sanitized external URL from a raw input. It infers unset
// URL parts from the OS and the given listen address.
func computeExternalURL(u, listenAddr string) (*url.URL, error) {
	if u == "" {
		hostname, err := os.Hostname()
		if err != nil {
			return nil, err
		}
		_, port, err := net.SplitHostPort(listenAddr)
		if err != nil {
			return nil, err
		}
		u = fmt.Sprintf("http://%s:%s/", hostname, port)
	}

	if ok := govalidator.IsURL(u); !ok {
		return nil, fmt.Errorf("invalid external URL %q", u)
	}

	eu, err := url.Parse(u)
	if err != nil {
		return nil, err
	}

	ppref := strings.TrimRight(eu.Path, "/")
	if ppref != "" && !strings.HasPrefix(ppref, "/") {
		ppref = "/" + ppref
	}
	eu.Path = ppref

	return eu, nil
}
<|endoftext|>


<commit_before>package cmd

import (
	"github.com/Masterminds/cookoo"
	"github.com/kylelemons/go-gypsy/yaml"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// Recurse does glide installs on dependent packages.
// Recurse looks in all known packages for a glide.yaml files and installs for
// each one it finds.
func Recurse(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	if !p.Get("enable", true).(bool) {
		return nil, nil
	}
	force := p.Get("force", true).(bool)

	godeps, gpm, deleteFlatten := false, false, false
	if g, ok := p.Has("importGodeps"); ok {
		godeps = g.(bool)
	}
	if g, ok := p.Has("importGPM"); ok {
		gpm = g.(bool)
	}

	if g, ok := p.Has("deleteFlatten"); ok {
		deleteFlatten = g.(bool)
	}

	Info("Checking dependencies for updates. Godeps: %v, GPM: %v\n", godeps, gpm)
	if deleteFlatten == true {
		Info("Deleting flattened dependencies enabled\n")
	}
	conf := p.Get("conf", &Config{}).(*Config)
	vend, _ := VendorPath(c)

	return recDepResolve(conf, vend, godeps, gpm, force, deleteFlatten)
}

func recDepResolve(conf *Config, vend string, godeps, gpm, force, deleteFlatten bool) (interface{}, error) {

	Info("Inspecting %s.\n", vend)

	if len(conf.Imports) == 0 {
		Info("No imports.\n")
	}

	// Look in each package to see whether it has a glide.yaml, and no vendor/
	for _, imp := range conf.Imports {
		if imp.Flattened == true {
			continue
		}
		base := path.Join(vend, imp.Name)
		Info("Looking in %s for a glide.yaml file.\n", base)
		if !needsGlideUp(base) {
			if godeps {
				importGodep(base, imp.Name)
			}
			if gpm {
				importGPM(base, imp.Name)
			}
			if !needsGlideUp(base) {
				Info("Package %s manages its own dependencies.\n", imp.Name)
				continue
			}
		}

		if err := dependencyGlideUp(conf, base, godeps, gpm, force, deleteFlatten); err != nil {
			Warn("Failed to update dependency %s: %s", imp.Name, err)
		}
	}

	// Run `glide up`
	return nil, nil
}

func dependencyGlideUp(parentConf *Config, base string, godep, gpm, force, deleteFlatten bool) error {
	Info("Doing a glide in %s\n", base)
	//conf := new(Config)
	fname := path.Join(base, "glide.yaml")
	f, err := yaml.ReadFile(fname)
	if err != nil {
		return err
	}

	conf, err := FromYaml(f.Root)
	conf.Parent = parentConf
	if err != nil {
		return err
	}
	for _, imp := range conf.Imports {
		vdir := path.Join(base, "vendor")
		wd := path.Join(vdir, imp.Name)
		// if our root glide.yaml says to flatten this, we skip it
		if dep := conf.GetRoot().Imports.Get(imp.Name); dep != nil {
			if dep.Flatten == true {
				Info("Skipping importing %s due to flatten being set in root import glide.yaml\n", imp.Name)
				imp.Flattened = true

			} else if conf.GetRoot().Flatten == true {
				Info("Skipping importing %s due to flatten being set in root config glide.yaml\n", imp.Name)
				imp.Flattened = true
			}

			if imp.Reference != dep.Reference {
				Warn("Main vendored package %s ref (%s) is diferent from sub vendored package ref (%s)\n", imp.Name, imp.Reference, dep.Reference)
			}

			if imp.Flattened == true && deleteFlatten == true {
				if exists, _ := fileExist(wd); exists == true || true {
					remove := wd + string(os.PathSeparator)
					Warn("Removing flattened sub vendored package: %s\n", strings.TrimPrefix(remove, base))
					rerr := os.RemoveAll(remove)
					if rerr != nil {
						return rerr
					}
				}
			}
			if imp.Flattened == true {
				continue
			}
		}

		// We don't use the global var to find vendor dir name because the
		// user may mis-use that var to modify the local vendor dir, and
		// we don't want that to break the embedded vendor dirs.

		if err := ensureDir(wd); err != nil {
			Warn("Skipped getting %s (vendor/ error): %s\n", imp.Name, err)
			continue
		}

		if VcsExists(imp, wd) {
			Info("Updating project %s (%s)\n", imp.Name, wd)
			if err := VcsUpdate(imp, vdir, force); err != nil {
				// We can still go on just fine even if this fails.
				Warn("Skipped update %s: %s\n", imp.Name, err)
				continue
			}
		} else {
			Info("Importing %s to project %s\n", imp.Name, base)
			if err := VcsGet(imp, wd); err != nil {
				Warn("Skipped getting %s: %v\n", imp.Name, err)
				continue
			}
		}

		// If a revision has been set use it.
		err = VcsVersion(imp, vdir)
		if err != nil {
			Warn("Problem setting version on %s: %s\n", imp.Name, err)
		}

		//recDepResolve(conf, path.Join(wd, "vendor"))
	}
	recDepResolve(conf, path.Join(base, "vendor"), godep, gpm, force, deleteFlatten)
	return nil
}

func ensureDir(dirpath string) error {
	if fi, err := os.Stat(dirpath); err == nil && fi.IsDir() {
		return nil
	}
	return os.MkdirAll(dirpath, 0755)
}

func needsGlideUp(dir string) bool {
	stat, err := os.Stat(path.Join(dir, "glide.yaml"))
	if err != nil || stat.IsDir() {
		return false
	}

	// Should probably see if vendor is there and non-empty.

	return true
}

func importGodep(dir, pkg string) error {
	Info("Looking in %s/Godeps/ for a Godeps.json file.\n", dir)
	d, err := parseGodepGodeps(dir)
	if err != nil {
		Warn("Looking for Godeps: %s\n", err)
		return err
	}
	return quickDirtyYAMLWrite(dir, d, pkg)
}

func importGPM(dir, pkg string) error {
	d, err := parseGPMGodeps(dir)
	if err != nil {
		return err
	}
	return quickDirtyYAMLWrite(dir, d, pkg)
}

func quickDirtyYAMLWrite(dir string, d []*Dependency, pkg string) error {
	if len(d) == 0 {
		return nil
	}
	c := &Config{Name: pkg, Imports: d}
	node := c.ToYaml()
	data := yaml.Render(node)
	f := path.Join(dir, "glide.yaml")
	Info("Writing new glide.yaml file in %s\n", dir)
	return ioutil.WriteFile(f, []byte(data), 0755)
}
<commit_msg>fixed imported pkg flatten overriding global flatten<commit_after>package cmd

import (
	"github.com/Masterminds/cookoo"
	"github.com/kylelemons/go-gypsy/yaml"
	"io/ioutil"
	"os"
	"path"
	"strings"
)

// Recurse does glide installs on dependent packages.
// Recurse looks in all known packages for a glide.yaml files and installs for
// each one it finds.
func Recurse(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
	if !p.Get("enable", true).(bool) {
		return nil, nil
	}
	force := p.Get("force", true).(bool)

	godeps, gpm, deleteFlatten := false, false, false
	if g, ok := p.Has("importGodeps"); ok {
		godeps = g.(bool)
	}
	if g, ok := p.Has("importGPM"); ok {
		gpm = g.(bool)
	}

	if g, ok := p.Has("deleteFlatten"); ok {
		deleteFlatten = g.(bool)
	}

	Info("Checking dependencies for updates. Godeps: %v, GPM: %v\n", godeps, gpm)
	if deleteFlatten == true {
		Info("Deleting flattened dependencies enabled\n")
	}
	conf := p.Get("conf", &Config{}).(*Config)
	vend, _ := VendorPath(c)

	return recDepResolve(conf, vend, godeps, gpm, force, deleteFlatten)
}

func recDepResolve(conf *Config, vend string, godeps, gpm, force, deleteFlatten bool) (interface{}, error) {

	Info("Inspecting %s.\n", vend)

	if len(conf.Imports) == 0 {
		Info("No imports.\n")
	}

	// Look in each package to see whether it has a glide.yaml, and no vendor/
	for _, imp := range conf.Imports {
		if imp.Flattened == true {
			continue
		}
		base := path.Join(vend, imp.Name)
		Info("Looking in %s for a glide.yaml file.\n", base)
		if !needsGlideUp(base) {
			if godeps {
				importGodep(base, imp.Name)
			}
			if gpm {
				importGPM(base, imp.Name)
			}
			if !needsGlideUp(base) {
				Info("Package %s manages its own dependencies.\n", imp.Name)
				continue
			}
		}

		if err := dependencyGlideUp(conf, base, godeps, gpm, force, deleteFlatten); err != nil {
			Warn("Failed to update dependency %s: %s", imp.Name, err)
		}
	}

	// Run `glide up`
	return nil, nil
}

func dependencyGlideUp(parentConf *Config, base string, godep, gpm, force, deleteFlatten bool) error {
	Info("Doing a glide in %s\n", base)
	//conf := new(Config)
	fname := path.Join(base, "glide.yaml")
	f, err := yaml.ReadFile(fname)
	if err != nil {
		return err
	}

	conf, err := FromYaml(f.Root)
	conf.Parent = parentConf
	if err != nil {
		return err
	}
	for _, imp := range conf.Imports {
		vdir := path.Join(base, "vendor")
		wd := path.Join(vdir, imp.Name)
		// if our root glide.yaml says to flatten this, we skip it
		if dep := conf.GetRoot().Imports.Get(imp.Name); dep != nil {
			flatten := conf.GetRoot().Flatten
			if flatten == true && dep.Flatten == false ||
				flatten == false && dep.Flatten == true {
				flatten = dep.Flatten
			}
			if flatten == true {
				Info("Skipping importing %s due to flatten being set in root import glide.yaml\n", imp.Name)
				imp.Flattened = true
			}

			if flatten == true && imp.Reference != dep.Reference {
				Warn("Flattened package %s ref (%s) is diferent from sub vendored package ref (%s)\n", imp.Name, imp.Reference, dep.Reference)
			}

			if imp.Flattened == true && deleteFlatten == true {
				if exists, _ := fileExist(wd); exists == true || true {
					remove := wd + string(os.PathSeparator)
					Warn("Removing flattened sub vendored package: %s\n", strings.TrimPrefix(remove, base))
					rerr := os.RemoveAll(remove)
					if rerr != nil {
						return rerr
					}
				}
			}
			if imp.Flattened == true {
				continue
			}
		}

		// We don't use the global var to find vendor dir name because the
		// user may mis-use that var to modify the local vendor dir, and
		// we don't want that to break the embedded vendor dirs.

		if err := ensureDir(wd); err != nil {
			Warn("Skipped getting %s (vendor/ error): %s\n", imp.Name, err)
			continue
		}

		if VcsExists(imp, wd) {
			Info("Updating project %s (%s)\n", imp.Name, wd)
			if err := VcsUpdate(imp, vdir, force); err != nil {
				// We can still go on just fine even if this fails.
				Warn("Skipped update %s: %s\n", imp.Name, err)
				continue
			}
		} else {
			Info("Importing %s to project %s\n", imp.Name, base)
			if err := VcsGet(imp, wd); err != nil {
				Warn("Skipped getting %s: %v\n", imp.Name, err)
				continue
			}
		}

		// If a revision has been set use it.
		err = VcsVersion(imp, vdir)
		if err != nil {
			Warn("Problem setting version on %s: %s\n", imp.Name, err)
		}

		//recDepResolve(conf, path.Join(wd, "vendor"))
	}
	recDepResolve(conf, path.Join(base, "vendor"), godep, gpm, force, deleteFlatten)
	return nil
}

func ensureDir(dirpath string) error {
	if fi, err := os.Stat(dirpath); err == nil && fi.IsDir() {
		return nil
	}
	return os.MkdirAll(dirpath, 0755)
}

func needsGlideUp(dir string) bool {
	stat, err := os.Stat(path.Join(dir, "glide.yaml"))
	if err != nil || stat.IsDir() {
		return false
	}

	// Should probably see if vendor is there and non-empty.

	return true
}

func importGodep(dir, pkg string) error {
	Info("Looking in %s/Godeps/ for a Godeps.json file.\n", dir)
	d, err := parseGodepGodeps(dir)
	if err != nil {
		Warn("Looking for Godeps: %s\n", err)
		return err
	}
	return quickDirtyYAMLWrite(dir, d, pkg)
}

func importGPM(dir, pkg string) error {
	d, err := parseGPMGodeps(dir)
	if err != nil {
		return err
	}
	return quickDirtyYAMLWrite(dir, d, pkg)
}

func quickDirtyYAMLWrite(dir string, d []*Dependency, pkg string) error {
	if len(d) == 0 {
		return nil
	}
	c := &Config{Name: pkg, Imports: d}
	node := c.ToYaml()
	data := yaml.Render(node)
	f := path.Join(dir, "glide.yaml")
	Info("Writing new glide.yaml file in %s\n", dir)
	return ioutil.WriteFile(f, []byte(data), 0755)
}
<|endoftext|>


<commit_before>package main

import (
	"archive/tar"
	"context"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"

	"github.com/spf13/cobra"
)

var cmdDump = &cobra.Command{
	Use:   "dump [flags] snapshotID file",
	Short: "Print a backed-up file to stdout",
	Long: `
The "dump" command extracts a single file from a snapshot from the repository and
prints its contents to stdout.

The special snapshot "latest" can be used to use the latest snapshot in the
repository.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDump(dumpOptions, globalOptions, args)
	},
}

// DumpOptions collects all options for the dump command.
type DumpOptions struct {
	Hosts []string
	Paths []string
	Tags  restic.TagLists
}

var dumpOptions DumpOptions

func init() {
	cmdRoot.AddCommand(cmdDump)

	flags := cmdDump.Flags()
	flags.StringArrayVarP(&dumpOptions.Hosts, "host", "H", nil, `only consider snapshots for this host when the snapshot ID is "latest" (can be specified multiple times)`)
	flags.Var(&dumpOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
	flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
}

func splitPath(p string) []string {
	d, f := path.Split(p)
	if d == "" {
		return []string{f}
	}
	if d == "/" {
		return []string{d}
	}
	s := splitPath(path.Clean(d))
	return append(s, f)
}

func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string, pathToPrint string) error {

	if tree == nil {
		return fmt.Errorf("called with a nil tree")
	}
	if repo == nil {
		return fmt.Errorf("called with a nil repository")
	}
	l := len(pathComponents)
	if l == 0 {
		return fmt.Errorf("empty path components")
	}
	item := filepath.Join(prefix, pathComponents[0])
	for _, node := range tree.Nodes {
		if node.Name == pathComponents[0] || pathComponents[0] == "/" {
			switch {
			case l == 1 && node.Type == "file":
				return getNodeData(ctx, os.Stdout, repo, node)
			case l > 1 && node.Type == "dir":
				subtree, err := repo.LoadTree(ctx, *node.Subtree)
				if err != nil {
					return errors.Wrapf(err, "cannot load subtree for %q", item)
				}
				return printFromTree(ctx, subtree, repo, item, pathComponents[1:], pathToPrint)
			case node.Type == "dir":
				node.Path = pathToPrint
				return tarTree(ctx, repo, node, pathToPrint)
			case l > 1:
				return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type)
			case node.Type != "file":
				return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
			}
		}
	}
	return fmt.Errorf("path %q not found in snapshot", item)
}

func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
	ctx := gopts.ctx

	if len(args) != 2 {
		return errors.Fatal("no file and no snapshot ID specified")
	}

	snapshotIDString := args[0]
	pathToPrint := args[1]

	debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)

	splittedPath := splitPath(path.Clean(pathToPrint))

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	err = repo.LoadIndex(ctx)
	if err != nil {
		return err
	}

	var id restic.ID

	if snapshotIDString == "latest" {
		id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Hosts)
		if err != nil {
			Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Hosts:%v", err, opts.Paths, opts.Hosts)
		}
	} else {
		id, err = restic.FindSnapshot(repo, snapshotIDString)
		if err != nil {
			Exitf(1, "invalid id %q: %v", snapshotIDString, err)
		}
	}

	sn, err := restic.LoadSnapshot(gopts.ctx, repo, id)
	if err != nil {
		Exitf(2, "loading snapshot %q failed: %v", snapshotIDString, err)
	}

	tree, err := repo.LoadTree(ctx, *sn.Tree)
	if err != nil {
		Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
	}

	err = printFromTree(ctx, tree, repo, "", splittedPath, pathToPrint)
	if err != nil {
		Exitf(2, "cannot dump file: %v", err)
	}

	return nil
}

func getNodeData(ctx context.Context, output io.Writer, repo restic.Repository, node *restic.Node) error {
	var (
		buf []byte
		err error
	)
	for _, id := range node.Content {
		buf, err = repo.LoadBlob(ctx, restic.DataBlob, id, buf)
		if err != nil {
			return err
		}

		_, err = output.Write(buf)
		if err != nil {
			return errors.Wrap(err, "Write")
		}

	}
	return nil
}

func tarTree(ctx context.Context, repo restic.Repository, rootNode *restic.Node, rootPath string) error {

	if stdoutIsTerminal() {
		return fmt.Errorf("stdout is the terminal, please redirect output")
	}

	tw := tar.NewWriter(os.Stdout)
	defer tw.Close()

	// If we want to dump "/" we'll need to add the name of the first node, too
	// as it would get lost otherwise.
	if rootNode.Path == "/" {
		rootNode.Path = path.Join(rootNode.Path, rootNode.Name)
		rootPath = rootNode.Path
	}

	// we know that rootNode is a folder and walker.Walk will already process
	// the next node, so we have to tar this one first, too
	if err := tarNode(ctx, tw, rootNode, repo); err != nil {
		return err
	}

	err := walker.Walk(ctx, repo, *rootNode.Subtree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
		if err != nil {
			return false, err
		}
		if node == nil {
			return false, nil
		}

		node.Path = path.Join(rootPath, nodepath)

		if node.Type == "file" || node.Type == "symlink" || node.Type == "dir" {
			err := tarNode(ctx, tw, node, repo)
			if err != err {
				return false, err
			}
		}

		return false, nil
	})

	return err
}

func tarNode(ctx context.Context, tw *tar.Writer, node *restic.Node, repo restic.Repository) error {

	header := &tar.Header{
		Name:       node.Path,
		Size:       int64(node.Size),
		Mode:       int64(node.Mode),
		Uid:        int(node.UID),
		Gid:        int(node.GID),
		ModTime:    node.ModTime,
		AccessTime: node.AccessTime,
		ChangeTime: node.ChangeTime,
		PAXRecords: parseXattrs(node.ExtendedAttributes),
	}

	if node.Type == "symlink" {
		header.Typeflag = tar.TypeSymlink
		header.Linkname = node.LinkTarget
	}

	if node.Type == "dir" {
		header.Typeflag = tar.TypeDir
	}

	err := tw.WriteHeader(header)

	if err != nil {
		return errors.Wrap(err, "TarHeader ")
	}

	return getNodeData(ctx, tw, repo, node)

}

func parseXattrs(xattrs []restic.ExtendedAttribute) map[string]string {
	tmpMap := make(map[string]string)

	for _, attr := range xattrs {
		attrString := string(attr.Value)

		if strings.HasPrefix(attr.Name, "system.posix_acl_") {
			na := acl{}
			na.decode(attr.Value)

			if na.String() != "" {
				if strings.Contains(attr.Name, "system.posix_acl_access") {
					tmpMap["SCHILY.acl.access"] = na.String()
				} else if strings.Contains(attr.Name, "system.posix_acl_default") {
					tmpMap["SCHILY.acl.default"] = na.String()
				}
			}

		} else {
			tmpMap["SCHILY.xattr."+attr.Name] = attrString
		}
	}

	return tmpMap
}
<commit_msg>Fix error handling in dump (err != err)<commit_after>package main

import (
	"archive/tar"
	"context"
	"fmt"
	"io"
	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"

	"github.com/spf13/cobra"
)

var cmdDump = &cobra.Command{
	Use:   "dump [flags] snapshotID file",
	Short: "Print a backed-up file to stdout",
	Long: `
The "dump" command extracts a single file from a snapshot from the repository and
prints its contents to stdout.

The special snapshot "latest" can be used to use the latest snapshot in the
repository.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDump(dumpOptions, globalOptions, args)
	},
}

// DumpOptions collects all options for the dump command.
type DumpOptions struct {
	Hosts []string
	Paths []string
	Tags  restic.TagLists
}

var dumpOptions DumpOptions

func init() {
	cmdRoot.AddCommand(cmdDump)

	flags := cmdDump.Flags()
	flags.StringArrayVarP(&dumpOptions.Hosts, "host", "H", nil, `only consider snapshots for this host when the snapshot ID is "latest" (can be specified multiple times)`)
	flags.Var(&dumpOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
	flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
}

func splitPath(p string) []string {
	d, f := path.Split(p)
	if d == "" {
		return []string{f}
	}
	if d == "/" {
		return []string{d}
	}
	s := splitPath(path.Clean(d))
	return append(s, f)
}

func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string, pathToPrint string) error {

	if tree == nil {
		return fmt.Errorf("called with a nil tree")
	}
	if repo == nil {
		return fmt.Errorf("called with a nil repository")
	}
	l := len(pathComponents)
	if l == 0 {
		return fmt.Errorf("empty path components")
	}
	item := filepath.Join(prefix, pathComponents[0])
	for _, node := range tree.Nodes {
		if node.Name == pathComponents[0] || pathComponents[0] == "/" {
			switch {
			case l == 1 && node.Type == "file":
				return getNodeData(ctx, os.Stdout, repo, node)
			case l > 1 && node.Type == "dir":
				subtree, err := repo.LoadTree(ctx, *node.Subtree)
				if err != nil {
					return errors.Wrapf(err, "cannot load subtree for %q", item)
				}
				return printFromTree(ctx, subtree, repo, item, pathComponents[1:], pathToPrint)
			case node.Type == "dir":
				node.Path = pathToPrint
				return tarTree(ctx, repo, node, pathToPrint)
			case l > 1:
				return fmt.Errorf("%q should be a dir, but is a %q", item, node.Type)
			case node.Type != "file":
				return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
			}
		}
	}
	return fmt.Errorf("path %q not found in snapshot", item)
}

func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
	ctx := gopts.ctx

	if len(args) != 2 {
		return errors.Fatal("no file and no snapshot ID specified")
	}

	snapshotIDString := args[0]
	pathToPrint := args[1]

	debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)

	splittedPath := splitPath(path.Clean(pathToPrint))

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	err = repo.LoadIndex(ctx)
	if err != nil {
		return err
	}

	var id restic.ID

	if snapshotIDString == "latest" {
		id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Hosts)
		if err != nil {
			Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Hosts:%v", err, opts.Paths, opts.Hosts)
		}
	} else {
		id, err = restic.FindSnapshot(repo, snapshotIDString)
		if err != nil {
			Exitf(1, "invalid id %q: %v", snapshotIDString, err)
		}
	}

	sn, err := restic.LoadSnapshot(gopts.ctx, repo, id)
	if err != nil {
		Exitf(2, "loading snapshot %q failed: %v", snapshotIDString, err)
	}

	tree, err := repo.LoadTree(ctx, *sn.Tree)
	if err != nil {
		Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
	}

	err = printFromTree(ctx, tree, repo, "", splittedPath, pathToPrint)
	if err != nil {
		Exitf(2, "cannot dump file: %v", err)
	}

	return nil
}

func getNodeData(ctx context.Context, output io.Writer, repo restic.Repository, node *restic.Node) error {
	var (
		buf []byte
		err error
	)
	for _, id := range node.Content {
		buf, err = repo.LoadBlob(ctx, restic.DataBlob, id, buf)
		if err != nil {
			return err
		}

		_, err = output.Write(buf)
		if err != nil {
			return errors.Wrap(err, "Write")
		}

	}
	return nil
}

func tarTree(ctx context.Context, repo restic.Repository, rootNode *restic.Node, rootPath string) error {

	if stdoutIsTerminal() {
		return fmt.Errorf("stdout is the terminal, please redirect output")
	}

	tw := tar.NewWriter(os.Stdout)
	defer tw.Close()

	// If we want to dump "/" we'll need to add the name of the first node, too
	// as it would get lost otherwise.
	if rootNode.Path == "/" {
		rootNode.Path = path.Join(rootNode.Path, rootNode.Name)
		rootPath = rootNode.Path
	}

	// we know that rootNode is a folder and walker.Walk will already process
	// the next node, so we have to tar this one first, too
	if err := tarNode(ctx, tw, rootNode, repo); err != nil {
		return err
	}

	err := walker.Walk(ctx, repo, *rootNode.Subtree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
		if err != nil {
			return false, err
		}
		if node == nil {
			return false, nil
		}

		node.Path = path.Join(rootPath, nodepath)

		if node.Type == "file" || node.Type == "symlink" || node.Type == "dir" {
			err := tarNode(ctx, tw, node, repo)
			if err != nil {
				return false, err
			}
		}

		return false, nil
	})

	return err
}

func tarNode(ctx context.Context, tw *tar.Writer, node *restic.Node, repo restic.Repository) error {

	header := &tar.Header{
		Name:       node.Path,
		Size:       int64(node.Size),
		Mode:       int64(node.Mode),
		Uid:        int(node.UID),
		Gid:        int(node.GID),
		ModTime:    node.ModTime,
		AccessTime: node.AccessTime,
		ChangeTime: node.ChangeTime,
		PAXRecords: parseXattrs(node.ExtendedAttributes),
	}

	if node.Type == "symlink" {
		header.Typeflag = tar.TypeSymlink
		header.Linkname = node.LinkTarget
	}

	if node.Type == "dir" {
		header.Typeflag = tar.TypeDir
	}

	err := tw.WriteHeader(header)

	if err != nil {
		return errors.Wrap(err, "TarHeader ")
	}

	return getNodeData(ctx, tw, repo, node)

}

func parseXattrs(xattrs []restic.ExtendedAttribute) map[string]string {
	tmpMap := make(map[string]string)

	for _, attr := range xattrs {
		attrString := string(attr.Value)

		if strings.HasPrefix(attr.Name, "system.posix_acl_") {
			na := acl{}
			na.decode(attr.Value)

			if na.String() != "" {
				if strings.Contains(attr.Name, "system.posix_acl_access") {
					tmpMap["SCHILY.acl.access"] = na.String()
				} else if strings.Contains(attr.Name, "system.posix_acl_default") {
					tmpMap["SCHILY.acl.default"] = na.String()
				}
			}

		} else {
			tmpMap["SCHILY.xattr."+attr.Name] = attrString
		}
	}

	return tmpMap
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"

	"github.com/spf13/cobra"
)

var cmdDump = &cobra.Command{
	Use:   "dump [flags] snapshotID file",
	Short: "Print a backed-up file to stdout",
	Long: `
The "dump" command extracts a single file from a snapshot from the repository and
prints its contents to stdout.

The special snapshot "latest" can be used to use the latest snapshot in the
repository.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDump(dumpOptions, globalOptions, args)
	},
}

// DumpOptions collects all options for the dump command.
type DumpOptions struct {
	Host  string
	Paths []string
	Tags  restic.TagLists
}

var dumpOptions DumpOptions

func init() {
	cmdRoot.AddCommand(cmdDump)

	flags := cmdDump.Flags()
	flags.StringVarP(&dumpOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`)
	flags.Var(&dumpOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
	flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
}

func splitPath(path string) []string {
	d, f := filepath.Split(path)
	if d == "" {
		return []string{f}
	}
	s := splitPath(filepath.Clean(d))
	return append(s, f)
}

func dumpNode(ctx context.Context, repo restic.Repository, node *restic.Node) error {
	var buf []byte
	for _, id := range node.Content {
		size, err := repo.LookupBlobSize(id, restic.DataBlob)
		if err != nil {
			return err
		}

		buf = buf[:cap(buf)]
		if len(buf) < restic.CiphertextLength(int(size)) {
			buf = restic.NewBlobBuffer(int(size))
		}

		n, err := repo.LoadBlob(ctx, restic.DataBlob, id, buf)
		if err != nil {
			return err
		}
		buf = buf[:n]

		_, err = os.Stdout.Write(buf)
		if err != nil {
			return errors.Wrap(err, "Write")
		}
	}
	return nil
}

func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string) error {
	if tree == nil {
		return fmt.Errorf("called with a nil tree")
	}
	if repo == nil {
		return fmt.Errorf("called with a nil repository")
	}
	l := len(pathComponents)
	if l == 0 {
		return fmt.Errorf("empty path components")
	}
	item := filepath.Join(prefix, pathComponents[0])
	for _, node := range tree.Nodes {
		if node.Name == pathComponents[0] {
			switch {
			case l == 1 && node.Type == "file":
				return dumpNode(ctx, repo, node)
			case l > 1 && node.Type == "dir":
				subtree, err := repo.LoadTree(ctx, *node.Subtree)
				if err != nil {
					return errors.Wrapf(err, "cannot load subtree for %q", item)
				}
				return printFromTree(ctx, subtree, repo, item, pathComponents[1:])
			case l > 1:
				return fmt.Errorf("%q should be a dir, but s a %q", item, node.Type)
			case node.Type != "file":
				return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
			}
		}
	}
	return fmt.Errorf("path %q not found in snapshot", item)
}

func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
	ctx := gopts.ctx

	if len(args) != 2 {
		return errors.Fatal("no file and no snapshot ID specified")
	}

	snapshotIDString := args[0]
	pathToPrint := args[1]

	debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)

	splittedPath := splitPath(pathToPrint)

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	err = repo.LoadIndex(ctx)
	if err != nil {
		return err
	}

	var id restic.ID

	if snapshotIDString == "latest" {
		id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host)
		if err != nil {
			Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host)
		}
	} else {
		id, err = restic.FindSnapshot(repo, snapshotIDString)
		if err != nil {
			Exitf(1, "invalid id %q: %v", snapshotIDString, err)
		}
	}

	sn, err := restic.LoadSnapshot(context.TODO(), repo, id)
	if err != nil {
		Exitf(2, "loading snapshot %q failed: %v", snapshotIDString, err)
	}

	tree, err := repo.LoadTree(ctx, *sn.Tree)
	if err != nil {
		Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
	}

	err = printFromTree(ctx, tree, repo, "", splittedPath)
	if err != nil {
		Exitf(2, "cannot dump file: %v", err)
	}

	return nil
}
<commit_msg>dump: Allow absolute paths<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/restic/restic/internal/debug"
	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/restic"

	"github.com/spf13/cobra"
)

var cmdDump = &cobra.Command{
	Use:   "dump [flags] snapshotID file",
	Short: "Print a backed-up file to stdout",
	Long: `
The "dump" command extracts a single file from a snapshot from the repository and
prints its contents to stdout.

The special snapshot "latest" can be used to use the latest snapshot in the
repository.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runDump(dumpOptions, globalOptions, args)
	},
}

// DumpOptions collects all options for the dump command.
type DumpOptions struct {
	Host  string
	Paths []string
	Tags  restic.TagLists
}

var dumpOptions DumpOptions

func init() {
	cmdRoot.AddCommand(cmdDump)

	flags := cmdDump.Flags()
	flags.StringVarP(&dumpOptions.Host, "host", "H", "", `only consider snapshots for this host when the snapshot ID is "latest"`)
	flags.Var(&dumpOptions.Tags, "tag", "only consider snapshots which include this `taglist` for snapshot ID \"latest\"")
	flags.StringArrayVar(&dumpOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path` for snapshot ID \"latest\"")
}

func splitPath(path string) []string {
	d, f := filepath.Split(path)
	if d == "" || d == "/" {
		return []string{f}
	}
	s := splitPath(filepath.Clean(d))
	return append(s, f)
}

func dumpNode(ctx context.Context, repo restic.Repository, node *restic.Node) error {
	var buf []byte
	for _, id := range node.Content {
		size, err := repo.LookupBlobSize(id, restic.DataBlob)
		if err != nil {
			return err
		}

		buf = buf[:cap(buf)]
		if len(buf) < restic.CiphertextLength(int(size)) {
			buf = restic.NewBlobBuffer(int(size))
		}

		n, err := repo.LoadBlob(ctx, restic.DataBlob, id, buf)
		if err != nil {
			return err
		}
		buf = buf[:n]

		_, err = os.Stdout.Write(buf)
		if err != nil {
			return errors.Wrap(err, "Write")
		}
	}
	return nil
}

func printFromTree(ctx context.Context, tree *restic.Tree, repo restic.Repository, prefix string, pathComponents []string) error {
	if tree == nil {
		return fmt.Errorf("called with a nil tree")
	}
	if repo == nil {
		return fmt.Errorf("called with a nil repository")
	}
	l := len(pathComponents)
	if l == 0 {
		return fmt.Errorf("empty path components")
	}
	item := filepath.Join(prefix, pathComponents[0])
	for _, node := range tree.Nodes {
		if node.Name == pathComponents[0] {
			switch {
			case l == 1 && node.Type == "file":
				return dumpNode(ctx, repo, node)
			case l > 1 && node.Type == "dir":
				subtree, err := repo.LoadTree(ctx, *node.Subtree)
				if err != nil {
					return errors.Wrapf(err, "cannot load subtree for %q", item)
				}
				return printFromTree(ctx, subtree, repo, item, pathComponents[1:])
			case l > 1:
				return fmt.Errorf("%q should be a dir, but s a %q", item, node.Type)
			case node.Type != "file":
				return fmt.Errorf("%q should be a file, but is a %q", item, node.Type)
			}
		}
	}
	return fmt.Errorf("path %q not found in snapshot", item)
}

func runDump(opts DumpOptions, gopts GlobalOptions, args []string) error {
	ctx := gopts.ctx

	if len(args) != 2 {
		return errors.Fatal("no file and no snapshot ID specified")
	}

	snapshotIDString := args[0]
	pathToPrint := args[1]

	debug.Log("dump file %q from %q", pathToPrint, snapshotIDString)

	splittedPath := splitPath(pathToPrint)

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if !gopts.NoLock {
		lock, err := lockRepo(repo)
		defer unlockRepo(lock)
		if err != nil {
			return err
		}
	}

	err = repo.LoadIndex(ctx)
	if err != nil {
		return err
	}

	var id restic.ID

	if snapshotIDString == "latest" {
		id, err = restic.FindLatestSnapshot(ctx, repo, opts.Paths, opts.Tags, opts.Host)
		if err != nil {
			Exitf(1, "latest snapshot for criteria not found: %v Paths:%v Host:%v", err, opts.Paths, opts.Host)
		}
	} else {
		id, err = restic.FindSnapshot(repo, snapshotIDString)
		if err != nil {
			Exitf(1, "invalid id %q: %v", snapshotIDString, err)
		}
	}

	sn, err := restic.LoadSnapshot(context.TODO(), repo, id)
	if err != nil {
		Exitf(2, "loading snapshot %q failed: %v", snapshotIDString, err)
	}

	tree, err := repo.LoadTree(ctx, *sn.Tree)
	if err != nil {
		Exitf(2, "loading tree for snapshot %q failed: %v", snapshotIDString, err)
	}

	err = printFromTree(ctx, tree, repo, "", splittedPath)
	if err != nil {
		Exitf(2, "cannot dump file: %v", err)
	}

	return nil
}
<|endoftext|>