<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	"golang.org/x/crypto/ssh"
)

// A project is made of project fields which has a program on it.
type program struct {
	setup              []string
	postUpdateFilename string
}

type projectField struct {
	name, label, inputQuestion, errorMsg, validationMsg string
	program                                             program
}

type project struct {
	projectname, hostname, pwd, port, typ projectField
}

var postUpdateContent string

func main() {

	// Initialization
	project := new(project)

	// Let's build our project!
	project.assemblyLine()

	// SSH connection config
	config := &ssh.ClientConfig{
		User: project.projectname.name,
		Auth: []ssh.AuthMethod{
			ssh.Password(project.pwd.name),
		},
	}

	// Now we need to know which instalation we going to make.
	// And once we get to know it, let's load the setup with
	// the aproppriate set of files and commands.

	// var yiiSteps, wpSteps []string
	commonSteps := []string{
		"echo -e '[User]\nname = Pipi, server girl' > .gitconfig",
		"cd ~/www/www/ && git init",
		"cd ~/www/www/ && git add . ",
		"cd ~/www/www/ && git commit -m 'on the beginning was the commit'",
		"cd ~/private/ && mkdir repos && cd repos && mkdir " + project.projectname.name + "_hub.git && cd " + project.projectname.name + "_hub.git && git --bare init",
		"cd ~/www/www && git remote add hub ~/private/repos/" + project.projectname.name + "_hub.git && git push hub master",
		"post-update configuration",
	}

	if project.typ.name == "Yii" {

		// Loading common steps into the selected setup
		yiiSteps := commonSteps
		project.typ.program.setup = yiiSteps
		project.typ.program.postUpdateFilename = "post-update-yii"

	} else {

		// Loading common steps into the selected setup
		wpSteps := commonSteps
		project.typ.program.setup = wpSteps
		project.typ.program.postUpdateFilename = "post-update-wp"
	}

	project.connect(config)
}

func (p *project) assemblyLine() {
	// project name
	p.projectname.inputQuestion = "project name: "
	p.projectname.label = "projectname"
	p.projectname.errorMsg = "error getting the project's name: "
	p.projectname.validationMsg = "make sure you type a valid name for your project (3 to 20 characters)."
	ask4Input(&p.projectname)

	// Hostname
	p.hostname.inputQuestion = "hostname: "
	p.hostname.label = "hostname"
	p.hostname.errorMsg = "error getting the project's hostname: "
	p.hostname.validationMsg = "make sure you type a valid hostname for your project. it must contain '.com', '.pt' or '.org', for example.)."
	ask4Input(&p.hostname)

	// Password
	p.pwd.inputQuestion = "password: "
	p.pwd.label = "pwd"
	p.pwd.errorMsg = "error getting the project's password: "
	p.pwd.validationMsg = "type a valid password. It must contain at least 6 digits"
	ask4Input(&p.pwd)

	// Port
	p.port.inputQuestion = "port (default 22): "
	p.port.label = "port"
	p.port.errorMsg = "error getting the project's port"
	p.port.validationMsg = "only digits allowed. min 0, max 999."
	ask4Input(&p.port)

	// Type
	p.typ.inputQuestion = "project type [1]yii [2]wp or gohugo: "
	p.typ.label = "type"
	p.typ.errorMsg = "error getting the project's type"
	p.typ.validationMsg = "pay attention to the options"
	ask4Input(&p.typ)
}

// Takes the assemblyLine's data and mount the prompt for the user.
func ask4Input(field *projectField) {
	fmt.Print(field.inputQuestion)

	var input string
	_, err := fmt.Scanln(&input)

	// The port admits empty string as user input. Setting the default value of "22".
	if err != nil && err.Error() == "unexpected newline" && field.label != "port" {
		ask4Input(field)
	} else if err != nil && err.Error() == "unexpected newline" {
		input = "22"
		checkInput(field, input)
	} else if err != nil {
		log.Fatal(field.errorMsg, err)
	}

	// After we've got the input we must check if it's valid.
	checkInput(field, input)
}

// A simple error checker.
func checkError(msg string, err error) {
	if err != nil {
		log.Fatal(msg, err.Error())
	}
}

// Check invalid parameters on the user input.
func checkInput(field *projectField, input string) {

	switch inputLength := len(input); field.label {
	case "projectname":
		if inputLength > 20 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "hostname":
		if inputLength <= 5 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "pwd":
		if inputLength <= 6 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "port":
		if inputLength == 0 {
			input = "22"
		} else if inputLength > 3 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "type":
		if input != "1" && input != "2" {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		} else if input == "1" {
			input = "Yii"
		} else if input == "2" {
			input = "WP"
		}
	}
	field.name = input
}

// Creates a ssh connection between the local machine and the remote server.
func (p *project) connect(config *ssh.ClientConfig) {

	log.Printf("Trying connection...\n")

	conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", p.hostname.name, p.port.name), config)
	checkError("Failed to dial: ", err)
	log.Printf("Connection established.\n")

	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	// Loops over the slice of commands to be executed on the remote.
	for step := range p.typ.program.setup {

		if p.typ.program.setup[step] == "post-update configuration" {
			p.secureCopy(conn)
		}

		p.install(step, conn)
	}
}

func (p *project) install(step int, conn *ssh.Client) {

	// Git and some other programs can send us an unsuccessful exit (< 0)
	// even if the command was successfully executed on the remote shell.
	// On these cases, we want to ignore those errors and move onto the next step.
	ignoredError := "Reason was:  ()"

	// Creates a session over the ssh connection to execute the commands
	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	var stdoutBuf bytes.Buffer
	session.Stdout = &stdoutBuf

	log.Printf("Executing command: %s", p.typ.program.setup[step])

	err = session.Run(p.typ.program.setup[step])

	if err != nil && !strings.Contains(err.Error(), ignoredError) {
		log.Printf("Command '%s' failed on execution", p.typ.program.setup[step])
		log.Fatal("Error on command execution: ", err.Error())
	}
}

func readFile(file string) string {
	data, err := ioutil.ReadFile(file)
	checkError("Error on reading file.", err)
	return string(data[:len(data)])
}

// Secure Copy a file from local machine to remote host.
func (p *project) secureCopy(conn *ssh.Client) {

	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	var stdoutBuf bytes.Buffer
	session.Stdout = &stdoutBuf

	go func() {
		w, _ := session.StdinPipe()
		defer w.Close()
		content := readFile(p.typ.program.postUpdateFilename)
		fmt.Fprintln(w, "C0644", len(content), "post-update")
		fmt.Fprint(w, content)
		fmt.Fprint(w, "\x00")
	}()

	if err := session.Run("scp -qrt ~/private/repos/" + p.projectname.name + "_hub.git/hooks"); err != nil {
		log.Fatal("Failed to run SCP: " + err.Error())
	}
}
<commit_msg>chmod on post-update<commit_after>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"
	"strings"

	"golang.org/x/crypto/ssh"
)

// A project is made of project fields which has a program on it.
type program struct {
	setup              []string
	postUpdateFilename string
}

type projectField struct {
	name, label, inputQuestion, errorMsg, validationMsg string
	program                                             program
}

type project struct {
	projectname, hostname, pwd, port, typ projectField
}

var postUpdateContent string

func main() {

	// Initialization
	project := new(project)

	// Let's build our project!
	project.assemblyLine()

	// SSH connection config
	config := &ssh.ClientConfig{
		User: project.projectname.name,
		Auth: []ssh.AuthMethod{
			ssh.Password(project.pwd.name),
		},
	}

	// Now we need to know which instalation we going to make.
	// And once we get to know it, let's load the setup with
	// the aproppriate set of files and commands.

	// var yiiSteps, wpSteps []string
	commonSteps := []string{
		"echo -e '[User]\nname = Pipi, server girl' > .gitconfig",
		"cd ~/www/www/ && git init",
		"cd ~/www/www/ && git add . ",
		"cd ~/www/www/ && git commit -m 'on the beginning was the commit'",
		"cd ~/private/ && mkdir repos && cd repos && mkdir " + project.projectname.name + "_hub.git && cd " + project.projectname.name + "_hub.git && git --bare init",
		"cd ~/www/www && git remote add hub ~/private/repos/" + project.projectname.name + "_hub.git && git push hub master",
		"post-update configuration",
		"cd ~/www/www && git remote add hub ~/private/repos/" + project.projectname.name + "_hub.git/hooks && chmod 755 post-update",
	}

	if project.typ.name == "Yii" {

		// Loading common steps into the selected setup
		yiiSteps := commonSteps
		project.typ.program.setup = yiiSteps
		project.typ.program.postUpdateFilename = "post-update-yii"

	} else {

		// Loading common steps into the selected setup
		wpSteps := commonSteps
		project.typ.program.setup = wpSteps
		project.typ.program.postUpdateFilename = "post-update-wp"
	}

	project.connect(config)
}

func (p *project) assemblyLine() {
	// project name
	p.projectname.inputQuestion = "project name: "
	p.projectname.label = "projectname"
	p.projectname.errorMsg = "error getting the project's name: "
	p.projectname.validationMsg = "make sure you type a valid name for your project (3 to 20 characters)."
	ask4Input(&p.projectname)

	// Hostname
	p.hostname.inputQuestion = "hostname: "
	p.hostname.label = "hostname"
	p.hostname.errorMsg = "error getting the project's hostname: "
	p.hostname.validationMsg = "make sure you type a valid hostname for your project. it must contain '.com', '.pt' or '.org', for example.)."
	ask4Input(&p.hostname)

	// Password
	p.pwd.inputQuestion = "password: "
	p.pwd.label = "pwd"
	p.pwd.errorMsg = "error getting the project's password: "
	p.pwd.validationMsg = "type a valid password. It must contain at least 6 digits"
	ask4Input(&p.pwd)

	// Port
	p.port.inputQuestion = "port (default 22): "
	p.port.label = "port"
	p.port.errorMsg = "error getting the project's port"
	p.port.validationMsg = "only digits allowed. min 0, max 999."
	ask4Input(&p.port)

	// Type
	p.typ.inputQuestion = "project type [1]yii [2]wp or gohugo: "
	p.typ.label = "type"
	p.typ.errorMsg = "error getting the project's type"
	p.typ.validationMsg = "pay attention to the options"
	ask4Input(&p.typ)
}

// Takes the assemblyLine's data and mount the prompt for the user.
func ask4Input(field *projectField) {
	fmt.Print(field.inputQuestion)

	var input string
	_, err := fmt.Scanln(&input)

	// The port admits empty string as user input. Setting the default value of "22".
	if err != nil && err.Error() == "unexpected newline" && field.label != "port" {
		ask4Input(field)
	} else if err != nil && err.Error() == "unexpected newline" {
		input = "22"
		checkInput(field, input)
	} else if err != nil {
		log.Fatal(field.errorMsg, err)
	}

	// After we've got the input we must check if it's valid.
	checkInput(field, input)
}

// A simple error checker.
func checkError(msg string, err error) {
	if err != nil {
		log.Fatal(msg, err.Error())
	}
}

// Check invalid parameters on the user input.
func checkInput(field *projectField, input string) {

	switch inputLength := len(input); field.label {
	case "projectname":
		if inputLength > 20 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "hostname":
		if inputLength <= 5 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "pwd":
		if inputLength <= 6 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "port":
		if inputLength == 0 {
			input = "22"
		} else if inputLength > 3 {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		}
	case "type":
		if input != "1" && input != "2" {
			fmt.Println(field.validationMsg)
			ask4Input(field)
		} else if input == "1" {
			input = "Yii"
		} else if input == "2" {
			input = "WP"
		}
	}
	field.name = input
}

// Creates a ssh connection between the local machine and the remote server.
func (p *project) connect(config *ssh.ClientConfig) {

	log.Printf("Trying connection...\n")

	conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%s", p.hostname.name, p.port.name), config)
	checkError("Failed to dial: ", err)
	log.Printf("Connection established.\n")

	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	// Loops over the slice of commands to be executed on the remote.
	for step := range p.typ.program.setup {

		if p.typ.program.setup[step] == "post-update configuration" {
			p.secureCopy(conn)
		}

		p.install(step, conn)
	}
}

func (p *project) install(step int, conn *ssh.Client) {

	// Git and some other programs can send us an unsuccessful exit (< 0)
	// even if the command was successfully executed on the remote shell.
	// On these cases, we want to ignore those errors and move onto the next step.
	ignoredError := "Reason was:  ()"

	// Creates a session over the ssh connection to execute the commands
	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	var stdoutBuf bytes.Buffer
	session.Stdout = &stdoutBuf

	log.Printf("Executing command: %s", p.typ.program.setup[step])

	err = session.Run(p.typ.program.setup[step])

	if err != nil && !strings.Contains(err.Error(), ignoredError) {
		log.Printf("Command '%s' failed on execution", p.typ.program.setup[step])
		log.Fatal("Error on command execution: ", err.Error())
	}
}

