<commit_before>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/debug"
	"time"

	"github.com/FreeFeed/clio-restore/internal/account"
	"github.com/FreeFeed/clio-restore/internal/config"
	"github.com/FreeFeed/clio-restore/internal/dbutil"
	"github.com/FreeFeed/clio-restore/internal/hashtags"
	"github.com/davidmz/mustbe"
	"github.com/lib/pq"
	"gopkg.in/gomail.v2"
)

// Globals
var (
	infoLog  = log.New(os.Stdout, "INFO  ", log.LstdFlags)
	errorLog = log.New(os.Stdout, "ERROR ", log.LstdFlags)
	fatalLog = log.New(os.Stdout, "FATAL ", log.LstdFlags)
)

func main() {
	defer mustbe.Catched(func(err error) {
		fatalLog.Println(err)
		debug.PrintStack()
	})

	flag.Parse()

	conf := mustbe.OKVal(config.Load()).(*config.Config)

	db := mustbe.OKVal(sql.Open("postgres", conf.DbStr)).(*sql.DB)
	mustbe.OK(db.Ping())

	accStore := account.NewStore(db)

	// Looking for users who allow to restore their comments and likes
	var accounts []*account.Account
	mustbe.OK(dbutil.QueryRows(
		db, "select old_username from archives where restore_comments_and_likes", nil,
		func(r dbutil.RowScanner) error {
			var name string
			if err := r.Scan(&name); err != nil {
				return err
			}
			accounts = append(accounts, accStore.Get(name))
			return nil
		},
	))

	infoLog.Printf("Found %d users who allow to restore comments and likes", len(accounts))

	for _, acc := range accounts {
		infoLog.Printf("Processing %q (now %q)", acc.OldUserName, acc.NewUserName)

		if !acc.IsExists() {
			errorLog.Printf("Looks like account with old username %q doesn't exists", acc.OldUserName)
			continue
		}

		var existsComments, existsLikes bool

		mustbe.OK(db.QueryRow(
			`select exists(select 1 from hidden_comments where user_id = $1 or old_username = $2)`,
			acc.UID, acc.OldUserName,
		).Scan(&existsComments))

		mustbe.OK(db.QueryRow(
			`select exists(select 1 from hidden_likes where user_id = $1 or old_username = $2)`,
			acc.UID, acc.OldUserName,
		).Scan(&existsLikes))

		if !existsComments && !existsLikes {
			continue
		}

		dbutil.MustTransact(db, func(tx *sql.Tx) {
			if existsComments {
				infoLog.Printf("Restoring hidden comments of %q (now %q)", acc.OldUserName, acc.NewUserName)
				restoreComments(tx, acc)
			}
			if existsLikes {
				infoLog.Printf("Restoring hidden likes of %q (now %q)", acc.OldUserName, acc.NewUserName)
				restoreLikes(tx, acc)
			}
		})

		if conf.SMTPHost != "" {
			dialer := gomail.NewDialer(conf.SMTPHost, conf.SMTPPort, conf.SMTPUsername, conf.SMTPPassword)
			mail := gomail.NewMessage()
			mail.SetHeader("From", conf.SMTPFrom)
			mail.SetHeader("To", acc.Email, conf.SMTPBcc)
			mail.SetHeader("Subject", "Archive comments restoration request")
			mail.SetBody("text/plain",
				fmt.Sprintf(
					"Comments restoration for FreeFeed user %q (FriendFeed username %q) has been completed.",
					acc.NewUserName, acc.OldUserName,
				),
			)
			if err := dialer.DialAndSend(mail); err != nil {
				errorLog.Printf("Cannot send email to %q: %v", acc.Email, err)
			}
		}
	}
}

const batchSize = 100

func restoreComments(tx *sql.Tx, acc *account.Account) {
	var (
		feeds pq.Int64Array
		count int
	)
	// Feeds to append commented post to
	mustbe.OK(tx.QueryRow(
		`select array_agg(distinct f.id) from
				feeds f join subscriptions s on
					f.user_id = s.user_id and f.name = 'RiverOfNews' or f.uid = s.feed_id
				where s.feed_id = $1`,
		acc.Feeds.Comments.UID,
	).Scan(&feeds))

	processedPosts := make(map[string]bool) // postID is a key

	type commentInfo struct {
		ID     string
		PostID string
		Body   string
	}

	for {
		var comments []commentInfo
		dbutil.MustQueryRows(tx,
			`select hc.comment_id, c.post_id, hc.body from
				hidden_comments hc
				join comments c on c.uid = hc.comment_id
				where hc.user_id = $1 or hc.old_username = $2
				limit $3`,
			dbutil.Args{acc.UID, acc.OldUserName, batchSize},
			func(r dbutil.RowScanner) {
				ci := commentInfo{}
				mustbe.OK(r.Scan(&ci.ID, &ci.PostID, &ci.Body))
				comments = append(comments, ci)
			})
		if len(comments) == 0 {
			break
		}

		for _, ci := range comments {
			mustbe.OKVal(tx.Exec(
				"update comments set (body, user_id, hide_type) = ($1, $2, $3) where uid = $4",
				ci.Body, acc.UID, 0, ci.ID,
			))
			mustbe.OKVal(tx.Exec("delete from hidden_comments where comment_id = $1", ci.ID))

			for _, h := range hashtags.Extract(ci.Body) {
				dbutil.MustInsertWithoutConflict(tx, "hashtag_usages", dbutil.H{
					"hashtag_id": hashtags.GetID(tx, h),
					"entity_id":  ci.ID,
					"type":       "comment",
				})
			}

			if !processedPosts[ci.PostID] {
				if len(feeds) != 0 {
					mustbe.OKVal(tx.Exec(
						"update posts set feed_ids = feed_ids | $1 where uid = $2",
						feeds, ci.PostID,
					))
				}
				processedPosts[ci.PostID] = true
			}
			count++
		}
	}

	mustbe.OKVal(tx.Exec(
		`update user_stats set comments_count = comments_count + $1 where user_id = $2`,
		count, acc.UID,
	))

	infoLog.Printf("Restored %d comments in %d posts", count, len(processedPosts))
}

func restoreLikes(tx *sql.Tx, acc *account.Account) {
	var (
		feeds pq.Int64Array
		count int
	)
	// Feeds to append liked post to
	mustbe.OK(tx.QueryRow(
		`select array_agg(distinct f.id) from
				feeds f join subscriptions s on
					f.user_id = s.user_id and f.name = 'RiverOfNews' or f.uid = s.feed_id
				where s.feed_id = $1`,
		acc.Feeds.Likes.UID,
	).Scan(&feeds))

	type likeInfo struct {
		ID     int
		PostID string
		Date   time.Time
	}

	for {
		var likes []likeInfo

		dbutil.MustQueryRows(tx,
			`select id, post_id, date from hidden_likes
			where user_id = $1 or old_username = $2`,
			dbutil.Args{acc.UID, acc.OldUserName},
			func(r dbutil.RowScanner) {
				li := likeInfo{}
				mustbe.OK(r.Scan(&li.ID, &li.PostID, &li.Date))
				likes = append(likes, li)
			},
		)
		if len(likes) == 0 {
			break
		}

		for _, li := range likes {
			// Probably this post alreaady have like from this user
			// so we should use 'WithoutConflict'
			res := dbutil.MustInsertWithoutConflict(tx, "likes", dbutil.H{
				"post_id":    li.PostID,
				"user_id":    acc.UID,
				"created_at": li.Date,
			})
			rowsAffected := mustbe.OKVal(res.RowsAffected()).(int64)
			mustbe.OKVal(tx.Exec("delete from hidden_likes where id = $1", li.ID))
			if rowsAffected > 0 {
				if len(feeds) != 0 {
					mustbe.OKVal(tx.Exec(
						"update posts set feed_ids = feed_ids | $1 where uid = $2",
						feeds, li.PostID,
					))
				}
				count++
			}
		}
	}

	mustbe.OKVal(tx.Exec(
		`update user_stats set likes_count = likes_count + $1 where user_id = $2`,
		count, acc.UID,
	))

	infoLog.Printf("Restored %d likes", count)
}
<commit_msg>Fix typo<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/debug"
	"time"

	"github.com/FreeFeed/clio-restore/internal/account"
	"github.com/FreeFeed/clio-restore/internal/config"
	"github.com/FreeFeed/clio-restore/internal/dbutil"
	"github.com/FreeFeed/clio-restore/internal/hashtags"
	"github.com/davidmz/mustbe"
	"github.com/lib/pq"
	"gopkg.in/gomail.v2"
)

// Globals
var (
	infoLog  = log.New(os.Stdout, "INFO  ", log.LstdFlags)
	errorLog = log.New(os.Stdout, "ERROR ", log.LstdFlags)
	fatalLog = log.New(os.Stdout, "FATAL ", log.LstdFlags)
)

func main() {
	defer mustbe.Catched(func(err error) {
		fatalLog.Println(err)
		debug.PrintStack()
	})

	flag.Parse()

	conf := mustbe.OKVal(config.Load()).(*config.Config)

	db := mustbe.OKVal(sql.Open("postgres", conf.DbStr)).(*sql.DB)
	mustbe.OK(db.Ping())

	accStore := account.NewStore(db)

	// Looking for users who allow to restore their comments and likes
	var accounts []*account.Account
	mustbe.OK(dbutil.QueryRows(
		db, "select old_username from archives where restore_comments_and_likes", nil,
		func(r dbutil.RowScanner) error {
			var name string
			if err := r.Scan(&name); err != nil {
				return err
			}
			accounts = append(accounts, accStore.Get(name))
			return nil
		},
	))

	infoLog.Printf("Found %d users who allow to restore comments and likes", len(accounts))

	for _, acc := range accounts {
		infoLog.Printf("Processing %q (now %q)", acc.OldUserName, acc.NewUserName)

		if !acc.IsExists() {
			errorLog.Printf("Looks like account with old username %q doesn't exists", acc.OldUserName)
			continue
		}

		var existsComments, existsLikes bool

		mustbe.OK(db.QueryRow(
			`select exists(select 1 from hidden_comments where user_id = $1 or old_username = $2)`,
			acc.UID, acc.OldUserName,
		).Scan(&existsComments))

		mustbe.OK(db.QueryRow(
			`select exists(select 1 from hidden_likes where user_id = $1 or old_username = $2)`,
			acc.UID, acc.OldUserName,
		).Scan(&existsLikes))

		if !existsComments && !existsLikes {
			continue
		}

		dbutil.MustTransact(db, func(tx *sql.Tx) {
			if existsComments {
				infoLog.Printf("Restoring hidden comments of %q (now %q)", acc.OldUserName, acc.NewUserName)
				restoreComments(tx, acc)
			}
			if existsLikes {
				infoLog.Printf("Restoring hidden likes of %q (now %q)", acc.OldUserName, acc.NewUserName)
				restoreLikes(tx, acc)
			}
		})

		if conf.SMTPHost != "" {
			dialer := gomail.NewDialer(conf.SMTPHost, conf.SMTPPort, conf.SMTPUsername, conf.SMTPPassword)
			mail := gomail.NewMessage()
			mail.SetHeader("From", conf.SMTPFrom)
			mail.SetHeader("To", acc.Email, conf.SMTPBcc)
			mail.SetHeader("Subject", "Archive comments restoration request")
			mail.SetBody("text/plain",
				fmt.Sprintf(
					"Comments restoration for FreeFeed user %q (FriendFeed username %q) has been completed.",
					acc.NewUserName, acc.OldUserName,
				),
			)
			if err := dialer.DialAndSend(mail); err != nil {
				errorLog.Printf("Cannot send email to %q: %v", acc.Email, err)
			}
		}
	}
}

const batchSize = 100

