<commit_before>package imdb

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/moovweb/gokogiri/xml"
	"github.com/moovweb/gokogiri/xpath"
)

// IsEpisode tells whether the show is part of a series
func (s *Show) IsEpisode() (bool, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return false, err
	}

	_, err = episodeTitle(mainPage)
	if err != nil {
		return false, nil
	}
	return true, nil
}

// Title returns the show's title
func (s *Show) Title() (string, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return "", err
	}

	episodeTitle, err := episodeTitle(mainPage)
	if err == nil {
		return episodeTitle, nil
	}

	h1, err := mainPage.Search(xpath.Compile(`//h1`))
	if err != nil {
		return "", err
	}

	if len(h1) == 0 {
		return "", fmt.Errorf("unable to find title element")
	}

	title := strings.Split(h1[0].InnerHtml(), "<span")[0]
	if title == "" {
		return "", fmt.Errorf("empty title")
	}

	return strings.Trim(title, " \n\r\t\""), nil
}

// Year returns the show's airing year
func (s *Show) Year() (int, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return 0, err
	}

	isEpisode, err := s.IsEpisode()
	if err != nil {
		return 0, err
	}

	var yearText string

	if isEpisode {
		yearSpans, err := mainPage.Search(xpath.Compile(`//h1//span`))
		if err != nil {
			return 0, err
		}

		if len(yearSpans) == 0 {
			return 0, fmt.Errorf("can't find year element")
		}

		yearText = strings.Trim(yearSpans[0].LastChild().Content(), " ()")
	} else {
		yearLinks, err := mainPage.Search(
			xpath.Compile(`//a[contains(@href,'/year/')]`),
		)
		if err != nil {
			return 0, err
		}

		if len(yearLinks) == 0 {
			return 0, fmt.Errorf("can't find year element")
		}

		yearText = yearLinks[0].Content()
	}

	year, err := strconv.Atoi(yearText)
	if err != nil {
		return 0, fmt.Errorf("year is not a number: %s", err)
	}

	return year, nil
}

func (s *Show) OtherTitles() ([]string, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) ReleaseDate() (*time.Time, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) Tagline() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) Duration() (*time.Duration, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) Plot(level int) (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) PosterURL() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) Rating() (float32, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) Votes() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) SeasonEpisode() (int, int, error) {
	return 0, 0, fmt.Errorf("dummy method")
}

func (s *Show) SeriesID() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) SeriesTitle() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) SeriesYear() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func episodeTitle(mainPage *xml.ElementNode) (string, error) {
	titleElements, err := mainPage.Search(xpath.Compile(`//h1//span//em`))
	if err != nil {
		return "", err
	}

	if len(titleElements) == 0 {
		return "", fmt.Errorf("unable to find title element (show not an episode?)")
	}

	return titleElements[0].InnerHtml(), nil
}
<commit_msg>use literal xpath expressions<commit_after>package imdb

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/moovweb/gokogiri/xml"
)

// IsEpisode tells whether the show is part of a series
func (s *Show) IsEpisode() (bool, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return false, err
	}

	_, err = episodeTitle(mainPage)
	if err != nil {
		return false, nil
	}
	return true, nil
}

// Title returns the show's title
func (s *Show) Title() (string, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return "", err
	}

	episodeTitle, err := episodeTitle(mainPage)
	if err == nil {
		return episodeTitle, nil
	}

	h1, err := mainPage.Search(`//h1`)
	if err != nil {
		return "", err
	}

	if len(h1) == 0 {
		return "", fmt.Errorf("unable to find title element")
	}

	title := strings.Split(h1[0].InnerHtml(), "<span")[0]
	if title == "" {
		return "", fmt.Errorf("empty title")
	}

	return strings.Trim(title, " \n\r\t\""), nil
}

// Year returns the show's airing year
func (s *Show) Year() (int, error) {
	mainPage, err := s.mainPage()
	if err != nil {
		return 0, err
	}

	isEpisode, err := s.IsEpisode()
	if err != nil {
		return 0, err
	}

	var yearText string

	if isEpisode {
		yearSpans, err := mainPage.Search(`//h1//span`)
		if err != nil {
			return 0, err
		}

		if len(yearSpans) == 0 {
			return 0, fmt.Errorf("can't find year element")
		}

		yearText = strings.Trim(yearSpans[0].LastChild().Content(), " ()")
	} else {
		yearLinks, err := mainPage.Search(`//a[contains(@href,'/year/')]`)
		if err != nil {
			return 0, err
		}

		if len(yearLinks) == 0 {
			return 0, fmt.Errorf("can't find year element")
		}

		yearText = yearLinks[0].Content()
	}

	year, err := strconv.Atoi(yearText)
	if err != nil {
		return 0, fmt.Errorf("year is not a number: %s", err)
	}

	return year, nil
}

func (s *Show) OtherTitles() ([]string, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) ReleaseDate() (*time.Time, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) Tagline() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) Duration() (*time.Duration, error) {
	return nil, fmt.Errorf("dummy method")
}

func (s *Show) Plot(level int) (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) PosterURL() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) Rating() (float32, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) Votes() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) SeasonEpisode() (int, int, error) {
	return 0, 0, fmt.Errorf("dummy method")
}

func (s *Show) SeriesID() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func (s *Show) SeriesTitle() (string, error) {
	return "", fmt.Errorf("dummy method")
}

func (s *Show) SeriesYear() (int, error) {
	return 0, fmt.Errorf("dummy method")
}

func episodeTitle(mainPage *xml.ElementNode) (string, error) {
	titleElements, err := mainPage.Search(`//h1//span//em`)
	if err != nil {
		return "", err
	}

	if len(titleElements) == 0 {
		return "", fmt.Errorf("unable to find title element (show not an episode?)")
	}

	return titleElements[0].InnerHtml(), nil
}
<|endoftext|>


<commit_before>package postgres

import (
	"context"
	"time"

	"github.com/getfider/fider/app/models/cmd"
	"github.com/getfider/fider/app/models/entity"
	"github.com/getfider/fider/app/models/query"
	"github.com/getfider/fider/app/pkg/dbx"
	"github.com/getfider/fider/app/pkg/errors"
)

type dbComment struct {
	ID          int          `db:"id"`
	Content     string       `db:"content"`
	CreatedAt   time.Time    `db:"created_at"`
	User        *dbUser      `db:"user"`
	Attachments []string     `db:"attachment_bkeys"`
	EditedAt    dbx.NullTime `db:"edited_at"`
	EditedBy    *dbUser      `db:"edited_by"`
}

func (c *dbComment) toModel(ctx context.Context) *entity.Comment {
	comment := &entity.Comment{
		ID:          c.ID,
		Content:     c.Content,
		CreatedAt:   c.CreatedAt,
		User:        c.User.toModel(ctx),
		Attachments: c.Attachments,
	}
	if c.EditedAt.Valid {
		comment.EditedBy = c.EditedBy.toModel(ctx)
		comment.EditedAt = &c.EditedAt.Time
	}
	return comment
}

func addNewComment(ctx context.Context, c *cmd.AddNewComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		var id int
		if err := trx.Get(&id, `
			INSERT INTO comments (tenant_id, post_id, content, user_id, created_at)
			VALUES ($1, $2, $3, $4, $5)
			RETURNING id
		`, tenant.ID, c.Post.ID, c.Content, user.ID, time.Now()); err != nil {
			return errors.Wrap(err, "failed add new comment")
		}

		if err := internalAddSubscriber(trx, c.Post, tenant, user, false); err != nil {
			return err
		}

		q := &query.GetCommentByID{CommentID: id}
		if err := getCommentByID(ctx, q); err != nil {
			return err
		}
		c.Result = q.Result

		return nil
	})
}

func updateComment(ctx context.Context, c *cmd.UpdateComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		_, err := trx.Execute(`
			UPDATE comments SET content = $1, edited_at = $2, edited_by_id = $3
			WHERE id = $4 AND tenant_id = $5`, c.Content, time.Now(), user.ID, c.CommentID, tenant.ID)
		if err != nil {
			return errors.Wrap(err, "failed update comment")
		}
		return nil
	})
}

func deleteComment(ctx context.Context, c *cmd.DeleteComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		if _, err := trx.Execute(
			"UPDATE comments SET deleted_at = $1, deleted_by_id = $2 WHERE id = $3 AND tenant_id = $4",
			time.Now(), user.ID, c.CommentID, tenant.ID,
		); err != nil {
			return errors.Wrap(err, "failed delete comment")
		}
		return nil
	})
}

func getCommentByID(ctx context.Context, q *query.GetCommentByID) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		q.Result = nil

		comment := dbComment{}
		err := trx.Get(&comment,
			`SELECT c.id,
							c.content,
							c.created_at,
							c.edited_at,
							u.id AS user_id,
							u.name AS user_name,
							u.email AS user_email,
							u.role AS user_role,
							u.status AS user_status,
							u.avatar_type AS user_avatar_type,
							u.avatar_bkey AS user_avatar_bkey,
							e.id AS edited_by_id,
							e.name AS edited_by_name,
							e.email AS edited_by_email,
							e.role AS edited_by_role,
							e.status AS edited_by_status,
							e.avatar_type AS edited_by_avatar_type,
							e.avatar_bkey AS edited_by_avatar_bkey
			FROM comments c
			INNER JOIN users u
			ON u.id = c.user_id
			AND u.tenant_id = c.tenant_id
			LEFT JOIN users e
			ON e.id = c.edited_by_id
			AND e.tenant_id = c.tenant_id
			WHERE c.id = $1
			AND c.tenant_id = $2
			AND c.deleted_at IS NULL`, q.CommentID, tenant.ID)

		if err != nil {
			return err
		}

		q.Result = comment.toModel(ctx)
		return nil
	})
}

func getCommentsByPost(ctx context.Context, q *query.GetCommentsByPost) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		q.Result = make([]*entity.Comment, 0)

		comments := []*dbComment{}
		err := trx.Select(&comments,
			`WITH agg_attachments AS (
					SELECT
							c.id as comment_id,
							ARRAY_REMOVE(ARRAY_AGG(at.attachment_bkey), NULL) as attachment_bkeys
					FROM attachments at
					INNER JOIN comments c
					ON at.tenant_id = c.tenant_id
					AND at.post_id = c.post_id
					AND at.comment_id = c.id
					WHERE at.post_id = $1
					AND at.tenant_id = $2
					AND at.comment_id IS NOT NULL
					GROUP BY c.id
			)
			SELECT c.id,
					c.content,
					c.created_at,
					c.edited_at,
					u.id AS user_id,
					u.name AS user_name,
					u.email AS user_email,
					u.role AS user_role,
					u.status AS user_status,
					u.avatar_type AS user_avatar_type,
					u.avatar_bkey AS user_avatar_bkey,
					e.id AS edited_by_id,
					e.name AS edited_by_name,
					e.email AS edited_by_email,
					e.role AS edited_by_role,
					e.status AS edited_by_status,
					e.avatar_type AS edited_by_avatar_type,
					e.avatar_bkey AS edited_by_avatar_bkey,
					at.attachment_bkeys
			FROM comments c
			INNER JOIN posts p
			ON p.id = c.post_id
			AND p.tenant_id = c.tenant_id
			INNER JOIN users u
			ON u.id = c.user_id
			AND u.tenant_id = c.tenant_id
			LEFT JOIN users e
			ON e.id = c.edited_by_id
			AND e.tenant_id = c.tenant_id
			LEFT JOIN agg_attachments at
			ON at.comment_id = c.id
			WHERE p.id = $1
			AND p.tenant_id = $2
			AND c.deleted_at IS NULL
			ORDER BY c.created_at ASC`, q.Post.ID, tenant.ID)
		if err != nil {
			return errors.Wrap(err, "failed get comments of post with id '%d'", q.Post.ID)
		}

		q.Result = make([]*entity.Comment, len(comments))
		for i, comment := range comments {
			q.Result[i] = comment.toModel(ctx)
		}
		return nil
	})
}
<commit_msg>enhancement: remove auto subscribe when adding a comment<commit_after>package postgres