func readFile(file string) string {
	data, err := ioutil.ReadFile(file)
	checkError("Error on reading file.", err)
	return string(data[:len(data)])
}

// Secure Copy a file from local machine to remote host.
func (p *project) secureCopy(conn *ssh.Client) {

	session, err := conn.NewSession()
	checkError("Failed to build session: ", err)
	defer session.Close()

	var stdoutBuf bytes.Buffer
	session.Stdout = &stdoutBuf

	go func() {
		w, _ := session.StdinPipe()
		defer w.Close()
		content := readFile(p.typ.program.postUpdateFilename)
		fmt.Fprintln(w, "C0644", len(content), "post-update")
		fmt.Fprint(w, content)
		fmt.Fprint(w, "\x00")
	}()

	if err := session.Run("scp -qrt ~/private/repos/" + p.projectname.name + "_hub.git/hooks"); err != nil {
		log.Fatal("Failed to run SCP: " + err.Error())
	}
}
<|endoftext|>


<commit_before>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package apiserver_test

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

	"github.com/juju/juju/state"
	"github.com/juju/juju/state/apiserver"
	"github.com/juju/utils"
	gc "launchpad.net/gocheck"
)

type backupSuite struct {
	authHttpSuite
	tempDir string
}

var _ = gc.Suite(&backupSuite{})

func (s *backupSuite) backupURL(c *gc.C) string {
	uri := s.baseURL(c)
	uri.Path += "/backup"
	return uri.String()
}

func (s *backupSuite) TestRequiresAuth(c *gc.C) {
	resp, err := s.sendRequest(c, "", "", "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized")
}

func (s *backupSuite) TestRequiresPOST(c *gc.C) {
	resp, err := s.authRequest(c, "PUT", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`)

	resp, err = s.authRequest(c, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "GET"`)
}

