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

package do

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"strconv"
	"time"

	"github.com/coreos/pkg/capnslog"
	"github.com/digitalocean/godo"
	"golang.org/x/crypto/ssh"
	"golang.org/x/oauth2"

	"github.com/coreos/mantle/auth"
	"github.com/coreos/mantle/platform"
	"github.com/coreos/mantle/util"
)

var (
	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "platform/api/do")
)

type Options struct {
	*platform.Options

	// Config file. Defaults to $HOME/.config/digitalocean.json.
	ConfigPath string
	// Profile name
	Profile string
	// Personal access token (overrides config profile)
	AccessToken string

	// Region slug (e.g. "sfo2")
	Region string
	// Droplet size slug (e.g. "512mb")
	Size string
	// Numeric image ID, {alpha, beta, stable}, or user image name
	Image string
}

type API struct {
	c     *godo.Client
	opts  *Options
	image godo.DropletCreateImage
}

func New(opts *Options) (*API, error) {
	if opts.AccessToken == "" {
		profiles, err := auth.ReadDOConfig(opts.ConfigPath)
		if err != nil {
			return nil, fmt.Errorf("couldn't read DigitalOcean config: %v", err)
		}

		if opts.Profile == "" {
			opts.Profile = "default"
		}
		profile, ok := profiles[opts.Profile]
		if !ok {
			return nil, fmt.Errorf("no such profile %q", opts.Profile)
		}
		if opts.AccessToken == "" {
			opts.AccessToken = profile.AccessToken
		}
	}

	ctx := context.TODO()
	client := godo.NewClient(oauth2.NewClient(ctx, &tokenSource{opts.AccessToken}))

	a := &API{
		c:    client,
		opts: opts,
	}

	var err error
	a.image, err = a.resolveImage(ctx, opts.Image)
	if err != nil {
		return nil, err
	}

	return a, nil
}

func (a *API) resolveImage(ctx context.Context, imageSpec string) (godo.DropletCreateImage, error) {
	// try numeric image ID first
	imageID, err := strconv.Atoi(imageSpec)
	if err == nil {
		return godo.DropletCreateImage{ID: imageID}, nil
	}

	// handle magic values
	switch imageSpec {
	case "":
		// pick the most conservative default
		imageSpec = "stable"
		fallthrough
	case "alpha", "beta", "stable":
		return godo.DropletCreateImage{Slug: "coreos-" + imageSpec}, nil
	}

	// resolve to user image ID
	image, err := a.GetUserImage(ctx, imageSpec, true)
	if err == nil {
		return godo.DropletCreateImage{ID: image.ID}, nil
	}

	return godo.DropletCreateImage{}, fmt.Errorf("couldn't resolve image %q in %v", imageSpec, a.opts.Region)
}

func (a *API) PreflightCheck(ctx context.Context) error {
	_, _, err := a.c.Account.Get(ctx)
	if err != nil {
		return fmt.Errorf("querying account: %v", err)
	}
	return nil
}

func (a *API) CreateDroplet(ctx context.Context, name string, sshKeyID int, userdata string) (*godo.Droplet, error) {
	var droplet *godo.Droplet
	var err error
	// DO frequently gives us 422 errors saying "Please try again"
	err = util.RetryConditional(6, 10*time.Second, shouldRetry, func() error {
		droplet, _, err = a.c.Droplets.Create(ctx, &godo.DropletCreateRequest{
			Name:              name,
			Region:            a.opts.Region,
			Size:              a.opts.Size,
			Image:             a.image,
			SSHKeys:           []godo.DropletCreateSSHKey{{ID: sshKeyID}},
			IPv6:              true,
			PrivateNetworking: true,
			UserData:          userdata,
			Tags:              []string{"mantle"},
		})
		if err != nil {
			plog.Errorf("Error creating droplet: %v. Retrying...", err)
		}
		return err
	})
	if err != nil {
		return nil, fmt.Errorf("couldn't create droplet: %v", err)
	}
	dropletID := droplet.ID

	err = util.WaitUntilReady(5*time.Minute, 10*time.Second, func() (bool, error) {
		var err error
		// update droplet in closure
		droplet, _, err = a.c.Droplets.Get(ctx, dropletID)
		if err != nil {
			return false, err
		}
		return droplet.Status == "active", nil
	})
	if err != nil {
		a.DeleteDroplet(ctx, dropletID)
		return nil, fmt.Errorf("waiting for droplet to run: %v", err)
	}

	return droplet, nil
}

func (a *API) listDropletsWithTag(ctx context.Context, tag string) ([]godo.Droplet, error) {
	page := godo.ListOptions{
		Page:    1,
		PerPage: 200,
	}
	var ret []godo.Droplet
	for {
		droplets, _, err := a.c.Droplets.ListByTag(ctx, tag, &page)
		if err != nil {
			return nil, err
		}
		ret = append(ret, droplets...)
		if len(droplets) < page.PerPage {
			return ret, nil
		}
		page.Page += 1
	}
}

func (a *API) GetDroplet(ctx context.Context, dropletID int) (*godo.Droplet, error) {
	droplet, _, err := a.c.Droplets.Get(ctx, dropletID)
	if err != nil {
		return nil, err
	}
	return droplet, nil
}

// SnapshotDroplet creates a snapshot of a droplet and waits until complete.
// The Snapshot API doesn't return the snapshot ID, so we don't either.
func (a *API) SnapshotDroplet(ctx context.Context, dropletID int, name string) error {
	action, _, err := a.c.DropletActions.Snapshot(ctx, dropletID, name)
	if err != nil {
		return err
	}
	actionID := action.ID

	err = util.WaitUntilReady(30*time.Minute, 15*time.Second, func() (bool, error) {
		action, _, err := a.c.Actions.Get(ctx, actionID)
		if err != nil {
			return false, err
		}
		switch action.Status {
		case "in-progress":
			return false, nil
		case "completed":
			return true, nil
		default:
			return false, fmt.Errorf("snapshot failed")
		}
	})
	if err != nil {
		return err
	}

	return nil
}

func (a *API) DeleteDroplet(ctx context.Context, dropletID int) error {
	_, err := a.c.Droplets.Delete(ctx, dropletID)
	if err != nil {
		return fmt.Errorf("deleting droplet %d: %v", dropletID, err)
	}
	return nil
}

func (a *API) GetUserImage(ctx context.Context, imageName string, inRegion bool) (*godo.Image, error) {
	var ret *godo.Image
	var regionMessage string
	if inRegion {
		regionMessage = fmt.Sprintf(" in %v", a.opts.Region)
	}
	page := godo.ListOptions{
		Page:    1,
		PerPage: 200,
	}
	for {
		images, _, err := a.c.Images.ListUser(ctx, &page)
		if err != nil {
			return nil, err
		}
		for _, image := range images {
			image := image
			if image.Name != imageName {
				continue
			}
			for _, region := range image.Regions {
				if inRegion && region != a.opts.Region {
					continue
				}
				if ret != nil {
					return nil, fmt.Errorf("found multiple images named %q%s", imageName, regionMessage)
				}
				ret = &image
				break
			}
		}
		if len(images) < page.PerPage {
			break
		}
		page.Page += 1
	}

	if ret == nil {
		return nil, fmt.Errorf("couldn't find image %q%s", imageName, regionMessage)
	}
	return ret, nil
}

func (a *API) DeleteImage(ctx context.Context, imageID int) error {
	_, err := a.c.Images.Delete(ctx, imageID)
	if err != nil {
		return fmt.Errorf("deleting image %d: %v", imageID, err)
	}
	return nil
}

func (a *API) AddKey(ctx context.Context, name, key string) (int, error) {
	sshKey, _, err := a.c.Keys.Create(ctx, &godo.KeyCreateRequest{
		Name:      name,
		PublicKey: key,
	})
	if err != nil {
		return 0, fmt.Errorf("couldn't create SSH key: %v", err)
	}
	return sshKey.ID, nil
}

func (a *API) DeleteKey(ctx context.Context, keyID int) error {
	_, err := a.c.Keys.DeleteByID(ctx, keyID)
	if err != nil {
		return fmt.Errorf("couldn't delete SSH key: %v", err)
	}
	return nil
}

// GenerateFakeKey generates a SSH key pair, returns the public key, and
// discards the private key. This is useful for droplets that don't need a
// public key, since DO insists on requiring one.
func GenerateFakeKey() (string, error) {
	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return "", err
	}
	sshKey, err := ssh.NewPublicKey(&rsaKey.PublicKey)
	if err != nil {
		return "", err
	}
	return string(ssh.MarshalAuthorizedKey(sshKey)), nil
}

func (a *API) GC(ctx context.Context, gracePeriod time.Duration) error {
	threshold := time.Now().Add(-gracePeriod)

	droplets, err := a.listDropletsWithTag(ctx, "mantle")
	if err != nil {
		return fmt.Errorf("listing droplets: %v", err)
	}
	for _, droplet := range droplets {
		if droplet.Status == "archive" {
			continue
		}

		created, err := time.Parse(time.RFC3339, droplet.Created)
		if err != nil {
			return fmt.Errorf("couldn't parse %q: %v", droplet.Created, err)
		}
		if created.After(threshold) {
			continue
		}

		if err := a.DeleteDroplet(ctx, droplet.ID); err != nil {
			return fmt.Errorf("couldn't delete droplet %d: %v", droplet.ID, err)
		}
	}
	return nil
}

type tokenSource struct {
	token string
}

func (t *tokenSource) Token() (*oauth2.Token, error) {
	return &oauth2.Token{
		AccessToken: t.token,
	}, nil
}

// shouldRetry returns if the error is from DigitalOcean and we should
// retry the request which generated it
func shouldRetry(err error) bool {
	errResp, ok := err.(*godo.ErrorResponse)
	if !ok {
		return false
	}
	status := errResp.Response.StatusCode
	return status == 422 || status >= 500
}
<commit_msg>platform/api/do: rety for up to 5 min instead of 1<commit_after>// Copyright 2017 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package do

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"strconv"
	"time"

	"github.com/coreos/pkg/capnslog"
	"github.com/digitalocean/godo"
	"golang.org/x/crypto/ssh"
	"golang.org/x/oauth2"

	"github.com/coreos/mantle/auth"
	"github.com/coreos/mantle/platform"
	"github.com/coreos/mantle/util"
)

var (
	plog = capnslog.NewPackageLogger("github.com/coreos/mantle", "platform/api/do")
)

type Options struct {
	*platform.Options

	// Config file. Defaults to $HOME/.config/digitalocean.json.
	ConfigPath string
	// Profile name
	Profile string
	// Personal access token (overrides config profile)
	AccessToken string

	// Region slug (e.g. "sfo2")
	Region string
	// Droplet size slug (e.g. "512mb")
	Size string
	// Numeric image ID, {alpha, beta, stable}, or user image name
	Image string
}

type API struct {
	c     *godo.Client
	opts  *Options
	image godo.DropletCreateImage
}

func New(opts *Options) (*API, error) {
	if opts.AccessToken == "" {
		profiles, err := auth.ReadDOConfig(opts.ConfigPath)
		if err != nil {
			return nil, fmt.Errorf("couldn't read DigitalOcean config: %v", err)
		}

		if opts.Profile == "" {
			opts.Profile = "default"
		}
		profile, ok := profiles[opts.Profile]
		if !ok {
			return nil, fmt.Errorf("no such profile %q", opts.Profile)
		}
		if opts.AccessToken == "" {
			opts.AccessToken = profile.AccessToken
		}
	}

	ctx := context.TODO()
	client := godo.NewClient(oauth2.NewClient(ctx, &tokenSource{opts.AccessToken}))

	a := &API{
		c:    client,
		opts: opts,
	}

	var err error
	a.image, err = a.resolveImage(ctx, opts.Image)
	if err != nil {
		return nil, err
	}

	return a, nil
}

func (a *API) resolveImage(ctx context.Context, imageSpec string) (godo.DropletCreateImage, error) {
	// try numeric image ID first
	imageID, err := strconv.Atoi(imageSpec)
	if err == nil {
		return godo.DropletCreateImage{ID: imageID}, nil
	}

	// handle magic values
	switch imageSpec {
	case "":
		// pick the most conservative default
		imageSpec = "stable"
		fallthrough
	case "alpha", "beta", "stable":
		return godo.DropletCreateImage{Slug: "coreos-" + imageSpec}, nil
	}

	// resolve to user image ID
	image, err := a.GetUserImage(ctx, imageSpec, true)
	if err == nil {
		return godo.DropletCreateImage{ID: image.ID}, nil
	}

	return godo.DropletCreateImage{}, fmt.Errorf("couldn't resolve image %q in %v", imageSpec, a.opts.Region)
}

func (a *API) PreflightCheck(ctx context.Context) error {
	_, _, err := a.c.Account.Get(ctx)
	if err != nil {
		return fmt.Errorf("querying account: %v", err)
	}
	return nil
}

func (a *API) CreateDroplet(ctx context.Context, name string, sshKeyID int, userdata string) (*godo.Droplet, error) {
	var droplet *godo.Droplet
	var err error
	// DO frequently gives us 422 errors saying "Please try again". Retry every 10 seconds
	// for up to 5 min
	err = util.RetryConditional(5*6, 10*time.Second, shouldRetry, func() error {
		droplet, _, err = a.c.Droplets.Create(ctx, &godo.DropletCreateRequest{
			Name:              name,
			Region:            a.opts.Region,
			Size:              a.opts.Size,
			Image:             a.image,
			SSHKeys:           []godo.DropletCreateSSHKey{{ID: sshKeyID}},
			IPv6:              true,
			PrivateNetworking: true,
			UserData:          userdata,
			Tags:              []string{"mantle"},
		})
		if err != nil {
			plog.Errorf("Error creating droplet: %v. Retrying...", err)
		}
		return err
	})
	if err != nil {
		return nil, fmt.Errorf("couldn't create droplet: %v", err)
	}
	dropletID := droplet.ID

	err = util.WaitUntilReady(5*time.Minute, 10*time.Second, func() (bool, error) {
		var err error
		// update droplet in closure
		droplet, _, err = a.c.Droplets.Get(ctx, dropletID)
		if err != nil {
			return false, err
		}
		return droplet.Status == "active", nil
	})
	if err != nil {
		a.DeleteDroplet(ctx, dropletID)
		return nil, fmt.Errorf("waiting for droplet to run: %v", err)
	}

	return droplet, nil
}