import (
	"context"
	"time"

	"github.com/getfider/fider/app/models/cmd"
	"github.com/getfider/fider/app/models/entity"
	"github.com/getfider/fider/app/models/query"
	"github.com/getfider/fider/app/pkg/dbx"
	"github.com/getfider/fider/app/pkg/errors"
)

type dbComment struct {
	ID          int          `db:"id"`
	Content     string       `db:"content"`
	CreatedAt   time.Time    `db:"created_at"`
	User        *dbUser      `db:"user"`
	Attachments []string     `db:"attachment_bkeys"`
	EditedAt    dbx.NullTime `db:"edited_at"`
	EditedBy    *dbUser      `db:"edited_by"`
}

func (c *dbComment) toModel(ctx context.Context) *entity.Comment {
	comment := &entity.Comment{
		ID:          c.ID,
		Content:     c.Content,
		CreatedAt:   c.CreatedAt,
		User:        c.User.toModel(ctx),
		Attachments: c.Attachments,
	}
	if c.EditedAt.Valid {
		comment.EditedBy = c.EditedBy.toModel(ctx)
		comment.EditedAt = &c.EditedAt.Time
	}
	return comment
}

func addNewComment(ctx context.Context, c *cmd.AddNewComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		var id int
		if err := trx.Get(&id, `
			INSERT INTO comments (tenant_id, post_id, content, user_id, created_at)
			VALUES ($1, $2, $3, $4, $5)
			RETURNING id
		`, tenant.ID, c.Post.ID, c.Content, user.ID, time.Now()); err != nil {
			return errors.Wrap(err, "failed add new comment")
		}

		q := &query.GetCommentByID{CommentID: id}
		if err := getCommentByID(ctx, q); err != nil {
			return err
		}
		c.Result = q.Result

		return nil
	})
}

func updateComment(ctx context.Context, c *cmd.UpdateComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		_, err := trx.Execute(`
			UPDATE comments SET content = $1, edited_at = $2, edited_by_id = $3
			WHERE id = $4 AND tenant_id = $5`, c.Content, time.Now(), user.ID, c.CommentID, tenant.ID)
		if err != nil {
			return errors.Wrap(err, "failed update comment")
		}
		return nil
	})
}

func deleteComment(ctx context.Context, c *cmd.DeleteComment) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		if _, err := trx.Execute(
			"UPDATE comments SET deleted_at = $1, deleted_by_id = $2 WHERE id = $3 AND tenant_id = $4",
			time.Now(), user.ID, c.CommentID, tenant.ID,
		); err != nil {
			return errors.Wrap(err, "failed delete comment")
		}
		return nil
	})
}

func getCommentByID(ctx context.Context, q *query.GetCommentByID) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		q.Result = nil

		comment := dbComment{}
		err := trx.Get(&comment,
			`SELECT c.id,
							c.content,
							c.created_at,
							c.edited_at,
							u.id AS user_id,
							u.name AS user_name,
							u.email AS user_email,
							u.role AS user_role,
							u.status AS user_status,
							u.avatar_type AS user_avatar_type,
							u.avatar_bkey AS user_avatar_bkey,
							e.id AS edited_by_id,
							e.name AS edited_by_name,
							e.email AS edited_by_email,
							e.role AS edited_by_role,
							e.status AS edited_by_status,
							e.avatar_type AS edited_by_avatar_type,
							e.avatar_bkey AS edited_by_avatar_bkey
			FROM comments c
			INNER JOIN users u
			ON u.id = c.user_id
			AND u.tenant_id = c.tenant_id
			LEFT JOIN users e
			ON e.id = c.edited_by_id
			AND e.tenant_id = c.tenant_id
			WHERE c.id = $1
			AND c.tenant_id = $2
			AND c.deleted_at IS NULL`, q.CommentID, tenant.ID)

		if err != nil {
			return err
		}

		q.Result = comment.toModel(ctx)
		return nil
	})
}

func getCommentsByPost(ctx context.Context, q *query.GetCommentsByPost) error {
	return using(ctx, func(trx *dbx.Trx, tenant *entity.Tenant, user *entity.User) error {
		q.Result = make([]*entity.Comment, 0)

		comments := []*dbComment{}
		err := trx.Select(&comments,
			`WITH agg_attachments AS (
					SELECT
							c.id as comment_id,
							ARRAY_REMOVE(ARRAY_AGG(at.attachment_bkey), NULL) as attachment_bkeys
					FROM attachments at
					INNER JOIN comments c
					ON at.tenant_id = c.tenant_id
					AND at.post_id = c.post_id
					AND at.comment_id = c.id
					WHERE at.post_id = $1
					AND at.tenant_id = $2
					AND at.comment_id IS NOT NULL
					GROUP BY c.id
			)
			SELECT c.id,
					c.content,
					c.created_at,
					c.edited_at,
					u.id AS user_id,
					u.name AS user_name,
					u.email AS user_email,
					u.role AS user_role,
					u.status AS user_status,
					u.avatar_type AS user_avatar_type,
					u.avatar_bkey AS user_avatar_bkey,
					e.id AS edited_by_id,
					e.name AS edited_by_name,
					e.email AS edited_by_email,
					e.role AS edited_by_role,
					e.status AS edited_by_status,
					e.avatar_type AS edited_by_avatar_type,
					e.avatar_bkey AS edited_by_avatar_bkey,
					at.attachment_bkeys
			FROM comments c
			INNER JOIN posts p
			ON p.id = c.post_id
			AND p.tenant_id = c.tenant_id
			INNER JOIN users u
			ON u.id = c.user_id
			AND u.tenant_id = c.tenant_id
			LEFT JOIN users e
			ON e.id = c.edited_by_id
			AND e.tenant_id = c.tenant_id
			LEFT JOIN agg_attachments at
			ON at.comment_id = c.id
			WHERE p.id = $1
			AND p.tenant_id = $2
			AND c.deleted_at IS NULL
			ORDER BY c.created_at ASC`, q.Post.ID, tenant.ID)
		if err != nil {
			return errors.Wrap(err, "failed get comments of post with id '%d'", q.Post.ID)
		}

		q.Result = make([]*entity.Comment, len(comments))
		for i, comment := range comments {
			q.Result[i] = comment.toModel(ctx)
		}
		return nil
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run all tests in hack/e2e-suite.")
	tests            = flag.String("tests", "", "Run only tests in hack/e2e-suite matching this glob. Ignored if -test is set.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")

	cfgCmd = flag.String("cfg", "", "If nonempty, pass this as an argument, and call kubecfg. Implies -v.")
	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

var signals = make(chan os.Signal, 100)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

func main() {
	flag.Parse()
	signal.Notify(signals, os.Interrupt)

	if *test {
		*tests = "*"
	}

	if *isup {
		status := 1
		if runBash("get status", `$KUBECFG -server_version`) {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		if !runBash("build-release", `test-build-release`) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !runBash("push", path.Join(*root, "/cluster/kube-push.sh")) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	failure := false
	switch {
	case *cfgCmd != "":
		failure = !runBash("'kubecfg "+*cfgCmd+"'", "$KUBECFG "+*cfgCmd)
	case *ctlCmd != "":
		failure = !runBash("'kubectl "+*ctlCmd+"'", "$KUBECTL "+*ctlCmd)
	case *tests != "":
		failed, passed := Test()
		log.Printf("Passed tests: %v", passed)
		log.Printf("Failed tests: %v", failed)
		failure = len(failed) > 0
	}

	if *down {
		TearDown()
	}

	if failure {
		os.Exit(1)
	}
}

func TearDown() {
	runBash("teardown", "test-teardown")
}

func Up() bool {
	if !tryUp() {
		log.Printf("kube-up failed; will tear down and retry. (Possibly your cluster was in some partially created state?)")
		TearDown()
		return tryUp()
	}
	return true
}

func tryUp() bool {
	return runBash("up", path.Join(*root, "/cluster/kube-up.sh; test-setup;"))
}

func Test() (failed, passed []string) {
	// run tests!
	dir, err := os.Open(filepath.Join(*root, "hack", "e2e-suite"))
	if err != nil {
		log.Fatal("Couldn't open e2e-suite dir")
	}
	defer dir.Close()
	names, err := dir.Readdirnames(0)
	if err != nil {
		log.Fatal("Couldn't read names in e2e-suite dir")
	}

	for i := range names {
		name := names[i]
		if name == "." || name == ".." {
			continue
		}
		if match, err := path.Match(*tests, name); !match && err == nil {
			continue
		}
		absName := filepath.Join(*root, "hack", "e2e-suite", name)
		log.Printf("%v matches %v. Starting test.", name, *tests)
		if runBash(name, absName) {
			log.Printf("%v passed", name)
			passed = append(passed, name)
		} else {
			log.Printf("%v failed", name)
			failed = append(failed, name)
		}
	}

	return
}

// All nonsense below is temporary until we have go versions of these things.

func runBash(stepName, bashFragment string) bool {
	cmd := exec.Command("bash", "-s")
	cmd.Stdin = strings.NewReader(bashWrap(bashFragment))
	return finishRunning(stepName, cmd)
}

func run(stepName, cmdPath string) bool {
	return finishRunning(stepName, exec.Command(filepath.Join(*root, cmdPath)))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	log.Printf("Running: %v", stepName)
	stdout, stderr := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	} else {
		cmd.Stdout = stdout
		cmd.Stderr = stderr
	}

	done := make(chan struct{})
	defer close(done)
	go func() {
		for {
			select {
			case <-done:
				return
			case s := <-signals:
				cmd.Process.Signal(s)
			}
		}
	}()

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		if !*verbose {
			fmt.Printf("stdout:\n------\n%v\n------\n", string(stdout.Bytes()))
			fmt.Printf("stderr:\n------\n%v\n------\n", string(stderr.Bytes()))
		}
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubecfg or kubectl commands (begining with a space).
func kubeClientArgs() string {
	if *checkVersionSkew {
		return " -expect_version_match"
	}
	return ""
}

func bashWrap(cmd string) string {
	return `
set -o errexit
set -o nounset
set -o pipefail

export KUBE_CONFIG_FILE="config-test.sh"

# TODO(jbeda): This will break on usage if there is a space in
# ${KUBE_ROOT}.  Covert to an array?  Or an exported function?
export KUBECFG="` + *root + `/cluster/kubecfg.sh` + kubeClientArgs() + `"
export KUBECTL="` + *root + `/cluster/kubectl.sh` + kubeClientArgs() + `"

source "` + *root + `/cluster/kube-env.sh"
source "` + *root + `/cluster/${KUBERNETES_PROVIDER}/util.sh"

prepare-e2e

` + cmd + `
`
}
<commit_msg>fix e2e.go args for kubectl<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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

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

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

// e2e.go runs the e2e test suite. No non-standard package dependencies; call with "go run".
package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"strings"
)

var (
	isup             = flag.Bool("isup", false, "Check to see if the e2e cluster is up, then exit.")
	build            = flag.Bool("build", false, "If true, build a new release. Otherwise, use whatever is there.")
	up               = flag.Bool("up", false, "If true, start the the e2e cluster. If cluster is already up, recreate it.")
	push             = flag.Bool("push", false, "If true, push to e2e cluster. Has no effect if -up is true.")
	down             = flag.Bool("down", false, "If true, tear down the cluster before exiting.")
	test             = flag.Bool("test", false, "Run all tests in hack/e2e-suite.")
	tests            = flag.String("tests", "", "Run only tests in hack/e2e-suite matching this glob. Ignored if -test is set.")
	root             = flag.String("root", absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), ".."))), "Root directory of kubernetes repository.")
	verbose          = flag.Bool("v", false, "If true, print all command output.")
	checkVersionSkew = flag.Bool("check_version_skew", true, ""+
		"By default, verify that client and server have exact version match. "+
		"You can explicitly set to false if you're, e.g., testing client changes "+
		"for which the server version doesn't make a difference.")

	cfgCmd = flag.String("cfg", "", "If nonempty, pass this as an argument, and call kubecfg. Implies -v.")
	ctlCmd = flag.String("ctl", "", "If nonempty, pass this as an argument, and call kubectl. Implies -v. (-test, -cfg, -ctl are mutually exclusive)")
)