func restoreComments(tx *sql.Tx, acc *account.Account) {
	var (
		feeds pq.Int64Array
		count int
	)
	// Feeds to append commented post to
	mustbe.OK(tx.QueryRow(
		`select array_agg(distinct f.id) from
				feeds f join subscriptions s on
					f.user_id = s.user_id and f.name = 'RiverOfNews' or f.uid = s.feed_id
				where s.feed_id = $1`,
		acc.Feeds.Comments.UID,
	).Scan(&feeds))

	processedPosts := make(map[string]bool) // postID is a key

	type commentInfo struct {
		ID     string
		PostID string
		Body   string
	}

	for {
		var comments []commentInfo
		dbutil.MustQueryRows(tx,
			`select hc.comment_id, c.post_id, hc.body from
				hidden_comments hc
				join comments c on c.uid = hc.comment_id
				where hc.user_id = $1 or hc.old_username = $2
				limit $3`,
			dbutil.Args{acc.UID, acc.OldUserName, batchSize},
			func(r dbutil.RowScanner) {
				ci := commentInfo{}
				mustbe.OK(r.Scan(&ci.ID, &ci.PostID, &ci.Body))
				comments = append(comments, ci)
			})
		if len(comments) == 0 {
			break
		}

		for _, ci := range comments {
			mustbe.OKVal(tx.Exec(
				"update comments set (body, user_id, hide_type) = ($1, $2, $3) where uid = $4",
				ci.Body, acc.UID, 0, ci.ID,
			))
			mustbe.OKVal(tx.Exec("delete from hidden_comments where comment_id = $1", ci.ID))

			for _, h := range hashtags.Extract(ci.Body) {
				dbutil.MustInsertWithoutConflict(tx, "hashtag_usages", dbutil.H{
					"hashtag_id": hashtags.GetID(tx, h),
					"entity_id":  ci.ID,
					"type":       "comment",
				})
			}

			if !processedPosts[ci.PostID] {
				if len(feeds) != 0 {
					mustbe.OKVal(tx.Exec(
						"update posts set feed_ids = feed_ids | $1 where uid = $2",
						feeds, ci.PostID,
					))
				}
				processedPosts[ci.PostID] = true
			}
			count++
		}
	}

	mustbe.OKVal(tx.Exec(
		`update user_stats set comments_count = comments_count + $1 where user_id = $2`,
		count, acc.UID,
	))

	infoLog.Printf("Restored %d comments in %d posts", count, len(processedPosts))
}

func restoreLikes(tx *sql.Tx, acc *account.Account) {
	var (
		feeds pq.Int64Array
		count int
	)
	// Feeds to append liked post to
	mustbe.OK(tx.QueryRow(
		`select array_agg(distinct f.id) from
				feeds f join subscriptions s on
					f.user_id = s.user_id and f.name = 'RiverOfNews' or f.uid = s.feed_id
				where s.feed_id = $1`,
		acc.Feeds.Likes.UID,
	).Scan(&feeds))

	type likeInfo struct {
		ID     int
		PostID string
		Date   time.Time
	}

	for {
		var likes []likeInfo

		dbutil.MustQueryRows(tx,
			`select id, post_id, date from hidden_likes
			where user_id = $1 or old_username = $2`,
			dbutil.Args{acc.UID, acc.OldUserName},
			func(r dbutil.RowScanner) {
				li := likeInfo{}
				mustbe.OK(r.Scan(&li.ID, &li.PostID, &li.Date))
				likes = append(likes, li)
			},
		)
		if len(likes) == 0 {
			break
		}

		for _, li := range likes {
			// Probably this post already have like from this user
			// so we should use 'WithoutConflict'
			res := dbutil.MustInsertWithoutConflict(tx, "likes", dbutil.H{
				"post_id":    li.PostID,
				"user_id":    acc.UID,
				"created_at": li.Date,
			})
			rowsAffected := mustbe.OKVal(res.RowsAffected()).(int64)
			mustbe.OKVal(tx.Exec("delete from hidden_likes where id = $1", li.ID))
			if rowsAffected > 0 {
				if len(feeds) != 0 {
					mustbe.OKVal(tx.Exec(
						"update posts set feed_ids = feed_ids | $1 where uid = $2",
						feeds, li.PostID,
					))
				}
				count++
			}
		}
	}

	mustbe.OKVal(tx.Exec(
		`update user_stats set likes_count = likes_count + $1 where user_id = $2`,
		count, acc.UID,
	))

	infoLog.Printf("Restored %d likes", count)
}
<|endoftext|>


<commit_before>package resolver

import (
	"fmt"

	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/installplan/v1alpha1"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

	log "github.com/sirupsen/logrus"

	csvv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/clusterserviceversion/v1alpha1"
)

// DependencyResolver defines how a something that resolves dependencies (CSVs, CRDs, etc...)
// should behave
type DependencyResolver interface {
	ResolveInstallPlan(sourceRefs []registry.SourceRef, catalogLabelKey string, plan *v1alpha1.InstallPlan) ([]v1alpha1.Step, []registry.SourceKey, error)
}

// MultiSourceResolver resolves resolves dependencies from multiple CatalogSources
type MultiSourceResolver struct{}

// ResolveInstallPlan resolves the given InstallPlan with all available sources
func (resolver *MultiSourceResolver) ResolveInstallPlan(sourceRefs []registry.SourceRef, catalogLabelKey string, plan *v1alpha1.InstallPlan) ([]v1alpha1.Step, []registry.SourceKey, error) {
	srm := make(stepResourceMap)
	var usedSourceKeys []registry.SourceKey

	for _, csvName := range plan.Spec.ClusterServiceVersionNames {
		csvSRM, used, err := resolver.resolveCSV(sourceRefs, catalogLabelKey, plan.Namespace, csvName)
		if err != nil {
			// Could not resolve CSV in any source
			return nil, nil, err
		}

		srm.Combine(csvSRM)
		usedSourceKeys = append(used, usedSourceKeys...)
	}

	return srm.Plan(), usedSourceKeys, nil
}

func (resolver *MultiSourceResolver) resolveCSV(sourceRefs []registry.SourceRef, catalogLabelKey, planNamespace, csvName string) (stepResourceMap, []registry.SourceKey, error) {
	log.Debugf("resolving CSV with name: %s", csvName)

	steps := make(stepResourceMap)
	csvNamesToBeResolved := []string{csvName}
	var usedSourceKeys []registry.SourceKey

	for len(csvNamesToBeResolved) != 0 {
		// Pop off a CSV name.
		currentName := csvNamesToBeResolved[0]
		csvNamesToBeResolved = csvNamesToBeResolved[1:]

		// If this CSV is already resolved, continue.
		if _, exists := steps[currentName]; exists {
			continue
		}

		var csvSourceKey registry.SourceKey
		var csv *csvv1alpha1.ClusterServiceVersion
		var err error

		// Attempt to Get the full CSV object for the name from any
		for _, ref := range sourceRefs {
			csv, err = ref.Source.FindCSVByName(currentName)

			if err == nil {
				// Found CSV
				csvSourceKey = ref.SourceKey
				break
			}

		}

		if err != nil {
			// Couldn't find CSV in any CatalogSource
			return nil, nil, err
		}

		log.Debugf("found %#v", csv)
		usedSourceKeys = append(usedSourceKeys, csvSourceKey)

		// Resolve each owned or required CRD for the CSV.
		for _, crdDesc := range csv.GetAllCRDDescriptions() {
			// Attempt to get CRD from same catalog source CSV was found in
			step, owner, err := resolver.resolveCRDDescription(sourceRefs, catalogLabelKey, crdDesc, csv.OwnsCRD(crdDesc.Name))
			if err != nil {
				return nil, nil, err
			}

			// If a different owner was resolved, add it to the list.
			if owner != "" && owner != currentName {
				csvNamesToBeResolved = append(csvNamesToBeResolved, owner)
			} else {
				// Add the resolved step to the plan.
				steps[currentName] = append(steps[currentName], step)
			}

		}

		// Manually override the namespace and create the final step for the CSV,
		// which is for the CSV itself.
		csv.SetNamespace(planNamespace)

		// Add the sourcename as a label on the CSV, so that we know where it came from
		labels := csv.GetLabels()
		if labels == nil {
			labels = map[string]string{}
		}
		labels[catalogLabelKey] = csvSourceKey.Name
		csv.SetLabels(labels)

		step, err := v1alpha1.NewStepResourceFromCSV(csv)
		if err != nil {
			return nil, nil, err
		}

		// Set the catalog source name and namespace
		step.CatalogSource = csvSourceKey.Name
		step.CatalogSourceNamespace = csvSourceKey.Namespace

		// Add the final step for the CSV to the plan.
		log.Infof("finished step: %v", step)
		steps[currentName] = append(steps[currentName], step)
	}

	return steps, usedSourceKeys, nil
}

func (resolver *MultiSourceResolver) resolveCRDDescription(sourceRefs []registry.SourceRef, catalogLabelKey string, crdDesc csvv1alpha1.CRDDescription, owned bool) (v1alpha1.StepResource, string, error) {
	log.Debugf("resolving %#v", crdDesc)

	crdKey := registry.CRDKey{
		Kind:    crdDesc.Kind,
		Name:    crdDesc.Name,
		Version: crdDesc.Version,
	}

	var crdSourceKey registry.SourceKey
	var crd *v1beta1.CustomResourceDefinition
	var source registry.Source
	var err error

	// Attempt to find the CRD in any other source if the CRD is not owned
	for _, ref := range sourceRefs {
		source = ref.Source
		crd, err = source.FindCRDByKey(crdKey)

		if err == nil {
			// Found the CRD
			crdSourceKey = ref.SourceKey
			break
		}
	}

	if err != nil {
		return v1alpha1.StepResource{}, "", err
	}

	log.Debugf("found %#v", crd)

	if owned {
		// Label CRD with catalog source
		labels := crd.GetLabels()
		if labels == nil {
			labels = map[string]string{}
		}
		labels[catalogLabelKey] = crdSourceKey.Name
		crd.SetLabels(labels)

		// Add CRD Step
		step, err := v1alpha1.NewStepResourceFromCRD(crd)

		// Set the catalog source name and namespace
		step.CatalogSource = crdSourceKey.Name
		step.CatalogSourceNamespace = crdSourceKey.Namespace

		return step, "", err
	}

	csvs, err := source.ListLatestCSVsForCRD(crdKey)
	if err != nil {
		return v1alpha1.StepResource{}, "", err
	}
	if len(csvs) == 0 {
		return v1alpha1.StepResource{}, "", fmt.Errorf("Unknown CRD %s", crdKey)
	}

	// TODO: Change to lookup the CSV from the preferred or default channel.
	log.Infof("found %v owner %s", crdKey, csvs[0].CSV.Name)
	return v1alpha1.StepResource{}, csvs[0].CSV.Name, nil

}

type stepResourceMap map[string][]v1alpha1.StepResource

func (srm stepResourceMap) Plan() []v1alpha1.Step {
	steps := make([]v1alpha1.Step, 0)
	for csvName, stepResSlice := range srm {
		for _, stepRes := range stepResSlice {
			steps = append(steps, v1alpha1.Step{
				Resolving: csvName,
				Resource:  stepRes,
				Status:    v1alpha1.StepStatusUnknown,
			})
		}
	}

	return steps
}

func (srm stepResourceMap) Combine(y stepResourceMap) {
	for csvName, stepResSlice := range y {
		// Skip any redundant steps.
		if _, alreadyExists := srm[csvName]; alreadyExists {
			continue
		}

		srm[csvName] = stepResSlice
	}
}
<commit_msg>refactor(resolver): make resolved step logging more helpful<commit_after>package resolver

import (
	"fmt"

	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/installplan/v1alpha1"
	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"

	log "github.com/sirupsen/logrus"

	csvv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/clusterserviceversion/v1alpha1"
)

// DependencyResolver defines how a something that resolves dependencies (CSVs, CRDs, etc...)
// should behave
type DependencyResolver interface {
	ResolveInstallPlan(sourceRefs []registry.SourceRef, catalogLabelKey string, plan *v1alpha1.InstallPlan) ([]v1alpha1.Step, []registry.SourceKey, error)
}

// MultiSourceResolver resolves resolves dependencies from multiple CatalogSources
type MultiSourceResolver struct{}

// ResolveInstallPlan resolves the given InstallPlan with all available sources
func (resolver *MultiSourceResolver) ResolveInstallPlan(sourceRefs []registry.SourceRef, catalogLabelKey string, plan *v1alpha1.InstallPlan) ([]v1alpha1.Step, []registry.SourceKey, error) {
	srm := make(stepResourceMap)
	var usedSourceKeys []registry.SourceKey

	for _, csvName := range plan.Spec.ClusterServiceVersionNames {
		csvSRM, used, err := resolver.resolveCSV(sourceRefs, catalogLabelKey, plan.Namespace, csvName)
		if err != nil {
			// Could not resolve CSV in any source
			return nil, nil, err
		}

		srm.Combine(csvSRM)
		usedSourceKeys = append(used, usedSourceKeys...)
	}

	return srm.Plan(), usedSourceKeys, nil
}