func (a *API) listDropletsWithTag(ctx context.Context, tag string) ([]godo.Droplet, error) {
	page := godo.ListOptions{
		Page:    1,
		PerPage: 200,
	}
	var ret []godo.Droplet
	for {
		droplets, _, err := a.c.Droplets.ListByTag(ctx, tag, &page)
		if err != nil {
			return nil, err
		}
		ret = append(ret, droplets...)
		if len(droplets) < page.PerPage {
			return ret, nil
		}
		page.Page += 1
	}
}

func (a *API) GetDroplet(ctx context.Context, dropletID int) (*godo.Droplet, error) {
	droplet, _, err := a.c.Droplets.Get(ctx, dropletID)
	if err != nil {
		return nil, err
	}
	return droplet, nil
}

// SnapshotDroplet creates a snapshot of a droplet and waits until complete.
// The Snapshot API doesn't return the snapshot ID, so we don't either.
func (a *API) SnapshotDroplet(ctx context.Context, dropletID int, name string) error {
	action, _, err := a.c.DropletActions.Snapshot(ctx, dropletID, name)
	if err != nil {
		return err
	}
	actionID := action.ID

	err = util.WaitUntilReady(30*time.Minute, 15*time.Second, func() (bool, error) {
		action, _, err := a.c.Actions.Get(ctx, actionID)
		if err != nil {
			return false, err
		}
		switch action.Status {
		case "in-progress":
			return false, nil
		case "completed":
			return true, nil
		default:
			return false, fmt.Errorf("snapshot failed")
		}
	})
	if err != nil {
		return err
	}

	return nil
}

func (a *API) DeleteDroplet(ctx context.Context, dropletID int) error {
	_, err := a.c.Droplets.Delete(ctx, dropletID)
	if err != nil {
		return fmt.Errorf("deleting droplet %d: %v", dropletID, err)
	}
	return nil
}

func (a *API) GetUserImage(ctx context.Context, imageName string, inRegion bool) (*godo.Image, error) {
	var ret *godo.Image
	var regionMessage string
	if inRegion {
		regionMessage = fmt.Sprintf(" in %v", a.opts.Region)
	}
	page := godo.ListOptions{
		Page:    1,
		PerPage: 200,
	}
	for {
		images, _, err := a.c.Images.ListUser(ctx, &page)
		if err != nil {
			return nil, err
		}
		for _, image := range images {
			image := image
			if image.Name != imageName {
				continue
			}
			for _, region := range image.Regions {
				if inRegion && region != a.opts.Region {
					continue
				}
				if ret != nil {
					return nil, fmt.Errorf("found multiple images named %q%s", imageName, regionMessage)
				}
				ret = &image
				break
			}
		}
		if len(images) < page.PerPage {
			break
		}
		page.Page += 1
	}

	if ret == nil {
		return nil, fmt.Errorf("couldn't find image %q%s", imageName, regionMessage)
	}
	return ret, nil
}

func (a *API) DeleteImage(ctx context.Context, imageID int) error {
	_, err := a.c.Images.Delete(ctx, imageID)
	if err != nil {
		return fmt.Errorf("deleting image %d: %v", imageID, err)
	}
	return nil
}

func (a *API) AddKey(ctx context.Context, name, key string) (int, error) {
	sshKey, _, err := a.c.Keys.Create(ctx, &godo.KeyCreateRequest{
		Name:      name,
		PublicKey: key,
	})
	if err != nil {
		return 0, fmt.Errorf("couldn't create SSH key: %v", err)
	}
	return sshKey.ID, nil
}

func (a *API) DeleteKey(ctx context.Context, keyID int) error {
	_, err := a.c.Keys.DeleteByID(ctx, keyID)
	if err != nil {
		return fmt.Errorf("couldn't delete SSH key: %v", err)
	}
	return nil
}

// GenerateFakeKey generates a SSH key pair, returns the public key, and
// discards the private key. This is useful for droplets that don't need a
// public key, since DO insists on requiring one.
func GenerateFakeKey() (string, error) {
	rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return "", err
	}
	sshKey, err := ssh.NewPublicKey(&rsaKey.PublicKey)
	if err != nil {
		return "", err
	}
	return string(ssh.MarshalAuthorizedKey(sshKey)), nil
}

func (a *API) GC(ctx context.Context, gracePeriod time.Duration) error {
	threshold := time.Now().Add(-gracePeriod)

	droplets, err := a.listDropletsWithTag(ctx, "mantle")
	if err != nil {
		return fmt.Errorf("listing droplets: %v", err)
	}
	for _, droplet := range droplets {
		if droplet.Status == "archive" {
			continue
		}

		created, err := time.Parse(time.RFC3339, droplet.Created)
		if err != nil {
			return fmt.Errorf("couldn't parse %q: %v", droplet.Created, err)
		}
		if created.After(threshold) {
			continue
		}

		if err := a.DeleteDroplet(ctx, droplet.ID); err != nil {
			return fmt.Errorf("couldn't delete droplet %d: %v", droplet.ID, err)
		}
	}
	return nil
}

type tokenSource struct {
	token string
}

func (t *tokenSource) Token() (*oauth2.Token, error) {
	return &oauth2.Token{
		AccessToken: t.token,
	}, nil
}

// shouldRetry returns if the error is from DigitalOcean and we should
// retry the request which generated it
func shouldRetry(err error) bool {
	errResp, ok := err.(*godo.ErrorResponse)
	if !ok {
		return false
	}
	status := errResp.Response.StatusCode
	return status == 422 || status >= 500
}
<|endoftext|>


<commit_before>/*
Package audio provides the Gobot adaptor for audio.

For more information refer to the README:
https://gobot.io/x/gobot/blob/master/platforms/audio/README.md
*/
package audio // import "gobot.io/x/gobot/platforms/audio"
<commit_msg>docs: correct audio readme link<commit_after>/*
Package audio provides the Gobot adaptor for audio.

For more information refer to the README:
https://github.com/hybridgroup/gobot/blob/master/platforms/audio/README.md
*/
package audio // import "gobot.io/x/gobot/platforms/audio"
<|endoftext|>


<commit_before>// Package playstate is responsible for assets and behavior of the actual game
// itself. This is where the action is. (Also handles the pause menu in the
// game.)
package playstate

import (
	"github.com/beejjorgensen/eggdrop/gamemanager"
	"github.com/beejjorgensen/eggdrop/menu"
	"github.com/beejjorgensen/eggdrop/util"

	"github.com/beejjorgensen/eggdrop/assetmanager"
	"github.com/beejjorgensen/eggdrop/gamecontext"
	"github.com/beejjorgensen/eggdrop/scenegraph"
	"github.com/veandco/go-sdl2/sdl"
)

// PlayState holds information about the main game and pause menu
type PlayState struct {
	assetManager                *assetmanager.AssetManager
	rootEntity, pauseMenuEntity *scenegraph.Entity

	fontNormalColor, fontHighlightColor sdl.Color
	bgColor                             uint32

	paused bool

	menu *menu.Menu

	nestEntity *scenegraph.Entity
}

// Init initializes this gamestate
func (ps *PlayState) Init() {
	// Create colors
	ps.bgColor = sdl.MapRGB(gamecontext.GContext.PixelFormat, 133, 187, 234)
	ps.fontNormalColor = sdl.Color{R: 255, G: 255, B: 255, A: 255}
	ps.fontHighlightColor = sdl.Color{R: 255, G: 255, B: 0, A: 255}

	ps.assetManager = assetmanager.New()
	ps.assetManager.SetOuterSurface(gamecontext.GContext.MainSurface)

	ps.assetManager.LoadJSON("playassets.json")
	ps.buildScene()
}

// buildScene constructs the necessary elements for the scene
func (ps *PlayState) buildScene() {
	am := ps.assetManager // asset manager

	mainW := gamecontext.GContext.MainSurface.W
	mainH := gamecontext.GContext.MainSurface.H

	ps.rootEntity = scenegraph.NewEntity(nil)
	ps.rootEntity.W = mainW
	ps.rootEntity.H = mainH

	// Nest
	ps.nestEntity = scenegraph.NewEntity(am.Surfaces["nestImage"])
	ps.nestEntity.Y = 473
	util.CenterEntityInParent(ps.nestEntity, ps.rootEntity)

	// Pause menu stuff
	ps.buildPauseMenu()
	util.CenterEntityInParent(ps.menu.RootEntity, ps.rootEntity)

	// Ground
	groundEntity := scenegraph.NewEntity(am.Surfaces["groundRect"])
	groundEntity.Y = gamecontext.GContext.WindowHeight - 60

	// Branch
	branchEntity := scenegraph.NewEntity(am.Surfaces["branchRect"])
	branchEntity.Y = 120

	// Chicken
	chickenLeftEntity := scenegraph.NewEntity(am.Surfaces["chickenLeftImage"])
	chickenRightEntity := scenegraph.NewEntity(am.Surfaces["chickenRightImage"])
	chickenRightEntity.X = 400

	// Build scenegraph
	ps.rootEntity.AddChild(chickenLeftEntity, chickenRightEntity)
	ps.rootEntity.AddChild(groundEntity, branchEntity)
	ps.rootEntity.AddChild(ps.nestEntity)
	ps.rootEntity.AddChild(ps.pauseMenuEntity)
}

// handleMenuItem does the right thing with a selected menu item
func (ps *PlayState) handleMenuItem(i int) bool {
	switch i {
	case 0: // Continue
		ps.pause(false)
	case 1: // Quit
		// back to introstate
		gamemanager.GGameManager.SetMode(gamemanager.GameModeIntro)
	}

	return false
}

// positionNest positions and clamps the nest
func (ps *PlayState) positionNest(x int32) {
	w := ps.nestEntity.W
	x -= w / 2 // center

	if x < 0 {
		x = 0
	}

	maxX := gamecontext.GContext.WindowWidth - w
	if x > maxX {
		x = maxX
	}

	ps.nestEntity.X = x
}

// handleEventPlaying deals with paused events in the play state
func (ps *PlayState) handleEventPlaying(event *sdl.Event) bool {
	switch event := (*event).(type) {
	case *sdl.KeyDownEvent:
		//fmt.Printf("Key: %#v\n", event)
		switch event.Keysym.Sym {

		case sdl.K_ESCAPE, sdl.K_p:
			ps.pause(true)
		}
	case *sdl.MouseMotionEvent:
		ps.positionNest(event.X)
	}

	return false
}

// HandleEvent handles SDL events for the intro state
func (ps *PlayState) HandleEvent(event *sdl.Event) bool {
	if ps.paused {
		return ps.handleEventPaused(event)
	}

	return ps.handleEventPlaying(event)
}

// Render renders the intro state
func (ps *PlayState) Render(mainWindowSurface *sdl.Surface) {
	rootEntity := ps.rootEntity

	mainWindowSurface.FillRect(nil, ps.bgColor)
	rootEntity.Render(mainWindowSurface)
}

// WillShow is called just before this state begins
func (ps *PlayState) WillShow() {
	ps.pause(false)

	// call this to move on to the next transition state
	gamemanager.GGameManager.WillShowComplete()
}

// WillHide is called just before this state ends
func (ps *PlayState) WillHide() {
}

// DidShow is called just after this statebegins
func (ps *PlayState) DidShow() {
}

// DidHide is called just after this state ends
func (ps *PlayState) DidHide() {
}
<commit_msg>fix comment<commit_after>// Package playstate is responsible for assets and behavior of the actual game
// itself. This is where the action is. (Also handles the pause menu in the
// game.)
package playstate

import (
	"github.com/beejjorgensen/eggdrop/gamemanager"
	"github.com/beejjorgensen/eggdrop/menu"
	"github.com/beejjorgensen/eggdrop/util"

	"github.com/beejjorgensen/eggdrop/assetmanager"
	"github.com/beejjorgensen/eggdrop/gamecontext"
	"github.com/beejjorgensen/eggdrop/scenegraph"
	"github.com/veandco/go-sdl2/sdl"
)

// PlayState holds information about the main game and pause menu
type PlayState struct {
	assetManager                *assetmanager.AssetManager
	rootEntity, pauseMenuEntity *scenegraph.Entity

	fontNormalColor, fontHighlightColor sdl.Color
	bgColor                             uint32

	paused bool

	menu *menu.Menu

	nestEntity *scenegraph.Entity
}

// Init initializes this gamestate
func (ps *PlayState) Init() {
	// Create colors
	ps.bgColor = sdl.MapRGB(gamecontext.GContext.PixelFormat, 133, 187, 234)
	ps.fontNormalColor = sdl.Color{R: 255, G: 255, B: 255, A: 255}
	ps.fontHighlightColor = sdl.Color{R: 255, G: 255, B: 0, A: 255}

	ps.assetManager = assetmanager.New()
	ps.assetManager.SetOuterSurface(gamecontext.GContext.MainSurface)

	ps.assetManager.LoadJSON("playassets.json")
	ps.buildScene()
}

// buildScene constructs the necessary elements for the scene
func (ps *PlayState) buildScene() {
	am := ps.assetManager // asset manager

	mainW := gamecontext.GContext.MainSurface.W
	mainH := gamecontext.GContext.MainSurface.H

	ps.rootEntity = scenegraph.NewEntity(nil)
	ps.rootEntity.W = mainW
	ps.rootEntity.H = mainH

	// Nest
	ps.nestEntity = scenegraph.NewEntity(am.Surfaces["nestImage"])
	ps.nestEntity.Y = 473
	util.CenterEntityInParent(ps.nestEntity, ps.rootEntity)

	// Pause menu stuff
	ps.buildPauseMenu()
	util.CenterEntityInParent(ps.menu.RootEntity, ps.rootEntity)

	// Ground
	groundEntity := scenegraph.NewEntity(am.Surfaces["groundRect"])
	groundEntity.Y = gamecontext.GContext.WindowHeight - 60

	// Branch
	branchEntity := scenegraph.NewEntity(am.Surfaces["branchRect"])
	branchEntity.Y = 120

	// Chicken
	chickenLeftEntity := scenegraph.NewEntity(am.Surfaces["chickenLeftImage"])
	chickenRightEntity := scenegraph.NewEntity(am.Surfaces["chickenRightImage"])
	chickenRightEntity.X = 400

	// Build scenegraph
	ps.rootEntity.AddChild(chickenLeftEntity, chickenRightEntity)
	ps.rootEntity.AddChild(groundEntity, branchEntity)
	ps.rootEntity.AddChild(ps.nestEntity)
	ps.rootEntity.AddChild(ps.pauseMenuEntity)
}

// handleMenuItem does the right thing with a selected menu item
func (ps *PlayState) handleMenuItem(i int) bool {
	switch i {
	case 0: // Continue
		ps.pause(false)
	case 1: // Quit
		// back to introstate
		gamemanager.GGameManager.SetMode(gamemanager.GameModeIntro)
	}

	return false
}