var signals = make(chan os.Signal, 100)

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

func main() {
	flag.Parse()
	signal.Notify(signals, os.Interrupt)

	if *test {
		*tests = "*"
	}

	if *isup {
		status := 1
		if runBash("get status", `$KUBECFG -server_version`) {
			status = 0
			log.Printf("Cluster is UP")
		} else {
			log.Printf("Cluster is DOWN")
		}
		os.Exit(status)
	}

	if *build {
		if !runBash("build-release", `test-build-release`) {
			log.Fatal("Error building. Aborting.")
		}
	}

	if *up {
		if !Up() {
			log.Fatal("Error starting e2e cluster. Aborting.")
		}
	} else if *push {
		if !runBash("push", path.Join(*root, "/cluster/kube-push.sh")) {
			log.Fatal("Error pushing e2e cluster. Aborting.")
		}
	}

	failure := false
	switch {
	case *cfgCmd != "":
		failure = !runBash("'kubecfg "+*cfgCmd+"'", "$KUBECFG "+*cfgCmd)
	case *ctlCmd != "":
		failure = !runBash("'kubectl "+*ctlCmd+"'", "$KUBECTL "+*ctlCmd)
	case *tests != "":
		failed, passed := Test()
		log.Printf("Passed tests: %v", passed)
		log.Printf("Failed tests: %v", failed)
		failure = len(failed) > 0
	}

	if *down {
		TearDown()
	}

	if failure {
		os.Exit(1)
	}
}

func TearDown() {
	runBash("teardown", "test-teardown")
}

func Up() bool {
	if !tryUp() {
		log.Printf("kube-up failed; will tear down and retry. (Possibly your cluster was in some partially created state?)")
		TearDown()
		return tryUp()
	}
	return true
}

func tryUp() bool {
	return runBash("up", path.Join(*root, "/cluster/kube-up.sh; test-setup;"))
}

func Test() (failed, passed []string) {
	// run tests!
	dir, err := os.Open(filepath.Join(*root, "hack", "e2e-suite"))
	if err != nil {
		log.Fatal("Couldn't open e2e-suite dir")
	}
	defer dir.Close()
	names, err := dir.Readdirnames(0)
	if err != nil {
		log.Fatal("Couldn't read names in e2e-suite dir")
	}

	for i := range names {
		name := names[i]
		if name == "." || name == ".." {
			continue
		}
		if match, err := path.Match(*tests, name); !match && err == nil {
			continue
		}
		absName := filepath.Join(*root, "hack", "e2e-suite", name)
		log.Printf("%v matches %v. Starting test.", name, *tests)
		if runBash(name, absName) {
			log.Printf("%v passed", name)
			passed = append(passed, name)
		} else {
			log.Printf("%v failed", name)
			failed = append(failed, name)
		}
	}

	return
}

// All nonsense below is temporary until we have go versions of these things.

func runBash(stepName, bashFragment string) bool {
	cmd := exec.Command("bash", "-s")
	cmd.Stdin = strings.NewReader(bashWrap(bashFragment))
	return finishRunning(stepName, cmd)
}

func run(stepName, cmdPath string) bool {
	return finishRunning(stepName, exec.Command(filepath.Join(*root, cmdPath)))
}

func finishRunning(stepName string, cmd *exec.Cmd) bool {
	log.Printf("Running: %v", stepName)
	stdout, stderr := bytes.NewBuffer(nil), bytes.NewBuffer(nil)
	if *verbose {
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	} else {
		cmd.Stdout = stdout
		cmd.Stderr = stderr
	}

	done := make(chan struct{})
	defer close(done)
	go func() {
		for {
			select {
			case <-done:
				return
			case s := <-signals:
				cmd.Process.Signal(s)
			}
		}
	}()

	if err := cmd.Run(); err != nil {
		log.Printf("Error running %v: %v", stepName, err)
		if !*verbose {
			fmt.Printf("stdout:\n------\n%v\n------\n", string(stdout.Bytes()))
			fmt.Printf("stderr:\n------\n%v\n------\n", string(stderr.Bytes()))
		}
		return false
	}
	return true
}

// returns either "", or a list of args intended for appending with the
// kubecfg or kubectl commands (begining with a space).
func kubecfgArgs() string {
	if *checkVersionSkew {
		return " -expect_version_match"
	}
	return ""
}

// returns either "", or a list of args intended for appending with the
// kubectl command (begining with a space).
func kubectlArgs() string {
	if *checkVersionSkew {
		return " --match-server-version"
	}
	return ""
}

func bashWrap(cmd string) string {
	return `
set -o errexit
set -o nounset
set -o pipefail

export KUBE_CONFIG_FILE="config-test.sh"

# TODO(jbeda): This will break on usage if there is a space in
# ${KUBE_ROOT}.  Covert to an array?  Or an exported function?
export KUBECFG="` + *root + `/cluster/kubecfg.sh` + kubecfgArgs() + `"
export KUBECTL="` + *root + `/cluster/kubectl.sh` + kubectlArgs() + `"

source "` + *root + `/cluster/kube-env.sh"
source "` + *root + `/cluster/${KUBERNETES_PROVIDER}/util.sh"

prepare-e2e

` + cmd + `
`
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Max Wolter
// Copyright (c) 2015 CIRCL - Computer Incident Response Center Luxembourg
//                           (c/o smile, security made in Lëtzebuerg, Groupement
//                           d'Intérêt Economique)
//
// This file is part of PBTC.
//
// PBTC is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PBTC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with PBTC.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bufio"
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"

	"github.com/gocql/gocql"
)

var session *gocql.Session

func main() {
	// set the parameters for our cassandra connection
	cluster := gocql.NewCluster("127.0.0.1")
	cluster.DiscoverHosts = true
	cluster.DefaultTimestamp = false

	// establish the cassandra session
	session, err := cluster.CreateSession()
	if err != nil {
		fmt.Println("could not create session")
		os.Exit(1)
	}
	defer session.Close()

	// get a list of iles in the logs folder
	files, err := ioutil.ReadDir("../logs")
	if err != nil {
		fmt.Println("could not read logs folder")
		os.Exit(1)
	}

	// iterate through files for processing
	for _, file := range files {
		err := process(file)
		if err != nil {
			fmt.Printf("%v", err)
		}
	}

	os.Exit(0)
}

func process(file os.FileInfo) error {
	// ignore all directories
	if file.IsDir() {
		return fmt.Errorf("can't process directory: %v", file.Name())
	}

	// ignore all non-log files
	if path.Ext(file.Name()) != ".txt" {
		return fmt.Errorf("can only process log files: %v", file.Name())
	}

	// open file
	f, err := os.Open(file.Name())
	if err != nil {
		return fmt.Errorf("could not open file: %v", file.Name())
	}
	defer f.Close()

	// try to use scanner to read first line
	scanner := bufio.NewScanner(f)
	if !scanner.Scan() {
		return fmt.Errorf("could not read first line: %v", file.Name())
	}

	// check first line header for log version
	line := scanner.Text()
	if line != "PBTC Log Version 1" {
		return fmt.Errorf("unknown header for file: %v (%v)", f.Name(), line)
	}

	// reset file pointer
	_, err = f.Seek(0, 0)
	if err != nil {
		return fmt.Errorf("could not reset file pointer: %v", f.Name())
	}

	// stream file into hasher to get fingerprint
	hasher := sha256.New()
	_, err = io.Copy(hasher, f)
	if err != nil {
		return fmt.Errorf("could not stream file data into hasher: %v", f.Name())
	}

	// get fingerprint hash and check for duplicate
	hash := hasher.Sum(nil)
	fmt.Printf("hash for file: %v - %v", f.Name(), hash)

	// import file into cassandra
	scanner = bufio.NewScanner(f)
	for scanner.Scan() {
		err := insert(scanner.Text())
		if err != nil {
			fmt.Printf("%v", err)
		}
	}

	return nil
}

func insert(line string) error {
	return nil
}
<commit_msg>Fixed importer file reading & hashing<commit_after>// Copyright (c) 2015 Max Wolter
// Copyright (c) 2015 CIRCL - Computer Incident Response Center Luxembourg
//                           (c/o smile, security made in Lëtzebuerg, Groupement
//                           d'Intérêt Economique)
//
// This file is part of PBTC.
//
// PBTC is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// PBTC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with PBTC.  If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"

	"github.com/gocql/gocql"
)

const (
	logDir = "logs"
	logHdr = "#PBTC Log Version 1"
)

var session *gocql.Session

func main() {
	// set the parameters for our cassandra connection
	cluster := gocql.NewCluster("127.0.0.1")
	cluster.DiscoverHosts = true
	cluster.DefaultTimestamp = false

	// establish the cassandra session
	session, err := cluster.CreateSession()
	if err != nil {
		fmt.Println("could not create session")
		os.Exit(1)
	}
	defer session.Close()

	// get a list of iles in the logs folder
	files, err := ioutil.ReadDir(path.Join("..", logDir))
	if err != nil {
		fmt.Println("could not read logs folder")
		os.Exit(1)
	}

	// iterate through files for processing
	for _, file := range files {
		err := process(file)
		if err != nil {
			fmt.Printf("%v", err)
		}
	}

	fmt.Printf("processed %v files\n", len(files))
	os.Exit(0)
}

