<commit_before>package db

import (
	"time"
)

type Stream struct {
	ID             int       `sql:"bigint(20) NOT NULL AUTO_INCREMENT"`
	MemberID       int       `gorm:"type:bigint;not null;default:0;unique_index"`
	Twitch         string    `gorm:"type:varchar(191);not null;default:'';index"`
	TwitchStreamID string    `gorm:"type:varchar(191);not null;default:''"`
	TwitchGame     string    `form:"type:varchar(191);not null;default:''"`
	TwitchStart    int64     `gorm:"type:bigint;not null;default:0"`
	TwitchStop     int64     `gorm:"type:bigint;not null;default:0"`
	Youtube        string    `gorm:"type:varchar(191);not null;default:'';index"`
	YoutubeStart   int64     `gorm:"type:bigint;not null;default:0"`
	YoutubeStop    int64     `gorm:"type:bigint;not null;default:0"`
	CreatedAt      time.Time `sql:"DEFAULT:current_timestamp"`
	UpdatedAt      time.Time ``
	db             *DB       `gorm:"-"`
}

func (s *Stream) Save() error {
	return s.db.Save(s).Error
}

func (d *DB) StreamByID(id int) (*Stream, error) {
	s := new(Stream)
	err := d.First(&s, id).Error
	if err != nil {
		s.db = d
	}
	return s, err
}

func (d *DB) StreamByMemberID(memberID int) (*Stream, error) {
	s := new(Stream)
	err := d.Where("member_id = ?", memberID).First(&s).Error
	s.db = d
	return s, err
}

func (d *DB) Streams() ([]*Stream, error) {
	var s []*Stream
	err := d.Find(&s, "twitch != ? OR youtube != ?", "", "").Error
	for _, i := range s {
		i.db = d
	}
	return s, err
}
<commit_msg>Add youtube stream id for db<commit_after>package db

import (
	"time"
)

type Stream struct {
	ID              int       `sql:"bigint(20) NOT NULL AUTO_INCREMENT"`
	MemberID        int       `gorm:"type:bigint;not null;default:0;unique_index"`
	Twitch          string    `gorm:"type:varchar(191);not null;default:'';index"`
	TwitchStreamID  string    `gorm:"type:varchar(191);not null;default:''"`
	TwitchGame      string    `form:"type:varchar(191);not null;default:''"`
	TwitchStart     int64     `gorm:"type:bigint;not null;default:0"`
	TwitchStop      int64     `gorm:"type:bigint;not null;default:0"`
	YoutubeStreamID string    `gorm:"type:varchar(64);not null;default:''"`
	Youtube         string    `gorm:"type:varchar(191);not null;default:'';index"`
	YoutubeStart    int64     `gorm:"type:bigint;not null;default:0"`
	YoutubeStop     int64     `gorm:"type:bigint;not null;default:0"`
	CreatedAt       time.Time `sql:"DEFAULT:current_timestamp"`
	UpdatedAt       time.Time ``
	db              *DB       `gorm:"-"`
}

func (s *Stream) Save() error {
	return s.db.Save(s).Error
}

func (d *DB) StreamByID(id int) (*Stream, error) {
	s := new(Stream)
	err := d.First(&s, id).Error
	if err != nil {
		s.db = d
	}
	return s, err
}

func (d *DB) StreamByMemberID(memberID int) (*Stream, error) {
	s := new(Stream)
	err := d.Where("member_id = ?", memberID).First(&s).Error
	s.db = d
	return s, err
}

func (d *DB) Streams() ([]*Stream, error) {
	var s []*Stream
	err := d.Find(&s, "twitch != ? OR youtube != ?", "", "").Error
	for _, i := range s {
		i.db = d
	}
	return s, err
}
<|endoftext|>


<commit_before>package client

import (
	"io"

	pb "github.com/alvindaiyan/goTalk2/proto/client_proto"

	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"

	"google.golang.org/grpc"
)

var (
	serverAddr *string
	title      string
)

func Chat(letters ...string) error {
	// get connection
	conn := connect(serverAddr)
	defer conn.Close()
	client := pb.NewChatClient(conn)

	stream, err := client.Chat(context.Background())
	if err != nil {
		grpclog.Println("%v.Chat(_) = _, %v", client, err) // better logging
		return err
	}

	// receive msg
	waitc := make(chan struct{})
	var recevieErr error
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				// read done
				close(waitc)
				return
			}
			if err != nil {
				grpclog.Printf("Failed to receive a msg : %v", err) // need better logging
				recevieErr = err
				return
			}
			grpclog.Printf("client -- server status: %s", in.Content)
		}
	}()

	if recevieErr != nil {
		return recevieErr
	}

	// send msg
	for _, str := range letters {
		grpclog.Printf("client -- send msg: %v", str)
		if err := stream.Send(&pb.Msg{Content: str, Title: title}); err != nil {
			grpclog.Printf("%v.Send(%v) = %v", stream, str, err) // need better logging
			return err
		}
	}

	// close send
	stream.CloseSend()
	<-waitc
	return nil
}

func InitChatClient(t string, srvAddr *string) {
	title = t
	serverAddr = srvAddr
}

func connect(srvAddr *string) *grpc.ClientConn {
	conn, err := grpc.Dial(*srvAddr, grpc.WithInsecure())

	if err != nil {
		grpclog.Fatalf("fail to dial: %v", err)
	}
	grpclog.Println("client started...")

	return conn

}
<commit_msg>test git<commit_after>package client

import (
	"io"

	pb "github.com/alvindaiyan/goTalk2/proto/client_proto"

	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"

	"google.golang.org/grpc"
)

var (
	serverAddr *string
	title      string
)

func Chat(letters ...string) error {
	// get connection for chat
	conn := connect(serverAddr)
	defer conn.Close()
	client := pb.NewChatClient(conn)

	stream, err := client.Chat(context.Background())
	if err != nil {
		grpclog.Println("%v.Chat(_) = _, %v", client, err) // better logging
		return err
	}

	// receive msg
	waitc := make(chan struct{})
	var recevieErr error
	go func() {
		for {
			in, err := stream.Recv()
			if err == io.EOF {
				// read done
				close(waitc)
				return
			}
			if err != nil {
				grpclog.Printf("Failed to receive a msg : %v", err) // need better logging
				recevieErr = err
				return
			}
			grpclog.Printf("client -- server status: %s", in.Content)
		}
	}()

	if recevieErr != nil {
		return recevieErr
	}

	// send msg
	for _, str := range letters {
		grpclog.Printf("client -- send msg: %v", str)
		if err := stream.Send(&pb.Msg{Content: str, Title: title}); err != nil {
			grpclog.Printf("%v.Send(%v) = %v", stream, str, err) // need better logging
			return err
		}
	}

	// close send
	stream.CloseSend()
	<-waitc
	return nil
}

func InitChatClient(t string, srvAddr *string) {
	title = t
	serverAddr = srvAddr
}

func connect(srvAddr *string) *grpc.ClientConn {
	conn, err := grpc.Dial(*srvAddr, grpc.WithInsecure())

	if err != nil {
		grpclog.Fatalf("fail to dial: %v", err)
	}
	grpclog.Println("client started...")

	return conn

}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"camli/blobref"
	"camli/client"
	"camli/schema"
	"camli/jsonsign"
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
)

// Things that can be uploaded.  (at most one of these)
var flagBlob = flag.Bool("blob", false, "upload a file's bytes as a single blob")
var flagFile = flag.Bool("file", false, "upload a file's bytes as a blob, as well as its JSON file record")
var flagPermanode = flag.Bool("permanode", false, "create a new permanode")
var flagInit = flag.Bool("init", false, "first-time configuration.")
var flagShare = flag.Bool("share", false, "create a camli share by haveref with the given blobrefs")
var flagTransitive = flag.Bool("transitive", true, "share the transitive closure of the given blobrefs")
var flagRemove = flag.Bool("remove", false, "remove the list of blobrefs")
var flagName = flag.String("name", "", "Optional name attribute to set on permanode when using -permanode and -file")
var flagVerbose = flag.Bool("verbose", false, "be verbose")

var flagSetAttr = flag.Bool("set-attr", false, "set (replace) an attribute")
var flagAddAttr = flag.Bool("add-attr", false, "add an attribute, additional if one already exists")

var wereErrors = false

type Uploader struct {
	*client.Client
}

func blobDetails(contents io.ReadSeeker) (bref *blobref.BlobRef, size int64, err os.Error) {
	s1 := sha1.New()
	contents.Seek(0, 0)
	size, err = io.Copy(s1, contents)
	if err == nil {
		bref = blobref.FromHash("sha1", s1)
	}
	return
}

func (up *Uploader) UploadFileBlob(filename string) (*client.PutResult, os.Error) {
	if *flagVerbose {
		log.Printf("Uploading filename: %s", filename)
	}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	ref, size, err := blobDetails(file)
	if err != nil {
		return nil, err
	}
	file.Seek(0, 0)
	handle := &client.UploadHandle{ref, size, file}
	return up.Upload(handle)
}

func (up *Uploader) UploadFile(filename string) (*client.PutResult, os.Error) {
	fi, err := os.Lstat(filename)
	if err != nil {
		return nil, err
	}

	m := schema.NewCommonFileMap(filename, fi)

	switch {
	case fi.IsRegular():
		// Put the blob of the file itself.  (TODO: smart boundary chunking)
		// For now we just store it as one range.
		blobpr, err := up.UploadFileBlob(filename)
		if err != nil {
			return nil, err
		}
		parts := []schema.ContentPart{{BlobRef: blobpr.BlobRef, Size: uint64(blobpr.Size)}}
		if blobpr.Size != fi.Size {
			// TODO: handle races of file changing while reading it
			// after the stat.
		}
		if err = schema.PopulateRegularFileMap(m, fi, parts); err != nil {
			return nil, err
		}
	case fi.IsSymlink():
		if err = schema.PopulateSymlinkMap(m, filename); err != nil {
			return nil, err
		}
	case fi.IsDirectory():
		ss := new(schema.StaticSet)
		dir, err := os.Open(filename)
		if err != nil {
			return nil, err
		}
		dirNames, err := dir.Readdirnames(-1)
		if err != nil {
			return nil, err
		}
		dir.Close()
		sort.SortStrings(dirNames)
		// TODO: process dirName entries in parallel
		for _, dirEntName := range dirNames {
			pr, err := up.UploadFile(filename + "/" + dirEntName)
			if err != nil {
				return nil, err
			}
			ss.Add(pr.BlobRef)
		}
		sspr, err := up.UploadMap(ss.Map())
		if err != nil {
			return nil, err
		}
		schema.PopulateDirectoryMap(m, sspr.BlobRef)
	case fi.IsBlock():
		fallthrough
	case fi.IsChar():
		fallthrough
	case fi.IsSocket():
		fallthrough
	case fi.IsFifo():
		fallthrough
	default:
		return nil, schema.UnimplementedError
	}

	mappr, err := up.UploadMap(m)
	return mappr, err
}

func (up *Uploader) UploadMap(m map[string]interface{}) (*client.PutResult, os.Error) {
	json, err := schema.MapToCamliJson(m)
	if err != nil {
		return nil, err
	}
	if *flagVerbose {
		fmt.Printf("json: %s\n", json)
	}
	return up.Upload(client.NewUploadHandleFromString(json))
}

