<commit_before>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	gcsStorage "code.google.com/p/google-api-go-client/storage/v1beta2"
)

type Item struct {
	Prefix string
	Path   string
	Size   int64
	io.ReadCloser
}

func (i *Item) String() string {
	return fmt.Sprintf("(Prefix: %s) %s", i.Prefix, i.Path)
}

type Storage interface {
	ListFiles() <-chan *Item
	PutFile(item *Item) error
}

type S3Storage struct {
	bucket *s3.Bucket
	prefix string
}

func NewS3Storage(accessKey, secretKey, bucketUrl string, prefix string) (*S3Storage, error) {
	auth := aws.Auth{
		AccessKey: accessKey,
		SecretKey: secretKey,
	}
	u, err := url.Parse(bucketUrl)
	if err != nil {
		return nil, err
	}

	bucketname := strings.SplitN(strings.TrimPrefix(u.Path, "/"), "/", 2)[0]
	region, err := regionByEndpoint(u.Scheme + "://" + u.Host)
	if err != nil {
		return nil, err
	}
	s3i := s3.New(auth, region)
	b := s3i.Bucket(bucketname)
	return &S3Storage{
		bucket: b,
		prefix: prefix,
	}, nil
}

func regionByEndpoint(ep string) (aws.Region, error) {
	for _, region := range aws.Regions {
		if region.S3Endpoint == ep {
			return region, nil
		}
	}
	return aws.Region{}, fmt.Errorf("Unknown region endpoint %s", ep)
}

func (s *S3Storage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		marker := ""
		defer close(c)
		for {
			resp, err := s.bucket.List(s.prefix, "", marker, 1000)
			if err != nil {
				log.Printf("Could not list items in bucket %s: %s", s.bucket.Name, err)
				return
			}
			for _, item := range resp.Contents {
				rc, err := s.bucket.GetReader(item.Key)
				if err != nil {
					log.Printf("Could not receive %s: %s", item, err)
					continue
				}
				c <- &Item{
					Prefix:     s.prefix,
					Path:       item.Key,
					Size:       item.Size,
					ReadCloser: rc,
				}
				marker = item.Key
			}
			if !resp.IsTruncated {
				break
			}
		}
	}()
	return c
}

func (s *S3Storage) PutFile(item *Item) error {
	defer item.Close()
	path := strings.TrimPrefix(item.Path, item.Prefix)
	key := filepath.Join(s.prefix, path)
	err := s.bucket.PutReader(key, item, item.Size, mime.TypeByExtension(filepath.Ext(item.Path)), s3.BucketOwnerFull)
	if err != nil {
		return err
	}
	return nil
}

type GcsStorage struct {
	service *gcsStorage.Service
	bucket  string
	prefix  string
	client  *http.Client
}

func NewGcsStorage(clientId string, pem io.ReadCloser, bucket, prefix string) (*GcsStorage, error) {
	pemBytes, err := ioutil.ReadAll(pem)
	if err != nil {
		return nil, err
	}

	token := jwt.NewToken(clientId, gcsStorage.DevstorageRead_writeScope, pemBytes)
	c := &http.Client{}
	oauthToken, err := token.Assert(c)
	if err != nil {
		return nil, err
	}

	c.Transport = &oauth.Transport{
		Token: oauthToken,
	}
	service, err := gcsStorage.New(c)
	if err != nil {
		return nil, err
	}
	return &GcsStorage{
		service: service,
		bucket:  bucket,
		prefix:  prefix,
		client:  c,
	}, nil
}

func (s *GcsStorage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		defer close(c)
		objs, err := s.service.Objects.List(s.bucket).Prefix(s.prefix).Do()
		if err != nil {
			log.Printf("Could not list items in bucket %s: %s", s.bucket, err)
			return
		}

		for _, obj := range objs.Items {
			resp, err := s.client.Get(obj.MediaLink)
			if err != nil {
				log.Printf("Could not get %s: %s", obj.Name, err)
				continue
			}
			c <- &Item{
				Prefix:     s.prefix,
				Path:       obj.Name,
				Size:       int64(obj.Size),
				ReadCloser: resp.Body,
			}
		}
	}()
	return c
}

func (s *GcsStorage) PutFile(item *Item) error {
	log.Printf("Putting %s not implemented", item)
	return nil
}

type LocalStorage struct {
	Prefix string
}

func (s *LocalStorage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		defer close(c)
		newprefix, err := filepath.Abs(s.Prefix)
		if err != nil {
			log.Printf("Path %s could not be made absolute: %s", newprefix, err)
			return
		}
		f, err := os.Open(newprefix)
		if err != nil {
			log.Printf("Could not open %s: %s", newprefix, err)
			return
		}
		fi, err := f.Stat()
		if err != nil {
			log.Printf("Could not stat %s: %s", newprefix, err)
			return
		}
		if !fi.IsDir() {
			c <- &Item{
				Prefix:     filepath.Dir(newprefix),
				Path:       newprefix,
				Size:       fi.Size(),
				ReadCloser: f,
			}
			return
		}
		log.Printf("Traversing %s...", newprefix)
		filepath.Walk(newprefix, func(path string, info os.FileInfo, err error) error {
			if info.IsDir() {
				return nil
			}
			f, err := os.Open(path)
			if err != nil {
				log.Printf("Could not open %s: %s", path, err)
				return nil
			}
			c <- &Item{
				Prefix:     newprefix,
				Path:       path,
				ReadCloser: f,
			}
			return nil
		})
	}()
	return c
}

func (s *LocalStorage) PutFile(item *Item) error {
	defer item.Close()
	itempath := strings.TrimPrefix(item.Path, item.Prefix)
	dirname, fname := filepath.Split(itempath)
	dirname = filepath.Join(s.Prefix, dirname)

	err := os.MkdirAll(dirname, os.FileMode(0755))
	if err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(dirname, fname))
	if err != nil {
		return err
	}
	defer f.Close()

	io.Copy(f, item)
	return nil
}

func CopyItems(dst Storage, items <-chan *Item, concurrency int) {
	wg := &sync.WaitGroup{}
	wg.Add(concurrency)
	log.Printf("Starting %d goroutines...", concurrency)
	for i := 0; i < concurrency; i++ {
		go func() {
			defer wg.Done()
			for item := range items {
				dst.PutFile(item)
				log.Printf("Transfer of %s done", item)
			}
		}()
	}
	wg.Wait()
}
<commit_msg>Implement gcs putting<commit_after>package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"launchpad.net/goamz/aws"
	"launchpad.net/goamz/s3"

	"code.google.com/p/goauth2/oauth"
	"code.google.com/p/goauth2/oauth/jwt"
	gcsStorage "code.google.com/p/google-api-go-client/storage/v1beta2"
)

type Item struct {
	Prefix string
	Path   string
	Size   int64
	io.ReadCloser
}

func (i *Item) String() string {
	return fmt.Sprintf("(Prefix: %s) %s", i.Prefix, i.Path)
}

type Storage interface {
	// Lists all files in the storage system. Any kind of
	// chrooting/prefixing has to be implemented and enforced manually.
	ListFiles() <-chan *Item
	// Saves a file to the storage system. Any kind of
	// chrooting/prefixing has to be implemented and enforced manually.
	PutFile(item *Item) error
}

type S3Storage struct {
	bucket *s3.Bucket
	prefix string
}

func NewS3Storage(accessKey, secretKey, bucketUrl string, prefix string) (*S3Storage, error) {
	auth := aws.Auth{
		AccessKey: accessKey,
		SecretKey: secretKey,
	}
	u, err := url.Parse(bucketUrl)
	if err != nil {
		return nil, err
	}

	bucketname := strings.SplitN(strings.TrimPrefix(u.Path, "/"), "/", 2)[0]
	region, err := regionByEndpoint(u.Scheme + "://" + u.Host)
	if err != nil {
		return nil, err
	}
	s3i := s3.New(auth, region)
	b := s3i.Bucket(bucketname)
	return &S3Storage{
		bucket: b,
		prefix: prefix,
	}, nil
}

func regionByEndpoint(ep string) (aws.Region, error) {
	for _, region := range aws.Regions {
		if region.S3Endpoint == ep {
			return region, nil
		}
	}
	return aws.Region{}, fmt.Errorf("Unknown region endpoint %s", ep)
}

func (s *S3Storage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		marker := ""
		defer close(c)
		for {
			resp, err := s.bucket.List(s.prefix, "", marker, 1000)
			if err != nil {
				log.Printf("Could not list items in bucket %s: %s", s.bucket.Name, err)
				return
			}
			for _, item := range resp.Contents {
				rc, err := s.bucket.GetReader(item.Key)
				if err != nil {
					log.Printf("Could not receive %s: %s", item, err)
					continue
				}
				c <- &Item{
					Prefix:     s.prefix,
					Path:       item.Key,
					Size:       item.Size,
					ReadCloser: rc,
				}
				marker = item.Key
			}
			if !resp.IsTruncated {
				break
			}
		}
	}()
	return c
}

func (s *S3Storage) PutFile(item *Item) error {
	defer item.Close()
	path := strings.TrimPrefix(item.Path, item.Prefix)
	key := filepath.Join(s.prefix, path)
	err := s.bucket.PutReader(key, item, item.Size, mime.TypeByExtension(filepath.Ext(item.Path)), s3.BucketOwnerFull)
	if err != nil {
		return err
	}
	return nil
}

type GcsStorage struct {
	service *gcsStorage.Service
	bucket  string
	prefix  string
	client  *http.Client
}

func NewGcsStorage(clientId string, pem io.ReadCloser, bucket, prefix string) (*GcsStorage, error) {
	pemBytes, err := ioutil.ReadAll(pem)
	if err != nil {
		return nil, err
	}

	token := jwt.NewToken(clientId, gcsStorage.DevstorageRead_writeScope, pemBytes)
	c := &http.Client{}
	oauthToken, err := token.Assert(c)
	if err != nil {
		return nil, err
	}

	c.Transport = &oauth.Transport{
		Token: oauthToken,
	}
	service, err := gcsStorage.New(c)
	if err != nil {
		return nil, err
	}
	return &GcsStorage{
		service: service,
		bucket:  bucket,
		prefix:  prefix,
		client:  c,
	}, nil
}

func (s *GcsStorage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		defer close(c)
		objs, err := s.service.Objects.List(s.bucket).Prefix(s.prefix).Do()
		if err != nil {
			log.Printf("Could not list items in bucket %s: %s", s.bucket, err)
			return
		}

		for _, obj := range objs.Items {
			resp, err := s.client.Get(obj.MediaLink)
			if err != nil {
				log.Printf("Could not get %s: %s", obj.Name, err)
				continue
			}
			c <- &Item{
				Prefix:     s.prefix,
				Path:       obj.Name,
				Size:       int64(obj.Size),
				ReadCloser: resp.Body,
			}
		}
	}()
	return c
}

func (s *GcsStorage) PutFile(item *Item) error {
	newprefix := strings.TrimPrefix(item.Path, item.Prefix)
	_, err := s.service.Objects.Insert(s.bucket, &gcsStorage.Object{}).Name(filepath.Join(s.prefix, newprefix)).Media(item).Do()
	if err != nil {
		log.Fatalf("Could not create new object: %s", err)
	}
	return nil
}

type LocalStorage struct {
	Prefix string
}

func (s *LocalStorage) ListFiles() <-chan *Item {
	c := make(chan *Item)
	go func() {
		defer close(c)
		newprefix, err := filepath.Abs(s.Prefix)
		if err != nil {
			log.Printf("Path %s could not be made absolute: %s", newprefix, err)
			return
		}
		f, err := os.Open(newprefix)
		if err != nil {
			log.Printf("Could not open %s: %s", newprefix, err)
			return
		}
		fi, err := f.Stat()
		if err != nil {
			log.Printf("Could not stat %s: %s", newprefix, err)
			return
		}
		if !fi.IsDir() {
			c <- &Item{
				Prefix:     filepath.Dir(newprefix),
				Path:       newprefix,
				Size:       fi.Size(),
				ReadCloser: f,
			}
			return
		}
		log.Printf("Traversing %s...", newprefix)
		filepath.Walk(newprefix, func(path string, info os.FileInfo, err error) error {
			if info.IsDir() {
				return nil
			}
			f, err := os.Open(path)
			if err != nil {
				log.Printf("Could not open %s: %s", path, err)
				return nil
			}
			c <- &Item{
				Prefix:     newprefix,
				Path:       path,
				ReadCloser: f,
			}
			return nil
		})
	}()
	return c
}