func process(file os.FileInfo) error {
	// ignore all directories
	if file.IsDir() {
		return fmt.Errorf("can't process directory: %v\n", file.Name())
	}

	// ignore all non-log files
	if path.Ext(file.Name()) != ".txt" {
		return fmt.Errorf("can only process log files: %v\n", file.Name())
	}

	// open file
	f, err := os.Open(path.Join("..", logDir, file.Name()))
	if err != nil {
		return fmt.Errorf("could not open file: %v\n", file.Name())
	}
	defer f.Close()

	// try to use scanner to read first line
	scanner := bufio.NewScanner(f)
	if !scanner.Scan() {
		return fmt.Errorf("could not read first line: %v\n", file.Name())
	}

	// check first line header for log version
	line := scanner.Text()
	if line != logHdr {
		return fmt.Errorf("unknown header for file: %v (%v)\n", f.Name(), line)
	}

	// reset file pointer
	_, err = f.Seek(0, 0)
	if err != nil {
		return fmt.Errorf("could not reset file pointer: %v\n", f.Name())
	}

	// stream file into hasher to get fingerprint
	hasher := sha256.New()
	_, err = io.Copy(hasher, f)
	if err != nil {
		return fmt.Errorf("could not stream file data into hasher: %v\n", f.Name())
	}

	// get fingerprint hash and check for duplicate
	hash := hasher.Sum(nil)
	fmt.Printf("hash for file: %v - %v\n", f.Name(), hex.EncodeToString(hash))

	// import file into cassandra
	scanner = bufio.NewScanner(f)
	for scanner.Scan() {
		err := insert(scanner.Text())
		if err != nil {
			fmt.Printf("%v", err)
		}
	}

	return nil
}

func insert(line string) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"sync"

	"buildblast/physics"
	"buildblast/mapgen"
	"buildblast/coords"
)

type Entity interface {
	Tick(w *World)
	Damage(amount int, what string)
	BoxAt(t float64) *physics.Box
	Pos() coords.World
	ID() string
}

type World struct {
	seed         float64
	chunks       map[coords.Chunk]mapgen.Chunk
	chunkLock    sync.Mutex
	generator    mapgen.ChunkSource

	entities     []Entity

	EntityCreate chan string
	EntityRemove chan string
}

func NewWorld(seed float64) *World {
	w := new(World)
	w.seed = seed;
	w.chunks = make(map[coords.Chunk]mapgen.Chunk)
	w.generator = mapgen.NewMazeArena(seed)

	w.entities = make([]Entity, 0)

	w.EntityCreate = make(chan string, 10)
	w.EntityRemove = make(chan string, 10)

	return w
}

func (w *World) RequestChunk(cc coords.Chunk) mapgen.Chunk {
	w.chunkLock.Lock()
	chunk := w.chunks[cc]
	w.chunkLock.Unlock()

	if chunk == nil {
		chunk = w.generator.Chunk(cc)

		w.chunkLock.Lock()
		w.chunks[cc] = chunk
		w.chunkLock.Unlock()
	}

	return chunk
}

func (w *World) Block(wc coords.World) mapgen.Block {
	cc := wc.Chunk()

	w.chunkLock.Lock()
	chunk := w.chunks[cc]
	w.chunkLock.Unlock()

	if chunk == nil {
		return mapgen.BLOCK_NIL
	}

	return chunk.Block(wc.Offset())
}

func (w *World) ChangeBlock(wc coords.World, newBlock mapgen.Block) {
	chunk := w.RequestChunk(wc.Chunk())
	chunk.SetBlock(wc.Offset(), newBlock)
}

func (w *World) AddEntity(e Entity) {
	w.entities = append(w.entities, e)
	w.EntityCreate <- e.ID()
}

func (w *World) RemoveEntity(e Entity) {
	for i, entity := range w.entities {
		if entity == e {
			w.entities[i] = w.entities[len(w.entities) - 1]
			w.entities = w.entities[:len(w.entities) - 1]
			w.EntityRemove <- e.ID()
		}
	}
}

func (w *World) GetEntityIDs() []string {
	result := make([]string, len(w.entities))
	for i, entity := range w.entities {
		result[i] = entity.ID()
	}
	return result
}

func (w *World) Tick(g *Game) {
	for _, e := range w.entities {
		e.Tick(w)
		g.BroadcastLossy(&MsgEntityPosition{
			Pos: e.Pos(),
			ID: e.ID(),
		})
	}
}

func (w *World) FindFirstIntersect(entity Entity, t float64, ray *physics.Ray) (*coords.World, Entity) {
	boxes := make([]*physics.Box, len(w.entities))
	for i, other := range w.entities {
		if other == entity {
			boxes[i] = nil
		} else {
			boxes[i] = other.BoxAt(t)
		}
	}

	hitPos, hitIndex := ray.FindAnyIntersect(w, boxes)
	if hitIndex != -1 {
		return hitPos, w.entities[hitIndex]
	}
	return hitPos, nil
}
<commit_msg>Reintroduce log for chunks<commit_after>package main

import (
	"sync"
	"log"

	"buildblast/physics"
	"buildblast/mapgen"
	"buildblast/coords"
)

type Entity interface {
	Tick(w *World)
	Damage(amount int, what string)
	BoxAt(t float64) *physics.Box
	Pos() coords.World
	ID() string
}

type World struct {
	seed         float64
	chunks       map[coords.Chunk]mapgen.Chunk
	chunkLock    sync.Mutex
	generator    mapgen.ChunkSource

	entities     []Entity

	EntityCreate chan string
	EntityRemove chan string
}

func NewWorld(seed float64) *World {
	w := new(World)
	w.seed = seed;
	w.chunks = make(map[coords.Chunk]mapgen.Chunk)
	w.generator = mapgen.NewMazeArena(seed)

	w.entities = make([]Entity, 0)

	w.EntityCreate = make(chan string, 10)
	w.EntityRemove = make(chan string, 10)

	return w
}

func (w *World) RequestChunk(cc coords.Chunk) mapgen.Chunk {
	w.chunkLock.Lock()
	chunk := w.chunks[cc]
	w.chunkLock.Unlock()

	if chunk == nil {
		chunk = w.generator.Chunk(cc)
		log.Println("Generated chunk at ", cc)

		w.chunkLock.Lock()
		w.chunks[cc] = chunk
		w.chunkLock.Unlock()
	}

	return chunk
}

func (w *World) Block(wc coords.World) mapgen.Block {
	cc := wc.Chunk()

	w.chunkLock.Lock()
	chunk := w.chunks[cc]
	w.chunkLock.Unlock()

	if chunk == nil {
		return mapgen.BLOCK_NIL
	}

	return chunk.Block(wc.Offset())
}

func (w *World) ChangeBlock(wc coords.World, newBlock mapgen.Block) {
	chunk := w.RequestChunk(wc.Chunk())
	chunk.SetBlock(wc.Offset(), newBlock)
}

func (w *World) AddEntity(e Entity) {
	w.entities = append(w.entities, e)
	w.EntityCreate <- e.ID()
}

func (w *World) RemoveEntity(e Entity) {
	for i, entity := range w.entities {
		if entity == e {
			w.entities[i] = w.entities[len(w.entities) - 1]
			w.entities = w.entities[:len(w.entities) - 1]
			w.EntityRemove <- e.ID()
		}
	}
}

func (w *World) GetEntityIDs() []string {
	result := make([]string, len(w.entities))
	for i, entity := range w.entities {
		result[i] = entity.ID()
	}
	return result
}

func (w *World) Tick(g *Game) {
	for _, e := range w.entities {
		e.Tick(w)
		g.BroadcastLossy(&MsgEntityPosition{
			Pos: e.Pos(),
			ID: e.ID(),
		})
	}
}

func (w *World) FindFirstIntersect(entity Entity, t float64, ray *physics.Ray) (*coords.World, Entity) {
	boxes := make([]*physics.Box, len(w.entities))
	for i, other := range w.entities {
		if other == entity {
			boxes[i] = nil
		} else {
			boxes[i] = other.BoxAt(t)
		}
	}

	hitPos, hitIndex := ray.FindAnyIntersect(w, boxes)
	if hitIndex != -1 {
		return hitPos, w.entities[hitIndex]
	}
	return hitPos, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func formatTime(t time.Time) string {
	return t.Format(ApiDateTimeFormat)
}

func daysSince(t time.Time) string {
	days := time.Since(t) / time.Hour / 24
	return fmt.Sprintf("%d", days)
}

var tFuncMap = template.FuncMap{
	"datetime":  formatTime,
	"dayssince": daysSince,
}

//apparently my sessions are bad and i should feel bad.
func victimsToStr(victims []int64) string {
	var strs []string
	for _, vid := range victims {
		strs = append(strs, fmt.Sprintf("%d", vid))
	}
	return strings.Join(strs, ",")
}

func strToVictims(victimStr string) []int64 {
	var victims []int64

	strs := strings.Split(victimStr, ",")
	for _, vidstr := range strs {
		vid, err := strconv.ParseInt(vidstr, 10, 64)
		if err != nil {
			continue
		}
		victims = append(victims, vid)
	}
	return victims
}

func handleStrip(w http.ResponseWriter, r *http.Request, ses Session) {
	r.ParseForm()

	purgeLock.Lock()
	defer purgeLock.Unlock()
	go saveState()

	victimsStr := ses.Get("strip_victims")
	victims := strToVictims(victimsStr)

	if r.PostFormValue("claim") != "" {
		// Complete the ones marked as completed.  This should be undone if
		// necessary on the next API pull.
		for _, id := range victims {
			var confirmed bool
			if _, ok := toBePurged[id]; !ok {
				continue
			}

			for _, strid := range r.PostForm["stripped"] {
				cid, err := strconv.ParseInt(strid, 10, 64)
				if err != nil {
					continue
				}

				if id == cid {
					confirmed = true
				}
			}

			// mark as purged if confirmation, unclaim it otherwise.
			if confirmed {
				log.Printf("Confirming %s as stripped by %s.", toBePurged[id].Name, ses.Get("username"))
				toBePurged[id].Stripped = time.Now()
			}
			toBePurged[id].Claimed = time.Time{}
		}
		ses.Set("strip_victims", "")
		victims = []int64{}

		if r.PostFormValue("claim") == "complete" {
			w.Header().Set("Location", "strip")
			w.WriteHeader(http.StatusFound)
			return
		}
	}

	if r.PostFormValue("claim") == "victims" {
		var victims []int64
		count := 0
		for id, m := range toBePurged {
			if !m.Roles {
				// Skip anything without roles
				continue
			}

			if time.Since(m.Stripped) <= time.Hour*24 {
				// Skip anything that we think we've stripped
				continue
			}

			if time.Since(m.Claimed) <= time.Hour*1 {
				// If it's been claimed in the last hour, skip it.
				continue
			}

			// IsRegistered is nil for registered characters
			if m.IsRegistered != nil {
				if m.IsRegistered() == true {
					continue
				}
			}

			toBePurged[id].Claimed = time.Now()
			victims = append(victims, id)
			count++
			if count >= 10 {
				break
			}
		}
		ses.Set("strip_victims", victimsToStr(victims))
		w.Header().Set("Location", "strip")
		w.WriteHeader(http.StatusFound)
		return
	}

	stripTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/strip.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}

	type StripData struct {
		Title   string
		Members []purgeMember
	}
	sd := StripData{Title: "Strip Roles"}

	for _, id := range victims {
		m, ok := toBePurged[id]
		if !ok {
			continue
		}

		if !m.Roles {
			// Skip anything without roles
			continue
		}

		if time.Since(m.Stripped) <= time.Hour*24 {
			// Skip anything that we think we've stripped
			continue
		}

		toBePurged[id].Claimed = time.Now()
		sd.Members = append(sd.Members, *m)
	}

	err = stripTemplate.Execute(w, sd)
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleBoot(w http.ResponseWriter, r *http.Request, ses Session) {
	r.ParseForm()

	purgeLock.Lock()
	defer purgeLock.Unlock()
	go saveState()

	victimsStr := ses.Get("boot_victims")
	victims := strToVictims(victimsStr)

	if r.PostFormValue("claim") != "" {
		// Complete the ones marked as completed.  This should be undone if
		// necessary on the next API pull.
		for _, id := range victims {
			var confirmed bool
			if _, ok := toBePurged[id]; !ok {
				continue
			}

			for _, strid := range r.PostForm["kicked"] {
				cid, err := strconv.ParseInt(strid, 10, 64)
				if err != nil {
					continue
				}

				if id == cid {
					confirmed = true
				}
			}

			// mark as purged if confirmation, unclaim it
			if confirmed {
				log.Printf("Confirming %s as purged by %s.", toBePurged[id].Name, ses.Get("username"))
				toBePurged[id].Purged = true
			}
			toBePurged[id].Claimed = time.Time{}
		}
		ses.Set("boot_victims", "")
		victims = []int64{}

		if r.PostFormValue("claim") == "complete" {
			w.Header().Set("Location", "boot")
			w.WriteHeader(http.StatusFound)
			return
		}
	}

	if r.PostFormValue("claim") == "victims" {
		var victims []int64
		count := 0
		for id, m := range toBePurged {
			if m.Roles || m.Purged {
				// Skip anything with roles or that we think we've purged
				continue
			}

			if time.Since(m.Stripped) <= time.Hour*24 {
				// Skip anything that we know is in stasis
				continue
			}

			if time.Since(m.Claimed) <= time.Hour*1 {
				// If it's been claimed in the last hour, skip it.
				continue
			}

			// IsRegistered is nil for registered characters
			if m.IsRegistered != nil {
				if m.IsRegistered() == true {
					continue
				}
			}

			toBePurged[id].Claimed = time.Now()
			victims = append(victims, id)
			count++
			if count >= 10 {
				break
			}
		}
		ses.Set("boot_victims", victimsToStr(victims))
		w.Header().Set("Location", "boot")
		w.WriteHeader(http.StatusFound)
		return
	}

	bootTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/boot.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}

	type BootData struct {
		Title   string
		Members []purgeMember
	}
	bd := BootData{Title: "Slopes for the Slope Throne"}

	for _, id := range victims {
		m, ok := toBePurged[id]
		if !ok {
			continue
		}

		if m.Roles || m.Purged {
			// Skip anything without roles
			continue
		}

		if time.Since(m.Stripped) <= time.Hour*24 {
			// Skip anything that we think we've stripped
			continue
		}

		toBePurged[id].Claimed = time.Now()
		bd.Members = append(bd.Members, *m)
	}

	err = bootTemplate.Execute(w, bd)
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request, ses Session) {
	rootTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/root.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}
	err = rootTemplate.Execute(w, map[string]string{"Title": "Slope Maker"})
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleStats(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	printStats(w)
}

