<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"time"

	sg "github.com/rbastic/dyndao/sqlgen"

	dorm "github.com/rbastic/dyndao/orm"

	"github.com/rbastic/dyndao/schema/test/mock"

	_ "github.com/mattn/go-sqlite3"
	"github.com/rbastic/dyndao/adapters/core"
	sqliteAdapter "github.com/rbastic/dyndao/adapters/sqlite"
)

var (
	defaultDriver = "sqlite3"
	defaultDSN    = "file::memory:?mode=memory&cache=shared"
)

func fatalIf(err error) {
	if err != nil {
		panic(err)
	}
}

func getDefaultContext() (context.Context, context.CancelFunc) {
	return context.WithTimeout(context.Background(), 3*time.Second)
}

func getDriver() string {
	return defaultDriver
}

func getDSN() string {
	dsn := os.Getenv("DSN")
	if dsn == "" {
		dsn = defaultDSN
	}

	return dsn
}

// TODO: refactor this so it is available from somewhere else
// (so that user code doesn't have to replicate this)
func getSQLGen() *sg.SQLGenerator {
	sqlGen := sqliteAdapter.New(core.New())
	sg.PanicIfInvalid(sqlGen)
	return sqlGen
}

func main() {
	driver := getDriver()
	dsn := getDSN()
	db, err := dorm.GetDB(driver, dsn)
	fatalIf(err)

	defer func() {
		err = db.Close()
		fatalIf(err)
	}()

	// Load the sample included schema
	// (which is used by the internal dyndao test suite)
	sch := mock.NestedSchema()

	// Instantiate the ORM instance
	orm := dorm.New(getSQLGen(), sch, db)

	// CreateTables will create all tables within a given schema
	{
		// Instantiate our default context
		ctx, cancel := getDefaultContext()
		err = orm.CreateTables(ctx)
		cancel()
		fatalIf(err)
	}

	{
		// Instantiate our default context
		ctx, cancel := getDefaultContext()

		// Attempt to insert a record
		numRows, err := orm.Insert(ctx, nil, mock.RandomPerson())
		fatalIf(err)
		if numRows == 0 {
			panic("Insert returned zero rows affected -- something is clearly wrong")
		} else {
			fmt.Println("Looks like we inserted something")
		}
		cancel()
	}

	// DropTables will create all tables within a given schema
	{
		ctx, cancel := getDefaultContext()
		err = orm.DropTables(ctx)
		cancel()
		fatalIf(err)
	}
}
<commit_msg>add comments, defers<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"time"

	sg "github.com/rbastic/dyndao/sqlgen"

	dorm "github.com/rbastic/dyndao/orm"

	"github.com/rbastic/dyndao/schema/test/mock"

	_ "github.com/mattn/go-sqlite3"
	"github.com/rbastic/dyndao/adapters/core"
	sqliteAdapter "github.com/rbastic/dyndao/adapters/sqlite"
)

// Look at the adapter Makefiles for other examples of drivers and supported DSNs.
// TODO: Perhaps I should offer these as standard config opts.
var (
	defaultDriver = "sqlite3"
	defaultDSN    = "file::memory:?mode=memory&cache=shared"
)

func fatalIf(err error) {
	if err != nil {
		panic(err)
	}
}

func getDefaultContext() (context.Context, context.CancelFunc) {
	return context.WithTimeout(context.Background(), 3*time.Second)
}

func getDriver() string {
	return defaultDriver
}

func getDSN() string {
	dsn := os.Getenv("DSN")
	if dsn == "" {
		dsn = defaultDSN
	}

	return dsn
}

// TODO: refactor this so it is available from somewhere else
// (so that user code doesn't have to replicate this)
func getSQLGen() *sg.SQLGenerator {
	sqlGen := sqliteAdapter.New(core.New())
	sg.PanicIfInvalid(sqlGen)
	return sqlGen
}

func main() {
	driver := getDriver()
	dsn := getDSN()
	db, err := dorm.GetDB(driver, dsn)
	fatalIf(err)

	defer func() {
		err = db.Close()
		fatalIf(err)
	}()

	// Load the sample included schema
	// (which is also used by the internal dyndao test suite)
	sch := mock.NestedSchema()

	// Instantiate the ORM instance
	orm := dorm.New(getSQLGen(), sch, db)

	// CreateTables will create all tables within a given schema
	{
		// Instantiate our default context
		ctx, cancel := getDefaultContext()
		defer cancel()
		err = orm.CreateTables(ctx)
		fatalIf(err)
	}

	{
		// Instantiate our default context
		ctx, cancel := getDefaultContext()
		defer cancel()
		// Attempt to insert a record
		numRows, err := orm.Insert(ctx, nil, mock.RandomPerson())
		fatalIf(err)
		if numRows == 0 {
			panic("Insert returned zero rows affected -- something is clearly wrong")
		} else {
			fmt.Println("Looks like we inserted something")
		}
	}

	// DropTables will create all tables within a given schema
	{
		ctx, cancel := getDefaultContext()
		defer cancel()
		err = orm.DropTables(ctx)
		fatalIf(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/jsgoecke/tesla"
)

func main() {
	client, err := tesla.NewClient(
		&tesla.Auth{
			ClientID:     os.Getenv("TESLA_CLIENT_ID"),
			ClientSecret: os.Getenv("TESLA_CLIENT_SECRET"),
			Email:        os.Getenv("TESLA_USERNAME"),
			Password:     os.Getenv("TESLA_PASSWORD"),
		})
	if err != nil {
		panic(err)
	}

	vehicles, err := client.Vehicles()
	if err != nil {
		panic(err)
	}
	vehicle := vehicles[0]
	status, err := vehicle.MobileEnabled()

	if err != nil {
		panic(err)
	}

	fmt.Println(status)
	fmt.Println(vehicle.ChargeState())
	fmt.Println(vehicle.ClimateState())
	fmt.Println(vehicle.DriveState())
	fmt.Println(vehicle.GuiSettings())
	fmt.Println(vehicle.VehicleState())
	fmt.Println(vehicle.HonkHorn())
	fmt.Println(vehicle.FlashLights())
	fmt.Println(vehicle.Wakeup())
	fmt.Println(vehicle.OpenChargePort())
	fmt.Println(vehicle.ResetValetPIN())
	fmt.Println(vehicle.SetChargeLimitStandard())
	fmt.Println(vehicle.SetChargeLimit(50))
	fmt.Println(vehicle.StartCharging())
	fmt.Println(vehicle.StopCharging())
	fmt.Println(vehicle.SetChargeLimitMax())
	fmt.Println(vehicle.StartAirConditioning())
	fmt.Println(vehicle.StopAirConditioning())
	fmt.Println(vehicle.UnlockDoors())
	fmt.Println(vehicle.LockDoors())
	fmt.Println(vehicle.SetTemprature(72.0, 72.0))
	fmt.Println(vehicle.Start(os.Getenv("TESLA_PASSWORD")))
	fmt.Println(vehicle.OpenTrunk("rear"))
	fmt.Println(vehicle.OpenTrunk("front"))
	fmt.Println(vehicle.MovePanoRoof("vent", 0))
	fmt.Println(vehicle.MovePanoRoof("open", 0))
	fmt.Println(vehicle.MovePanoRoof("move", 50))
	fmt.Println(vehicle.MovePanoRoof("close", 0))

	// Take care with these, as the car will move
	fmt.Println(vehicle.AutoparkForward())
	fmt.Println(vehicle.AutoparkReverse())
	// Take care with these, as the car will move

	// Stream vehicle events
	eventChan, err := vehicle.Stream()
	if err != nil {
		for {
			event := <-eventChan
			if event != nil {
				eventJSON, _ := json.Marshal(event)
				fmt.Println(string(eventJSON))
			} else {
				fmt.Println("HTTP Stream timeout!")
				break
			}
		}
	}
}
<commit_msg>Added TriggerHomeLink to example<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/jsgoecke/tesla"
)

func main() {
	client, err := tesla.NewClient(
		&tesla.Auth{
			ClientID:     os.Getenv("TESLA_CLIENT_ID"),
			ClientSecret: os.Getenv("TESLA_CLIENT_SECRET"),
			Email:        os.Getenv("TESLA_USERNAME"),
			Password:     os.Getenv("TESLA_PASSWORD"),
		})
	if err != nil {
		panic(err)
	}

	vehicles, err := client.Vehicles()
	if err != nil {
		panic(err)
	}
	vehicle := vehicles[0]
	status, err := vehicle.MobileEnabled()

	if err != nil {
		panic(err)
	}

	fmt.Println(status)
	fmt.Println(vehicle.ChargeState())
	fmt.Println(vehicle.ClimateState())
	fmt.Println(vehicle.DriveState())
	fmt.Println(vehicle.GuiSettings())
	fmt.Println(vehicle.VehicleState())
	fmt.Println(vehicle.HonkHorn())
	fmt.Println(vehicle.FlashLights())
	fmt.Println(vehicle.Wakeup())
	fmt.Println(vehicle.OpenChargePort())
	fmt.Println(vehicle.ResetValetPIN())
	fmt.Println(vehicle.SetChargeLimitStandard())
	fmt.Println(vehicle.SetChargeLimit(50))
	fmt.Println(vehicle.StartCharging())
	fmt.Println(vehicle.StopCharging())
	fmt.Println(vehicle.SetChargeLimitMax())
	fmt.Println(vehicle.StartAirConditioning())
	fmt.Println(vehicle.StopAirConditioning())
	fmt.Println(vehicle.UnlockDoors())
	fmt.Println(vehicle.LockDoors())
	fmt.Println(vehicle.SetTemprature(72.0, 72.0))
	fmt.Println(vehicle.Start(os.Getenv("TESLA_PASSWORD")))
	fmt.Println(vehicle.OpenTrunk("rear"))
	fmt.Println(vehicle.OpenTrunk("front"))
	fmt.Println(vehicle.MovePanoRoof("vent", 0))
	fmt.Println(vehicle.MovePanoRoof("open", 0))
	fmt.Println(vehicle.MovePanoRoof("move", 50))
	fmt.Println(vehicle.MovePanoRoof("close", 0))
	fmt.Println(vehicle.TriggerHomeLink())

	// Take care with these, as the car will move
	fmt.Println(vehicle.AutoparkForward())
	fmt.Println(vehicle.AutoparkReverse())
	// Take care with these, as the car will move

	// Stream vehicle events
	eventChan, err := vehicle.Stream()
	if err != nil {
		for {
			event := <-eventChan
			if event != nil {
				eventJSON, _ := json.Marshal(event)
				fmt.Println(string(eventJSON))
			} else {
				fmt.Println("HTTP Stream timeout!")
				break
			}
		}
	}
}
<|endoftext|>


<commit_before>package gcsresource

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/cheggaaa/pb"
	"golang.org/x/oauth2"
	oauthgoogle "golang.org/x/oauth2/google"
	"google.golang.org/api/storage/v1"
)