func (s *LocalStorage) PutFile(item *Item) error {
	defer item.Close()
	itempath := strings.TrimPrefix(item.Path, item.Prefix)
	dirname, fname := filepath.Split(itempath)
	dirname = filepath.Join(s.Prefix, dirname)

	err := os.MkdirAll(dirname, os.FileMode(0755))
	if err != nil {
		return err
	}

	f, err := os.Create(filepath.Join(dirname, fname))
	if err != nil {
		return err
	}
	defer f.Close()

	io.Copy(f, item)
	return nil
}

func CopyItems(dst Storage, items <-chan *Item, concurrency int) {
	wg := &sync.WaitGroup{}
	wg.Add(concurrency)
	log.Printf("Starting %d goroutines...", concurrency)
	for i := 0; i < concurrency; i++ {
		go func() {
			defer wg.Done()
			for item := range items {
				dst.PutFile(item)
				log.Printf("Transfer of %s done", item)
			}
		}()
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

var Version string

func main() {
	app := cli.NewApp()
	app.Name = "structr"
	app.Usage = "Generate structure definition(s) from JSON Schema"
	app.UsageText = "structr [command] [command options] [filepath...]"
	app.Author = "dameleon"
	app.Email = "dameleon@gmail.com"
	app.Version = Version
	app.Commands = Commands
	app.Run(os.Args)
}
<commit_msg>added version<commit_after>package main

import (
	"github.com/codegangsta/cli"
	"os"
)

var Version = "0.0.1"

func main() {
	app := cli.NewApp()
	app.Name = "structr"
	app.Usage = "Generate structure definition(s) from JSON Schema"
	app.UsageText = "structr [command] [command options] [filepath...]"
	app.Author = "dameleon"
	app.Email = "dameleon@gmail.com"
	app.Version = Version
	app.Commands = Commands
	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/aws/aws-sdk-go/service/cloudwatchevents"
)

type Rules struct {
	Rules []Rule
}

type Rule struct {
	Description        string   `yaml:"description"`
	EventPattern       string   `yaml:"event_pattern"`
	Name               string   `yaml:"name"`
	ScheduleExpression string   `yaml:"schedule_expression"`
	State              string   `yaml:"state"`
	Targets            []Target `yaml:"targets"`
	ActualRule         cloudwatchevents.Rule
	NeedUpdate         bool
}

type Target struct {
	Arn          string `yaml:"arn"`
	Id           string `yaml:"id"`
	Input        string `yaml:"input"`
	InputPath    string `yaml:"input_path"`
	ActualTarget *cloudwatchevents.Target
	NeedUpdate   bool
}
<commit_msg>Change type of ActualTarget in Target<commit_after>package main

import (
	"github.com/aws/aws-sdk-go/service/cloudwatchevents"
)

type Rules struct {
	Rules []Rule
}

type Rule struct {
	Description        string   `yaml:"description"`
	EventPattern       string   `yaml:"event_pattern"`
	Name               string   `yaml:"name"`
	ScheduleExpression string   `yaml:"schedule_expression"`
	State              string   `yaml:"state"`
	Targets            []Target `yaml:"targets"`
	ActualRule         cloudwatchevents.Rule
	NeedUpdate         bool
}

type Target struct {
	Arn          string `yaml:"arn"`
	Id           string `yaml:"id"`
	Input        string `yaml:"input"`
	InputPath    string `yaml:"input_path"`
	ActualTarget cloudwatchevents.Target
	NeedUpdate   bool
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/leonelquinteros/gorand"
	"gopkg.in/ldap.v2"
	"net/http"
	"strings"
	_ "strings"
	"time"
)

var authOptions *AuthorizationOptions

// An error that can be serialized to
// JSON and returned to the client
type Error struct {
	// Error message
	Message string
	// The http path to the api endpoint
	// to fix the error, eg: /login
	Path string
}

// A set of options necessary to find
// and login to a LDAP server
type AuthorizationOptions struct {
	realm          string
	ldapServer     string
	port           int
	username       string
	password       string
	ldapSearchBase string
}

type Credentials struct {
	Username string
	Password string
}

// setup handlers
func init() {
	var logoutHandler = http.HandlerFunc(Logout)
	var loginHandler = http.HandlerFunc(Login)
	http.Handle("/login", loginHandler)
	http.Handle("/logout", logoutHandler)
}

// LdapAuthFunc authenticates a user against an LDAP server
// The Request parameter is probably not necessary.
func LdapAuthFunc(creds *Credentials) bool {
	hostaddr := fmt.Sprintf("%s:%d", authOptions.ldapServer, authOptions.port) // move to config file
	conn, err := ldap.Dial("tcp", hostaddr)
	if err != nil {
		log.Fatal(err)
	}

	defer conn.Close()

	err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
	if err != nil {
		log.Printf("Could not connect to LDAP: %s", err)
		log.Fatal(err)
	}

	dn, err := SanitizeDN(creds.Username)
	if err != nil {
		log.Infof("User %s attempted authentication with an invalid username", creds.Username)
		return false
	}

	if len(creds.Password) == 0 {
		return false
	}

	if strings.LastIndexAny(dn, "@") < 0 { // not an email address
		dn = authOptions.realm + "\\" + dn
	}
	err = conn.Bind(dn, creds.Password)
	if err == nil {
		log.Debugf("User %s logged in", creds.Username)
		return true
	} else {
		log.Printf("LDAP: %s", err.Error())
		return false
	}
}

// Escape a string for use in a DN
// This follows the rules for allowed characters
// in a samaccountname field in Active Directory
// Other LDAP implementations may differ
func SanitizeDN(dn string) (string, error) {
	var newdn string = dn
	forbiddenChars := "\"[]:;|=+*?<>/\\,"

	// no empty distinguished names allowed
	if len(dn) == 0 {
		return "", fmt.Errorf("forbidden")
	}

	if strings.LastIndexAny(dn, forbiddenChars) >= 0 {
		return "", fmt.Errorf("forbidden")
	}
	// characters with values < 32 are also forbidden
	for _, c := range dn {
		if int(c) < 32 {
			return "", fmt.Errorf("forbidden")
		}
	}
	// escape a leading space or # character
	if len(dn) > 0 && (dn[0] == ' ' || dn[0] == '#') {
		newdn = "\\" + dn
	}

	// escape \ * ( ) and NUL characters
	replacer := strings.NewReplacer("\\", "\\5c", "*", "\\2a", "(", "\\28", ")", "\\29", "\u0000", "\\00")
	newdn = replacer.Replace(newdn)
	// escape a trailing space
	if dn[len(dn)-1] == ' ' {
		newdn = strings.TrimSpace(newdn)
		newdn += "\\ "
	}
	log.Debugf("Replaced username \"%s\" with \"%s\"", dn, newdn)
	return newdn, nil
}

// Find a person in LDAP
func FindUser(username string) (Person, error) {
	var user Person
	if authOptions == nil {
		log.Panicf("Auth options should not be nil")
	}
	hostaddr := fmt.Sprintf("%s:%d", authOptions.ldapServer, authOptions.port)
	conn, err := ldap.Dial("tcp", hostaddr)
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	defer conn.Close()

	err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	err = conn.Bind(authOptions.realm+"\\"+authOptions.username, authOptions.password)
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	dn, err := SanitizeDN(username)
	if err != nil {
		return user, fmt.Errorf("bad username")
	}

	var queryString string
	if strings.LastIndexAny(dn, "@") > 0 {
		queryString = fmt.Sprintf("(&(objectClass=organizationalPerson)(userPrincipalName=%s))", dn)
	} else {
		queryString = fmt.Sprintf("(&(objectClass=organizationalPerson)(sAMAccountName=%s))", dn)
	}

	searchRequest := ldap.NewSearchRequest(
		authOptions.ldapSearchBase,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		queryString,
		[]string{"userPrincipalName", "cn", "title", "department", "telephoneNumber", "mobile", "physicalDeliveryOfficeName"},
		nil,
	)
	res, err := conn.Search(searchRequest)
	if err != nil || len(res.Entries) != 1 {
		log.Fatal(err)
	}

	ldapPerson := res.Entries[0]

	user = Person{
		Username:   ldapPerson.GetAttributeValue("userPrincipalName"),
		Name:       ldapPerson.GetAttributeValue("cn"),
		Department: ldapPerson.GetAttributeValue("department"),
		Telephone:  ldapPerson.GetAttributeValue("telephoneNumber"),
		Mobile:     ldapPerson.GetAttributeValue("mobile"),
		Office:     ldapPerson.GetAttributeValue("physicalDeliveryOfficeName"),
		Title:      ldapPerson.GetAttributeValue("title"),
	}
	return user, err
}

// Create a user for a given username. This user must exist
// in LDAP
func CreateUser(username string) (*Person, error) {
	var err error
	var user *Person = new(Person)
	*user, err = FindUser(username)
	if err != nil {
		return nil, err
	}

	if user, err = GetPerson(user.Username); user != nil {
		return user, err
	}

	user, err = AddPerson(
		user.Username,
		user.Name,
		user.Department,
		user.Telephone,
		user.Mobile,
		user.Office,
		user.Title,
	)
	return user, err
}

// Update all the database users with attributes
// from LDAP. Accordingly, this takes a set of
// LDAP connection options as a parameter
func UpdateLdap(options AuthorizationOptions) error {
	authOptions = &options
	// get users
	people, err := GetUsers()
	if err != nil {
		log.Fatalf("Failed to get users from the database: %s", err.Error())
		return err
	}
	// for each user, get the LDAP entry
	for _, user := range people {
		updated, err := FindUser(user.Username)
		if err != nil {
			log.Printf("Failed to get user %s from the LDAP Server: %s", user.Username, err.Error())
		}
		// and update the database
		user.Name = updated.Name
		user.Department = updated.Department
		user.Office = updated.Office
		user.Telephone = updated.Telephone
		user.Mobile = updated.Mobile
		user.Title = updated.Title
		log.Debugf("updating %s with name = %s, department = %s, office = %s telephone = %s, mobile = %s, title = %s",
			user.Username,
			user.Name,
			user.Department,
			user.Office,
			user.Telephone,
			user.Mobile,
			user.Title)
		if SetPersonDetails(user) != nil {
			log.Printf("Failed to update user %s: %s", user.Username, err)
		}

		fmt.Printf(". ")
	}
	fmt.Printf("\n")
	return nil
}

// Handles cookie-based authentication. An incoming
// request will have its session ID read from a cookie, and if
// the session is not valid, returns a JSON-encoded response
// redirecting to the Login api endpoint.
func AuthorizationMiddleware(options AuthorizationOptions, next http.Handler) http.Handler {
	authOptions = &options
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var session string = ""
		cookies := r.Cookies()
		for _, cookie := range cookies {
			if cookie.Name == "session" {
				session = cookie.Value
				log.Println("Session ", cookie.Value)
				break
			}
		}

		if username, err := ValidateSession(session); err == nil {
			next.ServeHTTP(w, r.WithContext(newContextWithUsername(r.Context(), username)))
		} else {
			log.Println("no session found")
			sessionErr := &Error{Message: "unauthorized", Path: "/login"}
			log.Printf(sessionErr.Message)
			content, _ := json.Marshal(sessionErr)
			http.Error(w, string(content), http.StatusUnauthorized)
			if err := json.NewEncoder(w).Encode(sessionErr); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	})
}

// Removes a session from the database and sets
// the cookie to be expired.
func Logout(w http.ResponseWriter, r *http.Request) {
	log.Println("logged out")
	var cookie http.Cookie
	cookies := r.Cookies()
	for _, c := range cookies {
		if c.Name == "session" {
			cookie = *c
			break
		}
	}
	if err := RemoveSession(cookie.Value); err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	}
	cookie.Value = ""
	cookie.Expires = time.Unix(0, 0)
	http.SetCookie(w, &cookie)
	w.Write([]byte("Logged out"))
}