func printStats(w io.Writer) {
	purgeLock.Lock()
	defer purgeLock.Unlock()

	var totalMembers int
	var needsStripped int
	var needsPurged int
	var inStasis int
	var claimed int

	for _, m := range toBePurged {
		totalMembers++

		if time.Since(m.Claimed) <= 1*time.Hour && time.Since(m.Stripped) > 24*time.Hour {
			claimed++
		}
		if time.Since(m.Stripped) <= 24*time.Hour {
			inStasis++
		} else if m.Roles {
			needsStripped++
		}
		if !m.Roles && time.Since(m.Stripped) > 24*time.Hour {
			needsPurged++
		}
	}
	fmt.Fprintf(w, "Total: %d  Claimed: %d  ToBePurged:  %d\n", totalMembers, claimed, needsPurged)
	fmt.Fprintf(w, "ToBeStripped: %d  InStasis: %d\n\n--------------\n", needsStripped, inStasis)
	for _, m := range toBePurged {
		fmt.Fprintf(w, "%s\n", m.Name)
	}

	log.Printf("Total: %d  Claimed: %d  ToBePurged:  %d", totalMembers, claimed, needsPurged)
	log.Printf("ToBeStripped: %d  InStasis: %d", needsStripped, inStasis)
}
<commit_msg>add reason to stats<commit_after>package main

import (
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"time"
)

func formatTime(t time.Time) string {
	return t.Format(ApiDateTimeFormat)
}

func daysSince(t time.Time) string {
	days := time.Since(t) / time.Hour / 24
	return fmt.Sprintf("%d", days)
}

var tFuncMap = template.FuncMap{
	"datetime":  formatTime,
	"dayssince": daysSince,
}

//apparently my sessions are bad and i should feel bad.
func victimsToStr(victims []int64) string {
	var strs []string
	for _, vid := range victims {
		strs = append(strs, fmt.Sprintf("%d", vid))
	}
	return strings.Join(strs, ",")
}

func strToVictims(victimStr string) []int64 {
	var victims []int64

	strs := strings.Split(victimStr, ",")
	for _, vidstr := range strs {
		vid, err := strconv.ParseInt(vidstr, 10, 64)
		if err != nil {
			continue
		}
		victims = append(victims, vid)
	}
	return victims
}

func handleStrip(w http.ResponseWriter, r *http.Request, ses Session) {
	r.ParseForm()

	purgeLock.Lock()
	defer purgeLock.Unlock()
	go saveState()

	victimsStr := ses.Get("strip_victims")
	victims := strToVictims(victimsStr)

	if r.PostFormValue("claim") != "" {
		// Complete the ones marked as completed.  This should be undone if
		// necessary on the next API pull.
		for _, id := range victims {
			var confirmed bool
			if _, ok := toBePurged[id]; !ok {
				continue
			}

			for _, strid := range r.PostForm["stripped"] {
				cid, err := strconv.ParseInt(strid, 10, 64)
				if err != nil {
					continue
				}

				if id == cid {
					confirmed = true
				}
			}

			// mark as purged if confirmation, unclaim it otherwise.
			if confirmed {
				log.Printf("Confirming %s as stripped by %s.", toBePurged[id].Name, ses.Get("username"))
				toBePurged[id].Stripped = time.Now()
			}
			toBePurged[id].Claimed = time.Time{}
		}
		ses.Set("strip_victims", "")
		victims = []int64{}

		if r.PostFormValue("claim") == "complete" {
			w.Header().Set("Location", "strip")
			w.WriteHeader(http.StatusFound)
			return
		}
	}

	if r.PostFormValue("claim") == "victims" {
		var victims []int64
		count := 0
		for id, m := range toBePurged {
			if !m.Roles {
				// Skip anything without roles
				continue
			}

			if time.Since(m.Stripped) <= time.Hour*24 {
				// Skip anything that we think we've stripped
				continue
			}

			if time.Since(m.Claimed) <= time.Hour*1 {
				// If it's been claimed in the last hour, skip it.
				continue
			}

			// IsRegistered is nil for registered characters
			if m.IsRegistered != nil {
				if m.IsRegistered() == true {
					continue
				}
			}

			toBePurged[id].Claimed = time.Now()
			victims = append(victims, id)
			count++
			if count >= 10 {
				break
			}
		}
		ses.Set("strip_victims", victimsToStr(victims))
		w.Header().Set("Location", "strip")
		w.WriteHeader(http.StatusFound)
		return
	}

	stripTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/strip.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}

	type StripData struct {
		Title   string
		Members []purgeMember
	}
	sd := StripData{Title: "Strip Roles"}

	for _, id := range victims {
		m, ok := toBePurged[id]
		if !ok {
			continue
		}

		if !m.Roles {
			// Skip anything without roles
			continue
		}

		if time.Since(m.Stripped) <= time.Hour*24 {
			// Skip anything that we think we've stripped
			continue
		}

		toBePurged[id].Claimed = time.Now()
		sd.Members = append(sd.Members, *m)
	}

	err = stripTemplate.Execute(w, sd)
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleBoot(w http.ResponseWriter, r *http.Request, ses Session) {
	r.ParseForm()

	purgeLock.Lock()
	defer purgeLock.Unlock()
	go saveState()

	victimsStr := ses.Get("boot_victims")
	victims := strToVictims(victimsStr)

	if r.PostFormValue("claim") != "" {
		// Complete the ones marked as completed.  This should be undone if
		// necessary on the next API pull.
		for _, id := range victims {
			var confirmed bool
			if _, ok := toBePurged[id]; !ok {
				continue
			}

			for _, strid := range r.PostForm["kicked"] {
				cid, err := strconv.ParseInt(strid, 10, 64)
				if err != nil {
					continue
				}

				if id == cid {
					confirmed = true
				}
			}

			// mark as purged if confirmation, unclaim it
			if confirmed {
				log.Printf("Confirming %s as purged by %s.", toBePurged[id].Name, ses.Get("username"))
				toBePurged[id].Purged = true
			}
			toBePurged[id].Claimed = time.Time{}
		}
		ses.Set("boot_victims", "")
		victims = []int64{}

		if r.PostFormValue("claim") == "complete" {
			w.Header().Set("Location", "boot")
			w.WriteHeader(http.StatusFound)
			return
		}
	}

	if r.PostFormValue("claim") == "victims" {
		var victims []int64
		count := 0
		for id, m := range toBePurged {
			if m.Roles || m.Purged {
				// Skip anything with roles or that we think we've purged
				continue
			}

			if time.Since(m.Stripped) <= time.Hour*24 {
				// Skip anything that we know is in stasis
				continue
			}

			if time.Since(m.Claimed) <= time.Hour*1 {
				// If it's been claimed in the last hour, skip it.
				continue
			}

			// IsRegistered is nil for registered characters
			if m.IsRegistered != nil {
				if m.IsRegistered() == true {
					continue
				}
			}

			toBePurged[id].Claimed = time.Now()
			victims = append(victims, id)
			count++
			if count >= 10 {
				break
			}
		}
		ses.Set("boot_victims", victimsToStr(victims))
		w.Header().Set("Location", "boot")
		w.WriteHeader(http.StatusFound)
		return
	}

	bootTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/boot.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}

	type BootData struct {
		Title   string
		Members []purgeMember
	}
	bd := BootData{Title: "Slopes for the Slope Throne"}

	for _, id := range victims {
		m, ok := toBePurged[id]
		if !ok {
			continue
		}

		if m.Roles || m.Purged {
			// Skip anything without roles
			continue
		}

		if time.Since(m.Stripped) <= time.Hour*24 {
			// Skip anything that we think we've stripped
			continue
		}

		toBePurged[id].Claimed = time.Now()
		bd.Members = append(bd.Members, *m)
	}

	err = bootTemplate.Execute(w, bd)
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request, ses Session) {
	rootTemplate, err := template.New("base.html").Funcs(tFuncMap).ParseFiles("templates/base.html", "templates/root.html")
	if err != nil {
		log.Printf("Template error: %s", err)
		return
	}
	err = rootTemplate.Execute(w, map[string]string{"Title": "Slope Maker"})
	if err != nil {
		log.Printf("Template Error: %s", err)
	}
}