func (resolver *MultiSourceResolver) resolveCSV(sourceRefs []registry.SourceRef, catalogLabelKey, planNamespace, csvName string) (stepResourceMap, []registry.SourceKey, error) {
	log.Debugf("resolving CSV with name: %s", csvName)

	steps := make(stepResourceMap)
	csvNamesToBeResolved := []string{csvName}
	var usedSourceKeys []registry.SourceKey

	for len(csvNamesToBeResolved) != 0 {
		// Pop off a CSV name.
		currentName := csvNamesToBeResolved[0]
		csvNamesToBeResolved = csvNamesToBeResolved[1:]

		// If this CSV is already resolved, continue.
		if _, exists := steps[currentName]; exists {
			continue
		}

		var csvSourceKey registry.SourceKey
		var csv *csvv1alpha1.ClusterServiceVersion
		var err error

		// Attempt to Get the full CSV object for the name from any
		for _, ref := range sourceRefs {
			csv, err = ref.Source.FindCSVByName(currentName)

			if err == nil {
				// Found CSV
				csvSourceKey = ref.SourceKey
				break
			}

		}

		if err != nil {
			// Couldn't find CSV in any CatalogSource
			return nil, nil, err
		}

		log.Debugf("found %#v", csv)
		usedSourceKeys = append(usedSourceKeys, csvSourceKey)

		// Resolve each owned or required CRD for the CSV.
		for _, crdDesc := range csv.GetAllCRDDescriptions() {
			// Attempt to get CRD from same catalog source CSV was found in
			step, owner, err := resolver.resolveCRDDescription(sourceRefs, catalogLabelKey, crdDesc, csv.OwnsCRD(crdDesc.Name))
			if err != nil {
				return nil, nil, err
			}

			// If a different owner was resolved, add it to the list.
			if owner != "" && owner != currentName {
				csvNamesToBeResolved = append(csvNamesToBeResolved, owner)
			} else {
				// Add the resolved step to the plan.
				steps[currentName] = append(steps[currentName], step)
			}

		}

		// Manually override the namespace and create the final step for the CSV,
		// which is for the CSV itself.
		csv.SetNamespace(planNamespace)

		// Add the sourcename as a label on the CSV, so that we know where it came from
		labels := csv.GetLabels()
		if labels == nil {
			labels = map[string]string{}
		}
		labels[catalogLabelKey] = csvSourceKey.Name
		csv.SetLabels(labels)

		step, err := v1alpha1.NewStepResourceFromCSV(csv)
		if err != nil {
			return nil, nil, err
		}

		// Set the catalog source name and namespace
		step.CatalogSource = csvSourceKey.Name
		step.CatalogSourceNamespace = csvSourceKey.Namespace

		// Add the final step for the CSV to the plan.
		log.Infof("finished step: %s", step.Name)
		log.Debugf("step %s content: %v", step.Name, step)
		steps[currentName] = append(steps[currentName], step)
	}

	return steps, usedSourceKeys, nil
}

func (resolver *MultiSourceResolver) resolveCRDDescription(sourceRefs []registry.SourceRef, catalogLabelKey string, crdDesc csvv1alpha1.CRDDescription, owned bool) (v1alpha1.StepResource, string, error) {
	log.Debugf("resolving %#v", crdDesc)

	crdKey := registry.CRDKey{
		Kind:    crdDesc.Kind,
		Name:    crdDesc.Name,
		Version: crdDesc.Version,
	}

	var crdSourceKey registry.SourceKey
	var crd *v1beta1.CustomResourceDefinition
	var source registry.Source
	var err error

	// Attempt to find the CRD in any other source if the CRD is not owned
	for _, ref := range sourceRefs {
		source = ref.Source
		crd, err = source.FindCRDByKey(crdKey)

		if err == nil {
			// Found the CRD
			crdSourceKey = ref.SourceKey
			break
		}
	}

	if err != nil {
		return v1alpha1.StepResource{}, "", err
	}

	log.Debugf("found %#v", crd)

	if owned {
		// Label CRD with catalog source
		labels := crd.GetLabels()
		if labels == nil {
			labels = map[string]string{}
		}
		labels[catalogLabelKey] = crdSourceKey.Name
		crd.SetLabels(labels)

		// Add CRD Step
		step, err := v1alpha1.NewStepResourceFromCRD(crd)

		// Set the catalog source name and namespace
		step.CatalogSource = crdSourceKey.Name
		step.CatalogSourceNamespace = crdSourceKey.Namespace

		return step, "", err
	}

	csvs, err := source.ListLatestCSVsForCRD(crdKey)
	if err != nil {
		return v1alpha1.StepResource{}, "", err
	}
	if len(csvs) == 0 {
		return v1alpha1.StepResource{}, "", fmt.Errorf("Unknown CRD %s", crdKey)
	}

	// TODO: Change to lookup the CSV from the preferred or default channel.
	log.Infof("found %v owner %s", crdKey, csvs[0].CSV.Name)
	return v1alpha1.StepResource{}, csvs[0].CSV.Name, nil

}

type stepResourceMap map[string][]v1alpha1.StepResource

func (srm stepResourceMap) Plan() []v1alpha1.Step {
	steps := make([]v1alpha1.Step, 0)
	for csvName, stepResSlice := range srm {
		for _, stepRes := range stepResSlice {
			steps = append(steps, v1alpha1.Step{
				Resolving: csvName,
				Resource:  stepRes,
				Status:    v1alpha1.StepStatusUnknown,
			})
		}
	}

	return steps
}

func (srm stepResourceMap) Combine(y stepResourceMap) {
	for csvName, stepResSlice := range y {
		// Skip any redundant steps.
		if _, alreadyExists := srm[csvName]; alreadyExists {
			continue
		}

		srm[csvName] = stepResSlice
	}
}
<|endoftext|>


<commit_before>package helm

import (
	"context"
	"fmt"

	catalog "github.com/rancher/rancher/pkg/apis/catalog.cattle.io/v1"
	catalogcontrollers "github.com/rancher/rancher/pkg/generated/controllers/catalog.cattle.io/v1"
	corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
	"github.com/rancher/wrangler/pkg/kstatus"
	"github.com/rancher/wrangler/pkg/relatedresource"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
)

const (
	podIndex = "byPod"
)

type operationHandler struct {
	ctx             context.Context
	pods            corecontrollers.PodCache
	k8s             kubernetes.Interface
	operationsCache catalogcontrollers.OperationCache
}

func RegisterOperations(ctx context.Context,
	k8s kubernetes.Interface,
	pods corecontrollers.PodController,
	operations catalogcontrollers.OperationController) {

	o := operationHandler{
		ctx:             ctx,
		k8s:             k8s,
		pods:            pods.Cache(),
		operationsCache: operations.Cache(),
	}

	operations.Cache().AddIndexer(podIndex, indexOperationsByPod)
	relatedresource.Watch(ctx, "helm-operation", o.findOperationsFromPod, operations, pods)
	catalogcontrollers.RegisterOperationStatusHandler(ctx, operations, "", "helm-operation", o.onOperationChange)
}

func indexOperationsByPod(obj *catalog.Operation) ([]string, error) {
	return []string{
		obj.Status.PodNamespace + "/" + obj.Status.PodName,
	}, nil
}

func (o *operationHandler) findOperationsFromPod(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) {
	ops, err := o.operationsCache.GetByIndex(podIndex, namespace+"/"+name)
	if err != nil {
		return nil, err
	}
	var result []relatedresource.Key
	for _, op := range ops {
		result = append(result, relatedresource.NewKey(op.Namespace, op.Name))
	}
	return result, nil
}

func (o *operationHandler) onOperationChange(operation *catalog.Operation, status catalog.OperationStatus) (catalog.OperationStatus, error) {
	if status.PodName == "" || status.PodNamespace == "" {
		return status, nil
	}

	pod, err := o.pods.Get(status.PodNamespace, status.PodName)
	if apierrors.IsNotFound(err) {
		kstatus.SetActive(&status)
		return status, nil
	}

	for _, container := range pod.Status.ContainerStatuses {
		if container.Name != "helm" {
			continue
		}
		status.ObservedGeneration = operation.Generation
		if container.State.Running != nil {
			status.PodCreated = true
			kstatus.SetTransitioning(&status, "running operation")
		} else if container.State.Terminated != nil {
			status.PodCreated = true
			if container.State.Terminated.ExitCode == 0 {
				kstatus.SetActive(&status)
			} else {
				kstatus.SetError(&status,
					fmt.Sprintf("%s exit code: %d",
						container.State.Terminated.Message,
						container.State.Terminated.ExitCode))
			}
			o.cleanup(pod)
		} else if container.State.Waiting != nil {
			kstatus.SetTransitioning(&status, "waiting to run operation")
		} else {
			kstatus.SetTransitioning(&status, "unknown state operation")
		}
	}

	return status, nil
}

func (o *operationHandler) cleanup(pod *corev1.Pod) error {
	running := false
	success := false
	for _, container := range pod.Status.ContainerStatuses {
		if container.Name == "proxy" && container.State.Terminated == nil {
			running = true
		} else if container.Name == "helm" && container.State.Terminated != nil && container.State.Terminated.ExitCode == 0 {
			success = true
		}
	}
	if running && success {
		result := o.k8s.CoreV1().RESTClient().
			Get().
			Namespace(pod.Namespace).
			Resource("pods").
			Name(pod.Name).
			SubResource("exec").
			SetHeader("Upgrade", "websocket").
			SetHeader("Sec-Websocket-Key", "websocket").
			SetHeader("Sec-Websocket-Version", "13").
			SetHeader("Connection", "Upgrade").
			VersionedParams(&corev1.PodExecOptions{
				Stdout:    true,
				Stderr:    true,
				Container: "proxy",
				Command:   []string{"killall", "kubectl"},
			}, scheme.ParameterCodec).Do(o.ctx)
		if result.Error() != nil {
			logrus.Errorf("failed to shutdown helm operation: %s/%s: %v", pod.Namespace, pod.Name, result.Error())
		}
	}

	return nil
}
<commit_msg>Remove useless error return type<commit_after>package helm

import (
	"context"
	"fmt"

	catalog "github.com/rancher/rancher/pkg/apis/catalog.cattle.io/v1"
	catalogcontrollers "github.com/rancher/rancher/pkg/generated/controllers/catalog.cattle.io/v1"
	corecontrollers "github.com/rancher/wrangler/pkg/generated/controllers/core/v1"
	"github.com/rancher/wrangler/pkg/kstatus"
	"github.com/rancher/wrangler/pkg/relatedresource"
	"github.com/sirupsen/logrus"
	corev1 "k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
)

const (
	podIndex = "byPod"
)

type operationHandler struct {
	ctx             context.Context
	pods            corecontrollers.PodCache
	k8s             kubernetes.Interface
	operationsCache catalogcontrollers.OperationCache
}

func RegisterOperations(ctx context.Context,
	k8s kubernetes.Interface,
	pods corecontrollers.PodController,
	operations catalogcontrollers.OperationController) {

	o := operationHandler{
		ctx:             ctx,
		k8s:             k8s,
		pods:            pods.Cache(),
		operationsCache: operations.Cache(),
	}

	operations.Cache().AddIndexer(podIndex, indexOperationsByPod)
	relatedresource.Watch(ctx, "helm-operation", o.findOperationsFromPod, operations, pods)
	catalogcontrollers.RegisterOperationStatusHandler(ctx, operations, "", "helm-operation", o.onOperationChange)
}

func indexOperationsByPod(obj *catalog.Operation) ([]string, error) {
	return []string{
		obj.Status.PodNamespace + "/" + obj.Status.PodName,
	}, nil
}

func (o *operationHandler) findOperationsFromPod(namespace, name string, obj runtime.Object) ([]relatedresource.Key, error) {
	ops, err := o.operationsCache.GetByIndex(podIndex, namespace+"/"+name)
	if err != nil {
		return nil, err
	}
	var result []relatedresource.Key
	for _, op := range ops {
		result = append(result, relatedresource.NewKey(op.Namespace, op.Name))
	}
	return result, nil
}