func (up *Uploader) SignMap(m map[string]interface{}) (string, os.Error) {
	camliSigBlobref := up.Client.SignerPublicKeyBlobref()
	if camliSigBlobref == nil {
		// TODO: more helpful error message
		return "", os.NewError("No public key configured.")
	}

	m["camliSigner"] = camliSigBlobref.String()
	unsigned, err := schema.MapToCamliJson(m)
	if err != nil {
		return "", err
	}
	sr := &jsonsign.SignRequest{
		UnsignedJson: unsigned,
		Fetcher:      up.Client.GetBlobFetcher(),
		UseAgent:     true,
	}
	return sr.Sign()
}

func (up *Uploader) UploadAndSignMap(m map[string]interface{}) (*client.PutResult, os.Error) {
	signed, err := up.SignMap(m)
	if err != nil {
		return nil, err
	}
	return up.Upload(client.NewUploadHandleFromString(signed))
}

func (up *Uploader) UploadNewPermanode() (*client.PutResult, os.Error) {
	unsigned := schema.NewUnsignedPermanode()
	return up.UploadAndSignMap(unsigned)
}

func (up *Uploader) UploadShare(target *blobref.BlobRef, transitive bool) (*client.PutResult, os.Error) {
	unsigned := schema.NewShareRef(schema.ShareHaveRef, target, transitive)
	return up.UploadAndSignMap(unsigned)
}

func sumSet(flags ...*bool) (count int) {
	for _, f := range flags {
		if *f {
			count++
		}
	}
	return
}

func usage(msg string) {
	if msg != "" {
		fmt.Println("Error:", msg)
	}
	fmt.Println(`
Usage: camput

  camput --init       # first time configuration
  camput --blob <filename(s) to upload as blobs>
  camput --file <filename(s) to upload as blobs + JSON metadata>
  camput --share <blobref to share via haveref> [--transitive]
`)
	flag.PrintDefaults()
	os.Exit(1)
}

func handleResult(what string, pr *client.PutResult, err os.Error) {
	if err != nil {
		log.Printf("Error putting %s: %s", what, err)
		wereErrors = true
		return
	}
	if *flagVerbose {
		fmt.Printf("Put %s: %q\n", what, pr)
	} else {
		fmt.Println(pr.BlobRef.String())
	}
}