func handleStats(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain")
	printStats(w)
}

func printStats(w io.Writer) {
	purgeLock.Lock()
	defer purgeLock.Unlock()

	var totalMembers int
	var needsStripped int
	var needsPurged int
	var inStasis int
	var claimed int

	for _, m := range toBePurged {
		totalMembers++

		if time.Since(m.Claimed) <= 1*time.Hour && time.Since(m.Stripped) > 24*time.Hour {
			claimed++
		}
		if time.Since(m.Stripped) <= 24*time.Hour {
			inStasis++
		} else if m.Roles {
			needsStripped++
		}
		if !m.Roles && time.Since(m.Stripped) > 24*time.Hour {
			needsPurged++
		}
	}
	fmt.Fprintf(w, "Total: %d  Claimed: %d  ToBePurged:  %d\n", totalMembers, claimed, needsPurged)
	fmt.Fprintf(w, "ToBeStripped: %d  InStasis: %d\n\n--------------\n", needsStripped, inStasis)
	for _, m := range toBePurged {
		fmt.Fprintf(w, "%s	%s\n", m.Name, m.Reason)
	}

	log.Printf("Total: %d  Claimed: %d  ToBePurged:  %d", totalMembers, claimed, needsPurged)
	log.Printf("ToBeStripped: %d  InStasis: %d", needsStripped, inStasis)
}
<|endoftext|>


<commit_before>package main

import (
	"strconv"

	"github.com/nboughton/misc/markov"
	"github.com/pilu/traffic"
)

// ResponseData is exactly what you think it is
type ResponseData struct {
	Text string
}

// RootHandler renders and returns the main page
func RootHandler(w traffic.ResponseWriter, r *traffic.Request) {
	w.Render("index")
}

// ReqHandler returns generated markov strings from the source specified
// in the request URL or picks a source if the one given is invalid
func ReqHandler(w traffic.ResponseWriter, r *traffic.Request) {
	var (
		file     = r.Param("source")
		words, _ = strconv.Atoi(r.Param("words"))
		text     = markov.TrimToSentence(data[file].Generate(words))
	)

	// check that source is valid before checking string length
	_, ok := data[file]
	if !ok {
		// pseudo-randomly pick a source since maps do not return in order
		// of assignment
		for k := range data {
			file = k
			break
		}
	}

	// ensure we have return output
	for len(text) < 10 {
		words++
		text = markov.TrimToSentence(data[file].Generate(words))
	}

	w.WriteJSON(&ResponseData{Text: text})
}
<commit_msg>higher iteration for ensuring string length<commit_after>package main

import (
	"strconv"

	"github.com/nboughton/misc/markov"
	"github.com/pilu/traffic"
)

// ResponseData is exactly what you think it is
type ResponseData struct {
	Text string
}

// RootHandler renders and returns the main page
func RootHandler(w traffic.ResponseWriter, r *traffic.Request) {
	w.Render("index")
}

// ReqHandler returns generated markov strings from the source specified
// in the request URL or picks a source if the one given is invalid
func ReqHandler(w traffic.ResponseWriter, r *traffic.Request) {
	var (
		file     = r.Param("source")
		words, _ = strconv.Atoi(r.Param("words"))
		text     = markov.TrimToSentence(data[file].Generate(words))
	)

	// check that source is valid before checking string length
	_, ok := data[file]
	if !ok {
		// pseudo-randomly pick a source since maps do not return in order
		// of assignment
		for k := range data {
			file = k
			break
		}
	}

	// ensure we have return output
	for len(text) < 10 {
		words += 5
		text = markov.TrimToSentence(data[file].Generate(words))
	}

	w.WriteJSON(&ResponseData{Text: text})
}
<|endoftext|>


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

package routing

import (
	"net/http"
	"strings"
	"fmt"
	"time"
	"sync"
	"os"
	"path/filepath"
)

// LogFunc logs a message using the given format and optional arguments.
// The usage of format and arguments is similar to those for fmt.Printf().
type LogFunc func(format string, a ...interface{})

// HTTPHandlerFunc adapts a http.HandlerFunc into a routing.Handler.
func HTTPHandlerFunc(h http.HandlerFunc) Handler {
	return func(c *Context) {
		h(c.Response, c.Request)
	}
}

// HTTPHandler adapts a http.Handler into a routing.Handler.
func HTTPHandler(h http.Handler) Handler {
	return func(c *Context) {
		h.ServeHTTP(c.Response, c.Request)
	}
}

// ErrorHandler returns a handler that handles the error recorded in Context.Error.
//
// If Context.Error is an HTTPError, ErrorHandler will set the response status code
// as the status code specified by Context.Error; Otherwise, ErrorHandler will
// set the status code to be 500 (http.StatusInternalServerError) and log
// the error using the specified LogFunc (if it is not nil).
//
// This handler is usually used as one of the last handlers for a router.
func ErrorHandler(f LogFunc) Handler {
	return func(c *Context) HTTPError {
		if err, ok := c.Error.(HTTPError); ok {
			c.Response.WriteHeader(err.Code())
			return err
		}
		if f != nil {
			f("%v", c.Error)
		}
		c.Response.WriteHeader(http.StatusInternalServerError)
		return NewHTTPError(http.StatusInternalServerError)
	}
}

// NotFoundHandler returns a handler that triggers an HTTPError with the status http.StatusNotFound.
//
// This handler is usually used as one of the last handlers for a router.
func NotFoundHandler() Handler {
	return func() {
		panic(NewHTTPError(http.StatusNotFound))
	}
}

// TrailingSlashRemover returns a handler that removes trailing slashes from the requested URL.
// The handler will redirect the browser to the new URL without trailing slashes.
// The status parameter should be either http.StatusMovedPermanently (301) or http.StatusFound (302).
// If the original URL has no trailing slashes, nothing will be done by this handler.
func TrailingSlashRemover(status int) Handler {
	return func(c *Context) {
		if c.Request.URL.Path != "/" && strings.HasSuffix(c.Request.URL.Path, "/") {
			http.Redirect(c.Response, c.Request, strings.TrimRight(c.Request.URL.Path, "/"), status)
		} else {
			c.Next()
		}
	}
}

// AccessLogger returns a handler that logs a message for every request.
// The access log messages contain information including client IPs, time used to serve each request, request line,
// response status and size.
func AccessLogger(log LogFunc) Handler {
	var mu sync.Mutex
	return func(c *Context) {
		startTime := time.Now()

		req := c.Request
		rw := &logResponseWriter{c.Response, http.StatusOK, 0}
		c.Response = rw

		c.Next()

		clientIP := getClientIP(req)
		elapsed := float64(time.Now().Sub(startTime).Nanoseconds()) / 1e6
		requestLine := fmt.Sprintf("%s %s %s", req.Method, req.RequestURI, req.Proto)
		mu.Lock()
		defer mu.Unlock()
		log(`[%s] [%.3fms] %s %d %d`, clientIP, elapsed, requestLine, rw.status, rw.bytesWritten)
	}
}

func getClientIP(req *http.Request) string {
	ip := req.Header.Get("X-Real-IP")
	if ip == "" {
		ip = req.Header.Get("X-Forwarded-For")
		if ip == "" {
			ip = req.RemoteAddr
		}
	}
	if colon := strings.LastIndex(ip, ":"); colon != -1 {
		ip = ip[:colon]
	}
	return ip
}

type logResponseWriter struct {
	http.ResponseWriter
	status       int
	bytesWritten int64
}

func (r *logResponseWriter) Write(p []byte) (int, error) {
	written, err := r.ResponseWriter.Write(p)
	r.bytesWritten += int64(written)
	return written, err
}

func (r *logResponseWriter) WriteHeader(status int) {
	r.status = status
	r.ResponseWriter.WriteHeader(status)
}

// StaticOptions defines the possible options for StaticFolder handler.
type StaticOptions struct {
	// The prefix in the URL path that should not be considered as part of the file path.
	// For example, if the URL path is "/foo/bar/index.html" and the Prefix is set as "/foo",
	// then the file "/bar/index.html" would be served.
	Prefix    string
	// The file (e.g. index.html) to be served when the current request corresponds to a directory.
	// It is defaulted to "index.html". If the file does not exist, the handler will pass the control
	// to the next available handler.
	IndexFile string
	// A function that checks if the requested file path is allowed. If allowed, the function
	// may do additional work such as setting Expires HTTP header.
	// Note that if the requested file path is not allowed, the function should decide whether to
	// call Context.Next() to pass the control to the next available handler.
	Allow     func(*Context, string) bool
}

// Static returns a handler that serves the files under the specified folder as response content.
// For example, if root is "static" and the handler is handling the URL path "/app/index.html",
// then the content of the file "<working dir>/static/app/index.html" may be served as the response.
func Static(root string, options StaticOptions) Handler {
	if !filepath.IsAbs(root) {
		root = filepath.Join(RootPath, root)
	}
	if options.IndexFile == "" {
		options.IndexFile = "index.html"
	}

	// limit the files to be served within the specified folder
	dir := http.Dir(root)

	return func(c *Context) {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			c.Next()
			return
		}
		path := c.Request.URL.Path
		if options.Prefix != "" {
			if !strings.HasPrefix(path, options.Prefix) {
				c.Next()
				return
			}
			path = path[len(options.Prefix):]
			if path != "" && path[0] != '/' {
				c.Next()
				return
			}
		}
		if options.Allow != nil && !options.Allow(c, path) {
			return
		}

		var (
			file http.File
			fstat os.FileInfo
			err error
		)

		if file, err = dir.Open(path); err != nil {
			c.Next()
			return
		}
		defer file.Close()

		if fstat, err = file.Stat(); err != nil {
			c.Next()
			return
		}

		// if it's a directory, try the index file
		if fstat.IsDir() {
			path = filepath.Join(path, options.IndexFile)
			if file, err = dir.Open(path); err != nil {
				c.Next()
				return
			}
			defer file.Close()

			if fstat, err = file.Stat(); err != nil || fstat.IsDir() {
				c.Next()
				return
			}
		}

		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
	}
}

// StaticFile returns a handler that serves the content of the specified file as the response.
// If the specified file does not exist, the handler will pass the control to the next available handler.
func StaticFile(path string) Handler {
	if !filepath.IsAbs(path) {
		path = filepath.Join(RootPath, path)
	}
	return func(c *Context) {
		if file, err := os.Open(path); err == nil {
			if fs, err2 := file.Stat(); err2 == nil {
				http.ServeContent(c.Response, c.Request, path, fs.ModTime(), file)
				return
			}
		}
		c.Next()
	}
}
<commit_msg>make StaticOptions optional<commit_after>// Copyright 2015 Qiang Xue. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package routing

import (
	"net/http"
	"strings"
	"fmt"
	"time"
	"sync"
	"os"
	"path/filepath"
)