func (o *operationHandler) onOperationChange(operation *catalog.Operation, status catalog.OperationStatus) (catalog.OperationStatus, error) {
	if status.PodName == "" || status.PodNamespace == "" {
		return status, nil
	}

	pod, err := o.pods.Get(status.PodNamespace, status.PodName)
	if apierrors.IsNotFound(err) {
		kstatus.SetActive(&status)
		return status, nil
	}

	for _, container := range pod.Status.ContainerStatuses {
		if container.Name != "helm" {
			continue
		}
		status.ObservedGeneration = operation.Generation
		if container.State.Running != nil {
			status.PodCreated = true
			kstatus.SetTransitioning(&status, "running operation")
		} else if container.State.Terminated != nil {
			status.PodCreated = true
			if container.State.Terminated.ExitCode == 0 {
				kstatus.SetActive(&status)
			} else {
				kstatus.SetError(&status,
					fmt.Sprintf("%s exit code: %d",
						container.State.Terminated.Message,
						container.State.Terminated.ExitCode))
			}
			o.cleanup(pod)
		} else if container.State.Waiting != nil {
			kstatus.SetTransitioning(&status, "waiting to run operation")
		} else {
			kstatus.SetTransitioning(&status, "unknown state operation")
		}
	}

	return status, nil
}