type GCSClient interface {
	BucketObjects(bucketName string, prefix string) ([]string, error)
	ObjectGenerations(bucketName string, objectPath string) ([]int64, error)
	DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error
	UploadFile(bucketName string, objectPath string, localPath string) (int64, error)
	URL(bucketName string, objectPath string, generation int64) (string, error)
	DeleteObject(bucketName string, objectPath string, generation int64) error
}

type gcsclient struct {
	client         *storage.Service
	progressOutput io.Writer
}

func NewGCSClient(
	progressOutput io.Writer,
	project string,
	jsonKey string,
) (GCSClient, error) {
	var err error
	var storageClient *http.Client
	var userAgent = "gcs-resource/0.0.1"

	if jsonKey != "" {
		storageJwtConf, err := oauthgoogle.JWTConfigFromJSON([]byte(jsonKey), storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
		storageClient = storageJwtConf.Client(oauth2.NoContext)
	} else {
		storageClient, err = oauthgoogle.DefaultClient(oauth2.NoContext, storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
	}

	storageService, err := storage.New(storageClient)
	if err != nil {
		return &gcsclient{}, err
	}
	storageService.UserAgent = userAgent

	return &gcsclient{
		client:         storageService,
		progressOutput: progressOutput,
	}, nil
}

func (client *gcsclient) BucketObjects(bucketName string, prefix string) ([]string, error) {
	bucketObjects, err := client.getBucketObjects(bucketName, prefix)
	if err != nil {
		return []string{}, err
	}

	return bucketObjects, nil
}

func (client *gcsclient) ObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	isBucketVersioned, err := client.getBucketVersioning(bucketName)
	if err != nil {
		return []int64{}, err
	}

	if !isBucketVersioned {
		return []int64{}, errors.New("bucket is not versioned")
	}

	objectGenerations, err := client.getObjectGenerations(bucketName, objectPath)
	if err != nil {
		return []int64{}, err
	}

	return objectGenerations, nil
}

func (client *gcsclient) DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error {
	getCall := client.client.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	object, err := getCall.Do()
	if err != nil {
		return err
	}

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

	progress := client.newProgressBar(int64(object.Size))
	progress.Start()
	defer progress.Finish()

	// TODO
	_, err = getCall.Download()
	if err != nil {
		return err
	}

	return nil
}

func (client *gcsclient) UploadFile(bucketName string, objectPath string, localPath string) (int64, error) {
	stat, err := os.Stat(localPath)
	if err != nil {
		return 0, err
	}

	localFile, err := os.Open(localPath)
	if err != nil {
		return 0, err
	}
	defer localFile.Close()

	progress := client.newProgressBar(stat.Size())
	progress.Start()
	defer progress.Finish()

	object := &storage.Object{
		Name: objectPath,
	}

	uploadedObject, err := client.client.Objects.Insert(bucketName, object).Media(progress.NewProxyReader(localFile)).Do()
	if err != nil {
		return 0, err
	}

	return uploadedObject.Generation, nil
}

func (client *gcsclient) URL(bucketName string, objectPath string, generation int64) (string, error) {
	getCall := client.client.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	object, err := getCall.Do()
	if err != nil {
		return "", err
	}

	var url string
	if object.MediaLink != "" {
		url = object.MediaLink
	} else {
		if generation != 0 {
			url = fmt.Sprintf("gs://%s/%s#%d", bucketName, objectPath, generation)
		} else {
			url = fmt.Sprintf("gs://%s/%s", bucketName, objectPath)
		}
	}

	return url, nil
}

func (client *gcsclient) DeleteObject(bucketName string, objectPath string, generation int64) error {
	deleteCall := client.client.Objects.Delete(bucketName, objectPath)
	if generation != 0 {
		deleteCall = deleteCall.Generation(generation)
	}

	err := deleteCall.Do()
	if err != nil {
		return err
	}

	return nil
}

func (client *gcsclient) getBucketObjects(bucketName string, prefix string) ([]string, error) {
	var bucketObjects []string

	pageToken := ""
	for {
		listCall := client.client.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(prefix)
		listCall = listCall.Versions(false)

		objects, err := listCall.Do()
		if err != nil {
			return bucketObjects, err
		}

		for _, object := range objects.Items {
			bucketObjects = append(bucketObjects, object.Name)
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return bucketObjects, nil
}

func (client *gcsclient) getBucketVersioning(bucketName string) (bool, error) {
	bucket, err := client.client.Buckets.Get(bucketName).Do()
	if err != nil {
		return false, err
	}

	return bucket.Versioning.Enabled, nil
}

func (client *gcsclient) getObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	var objectGenerations []int64

	pageToken := ""
	for {
		listCall := client.client.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(objectPath)
		listCall = listCall.Versions(true)

		objects, err := listCall.Do()
		if err != nil {
			return objectGenerations, err
		}

		for _, object := range objects.Items {
			if object.Name == objectPath {
				objectGenerations = append(objectGenerations, object.Generation)
			}
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return objectGenerations, nil
}

func (client *gcsclient) newProgressBar(total int64) *pb.ProgressBar {
	progress := pb.New64(total)

	progress.Output = client.progressOutput
	progress.ShowSpeed = true
	progress.Units = pb.U_BYTES
	progress.NotPrint = true

	return progress.SetWidth(80)
}
<commit_msg>Remove MediaLink from URL<commit_after>package gcsresource

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"os"

	"github.com/cheggaaa/pb"
	"golang.org/x/oauth2"
	oauthgoogle "golang.org/x/oauth2/google"
	"google.golang.org/api/storage/v1"
)

type GCSClient interface {
	BucketObjects(bucketName string, prefix string) ([]string, error)
	ObjectGenerations(bucketName string, objectPath string) ([]int64, error)
	DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error
	UploadFile(bucketName string, objectPath string, localPath string) (int64, error)
	URL(bucketName string, objectPath string, generation int64) (string, error)
	DeleteObject(bucketName string, objectPath string, generation int64) error
}

type gcsclient struct {
	client         *storage.Service
	progressOutput io.Writer
}

func NewGCSClient(
	progressOutput io.Writer,
	project string,
	jsonKey string,
) (GCSClient, error) {
	var err error
	var storageClient *http.Client
	var userAgent = "gcs-resource/0.0.1"

	if jsonKey != "" {
		storageJwtConf, err := oauthgoogle.JWTConfigFromJSON([]byte(jsonKey), storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
		storageClient = storageJwtConf.Client(oauth2.NoContext)
	} else {
		storageClient, err = oauthgoogle.DefaultClient(oauth2.NoContext, storage.DevstorageFullControlScope)
		if err != nil {
			return &gcsclient{}, err
		}
	}

	storageService, err := storage.New(storageClient)
	if err != nil {
		return &gcsclient{}, err
	}
	storageService.UserAgent = userAgent

	return &gcsclient{
		client:         storageService,
		progressOutput: progressOutput,
	}, nil
}

func (client *gcsclient) BucketObjects(bucketName string, prefix string) ([]string, error) {
	bucketObjects, err := client.getBucketObjects(bucketName, prefix)
	if err != nil {
		return []string{}, err
	}

	return bucketObjects, nil
}

func (client *gcsclient) ObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	isBucketVersioned, err := client.getBucketVersioning(bucketName)
	if err != nil {
		return []int64{}, err
	}

	if !isBucketVersioned {
		return []int64{}, errors.New("bucket is not versioned")
	}

	objectGenerations, err := client.getObjectGenerations(bucketName, objectPath)
	if err != nil {
		return []int64{}, err
	}

	return objectGenerations, nil
}

func (client *gcsclient) DownloadFile(bucketName string, objectPath string, generation int64, localPath string) error {
	getCall := client.client.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	object, err := getCall.Do()
	if err != nil {
		return err
	}

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

	progress := client.newProgressBar(int64(object.Size))
	progress.Start()
	defer progress.Finish()

	// TODO
	_, err = getCall.Download()
	if err != nil {
		return err
	}

	return nil
}

func (client *gcsclient) UploadFile(bucketName string, objectPath string, localPath string) (int64, error) {
	stat, err := os.Stat(localPath)
	if err != nil {
		return 0, err
	}

	localFile, err := os.Open(localPath)
	if err != nil {
		return 0, err
	}
	defer localFile.Close()

	progress := client.newProgressBar(stat.Size())
	progress.Start()
	defer progress.Finish()

	object := &storage.Object{
		Name: objectPath,
	}

	uploadedObject, err := client.client.Objects.Insert(bucketName, object).Media(progress.NewProxyReader(localFile)).Do()
	if err != nil {
		return 0, err
	}

	return uploadedObject.Generation, nil
}

func (client *gcsclient) URL(bucketName string, objectPath string, generation int64) (string, error) {
	getCall := client.client.Objects.Get(bucketName, objectPath)
	if generation != 0 {
		getCall = getCall.Generation(generation)
	}

	_, err := getCall.Do()
	if err != nil {
		return "", err
	}

	var url string
	if generation != 0 {
		url = fmt.Sprintf("gs://%s/%s#%d", bucketName, objectPath, generation)
	} else {
		url = fmt.Sprintf("gs://%s/%s", bucketName, objectPath)
	}

	return url, nil
}

func (client *gcsclient) DeleteObject(bucketName string, objectPath string, generation int64) error {
	deleteCall := client.client.Objects.Delete(bucketName, objectPath)
	if generation != 0 {
		deleteCall = deleteCall.Generation(generation)
	}

	err := deleteCall.Do()
	if err != nil {
		return err
	}

	return nil
}

func (client *gcsclient) getBucketObjects(bucketName string, prefix string) ([]string, error) {
	var bucketObjects []string

	pageToken := ""
	for {
		listCall := client.client.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(prefix)
		listCall = listCall.Versions(false)

		objects, err := listCall.Do()
		if err != nil {
			return bucketObjects, err
		}

		for _, object := range objects.Items {
			bucketObjects = append(bucketObjects, object.Name)
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return bucketObjects, nil
}

func (client *gcsclient) getBucketVersioning(bucketName string) (bool, error) {
	bucket, err := client.client.Buckets.Get(bucketName).Do()
	if err != nil {
		return false, err
	}

	return bucket.Versioning.Enabled, nil
}

func (client *gcsclient) getObjectGenerations(bucketName string, objectPath string) ([]int64, error) {
	var objectGenerations []int64

	pageToken := ""
	for {
		listCall := client.client.Objects.List(bucketName)
		listCall = listCall.PageToken(pageToken)
		listCall = listCall.Prefix(objectPath)
		listCall = listCall.Versions(true)

		objects, err := listCall.Do()
		if err != nil {
			return objectGenerations, err
		}

		for _, object := range objects.Items {
			if object.Name == objectPath {
				objectGenerations = append(objectGenerations, object.Generation)
			}
		}

		if objects.NextPageToken != "" {
			pageToken = objects.NextPageToken
		} else {
			break
		}
	}

	return objectGenerations, nil
}

func (client *gcsclient) newProgressBar(total int64) *pb.ProgressBar {
	progress := pb.New64(total)

	progress.Output = client.progressOutput
	progress.ShowSpeed = true
	progress.Units = pb.U_BYTES
	progress.NotPrint = true

	return progress.SetWidth(80)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/GoogleCloudPlatform/compute-user-accounts/apiclient"
	"github.com/GoogleCloudPlatform/compute-user-accounts/logger"
	"github.com/GoogleCloudPlatform/compute-user-accounts/server"
	"github.com/GoogleCloudPlatform/compute-user-accounts/store"
)

const majorVersion = "0"

var (
	// version is set at compile time.
	version          string
	userAgent        = fmt.Sprintf("gcua/%v", version)
	refreshFrequency = 4 * time.Minute
	refreshCooldown  = 5 * time.Second
	keyExpiration    = 30 * time.Minute
	keyCooldown      = 500 * time.Millisecond

	apiBase      = flag.String("computeaccounts", "https://www.googleapis.com/computeaccounts/alpha/", "the URL to the base of the computeaccounts API")
	instanceBase = flag.String("compute", "https://www.googleapis.com/compute/v1/", "the URL to the base of the compute API")
)

func main() {
	flag.Parse()
	logger.Info("Starting daemon.")
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)
	api, err := apiclient.New(&apiclient.Config{
		APIBase:      *apiBase,
		InstanceBase: *instanceBase,
		UserAgent:    userAgent,
	})
	if err != nil {
		logger.Fatalf("Init failed: %v.", err)
	}
	srv := &server.Server{store.New(api, &store.Config{
		RefreshFrequency: refreshFrequency,
		RefreshCooldown:  refreshCooldown,
		KeyExpiration:    keyExpiration,
		KeyCooldown:      keyCooldown,
	})}
	go func() {
		err := srv.Serve()
		logger.Fatalf("Server failed: %v.", err)
	}()

	for {
		select {
		case sig := <-interrupt:
			logger.Fatalf("Got interrupt: %v.", sig)
		}
	}
}
<commit_msg>Removing unused majorVersion constant.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/GoogleCloudPlatform/compute-user-accounts/apiclient"
	"github.com/GoogleCloudPlatform/compute-user-accounts/logger"
	"github.com/GoogleCloudPlatform/compute-user-accounts/server"
	"github.com/GoogleCloudPlatform/compute-user-accounts/store"
)

var (
	// version is set at compile time.
	version          string
	userAgent        = fmt.Sprintf("gcua/%v", version)
	refreshFrequency = 4 * time.Minute
	refreshCooldown  = 5 * time.Second
	keyExpiration    = 30 * time.Minute
	keyCooldown      = 500 * time.Millisecond

	apiBase      = flag.String("computeaccounts", "https://www.googleapis.com/computeaccounts/alpha/", "the URL to the base of the computeaccounts API")
	instanceBase = flag.String("compute", "https://www.googleapis.com/compute/v1/", "the URL to the base of the compute API")
)

func main() {
	flag.Parse()
	logger.Info("Starting daemon.")
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)
	api, err := apiclient.New(&apiclient.Config{
		APIBase:      *apiBase,
		InstanceBase: *instanceBase,
		UserAgent:    userAgent,
	})
	if err != nil {
		logger.Fatalf("Init failed: %v.", err)
	}
	srv := &server.Server{store.New(api, &store.Config{
		RefreshFrequency: refreshFrequency,
		RefreshCooldown:  refreshCooldown,
		KeyExpiration:    keyExpiration,
		KeyCooldown:      keyCooldown,
	})}
	go func() {
		err := srv.Serve()
		logger.Fatalf("Server failed: %v.", err)
	}()

	for {
		select {
		case sig := <-interrupt:
			logger.Fatalf("Got interrupt: %v.", sig)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 options

import (
	"strings"
	"time"

	"github.com/spf13/pflag"

	versionedinformers "k8s.io/client-go/informers"
	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	"k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
	authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
)

type BuiltInAuthorizationOptions struct {
	Mode                        string
	PolicyFile                  string
	WebhookConfigFile           string
	WebhookCacheAuthorizedTTL   time.Duration
	WebhookCacheUnauthorizedTTL time.Duration
}

func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
	return &BuiltInAuthorizationOptions{
		Mode: authzmodes.ModeAlwaysAllow,
		WebhookCacheAuthorizedTTL:   5 * time.Minute,
		WebhookCacheUnauthorizedTTL: 30 * time.Second,
	}
}

func (s *BuiltInAuthorizationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+
		"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+
		strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")

	fs.StringVar(&s.PolicyFile, "authorization-policy-file", s.PolicyFile, ""+
		"File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.")

	fs.StringVar(&s.WebhookConfigFile, "authorization-webhook-config-file", s.WebhookConfigFile, ""+
		"File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
		"The API server will query the remote service to determine access on the API server's secure port.")

	fs.DurationVar(&s.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl",
		s.WebhookCacheAuthorizedTTL,
		"The duration to cache 'authorized' responses from the webhook authorizer.")

	fs.DurationVar(&s.WebhookCacheUnauthorizedTTL,
		"authorization-webhook-cache-unauthorized-ttl", s.WebhookCacheUnauthorizedTTL,
		"The duration to cache 'unauthorized' responses from the webhook authorizer.")

	fs.String("authorization-rbac-super-user", "", ""+
		"If specified, a username which avoids RBAC authorization checks and role binding "+
		"privilege escalation checks, to be used with --authorization-mode=RBAC.")
	fs.MarkDeprecated("authorization-rbac-super-user", "Removed during alpha to beta.  The 'system:masters' group has privileged access.")

}

func (s *BuiltInAuthorizationOptions) Modes() []string {
	modes := []string{}
	if len(s.Mode) > 0 {
		modes = strings.Split(s.Mode, ",")
	}
	return modes
}

func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory, versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.AuthorizationConfig {
	return authorizer.AuthorizationConfig{
		AuthorizationModes:          s.Modes(),
		PolicyFile:                  s.PolicyFile,
		WebhookConfigFile:           s.WebhookConfigFile,
		WebhookCacheAuthorizedTTL:   s.WebhookCacheAuthorizedTTL,
		WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL,
		InformerFactory:             informerFactory,
		VersionedInformerFactory:    versionedInformerFactory,
	}
}
<commit_msg>Remove deprecated paramter "authorization-rbac-super-user"<commit_after>/*
Copyright 2016 The Kubernetes 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 options

import (
	"strings"
	"time"

	"github.com/spf13/pflag"

	versionedinformers "k8s.io/client-go/informers"
	informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
	"k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
	authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
)

type BuiltInAuthorizationOptions struct {
	Mode                        string
	PolicyFile                  string
	WebhookConfigFile           string
	WebhookCacheAuthorizedTTL   time.Duration
	WebhookCacheUnauthorizedTTL time.Duration
}

func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
	return &BuiltInAuthorizationOptions{
		Mode: authzmodes.ModeAlwaysAllow,
		WebhookCacheAuthorizedTTL:   5 * time.Minute,
		WebhookCacheUnauthorizedTTL: 30 * time.Second,
	}
}

func (s *BuiltInAuthorizationOptions) Validate() []error {
	allErrors := []error{}
	return allErrors
}

func (s *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
	fs.StringVar(&s.Mode, "authorization-mode", s.Mode, ""+
		"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+
		strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")

	fs.StringVar(&s.PolicyFile, "authorization-policy-file", s.PolicyFile, ""+
		"File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.")

	fs.StringVar(&s.WebhookConfigFile, "authorization-webhook-config-file", s.WebhookConfigFile, ""+
		"File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
		"The API server will query the remote service to determine access on the API server's secure port.")

	fs.DurationVar(&s.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl",
		s.WebhookCacheAuthorizedTTL,
		"The duration to cache 'authorized' responses from the webhook authorizer.")

	fs.DurationVar(&s.WebhookCacheUnauthorizedTTL,
		"authorization-webhook-cache-unauthorized-ttl", s.WebhookCacheUnauthorizedTTL,
		"The duration to cache 'unauthorized' responses from the webhook authorizer.")
}

func (s *BuiltInAuthorizationOptions) Modes() []string {
	modes := []string{}
	if len(s.Mode) > 0 {
		modes = strings.Split(s.Mode, ",")
	}
	return modes
}

func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory, versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.AuthorizationConfig {
	return authorizer.AuthorizationConfig{
		AuthorizationModes:          s.Modes(),
		PolicyFile:                  s.PolicyFile,
		WebhookConfigFile:           s.WebhookConfigFile,
		WebhookCacheAuthorizedTTL:   s.WebhookCacheAuthorizedTTL,
		WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL,
		InformerFactory:             informerFactory,
		VersionedInformerFactory:    versionedInformerFactory,
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes 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 runtimeclass

import (
	"context"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/apiserver/pkg/registry/rest"
	"k8s.io/apiserver/pkg/storage/names"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/apis/node"
	"k8s.io/kubernetes/pkg/apis/node/validation"
)

// strategy implements verification logic for RuntimeClass.
type strategy struct {
	runtime.ObjectTyper
	names.NameGenerator
}

// Strategy is the default logic that applies when creating and updating RuntimeClass objects.
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}

// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy

// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy

// NamespaceScoped is false for RuntimeClasses
func (strategy) NamespaceScoped() bool {
	return false
}

// AllowCreateOnUpdate is true for RuntimeClasses.
func (strategy) AllowCreateOnUpdate() bool {
	return true
}

// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
	_ = obj.(*node.RuntimeClass)
}

// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
	newRuntimeClass := obj.(*node.RuntimeClass)
	oldRuntimeClass := old.(*node.RuntimeClass)

	_, _ = newRuntimeClass, oldRuntimeClass
}

// Validate validates a new RuntimeClass. Validation must check for a correct signature.
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
	runtimeClass := obj.(*node.RuntimeClass)
	return validation.ValidateRuntimeClass(runtimeClass)
}

// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
	_ = obj.(*node.RuntimeClass)
}

// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
	newObj := obj.(*node.RuntimeClass)
	errorList := validation.ValidateRuntimeClass(newObj)
	return append(errorList, validation.ValidateRuntimeClassUpdate(newObj, old.(*node.RuntimeClass))...)
}

// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
	return false
}
<commit_msg>pod overhead: drop from RuntimeClass base on feature-gate<commit_after>/*
Copyright 2019 The Kubernetes 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 runtimeclass

import (
	"context"

	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/validation/field"
	"k8s.io/apiserver/pkg/registry/rest"
	"k8s.io/apiserver/pkg/storage/names"
	utilfeature "k8s.io/apiserver/pkg/util/feature"
	"k8s.io/kubernetes/pkg/api/legacyscheme"
	"k8s.io/kubernetes/pkg/apis/node"
	"k8s.io/kubernetes/pkg/apis/node/validation"
	"k8s.io/kubernetes/pkg/features"
)

// strategy implements verification logic for RuntimeClass.
type strategy struct {
	runtime.ObjectTyper
	names.NameGenerator
}

// Strategy is the default logic that applies when creating and updating RuntimeClass objects.
var Strategy = strategy{legacyscheme.Scheme, names.SimpleNameGenerator}

// Strategy should implement rest.RESTCreateStrategy
var _ rest.RESTCreateStrategy = Strategy

// Strategy should implement rest.RESTUpdateStrategy
var _ rest.RESTUpdateStrategy = Strategy

// NamespaceScoped is false for RuntimeClasses
func (strategy) NamespaceScoped() bool {
	return false
}

// AllowCreateOnUpdate is true for RuntimeClasses.
func (strategy) AllowCreateOnUpdate() bool {
	return true
}

// PrepareForCreate clears fields that are not allowed to be set by end users
// on creation.
func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
	rc := obj.(*node.RuntimeClass)

	if !utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) && rc != nil {
		// Set Overhead to nil only if the feature is disabled and it is not used
		rc.Overhead = nil
	}
}

// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
	newRuntimeClass := obj.(*node.RuntimeClass)
	oldRuntimeClass := old.(*node.RuntimeClass)

	_, _ = newRuntimeClass, oldRuntimeClass
}

// Validate validates a new RuntimeClass. Validation must check for a correct signature.
func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
	runtimeClass := obj.(*node.RuntimeClass)
	return validation.ValidateRuntimeClass(runtimeClass)
}

// Canonicalize normalizes the object after validation.
func (strategy) Canonicalize(obj runtime.Object) {
	_ = obj.(*node.RuntimeClass)
}

// ValidateUpdate is the default update validation for an end user.
func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
	newObj := obj.(*node.RuntimeClass)
	errorList := validation.ValidateRuntimeClass(newObj)
	return append(errorList, validation.ValidateRuntimeClassUpdate(newObj, old.(*node.RuntimeClass))...)
}

// If AllowUnconditionalUpdate() is true and the object specified by
// the user does not have a resource version, then generic Update()
// populates it with the latest version. Else, it checks that the
// version specified by the user matches the version of latest etcd
// object.
func (strategy) AllowUnconditionalUpdate() bool {
	return false
}
<|endoftext|>


<commit_before>package alerting

import (
	"context"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

type conditionStub struct {
	firing   bool
	operator string
	matches  []*EvalMatch
}

func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) {
	return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator}, nil
}

func TestAlertingExecutor(t *testing.T) {
	Convey("Test alert execution", t, func() {
		handler := NewEvalHandler()

		Convey("Show return triggered with single passing condition", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{&conditionStub{
					firing: true,
				}},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "true = true")
		})

		Convey("Show return false with not passing asdf", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, matches: []*EvalMatch{&EvalMatch{}, &EvalMatch{}}},
					&conditionStub{firing: false},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[true AND false] = false")
		})

		Convey("Show return true if any of the condition is passing with OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[true OR false] = true")
		})

		Convey("Show return false if any of the condition is failing with AND operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[true AND false] = false")
		})

		Convey("Show return true if one condition is failing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[[true AND true] OR false] = true")
		})

		Convey("Show return false if one condition is passing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[[true AND false] OR false] = false")
		})

		Convey("Show return false if a condition is failing with nested AND operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
					&conditionStub{firing: true, operator: "and"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[[true AND false] AND true] = false")
		})

		Convey("Show return true if a condition is passing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
					&conditionStub{firing: true, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true")
		})
	})
}
<commit_msg>test(alerting): fixes broken unit test<commit_after>package alerting

import (
	"context"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

type conditionStub struct {
	firing   bool
	operator string
	matches  []*EvalMatch
}

func (c *conditionStub) Eval(context *EvalContext) (*ConditionResult, error) {
	return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator}, nil
}

func TestAlertingExecutor(t *testing.T) {
	Convey("Test alert execution", t, func() {
		handler := NewEvalHandler()

		Convey("Show return triggered with single passing condition", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{&conditionStub{
					firing: true,
				}},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "true = true")
		})

		Convey("Show return false with not passing asdf", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and", matches: []*EvalMatch{&EvalMatch{}, &EvalMatch{}}},
					&conditionStub{firing: false, operator: "and"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[true AND false] = false")
		})

		Convey("Show return true if any of the condition is passing with OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[true OR false] = true")
		})

		Convey("Show return false if any of the condition is failing with AND operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[true AND false] = false")
		})

		Convey("Show return true if one condition is failing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[[true AND true] OR false] = true")
		})

		Convey("Show return false if one condition is passing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[[true AND false] OR false] = false")
		})

		Convey("Show return false if a condition is failing with nested AND operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "and"},
					&conditionStub{firing: true, operator: "and"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, false)
			So(context.ConditionEvals, ShouldEqual, "[[true AND false] AND true] = false")
		})

		Convey("Show return true if a condition is passing with nested OR operator", func() {
			context := NewEvalContext(context.TODO(), &Rule{
				Conditions: []Condition{
					&conditionStub{firing: true, operator: "and"},
					&conditionStub{firing: false, operator: "or"},
					&conditionStub{firing: true, operator: "or"},
				},
			})

			handler.Eval(context)
			So(context.Firing, ShouldEqual, true)
			So(context.ConditionEvals, ShouldEqual, "[[true OR false] OR true] = true")
		})
	})
}
<|endoftext|>


<commit_before>package goinsta

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/url"
	"strings"
)

const (
	volatileSeed = "12345"
)

func generateMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func generateHMAC(text, key string) string {
	hasher := hmac.New(sha256.New, []byte(key))
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func generateDeviceID(seed string) string {
	hash := generateMD5Hash(seed + volatileSeed)
	return "android-" + hash[:16]
}

func generateUUID(replace bool) string {
	uuid := make([]byte, 16)
	io.ReadFull(rand.Reader, uuid)
	uuid[8] = uuid[8]&^0xc0 | 0x80
	uuid[6] = uuid[6]&^0xf0 | 0x40

	tUUID := fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])

	if replace {
		return strings.Replace(tUUID, "-", "", -1)
	}
	return tUUID
}

func generateSignature(data string) map[string]string {
	return map[string]string{
		"ig_sig_key_version": fmt.Sprintf(
			"%s&signed_body=%s.%s",
			goInstaSigKeyVersion,
			generateHMAC(data, goInstaIGSigKey),
			url.QueryEscape(data),
		),
	}
}
<commit_msg>Made easier generators<commit_after>package goinsta

import (
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"net/url"
	"strings"
)

const (
	volatileSeed = "12345"
)

func generateMD5Hash(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

func generateHMAC(text, key []byte) string {
	hasher := hmac.New(sha256.New, key)
	hasher.Write(text)
	return hex.EncodeToString(hasher.Sum(nil))
}

func generateDeviceID(seed string) string {
	hash := generateMD5Hash(seed + volatileSeed)
	return "android-" + hash[:16]
}

func generateUUID(replace bool) string {
	uuid := make([]byte, 16)
	io.ReadFull(rand.Reader, uuid)
	uuid[8] = uuid[8]&^0xc0 | 0x80
	uuid[6] = uuid[6]&^0xf0 | 0x40

	tUUID := fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])

	if replace {
		return strings.Replace(tUUID, "-", "", -1)
	}
	return tUUID
}

func generateSignature(data []byte) map[string]string {
	return map[string]string{
		"ig_sig_key_version": fmt.Sprintf(
			"%s&signed_body=%s.%s",
			goInstaSigKeyVersion,
			generateHMAC(data, []byte(goInstaIGSigKey)),
			url.QueryEscape(b2s(data)),
		),
	}
}
<|endoftext|>


<commit_before>package siad

// wallet.go contains things like signatures and scans the blockchain for
// available funds that can be spent.

import (
	"errors"
	"os"

	"github.com/NebulousLabs/Andromeda/encoding"
	"github.com/NebulousLabs/Andromeda/siacore"
	"github.com/NebulousLabs/Andromeda/signatures"
)

// Contains a secret key, the spend conditions associated with that key, the
// address associated with those spend conditions, and a list of outputs that
// the wallet knows how to spend.
type Wallet struct {
	state *siacore.State

	SecretKey       signatures.SecretKey
	SpendConditions siacore.SpendConditions

	OwnedOutputs map[siacore.OutputID]struct{} // A list of outputs spendable by this wallet.
	SpentOutputs map[siacore.OutputID]struct{} // A list of outputs spent by this wallet which may not yet be in the blockchain.
}

// Most of the parameters are already in the file contract, but what's not
// specified is how much of the ContractFund comes from the client, and how
// much comes from the host. This specifies how much the client is to add to
// the contract.
type FileContractParameters struct {
	Transaction        siacore.Transaction
	FileContractIndex  int
	ClientContribution siacore.Currency
}

// Creates a new wallet that can receive and spend coins.
func CreateWallet() (w *Wallet, err error) {
	w = new(Wallet)

	var pk signatures.PublicKey
	w.SecretKey, pk, err = signatures.GenerateKeyPair()
	w.SpendConditions.PublicKeys = append(w.SpendConditions.PublicKeys, pk)
	w.SpendConditions.NumSignatures = 1

	w.OwnedOutputs = make(map[siacore.OutputID]struct{})
	w.SpentOutputs = make(map[siacore.OutputID]struct{})

	return
}

// Scans all unspent transactions and adds the ones that are spendable by this
// wallet.
func (w *Wallet) Scan() {
	w.OwnedOutputs = make(map[siacore.OutputID]struct{})

	// Check for owned outputs from the standard SpendConditions.
	scanAddresses := make(map[siacore.CoinAddress]struct{})
	scanAddresses[w.SpendConditions.CoinAddress()] = struct{}{}

	// Get the matching set of outputs and add them to the OwnedOutputs map.
	outputs := w.state.ScanOutputs(scanAddresses)
	for _, output := range outputs {
		w.OwnedOutputs[output] = struct{}{}
	}
}

// fundTransaction() adds `amount` Currency to the inputs, creating a refund
// output for any excess.
func (w *Wallet) FundTransaction(amount siacore.Currency, t *siacore.Transaction) (err error) {
	// Check that a nonzero amount of coins is being sent.
	if amount == siacore.Currency(0) {
		err = errors.New("cannot send 0 coins")
		return
	}

	// Add to the list of inputs until enough funds have been allocated.
	total := siacore.Currency(0)
	var newInputs []siacore.Input
	for id, _ := range w.OwnedOutputs {
		// Check that the output has not already been assigned somewhere else.
		_, exists := w.SpentOutputs[id]
		if exists {
			continue
		}

		// Check that the output exists.
		var output siacore.Output
		output, err = w.state.Output(id)
		if err != nil {
			continue
		}

		// Create an input to add to the transaction.
		newInput := siacore.Input{
			OutputID:        id,
			SpendConditions: w.SpendConditions,
		}
		newInputs = append(newInputs, newInput)

		// Add the value of the output to the total and see if we've hit a
		// sufficient amount.
		total += output.Value
		if total >= amount {
			break
		}
	}

	// Check that the sum of the inputs is sufficient to complete the
	// transaction.
	if total < amount {
		err = errors.New("insufficient funds")
		return
	}

	// Add all of the inputs to the transaction.
	t.Inputs = append(t.Inputs, newInputs...)

	// Add all of the inputs to the spent outputs map.
	for _, input := range newInputs {
		w.SpentOutputs[input.OutputID] = struct{}{}
	}

	// Add a refund output to the transaction if needed.
	if total-amount > 0 {
		t.Outputs = append(t.Outputs, siacore.Output{Value: total - amount, SpendHash: w.SpendConditions.CoinAddress()})
	}

	return
}

// Wallet.signTransaction() takes a transaction and adds a signature for every input
// that the wallet understands how to spend.
func (w *Wallet) SignTransaction(t *siacore.Transaction) (err error) {
	for i, input := range t.Inputs {
		// If we recognize the input as something we are able to sign, we sign
		// the input.
		if input.SpendConditions.CoinAddress() == w.SpendConditions.CoinAddress() {
			txnSig := siacore.TransactionSignature{
				InputID: input.OutputID,
			}
			t.Signatures = append(t.Signatures, txnSig)

			sigHash := t.SigHash(i)
			t.Signatures[i].Signature, err = signatures.SignBytes(sigHash[:], w.SecretKey)
			if err != nil {
				return
			}
		}
	}

	return
}

// Wallet.SpendCoins creates a transaction sending 'amount' to 'address', and
// allocateding 'minerFee' as a miner fee. The transaction is submitted to the
// miner pool, but is also returned.
func (e *Environment) SpendCoins(amount, minerFee siacore.Currency, address siacore.CoinAddress) (t siacore.Transaction, err error) {
	// Scan blockchain for outputs.
	e.wallet.Scan()

	// Add `amount` of free coins to the transaction.
	err = e.wallet.FundTransaction(amount+minerFee, &t)
	if err != nil {
		return
	}

	// Add the miner fee.
	t.MinerFees = append(t.MinerFees, minerFee)

	// Add the output to `address`.
	t.Outputs = append(t.Outputs, siacore.Output{Value: amount, SpendHash: address})

	// Sign each input.
	err = e.wallet.SignTransaction(&t)
	if err != nil {
		return
	}

	// TODO: AcceptTransaction shoul be piped through a channel.
	err = e.wallet.state.AcceptTransaction(t)
	if err != nil {
		return
	}

	return
}

// Environment.CoinAddress returns the CoinAddress which foreign coins should
// be sent to.
func (e *Environment) CoinAddress() siacore.CoinAddress {
	return e.wallet.SpendConditions.CoinAddress()
}

// SaveCoinAddress saves the address of the wallet used within the environment.
func (e *Environment) SaveCoinAddress(filename string) (err error) {
	pubKeyBytes := encoding.Marshal(e.wallet.SpendConditions.CoinAddress())

	// Open the file and write the key to the filename.
	file, err := os.Create(filename)
	if err != nil {
		return
	}
	defer file.Close()
	_, err = file.Write(pubKeyBytes)
	if err != nil {
		return
	}

	return
}

func (e *Environment) LoadCoinAddress(filename string, friendName string) (err error) {
	// Open the file and read the key to a friend map.
	return
}

func (e *Environment) SaveSecretKey(filename string) (err error) {
	return
}

func (e *Environment) LoadSecretKey(filename string) (err error) {
	return
}
<commit_msg>implement envrionment friend list<commit_after>package siad

// wallet.go contains things like signatures and scans the blockchain for
// available funds that can be spent.

import (
	"errors"
	"os"

	"github.com/NebulousLabs/Andromeda/encoding"
	"github.com/NebulousLabs/Andromeda/siacore"
	"github.com/NebulousLabs/Andromeda/signatures"
)

// Contains a secret key, the spend conditions associated with that key, the
// address associated with those spend conditions, and a list of outputs that
// the wallet knows how to spend.
type Wallet struct {
	state *siacore.State

	SecretKey       signatures.SecretKey
	SpendConditions siacore.SpendConditions

	OwnedOutputs map[siacore.OutputID]struct{} // A list of outputs spendable by this wallet.
	SpentOutputs map[siacore.OutputID]struct{} // A list of outputs spent by this wallet which may not yet be in the blockchain.
}

// Most of the parameters are already in the file contract, but what's not
// specified is how much of the ContractFund comes from the client, and how
// much comes from the host. This specifies how much the client is to add to
// the contract.
type FileContractParameters struct {
	Transaction        siacore.Transaction
	FileContractIndex  int
	ClientContribution siacore.Currency
}

// Creates a new wallet that can receive and spend coins.
func CreateWallet() (w *Wallet, err error) {
	w = new(Wallet)

	var pk signatures.PublicKey
	w.SecretKey, pk, err = signatures.GenerateKeyPair()
	w.SpendConditions.PublicKeys = append(w.SpendConditions.PublicKeys, pk)
	w.SpendConditions.NumSignatures = 1

	w.OwnedOutputs = make(map[siacore.OutputID]struct{})
	w.SpentOutputs = make(map[siacore.OutputID]struct{})

	return
}

// Scans all unspent transactions and adds the ones that are spendable by this
// wallet.
func (w *Wallet) Scan() {
	w.OwnedOutputs = make(map[siacore.OutputID]struct{})

	// Check for owned outputs from the standard SpendConditions.
	scanAddresses := make(map[siacore.CoinAddress]struct{})
	scanAddresses[w.SpendConditions.CoinAddress()] = struct{}{}

	// Get the matching set of outputs and add them to the OwnedOutputs map.
	outputs := w.state.ScanOutputs(scanAddresses)
	for _, output := range outputs {
		w.OwnedOutputs[output] = struct{}{}
	}
}

// fundTransaction() adds `amount` Currency to the inputs, creating a refund
// output for any excess.
func (w *Wallet) FundTransaction(amount siacore.Currency, t *siacore.Transaction) (err error) {
	// Check that a nonzero amount of coins is being sent.
	if amount == siacore.Currency(0) {
		err = errors.New("cannot send 0 coins")
		return
	}

	// Add to the list of inputs until enough funds have been allocated.
	total := siacore.Currency(0)
	var newInputs []siacore.Input
	for id, _ := range w.OwnedOutputs {
		// Check that the output has not already been assigned somewhere else.
		_, exists := w.SpentOutputs[id]
		if exists {
			continue
		}

		// Check that the output exists.
		var output siacore.Output
		output, err = w.state.Output(id)
		if err != nil {
			continue
		}

		// Create an input to add to the transaction.
		newInput := siacore.Input{
			OutputID:        id,
			SpendConditions: w.SpendConditions,
		}
		newInputs = append(newInputs, newInput)

		// Add the value of the output to the total and see if we've hit a
		// sufficient amount.
		total += output.Value
		if total >= amount {
			break
		}
	}

	// Check that the sum of the inputs is sufficient to complete the
	// transaction.
	if total < amount {
		err = errors.New("insufficient funds")
		return
	}

	// Add all of the inputs to the transaction.
	t.Inputs = append(t.Inputs, newInputs...)

	// Add all of the inputs to the spent outputs map.
	for _, input := range newInputs {
		w.SpentOutputs[input.OutputID] = struct{}{}
	}

	// Add a refund output to the transaction if needed.
	if total-amount > 0 {
		t.Outputs = append(t.Outputs, siacore.Output{Value: total - amount, SpendHash: w.SpendConditions.CoinAddress()})
	}

	return
}

// Wallet.signTransaction() takes a transaction and adds a signature for every input
// that the wallet understands how to spend.
func (w *Wallet) SignTransaction(t *siacore.Transaction) (err error) {
	for i, input := range t.Inputs {
		// If we recognize the input as something we are able to sign, we sign
		// the input.
		if input.SpendConditions.CoinAddress() == w.SpendConditions.CoinAddress() {
			txnSig := siacore.TransactionSignature{
				InputID: input.OutputID,
			}
			t.Signatures = append(t.Signatures, txnSig)

			sigHash := t.SigHash(i)
			t.Signatures[i].Signature, err = signatures.SignBytes(sigHash[:], w.SecretKey)
			if err != nil {
				return
			}
		}
	}

	return
}

// Wallet.SpendCoins creates a transaction sending 'amount' to 'address', and
// allocateding 'minerFee' as a miner fee. The transaction is submitted to the
// miner pool, but is also returned.
func (e *Environment) SpendCoins(amount, minerFee siacore.Currency, address siacore.CoinAddress) (t siacore.Transaction, err error) {
	// Scan blockchain for outputs.
	e.wallet.Scan()

	// Add `amount` of free coins to the transaction.
	err = e.wallet.FundTransaction(amount+minerFee, &t)
	if err != nil {
		return
	}

	// Add the miner fee.
	t.MinerFees = append(t.MinerFees, minerFee)

	// Add the output to `address`.
	t.Outputs = append(t.Outputs, siacore.Output{Value: amount, SpendHash: address})

	// Sign each input.
	err = e.wallet.SignTransaction(&t)
	if err != nil {
		return
	}

	// TODO: AcceptTransaction shoul be piped through a channel.
	err = e.wallet.state.AcceptTransaction(t)
	if err != nil {
		return
	}

	return
}

// Environment.CoinAddress returns the CoinAddress which foreign coins should
// be sent to.
func (e *Environment) CoinAddress() siacore.CoinAddress {
	return e.wallet.SpendConditions.CoinAddress()
}

// SaveCoinAddress saves the address of the wallet used within the environment.
func (e *Environment) SaveCoinAddress(filename string) (err error) {
	pubKeyBytes := encoding.Marshal(e.wallet.SpendConditions.CoinAddress())

	// Open the file and write the key to the filename.
	file, err := os.Create(filename)
	if err != nil {
		return
	}
	defer file.Close()
	_, err = file.Write(pubKeyBytes)
	if err != nil {
		return
	}

	return
}

// LoadCoinAddress loads a coin address from a file and adds that address to
// the friend list using the input name. An error is returned if the name is
// already in the friend list.
func (e *Environment) LoadCoinAddress(filename string, friendName string) (err error) {
	// Open the file and read the key to a friend map.
	file, err := os.Open(filename)
	if err != nil {
		return
	}
	defer file.Close()

	// Read the contents of the file into a buffer.
	buffer := make([]byte, 256)
	bytes, err := file.Read(buffer)
	if err != nil {
		return
	}

	// Decode the bytes into an address.
	var address siacore.CoinAddress
	err = encoding.Unmarshal(buffer[:bytes], &address)
	if err != nil {
		return
	}

	// Add the address to the friends list.
	e.friends[friendName] = address

	return
}

func (e *Environment) SaveSecretKey(filename string) (err error) {
	return
}

func (e *Environment) LoadSecretKey(filename string) (err error) {
	return
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/opsworks"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

// These tests assume the existence of predefined Opsworks IAM roles named `aws-opsworks-ec2-role`
// and `aws-opsworks-service-role`.

func TestAccAWSOpsworksCustomLayer(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsOpsworksCustomLayerDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAwsOpsworksCustomLayerConfigCreate,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "auto_assign_elastic_ips", "false",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "auto_healing", "true",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "true",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "300",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
					),
				),
			},
			resource.TestStep{
				Config: testAccAwsOpsworksCustomLayerConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "false",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "120",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "3",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "3",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.4101929740", "subversion",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.type", "io1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.number_of_disks", "4",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.mount_point", "/var",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.size", "100",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.raid_level", "1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.iops", "3000",
					),
				),
			},
		},
	})
}

func testAccCheckAwsOpsworksCustomLayerDestroy(s *terraform.State) error {
	opsworksconn := testAccProvider.Meta().(*AWSClient).opsworksconn
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_opsworks_custom_layer" {
			continue
		}
		req := &opsworks.DescribeLayersInput{
			LayerIds: []*string{
				aws.String(rs.Primary.ID),
			},
		}

		_, err := opsworksconn.DescribeLayers(req)
		if err != nil {
			if awserr, ok := err.(awserr.Error); ok {
				if awserr.Code() == "ResourceNotFoundException" {
					// not found, good to go
					return nil
				}
			}
			return err
		}
	}

	return fmt.Errorf("Fall through error on OpsWorks custom layer test")
}

var testAccAwsOpsworksCustomLayerSecurityGroups = `
resource "aws_security_group" "tf-ops-acc-layer1" {
  name = "tf-ops-acc-layer1"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_security_group" "tf-ops-acc-layer2" {
  name = "tf-ops-acc-layer2"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
`

var testAccAwsOpsworksCustomLayerConfigCreate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
provider "aws" {
	region = "us-east-1"
}

resource "aws_opsworks_custom_layer" "tf-acc" {
  stack_id = "${aws_opsworks_stack.tf-acc.id}"
  name = "tf-ops-acc-custom-layer"
  short_name = "tf-ops-acc-custom-layer"
  auto_assign_public_ips = true
  custom_security_group_ids = [
    "${aws_security_group.tf-ops-acc-layer1.id}",
    "${aws_security_group.tf-ops-acc-layer2.id}",
  ]
  drain_elb_on_shutdown = true
  instance_shutdown_timeout = 300
  system_packages = [
    "git",
    "golang",
  ]
  ebs_volume {
    type = "gp2"
    number_of_disks = 2
    mount_point = "/home"
    size = 100
    raid_level = 0
  }
}
`

var testAccAwsOpsworksCustomLayerConfigUpdate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
resource "aws_security_group" "tf-ops-acc-layer3" {
  name = "tf-ops-acc-layer3"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_opsworks_custom_layer" "tf-acc" {
  stack_id = "${aws_opsworks_stack.tf-acc.id}"
  name = "tf-ops-acc-custom-layer"
  short_name = "tf-ops-acc-custom-layer"
  auto_assign_public_ips = true
  custom_security_group_ids = [
    "${aws_security_group.tf-ops-acc-layer1.id}",
    "${aws_security_group.tf-ops-acc-layer2.id}",
    "${aws_security_group.tf-ops-acc-layer3.id}",
  ]
  drain_elb_on_shutdown = false
  instance_shutdown_timeout = 120
  system_packages = [
    "git",
    "golang",
    "subversion",
  ]
  ebs_volume {
    type = "gp2"
    number_of_disks = 2
    mount_point = "/home"
    size = 100
    raid_level = 0
  }
  ebs_volume {
    type = "io1"
    number_of_disks = 4
    mount_point = "/var"
    size = 100
    raid_level = 1
    iops = 3000
  }
}
`
<commit_msg>provider/aws: Fix SG leak in opsworks custom layer test<commit_after>package aws

import (
	"fmt"
	"testing"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/opsworks"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

// These tests assume the existence of predefined Opsworks IAM roles named `aws-opsworks-ec2-role`
// and `aws-opsworks-service-role`.

func TestAccAWSOpsworksCustomLayer(t *testing.T) {
	resource.Test(t, resource.TestCase{
		PreCheck:     func() { testAccPreCheck(t) },
		Providers:    testAccProviders,
		CheckDestroy: testAccCheckAwsOpsworksCustomLayerDestroy,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccAwsOpsworksCustomLayerConfigCreate,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "auto_assign_elastic_ips", "false",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "auto_healing", "true",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "true",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "300",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
					),
				),
			},
			resource.TestStep{
				Config: testAccAwsOpsworksCustomLayerConfigUpdate,
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "name", "tf-ops-acc-custom-layer",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "drain_elb_on_shutdown", "false",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "instance_shutdown_timeout", "120",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "custom_security_group_ids.#", "3",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.#", "3",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.1368285564", "git",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.2937857443", "golang",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "system_packages.4101929740", "subversion",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.#", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.type", "gp2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.number_of_disks", "2",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.mount_point", "/home",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.3575749636.size", "100",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.type", "io1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.number_of_disks", "4",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.mount_point", "/var",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.size", "100",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.raid_level", "1",
					),
					resource.TestCheckResourceAttr(
						"aws_opsworks_custom_layer.tf-acc", "ebs_volume.1266957920.iops", "3000",
					),
				),
			},
		},
	})
}

func testAccCheckAwsOpsworksCustomLayerDestroy(s *terraform.State) error {
	opsworksconn := testAccProvider.Meta().(*AWSClient).opsworksconn
	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_opsworks_custom_layer" {
			continue
		}
		req := &opsworks.DescribeLayersInput{
			LayerIds: []*string{
				aws.String(rs.Primary.ID),
			},
		}

		_, err := opsworksconn.DescribeLayers(req)
		if err != nil {
			if awserr, ok := err.(awserr.Error); ok {
				if awserr.Code() == "ResourceNotFoundException" {
					// not found, good to go
					return nil
				}
			}
			return err
		}
	}

	return fmt.Errorf("Fall through error on OpsWorks custom layer test")
}

var testAccAwsOpsworksCustomLayerSecurityGroups = `
resource "aws_security_group" "tf-ops-acc-layer1" {
  name = "tf-ops-acc-layer1"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_security_group" "tf-ops-acc-layer2" {
  name = "tf-ops-acc-layer2"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
`

var testAccAwsOpsworksCustomLayerConfigCreate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
provider "aws" {
	region = "us-east-1"
}

resource "aws_opsworks_custom_layer" "tf-acc" {
  stack_id = "${aws_opsworks_stack.tf-acc.id}"
  name = "tf-ops-acc-custom-layer"
  short_name = "tf-ops-acc-custom-layer"
  auto_assign_public_ips = true
  custom_security_group_ids = [
    "${aws_security_group.tf-ops-acc-layer1.id}",
    "${aws_security_group.tf-ops-acc-layer2.id}",
  ]
  drain_elb_on_shutdown = true
  instance_shutdown_timeout = 300
  system_packages = [
    "git",
    "golang",
  ]
  ebs_volume {
    type = "gp2"
    number_of_disks = 2
    mount_point = "/home"
    size = 100
    raid_level = 0
  }
}
`

var testAccAwsOpsworksCustomLayerConfigUpdate = testAccAwsOpsworksStackConfigNoVpcCreate + testAccAwsOpsworksCustomLayerSecurityGroups + `
provider "aws" {
  region = "us-east-1"
}

resource "aws_security_group" "tf-ops-acc-layer3" {
  name = "tf-ops-acc-layer3"
  ingress {
    from_port = 8
    to_port = -1
    protocol = "icmp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
resource "aws_opsworks_custom_layer" "tf-acc" {
  stack_id = "${aws_opsworks_stack.tf-acc.id}"
  name = "tf-ops-acc-custom-layer"
  short_name = "tf-ops-acc-custom-layer"
  auto_assign_public_ips = true
  custom_security_group_ids = [
    "${aws_security_group.tf-ops-acc-layer1.id}",
    "${aws_security_group.tf-ops-acc-layer2.id}",
    "${aws_security_group.tf-ops-acc-layer3.id}",
  ]
  drain_elb_on_shutdown = false
  instance_shutdown_timeout = 120
  system_packages = [
    "git",
    "golang",
    "subversion",
  ]
  ebs_volume {
    type = "gp2"
    number_of_disks = 2
    mount_point = "/home"
    size = 100
    raid_level = 0
  }
  ebs_volume {
    type = "io1"
    number_of_disks = 4
    mount_point = "/var"
    size = 100
    raid_level = 1
    iops = 3000
  }
}
`
<|endoftext|>


<commit_before>package regionagogo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"

	"bufio"
	"os"

	"github.com/Workiva/go-datastructures/augmentedtree"
	"github.com/golang/geo/s2"
	"github.com/golang/protobuf/proto"
	"github.com/kpawlik/geojson"
)

const (
	mininumViableLevel = 3 // the minimum cell level we accept
)

// GeoSearch provides in memory storage and query engine for region lookup
type GeoSearch struct {
	augmentedtree.Tree
	rm    map[int64]Region
	Debug bool
}

// Regions a slice of *Region (type used mainly to return one GeoJSON of the regions)
type Regions []*Region

// Region is region for memory use
// it contains an S2 loop and the associated metadata
type Region struct {
	Data map[string]string `json:"data"`
	Loop *s2.Loop          `json:"-"`
}

// NewGeoSearch
func NewGeoSearch() *GeoSearch {
	gs := &GeoSearch{
		Tree: augmentedtree.New(1),
		rm:   make(map[int64]Region),
	}

	return gs
}

// ImportGeoData loads geodata file into a map loopID->Region
// fills the segment tree for fast lookup
func (gs *GeoSearch) ImportGeoData(filename string) error {
	var gd GeoDataStorage

	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	err = proto.Unmarshal(b, &gd)
	if err != nil {
		return err
	}

	for loopID, r := range gd.Rs {
		var points []s2.Point

		for _, c := range r.Points {
			ll := s2.LatLngFromDegrees(float64(c.Lat), float64(c.Lng))
			point := s2.PointFromLatLng(ll)
			points = append(points, point)
		}
		// add 1st point as last point to close the shape
		points = append(points, points[0])

		// load the loops into memory
		l := s2.LoopFromPoints(points)
		if l.IsEmpty() || l.IsFull() {
			return errors.New("invalid loop")
		}
		gs.rm[int64(loopID)] = Region{Data: r.Data, Loop: l}

	}

	// load the cell ranges into the tree
	for _, cLoop := range gd.Cl {
		gs.Add(&S2Interval{CellID: s2.CellID(cLoop.Cell), LoopIDs: cLoop.Loops})
	}

	// free some space
	gd.Cl = nil
	log.Println("loaded", len(gs.rm), "regions")

	return nil
}

// Query returns the country for the corresponding lat, lng point
func (gs *GeoSearch) StubbingQuery(lat, lng float64) *Region {
	q := s2.CellIDFromLatLng(s2.LatLngFromDegrees(lat, lng))
	i := &S2Interval{CellID: q}
	r := gs.Tree.Query(i)

	matchLoopID := int64(-1)

	for _, itv := range r {
		sitv := itv.(*S2Interval)
		if gs.Debug {
			fmt.Println("found", sitv, sitv.LoopIDs)
		}

		// a region can include a smaller region
		// return only the one that is contained in the other
		for _, loopID := range sitv.LoopIDs {
			if gs.rm[loopID].Loop.ContainsPoint(q.Point()) {

				if matchLoopID == -1 {
					matchLoopID = loopID
				} else {
					foundLoop := gs.rm[loopID].Loop
					previousLoop := gs.rm[matchLoopID].Loop

					// we take the 1st vertex of the foundloop if it is contained in previousLoop
					// foundLoop one is more precise
					if previousLoop.ContainsPoint(foundLoop.Vertex(0)) {
						matchLoopID = loopID
					}
				}
			}
		}
	}

	if matchLoopID != -1 {
		region := gs.rm[matchLoopID]
		return &region
	}

	return nil
}

// importGeoJSONFile will load a geo json and save the polygons into
// a msgpack file named geodata
// fields to lookup for in GeoJSON
func ImportGeoJSONFile(filename string, debug bool, fields []string) error {
	var loopID int64

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

	var geo geojson.FeatureCollection
	r := bufio.NewReader(fi)

	d := json.NewDecoder(r)
	if err := d.Decode(&geo); err != nil {
		return err
	}

	var geoData GeoDataStorage

	cl := make(map[s2.CellID][]int64)

	for _, f := range geo.Features {
		geom, err := f.GetGeometry()
		if err != nil {
			return err
		}

		rc := &s2.RegionCoverer{MinLevel: 1, MaxLevel: 30, MaxCells: 8}

		switch geom.GetType() {
		case "Polygon":
			mp := geom.(*geojson.Polygon)
			// multipolygon
			for _, p := range mp.Coordinates {
				// polygon
				var points []s2.Point
				var cpoints []*CPoint
				// For type "MultiPolygon", the "coordinates" member must be an array of Polygon coordinate arrays.
				// "Polygon", the "coordinates" member must be an array of LinearRing coordinate arrays.
				// For Polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.

				// reverse the slice
				for i := len(p)/2 - 1; i >= 0; i-- {
					opp := len(p) - 1 - i
					p[i], p[opp] = p[opp], p[i]
				}

				for i, c := range p {
					ll := s2.LatLngFromDegrees(float64(c[1]), float64(c[0]))
					point := s2.PointFromLatLng(ll)
					points = append(points, point)
					// do not add cpoint on storage (first point is last point)
					if i == len(p)-1 {
						break
					}
					cpoints = append(cpoints, &CPoint{Lat: float32(c[1]), Lng: float32(c[0])})
				}

				l := LoopRegionFromPoints(points)

				if l.IsEmpty() || l.IsFull() {
					log.Println("invalid loop")
					continue
				}

				covering := rc.Covering(l)

				data := make(map[string]string)
				for _, field := range fields {
					if v, ok := f.Properties[field].(string); !ok {
						log.Println("can't find field on", f.Properties)
					} else {
						data[field] = v
					}
				}

				cu := make([]uint64, len(covering))
				var invalidLoop bool

				for i, v := range covering {
					// added a security there if the level is too high it probably means the polygon is bogus
					// this to avoid a large cell to cover everything
					if v.Level() < mininumViableLevel {
						log.Print("cell level too big", v.Level(), loopID)
						invalidLoop = true
					}
					cu[i] = uint64(v)
				}

				// do not insert big loop
				if invalidLoop {
					break
				}

				if debug {
					fmt.Println("import", loopID, data)
				}

				rs := &RegionStorage{
					Data:      data,
					Points:    cpoints,
					Cellunion: cu,
				}

				geoData.Rs = append(geoData.Rs, rs)

				for _, cell := range covering {
					cl[cell] = append(cl[cell], loopID)
				}

				loopID = loopID + 1
			}

		case "MultiPolygon":
			mp := geom.(*geojson.MultiPolygon)
			// multipolygon
			for _, m := range mp.Coordinates {
				// polygon
				var points []s2.Point
				var cpoints []*CPoint
				// For type "MultiPolygon", the "coordinates" member must be an array of Polygon coordinate arrays.
				// "Polygon", the "coordinates" member must be an array of LinearRing coordinate arrays.
				// For Polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.

				if len(m) < 1 {
					continue
				}

				p := m[0]
				// coordinates

				// reverse the slice
				for i := len(p)/2 - 1; i >= 0; i-- {
					opp := len(p) - 1 - i
					p[i], p[opp] = p[opp], p[i]
				}

				for i, c := range p {
					ll := s2.LatLngFromDegrees(float64(c[1]), float64(c[0]))
					point := s2.PointFromLatLng(ll)
					points = append(points, point)
					// do not add cpoint on storage (first point is last point)
					if i == len(p)-1 {
						break
					}
					cpoints = append(cpoints, &CPoint{Lat: float32(c[1]), Lng: float32(c[0])})
				}

				// TODO: parallelized region cover
				l := LoopRegionFromPoints(points)

				if l.IsEmpty() || l.IsFull() {
					log.Println("invalid loop")
					continue
				}

				covering := rc.Covering(l)

				data := make(map[string]string)
				for _, field := range fields {
					if v, ok := f.Properties[field].(string); !ok {
						log.Println("can't find field on", f.Properties)
					} else {
						data[field] = v
					}
				}

				cu := make([]uint64, len(covering))
				var invalidLoop bool

				for i, v := range covering {
					// added a security there if the level is too high it probably means the polygon is bogus
					// this to avoid a large cell to cover everything
					if v.Level() < mininumViableLevel {
						log.Print("cell level too big", v.Level(), loopID)
						invalidLoop = true
					}
					cu[i] = uint64(v)
				}

				// do not insert big loop
				if invalidLoop {
					break
				}

				if debug {
					fmt.Println("import", loopID, data)
				}

				rs := &RegionStorage{
					Data:      data,
					Points:    cpoints,
					Cellunion: cu,
				}

				geoData.Rs = append(geoData.Rs, rs)

				for _, cell := range covering {
					cl[cell] = append(cl[cell], loopID)
				}

				loopID = loopID + 1
			}
		default:
			return errors.New("unknown type")
		}

	}

	for k, v := range cl {
		geoData.Cl = append(geoData.Cl, &CellIDLoopStorage{Cell: uint64(k), Loops: v})
	}

	log.Println("imported", filename, len(geoData.Rs), "regions")

	b, err := proto.Marshal(&geoData)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile("geodata", b, 0644)

	return nil
}
<commit_msg>prepare db query<commit_after>package regionagogo

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"

	"bufio"
	"os"

	"github.com/Workiva/go-datastructures/augmentedtree"
	"github.com/golang/geo/s2"
	"github.com/golang/protobuf/proto"
	"github.com/kpawlik/geojson"
)

const (
	mininumViableLevel = 3 // the minimum cell level we accept
)

// GeoSearch provides in memory storage and query engine for region lookup
type GeoSearch struct {
	augmentedtree.Tree
	rm    map[int64]Region
	Debug bool
}

// Regions a slice of *Region (type used mainly to return one GeoJSON of the regions)
type Regions []*Region

// Region is region for memory use
// it contains an S2 loop and the associated metadata
type Region struct {
	Data map[string]string `json:"data"`
	Loop *s2.Loop          `json:"-"`
}

// NewGeoSearch
func NewGeoSearch() *GeoSearch {
	gs := &GeoSearch{
		Tree: augmentedtree.New(1),
		rm:   make(map[int64]Region),
	}

	return gs
}

// ImportGeoData loads geodata file into a map loopID->Region
// fills the segment tree for fast lookup
func (gs *GeoSearch) ImportGeoData(filename string) error {
	var gd GeoDataStorage

	b, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	err = proto.Unmarshal(b, &gd)
	if err != nil {
		return err
	}

	for loopID, r := range gd.Rs {
		var points []s2.Point

		for _, c := range r.Points {
			ll := s2.LatLngFromDegrees(float64(c.Lat), float64(c.Lng))
			point := s2.PointFromLatLng(ll)
			points = append(points, point)
		}
		// add 1st point as last point to close the shape
		points = append(points, points[0])

		// load the loops into memory
		l := s2.LoopFromPoints(points)
		if l.IsEmpty() || l.IsFull() {
			return errors.New("invalid loop")
		}
		gs.rm[int64(loopID)] = Region{Data: r.Data, Loop: l}

	}

	// load the cell ranges into the tree
	for _, cLoop := range gd.Cl {
		gs.Add(&S2Interval{CellID: s2.CellID(cLoop.Cell), LoopIDs: cLoop.Loops})
	}

	// free some space
	gd.Cl = nil
	log.Println("loaded", len(gs.rm), "regions")

	return nil
}

// TODO: refactor as Fence ?
func (gs *GeoSearch) RegionByID(loopID int64) *Region {
	if l, ok := gs.rm[loopID]; ok {
		return &l
	}
	return nil
}

// Query returns the country for the corresponding lat, lng point
func (gs *GeoSearch) StubbingQuery(lat, lng float64) *Region {
	q := s2.CellIDFromLatLng(s2.LatLngFromDegrees(lat, lng))
	i := &S2Interval{CellID: q}
	r := gs.Tree.Query(i)

	matchLoopID := int64(-1)

	for _, itv := range r {
		sitv := itv.(*S2Interval)
		if gs.Debug {
			fmt.Println("found", sitv, sitv.LoopIDs)
		}

		// a region can include a smaller region
		// return only the one that is contained in the other
		for _, loopID := range sitv.LoopIDs {
			region := gs.RegionByID(loopID)
			if region.Loop.ContainsPoint(q.Point()) {

				if matchLoopID == -1 {
					matchLoopID = loopID
				} else {
					foundLoop := region.Loop
					previousLoop := gs.rm[matchLoopID].Loop

					// we take the 1st vertex of the foundloop if it is contained in previousLoop
					// foundLoop one is more precise
					if previousLoop.ContainsPoint(foundLoop.Vertex(0)) {
						matchLoopID = loopID
					}
				}
			}
		}
	}

	if matchLoopID != -1 {
		region := gs.rm[matchLoopID]
		return &region
	}

	return nil
}

// importGeoJSONFile will load a geo json and save the polygons into
// a msgpack file named geodata
// fields to lookup for in GeoJSON
func ImportGeoJSONFile(filename string, debug bool, fields []string) error {
	var loopID int64

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

	var geo geojson.FeatureCollection
	r := bufio.NewReader(fi)

	d := json.NewDecoder(r)
	if err := d.Decode(&geo); err != nil {
		return err
	}

	var geoData GeoDataStorage

	cl := make(map[s2.CellID][]int64)

	for _, f := range geo.Features {
		geom, err := f.GetGeometry()
		if err != nil {
			return err
		}

		rc := &s2.RegionCoverer{MinLevel: 1, MaxLevel: 30, MaxCells: 8}

		switch geom.GetType() {
		case "Polygon":
			mp := geom.(*geojson.Polygon)
			// multipolygon
			for _, p := range mp.Coordinates {
				// polygon
				var points []s2.Point
				var cpoints []*CPoint
				// For type "MultiPolygon", the "coordinates" member must be an array of Polygon coordinate arrays.
				// "Polygon", the "coordinates" member must be an array of LinearRing coordinate arrays.
				// For Polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.

				// reverse the slice
				for i := len(p)/2 - 1; i >= 0; i-- {
					opp := len(p) - 1 - i
					p[i], p[opp] = p[opp], p[i]
				}

				for i, c := range p {
					ll := s2.LatLngFromDegrees(float64(c[1]), float64(c[0]))
					point := s2.PointFromLatLng(ll)
					points = append(points, point)
					// do not add cpoint on storage (first point is last point)
					if i == len(p)-1 {
						break
					}
					cpoints = append(cpoints, &CPoint{Lat: float32(c[1]), Lng: float32(c[0])})
				}

				l := LoopRegionFromPoints(points)

				if l.IsEmpty() || l.IsFull() {
					log.Println("invalid loop")
					continue
				}

				covering := rc.Covering(l)

				data := make(map[string]string)
				for _, field := range fields {
					if v, ok := f.Properties[field].(string); !ok {
						log.Println("can't find field on", f.Properties)
					} else {
						data[field] = v
					}
				}

				cu := make([]uint64, len(covering))
				var invalidLoop bool

				for i, v := range covering {
					// added a security there if the level is too high it probably means the polygon is bogus
					// this to avoid a large cell to cover everything
					if v.Level() < mininumViableLevel {
						log.Print("cell level too big", v.Level(), loopID)
						invalidLoop = true
					}
					cu[i] = uint64(v)
				}

				// do not insert big loop
				if invalidLoop {
					break
				}

				if debug {
					fmt.Println("import", loopID, data)
				}

				rs := &RegionStorage{
					Data:      data,
					Points:    cpoints,
					Cellunion: cu,
				}

				geoData.Rs = append(geoData.Rs, rs)

				for _, cell := range covering {
					cl[cell] = append(cl[cell], loopID)
				}

				loopID = loopID + 1
			}

		case "MultiPolygon":
			mp := geom.(*geojson.MultiPolygon)
			// multipolygon
			for _, m := range mp.Coordinates {
				// polygon
				var points []s2.Point
				var cpoints []*CPoint
				// For type "MultiPolygon", the "coordinates" member must be an array of Polygon coordinate arrays.
				// "Polygon", the "coordinates" member must be an array of LinearRing coordinate arrays.
				// For Polygons with multiple rings, the first must be the exterior ring and any others must be interior rings or holes.

				if len(m) < 1 {
					continue
				}

				p := m[0]
				// coordinates

				// reverse the slice
				for i := len(p)/2 - 1; i >= 0; i-- {
					opp := len(p) - 1 - i
					p[i], p[opp] = p[opp], p[i]
				}

				for i, c := range p {
					ll := s2.LatLngFromDegrees(float64(c[1]), float64(c[0]))
					point := s2.PointFromLatLng(ll)
					points = append(points, point)
					// do not add cpoint on storage (first point is last point)
					if i == len(p)-1 {
						break
					}
					cpoints = append(cpoints, &CPoint{Lat: float32(c[1]), Lng: float32(c[0])})
				}

				// TODO: parallelized region cover
				l := LoopRegionFromPoints(points)

				if l.IsEmpty() || l.IsFull() {
					log.Println("invalid loop")
					continue
				}

				covering := rc.Covering(l)

				data := make(map[string]string)
				for _, field := range fields {
					if v, ok := f.Properties[field].(string); !ok {
						log.Println("can't find field on", f.Properties)
					} else {
						data[field] = v
					}
				}

				cu := make([]uint64, len(covering))
				var invalidLoop bool

				for i, v := range covering {
					// added a security there if the level is too high it probably means the polygon is bogus
					// this to avoid a large cell to cover everything
					if v.Level() < mininumViableLevel {
						log.Print("cell level too big", v.Level(), loopID)
						invalidLoop = true
					}
					cu[i] = uint64(v)
				}

				// do not insert big loop
				if invalidLoop {
					break
				}

				if debug {
					fmt.Println("import", loopID, data)
				}

				rs := &RegionStorage{
					Data:      data,
					Points:    cpoints,
					Cellunion: cu,
				}

				geoData.Rs = append(geoData.Rs, rs)

				for _, cell := range covering {
					cl[cell] = append(cl[cell], loopID)
				}

				loopID = loopID + 1
			}
		default:
			return errors.New("unknown type")
		}

	}

	for k, v := range cl {
		geoData.Cl = append(geoData.Cl, &CellIDLoopStorage{Cell: uint64(k), Loops: v})
	}

	log.Println("imported", filename, len(geoData.Rs), "regions")

	b, err := proto.Marshal(&geoData)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile("geodata", b, 0644)

	return nil
}
<|endoftext|>