// LogFunc logs a message using the given format and optional arguments.
// The usage of format and arguments is similar to those for fmt.Printf().
type LogFunc func(format string, a ...interface{})

// HTTPHandlerFunc adapts a http.HandlerFunc into a routing.Handler.
func HTTPHandlerFunc(h http.HandlerFunc) Handler {
	return func(c *Context) {
		h(c.Response, c.Request)
	}
}

// HTTPHandler adapts a http.Handler into a routing.Handler.
func HTTPHandler(h http.Handler) Handler {
	return func(c *Context) {
		h.ServeHTTP(c.Response, c.Request)
	}
}

// ErrorHandler returns a handler that handles the error recorded in Context.Error.
//
// If Context.Error is an HTTPError, ErrorHandler will set the response status code
// as the status code specified by Context.Error; Otherwise, ErrorHandler will
// set the status code to be 500 (http.StatusInternalServerError) and log
// the error using the specified LogFunc (if it is not nil).
//
// This handler is usually used as one of the last handlers for a router.
func ErrorHandler(f LogFunc) Handler {
	return func(c *Context) HTTPError {
		if err, ok := c.Error.(HTTPError); ok {
			c.Response.WriteHeader(err.Code())
			return err
		}
		if f != nil {
			f("%v", c.Error)
		}
		c.Response.WriteHeader(http.StatusInternalServerError)
		return NewHTTPError(http.StatusInternalServerError)
	}
}

// NotFoundHandler returns a handler that triggers an HTTPError with the status http.StatusNotFound.
//
// This handler is usually used as one of the last handlers for a router.
func NotFoundHandler() Handler {
	return func() {
		panic(NewHTTPError(http.StatusNotFound))
	}
}

// TrailingSlashRemover returns a handler that removes trailing slashes from the requested URL.
// The handler will redirect the browser to the new URL without trailing slashes.
// The status parameter should be either http.StatusMovedPermanently (301) or http.StatusFound (302).
// If the original URL has no trailing slashes, nothing will be done by this handler.
func TrailingSlashRemover(status int) Handler {
	return func(c *Context) {
		if c.Request.URL.Path != "/" && strings.HasSuffix(c.Request.URL.Path, "/") {
			http.Redirect(c.Response, c.Request, strings.TrimRight(c.Request.URL.Path, "/"), status)
		} else {
			c.Next()
		}
	}
}

// AccessLogger returns a handler that logs a message for every request.
// The access log messages contain information including client IPs, time used to serve each request, request line,
// response status and size.
func AccessLogger(log LogFunc) Handler {
	var mu sync.Mutex
	return func(c *Context) {
		startTime := time.Now()

		req := c.Request
		rw := &logResponseWriter{c.Response, http.StatusOK, 0}
		c.Response = rw

		c.Next()

		clientIP := getClientIP(req)
		elapsed := float64(time.Now().Sub(startTime).Nanoseconds()) / 1e6
		requestLine := fmt.Sprintf("%s %s %s", req.Method, req.RequestURI, req.Proto)
		mu.Lock()
		defer mu.Unlock()
		log(`[%s] [%.3fms] %s %d %d`, clientIP, elapsed, requestLine, rw.status, rw.bytesWritten)
	}
}

func getClientIP(req *http.Request) string {
	ip := req.Header.Get("X-Real-IP")
	if ip == "" {
		ip = req.Header.Get("X-Forwarded-For")
		if ip == "" {
			ip = req.RemoteAddr
		}
	}
	if colon := strings.LastIndex(ip, ":"); colon != -1 {
		ip = ip[:colon]
	}
	return ip
}

type logResponseWriter struct {
	http.ResponseWriter
	status       int
	bytesWritten int64
}

func (r *logResponseWriter) Write(p []byte) (int, error) {
	written, err := r.ResponseWriter.Write(p)
	r.bytesWritten += int64(written)
	return written, err
}

func (r *logResponseWriter) WriteHeader(status int) {
	r.status = status
	r.ResponseWriter.WriteHeader(status)
}

// StaticOptions defines the possible options for StaticFolder handler.
type StaticOptions struct {
	// The prefix in the URL path that should not be considered as part of the file path.
	// For example, if the URL path is "/foo/bar/index.html" and the Prefix is set as "/foo",
	// then the file "/bar/index.html" would be served.
	Prefix    string
	// The file (e.g. index.html) to be served when the current request corresponds to a directory.
	// It is defaulted to "index.html". If the file does not exist, the handler will pass the control
	// to the next available handler.
	IndexFile string
	// A function that checks if the requested file path is allowed. If allowed, the function
	// may do additional work such as setting Expires HTTP header.
	// Note that if the requested file path is not allowed, the function should decide whether to
	// call Context.Next() to pass the control to the next available handler.
	Allow     func(*Context, string) bool
}

// Static returns a handler that serves the files under the specified folder as response content.
// For example, if root is "static" and the handler is handling the URL path "/app/index.html",
// then the content of the file "<working dir>/static/app/index.html" may be served as the response.
func Static(root string, opts ...StaticOptions) Handler {
	if !filepath.IsAbs(root) {
		root = filepath.Join(RootPath, root)
	}
	options := StaticOptions{}
	if len(opts) > 0 {
		options = opts[0]
	}
	if options.IndexFile == "" {
		options.IndexFile = "index.html"
	}

	// limit the files to be served within the specified folder
	dir := http.Dir(root)

	return func(c *Context) {
		if c.Request.Method != "GET" && c.Request.Method != "HEAD" {
			c.Next()
			return
		}
		path := c.Request.URL.Path
		if options.Prefix != "" {
			if !strings.HasPrefix(path, options.Prefix) {
				c.Next()
				return
			}
			path = path[len(options.Prefix):]
			if path != "" && path[0] != '/' {
				c.Next()
				return
			}
		}
		if options.Allow != nil && !options.Allow(c, path) {
			return
		}

		var (
			file http.File
			fstat os.FileInfo
			err error
		)

		if file, err = dir.Open(path); err != nil {
			c.Next()
			return
		}
		defer file.Close()

		if fstat, err = file.Stat(); err != nil {
			c.Next()
			return
		}

		// if it's a directory, try the index file
		if fstat.IsDir() {
			path = filepath.Join(path, options.IndexFile)
			if file, err = dir.Open(path); err != nil {
				c.Next()
				return
			}
			defer file.Close()

			if fstat, err = file.Stat(); err != nil || fstat.IsDir() {
				c.Next()
				return
			}
		}

		http.ServeContent(c.Response, c.Request, path, fstat.ModTime(), file)
	}
}

// StaticFile returns a handler that serves the content of the specified file as the response.
// If the specified file does not exist, the handler will pass the control to the next available handler.
func StaticFile(path string) Handler {
	if !filepath.IsAbs(path) {
		path = filepath.Join(RootPath, path)
	}
	return func(c *Context) {
		if file, err := os.Open(path); err == nil {
			if fs, err2 := file.Stat(); err2 == nil {
				http.ServeContent(c.Response, c.Request, path, fs.ModTime(), file)
				return
			}
		}
		c.Next()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"net/http"

	router "github.com/julienschmidt/httprouter"
)

var (
	messages []Message
)

func getTemlates(name string) (*template.Template, error) {
	return template.ParseFiles(Config.Template.Root, Config.Template.TemplateMap[name])
}

// PageMainHandler handler for main page
func PageMainHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	Db.Preload("User").Find(&messages)
	Socket()
	templates, err := getTemlates("main")
	main := template.Must(templates, err)
	main.Execute(w, messages)
}

// GetLoginHandler - render template for login page
func GetLoginHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	templates, err := getTemlates("login")
	login := template.Must(templates, err)
	login.Execute(w, nil)
}

// PostLoginHandler - parce form, validate and save user
func PostLoginHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	r.ParseForm()
	// logic part of log in
	Debug.Printf("%#v", r.Form)
	Debug.Println("login:", r.Form["login"])
	Debug.Println("password:", r.Form["password"])
}
<commit_msg>Update login form<commit_after>package main

import (
	"html/template"
	"net/http"

	router "github.com/julienschmidt/httprouter"
)

var (
	messages []Message
)

func getTemlates(name string) (*template.Template, error) {
	return template.ParseFiles(Config.Template.Root, Config.Template.TemplateMap[name])
}

// PageMainHandler handler for main page
func PageMainHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	Db.Preload("User").Find(&messages)
	Socket()
	templates, err := getTemlates("main")
	main := template.Must(templates, err)
	main.Execute(w, messages)
}

// GetLoginHandler - render template for login page
func GetLoginHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	templates, err := getTemlates("login")
	login := template.Must(templates, err)
	login.Execute(w, nil)
}

// PostLoginHandler - parce form, validate and save user
func PostLoginHandler(w http.ResponseWriter, r *http.Request, _ router.Params) {
	r.ParseForm()
	// logic part of log in
	Debug.Printf("%#v", r.Form)
	Debug.Println("login:", r.FormValue("login"))
	Debug.Println("password:", r.FormValue("password"))
}
<|endoftext|>


<commit_before>package services

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

const (
	NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID = 222
	BLOCK_ITEM_PRICE_ID                    = 40678 // file or block item price id
	CREATE_ISCSI_VOLUME_MAX_RETRY_TIME     = 12
	CREATE_ISCSI_VOLUME_CHECK_INTERVAL     = 5 // seconds
)

type softLayer_Network_Storage_Service struct {
	client softlayer.Client
}

func NewSoftLayer_Network_Storage_Service(client softlayer.Client) *softLayer_Network_Storage_Service {
	return &softLayer_Network_Storage_Service{
		client: client,
	}
}

func (slns *softLayer_Network_Storage_Service) GetName() string {
	return "SoftLayer_Network_Storage"
}

func (slns *softLayer_Network_Storage_Service) CreateIscsiVolume(size int, location string) (datatypes.SoftLayer_Network_Storage, error) {
	if size < 0 {
		return datatypes.SoftLayer_Network_Storage{}, errors.New("Cannot create negative sized volumes")
	}

	sizeItemPriceId, err := slns.getIscsiVolumeItemIdBasedOnSize(size)
	iopsItemPriceId, err := slns.getPerformanceStorageItemPriceIdByIops(size)

	/*if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}*/

	order := datatypes.SoftLayer_Product_Order{
		Location:    location,
		ComplexType: "SoftLayer_Container_Product_Order_Network_PerformanceStorage_Iscsi",
		OsFormatType: datatypes.OsFormatType{
			Id:      12,
			KeyName: "LINUX",
		},
		Prices: []datatypes.SoftLayer_Item_Price{
			datatypes.SoftLayer_Item_Price{
				Id: sizeItemPriceId,
			},
			datatypes.SoftLayer_Item_Price{
				Id: iopsItemPriceId,
			},
			datatypes.SoftLayer_Item_Price{
				Id: BLOCK_ITEM_PRICE_ID,
			},
		},
		PackageId: NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID,
		Quantity:  1,
	}

	productOrderService, _ := slns.client.GetSoftLayer_Product_Order_Service()
	receipt, err := productOrderService.PlaceOrder(order)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	var iscsiStorage datatypes.SoftLayer_Network_Storage

	for i := 0; i < CREATE_ISCSI_VOLUME_MAX_RETRY_TIME; i++ {
		iscsiStorage, err = slns.findIscsiVolumeId(receipt.OrderId)
		if err == nil {
			break
		} else if i == CREATE_ISCSI_VOLUME_MAX_RETRY_TIME-1 {
			return datatypes.SoftLayer_Network_Storage{}, err
		}

		time.Sleep(CREATE_ISCSI_VOLUME_CHECK_INTERVAL * time.Second)
	}

	return iscsiStorage, nil
}