func (o *operationHandler) cleanup(pod *corev1.Pod) {
	running := false
	success := false
	for _, container := range pod.Status.ContainerStatuses {
		if container.Name == "proxy" && container.State.Terminated == nil {
			running = true
		} else if container.Name == "helm" && container.State.Terminated != nil && container.State.Terminated.ExitCode == 0 {
			success = true
		}
	}
	if running && success {
		result := o.k8s.CoreV1().RESTClient().
			Get().
			Namespace(pod.Namespace).
			Resource("pods").
			Name(pod.Name).
			SubResource("exec").
			SetHeader("Upgrade", "websocket").
			SetHeader("Sec-Websocket-Key", "websocket").
			SetHeader("Sec-Websocket-Version", "13").
			SetHeader("Connection", "Upgrade").
			VersionedParams(&corev1.PodExecOptions{
				Stdout:    true,
				Stderr:    true,
				Container: "proxy",
				Command:   []string{"killall", "kubectl"},
			}, scheme.ParameterCodec).Do(o.ctx)
		if result.Error() != nil {
			logrus.Errorf("failed to shutdown helm operation: %s/%s: %v", pod.Namespace, pod.Name, result.Error())
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The CDI Authors.

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

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

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

package cluster

import (
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

const (
	controllerServiceAccountName = "cdi-sa"
	controlerClusterRoleName     = "cdi"
)

func createControllerResources(args *FactoryArgs) []runtime.Object {
	return []runtime.Object{
		createControllerClusterRole(),
		createControllerClusterRoleBinding(args.Namespace),
	}
}

func createControllerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
	return createClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace)
}

func createControllerClusterRole() *rbacv1.ClusterRole {
	clusterRole := createClusterRole(controlerClusterRoleName)
	clusterRole.Rules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"events",
			},
			Verbs: []string{
				"create",
				"update",
				"patch",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"persistentvolumeclaims",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"update",
				"patch",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"persistentvolumeclaims/finalizers",
				"pods/finalizers",
			},
			Verbs: []string{
				"update",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"pods",
				"services",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"delete",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"secrets",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"configmaps",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"update",
			},
		},
		{
			APIGroups: []string{
				"storage.k8s.io",
			},
			Resources: []string{
				"storageclasses",
			},
			Verbs: []string{
				"get",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"*",
			},
			Verbs: []string{
				"*",
			},
		},
	}
	return clusterRole
}
<commit_msg>add permissions for listing and watching ingresses<commit_after>/*
Copyright 2018 The CDI Authors.

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

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

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

package cluster

import (
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/runtime"
)

const (
	controllerServiceAccountName = "cdi-sa"
	controlerClusterRoleName     = "cdi"
)

func createControllerResources(args *FactoryArgs) []runtime.Object {
	return []runtime.Object{
		createControllerClusterRole(),
		createControllerClusterRoleBinding(args.Namespace),
	}
}

func createControllerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding {
	return createClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace)
}

func createControllerClusterRole() *rbacv1.ClusterRole {
	clusterRole := createClusterRole(controlerClusterRoleName)
	clusterRole.Rules = []rbacv1.PolicyRule{
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"events",
			},
			Verbs: []string{
				"create",
				"update",
				"patch",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"persistentvolumeclaims",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"update",
				"patch",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"persistentvolumeclaims/finalizers",
				"pods/finalizers",
			},
			Verbs: []string{
				"update",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"pods",
				"services",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"delete",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"secrets",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
			},
		},
		{
			APIGroups: []string{
				"extensions",
			},
			Resources: []string{
				"ingresses",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
			},
		},
		{
			APIGroups: []string{
				"",
			},
			Resources: []string{
				"configmaps",
			},
			Verbs: []string{
				"get",
				"list",
				"watch",
				"create",
				"update",
			},
		},
		{
			APIGroups: []string{
				"storage.k8s.io",
			},
			Resources: []string{
				"storageclasses",
			},
			Verbs: []string{
				"get",
			},
		},
		{
			APIGroups: []string{
				"cdi.kubevirt.io",
			},
			Resources: []string{
				"*",
			},
			Verbs: []string{
				"*",
			},
		},
	}
	return clusterRole
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcd

import (
	"container/list"
	"fmt"
	"github.com/FactomProject/FactomCode/common"
	cp "github.com/FactomProject/FactomCode/controlpanel"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/davecgh/go-spew/spew"
	"sync/atomic"
)

// dirBlockMsg packages a directory block message and the peer it came from together
// so the block handler has access to that information.
type dirBlockMsg struct {
	block *common.DirectoryBlock
	peer  *peer
}

// dirInvMsg packages a dir block inv message and the peer it came from together
// so the block handler has access to that information.
type dirInvMsg struct {
	inv  *wire.MsgDirInv
	peer *peer
}

// handleDirInvMsg handles dir inv messages from all peers.
// We examine the inventory advertised by the remote peer and act accordingly.
func (b *blockManager) handleDirInvMsg(imsg *dirInvMsg) {
	bmgrLog.Debug("handleDirInvMsg: ", spew.Sdump(imsg))

	// Ignore invs from peers that aren't the sync if we are not current.
	// Helps prevent fetching a mass of orphans.
	if imsg.peer != b.syncPeer && !b.current() {
		return
	}

	// Attempt to find the final block in the inventory list.  There may
	// not be one.
	lastBlock := -1
	invVects := imsg.inv.InvList
	bmgrLog.Debugf("len(InvVects)=%d", len(invVects))
	for i := len(invVects) - 1; i >= 0; i-- {
		if invVects[i].Type == wire.InvTypeFactomDirBlock {
			lastBlock = i
			bmgrLog.Debugf("lastBlock=%d", lastBlock)
			break
		}
	}

	// Request the advertised inventory if we don't already have it.  Also,
	// request parent blocks of orphans if we receive one we already have.
	// Finally, attempt to detect potential stalls due to long side chains
	// we already have and request more blocks to prevent them.
	for i, iv := range invVects {
		// Ignore unsupported inventory types.
		if iv.Type != wire.InvTypeFactomDirBlock { //} && iv.Type != wire.InvTypeTx {
			continue
		}

		// Add the inventory to the cache of known inventory
		// for the peer.
		imsg.peer.AddKnownInventory(iv)

		// Request the inventory if we don't already have it.
		haveInv, err := b.haveInventory(iv)
		if err != nil {
			bmgrLog.Warnf("Unexpected failure when checking for "+
				"existing inventory during inv message "+
				"processing: %v", err)
			continue
		}
		if !haveInv {
			// Add it to the request queue.
			imsg.peer.requestQueue = append(imsg.peer.requestQueue, iv)
			continue
		}

		if iv.Type == wire.InvTypeFactomDirBlock {

			// We already have the final block advertised by this
			// inventory message, so force a request for more.  This
			// should only happen if we're on a really long side
			// chain.
			if i == lastBlock {
				// Request blocks after this one up to the
				// final one the remote peer knows about (zero
				// stop hash).
				bmgrLog.Debug("push for more dir blocks: PushGetDirBlocksMsg")
				locator := DirBlockLocatorFromHash(&iv.Hash)
				imsg.peer.PushGetDirBlocksMsg(locator, &zeroHash)
			}
		}
	}

	// Request as much as possible at once.  Anything that won't fit into
	// the request will be requested on the next inv message.
	numRequested := 0
	gdmsg := wire.NewMsgGetDirData()
	requestQueue := imsg.peer.requestQueue
	for len(requestQueue) != 0 {
		iv := requestQueue[0]
		requestQueue[0] = nil
		requestQueue = requestQueue[1:]

		switch iv.Type {
		case wire.InvTypeFactomDirBlock:
			// Request the block if there is not already a pending
			// request.
			if _, exists := b.requestedBlocks[iv.Hash]; !exists {
				b.requestedBlocks[iv.Hash] = struct{}{}
				imsg.peer.requestedBlocks[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}

		case wire.InvTypeTx:
			// Request the transaction if there is not already a
			// pending request.
			if _, exists := b.requestedTxns[iv.Hash]; !exists {
				b.requestedTxns[iv.Hash] = struct{}{}
				imsg.peer.requestedTxns[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}
		}

		if numRequested >= wire.MaxInvPerMsg {
			break
		}
	}
	imsg.peer.requestQueue = requestQueue
	if len(gdmsg.InvList) > 0 {
		imsg.peer.QueueMessage(gdmsg, nil)
	}
}

// QueueDirBlock adds the passed GetDirBlocks message and peer to the block handling queue.
func (b *blockManager) QueueDirBlock(msg *wire.MsgDirBlock, p *peer) {
	// Don't accept more blocks if we're shutting down.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		p.blockProcessed <- struct{}{}
		return
	}

	b.msgChan <- &dirBlockMsg{block: msg.DBlk, peer: p}
}

// QueueDirInv adds the passed inv message and peer to the block handling queue.
func (b *blockManager) QueueDirInv(inv *wire.MsgDirInv, p *peer) {
	// No channel handling here because peers do not need to block on inv
	// messages.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		return
	}

	b.msgChan <- &dirInvMsg{inv: inv, peer: p}
}

// startSyncFactom will choose the best peer among the available candidate peers to
// download/sync the blockchain from.  When syncing is already running, it
// simply returns.  It also examines the candidates for any which are no longer
// candidates and removes them as needed.
func (b *blockManager) startSyncFactom(peers *list.List) {
	// Return now if we're already syncing.
	if b.syncPeer != nil {
		return
	}

	// Find the height of the current known best block.
	_, height, err := db.FetchBlockHeightCache()
	if err != nil {
		bmgrLog.Errorf("%v", err)
		return
	}

	bmgrLog.Infof("Latest DirBlock Height: %d", height)

	var bestPeer *peer
	var enext *list.Element
	for e := peers.Front(); e != nil; e = enext {
		enext = e.Next()
		p := e.Value.(*peer)

		// Remove sync candidate peers that are no longer candidates due
		// to passing their latest known block.  NOTE: The < is
		// intentional as opposed to <=.  While techcnically the peer
		// doesn't have a later block when it's equal, it will likely
		// have one soon so it is a reasonable choice.  It also allows
		// the case where both are at 0 such as during regression test.
		if p.lastBlock < int32(height) {
			peers.Remove(e)
			continue
		}

		// TODO(davec): Use a better algorithm to choose the best peer.
		// For now, just pick the first available candidate.
		bestPeer = p
	}

	// Start syncing from the best peer if one was selected.
	if bestPeer != nil {
		locator, err := LatestDirBlockLocator()
		if err != nil {
			bmgrLog.Errorf("Failed to get block locator for the "+
				"latest block: %v", err)
			return
		}

		bmgrLog.Infof("LatestDirBlockLocator: %s", spew.Sdump(locator))

		str := fmt.Sprintf("At %d: syncing to block height %d from peer %v",
			height, bestPeer.lastBlock, bestPeer.addr)
		bmgrLog.Infof(str)

		cp.CP.AddUpdate(
			"Syncing", // tag
			"status",  // Category
			"Client is Syncing with Federated Server(s)", // Title
			str, // Message
			60)

		bestPeer.PushGetDirBlocksMsg(locator, &zeroHash)
		b.syncPeer = bestPeer
	} else {
		bmgrLog.Warnf("No sync peer candidates available")
	}
}

// isSyncCandidateFactom returns whether or not the peer is a candidate to consider
// syncing from.
func (b *blockManager) isSyncCandidateFactom(p *peer) bool {
	// Typically a peer is not a candidate for sync if it's not a Factom SERVER node,
	if common.SERVER_NODE == util.ReadConfig().App.NodeMode {
		return true
	}
	return true
}
<commit_msg>Fixed the spew error.<commit_after>// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package btcd

import (
	"container/list"
	"fmt"
	"github.com/FactomProject/FactomCode/common"
	cp "github.com/FactomProject/FactomCode/controlpanel"
	"github.com/FactomProject/FactomCode/util"
	"github.com/FactomProject/btcd/wire"
	"github.com/davecgh/go-spew/spew"
	"sync/atomic"
)

// dirBlockMsg packages a directory block message and the peer it came from together
// so the block handler has access to that information.
type dirBlockMsg struct {
	block *common.DirectoryBlock
	peer  *peer
}

// dirInvMsg packages a dir block inv message and the peer it came from together
// so the block handler has access to that information.
type dirInvMsg struct {
	inv  *wire.MsgDirInv
	peer *peer
}

// handleDirInvMsg handles dir inv messages from all peers.
// We examine the inventory advertised by the remote peer and act accordingly.
func (b *blockManager) handleDirInvMsg(imsg *dirInvMsg) {
	bmgrLog.Debug("handleDirInvMsg: ", spew.Sdump(imsg.inv))

	// Ignore invs from peers that aren't the sync if we are not current.
	// Helps prevent fetching a mass of orphans.
	if imsg.peer != b.syncPeer && !b.current() {
		return
	}

	// Attempt to find the final block in the inventory list.  There may
	// not be one.
	lastBlock := -1
	invVects := imsg.inv.InvList
	bmgrLog.Debugf("len(InvVects)=%d", len(invVects))
	for i := len(invVects) - 1; i >= 0; i-- {
		if invVects[i].Type == wire.InvTypeFactomDirBlock {
			lastBlock = i
			bmgrLog.Debugf("lastBlock=%d", lastBlock)
			break
		}
	}

	// Request the advertised inventory if we don't already have it.  Also,
	// request parent blocks of orphans if we receive one we already have.
	// Finally, attempt to detect potential stalls due to long side chains
	// we already have and request more blocks to prevent them.
	for i, iv := range invVects {
		// Ignore unsupported inventory types.
		if iv.Type != wire.InvTypeFactomDirBlock { //} && iv.Type != wire.InvTypeTx {
			continue
		}

		// Add the inventory to the cache of known inventory
		// for the peer.
		imsg.peer.AddKnownInventory(iv)

		// Request the inventory if we don't already have it.
		haveInv, err := b.haveInventory(iv)
		if err != nil {
			bmgrLog.Warnf("Unexpected failure when checking for "+
				"existing inventory during inv message "+
				"processing: %v", err)
			continue
		}
		if !haveInv {
			// Add it to the request queue.
			imsg.peer.requestQueue = append(imsg.peer.requestQueue, iv)
			continue
		}

		if iv.Type == wire.InvTypeFactomDirBlock {

			// We already have the final block advertised by this
			// inventory message, so force a request for more.  This
			// should only happen if we're on a really long side
			// chain.
			if i == lastBlock {
				// Request blocks after this one up to the
				// final one the remote peer knows about (zero
				// stop hash).
				bmgrLog.Debug("push for more dir blocks: PushGetDirBlocksMsg")
				locator := DirBlockLocatorFromHash(&iv.Hash)
				imsg.peer.PushGetDirBlocksMsg(locator, &zeroHash)
			}
		}
	}

	// Request as much as possible at once.  Anything that won't fit into
	// the request will be requested on the next inv message.
	numRequested := 0
	gdmsg := wire.NewMsgGetDirData()
	requestQueue := imsg.peer.requestQueue
	for len(requestQueue) != 0 {
		iv := requestQueue[0]
		requestQueue[0] = nil
		requestQueue = requestQueue[1:]

		switch iv.Type {
		case wire.InvTypeFactomDirBlock:
			// Request the block if there is not already a pending
			// request.
			if _, exists := b.requestedBlocks[iv.Hash]; !exists {
				b.requestedBlocks[iv.Hash] = struct{}{}
				imsg.peer.requestedBlocks[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}

		case wire.InvTypeTx:
			// Request the transaction if there is not already a
			// pending request.
			if _, exists := b.requestedTxns[iv.Hash]; !exists {
				b.requestedTxns[iv.Hash] = struct{}{}
				imsg.peer.requestedTxns[iv.Hash] = struct{}{}
				gdmsg.AddInvVect(iv)
				numRequested++
			}
		}

		if numRequested >= wire.MaxInvPerMsg {
			break
		}
	}
	imsg.peer.requestQueue = requestQueue
	if len(gdmsg.InvList) > 0 {
		imsg.peer.QueueMessage(gdmsg, nil)
	}
}

// QueueDirBlock adds the passed GetDirBlocks message and peer to the block handling queue.
func (b *blockManager) QueueDirBlock(msg *wire.MsgDirBlock, p *peer) {
	// Don't accept more blocks if we're shutting down.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		p.blockProcessed <- struct{}{}
		return
	}

	b.msgChan <- &dirBlockMsg{block: msg.DBlk, peer: p}
}

// QueueDirInv adds the passed inv message and peer to the block handling queue.
func (b *blockManager) QueueDirInv(inv *wire.MsgDirInv, p *peer) {
	// No channel handling here because peers do not need to block on inv
	// messages.
	if atomic.LoadInt32(&b.shutdown) != 0 {
		return
	}

	b.msgChan <- &dirInvMsg{inv: inv, peer: p}
}

// startSyncFactom will choose the best peer among the available candidate peers to
// download/sync the blockchain from.  When syncing is already running, it
// simply returns.  It also examines the candidates for any which are no longer
// candidates and removes them as needed.
func (b *blockManager) startSyncFactom(peers *list.List) {
	// Return now if we're already syncing.
	if b.syncPeer != nil {
		return
	}

	// Find the height of the current known best block.
	_, height, err := db.FetchBlockHeightCache()
	if err != nil {
		bmgrLog.Errorf("%v", err)
		return
	}

	bmgrLog.Infof("Latest DirBlock Height: %d", height)

	var bestPeer *peer
	var enext *list.Element
	for e := peers.Front(); e != nil; e = enext {
		enext = e.Next()
		p := e.Value.(*peer)

		// Remove sync candidate peers that are no longer candidates due
		// to passing their latest known block.  NOTE: The < is
		// intentional as opposed to <=.  While techcnically the peer
		// doesn't have a later block when it's equal, it will likely
		// have one soon so it is a reasonable choice.  It also allows
		// the case where both are at 0 such as during regression test.
		if p.lastBlock < int32(height) {
			peers.Remove(e)
			continue
		}

		// TODO(davec): Use a better algorithm to choose the best peer.
		// For now, just pick the first available candidate.
		bestPeer = p
	}

	// Start syncing from the best peer if one was selected.
	if bestPeer != nil {
		locator, err := LatestDirBlockLocator()
		if err != nil {
			bmgrLog.Errorf("Failed to get block locator for the "+
				"latest block: %v", err)
			return
		}

		bmgrLog.Infof("LatestDirBlockLocator: %s", spew.Sdump(locator))

		str := fmt.Sprintf("At %d: syncing to block height %d from peer %v",
			height, bestPeer.lastBlock, bestPeer.addr)
		bmgrLog.Infof(str)

		cp.CP.AddUpdate(
			"Syncing", // tag
			"status",  // Category
			"Client is Syncing with Federated Server(s)", // Title
			str, // Message
			60)

		bestPeer.PushGetDirBlocksMsg(locator, &zeroHash)
		b.syncPeer = bestPeer
	} else {
		bmgrLog.Warnf("No sync peer candidates available")
	}
}

// isSyncCandidateFactom returns whether or not the peer is a candidate to consider
// syncing from.
func (b *blockManager) isSyncCandidateFactom(p *peer) bool {
	// Typically a peer is not a candidate for sync if it's not a Factom SERVER node,
	if common.SERVER_NODE == util.ReadConfig().App.NodeMode {
		return true
	}
	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The OpenEBS Authors.

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

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

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

package executor

import (
	"strings"

	errors "github.com/pkg/errors"

	"github.com/spf13/cobra"
)

// UpgradeOptions stores information required for upgrade
type UpgradeOptions struct {
	fromVersion       string
	toVersion         string
	openebsNamespace  string
	imageURLPrefix    string
	toVersionImageTag string
	resourceKind      string
	jivaVolume        JivaVolumeOptions
	cstorSPC          CStorSPCOptions
	cstorVolume       CStorVolumeOptions
	resource          ResourceOptions
}

var (
	options = &UpgradeOptions{
		openebsNamespace: "openebs",
		imageURLPrefix:   "quay.io/openebs/",
	}
)

// RunPreFlightChecks will ensure the sanity of the common upgrade options
func (u *UpgradeOptions) RunPreFlightChecks(cmd *cobra.Command) error {
	if len(strings.TrimSpace(u.openebsNamespace)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: namespace is missing")
	}

	if len(strings.TrimSpace(u.fromVersion)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: from-version is missing")
	}

	if len(strings.TrimSpace(u.toVersion)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: to-version is missing")
	}

	if len(strings.TrimSpace(u.resourceKind)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: resource details are missing")
	}

	return nil
}

// InitializeDefaults will ensure the default values for optional options are
// set.
func (u *UpgradeOptions) InitializeDefaults(cmd *cobra.Command) error {
	if len(strings.TrimSpace(u.toVersionImageTag)) == 0 {
		u.toVersionImageTag = u.toVersion
	}

	return nil
}
<commit_msg>fix(upgrade): remove quay.io as default url-prefix<commit_after>/*
Copyright 2019 The OpenEBS Authors.

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

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

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

package executor

import (
	"strings"

	errors "github.com/pkg/errors"

	"github.com/spf13/cobra"
)

// UpgradeOptions stores information required for upgrade
type UpgradeOptions struct {
	fromVersion       string
	toVersion         string
	openebsNamespace  string
	imageURLPrefix    string
	toVersionImageTag string
	resourceKind      string
	jivaVolume        JivaVolumeOptions
	cstorSPC          CStorSPCOptions
	cstorVolume       CStorVolumeOptions
	resource          ResourceOptions
}

var (
	options = &UpgradeOptions{
		openebsNamespace: "openebs",
		imageURLPrefix:   "",
	}
)

// RunPreFlightChecks will ensure the sanity of the common upgrade options
func (u *UpgradeOptions) RunPreFlightChecks(cmd *cobra.Command) error {
	if len(strings.TrimSpace(u.openebsNamespace)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: namespace is missing")
	}

	if len(strings.TrimSpace(u.fromVersion)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: from-version is missing")
	}

	if len(strings.TrimSpace(u.toVersion)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: to-version is missing")
	}

	if len(strings.TrimSpace(u.resourceKind)) == 0 {
		return errors.Errorf("Cannot execute upgrade job: resource details are missing")
	}

	return nil
}

// InitializeDefaults will ensure the default values for optional options are
// set.
func (u *UpgradeOptions) InitializeDefaults(cmd *cobra.Command) error {
	if len(strings.TrimSpace(u.toVersionImageTag)) == 0 {
		u.toVersionImageTag = u.toVersion
	}

	return nil
}
<|endoftext|>


<commit_before>package oss_test

import (
	"bytes"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	//"net/http"
	"testing"
	"time"

	"github.com/denverdino/aliyungo/oss"
)

var (
	client     *oss.Client //= oss.NewOSSClient(TestRegion, false, TestAccessKeyId, TestAccessKeySecret, false)
	TestBucket = strconv.FormatInt(time.Now().Unix(), 10)
)

func init() {
	AccessKeyId := os.Getenv("AccessKeyId")
	AccessKeySecret := os.Getenv("AccessKeySecret")
	if len(AccessKeyId) != 0 && len(AccessKeySecret) != 0 {
		client = oss.NewOSSClient(TestRegion, false, AccessKeyId, AccessKeySecret, false)
	} else {
		client = oss.NewOSSClient(TestRegion, false, TestAccessKeyId, TestAccessKeySecret, false)
	}

}

func TestCreateBucket(t *testing.T) {
	b := client.Bucket(TestBucket)
	err := b.PutBucket(oss.Private)
	if err != nil {
		t.Errorf("Failed for PutBucket: %v", err)
	}
	t.Log("Wait a while for bucket creation ...")
	time.Sleep(10 * time.Second)
}

func TestHead(t *testing.T) {

	b := client.Bucket(TestBucket)
	_, err := b.Head("name", nil)

	if err == nil {
		t.Errorf("Failed for Head: %v", err)
	}
}

func TestPutObject(t *testing.T) {
	const DISPOSITION = "attachment; filename=\"0x1a2b3c.jpg\""

	b := client.Bucket(TestBucket)
	err := b.Put("name", []byte("content"), "content-type", oss.Private, oss.Options{ContentDisposition: DISPOSITION})
	if err != nil {
		t.Errorf("Failed for Put: %v", err)
	}
}

func TestGet(t *testing.T) {

	b := client.Bucket(TestBucket)
	data, err := b.Get("name")

	if err != nil || string(data) != "content" {
		t.Errorf("Failed for Get: %v", err)
	}
}

func TestURL(t *testing.T) {

	b := client.Bucket(TestBucket)
	url := b.URL("name")

	t.Log("URL: ", url)
	//	/c.Assert(req.URL.Path, check.Equals, "/denverdino_test/name")
}

func TestGetReader(t *testing.T) {

	b := client.Bucket(TestBucket)
	rc, err := b.GetReader("name")
	if err != nil {
		t.Fatalf("Failed for GetReader: %v", err)
	}
	data, err := ioutil.ReadAll(rc)
	rc.Close()
	if err != nil || string(data) != "content" {
		t.Errorf("Failed for ReadAll: %v", err)
	}
}

func aTestGetNotFound(t *testing.T) {

	b := client.Bucket("non-existent-bucket")
	_, err := b.Get("non-existent")
	if err == nil {
		t.Fatalf("Failed for TestGetNotFound: %v", err)
	}
	ossErr, _ := err.(*oss.Error)
	if ossErr.StatusCode != 404 || ossErr.BucketName != "non-existent-bucket" {
		t.Errorf("Failed for TestGetNotFound: %v", err)
	}

}

func TestPutCopy(t *testing.T) {
	b := client.Bucket(TestBucket)
	t.Log("Source: ", b.Path("name"))
	res, err := b.PutCopy("newname", oss.Private, oss.CopyOptions{},
		b.Path("name"))
	if err == nil {
		t.Logf("Copy result: %v", res)
	} else {
		t.Errorf("Failed for PutCopy: %v", err)
	}
}

func TestList(t *testing.T) {

	b := client.Bucket(TestBucket)

	data, err := b.List("n", "", "", 0)
	if err != nil || len(data.Contents) != 2 {
		t.Errorf("Failed for List: %v", err)
	} else {
		t.Logf("Contents = %++v", data)
	}
}

func TestListWithDelimiter(t *testing.T) {

	b := client.Bucket(TestBucket)

	data, err := b.List("photos/2006/", "/", "some-marker", 1000)
	if err != nil || len(data.Contents) != 0 {
		t.Errorf("Failed for List: %v", err)
	} else {
		t.Logf("Contents = %++v", data)
	}

}

func TestPutReader(t *testing.T) {

	b := client.Bucket(TestBucket)
	buf := bytes.NewBufferString("content")
	err := b.PutReader("name", buf, int64(buf.Len()), "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for PutReader: %v", err)
	}
	TestGetReader(t)
}

var _fileSize int64 = 25 * 1024 * 1024
var _offset int64 = 10 * 1024 * 1024

func TestPutLargeFile(t *testing.T) {

	reader := newRandReader(_fileSize)

	b := client.Bucket(TestBucket)
	err := b.PutReader("largefile", reader, _fileSize, "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for PutReader: %v", err)
	}
}

func TestGetLargeFile(t *testing.T) {
	b := client.Bucket(TestBucket)
	headers := http.Header{}
	resp, err := b.GetResponseWithHeaders("largefile", headers)
	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	if resp.ContentLength != _fileSize {
		t.Errorf("Read file with incorrect ContentLength: %d", resp.ContentLength)

	}
	t.Logf("Large file response headers: %++v", resp.Header)

	data, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		t.Errorf("Failed for Read file: %v", err)
	}

	if len(data) != int(_fileSize) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestGetLargeFileWithOffset(t *testing.T) {
	b := client.Bucket(TestBucket)
	headers := http.Header{}
	headers.Add("Range", "bytes="+strconv.FormatInt(_offset, 10)+"-")
	resp, err := b.GetResponseWithHeaders("largefile", headers)
	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	t.Logf("Large file response headers: %++v", resp.Header)

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("Failed for Read with offset: %v", err)
	}
	if len(data) != int(_fileSize-_offset) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestSignedURL(t *testing.T) {
	b := client.Bucket(TestBucket)
	expires := time.Now().Add(20 * time.Minute)
	url := b.SignedURL("largefile", expires)
	resp, err := http.Get(url)
	t.Logf("Large file response headers: %++v", resp.Header)

	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	data, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		t.Errorf("Failed for Read file: %v", err)
	}

	if len(data) != int(_fileSize) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestCopyLargeFile(t *testing.T) {
	b := client.Bucket(TestBucket)
	err := b.CopyLargeFile("largefile", "largefile2", "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for copy large file: %v", err)
	}
	t.Log("Large file copy successfully.")
	len1, err := b.GetContentLength("largefile")

	if err != nil {
		t.Fatalf("Failed for Head file: %v", err)
	}
	len2, err := b.GetContentLength("largefile2")

	if err != nil {
		t.Fatalf("Failed for Head file: %v", err)
	}

	if len1 != len2 || len1 != _fileSize {
		t.Fatalf("Content-Length should be equal %d != %d", len1, len2)
	}

	bytes1, err := b.Get("largefile")
	if err != nil {
		t.Fatalf("Failed for Get file: %v", err)
	}
	bytes2, err := b.Get("largefile2")
	if err != nil {
		t.Fatalf("Failed for Get file: %v", err)
	}

	if bytes.Compare(bytes1, bytes2) != 0 {
		t.Fatal("The result should be equal")
	}
}

func TestDelLargeObject(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.Del("largefile")
	if err != nil {
		t.Errorf("Failed for Del largefile: %v", err)
	}
	err = b.Del("largefile2")
	if err != nil {
		t.Errorf("Failed for Del largefile2: %v", err)
	}
}

func TestExists(t *testing.T) {

	b := client.Bucket(TestBucket)
	result, err := b.Exists("name")
	if err != nil || result != true {
		t.Errorf("Failed for Exists: %v", err)
	}
}

func TestLocation(t *testing.T) {
	b := client.Bucket(TestBucket)
	result, err := b.Location()

	if err != nil || result != string(TestRegion) {
		t.Errorf("Failed for Location: %v %s", err, result)
	}
}

func TestACL(t *testing.T) {
	b := client.Bucket(TestBucket)
	result, err := b.ACL()

	if err != nil {
		t.Errorf("Failed for ACL: %v", err)
	} else {
		t.Logf("AccessControlPolicy: %++v", result)
	}
}

func TestDelObject(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.Del("name")
	if err != nil {
		t.Errorf("Failed for Del: %v", err)
	}
}

func TestDelMultiObjects(t *testing.T) {

	b := client.Bucket(TestBucket)
	objects := []oss.Object{oss.Object{Key: "newname"}}
	err := b.DelMulti(oss.Delete{
		Quiet:   false,
		Objects: objects,
	})
	if err != nil {
		t.Errorf("Failed for DelMulti: %v", err)
	}
}

func TestGetService(t *testing.T) {
	bucketList, err := client.GetService()
	if err != nil {
		t.Errorf("Unable to get service: %v", err)
	} else {
		t.Logf("GetService: %++v", bucketList)
	}
}

func TestGetBucketInfo(t *testing.T) {
	b := client.Bucket(TestBucket)
	resp, err := b.Info()
	if err != nil {
		t.Errorf("Failed for Info: %v", err)
	} else {
		t.Logf("Bucket Info: %v", resp)
	}
}

func TestDelBucket(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.DelBucket()
	if err != nil {
		t.Errorf("Failed for DelBucket: %v", err)
	}
}

type randReader struct {
	r int64
	m sync.Mutex
}

func (rr *randReader) Read(p []byte) (n int, err error) {
	rr.m.Lock()
	defer rr.m.Unlock()
	for i := 0; i < len(p) && rr.r > 0; i++ {
		p[i] = byte(rand.Intn(255))
		n++
		rr.r--
	}
	if rr.r == 0 {
		err = io.EOF
	}
	return
}

func newRandReader(n int64) *randReader {
	return &randReader{r: n}
}
<commit_msg>removed commented code<commit_after>package oss_test

import (
	"bytes"
	"io"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	//"net/http"
	"testing"
	"time"

	"github.com/denverdino/aliyungo/oss"
)

var (
	client     *oss.Client
	TestBucket = strconv.FormatInt(time.Now().Unix(), 10)
)

func init() {
	AccessKeyId := os.Getenv("AccessKeyId")
	AccessKeySecret := os.Getenv("AccessKeySecret")
	if len(AccessKeyId) != 0 && len(AccessKeySecret) != 0 {
		client = oss.NewOSSClient(TestRegion, false, AccessKeyId, AccessKeySecret, false)
	} else {
		client = oss.NewOSSClient(TestRegion, false, TestAccessKeyId, TestAccessKeySecret, false)
	}

}

func TestCreateBucket(t *testing.T) {
	b := client.Bucket(TestBucket)
	err := b.PutBucket(oss.Private)
	if err != nil {
		t.Errorf("Failed for PutBucket: %v", err)
	}
	t.Log("Wait a while for bucket creation ...")
	time.Sleep(10 * time.Second)
}

func TestHead(t *testing.T) {

	b := client.Bucket(TestBucket)
	_, err := b.Head("name", nil)

	if err == nil {
		t.Errorf("Failed for Head: %v", err)
	}
}

func TestPutObject(t *testing.T) {
	const DISPOSITION = "attachment; filename=\"0x1a2b3c.jpg\""

	b := client.Bucket(TestBucket)
	err := b.Put("name", []byte("content"), "content-type", oss.Private, oss.Options{ContentDisposition: DISPOSITION})
	if err != nil {
		t.Errorf("Failed for Put: %v", err)
	}
}

func TestGet(t *testing.T) {

	b := client.Bucket(TestBucket)
	data, err := b.Get("name")

	if err != nil || string(data) != "content" {
		t.Errorf("Failed for Get: %v", err)
	}
}

func TestURL(t *testing.T) {

	b := client.Bucket(TestBucket)
	url := b.URL("name")

	t.Log("URL: ", url)
	//	/c.Assert(req.URL.Path, check.Equals, "/denverdino_test/name")
}

func TestGetReader(t *testing.T) {

	b := client.Bucket(TestBucket)
	rc, err := b.GetReader("name")
	if err != nil {
		t.Fatalf("Failed for GetReader: %v", err)
	}
	data, err := ioutil.ReadAll(rc)
	rc.Close()
	if err != nil || string(data) != "content" {
		t.Errorf("Failed for ReadAll: %v", err)
	}
}

func aTestGetNotFound(t *testing.T) {

	b := client.Bucket("non-existent-bucket")
	_, err := b.Get("non-existent")
	if err == nil {
		t.Fatalf("Failed for TestGetNotFound: %v", err)
	}
	ossErr, _ := err.(*oss.Error)
	if ossErr.StatusCode != 404 || ossErr.BucketName != "non-existent-bucket" {
		t.Errorf("Failed for TestGetNotFound: %v", err)
	}

}

func TestPutCopy(t *testing.T) {
	b := client.Bucket(TestBucket)
	t.Log("Source: ", b.Path("name"))
	res, err := b.PutCopy("newname", oss.Private, oss.CopyOptions{},
		b.Path("name"))
	if err == nil {
		t.Logf("Copy result: %v", res)
	} else {
		t.Errorf("Failed for PutCopy: %v", err)
	}
}

func TestList(t *testing.T) {

	b := client.Bucket(TestBucket)

	data, err := b.List("n", "", "", 0)
	if err != nil || len(data.Contents) != 2 {
		t.Errorf("Failed for List: %v", err)
	} else {
		t.Logf("Contents = %++v", data)
	}
}

func TestListWithDelimiter(t *testing.T) {

	b := client.Bucket(TestBucket)

	data, err := b.List("photos/2006/", "/", "some-marker", 1000)
	if err != nil || len(data.Contents) != 0 {
		t.Errorf("Failed for List: %v", err)
	} else {
		t.Logf("Contents = %++v", data)
	}

}

func TestPutReader(t *testing.T) {

	b := client.Bucket(TestBucket)
	buf := bytes.NewBufferString("content")
	err := b.PutReader("name", buf, int64(buf.Len()), "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for PutReader: %v", err)
	}
	TestGetReader(t)
}

var _fileSize int64 = 25 * 1024 * 1024
var _offset int64 = 10 * 1024 * 1024

func TestPutLargeFile(t *testing.T) {

	reader := newRandReader(_fileSize)

	b := client.Bucket(TestBucket)
	err := b.PutReader("largefile", reader, _fileSize, "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for PutReader: %v", err)
	}
}

func TestGetLargeFile(t *testing.T) {
	b := client.Bucket(TestBucket)
	headers := http.Header{}
	resp, err := b.GetResponseWithHeaders("largefile", headers)
	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	if resp.ContentLength != _fileSize {
		t.Errorf("Read file with incorrect ContentLength: %d", resp.ContentLength)

	}
	t.Logf("Large file response headers: %++v", resp.Header)

	data, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		t.Errorf("Failed for Read file: %v", err)
	}

	if len(data) != int(_fileSize) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestGetLargeFileWithOffset(t *testing.T) {
	b := client.Bucket(TestBucket)
	headers := http.Header{}
	headers.Add("Range", "bytes="+strconv.FormatInt(_offset, 10)+"-")
	resp, err := b.GetResponseWithHeaders("largefile", headers)
	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	t.Logf("Large file response headers: %++v", resp.Header)

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("Failed for Read with offset: %v", err)
	}
	if len(data) != int(_fileSize-_offset) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestSignedURL(t *testing.T) {
	b := client.Bucket(TestBucket)
	expires := time.Now().Add(20 * time.Minute)
	url := b.SignedURL("largefile", expires)
	resp, err := http.Get(url)
	t.Logf("Large file response headers: %++v", resp.Header)

	if err != nil {
		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
	}
	data, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		t.Errorf("Failed for Read file: %v", err)
	}

	if len(data) != int(_fileSize) {
		t.Errorf("Incorrect length for Read with offset: %v", len(data))
	}
	resp.Body.Close()
}

func TestCopyLargeFile(t *testing.T) {
	b := client.Bucket(TestBucket)
	err := b.CopyLargeFile("largefile", "largefile2", "application/octet-stream", oss.Private, oss.Options{})
	if err != nil {
		t.Errorf("Failed for copy large file: %v", err)
	}
	t.Log("Large file copy successfully.")
	len1, err := b.GetContentLength("largefile")

	if err != nil {
		t.Fatalf("Failed for Head file: %v", err)
	}
	len2, err := b.GetContentLength("largefile2")

	if err != nil {
		t.Fatalf("Failed for Head file: %v", err)
	}

	if len1 != len2 || len1 != _fileSize {
		t.Fatalf("Content-Length should be equal %d != %d", len1, len2)
	}

	bytes1, err := b.Get("largefile")
	if err != nil {
		t.Fatalf("Failed for Get file: %v", err)
	}
	bytes2, err := b.Get("largefile2")
	if err != nil {
		t.Fatalf("Failed for Get file: %v", err)
	}

	if bytes.Compare(bytes1, bytes2) != 0 {
		t.Fatal("The result should be equal")
	}
}

func TestDelLargeObject(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.Del("largefile")
	if err != nil {
		t.Errorf("Failed for Del largefile: %v", err)
	}
	err = b.Del("largefile2")
	if err != nil {
		t.Errorf("Failed for Del largefile2: %v", err)
	}
}

func TestExists(t *testing.T) {

	b := client.Bucket(TestBucket)
	result, err := b.Exists("name")
	if err != nil || result != true {
		t.Errorf("Failed for Exists: %v", err)
	}
}

func TestLocation(t *testing.T) {
	b := client.Bucket(TestBucket)
	result, err := b.Location()

	if err != nil || result != string(TestRegion) {
		t.Errorf("Failed for Location: %v %s", err, result)
	}
}

func TestACL(t *testing.T) {
	b := client.Bucket(TestBucket)
	result, err := b.ACL()

	if err != nil {
		t.Errorf("Failed for ACL: %v", err)
	} else {
		t.Logf("AccessControlPolicy: %++v", result)
	}
}

func TestDelObject(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.Del("name")
	if err != nil {
		t.Errorf("Failed for Del: %v", err)
	}
}

func TestDelMultiObjects(t *testing.T) {

	b := client.Bucket(TestBucket)
	objects := []oss.Object{oss.Object{Key: "newname"}}
	err := b.DelMulti(oss.Delete{
		Quiet:   false,
		Objects: objects,
	})
	if err != nil {
		t.Errorf("Failed for DelMulti: %v", err)
	}
}

func TestGetService(t *testing.T) {
	bucketList, err := client.GetService()
	if err != nil {
		t.Errorf("Unable to get service: %v", err)
	} else {
		t.Logf("GetService: %++v", bucketList)
	}
}

func TestGetBucketInfo(t *testing.T) {
	b := client.Bucket(TestBucket)
	resp, err := b.Info()
	if err != nil {
		t.Errorf("Failed for Info: %v", err)
	} else {
		t.Logf("Bucket Info: %v", resp)
	}
}

func TestDelBucket(t *testing.T) {

	b := client.Bucket(TestBucket)
	err := b.DelBucket()
	if err != nil {
		t.Errorf("Failed for DelBucket: %v", err)
	}
}

type randReader struct {
	r int64
	m sync.Mutex
}

func (rr *randReader) Read(p []byte) (n int, err error) {
	rr.m.Lock()
	defer rr.m.Unlock()
	for i := 0; i < len(p) && rr.r > 0; i++ {
		p[i] = byte(rand.Intn(255))
		n++
		rr.r--
	}
	if rr.r == 0 {
		err = io.EOF
	}
	return
}

func newRandReader(n int64) *randReader {
	return &randReader{r: n}
}
<|endoftext|>


<commit_before>package quadedge

import (
	"context"
	"fmt"

	"github.com/go-spatial/geom"
	"github.com/go-spatial/geom/planar/intersect"
	"github.com/go-spatial/geom/winding"
)

const (
	precision = 6
)

var glbIdx uint64

// Edge describes a directional edge in a quadedge
type Edge struct {
	glbIdx uint64
	num    int
	next   *Edge
	qe     *QuadEdge
	v      *geom.Point
}

// New will return a new edge that is part of an QuadEdge
func New() *Edge {
	ql := NewQEdge()
	return &ql.e[0]
}

// NewWithEndPoints creates a new edge with the given end points
func NewWithEndPoints(a, b *geom.Point) *Edge {
	e := New()
	e.EndPoints(a, b)
	return e
}

// QEdge returns the quadedge this edge is part of
func (e *Edge) QEdge() *QuadEdge {
	if e == nil {
		return nil
	}
	return e.qe
}

// Orig returns the origin end point
func (e *Edge) Orig() *geom.Point {
	if e == nil {
		return nil
	}
	return e.v
}

// Dest returns the destination end point
func (e *Edge) Dest() *geom.Point {
	return e.Sym().Orig()
}

// EndPoints sets the end points of the Edge
func (e *Edge) EndPoints(org, dest *geom.Point) {
	e.v = org
	e.Sym().v = dest
}

// AsLine returns the Edge as a geom.Line
func (e *Edge) AsLine() geom.Line {
	porig, pdest := e.Orig(), e.Dest()
	orig, dest := geom.EmptyPoint, geom.EmptyPoint
	if porig != nil {
		orig = *porig
	}
	if pdest != nil {
		dest = *pdest
	}
	return geom.Line{[2]float64(orig), [2]float64(dest)}
}

/******** Edge Algebra *********************************************************/