// positionNest positions and clamps the nest
func (ps *PlayState) positionNest(x int32) {
	w := ps.nestEntity.W
	x -= w / 2 // center

	if x < 0 {
		x = 0
	}

	maxX := gamecontext.GContext.WindowWidth - w
	if x > maxX {
		x = maxX
	}

	ps.nestEntity.X = x
}

// handleEventPlaying deals with events in the play state
func (ps *PlayState) handleEventPlaying(event *sdl.Event) bool {
	switch event := (*event).(type) {
	case *sdl.KeyDownEvent:
		//fmt.Printf("Key: %#v\n", event)
		switch event.Keysym.Sym {

		case sdl.K_ESCAPE, sdl.K_p:
			ps.pause(true)
		}
	case *sdl.MouseMotionEvent:
		ps.positionNest(event.X)
	}

	return false
}

// HandleEvent handles SDL events for the intro state
func (ps *PlayState) HandleEvent(event *sdl.Event) bool {
	if ps.paused {
		return ps.handleEventPaused(event)
	}

	return ps.handleEventPlaying(event)
}

// Render renders the intro state
func (ps *PlayState) Render(mainWindowSurface *sdl.Surface) {
	rootEntity := ps.rootEntity

	mainWindowSurface.FillRect(nil, ps.bgColor)
	rootEntity.Render(mainWindowSurface)
}

// WillShow is called just before this state begins
func (ps *PlayState) WillShow() {
	ps.pause(false)

	// call this to move on to the next transition state
	gamemanager.GGameManager.WillShowComplete()
}

// WillHide is called just before this state ends
func (ps *PlayState) WillHide() {
}

// DidShow is called just after this statebegins
func (ps *PlayState) DidShow() {
}

// DidHide is called just after this state ends
func (ps *PlayState) DidHide() {
}
<|endoftext|>


<commit_before>package osdn

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"

	"github.com/openshift/openshift-sdn/plugins/osdn/api"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	kubeletTypes "k8s.io/kubernetes/pkg/kubelet/container"
	knetwork "k8s.io/kubernetes/pkg/kubelet/network"
	utilsets "k8s.io/kubernetes/pkg/util/sets"
)

const (
	SingleTenantPluginName string = "redhat/openshift-ovs-subnet"
	MultiTenantPluginName  string = "redhat/openshift-ovs-multitenant"

	IngressBandwidthAnnotation string = "kubernetes.io/ingress-bandwidth"
	EgressBandwidthAnnotation  string = "kubernetes.io/egress-bandwidth"
	AssignMacVlanAnnotation    string = "pod.network.openshift.io/assign-macvlan"
)

func IsOpenShiftNetworkPlugin(pluginName string) bool {
	switch strings.ToLower(pluginName) {
	case SingleTenantPluginName, MultiTenantPluginName:
		return true
	}
	return false
}

func IsOpenShiftMultitenantNetworkPlugin(pluginName string) bool {
	if strings.ToLower(pluginName) == MultiTenantPluginName {
		return true
	}
	return false
}

//-----------------------------------------------

const (
	setUpCmd    = "setup"
	tearDownCmd = "teardown"
	statusCmd   = "status"
	updateCmd   = "update"
)

func (plugin *OsdnNode) getExecutable() string {
	return "openshift-sdn-ovs"
}

func (plugin *OsdnNode) Init(host knetwork.Host) error {
	return nil
}

func (plugin *OsdnNode) Name() string {
	if plugin.multitenant {
		return MultiTenantPluginName
	} else {
		return SingleTenantPluginName
	}
}

func (plugin *OsdnNode) Capabilities() utilsets.Int {
	return utilsets.NewInt(knetwork.NET_PLUGIN_CAPABILITY_SHAPING)
}

func (plugin *OsdnNode) getVNID(namespace string) (string, error) {
	if plugin.multitenant {
		vnid, err := plugin.vnids.WaitAndGetVNID(namespace)
		if err != nil {
			return "", err
		}
		return strconv.FormatUint(uint64(vnid), 10), nil
	}

	return "0", nil
}

var minRsrc = resource.MustParse("1k")
var maxRsrc = resource.MustParse("1P")

func parseAndValidateBandwidth(value string) (int64, error) {
	rsrc, err := resource.ParseQuantity(value)
	if err != nil {
		return -1, err
	}

	if rsrc.Value() < minRsrc.Value() {
		return -1, fmt.Errorf("resource value %d is unreasonably small (< %d)", rsrc.Value(), minRsrc.Value())
	}
	if rsrc.Value() > maxRsrc.Value() {
		return -1, fmt.Errorf("resource value %d is unreasonably large (> %d)", rsrc.Value(), maxRsrc.Value())
	}
	return rsrc.Value(), nil
}

func extractBandwidthResources(pod *kapi.Pod) (ingress, egress int64, err error) {
	str, found := pod.Annotations[IngressBandwidthAnnotation]
	if found {
		ingress, err = parseAndValidateBandwidth(str)
		if err != nil {
			return -1, -1, err
		}
	}
	str, found = pod.Annotations[EgressBandwidthAnnotation]
	if found {
		egress, err = parseAndValidateBandwidth(str)
		if err != nil {
			return -1, -1, err
		}
	}
	return ingress, egress, nil
}

func wantsMacvlan(pod *kapi.Pod) (bool, error) {
	val, found := pod.Annotations[AssignMacVlanAnnotation]
	if !found || val != "true" {
		return false, nil
	}
	for _, container := range pod.Spec.Containers {
		if container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
			return true, nil
		}
	}
	return false, fmt.Errorf("Pod has %q annotation but is not privileged", AssignMacVlanAnnotation)
}

func isScriptError(err error) bool {
	_, ok := err.(*exec.ExitError)
	return ok
}

// Get the last command (which is prefixed with "+" because of "set -x") and its output
// (Unless the script ended with "echo ...; exit", in which case we just return the
// echoed text.)
func getScriptError(output []byte) string {
	lines := strings.Split(string(output), "\n")
	last := len(lines)
	for n := last - 1; n >= 0; n-- {
		if strings.HasPrefix(lines[n], "+ exit") {
			last = n
		} else if strings.HasPrefix(lines[n], "+ echo") {
			return strings.Join(lines[n+1:last], "\n")
		} else if strings.HasPrefix(lines[n], "+") {
			return strings.Join(lines[n:], "\n")
		}
	}
	return string(output)
}