func main() {
	flag.Parse()

	nOpts := sumSet(flagFile, flagBlob, flagPermanode, flagInit, flagShare, flagRemove,
		flagSetAttr, flagAddAttr)
	if !(nOpts == 1 ||
		(nOpts == 2 && *flagFile && *flagPermanode)) {
		usage("Conflicting mode options.")
	}

	cc := client.NewOrFail()
	if !*flagVerbose {
		cc.SetLogger(nil)
	}
	up := &Uploader{cc}

	switch {
	case *flagInit:
		doInit()
		return
	case *flagFile || *flagBlob:
		var (
			permaNode *client.PutResult
			lastPut *client.PutResult
			err os.Error
		)
		if n := flag.NArg(); *flagPermanode {
			if n != 1 {
				log.Fatalf("Options --permanode and --file can only be used together when there's exactly one argument")
			}
			permaNode, err = up.UploadNewPermanode()
			if err != nil {
				log.Fatalf("Error uploading permanode: %v", err)
			}
		}
		for n := 0; n < flag.NArg(); n++ {
			if *flagBlob {
				lastPut, err = up.UploadFileBlob(flag.Arg(n))
				handleResult("blob", lastPut, err)
			} else {
				lastPut, err = up.UploadFile(flag.Arg(n))
				handleResult("file", lastPut, err)
			}
		}
		if permaNode != nil {
			put, err := up.UploadAndSignMap(schema.NewSetAttributeClaim(permaNode.BlobRef, "camliContent", lastPut.BlobRef.String()))
			handleResult("claim-permanode-content", put, err)
			if *flagName != "" {
				put, err := up.UploadAndSignMap(schema.NewSetAttributeClaim(permaNode.BlobRef, "name", *flagName))
                                handleResult("claim-permanode-name", put, err)
			}
                        handleResult("permanode", permaNode, nil)
		}
	case *flagPermanode:
		if flag.NArg() > 0 {
			log.Fatalf("--permanode doesn't take any additional arguments")
		}
		pr, err := up.UploadNewPermanode()
		handleResult("permanode", pr, err)
	case *flagShare:
		if flag.NArg() != 1 {
			log.Fatalf("--share only supports one blobref")
		}
		br := blobref.Parse(flag.Arg(0))
		if br == nil {
			log.Fatalf("BlobRef is invalid: %q", flag.Arg(0))
		}
		pr, err := up.UploadShare(br, *flagTransitive)
		handleResult("share", pr, err)
	case *flagRemove:
		if flag.NArg() == 0 {
			log.Fatalf("--remove takes one or more blobrefs")
		}
		err := up.RemoveBlobs(blobref.ParseMulti(flag.Args()))
		if err != nil {
			log.Printf("Error removing blobs %s: %s", strings.Join(flag.Args(), ","), err)
			wereErrors = true
		}
	case *flagAddAttr || *flagSetAttr:
		if flag.NArg() != 3 {
                        log.Fatalf("--set-attr and --add-attr take 3 args: <permanode> <attr> <value>")
                }
		pn := blobref.Parse(flag.Arg(0))
		if pn == nil {
			log.Fatalf("Error parsing blobref %q", flag.Arg(0))
		}
		m := schema.NewSetAttributeClaim(pn, flag.Arg(1), flag.Arg(2))
		if *flagAddAttr {
			m = schema.NewAddAttributeClaim(pn, flag.Arg(1), flag.Arg(2))
		}
		put, err := up.UploadAndSignMap(m)
		handleResult(m["claimType"].(string), put, err)
	}

	if *flagVerbose {
		stats := up.Stats()
		log.Printf("Client stats: %s", stats.String())
	}
	if wereErrors {
		os.Exit(2)
	}
}
<commit_msg>camput: ask jsonsign to enable some of its flags<commit_after>/*
Copyright 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"

	"camli/blobref"
	"camli/client"
	"camli/schema"
	"camli/jsonsign"
)

// Things that can be uploaded.  (at most one of these)
var flagBlob = flag.Bool("blob", false, "upload a file's bytes as a single blob")
var flagFile = flag.Bool("file", false, "upload a file's bytes as a blob, as well as its JSON file record")
var flagPermanode = flag.Bool("permanode", false, "create a new permanode")
var flagInit = flag.Bool("init", false, "first-time configuration.")
var flagShare = flag.Bool("share", false, "create a camli share by haveref with the given blobrefs")
var flagTransitive = flag.Bool("transitive", true, "share the transitive closure of the given blobrefs")
var flagRemove = flag.Bool("remove", false, "remove the list of blobrefs")
var flagName = flag.String("name", "", "Optional name attribute to set on permanode when using -permanode and -file")
var flagVerbose = flag.Bool("verbose", false, "be verbose")

var flagSetAttr = flag.Bool("set-attr", false, "set (replace) an attribute")
var flagAddAttr = flag.Bool("add-attr", false, "add an attribute, additional if one already exists")

var wereErrors = false

type Uploader struct {
	*client.Client
}

func blobDetails(contents io.ReadSeeker) (bref *blobref.BlobRef, size int64, err os.Error) {
	s1 := sha1.New()
	contents.Seek(0, 0)
	size, err = io.Copy(s1, contents)
	if err == nil {
		bref = blobref.FromHash("sha1", s1)
	}
	return
}

func (up *Uploader) UploadFileBlob(filename string) (*client.PutResult, os.Error) {
	if *flagVerbose {
		log.Printf("Uploading filename: %s", filename)
	}
	file, err := os.Open(filename)
	if err != nil {
		return nil, err
	}

	ref, size, err := blobDetails(file)
	if err != nil {
		return nil, err
	}
	file.Seek(0, 0)
	handle := &client.UploadHandle{ref, size, file}
	return up.Upload(handle)
}

func (up *Uploader) UploadFile(filename string) (*client.PutResult, os.Error) {
	fi, err := os.Lstat(filename)
	if err != nil {
		return nil, err
	}

	m := schema.NewCommonFileMap(filename, fi)

	switch {
	case fi.IsRegular():
		// Put the blob of the file itself.  (TODO: smart boundary chunking)
		// For now we just store it as one range.
		blobpr, err := up.UploadFileBlob(filename)
		if err != nil {
			return nil, err
		}
		parts := []schema.ContentPart{{BlobRef: blobpr.BlobRef, Size: uint64(blobpr.Size)}}
		if blobpr.Size != fi.Size {
			// TODO: handle races of file changing while reading it
			// after the stat.
		}
		if err = schema.PopulateRegularFileMap(m, fi, parts); err != nil {
			return nil, err
		}
	case fi.IsSymlink():
		if err = schema.PopulateSymlinkMap(m, filename); err != nil {
			return nil, err
		}
	case fi.IsDirectory():
		ss := new(schema.StaticSet)
		dir, err := os.Open(filename)
		if err != nil {
			return nil, err
		}
		dirNames, err := dir.Readdirnames(-1)
		if err != nil {
			return nil, err
		}
		dir.Close()
		sort.SortStrings(dirNames)
		// TODO: process dirName entries in parallel
		for _, dirEntName := range dirNames {
			pr, err := up.UploadFile(filename + "/" + dirEntName)
			if err != nil {
				return nil, err
			}
			ss.Add(pr.BlobRef)
		}
		sspr, err := up.UploadMap(ss.Map())
		if err != nil {
			return nil, err
		}
		schema.PopulateDirectoryMap(m, sspr.BlobRef)
	case fi.IsBlock():
		fallthrough
	case fi.IsChar():
		fallthrough
	case fi.IsSocket():
		fallthrough
	case fi.IsFifo():
		fallthrough
	default:
		return nil, schema.UnimplementedError
	}

	mappr, err := up.UploadMap(m)
	return mappr, err
}

func (up *Uploader) UploadMap(m map[string]interface{}) (*client.PutResult, os.Error) {
	json, err := schema.MapToCamliJson(m)
	if err != nil {
		return nil, err
	}
	if *flagVerbose {
		fmt.Printf("json: %s\n", json)
	}
	return up.Upload(client.NewUploadHandleFromString(json))
}

func (up *Uploader) SignMap(m map[string]interface{}) (string, os.Error) {
	camliSigBlobref := up.Client.SignerPublicKeyBlobref()
	if camliSigBlobref == nil {
		// TODO: more helpful error message
		return "", os.NewError("No public key configured.")
	}

	m["camliSigner"] = camliSigBlobref.String()
	unsigned, err := schema.MapToCamliJson(m)
	if err != nil {
		return "", err
	}
	sr := &jsonsign.SignRequest{
		UnsignedJson: unsigned,
		Fetcher:      up.Client.GetBlobFetcher(),
		UseAgent:     true,
	}
	return sr.Sign()
}

func (up *Uploader) UploadAndSignMap(m map[string]interface{}) (*client.PutResult, os.Error) {
	signed, err := up.SignMap(m)
	if err != nil {
		return nil, err
	}
	return up.Upload(client.NewUploadHandleFromString(signed))
}

func (up *Uploader) UploadNewPermanode() (*client.PutResult, os.Error) {
	unsigned := schema.NewUnsignedPermanode()
	return up.UploadAndSignMap(unsigned)
}

func (up *Uploader) UploadShare(target *blobref.BlobRef, transitive bool) (*client.PutResult, os.Error) {
	unsigned := schema.NewShareRef(schema.ShareHaveRef, target, transitive)
	return up.UploadAndSignMap(unsigned)
}

func sumSet(flags ...*bool) (count int) {
	for _, f := range flags {
		if *f {
			count++
		}
	}
	return
}

func usage(msg string) {
	if msg != "" {
		fmt.Println("Error:", msg)
	}
	fmt.Println(`
Usage: camput

  camput --init       # first time configuration
  camput --blob <filename(s) to upload as blobs>
  camput --file <filename(s) to upload as blobs + JSON metadata>
  camput --share <blobref to share via haveref> [--transitive]
`)
	flag.PrintDefaults()
	os.Exit(1)
}

func handleResult(what string, pr *client.PutResult, err os.Error) {
	if err != nil {
		log.Printf("Error putting %s: %s", what, err)
		wereErrors = true
		return
	}
	if *flagVerbose {
		fmt.Printf("Put %s: %q\n", what, pr)
	} else {
		fmt.Println(pr.BlobRef.String())
	}
}

func main() {
	jsonsign.AddFlags()
	flag.Parse()

	nOpts := sumSet(flagFile, flagBlob, flagPermanode, flagInit, flagShare, flagRemove,
		flagSetAttr, flagAddAttr)
	if !(nOpts == 1 ||
		(nOpts == 2 && *flagFile && *flagPermanode)) {
		usage("Conflicting mode options.")
	}

	cc := client.NewOrFail()
	if !*flagVerbose {
		cc.SetLogger(nil)
	}
	up := &Uploader{cc}

	switch {
	case *flagInit:
		doInit()
		return
	case *flagFile || *flagBlob:
		var (
			permaNode *client.PutResult
			lastPut *client.PutResult
			err os.Error
		)
		if n := flag.NArg(); *flagPermanode {
			if n != 1 {
				log.Fatalf("Options --permanode and --file can only be used together when there's exactly one argument")
			}
			permaNode, err = up.UploadNewPermanode()
			if err != nil {
				log.Fatalf("Error uploading permanode: %v", err)
			}
		}
		for n := 0; n < flag.NArg(); n++ {
			if *flagBlob {
				lastPut, err = up.UploadFileBlob(flag.Arg(n))
				handleResult("blob", lastPut, err)
			} else {
				lastPut, err = up.UploadFile(flag.Arg(n))
				handleResult("file", lastPut, err)
			}
		}
		if permaNode != nil {
			put, err := up.UploadAndSignMap(schema.NewSetAttributeClaim(permaNode.BlobRef, "camliContent", lastPut.BlobRef.String()))
			handleResult("claim-permanode-content", put, err)
			if *flagName != "" {
				put, err := up.UploadAndSignMap(schema.NewSetAttributeClaim(permaNode.BlobRef, "name", *flagName))
                                handleResult("claim-permanode-name", put, err)
			}
                        handleResult("permanode", permaNode, nil)
		}
	case *flagPermanode:
		if flag.NArg() > 0 {
			log.Fatalf("--permanode doesn't take any additional arguments")
		}
		pr, err := up.UploadNewPermanode()
		handleResult("permanode", pr, err)
	case *flagShare:
		if flag.NArg() != 1 {
			log.Fatalf("--share only supports one blobref")
		}
		br := blobref.Parse(flag.Arg(0))
		if br == nil {
			log.Fatalf("BlobRef is invalid: %q", flag.Arg(0))
		}
		pr, err := up.UploadShare(br, *flagTransitive)
		handleResult("share", pr, err)
	case *flagRemove:
		if flag.NArg() == 0 {
			log.Fatalf("--remove takes one or more blobrefs")
		}
		err := up.RemoveBlobs(blobref.ParseMulti(flag.Args()))
		if err != nil {
			log.Printf("Error removing blobs %s: %s", strings.Join(flag.Args(), ","), err)
			wereErrors = true
		}
	case *flagAddAttr || *flagSetAttr:
		if flag.NArg() != 3 {
                        log.Fatalf("--set-attr and --add-attr take 3 args: <permanode> <attr> <value>")
                }
		pn := blobref.Parse(flag.Arg(0))
		if pn == nil {
			log.Fatalf("Error parsing blobref %q", flag.Arg(0))
		}
		m := schema.NewSetAttributeClaim(pn, flag.Arg(1), flag.Arg(2))
		if *flagAddAttr {
			m = schema.NewAddAttributeClaim(pn, flag.Arg(1), flag.Arg(2))
		}
		put, err := up.UploadAndSignMap(m)
		handleResult(m["claimType"].(string), put, err)
	}

	if *flagVerbose {
		stats := up.Stats()
		log.Printf("Client stats: %s", stats.String())
	}
	if wereErrors {
		os.Exit(2)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha1"
	"encoding/base32"
	"io"
	"strconv"
	"strings"

	"github.com/globalsign/mgo/bson"
)

func JellyfishIDHash(bsonData bson.M) string {
	var theHash string
	switch bsonData["type"] {
	case "basal":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deliveryType"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	case "bolus":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["subType"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	case "deviceEvent":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["subType"].(string),
			bsonData["time"].(string),
			bsonData["deviceId"].(string),
		)
	case "smbg":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
			strconv.FormatFloat(bsonData["value"].(float64), 'f', -1, 64),
		)
	default:
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	}
	return theHash
}

func JellyfishObjectIDHash(bsonData bson.M) string {
	return makeJellyfishHash(
		bsonData["id"].(string),
		bsonData["_groupId"].(string),
	)
}

func makeJellyfishHash(fields ...string) string {
	h := sha1.New()
	hashFields := append(fields, "bootstrap")
	for _, field := range hashFields {
		io.WriteString(h, field)
		io.WriteString(h, "_")
	}
	sha1 := h.Sum(nil)
	return strings.ToLower(base32.HexEncoding.WithPadding('-').EncodeToString(sha1))
}
<commit_msg>Generate ObjectIDHash based on the correct IDHash<commit_after>package main

import (
	"crypto/sha1"
	"encoding/base32"
	"io"
	"strconv"
	"strings"

	"github.com/globalsign/mgo/bson"
)

func JellyfishIDHash(bsonData bson.M) string {
	var theHash string
	switch bsonData["type"] {
	case "basal":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deliveryType"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	case "bolus":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["subType"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	case "deviceEvent":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["subType"].(string),
			bsonData["time"].(string),
			bsonData["deviceId"].(string),
		)
	case "smbg":
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
			strconv.FormatFloat(bsonData["value"].(float64), 'f', -1, 64),
		)
	default:
		theHash = makeJellyfishHash(
			bsonData["type"].(string),
			bsonData["deviceId"].(string),
			bsonData["time"].(string),
		)
	}
	return theHash
}

func JellyfishObjectIDHash(bsonData bson.M) string {
	return makeJellyfishHash(
		JellyfishIDHash(bsonData),
		bsonData["_groupId"].(string),
	)
}

func makeJellyfishHash(fields ...string) string {
	h := sha1.New()
	hashFields := append(fields, "bootstrap")
	for _, field := range hashFields {
		io.WriteString(h, field)
		io.WriteString(h, "_")
	}
	sha1 := h.Sum(nil)
	return strings.ToLower(base32.HexEncoding.WithPadding('-').EncodeToString(sha1))
}
<|endoftext|>


<commit_before>// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"net/http"

	"miniflux.app/http/request"
	"miniflux.app/http/response/json"
	"miniflux.app/http/route"
	"miniflux.app/model"
)

func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) {
	type webManifestShareTargetParams struct {
		URL  string `json:"url"`
		Text string `json:"text"`
	}

	type webManifestShareTarget struct {
		Action  string                       `json:"action"`
		Method  string                       `json:"method"`
		Enctype string                       `json:"enctype"`
		Params  webManifestShareTargetParams `json:"params"`
	}

	type webManifestIcon struct {
		Source string `json:"src"`
		Sizes  string `json:"sizes"`
		Type   string `json:"type"`
	}

	type webManifest struct {
		Name            string                 `json:"name"`
		Description     string                 `json:"description"`
		ShortName       string                 `json:"short_name"`
		StartURL        string                 `json:"start_url"`
		Icons           []webManifestIcon      `json:"icons"`
		ShareTarget     webManifestShareTarget `json:"share_target"`
		Display         string                 `json:"display"`
		ThemeColor      string                 `json:"theme_color"`
		BackgroundColor string                 `json:"background_color"`
	}

	themeColor := model.ThemeColor(request.UserTheme(r))
	manifest := &webManifest{
		Name:            "Miniflux",
		ShortName:       "Miniflux",
		Description:     "Minimalist Feed Reader",
		Display:         "minimal-ui",
		StartURL:        route.Path(h.router, "unread"),
		ThemeColor:      themeColor,
		BackgroundColor: themeColor,
		Icons: []webManifestIcon{
			{Source: route.Path(h.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png"},
			{Source: route.Path(h.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png"},
			{Source: route.Path(h.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png"},
		},
		ShareTarget: webManifestShareTarget{
			Action:  route.Path(h.router, "bookmarklet"),
			Method:  http.MethodGet,
			Enctype: "application/x-www-form-urlencoded",
			Params:  webManifestShareTargetParams{URL: "uri", Text: "text"},
		},
	}

	json.OK(w, r, manifest)
}
<commit_msg>Change PWA display mode to standalone<commit_after>// Copyright 2018 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"net/http"

	"miniflux.app/http/request"
	"miniflux.app/http/response/json"
	"miniflux.app/http/route"
	"miniflux.app/model"
)

func (h *handler) showWebManifest(w http.ResponseWriter, r *http.Request) {
	type webManifestShareTargetParams struct {
		URL  string `json:"url"`
		Text string `json:"text"`
	}

	type webManifestShareTarget struct {
		Action  string                       `json:"action"`
		Method  string                       `json:"method"`
		Enctype string                       `json:"enctype"`
		Params  webManifestShareTargetParams `json:"params"`
	}

	type webManifestIcon struct {
		Source string `json:"src"`
		Sizes  string `json:"sizes"`
		Type   string `json:"type"`
	}

	type webManifest struct {
		Name            string                 `json:"name"`
		Description     string                 `json:"description"`
		ShortName       string                 `json:"short_name"`
		StartURL        string                 `json:"start_url"`
		Icons           []webManifestIcon      `json:"icons"`
		ShareTarget     webManifestShareTarget `json:"share_target"`
		Display         string                 `json:"display"`
		ThemeColor      string                 `json:"theme_color"`
		BackgroundColor string                 `json:"background_color"`
	}

	themeColor := model.ThemeColor(request.UserTheme(r))
	manifest := &webManifest{
		Name:            "Miniflux",
		ShortName:       "Miniflux",
		Description:     "Minimalist Feed Reader",
		Display:         "standalone",
		StartURL:        route.Path(h.router, "unread"),
		ThemeColor:      themeColor,
		BackgroundColor: themeColor,
		Icons: []webManifestIcon{
			{Source: route.Path(h.router, "appIcon", "filename", "icon-120.png"), Sizes: "120x120", Type: "image/png"},
			{Source: route.Path(h.router, "appIcon", "filename", "icon-192.png"), Sizes: "192x192", Type: "image/png"},
			{Source: route.Path(h.router, "appIcon", "filename", "icon-512.png"), Sizes: "512x512", Type: "image/png"},
		},
		ShareTarget: webManifestShareTarget{
			Action:  route.Path(h.router, "bookmarklet"),
			Method:  http.MethodGet,
			Enctype: "application/x-www-form-urlencoded",
			Params:  webManifestShareTargetParams{URL: "uri", Text: "text"},
		},
	}

	json.OK(w, r, manifest)
}
<|endoftext|>


<commit_before>package common

import (
	"github.com/meshbird/meshbird/network/protocol"
	"github.com/meshbird/meshbird/secure"
	log "github.com/mgutz/logxi/v1"
	"net"
	"os"
	"sync"
	"time"
)

type NetTable struct {
	BaseService

	localNode *LocalNode
	waitGroup sync.WaitGroup
	dhtInChan chan string

	lock      sync.RWMutex
	blackList map[string]time.Time
	peers     map[string]*RemoteNode

	heartbeatTicker <-chan time.Time

	logger log.Logger
}

func (nt NetTable) Name() string {
	return "net-table"
}

func (nt *NetTable) Init(ln *LocalNode) error {
	nt.logger = log.NewLogger(log.NewConcurrentWriter(os.Stderr), "[net-table] ")
	nt.localNode = ln
	nt.dhtInChan = make(chan string, 10)
	nt.blackList = make(map[string]time.Time)
	nt.peers = make(map[string]*RemoteNode)
	return nil
}

func (nt *NetTable) Run() error {
	for i := 0; i < 10; i++ {
		go nt.processDHTIn()
	}
	go nt.heartbeat()
	return nil
}

func (nt *NetTable) Stop() {
	nt.SetStatus(StatusStopping)
	nt.lock.Lock()
	defer nt.lock.Unlock()
	for _, peer := range nt.peers {
		peer.Close()
	}
}

func (nt *NetTable) GetDHTInChannel() chan<- string {
	return nt.dhtInChan
}

func (nt *NetTable) RemoteNodeByIP(ip net.IP) *RemoteNode {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	return nt.peers[ip.To4().String()]
}

func (nt *NetTable) AddRemoteNode(rn *RemoteNode) {
	if nt.logger.IsDebug() {

		nt.logger.Debug("Trying to add node %s/%s ...", rn.privateIP.String(), rn.publicAddress)
	}

	if nt.localNode.State().PrivateIP.Equal(rn.privateIP) {
		if nt.logger.IsDebug() {

			nt.logger.Debug("Found myself, node will not be added!")
		}
		return
	}

	nt.lock.Lock()
	defer nt.lock.Unlock()
	nt.peers[rn.privateIP.To4().String()] = rn
	if nt.logger.IsInfo() {

		nt.logger.Info("Added remote node: %s/%s", rn.privateIP.String(), rn.publicAddress)
	}
	go rn.listen(nt.localNode)
}

func (nt *NetTable) RemoveRemoteNode(ip net.IP) {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	delete(nt.peers, ip.To4().String())
}

func (nt *NetTable) processDHTIn() {
	for nt.Status() != StatusStopping {
		select {
		case host, ok := <-nt.dhtInChan:
			if !ok {
				return
			}
			nt.lock.Lock()
			_, ok = nt.blackList[host]
			if !ok {
				for _, peer := range nt.peers {
					if peer.publicAddress == host {
						ok = true
						break
					}
				}
			}
			nt.lock.Unlock()

			if !ok {
				nt.tryConnect(host)
			}
		}
	}
}

func (nt *NetTable) heartbeat() {
	nt.heartbeatTicker = time.Tick(5 * time.Second)

	for {
		select {
		case _, ok := <-nt.heartbeatTicker:
			if !ok {
				break
			}
			nt.lock.Lock()
			for _, peer := range nt.peers {
				if err := peer.SendPack(protocol.NewHeartbeatMessage(nt.localNode.State().PrivateIP)); err != nil {

					nt.logger.Error("Error on send heartbeat: %v", err)
				}
			}
			nt.lock.Unlock()
		}
	}
}

func (nt *NetTable) tryConnect(h string) {
	rn, err := TryConnect(h, nt.localNode.NetworkSecret(), nt.localNode)
	if err != nil {
		nt.addToBlackList(h)
		return
	}
	if nt.logger.IsDebug() {

		nt.logger.Debug("Adding remote node from try connect...")
	}
	nt.AddRemoteNode(rn)
}

func (nt *NetTable) addToBlackList(h string) {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	//nt.blackList[h] = time.Now()
}

func (nt *NetTable) SendPacket(dstIP net.IP, payload []byte) {
	if nt.logger.IsDebug() {

		nt.logger.Debug("Sending to %s packet len %d", dstIP.String(), len(payload))
	}

	rn := nt.RemoteNodeByIP(dstIP)
	if rn == nil {
		if nt.logger.IsDebug() {
			nt.logger.Debug("Destination host unreachable: %s", dstIP.String())
			nt.logger.Debug("Known hosts: %v", nt.knownHosts())
		}

		return
	}

	payloadEnc, err := secure.EncryptIV(payload, nt.localNode.State().Secret.Key, nt.localNode.State().Secret.Key)
	if err != nil {
		nt.logger.Error("Error on encrypt", err)
		return
	}

	if err := rn.SendToInterface(payloadEnc); err != nil {
		nt.logger.Error("Send packet to %s err: %s", dstIP.String(), err)
	}
}

func (nt *NetTable) PeerAddresses() map[string]string {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	peers := make(map[string]string)
	for l, peer := range nt.peers {
		peers[l] = peer.publicAddress
	}
	return peers
}

func (nt *NetTable) knownHosts() []string {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	ips := make([]string, len(nt.peers))
	var i int
	for k := range nt.peers {
		ips[i] = k
		i++
	}
	return ips
}
<commit_msg>Net-table logging fixes<commit_after>package common

import (
	"github.com/meshbird/meshbird/network/protocol"
	"github.com/meshbird/meshbird/secure"
	log "github.com/mgutz/logxi/v1"
	"net"
	"os"
	"sync"
	"time"
	"fmt"
)

type NetTable struct {
	BaseService

	localNode *LocalNode
	waitGroup sync.WaitGroup
	dhtInChan chan string

	lock      sync.RWMutex
	blackList map[string]time.Time
	peers     map[string]*RemoteNode

	heartbeatTicker <-chan time.Time

	logger log.Logger
}

func (nt NetTable) Name() string {
	return "net-table"
}

func (nt *NetTable) Init(ln *LocalNode) error {
	nt.logger = log.NewLogger(log.NewConcurrentWriter(os.Stderr), "[net-table] ")
	nt.localNode = ln
	nt.dhtInChan = make(chan string, 10)
	nt.blackList = make(map[string]time.Time)
	nt.peers = make(map[string]*RemoteNode)
	return nil
}

func (nt *NetTable) Run() error {
	for i := 0; i < 10; i++ {
		go nt.processDHTIn()
	}
	go nt.heartbeat()
	return nil
}

func (nt *NetTable) Stop() {
	nt.SetStatus(StatusStopping)
	nt.lock.Lock()
	defer nt.lock.Unlock()
	for _, peer := range nt.peers {
		peer.Close()
	}
}

func (nt *NetTable) GetDHTInChannel() chan<- string {
	return nt.dhtInChan
}

func (nt *NetTable) RemoteNodeByIP(ip net.IP) *RemoteNode {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	return nt.peers[ip.To4().String()]
}

func (nt *NetTable) AddRemoteNode(rn *RemoteNode) {
	if nt.logger.IsDebug() {
		nt.logger.Debug(fmt.Sprintf("Trying to add node %s/%s ...", rn.privateIP.String(), rn.publicAddress))
	}

	if nt.localNode.State().PrivateIP.Equal(rn.privateIP) {
		if nt.logger.IsDebug() {
			nt.logger.Debug("Found myself, node will not be added!")
		}
		return
	}

	nt.lock.Lock()
	defer nt.lock.Unlock()
	nt.peers[rn.privateIP.To4().String()] = rn
	if nt.logger.IsInfo() {
		nt.logger.Info(fmt.Sprintf("Added remote node: %s/%s", rn.privateIP.String(), rn.publicAddress))
	}
	go rn.listen(nt.localNode)
}

func (nt *NetTable) RemoveRemoteNode(ip net.IP) {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	delete(nt.peers, ip.To4().String())
}

func (nt *NetTable) processDHTIn() {
	for nt.Status() != StatusStopping {
		select {
		case host, ok := <-nt.dhtInChan:
			if !ok {
				return
			}
			nt.lock.Lock()
			_, ok = nt.blackList[host]
			if !ok {
				for _, peer := range nt.peers {
					if peer.publicAddress == host {
						ok = true
						break
					}
				}
			}
			nt.lock.Unlock()

			if !ok {
				nt.tryConnect(host)
			}
		}
	}
}

func (nt *NetTable) heartbeat() {
	nt.heartbeatTicker = time.Tick(5 * time.Second)

	for {
		select {
		case _, ok := <-nt.heartbeatTicker:
			if !ok {
				break
			}
			nt.lock.Lock()
			for _, peer := range nt.peers {
				if err := peer.SendPack(protocol.NewHeartbeatMessage(nt.localNode.State().PrivateIP)); err != nil {
					nt.logger.Error(fmt.Sprintf("Error on send heartbeat: %v", err))
				}
			}
			nt.lock.Unlock()
		}
	}
}

func (nt *NetTable) tryConnect(h string) {
	rn, err := TryConnect(h, nt.localNode.NetworkSecret(), nt.localNode)
	if err != nil {
		nt.addToBlackList(h)
		return
	}
	if nt.logger.IsDebug() {
		nt.logger.Debug("Adding remote node from try connect...")
	}
	nt.AddRemoteNode(rn)
}

func (nt *NetTable) addToBlackList(h string) {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	//nt.blackList[h] = time.Now()
}

func (nt *NetTable) SendPacket(dstIP net.IP, payload []byte) {
	if nt.logger.IsDebug() {

		nt.logger.Debug(fmt.Sprintf("Sending to %s packet len %d", dstIP.String(), len(payload)))
	}

	rn := nt.RemoteNodeByIP(dstIP)
	if rn == nil {
		if nt.logger.IsDebug() {
			nt.logger.Debug(fmt.Sprintf("Destination host unreachable: %s", dstIP.String()))
			nt.logger.Debug(fmt.Sprintf("Known hosts: %v", nt.knownHosts()))
		}

		return
	}

	payloadEnc, err := secure.EncryptIV(payload, nt.localNode.State().Secret.Key, nt.localNode.State().Secret.Key)
	if err != nil {
		nt.logger.Error(fmt.Sprintf("Error on encrypt", err))
		return
	}

	if err := rn.SendToInterface(payloadEnc); err != nil {
		nt.logger.Error(fmt.Sprintf("Send packet to %s err: %s", dstIP.String(), err))
	}
}

func (nt *NetTable) PeerAddresses() map[string]string {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	peers := make(map[string]string)
	for l, peer := range nt.peers {
		peers[l] = peer.publicAddress
	}
	return peers
}

func (nt *NetTable) knownHosts() []string {
	nt.lock.Lock()
	defer nt.lock.Unlock()
	ips := make([]string, len(nt.peers))
	var i int
	for k := range nt.peers {
		ips[i] = k
		i++
	}
	return ips
}
<|endoftext|>


<commit_before>package dbmigrate

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/gocql/gocql"
)

// Database interface needs to be inmplemented to migrate a new type of database

type Database interface {
	CreateMigrationsTable() error
	HasMigrated(filename string) (bool, error)
	Migrate(filename string, migration string) error
}

// CassandraDatabase migrates Cassandra databases

type CassandraDatabase struct {
	readerSession *gocql.Session
	writerSession *gocql.Session
}

func (cassandra *CassandraDatabase) CreateMigrationsTable() error {
	err := cassandra.writerSession.Query(`
		CREATE TABLE migrations (
			name TEXT,
			created_at TIMEUUID,
			PRIMARY KEY (name)
		);
	`).Exec()
	if err != nil {
		if !strings.Contains(err.Error(), "Cannot add already existing column family") {
			return err
		}
	}
	fmt.Println("Created migrations table")
	return nil
}

func (cassandra *CassandraDatabase) HasMigrated(filename string) (bool, error) {
	var count int
	iter := cassandra.readerSession.Query(`
		SELECT COUNT(*) FROM migrations WHERE name = ?
	`, filename).Iter()
	if !iter.Scan(&count) {
		return false, iter.Close()
	}
	if err := iter.Close(); err != nil {
		return false, err
	}
	return count > 0, nil
}

func (cassandra *CassandraDatabase) Migrate(filename string, migration string) error {
	if err := cassandra.writerSession.Query(migration).Exec(); err != nil {
		return err
	}
	return cassandra.writerSession.Query(`
		INSERT INTO migrations(name, created_at)
		VALUES(?, NOW())
	`, filename).Exec()
}

func NewCassandraDatabase(readerSession *gocql.Session, writerSession *gocql.Session) *CassandraDatabase {
	return &CassandraDatabase{
		readerSession: readerSession,
		writerSession: writerSession,
	}
}

// PostgresDatabase migrates Postgresql databases

type PostgresDatabase struct {
	database *sql.DB
}

func (postgres *PostgresDatabase) CreateMigrationsTable() error {
	_, err := postgres.database.Exec(`
		create table if not exists migrations(id serial, name text not null, created_at timestamp with time zone not null)
	`)
	if err != nil {
		return err
	}
	_, err = postgres.database.Exec("create unique index idx_migrations_name on migrations(name)")
	if err != nil {
		if !strings.Contains(err.Error(), "already exists") {
			return err
		}
	}
	return nil
}

func (postgres *PostgresDatabase) HasMigrated(filename string) (bool, error) {
	var count int
	err := postgres.database.QueryRow("select count(1) from migrations where name = $1", filename).Scan(&count)
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (postgres *PostgresDatabase) Migrate(filename string, migration string) error {
	_, err := postgres.database.Exec(migration)
	if err != nil {
		return err
	}
	_, err = postgres.database.Exec("insert into migrations(name, created_at) values($1, current_timestamp)", filename)
	return err
}

func NewPostgresDatabase(db *sql.DB) *PostgresDatabase {
	return &PostgresDatabase{database: db}
}

// By default, apply Postgresql migrations, as in older versions
func Run(db *sql.DB, migrationsFolder string) error {
	postgres := NewPostgresDatabase(db)
	return ApplyMigrations(postgres, migrationsFolder)
}

// Run applies migrations from migrationsFolder to database.
func ApplyMigrations(database Database, migrationsFolder string) error {
	// Initialize migrations table, if it does not exist yet
	if err := database.CreateMigrationsTable(); err != nil {
		return err
	}

	// Scan migration file names in migrations folder
	d, err := os.Open(migrationsFolder)
	if err != nil {
		return err
	}
	dir, err := d.Readdir(-1)
	if err != nil {
		return err
	}

	// Run migrations
	sqlFiles := make([]string, 0)
	for _, f := range dir {
		ext := filepath.Ext(f.Name())
		if ".sql" == ext || ".cql" == ext {
			sqlFiles = append(sqlFiles, f.Name())
		}
	}
	sort.Strings(sqlFiles)
	for _, filename := range sqlFiles {
		// if exists in migrations table, leave it
		// else execute sql
		migrated, err := database.HasMigrated(filename)
		if err != nil {
			return err
		}
		fullpath := filepath.Join(migrationsFolder, filename)
		if migrated {
			fmt.Println("Already migrated", fullpath)
			continue
		}
		b, err := ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		migration := string(b)
		if len(migration) == 0 {
			fmt.Println("Skipping empty file", fullpath)
			continue // empty file
		}
		err = database.Migrate(filename, migration)
		if err != nil {
			return err
		}
		fmt.Println("Migrated", fullpath)
	}

	return nil
}
<commit_msg>Add primary key constraint to migrations table<commit_after>package dbmigrate

import (
	"database/sql"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/gocql/gocql"
)

// Database interface needs to be inmplemented to migrate a new type of database

type Database interface {
	CreateMigrationsTable() error
	HasMigrated(filename string) (bool, error)
	Migrate(filename string, migration string) error
}

// CassandraDatabase migrates Cassandra databases

type CassandraDatabase struct {
	readerSession *gocql.Session
	writerSession *gocql.Session
}

func (cassandra *CassandraDatabase) CreateMigrationsTable() error {
	err := cassandra.writerSession.Query(`
		CREATE TABLE migrations (
			name TEXT,
			created_at TIMEUUID,
			PRIMARY KEY (name)
		);
	`).Exec()
	if err != nil {
		if !strings.Contains(err.Error(), "Cannot add already existing column family") {
			return err
		}
	}
	fmt.Println("Created migrations table")
	return nil
}

func (cassandra *CassandraDatabase) HasMigrated(filename string) (bool, error) {
	var count int
	iter := cassandra.readerSession.Query(`
		SELECT COUNT(*) FROM migrations WHERE name = ?
	`, filename).Iter()
	if !iter.Scan(&count) {
		return false, iter.Close()
	}
	if err := iter.Close(); err != nil {
		return false, err
	}
	return count > 0, nil
}

func (cassandra *CassandraDatabase) Migrate(filename string, migration string) error {
	if err := cassandra.writerSession.Query(migration).Exec(); err != nil {
		return err
	}
	return cassandra.writerSession.Query(`
		INSERT INTO migrations(name, created_at)
		VALUES(?, NOW())
	`, filename).Exec()
}

func NewCassandraDatabase(readerSession *gocql.Session, writerSession *gocql.Session) *CassandraDatabase {
	return &CassandraDatabase{
		readerSession: readerSession,
		writerSession: writerSession,
	}
}

// PostgresDatabase migrates Postgresql databases

type PostgresDatabase struct {
	database *sql.DB
}

func (postgres *PostgresDatabase) CreateMigrationsTable() error {
	_, err := postgres.Exec(`
		CREATE TABLE IF NOT EXISTS migrations (
			id serial,
			name text NOT NULL,
			created_at timestamp with time zone NOT NULL,
			CONSTRAINT "PK_migrations_id" PRIMARY KEY (id)
	);`)
	if err != nil {
		return err
	}
	_, err = postgres.database.Exec("create unique index idx_migrations_name on migrations(name)")
	if err != nil {
		if !strings.Contains(err.Error(), "already exists") {
			return err
		}
	}
	return nil
}

func (postgres *PostgresDatabase) HasMigrated(filename string) (bool, error) {
	var count int
	err := postgres.database.QueryRow("select count(1) from migrations where name = $1", filename).Scan(&count)
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (postgres *PostgresDatabase) Migrate(filename string, migration string) error {
	_, err := postgres.database.Exec(migration)
	if err != nil {
		return err
	}
	_, err = postgres.database.Exec("insert into migrations(name, created_at) values($1, current_timestamp)", filename)
	return err
}

func NewPostgresDatabase(db *sql.DB) *PostgresDatabase {
	return &PostgresDatabase{database: db}
}

// By default, apply Postgresql migrations, as in older versions
func Run(db *sql.DB, migrationsFolder string) error {
	postgres := NewPostgresDatabase(db)
	return ApplyMigrations(postgres, migrationsFolder)
}

// Run applies migrations from migrationsFolder to database.
func ApplyMigrations(database Database, migrationsFolder string) error {
	// Initialize migrations table, if it does not exist yet
	if err := database.CreateMigrationsTable(); err != nil {
		return err
	}

	// Scan migration file names in migrations folder
	d, err := os.Open(migrationsFolder)
	if err != nil {
		return err
	}
	dir, err := d.Readdir(-1)
	if err != nil {
		return err
	}

	// Run migrations
	sqlFiles := make([]string, 0)
	for _, f := range dir {
		ext := filepath.Ext(f.Name())
		if ".sql" == ext || ".cql" == ext {
			sqlFiles = append(sqlFiles, f.Name())
		}
	}
	sort.Strings(sqlFiles)
	for _, filename := range sqlFiles {
		// if exists in migrations table, leave it
		// else execute sql
		migrated, err := database.HasMigrated(filename)
		if err != nil {
			return err
		}
		fullpath := filepath.Join(migrationsFolder, filename)
		if migrated {
			fmt.Println("Already migrated", fullpath)
			continue
		}
		b, err := ioutil.ReadFile(fullpath)
		if err != nil {
			return err
		}
		migration := string(b)
		if len(migration) == 0 {
			fmt.Println("Skipping empty file", fullpath)
			continue // empty file
		}
		err = database.Migrate(filename, migration)
		if err != nil {
			return err
		}
		fmt.Println("Migrated", fullpath)
	}

	return nil
}
<|endoftext|>


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

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/BurntSushi/toml"
	vc "github.com/containers/virtcontainers"
	"github.com/containers/virtcontainers/pkg/oci"
)

const (
	defaultHypervisor = vc.QemuHypervisor
	defaultProxy      = vc.CCProxyType
	defaultShim       = vc.CCShimType
	defaultAgent      = vc.HyperstartAgent
)

const (
	shimBinary = "cc-shim"
)

// The TOML configuration file contains a number of sections (or
// tables). The names of these tables are in dotted ("nested table")
// form:
//
//   [<component>.<type>]
//
// The components are hypervisor, proxy, shim and agent. For example,
//
//   [proxy.cc]
//
// The currently supported types are listed below:
const (
	// supported hypervisor component types
	qemuLiteHypervisorTableType = "qemu-lite"
	qemuHypervisorTableType     = "qemu"

	// supported proxy component types
	ccProxyTableType = "cc"

	// supported shim component types
	ccShimTableType = "cc"

	// supported agent component types
	hyperstartAgentTableType = "hyperstart"
)

var (
	errUnknownHypervisor = errors.New("unknown hypervisor")
	errUnknownAgent      = errors.New("unknown agent")
)

type tomlConfig struct {
	Hypervisor map[string]hypervisor
	Proxy      map[string]proxy
	Shim       map[string]shim
	Agent      map[string]agent
	Runtime    runtime
}

type hypervisor struct {
	Path   string
	Kernel string
	Image  string
}

type proxy struct {
	URL string `toml:"url"`
}

type runtime struct {
	GlobalLogPath string `toml:"global_log_path"`
}

type shim struct {
	Path string `toml:"path"`
}

type agent struct {
	PauseRootPath string `toml:"pause_root_path"`
}

func (h hypervisor) path() string {
	if h.Path == "" {
		return defaultHypervisorPath
	}

	return h.Path
}

func (h hypervisor) kernel() string {
	if h.Kernel == "" {
		return defaultKernelPath
	}

	return h.Kernel
}

func (h hypervisor) image() string {
	if h.Image == "" {
		return defaultImagePath
	}

	return h.Image
}

func (p proxy) url() string {
	if p.URL == "" {
		return defaultProxyURL
	}

	return p.URL
}

func (s shim) path() string {
	if s.Path == "" {
		return defaultShimPath
	}

	return s.Path
}

func (a agent) pauseRootPath() string {
	if a.PauseRootPath == "" {
		return defaultPauseRootPath
	}

	return a.PauseRootPath
}

func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
	hypervisor := h.path()
	kernel := h.kernel()
	image := h.image()

	for _, file := range []string{hypervisor, kernel, image} {
		if !fileExists(file) {
			return vc.HypervisorConfig{},
				fmt.Errorf("File does not exist: %v", file)
		}
	}

	return vc.HypervisorConfig{
		HypervisorPath: hypervisor,
		KernelPath:     kernel,
		ImagePath:      image,
	}, nil
}

func newHyperstartAgentConfig(a agent) (vc.HyperConfig, error) {
	dir := a.pauseRootPath()

	if !fileExists(dir) {
		return vc.HyperConfig{}, fmt.Errorf("Directory does not exist: %v", dir)
	}

	path := filepath.Join(dir, pauseBinRelativePath)

	if !fileExists(path) {
		return vc.HyperConfig{}, fmt.Errorf("File does not exist: %v", path)
	}

	return vc.HyperConfig{
		PauseBinPath: path,
	}, nil
}

func newCCShimConfig(s shim) (vc.CCShimConfig, error) {
	path := s.path()

	if !fileExists(path) {
		return vc.CCShimConfig{}, fmt.Errorf("File does not exist: %v", path)
	}

	return vc.CCShimConfig{
		Path: path,
	}, nil
}

func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig) error {
	for k, hypervisor := range tomlConf.Hypervisor {
		switch k {
		case qemuHypervisorTableType:
			fallthrough
		case qemuLiteHypervisorTableType:
			hConfig, err := newQemuHypervisorConfig(hypervisor)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.HypervisorConfig = hConfig

			break
		}
	}

	for k, proxy := range tomlConf.Proxy {
		switch k {
		case ccProxyTableType:
			pConfig := vc.CCProxyConfig{
				URL: proxy.url(),
			}

			config.ProxyType = vc.CCProxyType
			config.ProxyConfig = pConfig

			break
		}
	}

	for k, agent := range tomlConf.Agent {
		switch k {
		case hyperstartAgentTableType:
			agentConfig, err := newHyperstartAgentConfig(agent)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.AgentConfig = agentConfig

			break
		}
	}

	for k, shim := range tomlConf.Shim {
		switch k {
		case ccShimTableType:
			shConfig, err := newCCShimConfig(shim)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.ShimType = vc.CCShimType
			config.ShimConfig = shConfig

			break
		}
	}

	return nil
}

// loadConfiguration loads the configuration file and converts it into a
// runtime configuration.
//
// If ignoreLogging is true, the global log will not be initialised nor
// will this function make any log calls.
func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPath, logfilePath string, config oci.RuntimeConfig, err error) {
	defaultHypervisorConfig := vc.HypervisorConfig{
		HypervisorPath: defaultHypervisorPath,
		KernelPath:     defaultKernelPath,
		ImagePath:      defaultImagePath,
	}

	defaultAgentConfig := vc.HyperConfig{
		PauseBinPath: filepath.Join(defaultPauseRootPath,
			pauseBinRelativePath),
	}

	config = oci.RuntimeConfig{
		HypervisorType:   defaultHypervisor,
		HypervisorConfig: defaultHypervisorConfig,
		AgentType:        defaultAgent,
		AgentConfig:      defaultAgentConfig,
		ProxyType:        defaultProxy,
		ShimType:         defaultShim,
	}

	if configPath == "" {
		configPath = defaultRuntimeConfiguration
	}

	resolved, err := resolvePath(configPath)
	if err != nil {
		if os.IsNotExist(err) {
			// Make the error clearer than the one returned
			// by EvalSymlinks().
			return "", "", config, fmt.Errorf("Config file %v does not exist", configPath)
		}

		return "", "", config, err
	}

	configData, err := ioutil.ReadFile(resolved)
	if err != nil {
		return "", "", config, err
	}

	var tomlConf tomlConfig
	_, err = toml.Decode(string(configData), &tomlConf)
	if err != nil {
		return "", "", config, err
	}

	logfilePath = tomlConf.Runtime.GlobalLogPath

	if !ignoreLogging {
		// The configuration file may have enabled global logging,
		// so handle that before any log calls.
		err = handleGlobalLog(logfilePath)
		if err != nil {
			return "", "", config, err
		}

		ccLog.Debugf("TOML configuration: %v", tomlConf)
	}

	if err := updateRuntimeConfig(resolved, tomlConf, &config); err != nil {
		return "", "", config, err
	}

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

package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"github.com/BurntSushi/toml"
	vc "github.com/containers/virtcontainers"
	"github.com/containers/virtcontainers/pkg/oci"
)

const (
	defaultHypervisor = vc.QemuHypervisor
	defaultProxy      = vc.CCProxyType
	defaultShim       = vc.CCShimType
	defaultAgent      = vc.HyperstartAgent
)

// The TOML configuration file contains a number of sections (or
// tables). The names of these tables are in dotted ("nested table")
// form:
//
//   [<component>.<type>]
//
// The components are hypervisor, proxy, shim and agent. For example,
//
//   [proxy.cc]
//
// The currently supported types are listed below:
const (
	// supported hypervisor component types
	qemuLiteHypervisorTableType = "qemu-lite"
	qemuHypervisorTableType     = "qemu"

	// supported proxy component types
	ccProxyTableType = "cc"

	// supported shim component types
	ccShimTableType = "cc"

	// supported agent component types
	hyperstartAgentTableType = "hyperstart"
)

var (
	errUnknownHypervisor = errors.New("unknown hypervisor")
	errUnknownAgent      = errors.New("unknown agent")
)

type tomlConfig struct {
	Hypervisor map[string]hypervisor
	Proxy      map[string]proxy
	Shim       map[string]shim
	Agent      map[string]agent
	Runtime    runtime
}

type hypervisor struct {
	Path   string
	Kernel string
	Image  string
}

type proxy struct {
	URL string `toml:"url"`
}

type runtime struct {
	GlobalLogPath string `toml:"global_log_path"`
}

type shim struct {
	Path string `toml:"path"`
}

type agent struct {
	PauseRootPath string `toml:"pause_root_path"`
}

func (h hypervisor) path() string {
	if h.Path == "" {
		return defaultHypervisorPath
	}

	return h.Path
}

func (h hypervisor) kernel() string {
	if h.Kernel == "" {
		return defaultKernelPath
	}

	return h.Kernel
}

func (h hypervisor) image() string {
	if h.Image == "" {
		return defaultImagePath
	}

	return h.Image
}

func (p proxy) url() string {
	if p.URL == "" {
		return defaultProxyURL
	}

	return p.URL
}

func (s shim) path() string {
	if s.Path == "" {
		return defaultShimPath
	}

	return s.Path
}

func (a agent) pauseRootPath() string {
	if a.PauseRootPath == "" {
		return defaultPauseRootPath
	}

	return a.PauseRootPath
}

func newQemuHypervisorConfig(h hypervisor) (vc.HypervisorConfig, error) {
	hypervisor := h.path()
	kernel := h.kernel()
	image := h.image()

	for _, file := range []string{hypervisor, kernel, image} {
		if !fileExists(file) {
			return vc.HypervisorConfig{},
				fmt.Errorf("File does not exist: %v", file)
		}
	}

	return vc.HypervisorConfig{
		HypervisorPath: hypervisor,
		KernelPath:     kernel,
		ImagePath:      image,
	}, nil
}

func newHyperstartAgentConfig(a agent) (vc.HyperConfig, error) {
	dir := a.pauseRootPath()

	if !fileExists(dir) {
		return vc.HyperConfig{}, fmt.Errorf("Directory does not exist: %v", dir)
	}

	path := filepath.Join(dir, pauseBinRelativePath)

	if !fileExists(path) {
		return vc.HyperConfig{}, fmt.Errorf("File does not exist: %v", path)
	}

	return vc.HyperConfig{
		PauseBinPath: path,
	}, nil
}

func newCCShimConfig(s shim) (vc.CCShimConfig, error) {
	path := s.path()

	if !fileExists(path) {
		return vc.CCShimConfig{}, fmt.Errorf("File does not exist: %v", path)
	}

	return vc.CCShimConfig{
		Path: path,
	}, nil
}

func updateRuntimeConfig(configPath string, tomlConf tomlConfig, config *oci.RuntimeConfig) error {
	for k, hypervisor := range tomlConf.Hypervisor {
		switch k {
		case qemuHypervisorTableType:
			fallthrough
		case qemuLiteHypervisorTableType:
			hConfig, err := newQemuHypervisorConfig(hypervisor)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.HypervisorConfig = hConfig

			break
		}
	}

	for k, proxy := range tomlConf.Proxy {
		switch k {
		case ccProxyTableType:
			pConfig := vc.CCProxyConfig{
				URL: proxy.url(),
			}

			config.ProxyType = vc.CCProxyType
			config.ProxyConfig = pConfig

			break
		}
	}

	for k, agent := range tomlConf.Agent {
		switch k {
		case hyperstartAgentTableType:
			agentConfig, err := newHyperstartAgentConfig(agent)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.AgentConfig = agentConfig

			break
		}
	}

	for k, shim := range tomlConf.Shim {
		switch k {
		case ccShimTableType:
			shConfig, err := newCCShimConfig(shim)
			if err != nil {
				return fmt.Errorf("%v: %v", configPath, err)
			}

			config.ShimType = vc.CCShimType
			config.ShimConfig = shConfig

			break
		}
	}

	return nil
}

// loadConfiguration loads the configuration file and converts it into a
// runtime configuration.
//
// If ignoreLogging is true, the global log will not be initialised nor
// will this function make any log calls.
func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPath, logfilePath string, config oci.RuntimeConfig, err error) {
	defaultHypervisorConfig := vc.HypervisorConfig{
		HypervisorPath: defaultHypervisorPath,
		KernelPath:     defaultKernelPath,
		ImagePath:      defaultImagePath,
	}

	defaultAgentConfig := vc.HyperConfig{
		PauseBinPath: filepath.Join(defaultPauseRootPath,
			pauseBinRelativePath),
	}

	config = oci.RuntimeConfig{
		HypervisorType:   defaultHypervisor,
		HypervisorConfig: defaultHypervisorConfig,
		AgentType:        defaultAgent,
		AgentConfig:      defaultAgentConfig,
		ProxyType:        defaultProxy,
		ShimType:         defaultShim,
	}

	if configPath == "" {
		configPath = defaultRuntimeConfiguration
	}

	resolved, err := resolvePath(configPath)
	if err != nil {
		if os.IsNotExist(err) {
			// Make the error clearer than the one returned
			// by EvalSymlinks().
			return "", "", config, fmt.Errorf("Config file %v does not exist", configPath)
		}

		return "", "", config, err
	}

	configData, err := ioutil.ReadFile(resolved)
	if err != nil {
		return "", "", config, err
	}

	var tomlConf tomlConfig
	_, err = toml.Decode(string(configData), &tomlConf)
	if err != nil {
		return "", "", config, err
	}

	logfilePath = tomlConf.Runtime.GlobalLogPath

	if !ignoreLogging {
		// The configuration file may have enabled global logging,
		// so handle that before any log calls.
		err = handleGlobalLog(logfilePath)
		if err != nil {
			return "", "", config, err
		}

		ccLog.Debugf("TOML configuration: %v", tomlConf)
	}

	if err := updateRuntimeConfig(resolved, tomlConf, &config); err != nil {
		return "", "", config, err
	}

	return resolved, logfilePath, config, nil
}
<|endoftext|>


<commit_before>package goConfig

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/crgimenes/goConfig/goEnv"
	"github.com/crgimenes/goConfig/goFlags"
)

// Settings default
type Settings struct {
	// Path sets default config path
	Path string
	// File name of default config file
	File string
	// FileRequired config file required
	FileRequired bool
}

// Setup Pointer to internal variables
var Setup *Settings

func init() {
	Setup = &Settings{
		Path:         "./",
		File:         "config.json",
		FileRequired: false,
	}
}

// Parse configuration
func Parse(config interface{}) (err error) {

	err = LoadJSON(config)
	if err != nil {
		return
	}

	err = goEnv.Parse(config)
	if err != nil {
		return
	}

	err = goFlags.Parse(config)
	if err != nil {
		return
	}

	return
}

// LoadJSON config file
func LoadJSON(config interface{}) (err error) {
	configFile := Setup.Path + Setup.File
	file, err := os.Open(configFile)
	if os.IsNotExist(err) && !Setup.FileRequired {
		err = nil
		return
	} else if err != nil {
		return
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		return
	}

	return
}

// Save config file
func Save(config interface{}) (err error) {
	_, err = os.Stat(Setup.Path)
	if os.IsNotExist(err) {
		os.Mkdir(Setup.Path, 0700)
	} else if err != nil {
		return
	}

	configFile := Setup.Path + Setup.File

	_, err = os.Stat(configFile)
	if err != nil {
		return
	}

	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(configFile, b, 0644)
	if err != nil {
		return
	}
	return
}
<commit_msg>setup before parse<commit_after>package goConfig

import (
	"encoding/json"
	"io/ioutil"
	"os"

	"github.com/crgimenes/goConfig/goEnv"
	"github.com/crgimenes/goConfig/goFlags"
)

// Settings default
type Settings struct {
	// Path sets default config path
	Path string
	// File name of default config file
	File string
	// FileRequired config file required
	FileRequired bool
}

// Setup Pointer to internal variables
var Setup *Settings

func init() {
	Setup = &Settings{
		Path:         "./",
		File:         "config.json",
		FileRequired: false,
	}
}

// Parse configuration
func Parse(config interface{}) (err error) {

	err = LoadJSON(config)
	if err != nil {
		return
	}

	goEnv.Setup()
	err = goEnv.Parse(config)
	if err != nil {
		return
	}

	goFlags.Setup()
	goFlags.Preserve = true
	err = goFlags.Parse(config)
	if err != nil {
		return
	}

	return
}

// LoadJSON config file
func LoadJSON(config interface{}) (err error) {
	configFile := Setup.Path + Setup.File
	file, err := os.Open(configFile)
	if os.IsNotExist(err) && !Setup.FileRequired {
		err = nil
		return
	} else if err != nil {
		return
	}
	defer file.Close()

	decoder := json.NewDecoder(file)
	err = decoder.Decode(&config)
	if err != nil {
		return
	}

	return
}

// Save config file
func Save(config interface{}) (err error) {
	_, err = os.Stat(Setup.Path)
	if os.IsNotExist(err) {
		os.Mkdir(Setup.Path, 0700)
	} else if err != nil {
		return
	}

	configFile := Setup.Path + Setup.File

	_, err = os.Stat(configFile)
	if err != nil {
		return
	}

	b, err := json.MarshalIndent(config, "", "\t")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(configFile, b, 0644)
	if err != nil {
		return
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright © 2017 MeteoGroup Deutschland GmbH

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package main

import (
  "flag"
  "os"
)

var (
  awsProfile = ""
  awsAccessKey = ""
  awsSecretKey = ""
  awsRegion = ""
  sqsUrl = ""
  awsEndpoint = ""
  kafkaBrokers = ""
  kafkaTopic = ""
  metricsAddress = ""
  awsReadConfig = false
)

func init() {
  flag.StringVar(&awsAccessKey, "aws-access-key", "", "AWS access key")
  flag.StringVar(&awsSecretKey, "aws-secret-key", "", "AWS secret key")
  flag.StringVar(&awsRegion, "aws-region", os.Getenv("AWS_REGION"), "AWS region")
  flag.StringVar(&awsProfile, "aws-profile", os.Getenv("AWS_PROFILE"), "AWS profile")
  flag.BoolVar(&awsReadConfig, "aws-read-config", false, "read AWS configuration from `~/.aws/config`")
  flag.StringVar(&awsEndpoint, "aws-endpoint", os.Getenv("AWS_ENDPOINT"), "URL of the AWS endpoint")
  flag.StringVar(&sqsUrl, "sqs-url", os.Getenv("SQS_URL"), "URL of the SQS queue for incomming messages")
  flag.StringVar(&kafkaBrokers, "kafka-brokers", os.Getenv("KAFKA_BROKERS"), "list of Kafka brokers used for bootstrapping")
  flag.StringVar(&kafkaTopic, "kafka-topic", os.Getenv("KAFKA_TOPIC"), "Kafka topic for outgoing messages")
  flag.StringVar(&metricsAddress, "metrics-address", os.Getenv("METRICS_ADDRESS"), "Listening address to serve metrics")
  flag.Parse()

  if sqsUrl == "" {
    panic("Required parameter `sqs-url` is missing.")
  }
  if kafkaBrokers == "" {
    panic("Required parameter `kafka-brokers` is missing.")
  }
  if kafkaTopic == "" {
    panic("Required parameter `kafka-topic` is missing.")
  }

  logInfo("starting with configuration",
    "awsProfile", awsProfile,
    "awsRegion", awsRegion,
    "awsEndpoint", awsEndpoint,
    "metricsAddress", metricsAddress,
    "sqsUrl", sqsUrl,
    "kafkaBrokers", kafkaBrokers,
    "kafkaTopic", kafkaTopic,
  )
}
<commit_msg>Only log parameters that are set<commit_after>/*
Copyright © 2017 MeteoGroup Deutschland GmbH

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
package main

import (
  "flag"
  "os"
)

var (
  awsProfile = ""
  awsAccessKey = ""
  awsSecretKey = ""
  awsRegion = ""
  sqsUrl = ""
  awsEndpoint = ""
  kafkaBrokers = ""
  kafkaTopic = ""
  metricsAddress = ""
  awsReadConfig = false
)

func init() {
  flag.StringVar(&awsAccessKey, "aws-access-key", "", "AWS access key")
  flag.StringVar(&awsSecretKey, "aws-secret-key", "", "AWS secret key")
  flag.StringVar(&awsRegion, "aws-region", os.Getenv("AWS_REGION"), "AWS region")
  flag.StringVar(&awsProfile, "aws-profile", os.Getenv("AWS_PROFILE"), "AWS profile")
  flag.BoolVar(&awsReadConfig, "aws-read-config", false, "read AWS configuration from `~/.aws/config`")
  flag.StringVar(&awsEndpoint, "aws-endpoint", os.Getenv("AWS_ENDPOINT"), "URL of the AWS endpoint")
  flag.StringVar(&sqsUrl, "sqs-url", os.Getenv("SQS_URL"), "URL of the SQS queue for incomming messages")
  flag.StringVar(&kafkaBrokers, "kafka-brokers", os.Getenv("KAFKA_BROKERS"), "list of Kafka brokers used for bootstrapping")
  flag.StringVar(&kafkaTopic, "kafka-topic", os.Getenv("KAFKA_TOPIC"), "Kafka topic for outgoing messages")
  flag.StringVar(&metricsAddress, "metrics-address", os.Getenv("METRICS_ADDRESS"), "Listening address to serve metrics")
  flag.Parse()

  if sqsUrl == "" {
    panic("Required parameter `sqs-url` is missing.")
  }
  if kafkaBrokers == "" {
    panic("Required parameter `kafka-brokers` is missing.")
  }
  if kafkaTopic == "" {
    panic("Required parameter `kafka-topic` is missing.")
  }

  logConfig()
}

func logConfig() {
  parameters := []interface{}{}
  appendIfDefined := func(name string, value string) {
    if (value != "") {
      parameters = append(parameters, name, value)
    }
  }
  appendIfDefined("awsProfile", awsProfile)
  appendIfDefined("awsRegion", awsRegion)
  appendIfDefined("awsEndpoint", awsEndpoint)
  appendIfDefined("metricsAddress", metricsAddress)
  appendIfDefined("sqsUrl", sqsUrl)
  appendIfDefined("kafkaBrokers", kafkaBrokers)
  appendIfDefined("kafkaTopic", kafkaTopic)
  logInfo("starting with configuration", parameters...)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/hashicorp/terraform/helper/multierror"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/elasticache"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sqs"
)

type Config struct {
	AccessKey  string
	SecretKey  string
	Token      string
	Region     string
	MaxRetries int

	AllowedAccountIds   []interface{}
	ForbiddenAccountIds []interface{}

	DynamoDBEndpoint string
}

type AWSClient struct {
	cloudwatchconn  *cloudwatch.CloudWatch
	dynamodbconn    *dynamodb.DynamoDB
	ec2conn         *ec2.EC2
	ecsconn         *ecs.ECS
	elbconn         *elb.ELB
	autoscalingconn *autoscaling.AutoScaling
	s3conn          *s3.S3
	sqsconn         *sqs.SQS
	snsconn         *sns.SNS
	r53conn         *route53.Route53
	region          string
	rdsconn         *rds.RDS
	iamconn         *iam.IAM
	kinesisconn     *kinesis.Kinesis
	elasticacheconn *elasticache.ElastiCache
	lambdaconn      *lambda.Lambda
}

// Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) {
	var client AWSClient

	// Get the auth and region. This can fail if keys/regions were not
	// specified and we're attempting to use the environment.
	var errs []error

	log.Println("[INFO] Building AWS region structure")
	err := c.ValidateRegion()
	if err != nil {
		errs = append(errs, err)
	}

	if len(errs) == 0 {
		// store AWS region in client struct, for region specific operations such as
		// bucket storage in S3
		client.region = c.Region

		log.Println("[INFO] Building AWS auth structure")
		// We fetched all credential sources in Provider. If they are
		// available, they'll already be in c. See Provider definition.
		creds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
		awsConfig := &aws.Config{
			Credentials: creds,
			Region:      aws.String(c.Region),
			MaxRetries:  aws.Int(c.MaxRetries),
		}

		log.Println("[INFO] Initializing IAM Connection")
		client.iamconn = iam.New(awsConfig)

		err := c.ValidateCredentials(client.iamconn)
		if err != nil {
			errs = append(errs, err)
		}

		awsDynamoDBConfig := &aws.Config{
			Credentials: creds,
			Region:      aws.String(c.Region),
			MaxRetries:  aws.Int(c.MaxRetries),
			Endpoint:    aws.String(c.DynamoDBEndpoint),
		}

		log.Println("[INFO] Initializing DynamoDB connection")
		client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)

		log.Println("[INFO] Initializing ELB connection")
		client.elbconn = elb.New(awsConfig)

		log.Println("[INFO] Initializing S3 connection")
		client.s3conn = s3.New(awsConfig)

		log.Println("[INFO] Initializing SQS connection")
		client.sqsconn = sqs.New(awsConfig)

		log.Println("[INFO] Initializing SNS connection")
		client.snsconn = sns.New(awsConfig)

		log.Println("[INFO] Initializing RDS Connection")
		client.rdsconn = rds.New(awsConfig)

		log.Println("[INFO] Initializing Kinesis Connection")
		client.kinesisconn = kinesis.New(awsConfig)

		authErr := c.ValidateAccountId(client.iamconn)
		if authErr != nil {
			errs = append(errs, authErr)
		}

		log.Println("[INFO] Initializing AutoScaling connection")
		client.autoscalingconn = autoscaling.New(awsConfig)

		log.Println("[INFO] Initializing EC2 Connection")
		client.ec2conn = ec2.New(awsConfig)

		log.Println("[INFO] Initializing ECS Connection")
		client.ecsconn = ecs.New(awsConfig)

		// aws-sdk-go uses v4 for signing requests, which requires all global
		// endpoints to use 'us-east-1'.
		// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
		log.Println("[INFO] Initializing Route 53 connection")
		client.r53conn = route53.New(&aws.Config{
			Credentials: creds,
			Region:      aws.String("us-east-1"),
			MaxRetries:  aws.Int(c.MaxRetries),
		})

		log.Println("[INFO] Initializing Elasticache Connection")
		client.elasticacheconn = elasticache.New(awsConfig)

		log.Println("[INFO] Initializing Lambda Connection")
		client.lambdaconn = lambda.New(awsConfig)

		log.Println("[INFO] Initializing CloudWatch SDK connection")
		client.cloudwatchconn = cloudwatch.New(awsConfig)
	}

	if len(errs) > 0 {
		return nil, &multierror.Error{Errors: errs}
	}

	return &client, nil
}

// ValidateRegion returns an error if the configured region is not a
// valid aws region and nil otherwise.
func (c *Config) ValidateRegion() error {
	var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
		"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
		"sa-east-1", "cn-north-1", "us-gov-west-1"}

	for _, valid := range regions {
		if c.Region == valid {
			return nil
		}
	}
	return fmt.Errorf("Not a valid region: %s", c.Region)
}

// Validate credentials early and fail before we do any graph walking.
// In the case of an IAM role/profile with insuffecient privileges, fail
// silently
func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
	_, err := iamconn.GetUser(nil)

	if awsErr, ok := err.(awserr.Error); ok {

		if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" {
			log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile")
			// User may be an IAM instance profile, or otherwise IAM role without the
			// GetUser permissions, so fail silently
			return nil
		}

		if awsErr.Code() == "SignatureDoesNotMatch" {
			return fmt.Errorf("Failed authenticating with AWS: please verify credentials")
		}
	}

	return err
}

// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
		return nil
	}

	log.Printf("[INFO] Validating account ID")

	out, err := iamconn.GetUser(nil)
	if err != nil {
		return fmt.Errorf("Failed getting account ID from IAM: %s", err)
	}

	account_id := strings.Split(*out.User.ARN, ":")[4]

	if c.ForbiddenAccountIds != nil {
		for _, id := range c.ForbiddenAccountIds {
			if id == account_id {
				return fmt.Errorf("Forbidden account ID (%s)", id)
			}
		}
	}

	if c.AllowedAccountIds != nil {
		for _, id := range c.AllowedAccountIds {
			if id == account_id {
				return nil
			}
		}
		return fmt.Errorf("Account ID not allowed (%s)", account_id)
	}

	return nil
}
<commit_msg>fail silently when users have forbiddenaccount/allowedaccount configured while authenticating via iam profile instance<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/hashicorp/terraform/helper/multierror"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/autoscaling"
	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/dynamodb"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ecs"
	"github.com/aws/aws-sdk-go/service/elasticache"
	"github.com/aws/aws-sdk-go/service/elb"
	"github.com/aws/aws-sdk-go/service/iam"
	"github.com/aws/aws-sdk-go/service/kinesis"
	"github.com/aws/aws-sdk-go/service/lambda"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/route53"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/sns"
	"github.com/aws/aws-sdk-go/service/sqs"
)

type Config struct {
	AccessKey  string
	SecretKey  string
	Token      string
	Region     string
	MaxRetries int

	AllowedAccountIds   []interface{}
	ForbiddenAccountIds []interface{}

	DynamoDBEndpoint string
}

type AWSClient struct {
	cloudwatchconn  *cloudwatch.CloudWatch
	dynamodbconn    *dynamodb.DynamoDB
	ec2conn         *ec2.EC2
	ecsconn         *ecs.ECS
	elbconn         *elb.ELB
	autoscalingconn *autoscaling.AutoScaling
	s3conn          *s3.S3
	sqsconn         *sqs.SQS
	snsconn         *sns.SNS
	r53conn         *route53.Route53
	region          string
	rdsconn         *rds.RDS
	iamconn         *iam.IAM
	kinesisconn     *kinesis.Kinesis
	elasticacheconn *elasticache.ElastiCache
	lambdaconn      *lambda.Lambda
}

// Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) {
	var client AWSClient

	// Get the auth and region. This can fail if keys/regions were not
	// specified and we're attempting to use the environment.
	var errs []error

	log.Println("[INFO] Building AWS region structure")
	err := c.ValidateRegion()
	if err != nil {
		errs = append(errs, err)
	}

	if len(errs) == 0 {
		// store AWS region in client struct, for region specific operations such as
		// bucket storage in S3
		client.region = c.Region

		log.Println("[INFO] Building AWS auth structure")
		// We fetched all credential sources in Provider. If they are
		// available, they'll already be in c. See Provider definition.
		creds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
		awsConfig := &aws.Config{
			Credentials: creds,
			Region:      aws.String(c.Region),
			MaxRetries:  aws.Int(c.MaxRetries),
		}

		log.Println("[INFO] Initializing IAM Connection")
		client.iamconn = iam.New(awsConfig)

		err := c.ValidateCredentials(client.iamconn)
		if err != nil {
			errs = append(errs, err)
		}

		awsDynamoDBConfig := &aws.Config{
			Credentials: creds,
			Region:      aws.String(c.Region),
			MaxRetries:  aws.Int(c.MaxRetries),
			Endpoint:    aws.String(c.DynamoDBEndpoint),
		}

		log.Println("[INFO] Initializing DynamoDB connection")
		client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)

		log.Println("[INFO] Initializing ELB connection")
		client.elbconn = elb.New(awsConfig)

		log.Println("[INFO] Initializing S3 connection")
		client.s3conn = s3.New(awsConfig)

		log.Println("[INFO] Initializing SQS connection")
		client.sqsconn = sqs.New(awsConfig)

		log.Println("[INFO] Initializing SNS connection")
		client.snsconn = sns.New(awsConfig)

		log.Println("[INFO] Initializing RDS Connection")
		client.rdsconn = rds.New(awsConfig)

		log.Println("[INFO] Initializing Kinesis Connection")
		client.kinesisconn = kinesis.New(awsConfig)

		authErr := c.ValidateAccountId(client.iamconn)
		if authErr != nil {
			errs = append(errs, authErr)
		}

		log.Println("[INFO] Initializing AutoScaling connection")
		client.autoscalingconn = autoscaling.New(awsConfig)

		log.Println("[INFO] Initializing EC2 Connection")
		client.ec2conn = ec2.New(awsConfig)

		log.Println("[INFO] Initializing ECS Connection")
		client.ecsconn = ecs.New(awsConfig)

		// aws-sdk-go uses v4 for signing requests, which requires all global
		// endpoints to use 'us-east-1'.
		// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
		log.Println("[INFO] Initializing Route 53 connection")
		client.r53conn = route53.New(&aws.Config{
			Credentials: creds,
			Region:      aws.String("us-east-1"),
			MaxRetries:  aws.Int(c.MaxRetries),
		})

		log.Println("[INFO] Initializing Elasticache Connection")
		client.elasticacheconn = elasticache.New(awsConfig)

		log.Println("[INFO] Initializing Lambda Connection")
		client.lambdaconn = lambda.New(awsConfig)

		log.Println("[INFO] Initializing CloudWatch SDK connection")
		client.cloudwatchconn = cloudwatch.New(awsConfig)
	}

	if len(errs) > 0 {
		return nil, &multierror.Error{Errors: errs}
	}

	return &client, nil
}

// ValidateRegion returns an error if the configured region is not a
// valid aws region and nil otherwise.
func (c *Config) ValidateRegion() error {
	var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
		"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
		"sa-east-1", "cn-north-1", "us-gov-west-1"}

	for _, valid := range regions {
		if c.Region == valid {
			return nil
		}
	}
	return fmt.Errorf("Not a valid region: %s", c.Region)
}

// Validate credentials early and fail before we do any graph walking.
// In the case of an IAM role/profile with insuffecient privileges, fail
// silently
func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
	_, err := iamconn.GetUser(nil)

	if awsErr, ok := err.(awserr.Error); ok {

		if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" {
			log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile")
			// User may be an IAM instance profile, or otherwise IAM role without the
			// GetUser permissions, so fail silently
			return nil
		}

		if awsErr.Code() == "SignatureDoesNotMatch" {
			return fmt.Errorf("Failed authenticating with AWS: please verify credentials")
		}
	}

	return err
}

// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
		return nil
	}

	log.Printf("[INFO] Validating account ID")

	out, err := iamconn.GetUser(nil)

  if err != nil {
    awsErr, _ := err.(awserr.Error)
    if awsErr.Code() == "ValidationError" {
      log.Printf("[WARN] ValidationError with iam.GetUser, assuming its an IAM profile")
      // User may be an IAM instance profile, so fail silently.
      // If it is an IAM instance profile
      // validating account might be superfluous
    } else {
		  return fmt.Errorf("Failed getting account ID from IAM: %s", err)
      // return error if the account id is explicitly not authorised
    }
	}

	account_id := strings.Split(*out.User.ARN, ":")[4]

	if c.ForbiddenAccountIds != nil {
		for _, id := range c.ForbiddenAccountIds {
			if id == account_id {
				return fmt.Errorf("Forbidden account ID (%s)", id)
			}
		}
	}

	if c.AllowedAccountIds != nil {
		for _, id := range c.AllowedAccountIds {
			if id == account_id {
				return nil
			}
		}
		return fmt.Errorf("Account ID not allowed (%s)", account_id)
	}

	return nil
}
<|endoftext|>