<commit_before>package main

import (
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jonhoo/icebreaker/Godeps/_workspace/src/github.com/gin-gonic/gin"
	"github.com/jonhoo/icebreaker/Godeps/_workspace/src/golang.org/x/crypto/sha3"
)

type question struct {
	Text string `json:"text"`
	Inst bool   `json:"by_instructor"`
	By   string `json:"author_nic"`
}

type room struct {
	mx    sync.Mutex
	qs    []question
	icode string
	scode string
}

var rmx sync.Mutex
var rooms map[string]*room

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set")
	}

	rooms = make(map[string]*room)
	router := gin.New()
	router.Use(gin.Logger())
	router.Static("/static", "static")
	router.LoadHTMLGlob("templates/*")

	router.GET("/poll/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		locked := true
		defer func() {
			if locked {
				rmx.Unlock()
			}
		}()

		r, ok := rooms[c.Param("slug")]
		if !ok {
			c.String(http.StatusNotFound, "That room does not exist.")
			return
		}

		if c.Param("code") == r.scode {
			c.String(http.StatusForbidden, "This page is only available to instructors.")
			return
		}
		if c.Param("code") != r.icode {
			c.String(http.StatusUnauthorized, "Wrong code given for this room.")
			return
		}

		since, err := strconv.Atoi(c.DefaultQuery("since", "0"))
		if err != nil || since < 0 {
			c.String(http.StatusBadRequest, "Non-integer 'since' given")
			return
		}

		first := time.Now()
		for since >= len(r.qs) {
			rmx.Unlock()
			locked = false

			// Heroku doesn't allow long-polling for more than 30s
			// without sending data. We could potentially send
			// parts of an empty JSON array or something, but this
			// is vastly simpler, and still improves on the simple
			// polling we used before.
			if time.Now().Sub(first) >= 20*time.Second {
				c.Status(http.StatusNoContent)
				//c.JSON(http.StatusOK, []question{})
				return
			}

			<-time.After(500 * time.Millisecond)
			rmx.Lock()
			locked = true
		}

		c.JSON(http.StatusOK, r.qs[since:])
	})

	router.GET("/room/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		defer rmx.Unlock()
		r, ok := rooms[c.Param("slug")]
		if !ok {
			if len(c.Param("code")) == 8 {
				if _, err := hex.DecodeString(c.Param("code")); err == nil {
					c.String(http.StatusGone,
						"Cannot create rooms with an instructor key that resembles a hash.\nYou may get this error if the instructors haven't created the room yet.",
					)
					return
				}
			}

			h := sha3.Sum512([]byte(c.Param("code")))
			scode := hex.EncodeToString(h[:])[0:8]
			r = &room{
				qs: []question{{
					Text: fmt.Sprintf("Room created. Student code is '%s'", scode),
					Inst: true,
					By:   "<master>",
				}},
				icode: c.Param("code"),
				scode: scode,
			}
			rooms[c.Param("slug")] = r
		}

		if c.Param("code") != r.icode && c.Param("code") != r.scode {
			c.HTML(http.StatusUnauthorized, "bad.tmpl", gin.H{
				"room": c.Param("slug"),
			})
			return
		}

		revqs := make([]question, len(r.qs))
		for i := range r.qs {
			revqs[i] = r.qs[len(r.qs)-i-1]
		}
		c.HTML(http.StatusOK, "room.tmpl", gin.H{
			"room":       c.Param("slug"),
			"instructor": c.Param("code") == r.icode,
			"submitted":  c.DefaultQuery("submitted", "0"),
			"qs":         revqs,
			"scode":      r.scode,
		})
	})

	router.POST("/room/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		defer rmx.Unlock()

		r, ok := rooms[c.Param("slug")]
		if !ok {
			c.String(http.StatusNotFound, "That room does not exist.")
			return
		}

		if c.Param("code") != r.icode && c.Param("code") != r.scode {
			c.String(http.StatusUnauthorized, "Wrong code given for this room.")
			return
		}

		q := c.PostForm("question")
		if strings.TrimSpace(q) == "" {
			c.String(http.StatusBadRequest, "Question was empty.")
			return
		}

		nick := strings.TrimSpace(c.DefaultPostForm("nick", ""))
		r.qs = append(r.qs, question{
			Text: q,
			Inst: c.Param("code") == r.icode,
			By:   nick,
		})
		to := *c.Request.URL
		to.RawQuery = "submitted=1"
		c.Redirect(http.StatusFound, to.RequestURI())
	})

	router.Run(":" + port)
}
<commit_msg>Don't cache poll responses<commit_after>package main

import (
	"encoding/hex"
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/jonhoo/icebreaker/Godeps/_workspace/src/github.com/gin-gonic/gin"
	"github.com/jonhoo/icebreaker/Godeps/_workspace/src/golang.org/x/crypto/sha3"
)

type question struct {
	Text string `json:"text"`
	Inst bool   `json:"by_instructor"`
	By   string `json:"author_nic"`
}

type room struct {
	mx    sync.Mutex
	qs    []question
	icode string
	scode string
}

var rmx sync.Mutex
var rooms map[string]*room

func main() {
	port := os.Getenv("PORT")
	if port == "" {
		log.Fatal("$PORT must be set")
	}

	rooms = make(map[string]*room)
	router := gin.New()
	router.Use(gin.Logger())
	router.Static("/static", "static")
	router.LoadHTMLGlob("templates/*")

	router.GET("/poll/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		locked := true
		defer func() {
			if locked {
				rmx.Unlock()
			}
		}()

		r, ok := rooms[c.Param("slug")]
		if !ok {
			c.String(http.StatusNotFound, "That room does not exist.")
			return
		}

		if c.Param("code") == r.scode {
			c.String(http.StatusForbidden, "This page is only available to instructors.")
			return
		}
		if c.Param("code") != r.icode {
			c.String(http.StatusUnauthorized, "Wrong code given for this room.")
			return
		}

		since, err := strconv.Atoi(c.DefaultQuery("since", "0"))
		if err != nil || since < 0 {
			c.String(http.StatusBadRequest, "Non-integer 'since' given")
			return
		}

		first := time.Now()
		for since >= len(r.qs) {
			rmx.Unlock()
			locked = false

			// Heroku doesn't allow long-polling for more than 30s
			// without sending data. We could potentially send
			// parts of an empty JSON array or something, but this
			// is vastly simpler, and still improves on the simple
			// polling we used before.
			if time.Now().Sub(first) >= 20*time.Second {
				c.Status(http.StatusNoContent)
				//c.JSON(http.StatusOK, []question{})
				return
			}

			<-time.After(500 * time.Millisecond)
			rmx.Lock()
			locked = true
		}

		// For minimal latency between a student asking a question and
		// it appearing in the instructor's view, no caches should
		// store the reply to a poll. Note that this means that if many
		// instructors are accessing the instructor's view, every
		// single one will cause a request to be sent to the backend.
		// For most use-cases, this should not be an issue. That said,
		// if your class has *a lot* of instructors, you may want to
		// set this to something like max-age=5.
		c.Header("Cache-Control", "no-cache")
		c.JSON(http.StatusOK, r.qs[since:])
	})

	router.GET("/room/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		defer rmx.Unlock()
		r, ok := rooms[c.Param("slug")]
		if !ok {
			if len(c.Param("code")) == 8 {
				if _, err := hex.DecodeString(c.Param("code")); err == nil {
					c.String(http.StatusGone,
						"Cannot create rooms with an instructor key that resembles a hash.\nYou may get this error if the instructors haven't created the room yet.",
					)
					return
				}
			}

			h := sha3.Sum512([]byte(c.Param("code")))
			scode := hex.EncodeToString(h[:])[0:8]
			r = &room{
				qs: []question{{
					Text: fmt.Sprintf("Room created. Student code is '%s'", scode),
					Inst: true,
					By:   "<master>",
				}},
				icode: c.Param("code"),
				scode: scode,
			}
			rooms[c.Param("slug")] = r
		}

		if c.Param("code") != r.icode && c.Param("code") != r.scode {
			c.HTML(http.StatusUnauthorized, "bad.tmpl", gin.H{
				"room": c.Param("slug"),
			})
			return
		}

		revqs := make([]question, len(r.qs))
		for i := range r.qs {
			revqs[i] = r.qs[len(r.qs)-i-1]
		}
		c.HTML(http.StatusOK, "room.tmpl", gin.H{
			"room":       c.Param("slug"),
			"instructor": c.Param("code") == r.icode,
			"submitted":  c.DefaultQuery("submitted", "0"),
			"qs":         revqs,
			"scode":      r.scode,
		})
	})

	router.POST("/room/:slug/:code", func(c *gin.Context) {
		rmx.Lock()
		defer rmx.Unlock()

		r, ok := rooms[c.Param("slug")]
		if !ok {
			c.String(http.StatusNotFound, "That room does not exist.")
			return
		}

		if c.Param("code") != r.icode && c.Param("code") != r.scode {
			c.String(http.StatusUnauthorized, "Wrong code given for this room.")
			return
		}

		q := c.PostForm("question")
		if strings.TrimSpace(q) == "" {
			c.String(http.StatusBadRequest, "Question was empty.")
			return
		}

		nick := strings.TrimSpace(c.DefaultPostForm("nick", ""))
		r.qs = append(r.qs, question{
			Text: q,
			Inst: c.Param("code") == r.icode,
			By:   nick,
		})
		to := *c.Request.URL
		to.RawQuery = "submitted=1"
		c.Redirect(http.StatusFound, to.RequestURI())
	})

	router.Run(":" + port)
}
<|endoftext|>