// Rot returns the dual of the current edge, directed from its right
// to its left.
func (e *Edge) Rot() *Edge {
	if e == nil {
		return nil
	}
	if e.num == 3 {
		return &(e.qe.e[0])
	}
	return &(e.qe.e[e.num+1])
}

// InvRot returns the dual of the current edge, directed from its left
// to its right.
func (e *Edge) InvRot() *Edge {
	if e == nil {
		return nil
	}
	if e.num == 0 {
		return &(e.qe.e[3])
	}
	return &(e.qe.e[e.num-1])
}

// Sym returns the edge from the destination to the origin of the current edge.
func (e *Edge) Sym() *Edge {
	if e == nil {
		return nil
	}
	if e.num < 2 {
		return &(e.qe.e[e.num+2])
	}
	return &(e.qe.e[e.num-2])
}

// ONext returns the next ccw edge around (from) the origin of the current edge
func (e *Edge) ONext() *Edge {
	if e == nil {
		return nil
	}
	return e.next
}

// OPrev returns the next cw edge around (from) the origin of the current edge.
func (e *Edge) OPrev() *Edge {
	return e.Rot().ONext().Rot()
}

// DNext returns the next ccw edge around (into) the destination of the current edge.
func (e *Edge) DNext() *Edge {
	return e.Sym().ONext().Sym()
}