func (plugin *OsdnNode) SetUpPod(namespace string, name string, id kubeletTypes.ContainerID) error {
	err := plugin.WaitForPodNetworkReady()
	if err != nil {
		return err
	}

	pod, err := plugin.registry.GetPod(plugin.hostName, namespace, name)
	if err != nil {
		return err
	}
	if pod == nil {
		return fmt.Errorf("failed to retrieve pod %s/%s", namespace, name)
	}
	ingress, egress, err := extractBandwidthResources(pod)
	if err != nil {
		return fmt.Errorf("failed to parse pod %s/%s ingress/egress quantity: %v", namespace, name, err)
	}
	var ingressStr, egressStr string
	if ingress > 0 {
		ingressStr = fmt.Sprintf("%d", ingress)
	}
	if egress > 0 {
		egressStr = fmt.Sprintf("%d", egress)
	}

	vnidstr, err := plugin.getVNID(namespace)
	if err != nil {
		return err
	}

	macvlan, err := wantsMacvlan(pod)
	if err != nil {
		return err
	}

	out, err := exec.Command(plugin.getExecutable(), setUpCmd, id.ID, vnidstr, ingressStr, egressStr, fmt.Sprintf("%t", macvlan)).CombinedOutput()
	glog.V(5).Infof("SetUpPod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network setup script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) TearDownPod(namespace string, name string, id kubeletTypes.ContainerID) error {
	// The script's teardown functionality doesn't need the VNID
	out, err := exec.Command(plugin.getExecutable(), tearDownCmd, id.ID, "-1", "-1", "-1").CombinedOutput()
	glog.V(5).Infof("TearDownPod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network teardown script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) Status() error {
	return nil
}

func (plugin *OsdnNode) GetPodNetworkStatus(namespace string, name string, podInfraContainerID kubeletTypes.ContainerID) (*knetwork.PodNetworkStatus, error) {
	return nil, nil
}

func (plugin *OsdnNode) UpdatePod(namespace string, name string, id kubeletTypes.DockerID) error {
	vnidstr, err := plugin.getVNID(namespace)
	if err != nil {
		return err
	}

	out, err := exec.Command(plugin.getExecutable(), updateCmd, string(id), vnidstr).CombinedOutput()
	glog.V(5).Infof("UpdatePod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network update script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) Event(name string, details map[string]interface{}) {
}
<commit_msg>Oops, branch got broken by a last-minute change<commit_after>package osdn

import (
	"fmt"
	"os/exec"
	"strconv"
	"strings"

	"github.com/golang/glog"

	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/resource"
	kubeletTypes "k8s.io/kubernetes/pkg/kubelet/container"
	knetwork "k8s.io/kubernetes/pkg/kubelet/network"
	utilsets "k8s.io/kubernetes/pkg/util/sets"
)

const (
	SingleTenantPluginName string = "redhat/openshift-ovs-subnet"
	MultiTenantPluginName  string = "redhat/openshift-ovs-multitenant"

	IngressBandwidthAnnotation string = "kubernetes.io/ingress-bandwidth"
	EgressBandwidthAnnotation  string = "kubernetes.io/egress-bandwidth"
	AssignMacVlanAnnotation    string = "pod.network.openshift.io/assign-macvlan"
)

func IsOpenShiftNetworkPlugin(pluginName string) bool {
	switch strings.ToLower(pluginName) {
	case SingleTenantPluginName, MultiTenantPluginName:
		return true
	}
	return false
}

func IsOpenShiftMultitenantNetworkPlugin(pluginName string) bool {
	if strings.ToLower(pluginName) == MultiTenantPluginName {
		return true
	}
	return false
}

//-----------------------------------------------

const (
	setUpCmd    = "setup"
	tearDownCmd = "teardown"
	statusCmd   = "status"
	updateCmd   = "update"
)

func (plugin *OsdnNode) getExecutable() string {
	return "openshift-sdn-ovs"
}

func (plugin *OsdnNode) Init(host knetwork.Host) error {
	return nil
}

func (plugin *OsdnNode) Name() string {
	if plugin.multitenant {
		return MultiTenantPluginName
	} else {
		return SingleTenantPluginName
	}
}

func (plugin *OsdnNode) Capabilities() utilsets.Int {
	return utilsets.NewInt(knetwork.NET_PLUGIN_CAPABILITY_SHAPING)
}

func (plugin *OsdnNode) getVNID(namespace string) (string, error) {
	if plugin.multitenant {
		vnid, err := plugin.vnids.WaitAndGetVNID(namespace)
		if err != nil {
			return "", err
		}
		return strconv.FormatUint(uint64(vnid), 10), nil
	}

	return "0", nil
}

var minRsrc = resource.MustParse("1k")
var maxRsrc = resource.MustParse("1P")

func parseAndValidateBandwidth(value string) (int64, error) {
	rsrc, err := resource.ParseQuantity(value)
	if err != nil {
		return -1, err
	}

	if rsrc.Value() < minRsrc.Value() {
		return -1, fmt.Errorf("resource value %d is unreasonably small (< %d)", rsrc.Value(), minRsrc.Value())
	}
	if rsrc.Value() > maxRsrc.Value() {
		return -1, fmt.Errorf("resource value %d is unreasonably large (> %d)", rsrc.Value(), maxRsrc.Value())
	}
	return rsrc.Value(), nil
}

func extractBandwidthResources(pod *kapi.Pod) (ingress, egress int64, err error) {
	str, found := pod.Annotations[IngressBandwidthAnnotation]
	if found {
		ingress, err = parseAndValidateBandwidth(str)
		if err != nil {
			return -1, -1, err
		}
	}
	str, found = pod.Annotations[EgressBandwidthAnnotation]
	if found {
		egress, err = parseAndValidateBandwidth(str)
		if err != nil {
			return -1, -1, err
		}
	}
	return ingress, egress, nil
}

func wantsMacvlan(pod *kapi.Pod) (bool, error) {
	val, found := pod.Annotations[AssignMacVlanAnnotation]
	if !found || val != "true" {
		return false, nil
	}
	for _, container := range pod.Spec.Containers {
		if container.SecurityContext.Privileged != nil && *container.SecurityContext.Privileged {
			return true, nil
		}
	}
	return false, fmt.Errorf("Pod has %q annotation but is not privileged", AssignMacVlanAnnotation)
}

func isScriptError(err error) bool {
	_, ok := err.(*exec.ExitError)
	return ok
}

// Get the last command (which is prefixed with "+" because of "set -x") and its output
// (Unless the script ended with "echo ...; exit", in which case we just return the
// echoed text.)
func getScriptError(output []byte) string {
	lines := strings.Split(string(output), "\n")
	last := len(lines)
	for n := last - 1; n >= 0; n-- {
		if strings.HasPrefix(lines[n], "+ exit") {
			last = n
		} else if strings.HasPrefix(lines[n], "+ echo") {
			return strings.Join(lines[n+1:last], "\n")
		} else if strings.HasPrefix(lines[n], "+") {
			return strings.Join(lines[n:], "\n")
		}
	}
	return string(output)
}

func (plugin *OsdnNode) SetUpPod(namespace string, name string, id kubeletTypes.ContainerID) error {
	err := plugin.WaitForPodNetworkReady()
	if err != nil {
		return err
	}

	pod, err := plugin.registry.GetPod(plugin.hostName, namespace, name)
	if err != nil {
		return err
	}
	if pod == nil {
		return fmt.Errorf("failed to retrieve pod %s/%s", namespace, name)
	}
	ingress, egress, err := extractBandwidthResources(pod)
	if err != nil {
		return fmt.Errorf("failed to parse pod %s/%s ingress/egress quantity: %v", namespace, name, err)
	}
	var ingressStr, egressStr string
	if ingress > 0 {
		ingressStr = fmt.Sprintf("%d", ingress)
	}
	if egress > 0 {
		egressStr = fmt.Sprintf("%d", egress)
	}

	vnidstr, err := plugin.getVNID(namespace)
	if err != nil {
		return err
	}

	macvlan, err := wantsMacvlan(pod)
	if err != nil {
		return err
	}

	out, err := exec.Command(plugin.getExecutable(), setUpCmd, id.ID, vnidstr, ingressStr, egressStr, fmt.Sprintf("%t", macvlan)).CombinedOutput()
	glog.V(5).Infof("SetUpPod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network setup script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) TearDownPod(namespace string, name string, id kubeletTypes.ContainerID) error {
	// The script's teardown functionality doesn't need the VNID
	out, err := exec.Command(plugin.getExecutable(), tearDownCmd, id.ID, "-1", "-1", "-1").CombinedOutput()
	glog.V(5).Infof("TearDownPod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network teardown script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) Status() error {
	return nil
}

func (plugin *OsdnNode) GetPodNetworkStatus(namespace string, name string, podInfraContainerID kubeletTypes.ContainerID) (*knetwork.PodNetworkStatus, error) {
	return nil, nil
}

func (plugin *OsdnNode) UpdatePod(namespace string, name string, id kubeletTypes.DockerID) error {
	vnidstr, err := plugin.getVNID(namespace)
	if err != nil {
		return err
	}

	out, err := exec.Command(plugin.getExecutable(), updateCmd, string(id), vnidstr).CombinedOutput()
	glog.V(5).Infof("UpdatePod network plugin output: %s, %v", string(out), err)

	if isScriptError(err) {
		return fmt.Errorf("Error running network update script: %s", getScriptError(out))
	} else {
		return err
	}
}

func (plugin *OsdnNode) Event(name string, details map[string]interface{}) {
}
<|endoftext|>


<commit_before>// Package editor enables users to create edit views from their content
// structs so that admins can manage content
package editor

import (
	"bytes"
)

// Editable ensures data is editable
type Editable interface {
	SetContentID(id int)
	ContentID() int
	ContentName() string
	SetSlug(slug string)
	Editor() *Editor
	MarshalEditor() ([]byte, error)
}

// Sortable ensures data is sortable by time
type Sortable interface {
	Time() int64
	Touch() int64
	ContentID() int
}

// Editor is a view containing fields to manage content
type Editor struct {
	ViewBuf *bytes.Buffer
}

// Field is used to create the editable view for a field
// within a particular content struct
type Field struct {
	View []byte
}

// Form takes editable content and any number of Field funcs to describe the edit
// page for any content struct added by a user
func Form(post Editable, fields ...Field) ([]byte, error) {
	editor := post.Editor()

	editor.ViewBuf = &bytes.Buffer{}
	editor.ViewBuf.Write([]byte(`<table><tbody class="row"><tr class="col s8"><td>`))

	for _, f := range fields {
		addFieldToEditorView(editor, f)
	}

	editor.ViewBuf.Write([]byte(`</td></tr>`))

	// content items with Item embedded have some default fields we need to render
	editor.ViewBuf.Write([]byte(`<tr class="col s4 default-fields"><td>`))

	publishTime := `

		MM
		<select class="month __ponzu browser-default">
			<option value="1">Jan - 01</option>
			<option value="2">Feb - 02</option>
			<option value="3">Mar - 03</option>
			<option value="4">Apr - 04</option>
			<option value="5">May - 05</option>
			<option value="6">Jun - 06</option>
			<option value="7">Jul - 07</option>
			<option value="8">Aug - 08</option>
			<option value="9">Sep - 09</option>
			<option value="10">Oct - 10</option>
			<option value="11">Nov - 11</option>
			<option value="12">Dec - 12</option>
		</select>

		DD
		<input value="" class="day __ponzu" maxlength="2" type="text" placeholder="DD" />

		YYYY
		<input value="" class="year __ponzu" maxlength="4" type="text" placeholder="YYYY" />

		HH
		<input value="" class="hour __ponzu" maxlength="2" type="text" placeholder="HH" />

:

		MM
		<input value="" class="minute __ponzu" maxlength="2" type="text" placeholder="MM" />

		Period
		<select class="period __ponzu browser-default">
			<option value="AM">AM</option>
			<option value="PM">PM</option>
		</select>

	`

	editor.ViewBuf.Write([]byte(publishTime))

	addPostDefaultFieldsToEditorView(post, editor)

	submit := `

	<button class="right waves-effect waves-light btn green save-post" type="submit">Save</button>
	<button class="right waves-effect waves-light btn red delete-post" type="submit">Delete</button>

	<button class="right waves-effect waves-light btn blue approve-post" type="submit">Approve</button>

This post is pending approval. By clicking 'Approve' it will be immediately published.

`
	editor.ViewBuf.Write([]byte(submit + `</td></tr></tbody></table>`))

	return editor.ViewBuf.Bytes(), nil
}

func addFieldToEditorView(e *Editor, f Field) {
	e.ViewBuf.Write(f.View)
}

func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
	defaults := []Field{
		Field{
			View: Input("Slug", p, map[string]string{
				"label":       "URL Slug",
				"type":        "text",
				"disabled":    "true",
				"placeholder": "Will be set automatically",
			}),
		},
		Field{
			View: Timestamp("Timestamp", p, map[string]string{
				"type":  "hidden",
				"class": "timestamp __ponzu",
			}),
		},
		Field{
			View: Timestamp("Updated", p, map[string]string{
				"type":  "hidden",
				"class": "updated __ponzu",
			}),
		},
	}

	for _, f := range defaults {
		addFieldToEditorView(e, f)
	}

}
<commit_msg>UI code fix on approval section<commit_after>// Package editor enables users to create edit views from their content
// structs so that admins can manage content
package editor

import (
	"bytes"
)

// Editable ensures data is editable
type Editable interface {
	SetContentID(id int)
	ContentID() int
	ContentName() string
	SetSlug(slug string)
	Editor() *Editor
	MarshalEditor() ([]byte, error)
}

// Sortable ensures data is sortable by time
type Sortable interface {
	Time() int64
	Touch() int64
	ContentID() int
}

// Editor is a view containing fields to manage content
type Editor struct {
	ViewBuf *bytes.Buffer
}

// Field is used to create the editable view for a field
// within a particular content struct
type Field struct {
	View []byte
}

// Form takes editable content and any number of Field funcs to describe the edit
// page for any content struct added by a user
func Form(post Editable, fields ...Field) ([]byte, error) {
	editor := post.Editor()

	editor.ViewBuf = &bytes.Buffer{}
	editor.ViewBuf.Write([]byte(`<table><tbody class="row"><tr class="col s8"><td>`))

	for _, f := range fields {
		addFieldToEditorView(editor, f)
	}

	editor.ViewBuf.Write([]byte(`</td></tr>`))

	// content items with Item embedded have some default fields we need to render
	editor.ViewBuf.Write([]byte(`<tr class="col s4 default-fields"><td>`))

	publishTime := `

		MM
		<select class="month __ponzu browser-default">
			<option value="1">Jan - 01</option>
			<option value="2">Feb - 02</option>
			<option value="3">Mar - 03</option>
			<option value="4">Apr - 04</option>
			<option value="5">May - 05</option>
			<option value="6">Jun - 06</option>
			<option value="7">Jul - 07</option>
			<option value="8">Aug - 08</option>
			<option value="9">Sep - 09</option>
			<option value="10">Oct - 10</option>
			<option value="11">Nov - 11</option>
			<option value="12">Dec - 12</option>
		</select>

		DD
		<input value="" class="day __ponzu" maxlength="2" type="text" placeholder="DD" />

		YYYY
		<input value="" class="year __ponzu" maxlength="4" type="text" placeholder="YYYY" />

		HH
		<input value="" class="hour __ponzu" maxlength="2" type="text" placeholder="HH" />

:

		MM
		<input value="" class="minute __ponzu" maxlength="2" type="text" placeholder="MM" />

		Period
		<select class="period __ponzu browser-default">
			<option value="AM">AM</option>
			<option value="PM">PM</option>
		</select>

	`

	editor.ViewBuf.Write([]byte(publishTime))

	addPostDefaultFieldsToEditorView(post, editor)

	submit := `

	<button class="right waves-effect waves-light btn green save-post" type="submit">Save</button>
	<button class="right waves-effect waves-light btn red delete-post" type="submit">Delete</button>

This post is pending approval. By clicking 'Approve' it will be immediately published.

	<button class="right waves-effect waves-light btn blue approve-post" type="submit">Approve</button>

`
	editor.ViewBuf.Write([]byte(submit + `</td></tr></tbody></table>`))

	return editor.ViewBuf.Bytes(), nil
}

func addFieldToEditorView(e *Editor, f Field) {
	e.ViewBuf.Write(f.View)
}

func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
	defaults := []Field{
		Field{
			View: Input("Slug", p, map[string]string{
				"label":       "URL Slug",
				"type":        "text",
				"disabled":    "true",
				"placeholder": "Will be set automatically",
			}),
		},
		Field{
			View: Timestamp("Timestamp", p, map[string]string{
				"type":  "hidden",
				"class": "timestamp __ponzu",
			}),
		},
		Field{
			View: Timestamp("Updated", p, map[string]string{
				"type":  "hidden",
				"class": "updated __ponzu",
			}),
		},
	}

	for _, f := range defaults {
		addFieldToEditorView(e, f)
	}

}
<|endoftext|>


<commit_before>// Package editor enables users to create edit views from their content
// structs so that admins can manage content
package editor

import (
	"bytes"
)

// Editable ensures data is editable
type Editable interface {
	SetContentID(id int)
	ContentID() int
	ContentName() string
	SetSlug(slug string)
	Editor() *Editor
	MarshalEditor() ([]byte, error)
}

// Editor is a view containing fields to manage content
type Editor struct {
	ViewBuf *bytes.Buffer
}

// Field is used to create the editable view for a field
// within a particular content struct
type Field struct {
	View []byte
}

// Form takes editable content and any number of Field funcs to describe the edit
// page for any content struct added by a user
func Form(post Editable, fields ...Field) ([]byte, error) {
	editor := post.Editor()

	editor.ViewBuf = &bytes.Buffer{}
	editor.ViewBuf.Write([]byte(`<table><tbody class="row"><tr class="col s8"><td>`))

	for _, f := range fields {
		addFieldToEditorView(editor, f)
	}

	editor.ViewBuf.Write([]byte(`</td></tr>`))

	// content items with Item embedded have some default fields we need to render
	editor.ViewBuf.Write([]byte(`<tr class="col s4 default-fields"><td>`))
	addPostDefaultFieldsToEditorView(post, editor)

	submit := `

	<button class="right waves-effect waves-light btn green" type="submit">Save</button>

`
	editor.ViewBuf.Write([]byte(submit + `</td></tr></tbody></table>`))

	return editor.ViewBuf.Bytes(), nil
}

func addFieldToEditorView(e *Editor, f Field) {
	e.ViewBuf.Write(f.View)
}

func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
	defaults := []Field{
		Field{
			View: Input("Timestamp", p, map[string]string{
				"label": "Publish Date",
				"type":  "date",
			}),
		},
		Field{
			View: Input("Slug", p, map[string]string{
				"label":       "URL Slug",
				"type":        "text",
				"disabled":    "true",
				"placeholder": "Will be set automatically",
			}),
		},
	}

	for _, f := range defaults {
		addFieldToEditorView(e, f)
	}

}
<commit_msg>wait to delete for confirmation<commit_after>// Package editor enables users to create edit views from their content
// structs so that admins can manage content
package editor

import (
	"bytes"
)

// Editable ensures data is editable
type Editable interface {
	SetContentID(id int)
	ContentID() int
	ContentName() string
	SetSlug(slug string)
	Editor() *Editor
	MarshalEditor() ([]byte, error)
}

// Editor is a view containing fields to manage content
type Editor struct {
	ViewBuf *bytes.Buffer
}

// Field is used to create the editable view for a field
// within a particular content struct
type Field struct {
	View []byte
}

// Form takes editable content and any number of Field funcs to describe the edit
// page for any content struct added by a user
func Form(post Editable, fields ...Field) ([]byte, error) {
	editor := post.Editor()

	editor.ViewBuf = &bytes.Buffer{}
	editor.ViewBuf.Write([]byte(`<table><tbody class="row"><tr class="col s8"><td>`))

	for _, f := range fields {
		addFieldToEditorView(editor, f)
	}

	editor.ViewBuf.Write([]byte(`</td></tr>`))

	// content items with Item embedded have some default fields we need to render
	editor.ViewBuf.Write([]byte(`<tr class="col s4 default-fields"><td>`))
	addPostDefaultFieldsToEditorView(post, editor)

	submit := `

	<button class="right waves-effect waves-light btn green save-post" type="submit">Save</button>
	<button class="right waves-effect waves-light btn red delete-post" type="submit">Delete</button>

`
	editor.ViewBuf.Write([]byte(submit + `</td></tr></tbody></table>`))

	return editor.ViewBuf.Bytes(), nil
}

func addFieldToEditorView(e *Editor, f Field) {
	e.ViewBuf.Write(f.View)
}

func addPostDefaultFieldsToEditorView(p Editable, e *Editor) {
	defaults := []Field{
		Field{
			View: Input("Timestamp", p, map[string]string{
				"label": "Publish Date",
				"type":  "date",
			}),
		},
		Field{
			View: Input("Slug", p, map[string]string{
				"label":       "URL Slug",
				"type":        "text",
				"disabled":    "true",
				"placeholder": "Will be set automatically",
			}),
		},
	}

	for _, f := range defaults {
		addFieldToEditorView(e, f)
	}

}
<|endoftext|>


<commit_before>package url

import (
	"context"
	"net/url"
	"regexp"
	"text/template"

	"github.com/sirupsen/logrus"
	"github.com/zmb3/spotify"
	"golang.org/x/oauth2/clientcredentials"

	seabird "github.com/belak/go-seabird"
	"github.com/belak/go-seabird/internal"
)

func init() {
	seabird.RegisterPlugin("url/spotify", newSpotifyProvider)
}

type spotifyConfig struct {
	ClientID     string
	ClientSecret string
}

type spotifyProvider struct {
	api spotify.Client
}

var spotifyPrefix = "[Spotify]"

type spotifyMatch struct {
	matchCount int
	regex      *regexp.Regexp
	uriRegex   *regexp.Regexp
	template   *template.Template
	lookup     func(*spotifyProvider, *logrus.Entry, []string) interface{}
}

var spotifyMatchers = []spotifyMatch{
	{
		matchCount: 1,
		regex:      regexp.MustCompile(`^/artist/(.+)$`),
		uriRegex:   regexp.MustCompile(`\bspotify:artist:(\w+)\b`),
		template:   internal.TemplateMustCompile("spotifyArtist", `{{- .Name -}}`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			artist, err := s.api.GetArtist(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get artist info from Spotify")
				return nil
			}
			return artist
		},
	},
	{
		matchCount: 1,
		regex:      regexp.MustCompile(`^/album/(.+)$`),
		uriRegex:   regexp.MustCompile(`\bspotify:album:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyAlbum", `
			{{- .Name }} by
			{{- range $index, $element := .Artists }}
			{{- if $index }},{{ end }} {{ $element.Name -}}
			{{- end }} ({{ pluralize .Tracks.Total "track" }})`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			album, err := s.api.GetAlbum(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get album info from Spotify")
				return nil
			}
			return album
		},
	},
	{
		matchCount: 1,
		regex:      regexp.MustCompile(`^/track/(.+)$`),
		uriRegex:   regexp.MustCompile(`\bspotify:track:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyTrack", `
			"{{ .Name }}" from {{ .Album.Name }} by
			{{- range $index, $element := .Artists }}
			{{- if $index }},{{ end }} {{ $element.Name }}
			{{- end }}`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			track, err := s.api.GetTrack(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get track info from Spotify")
				return nil
			}
			return track
		},
	},
	{
		matchCount: 2,
		regex:      regexp.MustCompile(`^/user/([^/]*)/playlist/([^/]*)$`),
		uriRegex:   regexp.MustCompile(`\bspotify:user:(\w+):playlist:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyPlaylist", `
			"{{- .Name }}" playlist by {{ .Owner.DisplayName }} ({{ pluralize .Tracks.Total "track" }})`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			// playlist, err := s.api.GetPlaylist(matches[0], spotify.ID(matches[1]))
			playlist, err := s.api.GetPlaylist(spotify.ID(matches[1]))
			if err != nil {
				logger.WithError(err).Error("Failed to get track info from Spotify")
				return nil
			}
			return playlist
		},
	},
}

func newSpotifyProvider(b *seabird.Bot) error {
	if err := b.EnsurePlugin("url"); err != nil {
		return err
	}

	bm := b.BasicMux()
	urlPlugin := CtxPlugin(b.Context())

	s := &spotifyProvider{}

	sc := &spotifyConfig{}
	if err := b.Config("spotify", sc); err != nil {
		return err
	}

	config := &clientcredentials.Config{
		ClientID:     sc.ClientID,
		ClientSecret: sc.ClientSecret,
		TokenURL:     spotify.TokenURL,
	}

	token, err := config.Token(context.Background())
	if err != nil {
		return err
	}

	s.api = spotify.Authenticator{}.NewClient(token)

	bm.Event("PRIVMSG", s.privmsgCallback)

	urlPlugin.RegisterProvider("open.spotify.com", s.HandleURL)

	return nil
}

func (s *spotifyProvider) privmsgCallback(r *seabird.Request) {
	for _, matcher := range spotifyMatchers {
		if s.handleTarget(r, matcher, matcher.uriRegex, r.Message.Trailing()) {
			return
		}
	}
}

func (s *spotifyProvider) HandleURL(r *seabird.Request, u *url.URL) bool {
	for _, matcher := range spotifyMatchers {
		if s.handleTarget(r, matcher, matcher.regex, u.Path) {
			return true
		}
	}

	return false
}

func (s *spotifyProvider) handleTarget(r *seabird.Request, matcher spotifyMatch, regex *regexp.Regexp, target string) bool {
	logger := r.GetLogger("url/spotify")

	if !regex.MatchString(target) {
		return false
	}

	matches := regex.FindStringSubmatch(target)
	if len(matches) != matcher.matchCount+1 {
		return false
	}

	data := matcher.lookup(s, logger, matches[1:])
	if data == nil {
		return false
	}

	return internal.RenderRespond(r.Reply, logger, matcher.template, spotifyPrefix, data)
}
<commit_msg>Fix spotify url matcher<commit_after>package url

import (
	"context"
	"net/url"
	"regexp"
	"text/template"

	"github.com/sirupsen/logrus"
	"github.com/zmb3/spotify"
	"golang.org/x/oauth2/clientcredentials"

	seabird "github.com/belak/go-seabird"
	"github.com/belak/go-seabird/internal"
)

func init() {
	seabird.RegisterPlugin("url/spotify", newSpotifyProvider)
}

type spotifyConfig struct {
	ClientID     string
	ClientSecret string
}

type spotifyProvider struct {
	api spotify.Client
}

var spotifyPrefix = "[Spotify]"

type spotifyMatch struct {
	regex    *regexp.Regexp
	uriRegex *regexp.Regexp
	template *template.Template
	lookup   func(*spotifyProvider, *logrus.Entry, []string) interface{}
}

var spotifyMatchers = []spotifyMatch{
	{
		regex:    regexp.MustCompile(`^/artist/(.+)$`),
		uriRegex: regexp.MustCompile(`\bspotify:artist:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyArtist", `{{- .Name -}}`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			artist, err := s.api.GetArtist(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get artist info from Spotify")
				return nil
			}
			return artist
		},
	},
	{
		regex:    regexp.MustCompile(`^/album/(.+)$`),
		uriRegex: regexp.MustCompile(`\bspotify:album:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyAlbum", `
			{{- .Name }} by
			{{- range $index, $element := .Artists }}
			{{- if $index }},{{ end }} {{ $element.Name -}}
			{{- end }} ({{ pluralize .Tracks.Total "track" }})`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			album, err := s.api.GetAlbum(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get album info from Spotify")
				return nil
			}
			return album
		},
	},
	{
		regex:    regexp.MustCompile(`^/track/(.+)$`),
		uriRegex: regexp.MustCompile(`\bspotify:track:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyTrack", `
			"{{ .Name }}" from {{ .Album.Name }} by
			{{- range $index, $element := .Artists }}
			{{- if $index }},{{ end }} {{ $element.Name }}
			{{- end }}`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			track, err := s.api.GetTrack(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get track info from Spotify")
				return nil
			}
			return track
		},
	},
	{
		regex:    regexp.MustCompile(`^/playlist/([^/]*)$`),
		uriRegex: regexp.MustCompile(`\bspotify:playlist:(\w+)\b`),
		template: internal.TemplateMustCompile("spotifyPlaylist", `
			"{{- .Name }}" playlist by {{ .Owner.DisplayName }} ({{ pluralize .Tracks.Total "track" }})`),
		lookup: func(s *spotifyProvider, logger *logrus.Entry, matches []string) interface{} {
			playlist, err := s.api.GetPlaylist(spotify.ID(matches[0]))
			if err != nil {
				logger.WithError(err).Error("Failed to get track info from Spotify")
				return nil
			}
			return playlist
		},
	},
}

func newSpotifyProvider(b *seabird.Bot) error {
	if err := b.EnsurePlugin("url"); err != nil {
		return err
	}

	bm := b.BasicMux()
	urlPlugin := CtxPlugin(b.Context())

	s := &spotifyProvider{}

	sc := &spotifyConfig{}
	if err := b.Config("spotify", sc); err != nil {
		return err
	}

	config := &clientcredentials.Config{
		ClientID:     sc.ClientID,
		ClientSecret: sc.ClientSecret,
		TokenURL:     spotify.TokenURL,
	}

	token, err := config.Token(context.Background())
	if err != nil {
		return err
	}

	s.api = spotify.Authenticator{}.NewClient(token)

	bm.Event("PRIVMSG", s.privmsgCallback)

	urlPlugin.RegisterProvider("open.spotify.com", s.HandleURL)

	return nil
}

func (s *spotifyProvider) privmsgCallback(r *seabird.Request) {
	for _, matcher := range spotifyMatchers {
		if s.handleTarget(r, matcher, matcher.uriRegex, r.Message.Trailing()) {
			return
		}
	}
}

func (s *spotifyProvider) HandleURL(r *seabird.Request, u *url.URL) bool {
	for _, matcher := range spotifyMatchers {
		if s.handleTarget(r, matcher, matcher.regex, u.Path) {
			return true
		}
	}

	return false
}

func (s *spotifyProvider) handleTarget(r *seabird.Request, matcher spotifyMatch, regex *regexp.Regexp, target string) bool {
	logger := r.GetLogger("url/spotify")

	if !regex.MatchString(target) {
		return false
	}

	matches := regex.FindStringSubmatch(target)
	if len(matches) != 2 {
		return false
	}

	data := matcher.lookup(s, logger, matches[1:])
	if data == nil {
		return false
	}

	return internal.RenderRespond(r.Reply, logger, matcher.template, spotifyPrefix, data)
}
<|endoftext|>


<commit_before>package bamstats

import (
	"bufio"
	"bytes"
	"fmt"
	"regexp"
	"testing"
)

func TestCreateIndex(t *testing.T) {
	elements := []byte(`chr1	11868	12227	exon
chr2	12612	12721	exon
chr3	12974	13052	exon
chr4	13220	14501	exon
chr5	15004	15038	exon
chr6	15795	15947	exon
chr7	16606	16765	exon
chr8	16857	17055	exon
chr9	17232	17436	exon
chr10	17605	17742	exon
chr11	17914	18061	exon
chr12	18267	18366	exon
chr13	24737	24891	exon
chr14	29533	30039	exon
chr15	30266	30667	exon
chr16	30975	31109	exon
`)
	index := createIndex(bufio.NewScanner(bytes.NewReader(elements)))
	l := len(*index)
	if l != 16 {
		t.Errorf("(createIndex) expected length 15, got %v", l)
	}
	for key, value := range *index {
		typeString := fmt.Sprintf("%T", value)
		if typeString != "*rtreego.Rtree" {
			t.Errorf("(createIndex) expected *rtreego.Rtree, got %v", typeString)
		}
		validChr := regexp.MustCompile(`^chr`)
		if !validChr.MatchString(key) {
			t.Errorf("(createIndex) expected chrN key, got %v", key)
		}
		indexSize := value.Size()
		if indexSize != 1 {
			t.Errorf("(createIndex) expected one value per chromosome, got %v", indexSize)
		}
	}
}

func TestQueryIndex(t *testing.T) {
	elements := []byte(`chr1	11868	12227	exon
chr1	11868	31109	gene
chr1	12227	12612	intron
chr1	12612	12721	exon
chr1	12721	12974	intron
chr1	12974	13052	exon
chr1	13052	13220	intron
chr1	13220	14501	exon
chr1	14501	15004	intron
chr1	15004	15038	exon
chr1	15038	15795	intron
chr1	15795	15947	exon
chr1	15947	16606	intron
chr1	16606	16765	exon
chr1	16765	16857	intron
chr1	16857	17055	exon
chr1	17055	17232	intron
chr1	17232	17436	exon
chr1	17436	17605	intron
chr1	17605	17742	exon
chr1	17742	17914	intron
chr1	17914	18061	exon
chr1	18061	18267	intron
chr1	18267	18366	exon
chr1	18366	24737	intron
chr1	24737	24891	exon
chr1	24891	29533	intron
chr1	29533	30039	exon
chr1	30039	30266	intron
chr1	30266	30667	exon
chr1	30667	30975	intron
chr1	30975	31109	exon
`)
	index := createIndex(bufio.NewScanner(bytes.NewReader(elements)))
	for _, item := range []struct {
		query          location
		expectedLength int
	}{
		{location{"chr1", 17145, 17234}, 3},
	} {
		results := QueryIndex(index.Get(item.query.Chrom()), item.query.Start(), item.query.End())

		l := len(results)
		if l != item.expectedLength {
			t.Errorf("(QueryIndex) expected %v, got %v results", item.expectedLength, l)
		}
	}
}
<commit_msg>Update annotation tests<commit_after>package bamstats

import (
	"bufio"
	"bytes"
	"fmt"
	"regexp"
	"testing"
)

func TestCreateIndex(t *testing.T) {
	elements := []byte(`chr1	11868	12227	exon
chr2	12612	12721	exon
chr3	12974	13052	exon
chr4	13220	14501	exon
chr5	15004	15038	exon
chr6	15795	15947	exon
chr7	16606	16765	exon
chr8	16857	17055	exon
chr9	17232	17436	exon
chr10	17605	17742	exon
chr11	17914	18061	exon
chr12	18267	18366	exon
chr13	24737	24891	exon
chr14	29533	30039	exon
chr15	30266	30667	exon
chr16	30975	31109	exon
`)
	index := createIndex(bufio.NewScanner(bytes.NewReader(elements)), 1)
	l := len(*index)
	if l != 16 {
		t.Errorf("(createIndex) expected length 15, got %v", l)
	}
	for key, value := range *index {
		typeString := fmt.Sprintf("%T", value)
		if typeString != "*rtreego.Rtree" {
			t.Errorf("(createIndex) expected *rtreego.Rtree, got %v", typeString)
		}
		validChr := regexp.MustCompile(`^chr`)
		if !validChr.MatchString(key) {
			t.Errorf("(createIndex) expected chrN key, got %v", key)
		}
		indexSize := value.Size()
		if indexSize != 1 {
			t.Errorf("(createIndex) expected one value per chromosome, got %v", indexSize)
		}
	}
}

func TestQueryIndex(t *testing.T) {
	elements := []byte(`chr1	11868	12227	exon
chr1	11868	31109	gene
chr1	12227	12612	intron
chr1	12612	12721	exon
chr1	12721	12974	intron
chr1	12974	13052	exon
chr1	13052	13220	intron
chr1	13220	14501	exon
chr1	14501	15004	intron
chr1	15004	15038	exon
chr1	15038	15795	intron
chr1	15795	15947	exon
chr1	15947	16606	intron
chr1	16606	16765	exon
chr1	16765	16857	intron
chr1	16857	17055	exon
chr1	17055	17232	intron
chr1	17232	17436	exon
chr1	17436	17605	intron
chr1	17605	17742	exon
chr1	17742	17914	intron
chr1	17914	18061	exon
chr1	18061	18267	intron
chr1	18267	18366	exon
chr1	18366	24737	intron
chr1	24737	24891	exon
chr1	24891	29533	intron
chr1	29533	30039	exon
chr1	30039	30266	intron
chr1	30266	30667	exon
chr1	30667	30975	intron
chr1	30975	31109	exon
`)
	index := createIndex(bufio.NewScanner(bytes.NewReader(elements)), 1)
	for _, item := range []struct {
		query          location
		expectedLength int
	}{
		{location{"chr1", 17145, 17234}, 3},
	} {
		results := QueryIndex(index.Get(item.query.Chrom()), item.query.Start(), item.query.End())

		l := len(results)
		if l != item.expectedLength {
			t.Errorf("(QueryIndex) expected %v, got %v results", item.expectedLength, l)
		}
	}
}
<|endoftext|>


<commit_before>package primitive

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/fogleman/gg"
)

type Rectangle struct {
	W, H   int
	X1, Y1 int
	X2, Y2 int
	rnd    *rand.Rand
}

func NewRandomRectangle(w, h int, rnd *rand.Rand) *Rectangle {
	x1 := rnd.Intn(w)
	y1 := rnd.Intn(h)
	x2 := rnd.Intn(w)
	y2 := rnd.Intn(h)
	return &Rectangle{w, h, x1, y1, x2, y2, rnd}
}

func (r *Rectangle) bounds() (x1, y1, x2, y2 int) {
	x1, y1 = r.X1, r.Y1
	x2, y2 = r.X2, r.Y2
	if x1 > x2 {
		x1, x2 = x2, x1
	}
	if y1 > y2 {
		y1, y2 = y2, y1
	}
	return
}

func (r *Rectangle) Draw(dc *gg.Context) {
	x1, y1, x2, y2 := r.bounds()
	dc.DrawRectangle(float64(x1), float64(y1), float64(x2-x1+1), float64(y2-y1+1))
}

func (r *Rectangle) SVG(attrs string) string {
	x1, y1, x2, y2 := r.bounds()
	w := x2 - x1 + 1
	h := y2 - y1 + 1
	return fmt.Sprintf(
		"<rect %s x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" />",
		attrs, x1, y1, w, h)
}

func (r *Rectangle) Copy() Shape {
	a := *r
	return &a
}

func (r *Rectangle) Mutate() {
	rnd := r.rnd
	switch rnd.Intn(2) {
	case 0:
		r.X1 = clampInt(r.X1+rnd.Intn(21)-10, 0, r.W-1)
		r.Y1 = clampInt(r.Y1+rnd.Intn(21)-10, 0, r.H-1)
	case 1:
		r.X2 = clampInt(r.X2+rnd.Intn(21)-10, 0, r.W-1)
		r.Y2 = clampInt(r.Y2+rnd.Intn(21)-10, 0, r.H-1)
	}
}

func (r *Rectangle) Rasterize() []Scanline {
	x1, y1, x2, y2 := r.bounds()
	lines := make([]Scanline, y2-y1+1)
	i := 0
	for y := y1; y <= y2; y++ {
		lines[i] = Scanline{y, x1, x2}
		i++
	}
	return lines
}

type RotatedRectangle struct {
	W, H   int
	X, Y   int
	Sx, Sy int
	Angle  int
	rnd    *rand.Rand
}

func NewRandomRotatedRectangle(w, h int, rnd *rand.Rand) *RotatedRectangle {
	x := rnd.Intn(w)
	y := rnd.Intn(h)
	sx := rnd.Intn(w / 2)
	sy := rnd.Intn(h / 2)
	a := rnd.Intn(360)
	r := &RotatedRectangle{w, h, x, y, sx, sy, a, rnd}
	r.Mutate()
	return r
}

func (r *RotatedRectangle) Draw(dc *gg.Context) {
	sx, sy := float64(r.Sx), float64(r.Sy)
	dc.Push()
	dc.Translate(float64(r.X), float64(r.Y))
	dc.Rotate(radians(float64(r.Angle)))
	dc.DrawRectangle(-sx/2, -sy/2, sx, sy)
	dc.Pop()
}

func (r *RotatedRectangle) SVG(attrs string) string {
	return fmt.Sprintf(
		"<g transform=\"translate(%d %d) rotate(%d) scale(%d %d)\"><rect %s x=\"-0.5\" y=\"-0.5\" width=\"1\" height=\"1\" /></g>",
		r.X, r.Y, r.Angle, r.Sx, r.Sy, attrs)
}

func (r *RotatedRectangle) Copy() Shape {
	a := *r
	return &a
}

func (r *RotatedRectangle) Mutate() {
	rnd := r.rnd
	switch rnd.Intn(3) {
	case 0:
		r.X = clampInt(r.X+rnd.Intn(21)-10, 0, r.W-1)
		r.Y = clampInt(r.Y+rnd.Intn(21)-10, 0, r.H-1)
	case 1:
		r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1)
		r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1)
	case 2:
		r.Angle = r.Angle + rnd.Intn(41) - 20
	}
	for !r.Valid() {
		r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1)
		r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1)
	}
}

func (r *RotatedRectangle) Valid() bool {
	a, b := r.Sx, r.Sy
	if a < b {
		a, b = b, a
	}
	aspect := float64(a) / float64(b)
	return aspect <= 5
}

func (r *RotatedRectangle) Rasterize() []Scanline {
	sx, sy := float64(r.Sx), float64(r.Sy)
	angle := radians(float64(r.Angle))
	rx1, ry1 := rotate(-sx/2, -sy/2, angle)
	rx2, ry2 := rotate(sx/2, -sy/2, angle)
	rx3, ry3 := rotate(sx/2, sy/2, angle)
	rx4, ry4 := rotate(-sx/2, sy/2, angle)
	x1, y1 := int(rx1)+r.X, int(ry1)+r.Y
	x2, y2 := int(rx2)+r.X, int(ry2)+r.Y
	x3, y3 := int(rx3)+r.X, int(ry3)+r.Y
	x4, y4 := int(rx4)+r.X, int(ry4)+r.Y
	miny := minInt(y1, minInt(y2, minInt(y3, y4)))
	maxy := maxInt(y1, maxInt(y2, maxInt(y3, y4)))
	n := maxy - miny + 1
	min := make([]int, n)
	max := make([]int, n)
	for i := range min {
		min[i] = r.W
	}
	xs := []int{x1, x2, x3, x4, x1}
	ys := []int{y1, y2, y3, y4, y1}
	// TODO: this could be better probably
	for i := 0; i < 4; i++ {
		x, y := float64(xs[i]), float64(ys[i])
		dx, dy := float64(xs[i+1]-xs[i]), float64(ys[i+1]-ys[i])
		count := int(math.Sqrt(dx*dx+dy*dy)) * 2
		for j := 0; j < count; j++ {
			t := float64(j) / float64(count-1)
			xi := int(x + dx*t)
			yi := int(y+dy*t) - miny
			min[yi] = minInt(min[yi], xi)
			max[yi] = maxInt(max[yi], xi)
		}
	}
	lines := make([]Scanline, 0, n)
	for i := 0; i < n; i++ {
		y := miny + i
		if y < 0 || y >= r.H {
			continue
		}
		a := maxInt(min[i], 0)
		b := minInt(max[i], r.W-1)
		lines = append(lines, Scanline{y, a, b})
	}
	return lines
}
<commit_msg>bug fix<commit_after>package primitive

import (
	"fmt"
	"math"
	"math/rand"

	"github.com/fogleman/gg"
)

type Rectangle struct {
	W, H   int
	X1, Y1 int
	X2, Y2 int
	rnd    *rand.Rand
}

func NewRandomRectangle(w, h int, rnd *rand.Rand) *Rectangle {
	x1 := rnd.Intn(w)
	y1 := rnd.Intn(h)
	x2 := rnd.Intn(w)
	y2 := rnd.Intn(h)
	return &Rectangle{w, h, x1, y1, x2, y2, rnd}
}

func (r *Rectangle) bounds() (x1, y1, x2, y2 int) {
	x1, y1 = r.X1, r.Y1
	x2, y2 = r.X2, r.Y2
	if x1 > x2 {
		x1, x2 = x2, x1
	}
	if y1 > y2 {
		y1, y2 = y2, y1
	}
	return
}

func (r *Rectangle) Draw(dc *gg.Context) {
	x1, y1, x2, y2 := r.bounds()
	dc.DrawRectangle(float64(x1), float64(y1), float64(x2-x1+1), float64(y2-y1+1))
}

func (r *Rectangle) SVG(attrs string) string {
	x1, y1, x2, y2 := r.bounds()
	w := x2 - x1 + 1
	h := y2 - y1 + 1
	return fmt.Sprintf(
		"<rect %s x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" />",
		attrs, x1, y1, w, h)
}

func (r *Rectangle) Copy() Shape {
	a := *r
	return &a
}

func (r *Rectangle) Mutate() {
	rnd := r.rnd
	switch rnd.Intn(2) {
	case 0:
		r.X1 = clampInt(r.X1+rnd.Intn(21)-10, 0, r.W-1)
		r.Y1 = clampInt(r.Y1+rnd.Intn(21)-10, 0, r.H-1)
	case 1:
		r.X2 = clampInt(r.X2+rnd.Intn(21)-10, 0, r.W-1)
		r.Y2 = clampInt(r.Y2+rnd.Intn(21)-10, 0, r.H-1)
	}
}

func (r *Rectangle) Rasterize() []Scanline {
	x1, y1, x2, y2 := r.bounds()
	lines := make([]Scanline, y2-y1+1)
	i := 0
	for y := y1; y <= y2; y++ {
		lines[i] = Scanline{y, x1, x2}
		i++
	}
	return lines
}

type RotatedRectangle struct {
	W, H   int
	X, Y   int
	Sx, Sy int
	Angle  int
	rnd    *rand.Rand
}

func NewRandomRotatedRectangle(w, h int, rnd *rand.Rand) *RotatedRectangle {
	x := rnd.Intn(w)
	y := rnd.Intn(h)
	sx := rnd.Intn(w / 2)
	sy := rnd.Intn(h / 2)
	a := rnd.Intn(360)
	r := &RotatedRectangle{w, h, x, y, sx, sy, a, rnd}
	r.Mutate()
	return r
}

func (r *RotatedRectangle) Draw(dc *gg.Context) {
	sx, sy := float64(r.Sx), float64(r.Sy)
	dc.Push()
	dc.Translate(float64(r.X), float64(r.Y))
	dc.Rotate(radians(float64(r.Angle)))
	dc.DrawRectangle(-sx/2, -sy/2, sx, sy)
	dc.Pop()
}

func (r *RotatedRectangle) SVG(attrs string) string {
	return fmt.Sprintf(
		"<g transform=\"translate(%d %d) rotate(%d) scale(%d %d)\"><rect %s x=\"-0.5\" y=\"-0.5\" width=\"1\" height=\"1\" /></g>",
		r.X, r.Y, r.Angle, r.Sx, r.Sy, attrs)
}

func (r *RotatedRectangle) Copy() Shape {
	a := *r
	return &a
}

func (r *RotatedRectangle) Mutate() {
	rnd := r.rnd
	switch rnd.Intn(3) {
	case 0:
		r.X = clampInt(r.X+rnd.Intn(21)-10, 0, r.W-1)
		r.Y = clampInt(r.Y+rnd.Intn(21)-10, 0, r.H-1)
	case 1:
		r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1)
		r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1)
	case 2:
		r.Angle = r.Angle + rnd.Intn(41) - 20
	}
	for !r.Valid() {
		r.Sx = clampInt(r.Sx+rnd.Intn(21)-10, 0, r.W-1)
		r.Sy = clampInt(r.Sy+rnd.Intn(21)-10, 0, r.H-1)
	}
}

func (r *RotatedRectangle) Valid() bool {
	a, b := r.Sx, r.Sy
	if a < b {
		a, b = b, a
	}
	aspect := float64(a) / float64(b)
	return aspect <= 5
}

func (r *RotatedRectangle) Rasterize() []Scanline {
	sx, sy := float64(r.Sx), float64(r.Sy)
	angle := radians(float64(r.Angle))
	rx1, ry1 := rotate(-sx/2, -sy/2, angle)
	rx2, ry2 := rotate(sx/2, -sy/2, angle)
	rx3, ry3 := rotate(sx/2, sy/2, angle)
	rx4, ry4 := rotate(-sx/2, sy/2, angle)
	x1, y1 := int(rx1)+r.X, int(ry1)+r.Y
	x2, y2 := int(rx2)+r.X, int(ry2)+r.Y
	x3, y3 := int(rx3)+r.X, int(ry3)+r.Y
	x4, y4 := int(rx4)+r.X, int(ry4)+r.Y
	miny := minInt(y1, minInt(y2, minInt(y3, y4)))
	maxy := maxInt(y1, maxInt(y2, maxInt(y3, y4)))
	n := maxy - miny + 1
	min := make([]int, n)
	max := make([]int, n)
	for i := range min {
		min[i] = r.W
	}
	xs := []int{x1, x2, x3, x4, x1}
	ys := []int{y1, y2, y3, y4, y1}
	// TODO: this could be better probably
	for i := 0; i < 4; i++ {
		x, y := float64(xs[i]), float64(ys[i])
		dx, dy := float64(xs[i+1]-xs[i]), float64(ys[i+1]-ys[i])
		count := int(math.Sqrt(dx*dx+dy*dy)) * 2
		for j := 0; j < count; j++ {
			t := float64(j) / float64(count-1)
			xi := int(x + dx*t)
			yi := int(y+dy*t) - miny
			min[yi] = minInt(min[yi], xi)
			max[yi] = maxInt(max[yi], xi)
		}
	}
	lines := make([]Scanline, 0, n)
	for i := 0; i < n; i++ {
		y := miny + i
		if y < 0 || y >= r.H {
			continue
		}
		a := maxInt(min[i], 0)
		b := minInt(max[i], r.W-1)
		if b >= a {
			lines = append(lines, Scanline{y, a, b})
		}
	}
	return lines
}
<|endoftext|>


<commit_before>package processor

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/krallistic/kafka-operator/controller"
	"github.com/krallistic/kafka-operator/kafka"
	spec "github.com/krallistic/kafka-operator/spec"
	"github.com/krallistic/kafka-operator/util"
	k8sclient "k8s.io/client-go/kubernetes"
	"time"
)

type Processor struct {
	client          k8sclient.Clientset
	baseBrokerImage string
	util            util.ClientUtil
	tprController   controller.ThirdPartyResourceController
	kafkaClusters   map[string]*spec.KafkaCluster
	watchEvents     chan spec.KafkaClusterWatchEvent
	clusterEvents   chan spec.KafkaClusterEvent
	kafkaClient     map[string]*kafka.KafkaUtil
	control         chan int
	errors          chan error
}

func New(client k8sclient.Clientset, image string, util util.ClientUtil, tprClient controller.ThirdPartyResourceController, control chan int) (*Processor, error) {
	p := &Processor{
		client:          client,
		baseBrokerImage: image,
		util:            util,
		kafkaClusters:   make(map[string]*spec.KafkaCluster),
		watchEvents:     make(chan spec.KafkaClusterWatchEvent, 100),
		clusterEvents:   make(chan spec.KafkaClusterEvent, 100),
		tprController:   tprClient,
		kafkaClient:     make(map[string]*kafka.KafkaUtil),
		control:         control,
		errors:          make(chan error),
	}
	fmt.Println("Created Processor")
	return p, nil
}

func (p *Processor) Run() error {
	//TODO getListOfAlredyRunningCluster/Refresh
	fmt.Println("Running Processor")
	p.watchKafkaEvents()

	return nil
}

//We detect basic change through the event type, beyond that we use the API server to find differences.
//Functions compares the KafkaClusterSpec with the real Pods/Services which are there.
//We do that because otherwise we would have to use a local state to track changes.
func (p *Processor) DetectChangeType(event spec.KafkaClusterWatchEvent) spec.KafkaClusterEvent {
	fmt.Println("DetectChangeType: ", event)

	//TODO multiple changes in one Update? right now we only detect one change
	clusterEvent := spec.KafkaClusterEvent{
		Cluster: event.Object,
	}
	if event.Type == "ADDED" {
		clusterEvent.Type = spec.NEW_CLUSTER
		return clusterEvent
	}
	if event.Type == "DELETED" {
		clusterEvent.Type = spec.DELTE_CLUSTER
		return clusterEvent
		//EVENT type must be modfied now
	} else if p.util.BrokerStatefulSetExist(event.Object) {
		clusterEvent.Type = spec.UNKNOWN_CHANGE
		//TODO change to reconsilation event?
		return clusterEvent
	} else if p.util.BrokerStSImageUpdate(event.Object) {
		clusterEvent.Type = spec.CHANGE_IMAGE
		return clusterEvent
	} else if p.util.BrokerStSUpsize(event.Object) {
		clusterEvent.Type = spec.UPSIZE_CLUSTER
		return clusterEvent
	} else if p.util.BrokerStSDownsize(event.Object) {
		fmt.Println("No Downsizing currently supported, TODO without dataloss?")
		clusterEvent.Type = spec.DOWNSIZE_CLUSTER
		return clusterEvent
	}

	//check IfClusterExist -> NEW_CLUSTER
	//check if Image/TAG same -> Change_IMAGE
	//check if BrokerCount same -> Down/Upsize Cluster

	clusterEvent.Type = spec.UNKNOWN_CHANGE
	return clusterEvent
}

func (p *Processor) initKafkaClient(cluster spec.KafkaCluster) error {
	methodLogger := log.WithFields(log.Fields{
		"method":            "initKafkaClient",
		"clusterName":       cluster.Metadata.Name,
		"zookeeperConnectL": cluster.Spec.ZookeeperConnect,
	})
	methodLogger.Info("Creating KafkaCLient for cluster")

	//TODO
	brokerList := util.GetBrokerAdressess(cluster)

	client, err := kafka.New(brokerList, cluster.Metadata.Name)
	if err != nil {
		return err
	}

	//TODO can metadata.uuid used? check how that changed
	name := cluster.Metadata.Namespace + "-" + cluster.Metadata.Name
	p.kafkaClient[name] = client

	methodLogger.Info("Create KakfaClient for cluser")
	return nil
}

//Takes in raw Kafka events, lets then detected and the proced to initiate action accoriding to the detected event.
func (p *Processor) processKafkaEvent(currentEvent spec.KafkaClusterEvent) {
	fmt.Println("Recieved Event, proceeding: ", currentEvent)
	switch currentEvent.Type {
	case spec.NEW_CLUSTER:
		fmt.Println("ADDED")
		clustersTotal.Inc()
		clustersCreated.Inc()
		p.CreateKafkaCluster(currentEvent.Cluster)
		go func() {
			fmt.Println("Init heartbeat type checking...")
			time.Sleep(30 * time.Second)
			clusterEvent := spec.KafkaClusterEvent{
				Cluster: currentEvent.Cluster,
				Type:    spec.KAKFA_EVENT,
			}
			p.clusterEvents <- clusterEvent
		}()
		break

	case spec.DELTE_CLUSTER:
		fmt.Println("Delete Cluster, deleting all Objects: ", currentEvent.Cluster, currentEvent.Cluster.Spec)
		if p.util.DeleteKafkaCluster(currentEvent.Cluster) != nil {
			//Error while deleting, just resubmit event after wait time.
			go func() {
				time.Sleep(30 * time.Second)
				p.clusterEvents <- currentEvent
			}()
			break
		}

		go func() {
			time.Sleep(time.Duration(currentEvent.Cluster.Spec.BrokerCount) * time.Minute)
			//TODO dynamic sleep, depending till sts is completely scaled down.
			clusterEvent := spec.KafkaClusterEvent{
				Cluster: currentEvent.Cluster,
				Type:    spec.CLEANUP_EVENT,
			}
			p.clusterEvents <- clusterEvent
		}()
		clustersTotal.Dec()
		clustersDeleted.Inc()
	case spec.CHANGE_IMAGE:
		fmt.Println("Change Image, updating StatefulSet should be enoguh to trigger a new Image Rollout")
		if p.util.UpdateBrokerImage(currentEvent.Cluster) != nil {
			//Error updating
			go func() {
				time.Sleep(30 * time.Second)
				p.clusterEvents <- currentEvent
			}()
			break
		}
		clustersModified.Inc()
	case spec.UPSIZE_CLUSTER:
		fmt.Println("Upsize Cluster, changing StewtefulSet with higher Replicas, no Rebalacing")
		p.util.UpsizeBrokerStS(currentEvent.Cluster)
		clustersModified.Inc()
	case spec.UNKNOWN_CHANGE:
		fmt.Println("Unkown (or unsupported) change occured, doing nothing. Maybe manually check the cluster")
		clustersModified.Inc()
	case spec.DOWNSIZE_CLUSTER:
		fmt.Println("Downsize Cluster")
		clustersModified.Inc()
	case spec.CHANGE_ZOOKEEPER_CONNECT:
		fmt.Println("Trying to change zookeeper connect, not supported currently")
		clustersModified.Inc()
	case spec.CLEANUP_EVENT:
		fmt.Println("Recieved CleanupEvent, force delete of StatefuleSet.")
		clustersModified.Inc()
	case spec.KAKFA_EVENT:
		fmt.Println("Kafka Event, checking now that topics exist etc..")
		go func() {
			time.Sleep(30 * time.Second)
			p.clusterEvents <- currentEvent
		}()
		name := currentEvent.Cluster.Metadata.Namespace + "-" + currentEvent.Cluster.Metadata.Name
		p.kafkaClient[name].PrintFullStats()

	}
}

//Creates inside a goroutine a watch channel on the KafkaCLuster Endpoint and distibutes the events.
//control chan used for showdown events from outside
func (p *Processor) watchKafkaEvents() {

	p.tprController.MonitorKafkaEvents(p.watchEvents, p.control)
	fmt.Println("Watching Kafka Events")
	go func() {
		for {

			select {
			case currentEvent := <-p.watchEvents:
				classifiedEvent := p.DetectChangeType(currentEvent)
				p.clusterEvents <- classifiedEvent
			case clusterEvent := <-p.clusterEvents:
				p.processKafkaEvent(clusterEvent)
			case err := <-p.errors:
				println("Error Channel", err)
			case <-p.control:
				fmt.Println("Recieved Something on Control Channel, shutting down: ")
				return
			}
		}
	}()
}

//Create the KafkaCluster, with the following components: Service, Volumes, StatefulSet.
//Maybe move this also into util
func (p *Processor) CreateKafkaCluster(clusterSpec spec.KafkaCluster) {
	fmt.Println("CreatingKafkaCluster", clusterSpec)
	fmt.Println("SPEC: ", clusterSpec.Spec)

	suffix := ".cluster.local:9092"
	brokerNames := make([]string, clusterSpec.Spec.BrokerCount)

	headless_SVC_Name := clusterSpec.Metadata.Name
	round_robing_dns := headless_SVC_Name + suffix
	fmt.Println("Headless Service Name: ", headless_SVC_Name, " Should be accessable through LB: ", round_robing_dns)

	var i int32
	for i = 0; i < clusterSpec.Spec.BrokerCount; i++ {
		brokerNames[i] = "kafka-0." + headless_SVC_Name + suffix
		fmt.Println("Broker", i, " ServiceName: ", brokerNames[i])
	}

	//Create Headless Brokersvc
	//TODO better naming
	p.util.CreateBrokerService(clusterSpec, true)

	//TODO createVolumes

	//CREATE Broker sts
	//Currently we extract name out of spec, maybe move to metadata to be more inline with other k8s komponents.
	p.util.CreateBrokerStatefulSet(clusterSpec)

	p.util.CreateDirectBrokerService(clusterSpec)

	p.initKafkaClient(clusterSpec)

}
<commit_msg>Working processor<commit_after>package processor

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/krallistic/kafka-operator/controller"
	"github.com/krallistic/kafka-operator/kafka"
	spec "github.com/krallistic/kafka-operator/spec"
	"github.com/krallistic/kafka-operator/util"
	k8sclient "k8s.io/client-go/kubernetes"
	"time"
)

type Processor struct {
	client          k8sclient.Clientset
	baseBrokerImage string
	util            util.ClientUtil
	tprController   controller.ThirdPartyResourceController
	kafkaClusters   map[string]*spec.KafkaCluster
	watchEvents     chan spec.KafkaClusterWatchEvent
	clusterEvents   chan spec.KafkaClusterEvent
	kafkaClient     map[string]*kafka.KafkaUtil
	control         chan int
	errors          chan error
}

func New(client k8sclient.Clientset, image string, util util.ClientUtil, tprClient controller.ThirdPartyResourceController, control chan int) (*Processor, error) {
	p := &Processor{
		client:          client,
		baseBrokerImage: image,
		util:            util,
		kafkaClusters:   make(map[string]*spec.KafkaCluster),
		watchEvents:     make(chan spec.KafkaClusterWatchEvent, 100),
		clusterEvents:   make(chan spec.KafkaClusterEvent, 100),
		tprController:   tprClient,
		kafkaClient:     make(map[string]*kafka.KafkaUtil),
		control:         control,
		errors:          make(chan error),
	}
	fmt.Println("Created Processor")
	return p, nil
}

func (p *Processor) Run() error {
	//TODO getListOfAlredyRunningCluster/Refresh
	fmt.Println("Running Processor")
	p.watchKafkaEvents()

	return nil
}

//We detect basic change through the event type, beyond that we use the API server to find differences.
//Functions compares the KafkaClusterSpec with the real Pods/Services which are there.
//We do that because otherwise we would have to use a local state to track changes.
func (p *Processor) DetectChangeType(event spec.KafkaClusterWatchEvent) spec.KafkaClusterEvent {
	fmt.Println("DetectChangeType: ", event)

	//TODO multiple changes in one Update? right now we only detect one change
	clusterEvent := spec.KafkaClusterEvent{
		Cluster: event.Object,
	}
	if event.Type == "ADDED" {
		clusterEvent.Type = spec.NEW_CLUSTER
		return clusterEvent
	}
	if event.Type == "DELETED" {
		clusterEvent.Type = spec.DELTE_CLUSTER
		return clusterEvent
		//EVENT type must be modfied now
	}  else if p.util.BrokerStSImageUpdate(event.Object) {
		clusterEvent.Type = spec.CHANGE_IMAGE
		return clusterEvent
	} else if p.util.BrokerStSUpsize(event.Object) {
		clusterEvent.Type = spec.UPSIZE_CLUSTER
		return clusterEvent
	} else if p.util.BrokerStSDownsize(event.Object) {
		clusterEvent.Type = spec.DOWNSIZE_CLUSTER
		return clusterEvent
	} else if p.util.BrokerStatefulSetExist(event.Object) {
		clusterEvent.Type = spec.UNKNOWN_CHANGE
		//TODO change to reconsilation event?
		return clusterEvent
	}

	//check IfClusterExist -> NEW_CLUSTER
	//check if Image/TAG same -> Change_IMAGE
	//check if BrokerCount same -> Down/Upsize Cluster

	clusterEvent.Type = spec.UNKNOWN_CHANGE
	return clusterEvent
}

func (p *Processor) initKafkaClient(cluster spec.KafkaCluster) error {
	methodLogger := log.WithFields(log.Fields{
		"method":            "initKafkaClient",
		"clusterName":       cluster.Metadata.Name,
		"zookeeperConnectL": cluster.Spec.ZookeeperConnect,
	})
	methodLogger.Info("Creating KafkaCLient for cluster")

	//TODO
	brokerList := util.GetBrokerAdressess(cluster)

	client, err := kafka.New(brokerList, cluster.Metadata.Name)
	if err != nil {
		return err
	}

	//TODO can metadata.uuid used? check how that changed
	name := cluster.Metadata.Namespace + "-" + cluster.Metadata.Name
	p.kafkaClient[name] = client

	methodLogger.Info("Create KakfaClient for cluser")
	return nil
}

//Takes in raw Kafka events, lets then detected and the proced to initiate action accoriding to the detected event.
func (p *Processor) processKafkaEvent(currentEvent spec.KafkaClusterEvent) {
	fmt.Println("Recieved Event, proceeding: ", currentEvent)
	switch currentEvent.Type {
	case spec.NEW_CLUSTER:
		fmt.Println("ADDED")
		clustersTotal.Inc()
		clustersCreated.Inc()
		p.CreateKafkaCluster(currentEvent.Cluster)
		go func() {
			fmt.Println("Init heartbeat type checking...")
			time.Sleep(30 * time.Second)
			clusterEvent := spec.KafkaClusterEvent{
				Cluster: currentEvent.Cluster,
				Type:    spec.KAKFA_EVENT,
			}
			p.clusterEvents <- clusterEvent
		}()
		break

	case spec.DELTE_CLUSTER:
		fmt.Println("Delete Cluster, deleting all Objects: ", currentEvent.Cluster, currentEvent.Cluster.Spec)
		if p.util.DeleteKafkaCluster(currentEvent.Cluster) != nil {
			//Error while deleting, just resubmit event after wait time.
			go func() {
				time.Sleep(30 * time.Second)
				p.clusterEvents <- currentEvent
			}()
			break
		}

		go func() {
			time.Sleep(time.Duration(currentEvent.Cluster.Spec.BrokerCount) * time.Minute)
			//TODO dynamic sleep, depending till sts is completely scaled down.
			clusterEvent := spec.KafkaClusterEvent{
				Cluster: currentEvent.Cluster,
				Type:    spec.CLEANUP_EVENT,
			}
			p.clusterEvents <- clusterEvent
		}()
		clustersTotal.Dec()
		clustersDeleted.Inc()
	case spec.CHANGE_IMAGE:
		fmt.Println("Change Image, updating StatefulSet should be enoguh to trigger a new Image Rollout")
		if p.util.UpdateBrokerImage(currentEvent.Cluster) != nil {
			//Error updating
			go func() {
				time.Sleep(30 * time.Second)
				p.clusterEvents <- currentEvent
			}()
			break
		}
		clustersModified.Inc()
	case spec.UPSIZE_CLUSTER:
		fmt.Println("Upsize Cluster, changing StatefulSet with higher Replicas, no Rebalacing")
		p.util.UpsizeBrokerStS(currentEvent.Cluster)
		clustersModified.Inc()
	case spec.UNKNOWN_CHANGE:
		fmt.Println("Unkown (or unsupported) change occured, doing nothing. Maybe manually check the cluster")
		clustersModified.Inc()
	case spec.DOWNSIZE_CLUSTER:
		fmt.Println("Downsize Cluster")
		//TODO remove poor mans casting :P
		//TODO support Downsizing Multiple Brokers
		brokerToDelete := currentEvent.Cluster.Spec.BrokerCount - 0
		fmt.Println("Downsizing Broker, deleting Data on Broker: ", brokerToDelete)
		p.util.SetBrokerState(currentEvent.Cluster, brokerToDelete, "deleting")

		clustersModified.Inc()
	case spec.CHANGE_ZOOKEEPER_CONNECT:
		fmt.Println("Trying to change zookeeper connect, not supported currently")
		clustersModified.Inc()
	case spec.CLEANUP_EVENT:
		fmt.Println("Recieved CleanupEvent, force delete of StatefuleSet.")
		clustersModified.Inc()
	case spec.KAKFA_EVENT:
		fmt.Println("Kafka Event, heartbeat etc..")
		go func() {
			time.Sleep(30 * time.Second)
			p.clusterEvents <- currentEvent
		}()

		//states := p.util.GetPodAnnotations(currentEvent.Cluster)
		//name := currentEvent.Cluster.Metadata.Namespace + "-" + currentEvent.Cluster.Metadata.Name
		//p.kafkaClient[name].PrintFullStats()

	}
}

func (p *Processor) EmptyingBroker(cluster spec.KafkaCluster, states []string) error {

	for i, state := range states {
		fmt.Println("State, Index: ", state, i)
		if state == "toDelete" {
			// EMPTY Broker,
			// generate Downsize Options
			// Save downsize option and store in k8s
		} else if state == "deleting" {
			//get downsize option from k8s
			//check if downsize done
		} else if state == "deleted" {
			//downsize Broker

		} else {
			//DO nothing?
		}
	}

	return nil
}

//Creates inside a goroutine a watch channel on the KafkaCLuster Endpoint and distibutes the events.
//control chan used for showdown events from outside
func (p *Processor) watchKafkaEvents() {

	p.tprController.MonitorKafkaEvents(p.watchEvents, p.control)
	fmt.Println("Watching Kafka Events")
	go func() {
		for {

			select {
			case currentEvent := <-p.watchEvents:
				classifiedEvent := p.DetectChangeType(currentEvent)
				p.clusterEvents <- classifiedEvent
			case clusterEvent := <-p.clusterEvents:
				p.processKafkaEvent(clusterEvent)
			case err := <-p.errors:
				println("Error Channel", err)
			case <-p.control:
				fmt.Println("Recieved Something on Control Channel, shutting down: ")
				return
			}
		}
	}()
}

//Create the KafkaCluster, with the following components: Service, Volumes, StatefulSet.
//Maybe move this also into util
func (p *Processor) CreateKafkaCluster(clusterSpec spec.KafkaCluster) {
	fmt.Println("CreatingKafkaCluster", clusterSpec)
	fmt.Println("SPEC: ", clusterSpec.Spec)

	suffix := ".cluster.local:9092"
	brokerNames := make([]string, clusterSpec.Spec.BrokerCount)

	headless_SVC_Name := clusterSpec.Metadata.Name
	round_robing_dns := headless_SVC_Name + suffix
	fmt.Println("Headless Service Name: ", headless_SVC_Name, " Should be accessable through LB: ", round_robing_dns)

	var i int32
	for i = 0; i < clusterSpec.Spec.BrokerCount; i++ {
		brokerNames[i] = "kafka-0." + headless_SVC_Name + suffix
		fmt.Println("Broker", i, " ServiceName: ", brokerNames[i])
	}

	//Create Headless Brokersvc
	//TODO better naming
	p.util.CreateBrokerService(clusterSpec, true)

	//TODO createVolumes

	//CREATE Broker sts
	//Currently we extract name out of spec, maybe move to metadata to be more inline with other k8s komponents.
	p.util.CreateBrokerStatefulSet(clusterSpec)

	p.util.CreateDirectBrokerService(clusterSpec)

	p.initKafkaClient(clusterSpec)

}
<|endoftext|>


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

package api

const (
	SQL_OV_ODBC3 = 3

	SQL_ATTR_ODBC_VERSION = 200

	SQL_DRIVER_NOPROMPT = 0

	SQL_HANDLE_ENV  = 1
	SQL_HANDLE_DBC  = 2
	SQL_HANDLE_STMT = 3

	SQL_SUCCESS            = 0
	SQL_SUCCESS_WITH_INFO  = 1
	SQL_INVALID_HANDLE     = -2
	SQL_NO_DATA            = 100
	SQL_NO_TOTAL           = -4
	SQL_NTS                = -3
	SQL_MAX_MESSAGE_LENGTH = 512
	SQL_NULL_HANDLE        = 0
	SQL_NULL_HENV          = 0
	SQL_NULL_HDBC          = 0
	SQL_NULL_HSTMT         = 0

	SQL_PARAM_INPUT = 1

	SQL_NULL_DATA    = -1
	SQL_DATA_AT_EXEC = -2

	SQL_UNKNOWN_TYPE    = 0
	SQL_CHAR            = 1
	SQL_NUMERIC         = 2
	SQL_DECIMAL         = 3
	SQL_INTEGER         = 4
	SQL_SMALLINT        = 5
	SQL_FLOAT           = 6
	SQL_REAL            = 7
	SQL_DOUBLE          = 8
	SQL_DATETIME        = 9
	SQL_DATE            = 9
	SQL_TIME            = 10
	SQL_VARCHAR         = 12
	SQL_TYPE_DATE       = 91
	SQL_TYPE_TIME       = 92
	SQL_TYPE_TIMESTAMP  = 93
	SQL_TIMESTAMP       = 11
	SQL_LONGVARCHAR     = -1
	SQL_BINARY          = -2
	SQL_VARBINARY       = -3
	SQL_LONGVARBINARY   = -4
	SQL_BIGINT          = -5
	SQL_TINYINT         = -6
	SQL_BIT             = -7
	SQL_WCHAR           = -8
	SQL_WVARCHAR        = -9
	SQL_WLONGVARCHAR    = -10
	SQL_GUID            = -11
	SQL_SIGNED_OFFSET   = -20
	SQL_UNSIGNED_OFFSET = -22
	SQL_SS_XML          = -152

	SQL_C_CHAR           = SQL_CHAR
	SQL_C_LONG           = SQL_INTEGER
	SQL_C_SHORT          = SQL_SMALLINT
	SQL_C_FLOAT          = SQL_REAL
	SQL_C_DOUBLE         = SQL_DOUBLE
	SQL_C_NUMERIC        = SQL_NUMERIC
	SQL_C_DATE           = SQL_DATE
	SQL_C_TIME           = SQL_TIME
	SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP
	SQL_C_TIMESTAMP      = SQL_TIMESTAMP
	SQL_C_BINARY         = SQL_BINARY
	SQL_C_BIT            = SQL_BIT
	SQL_C_WCHAR          = SQL_WCHAR
	SQL_C_DEFAULT        = 99
	SQL_C_SBIGINT        = SQL_BIGINT + SQL_SIGNED_OFFSET
	SQL_C_UBIGINT        = SQL_BIGINT + SQL_UNSIGNED_OFFSET
	SQL_C_GUID           = SQL_GUID

	SQL_COMMIT   = 0
	SQL_ROLLBACK = 1

	SQL_AUTOCOMMIT         = 102
	SQL_ATTR_AUTOCOMMIT    = SQL_AUTOCOMMIT
	SQL_AUTOCOMMIT_OFF     = 0
	SQL_AUTOCOMMIT_ON      = 1
	SQL_AUTOCOMMIT_DEFAULT = SQL_AUTOCOMMIT_ON

	SQL_IS_UINTEGER = -5

	//Connection pooling
	SQL_ATTR_CONNECTION_POOLING = 201
	SQL_ATTR_CP_MATCH           = 202
	SQL_CP_OFF                  = 0
	SQL_CP_ONE_PER_DRIVER       = 1
	SQL_CP_ONE_PER_HENV         = 2
	SQL_CP_DEFAULT              = SQL_CP_OFF
	SQL_CP_STRICT_MATCH         = 0
	SQL_CP_RELAXED_MATCH        = 1
)

type (
	SQLHANDLE uintptr
	SQLHENV   SQLHANDLE
	SQLHDBC   SQLHANDLE
	SQLHSTMT  SQLHANDLE
	SQLHWND   uintptr

	SQLWCHAR     uint16
	SQLSCHAR     int8
	SQLSMALLINT  int16
	SQLUSMALLINT uint16
	SQLINTEGER   int32
	SQLUINTEGER  uint32
	SQLPOINTER   uintptr
	SQLRETURN    SQLSMALLINT

	SQLGUID struct {
		Data1 uint32
		Data2 uint16
		Data3 uint16
		Data4 [8]byte
	}
)
<commit_msg>odbc: treat SQLPOINTER as unsafe.Pointer on windows<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package api

import "unsafe"

const (
	SQL_OV_ODBC3 = uintptr(3)

	SQL_ATTR_ODBC_VERSION = 200

	SQL_DRIVER_NOPROMPT = 0

	SQL_HANDLE_ENV  = 1
	SQL_HANDLE_DBC  = 2
	SQL_HANDLE_STMT = 3

	SQL_SUCCESS            = 0
	SQL_SUCCESS_WITH_INFO  = 1
	SQL_INVALID_HANDLE     = -2
	SQL_NO_DATA            = 100
	SQL_NO_TOTAL           = -4
	SQL_NTS                = -3
	SQL_MAX_MESSAGE_LENGTH = 512
	SQL_NULL_HANDLE        = 0
	SQL_NULL_HENV          = 0
	SQL_NULL_HDBC          = 0
	SQL_NULL_HSTMT         = 0

	SQL_PARAM_INPUT = 1

	SQL_NULL_DATA    = -1
	SQL_DATA_AT_EXEC = -2

	SQL_UNKNOWN_TYPE    = 0
	SQL_CHAR            = 1
	SQL_NUMERIC         = 2
	SQL_DECIMAL         = 3
	SQL_INTEGER         = 4
	SQL_SMALLINT        = 5
	SQL_FLOAT           = 6
	SQL_REAL            = 7
	SQL_DOUBLE          = 8
	SQL_DATETIME        = 9
	SQL_DATE            = 9
	SQL_TIME            = 10
	SQL_VARCHAR         = 12
	SQL_TYPE_DATE       = 91
	SQL_TYPE_TIME       = 92
	SQL_TYPE_TIMESTAMP  = 93
	SQL_TIMESTAMP       = 11
	SQL_LONGVARCHAR     = -1
	SQL_BINARY          = -2
	SQL_VARBINARY       = -3
	SQL_LONGVARBINARY   = -4
	SQL_BIGINT          = -5
	SQL_TINYINT         = -6
	SQL_BIT             = -7
	SQL_WCHAR           = -8
	SQL_WVARCHAR        = -9
	SQL_WLONGVARCHAR    = -10
	SQL_GUID            = -11
	SQL_SIGNED_OFFSET   = -20
	SQL_UNSIGNED_OFFSET = -22
	SQL_SS_XML          = -152

	SQL_C_CHAR           = SQL_CHAR
	SQL_C_LONG           = SQL_INTEGER
	SQL_C_SHORT          = SQL_SMALLINT
	SQL_C_FLOAT          = SQL_REAL
	SQL_C_DOUBLE         = SQL_DOUBLE
	SQL_C_NUMERIC        = SQL_NUMERIC
	SQL_C_DATE           = SQL_DATE
	SQL_C_TIME           = SQL_TIME
	SQL_C_TYPE_TIMESTAMP = SQL_TYPE_TIMESTAMP
	SQL_C_TIMESTAMP      = SQL_TIMESTAMP
	SQL_C_BINARY         = SQL_BINARY
	SQL_C_BIT            = SQL_BIT
	SQL_C_WCHAR          = SQL_WCHAR
	SQL_C_DEFAULT        = 99
	SQL_C_SBIGINT        = SQL_BIGINT + SQL_SIGNED_OFFSET
	SQL_C_UBIGINT        = SQL_BIGINT + SQL_UNSIGNED_OFFSET
	SQL_C_GUID           = SQL_GUID

	SQL_COMMIT   = 0
	SQL_ROLLBACK = 1

	SQL_AUTOCOMMIT         = 102
	SQL_ATTR_AUTOCOMMIT    = SQL_AUTOCOMMIT
	SQL_AUTOCOMMIT_OFF     = 0
	SQL_AUTOCOMMIT_ON      = 1
	SQL_AUTOCOMMIT_DEFAULT = SQL_AUTOCOMMIT_ON

	SQL_IS_UINTEGER = -5

	//Connection pooling
	SQL_ATTR_CONNECTION_POOLING = 201
	SQL_ATTR_CP_MATCH           = 202
	SQL_CP_OFF                  = 0
	SQL_CP_ONE_PER_DRIVER       = 1
	SQL_CP_ONE_PER_HENV         = uintptr(2)
	SQL_CP_DEFAULT              = SQL_CP_OFF
	SQL_CP_STRICT_MATCH         = 0
	SQL_CP_RELAXED_MATCH        = uintptr(1)
)

type (
	SQLHANDLE uintptr
	SQLHENV   SQLHANDLE
	SQLHDBC   SQLHANDLE
	SQLHSTMT  SQLHANDLE
	SQLHWND   uintptr

	SQLWCHAR     uint16
	SQLSCHAR     int8
	SQLSMALLINT  int16
	SQLUSMALLINT uint16
	SQLINTEGER   int32
	SQLUINTEGER  uint32
	SQLPOINTER   unsafe.Pointer
	SQLRETURN    SQLSMALLINT

	SQLGUID struct {
		Data1 uint32
		Data2 uint16
		Data3 uint16
		Data4 [8]byte
	}
)
<|endoftext|>