func (slns *softLayer_Network_Storage_Service) DeleteIscsiVolume(volumeId int, immediateCancellationFlag bool) error {
	ObjectFilter := string(`{"iscsiNetworkStorage":{"id":{"operation":` + strconv.Itoa(volumeId) + `}}}`)
	accountService, _ := slns.client.GetSoftLayer_Account_Service()
	iscsiStorages, _ := accountService.GetIscsiNetworkStorageWithFilter(ObjectFilter)

	var accountId, billingItemId int

	for i := 0; i < len(iscsiStorages); i++ {
		if iscsiStorages[i].Id == volumeId {
			accountId = iscsiStorages[i].AccountId
			billingItemId = iscsiStorages[i].BillingItem.Id
			break
		}
	}

	billingItemCancellationRequest := datatypes.SoftLayer_Billing_Item_Cancellation_Request{
		ComplexType: "SoftLayer_Billing_Item_Cancellation_Request",
		AccountId:   accountId,
		Items: []datatypes.SoftLayer_Billing_Item_Cancellation_Request_Item{
			{
				BillingItemId:             billingItemId,
				ImmediateCancellationFlag: immediateCancellationFlag,
			},
		},
	}

	billingItemCancellationRequestService, _ := slns.client.GetSoftLayer_Billing_Item_Cancellation_Request_Service()
	_, err := billingItemCancellationRequestService.CreateObject(billingItemCancellationRequest)
	if err != nil {
		return err
	}

	return nil
}

func (slns *softLayer_Network_Storage_Service) GetIscsiVolume(volumeId int) (datatypes.SoftLayer_Network_Storage, error) {
	response, err := slns.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getObject.json", slns.GetName(), volumeId), "GET", new(bytes.Buffer))
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	volume := datatypes.SoftLayer_Network_Storage{}
	err = json.Unmarshal(response, &volume)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	return volume, nil
}

// Private methods

func (slns *softLayer_Network_Storage_Service) findIscsiVolumeId(orderId int) (datatypes.SoftLayer_Network_Storage, error) {
	ObjectFilter := string(`{"iscsiNetworkStorage":{"billingItem":{"orderItem":{"order":{"id":{"operation":` + strconv.Itoa(orderId) + `}}}}}}`)
	accountService, _ := slns.client.GetSoftLayer_Account_Service()

	iscsiStorages, _ := accountService.GetIscsiNetworkStorageWithFilter(ObjectFilter)

	if len(iscsiStorages) == 1 {
		return iscsiStorages[0], nil
	}

	return datatypes.SoftLayer_Network_Storage{}, errors.New(fmt.Sprintf("Can not find an performance storage (iSCSI volume) with order id %d", orderId))
}

func (slns *softLayer_Network_Storage_Service) getIscsiVolumeItemIdBasedOnSize(size int) (int, error) {
	productPackageService, err := slns.client.GetSoftLayer_Product_Package_Service()
	if err != nil {
		return 0, err
	}

	itemPrices, err := productPackageService.GetItemPricesBySize(NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID, size)
	if err != nil {
		return 0, err
	}

	var currentItemId int

	if len(itemPrices) > 0 {
		for _, itemPrice := range itemPrices {
			if itemPrice.LocationGroupId == 0 {
				currentItemId = itemPrice.Id
			}
		}
	}

	if currentItemId == 0 {
		return 0, errors.New(fmt.Sprintf("No proper performance storage (iSCSI volume)for size %d", size))
	}

	return currentItemId, nil
}

func (slns *softLayer_Network_Storage_Service) getPerformanceStorageItemPriceIdByIops(size int) (int, error) {
	switch size {
	case 20:
		return 40838, nil // 500 IOPS
	case 40:
		return 40988, nil // 1000 IOPS
	case 80:
		return 41288, nil // 2000 IOPS
	default:
		return 41788, nil // 3000 IOPS
	}
}
<commit_msg>Add a check for error after each call<commit_after>package services

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"
	"time"

	datatypes "github.com/maximilien/softlayer-go/data_types"
	softlayer "github.com/maximilien/softlayer-go/softlayer"
)

const (
	NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID = 222
	BLOCK_ITEM_PRICE_ID                    = 40678 // file or block item price id
	CREATE_ISCSI_VOLUME_MAX_RETRY_TIME     = 12
	CREATE_ISCSI_VOLUME_CHECK_INTERVAL     = 5 // seconds
)

type softLayer_Network_Storage_Service struct {
	client softlayer.Client
}

func NewSoftLayer_Network_Storage_Service(client softlayer.Client) *softLayer_Network_Storage_Service {
	return &softLayer_Network_Storage_Service{
		client: client,
	}
}

func (slns *softLayer_Network_Storage_Service) GetName() string {
	return "SoftLayer_Network_Storage"
}

func (slns *softLayer_Network_Storage_Service) CreateIscsiVolume(size int, location string) (datatypes.SoftLayer_Network_Storage, error) {
	if size < 0 {
		return datatypes.SoftLayer_Network_Storage{}, errors.New("Cannot create negative sized volumes")
	}

	sizeItemPriceId, err := slns.getIscsiVolumeItemIdBasedOnSize(size)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	iopsItemPriceId := slns.getPerformanceStorageItemPriceIdByIops(size)

	order := datatypes.SoftLayer_Product_Order{
		Location:    location,
		ComplexType: "SoftLayer_Container_Product_Order_Network_PerformanceStorage_Iscsi",
		OsFormatType: datatypes.OsFormatType{
			Id:      12,
			KeyName: "LINUX",
		},
		Prices: []datatypes.SoftLayer_Item_Price{
			datatypes.SoftLayer_Item_Price{
				Id: sizeItemPriceId,
			},
			datatypes.SoftLayer_Item_Price{
				Id: iopsItemPriceId,
			},
			datatypes.SoftLayer_Item_Price{
				Id: BLOCK_ITEM_PRICE_ID,
			},
		},
		PackageId: NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID,
		Quantity:  1,
	}

	productOrderService, err := slns.client.GetSoftLayer_Product_Order_Service()
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	receipt, err := productOrderService.PlaceOrder(order)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	var iscsiStorage datatypes.SoftLayer_Network_Storage

	for i := 0; i < CREATE_ISCSI_VOLUME_MAX_RETRY_TIME; i++ {
		iscsiStorage, err = slns.findIscsiVolumeId(receipt.OrderId)
		if err == nil {
			break
		} else if i == CREATE_ISCSI_VOLUME_MAX_RETRY_TIME-1 {
			return datatypes.SoftLayer_Network_Storage{}, err
		}

		time.Sleep(CREATE_ISCSI_VOLUME_CHECK_INTERVAL * time.Second)
	}

	return iscsiStorage, nil
}

func (slns *softLayer_Network_Storage_Service) DeleteIscsiVolume(volumeId int, immediateCancellationFlag bool) error {
	ObjectFilter := string(`{"iscsiNetworkStorage":{"id":{"operation":` + strconv.Itoa(volumeId) + `}}}`)

	accountService, err := slns.client.GetSoftLayer_Account_Service()
	if err != nil {
		return err
	}

	iscsiStorages, err := accountService.GetIscsiNetworkStorageWithFilter(ObjectFilter)
	if err != nil {
		return err
	}

	var accountId, billingItemId int

	for i := 0; i < len(iscsiStorages); i++ {
		if iscsiStorages[i].Id == volumeId {
			accountId = iscsiStorages[i].AccountId
			billingItemId = iscsiStorages[i].BillingItem.Id
			break
		}
	}

	billingItemCancellationRequest := datatypes.SoftLayer_Billing_Item_Cancellation_Request{
		ComplexType: "SoftLayer_Billing_Item_Cancellation_Request",
		AccountId:   accountId,
		Items: []datatypes.SoftLayer_Billing_Item_Cancellation_Request_Item{
			{
				BillingItemId:             billingItemId,
				ImmediateCancellationFlag: immediateCancellationFlag,
			},
		},
	}

	billingItemCancellationRequestService, err := slns.client.GetSoftLayer_Billing_Item_Cancellation_Request_Service()
	if err != nil {
		return err
	}

	_, err = billingItemCancellationRequestService.CreateObject(billingItemCancellationRequest)
	if err != nil {
		return err
	}

	return nil
}

func (slns *softLayer_Network_Storage_Service) GetIscsiVolume(volumeId int) (datatypes.SoftLayer_Network_Storage, error) {
	response, err := slns.client.DoRawHttpRequest(fmt.Sprintf("%s/%d/getObject.json", slns.GetName(), volumeId), "GET", new(bytes.Buffer))
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	volume := datatypes.SoftLayer_Network_Storage{}
	err = json.Unmarshal(response, &volume)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	return volume, nil
}

// Private methods

func (slns *softLayer_Network_Storage_Service) findIscsiVolumeId(orderId int) (datatypes.SoftLayer_Network_Storage, error) {
	ObjectFilter := string(`{"iscsiNetworkStorage":{"billingItem":{"orderItem":{"order":{"id":{"operation":` + strconv.Itoa(orderId) + `}}}}}}`)

	accountService, err := slns.client.GetSoftLayer_Account_Service()
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	iscsiStorages, err := accountService.GetIscsiNetworkStorageWithFilter(ObjectFilter)
	if err != nil {
		return datatypes.SoftLayer_Network_Storage{}, err
	}

	if len(iscsiStorages) == 1 {
		return iscsiStorages[0], nil
	}

	return datatypes.SoftLayer_Network_Storage{}, errors.New(fmt.Sprintf("Can not find an performance storage (iSCSI volume) with order id %d", orderId))
}

func (slns *softLayer_Network_Storage_Service) getIscsiVolumeItemIdBasedOnSize(size int) (int, error) {
	productPackageService, err := slns.client.GetSoftLayer_Product_Package_Service()
	if err != nil {
		return 0, err
	}

	itemPrices, err := productPackageService.GetItemPricesBySize(NETWORK_PERFORMANCE_STORAGE_PACKAGE_ID, size)
	if err != nil {
		return 0, err
	}

	var currentItemId int

	if len(itemPrices) > 0 {
		for _, itemPrice := range itemPrices {
			if itemPrice.LocationGroupId == 0 {
				currentItemId = itemPrice.Id
			}
		}
	}

	if currentItemId == 0 {
		return 0, errors.New(fmt.Sprintf("No proper performance storage (iSCSI volume)for size %d", size))
	}

	return currentItemId, nil
}

func (slns *softLayer_Network_Storage_Service) getPerformanceStorageItemPriceIdByIops(size int) int {
	switch size {
	case 20:
		return 40838 // 500 IOPS
	case 40:
		return 40988 // 1000 IOPS
	case 80:
		return 41288 // 2000 IOPS
	default:
		return 41788 // 3000 IOPS
	}
}
<|endoftext|>