// Authenticate a user and create a session in the
// database, returning a cookie with the session ID
func Login(w http.ResponseWriter, r *http.Request) {
	log.Println("Creating session")
	id, err := gorand.UUIDv4()
	session, err := gorand.MarshalUUID(id)
	if err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	}

	creds := new(Credentials)
	err = json.NewDecoder(r.Body).Decode(creds)
	creds.Username = strings.TrimSpace(creds.Username)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if LdapAuthFunc(creds) {
		var person *Person

		if person, err = GetPerson(creds.Username); err != nil {
			// create user from ldap store
			person, err = CreateUser(creds.Username)
			if err != nil {
				log.Fatalf("Failed to create user: %s", err.Error())
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
		if err = CreateSession(session, person.ID); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		log.Printf("created session %s", session)

		cookie := &http.Cookie{
			Name:     "session",
			Value:    session,
			HttpOnly: false,
			Expires:  time.Now().AddDate(1, 0, 0),
		}
		http.SetCookie(w, cookie)
		w.Write([]byte("success"))

	} else {
		log.Printf("login failed")
		sessionErr := Error{Message: "login failed", Path: "/login"}
		content, err := json.Marshal(sessionErr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Error(w, string(content), http.StatusUnauthorized)
	}
}

const requestUsernameKey = 0

// get the username from a context object
func usernameFromContext(ctx context.Context) string {
	return ctx.Value(requestUsernameKey).(string)
}

// return a new context object with the username in it
func newContextWithUsername(ctx context.Context, username string) context.Context {
	return context.WithValue(ctx, requestUsernameKey, username)
}
<commit_msg>fixed crash bug on user creation<commit_after>package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/leonelquinteros/gorand"
	"gopkg.in/ldap.v2"
	"net/http"
	"strings"
	_ "strings"
	"time"
)

var authOptions *AuthorizationOptions

// An error that can be serialized to
// JSON and returned to the client
type Error struct {
	// Error message
	Message string
	// The http path to the api endpoint
	// to fix the error, eg: /login
	Path string
}

// A set of options necessary to find
// and login to a LDAP server
type AuthorizationOptions struct {
	realm          string
	ldapServer     string
	port           int
	username       string
	password       string
	ldapSearchBase string
}

type Credentials struct {
	Username string
	Password string
}

// setup handlers
func init() {
	var logoutHandler = http.HandlerFunc(Logout)
	var loginHandler = http.HandlerFunc(Login)
	http.Handle("/login", loginHandler)
	http.Handle("/logout", logoutHandler)
}

// LdapAuthFunc authenticates a user against an LDAP server
// The Request parameter is probably not necessary.
func LdapAuthFunc(creds *Credentials) bool {
	hostaddr := fmt.Sprintf("%s:%d", authOptions.ldapServer, authOptions.port) // move to config file
	conn, err := ldap.Dial("tcp", hostaddr)
	if err != nil {
		log.Fatal(err)
	}

	defer conn.Close()

	err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
	if err != nil {
		log.Printf("Could not connect to LDAP: %s", err)
		log.Fatal(err)
	}

	dn, err := SanitizeDN(creds.Username)
	if err != nil {
		log.Infof("User %s attempted authentication with an invalid username", creds.Username)
		return false
	}

	if len(creds.Password) == 0 {
		return false
	}

	if strings.LastIndexAny(dn, "@") < 0 { // not an email address
		dn = authOptions.realm + "\\" + dn
	}
	err = conn.Bind(dn, creds.Password)
	if err == nil {
		log.Debugf("User %s logged in", creds.Username)
		return true
	} else {
		log.Printf("LDAP: %s", err.Error())
		return false
	}
}

// Escape a string for use in a DN
// This follows the rules for allowed characters
// in a samaccountname field in Active Directory
// Other LDAP implementations may differ
func SanitizeDN(dn string) (string, error) {
	var newdn string = dn
	forbiddenChars := "\"[]:;|=+*?<>/\\,"

	// no empty distinguished names allowed
	if len(dn) == 0 {
		return "", fmt.Errorf("forbidden")
	}

	if strings.LastIndexAny(dn, forbiddenChars) >= 0 {
		return "", fmt.Errorf("forbidden")
	}
	// characters with values < 32 are also forbidden
	for _, c := range dn {
		if int(c) < 32 {
			return "", fmt.Errorf("forbidden")
		}
	}
	// escape a leading space or # character
	if len(dn) > 0 && (dn[0] == ' ' || dn[0] == '#') {
		newdn = "\\" + dn
	}

	// escape \ * ( ) and NUL characters
	replacer := strings.NewReplacer("\\", "\\5c", "*", "\\2a", "(", "\\28", ")", "\\29", "\u0000", "\\00")
	newdn = replacer.Replace(newdn)
	// escape a trailing space
	if dn[len(dn)-1] == ' ' {
		newdn = strings.TrimSpace(newdn)
		newdn += "\\ "
	}
	log.Debugf("Replaced username \"%s\" with \"%s\"", dn, newdn)
	return newdn, nil
}

// Find a person in LDAP
func FindUser(username string) (Person, error) {
	var user Person
	if authOptions == nil {
		log.Panicf("Auth options should not be nil")
	}
	hostaddr := fmt.Sprintf("%s:%d", authOptions.ldapServer, authOptions.port)
	conn, err := ldap.Dial("tcp", hostaddr)
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	defer conn.Close()

	err = conn.StartTLS(&tls.Config{InsecureSkipVerify: true})
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	err = conn.Bind(authOptions.realm+"\\"+authOptions.username, authOptions.password)
	if err != nil {
		log.Fatal(err)
		return user, err
	}

	dn, err := SanitizeDN(username)
	if err != nil {
		return user, fmt.Errorf("bad username")
	}

	var queryString string
	if strings.LastIndexAny(dn, "@") > 0 {
		queryString = fmt.Sprintf("(&(objectClass=organizationalPerson)(userPrincipalName=%s))", dn)
	} else {
		queryString = fmt.Sprintf("(&(objectClass=organizationalPerson)(sAMAccountName=%s))", dn)
	}

	searchRequest := ldap.NewSearchRequest(
		authOptions.ldapSearchBase,
		ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
		queryString,
		[]string{"userPrincipalName", "cn", "title", "department", "telephoneNumber", "mobile", "physicalDeliveryOfficeName"},
		nil,
	)
	res, err := conn.Search(searchRequest)
	if err != nil || len(res.Entries) != 1 {
		log.Fatal(err)
	}

	ldapPerson := res.Entries[0]

	user = Person{
		Username:   ldapPerson.GetAttributeValue("userPrincipalName"),
		Name:       ldapPerson.GetAttributeValue("cn"),
		Department: ldapPerson.GetAttributeValue("department"),
		Telephone:  ldapPerson.GetAttributeValue("telephoneNumber"),
		Mobile:     ldapPerson.GetAttributeValue("mobile"),
		Office:     ldapPerson.GetAttributeValue("physicalDeliveryOfficeName"),
		Title:      ldapPerson.GetAttributeValue("title"),
	}
	return user, err
}

// Create a user for a given username. This user must exist
// in LDAP
func CreateUser(username string) (*Person, error) {
	var err error
	var user *Person = new(Person)
	*user, err = FindUser(username)
	if err != nil {
		return nil, err
	}

	if sqlUser, err := GetPerson(user.Username); sqlUser != nil {
		return sqlUser, err
	}

	user, err = AddPerson(
		user.Username,
		user.Name,
		user.Department,
		user.Telephone,
		user.Mobile,
		user.Office,
		user.Title,
	)
	return user, err
}

// Update all the database users with attributes
// from LDAP. Accordingly, this takes a set of
// LDAP connection options as a parameter
func UpdateLdap(options AuthorizationOptions) error {
	authOptions = &options
	// get users
	people, err := GetUsers()
	if err != nil {
		log.Fatalf("Failed to get users from the database: %s", err.Error())
		return err
	}
	// for each user, get the LDAP entry
	for _, user := range people {
		updated, err := FindUser(user.Username)
		if err != nil {
			log.Printf("Failed to get user %s from the LDAP Server: %s", user.Username, err.Error())
		}
		// and update the database
		user.Name = updated.Name
		user.Department = updated.Department
		user.Office = updated.Office
		user.Telephone = updated.Telephone
		user.Mobile = updated.Mobile
		user.Title = updated.Title
		log.Debugf("updating %s with name = %s, department = %s, office = %s telephone = %s, mobile = %s, title = %s",
			user.Username,
			user.Name,
			user.Department,
			user.Office,
			user.Telephone,
			user.Mobile,
			user.Title)
		if SetPersonDetails(user) != nil {
			log.Printf("Failed to update user %s: %s", user.Username, err)
		}

		fmt.Printf(". ")
	}
	fmt.Printf("\n")
	return nil
}

// Handles cookie-based authentication. An incoming
// request will have its session ID read from a cookie, and if
// the session is not valid, returns a JSON-encoded response
// redirecting to the Login api endpoint.
func AuthorizationMiddleware(options AuthorizationOptions, next http.Handler) http.Handler {
	authOptions = &options
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		var session string = ""
		cookies := r.Cookies()
		for _, cookie := range cookies {
			if cookie.Name == "session" {
				session = cookie.Value
				log.Println("Session ", cookie.Value)
				break
			}
		}

		if username, err := ValidateSession(session); err == nil {
			next.ServeHTTP(w, r.WithContext(newContextWithUsername(r.Context(), username)))
		} else {
			log.Println("no session found")
			sessionErr := &Error{Message: "unauthorized", Path: "/login"}
			log.Printf(sessionErr.Message)
			content, _ := json.Marshal(sessionErr)
			http.Error(w, string(content), http.StatusUnauthorized)
			if err := json.NewEncoder(w).Encode(sessionErr); err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
	})
}

// Removes a session from the database and sets
// the cookie to be expired.
func Logout(w http.ResponseWriter, r *http.Request) {
	log.Println("logged out")
	var cookie http.Cookie
	cookies := r.Cookies()
	for _, c := range cookies {
		if c.Name == "session" {
			cookie = *c
			break
		}
	}
	if err := RemoveSession(cookie.Value); err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	}
	cookie.Value = ""
	cookie.Expires = time.Unix(0, 0)
	http.SetCookie(w, &cookie)
	w.Write([]byte("Logged out"))
}

// Authenticate a user and create a session in the
// database, returning a cookie with the session ID
func Login(w http.ResponseWriter, r *http.Request) {
	log.Println("Creating session")
	id, err := gorand.UUIDv4()
	session, err := gorand.MarshalUUID(id)
	if err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	}

	creds := new(Credentials)
	err = json.NewDecoder(r.Body).Decode(creds)
	creds.Username = strings.TrimSpace(creds.Username)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	if LdapAuthFunc(creds) {
		var person *Person

		if person, err = GetPerson(creds.Username); err != nil {
			// create user from ldap store
			person, err = CreateUser(creds.Username)
			if err != nil {
				log.Fatalf("Failed to create user: %s", err.Error())
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
		}
		if err = CreateSession(session, person.ID); err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		log.Printf("created session %s", session)

		cookie := &http.Cookie{
			Name:     "session",
			Value:    session,
			HttpOnly: false,
			Expires:  time.Now().AddDate(1, 0, 0),
		}
		http.SetCookie(w, cookie)
		w.Write([]byte("success"))

	} else {
		log.Printf("login failed")
		sessionErr := Error{Message: "login failed", Path: "/login"}
		content, err := json.Marshal(sessionErr)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Error(w, string(content), http.StatusUnauthorized)
	}
}

const requestUsernameKey = 0

// get the username from a context object
func usernameFromContext(ctx context.Context) string {
	return ctx.Value(requestUsernameKey).(string)
}

// return a new context object with the username in it
func newContextWithUsername(ctx context.Context, username string) context.Context {
	return context.WithValue(ctx, requestUsernameKey, username)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"errors"
	"strings"
	"strconv"
	"math"
	"os"
	"path"
	"io"
	"bufio"
	"container/list"
	"github.com/jessevdk/go-flags"
)

type subtitle struct {
	text string
	start uint
	end uint
}