<commit_before>/*
 * prompt.go - Functions for handling user input and options
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.com)
 *
 * 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 main

import (
	"bufio"
	"fmt"
	"log"
	"os"
	"os/user"
	"strconv"
	"strings"

	"github.com/google/fscrypt/actions"
	"github.com/google/fscrypt/metadata"
)

const (
	// Suffixes for questions with a yes or no default
	defaultYesSuffix = " [Y/n] "
	defaultNoSuffix  = " [y/N] "
)

// Descriptions for each of the protector sources
var sourceDescriptions = map[metadata.SourceType]string{
	metadata.SourceType_pam_passphrase:    "Your login passphrase",
	metadata.SourceType_custom_passphrase: "A custom passphrase",
	metadata.SourceType_raw_key:           "A raw 256-bit key",
}

// promptUser presents a message to the user and returns their input string. An
// error is returned if our read from standard input fails.
func promptUser(prompt string) (string, error) {
	scanner := bufio.NewScanner(os.Stdin)
	fmt.Print(prompt)
	if !scanner.Scan() {
		return "", ErrReadingStdin
	}
	return scanner.Text(), nil
}

// askQuestion asks the user a yes or no question. Returning a boolean on a
// successful answer and an error if there was not a response from the user.
// Returns the defaultChoice on empty input (or in quiet mode).
func askQuestion(question string, defaultChoice bool) (bool, error) {
	// If in quiet mode, we just use the default
	if quietFlag.Value {
		return defaultChoice, nil
	}
	// Loop until failure or valid input
	var input string
	var err error
	for {
		if defaultChoice {
			input, err = promptUser(question + defaultYesSuffix)
		} else {
			input, err = promptUser(question + defaultNoSuffix)
		}
		if err != nil {
			return false, err
		}

		switch strings.ToLower(input) {
		case "y", "yes":
			return true, nil
		case "n", "no":
			return false, nil
		case "":
			return defaultChoice, nil
		}
	}
}

// askConfirmation asks the user for confirmation of a specific action. An error
// is returned if the user declines or IO fails.
func askConfirmation(question string, defaultChoice bool, warning string) error {
	// All confirmations are "yes" if we are forcing.
	if forceFlag.Value {
		return nil
	}

	// Defaults of "no" require forcing.
	if !defaultChoice {
		if quietFlag.Value {
			return ErrNoDesctructiveOps
		}
	}

	if warning != "" && !quietFlag.Value {
		fmt.Println(wrapText("WARNING: "+warning, 0))
	}

	confirmed, err := askQuestion(question, defaultChoice)
	if err != nil {
		return err
	}
	if !confirmed {
		return ErrCanceled
	}
	return nil
}

// getUsername returns the username for the provided UID. If the UID does not
// correspond to a user or the username is blank, "UID=<uid>" is returned.
func getUsername(uid int64) string {
	u, err := user.LookupId(strconv.Itoa(int(uid)))
	if err != nil || u.Username == "" {
		return fmt.Sprintf("UID=%d", uid)
	}
	return u.Username
}

// formatInfo gives a string description of metadata.ProtectorData.
func formatInfo(data actions.ProtectorInfo) string {
	switch data.Source() {
	case metadata.SourceType_pam_passphrase:
		return "login protector for " + getUsername(data.UID())
	case metadata.SourceType_custom_passphrase:
		return fmt.Sprintf("custom protector %q", data.Name())
	case metadata.SourceType_raw_key:
		return fmt.Sprintf("raw key protector %q", data.Name())
	default:
		panic(ErrInvalidSource)
	}
}

// promptForName gets a name from user input (or flags) and returns it.
func promptForName(ctx *actions.Context) (string, error) {
	// A name flag means we do not need to prompt
	if nameFlag.Value != "" {
		return nameFlag.Value, nil
	}

	// Don't ask for a name if we do not need it
	if quietFlag.Value || ctx.Config.Source == metadata.SourceType_pam_passphrase {
		return "", nil
	}

	for {
		name, err := promptUser("Enter a name for the new protector: ")
		if err != nil {
			return "", err
		}
		if name != "" {
			return name, nil
		}
	}
}

// promptForSource gets a source type from user input (or flags) and modifies
// the context to use that source.
func promptForSource(ctx *actions.Context) error {
	// A source flag overrides everything else.
	if sourceFlag.Value != "" {
		val, ok := metadata.SourceType_value[sourceFlag.Value]
		if !ok || val == 0 {
			return ErrInvalidSource
		}
		ctx.Config.Source = metadata.SourceType(val)
		return nil
	}

	// Just use the default in quiet mode
	if quietFlag.Value {
		return nil
	}

	// We print all the sources with their number, description, and name.
	fmt.Println("Your data can be protected with one of the following sources:")
	for idx := 1; idx < len(metadata.SourceType_value); idx++ {
		source := metadata.SourceType(idx)
		description := sourceDescriptions[source]
		fmt.Printf("%d - %s (%s)\n", idx, description, source)
	}

	prompt := fmt.Sprintf("Enter the source number for the new protector [%d - %s]: ",
		ctx.Config.Source, ctx.Config.Source)
	for {
		input, err := promptUser(prompt)
		if err != nil {
			return err
		}

		// Use the default if the user just hits enter
		if input == "" {
			return nil
		}

		// Check for a valid index, reprompt if invalid.
		index, err := strconv.Atoi(input)
		if err == nil && index >= 1 && index < len(metadata.SourceType_value) {
			ctx.Config.Source = metadata.SourceType(index)
			return nil
		}
	}
}

// promptForKeyFile returns an open file that should be used to create or unlock
// a raw_key protector. Be sure to close the file when done.
func promptForKeyFile(prompt string) (*os.File, error) {
	// If specified on the command line, we only try no open it once.
	if keyFileFlag.Value != "" {
		return os.Open(keyFileFlag.Value)
	}
	if quietFlag.Value {
		return nil, ErrSpecifyKeyFile
	}

	// Prompt for a valid path until we get a file we can open.
	for {
		filename, err := promptUser(prompt)
		if err != nil {
			return nil, err
		}
		file, err := os.Open(filename)
		if err == nil {
			return file, nil
		}
		fmt.Println(err)
	}

}

// promptForProtector, given a non-empty list of protector options, uses user
// input to select the desired protector. If there is only one option to choose
// from, that protector is automatically selected.
func promptForProtector(options []*actions.ProtectorOption) (int, error) {
	numOptions := len(options)
	log.Printf("selecting from %s", pluralize(numOptions, "protector"))

	// Get the number of load errors.
	numLoadErrors := 0
	for _, option := range options {
		if option.LoadError != nil {
			log.Printf("when loading option: %v", option.LoadError)
			numLoadErrors++
		}
	}

	if numLoadErrors == numOptions {
		return 0, ErrAllLoadsFailed
	}
	if numOptions == 1 {
		return 0, nil
	}
	if quietFlag.Value {
		return 0, ErrSpecifyProtector
	}

	// List all of the protector options which did not have a load error.
	fmt.Println("The available protectors are: ")
	for idx, option := range options {
		if option.LoadError != nil {
			continue
		}

		description := fmt.Sprintf("%d - %s", idx, formatInfo(option.ProtectorInfo))
		if option.LinkedMount != nil {
			description += fmt.Sprintf(" (linked protector on %q)", option.LinkedMount.Path)
		}
		fmt.Println(description)
	}

	if numLoadErrors > 0 {
		fmt.Printf(wrapText("NOTE: %d of the %d protectors failed to load. "+loadHelpText, 0))
	}

	for {
		input, err := promptUser("Enter the number of protector to use: ")
		if err != nil {
			return 0, err
		}

		// Check for a valid index, reprompt if invalid.
		index, err := strconv.Atoi(input)
		if err == nil && index >= 0 && index < len(options) {
			return index, nil
		}
	}
}

// optionFn is an actions.OptionFunc which handles selecting an option for a
// specific policy. This is either done interactively, or by deferring to the
// protectorFlag.
func optionFn(policyDescriptor string, options []*actions.ProtectorOption) (int, error) {
	// If we have an unlock-with flag, we directly select the specified
	// protector to unlock the policy.
	if unlockWithFlag.Value != "" {
		log.Printf("optionFn(%s) w/ unlock flag", policyDescriptor)
		protector, err := getProtectorFromFlag(unlockWithFlag.Value)
		if err != nil {
			return 0, err
		}

		for idx, option := range options {
			if option.Descriptor() == protector.Descriptor() {
				return idx, nil
			}
		}
		return 0, actions.ErrNotProtected
	}

	log.Printf("optionFn(%s)", policyDescriptor)
	return promptForProtector(options)
}
<commit_msg>cmd/fscrypt: prompts now use command functions<commit_after>/*
 * prompt.go - Functions for handling user input and options
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.com)
 *
 * 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 main

import (
	"fmt"
	"log"
	"os"
	"os/user"
	"strconv"
	"strings"

	"github.com/google/fscrypt/actions"
	"github.com/google/fscrypt/metadata"
	"github.com/google/fscrypt/util"
)

const (
	// Suffixes for questions with a yes or no default
	defaultYesSuffix = " [Y/n] "
	defaultNoSuffix  = " [y/N] "
)

// Descriptions for each of the protector sources
var sourceDescriptions = map[metadata.SourceType]string{
	metadata.SourceType_pam_passphrase:    "Your login passphrase",
	metadata.SourceType_custom_passphrase: "A custom passphrase",
	metadata.SourceType_raw_key:           "A raw 256-bit key",
}

// askQuestion asks the user a yes or no question. Returning a boolean on a
// successful answer and an error if there was not a response from the user.
// Returns the defaultChoice on empty input (or in quiet mode).
func askQuestion(question string, defaultChoice bool) (bool, error) {
	// If in quiet mode, we just use the default
	if quietFlag.Value {
		return defaultChoice, nil
	}
	// Loop until failure or valid input
	for {
		if defaultChoice {
			fmt.Print(question + defaultYesSuffix)
		} else {
			fmt.Print(question + defaultNoSuffix)
		}

		input, err := util.ReadLine()
		if err != nil {
			return false, err
		}

		switch strings.ToLower(input) {
		case "y", "yes":
			return true, nil
		case "n", "no":
			return false, nil
		case "":
			return defaultChoice, nil
		}
	}
}

// askConfirmation asks the user for confirmation of a specific action. An error
// is returned if the user declines or IO fails.
func askConfirmation(question string, defaultChoice bool, warning string) error {
	// All confirmations are "yes" if we are forcing.
	if forceFlag.Value {
		return nil
	}

	// Defaults of "no" require forcing.
	if !defaultChoice {
		if quietFlag.Value {
			return ErrNoDesctructiveOps
		}
	}

	if warning != "" && !quietFlag.Value {
		fmt.Println(wrapText("WARNING: "+warning, 0))
	}

	confirmed, err := askQuestion(question, defaultChoice)
	if err != nil {
		return err
	}
	if !confirmed {
		return ErrCanceled
	}
	return nil
}

// getUsername returns the username for the provided UID. If the UID does not
// correspond to a user or the username is blank, "UID=<uid>" is returned.
func getUsername(uid int64) string {
	u, err := user.LookupId(strconv.Itoa(int(uid)))
	if err != nil || u.Username == "" {
		return fmt.Sprintf("UID=%d", uid)
	}
	return u.Username
}

// formatInfo gives a string description of metadata.ProtectorData.
func formatInfo(data actions.ProtectorInfo) string {
	switch data.Source() {
	case metadata.SourceType_pam_passphrase:
		return "login protector for " + getUsername(data.UID())
	case metadata.SourceType_custom_passphrase:
		return fmt.Sprintf("custom protector %q", data.Name())
	case metadata.SourceType_raw_key:
		return fmt.Sprintf("raw key protector %q", data.Name())
	default:
		panic(ErrInvalidSource)
	}
}

// promptForName gets a name from user input (or flags) and returns it.
func promptForName(ctx *actions.Context) (string, error) {
	// A name flag means we do not need to prompt
	if nameFlag.Value != "" {
		return nameFlag.Value, nil
	}

	// Don't ask for a name if we do not need it
	if quietFlag.Value || ctx.Config.Source == metadata.SourceType_pam_passphrase {
		return "", nil
	}

	for {
		fmt.Print("Enter a name for the new protector: ")
		name, err := util.ReadLine()
		if err != nil {
			return "", err
		}
		if name != "" {
			return name, nil
		}
	}
}

// promptForSource gets a source type from user input (or flags) and modifies
// the context to use that source.
func promptForSource(ctx *actions.Context) error {
	// A source flag overrides everything else.
	if sourceFlag.Value != "" {
		val, ok := metadata.SourceType_value[sourceFlag.Value]
		if !ok || val == 0 {
			return ErrInvalidSource
		}
		ctx.Config.Source = metadata.SourceType(val)
		return nil
	}

	// Just use the default in quiet mode
	if quietFlag.Value {
		return nil
	}

	// We print all the sources with their number, description, and name.
	fmt.Println("Your data can be protected with one of the following sources:")
	for idx := 1; idx < len(metadata.SourceType_value); idx++ {
		source := metadata.SourceType(idx)
		description := sourceDescriptions[source]
		fmt.Printf("%d - %s (%s)\n", idx, description, source)
	}

	for {
		fmt.Printf("Enter the source number for the new protector [%d - %s]: ",
			ctx.Config.Source, ctx.Config.Source)
		input, err := util.ReadLine()
		if err != nil {
			return err
		}

		// Use the default if the user just hits enter
		if input == "" {
			return nil
		}

		// Check for a valid index, reprompt if invalid.
		index, err := strconv.Atoi(input)
		if err == nil && index >= 1 && index < len(metadata.SourceType_value) {
			ctx.Config.Source = metadata.SourceType(index)
			return nil
		}
	}
}

// promptForKeyFile returns an open file that should be used to create or unlock
// a raw_key protector. Be sure to close the file when done.
func promptForKeyFile(prompt string) (*os.File, error) {
	// If specified on the command line, we only try no open it once.
	if keyFileFlag.Value != "" {
		return os.Open(keyFileFlag.Value)
	}
	if quietFlag.Value {
		return nil, ErrSpecifyKeyFile
	}

	// Prompt for a valid path until we get a file we can open.
	for {
		fmt.Print(prompt)
		filename, err := util.ReadLine()
		if err != nil {
			return nil, err
		}
		file, err := os.Open(filename)
		if err == nil {
			return file, nil
		}
		fmt.Println(err)
	}

}

// promptForProtector, given a non-empty list of protector options, uses user
// input to select the desired protector. If there is only one option to choose
// from, that protector is automatically selected.
func promptForProtector(options []*actions.ProtectorOption) (int, error) {
	numOptions := len(options)
	log.Printf("selecting from %s", pluralize(numOptions, "protector"))

	// Get the number of load errors.
	numLoadErrors := 0
	for _, option := range options {
		if option.LoadError != nil {
			log.Printf("when loading option: %v", option.LoadError)
			numLoadErrors++
		}
	}

	if numLoadErrors == numOptions {
		return 0, ErrAllLoadsFailed
	}
	if numOptions == 1 {
		return 0, nil
	}
	if quietFlag.Value {
		return 0, ErrSpecifyProtector
	}

	// List all of the protector options which did not have a load error.
	fmt.Println("The available protectors are: ")
	for idx, option := range options {
		if option.LoadError != nil {
			continue
		}

		description := fmt.Sprintf("%d - %s", idx, formatInfo(option.ProtectorInfo))
		if option.LinkedMount != nil {
			description += fmt.Sprintf(" (linked protector on %q)", option.LinkedMount.Path)
		}
		fmt.Println(description)
	}

	if numLoadErrors > 0 {
		fmt.Printf(wrapText("NOTE: %d of the %d protectors failed to load. "+loadHelpText, 0))
	}

	for {
		fmt.Print("Enter the number of protector to use: ")
		input, err := util.ReadLine()
		if err != nil {
			return 0, err
		}

		// Check for a valid index, reprompt if invalid.
		index, err := strconv.Atoi(input)
		if err == nil && index >= 0 && index < len(options) {
			return index, nil
		}
	}
}

// optionFn is an actions.OptionFunc which handles selecting an option for a
// specific policy. This is either done interactively, or by deferring to the
// protectorFlag.
func optionFn(policyDescriptor string, options []*actions.ProtectorOption) (int, error) {
	// If we have an unlock-with flag, we directly select the specified
	// protector to unlock the policy.
	if unlockWithFlag.Value != "" {
		log.Printf("optionFn(%s) w/ unlock flag", policyDescriptor)
		protector, err := getProtectorFromFlag(unlockWithFlag.Value)
		if err != nil {
			return 0, err
		}

		for idx, option := range options {
			if option.Descriptor() == protector.Descriptor() {
				return idx, nil
			}
		}
		return 0, actions.ErrNotProtected
	}

	log.Printf("optionFn(%s)", policyDescriptor)
	return promptForProtector(options)
}
<|endoftext|>


<commit_before>package internal

import (
	"errors"
	"fmt"
	"math"
	"sync"

	"github.com/ready-steady/probability"
	"github.com/ready-steady/probability/uniform"
)

type Target interface {
	Dimensions() (uint, uint)
	Compute([]float64, []float64)
	Refine([]float64, []float64, float64) float64
	Monitor(uint, uint, uint)

	Config() *TargetConfig
	Generate(uint) []float64
}

func NewTarget(problem *Problem) (Target, error) {
	config := problem.Config.Target

	if len(config.Stencil) == 0 {
		config.Stencil = []bool{true, false}
	}

	switch config.Name {
	case "end-to-end-delay":
		return newDelayTarget(problem, &config), nil
	case "total-energy":
		return newEnergyTarget(problem, &config), nil
	case "temperature-slice":
		return newSliceTarget(problem, &config, &problem.Config.Temperature)
	case "temperature-switch":
		return newSwitchTarget(problem, &config, &problem.Config.Temperature)
	case "temperature-profile":
		return newProfileTarget(problem, &config, &problem.Config.Temperature)
	default:
		return nil, errors.New("the target is unknown")
	}
}

func String(target Target) string {
	ni, no := target.Dimensions()
	return fmt.Sprintf("Target{inputs: %d, outputs: %d}", ni, no)
}

func Refine(target Target, _, surplus []float64, volume float64) float64 {
	config := target.Config()

	stencil := config.Stencil

	no, ns := uint(len(surplus)), uint(len(stencil))

	score := 0.0
	for i := uint(0); i < no; i++ {
		if stencil[i%ns] {
			score += surplus[i] * surplus[i]
		}
	}
	score = volume * math.Sqrt(score)

	if score <= config.Tolerance {
		score = 0
	}

	return score
}

func Monitor(target Target, k, np, na uint) {
	if !target.Config().Verbose {
		return
	}
	if k == 0 {
		fmt.Printf("%10s %15s %15s\n", "Iteration", "Passive Nodes", "Active Nodes")
	}
	fmt.Printf("%10d %15d %15d\n", k, np, na)
}

func Generate(target Target, ns uint) []float64 {
	ni, _ := target.Dimensions()
	return probability.Sample(uniform.New(0, 1), ns*ni)
}

func Invoke(target Target, points []float64, nw uint) []float64 {
	ni, no := target.Dimensions()
	np := uint(len(points)) / ni

	values := make([]float64, np*no)
	jobs := make(chan uint, np)
	group := sync.WaitGroup{}
	group.Add(int(np))

	for i := uint(0); i < nw; i++ {
		go func() {
			for j := range jobs {
				target.Compute(points[j*ni:(j+1)*ni], values[j*no:(j+1)*no])
				group.Done()
			}
		}()
	}

	for i := uint(0); i < np; i++ {
		jobs <- i
	}

	group.Wait()
	close(jobs)

	return values
}
<commit_msg>Do not take volume into account in Refine<commit_after>package internal

import (
	"errors"
	"fmt"
	"math"
	"sync"

	"github.com/ready-steady/probability"
	"github.com/ready-steady/probability/uniform"
)

type Target interface {
	Dimensions() (uint, uint)
	Compute([]float64, []float64)
	Refine([]float64, []float64, float64) float64
	Monitor(uint, uint, uint)

	Config() *TargetConfig
	Generate(uint) []float64
}

func NewTarget(problem *Problem) (Target, error) {
	config := problem.Config.Target

	if len(config.Stencil) == 0 {
		config.Stencil = []bool{true, false}
	}

	switch config.Name {
	case "end-to-end-delay":
		return newDelayTarget(problem, &config), nil
	case "total-energy":
		return newEnergyTarget(problem, &config), nil
	case "temperature-slice":
		return newSliceTarget(problem, &config, &problem.Config.Temperature)
	case "temperature-switch":
		return newSwitchTarget(problem, &config, &problem.Config.Temperature)
	case "temperature-profile":
		return newProfileTarget(problem, &config, &problem.Config.Temperature)
	default:
		return nil, errors.New("the target is unknown")
	}
}

func String(target Target) string {
	ni, no := target.Dimensions()
	return fmt.Sprintf("Target{inputs: %d, outputs: %d}", ni, no)
}

func Refine(target Target, _, surplus []float64, _ float64) float64 {
	config := target.Config()

	stencil := config.Stencil

	no, ns := uint(len(surplus)), uint(len(stencil))

	score := 0.0
	for i := uint(0); i < no; i++ {
		if stencil[i%ns] {
			score += surplus[i] * surplus[i]
		}
	}
	score = math.Sqrt(score)

	if score <= config.Tolerance {
		score = 0
	}

	return score
}

func Monitor(target Target, k, np, na uint) {
	if !target.Config().Verbose {
		return
	}
	if k == 0 {
		fmt.Printf("%10s %15s %15s\n", "Iteration", "Passive Nodes", "Active Nodes")
	}
	fmt.Printf("%10d %15d %15d\n", k, np, na)
}

func Generate(target Target, ns uint) []float64 {
	ni, _ := target.Dimensions()
	return probability.Sample(uniform.New(0, 1), ns*ni)
}

func Invoke(target Target, points []float64, nw uint) []float64 {
	ni, no := target.Dimensions()
	np := uint(len(points)) / ni

	values := make([]float64, np*no)
	jobs := make(chan uint, np)
	group := sync.WaitGroup{}
	group.Add(int(np))

	for i := uint(0); i < nw; i++ {
		go func() {
			for j := range jobs {
				target.Compute(points[j*ni:(j+1)*ni], values[j*no:(j+1)*no])
				group.Done()
			}
		}()
	}

	for i := uint(0); i < np; i++ {
		jobs <- i
	}

	group.Wait()
	close(jobs)

	return values
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"testing"

	"github.com/jbenet/go-ipfs/tour"
)

func TestParseTourTemplate(t *testing.T) {
	topic := &tour.Topic{
		ID:    "42",
		Title: "IPFS CLI test files",
		Text: `
Welcome to the IPFS test files
This is where we test our beautiful command line interfaces
		`,
	}
	var buf bytes.Buffer
	err := fprintTourShow(&buf, topic)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(buf.String())
}
<commit_msg>fix(test) tour content struct now exists<commit_after>package main

import (
	"bytes"
	"testing"

	"github.com/jbenet/go-ipfs/tour"
)

func TestParseTourTemplate(t *testing.T) {
	topic := &tour.Topic{
		ID: "42",
		Content: tour.Content{
			Title: "IPFS CLI test files",
			Text: `
Welcome to the IPFS test files
This is where we test our beautiful command line interfaces
		`,
		},
	}
	var buf bytes.Buffer
	err := fprintTourShow(&buf, topic)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(buf.String())
}
<|endoftext|>


<commit_before>//-----------------------------------------------------------------------------
// Package membership:
//-----------------------------------------------------------------------------

package main

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Standard library:
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"

	// Community:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/packethost/packngo"
	"gopkg.in/alecthomas/kingpin.v2"
)

//----------------------------------------------------------------------------
// Typedefs:
//----------------------------------------------------------------------------

type userData struct {
	HostId           string
	Domain           string
	Role             string
	Ns1ApiKey        string
	FleetTags        string
	CaCert           string
	EtcdToken        string
	FlannelNetwork   string
	FlannelSubnetLen string
	FlannelSubnetMin string
	FlannelSubnetMax string
	FlannelBackend   string
}

//-----------------------------------------------------------------------------
// Package variable declarations factored into a block:
//-----------------------------------------------------------------------------

var (

	//----------------------------
	// katoctl: top level command
	//----------------------------

	app = kingpin.New("katoctl", "Katoctl defines and deploys CoreOS clusters.")

	//-----------------------
	// udata: nested command
	//-----------------------

	cmdUdata = app.Command("udata", "Generate CoreOS cloud-config user-data.")

	flHostId = cmdUdata.Flag("hostid", "hostname = role-id").
			Required().PlaceHolder("CS_HOSTID").
			OverrideDefaultFromEnvar("CS_HOSTID").
			Short('i').String()

	flDomain = cmdUdata.Flag("domain", "Domain name as in (hostname -d)").
			Required().PlaceHolder("CS_DOMAIN").
			OverrideDefaultFromEnvar("CS_DOMAIN").
			Short('d').String()

	flRole = cmdUdata.Flag("role", "Choose one of [ master | node | edge ]").
			Required().PlaceHolder("CS_ROLE").
			OverrideDefaultFromEnvar("CS_ROLE").
			Short('r').String()

	flNs1Apikey = cmdUdata.Flag("ns1-api-key", "NS1 private API key.").
			Required().PlaceHolder("CS_NS1_API_KEY").
			OverrideDefaultFromEnvar("CS_NS1_API_KEY").
			Short('k').String()

	flFleetTags = cmdUdata.Flag("fleet-tags", "Comma separated list of fleet tags.").
			PlaceHolder("CS_FLEET_TAGS").
			OverrideDefaultFromEnvar("CS_FLEET_TAGS").
			Short('t').String()

	flCAcert = cmdUdata.Flag("ca-cert", "Path to CA certificate.").
			PlaceHolder("CS_CA_CERT").
			OverrideDefaultFromEnvar("CS_CA_CERT").
			Short('c').String()

	flEtcdToken = cmdUdata.Flag("etcd-token", "Provide an etcd discovery token.").
			PlaceHolder("CS_ETCD_TOKEN").
			OverrideDefaultFromEnvar("CS_ETCD_TOKEN").
			Short('e').String()

	flFlannelNetwork = cmdUdata.Flag("flannel-network", "Flannel entire overlay network.").
			PlaceHolder("CS_FLANNEL_NETWORK").
			OverrideDefaultFromEnvar("CS_FLANNEL_NETWORK").
			Short('n').String()

	flFlannelSubnetLen = cmdUdata.Flag("flannel-subnet-len", "Subnet len to llocate to each host.").
			PlaceHolder("CS_FLANNEL_SUBNET_LEN").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_LEN").
			Short('s').String()

	flFlannelSubnetMin = cmdUdata.Flag("flannel-subnet-min", "Minimum subnet IP addresses.").
			PlaceHolder("CS_FLANNEL_SUBNET_MIN").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MIN").
			Short('m').String()

	flFlannelSubnetMax = cmdUdata.Flag("flannel-subnet-max", "Maximum subnet IP addresses.").
			PlaceHolder("CS_FLANNEL_SUBNET_MAX").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MAX").
			Short('x').String()

	flFlannelBackend = cmdUdata.Flag("flannel-backend", "Flannel backend type: [ udp | vxlan | host-gw | gce | aws-vpc | alloc ]").
			PlaceHolder("CS_FLANNEL_SUBNET_MAX").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MAX").
			Short('b').String()

	//----------------------------
	// run-packet: nested command
	//----------------------------

	cmdRunPacket = app.Command("run-packet", "Starts a CoreOS instance on Packet.net")

	flPktApiKey = cmdRunPacket.Flag("api-key", "Packet API key.").
			Required().PlaceHolder("PKT_APIKEY").
			OverrideDefaultFromEnvar("PKT_APIKEY").
			Short('k').String()

	flPktHostName = cmdRunPacket.Flag("hostname", "For the Packet.net dashboard.").
			Required().PlaceHolder("PKT_HOSTNAME").
			OverrideDefaultFromEnvar("PKT_HOSTNAME").
			Short('h').String()

	flPktProjId = cmdRunPacket.Flag("project-id", "Format: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee").
			Required().PlaceHolder("PKT_PROJID").
			OverrideDefaultFromEnvar("PKT_PROJID").
			Short('i').String()

	flPktPlan = cmdRunPacket.Flag("plan", "One of [ baremetal_0 | baremetal_1 | baremetal_2 | baremetal_3 ]").
			Required().PlaceHolder("PKT_PLAN").
			OverrideDefaultFromEnvar("PKT_PLAN").
			Short('p').String()

	flPktOsys = cmdRunPacket.Flag("os", "One of [ coreos_stable | coreos_beta | coreos_alpha ]").
			Required().PlaceHolder("PKT_OS").
			OverrideDefaultFromEnvar("PKT_OS").
			Short('o').String()

	flPktFacility = cmdRunPacket.Flag("facility", "One of [ ewr1 | ams1 ]").
			Required().PlaceHolder("PKT_FACILITY").
			OverrideDefaultFromEnvar("PKT_FACILITY").
			Short('f').String()

	flPktBilling = cmdRunPacket.Flag("billing", "One of [ hourly | monthly ]").
			Required().PlaceHolder("PKT_BILLING").
			OverrideDefaultFromEnvar("PKT_BILLING").
			Short('b').String()

	//-------------------------
	// run-ec2: nested command
	//-------------------------

	cmdRunEc2 = app.Command("run-ec2", "Starts a CoreOS instance on Amazon EC2")

	flEc2HostName = cmdRunEc2.Flag("hostname", "For the EC2 dashboard.").
			PlaceHolder("EC2_HOSTNAME").
			OverrideDefaultFromEnvar("EC2_HOSTNAME").
			Short('h').String()

	flEc2Region = cmdRunEc2.Flag("region", "EC2 region.").
			Required().PlaceHolder("EC2_REGION").
			OverrideDefaultFromEnvar("EC2_REGION").
			Short('r').String()

	flEc2ImageId = cmdRunEc2.Flag("image-id", "EC2 image id.").
			Required().PlaceHolder("EC2_IMAGE_ID").
			OverrideDefaultFromEnvar("EC2_IMAGE_ID").
			Short('i').String()

	flEc2InsType = cmdRunEc2.Flag("instance-type", "EC2 instance type.").
			Required().PlaceHolder("EC2_INSTANCE_TYPE").
			OverrideDefaultFromEnvar("EC2_INSTANCE_TYPE").
			Short('t').String()

	flEc2KeyPair = cmdRunEc2.Flag("key-pair", "EC2 key pair.").
			Required().PlaceHolder("EC2_KEY_PAIR").
			OverrideDefaultFromEnvar("EC2_KEY_PAIR").
			Short('k').String()

	flEc2VpcId = cmdRunEc2.Flag("vpc-id", "EC2 VPC id.").
			Required().PlaceHolder("EC2_VPC_ID").
			OverrideDefaultFromEnvar("EC2_VPC_ID").
			Short('v').String()

	flEc2SubnetIds = cmdRunEc2.Flag("subnet-ids", "EC2 subnet ids.").
			Required().PlaceHolder("EC2_SUBNET_ID").
			OverrideDefaultFromEnvar("EC2_SUBNET_ID").
			Short('s').String()

	flEc2ElasticIp = cmdRunEc2.Flag("elastic-ip", "Allocate an elastic IP [ true | false ]").
			Default("false").PlaceHolder("EC2_ELASTIC_IP").
			OverrideDefaultFromEnvar("EC2_ELASTIC_IP").
			Short('e').String()
)

//----------------------------------------------------------------------------
// Entry point:
//----------------------------------------------------------------------------

func main() {

	// Sub-command selector:
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	// katoctl udata ...
	case cmdUdata.FullCommand():
		udata()

	// katoctl run-packet ...
	case cmdRunPacket.FullCommand():
		runPacket()

	// katoctl run-ec2 ...
	case cmdRunEc2.FullCommand():
		runEc2()
	}
}

//--------------------------------------------------------------------------
// func: udata
//--------------------------------------------------------------------------

func udata() {

	// Template udata structure:
	udata := userData {
		HostId:           *flHostId,
		Domain:           *flDomain,
		Role:             *flRole,
		Ns1ApiKey:        *flNs1Apikey,
		FleetTags:        *flFleetTags,
		EtcdToken:        *flEtcdToken,
		FlannelNetwork:   *flFlannelNetwork,
		FlannelSubnetLen: *flFlannelSubnetLen,
		FlannelSubnetMin: *flFlannelSubnetMin,
		FlannelSubnetMax: *flFlannelSubnetMax,
		FlannelBackend:   *flFlannelBackend,
	}

	// Read the CA certificate:
	if *flCAcert != "" {
		dat, err := ioutil.ReadFile(*flCAcert)
		checkError(err)
		udata.CaCert = strings.TrimSpace(strings.Replace(string(dat), "\n", "\n    ", -1))
	}

	// Render the template for the selected role:
	switch *flRole {
	case "master":
		t := template.New("master_udata")
		t, err := t.Parse(templMaster)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	case "node":
		t := template.New("node_udata")
		t, err := t.Parse(templNode)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	case "edge":
		t := template.New("edge_udata")
		t, err := t.Parse(templEdge)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	}
}

//--------------------------------------------------------------------------
// func: runPacket
//--------------------------------------------------------------------------

func runPacket() {

	// Read user-data from stdin:
	udata, err := ioutil.ReadAll(os.Stdin)
	checkError(err)

	// Connect and authenticate to the API endpoint:
	client := packngo.NewClient("", *flPktApiKey, nil)

	// Forge the request:
	createRequest := &packngo.DeviceCreateRequest{
		HostName:     *flPktHostName,
		Plan:         *flPktPlan,
		Facility:     *flPktFacility,
		OS:           *flPktOsys,
		BillingCycle: *flPktBilling,
		ProjectID:    *flPktProjId,
		UserData:     string(udata),
	}

	// Send the request:
	newDevice, _, err := client.Devices.Create(createRequest)
	checkError(err)

	// Pretty-print the response data:
	fmt.Println(newDevice)
}

//--------------------------------------------------------------------------
// func: runEc2
//--------------------------------------------------------------------------

func runEc2() {

	// Read user-data from stdin:
	udata, err := ioutil.ReadAll(os.Stdin)
	checkError(err)

	// Connect and authenticate to the API endpoint:
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(*flEc2Region)}))

	// Forge the network interfaces:
	var networkInterfaces []*ec2.InstanceNetworkInterfaceSpecification
	subnetIds := strings.Split(*flEc2SubnetIds, ",")

	for i := 0; i < len(subnetIds); i++ {

		// Forge the security group ids:
		var securityGroupIds []*string
		for _, gid := range strings.Split(subnetIds[i], ":")[1:] {
			securityGroupIds = append(securityGroupIds, aws.String(gid))
		}

		iface := ec2.InstanceNetworkInterfaceSpecification{
			DeleteOnTermination: aws.Bool(true),
			DeviceIndex:         aws.Int64(int64(i)),
			Groups:              securityGroupIds,
			SubnetId:            aws.String(strings.Split(subnetIds[i], ":")[0]),
		}

		networkInterfaces = append(networkInterfaces, &iface)
	}

	// Send the request:
	runResult, err := svc.RunInstances(&ec2.RunInstancesInput{
		ImageId:           aws.String(*flEc2ImageId),
		MinCount:          aws.Int64(1),
		MaxCount:          aws.Int64(1),
		KeyName:           aws.String(*flEc2KeyPair),
		InstanceType:      aws.String(*flEc2InsType),
		NetworkInterfaces: networkInterfaces,
		UserData:          aws.String(base64.StdEncoding.EncodeToString([]byte(udata))),
	})

	checkError(err)

	// Pretty-print the response data:
	fmt.Println("Created instance", *runResult.Instances[0].InstanceId)

	// Add tags to the created instance:
	_, err = svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{runResult.Instances[0].InstanceId},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String("Name"),
				Value: aws.String(*flEc2HostName),
			},
		},
	})

	checkError(err)

	// Allocate an elastic IP address:
	if *flEc2ElasticIp == "true" {

		params := &ec2.AllocateAddressInput{
			Domain: aws.String("vpc"),
			DryRun: aws.Bool(false),
		}

		// Send the request:
		resp, err := svc.AllocateAddress(params)
		checkError(err)

		// Pretty-print the response data:
		fmt.Println(resp)
	}
}

//---------------------------------------------------------------------------
// func: checkError
//---------------------------------------------------------------------------

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}
<commit_msg>Fix golint warnings<commit_after>//-----------------------------------------------------------------------------
// Package membership:
//-----------------------------------------------------------------------------

package main

//-----------------------------------------------------------------------------
// Package factored import statement:
//-----------------------------------------------------------------------------

import (

	// Standard library:
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"

	// Community:
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/packethost/packngo"
	"gopkg.in/alecthomas/kingpin.v2"
)

//----------------------------------------------------------------------------
// Typedefs:
//----------------------------------------------------------------------------

type userData struct {
	HostId           string
	Domain           string
	Role             string
	Ns1ApiKey        string
	FleetTags        string
	CaCert           string
	EtcdToken        string
	FlannelNetwork   string
	FlannelSubnetLen string
	FlannelSubnetMin string
	FlannelSubnetMax string
	FlannelBackend   string
}

//-----------------------------------------------------------------------------
// Package variable declarations factored into a block:
//-----------------------------------------------------------------------------

var (

	//----------------------------
	// katoctl: top level command
	//----------------------------

	app = kingpin.New("katoctl", "Katoctl defines and deploys CoreOS clusters.")

	//-----------------------
	// udata: nested command
	//-----------------------

	cmdUdata = app.Command("udata", "Generate CoreOS cloud-config user-data.")

	flHostID = cmdUdata.Flag("hostid", "hostname = role-id").
			Required().PlaceHolder("CS_HOSTID").
			OverrideDefaultFromEnvar("CS_HOSTID").
			Short('i').String()

	flDomain = cmdUdata.Flag("domain", "Domain name as in (hostname -d)").
			Required().PlaceHolder("CS_DOMAIN").
			OverrideDefaultFromEnvar("CS_DOMAIN").
			Short('d').String()

	flRole = cmdUdata.Flag("role", "Choose one of [ master | node | edge ]").
			Required().PlaceHolder("CS_ROLE").
			OverrideDefaultFromEnvar("CS_ROLE").
			Short('r').String()

	flNs1Apikey = cmdUdata.Flag("ns1-api-key", "NS1 private API key.").
			Required().PlaceHolder("CS_NS1_API_KEY").
			OverrideDefaultFromEnvar("CS_NS1_API_KEY").
			Short('k').String()

	flFleetTags = cmdUdata.Flag("fleet-tags", "Comma separated list of fleet tags.").
			PlaceHolder("CS_FLEET_TAGS").
			OverrideDefaultFromEnvar("CS_FLEET_TAGS").
			Short('t').String()

	flCAcert = cmdUdata.Flag("ca-cert", "Path to CA certificate.").
			PlaceHolder("CS_CA_CERT").
			OverrideDefaultFromEnvar("CS_CA_CERT").
			Short('c').String()

	flEtcdToken = cmdUdata.Flag("etcd-token", "Provide an etcd discovery token.").
			PlaceHolder("CS_ETCD_TOKEN").
			OverrideDefaultFromEnvar("CS_ETCD_TOKEN").
			Short('e').String()

	flFlannelNetwork = cmdUdata.Flag("flannel-network", "Flannel entire overlay network.").
			PlaceHolder("CS_FLANNEL_NETWORK").
			OverrideDefaultFromEnvar("CS_FLANNEL_NETWORK").
			Short('n').String()

	flFlannelSubnetLen = cmdUdata.Flag("flannel-subnet-len", "Subnet len to llocate to each host.").
			PlaceHolder("CS_FLANNEL_SUBNET_LEN").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_LEN").
			Short('s').String()

	flFlannelSubnetMin = cmdUdata.Flag("flannel-subnet-min", "Minimum subnet IP addresses.").
			PlaceHolder("CS_FLANNEL_SUBNET_MIN").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MIN").
			Short('m').String()

	flFlannelSubnetMax = cmdUdata.Flag("flannel-subnet-max", "Maximum subnet IP addresses.").
			PlaceHolder("CS_FLANNEL_SUBNET_MAX").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MAX").
			Short('x').String()

	flFlannelBackend = cmdUdata.Flag("flannel-backend", "Flannel backend type: [ udp | vxlan | host-gw | gce | aws-vpc | alloc ]").
			PlaceHolder("CS_FLANNEL_SUBNET_MAX").
			OverrideDefaultFromEnvar("CS_FLANNEL_SUBNET_MAX").
			Short('b').String()

	//----------------------------
	// run-packet: nested command
	//----------------------------

	cmdRunPacket = app.Command("run-packet", "Starts a CoreOS instance on Packet.net")

	flPktAPIKey = cmdRunPacket.Flag("api-key", "Packet API key.").
			Required().PlaceHolder("PKT_APIKEY").
			OverrideDefaultFromEnvar("PKT_APIKEY").
			Short('k').String()

	flPktHostName = cmdRunPacket.Flag("hostname", "For the Packet.net dashboard.").
			Required().PlaceHolder("PKT_HOSTNAME").
			OverrideDefaultFromEnvar("PKT_HOSTNAME").
			Short('h').String()

	flPktProjID = cmdRunPacket.Flag("project-id", "Format: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee").
			Required().PlaceHolder("PKT_PROJID").
			OverrideDefaultFromEnvar("PKT_PROJID").
			Short('i').String()

	flPktPlan = cmdRunPacket.Flag("plan", "One of [ baremetal_0 | baremetal_1 | baremetal_2 | baremetal_3 ]").
			Required().PlaceHolder("PKT_PLAN").
			OverrideDefaultFromEnvar("PKT_PLAN").
			Short('p').String()

	flPktOsys = cmdRunPacket.Flag("os", "One of [ coreos_stable | coreos_beta | coreos_alpha ]").
			Required().PlaceHolder("PKT_OS").
			OverrideDefaultFromEnvar("PKT_OS").
			Short('o').String()

	flPktFacility = cmdRunPacket.Flag("facility", "One of [ ewr1 | ams1 ]").
			Required().PlaceHolder("PKT_FACILITY").
			OverrideDefaultFromEnvar("PKT_FACILITY").
			Short('f').String()

	flPktBilling = cmdRunPacket.Flag("billing", "One of [ hourly | monthly ]").
			Required().PlaceHolder("PKT_BILLING").
			OverrideDefaultFromEnvar("PKT_BILLING").
			Short('b').String()

	//-------------------------
	// run-ec2: nested command
	//-------------------------

	cmdRunEc2 = app.Command("run-ec2", "Starts a CoreOS instance on Amazon EC2")

	flEc2HostName = cmdRunEc2.Flag("hostname", "For the EC2 dashboard.").
			PlaceHolder("EC2_HOSTNAME").
			OverrideDefaultFromEnvar("EC2_HOSTNAME").
			Short('h').String()

	flEc2Region = cmdRunEc2.Flag("region", "EC2 region.").
			Required().PlaceHolder("EC2_REGION").
			OverrideDefaultFromEnvar("EC2_REGION").
			Short('r').String()

	flEc2ImageID = cmdRunEc2.Flag("image-id", "EC2 image id.").
			Required().PlaceHolder("EC2_IMAGE_ID").
			OverrideDefaultFromEnvar("EC2_IMAGE_ID").
			Short('i').String()

	flEc2InsType = cmdRunEc2.Flag("instance-type", "EC2 instance type.").
			Required().PlaceHolder("EC2_INSTANCE_TYPE").
			OverrideDefaultFromEnvar("EC2_INSTANCE_TYPE").
			Short('t').String()

	flEc2KeyPair = cmdRunEc2.Flag("key-pair", "EC2 key pair.").
			Required().PlaceHolder("EC2_KEY_PAIR").
			OverrideDefaultFromEnvar("EC2_KEY_PAIR").
			Short('k').String()

	flEc2VpcID = cmdRunEc2.Flag("vpc-id", "EC2 VPC id.").
			Required().PlaceHolder("EC2_VPC_ID").
			OverrideDefaultFromEnvar("EC2_VPC_ID").
			Short('v').String()

	flEc2SubnetIds = cmdRunEc2.Flag("subnet-ids", "EC2 subnet ids.").
			Required().PlaceHolder("EC2_SUBNET_ID").
			OverrideDefaultFromEnvar("EC2_SUBNET_ID").
			Short('s').String()

	flEc2ElasticIP = cmdRunEc2.Flag("elastic-ip", "Allocate an elastic IP [ true | false ]").
			Default("false").PlaceHolder("EC2_ELASTIC_IP").
			OverrideDefaultFromEnvar("EC2_ELASTIC_IP").
			Short('e').String()
)

//----------------------------------------------------------------------------
// Entry point:
//----------------------------------------------------------------------------

func main() {

	// Sub-command selector:
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	// katoctl udata ...
	case cmdUdata.FullCommand():
		udata()

	// katoctl run-packet ...
	case cmdRunPacket.FullCommand():
		runPacket()

	// katoctl run-ec2 ...
	case cmdRunEc2.FullCommand():
		runEc2()
	}
}

//--------------------------------------------------------------------------
// func: udata
//--------------------------------------------------------------------------

func udata() {

	// Template udata structure:
	udata := userData {
		HostId:           *flHostID,
		Domain:           *flDomain,
		Role:             *flRole,
		Ns1ApiKey:        *flNs1Apikey,
		FleetTags:        *flFleetTags,
		EtcdToken:        *flEtcdToken,
		FlannelNetwork:   *flFlannelNetwork,
		FlannelSubnetLen: *flFlannelSubnetLen,
		FlannelSubnetMin: *flFlannelSubnetMin,
		FlannelSubnetMax: *flFlannelSubnetMax,
		FlannelBackend:   *flFlannelBackend,
	}

	// Read the CA certificate:
	if *flCAcert != "" {
		dat, err := ioutil.ReadFile(*flCAcert)
		checkError(err)
		udata.CaCert = strings.TrimSpace(strings.Replace(string(dat), "\n", "\n    ", -1))
	}

	// Render the template for the selected role:
	switch *flRole {
	case "master":
		t := template.New("master_udata")
		t, err := t.Parse(templMaster)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	case "node":
		t := template.New("node_udata")
		t, err := t.Parse(templNode)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	case "edge":
		t := template.New("edge_udata")
		t, err := t.Parse(templEdge)
		err = t.Execute(os.Stdout, udata)
		checkError(err)
	}
}

//--------------------------------------------------------------------------
// func: runPacket
//--------------------------------------------------------------------------

func runPacket() {

	// Read user-data from stdin:
	udata, err := ioutil.ReadAll(os.Stdin)
	checkError(err)

	// Connect and authenticate to the API endpoint:
	client := packngo.NewClient("", *flPktAPIKey, nil)

	// Forge the request:
	createRequest := &packngo.DeviceCreateRequest{
		HostName:     *flPktHostName,
		Plan:         *flPktPlan,
		Facility:     *flPktFacility,
		OS:           *flPktOsys,
		BillingCycle: *flPktBilling,
		ProjectID:    *flPktProjID,
		UserData:     string(udata),
	}

	// Send the request:
	newDevice, _, err := client.Devices.Create(createRequest)
	checkError(err)

	// Pretty-print the response data:
	fmt.Println(newDevice)
}

//--------------------------------------------------------------------------
// func: runEc2
//--------------------------------------------------------------------------

func runEc2() {

	// Read user-data from stdin:
	udata, err := ioutil.ReadAll(os.Stdin)
	checkError(err)

	// Connect and authenticate to the API endpoint:
	svc := ec2.New(session.New(&aws.Config{Region: aws.String(*flEc2Region)}))

	// Forge the network interfaces:
	var networkInterfaces []*ec2.InstanceNetworkInterfaceSpecification
	subnetIds := strings.Split(*flEc2SubnetIds, ",")

	for i := 0; i < len(subnetIds); i++ {

		// Forge the security group ids:
		var securityGroupIds []*string
		for _, gid := range strings.Split(subnetIds[i], ":")[1:] {
			securityGroupIds = append(securityGroupIds, aws.String(gid))
		}

		iface := ec2.InstanceNetworkInterfaceSpecification{
			DeleteOnTermination: aws.Bool(true),
			DeviceIndex:         aws.Int64(int64(i)),
			Groups:              securityGroupIds,
			SubnetId:            aws.String(strings.Split(subnetIds[i], ":")[0]),
		}

		networkInterfaces = append(networkInterfaces, &iface)
	}

	// Send the request:
	runResult, err := svc.RunInstances(&ec2.RunInstancesInput{
		ImageId:           aws.String(*flEc2ImageID),
		MinCount:          aws.Int64(1),
		MaxCount:          aws.Int64(1),
		KeyName:           aws.String(*flEc2KeyPair),
		InstanceType:      aws.String(*flEc2InsType),
		NetworkInterfaces: networkInterfaces,
		UserData:          aws.String(base64.StdEncoding.EncodeToString([]byte(udata))),
	})

	checkError(err)

	// Pretty-print the response data:
	fmt.Println("Created instance", *runResult.Instances[0].InstanceId)

	// Add tags to the created instance:
	_, err = svc.CreateTags(&ec2.CreateTagsInput{
		Resources: []*string{runResult.Instances[0].InstanceId},
		Tags: []*ec2.Tag{
			{
				Key:   aws.String("Name"),
				Value: aws.String(*flEc2HostName),
			},
		},
	})

	checkError(err)

	// Allocate an elastic IP address:
	if *flEc2ElasticIP == "true" {

		params := &ec2.AllocateAddressInput{
			Domain: aws.String("vpc"),
			DryRun: aws.Bool(false),
		}

		// Send the request:
		resp, err := svc.AllocateAddress(params)
		checkError(err)

		// Pretty-print the response data:
		fmt.Println(resp)
	}
}

//---------------------------------------------------------------------------
// func: checkError
//---------------------------------------------------------------------------

func checkError(err error) {
	if err != nil {
		fmt.Println("Fatal error ", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"

	"github.com/horgh/gorse"
)

// DBItem holds the information about an input/entry that is in the database.
// TODO(will@summercat.com): Refactor to combine with gorse.DBItem. I think we
// should have one that is the generic item. This one includes a field related
// to a single user.
type DBItem struct {
	gorse.DBItem

	// Name from the rss_feed table.
	FeedName string

	// Read state from rss_item_state table
	ReadState string
}

// connectToDB opens a new connection to the database.
func connectToDB(settings *Config) (*sql.DB, error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s connect_timeout=10",
		settings.DBUser, settings.DBPass, settings.DBName, settings.DBHost)

	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err)
		return nil, err
	}

	log.Print("Opened new connection to the database.")
	return db, nil
}

// getDB connects us to the database if necessary, and returns an active
// database connection.
//
// We use the global DB variable to try to ensure we use a single connection.
func getDB(settings *Config) (*sql.DB, error) {
	// If we have a db connection, ensure that it is still available so that we
	// reconnect if it is not.
	if DB != nil {
		err := DB.Ping()
		if err == nil {
			return DB, nil
		}

		log.Printf("Database ping failed: %s", err)

		// Continue on, but set us so that we attempt to reconnect.

		DBLock.Lock()
		if DB != nil {
			_ = DB.Close()
			DB = nil
		}
		DBLock.Unlock()
	}

	DBLock.Lock()
	defer DBLock.Unlock()

	if DB != nil {
		return DB, nil
	}

	db, err := connectToDB(settings)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err)
		return nil, err
	}

	// Set global
	DB = db

	return DB, nil
}

// dbCountItems retrieves a count of items.
//
// This is for pagination.
func dbCountItems(db *sql.DB, userID int, state gorse.ReadState) (int, error) {
	query := `
SELECT COUNT(1) AS count
FROM rss_item ri
LEFT JOIN rss_feed rf ON rf.id = ri.rss_feed_id
LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
WHERE
rf.active = true AND
COALESCE(ris.state, 'unread') = $1 AND
COALESCE(ris.user_id, $2) = $3
`

	rows, err := db.Query(query, state.String(), userID, userID)
	if err != nil {
		return -1, err
	}

	if !rows.Next() {
		return -1, errors.New("count not found")
	}

	var count int
	if err := rows.Scan(&count); err != nil {
		_ = rows.Close()
		return -1, err
	}

	if err := rows.Close(); err != nil {
		return -1, fmt.Errorf("problem closing rows: %s", err)
	}

	return count, nil
}

// dbRetrieveFeedItems retrieves feed items from the database which are marked
// a given state.
func dbRetrieveFeedItems(db *sql.DB, settings *Config, order sortOrder,
	page, userID int, state gorse.ReadState) ([]DBItem, error) {

	if page < 1 {
		return nil, errors.New("invalid page number")
	}

	query := `
SELECT
rf.name, ri.id, ri.title, ri.link, ri.description, ri.publication_date, ri.guid
FROM rss_item ri
LEFT JOIN rss_feed rf ON rf.id = ri.rss_feed_id
LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
WHERE
rf.active = true AND
COALESCE(ris.state, 'unread') = $1 AND
COALESCE(ris.user_id, $2) = $3
`

	if order == sortAscending {
		query += "ORDER BY ri.publication_date ASC, rf.name, ri.title"
	} else {
		query += "ORDER BY ri.publication_date DESC, rf.name, ri.title"
	}

	query += " LIMIT $4 OFFSET $5"

	offset := (page - 1) * pageSize

	rows, err := db.Query(query, state.String(), userID, userID, pageSize, offset)
	if err != nil {
		return nil, err
	}

	var items []DBItem
	for rows.Next() {
		item := DBItem{}

		err := rows.Scan(&item.FeedName, &item.ID, &item.Title, &item.Link,
			&item.Description, &item.PublicationDate, &item.GUID)
		if err != nil {
			log.Printf("Failed to scan row information: %s", err)
			_ = rows.Close()
			return nil, err
		}

		items = append(items, item)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("failure fetching rows: %s", err)
	}

	return items, nil
}

// Retrieve an item's information from the database. This includes the item's
// state for the given user.
func dbGetItem(db *sql.DB, itemID int64, userID int) (DBItem, error) {
	query := `
SELECT
ri.id, ri.title, ri.description, ri.link, ri.publication_date, ri.guid,
ri.rss_feed_id, rf.name, COALESCE(ris.state, 'unread')
FROM rss_item ri
JOIN rss_feed rf ON ri.rss_feed_id = rf.id
LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
WHERE ri.id = $1 AND
COALESCE(ris.user_id, $2) = $3
`
	rows, err := db.Query(query, itemID, userID, userID)
	if err != nil {
		return DBItem{}, err
	}

	for rows.Next() {
		item := DBItem{}

		if err := rows.Scan(&item.ID, &item.Title, &item.Description, &item.Link,
			&item.PublicationDate, &item.GUID, &item.RSSFeedID, &item.FeedName,
			&item.ReadState); err != nil {
			_ = rows.Close()
			return DBItem{}, fmt.Errorf("failed to scan row: %s", err)
		}

		if err := rows.Close(); err != nil {
			return DBItem{}, fmt.Errorf("error closing rows: %s", err)
		}

		return item, nil
	}

	if err := rows.Err(); err != nil {
		return DBItem{}, fmt.Errorf("failure fetching rows: %s", err)
	}

	return DBItem{}, fmt.Errorf("item not found")
}

// Record the item was read after having been saved to read later.
//
// It is useful to be able to refer back to such items as it is likely they were
// looked at more closely than others.
func dbRecordReadAfterReadLater(db *sql.DB, userID int, item DBItem) error {
	query := `
INSERT INTO rss_item_read_after_archive
(user_id, rss_feed_id, rss_item_id)
VALUES($1, $2, $3)
`
	if _, err := db.Exec(query, userID, item.RSSFeedID, item.ID); err != nil {
		return fmt.Errorf("unable to insert: %s", err)
	}

	return nil
}
<commit_msg>Reformat SQL<commit_after>package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"

	"github.com/horgh/gorse"
)

// DBItem holds the information about an input/entry that is in the database.
// TODO(will@summercat.com): Refactor to combine with gorse.DBItem. I think we
// should have one that is the generic item. This one includes a field related
// to a single user.
type DBItem struct {
	gorse.DBItem

	// Name from the rss_feed table.
	FeedName string

	// Read state from rss_item_state table
	ReadState string
}

// connectToDB opens a new connection to the database.
func connectToDB(settings *Config) (*sql.DB, error) {
	dsn := fmt.Sprintf("user=%s password=%s dbname=%s host=%s connect_timeout=10",
		settings.DBUser, settings.DBPass, settings.DBName, settings.DBHost)

	db, err := sql.Open("postgres", dsn)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err)
		return nil, err
	}

	log.Print("Opened new connection to the database.")
	return db, nil
}

// getDB connects us to the database if necessary, and returns an active
// database connection.
//
// We use the global DB variable to try to ensure we use a single connection.
func getDB(settings *Config) (*sql.DB, error) {
	// If we have a db connection, ensure that it is still available so that we
	// reconnect if it is not.
	if DB != nil {
		err := DB.Ping()
		if err == nil {
			return DB, nil
		}

		log.Printf("Database ping failed: %s", err)

		// Continue on, but set us so that we attempt to reconnect.

		DBLock.Lock()
		if DB != nil {
			_ = DB.Close()
			DB = nil
		}
		DBLock.Unlock()
	}

	DBLock.Lock()
	defer DBLock.Unlock()

	if DB != nil {
		return DB, nil
	}

	db, err := connectToDB(settings)
	if err != nil {
		log.Printf("Failed to connect to the database: %s", err)
		return nil, err
	}

	// Set global
	DB = db

	return DB, nil
}

// dbCountItems retrieves a count of items.
//
// This is for pagination.
func dbCountItems(db *sql.DB, userID int, state gorse.ReadState) (int, error) {
	query := `
		SELECT COUNT(*)
		FROM rss_item ri
		LEFT JOIN rss_feed rf ON rf.id = ri.rss_feed_id
		LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
		WHERE rf.active = true AND
			COALESCE(ris.state, 'unread') = $1 AND
			COALESCE(ris.user_id, $2) = $3
`

	rows, err := db.Query(query, state.String(), userID, userID)
	if err != nil {
		return -1, err
	}

	if !rows.Next() {
		return -1, errors.New("count not found")
	}

	var count int
	if err := rows.Scan(&count); err != nil {
		_ = rows.Close()
		return -1, err
	}

	if err := rows.Close(); err != nil {
		return -1, fmt.Errorf("problem closing rows: %s", err)
	}

	return count, nil
}

// dbRetrieveFeedItems retrieves feed items from the database which are marked
// a given state.
func dbRetrieveFeedItems(db *sql.DB, settings *Config, order sortOrder,
	page, userID int, state gorse.ReadState) ([]DBItem, error) {

	if page < 1 {
		return nil, errors.New("invalid page number")
	}

	query := `
		SELECT
			rf.name,
			ri.id,
			ri.title,
			ri.link,
			ri.description,
			ri.publication_date,
			ri.guid
		FROM rss_item ri
		LEFT JOIN rss_feed rf ON rf.id = ri.rss_feed_id
		LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
		WHERE rf.active = true AND
			COALESCE(ris.state, 'unread') = $1 AND
			COALESCE(ris.user_id, $2) = $3
`

	if order == sortAscending {
		query += "ORDER BY ri.publication_date ASC, rf.name, ri.title"
	} else {
		query += "ORDER BY ri.publication_date DESC, rf.name, ri.title"
	}

	query += " LIMIT $4 OFFSET $5"

	offset := (page - 1) * pageSize

	rows, err := db.Query(query, state.String(), userID, userID, pageSize, offset)
	if err != nil {
		return nil, err
	}

	var items []DBItem
	for rows.Next() {
		item := DBItem{}

		err := rows.Scan(&item.FeedName, &item.ID, &item.Title, &item.Link,
			&item.Description, &item.PublicationDate, &item.GUID)
		if err != nil {
			log.Printf("Failed to scan row information: %s", err)
			_ = rows.Close()
			return nil, err
		}

		items = append(items, item)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("failure fetching rows: %s", err)
	}

	return items, nil
}

// Retrieve an item's information from the database. This includes the item's
// state for the given user.
func dbGetItem(db *sql.DB, itemID int64, userID int) (DBItem, error) {
	query := `
		SELECT
			ri.id,
			ri.title,
			ri.description,
			ri.link,
			ri.publication_date,
			ri.guid,
			ri.rss_feed_id,
			rf.name,
			COALESCE(ris.state, 'unread')
		FROM rss_item ri
		JOIN rss_feed rf ON ri.rss_feed_id = rf.id
		LEFT JOIN rss_item_state ris ON ris.item_id = ri.id
		WHERE ri.id = $1 AND
			COALESCE(ris.user_id, $2) = $3
`
	rows, err := db.Query(query, itemID, userID, userID)
	if err != nil {
		return DBItem{}, err
	}

	for rows.Next() {
		item := DBItem{}

		if err := rows.Scan(&item.ID, &item.Title, &item.Description, &item.Link,
			&item.PublicationDate, &item.GUID, &item.RSSFeedID, &item.FeedName,
			&item.ReadState); err != nil {
			_ = rows.Close()
			return DBItem{}, fmt.Errorf("failed to scan row: %s", err)
		}

		if err := rows.Close(); err != nil {
			return DBItem{}, fmt.Errorf("error closing rows: %s", err)
		}

		return item, nil
	}

	if err := rows.Err(); err != nil {
		return DBItem{}, fmt.Errorf("failure fetching rows: %s", err)
	}

	return DBItem{}, fmt.Errorf("item not found")
}

// Record the item was read after having been saved to read later.
//
// It is useful to be able to refer back to such items as it is likely they were
// looked at more closely than others.
func dbRecordReadAfterReadLater(db *sql.DB, userID int, item DBItem) error {
	query := `
		INSERT INTO rss_item_read_after_archive
		(user_id, rss_feed_id, rss_item_id)
		VALUES ($1, $2, $3)
`
	if _, err := db.Exec(query, userID, item.RSSFeedID, item.ID); err != nil {
		return fmt.Errorf("unable to insert: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The Kubeflow 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 cmd

import (
	"fmt"

	ep "github.com/jlewi/cloud-endpoints-controller/pkg"
	kftypes "github.com/kubeflow/kfctl/v3/pkg/apis/apps"
	"github.com/kubeflow/kfctl/v3/pkg/kfapp/coordinator"
	"github.com/kubeflow/kfctl/v3/pkg/kfupgrade"
	"github.com/kubeflow/kfctl/v3/pkg/utils"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var applyCfg = viper.New()
var kfApp kftypes.KfApp
var err error

var kubeContext = ""

// KFDef example configs to be printed out from apply --help
const (
	awsConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_aws.v1.0.0.yaml"
	gcpConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_gcp_iap.v1.0.0.yaml"
	istioDexConfig = "https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_istio_dex.v1.0.0.yaml"
	k8sConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_k8s_istio.v1.0.0.yaml"
)

// applyCmd represents the apply command
var applyCmd = &cobra.Command{
	Use:   "apply -f ${CONFIG}",
	Short: "deploys a kubeflow application.",
	Long: `'kfctl apply' builds and deploys a kubeflow application from a KFDef config.` + "\n" +
		`To install run -> ` + ColorPrint("kfctl apply -f ${CONFIG}") + "\n" +
		`For more information, run 'kfctl apply -h' or read the docs at www.kubeflow.org.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		log.SetLevel(log.InfoLevel)
		if applyCfg.GetBool(string(kftypes.VERBOSE)) != true {
			log.SetLevel(log.WarnLevel)
		}

		// Load config from exisiting app.yaml
		if configFilePath == "" {
			return fmt.Errorf("Must pass in -f configFile")
		}

		kind, err := utils.GetObjectKindFromUri(configFilePath)
		if err != nil {
			return fmt.Errorf("Cannot determine the object kind: %v", err)
		}
		switch kind {
		case string(kftypes.KFDEF):
			kfApp, err = coordinator.NewLoadKfAppFromURI(configFilePath)
			if err != nil {
				return fmt.Errorf("failed to build kfApp from URI %s: %v", configFilePath, err)
			}
			if err := kfApp.Apply(kftypes.ALL); err != nil {
				return fmt.Errorf("failed to apply: %s", err)
			}
			log.Info("Applied the configuration Successfully!")
			return nil
		case string(kftypes.KFUPGRADE):
			log.Warnf("Support for kind %s is deprecated and will be removed in subsequent versions", kftypes.KFUPGRADE)
			kfUpgrade, err := kfupgrade.NewKfUpgrade(configFilePath)
			if err != nil {
				return fmt.Errorf("couldn't load KfUpgrade: %v", err)
			}

			err = kfUpgrade.Apply()
			if err != nil {
				return fmt.Errorf("couldn't apply KfUpgrade: %v", err)
			}
			return nil
		case ep.Kind:
			return ep.Process(configFilePath, kubeContext)
		default:
			return fmt.Errorf("Unsupported object kind: %v", kind)
		}
	},
}

func init() {
	rootCmd.AddCommand(applyCmd)

	applyCfg.SetConfigName("app")
	applyCfg.SetConfigType("yaml")

	// Config file option
	applyCmd.PersistentFlags().StringVarP(&configFilePath, string(kftypes.FILE), "f", "",
		`Static config file to use. Can be either a local path:
		export CONFIG=./kfctl_gcp_iap.yaml
	or a URL:
		export CONFIG=`+gcpConfig+`
		export CONFIG=`+istioDexConfig+`
		export CONFIG=`+awsConfig+`
		export CONFIG=`+k8sConfig+`
	kfctl apply -V --file=${CONFIG}`)

	// verbose output
	applyCmd.Flags().BoolP(string(kftypes.VERBOSE), "V", false,
		string(kftypes.VERBOSE)+" output default is false")

	applyCmd.Flags().StringVar(&kubeContext, "context", "", "Optional kubernetes context to use when applying resources. Currently not used by KFDef resources.")
	bindErr := applyCfg.BindPFlag(string(kftypes.VERBOSE), applyCmd.Flags().Lookup(string(kftypes.VERBOSE)))
	if bindErr != nil {
		log.Errorf("Couldn't set flag --%v: %v", string(kftypes.VERBOSE), bindErr)
		return
	}
}
<commit_msg>Fixed the default output to point to `1.2` instead of `1.0` (#451)<commit_after>// Copyright 2018 The Kubeflow 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 cmd

import (
	"fmt"

	ep "github.com/jlewi/cloud-endpoints-controller/pkg"
	kftypes "github.com/kubeflow/kfctl/v3/pkg/apis/apps"
	"github.com/kubeflow/kfctl/v3/pkg/kfapp/coordinator"
	"github.com/kubeflow/kfctl/v3/pkg/kfupgrade"
	"github.com/kubeflow/kfctl/v3/pkg/utils"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var applyCfg = viper.New()
var kfApp kftypes.KfApp
var err error

var kubeContext = ""

// KFDef example configs to be printed out from apply --help
const (
	awsConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.2-branch/kfdef/kfctl_aws.v1.2.0.yaml"
	gcpConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.0-branch/kfdef/kfctl_gcp_iap.v1.0.0.yaml"
	istioDexConfig = "https://raw.githubusercontent.com/kubeflow/manifests/v1.2-branch/kfdef/kfctl_istio_dex.v1.2.0.yaml"
	k8sConfig      = "https://raw.githubusercontent.com/kubeflow/manifests/v1.2-branch/kfdef/kfctl_k8s_istio.v1.2.0.yaml"
)

// applyCmd represents the apply command
var applyCmd = &cobra.Command{
	Use:   "apply -f ${CONFIG}",
	Short: "deploys a kubeflow application.",
	Long: `'kfctl apply' builds and deploys a kubeflow application from a KFDef config.` + "\n" +
		`To install run -> ` + ColorPrint("kfctl apply -f ${CONFIG}") + "\n" +
		`For more information, run 'kfctl apply -h' or read the docs at www.kubeflow.org.`,
	RunE: func(cmd *cobra.Command, args []string) error {
		log.SetLevel(log.InfoLevel)
		if applyCfg.GetBool(string(kftypes.VERBOSE)) != true {
			log.SetLevel(log.WarnLevel)
		}

		// Load config from exisiting app.yaml
		if configFilePath == "" {
			return fmt.Errorf("Must pass in -f configFile")
		}

		kind, err := utils.GetObjectKindFromUri(configFilePath)
		if err != nil {
			return fmt.Errorf("Cannot determine the object kind: %v", err)
		}
		switch kind {
		case string(kftypes.KFDEF):
			kfApp, err = coordinator.NewLoadKfAppFromURI(configFilePath)
			if err != nil {
				return fmt.Errorf("failed to build kfApp from URI %s: %v", configFilePath, err)
			}
			if err := kfApp.Apply(kftypes.ALL); err != nil {
				return fmt.Errorf("failed to apply: %s", err)
			}
			log.Info("Applied the configuration Successfully!")
			return nil
		case string(kftypes.KFUPGRADE):
			log.Warnf("Support for kind %s is deprecated and will be removed in subsequent versions", kftypes.KFUPGRADE)
			kfUpgrade, err := kfupgrade.NewKfUpgrade(configFilePath)
			if err != nil {
				return fmt.Errorf("couldn't load KfUpgrade: %v", err)
			}

			err = kfUpgrade.Apply()
			if err != nil {
				return fmt.Errorf("couldn't apply KfUpgrade: %v", err)
			}
			return nil
		case ep.Kind:
			return ep.Process(configFilePath, kubeContext)
		default:
			return fmt.Errorf("Unsupported object kind: %v", kind)
		}
	},
}

func init() {
	rootCmd.AddCommand(applyCmd)

	applyCfg.SetConfigName("app")
	applyCfg.SetConfigType("yaml")

	// Config file option
	applyCmd.PersistentFlags().StringVarP(&configFilePath, string(kftypes.FILE), "f", "",
		`Static config file to use. Can be either a local path:
		export CONFIG=./kfctl_gcp_iap.yaml
	or a URL:
		export CONFIG=`+gcpConfig+`
		export CONFIG=`+istioDexConfig+`
		export CONFIG=`+awsConfig+`
		export CONFIG=`+k8sConfig+`
	kfctl apply -V --file=${CONFIG}`)

	// verbose output
	applyCmd.Flags().BoolP(string(kftypes.VERBOSE), "V", false,
		string(kftypes.VERBOSE)+" output default is false")

	applyCmd.Flags().StringVar(&kubeContext, "context", "", "Optional kubernetes context to use when applying resources. Currently not used by KFDef resources.")
	bindErr := applyCfg.BindPFlag(string(kftypes.VERBOSE), applyCmd.Flags().Lookup(string(kftypes.VERBOSE)))
	if bindErr != nil {
		log.Errorf("Couldn't set flag --%v: %v", string(kftypes.VERBOSE), bindErr)
		return
	}
}
<|endoftext|>


<commit_before>package internal

import (
	"errors"
	"fmt"

	"github.com/ready-steady/probability/gaussian"
	"github.com/ready-steady/probability/generator"
	"github.com/ready-steady/probability/uniform"
	"github.com/ready-steady/staircase"
	"github.com/ready-steady/statistics/correlation"

	acorrelation "../../pkg/correlation"
)

var (
	standardGaussian = gaussian.New(0, 1)
)

type model struct {
	taskIndex  []uint
	correlator []float64
	modes      []mode

	nt uint
	nu uint
	nz uint
}

type mode *staircase.Staircase

func newModel(c *ProbabilityConfig, s *system) (*model, error) {
	nt := s.nt

	taskIndex, err := parseNaturalIndex(c.TaskIndex, 0, nt-1)
	if err != nil {
		return nil, err
	}

	correlator, err := computeCorrelator(c, s, taskIndex)
	if err != nil {
		return nil, err
	}

	nu := uint(len(taskIndex))
	nz := uint(len(correlator)) / nu

	modes, err := computeModes(c, nu)
	if err != nil {
		return nil, err
	}

	model := &model{
		taskIndex:  taskIndex,
		correlator: correlator,
		modes:      modes,

		nt: nt,
		nu: nu,
		nz: nz,
	}

	return model, nil
}

func (m *model) String() string {
	return fmt.Sprintf(`{"parameters": %d, "variables": %d}`, m.nu, m.nz)
}

func (m *model) transform(z []float64) []float64 {
	nt, nu, nz := m.nt, m.nu, m.nz

	n := make([]float64, nz)
	u := make([]float64, nu)

	// Independent uniform to independent Gaussian
	for i := range n {
		n[i] = standardGaussian.InvCDF(z[i])
	}

	// Independent Gaussian to dependent Gaussian
	combine(m.correlator, n, u, nu, nz)

	// Dependent Gaussian to dependent uniform
	for i := range u {
		u[i] = standardGaussian.CDF(u[i])
	}

	modes := make([]float64, nt)
	for i, tid := range m.taskIndex {
		modes[tid] = (*staircase.Staircase)(m.modes[i]).Evaluate(u[i])
	}

	return modes
}

func computeCorrelator(c *ProbabilityConfig, s *system, taskIndex []uint) ([]float64, error) {
	if c.CorrLength < 0 {
		return nil, errors.New("the correlation length should be nonnegative")
	}
	if c.VarThreshold <= 0 {
		return nil, errors.New("the variance-reduction threshold should be positive")
	}

	C := acorrelation.Compute(s.application, taskIndex, c.CorrLength)
	correlator, _, err := correlation.Decompose(C, uint(len(taskIndex)), c.VarThreshold)
	if err != nil {
		return nil, err
	}

	return correlator, nil
}

func computeModes(c *ProbabilityConfig, count uint) ([]mode, error) {
	if c.MaxModes == 0 {
		return nil, errors.New("the number of modes should be positive")
	}
	if c.MinScale <= -1 || c.MaxScale <= -1 {
		return nil, errors.New("the scaling factors should be greater than -1")
	}

	generator := generator.New(NewSeed(c.Seed))
	uniform := uniform.New(0, 1)

	result := make([]mode, count)
	for i := range result {
		count := uint(uniform.Sample(generator)*float64(c.MaxModes)) + 1

		Σ := 0.0
		values := make([]float64, count)
		probabilities := make([]float64, count)
		for j := uint(0); j < count; j++ {
			values[j] = uniform.Sample(generator)
			probabilities[j] = uniform.Sample(generator)
			Σ += probabilities[j]
		}
		for j := uint(0); j < count; j++ {
			values[j] *= c.MaxScale - c.MinScale
			values[j] += c.MinScale
			probabilities[j] /= Σ
		}

		mode, err := staircase.New(probabilities, values, c.Transition, c.Steepness)
		if err != nil {
			return nil, err
		}
		result[i] = mode
	}

	return result, nil
}
<commit_msg>cmd/internal: update the usage of probability<commit_after>package internal

import (
	"errors"
	"fmt"

	"github.com/ready-steady/probability"
	"github.com/ready-steady/staircase"
	"github.com/ready-steady/statistics/correlation"

	acorrelation "../../pkg/correlation"
)

var (
	standardGaussian = probability.NewGaussian(0, 1)
)

type model struct {
	taskIndex  []uint
	correlator []float64
	modes      []mode

	nt uint
	nu uint
	nz uint
}

type mode *staircase.Staircase

func newModel(c *ProbabilityConfig, s *system) (*model, error) {
	nt := s.nt

	taskIndex, err := parseNaturalIndex(c.TaskIndex, 0, nt-1)
	if err != nil {
		return nil, err
	}

	correlator, err := computeCorrelator(c, s, taskIndex)
	if err != nil {
		return nil, err
	}

	nu := uint(len(taskIndex))
	nz := uint(len(correlator)) / nu

	modes, err := computeModes(c, nu)
	if err != nil {
		return nil, err
	}

	model := &model{
		taskIndex:  taskIndex,
		correlator: correlator,
		modes:      modes,

		nt: nt,
		nu: nu,
		nz: nz,
	}

	return model, nil
}

func (m *model) String() string {
	return fmt.Sprintf(`{"parameters": %d, "variables": %d}`, m.nu, m.nz)
}

func (m *model) transform(z []float64) []float64 {
	nt, nu, nz := m.nt, m.nu, m.nz

	n := make([]float64, nz)
	u := make([]float64, nu)

	// Independent uniform to independent Gaussian
	for i := range n {
		n[i] = standardGaussian.InvCDF(z[i])
	}

	// Independent Gaussian to dependent Gaussian
	combine(m.correlator, n, u, nu, nz)

	// Dependent Gaussian to dependent uniform
	for i := range u {
		u[i] = standardGaussian.CDF(u[i])
	}

	modes := make([]float64, nt)
	for i, tid := range m.taskIndex {
		modes[tid] = (*staircase.Staircase)(m.modes[i]).Evaluate(u[i])
	}

	return modes
}

func computeCorrelator(c *ProbabilityConfig, s *system, taskIndex []uint) ([]float64, error) {
	if c.CorrLength < 0 {
		return nil, errors.New("the correlation length should be nonnegative")
	}
	if c.VarThreshold <= 0 {
		return nil, errors.New("the variance-reduction threshold should be positive")
	}

	C := acorrelation.Compute(s.application, taskIndex, c.CorrLength)
	correlator, _, err := correlation.Decompose(C, uint(len(taskIndex)), c.VarThreshold)
	if err != nil {
		return nil, err
	}

	return correlator, nil
}

func computeModes(c *ProbabilityConfig, count uint) ([]mode, error) {
	if c.MaxModes == 0 {
		return nil, errors.New("the number of modes should be positive")
	}
	if c.MinScale <= -1 || c.MaxScale <= -1 {
		return nil, errors.New("the scaling factors should be greater than -1")
	}

	generator := probability.NewGenerator(NewSeed(c.Seed))
	uniform := probability.NewUniform(0, 1)

	result := make([]mode, count)
	for i := range result {
		count := uint(uniform.Sample(generator)*float64(c.MaxModes)) + 1

		Σ := 0.0
		values := make([]float64, count)
		probabilities := make([]float64, count)
		for j := uint(0); j < count; j++ {
			values[j] = uniform.Sample(generator)
			probabilities[j] = uniform.Sample(generator)
			Σ += probabilities[j]
		}
		for j := uint(0); j < count; j++ {
			values[j] *= c.MaxScale - c.MinScale
			values[j] += c.MinScale
			probabilities[j] /= Σ
		}

		mode, err := staircase.New(probabilities, values, c.Transition, c.Steepness)
		if err != nil {
			return nil, err
		}
		result[i] = mode
	}

	return result, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"os"

	logf "github.com/johnweldon/go/log"
)

var importPath string

func init() {
	flag.StringVar(&importPath, "import", "", "path to the json formatted import file")
}

func main() {
	flag.Parse()
	reader, err := os.Open(importPath)
	if err != nil {
		log.Fatal(err)
	}

	report, err := logf.ImportReportFromJson(reader)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("Projects (%d)\n%s\n", len(report.Projects), report.Projects)
}
<commit_msg>import tool<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"log"
	"os"

	logf "github.com/johnweldon/go/log"
)

var importPath string

func init() {
	flag.StringVar(&importPath, "import", "", "path to the json formatted import file")
}

func main() {
	flag.Parse()
	reader, err := os.Open(importPath)
	if err != nil {
		log.Fatal(err)
	}

	report, err := logf.ImportReportFromJson(reader)
	if err != nil {
		log.Fatal(err)
	}

	records, projects := logf.ConvertLegacyRecords(report)

	b, err := json.Marshal(struct {
		Records  []logf.TimeRecord
		Projects []logf.Project
	}{records, projects})
	if err != nil {
		log.Fatal(err)
	}

	var buf bytes.Buffer
	err = json.Indent(&buf, b, "", "  ")
	if err != nil {
		log.Fatal(err)
	}

	out, err := os.Create("imported.json")
	if err != nil {
		log.Fatal(err)
	}
	defer out.Close()

	buf.WriteTo(out)
}
<|endoftext|>


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

package user

import (
	"fmt"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/api/usermanager"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/modelcmd"
)

var helpSummary = `
Show information about a user.`[1:]

var helpDetails = `
Display infomation (user-name, display-name, date-created, and
last-connection) about a user. By default it provides information about
the current user.

Examples:
juju show-user
juju show-user jsmith
juju show-user --format json
juju show-user --format yaml

See also:
add-user
register
list-users`[1:]

// UserInfoAPI defines the API methods that the info command uses.
type UserInfoAPI interface {
	UserInfo([]string, usermanager.IncludeDisabled) ([]params.UserInfo, error)
	Close() error
}

// infoCommandBase is a common base for 'juju show-user' and 'juju list-user'.
type infoCommandBase struct {
	modelcmd.ControllerCommandBase
	api       UserInfoAPI
	exactTime bool
	out       cmd.Output
}

func (c *infoCommandBase) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.exactTime, "exact-time", false, "use full timestamp precision")
}

func NewShowUserCommand() cmd.Command {
	return modelcmd.WrapController(&infoCommand{})
}

// infoCommand retrieves information about a single user.
type infoCommand struct {
	infoCommandBase
	Username string
}

// UserInfo defines the serialization behaviour of the user information.
type UserInfo struct {
	Username       string `yaml:"user-name" json:"user-name"`
	DisplayName    string `yaml:"display-name" json:"display-name"`
	DateCreated    string `yaml:"date-created" json:"date-created"`
	LastConnection string `yaml:"last-connection" json:"last-connection"`
	Disabled       bool   `yaml:"disabled,omitempty" json:"disabled,omitempty"`
}

// Info implements Command.Info.
func (c *infoCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "show-user",
		Args:    "<username>",
		Purpose: helpSummary,
		Doc:     helpDetails,
	}
}

// SetFlags implements Command.SetFlags.
func (c *infoCommand) SetFlags(f *gnuflag.FlagSet) {
	c.infoCommandBase.SetFlags(f)
	c.out.AddFlags(f, "yaml", cmd.DefaultFormatters)
}

// Init implements Command.Init.
func (c *infoCommand) Init(args []string) (err error) {
	c.Username, err = cmd.ZeroOrOneArgs(args)
	return err
}

func (c *infoCommandBase) getUserInfoAPI() (UserInfoAPI, error) {
	if c.api != nil {
		return c.api, nil
	}
	return c.NewUserManagerAPIClient()
}

// Run implements Command.Run.
func (c *infoCommand) Run(ctx *cmd.Context) (err error) {
	client, err := c.getUserInfoAPI()
	if err != nil {
		return err
	}
	defer client.Close()
	username := c.Username
	if username == "" {
		accountDetails, err := c.ClientStore().AccountByName(
			c.ControllerName(), c.AccountName(),
		)
		if err != nil {
			return err
		}
		username = accountDetails.User
	}
	result, err := client.UserInfo([]string{username}, false)
	if err != nil {
		return err
	}
	// Don't output the params type, be explicit. We convert before checking
	// length because we want to reuse the conversion function, and we are
	// pretty sure that there is one value there.
	output := c.apiUsersToUserInfoSlice(result)
	if len(output) != 1 {
		return errors.Errorf("expected 1 result, got %d", len(output))
	}
	return c.out.Write(ctx, output[0])
}

func (c *infoCommandBase) apiUsersToUserInfoSlice(users []params.UserInfo) []UserInfo {
	var output []UserInfo
	var now = time.Now()
	for _, info := range users {
		outInfo := UserInfo{
			Username:       info.Username,
			DisplayName:    info.DisplayName,
			Disabled:       info.Disabled,
			LastConnection: LastConnection(info.LastConnection, now, c.exactTime),
		}
		if c.exactTime {
			outInfo.DateCreated = info.DateCreated.String()
		} else {
			outInfo.DateCreated = UserFriendlyDuration(info.DateCreated, now)
		}

		output = append(output, outInfo)
	}

	return output
}

// LastConnection turns the *time.Time returned from the API server
// into a user facing string with either exact time or a user friendly
// string based on the args.
func LastConnection(connectionTime *time.Time, now time.Time, exact bool) string {
	if connectionTime == nil {
		return "never connected"
	}
	if exact {
		return connectionTime.String()
	}
	return UserFriendlyDuration(*connectionTime, now)
}

// UserFriendlyDuration translates a time in the past into a user
// friendly string representation relative to the "now" time argument.
func UserFriendlyDuration(when, now time.Time) string {
	since := now.Sub(when)
	// if over 24 hours ago, just say the date.
	if since.Hours() >= 24 {
		return when.Format("2006-01-02")
	}
	if since.Hours() >= 1 {
		unit := "hours"
		if int(since.Hours()) == 1 {
			unit = "hour"
		}
		return fmt.Sprintf("%d %s ago", int(since.Hours()), unit)
	}
	if since.Minutes() >= 1 {
		unit := "minutes"
		if int(since.Minutes()) == 1 {
			unit = "minute"
		}
		return fmt.Sprintf("%d %s ago", int(since.Minutes()), unit)
	}
	if since.Seconds() >= 2 {
		return fmt.Sprintf("%d seconds ago", int(since.Seconds()))
	}
	return "just now"
}
<commit_msg>Fix spello.<commit_after>// Copyright 2012-2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for infos.

package user

import (
	"fmt"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"launchpad.net/gnuflag"

	"github.com/juju/juju/api/usermanager"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/modelcmd"
)

var helpSummary = `
Show information about a user.`[1:]

var helpDetails = `
Output information (user-name, display-name, date-created, and
last-connection) about a user. By default it provides information about
the current user.

Examples:
juju show-user
juju show-user jsmith
juju show-user --format json
juju show-user --format yaml

See also:
add-user
register
list-users`[1:]

// UserInfoAPI defines the API methods that the info command uses.
type UserInfoAPI interface {
	UserInfo([]string, usermanager.IncludeDisabled) ([]params.UserInfo, error)
	Close() error
}

// infoCommandBase is a common base for 'juju show-user' and 'juju list-user'.
type infoCommandBase struct {
	modelcmd.ControllerCommandBase
	api       UserInfoAPI
	exactTime bool
	out       cmd.Output
}

func (c *infoCommandBase) SetFlags(f *gnuflag.FlagSet) {
	f.BoolVar(&c.exactTime, "exact-time", false, "use full timestamp precision")
}

func NewShowUserCommand() cmd.Command {
	return modelcmd.WrapController(&infoCommand{})
}

// infoCommand retrieves information about a single user.
type infoCommand struct {
	infoCommandBase
	Username string
}

// UserInfo defines the serialization behaviour of the user information.
type UserInfo struct {
	Username       string `yaml:"user-name" json:"user-name"`
	DisplayName    string `yaml:"display-name" json:"display-name"`
	DateCreated    string `yaml:"date-created" json:"date-created"`
	LastConnection string `yaml:"last-connection" json:"last-connection"`
	Disabled       bool   `yaml:"disabled,omitempty" json:"disabled,omitempty"`
}

// Info implements Command.Info.
func (c *infoCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "show-user",
		Args:    "<username>",
		Purpose: helpSummary,
		Doc:     helpDetails,
	}
}

// SetFlags implements Command.SetFlags.
func (c *infoCommand) SetFlags(f *gnuflag.FlagSet) {
	c.infoCommandBase.SetFlags(f)
	c.out.AddFlags(f, "yaml", cmd.DefaultFormatters)
}

// Init implements Command.Init.
func (c *infoCommand) Init(args []string) (err error) {
	c.Username, err = cmd.ZeroOrOneArgs(args)
	return err
}

func (c *infoCommandBase) getUserInfoAPI() (UserInfoAPI, error) {
	if c.api != nil {
		return c.api, nil
	}
	return c.NewUserManagerAPIClient()
}

// Run implements Command.Run.
func (c *infoCommand) Run(ctx *cmd.Context) (err error) {
	client, err := c.getUserInfoAPI()
	if err != nil {
		return err
	}
	defer client.Close()
	username := c.Username
	if username == "" {
		accountDetails, err := c.ClientStore().AccountByName(
			c.ControllerName(), c.AccountName(),
		)
		if err != nil {
			return err
		}
		username = accountDetails.User
	}
	result, err := client.UserInfo([]string{username}, false)
	if err != nil {
		return err
	}
	// Don't output the params type, be explicit. We convert before checking
	// length because we want to reuse the conversion function, and we are
	// pretty sure that there is one value there.
	output := c.apiUsersToUserInfoSlice(result)
	if len(output) != 1 {
		return errors.Errorf("expected 1 result, got %d", len(output))
	}
	return c.out.Write(ctx, output[0])
}

func (c *infoCommandBase) apiUsersToUserInfoSlice(users []params.UserInfo) []UserInfo {
	var output []UserInfo
	var now = time.Now()
	for _, info := range users {
		outInfo := UserInfo{
			Username:       info.Username,
			DisplayName:    info.DisplayName,
			Disabled:       info.Disabled,
			LastConnection: LastConnection(info.LastConnection, now, c.exactTime),
		}
		if c.exactTime {
			outInfo.DateCreated = info.DateCreated.String()
		} else {
			outInfo.DateCreated = UserFriendlyDuration(info.DateCreated, now)
		}

		output = append(output, outInfo)
	}

	return output
}

// LastConnection turns the *time.Time returned from the API server
// into a user facing string with either exact time or a user friendly
// string based on the args.
func LastConnection(connectionTime *time.Time, now time.Time, exact bool) string {
	if connectionTime == nil {
		return "never connected"
	}
	if exact {
		return connectionTime.String()
	}
	return UserFriendlyDuration(*connectionTime, now)
}

// UserFriendlyDuration translates a time in the past into a user
// friendly string representation relative to the "now" time argument.
func UserFriendlyDuration(when, now time.Time) string {
	since := now.Sub(when)
	// if over 24 hours ago, just say the date.
	if since.Hours() >= 24 {
		return when.Format("2006-01-02")
	}
	if since.Hours() >= 1 {
		unit := "hours"
		if int(since.Hours()) == 1 {
			unit = "hour"
		}
		return fmt.Sprintf("%d %s ago", int(since.Hours()), unit)
	}
	if since.Minutes() >= 1 {
		unit := "minutes"
		if int(since.Minutes()) == 1 {
			unit = "minute"
		}
		return fmt.Sprintf("%d %s ago", int(since.Minutes()), unit)
	}
	if since.Seconds() >= 2 {
		return fmt.Sprintf("%d seconds ago", int(since.Seconds()))
	}
	return "just now"
}
<|endoftext|>