func (s *backupSuite) TestAuthRequiresUser(c *gc.C) {
	// Add a machine and try to login.
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = machine.SetProvisioned("foo", "fake_nonce", nil)
	c.Assert(err, gc.IsNil)
	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = machine.SetPassword(password)
	c.Assert(err, gc.IsNil)

	resp, err := s.sendRequest(c, machine.Tag(), password, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized")

	// Now try a user login.
	resp, err = s.authRequest(c, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "GET"`)
}

func (s *backupSuite) TestBackupCalledAndFileServed(c *gc.C) {
	testBackup := func(tempDir string) (string, string, error) {
		s.tempDir = tempDir
		backupFilePath := filepath.Join(tempDir, "testBackupFile")
		file, err := os.Create(backupFilePath)
		if err != nil {
			return "", "", err
		}
		file.Write([]byte("foobarbam"))
		file.Close()
		return backupFilePath, "some-sha", nil
	}
	s.PatchValue(&apiserver.DoBackup, testBackup)

	_, err := s.authRequest(c, "POST", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
}
<commit_msg>Fleshed out download test<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package apiserver_test

import (
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/juju/juju/state"
	"github.com/juju/juju/state/apiserver"
	jc "github.com/juju/testing/checkers"
	"github.com/juju/utils"
	gc "launchpad.net/gocheck"
)

type backupSuite struct {
	authHttpSuite
	tempDir string
}

var _ = gc.Suite(&backupSuite{})

func (s *backupSuite) backupURL(c *gc.C) string {
	uri := s.baseURL(c)
	uri.Path += "/backup"
	return uri.String()
}

func (s *backupSuite) TestRequiresAuth(c *gc.C) {
	resp, err := s.sendRequest(c, "", "", "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized")
}

func (s *backupSuite) TestRequiresPOST(c *gc.C) {
	resp, err := s.authRequest(c, "PUT", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "PUT"`)

	resp, err = s.authRequest(c, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "GET"`)
}

func (s *backupSuite) TestAuthRequiresUser(c *gc.C) {
	// Add a machine and try to login.
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	err = machine.SetProvisioned("foo", "fake_nonce", nil)
	c.Assert(err, gc.IsNil)
	password, err := utils.RandomPassword()
	c.Assert(err, gc.IsNil)
	err = machine.SetPassword(password)
	c.Assert(err, gc.IsNil)

	resp, err := s.sendRequest(c, machine.Tag(), password, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusUnauthorized, "unauthorized")

	// Now try a user login.
	resp, err = s.authRequest(c, "GET", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	s.assertErrorResponse(c, resp, http.StatusMethodNotAllowed, `unsupported method: "GET"`)
}

func (s *backupSuite) TestBackupCalledAndFileServed(c *gc.C) {
	testBackup := func(tempDir string) (string, string, error) {
		s.tempDir = tempDir
		backupFilePath := filepath.Join(tempDir, "testBackupFile")
		file, err := os.Create(backupFilePath)
		if err != nil {
			return "", "", err
		}
		file.Write([]byte("foobarbam"))
		file.Close()
		return backupFilePath, "some-sha", nil
	}
	s.PatchValue(&apiserver.DoBackup, testBackup)

	resp, err := s.authRequest(c, "POST", s.backupURL(c), "", nil)
	c.Assert(err, gc.IsNil)
	defer resp.Body.Close()

	c.Assert(s.tempDir, gc.NotNil)
	_, err = os.Stat(s.tempDir)
	c.Assert(os.IsNotExist(err), jc.IsTrue)

	c.Assert(resp.StatusCode, gc.Equals, 200)
	//	c.Assert(resp.Header["X-Content-SHA"], gc.Equals, "some-sha")
	//c.Assert(resp.Header["Content-Type"], gc.Equals, "application/octet-stream")

	body, _ := ioutil.ReadAll(resp.Body)
	c.Assert(body, jc.DeepEquals, []byte("foobarbam"))
}
<|endoftext|>


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

package longops

import (
	"context"
	"fmt"
	"time"

	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	gerritpb "go.chromium.org/luci/common/proto/gerrit"
	"go.chromium.org/luci/common/retry/transient"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/gae/service/datastore"
	"go.chromium.org/luci/grpc/grpcutil"

	"go.chromium.org/luci/cv/internal/changelist"
	"go.chromium.org/luci/cv/internal/common"
	"go.chromium.org/luci/cv/internal/configs/prjcfg"
	"go.chromium.org/luci/cv/internal/gerrit"
	"go.chromium.org/luci/cv/internal/gerrit/botdata"
	"go.chromium.org/luci/cv/internal/run"
	"go.chromium.org/luci/cv/internal/run/eventpb"
	"go.chromium.org/luci/cv/internal/usertext"
)

// staleCLAgeThreshold controls when CV should fetch latest Gerrit info before
// trying to post the starting message.
//
// Large values increase the chance of duplicate starting messages,
// while low values increase load on Gerrit.
const staleCLAgeThreshold = 10 * time.Second

// PostStartMessageOp posts a start message on each of the Run CLs.
//
// PostStartMessageOp is a single-use object.
type PostStartMessageOp struct {
	// All public fields must be set.

	*Base
	GFactory gerrit.Factory
	Env      *common.Env

	// These private fields are set internally as implementation details.

	rcls       []*run.RunCL
	cfg        *prjcfg.ConfigGroup
	botdataCLs []botdata.ChangeID
}

// Do actually posts the start messages.
func (op *PostStartMessageOp) Do(ctx context.Context) (*eventpb.LongOpCompleted, error) {
	op.assertCalledOnce()

	if op.IsCancelRequested() {
		return &eventpb.LongOpCompleted{Status: eventpb.LongOpCompleted_CANCELLED}, nil
	}

	if err := op.prepare(ctx); err != nil {
		return nil, err
	}

	errs := make(errors.MultiError, len(op.rcls))
	poolError := parallel.WorkPool(min(len(op.rcls), 8), func(work chan<- func() error) {
		for i, rcl := range op.rcls {
			i, rcl := i, rcl
			work <- func() error {
				// all errors will be aggregated across all CLs below.
				errs[i] = op.doCL(ctx, rcl)
				return nil
			}
		}
	})
	if poolError != nil {
		panic(fmt.Errorf("unexpected WorkPool error %s", poolError))
	}

	// Aggregate all errors into the final result.
	psm := &eventpb.LongOpCompleted_PostStartMessage{}
	result := &eventpb.LongOpCompleted{
		Status: eventpb.LongOpCompleted_SUCCEEDED, // be hopeful by default
		Result: &eventpb.LongOpCompleted_PostStartMessage_{
			PostStartMessage: psm,
		},
	}
	var firstError error
	for i, rcl := range op.rcls {
		switch err := errs[i]; {
		case err == nil:
			psm.Posted = append(psm.Posted, int64(rcl.ID))
		case errors.Unwrap(err) == errCancelHonored:
			result.Status = eventpb.LongOpCompleted_CANCELLED
			errs[i] = nil
		case !transient.Tag.In(err):
			if psm.GetPermanentErrors() == nil {
				psm.PermanentErrors = make(map[int64]string, 1)
				firstError = err
			}
			psm.GetPermanentErrors()[int64(rcl.ID)] = err.Error()
			logging.Errorf(ctx, "failed to post start message on CL %d %q: %s", rcl.ID, rcl.ExternalID, err)
		case firstError == nil:
			// First transient error.
			firstError = err
		}
	}

	switch {
	case len(psm.GetPermanentErrors()) > 0:
		result.Status = eventpb.LongOpCompleted_FAILED
		return result, firstError
	case firstError != nil:
		// Must be a transient error, expect a retry.
		return nil, firstError
	default:
		return result, nil
	}
}

func (op *PostStartMessageOp) prepare(ctx context.Context) error {
	eg, eCtx := errgroup.WithContext(ctx)
	eg.Go(func() (err error) {
		op.rcls, err = run.LoadRunCLs(eCtx, op.Run.ID, op.Run.CLs)
		return
	})
	eg.Go(func() (err error) {
		op.cfg, err = prjcfg.GetConfigGroup(eCtx, op.Run.ID.LUCIProject(), op.Run.ConfigGroupID)
		return
	})
	if err := eg.Wait(); err != nil {
		return err
	}

	op.botdataCLs = make([]botdata.ChangeID, len(op.rcls))
	for i, rcl := range op.rcls {
		op.botdataCLs[i].Host = rcl.Detail.GetGerrit().GetHost()
		op.botdataCLs[i].Number = rcl.Detail.GetGerrit().GetInfo().GetNumber()
	}
	return nil
}

func (op *PostStartMessageOp) doCL(ctx context.Context, rcl *run.RunCL) error {
	if rcl.Detail.GetGerrit() == nil {
		panic(fmt.Errorf("CL %d is not a Gerrit CL", rcl.ID))
	}

	if op.IsCancelRequested() {
		return errCancelHonored
	}
	switch posted, err := op.isAlreadyPosted(ctx, rcl); {
	case err != nil:
		return err
	case posted:
		logging.Debugf(ctx, "CL %d %s already has the starting message", rcl.ID, rcl.ExternalID)
		return nil
	}

	if op.IsCancelRequested() {
		return errCancelHonored
	}
	return op.post(ctx, rcl)
}

func (op *PostStartMessageOp) isAlreadyPosted(ctx context.Context, rcl *run.RunCL) (bool, error) {
	// Check if Gerrit CL already has the starting message by first checking CV's
	// own cache (CL entity in Datastore) and if the cache is old, fetching latest
	// info from Gerrit.
	cl := changelist.CL{ID: rcl.ID}
	switch err := datastore.Get(ctx, &cl); {
	case err == datastore.ErrNoSuchEntity:
		return false, errors.Annotate(err, "CL %d %s no longer exists", rcl.ID, rcl.ExternalID).Err()
	case err != nil:
		return false, errors.Annotate(err, "failed to load CL %d %s", rcl.ID, rcl.ExternalID).Tag(transient.Tag).Err()
	case op.isAlreadyPostedOn(rcl, cl.Snapshot.GetGerrit().GetInfo()):
		return true, nil
	case clock.Since(ctx, cl.Snapshot.GetExternalUpdateTime().AsTime()) < staleCLAgeThreshold:
		// Accept possibility of duplicate messages within the staleCLAgeThreshold.
		return false, nil
	}

	// Fetch the latest CL details from Gerrit.
	gc, err := op.GFactory.MakeClient(ctx, rcl.Detail.GetGerrit().GetHost(), op.Run.ID.LUCIProject())
	if err != nil {
		return false, err
	}
	ci, err := gc.GetChange(ctx, &gerritpb.GetChangeRequest{
		Project: rcl.Detail.GetGerrit().GetInfo().GetProject(),
		Number:  rcl.Detail.GetGerrit().GetInfo().GetNumber(),
		Options: []gerritpb.QueryOption{gerritpb.QueryOption_MESSAGES},
	})
	switch code := grpcutil.Code(err); code {
	case codes.OK:
		return op.isAlreadyPostedOn(rcl, ci), nil
	case codes.PermissionDenied, codes.NotFound:
		// This is permanent error which shouldn't be retried.
		return false, errors.Annotate(err, "no Gerrit.GetChange access to CL %d %s", rcl.ID, rcl.ExternalID).Err()
	default:
		return false, gerrit.UnhandledError(ctx, err, "failed to Gerrtit.GetChange on CL %d %s messages", rcl.ID, rcl.ExternalID)
	}
}

func (op *PostStartMessageOp) isAlreadyPostedOn(rcl *run.RunCL, ci *gerritpb.ChangeInfo) bool {
	// Look at latest messages first for efficiency,
	// and skip all messages which are too old.
	clTriggeredAt := rcl.Trigger.Time.AsTime()
	for i := len(ci.GetMessages()) - 1; i >= 0; i-- {
		m := ci.GetMessages()[i]
		if m.GetDate().AsTime().Before(clTriggeredAt) {
			// i-th message is too old, no need to check even older ones.
			return false
		}
		switch data, ok := botdata.Parse(m); {
		case !ok:
		case data.Action != botdata.Start:
		case data.TriggeredAt != clTriggeredAt:
		case data.Revision != rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision():
		case len(data.CLs) != len(op.Run.CLs):
		default:
			return true
		}
	}
	return false
}

func (op *PostStartMessageOp) post(ctx context.Context, rcl *run.RunCL) error {
	msg, err := op.makeMessage(rcl)
	if err != nil {
		return errors.Annotate(err, "failed to generate the starting message").Err()
	}
	req := &gerritpb.SetReviewRequest{
		Project:    rcl.Detail.GetGerrit().GetInfo().GetProject(),
		Number:     rcl.Detail.GetGerrit().GetInfo().GetNumber(),
		RevisionId: rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision(),
		// Kept compatible with CQDaemon to avoid breaking user filters.
		Tag:     "autogenerated:cq",
		Notify:  gerritpb.Notify_NOTIFY_NONE,
		Message: msg,
	}

	gc, err := op.GFactory.MakeClient(ctx, rcl.Detail.GetGerrit().GetHost(), op.Run.ID.LUCIProject())
	if err != nil {
		return err
	}
	return op.GFactory.MakeMirrorIterator(ctx).RetryIfStale(func(opt grpc.CallOption) error {
		switch _, err := gc.SetReview(ctx, req); grpcutil.Code(err) {
		case codes.OK:
			return nil
		case codes.PermissionDenied:
			// This is a permanent error which shouldn't be retried.
			return errors.Annotate(err, "no Gerrit.SetReview access to CL %d %s", rcl.ID, rcl.ExternalID).Err()
		case codes.NotFound:
			// This is known to happen on new CLs or on recently created revisions.
			logging.Debugf(ctx, "HTTP 404 when posting start message: %s", err)
			return errors.Annotate(gerrit.ErrStaleData, "no Gerrit.SetReview access to CL %d %s, likely stale replica", rcl.ID, rcl.ExternalID).Err()
		default:
			return gerrit.UnhandledError(ctx, err, "failed to Gerrit.SetReview details of CL %d %s messages", rcl.ID, rcl.ExternalID)
		}
	})
}

func (op *PostStartMessageOp) makeMessage(rcl *run.RunCL) (string, error) {
	humanMsg := usertext.OnRunStartedGerritMessage(op.Run, op.cfg, op.Env)
	bd := botdata.BotData{
		Action:      botdata.Start,
		CLs:         op.botdataCLs,
		Revision:    rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision(),
		TriggeredAt: rcl.Trigger.Time.AsTime(),
	}
	return botdata.Append(humanMsg, bd)
}
<commit_msg>[cv] handle both cases of spurious HTTP 404 when posting start msg.<commit_after>// Copyright 2021 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package longops

import (
	"context"
	"fmt"
	"time"

	"golang.org/x/sync/errgroup"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/errors"
	"go.chromium.org/luci/common/logging"
	gerritpb "go.chromium.org/luci/common/proto/gerrit"
	"go.chromium.org/luci/common/retry/transient"
	"go.chromium.org/luci/common/sync/parallel"
	"go.chromium.org/luci/gae/service/datastore"
	"go.chromium.org/luci/grpc/grpcutil"

	"go.chromium.org/luci/cv/internal/changelist"
	"go.chromium.org/luci/cv/internal/common"
	"go.chromium.org/luci/cv/internal/configs/prjcfg"
	"go.chromium.org/luci/cv/internal/gerrit"
	"go.chromium.org/luci/cv/internal/gerrit/botdata"
	"go.chromium.org/luci/cv/internal/run"
	"go.chromium.org/luci/cv/internal/run/eventpb"
	"go.chromium.org/luci/cv/internal/usertext"
)

// staleCLAgeThreshold controls when CV should fetch latest Gerrit info before
// trying to post the starting message.
//
// Large values increase the chance of duplicate starting messages,
// while low values increase load on Gerrit.
const staleCLAgeThreshold = 10 * time.Second

// PostStartMessageOp posts a start message on each of the Run CLs.
//
// PostStartMessageOp is a single-use object.
type PostStartMessageOp struct {
	// All public fields must be set.

	*Base
	GFactory gerrit.Factory
	Env      *common.Env

	// These private fields are set internally as implementation details.

	rcls       []*run.RunCL
	cfg        *prjcfg.ConfigGroup
	botdataCLs []botdata.ChangeID
}

// Do actually posts the start messages.
func (op *PostStartMessageOp) Do(ctx context.Context) (*eventpb.LongOpCompleted, error) {
	op.assertCalledOnce()

	if op.IsCancelRequested() {
		return &eventpb.LongOpCompleted{Status: eventpb.LongOpCompleted_CANCELLED}, nil
	}

	if err := op.prepare(ctx); err != nil {
		return nil, err
	}

	errs := make(errors.MultiError, len(op.rcls))
	poolError := parallel.WorkPool(min(len(op.rcls), 8), func(work chan<- func() error) {
		for i, rcl := range op.rcls {
			i, rcl := i, rcl
			work <- func() error {
				// all errors will be aggregated across all CLs below.
				errs[i] = op.doCL(ctx, rcl)
				return nil
			}
		}
	})
	if poolError != nil {
		panic(fmt.Errorf("unexpected WorkPool error %s", poolError))
	}

	// Aggregate all errors into the final result.
	psm := &eventpb.LongOpCompleted_PostStartMessage{}
	result := &eventpb.LongOpCompleted{
		Status: eventpb.LongOpCompleted_SUCCEEDED, // be hopeful by default
		Result: &eventpb.LongOpCompleted_PostStartMessage_{
			PostStartMessage: psm,
		},
	}
	var firstError error
	for i, rcl := range op.rcls {
		switch err := errs[i]; {
		case err == nil:
			psm.Posted = append(psm.Posted, int64(rcl.ID))
		case errors.Unwrap(err) == errCancelHonored:
			result.Status = eventpb.LongOpCompleted_CANCELLED
			errs[i] = nil
		case !transient.Tag.In(err):
			if psm.GetPermanentErrors() == nil {
				psm.PermanentErrors = make(map[int64]string, 1)
				firstError = err
			}
			psm.GetPermanentErrors()[int64(rcl.ID)] = err.Error()
			logging.Errorf(ctx, "failed to post start message on CL %d %q: %s", rcl.ID, rcl.ExternalID, err)
		case firstError == nil:
			// First transient error.
			firstError = err
		}
	}

	switch {
	case len(psm.GetPermanentErrors()) > 0:
		result.Status = eventpb.LongOpCompleted_FAILED
		return result, firstError
	case firstError != nil:
		// Must be a transient error, expect a retry.
		return nil, firstError
	default:
		return result, nil
	}
}

func (op *PostStartMessageOp) prepare(ctx context.Context) error {
	eg, eCtx := errgroup.WithContext(ctx)
	eg.Go(func() (err error) {
		op.rcls, err = run.LoadRunCLs(eCtx, op.Run.ID, op.Run.CLs)
		return
	})
	eg.Go(func() (err error) {
		op.cfg, err = prjcfg.GetConfigGroup(eCtx, op.Run.ID.LUCIProject(), op.Run.ConfigGroupID)
		return
	})
	if err := eg.Wait(); err != nil {
		return err
	}

	op.botdataCLs = make([]botdata.ChangeID, len(op.rcls))
	for i, rcl := range op.rcls {
		op.botdataCLs[i].Host = rcl.Detail.GetGerrit().GetHost()
		op.botdataCLs[i].Number = rcl.Detail.GetGerrit().GetInfo().GetNumber()
	}
	return nil
}

func (op *PostStartMessageOp) doCL(ctx context.Context, rcl *run.RunCL) error {
	if rcl.Detail.GetGerrit() == nil {
		panic(fmt.Errorf("CL %d is not a Gerrit CL", rcl.ID))
	}

	if op.IsCancelRequested() {
		return errCancelHonored
	}
	switch posted, err := op.isAlreadyPosted(ctx, rcl); {
	case err != nil:
		return errors.Annotate(err, "failed to check if message was already posted").Err()
	case posted:
		logging.Debugf(ctx, "CL %d %s already has the starting message", rcl.ID, rcl.ExternalID)
		return nil
	}

	if op.IsCancelRequested() {
		return errCancelHonored
	}
	return op.post(ctx, rcl)
}

func (op *PostStartMessageOp) isAlreadyPosted(ctx context.Context, rcl *run.RunCL) (bool, error) {
	// Check if Gerrit CL already has the starting message by first checking CV's
	// own cache (CL entity in Datastore) and if the cache is old, fetching latest
	// info from Gerrit.
	cl := changelist.CL{ID: rcl.ID}
	switch err := datastore.Get(ctx, &cl); {
	case err == datastore.ErrNoSuchEntity:
		return false, errors.Annotate(err, "CL no longer exists").Err()
	case err != nil:
		return false, errors.Annotate(err, "failed to load CL").Tag(transient.Tag).Err()
	case op.isAlreadyPostedOn(rcl, cl.Snapshot.GetGerrit().GetInfo()):
		return true, nil
	case clock.Since(ctx, cl.Snapshot.GetExternalUpdateTime().AsTime()) < staleCLAgeThreshold:
		// Accept possibility of duplicate messages within the staleCLAgeThreshold.
		return false, nil
	}

	// Fetch the latest CL details from Gerrit.
	gc, err := op.GFactory.MakeClient(ctx, rcl.Detail.GetGerrit().GetHost(), op.Run.ID.LUCIProject())
	if err != nil {
		return false, err
	}

	req := &gerritpb.GetChangeRequest{
		Project: rcl.Detail.GetGerrit().GetInfo().GetProject(),
		Number:  rcl.Detail.GetGerrit().GetInfo().GetNumber(),
		Options: []gerritpb.QueryOption{gerritpb.QueryOption_MESSAGES},
	}
	var ci *gerritpb.ChangeInfo
	outerErr := op.GFactory.MakeMirrorIterator(ctx).RetryIfStale(func(opt grpc.CallOption) error {
		ci, err = gc.GetChange(ctx, req)
		switch grpcutil.Code(err) {
		case codes.OK:
			return nil
		case codes.PermissionDenied:
			// This is permanent error which shouldn't be retried.
			return err
		case codes.NotFound:
			return gerrit.ErrStaleData
		default:
			err = gerrit.UnhandledError(ctx, err, "Gerrit.GetChange")
			return err
		}
	})
	switch {
	case err != nil:
		return false, errors.Annotate(err, "failed to get the latest Gerrit ChangeInfo").Err()
	case outerErr != nil:
		// Shouldn't happen, unless Mirror iterate itself errors out for some
		// reason.
		return false, outerErr
	default:
		return op.isAlreadyPostedOn(rcl, ci), nil
	}
}

func (op *PostStartMessageOp) isAlreadyPostedOn(rcl *run.RunCL, ci *gerritpb.ChangeInfo) bool {
	// Look at latest messages first for efficiency,
	// and skip all messages which are too old.
	clTriggeredAt := rcl.Trigger.Time.AsTime()
	for i := len(ci.GetMessages()) - 1; i >= 0; i-- {
		m := ci.GetMessages()[i]
		if m.GetDate().AsTime().Before(clTriggeredAt) {
			// i-th message is too old, no need to check even older ones.
			return false
		}
		switch data, ok := botdata.Parse(m); {
		case !ok:
		case data.Action != botdata.Start:
		case data.TriggeredAt != clTriggeredAt:
		case data.Revision != rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision():
		case len(data.CLs) != len(op.Run.CLs):
		default:
			return true
		}
	}
	return false
}

func (op *PostStartMessageOp) post(ctx context.Context, rcl *run.RunCL) error {
	msg, err := op.makeMessage(rcl)
	if err != nil {
		return errors.Annotate(err, "failed to generate the starting message").Err()
	}

	gc, err := op.GFactory.MakeClient(ctx, rcl.Detail.GetGerrit().GetHost(), op.Run.ID.LUCIProject())
	if err != nil {
		return err
	}

	req := &gerritpb.SetReviewRequest{
		Project:    rcl.Detail.GetGerrit().GetInfo().GetProject(),
		Number:     rcl.Detail.GetGerrit().GetInfo().GetNumber(),
		RevisionId: rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision(),
		// Kept compatible with CQDaemon to avoid breaking user filters.
		Tag:     "autogenerated:cq",
		Notify:  gerritpb.Notify_NOTIFY_NONE,
		Message: msg,
	}
	outerErr := op.GFactory.MakeMirrorIterator(ctx).RetryIfStale(func(opt grpc.CallOption) error {
		_, err = gc.SetReview(ctx, req)
		switch grpcutil.Code(err) {
		case codes.OK:
			return nil
		case codes.PermissionDenied:
			// This is a permanent error which shouldn't be retried.
			return err
		case codes.NotFound:
			// This is known to happen on new CLs or on recently created revisions.
			return gerrit.ErrStaleData
		default:
			err = gerrit.UnhandledError(ctx, err, "Gerrit.SetReview")
			return err
		}
	})
	switch {
	case err != nil:
		return errors.Annotate(err, "failed to Gerrit.SetReview").Err()
	case outerErr != nil:
		// Shouldn't happen, unless MirrorIterator itself errors out for some
		// reason.
		return outerErr
	default:
		return nil
	}
}

func (op *PostStartMessageOp) makeMessage(rcl *run.RunCL) (string, error) {
	humanMsg := usertext.OnRunStartedGerritMessage(op.Run, op.cfg, op.Env)
	bd := botdata.BotData{
		Action:      botdata.Start,
		CLs:         op.botdataCLs,
		Revision:    rcl.Detail.GetGerrit().GetInfo().GetCurrentRevision(),
		TriggeredAt: rcl.Trigger.Time.AsTime(),
	}
	return botdata.Append(humanMsg, bd)
}
<|endoftext|>


<commit_before>// +build windows

package main

import (
	"log"
)

func terminateGracefully(process *os.Process) {
	log.Fatal("Attempting to terminate gracefully on Windows is not supported.")
}

func gracefulTerminationPossible() bool {
	return false
}
<commit_msg>Removed unneeded build flags on process_windows.go<commit_after>package main

import (
	"log"
)

func terminateGracefully(process *os.Process) {
	log.Fatal("Attempting to terminate gracefully on Windows is not supported.")
}

func gracefulTerminationPossible() bool {
	return false
}
<|endoftext|>


<commit_before>package promptui

import (
	"bytes"
	"io"
	"os"
	"strings"

	"github.com/chzyer/readline"
)

// SelectedAdd is returned from SelectWithAdd when add is selected.
const SelectedAdd = -1

// Select represents a list for selecting a single item
type Select struct {
	Label string   // Label is the value displayed on the command line prompt.
	Items []string // Items are the items to use in the list.
}

// Run runs the Select list. It returns the index of the selected element,
// and its value.
func (s *Select) Run() (int, string, error) {
	return s.innerRun(0, ' ')
}

func (s *Select) innerRun(starting int, top rune) (int, string, error) {
	stdin := readline.NewCancelableStdin()
	c := &readline.Config{}
	err := c.Init()
	if err != nil {
		return 0, "", err
	}

	c.Stdin = stdin

	prompt := s.Label + ": "

	c.HistoryLimit = -1
	c.UniqueEditLine = true

	start := 0
	end := 4

	if len(s.Items) <= end {
		end = len(s.Items) - 1
	}

	selected := starting

	rl, err := readline.NewEx(c)
	if err != nil {
		return 0, "", err
	}

	rl.Write([]byte(hideCursor))
	rl.Write([]byte(strings.Repeat("\n", end-start+1)))

	counter := 0

	c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
		switch key {
		case readline.CharEnter:
			return nil, 0, true
		case readline.CharNext:
			switch selected {
			case len(s.Items) - 1:
			case end:
				start++
				end++
				fallthrough
			default:
				selected++
			}
		case readline.CharPrev:
			switch selected {
			case 0:
			case start:
				start--
				end--
				fallthrough
			default:
				selected--
			}
		}

		list := make([]string, end-start+1)
		for i := start; i <= end; i++ {
			page := ' '
			selection := " "
			item := s.Items[i]

			switch i {
			case 0:
				page = top
			case len(s.Items) - 1:
			case start:
				page = '↑'
			case end:
				page = '↓'
			}
			if i == selected {
				selection = "☞"
				item = underlined(item)
			}
			list[i-start] = clearLine + "\r" + string(page) + selection + " " + item
		}

		prefix := ""
		prefix += upLine(uint(len(list))) + "\r" + clearLine
		p := prefix + bold(iconInitial) + " " + bold(prompt) + downLine(1) + strings.Join(list, downLine(1))
		rl.SetPrompt(p)
		rl.Refresh()

		counter++

		return nil, 0, true
	})

	_, err = rl.Readline()
	rl.Close()

	if err != nil {
		switch {
		case err == readline.ErrInterrupt, err.Error() == "Interrupt":
			err = ErrInterrupt
		case err == io.EOF:
			err = ErrEOF
		}

		rl.Write([]byte("\n"))
		rl.Write([]byte(showCursor))
		rl.Refresh()
		return 0, "", err
	}

	rl.Write(bytes.Repeat([]byte(clearLine+upLine(1)), end-start+1))
	rl.Write([]byte("\r"))

	out := s.Items[selected]
	rl.Write([]byte(iconGood + " " + prompt + faint(out) + "\n"))

	rl.Write([]byte(showCursor))
	return selected, out, err
}

// SelectWithAdd represents a list for selecting a single item, or selecting
// a newly created item.
type SelectWithAdd struct {
	Label string   // Label is the value displayed on the command line prompt.
	Items []string // Items are the items to use in the list.

	AddLabel string // The label used in the item list for creating a new item.

	// Validate is optional. If set, this function is used to validate the input
	// after each character entry.
	Validate ValidateFunc
}

// Run runs the Select list. It returns the index of the selected element,
// and its value. If a new element is created, -1 is returned as the index.
func (sa *SelectWithAdd) Run() (int, string, error) {
	if len(sa.Items) > 0 {
		newItems := append([]string{sa.AddLabel}, sa.Items...)

		s := Select{
			Label: sa.Label,
			Items: newItems,
		}

		selected, value, err := s.innerRun(1, '+')
		if err != nil || selected != 0 {
			return selected - 1, value, err
		}

		// XXX run through terminal for windows
		os.Stdout.Write([]byte(upLine(1) + "\r" + clearLine))
	}

	p := Prompt{
		Label:    sa.Label,
		Validate: sa.Validate,
	}
	value, err := p.Run()
	return SelectedAdd, value, err
}
<commit_msg>Fix SelectWithAdd AddLabel Bug<commit_after>package promptui

import (
	"bytes"
	"io"
	"os"
	"strings"

	"github.com/chzyer/readline"
)

// SelectedAdd is returned from SelectWithAdd when add is selected.
const SelectedAdd = -1

// Select represents a list for selecting a single item
type Select struct {
	Label string   // Label is the value displayed on the command line prompt.
	Items []string // Items are the items to use in the list.
}

// Run runs the Select list. It returns the index of the selected element,
// and its value.
func (s *Select) Run() (int, string, error) {
	return s.innerRun(0, ' ')
}

func (s *Select) innerRun(starting int, top rune) (int, string, error) {
	stdin := readline.NewCancelableStdin()
	c := &readline.Config{}
	err := c.Init()
	if err != nil {
		return 0, "", err
	}

	c.Stdin = stdin

	prompt := s.Label + ": "

	c.HistoryLimit = -1
	c.UniqueEditLine = true

	start := 0
	end := 4

	if len(s.Items) <= end {
		end = len(s.Items) - 1
	}

	selected := starting

	rl, err := readline.NewEx(c)
	if err != nil {
		return 0, "", err
	}

	rl.Write([]byte(hideCursor))
	rl.Write([]byte(strings.Repeat("\n", end-start+1)))

	counter := 0

	c.SetListener(func(line []rune, pos int, key rune) ([]rune, int, bool) {
		switch key {
		case readline.CharEnter:
			return nil, 0, true
		case readline.CharNext:
			switch selected {
			case len(s.Items) - 1:
			case end:
				start++
				end++
				fallthrough
			default:
				selected++
			}
		case readline.CharPrev:
			switch selected {
			case 0:
			case start:
				start--
				end--
				fallthrough
			default:
				selected--
			}
		}

		list := make([]string, end-start+1)
		for i := start; i <= end; i++ {
			page := ' '
			selection := " "
			item := s.Items[i]

			switch i {
			case 0:
				page = top
			case len(s.Items) - 1:
			case start:
				page = '↑'
			case end:
				page = '↓'
			}
			if i == selected {
				selection = "☞"
				item = underlined(item)
			}
			list[i-start] = clearLine + "\r" + string(page) + selection + " " + item
		}

		prefix := ""
		prefix += upLine(uint(len(list))) + "\r" + clearLine
		p := prefix + bold(iconInitial) + " " + bold(prompt) + downLine(1) + strings.Join(list, downLine(1))
		rl.SetPrompt(p)
		rl.Refresh()

		counter++

		return nil, 0, true
	})

	_, err = rl.Readline()
	rl.Close()

	if err != nil {
		switch {
		case err == readline.ErrInterrupt, err.Error() == "Interrupt":
			err = ErrInterrupt
		case err == io.EOF:
			err = ErrEOF
		}

		rl.Write([]byte("\n"))
		rl.Write([]byte(showCursor))
		rl.Refresh()
		return 0, "", err
	}

	rl.Write(bytes.Repeat([]byte(clearLine+upLine(1)), end-start+1))
	rl.Write([]byte("\r"))

	out := s.Items[selected]
	rl.Write([]byte(iconGood + " " + prompt + faint(out) + "\n"))

	rl.Write([]byte(showCursor))
	return selected, out, err
}

// SelectWithAdd represents a list for selecting a single item, or selecting
// a newly created item.
type SelectWithAdd struct {
	Label string   // Label is the value displayed on the command line prompt.
	Items []string // Items are the items to use in the list.

	AddLabel string // The label used in the item list for creating a new item.

	// Validate is optional. If set, this function is used to validate the input
	// after each character entry.
	Validate ValidateFunc
}

// Run runs the Select list. It returns the index of the selected element,
// and its value. If a new element is created, -1 is returned as the index.
func (sa *SelectWithAdd) Run() (int, string, error) {
	if len(sa.Items) > 0 {
		newItems := append([]string{sa.AddLabel}, sa.Items...)

		s := Select{
			Label: sa.Label,
			Items: newItems,
		}

		selected, value, err := s.innerRun(1, '+')
		if err != nil || selected != 0 {
			return selected - 1, value, err
		}

		// XXX run through terminal for windows
		os.Stdout.Write([]byte(upLine(1) + "\r" + clearLine))
	}

	p := Prompt{
		Label:    sa.AddLabel,
		Validate: sa.Validate,
	}
	value, err := p.Run()
	return SelectedAdd, value, err
}
<|endoftext|>


<commit_before>// Package protocol implements functions for reading, writing, decoding, and
// encoding data for the Minecraft protocol.
package protocol

import (
	"io"
)

type Stream struct {
	io.ReadWriter
}

type PacketStream struct {
	Stream
	reader *io.LimitedReader
}

type readWriter struct {
	io.Reader
	io.Writer
}

func (s PacketStream) ExhaustPacket() (int, error) {
	if s.reader.N == 0 {
		return 0, nil
	}

	return int(s.reader.N), s.ReadFull(make([]byte, s.reader.N))
}

func (s PacketStream) GetRemainingBytes() int {
	return int(s.reader.N)
}

func (s Stream) GetPacketStream() (PacketStream, int, error) {
	length, err := s.ReadVarInt()
	if err != nil {
		return PacketStream{}, 0, err
	}

	if length == 0 {
		return PacketStream{}, 0, ErrInvalidData
	}

	limitedReader := &io.LimitedReader{R: s, N: int64(length)}

	return PacketStream{Stream{readWriter{limitedReader, s}}, limitedReader},
		length, nil
}

func NewStream(readWriter io.ReadWriter) Stream {
	return Stream{readWriter}
}

// Returns decoded (little endian) data of length data.
func (s Stream) DecodeReadFull(data []byte) error {
	_, err := io.ReadFull(s, data)
	if err != nil {
		return err
	}

	for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
		data[i], data[j] = data[j], data[i]
	}

	return nil
}

func (s Stream) ReadFull(data []byte) error {
	_, err := io.ReadFull(s, data)
	if err != nil {
		return err
	}

	return nil
}

func (s Stream) WritePacket(p *Packet) error {
	lengthPacket := &Packet{}
	lengthPacket.WriteVarInt(len(p.Data))

	_, err := s.Write(lengthPacket.Data)
	if err != nil {
		return err
	}

	_, err = s.Write(p.Data)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Send packets as a whole<commit_after>// Package protocol implements functions for reading, writing, decoding, and
// encoding data for the Minecraft protocol.
package protocol

import (
	"io"
)

type Stream struct {
	io.ReadWriter
}

type PacketStream struct {
	Stream
	reader *io.LimitedReader
}

type readWriter struct {
	io.Reader
	io.Writer
}

func (s PacketStream) ExhaustPacket() (int, error) {
	if s.reader.N == 0 {
		return 0, nil
	}

	return int(s.reader.N), s.ReadFull(make([]byte, s.reader.N))
}

func (s PacketStream) GetRemainingBytes() int {
	return int(s.reader.N)
}

func (s Stream) GetPacketStream() (PacketStream, int, error) {
	length, err := s.ReadVarInt()
	if err != nil {
		return PacketStream{}, 0, err
	}

	if length == 0 {
		return PacketStream{}, 0, ErrInvalidData
	}

	limitedReader := &io.LimitedReader{R: s, N: int64(length)}

	return PacketStream{Stream{readWriter{limitedReader, s}}, limitedReader},
		length, nil
}

func NewStream(readWriter io.ReadWriter) Stream {
	return Stream{readWriter}
}

// Returns decoded (little endian) data of length data.
func (s Stream) DecodeReadFull(data []byte) error {
	_, err := io.ReadFull(s, data)
	if err != nil {
		return err
	}

	for i, j := 0, len(data)-1; i < j; i, j = i+1, j-1 {
		data[i], data[j] = data[j], data[i]
	}

	return nil
}

func (s Stream) ReadFull(data []byte) error {
	_, err := io.ReadFull(s, data)
	if err != nil {
		return err
	}

	return nil
}

func (s Stream) WritePacket(p *Packet) error {
	lengthPacket := &Packet{}
	lengthPacket.WriteVarInt(len(p.Data))

	_, err := s.Write(append(lengthPacket.Data, p.Data...))
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


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

package repository

import (
	"fmt"
	"github.com/andreaskoch/allmark/markdown"
	"github.com/andreaskoch/allmark/path"
	"github.com/andreaskoch/allmark/util"
	"io/ioutil"
	"path/filepath"
)

type ItemIndex struct {
	path  string
	items []*Item
}

func NewItemIndex(indexPath string) (*ItemIndex, error) {

	// check if path exists
	if !util.PathExists(indexPath) {
		return nil, fmt.Errorf("The path %q does not exist.", indexPath)
	}

	if isReservedDirectory(indexPath) {
		return nil, fmt.Errorf("The path %q is using a reserved name and cannot be an index.", indexPath)
	}

	// check if the path is a directory
	if isDirectory, _ := util.IsDirectory(indexPath); !isDirectory {
		indexPath = filepath.Dir(indexPath)
	}

	// create the index
	index := &ItemIndex{
		path:  indexPath,
		items: findAllItems(indexPath),
	}

	return index, nil
}

func (itemIndex *ItemIndex) String() string {
	return fmt.Sprintf("%s", itemIndex.path)
}

func (itemIndex *ItemIndex) Path() string {
	return itemIndex.path
}

func (itemIndex *ItemIndex) Directory() string {
	return itemIndex.Path()
}

func (itemIndex *ItemIndex) PathType() string {
	return path.PatherTypeIndex
}

func (itemIndex *ItemIndex) Items() []*Item {
	return itemIndex.items
}

func (itemIndex *ItemIndex) Add(item *Item) {
	itemIndex.items = append(itemIndex.items, item)
}

func findAllItems(itemDirectory string) []*Item {

	items := make([]*Item, 0)

	directoryEntries, err := ioutil.ReadDir(itemDirectory)
	if err != nil {
		fmt.Printf("An error occured while indexing the directory `%v`.\nError: %v\n", itemDirectory, err)
		return nil
	}

	// create a path provider
	parentDir := filepath.Dir(itemDirectory)
	pathProvider := path.NewProvider(parentDir, true)

	// item search
	directoryContainsItem := false
	for _, element := range directoryEntries {

		itemPath := filepath.Join(itemDirectory, element.Name())

		// check if the file a markdown file
		isMarkdown := markdown.IsMarkdownFile(itemPath)
		if !isMarkdown {
			continue
		}

		// search for child items
		childs := getChildItems(itemDirectory)

		// create item
		item, err := NewItem(itemPath, childs, pathProvider)
		if err != nil {
			fmt.Printf("Skipping item: %s\n", err)
			continue
		}

		// append item to list
		items = append(items, item)

		// item has been found
		directoryContainsItem = true
		break
	}

	// search in sub directories if there is no item in the current folder
	if !directoryContainsItem {

		if virtualItem, err := NewVirtualItem(itemDirectory, getChildItems(itemDirectory), pathProvider); err == nil {
			items = append(items, virtualItem)
		} else {
			fmt.Println(err)
		}

	}

	return items
}

func getChildItems(itemDirectory string) []*Item {

	childItems := make([]*Item, 0)

	files, _ := ioutil.ReadDir(itemDirectory)
	for _, folder := range files {

		if !folder.IsDir() {
			continue // skip files
		}

		childItemDirectory := filepath.Join(itemDirectory, folder.Name())
		if isReservedDirectory(childItemDirectory) {
			continue // skip reserved directories
		}

		childsInPath := findAllItems(childItemDirectory)
		childItems = append(childItems, childsInPath...)

	}

	return childItems
}
<commit_msg>Fixed a bug related to lop-level items paths<commit_after>// Copyright 2013 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package repository

import (
	"fmt"
	"github.com/andreaskoch/allmark/markdown"
	"github.com/andreaskoch/allmark/path"
	"github.com/andreaskoch/allmark/util"
	"io/ioutil"
	"path/filepath"
)

type ItemIndex struct {
	path  string
	items []*Item
}

func NewItemIndex(indexPath string) (*ItemIndex, error) {

	// check if path exists
	if !util.PathExists(indexPath) {
		return nil, fmt.Errorf("The path %q does not exist.", indexPath)
	}

	if isReservedDirectory(indexPath) {
		return nil, fmt.Errorf("The path %q is using a reserved name and cannot be an index.", indexPath)
	}

	// check if the path is a directory
	if isDirectory, _ := util.IsDirectory(indexPath); !isDirectory {
		indexPath = filepath.Dir(indexPath)
	}

	// create the index
	index := &ItemIndex{
		path:  indexPath,
		items: findAllItems(0, indexPath),
	}

	return index, nil
}

func (itemIndex *ItemIndex) String() string {
	return fmt.Sprintf("%s", itemIndex.path)
}

func (itemIndex *ItemIndex) Path() string {
	return itemIndex.path
}

func (itemIndex *ItemIndex) Directory() string {
	return itemIndex.Path()
}

func (itemIndex *ItemIndex) PathType() string {
	return path.PatherTypeIndex
}

func (itemIndex *ItemIndex) Items() []*Item {
	return itemIndex.items
}

func (itemIndex *ItemIndex) Add(item *Item) {
	itemIndex.items = append(itemIndex.items, item)
}

func findAllItems(level int, itemDirectory string) []*Item {

	items := make([]*Item, 0)

	directoryEntries, err := ioutil.ReadDir(itemDirectory)
	if err != nil {
		fmt.Printf("An error occured while indexing the directory `%v`.\nError: %v\n", itemDirectory, err)
		return nil
	}

	// create a path provider
	pathProviderDirectory := itemDirectory
	if level > 0 {
		pathProviderDirectory = filepath.Dir(itemDirectory)
	}

	pathProvider := path.NewProvider(pathProviderDirectory, false)

	// item search
	directoryContainsItem := false
	for _, element := range directoryEntries {

		itemPath := filepath.Join(itemDirectory, element.Name())

		// check if the file a markdown file
		isMarkdown := markdown.IsMarkdownFile(itemPath)
		if !isMarkdown {
			continue
		}

		// search for child items
		childs := getChildItems((level + 1), itemDirectory)

		// create item
		item, err := NewItem(itemPath, childs, pathProvider)
		if err != nil {
			fmt.Printf("Skipping item: %s\n", err)
			continue
		}

		// append item to list
		items = append(items, item)

		// item has been found
		directoryContainsItem = true
		break
	}

	// search in sub directories if there is no item in the current folder
	if !directoryContainsItem {

		if virtualItem, err := NewVirtualItem(itemDirectory, getChildItems((level+1), itemDirectory), pathProvider); err == nil {
			items = append(items, virtualItem)
		} else {
			fmt.Println(err)
		}

	}

	return items
}

func getChildItems(level int, itemDirectory string) []*Item {

	childItems := make([]*Item, 0)

	files, _ := ioutil.ReadDir(itemDirectory)
	for _, folder := range files {

		if !folder.IsDir() {
			continue // skip files
		}

		childItemDirectory := filepath.Join(itemDirectory, folder.Name())
		if isReservedDirectory(childItemDirectory) {
			continue // skip reserved directories
		}

		childsInPath := findAllItems(level, childItemDirectory)
		childItems = append(childItems, childsInPath...)

	}

	return childItems
}
<|endoftext|>


<commit_before>package repositories

import (
	"strconv"
	"log"

	"github.com/steffen25/golang.zone/models"
	"github.com/steffen25/golang.zone/database"
	"fmt"
)

type PostRepository interface {
	Create (p *models.Post) error
	GetAll() ([]*models.Post, error)
	FindById(id int) (*models.Post, error)
	FindByUser(u *models.User) ([]*models.Post, error)
	Exists(slug string) bool
	Delete(id int) error
	Update(p *models.Post) error
}

type postRepository struct {
	*database.DB
}

func NewPostRepository(db *database.DB) PostRepository {
	return &postRepository{db}
}

func (pr *postRepository) Create(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if exists {
		err := pr.createWithSlugCount(p)
		if err != nil {
			return err
		}

		return nil
	}

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	res, err := stmt.Exec(p.Title, p.Slug, p.Body, p.CreatedAt, p.UserID)
	if err != nil {
		return err
	}
	log.Println(res)

	return nil
}

func (pr *postRepository) GetAll() ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id from posts")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

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

	return posts, nil
}

func (pr *postRepository) FindById(id int) (*models.Post, error) {
	return nil, nil
}

func (pr *postRepository) FindByUser(u *models.User) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE user_id=?", u.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

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

	return posts, nil
}

func (pr *postRepository) Delete(id int) error {
	return nil
}

func (pr *postRepository) Update(p *models.Post) error {
	return nil
}

// Check if a slug already exists
func (pr *postRepository) Exists(slug string) bool {
	var exists bool
	query := fmt.Sprintf("SELECT EXISTS (SELECT id FROM posts WHERE slug = '%s')", slug)
	err := pr.DB.QueryRow(query).Scan(&exists)
	if err != nil {
		log.Printf("[POST REPO]: Exists err %v", err)
		return true
	}

	return exists
}

func (pr *postRepository) createWithSlugCount(p *models.Post) error {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&count)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(count)

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug+"-"+counter, p.Body, p.CreatedAt, p.UserID)
	if err != nil {
		return err
	}

	return nil
}

<commit_msg>minor clean up<commit_after>package repositories

import (
	"strconv"
	"log"

	"github.com/steffen25/golang.zone/models"
	"github.com/steffen25/golang.zone/database"
)

type PostRepository interface {
	Create (p *models.Post) error
	GetAll() ([]*models.Post, error)
	FindById(id int) (*models.Post, error)
	FindByUser(u *models.User) ([]*models.Post, error)
	Exists(slug string) bool
	Delete(id int) error
	Update(p *models.Post) error
}

type postRepository struct {
	*database.DB
}

func NewPostRepository(db *database.DB) PostRepository {
	return &postRepository{db}
}

func (pr *postRepository) Create(p *models.Post) error {
	exists := pr.Exists(p.Slug)
	if exists {
		err := pr.createWithSlugCount(p)
		if err != nil {
			return err
		}

		return nil
	}

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug, p.Body, p.CreatedAt, p.UserID)
	if err != nil {
		return err
	}

	return nil
}

func (pr *postRepository) GetAll() ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id from posts")
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			log.Println(err)
			return nil, err
		}
		posts = append(posts, p)
	}

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

	return posts, nil
}

func (pr *postRepository) FindById(id int) (*models.Post, error) {
	return nil, nil
}

func (pr *postRepository) FindByUser(u *models.User) ([]*models.Post, error) {
	var posts []*models.Post

	rows, err := pr.DB.Query("SELECT id, title, slug, body, created_at, updated_at, user_id FROM posts WHERE user_id=?", u.ID)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	for rows.Next() {
		p := new(models.Post)
		err := rows.Scan(&p.ID, &p.Title, &p.Slug, &p.Body, &p.CreatedAt, &p.UpdatedAt, &p.UserID)
		if err != nil {
			return nil, err
		}
		posts = append(posts, p)
	}

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

	return posts, nil
}

func (pr *postRepository) Delete(id int) error {
	return nil
}

func (pr *postRepository) Update(p *models.Post) error {
	return nil
}

// Check if a slug already exists
func (pr *postRepository) Exists(slug string) bool {
	var exists bool
	err := pr.DB.QueryRow("SELECT EXISTS (SELECT id FROM posts WHERE slug = '?')", slug).Scan(&exists)
	if err != nil {
		log.Printf("[POST REPO]: Exists err %v", err)
		return true
	}

	return exists
}

// This is a 'private' function to be used in cases where a slug already exists
func (pr *postRepository) createWithSlugCount(p *models.Post) error {
	var count int
	err := pr.DB.QueryRow("SELECT COUNT(*) FROM posts where slug LIKE ?", "%"+p.Slug+"%").Scan(&count)
	if err != nil {
		return err
	}
	counter := strconv.Itoa(count)

	stmt, err := pr.DB.Prepare("INSERT INTO posts SET title=?, slug=?, body=?, created_at=?, user_id=?")
	if err != nil {
		return err
	}
	defer stmt.Close()
	_, err = stmt.Exec(p.Title, p.Slug+"-"+counter, p.Body, p.CreatedAt, p.UserID)
	if err != nil {
		return err
	}

	return nil
}<|endoftext|>


<commit_before>package metricsd

import (
	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/vertice/meta"
	"github.com/megamsys/vertice/metrix"
	"github.com/megamsys/vertice/storage"
	"github.com/megamsys/vertice/subd/deployd"
	"github.com/megamsys/vertice/subd/docker"
	"time"

)

// Service manages the listener and handler for an HTTP endpoint.
type Service struct {
	err     chan error
	Handler *Handler
	stop    chan struct{}
	Meta    *meta.Config
	Deployd *deployd.Config
	Dockerd *docker.Config
	Config  *Config
	Storage *storage.Config
}

// NewService returns a new instance of Service.
func NewService(c *meta.Config, one *deployd.Config, doc *docker.Config, f *Config, strg *storage.Config) *Service {
	s := &Service{
		err:     make(chan error),
		Meta:    c,
		Deployd: one,
		Dockerd: doc,
		Config:  f,
	}
	s.Handler = NewHandler()
	return s
}

// Open starts the service
func (s *Service) Open() error {
	log.Info("starting metricsd service")
	if s.stop != nil {
		return nil
	}

	s.stop = make(chan struct{})
	go s.backgroundLoop()
	return nil
}

func (s *Service) backgroundLoop() {
	for {
		select {
		case <-s.stop:
			log.Info("metricsd terminating")
			break
		case <-time.After(time.Duration(s.Config.CollectInterval)):
			s.runMetricsCollectors()
		}
	}

}

func (s *Service) runMetricsCollectors() error {
	output := &metrix.OutputHandler{
		ScyllaAddress: s.Meta.Api,
	}

	metrix.MetricsInterval = time.Duration(s.Config.CollectInterval)

  if s.Deployd.One.Enabled {
		s.onedCollectors(output)
	}

	if s.Dockerd.Docker.Enabled {
		s.dockerCollectors(output)
	}

	if s.Storage.Enabled {
		s.storageCollectors(output)
	}

	if s.Config.Backups.Enabled {
		s.backupsCollectors(output)
	}

	if s.Config.Snapshots.Enabled {
		s.snapshotsCollectors(output)
	}
	return nil
}

func (s *Service) Close() error {
	if s.stop == nil {
		return nil
	}
	close(s.stop)
	s.stop = nil
	return nil
}

// Err returns a channel for fatal errors that occur on the listener.
func (s *Service) Err() <-chan error { return s.err }

func (s *Service) onedCollectors(output *metrix.OutputHandler) {
	// One VirtualMachine Metrics collectors
	 if s.Deployd.One.Enabled {
		 for _, region := range s.Deployd.One.Regions {
			 collectors := map[string]metrix.MetricCollector{
				 metrix.OPENNEBULA: &metrix.OpenNebula{
					 Url: region.OneEndPoint,
					 Region: region.OneZone,
					 DefaultUnits: map[string]string{metrix.MEMORY_UNIT: region.MemoryUnit, metrix.CPU_UNIT: region.CpuUnit, metrix.DISK_UNIT: region.DiskUnit},
				 },
			 }

			 mh := &metrix.MetricHandler{}

			 for _, collector := range collectors {
				 go s.Handler.processCollector(mh, output, collector)
			 }
		 }
	 }

}

func (s *Service) dockerCollectors(output *metrix.OutputHandler) {
	// Docker container Metrics collectors
  if s.Dockerd.Docker.Enabled {
 	 for _, region := range s.Dockerd.Docker.Regions {
 		 collectors := map[string]metrix.MetricCollector{
 			 metrix.DOCKER: &metrix.Swarm{Url: region.SwarmEndPoint, DefaultUnits: map[string]string{metrix.MEMORY_UNIT: region.MemoryUnit, metrix.CPU_UNIT: region.CpuUnit, metrix.DISK_UNIT: region.DiskUnit}},
 		 }

 		 mh := &metrix.MetricHandler{}

 		 for _, collector := range collectors {
 			 go s.Handler.processCollector(mh, output, collector)
 		 }

 	 }
  }
}

func (s *Service) storageCollectors(output *metrix.OutputHandler) {
	if s.Storage.RgwStorage.Enabled {
 	// Ceph RadosGW (storage buckets) Metrics collectors
 	for _, region := range s.Storage.RgwStorage.Regions {
 		collectors := map[string]metrix.MetricCollector{
 			metrix.CEPHRGW: &metrix.CephRGWStats{Url: region.EndPoint,
 				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: region.StorageUnit,  metrix.STORAGE_COST_PER_HOUR: region.CostPerHour},
 				AdminUser: region.AdminUser,
 				MasterKey: s.Meta.MasterKey,
 				AccessKey: region.AdminAccess,
 				SecretKey: region.AdminSecret,
 			},
 		}

 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}

 	}
  }
}

func (s *Service) snapshotsCollectors(output *metrix.OutputHandler) {
 	// snapshots collectors
 		collectors := map[string]metrix.MetricCollector{
 			metrix.SNAPSHOTS: &metrix.Snapshots{
				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: s.Config.Snapshots.StorageUnit, metrix.STORAGE_COST_PER_HOUR: s.Config.Snapshots.CostPerHour},
 		  },
	  }
 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}
}

func (s *Service) backupsCollectors(output *metrix.OutputHandler) {
 	// snapshots collectors
 		collectors := map[string]metrix.MetricCollector{
 			metrix.BACKUPS: &metrix.Backups{
				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: s.Config.Backups.StorageUnit, metrix.STORAGE_COST_PER_HOUR: s.Config.Backups.CostPerHour},
 		  },
	  }
 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}
}
<commit_msg>metrics for storage conf<commit_after>package metricsd

import (
	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/vertice/meta"
	"github.com/megamsys/vertice/metrix"
	"github.com/megamsys/vertice/storage"
	"github.com/megamsys/vertice/subd/deployd"
	"github.com/megamsys/vertice/subd/docker"
	"time"

)

// Service manages the listener and handler for an HTTP endpoint.
type Service struct {
	err     chan error
	Handler *Handler
	stop    chan struct{}
	Meta    *meta.Config
	Deployd *deployd.Config
	Dockerd *docker.Config
	Config  *Config
	Storage *storage.Config
}

// NewService returns a new instance of Service.
func NewService(c *meta.Config, one *deployd.Config, doc *docker.Config, f *Config, strg *storage.Config) *Service {
	s := &Service{
		err:     make(chan error),
		Meta:    c,
		Deployd: one,
		Dockerd: doc,
		Config:  f,
		Storage: strg,
	}
	s.Handler = NewHandler()
	return s
}

// Open starts the service
func (s *Service) Open() error {
	log.Info("starting metricsd service")
	if s.stop != nil {
		return nil
	}

	s.stop = make(chan struct{})
	go s.backgroundLoop()
	return nil
}

func (s *Service) backgroundLoop() {
	for {
		select {
		case <-s.stop:
			log.Info("metricsd terminating")
			break
		case <-time.After(time.Duration(s.Config.CollectInterval)):
			s.runMetricsCollectors()
		}
	}

}

func (s *Service) runMetricsCollectors() error {
	output := &metrix.OutputHandler{
		ScyllaAddress: s.Meta.Api,
	}

	metrix.MetricsInterval = time.Duration(s.Config.CollectInterval)

  if s.Deployd.One.Enabled {
		s.onedCollectors(output)
	}

	if s.Dockerd.Docker.Enabled {
		s.dockerCollectors(output)
	}

	if s.Storage.Enabled {
		s.storageCollectors(output)
	}

	if s.Config.Backups.Enabled {
		s.backupsCollectors(output)
	}

	if s.Config.Snapshots.Enabled {
		s.snapshotsCollectors(output)
	}
	return nil
}

func (s *Service) Close() error {
	if s.stop == nil {
		return nil
	}
	close(s.stop)
	s.stop = nil
	return nil
}

// Err returns a channel for fatal errors that occur on the listener.
func (s *Service) Err() <-chan error { return s.err }

func (s *Service) onedCollectors(output *metrix.OutputHandler) {
	// One VirtualMachine Metrics collectors
	 if s.Deployd.One.Enabled {
		 for _, region := range s.Deployd.One.Regions {
			 collectors := map[string]metrix.MetricCollector{
				 metrix.OPENNEBULA: &metrix.OpenNebula{
					 Url: region.OneEndPoint,
					 Region: region.OneZone,
					 DefaultUnits: map[string]string{metrix.MEMORY_UNIT: region.MemoryUnit, metrix.CPU_UNIT: region.CpuUnit, metrix.DISK_UNIT: region.DiskUnit},
				 },
			 }

			 mh := &metrix.MetricHandler{}

			 for _, collector := range collectors {
				 go s.Handler.processCollector(mh, output, collector)
			 }
		 }
	 }

}

func (s *Service) dockerCollectors(output *metrix.OutputHandler) {
	// Docker container Metrics collectors
  if s.Dockerd.Docker.Enabled {
 	 for _, region := range s.Dockerd.Docker.Regions {
 		 collectors := map[string]metrix.MetricCollector{
 			 metrix.DOCKER: &metrix.Swarm{Url: region.SwarmEndPoint, DefaultUnits: map[string]string{metrix.MEMORY_UNIT: region.MemoryUnit, metrix.CPU_UNIT: region.CpuUnit, metrix.DISK_UNIT: region.DiskUnit}},
 		 }

 		 mh := &metrix.MetricHandler{}

 		 for _, collector := range collectors {
 			 go s.Handler.processCollector(mh, output, collector)
 		 }

 	 }
  }
}

func (s *Service) storageCollectors(output *metrix.OutputHandler) {
	if s.Storage.RgwStorage.Enabled {
 	// Ceph RadosGW (storage buckets) Metrics collectors
 	for _, region := range s.Storage.RgwStorage.Regions {
 		collectors := map[string]metrix.MetricCollector{
 			metrix.CEPHRGW: &metrix.CephRGWStats{Url: region.EndPoint,
 				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: region.StorageUnit,  metrix.STORAGE_COST_PER_HOUR: region.CostPerHour},
 				AdminUser: region.AdminUser,
 				MasterKey: s.Meta.MasterKey,
 				AccessKey: region.AdminAccess,
 				SecretKey: region.AdminSecret,
 			},
 		}

 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}

 	}
  }
}

func (s *Service) snapshotsCollectors(output *metrix.OutputHandler) {
 	// snapshots collectors
 		collectors := map[string]metrix.MetricCollector{
 			metrix.SNAPSHOTS: &metrix.Snapshots{
				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: s.Config.Snapshots.StorageUnit, metrix.STORAGE_COST_PER_HOUR: s.Config.Snapshots.CostPerHour},
 		  },
	  }
 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}
}

func (s *Service) backupsCollectors(output *metrix.OutputHandler) {
 	// snapshots collectors
 		collectors := map[string]metrix.MetricCollector{
 			metrix.BACKUPS: &metrix.Backups{
				DefaultUnits: map[string]string{metrix.STORAGE_UNIT: s.Config.Backups.StorageUnit, metrix.STORAGE_COST_PER_HOUR: s.Config.Backups.CostPerHour},
 		  },
	  }
 		mh := &metrix.MetricHandler{}

 		for _, collector := range collectors {
 			go s.Handler.processCollector(mh, output, collector)
 		}
}
<|endoftext|>


<commit_before>// The supervisor acts as the glue between the cli and the storm topology
// - The storm topology communicates with the supervisor in order to determine settings, etc.
// - The cli communicates with the supervisor to modify settings, get results, etc.
// @author Robin Verlangen

package main

import (
	"flag"
	"fmt"
	"github.com/RobinUS2/golang-jresp"
	"github.com/julienschmidt/httprouter"
	"log"
	"net/http"
)

var serverPort int

func init() {
	flag.IntVar(&serverPort, "port", 1525, "Server port")
	flag.Parse()
}

func main() {
	// Routing
	router := httprouter.New()

	// Docs
	router.GET("/", GetHome)

	// Start webserver
	log.Println(fmt.Sprintf("Starting supervisor service at port %d", serverPort))
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", serverPort), router))
}

func GetHome(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	if !basicAuth(w, r) {
		return
	}
	jresp := jresp.NewJsonResp()
	jresp.Set("hello", "This is the CloudPelican supervisor")
	jresp.OK()
	fmt.Fprint(w, jresp.ToString(false))
}

func basicAuth(w http.ResponseWriter, r *http.Request) {
	auth := strings.SplitN(r.Header["Authorization"][0], " ", 2)

	if len(auth) != 2 || auth[0] != "Basic" {
		http.Error(w, "bad syntax", http.StatusBadRequest)
		return false
	}

	payload, _ := base64.StdEncoding.DecodeString(auth[1])
	pair := strings.SplitN(string(payload), ":", 2)

	if len(pair) != 2 || !Validate(pair[0], pair[1]) {
		http.Error(w, "authorization failed", http.StatusUnauthorized)
		return false
	}
	return true
}
<commit_msg>Basic auth<commit_after>// The supervisor acts as the glue between the cli and the storm topology
// - The storm topology communicates with the supervisor in order to determine settings, etc.
// - The cli communicates with the supervisor to modify settings, get results, etc.
// @author Robin Verlangen

package main

import (
	"encoding/base64"
	"flag"
	"fmt"
	"github.com/RobinUS2/golang-jresp"
	"github.com/julienschmidt/httprouter"
	"log"
	"net/http"
	"strings"
)

var serverPort int
var basicAuthUsr string
var basicAuthPwd string

func init() {
	flag.IntVar(&serverPort, "port", 1525, "Server port")
	flag.StringVar(&basicAuthUsr, "auth-user", "cloud", "Username")
	flag.StringVar(&basicAuthPwd, "auth-password", "pelican", "Password")
	flag.Parse()
}

func main() {
	// Routing
	router := httprouter.New()

	// Docs
	router.GET("/", GetHome)

	// Start webserver
	log.Println(fmt.Sprintf("Starting supervisor service at port %d", serverPort))
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", serverPort), router))
}

func GetHome(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	if !basicAuth(w, r) {
		return
	}
	jresp := jresp.NewJsonResp()
	jresp.Set("hello", "This is the CloudPelican supervisor")
	jresp.OK()
	fmt.Fprint(w, jresp.ToString(false))
}

func basicAuth(w http.ResponseWriter, r *http.Request) bool {
	if r.Header["Authorization"] == nil || len(r.Header["Authorization"]) < 1 {
		http.Error(w, "bad syntax", http.StatusBadRequest)
		return false
	}
	auth := strings.SplitN(r.Header["Authorization"][0], " ", 2)

	if len(auth) != 2 || auth[0] != "Basic" {
		http.Error(w, "bad syntax", http.StatusBadRequest)
		return false
	}

	payload, _ := base64.StdEncoding.DecodeString(auth[1])
	pair := strings.SplitN(string(payload), ":", 2)

	if len(pair) != 2 || !validateAuth(pair[0], pair[1]) {
		http.Error(w, "authorization failed", http.StatusUnauthorized)
		return false
	}
	return true
}

func validateAuth(username, password string) bool {
	if username == basicAuthUsr && password == basicAuthPwd {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package support

import (
	"fmt"

	"github.com/juju/loggo"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
)

// ModuleSupport is contains implementations of methods that are common to all modules whether they
// be apps or drivers. It provides access to, the module information, the logger and the connection
// to the local message bus.
type ModuleSupport struct {
	Info   *model.Module
	Log    *logger.Logger
	Conn   *ninja.Connection
	sender func(event string, payload interface{}) error
}

// This method is called to initialize the Info, Log and Conn members
// of the receiving ModuleSupport object and to acquire a named
// connection to the local message bus.
//
// Info is initialized with the supplied *model.Module argument
// which must be non-nil and must have a non-empty ID whose value is
// member referred to here as {id}.
//
// Log is initialized with a Logger member named "{id}.module".
//
// Conn is initialized with the results of a call to ninja.Connect
// passing {id} as the client id parameter.
// This connection will log to "{id}.connection".
//
// If initialization was not successful for any reason, either because
// the supplied info object was incomplete or because the connection
// attemped failed, the method will return a non-nil error object and
// the receiver should not be used for any further operations.
//
// However, to avoid the need for the caller to acquire its own logging
// object, and provided the receiver itself is not nil, the Log member of
// the receiver will be initialized with a valid Logger even if initialization
// itself fails.
//
func (self *ModuleSupport) Init(info *model.Module) error {
	log := safeLog(self, info)

	if self == nil {
		return fmt.Errorf("assertion failed: receiver != nil")
	}

	self.Log = log

	if info == nil {
		return fmt.Errorf("invalid argument: info == nil")
	}

	if info.ID == "" {
		return fmt.Errorf("invalid argument: info.ID == \"\"")
	}

	self.Info = info

	conn, err := ninja.Connect(info.ID)
	self.Conn = conn

	return err
}

// Return the module info that describes the module. This will be nil unless the Init
// method has been called.
func (self *ModuleSupport) GetModuleInfo() *model.Module {
	return self.Info
}

// This method can be used by the module itself to emit a payload on one
// of its own event topics. This method should not be called until both
// the Init and Export methods have been called.
func (self *ModuleSupport) SendEvent(event string, payload interface{}) error {
	err := failIfNotInitialized(self)
	if err == nil {
		if self.sender != nil {
			return self.sender(event, payload)
		} else {
			return fmt.Errorf("illegal state: module has not been exported")
		}
	} else {
		return err
	}
}

// This method is used to receive a reference to the event handler that the module
// should use to emit events. Consumers of the ModuleSupport object should not
// need to override this method, but should instead call SendEvent method as required
// to make use of the handler.
func (self *ModuleSupport) SetEventHandler(handler func(event string, payload interface{}) error) {
	// FIXME: this method should probably be renamed to SetEventSender.
	self.sender = handler
}

// Configure the og level of the root logger for the module's process.
func (self *ModuleSupport) SetLogLevel(level string) error {
	// FIXME: maybe move this implementation into the logger package
	parsed, ok := loggo.ParseLevel(level)
	if ok && parsed != loggo.UNSPECIFIED {
		loggo.GetLogger("").SetLogLevel(parsed)
		safeLog(self, nil).Logf(parsed, "Log level has been reset to %s", level)
		return nil
	} else {
		return fmt.Errorf("%s is not a valid logging level")
	}
}

// Return an error if the receiver has not been successfully initialized.
func failIfNotInitialized(self *ModuleSupport) error {
	if self == nil ||
		self.Info == nil ||
		self.Log == nil ||
		self.Conn == nil {
		return fmt.Errorf("illegal state: module has not been successfully initialized")
	} else {
		return nil
	}
}

// Given a possible nil or uninitialized module, always return
// a string that identifies the module in some fashion.
func safeID(info *model.Module) string {
	if info == nil || info.ID == "" {
		return "{uninitialized-module-id}"
	} else {
		return info.ID
	}
}

// this function will always return a logger that can be used even if the
// support object has not been initialized in the correct sequence or with
// the correct arguments.
func safeLog(self *ModuleSupport, info *model.Module) *logger.Logger {
	if self == nil || self.Log == nil {
		return logger.GetLogger(fmt.Sprintf("%s.module", safeID(info)))
	} else {
		return self.Log
	}
}
<commit_msg>Catch a straggler who was left in an edit buffer.<commit_after>package support

import (
	"fmt"

	"github.com/juju/loggo"

	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-ninja/model"
)

// ModuleSupport is contains implementations of methods that are common to all modules whether they
// be apps or drivers. It provides access to, the module information, the logger and the connection
// to the local message bus.
type ModuleSupport struct {
	Info   *model.Module
	Log    *logger.Logger
	Conn   *ninja.Connection
	sender func(event string, payload interface{}) error
}

// This method is called to initialize the Info, Log and Conn members
// of the receiving ModuleSupport object and to acquire a named
// connection to the local message bus.
//
// Info is initialized with the supplied *model.Module argument
// which must be non-nil and must have a non-empty ID whose value is
// member referred to here as {id}.
//
// Log is initialized with a Logger member named "{id}.module".
//
// Conn is initialized with the results of a call to ninja.Connect
// passing {id} as the client id parameter.
// This connection will log to "{id}.connection".
//
// If initialization was not successful for any reason, either because
// the supplied info object was incomplete or because the connection
// attemped failed, the method will return a non-nil error object and
// the receiver should not be used for any further operations.
//
// However, to avoid the need for the caller to acquire its own logging
// object, and provided the receiver itm is not nil, the Log member of
// the receiver will be initialized with a valid Logger even if initialization
// itm fails.
//
func (m *ModuleSupport) Init(info *model.Module) error {
	log := safeLog(m, info)

	if m == nil {
		return fmt.Errorf("assertion failed: receiver != nil")
	}

	m.Log = log

	if info == nil {
		return fmt.Errorf("invalid argument: info == nil")
	}

	if info.ID == "" {
		return fmt.Errorf("invalid argument: info.ID == \"\"")
	}

	m.Info = info

	conn, err := ninja.Connect(info.ID)
	m.Conn = conn

	return err
}

// Return the module info that describes the module. This will be nil unless the Init
// method has been called.
func (m *ModuleSupport) GetModuleInfo() *model.Module {
	return m.Info
}

// This method can be used by the module itm to emit a payload on one
// of its own event topics. This method should not be called until both
// the Init and Export methods have been called.
func (m *ModuleSupport) SendEvent(event string, payload interface{}) error {
	err := failIfNotInitialized(m)
	if err == nil {
		if m.sender != nil {
			return m.sender(event, payload)
		} else {
			return fmt.Errorf("illegal state: module has not been exported")
		}
	} else {
		return err
	}
}

// This method is used to receive a reference to the event handler that the module
// should use to emit events. Consumers of the ModuleSupport object should not
// need to override this method, but should instead call SendEvent method as required
// to make use of the handler.
func (m *ModuleSupport) SetEventHandler(handler func(event string, payload interface{}) error) {
	// FIXME: this method should probably be renamed to SetEventSender.
	m.sender = handler
}

// Configure the og level of the root logger for the module's process.
func (m *ModuleSupport) SetLogLevel(level string) error {
	// FIXME: maybe move this implementation into the logger package
	parsed, ok := loggo.ParseLevel(level)
	if ok && parsed != loggo.UNSPECIFIED {
		loggo.GetLogger("").SetLogLevel(parsed)
		safeLog(m, nil).Logf(parsed, "Log level has been reset to %s", level)
		return nil
	} else {
		return fmt.Errorf("%s is not a valid logging level")
	}
}

// Return an error if the receiver has not been successfully initialized.
func failIfNotInitialized(m *ModuleSupport) error {
	if m == nil ||
		m.Info == nil ||
		m.Log == nil ||
		m.Conn == nil {
		return fmt.Errorf("illegal state: module has not been successfully initialized")
	} else {
		return nil
	}
}

// Given a possible nil or uninitialized module, always return
// a string that identifies the module in some fashion.
func safeID(info *model.Module) string {
	if info == nil || info.ID == "" {
		return "{uninitialized-module-id}"
	} else {
		return info.ID
	}
}

// this function will always return a logger that can be used even if the
// support object has not been initialized in the correct sequence or with
// the correct arguments.
func safeLog(m *ModuleSupport, info *model.Module) *logger.Logger {
	if m == nil || m.Log == nil {
		return logger.GetLogger(fmt.Sprintf("%s.module", safeID(info)))
	} else {
		return m.Log
	}
}
<|endoftext|>


<commit_before>//go:generate protoc  --go_out=plugins=grpc:. telemetry.proto
package telemetry

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/golang/protobuf/proto"
)

var iotKey = flag.String("iot-key", "rsa-private.pem", "Path to RSA private key file for Cloud IoT")
var projectID = flag.String("project-id", "hodoor-211bb", "Project ID for Cloud IoT")

type WrapperMessage struct {
	BinaryData []byte `json:"binary_data"`
}

type Publisher struct {
	key *rsa.PrivateKey
}

func (p *Publisher) Publish(name string, temp float64, on bool) error {
	data, err := marshal(&IOTMessage{
		IotMessage: &IOTMessage_Telemetry{
			Telemetry: &TelemetryMessage{
				Name:        name,
				Temperature: temp,
				On:          on,
			},
		},
	})
	if err != nil {
		return fmt.Errorf("failed to serialise message: %v", err)
	}
	return p.send(data)
}

func (p *Publisher) Hello() error {
	data, err := marshal(&IOTMessage{
		IotMessage: &IOTMessage_Hello{
			Hello: &HelloMessage{},
		},
	})
	if err != nil {
		return fmt.Errorf("failed to serialise message: %v", err)
	}
	return p.send(data)
}

func NewPublisher() *Publisher {
	keyData, err := ioutil.ReadFile(*iotKey)
	if err != nil {
		log.Fatalf("Failed to load IoT private key: %v", err)
	}
	pemBlock, _ := pem.Decode(keyData)
	if pemBlock == nil || pemBlock.Type != "PRIVATE KEY" {
		log.Fatalf("Failed to parse PEM: %v", err)
	}
	key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
	if err != nil {
		log.Fatalf("Failed to parse RSA key: %v", err)
	}
	rsaKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		log.Fatalf("Failed to extract RSA key")
	}

	return &Publisher{
		key: rsaKey,
	}
}

func marshal(message proto.Message) ([]byte, error) {
	data, err := proto.Marshal(message)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal proto: %v", err)
	}

	encoded, err := json.Marshal(&WrapperMessage{
		BinaryData: data,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to wrap message: %v", err)
	}
	return encoded, nil
}

func (p *Publisher) send(data []byte) error {
	now := time.Now()
	token := jwt.NewWithClaims(
		jwt.SigningMethodRS256,
		&jwt.StandardClaims{
			IssuedAt:  now.Unix(),
			ExpiresAt: now.Add(time.Minute).Unix(),
			Audience:  *projectID,
		})
	sig, err := token.SignedString(p.key)
	if err != nil {
		log.Fatalf("Failed to sign JWT: %v", err)
	}

	req, err := http.NewRequest("POST", fmt.Sprintf(
		"https://cloudiotdevice.googleapis.com/v1/projects/%s/locations/%s/registries/%s/devices/%s:publishEvent",
		*projectID, "europe-west1", "hodoor", "hodoor"),
		bytes.NewReader(data))
	if err != nil {
		return fmt.Errorf("Failed to build message: %v", err)
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", sig))
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Cache-Control", "no-cache")

	_, err = http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send startup message: %v", err)
	}
	return nil
}
<commit_msg>Fetch IoT key from env<commit_after>//go:generate protoc  --go_out=plugins=grpc:. telemetry.proto
package telemetry

import (
	"bytes"
	"crypto/rsa"
	"crypto/x509"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/golang/protobuf/proto"
)

var iotKey = flag.String("iot-key", "rsa-private.pem", "Path to RSA private key file for Cloud IoT")
var projectID = flag.String("project-id", "hodoor-211bb", "Project ID for Cloud IoT")

type WrapperMessage struct {
	BinaryData []byte `json:"binary_data"`
}

type Publisher struct {
	key *rsa.PrivateKey
}

func (p *Publisher) Publish(name string, temp float64, on bool) error {
	data, err := marshal(&IOTMessage{
		IotMessage: &IOTMessage_Telemetry{
			Telemetry: &TelemetryMessage{
				Name:        name,
				Temperature: temp,
				On:          on,
			},
		},
	})
	if err != nil {
		return fmt.Errorf("failed to serialise message: %v", err)
	}
	return p.send(data)
}

func (p *Publisher) Hello() error {
	data, err := marshal(&IOTMessage{
		IotMessage: &IOTMessage_Hello{
			Hello: &HelloMessage{},
		},
	})
	if err != nil {
		return fmt.Errorf("failed to serialise message: %v", err)
	}
	return p.send(data)
}

func NewPublisher() *Publisher {
	keyData := []byte(os.Getenv("IOT_KEY"))
	pemBlock, _ := pem.Decode(keyData)
	if pemBlock == nil || pemBlock.Type != "PRIVATE KEY" {
		log.Fatalf(`Failed to parse PEM: ¯\_(ツ)_/¯`)
	}
	key, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
	if err != nil {
		log.Fatalf("Failed to parse RSA key: %v", err)
	}
	rsaKey, ok := key.(*rsa.PrivateKey)
	if !ok {
		log.Fatalf("Failed to extract RSA key")
	}

	return &Publisher{
		key: rsaKey,
	}
}

func marshal(message proto.Message) ([]byte, error) {
	data, err := proto.Marshal(message)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal proto: %v", err)
	}

	encoded, err := json.Marshal(&WrapperMessage{
		BinaryData: data,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to wrap message: %v", err)
	}
	return encoded, nil
}

func (p *Publisher) send(data []byte) error {
	now := time.Now()
	token := jwt.NewWithClaims(
		jwt.SigningMethodRS256,
		&jwt.StandardClaims{
			IssuedAt:  now.Unix(),
			ExpiresAt: now.Add(time.Minute).Unix(),
			Audience:  *projectID,
		})
	sig, err := token.SignedString(p.key)
	if err != nil {
		log.Fatalf("Failed to sign JWT: %v", err)
	}

	req, err := http.NewRequest("POST", fmt.Sprintf(
		"https://cloudiotdevice.googleapis.com/v1/projects/%s/locations/%s/registries/%s/devices/%s:publishEvent",
		*projectID, "europe-west1", "hodoor", "hodoor"),
		bytes.NewReader(data))
	if err != nil {
		return fmt.Errorf("Failed to build message: %v", err)
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", sig))
	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("Cache-Control", "no-cache")

	_, err = http.DefaultClient.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send startup message: %v", err)
	}
	return nil
}
<|endoftext|>


<commit_before>package test_bundle

import (
	"fmt"
	. "github.com/sdboyer/gogl"
	. "github.com/smartystreets/goconvey/convey"
	"testing"
	. "launchpad.net/gocheck"
)

var _ = fmt.Println

var edgeSet = []Edge{
	&BaseEdge{"foo", "bar"},
	&BaseEdge{"bar", "baz"},
}

type GraphFactory struct {
	CreateMutableGraph func() MutableGraph
	CreateGraph        func([]Edge) Graph
}

type GraphSuite struct {
	Graph   Graph
	Factory *GraphFactory
}

type MutableGraphSuite struct {
	Graph   MutableGraph
	Factory *GraphFactory
}

func (s *MutableGraphSuite) SetUpTest(c *C) {
	s.Graph = s.Factory.CreateMutableGraph()
}

func (s *MutableGraphSuite) TestEnsureVertex(c *C) {
	s.Graph.EnsureVertex("foo")
	c.Assert(s.Graph.HasVertex("foo"), Equals, true)
}

func (s *MutableGraphSuite) TestMultiEnsureVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, true)
	c.Assert(s.Graph.HasVertex("baz"), Equals, true)
}

func (s *MutableGraphSuite) TestRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
}

func (s *MutableGraphSuite) TestMultiRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
	c.Assert(s.Graph.HasVertex("baz"), Equals, false)
}

func GraphTestVertexMembership(f *GraphFactory, t *testing.T) {
	g := f.CreateMutableGraph()

	Convey("Test adding, removal, and membership of string literal vertex.", t, func() {
		So(g.HasVertex("foo"), ShouldEqual, false)
		g.EnsureVertex("foo")
		So(g.HasVertex("foo"), ShouldEqual, true)
		g.RemoveVertex("foo")
		So(g.HasVertex("foo"), ShouldEqual, false)
	})

	Convey("Test adding, removal, and membership of int literal vertex.", t, func() {
		So(g.HasVertex(1), ShouldEqual, false)
		g.EnsureVertex(1)
		So(g.HasVertex(1), ShouldEqual, true)
		g.RemoveVertex(1)
		So(g.HasVertex(1), ShouldEqual, false)
	})

	Convey("Test adding, removal, and membership of composite literal vertex.", t, func() {
		So(g.HasVertex(edgeSet[0]), ShouldEqual, false)
		g.EnsureVertex(edgeSet[0])
		So(g.HasVertex(edgeSet[0]), ShouldEqual, true)

		Convey("No membership match on new struct with same values or new pointer", func() {
			So(g.HasVertex(BaseEdge{"foo", "bar"}), ShouldEqual, false)
			So(g.HasVertex(&BaseEdge{"foo", "bar"}), ShouldEqual, false)
		})

		g.RemoveVertex(edgeSet[0])
		So(g.HasVertex(edgeSet[0]), ShouldEqual, false)
	})

}

func GraphTestVertexMultiOps(f *GraphFactory, t *testing.T) {
	g := f.CreateMutableGraph()

	Convey("Add and remove multiple vertices at once.", t, func() {
		g.EnsureVertex("foo", 1, edgeSet[0])
		So(g.HasVertex("foo"), ShouldEqual, true)
		So(g.HasVertex(1), ShouldEqual, true)
		So(g.HasVertex(edgeSet[0]), ShouldEqual, true)

		g.RemoveVertex("foo", 1, edgeSet[0])
		So(g.HasVertex("foo"), ShouldEqual, false)
		So(g.HasVertex(1), ShouldEqual, false)
		So(g.HasVertex(edgeSet[0]), ShouldEqual, false)
	})

	Convey("Ensure zero-length param to add/remove work correctly as no-ops.", t, func() {
		g.EnsureVertex()
		So(g.Order(), ShouldEqual, 0)
		g.RemoveVertex()
		So(g.Order(), ShouldEqual, 0)
	})
}

func GraphTestRemoveVertexWithEdges(f *GraphFactory, t *testing.T) {
	g := f.CreateMutableGraph()

	g.AddEdge(edgeSet[0])
	g.AddEdge(edgeSet[1])

	Convey("Ensure outdegree is decremented when vertex is removed.", t, func() {
		g.RemoveVertex("bar")
		count, exists := g.OutDegree("foo")
		So(count, ShouldEqual, 0)
		So(exists, ShouldEqual, true)
	})
}

func GraphTestEachVertex(f *GraphFactory, t *testing.T) {
	g := f.CreateMutableGraph()

	var hit int
	it := func(v Vertex) {
		hit++
	}

	Convey("With no vertices, EachVertex does not call the injected closure at all.", t, func() {
		g.EachVertex(it)
		So(hit, ShouldEqual, 0)
	})

	// Ensure clean state, since goconvey failures do not stop the test
	hit = 0

	Convey("With two vertices, EachVertex calls the injected closure twice.", t, func() {
		g.EnsureVertex("foo")
		g.EnsureVertex("bar")
		g.EachVertex(it)
		So(hit, ShouldEqual, 2)
	})
}
<commit_msg>Forgot to remove the test bundle.<commit_after><|endoftext|>