func die(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func roundFloat64(f float64) float64 {
	val := f - float64(int64(f))
	if val >= 0.5 {
		return math.Ceil(f)
	} else if val > 0 {
		return math.Floor(f)
	} else if val <= -0.5 {
		return math.Floor(f)
	} else if val < 0 {
		return math.Ceil(f)
	}
	return f
}

/* converts hh:mm:ss,mss to milliseconds */
func time_to_msecs(tm string) (uint, error) {
	var msecs uint
	var h, m, s, ms uint

	tm = strings.Replace(tm, ".", ",", 1)
	num, err := fmt.Sscanf(tm, "%d:%d:%d,%d", &h, &m, &s, &ms)

	if num != 4 || err != nil {
		return 0, errors.New("Parsing error: Can not covert `" + tm + "' to milliseconds.")
	}

	msecs = h * 60 * 60 * 1000
	msecs += m * 60 * 1000
	msecs += s * 1000
	msecs += ms

	return msecs, nil
}

/* converts milliseconds to hh:mm:ss,mss */
func msecs_to_time(msecs uint) string {
	var h, m, s, ms uint

	h = msecs / (60 * 60 * 1000)
	msecs = msecs % (60 * 60 * 1000)
	m = msecs / (60 * 1000)
	msecs = msecs % (60 * 1000)
	s = msecs / 1000
	msecs = msecs % 1000
	ms = msecs

	tm := fmt.Sprintf("%02d:%02d:%02d,%03d", h, m, s, ms)

	return tm
}

/* read SubRip (srt) file */
func read_srt(filename string) (*list.List, error) {
	var state int = 0
	var subs *list.List
	var sub *subtitle

	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	r := bufio.NewReader(f)
	subs = list.New()
	sub = new(subtitle)

	for {
		var (
			isprefix bool = true
			err error = nil
			ln, line []byte
		)

		for isprefix && err == nil {
			line, isprefix, err = r.ReadLine()
			if err != nil && err != io.EOF {
				return nil, err
			}
			ln = append(ln, line...)
		}

		/* parse subtitle id */
		if state == 0 {
			/* avoid false-positive parsing error */
			if err == io.EOF && len(ln) == 0 {
				break;
			}
			id := strings.Split(string(ln), " ")
			if len(id) != 1 {
				return nil, errors.New("Parsing error: Wrong file format")
			}
			_, err = strconv.ParseUint(id[0], 10, 0)
			if err != nil {
				return nil, err
				return nil, errors.New("Parsing error: Wrong file format")
			}
			state = 1
		/* parse start, end times */
		} else if state == 1 {
			tm := strings.Split(string(ln), " ")
			if len(tm) != 3 || tm[1] != "-->" {
				return nil, errors.New("Parsing error: Wrong file format")
			}
			sub.start, err = time_to_msecs(tm[0])
			if err != nil {
				return nil, err
			}
			sub.end, err = time_to_msecs(tm[2])
			if err != nil {
				return nil, err
			}
			state = 2
		/* parse the actual subtitle text */
		} else if state == 2 {
			if len(ln) == 0 {
				subs.PushBack(sub)
				sub = new(subtitle)
				state = 0
			} else {
				sub.text += string(ln) + "\r\n"
			}
		}

		if err == io.EOF {
			break;
		}
	}

	return subs, nil
}

/* write SubRip (srt) file */
func write_srt(filename string, subs *list.List) error {
	var id int = 0

	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	w := bufio.NewWriter(f)
	defer w.Flush()

	for e := subs.Front(); e != nil; e = e.Next() {
		id++
		sub := e.Value.(*subtitle)
		fmt.Fprintf(w, "%d\r\n", id)
		fmt.Fprintf(w, "%s --> %s\r\n", msecs_to_time(sub.start), msecs_to_time(sub.end))
		fmt.Fprintf(w, "%s\r\n", sub.text)
	}

	return nil
}

/* synchronize subtitles by knowing the time of the first and the last subtitle.
 * to archive this we must use the linear equation: y = mx + b */
func sync_subs(subs *list.List, synced_first_ms uint, synced_last_ms uint) {
	var slope, yint float64

	desynced_first_ms := subs.Front().Value.(*subtitle).start
	desynced_last_ms := subs.Back().Value.(*subtitle).start

	slope = float64(synced_last_ms - synced_first_ms) / float64(desynced_last_ms - desynced_first_ms)
	yint = float64(synced_last_ms) - slope * float64(desynced_last_ms)

	for e := subs.Front(); e != nil; e = e.Next() {
		sub := e.Value.(*subtitle)
		sub.start = uint(roundFloat64(slope * float64(sub.start) + yint))
		sub.end = uint(roundFloat64(slope * float64(sub.end) + yint))
	}
}

func main() {
	var first_ms, last_ms uint

	var opts struct {
		FirstTm string `short:"f" long:"first-sub" description:"Time of first subtitle"`
		LastTm string `short:"l" long:"last-sub" description:"Time of last subtitle"`
		InputFl string `short:"i" long:"input" description:"Input file" required:"true"`
		OutputFl string `short:"o" long:"output" description:"Output file"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		if err.(*flags.Error).Type == flags.ErrHelp {
			fmt.Printf("example: %s -f 00:01:33,492 -l 01:39:23,561 -i sub.srt\n",
				path.Base(os.Args[0]))
			os.Exit(0)
		}
		os.Exit(1)
	}

	if opts.FirstTm != "" {
		first_ms, err = time_to_msecs(opts.FirstTm)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Please check the value of -f option.\n")
			die(err)
		}
	}

	if opts.LastTm != "" {
		last_ms, err = time_to_msecs(opts.LastTm)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Please check the value of -l option.\n")
			die(err)
		}
	}

	/* if output file is not set, use the input file */
	if opts.OutputFl == "" {
		opts.OutputFl = opts.InputFl
	}

	subs, err := read_srt(opts.InputFl)
	if err != nil {
		die(err)
	}

	/* if time of the first synced subtitle is not set,
	 * use the time of the first desynced subtitle */
	if opts.FirstTm == "" {
		first_ms = subs.Front().Value.(*subtitle).start
	}

	/* if time of the last synced subtitle is not set,
	 * use the time of the last desynced subtitle */
	if opts.LastTm == "" {
		last_ms = subs.Back().Value.(*subtitle).start
	}

	if first_ms > last_ms {
		fmt.Fprintf(os.Stderr, "First subtitle can not be after last subtitle.\n")
		fmt.Fprintf(os.Stderr, "Please check the values of -f and/or -l options.\n")
		os.Exit(1)
	}

	sync_subs(subs, first_ms, last_ms)

	err = write_srt(opts.OutputFl, subs)
	if err != nil {
		die(err)
	}
}<commit_msg>add more comments<commit_after>package main

import (
	"fmt"
	"errors"
	"strings"
	"strconv"
	"math"
	"os"
	"path"
	"io"
	"bufio"
	"container/list"
	"github.com/jessevdk/go-flags"
)

type subtitle struct {
	text string
	start uint
	end uint
}

func die(err error) {
	fmt.Fprintf(os.Stderr, "%v\n", err)
	os.Exit(1)
}

func roundFloat64(f float64) float64 {
	val := f - float64(int64(f))
	if val >= 0.5 {
		return math.Ceil(f)
	} else if val > 0 {
		return math.Floor(f)
	} else if val <= -0.5 {
		return math.Floor(f)
	} else if val < 0 {
		return math.Ceil(f)
	}
	return f
}

/* converts hh:mm:ss,mss to milliseconds */
func time_to_msecs(tm string) (uint, error) {
	var msecs uint
	var h, m, s, ms uint

	tm = strings.Replace(tm, ".", ",", 1)
	num, err := fmt.Sscanf(tm, "%d:%d:%d,%d", &h, &m, &s, &ms)

	if num != 4 || err != nil {
		return 0, errors.New("Parsing error: Can not covert `" + tm + "' to milliseconds.")
	}

	msecs = h * 60 * 60 * 1000
	msecs += m * 60 * 1000
	msecs += s * 1000
	msecs += ms

	return msecs, nil
}

/* converts milliseconds to hh:mm:ss,mss */
func msecs_to_time(msecs uint) string {
	var h, m, s, ms uint

	h = msecs / (60 * 60 * 1000)
	msecs = msecs % (60 * 60 * 1000)
	m = msecs / (60 * 1000)
	msecs = msecs % (60 * 1000)
	s = msecs / 1000
	msecs = msecs % 1000
	ms = msecs

	tm := fmt.Sprintf("%02d:%02d:%02d,%03d", h, m, s, ms)

	return tm
}

/* read SubRip (srt) file */
func read_srt(filename string) (*list.List, error) {
	var state int = 0
	var subs *list.List
	var sub *subtitle

	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	r := bufio.NewReader(f)
	subs = list.New()
	sub = new(subtitle)

	for {
		var (
			isprefix bool = true
			err error = nil
			ln, line []byte
		)

		for isprefix && err == nil {
			line, isprefix, err = r.ReadLine()
			if err != nil && err != io.EOF {
				return nil, err
			}
			ln = append(ln, line...)
		}

		/* parse subtitle id */
		if state == 0 {
			/* avoid false-positive parsing error */
			if err == io.EOF && len(ln) == 0 {
				break;
			}
			id := strings.Split(string(ln), " ")
			if len(id) != 1 {
				return nil, errors.New("Parsing error: Wrong file format")
			}
			_, err = strconv.ParseUint(id[0], 10, 0)
			if err != nil {
				return nil, err
				return nil, errors.New("Parsing error: Wrong file format")
			}
			state = 1
		/* parse start, end times */
		} else if state == 1 {
			tm := strings.Split(string(ln), " ")
			if len(tm) != 3 || tm[1] != "-->" {
				return nil, errors.New("Parsing error: Wrong file format")
			}
			sub.start, err = time_to_msecs(tm[0])
			if err != nil {
				return nil, err
			}
			sub.end, err = time_to_msecs(tm[2])
			if err != nil {
				return nil, err
			}
			state = 2
		/* parse the actual subtitle text */
		} else if state == 2 {
			if len(ln) == 0 {
				subs.PushBack(sub)
				sub = new(subtitle)
				state = 0
			} else {
				sub.text += string(ln) + "\r\n"
			}
		}

		if err == io.EOF {
			break;
		}
	}

	return subs, nil
}

/* write SubRip (srt) file */
func write_srt(filename string, subs *list.List) error {
	var id int = 0

	f, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	w := bufio.NewWriter(f)
	defer w.Flush()

	for e := subs.Front(); e != nil; e = e.Next() {
		id++
		sub := e.Value.(*subtitle)
		fmt.Fprintf(w, "%d\r\n", id)
		fmt.Fprintf(w, "%s --> %s\r\n", msecs_to_time(sub.start), msecs_to_time(sub.end))
		fmt.Fprintf(w, "%s\r\n", sub.text)
	}

	return nil
}

/* synchronize subtitles by knowing the time of the first and the last subtitle.
 * to archive this we must use the linear equation: y = mx + b */
func sync_subs(subs *list.List, synced_first_ms uint, synced_last_ms uint) {
	var slope, yint float64

	desynced_first_ms := subs.Front().Value.(*subtitle).start
	desynced_last_ms := subs.Back().Value.(*subtitle).start

	/* m = (y2 - y1) / (x2 - x1)
	 * m: slope
	 * y2: synced_last_ms
	 * y1: synced_first_ms
	 * x2: desynced_last_ms
	 * x1: desynced_first_ms */
	slope = float64(synced_last_ms - synced_first_ms) / float64(desynced_last_ms - desynced_first_ms)
	/* b = y - mx
	 * b: yint
	 * y: synced_last_ms
	 * m: slope
	 * x: desynced_last_ms */
	yint = float64(synced_last_ms) - slope * float64(desynced_last_ms)

	for e := subs.Front(); e != nil; e = e.Next() {
		sub := e.Value.(*subtitle)
		/* y = mx + b
		 * y: sub.start and sub.end
		 * m: slope
		 * x: sub.start and sub.end
		 * b: yint */
		sub.start = uint(roundFloat64(slope * float64(sub.start) + yint))
		sub.end = uint(roundFloat64(slope * float64(sub.end) + yint))
	}
}

func main() {
	var first_ms, last_ms uint

	var opts struct {
		FirstTm string `short:"f" long:"first-sub" description:"Time of first subtitle"`
		LastTm string `short:"l" long:"last-sub" description:"Time of last subtitle"`
		InputFl string `short:"i" long:"input" description:"Input file" required:"true"`
		OutputFl string `short:"o" long:"output" description:"Output file"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		if err.(*flags.Error).Type == flags.ErrHelp {
			fmt.Printf("example: %s -f 00:01:33,492 -l 01:39:23,561 -i sub.srt\n",
				path.Base(os.Args[0]))
			os.Exit(0)
		}
		os.Exit(1)
	}

	if opts.FirstTm != "" {
		first_ms, err = time_to_msecs(opts.FirstTm)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Please check the value of -f option.\n")
			die(err)
		}
	}

	if opts.LastTm != "" {
		last_ms, err = time_to_msecs(opts.LastTm)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Please check the value of -l option.\n")
			die(err)
		}
	}

	/* if output file is not set, use the input file */
	if opts.OutputFl == "" {
		opts.OutputFl = opts.InputFl
	}

	subs, err := read_srt(opts.InputFl)
	if err != nil {
		die(err)
	}

	/* if time of the first synced subtitle is not set,
	 * use the time of the first desynced subtitle */
	if opts.FirstTm == "" {
		first_ms = subs.Front().Value.(*subtitle).start
	}

	/* if time of the last synced subtitle is not set,
	 * use the time of the last desynced subtitle */
	if opts.LastTm == "" {
		last_ms = subs.Back().Value.(*subtitle).start
	}

	if first_ms > last_ms {
		fmt.Fprintf(os.Stderr, "First subtitle can not be after last subtitle.\n")
		fmt.Fprintf(os.Stderr, "Please check the values of -f and/or -l options.\n")
		os.Exit(1)
	}

	sync_subs(subs, first_ms, last_ms)

	err = write_srt(opts.OutputFl, subs)
	if err != nil {
		die(err)
	}
}<|endoftext|>


<commit_before>package config

import (
	"xd/lib/configparser"
)

type LogConfig struct {
	Level string
}

func (cfg *LogConfig) Load(s *configparser.Section) error {

	cfg.Level = "info"
	if s != nil {
		cfg.Level = s.Get("level", "info")
	}
	return nil
}

func (cfg *LogConfig) Save(s *configparser.Section) error {
	s.Add("level", cfg.Level)
	return nil
}

func (cfg *LogConfig) LoadEnv() {

}
<commit_msg>make log level configurable via environmental variable<commit_after>package config

import (
	"os"
	"xd/lib/configparser"
)

const EnvLogLevel = "XD_LOG_LEVEL"

type LogConfig struct {
	Level string
}

func (cfg *LogConfig) Load(s *configparser.Section) error {

	cfg.Level = "info"
	if s != nil {
		cfg.Level = s.Get("level", "info")
	}
	return nil
}

func (cfg *LogConfig) Save(s *configparser.Section) error {
	s.Add("level", cfg.Level)
	return nil
}

func (cfg *LogConfig) LoadEnv() {
	lvl := os.Getenv(EnvLogLevel)
	if lvl != "" {
		cfg.Level = lvl
	}
}
<|endoftext|>


<commit_before>package main

// This is a basic example to illustrate how to access the Fidor API
// using Go.
//
// To run this example type:
//
//     $ go run example.go
//
// And point your browser to:
//
//     $ http://localhost:8080

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"errors"
)

// The following sections define the settings you require for the
// app to be able to connect to and authorize itself against the api.

// app ID and secret, can be found in this apps "Details" page in the
// AppManager.
var client_id     = "<CLIENT_ID>"
var client_secret = "<CLIENT_SECRET>"

// Fidor's OAuth Endpoint (this changes between Sandbox and Production)
var fidor_oauth_url = "<FIDOR_OAUTH_URL>" // e.g https://fidor.com/api_sandbox/oauth
// The OAuth Endpoint this App provides
var oauth_cb_url    = "<APP_URL>"

// The URL of the Fidor API (this changes between Sandbox and
// Production)
var fidor_api_url   = "<FIDOR_API_URL>" // e.g https://fidor.com/api_sandbox vs /api

func main() {
	// register a handler function (see next function) to service
	// requests ...
	http.HandleFunc("/", indexHandler)
	fmt.Printf("Now open http://localhost:8080")
	// ... and start listening.
	http.ListenAndServe(":8080", nil)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {

	// ignore any favicon requests, etc.
	if r.URL.Path != "/" {
		w.WriteHeader(404)
		return
	}

	// check whether we have a GET parameter named `code`, if so,
	// this is a redirect back from the Fidor OAuth server.
	values := r.URL.Query()
	if values["code"] != nil {
		// retrieve the actual OAuth access token ....
		code := values.Get("code")
		if token, err := retrieveTokenFromCode(code); err != nil {
			fmt.Printf("err: %v\n", err)
			w.WriteHeader(500)
			fmt.Fprintf(w, "Unfortunately, an error occurred retrieving oauth token")
		} else {
			// ... and finally, greet the user and assemble links
			renderWelcome(w, token)
		}
	} else {
		// we don't have an oauth `code` yet, so we need to
		// redirect the user to the OAuth provider to get one ...
		oauth_url := fmt.Sprintf("%s/authorize?client_id=%s&state=123&response_type=code&redirect_uri=%s",
			fidor_oauth_url,
			client_id,
			url.QueryEscape(oauth_cb_url))

		header := w.Header()
		header.Add("location", oauth_url)
		w.WriteHeader(307)
	}
}

// Our TokenResponse representation used to pick it out from the JSON
// returned by the OAuth server.
type TokenResponse struct {
	Token string `json:"access_token"`
}

// Use the OAuth code that the user's browser picked up from the OAuth
// server to request an OAuth access_token to use in API requests.
func retrieveTokenFromCode(code string) (token string, err error) {
	// assemble the API endpoint URL and request payload
	tokenUrl := fmt.Sprintf("%s/token", fidor_oauth_url)
	tokenPayload := url.Values{
		"client_id":     {client_id},
		"client_secret": {client_secret},
		"code":          {code},
		"redirect_uri":  {url.QueryEscape(oauth_cb_url)},
		"grant_type":    {"autorization_code"}
	}
	// Call API
	if resp, err := http.PostForm(tokenUrl, tokenPayload); err != nil {
		println(err)
		return "", err
	} else {
		if resp.StatusCode != 200 {
			return "", errors.New(resp.Status)
		}
		// if successful, pick the access_token out of the reply.
		var tokenResponse TokenResponse
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(&tokenResponse); err != nil {
			return "", err
		} else {
			return tokenResponse.Token, nil
		}
	}
}

// Our server code only makes a single call to the API to retrieve user
// information. This is our internal representation of the returned JSON
// used to pick out the user's email.
type UserResponse struct {
	Email string `json:"email"`
}

// function to retrieve user information from the API
func getUser(token string) (u UserResponse, err error) {
	// Assemble endpoint URL...
	url := fmt.Sprintf("%s/users/current?access_token=%s", fidor_api_url, token)
	if resp, err := http.Get(url); err != nil {
		return u, err
	} else {
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(&u); err != nil {
			return u, err
		} else {
			return u, nil
		}
	}
}

// once all the OAuth calls have been taken care of, this function is
// called from the http handler. It retrieves the user's email address
// and inserts links to `transaction` and `accounts` endpoints.

func renderWelcome(w http.ResponseWriter, token string) {
	if user, err := getUser(token); err != nil {
		fmt.Printf("err: %v\n", err)
		w.WriteHeader(500)
	} else {
		txLink := fmt.Sprintf("%s/transactions?access_token=%s", fidor_api_url, token)
		acctsLink := fmt.Sprintf("%s/accounts?access_token=%s", fidor_api_url, token)
		fmt.Fprintf(w, indexTemplate, user.Email, token, txLink, acctsLink)
	}
}

var indexTemplate = `

Welcome %s!

	retrieved <tt>access_token</tt>: %s

Transactions

Accounts

`
<commit_msg>cleanup<commit_after>package main

// This is a basic example to illustrate how to access the Fidor API
// using Go.
//
// To run this example type:
//
//     $ go run example.go
//
// And point your browser to:
//
//     $ http://localhost:8080

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"errors"
)

// The following sections define the settings you require for the
// app to be able to connect to and authorize itself against the api.

// app ID and secret, can be found in this apps "Details" page in the
// AppManager.
var client_id     = "<CLIENT_ID>"
var client_secret = "<CLIENT_SECRET>"

// Fidor's OAuth Endpoint (this changes between Sandbox and Production)
var fidor_oauth_url = "<FIDOR_OAUTH_URL>" // e.g https://fidor.com/api_sandbox/oauth
// The OAuth Endpoint this App provides
var oauth_cb_url    = "<APP_URL>"

// The URL of the Fidor API (this changes between Sandbox and
// Production)
var fidor_api_url   = "<FIDOR_API_URL>" // e.g https://fidor.com/api_sandbox vs /api

func main() {
	// register a handler function (see next function) to service
	// requests ...
	http.HandleFunc("/", indexHandler)
	fmt.Printf("Now open http://localhost:8080")
	// ... and start listening.
	http.ListenAndServe(":8080", nil)
}

func indexHandler(w http.ResponseWriter, r *http.Request) {

	// ignore any favicon requests, etc.
	if r.URL.Path != "/" {
		w.WriteHeader(404)
		return
	}

	// check whether we have a GET parameter named `code`, if so,
	// this is a redirect back from the Fidor OAuth server.
	values := r.URL.Query()
	if values["code"] != nil {
		// retrieve the actual OAuth access token ....
		code := values.Get("code")
		if token, err := retrieveTokenFromCode(code); err != nil {
			fmt.Printf("err: %v\n", err)
			w.WriteHeader(500)
			fmt.Fprintf(w, "Unfortunately, an error occurred retrieving oauth token")
		} else {
			// ... and finally, greet the user and assemble links
			renderWelcome(w, token)
		}
	} else {
		// we don't have an oauth `code` yet, so we need to
		// redirect the user to the OAuth provider to get one ...
		oauth_url := fmt.Sprintf("%s/authorize?client_id=%s&state=123&response_type=code&redirect_uri=%s",
			fidor_oauth_url,
			client_id,
			url.QueryEscape(oauth_cb_url))

		header := w.Header()
		header.Add("location", oauth_url)
		w.WriteHeader(307)
	}
}

// Our TokenResponse representation used to pick it out from the JSON
// returned by the OAuth server.
type TokenResponse struct {
	Token string `json:"access_token"`
}

// Use the OAuth code that the user's browser picked up from the OAuth
// server to request an OAuth access_token to use in API requests.
func retrieveTokenFromCode(code string) (token string, err error) {
	// assemble the API endpoint URL and request payload
	tokenUrl := fmt.Sprintf("%s/token", fidor_oauth_url)
	tokenPayload := url.Values{
		"client_id":     {client_id},
		"client_secret": {client_secret},
		"code":          {code},
		"redirect_uri":  {url.QueryEscape(oauth_cb_url)},
		"grant_type":    {"authorization_code"},
	}
	// Call API
	if resp, err := http.PostForm(tokenUrl, tokenPayload); err != nil {
		println(err)
		return "", err
	} else {
		if resp.StatusCode != 200 {
			return "", errors.New(resp.Status)
		}
		// if successful, pick the access_token out of the reply.
		var tokenResponse TokenResponse
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(&tokenResponse); err != nil {
			return "", err
		} else {
			return tokenResponse.Token, nil
		}
	}
}

// Our server code only makes a single call to the API to retrieve user
// information. This is our internal representation of the returned JSON
// used to pick out the user's email.
type UserResponse struct {
	Email string `json:"email"`
}

// function to retrieve user information from the API
func getUser(token string) (u UserResponse, err error) {
	// Assemble endpoint URL...
	url := fmt.Sprintf("%s/users/current?access_token=%s", fidor_api_url, token)
	if resp, err := http.Get(url); err != nil {
		return u, err
	} else {
		decoder := json.NewDecoder(resp.Body)
		if err = decoder.Decode(&u); err != nil {
			return u, err
		} else {
			return u, nil
		}
	}
}

// once all the OAuth calls have been taken care of, this function is
// called from the http handler. It retrieves the user's email address
// and inserts links to `transaction` and `accounts` endpoints.

func renderWelcome(w http.ResponseWriter, token string) {
	if user, err := getUser(token); err != nil {
		fmt.Printf("err: %v\n", err)
		w.WriteHeader(500)
	} else {
		txLink := fmt.Sprintf("%s/transactions?access_token=%s", fidor_api_url, token)
		acctsLink := fmt.Sprintf("%s/accounts?access_token=%s", fidor_api_url, token)
		fmt.Fprintf(w, indexTemplate, user.Email, token, txLink, acctsLink)
	}
}

var indexTemplate = `

Welcome %s!

	retrieved <tt>access_token</tt>: %s

Transactions

Accounts

`
<|endoftext|>


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

package common

import (
	"github.com/juju/names"
)

// AuthFunc returns whether the given entity is available to some operation.
type AuthFunc func(tag string) bool

// GetAuthFunc returns an AuthFunc.
type GetAuthFunc func() (AuthFunc, error)

// Authorizer represents a value that can be asked for authorization
// information on its associated authenticated entity. It is
// implemented by an API server to allow an API implementation to ask
// questions about the client that is currently connected.
type Authorizer interface {
	// AuthMachineAgent returns whether the authenticated entity is a
	// machine agent.
	AuthMachineAgent() bool

	// AuthUnitAgent returns whether the authenticated entity is a
	// unit agent.
	AuthUnitAgent() bool

	// AuthOwner returns whether the authenticated entity is the same
	// as the given entity.
	// TODO(dfc) this should take a tag not a string
	AuthOwner(tag string) bool

	// AuthEnvironManager returns whether the authenticated entity is
	// a machine running the environment manager job.
	AuthEnvironManager() bool

	// AuthClient returns whether the authenticated entity
	// is a client user.
	AuthClient() bool

	// GetAuthTag returns the tag of the authenticated entity.
	GetAuthTag() names.Tag
}

// AuthEither returns an AuthFunc generator that returns an AuthFunc
// that accepts any tag authorized by either of its arguments.
func AuthEither(a, b GetAuthFunc) GetAuthFunc {
	return func() (AuthFunc, error) {
		f1, err := a()
		if err != nil {
			return nil, err
		}
		f2, err := b()
		if err != nil {
			return nil, err
		}
		return func(tag string) bool {
			return f1(tag) || f2(tag)
		}, nil
	}
}

// AuthAlways returns an authentication function that always returns true.
func AuthAlways() GetAuthFunc {
	return func() (AuthFunc, error) {
		return func(tag string) bool {
			_, err := names.ParseTag(tag)
			return err == nil
		}, nil
	}
}

// AuthNever returns an authentication function that never returns true.
func AuthNever() GetAuthFunc {
	return func() (AuthFunc, error) {
		return func(tag string) bool {
			return false
		}, nil
	}
}
<commit_msg>update commentary<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package common

import (
	"github.com/juju/names"
)

// AuthFunc returns whether the given entity is available to some operation.
type AuthFunc func(tag string) bool

// GetAuthFunc returns an AuthFunc.
type GetAuthFunc func() (AuthFunc, error)

// Authorizer represents a value that can be asked for authorization
// information on its associated authenticated entity. It is
// implemented by an API server to allow an API implementation to ask
// questions about the client that is currently connected.
type Authorizer interface {
	// AuthMachineAgent returns whether the authenticated entity is a
	// machine agent.
	AuthMachineAgent() bool

	// AuthUnitAgent returns whether the authenticated entity is a
	// unit agent.
	AuthUnitAgent() bool

	// AuthOwner returns whether the authenticated entity is the same
	// as the given entity.
	// TODO(dfc) this should take a tag not a string
	AuthOwner(tag string) bool

	// AuthEnvironManager returns whether the authenticated entity is
	// a machine running the environment manager job.
	AuthEnvironManager() bool

	// AuthClient returns whether the authenticated entity
	// is a client user.
	AuthClient() bool

	// GetAuthTag returns the tag of the authenticated entity.
	GetAuthTag() names.Tag
}

// AuthEither returns an AuthFunc generator that returns an AuthFunc
// that accepts any tag authorized by either of its arguments.
func AuthEither(a, b GetAuthFunc) GetAuthFunc {
	return func() (AuthFunc, error) {
		f1, err := a()
		if err != nil {
			return nil, err
		}
		f2, err := b()
		if err != nil {
			return nil, err
		}
		return func(tag string) bool {
			return f1(tag) || f2(tag)
		}, nil
	}
}

// AuthAlways returns an authentication function that always returns true iff it is passed a valid tag.
func AuthAlways() GetAuthFunc {
	return func() (AuthFunc, error) {
		return func(tag string) bool {
			_, err := names.ParseTag(tag)
			return err == nil
		}, nil
	}
}

// AuthNever returns an authentication function that never returns true.
func AuthNever() GetAuthFunc {
	return func() (AuthFunc, error) {
		return func(tag string) bool {
			return false
		}, nil
	}
}
<|endoftext|>


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

package commands

import (
	"strings"

	"github.com/GoogleContainerTools/kpt/internal/cmddesc"
	"github.com/GoogleContainerTools/kpt/internal/cmddiff"
	"github.com/GoogleContainerTools/kpt/internal/cmdget"
	"github.com/GoogleContainerTools/kpt/internal/cmdinit"
	"github.com/GoogleContainerTools/kpt/internal/cmdsync"
	"github.com/GoogleContainerTools/kpt/internal/cmdupdate"
	"github.com/GoogleContainerTools/kpt/internal/util/cmdutil"
	"github.com/spf13/cobra"
)

func GetAnthosCommands(name string) []*cobra.Command {
	c := []*cobra.Command{cmddesc.NewCommand(name),
		cmdget.NewCommand(name), cmdinit.NewCommand(name),
		cmdsync.NewCommand(name),
		cmdupdate.NewCommand(name), cmddiff.NewCommand(name),
	}

	// apply cross-cutting issues to commands
	NormalizeCommand(c...)
	return c
}

// NormalizeCommand will modify commands to be consistent, e.g. silencing errors
func NormalizeCommand(c ...*cobra.Command) {
	for i := range c {
		cmd := c[i]
		// check if silencing errors is off
		cmdutil.SetSilenceErrors(cmd)
		cmd.Short = strings.TrimPrefix(cmd.Short, "[Alpha] ")

		// check if stack printing is on
		if cmd.PreRunE != nil {
			fn := cmd.PreRunE
			cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
				err := fn(cmd, args)
				return cmdutil.HandlePreRunError(cmd, err)
			}
		}
		if cmd.RunE != nil {
			fn := cmd.RunE
			cmd.RunE = func(cmd *cobra.Command, args []string) error {
				err := fn(cmd, args)
				return cmdutil.HandleError(cmd, err)
			}
		}
		NormalizeCommand(cmd.Commands()...)
	}
}

// GetKptCommands returns the set of kpt commands to be registered
func GetKptCommands(name string) []*cobra.Command {
	var c []*cobra.Command
	cfgCmd := GetConfigCommand(name)
	fnCmd := GetFnCommand(name)
	pkgCmd := GetPkgCommand(name)
	ttlCmd := GetTTLCommand(name)
	// Initially, the "live" command group is not visible.
	liveCmd := GetLiveCommand(name)
	liveCmd.Hidden = true

	c = append(c, cfgCmd, pkgCmd, fnCmd, ttlCmd, liveCmd)

	// apply cross-cutting issues to commands
	NormalizeCommand(c...)
	return c
}
<commit_msg>Expose the live command (unhide)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package commands

import (
	"strings"

	"github.com/GoogleContainerTools/kpt/internal/cmddesc"
	"github.com/GoogleContainerTools/kpt/internal/cmddiff"
	"github.com/GoogleContainerTools/kpt/internal/cmdget"
	"github.com/GoogleContainerTools/kpt/internal/cmdinit"
	"github.com/GoogleContainerTools/kpt/internal/cmdsync"
	"github.com/GoogleContainerTools/kpt/internal/cmdupdate"
	"github.com/GoogleContainerTools/kpt/internal/util/cmdutil"
	"github.com/spf13/cobra"
)

func GetAnthosCommands(name string) []*cobra.Command {
	c := []*cobra.Command{cmddesc.NewCommand(name),
		cmdget.NewCommand(name), cmdinit.NewCommand(name),
		cmdsync.NewCommand(name),
		cmdupdate.NewCommand(name), cmddiff.NewCommand(name),
	}

	// apply cross-cutting issues to commands
	NormalizeCommand(c...)
	return c
}

// NormalizeCommand will modify commands to be consistent, e.g. silencing errors
func NormalizeCommand(c ...*cobra.Command) {
	for i := range c {
		cmd := c[i]
		// check if silencing errors is off
		cmdutil.SetSilenceErrors(cmd)
		cmd.Short = strings.TrimPrefix(cmd.Short, "[Alpha] ")

		// check if stack printing is on
		if cmd.PreRunE != nil {
			fn := cmd.PreRunE
			cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
				err := fn(cmd, args)
				return cmdutil.HandlePreRunError(cmd, err)
			}
		}
		if cmd.RunE != nil {
			fn := cmd.RunE
			cmd.RunE = func(cmd *cobra.Command, args []string) error {
				err := fn(cmd, args)
				return cmdutil.HandleError(cmd, err)
			}
		}
		NormalizeCommand(cmd.Commands()...)
	}
}

// GetKptCommands returns the set of kpt commands to be registered
func GetKptCommands(name string) []*cobra.Command {
	var c []*cobra.Command
	cfgCmd := GetConfigCommand(name)
	fnCmd := GetFnCommand(name)
	pkgCmd := GetPkgCommand(name)
	ttlCmd := GetTTLCommand(name)
	liveCmd := GetLiveCommand(name)

	c = append(c, cfgCmd, pkgCmd, fnCmd, ttlCmd, liveCmd)

	// apply cross-cutting issues to commands
	NormalizeCommand(c...)
	return c
}
<|endoftext|>


<commit_before>package gitmedia

import (
	"fmt"
	"flag"
	"os"
)

var commands = make(map[string]func(*Command) RunnableCommand)

func Run() {
	subcommand := SubCommand()
	basecmd := NewCommand()

	cmdcb, ok := commands[subcommand]
	if ok {
		cmd := cmdcb(basecmd)
		cmd.Setup()
		cmd.Parse()
		cmd.Run()
	} else {
		missingCommand(subcommand)
	}
}

func SubCommand() string {
	if len(os.Args) < 2 {
		return "version"
	} else {
		return os.Args[1]
	}
}

func NewCommand() *Command {
	var args []string
	if len(os.Args) > 1 {
		args = os.Args[2:]
	}

	return &Command{flag.NewFlagSet(os.Args[0], flag.ExitOnError), args}
}

type RunnableCommand interface {
	Setup()
	Parse()
	Run()
}

type Command struct {
	FlagSet *flag.FlagSet
	Args    []string
}

func (c *Command) Parse() {
	c.FlagSet.Parse(c.Args)
}

func (c *Command) Setup() {}
func (c *Command) Run() {}

func registerCommand(name string, cmdcb func(*Command) RunnableCommand) {
	commands[name] = cmdcb
}

func missingCommand(cmd string) {
	fmt.Printf("git-media: '%s' is not a git-media command.  See git-media help.\n", cmd)
}
<commit_msg>basic Usage()<commit_after>package gitmedia

import (
	"fmt"
	"flag"
	"os"
)

var commands = make(map[string]func(*Command) RunnableCommand)

func Run() {
	runcmd := true
	subcommand := SubCommand(1)

	if subcommand == "help" {
		runcmd = false
		subcommand = SubCommand(2)
	}

	cmdcb, ok := commands[subcommand]
	if ok {
		cmd := cmdcb(NewCommand(subcommand))
		cmd.Setup()

		if runcmd {
			cmd.Parse()
			cmd.Run()
		} else {
			cmd.Usage()
		}
	} else {
		missingCommand(subcommand)
	}
}

func SubCommand(pos int) string {
	if len(os.Args) < (pos + 1) {
		return "version"
	} else {
		return os.Args[pos]
	}
}

func NewCommand(name string) *Command {
	var args []string
	if len(os.Args) > 1 {
		args = os.Args[2:]
	}

	return &Command{name, flag.NewFlagSet(os.Args[0], flag.ExitOnError), args}
}

type RunnableCommand interface {
	Setup()
	Parse()
	Run()
	Usage()
}

type Command struct {
	Name string
	FlagSet *flag.FlagSet
	Args    []string
}

func (c *Command) Usage() {
	fmt.Printf("git-media %s\n", c.Name)
	c.FlagSet.PrintDefaults()
}

func (c *Command) Parse() {
	c.FlagSet.Parse(c.Args)
}

func (c *Command) Setup() {}
func (c *Command) Run() {}

func registerCommand(name string, cmdcb func(*Command) RunnableCommand) {
	commands[name] = cmdcb
}

func missingCommand(cmd string) {
	fmt.Printf("git-media: '%s' is not a git-media command.  See git-media help.\n", cmd)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Xuyuan Pang <xuyuanp@gmail.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 handlers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/Xuyuanp/glogger"
)

func init() {
	glogger.RegisterConfigLoaderBuilder("github.com/Xuyuanp/glogger/handlers.RotatedFileHandler", func() glogger.ConfigLoader {
		return NewRotatedFileHandler()
	})
}

// RotatedFileHandler struct
type RotatedFileHandler struct {
	*glogger.GenericHandler
	FileName       string
	File           *os.File
	AutoRotated    bool
	MaxSize        uint64
	MaxLine        uint64
	Daily          bool
	nextRotateTime time.Time
	currentSize    uint64
	currentLine    uint64
	BackupCount    int
	mu             sync.Mutex
}

// NewRotatedFileHandler return a new RotatedFileHandler
func NewRotatedFileHandler() *RotatedFileHandler {
	fh := &RotatedFileHandler{
		GenericHandler: glogger.NewHandler(),
		AutoRotated:    true,
		Daily:          true,
	}
	return fh
}

// Handle a record
func (fh *RotatedFileHandler) Handle(rec *glogger.Record) {
	fh.mu.Lock()
	defer fh.mu.Unlock()
	if fh.File == nil {
		fmt.Fprintln(os.Stderr, "No log file")
		return
	}
	msg := fh.Format(rec)
	fh.currentLine += uint64(len(strings.Split(msg, "\n")))
	fh.currentSize += uint64(len(msg))
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	fh.File.WriteString(msg)

	if fh.checkRotated() {
		fh.doRotated()
	}
}

// SetFileName set the name of file to output
func (fh *RotatedFileHandler) SetFileName(fileName string) {
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0640)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}

	if fh.AutoRotated {
		data, _ := ioutil.ReadAll(file)
		fh.currentSize = uint64(len(data))
		fh.currentLine = uint64(len(strings.Split(string(data), "\n"))) - 1
		fh.setupNextRotateTime()
	}

	if fh.File != nil {
		fh.File.Close()
	}
	fh.File = file
	fh.FileName = fileName
}

func (fh *RotatedFileHandler) checkRotated() bool {
	if !fh.AutoRotated {
		return false
	}
	if fh.MaxLine > 0 && fh.currentLine >= fh.MaxLine {
		return true
	}
	if fh.MaxSize > 0 && fh.currentSize >= fh.MaxSize {
		return true
	}
	if fh.Daily {
		now := time.Now()
		if now.After(fh.nextRotateTime) {
			return true
		}
	}
	return false
}

func (fh *RotatedFileHandler) doRotated() {
	nextFileName := ""
	for i := 1; fh.BackupCount == 0 || i <= fh.BackupCount; i++ {
		fileName := fmt.Sprintf("%s.%d", fh.FileName, i)
		if _, err := os.Open(fileName); err != nil {
			nextFileName = fileName
			break
		}
	}
	if nextFileName == "" {
		fmt.Fprintln(os.Stderr, "No more backup file")
		fh.File.Close()
		fh.File = nil
		return
	}
	fh.File.Close()
	fh.File = nil
	os.Rename(fh.FileName, nextFileName)

	fh.SetFileName(fh.FileName)
	fh.currentLine = 0
	fh.currentSize = 0
	fh.setupNextRotateTime()
}

func (fh *RotatedFileHandler) setupNextRotateTime() {
	now := time.Now()
	nextTimeStr := fmt.Sprintf("%d-%d-%d 00:00:00", now.Year(), now.Month(), now.Day())
	nextTime, _ := time.ParseInLocation("2006-1-2 15:04:05", nextTimeStr, time.Local)
	nextTime = nextTime.Add(24 * time.Hour)
	fh.nextRotateTime = nextTime
}

// LoadConfig load configuration from a map
func (fh *RotatedFileHandler) LoadConfig(config map[string]interface{}) error {
	if err := fh.GenericHandler.LoadConfig(config); err != nil {
		return err
	}
	data, _ := json.Marshal(config)
	json.Unmarshal(data, fh)
	if fh.FileName == "" {
		return fmt.Errorf("'filename' field is required")
	}
	fh.SetFileName(fh.FileName)
	return nil
}
<commit_msg>checked error<commit_after>/*
 * Copyright 2014 Xuyuan Pang <xuyuanp@gmail.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 handlers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/Xuyuanp/glogger"
)

func init() {
	glogger.RegisterConfigLoaderBuilder("github.com/Xuyuanp/glogger/handlers.RotatedFileHandler", func() glogger.ConfigLoader {
		return NewRotatedFileHandler()
	})
}

// RotatedFileHandler struct
type RotatedFileHandler struct {
	*glogger.GenericHandler
	FileName       string
	File           *os.File
	AutoRotated    bool
	MaxSize        uint64
	MaxLine        uint64
	Daily          bool
	nextRotateTime time.Time
	currentSize    uint64
	currentLine    uint64
	BackupCount    int
	mu             sync.Mutex
}

// NewRotatedFileHandler return a new RotatedFileHandler
func NewRotatedFileHandler() *RotatedFileHandler {
	fh := &RotatedFileHandler{
		GenericHandler: glogger.NewHandler(),
		AutoRotated:    true,
		Daily:          true,
	}
	return fh
}

// Handle a record
func (fh *RotatedFileHandler) Handle(rec *glogger.Record) {
	fh.mu.Lock()
	defer fh.mu.Unlock()
	if fh.File == nil {
		fmt.Fprintln(os.Stderr, "No log file")
		return
	}
	msg := fh.Format(rec)
	fh.currentLine += uint64(len(strings.Split(msg, "\n")))
	fh.currentSize += uint64(len(msg))
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	fh.File.WriteString(msg)

	if fh.checkRotated() {
		fh.doRotated()
	}
}

// SetFileName set the name of file to output
func (fh *RotatedFileHandler) SetFileName(fileName string) {
	file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0640)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}

	if fh.AutoRotated {
		data, _ := ioutil.ReadAll(file)
		fh.currentSize = uint64(len(data))
		fh.currentLine = uint64(len(strings.Split(string(data), "\n"))) - 1
		fh.setupNextRotateTime()
	}

	if fh.File != nil {
		fh.File.Close()
	}
	fh.File = file
	fh.FileName = fileName
}

func (fh *RotatedFileHandler) checkRotated() bool {
	if !fh.AutoRotated {
		return false
	}
	if fh.MaxLine > 0 && fh.currentLine >= fh.MaxLine {
		return true
	}
	if fh.MaxSize > 0 && fh.currentSize >= fh.MaxSize {
		return true
	}
	if fh.Daily {
		now := time.Now()
		if now.After(fh.nextRotateTime) {
			return true
		}
	}
	return false
}

func (fh *RotatedFileHandler) doRotated() {
	nextFileName := ""
	for i := 1; fh.BackupCount == 0 || i <= fh.BackupCount; i++ {
		fileName := fmt.Sprintf("%s.%d", fh.FileName, i)
		if _, err := os.Open(fileName); err != nil {
			nextFileName = fileName
			break
		}
	}
	if nextFileName == "" {
		fmt.Fprintln(os.Stderr, "No more backup file")
		fh.File.Close()
		fh.File = nil
		return
	}
	fh.File.Close()
	fh.File = nil
	os.Rename(fh.FileName, nextFileName)

	fh.SetFileName(fh.FileName)
	fh.currentLine = 0
	fh.currentSize = 0
	fh.setupNextRotateTime()
}

func (fh *RotatedFileHandler) setupNextRotateTime() {
	now := time.Now()
	nextTimeStr := fmt.Sprintf("%d-%d-%d 00:00:00", now.Year(), now.Month(), now.Day())
	nextTime, _ := time.ParseInLocation("2006-1-2 15:04:05", nextTimeStr, time.Local)
	nextTime = nextTime.Add(24 * time.Hour)
	fh.nextRotateTime = nextTime
}

// LoadConfig load configuration from a map
func (fh *RotatedFileHandler) LoadConfig(config map[string]interface{}) error {
	if err := fh.GenericHandler.LoadConfig(config); err != nil {
		return err
	}
	data, err := json.Marshal(config)
	if err != nil {
		return err
	}
	if err = json.Unmarshal(data, fh); err != nil {
		return err
	}
	if fh.FileName == "" {
		return fmt.Errorf("'filename' field is required")
	}
	fh.SetFileName(fh.FileName)
	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"io"
	"strings"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
)

// Var is a struct which contains a name-value-source triplet
// Source is up to two words separated by a space. The first word is the source type: FLAG, ENV, DIR, CODE.
// The second is the name of the flag/file/environment var used.
type Var struct {
	Name   string
	Value  string
	Source string
}

// SourceType returns one of the following:
// INTERACTION for a configVar set by some interaction with the user
// FLAG for a configVar whose value was set by passing a flag on the command line
// ENV for a configVar whose value was set from an environment variable
// DIR for a configVar whose value was set from a file in the config dir
// CODE for the default hardcoded into bytemark-client
//
func (v *Var) SourceType() string {
	bits := strings.Fields(v.Source)

	return bits[0]
}

func (v *Var) SourceTypeAtLeast(min string) bool {
	scores := map[string]int{
		"CODE":        0,
		"DIR":         1,
		"ENV":         2,
		"FLAG":        3,
		"INTERACTION": 9999,
	}
	minScore, ok := scores[min]
	if !ok {
		minScore = 0
	}
	myScore, ok := scores[v.SourceType()]
	if !ok {
		myScore = 0
	}

	return myScore >= minScore
}

// SourceBaseName returns the basename of the configVar's source.
// it's a bit stupid and so its output is only valid for configVars with SourceType() of DIR
func (v *Var) SourceBaseName() string {
	bits := strings.Split(v.Source, "/")
	return bits[len(bits)-1]
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (v Var) DefaultFields(f output.Format) string {
	return "Name, Value, Source"
}

// PrettyPrint outputs a nice human-readable overview of the server to the given writer.
func (v Var) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	const template = `{{ define "var_sgl" }} ▸ {{.Name}} {{.Value}} ({{.Source}}){{ end }}{{ define "var_medium" }}{{ template "var_sgl" . }}{{ end }}{{ define "var_full" }}{{ template "var_sgl" . }}{{ end }}`
	return prettyprint.Run(wr, template, "var"+string(detail), v)
}
<commit_msg>Document SourceTypeAtLeast<commit_after>package config

import (
	"io"
	"strings"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
)

// Var is a struct which contains a name-value-source triplet
// Source is up to two words separated by a space. The first word is the source type: FLAG, ENV, DIR, CODE.
// The second is the name of the flag/file/environment var used.
type Var struct {
	Name   string
	Value  string
	Source string
}

// SourceType returns one of the following:
// INTERACTION for a configVar set by some interaction with the user
// FLAG for a configVar whose value was set by passing a flag on the command line
// ENV for a configVar whose value was set from an environment variable
// DIR for a configVar whose value was set from a file in the config dir
// CODE for the default hardcoded into bytemark-client
//
func (v *Var) SourceType() string {
	bits := strings.Fields(v.Source)

	return bits[0]
}

// SourceTypeAtLeast ensures that this Var's SourceType is at least as 'good' as the one specified.
// The ranking is currently INTERACTION > FLAG > ENV > DIR > FLAG
func (v *Var) SourceTypeAtLeast(min string) bool {
	scores := map[string]int{
		"CODE":        0,
		"DIR":         1,
		"ENV":         2,
		"FLAG":        3,
		"INTERACTION": 9999,
	}
	minScore, ok := scores[min]
	if !ok {
		minScore = 0
	}
	myScore, ok := scores[v.SourceType()]
	if !ok {
		myScore = 0
	}

	return myScore >= minScore
}

// SourceBaseName returns the basename of the configVar's source.
// it's a bit stupid and so its output is only valid for configVars with SourceType() of DIR
func (v *Var) SourceBaseName() string {
	bits := strings.Split(v.Source, "/")
	return bits[len(bits)-1]
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (v Var) DefaultFields(f output.Format) string {
	return "Name, Value, Source"
}

// PrettyPrint outputs a nice human-readable overview of the server to the given writer.
func (v Var) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	const template = `{{ define "var_sgl" }} ▸ {{.Name}} {{.Value}} ({{.Source}}){{ end }}{{ define "var_medium" }}{{ template "var_sgl" . }}{{ end }}{{ define "var_full" }}{{ template "var_sgl" . }}{{ end }}`
	return prettyprint.Run(wr, template, "var"+string(detail), v)
}
<|endoftext|>


<commit_before>// Copyright 2018 ePoxy 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 command

import (
	"context"
	"fmt"
	"log"
	"regexp"
	"time"

	"cloud.google.com/go/datastore"
	"github.com/m-lab/epoxy/storage"
	"github.com/m-lab/go/rtx"
	"github.com/spf13/cobra"
)

// updateCmd represents the update command
var updateCmd = &cobra.Command{
	Use:   "update",
	Short: "Updates all ePoxy Host records matching --hostname pattern",
	Long: `
USAGE:
    **ONLY FOR TESTING**

    Updates Host records matching the regex pattern in the --hostname flag.

EXAMPLE:

    # Set the "update enabled" flag on the Host record.
    epoxy_admin update --project mlab-sandbox \
        --hostname mlab3.iad1t.measurement-lab.org \
        --update

    # Set the "update enabled" flag on all mlab4 Host records.
    epoxy_admin update --project mlab-sandbox \
        --hostname 'mlab4.*' \
        --update
`,
	Run: runUpdate,
}

func updateURL(url, original string) string {
	if url != "" {
		return fmtURL(url)
	}
	return original
}

// TODO: add unit tests by masking out NewClient & NewDatstoreConfig. Consider
// promoting the fake datastore types from storage/datastore_test.go to an
// internal fake package.
func runUpdate(cmd *cobra.Command, args []string) {
	// Setup Datastore client.
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	client, err := datastore.NewClient(ctx, fProject)
	rtx.Must(err, "Failed to create new datastore client")

	// Save the host record to Datstore.
	ds := storage.NewDatastoreConfig(client)
	hosts, err := ds.List()
	rtx.Must(err, "Failed to list host records")

	// Compile given regex.
	r, err := regexp.Compile(ufHostname)
	rtx.Must(err, "Failed to compile given hostname pattern: %q", ufHostname)

	for _, h := range hosts {
		if !r.MatchString(h.Name) {
			continue
		}
		log.Printf("Updating: %s", h.Name)

		handleUpdate(h)

		// Save the host record.
		err = ds.Save(h)
		rtx.Must(err, "Failed to save new host record")

		// Retrieve the host record from Datastore to exercise the full save & load path.
		h, err = ds.Load(h.Name)
		rtx.Must(err, "Failed to save new host record")
		fmt.Println(h.String())
	}
	return
}

func handleUpdate(h *storage.Host) {
	h.UpdateEnabled = ufUpdate

	if len(ufExtensions) > 0 {
		h.Extensions = ufExtensions
	}

	if ufAddress != "" {
		h.IPv4Addr = ufAddress
	}
	h.Boot[storage.Stage1IPXE] = updateURL(fmtURL(ufBootStage1), h.Boot[storage.Stage1IPXE])
	h.Boot[storage.Stage1JSON] = updateURL(fmtURL(ufBootStage1JSON), h.Boot[storage.Stage1JSON])
	h.Boot[storage.Stage2] = updateURL(fmtURL(ufBootStage2), h.Boot[storage.Stage2])
	h.Boot[storage.Stage3] = updateURL(fmtURL(ufBootStage3), h.Boot[storage.Stage3])
	h.Update[storage.Stage1IPXE] = updateURL(fmtURL(ufUpdateStage1), h.Update[storage.Stage1IPXE])
	h.Update[storage.Stage1JSON] = updateURL(fmtURL(ufUpdateStage1JSON), h.Update[storage.Stage1JSON])
	h.Update[storage.Stage2] = updateURL(fmtURL(ufUpdateStage2), h.Update[storage.Stage2])
	h.Update[storage.Stage3] = updateURL(fmtURL(ufUpdateStage3), h.Update[storage.Stage3])

	h.ImagesVersion = ufImagesVersion
}

func init() {
	rootCmd.AddCommand(updateCmd)

	// Required local flags.
	updateCmd.Flags().StringVar(&ufHostname, "hostname", "",
		"Hostname of new record.")
	updateCmd.MarkFlagRequired("hostname")

	// Local flags which will only run when "update" is called directly.
	updateCmd.Flags().StringSliceVar(&ufExtensions, "extensions", []string{},
		"List of extensions to enable.")
	updateCmd.Flags().StringVar(&ufAddress, "address", "",
		"IP address of hostname.")
	updateCmd.Flags().BoolVar(&ufUpdate, "update", false,
		"Set Host.UpdateEnabled to true for an existing Host.")
	updateCmd.Flags().StringVar(&ufBootStage1, "boot-stage1", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufBootStage1JSON, "boot-stage1-json", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufBootStage2, "boot-stage2", "",
		"Absolute URL to an action definition to run during stage2 to stage3 boot.")
	updateCmd.Flags().StringVar(&ufBootStage3, "boot-stage3", "",
		"Absolute URL to an action definition to run after running stage3 boot.")
	updateCmd.Flags().StringVar(&ufUpdateStage1, "update-stage1", "",
		"Absolute URL to an action definition to run during stage1 to stage2 update.")
	updateCmd.Flags().StringVar(&ufUpdateStage1JSON, "update-stage1-json", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufUpdateStage2, "update-stage2", "",
		"Absolute URL to an action definition to run during stage2 to stage3 update.")
	updateCmd.Flags().StringVar(&ufUpdateStage3, "update-stage3", "",
		"Absolute URL to an action definition to run after running stage3 update.")
	updateCmd.Flags().StringVar(&ufImagesVersion, "images-version", "",
		"Version of epoxy-images to use in each boot stage.")
}
<commit_msg>Updates ImagesVersion field only if flag is passed<commit_after>// Copyright 2018 ePoxy 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 command

import (
	"context"
	"fmt"
	"log"
	"regexp"
	"time"

	"cloud.google.com/go/datastore"
	"github.com/m-lab/epoxy/storage"
	"github.com/m-lab/go/rtx"
	"github.com/spf13/cobra"
)

// updateCmd represents the update command
var updateCmd = &cobra.Command{
	Use:   "update",
	Short: "Updates all ePoxy Host records matching --hostname pattern",
	Long: `
USAGE:
    **ONLY FOR TESTING**

    Updates Host records matching the regex pattern in the --hostname flag.

EXAMPLE:

    # Set the "update enabled" flag on the Host record.
    epoxy_admin update --project mlab-sandbox \
        --hostname mlab3.iad1t.measurement-lab.org \
        --update

    # Set the "update enabled" flag on all mlab4 Host records.
    epoxy_admin update --project mlab-sandbox \
        --hostname 'mlab4.*' \
        --update
`,
	Run: runUpdate,
}

func updateURL(url, original string) string {
	if url != "" {
		return fmtURL(url)
	}
	return original
}

// TODO: add unit tests by masking out NewClient & NewDatstoreConfig. Consider
// promoting the fake datastore types from storage/datastore_test.go to an
// internal fake package.
func runUpdate(cmd *cobra.Command, args []string) {
	// Setup Datastore client.
	ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
	defer cancel()

	client, err := datastore.NewClient(ctx, fProject)
	rtx.Must(err, "Failed to create new datastore client")

	// Save the host record to Datstore.
	ds := storage.NewDatastoreConfig(client)
	hosts, err := ds.List()
	rtx.Must(err, "Failed to list host records")

	// Compile given regex.
	r, err := regexp.Compile(ufHostname)
	rtx.Must(err, "Failed to compile given hostname pattern: %q", ufHostname)

	for _, h := range hosts {
		if !r.MatchString(h.Name) {
			continue
		}
		log.Printf("Updating: %s", h.Name)

		handleUpdate(h)

		// Save the host record.
		err = ds.Save(h)
		rtx.Must(err, "Failed to save new host record")

		// Retrieve the host record from Datastore to exercise the full save & load path.
		h, err = ds.Load(h.Name)
		rtx.Must(err, "Failed to save new host record")
		fmt.Println(h.String())
	}
	return
}

func handleUpdate(h *storage.Host) {
	h.UpdateEnabled = ufUpdate

	if len(ufExtensions) > 0 {
		h.Extensions = ufExtensions
	}

	if ufAddress != "" {
		h.IPv4Addr = ufAddress
	}
	h.Boot[storage.Stage1IPXE] = updateURL(fmtURL(ufBootStage1), h.Boot[storage.Stage1IPXE])
	h.Boot[storage.Stage1JSON] = updateURL(fmtURL(ufBootStage1JSON), h.Boot[storage.Stage1JSON])
	h.Boot[storage.Stage2] = updateURL(fmtURL(ufBootStage2), h.Boot[storage.Stage2])
	h.Boot[storage.Stage3] = updateURL(fmtURL(ufBootStage3), h.Boot[storage.Stage3])
	h.Update[storage.Stage1IPXE] = updateURL(fmtURL(ufUpdateStage1), h.Update[storage.Stage1IPXE])
	h.Update[storage.Stage1JSON] = updateURL(fmtURL(ufUpdateStage1JSON), h.Update[storage.Stage1JSON])
	h.Update[storage.Stage2] = updateURL(fmtURL(ufUpdateStage2), h.Update[storage.Stage2])
	h.Update[storage.Stage3] = updateURL(fmtURL(ufUpdateStage3), h.Update[storage.Stage3])

	if ufImagesVersion != "" {
		h.ImagesVersion = ufImagesVersion
	}
}

func init() {
	rootCmd.AddCommand(updateCmd)

	// Required local flags.
	updateCmd.Flags().StringVar(&ufHostname, "hostname", "",
		"Hostname of new record.")
	updateCmd.MarkFlagRequired("hostname")

	// Local flags which will only run when "update" is called directly.
	updateCmd.Flags().StringSliceVar(&ufExtensions, "extensions", []string{},
		"List of extensions to enable.")
	updateCmd.Flags().StringVar(&ufAddress, "address", "",
		"IP address of hostname.")
	updateCmd.Flags().BoolVar(&ufUpdate, "update", false,
		"Set Host.UpdateEnabled to true for an existing Host.")
	updateCmd.Flags().StringVar(&ufBootStage1, "boot-stage1", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufBootStage1JSON, "boot-stage1-json", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufBootStage2, "boot-stage2", "",
		"Absolute URL to an action definition to run during stage2 to stage3 boot.")
	updateCmd.Flags().StringVar(&ufBootStage3, "boot-stage3", "",
		"Absolute URL to an action definition to run after running stage3 boot.")
	updateCmd.Flags().StringVar(&ufUpdateStage1, "update-stage1", "",
		"Absolute URL to an action definition to run during stage1 to stage2 update.")
	updateCmd.Flags().StringVar(&ufUpdateStage1JSON, "update-stage1-json", "",
		"Absolute URL to an action definition to run during stage1 to stage2 boot.")
	updateCmd.Flags().StringVar(&ufUpdateStage2, "update-stage2", "",
		"Absolute URL to an action definition to run during stage2 to stage3 update.")
	updateCmd.Flags().StringVar(&ufUpdateStage3, "update-stage3", "",
		"Absolute URL to an action definition to run after running stage3 update.")
	updateCmd.Flags().StringVar(&ufImagesVersion, "images-version", "",
		"Version of epoxy-images to use in each boot stage.")
}
<|endoftext|>