// DPrev returns the next cw edge around (into) the destination of the current edge.
func (e *Edge) DPrev() *Edge {
	return e.InvRot().ONext().InvRot()
}

// LNext returns the ccw edge around the left face following the current edge.
func (e *Edge) LNext() *Edge {
	return e.InvRot().ONext().Rot()
}

// LPrev returns the ccw edge around the left face before the current edge.
func (e *Edge) LPrev() *Edge {
	return e.ONext().Sym()
}

// RNext returns the edge around the right face ccw following the current edge.
func (e *Edge) RNext() *Edge {
	return e.Rot().ONext().InvRot()
}

// RPrev returns the edge around the right face ccw before the current edge.
func (e *Edge) RPrev() *Edge {
	return e.Sym().ONext()
}

/*****************************************************************************/
/*         Convenience functions to find edges                                 */
/*****************************************************************************/

// FindONextDest will look for and return a ccw edge the given dest point, if it
// exists.
func (e *Edge) FindONextDest(dest geom.Point) *Edge {
	if e == nil {
		return nil
	}
	if cmp.GeomPointEqual(dest, *e.Dest()) {
		return e
	}
	for ne := e.ONext(); ne != e; ne = ne.ONext() {
		if cmp.GeomPointEqual(dest, *ne.Dest()) {
			return ne
		}
	}
	return nil
}

// DumpAllEdges dumps all the edges as a multiline string
func (e *Edge) DumpAllEdges() string {
	var ml geom.MultiLineString

	e.WalkAllONext(func(ee *Edge) bool {
		ln := ee.AsLine()
		ml = append(ml, [][2]float64{ln[0], ln[1]})
		return true
	})
	str, err := wkt.EncodeString(ml)
	if err != nil {
		return err.Error()
	}
	return str
}

func (e *Edge) WalkAllOPrev(fn func(*Edge) (loop bool)) {
	var seen = map[uint64]bool{}
	cwe := e
	for cwe != nil && !seen[cwe.glbIdx] {
		if !fn(cwe) {
			return
		}
		seen[cwe.glbIdx] = true
		cwe = cwe.OPrev()
	}
}
func (e *Edge) WalkAllONext(fn func(*Edge) (loop bool)) {
	var seen = map[uint64]bool{}
	ccwe := e
	for ccwe != nil && !seen[ccwe.glbIdx] {
		if !fn(ccwe) {
			return
		}
		seen[ccwe.glbIdx] = true
		ccwe = ccwe.ONext()
	}
}

// IsEqual checks to see if the edges are the same
func (e *Edge) IsEqual(e1 *Edge) bool {
	if e == nil {
		return e1 == nil
	}

	if e1 == nil {
		return e == nil
	}
	// first let's get the edge numbers the same
	return e == &e1.qe.e[e.num]
}

// Validate check to se if the edges in the edges are correctly
// oriented
func Validate(e *Edge, order winding.Order) (err1 error) {

	const radius = 10
	var err ErrInvalid

	el := e.Rot()
	ed := el.Rot()
	er := ed.Rot()

	if ed.Sym() != e {
		// The Sym of Sym should be self
		err = append(err, "invalid Sym")
	}
	if ed != e.Sym() {
		err = append(err, fmt.Sprintf("invalid Rot: left.Rot != e.Sym %p : %p", el, e.Sym()))
	}
	if er != el.Sym() {
		err = append(err, fmt.Sprintf("invalid Rot: rot != e %p : %p", er, el.Sym()))
	}

	if e != el.InvRot() {
		err = append(err, "invalid Rot: rot != esym.InvRot")
	}

	if len(err) != 0 {
		return err
	}

	if e.Orig() == nil {
		err = append(err, "expected edge to have origin")
		return err
	}

	orig := *e.Orig()
	seen := make(map[geom.Point]bool)

	points := []geom.Point{}
	segs := []geom.Line{}
	e.WalkAllONext(func(ee *Edge) bool {
		dest := ee.Dest()
		if dest == nil {
			err = append(err, "dest is nil")
			return false
		}
		if ee.Orig() == nil {
			err = append(err, "expected edge to have origin")
			return false
		}
		if seen[*dest] {
			err = append(err, "dest not unique")
			err = append(err, ee.DumpAllEdges())
			return false
		}
		seen[*ee.Dest()] = true
		points = append(points, *ee.Dest())

		if !cmp.GeomPointEqual(*ee.Orig(), orig) {
			err = append(
				err,
				fmt.Sprintf(
					"expected edge %v to have same origin %v instead of %v",
					len(points), wkt.MustEncode(orig),
					wkt.MustEncode(*ee.Orig()),
				),
			)
		}
		segs = append(segs, e.AsLine())
		return true
	})
	if len(err) != 0 {
		return err
	}

	if len(points) > 2 {

		winding := order.OfGeomPoints(points...)
		if winding.IsColinear() {
			// All points are colinear to each other,
			// Need to check winding order with original point
			// not enough information just using the outer points, we need to include the origin
			winding = order.OfGeomPoints(append(points, orig)...)
		}

		if !winding.IsCounterClockwise() {
			err = append(err, fmt.Sprintf("1. (%v) expected all points to be counter-clockwise(%v):\n%v:%v\n%v",
				winding.ShortString(),
				order.CounterClockwise().ShortString(),
				wkt.MustEncode(orig),
				wkt.MustEncode(points),
				e.DumpAllEdges(),
			))
		}

		// New we need to check that there are no self intersecting lines.
		eq := intersect.NewEventQueue(segs)
		eq.CMP = cmp
		_ = eq.FindIntersects(
			context.Background(),
			true,
			func(src, dest int, pt [2]float64) error {
				// make sure the point is not an end point
				gpt := geom.Point(pt)
				if (cmp.GeomPointEqual(gpt, *segs[src].Point1()) || cmp.GeomPointEqual(gpt, *segs[src].Point2())) ||
					(cmp.GeomPointEqual(gpt, *segs[dest].Point1()) || cmp.GeomPointEqual(gpt, *segs[dest].Point2())) {
					return nil
				}
				// the second point in each segment should be the vertex we care about.
				// this is because of the way we build up the segments above.
				err = append(err,
					fmt.Sprintf("found self interstion for vertices %v and %v at %v",
						wkt.MustEncode(segs[src]),
						wkt.MustEncode(segs[dest]),
						pt,
					),
				)
				return err
			},
		)
	}

	if len(err) == 0 {
		return nil
	}
	return err
}
<commit_msg>[quadedge] fixed winding of ONext and OPrev detection<commit_after>package quadedge

import (
	"context"
	"fmt"
	"log"

	"github.com/go-spatial/geom"
	"github.com/go-spatial/geom/planar/intersect"
	"github.com/go-spatial/geom/winding"
)

const (
	precision = 6
)

var glbIdx uint64

// Edge describes a directional edge in a quadedge
type Edge struct {
	glbIdx uint64
	num    int
	next   *Edge
	qe     *QuadEdge
	v      *geom.Point
}

// New will return a new edge that is part of an QuadEdge
func New() *Edge {
	ql := NewQEdge()
	return &ql.e[0]
}

// NewWithEndPoints creates a new edge with the given end points
func NewWithEndPoints(a, b *geom.Point) *Edge {
	e := New()
	e.EndPoints(a, b)
	return e
}

// QEdge returns the quadedge this edge is part of
func (e *Edge) QEdge() *QuadEdge {
	if e == nil {
		return nil
	}
	return e.qe
}

// Orig returns the origin end point
func (e *Edge) Orig() *geom.Point {
	if e == nil {
		return nil
	}
	return e.v
}

// Dest returns the destination end point
func (e *Edge) Dest() *geom.Point {
	return e.Sym().Orig()
}

// EndPoints sets the end points of the Edge
func (e *Edge) EndPoints(org, dest *geom.Point) {
	e.v = org
	e.Sym().v = dest
}

// AsLine returns the Edge as a geom.Line
func (e *Edge) AsLine() geom.Line {
	porig, pdest := e.Orig(), e.Dest()
	orig, dest := geom.EmptyPoint, geom.EmptyPoint
	if porig != nil {
		orig = *porig
	}
	if pdest != nil {
		dest = *pdest
	}
	return geom.Line{[2]float64(orig), [2]float64(dest)}
}

/******** Edge Algebra *********************************************************/

// Rot returns the dual of the current edge, directed from its right
// to its left.
func (e *Edge) Rot() *Edge {
	if e == nil {
		return nil
	}
	if e.num == 3 {
		return &(e.qe.e[0])
	}
	return &(e.qe.e[e.num+1])
}

// InvRot returns the dual of the current edge, directed from its left
// to its right.
func (e *Edge) InvRot() *Edge {
	if e == nil {
		return nil
	}
	if e.num == 0 {
		return &(e.qe.e[3])
	}
	return &(e.qe.e[e.num-1])
}

// Sym returns the edge from the destination to the origin of the current edge.
func (e *Edge) Sym() *Edge {
	if e == nil {
		return nil
	}
	if e.num < 2 {
		return &(e.qe.e[e.num+2])
	}
	return &(e.qe.e[e.num-2])
}

// ONext returns the next ccw edge around (from) the origin of the current edge
func (e *Edge) ONext() *Edge {
	if e == nil {
		return nil
	}
	return e.next
}

// OPrev returns the next cw edge around (from) the origin of the current edge.
func (e *Edge) OPrev() *Edge {
	return e.Rot().ONext().Rot()
}

// DNext returns the next ccw edge around (into) the destination of the current edge.
func (e *Edge) DNext() *Edge {
	return e.Sym().ONext().Sym()
}

// DPrev returns the next cw edge around (into) the destination of the current edge.
func (e *Edge) DPrev() *Edge {
	return e.InvRot().ONext().InvRot()
}

// LNext returns the ccw edge around the left face following the current edge.
func (e *Edge) LNext() *Edge {
	return e.InvRot().ONext().Rot()
}

// LPrev returns the ccw edge around the left face before the current edge.
func (e *Edge) LPrev() *Edge {
	return e.ONext().Sym()
}

// RNext returns the edge around the right face ccw following the current edge.
func (e *Edge) RNext() *Edge {
	return e.Rot().ONext().InvRot()
}

// RPrev returns the edge around the right face ccw before the current edge.
func (e *Edge) RPrev() *Edge {
	return e.Sym().ONext()
}

/*****************************************************************************/
/*         Convenience functions to find edges                                 */
/*****************************************************************************/

// FindONextDest will look for and return a ccw edge the given dest point, if it
// exists.
func (e *Edge) FindONextDest(dest geom.Point) *Edge {
	if e == nil {
		return nil
	}
	if cmp.GeomPointEqual(dest, *e.Dest()) {
		return e
	}
	for ne := e.ONext(); ne != e; ne = ne.ONext() {
		if cmp.GeomPointEqual(dest, *ne.Dest()) {
			return ne
		}
	}
	return nil
}

// DumpAllEdges dumps all the edges as a multiline string
func (e *Edge) DumpAllEdges() string {
	var ml geom.MultiLineString

	e.WalkAllONext(func(ee *Edge) bool {
		ln := ee.AsLine()
		ml = append(ml, [][2]float64{ln[0], ln[1]})
		return true
	})
	str, err := wkt.EncodeString(ml)
	if err != nil {
		return err.Error()
	}
	return str
}

func (e *Edge) WalkAllOPrev(fn func(*Edge) (loop bool)) {
	var seen = map[uint64]bool{}
	cwe := e
	for cwe != nil && !seen[cwe.glbIdx] {
		if !fn(cwe) {
			return
		}
		seen[cwe.glbIdx] = true
		cwe = cwe.OPrev()
	}
}
func (e *Edge) WalkAllONext(fn func(*Edge) (loop bool)) {
	var seen = map[uint64]bool{}
	ccwe := e
	for ccwe != nil && !seen[ccwe.glbIdx] {
		if !fn(ccwe) {
			return
		}
		seen[ccwe.glbIdx] = true
		ccwe = ccwe.ONext()
	}
}

// IsEqual checks to see if the edges are the same
func (e *Edge) IsEqual(e1 *Edge) bool {
	if e == nil {
		return e1 == nil
	}

	if e1 == nil {
		return e == nil
	}
	// first let's get the edge numbers the same
	return e == &e1.qe.e[e.num]
}

// Validate check to se if the edges in the edges are correctly
// oriented
func Validate(e *Edge, order winding.Order) (err1 error) {

	const radius = 10
	var err ErrInvalid

	el := e.Rot()
	ed := el.Rot()
	er := ed.Rot()

	if ed.Sym() != e {
		// The Sym of Sym should be self
		err = append(err, "invalid Sym")
	}
	if ed != e.Sym() {
		err = append(err, fmt.Sprintf("invalid Rot: left.Rot != e.Sym %p : %p", el, e.Sym()))
	}
	if er != el.Sym() {
		err = append(err, fmt.Sprintf("invalid Rot: rot != e %p : %p", er, el.Sym()))
	}

	if e != el.InvRot() {
		err = append(err, "invalid Rot: rot != esym.InvRot")
	}

	if len(err) != 0 {
		return err
	}

	if e.Orig() == nil {
		err = append(err, "expected edge to have origin")
		return err
	}

	orig := *e.Orig()
	seen := make(map[geom.Point]bool)

	points := []geom.Point{}
	segs := []geom.Line{}
	var (
		onextCounterClockwiseCount int
		oprevClockwiseCount        int
	)
	e.WalkAllONext(func(ee *Edge) bool {
		dest := ee.Dest()
		if dest == nil {
			err = append(err, "dest is nil")
			return false
		}
		if ee.Orig() == nil {
			err = append(err, "expected edge to have origin")
			return false
		}
		if seen[*dest] {
			err = append(err, "dest not unique")
			err = append(err, ee.DumpAllEdges())
			return false
		}
		seen[*ee.Dest()] = true
		points = append(points, *ee.Dest())

		if !cmp.GeomPointEqual(*ee.Orig(), orig) {
			err = append(
				err,
				fmt.Sprintf(
					"expected edge %v to have same origin %v instead of %v",
					len(points), wkt.MustEncode(orig),
					wkt.MustEncode(*ee.Orig()),
				),
			)
		}
		segs = append(segs, e.AsLine())

		if debug {
			log.Printf("edge .    : %v", wkt.MustEncode(ee.AsLine()))
			log.Printf("edge.ONext: %v", wkt.MustEncode(ee.ONext().AsLine()))
			log.Printf("edge.OPrev: %v", wkt.MustEncode(ee.OPrev().AsLine()))
		}
		// Check to see if ONext edge is not clockwise
		onextDest := ee.ONext().Dest()
		onextWinding := order.OfGeomPoints(orig, *dest, *onextDest)
		switch {
		case onextWinding.IsClockwise():
			onextCounterClockwiseCount--
		case onextWinding.IsCounterClockwise():
			onextCounterClockwiseCount++
		}
		oprevDest := ee.OPrev().Dest()
		oprevWinding := order.OfGeomPoints(orig, *dest, *oprevDest)
		switch {
		case oprevWinding.IsClockwise():
			oprevClockwiseCount++
		case oprevWinding.IsCounterClockwise():
			oprevClockwiseCount--
		}

		return true
	})
	if len(err) != 0 {
		return err
	}

	if len(points) > 2 {
		if oprevClockwiseCount <= 0 {
			err = append(
				err,
				fmt.Sprintf("expected all points to be clockwise"),
			)

		}
		if onextCounterClockwiseCount <= 0 {
			err = append(
				err,
				fmt.Sprintf("expected all points to be counter-clockwise"),
			)
		}

		// New we need to check that there are no self intersecting lines.
		eq := intersect.NewEventQueue(segs)
		eq.CMP = cmp
		_ = eq.FindIntersects(
			context.Background(),
			true,
			func(src, dest int, pt [2]float64) error {
				// make sure the point is not an end point
				gpt := geom.Point(pt)
				if (cmp.GeomPointEqual(gpt, *segs[src].Point1()) || cmp.GeomPointEqual(gpt, *segs[src].Point2())) ||
					(cmp.GeomPointEqual(gpt, *segs[dest].Point1()) || cmp.GeomPointEqual(gpt, *segs[dest].Point2())) {
					return nil
				}
				// the second point in each segment should be the vertex we care about.
				// this is because of the way we build up the segments above.
				err = append(err,
					fmt.Sprintf("found self interstion for vertices %v and %v at %v",
						wkt.MustEncode(segs[src]),
						wkt.MustEncode(segs[dest]),
						pt,
					),
				)
				return err
			},
		)
	}

	if len(err) == 0 {
		return nil
	}
	return err
}
<|endoftext|>