<commit_before>// vim: tabstop=2 shiftwidth=2

package main

import (
	"fmt"
	"os"
	"strings"
	"path"
	"io/ioutil"
	"net/mail"
	"crypto/sha256"
	"encoding/hex"
	//"github.com/codahale/blake2"
	"github.com/crooks/yamn/idlog"
	"github.com/crooks/yamn/keymgr"
	"github.com/luksen/maildir"
)

func poolOutboundSend() {
	var err error
	var filenames []string
	if flag_send {
		// Read all the pool files
		filenames, err = readDir(cfg.Files.Pooldir, "m")
	} else {
		// Read dynamic mix of outbound files from the Pool
		filenames, err = poolRead()
	}
	if err != nil {
		Warn.Printf("Reading pool failed: %s", err)
		return
	}
	for _, file := range filenames {
		err = mailPoolFile(path.Join(cfg.Files.Pooldir, file))
		if err != nil {
			Warn.Printf("Pool mailing failed: %s", err)
		}
		poolDelete(file)
	}
}

// poolRead returns a dynamic Mix of filenames from the outbound pool.
func poolRead() (selectedPoolFiles []string, err error) {
	poolFiles, err := readDir(cfg.Files.Pooldir, "m")
	if err != nil {
		Warn.Printf("Unable to access pool: %s", err)
		return
	}
	poolSize := len(poolFiles)
	var numToSend int // Number of files to return for processing
	if poolSize < cfg.Pool.Size {
		// Pool isn't sufficiently populated
		Trace.Println("Pool insufficiently populated to trigger sending.",
			fmt.Sprintf("Require=%d, Got=%d", cfg.Pool.Size, poolSize))
		return
	} else {
		// Normal pool processing condition
		numToSend = int((float32(poolSize) / 100.0) * float32(cfg.Pool.Rate))
	}
	keys := randInts(len(poolFiles))
	if poolSize > 0 {
		Trace.Printf("Processing %d pool messages.\n", poolSize)
	}
	for n := 0; n < numToSend; n++ {
		mykey := keys[n]
		selectedPoolFiles = append(selectedPoolFiles, poolFiles[mykey])
	}
	return
}

// Delete a given file from the pool
func poolDelete(filename string) (err error) {
	// Delete a pool file
	err = os.Remove(path.Join(cfg.Files.Pooldir, filename))
	if err != nil {
		Error.Printf("Failed to remove %s from %s\n", filename, cfg.Files.Pooldir)
	}
	return
}

// processMail reads the Remailer's Maildir and processes the content
func processMail(
	public *keymgr.Pubring,
	secret *keymgr.Secring,
	id idlog.IDLog,
	chunkDB Chunk) (err error) {

	dir := maildir.Dir(cfg.Files.Maildir)
	// Get a list of Maildir keys from the directory
	keys, err := dir.Unseen()
	if err != nil {
		return
	}
	if len(keys) > 0 {
		Trace.Printf("Reading %d messages from %s\n", len(keys), cfg.Files.Maildir)
	}
	// Fetch headers for each Maildir key
	var head mail.Header
	for _, key := range keys {
		head, err = dir.Header(key)
		if err != nil {
			Warn.Printf("%s: Getting headers failed with: %s", key, err)
			continue
		}
		// The Subject determines if the message needs remailer-foo handling
		subject := strings.TrimSpace(strings.ToLower(head.Get("Subject")))
		if strings.HasPrefix(subject, "remailer-") {
			// It's a remailer-foo request
			err = remailerFoo(subject, head.Get("From"))
			if err != nil {
				Info.Println(err)
			}
		} else {
			// It's not a remailer-foo request so assume a remailer message
			var mailMsg *mail.Message
			mailMsg, err := dir.Message(key)
			if err != nil {
				Warn.Printf("%s: Reading message failed with: %s", key, err)
				continue
			}
			var msg []byte
			// Convert the armored Yamn message to its byte components
			msg, err = stripArmor(mailMsg.Body)
			if err != nil {
				Info.Println(err)
			}
			if msg == nil {
				Warn.Println("Dearmor returned zero bytes")
				continue
			}
			err = decodeMsg(msg, public, secret, id, chunkDB)
			if err != nil {
				Info.Println(err)
			}
		} // End of remailer-foo or remailer message
		err = dir.Purge(key)
		if err != nil {
			Warn.Printf("Cannot delete mail: %s", err)
		}
	} // Maildir keys loop
	return
}

// processInpool is similar to processMail but reads the Inbound Pool
func processInpool(
	prefix string,
	public *keymgr.Pubring,
	secret *keymgr.Secring,
	id idlog.IDLog,
	chunkDB Chunk) {
	poolFiles, err := readDir(cfg.Files.Pooldir, prefix)
	if err != nil {
		Warn.Printf("Unable to access inbound pool: %s", err)
		return
	}
	poolSize := len(poolFiles)
	processed := 0
	for _, f := range poolFiles {
		filename := path.Join(cfg.Files.Pooldir, f)
		msg := make([]byte, messageBytes)
		msg, err = ioutil.ReadFile(filename)
		if err != nil {
			Warn.Printf("Failed to read %s from pool: %s", f, err)
			continue
		}
		err = decodeMsg(msg, public, secret, id, chunkDB)
		if err != nil {
			Warn.Println(err)
		}
		poolDelete(f)
		processed++
	}
	if poolSize > 0 {
		Trace.Printf("Inbound pool processing complete. Read=%d, Decoded=%d",
			poolSize, processed)
	}
}

// PoolWrite writes a raw Byte Yamn message to the Outbound pool with a prefix
// string.
func poolWrite(yamnMsg []byte, prefix string) (poolFileName string) {
	/*
	Currently supported prefixs are:-
	[ m              Oubound message (final or intermediate) ]
	[ i          Inbound message (destined for this remailer ]
	[ p               Partial message chunk needing assembly ]
	*/
	digest := sha256.New()
	digest.Write(yamnMsg)
	poolFileName = prefix + hex.EncodeToString(digest.Sum(nil))[:14]
	fqfn := path.Join(cfg.Files.Pooldir, poolFileName)
	err := ioutil.WriteFile(fqfn, yamnMsg, 0600)
	if err != nil {
		panic(err)
	}
	return
}
<commit_msg>Send all outbound pool files in client mode<commit_after>// vim: tabstop=2 shiftwidth=2

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io/ioutil"
	"net/mail"
	"os"
	"path"
	"strings"
	//"github.com/codahale/blake2"
	"github.com/crooks/yamn/idlog"
	"github.com/crooks/yamn/keymgr"
	"github.com/luksen/maildir"
)

func poolOutboundSend() {
	var err error
	var filenames []string
	if flag_send || flag_client {
		// Read all the pool files
		filenames, err = readDir(cfg.Files.Pooldir, "m")
	} else {
		// Read dynamic mix of outbound files from the Pool
		filenames, err = poolRead()
	}
	if err != nil {
		Warn.Printf("Reading pool failed: %s", err)
		return
	}
	for _, file := range filenames {
		err = mailPoolFile(path.Join(cfg.Files.Pooldir, file))
		if err != nil {
			Warn.Printf("Pool mailing failed: %s", err)
		}
		poolDelete(file)
	}
}

// poolRead returns a dynamic Mix of filenames from the outbound pool.
func poolRead() (selectedPoolFiles []string, err error) {
	poolFiles, err := readDir(cfg.Files.Pooldir, "m")
	if err != nil {
		Warn.Printf("Unable to access pool: %s", err)
		return
	}
	poolSize := len(poolFiles)
	var numToSend int // Number of files to return for processing
	if poolSize < cfg.Pool.Size {
		// Pool isn't sufficiently populated
		Trace.Println("Pool insufficiently populated to trigger sending.",
			fmt.Sprintf("Require=%d, Got=%d", cfg.Pool.Size, poolSize))
		return
	} else {
		// Normal pool processing condition
		numToSend = int((float32(poolSize) / 100.0) * float32(cfg.Pool.Rate))
	}
	keys := randInts(len(poolFiles))
	if poolSize > 0 {
		Trace.Printf("Processing %d pool messages.\n", poolSize)
	}
	for n := 0; n < numToSend; n++ {
		mykey := keys[n]
		selectedPoolFiles = append(selectedPoolFiles, poolFiles[mykey])
	}
	return
}

// Delete a given file from the pool
func poolDelete(filename string) (err error) {
	// Delete a pool file
	err = os.Remove(path.Join(cfg.Files.Pooldir, filename))
	if err != nil {
		Error.Printf("Failed to remove %s from %s\n", filename, cfg.Files.Pooldir)
	}
	return
}

// processMail reads the Remailer's Maildir and processes the content
func processMail(
	public *keymgr.Pubring,
	secret *keymgr.Secring,
	id idlog.IDLog,
	chunkDB Chunk) (err error) {

	dir := maildir.Dir(cfg.Files.Maildir)
	// Get a list of Maildir keys from the directory
	keys, err := dir.Unseen()
	if err != nil {
		return
	}
	if len(keys) > 0 {
		Trace.Printf("Reading %d messages from %s\n", len(keys), cfg.Files.Maildir)
	}
	// Fetch headers for each Maildir key
	var head mail.Header
	for _, key := range keys {
		head, err = dir.Header(key)
		if err != nil {
			Warn.Printf("%s: Getting headers failed with: %s", key, err)
			continue
		}
		// The Subject determines if the message needs remailer-foo handling
		subject := strings.TrimSpace(strings.ToLower(head.Get("Subject")))
		if strings.HasPrefix(subject, "remailer-") {
			// It's a remailer-foo request
			err = remailerFoo(subject, head.Get("From"))
			if err != nil {
				Info.Println(err)
			}
		} else {
			// It's not a remailer-foo request so assume a remailer message
			var mailMsg *mail.Message
			mailMsg, err := dir.Message(key)
			if err != nil {
				Warn.Printf("%s: Reading message failed with: %s", key, err)
				continue
			}
			var msg []byte
			// Convert the armored Yamn message to its byte components
			msg, err = stripArmor(mailMsg.Body)
			if err != nil {
				Info.Println(err)
			}
			if msg == nil {
				Warn.Println("Dearmor returned zero bytes")
				continue
			}
			err = decodeMsg(msg, public, secret, id, chunkDB)
			if err != nil {
				Info.Println(err)
			}
		} // End of remailer-foo or remailer message
		err = dir.Purge(key)
		if err != nil {
			Warn.Printf("Cannot delete mail: %s", err)
		}
	} // Maildir keys loop
	return
}

// processInpool is similar to processMail but reads the Inbound Pool
func processInpool(
	prefix string,
	public *keymgr.Pubring,
	secret *keymgr.Secring,
	id idlog.IDLog,
	chunkDB Chunk) {
	poolFiles, err := readDir(cfg.Files.Pooldir, prefix)
	if err != nil {
		Warn.Printf("Unable to access inbound pool: %s", err)
		return
	}
	poolSize := len(poolFiles)
	processed := 0
	for _, f := range poolFiles {
		filename := path.Join(cfg.Files.Pooldir, f)
		msg := make([]byte, messageBytes)
		msg, err = ioutil.ReadFile(filename)
		if err != nil {
			Warn.Printf("Failed to read %s from pool: %s", f, err)
			continue
		}
		err = decodeMsg(msg, public, secret, id, chunkDB)
		if err != nil {
			Warn.Println(err)
		}
		poolDelete(f)
		processed++
	}
	if poolSize > 0 {
		Trace.Printf("Inbound pool processing complete. Read=%d, Decoded=%d",
			poolSize, processed)
	}
}

// PoolWrite writes a raw Byte Yamn message to the Outbound pool with a prefix
// string.
func poolWrite(yamnMsg []byte, prefix string) (poolFileName string) {
	/*
		Currently supported prefixs are:-
		[ m              Oubound message (final or intermediate) ]
		[ i          Inbound message (destined for this remailer ]
		[ p               Partial message chunk needing assembly ]
	*/
	digest := sha256.New()
	digest.Write(yamnMsg)
	poolFileName = prefix + hex.EncodeToString(digest.Sum(nil))[:14]
	fqfn := path.Join(cfg.Files.Pooldir, poolFileName)
	err := ioutil.WriteFile(fqfn, yamnMsg, 0600)
	if err != nil {
		panic(err)
	}
	return
}
<|endoftext|>


<commit_before>package pool

import (
	"errors"
	"net/url"
	"sync"
	"time"
)

var (
	timeout    = time.Duration(5) * time.Second
	t50ms      = time.Duration(50) * time.Millisecond
	errNilTask = errors.New("task is nil")
)

// Pool - pool of goroutines
type Pool struct {
	sync.RWMutex
	timerIsRunning bool
	numWorkers     int
	freeWorkers    int
	inputJobs      int
	workChan       chan Task
	inputTaskChan  chan Task
	ResultChan     chan Task
	quit           chan bool
	endTaskChan    chan bool
	queue          taskList
	quitTimeout    time.Duration
	timer          *time.Timer
}

// New - create new pool
func New(numWorkers int) *Pool {
	p := new(Pool)
	p.numWorkers = numWorkers
	p.freeWorkers = numWorkers
	p.workChan = make(chan Task)
	p.inputTaskChan = make(chan Task)
	p.ResultChan = make(chan Task)
	p.endTaskChan = make(chan bool)
	p.quit = make(chan bool)
	go p.runBroker()
	go p.runWorkers()
	return p
}

// Add - add new task to pool
func (p *Pool) Add(hostname string, proxy *url.URL) error {
	if hostname == "" {
		return errNilTask
	}
	_, err := url.Parse(hostname)
	if err != nil {
		return err
	}
	task := Task{
		Hostname: hostname,
		Proxy:    proxy,
	}
	p.inputTaskChan <- task
	return nil
}

func (p *Pool) runBroker() {
loopPool:
	for {
		select {
		case task := <-p.inputTaskChan:
			p.incJobs()
			task.ID = p.getJobs()
			p.addTask(task)
		case <-p.endTaskChan:
			p.incWorkers()
			if p.timerIsRunning && p.getFreeWorkers() == p.numWorkers {
				p.timer.Reset(p.quitTimeout)
			}
		case <-p.quit:
			close(p.workChan)
			close(p.ResultChan)
			break loopPool
		case <-time.After(t50ms):
			p.tryGetTask()
		}
	}
}

// SetHTTPTimeout - set http client timeout in second
func (p *Pool) SetHTTPTimeout(t int) {
	timeout = time.Duration(t) * time.Second
}

// SetTaskTimeout - set task timeout in second before send quit signal
func (p *Pool) SetTaskTimeout(t int) {
	p.quitTimeout = time.Duration(t) * time.Second
	p.timer = time.NewTimer(p.quitTimeout)
	p.timerIsRunning = true
	go func() {
		<-p.timer.C
		p.quit <- true
	}()
}

// Quit - send quit signal to pool
func (p *Pool) Quit() {
	p.quit <- true
}

func (p *Pool) addTask(task Task) {
	if p.getFreeWorkers() > 0 {
		if p.timerIsRunning {
			p.timer.Stop()
		}
		p.decWorkers()
		p.workChan <- task
	} else {
		p.queue.put(task)
	}
}

func (p *Pool) tryGetTask() {
	if p.freeWorkers > 0 {
		task, ok := p.queue.get()
		if ok {
			if p.timerIsRunning {
				p.timer.Stop()
			}
			p.decWorkers()
			p.workChan <- task
		}
	}
}

func (p *Pool) getFreeWorkers() int {
	p.RLock()
	freeWorkers := p.freeWorkers
	p.RUnlock()
	return freeWorkers
}

func (p *Pool) incWorkers() {
	p.Lock()
	p.freeWorkers++
	p.Unlock()
}

func (p *Pool) decWorkers() {
	p.Lock()
	p.freeWorkers--
	p.Unlock()
}

func (p *Pool) getJobs() int {
	p.RLock()
	inputJobs := p.inputJobs
	p.RUnlock()
	return inputJobs
}

func (p *Pool) incJobs() {
	p.Lock()
	p.inputJobs++
	p.Unlock()
}
<commit_msg>add tryGetTask<commit_after>package pool

import (
	"errors"
	"net/url"
	"sync"
	"time"
)

var (
	timeout    = time.Duration(5) * time.Second
	t50ms      = time.Duration(50) * time.Millisecond
	errNilTask = errors.New("task is nil")
)

// Pool - pool of goroutines
type Pool struct {
	sync.RWMutex
	timerIsRunning bool
	numWorkers     int
	freeWorkers    int
	inputJobs      int
	workChan       chan Task
	inputTaskChan  chan Task
	ResultChan     chan Task
	quit           chan bool
	endTaskChan    chan bool
	queue          taskList
	quitTimeout    time.Duration
	timer          *time.Timer
}

// New - create new pool
func New(numWorkers int) *Pool {
	p := new(Pool)
	p.numWorkers = numWorkers
	p.freeWorkers = numWorkers
	p.workChan = make(chan Task)
	p.inputTaskChan = make(chan Task)
	p.ResultChan = make(chan Task)
	p.endTaskChan = make(chan bool)
	p.quit = make(chan bool)
	go p.runBroker()
	go p.runWorkers()
	return p
}

// Add - add new task to pool
func (p *Pool) Add(hostname string, proxy *url.URL) error {
	if hostname == "" {
		return errNilTask
	}
	_, err := url.Parse(hostname)
	if err != nil {
		return err
	}
	task := Task{
		Hostname: hostname,
		Proxy:    proxy,
	}
	p.inputTaskChan <- task
	return nil
}

func (p *Pool) runBroker() {
loopPool:
	for {
		select {
		case task := <-p.inputTaskChan:
			p.incJobs()
			task.ID = p.getJobs()
			p.addTask(task)
		case <-p.endTaskChan:
			p.incWorkers()
			if p.timerIsRunning && p.getFreeWorkers() == p.numWorkers {
				p.timer.Reset(p.quitTimeout)
			}
			p.tryGetTask()
		case <-p.quit:
			close(p.workChan)
			close(p.ResultChan)
			break loopPool
		case <-time.After(t50ms):
			p.tryGetTask()
		}
	}
}

// SetHTTPTimeout - set http client timeout in second
func (p *Pool) SetHTTPTimeout(t int) {
	timeout = time.Duration(t) * time.Second
}

// SetTaskTimeout - set task timeout in second before send quit signal
func (p *Pool) SetTaskTimeout(t int) {
	p.quitTimeout = time.Duration(t) * time.Second
	p.timer = time.NewTimer(p.quitTimeout)
	p.timerIsRunning = true
	go func() {
		<-p.timer.C
		p.quit <- true
	}()
}

// Quit - send quit signal to pool
func (p *Pool) Quit() {
	p.quit <- true
}

func (p *Pool) addTask(task Task) {
	if p.getFreeWorkers() > 0 {
		if p.timerIsRunning {
			p.timer.Stop()
		}
		p.decWorkers()
		p.workChan <- task
	} else {
		p.queue.put(task)
	}
}

func (p *Pool) tryGetTask() {
	if p.freeWorkers > 0 {
		task, ok := p.queue.get()
		if ok {
			if p.timerIsRunning {
				p.timer.Stop()
			}
			p.decWorkers()
			p.workChan <- task
		}
	}
}

func (p *Pool) getFreeWorkers() int {
	p.RLock()
	freeWorkers := p.freeWorkers
	p.RUnlock()
	return freeWorkers
}

func (p *Pool) incWorkers() {
	p.Lock()
	p.freeWorkers++
	p.Unlock()
}

func (p *Pool) decWorkers() {
	p.Lock()
	p.freeWorkers--
	p.Unlock()
}

func (p *Pool) getJobs() int {
	p.RLock()
	inputJobs := p.inputJobs
	p.RUnlock()
	return inputJobs
}

func (p *Pool) incJobs() {
	p.Lock()
	p.inputJobs++
	p.Unlock()
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/lang"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/tfdiags"
)

// ConcreteResourceNodeFunc is a callback type used to convert an
// abstract resource to a concrete one of some type.
type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex

// GraphNodeConfigResource is implemented by any nodes that represent a resource.
// The type of operation cannot be assumed, only that this node represents
// the given resource.
type GraphNodeConfigResource interface {
	ResourceAddr() addrs.ConfigResource
}

// ConcreteResourceInstanceNodeFunc is a callback type used to convert an
// abstract resource instance to a concrete one of some type.
type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex

// GraphNodeResourceInstance is implemented by any nodes that represent
// a resource instance. A single resource may have multiple instances if,
// for example, the "count" or "for_each" argument is used for it in
// configuration.
type GraphNodeResourceInstance interface {
	ResourceInstanceAddr() addrs.AbsResourceInstance

	// StateDependencies returns any inter-resource dependencies that are
	// stored in the state.
	StateDependencies() []addrs.ConfigResource
}

// NodeAbstractResource represents a resource that has no associated
// operations. It registers all the interfaces for a resource that common
// across multiple operation types.
type NodeAbstractResource struct {
	Addr addrs.ConfigResource

	// The fields below will be automatically set using the Attach
	// interfaces if you're running those transforms, but also be explicitly
	// set if you already have that information.

	Schema        *configschema.Block // Schema for processing the configuration body
	SchemaVersion uint64              // Schema version of "Schema", as decided by the provider
	Config        *configs.Resource   // Config is the resource in the config

	// ProviderMetas is the provider_meta configs for the module this resource belongs to
	ProviderMetas map[addrs.Provider]*configs.ProviderMeta

	ProvisionerSchemas map[string]*configschema.Block

	// Set from GraphNodeTargetable
	Targets []addrs.Targetable

	// Set from AttachResourceDependencies
	dependsOn      []addrs.ConfigResource
	forceDependsOn bool

	// The address of the provider this resource will use
	ResolvedProvider addrs.AbsProviderConfig
}

var (
	_ GraphNodeReferenceable              = (*NodeAbstractResource)(nil)
	_ GraphNodeReferencer                 = (*NodeAbstractResource)(nil)
	_ GraphNodeProviderConsumer           = (*NodeAbstractResource)(nil)
	_ GraphNodeProvisionerConsumer        = (*NodeAbstractResource)(nil)
	_ GraphNodeConfigResource             = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachResourceConfig       = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachResourceSchema       = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachProvisionerSchema    = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachProviderMetaConfigs  = (*NodeAbstractResource)(nil)
	_ GraphNodeTargetable                 = (*NodeAbstractResource)(nil)
	_ graphNodeAttachResourceDependencies = (*NodeAbstractResource)(nil)
	_ dag.GraphNodeDotter                 = (*NodeAbstractResource)(nil)
)

// NewNodeAbstractResource creates an abstract resource graph node for
// the given absolute resource address.
func NewNodeAbstractResource(addr addrs.ConfigResource) *NodeAbstractResource {
	return &NodeAbstractResource{
		Addr: addr,
	}
}

var (
	_ GraphNodeModuleInstance            = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeReferenceable             = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeReferencer                = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeProviderConsumer          = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeProvisionerConsumer       = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeConfigResource            = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeResourceInstance          = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceState       = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceConfig      = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceSchema      = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachProvisionerSchema   = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeTargetable                = (*NodeAbstractResourceInstance)(nil)
	_ dag.GraphNodeDotter                = (*NodeAbstractResourceInstance)(nil)
)

func (n *NodeAbstractResource) Name() string {
	return n.ResourceAddr().String()
}

// GraphNodeModulePath
func (n *NodeAbstractResource) ModulePath() addrs.Module {
	return n.Addr.Module
}

// GraphNodeReferenceable
func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable {
	return []addrs.Referenceable{n.Addr.Resource}
}

// GraphNodeReferencer
func (n *NodeAbstractResource) References() []*addrs.Reference {
	// If we have a config then we prefer to use that.
	if c := n.Config; c != nil {
		var result []*addrs.Reference

		result = append(result, n.DependsOn()...)

		if n.Schema == nil {
			// Should never happen, but we'll log if it does so that we can
			// see this easily when debugging.
			log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
		}

		refs, _ := lang.ReferencesInExpr(c.Count)
		result = append(result, refs...)
		refs, _ = lang.ReferencesInExpr(c.ForEach)
		result = append(result, refs...)

		// ReferencesInBlock() requires a schema
		if n.Schema != nil {
			refs, _ = lang.ReferencesInBlock(c.Config, n.Schema)
		}

		result = append(result, refs...)
		if c.Managed != nil {
			if c.Managed.Connection != nil {
				refs, _ = lang.ReferencesInBlock(c.Managed.Connection.Config, connectionBlockSupersetSchema)
				result = append(result, refs...)
			}

			for _, p := range c.Managed.Provisioners {
				if p.When != configs.ProvisionerWhenCreate {
					continue
				}
				if p.Connection != nil {
					refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema)
					result = append(result, refs...)
				}

				schema := n.ProvisionerSchemas[p.Type]
				if schema == nil {
					log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name())
				}
				refs, _ = lang.ReferencesInBlock(p.Config, schema)
				result = append(result, refs...)
			}
		}
		return result
	}

	// Otherwise, we have no references.
	return nil
}

func (n *NodeAbstractResource) DependsOn() []*addrs.Reference {
	var result []*addrs.Reference
	if c := n.Config; c != nil {

		for _, traversal := range c.DependsOn {
			ref, diags := addrs.ParseRef(traversal)
			if diags.HasErrors() {
				// We ignore this here, because this isn't a suitable place to return
				// errors. This situation should be caught and rejected during
				// validation.
				log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err())
				continue
			}

			result = append(result, ref)
		}
	}
	return result
}

func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) {
	n.ResolvedProvider = p
}

// GraphNodeProviderConsumer
func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) {
	// If we have a config we prefer that above all else
	if n.Config != nil {
		relAddr := n.Config.ProviderConfigAddr()
		return addrs.LocalProviderConfig{
			LocalName: relAddr.LocalName,
			Alias:     relAddr.Alias,
		}, false
	}

	// No provider configuration found; return a default address
	return addrs.AbsProviderConfig{
		Provider: n.Provider(),
		Module:   n.ModulePath(),
	}, false
}

// GraphNodeProviderConsumer
func (n *NodeAbstractResource) Provider() addrs.Provider {
	if n.Config != nil {
		return n.Config.Provider
	}
	return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ImpliedProvider())
}

// GraphNodeProvisionerConsumer
func (n *NodeAbstractResource) ProvisionedBy() []string {
	// If we have no configuration, then we have no provisioners
	if n.Config == nil || n.Config.Managed == nil {
		return nil
	}

	// Build the list of provisioners we need based on the configuration.
	// It is okay to have duplicates here.
	result := make([]string, len(n.Config.Managed.Provisioners))
	for i, p := range n.Config.Managed.Provisioners {
		result[i] = p.Type
	}

	return result
}

// GraphNodeProvisionerConsumer
func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) {
	if n.ProvisionerSchemas == nil {
		n.ProvisionerSchemas = make(map[string]*configschema.Block)
	}
	n.ProvisionerSchemas[name] = schema
}

// GraphNodeResource
func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource {
	return n.Addr
}

// GraphNodeTargetable
func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) {
	n.Targets = targets
}

// graphNodeAttachResourceDependencies
func (n *NodeAbstractResource) AttachResourceDependencies(deps []addrs.ConfigResource, force bool) {
	n.dependsOn = deps
	n.forceDependsOn = force
}

// GraphNodeAttachResourceConfig
func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) {
	n.Config = c
}

// GraphNodeAttachResourceSchema impl
func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) {
	n.Schema = schema
	n.SchemaVersion = version
}

// GraphNodeAttachProviderMetaConfigs impl
func (n *NodeAbstractResource) AttachProviderMetaConfigs(c map[addrs.Provider]*configs.ProviderMeta) {
	n.ProviderMetas = c
}

// GraphNodeDotter impl.
func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
	return &dag.DotNode{
		Name: name,
		Attrs: map[string]string{
			"label": n.Name(),
			"shape": "box",
		},
	}
}

// writeResourceState ensures that a suitable resource-level state record is
// present in the state, if that's required for the "each mode" of that
// resource.
//
// This is important primarily for the situation where count = 0, since this
// eval is the only change we get to set the resource "each mode" to list
// in that case, allowing expression evaluation to see it as a zero-element list
// rather than as not set at all.
func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) (diags tfdiags.Diagnostics) {
	state := ctx.State()

	// We'll record our expansion decision in the shared "expander" object
	// so that later operations (i.e. DynamicExpand and expression evaluation)
	// can refer to it. Since this node represents the abstract module, we need
	// to expand the module here to create all resources.
	expander := ctx.InstanceExpander()

	switch {
	case n.Config.Count != nil:
		count, countDiags := evaluateCountExpression(n.Config.Count, ctx)
		diags = diags.Append(countDiags)
		if countDiags.HasErrors() {
			return diags
		}

		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceCount(addr.Module, n.Addr.Resource, count)

	case n.Config.ForEach != nil:
		forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
		diags = diags.Append(forEachDiags)
		if forEachDiags.HasErrors() {
			return diags
		}

		// This method takes care of all of the business logic of updating this
		// while ensuring that any existing instances are preserved, etc.
		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach)

	default:
		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceSingle(addr.Module, n.Addr.Resource)
	}

	return diags
}

// ReadResourceInstanceState reads the current object for a specific instance in
// the state.
func (n *NodeAbstractResource) ReadResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, error) {
	provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)

	if provider == nil {
		panic("ReadResourceInstanceState used with no Provider object")
	}
	if providerSchema == nil {
		panic("ReadResourceInstanceState used with no ProviderSchema object")
	}

	log.Printf("[TRACE] ReadResourceInstanceState: reading state for %s", addr)

	src := ctx.State().ResourceInstanceObject(addr, states.CurrentGen)
	if src == nil {
		// Presumably we only have deposed objects, then.
		log.Printf("[TRACE] ReadResourceInstanceState: no state present for %s", addr)
		return nil, nil
	}

	schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
	if schema == nil {
		// Shouldn't happen since we should've failed long ago if no schema is present
		return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)
	}
	var diags tfdiags.Diagnostics
	src, diags = UpgradeResourceState(addr, provider, src, schema, currentVersion)
	if diags.HasErrors() {
		// Note that we don't have any channel to return warnings here. We'll
		// accept that for now since warnings during a schema upgrade would
		// be pretty weird anyway, since this operation is supposed to seem
		// invisible to the user.
		return nil, diags.Err()
	}

	obj, err := src.Decode(schema.ImpliedType())
	if err != nil {
		return nil, err
	}

	return obj, nil
}

// graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an
// annoyingly-task-specific helper function that returns true if and only if
// the following conditions hold:
// - Both of the given vertices represent specific resource instances, as
//   opposed to unexpanded resources or any other non-resource-related object.
// - The module instance addresses for both of the resource instances belong
//   to the same static module.
// - The module instance addresses for both of the resource instances are
//   not equal, indicating that they belong to different instances of the
//   same module.
//
// This result can be used as a way to compensate for the effects of
// conservative analyses passes in our graph builders which make their
// decisions based only on unexpanded addresses, often so that they can behave
// correctly for interactions between expanded and not-yet-expanded objects.
//
// Callers of this helper function will typically skip adding an edge between
// the two given nodes if this function returns true.
func graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(a, b dag.Vertex) bool {
	aRI, aOK := a.(GraphNodeResourceInstance)
	bRI, bOK := b.(GraphNodeResourceInstance)
	if !(aOK && bOK) {
		return false
	}
	aModInst := aRI.ResourceInstanceAddr().Module
	bModInst := bRI.ResourceInstanceAddr().Module
	aMod := aModInst.Module()
	bMod := bModInst.Module()
	if !aMod.Equal(bMod) {
		return false
	}
	return !aModInst.Equal(bModInst)
}
<commit_msg>unchecked error<commit_after>package terraform

import (
	"fmt"
	"log"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/configs/configschema"
	"github.com/hashicorp/terraform/dag"
	"github.com/hashicorp/terraform/lang"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/tfdiags"
)

// ConcreteResourceNodeFunc is a callback type used to convert an
// abstract resource to a concrete one of some type.
type ConcreteResourceNodeFunc func(*NodeAbstractResource) dag.Vertex

// GraphNodeConfigResource is implemented by any nodes that represent a resource.
// The type of operation cannot be assumed, only that this node represents
// the given resource.
type GraphNodeConfigResource interface {
	ResourceAddr() addrs.ConfigResource
}

// ConcreteResourceInstanceNodeFunc is a callback type used to convert an
// abstract resource instance to a concrete one of some type.
type ConcreteResourceInstanceNodeFunc func(*NodeAbstractResourceInstance) dag.Vertex

// GraphNodeResourceInstance is implemented by any nodes that represent
// a resource instance. A single resource may have multiple instances if,
// for example, the "count" or "for_each" argument is used for it in
// configuration.
type GraphNodeResourceInstance interface {
	ResourceInstanceAddr() addrs.AbsResourceInstance

	// StateDependencies returns any inter-resource dependencies that are
	// stored in the state.
	StateDependencies() []addrs.ConfigResource
}

// NodeAbstractResource represents a resource that has no associated
// operations. It registers all the interfaces for a resource that common
// across multiple operation types.
type NodeAbstractResource struct {
	Addr addrs.ConfigResource

	// The fields below will be automatically set using the Attach
	// interfaces if you're running those transforms, but also be explicitly
	// set if you already have that information.

	Schema        *configschema.Block // Schema for processing the configuration body
	SchemaVersion uint64              // Schema version of "Schema", as decided by the provider
	Config        *configs.Resource   // Config is the resource in the config

	// ProviderMetas is the provider_meta configs for the module this resource belongs to
	ProviderMetas map[addrs.Provider]*configs.ProviderMeta

	ProvisionerSchemas map[string]*configschema.Block

	// Set from GraphNodeTargetable
	Targets []addrs.Targetable

	// Set from AttachResourceDependencies
	dependsOn      []addrs.ConfigResource
	forceDependsOn bool

	// The address of the provider this resource will use
	ResolvedProvider addrs.AbsProviderConfig
}

var (
	_ GraphNodeReferenceable              = (*NodeAbstractResource)(nil)
	_ GraphNodeReferencer                 = (*NodeAbstractResource)(nil)
	_ GraphNodeProviderConsumer           = (*NodeAbstractResource)(nil)
	_ GraphNodeProvisionerConsumer        = (*NodeAbstractResource)(nil)
	_ GraphNodeConfigResource             = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachResourceConfig       = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachResourceSchema       = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachProvisionerSchema    = (*NodeAbstractResource)(nil)
	_ GraphNodeAttachProviderMetaConfigs  = (*NodeAbstractResource)(nil)
	_ GraphNodeTargetable                 = (*NodeAbstractResource)(nil)
	_ graphNodeAttachResourceDependencies = (*NodeAbstractResource)(nil)
	_ dag.GraphNodeDotter                 = (*NodeAbstractResource)(nil)
)

// NewNodeAbstractResource creates an abstract resource graph node for
// the given absolute resource address.
func NewNodeAbstractResource(addr addrs.ConfigResource) *NodeAbstractResource {
	return &NodeAbstractResource{
		Addr: addr,
	}
}

var (
	_ GraphNodeModuleInstance            = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeReferenceable             = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeReferencer                = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeProviderConsumer          = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeProvisionerConsumer       = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeConfigResource            = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeResourceInstance          = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceState       = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceConfig      = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachResourceSchema      = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachProvisionerSchema   = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeAttachProviderMetaConfigs = (*NodeAbstractResourceInstance)(nil)
	_ GraphNodeTargetable                = (*NodeAbstractResourceInstance)(nil)
	_ dag.GraphNodeDotter                = (*NodeAbstractResourceInstance)(nil)
)

func (n *NodeAbstractResource) Name() string {
	return n.ResourceAddr().String()
}

// GraphNodeModulePath
func (n *NodeAbstractResource) ModulePath() addrs.Module {
	return n.Addr.Module
}

// GraphNodeReferenceable
func (n *NodeAbstractResource) ReferenceableAddrs() []addrs.Referenceable {
	return []addrs.Referenceable{n.Addr.Resource}
}

// GraphNodeReferencer
func (n *NodeAbstractResource) References() []*addrs.Reference {
	// If we have a config then we prefer to use that.
	if c := n.Config; c != nil {
		var result []*addrs.Reference

		result = append(result, n.DependsOn()...)

		if n.Schema == nil {
			// Should never happen, but we'll log if it does so that we can
			// see this easily when debugging.
			log.Printf("[WARN] no schema is attached to %s, so config references cannot be detected", n.Name())
		}

		refs, _ := lang.ReferencesInExpr(c.Count)
		result = append(result, refs...)
		refs, _ = lang.ReferencesInExpr(c.ForEach)
		result = append(result, refs...)

		// ReferencesInBlock() requires a schema
		if n.Schema != nil {
			refs, _ = lang.ReferencesInBlock(c.Config, n.Schema)
		}

		result = append(result, refs...)
		if c.Managed != nil {
			if c.Managed.Connection != nil {
				refs, _ = lang.ReferencesInBlock(c.Managed.Connection.Config, connectionBlockSupersetSchema)
				result = append(result, refs...)
			}

			for _, p := range c.Managed.Provisioners {
				if p.When != configs.ProvisionerWhenCreate {
					continue
				}
				if p.Connection != nil {
					refs, _ = lang.ReferencesInBlock(p.Connection.Config, connectionBlockSupersetSchema)
					result = append(result, refs...)
				}

				schema := n.ProvisionerSchemas[p.Type]
				if schema == nil {
					log.Printf("[WARN] no schema for provisioner %q is attached to %s, so provisioner block references cannot be detected", p.Type, n.Name())
				}
				refs, _ = lang.ReferencesInBlock(p.Config, schema)
				result = append(result, refs...)
			}
		}
		return result
	}

	// Otherwise, we have no references.
	return nil
}

func (n *NodeAbstractResource) DependsOn() []*addrs.Reference {
	var result []*addrs.Reference
	if c := n.Config; c != nil {

		for _, traversal := range c.DependsOn {
			ref, diags := addrs.ParseRef(traversal)
			if diags.HasErrors() {
				// We ignore this here, because this isn't a suitable place to return
				// errors. This situation should be caught and rejected during
				// validation.
				log.Printf("[ERROR] Can't parse %#v from depends_on as reference: %s", traversal, diags.Err())
				continue
			}

			result = append(result, ref)
		}
	}
	return result
}

func (n *NodeAbstractResource) SetProvider(p addrs.AbsProviderConfig) {
	n.ResolvedProvider = p
}

// GraphNodeProviderConsumer
func (n *NodeAbstractResource) ProvidedBy() (addrs.ProviderConfig, bool) {
	// If we have a config we prefer that above all else
	if n.Config != nil {
		relAddr := n.Config.ProviderConfigAddr()
		return addrs.LocalProviderConfig{
			LocalName: relAddr.LocalName,
			Alias:     relAddr.Alias,
		}, false
	}

	// No provider configuration found; return a default address
	return addrs.AbsProviderConfig{
		Provider: n.Provider(),
		Module:   n.ModulePath(),
	}, false
}

// GraphNodeProviderConsumer
func (n *NodeAbstractResource) Provider() addrs.Provider {
	if n.Config != nil {
		return n.Config.Provider
	}
	return addrs.ImpliedProviderForUnqualifiedType(n.Addr.Resource.ImpliedProvider())
}

// GraphNodeProvisionerConsumer
func (n *NodeAbstractResource) ProvisionedBy() []string {
	// If we have no configuration, then we have no provisioners
	if n.Config == nil || n.Config.Managed == nil {
		return nil
	}

	// Build the list of provisioners we need based on the configuration.
	// It is okay to have duplicates here.
	result := make([]string, len(n.Config.Managed.Provisioners))
	for i, p := range n.Config.Managed.Provisioners {
		result[i] = p.Type
	}

	return result
}

// GraphNodeProvisionerConsumer
func (n *NodeAbstractResource) AttachProvisionerSchema(name string, schema *configschema.Block) {
	if n.ProvisionerSchemas == nil {
		n.ProvisionerSchemas = make(map[string]*configschema.Block)
	}
	n.ProvisionerSchemas[name] = schema
}

// GraphNodeResource
func (n *NodeAbstractResource) ResourceAddr() addrs.ConfigResource {
	return n.Addr
}

// GraphNodeTargetable
func (n *NodeAbstractResource) SetTargets(targets []addrs.Targetable) {
	n.Targets = targets
}

// graphNodeAttachResourceDependencies
func (n *NodeAbstractResource) AttachResourceDependencies(deps []addrs.ConfigResource, force bool) {
	n.dependsOn = deps
	n.forceDependsOn = force
}

// GraphNodeAttachResourceConfig
func (n *NodeAbstractResource) AttachResourceConfig(c *configs.Resource) {
	n.Config = c
}

// GraphNodeAttachResourceSchema impl
func (n *NodeAbstractResource) AttachResourceSchema(schema *configschema.Block, version uint64) {
	n.Schema = schema
	n.SchemaVersion = version
}

// GraphNodeAttachProviderMetaConfigs impl
func (n *NodeAbstractResource) AttachProviderMetaConfigs(c map[addrs.Provider]*configs.ProviderMeta) {
	n.ProviderMetas = c
}

// GraphNodeDotter impl.
func (n *NodeAbstractResource) DotNode(name string, opts *dag.DotOpts) *dag.DotNode {
	return &dag.DotNode{
		Name: name,
		Attrs: map[string]string{
			"label": n.Name(),
			"shape": "box",
		},
	}
}

// writeResourceState ensures that a suitable resource-level state record is
// present in the state, if that's required for the "each mode" of that
// resource.
//
// This is important primarily for the situation where count = 0, since this
// eval is the only change we get to set the resource "each mode" to list
// in that case, allowing expression evaluation to see it as a zero-element list
// rather than as not set at all.
func (n *NodeAbstractResource) writeResourceState(ctx EvalContext, addr addrs.AbsResource) (diags tfdiags.Diagnostics) {
	state := ctx.State()

	// We'll record our expansion decision in the shared "expander" object
	// so that later operations (i.e. DynamicExpand and expression evaluation)
	// can refer to it. Since this node represents the abstract module, we need
	// to expand the module here to create all resources.
	expander := ctx.InstanceExpander()

	switch {
	case n.Config.Count != nil:
		count, countDiags := evaluateCountExpression(n.Config.Count, ctx)
		diags = diags.Append(countDiags)
		if countDiags.HasErrors() {
			return diags
		}

		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceCount(addr.Module, n.Addr.Resource, count)

	case n.Config.ForEach != nil:
		forEach, forEachDiags := evaluateForEachExpression(n.Config.ForEach, ctx)
		diags = diags.Append(forEachDiags)
		if forEachDiags.HasErrors() {
			return diags
		}

		// This method takes care of all of the business logic of updating this
		// while ensuring that any existing instances are preserved, etc.
		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceForEach(addr.Module, n.Addr.Resource, forEach)

	default:
		state.SetResourceProvider(addr, n.ResolvedProvider)
		expander.SetResourceSingle(addr.Module, n.Addr.Resource)
	}

	return diags
}

// ReadResourceInstanceState reads the current object for a specific instance in
// the state.
func (n *NodeAbstractResource) ReadResourceInstanceState(ctx EvalContext, addr addrs.AbsResourceInstance) (*states.ResourceInstanceObject, error) {
	provider, providerSchema, err := GetProvider(ctx, n.ResolvedProvider)
	if err != nil {
		return nil, err
	}

	log.Printf("[TRACE] ReadResourceInstanceState: reading state for %s", addr)

	src := ctx.State().ResourceInstanceObject(addr, states.CurrentGen)
	if src == nil {
		// Presumably we only have deposed objects, then.
		log.Printf("[TRACE] ReadResourceInstanceState: no state present for %s", addr)
		return nil, nil
	}

	schema, currentVersion := (providerSchema).SchemaForResourceAddr(addr.Resource.ContainingResource())
	if schema == nil {
		// Shouldn't happen since we should've failed long ago if no schema is present
		return nil, fmt.Errorf("no schema available for %s while reading state; this is a bug in Terraform and should be reported", addr)
	}
	var diags tfdiags.Diagnostics
	src, diags = UpgradeResourceState(addr, provider, src, schema, currentVersion)
	if diags.HasErrors() {
		// Note that we don't have any channel to return warnings here. We'll
		// accept that for now since warnings during a schema upgrade would
		// be pretty weird anyway, since this operation is supposed to seem
		// invisible to the user.
		return nil, diags.Err()
	}

	obj, err := src.Decode(schema.ImpliedType())
	if err != nil {
		return nil, err
	}

	return obj, nil
}

// graphNodesAreResourceInstancesInDifferentInstancesOfSameModule is an
// annoyingly-task-specific helper function that returns true if and only if
// the following conditions hold:
// - Both of the given vertices represent specific resource instances, as
//   opposed to unexpanded resources or any other non-resource-related object.
// - The module instance addresses for both of the resource instances belong
//   to the same static module.
// - The module instance addresses for both of the resource instances are
//   not equal, indicating that they belong to different instances of the
//   same module.
//
// This result can be used as a way to compensate for the effects of
// conservative analyses passes in our graph builders which make their
// decisions based only on unexpanded addresses, often so that they can behave
// correctly for interactions between expanded and not-yet-expanded objects.
//
// Callers of this helper function will typically skip adding an edge between
// the two given nodes if this function returns true.
func graphNodesAreResourceInstancesInDifferentInstancesOfSameModule(a, b dag.Vertex) bool {
	aRI, aOK := a.(GraphNodeResourceInstance)
	bRI, bOK := b.(GraphNodeResourceInstance)
	if !(aOK && bOK) {
		return false
	}
	aModInst := aRI.ResourceInstanceAddr().Module
	bModInst := bRI.ResourceInstanceAddr().Module
	aMod := aModInst.Module()
	bMod := bModInst.Module()
	if !aMod.Equal(bMod) {
		return false
	}
	return !aModInst.Equal(bModInst)
}
<|endoftext|>


<commit_before>package v1

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/solefaucet/sole-server/errors"
	"github.com/solefaucet/sole-server/models"
)

// offerwow const
const (
	offerwowWebsiteID     = "2626"
	offerwowImmediate0    = "0" // 非即时返利活动,处于待审核状态；
	offerwowImmediate1    = "1" // 即时返利活动，需发放奖励给会员；
	offerwowImmediate2    = "2" // 非即时返利活动，审核通过，重新回传，发放奖励给会员；
	offerwowImmediate3    = "3" // 非即时返利活动，审核不通过，重新回传，不发放奖励；
	offerwowStatusSuccess = "success"
	offerwowStatusFailure = "failure"
	offerwowErrno01       = "offerwow-01"
	offerwowErrno02       = "offerwow-02"
	offerwowErrno03       = "offerwow-03"
	offerwowErrno04       = "offerwow-04"
	offerwowErrno05       = "offerwow-05"
	offerwowErrno06       = "offerwow-06"
)

type offerwowPayload struct {
	UserID    int64   `form:"memberid"`
	Amount    float64 `form:"point"`
	EventID   string  `form:"eventid"`
	WebsiteID string  `form:"websiteid"`
	Immediate string  `form:"immediate"`
	Sign      string  `form:"sign"`
}

type offerwowResponse struct {
	UserID    int64
	Amount    float64
	EventID   string
	Immediate string
	Status    string
	Error     string
}

func (r offerwowResponse) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"memberid":  fmt.Sprint(r.UserID),
		"point":     fmt.Sprint(r.Amount),
		"websiteid": offerwowWebsiteID,
		"eventid":   r.EventID,
		"immediate": r.Immediate,
		"status":    r.Status,
	}
	if r.Error != "" {
		m["errno"] = r.Error
	}
	return json.Marshal(m)
}

// OfferwowCallback handles callback from offerwow
func OfferwowCallback(
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
	getSystemConfig dependencyGetSystemConfig,
	createOfferwowIncome dependencyCreateOfferwowIncome,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		payload := offerwowPayload{}
		c.BindWith(&payload, binding.Form)

		if !validateOfferwowSignature(payload, "86611105freeusd") {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		responseAndLog := func(status, err string) {
			response := offerwowResponse{
				UserID:    payload.UserID,
				Amount:    payload.Amount,
				EventID:   payload.EventID,
				Immediate: payload.Immediate,
				Status:    status,
				Error:     err,
			}
			c.JSON(http.StatusOK, response)
			logrus.WithFields(logrus.Fields{
				"event":    models.EventOfferwowCallback,
				"query":    c.Request.URL.Query().Encode(),
				"payload":  payload,
				"response": response,
			}).Debug("get offerwow callback")
		}

		errno, user, err := validateOfferwowEvent(payload, getUserByID, getOfferwowEventByID)
		if err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		} else if errno != "" {
			responseAndLog(offerwowStatusFailure, errno)
			return
		}

		// create income offerwow
		income := models.Income{
			UserID:        user.ID,
			RefererID:     user.RefererID,
			Type:          models.IncomeTypeOfferwow,
			Income:        payload.Amount / 1e8,
			RefererIncome: getSystemConfig().RefererRewardRate * payload.Amount,
		}
		if err := createOfferwowIncome(income, payload.EventID); err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		responseAndLog(offerwowStatusSuccess, "")
	}
}

func validateOfferwowSignature(payload offerwowPayload, key string) bool {
	data := fmt.Sprintf("%v%v%v%v%v%v", payload.UserID, payload.Amount, payload.EventID, payload.WebsiteID, payload.Immediate, key)
	sign := strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(data))))
	logrus.WithFields(logrus.Fields{
		"event": models.EventOfferwowCallback,
	}).Info(sign)
	return sign == payload.Sign
}

/*
We need to check it one by one

offerwow-01: 出现空参数
offerwow-02: 网站id不存在
offerwow-03: uid会员不存在
offerwow-04: 已发放奖励的Eventid重复
offerwow-05: immediate=0
offerwow-06: immediate=3
*/
func validateOfferwowEvent(
	payload offerwowPayload,
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
) (errno string, user models.User, err error) {
	if payload.UserID == 0 || payload.Amount == 0 || payload.EventID == "" || payload.WebsiteID == "" || payload.Immediate == "" {
		errno = offerwowErrno01
		return
	}

	if payload.WebsiteID != offerwowWebsiteID {
		errno = offerwowErrno02
		return
	}

	// check if user exists
	user, err = getUserByID(payload.UserID)
	if err == errors.ErrNotFound {
		errno = offerwowErrno03
		err = nil
		return
	} else if err != nil {
		return
	}

	// check if eventid duplicates
	_, err = getOfferwowEventByID(payload.EventID)
	switch err {
	case errors.ErrNotFound:
		err = nil
	case nil:
		errno = offerwowErrno04
		return
	default:
		return
	}

	if payload.Immediate == offerwowImmediate0 {
		errno = offerwowErrno05
		return
	}

	if payload.Immediate == offerwowImmediate3 {
		errno = offerwowErrno06
		return
	}

	return
}
<commit_msg>Log signature if it's not match<commit_after>package v1

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/solefaucet/sole-server/errors"
	"github.com/solefaucet/sole-server/models"
)

// offerwow const
const (
	offerwowWebsiteID     = "2626"
	offerwowImmediate0    = "0" // 非即时返利活动,处于待审核状态；
	offerwowImmediate1    = "1" // 即时返利活动，需发放奖励给会员；
	offerwowImmediate2    = "2" // 非即时返利活动，审核通过，重新回传，发放奖励给会员；
	offerwowImmediate3    = "3" // 非即时返利活动，审核不通过，重新回传，不发放奖励；
	offerwowStatusSuccess = "success"
	offerwowStatusFailure = "failure"
	offerwowErrno01       = "offerwow-01"
	offerwowErrno02       = "offerwow-02"
	offerwowErrno03       = "offerwow-03"
	offerwowErrno04       = "offerwow-04"
	offerwowErrno05       = "offerwow-05"
	offerwowErrno06       = "offerwow-06"
)

type offerwowPayload struct {
	UserID    int64   `form:"memberid"`
	Amount    float64 `form:"point"`
	EventID   string  `form:"eventid"`
	WebsiteID string  `form:"websiteid"`
	Immediate string  `form:"immediate"`
	Sign      string  `form:"sign"`
}

type offerwowResponse struct {
	UserID    int64
	Amount    float64
	EventID   string
	Immediate string
	Status    string
	Error     string
}

func (r offerwowResponse) MarshalJSON() ([]byte, error) {
	m := map[string]interface{}{
		"memberid":  fmt.Sprint(r.UserID),
		"point":     fmt.Sprint(r.Amount),
		"websiteid": offerwowWebsiteID,
		"eventid":   r.EventID,
		"immediate": r.Immediate,
		"status":    r.Status,
	}
	if r.Error != "" {
		m["errno"] = r.Error
	}
	return json.Marshal(m)
}

// OfferwowCallback handles callback from offerwow
func OfferwowCallback(
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
	getSystemConfig dependencyGetSystemConfig,
	createOfferwowIncome dependencyCreateOfferwowIncome,
) gin.HandlerFunc {
	return func(c *gin.Context) {
		payload := offerwowPayload{}
		c.BindWith(&payload, binding.Form)

		if !validateOfferwowSignature(payload, "86611105freeusd") {
			c.AbortWithStatus(http.StatusForbidden)
			return
		}

		responseAndLog := func(status, err string) {
			response := offerwowResponse{
				UserID:    payload.UserID,
				Amount:    payload.Amount,
				EventID:   payload.EventID,
				Immediate: payload.Immediate,
				Status:    status,
				Error:     err,
			}
			logrus.WithFields(logrus.Fields{
				"event":    models.EventOfferwowCallback,
				"query":    c.Request.URL.Query().Encode(),
				"payload":  payload,
				"response": response,
			}).Debug("get offerwow callback")
			c.JSON(http.StatusOK, response)
		}

		errno, user, err := validateOfferwowEvent(payload, getUserByID, getOfferwowEventByID)
		if err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		} else if errno != "" {
			responseAndLog(offerwowStatusFailure, errno)
			return
		}

		// create income offerwow
		income := models.Income{
			UserID:        user.ID,
			RefererID:     user.RefererID,
			Type:          models.IncomeTypeOfferwow,
			Income:        payload.Amount / 1e8,
			RefererIncome: getSystemConfig().RefererRewardRate * payload.Amount,
		}
		if err := createOfferwowIncome(income, payload.EventID); err != nil {
			c.AbortWithError(http.StatusInternalServerError, err)
			return
		}

		responseAndLog(offerwowStatusSuccess, "")
	}
}

func validateOfferwowSignature(payload offerwowPayload, key string) bool {
	data := fmt.Sprintf("%v%v%v%v%v%v", payload.UserID, payload.Amount, payload.EventID, payload.WebsiteID, payload.Immediate, key)
	if sign := strings.ToUpper(fmt.Sprintf("%x", md5.Sum([]byte(data)))); sign != payload.Sign {
		logrus.WithFields(logrus.Fields{
			"event":   models.EventOfferwowCallback,
			"payload": payload,
			"sign":    sign,
		}).Debug("signature not match")
		return false
	}
	return true
}

/*
We need to check it one by one

offerwow-01: 出现空参数
offerwow-02: 网站id不存在
offerwow-03: uid会员不存在
offerwow-04: 已发放奖励的Eventid重复
offerwow-05: immediate=0
offerwow-06: immediate=3
*/
func validateOfferwowEvent(
	payload offerwowPayload,
	getUserByID dependencyGetUserByID,
	getOfferwowEventByID dependencyGetOfferwowEventByID,
) (errno string, user models.User, err error) {
	if payload.UserID == 0 || payload.Amount == 0 || payload.EventID == "" || payload.WebsiteID == "" || payload.Immediate == "" {
		errno = offerwowErrno01
		return
	}

	if payload.WebsiteID != offerwowWebsiteID {
		errno = offerwowErrno02
		return
	}

	// check if user exists
	user, err = getUserByID(payload.UserID)
	if err == errors.ErrNotFound {
		errno = offerwowErrno03
		err = nil
		return
	} else if err != nil {
		return
	}

	// check if eventid duplicates
	_, err = getOfferwowEventByID(payload.EventID)
	switch err {
	case errors.ErrNotFound:
		err = nil
	case nil:
		errno = offerwowErrno04
		return
	default:
		return
	}

	if payload.Immediate == offerwowImmediate0 {
		errno = offerwowErrno05
		return
	}

	if payload.Immediate == offerwowImmediate3 {
		errno = offerwowErrno06
		return
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/brotherlogic/godiscogs"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/godiscogs"
)

import "github.com/golang/protobuf/proto"

import "log"

import "strconv"

import "time"

import pb "github.com/brotherlogic/discogssyncer/server"

// GetRelease Gets the release and metadata for the release
func (syncer *Syncer) GetRelease(id int, folder int) (*pbd.Release, *pb.ReleaseMetadata) {
	log.Printf("READING: %v", syncer.saveLocation+"/"+strconv.Itoa(folder)+"/"+strconv.Itoa(id)+".release")
	releaseData, err := ioutil.ReadFile(syncer.saveLocation + "/" + strconv.Itoa(folder) + "/" + strconv.Itoa(id) + ".release")
	if err != nil {
		return nil, nil
	}

	metadataData, _ := ioutil.ReadFile(syncer.saveLocation + "/static-metadata/" + strconv.Itoa(id) + ".metadata")
	release := &pbd.Release{}
	metadata := &pb.ReleaseMetadata{}

	proto.Unmarshal(releaseData, release)
	proto.Unmarshal(metadataData, metadata)

	log.Printf("Unmarshalled to %v", release)

	log.Printf("Reading %v from %v", metadataData, syncer.saveLocation+"/static-metadata/"+strconv.Itoa(id)+".metadata")

	return release, metadata
}

func (syncer *Syncer) saveMetadata(rel *godiscogs.Release) {
	metadataRoot := syncer.saveLocation + "/static-metadata/"
	metadataPath := metadataRoot + strconv.Itoa(int(rel.Id)) + ".metadata"
	if _, err := os.Stat(metadataRoot); os.IsNotExist(err) {
		os.MkdirAll(metadataRoot, 0777)
	}

	metadata := &pb.ReleaseMetadata{}
	if _, err := os.Stat(metadataPath); os.IsNotExist(err) {
		metadata.DateAdded = time.Now().Unix()
		metadata.DateRefreshed = time.Now().Unix()
	} else {
		data, _ := ioutil.ReadFile(metadataPath)
		proto.Unmarshal(data, metadata)
		metadata.DateRefreshed = time.Now().Unix()
	}
	log.Printf("Writing out %v", metadata)
	data, _ := proto.Marshal(metadata)
	ioutil.WriteFile(metadataPath, data, 0644)
}

func (syncer *Syncer) deleteRelease(rel *godiscogs.Release, folder int) {
	os.Remove(syncer.saveLocation + "/" + strconv.Itoa(folder) + "/" + strconv.Itoa(int(rel.Id)) + ".release")
}

func (syncer *Syncer) saveRelease(rel *godiscogs.Release, folder int) {
	//Check that the save folder exists
	savePath := syncer.saveLocation + "/" + strconv.Itoa(folder) + "/"
	if _, err := os.Stat(savePath); os.IsNotExist(err) {
		os.MkdirAll(savePath, 0777)
	}

	log.Printf("Saving %v to %v", rel, savePath+strconv.Itoa(int(rel.Id))+".release")
	data, _ := proto.Marshal(rel)
	ioutil.WriteFile(savePath+strconv.Itoa(int(rel.Id))+".release", data, 0644)
	syncer.saveMetadata(rel)
}

type saver interface {
	GetCollection() []godiscogs.Release
	GetFolders() []godiscogs.Folder
	GetRelease(id int) (godiscogs.Release, error)
	MoveToFolder(folderID int, releaseID int, instanceID int, newFolderID int)
	AddToFolder(folderID int, releaseID int)
	SetRating(folderID int, releaseID int, instanceID int, rating int)
}

// SaveCollection writes out the full collection to files.
func (syncer *Syncer) SaveCollection(retr saver) {
	releases := retr.GetCollection()
	for _, release := range releases {
		fullRelease, _ := retr.GetRelease(int(release.Id))
		fullRelease.InstanceId = release.InstanceId
		fullRelease.FolderId = release.FolderId
		fullRelease.Rating = release.Rating
		syncer.saveRelease(&fullRelease, int(release.FolderId))
	}
	folders := retr.GetFolders()
	folderList := pb.FolderList{}
	for i := range folders {
		folder := folders[i]
		folderList.Folders = append(folderList.Folders, &folder)
	}
	syncer.SaveFolders(&folderList)
}

func (syncer *Syncer) getFolders() *pb.FolderList {
	data, _ := ioutil.ReadFile(syncer.saveLocation + "/metadata/folders")
	folderData := &pb.FolderList{}
	proto.Unmarshal(data, folderData)
	return folderData
}

// GetSingleRelease gets a single release
func (syncer *Syncer) GetSingleRelease(ctx context.Context, in *pbd.Release) (*pbd.Release, error) {
	if val, ok := syncer.relMap[in.Id]; ok {
		return val, nil
	}
	return nil, errors.New("Unable to find release")
}

// MoveToFolder moves a release to the specified folder
func (syncer *Syncer) MoveToFolder(ctx context.Context, in *pb.ReleaseMove) (*pb.Empty, error) {
	syncer.retr.MoveToFolder(int(in.Release.FolderId), int(in.Release.Id), int(in.Release.InstanceId), int(in.NewFolderId))
	oldFolder := int(in.Release.FolderId)
	fullRelease, _ := syncer.retr.GetRelease(int(in.Release.Id))
	fullRelease.FolderId = int32(in.NewFolderId)
	syncer.relMap[fullRelease.Id] = &fullRelease
	log.Printf("WHAT = %v", syncer)
	syncer.Log(fmt.Sprintf("Moving %v from %v to %v", in.Release.Id, in.Release.FolderId, in.NewFolderId))
	syncer.saveRelease(&fullRelease, int(in.NewFolderId))
	syncer.deleteRelease(&fullRelease, oldFolder)
	return &pb.Empty{}, nil
}

// AddToFolder adds a release to the specified folder
func (syncer *Syncer) AddToFolder(ctx context.Context, in *pb.ReleaseMove) (*pb.Empty, error) {
	log.Printf("Adding releases %v", in)
	syncer.retr.AddToFolder(int(in.NewFolderId), int(in.Release.Id))
	log.Printf("BLAH = %v", syncer.relMap)
	fullRelease, _ := syncer.retr.GetRelease(int(in.Release.Id))
	fullRelease.FolderId = int32(in.NewFolderId)
	syncer.saveRelease(&fullRelease, int(in.NewFolderId))
	syncer.relMap[in.Release.Id] = &fullRelease
	log.Printf("NOW = %v", syncer.relMap)
	return &pb.Empty{}, nil
}

// UpdateRating updates the rating of a release
func (syncer *Syncer) UpdateRating(ctx context.Context, in *pbd.Release) (*pb.Empty, error) {
	log.Printf("Updating rating %v", in)
	syncer.retr.SetRating(int(in.FolderId), int(in.Id), int(in.InstanceId), int(in.Rating))
	fullRelease, _ := syncer.GetRelease(int(in.Id), int(in.FolderId))
	fullRelease.Rating = int32(in.Rating)
	log.Printf("SAVING %v", fullRelease)
	syncer.saveRelease(fullRelease, int(fullRelease.FolderId))
	return &pb.Empty{}, nil
}

// UpdateMetadata updates the metadata of a given record
func (syncer *Syncer) UpdateMetadata(ctx context.Context, in *pb.MetadataUpdate) (*pb.ReleaseMetadata, error) {
	release, metadata := syncer.GetRelease(int(in.Release.Id), int(in.Release.FolderId))
	if release == nil {
		return nil, errors.New("Unable to locate release")
	}
	proto.Merge(metadata, in.Update)

	metadataRoot := syncer.saveLocation + "/static-metadata/"
	metadataPath := metadataRoot + strconv.Itoa(int(in.Release.Id)) + ".metadata"
	data, _ := proto.Marshal(metadata)
	ioutil.WriteFile(metadataPath, data, 0644)

	return metadata, nil
}

// GetMetadata gets the metadata for a given release
func (syncer *Syncer) GetMetadata(ctx context.Context, in *pbd.Release) (*pb.ReleaseMetadata, error) {
	_, metadata := syncer.GetRelease(int(in.Id), int(in.FolderId))
	return metadata, nil
}

// GetReleasesInFolder serves up the releases in a given folder
func (syncer *Syncer) GetReleasesInFolder(ctx context.Context, in *pb.FolderList) (*pb.ReleaseList, error) {

	releases := pb.ReleaseList{}
	for _, folderSpec := range in.Folders {
		folders := syncer.getFolders()
		for _, folder := range folders.Folders {
			if folder.Name == folderSpec.Name || folder.Id == folderSpec.Id {
				innerReleases := syncer.getReleases(int(folder.Id))
				releases.Releases = append(releases.Releases, innerReleases.Releases...)
			}
		}
	}

	return &releases, nil
}

func (syncer *Syncer) getReleases(folderID int) *pb.ReleaseList {
	releases := pb.ReleaseList{}
	files, _ := ioutil.ReadDir(syncer.saveLocation + "/" + strconv.Itoa(folderID) + "/")
	for _, file := range files {
		if strings.HasSuffix(file.Name(), ".release") {
			data, _ := ioutil.ReadFile(syncer.saveLocation + "/" + strconv.Itoa(folderID) + "/" + file.Name())
			release := &pbd.Release{}
			proto.Unmarshal(data, release)
			releases.Releases = append(releases.Releases, release)
		}
	}
	return &releases
}

// SaveFolders saves out the list of folders
func (syncer *Syncer) SaveFolders(list *pb.FolderList) {
	savePath := syncer.saveLocation + "/metadata/"
	if _, err := os.Stat(savePath); os.IsNotExist(err) {
		os.MkdirAll(savePath, 0777)
	}

	data, _ := proto.Marshal(list)
	ioutil.WriteFile(savePath+"folders", data, 0644)
}

// GetCollection serves up the whole of the collection
func (syncer *Syncer) GetCollection(ctx context.Context, in *pb.Empty) (*pb.ReleaseList, error) {
	releases := pb.ReleaseList{}
	bfiles, _ := ioutil.ReadDir(syncer.saveLocation)
	for _, bfile := range bfiles {
		if bfile.IsDir() {
			log.Printf("Searching %v", bfile)
			folderID, _ := strconv.Atoi(bfile.Name())
			for _, release := range syncer.getReleases(folderID).Releases {
				releases.Releases = append(releases.Releases, release)
			}
		}
	}
	return &releases, nil
}
<commit_msg>Re adding map on rating update<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	"github.com/brotherlogic/godiscogs"
	"golang.org/x/net/context"

	pbd "github.com/brotherlogic/godiscogs"
)

import "github.com/golang/protobuf/proto"

import "log"

import "strconv"

import "time"

import pb "github.com/brotherlogic/discogssyncer/server"

// GetRelease Gets the release and metadata for the release
func (syncer *Syncer) GetRelease(id int, folder int) (*pbd.Release, *pb.ReleaseMetadata) {
	log.Printf("READING: %v", syncer.saveLocation+"/"+strconv.Itoa(folder)+"/"+strconv.Itoa(id)+".release")
	releaseData, err := ioutil.ReadFile(syncer.saveLocation + "/" + strconv.Itoa(folder) + "/" + strconv.Itoa(id) + ".release")
	if err != nil {
		return nil, nil
	}

	metadataData, _ := ioutil.ReadFile(syncer.saveLocation + "/static-metadata/" + strconv.Itoa(id) + ".metadata")
	release := &pbd.Release{}
	metadata := &pb.ReleaseMetadata{}

	proto.Unmarshal(releaseData, release)
	proto.Unmarshal(metadataData, metadata)

	log.Printf("Unmarshalled to %v", release)

	log.Printf("Reading %v from %v", metadataData, syncer.saveLocation+"/static-metadata/"+strconv.Itoa(id)+".metadata")

	return release, metadata
}

func (syncer *Syncer) saveMetadata(rel *godiscogs.Release) {
	metadataRoot := syncer.saveLocation + "/static-metadata/"
	metadataPath := metadataRoot + strconv.Itoa(int(rel.Id)) + ".metadata"
	if _, err := os.Stat(metadataRoot); os.IsNotExist(err) {
		os.MkdirAll(metadataRoot, 0777)
	}

	metadata := &pb.ReleaseMetadata{}
	if _, err := os.Stat(metadataPath); os.IsNotExist(err) {
		metadata.DateAdded = time.Now().Unix()
		metadata.DateRefreshed = time.Now().Unix()
	} else {
		data, _ := ioutil.ReadFile(metadataPath)
		proto.Unmarshal(data, metadata)
		metadata.DateRefreshed = time.Now().Unix()
	}
	log.Printf("Writing out %v", metadata)
	data, _ := proto.Marshal(metadata)
	ioutil.WriteFile(metadataPath, data, 0644)
}

func (syncer *Syncer) deleteRelease(rel *godiscogs.Release, folder int) {
	os.Remove(syncer.saveLocation + "/" + strconv.Itoa(folder) + "/" + strconv.Itoa(int(rel.Id)) + ".release")
}

func (syncer *Syncer) saveRelease(rel *godiscogs.Release, folder int) {
	//Check that the save folder exists
	savePath := syncer.saveLocation + "/" + strconv.Itoa(folder) + "/"
	if _, err := os.Stat(savePath); os.IsNotExist(err) {
		os.MkdirAll(savePath, 0777)
	}

	log.Printf("Saving %v to %v", rel, savePath+strconv.Itoa(int(rel.Id))+".release")
	data, _ := proto.Marshal(rel)
	ioutil.WriteFile(savePath+strconv.Itoa(int(rel.Id))+".release", data, 0644)
	syncer.saveMetadata(rel)
}

type saver interface {
	GetCollection() []godiscogs.Release
	GetFolders() []godiscogs.Folder
	GetRelease(id int) (godiscogs.Release, error)
	MoveToFolder(folderID int, releaseID int, instanceID int, newFolderID int)
	AddToFolder(folderID int, releaseID int)
	SetRating(folderID int, releaseID int, instanceID int, rating int)
}

// SaveCollection writes out the full collection to files.
func (syncer *Syncer) SaveCollection(retr saver) {
	releases := retr.GetCollection()
	for _, release := range releases {
		fullRelease, _ := retr.GetRelease(int(release.Id))
		fullRelease.InstanceId = release.InstanceId
		fullRelease.FolderId = release.FolderId
		fullRelease.Rating = release.Rating
		syncer.saveRelease(&fullRelease, int(release.FolderId))
	}
	folders := retr.GetFolders()
	folderList := pb.FolderList{}
	for i := range folders {
		folder := folders[i]
		folderList.Folders = append(folderList.Folders, &folder)
	}
	syncer.SaveFolders(&folderList)
}

func (syncer *Syncer) getFolders() *pb.FolderList {
	data, _ := ioutil.ReadFile(syncer.saveLocation + "/metadata/folders")
	folderData := &pb.FolderList{}
	proto.Unmarshal(data, folderData)
	return folderData
}

// GetSingleRelease gets a single release
func (syncer *Syncer) GetSingleRelease(ctx context.Context, in *pbd.Release) (*pbd.Release, error) {
	if val, ok := syncer.relMap[in.Id]; ok {
		return val, nil
	}
	return nil, errors.New("Unable to find release")
}

// MoveToFolder moves a release to the specified folder
func (syncer *Syncer) MoveToFolder(ctx context.Context, in *pb.ReleaseMove) (*pb.Empty, error) {
	syncer.retr.MoveToFolder(int(in.Release.FolderId), int(in.Release.Id), int(in.Release.InstanceId), int(in.NewFolderId))
	oldFolder := int(in.Release.FolderId)
	fullRelease, _ := syncer.retr.GetRelease(int(in.Release.Id))
	fullRelease.FolderId = int32(in.NewFolderId)
	syncer.relMap[fullRelease.Id] = &fullRelease
	log.Printf("WHAT = %v", syncer)
	syncer.Log(fmt.Sprintf("Moving %v from %v to %v", in.Release.Id, in.Release.FolderId, in.NewFolderId))
	syncer.saveRelease(&fullRelease, int(in.NewFolderId))
	syncer.deleteRelease(&fullRelease, oldFolder)
	return &pb.Empty{}, nil
}

// AddToFolder adds a release to the specified folder
func (syncer *Syncer) AddToFolder(ctx context.Context, in *pb.ReleaseMove) (*pb.Empty, error) {
	log.Printf("Adding releases %v", in)
	syncer.retr.AddToFolder(int(in.NewFolderId), int(in.Release.Id))
	log.Printf("BLAH = %v", syncer.relMap)
	fullRelease, _ := syncer.retr.GetRelease(int(in.Release.Id))
	fullRelease.FolderId = int32(in.NewFolderId)
	syncer.saveRelease(&fullRelease, int(in.NewFolderId))
	syncer.relMap[in.Release.Id] = &fullRelease
	log.Printf("NOW = %v", syncer.relMap)
	return &pb.Empty{}, nil
}

// UpdateRating updates the rating of a release
func (syncer *Syncer) UpdateRating(ctx context.Context, in *pbd.Release) (*pb.Empty, error) {
	log.Printf("Updating rating %v", in)
	syncer.retr.SetRating(int(in.FolderId), int(in.Id), int(in.InstanceId), int(in.Rating))
	fullRelease, _ := syncer.GetRelease(int(in.Id), int(in.FolderId))
	fullRelease.Rating = int32(in.Rating)
	log.Printf("SAVING %v", fullRelease)
	syncer.relMap[in.Id] = fullRelease
	syncer.saveRelease(fullRelease, int(fullRelease.FolderId))
	return &pb.Empty{}, nil
}

// UpdateMetadata updates the metadata of a given record
func (syncer *Syncer) UpdateMetadata(ctx context.Context, in *pb.MetadataUpdate) (*pb.ReleaseMetadata, error) {
	release, metadata := syncer.GetRelease(int(in.Release.Id), int(in.Release.FolderId))
	if release == nil {
		return nil, errors.New("Unable to locate release")
	}
	proto.Merge(metadata, in.Update)

	metadataRoot := syncer.saveLocation + "/static-metadata/"
	metadataPath := metadataRoot + strconv.Itoa(int(in.Release.Id)) + ".metadata"
	data, _ := proto.Marshal(metadata)
	ioutil.WriteFile(metadataPath, data, 0644)

	return metadata, nil
}

// GetMetadata gets the metadata for a given release
func (syncer *Syncer) GetMetadata(ctx context.Context, in *pbd.Release) (*pb.ReleaseMetadata, error) {
	_, metadata := syncer.GetRelease(int(in.Id), int(in.FolderId))
	return metadata, nil
}

// GetReleasesInFolder serves up the releases in a given folder
func (syncer *Syncer) GetReleasesInFolder(ctx context.Context, in *pb.FolderList) (*pb.ReleaseList, error) {

	releases := pb.ReleaseList{}
	for _, folderSpec := range in.Folders {
		folders := syncer.getFolders()
		for _, folder := range folders.Folders {
			if folder.Name == folderSpec.Name || folder.Id == folderSpec.Id {
				innerReleases := syncer.getReleases(int(folder.Id))
				releases.Releases = append(releases.Releases, innerReleases.Releases...)
			}
		}
	}

	return &releases, nil
}

func (syncer *Syncer) getReleases(folderID int) *pb.ReleaseList {
	releases := pb.ReleaseList{}
	files, _ := ioutil.ReadDir(syncer.saveLocation + "/" + strconv.Itoa(folderID) + "/")
	for _, file := range files {
		if strings.HasSuffix(file.Name(), ".release") {
			data, _ := ioutil.ReadFile(syncer.saveLocation + "/" + strconv.Itoa(folderID) + "/" + file.Name())
			release := &pbd.Release{}
			proto.Unmarshal(data, release)
			releases.Releases = append(releases.Releases, release)
		}
	}
	return &releases
}

// SaveFolders saves out the list of folders
func (syncer *Syncer) SaveFolders(list *pb.FolderList) {
	savePath := syncer.saveLocation + "/metadata/"
	if _, err := os.Stat(savePath); os.IsNotExist(err) {
		os.MkdirAll(savePath, 0777)
	}

	data, _ := proto.Marshal(list)
	ioutil.WriteFile(savePath+"folders", data, 0644)
}

// GetCollection serves up the whole of the collection
func (syncer *Syncer) GetCollection(ctx context.Context, in *pb.Empty) (*pb.ReleaseList, error) {
	releases := pb.ReleaseList{}
	bfiles, _ := ioutil.ReadDir(syncer.saveLocation)
	for _, bfile := range bfiles {
		if bfile.IsDir() {
			log.Printf("Searching %v", bfile)
			folderID, _ := strconv.Atoi(bfile.Name())
			for _, release := range syncer.getReleases(folderID).Releases {
				releases.Releases = append(releases.Releases, release)
			}
		}
	}
	return &releases, nil
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type Post struct {
	Page
	Date time.Time
}

func LoadPost(basedir, filename, outNameTemplate string) (p *Post, err error) {
	page, err := LoadPage(basedir, filename)
	if err != nil {
		return
	}
	// Extract date from:
	// 	/path/to/2006-01-02-postname.html
	basefile := path.Base(filename)
	// Remove extensions.
	basefile = basefile[:len(basefile)-len(path.Ext(basefile))]
	if len(basefile) < len("2006-01-02-") {
		err = fmt.Errorf("wrong post filename format %q", basefile)
		return
	}
	date, err := time.Parse("2006-01-02", basefile[0:len("2006-01-02")])
	if err != nil {
		return
	}
	// Now try getting date from meta.
	if md, ok := page.Meta["date"]; ok {
		switch d := md.(type) {
		case string:
			date, err = parseAnyDate(d)
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("'date' is not string")
		}
	}

	// Fill out name template.
	replacements := map[string]string{
		":year":  basefile[0:4],
		":month": basefile[5:7],
		":day":   basefile[8:10],
		":name":  basefile[11:],
	}
	outname := outNameTemplate
	for k, v := range replacements {
		outname = strings.Replace(outname, k, v, -1)
	}

	url := cleanPermalink(outname)
	// Add properies to meta
	page.Meta["date"] = date
	page.Meta["url"] = url
	page.Meta["id"] = basefile

	// Add index.html if ends with slash.
	if outname[len(outname)-1] == '/' {
		outname += "index.html"
	}
	page.Filename = filepath.FromSlash(outname)
	page.URL = url
	return &Post{
		Page: *page,
		Date: date,
	}, nil
}

type Posts []*Post

func (pp Posts) Limit(n int) Posts {
	if n > len(pp) {
		n = len(pp)
	}
	return pp[:n]
}

func (pp Posts) From(n int) Posts {
	return pp[n:]
}

func (pp Posts) Len() int           { return len(pp) }
func (pp Posts) Less(i, j int) bool { return pp[i].Date.Before(pp[j].Date) }
func (pp Posts) Swap(i, j int)      { pp[i], pp[j] = pp[j], pp[i] }

func (pp Posts) Sort() {
	sort.Sort(pp)
}
<commit_msg>Posts should use reverse sort order<commit_after>package main

import (
	"errors"
	"fmt"
	"path"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

type Post struct {
	Page
	Date time.Time
}

func LoadPost(basedir, filename, outNameTemplate string) (p *Post, err error) {
	page, err := LoadPage(basedir, filename)
	if err != nil {
		return
	}
	// Extract date from:
	// 	/path/to/2006-01-02-postname.html
	basefile := path.Base(filename)
	// Remove extensions.
	basefile = basefile[:len(basefile)-len(path.Ext(basefile))]
	if len(basefile) < len("2006-01-02-") {
		err = fmt.Errorf("wrong post filename format %q", basefile)
		return
	}
	date, err := time.Parse("2006-01-02", basefile[0:len("2006-01-02")])
	if err != nil {
		return
	}
	// Now try getting date from meta.
	if md, ok := page.Meta["date"]; ok {
		switch d := md.(type) {
		case string:
			date, err = parseAnyDate(d)
			if err != nil {
				return nil, err
			}
		default:
			return nil, errors.New("'date' is not string")
		}
	}

	// Fill out name template.
	replacements := map[string]string{
		":year":  basefile[0:4],
		":month": basefile[5:7],
		":day":   basefile[8:10],
		":name":  basefile[11:],
	}
	outname := outNameTemplate
	for k, v := range replacements {
		outname = strings.Replace(outname, k, v, -1)
	}

	url := cleanPermalink(outname)
	// Add properies to meta
	page.Meta["date"] = date
	page.Meta["url"] = url
	page.Meta["id"] = basefile

	// Add index.html if ends with slash.
	if outname[len(outname)-1] == '/' {
		outname += "index.html"
	}
	page.Filename = filepath.FromSlash(outname)
	page.URL = url
	return &Post{
		Page: *page,
		Date: date,
	}, nil
}

type Posts []*Post

func (pp Posts) Limit(n int) Posts {
	if n > len(pp) {
		n = len(pp)
	}
	return pp[:n]
}

func (pp Posts) From(n int) Posts {
	return pp[n:]
}

func (pp Posts) Len() int           { return len(pp) }
func (pp Posts) Less(i, j int) bool { return pp[i].Date.After(pp[j].Date) }
func (pp Posts) Swap(i, j int)      { pp[i], pp[j] = pp[j], pp[i] }

func (pp Posts) Sort() {
	sort.Sort(pp)
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package events

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"

	"github.com/onsi/ginkgo"
	"k8s.io/apimachinery/pkg/types"
)

// Action is a function to be performed by the system.
type Action func() error

var _ = ginkgo.Describe("[sig-api-machinery] Events", func() {
	f := framework.NewDefaultFramework("events")

	/*
		   Release : v1.19
		   Testname: Event resource lifecycle
		   Description: Create an event, the event MUST exist.
	           The event is patched with a new message, the check MUST have the update message.
	           The event is deleted and MUST NOT show up when listing all events.
	*/
	framework.ConformanceIt("should ensure that an event can be fetched, patched, deleted, and listed", func() {
		eventTestName := "event-test"

		ginkgo.By("creating a test event")
		// create a test event in test namespace
		_, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Create(context.TODO(), &v1.Event{
			ObjectMeta: metav1.ObjectMeta{
				Name: eventTestName,
				Labels: map[string]string{
					"testevent-constant": "true",
				},
			},
			Message: "This is a test event",
			Reason:  "Test",
			Type:    "Normal",
			Count:   1,
			InvolvedObject: v1.ObjectReference{
				Namespace: f.Namespace.Name,
			},
		}, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create test event")

		ginkgo.By("listing all events in all namespaces")
		// get a list of Events in all namespaces to ensure endpoint coverage
		eventsList, err := f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "testevent-constant=true",
		})
		framework.ExpectNoError(err, "failed list all events")

		foundCreatedEvent := false
		var eventCreatedName string
		for _, val := range eventsList.Items {
			if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
				foundCreatedEvent = true
				eventCreatedName = val.ObjectMeta.Name
				break
			}
		}
		framework.ExpectEqual(foundCreatedEvent, true, "unable to find the test event")

		ginkgo.By("patching the test event")
		// patch the event's message
		eventPatchMessage := "This is a test event - patched"
		eventPatch, err := json.Marshal(map[string]interface{}{
			"message": eventPatchMessage,
		})
		framework.ExpectNoError(err, "failed to marshal the patch JSON payload")

		_, err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Patch(context.TODO(), eventTestName, types.StrategicMergePatchType, []byte(eventPatch), metav1.PatchOptions{})
		framework.ExpectNoError(err, "failed to patch the test event")

		ginkgo.By("fetching the test event")
		// get event by name
		event, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Get(context.TODO(), eventCreatedName, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to fetch the test event")
		framework.ExpectEqual(event.Message, eventPatchMessage, "test event message does not match patch message")

		ginkgo.By("deleting the test event")
		// delete original event
		err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(context.TODO(), eventCreatedName, metav1.DeleteOptions{})
		framework.ExpectNoError(err, "failed to delete the test event")

		ginkgo.By("listing all events in all namespaces")
		// get a list of Events list namespace
		eventsList, err = f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "testevent-constant=true",
		})
		framework.ExpectNoError(err, "fail to list all events")
		foundCreatedEvent = false
		for _, val := range eventsList.Items {
			if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
				foundCreatedEvent = true
				break
			}
		}
		framework.ExpectEqual(foundCreatedEvent, false, "should not have found test event after deletion")
	})
})

// WaitTimeoutForEvent waits the given timeout duration for an event to occur.
func WaitTimeoutForEvent(c clientset.Interface, namespace, eventSelector, msg string, timeout time.Duration) error {
	interval := 2 * time.Second
	return wait.PollImmediate(interval, timeout, eventOccurred(c, namespace, eventSelector, msg))
}

func eventOccurred(c clientset.Interface, namespace, eventSelector, msg string) wait.ConditionFunc {
	options := metav1.ListOptions{FieldSelector: eventSelector}
	return func() (bool, error) {
		events, err := c.CoreV1().Events(namespace).List(context.TODO(), options)
		if err != nil {
			return false, fmt.Errorf("got error while getting events: %v", err)
		}
		for _, event := range events.Items {
			if strings.Contains(event.Message, msg) {
				return true, nil
			}
		}
		return false, nil
	}
}
<commit_msg>Fix formatting<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package events

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"time"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/kubernetes/test/e2e/framework"

	"github.com/onsi/ginkgo"
	"k8s.io/apimachinery/pkg/types"
)

// Action is a function to be performed by the system.
type Action func() error

var _ = ginkgo.Describe("[sig-api-machinery] Events", func() {
	f := framework.NewDefaultFramework("events")

	/*
			   Release : v1.19
			   Testname: Event resource lifecycle
			   Description: Create an event, the event MUST exist.
		           The event is patched with a new message, the check MUST have the update message.
		           The event is deleted and MUST NOT show up when listing all events.
	*/
	framework.ConformanceIt("should ensure that an event can be fetched, patched, deleted, and listed", func() {
		eventTestName := "event-test"

		ginkgo.By("creating a test event")
		// create a test event in test namespace
		_, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Create(context.TODO(), &v1.Event{
			ObjectMeta: metav1.ObjectMeta{
				Name: eventTestName,
				Labels: map[string]string{
					"testevent-constant": "true",
				},
			},
			Message: "This is a test event",
			Reason:  "Test",
			Type:    "Normal",
			Count:   1,
			InvolvedObject: v1.ObjectReference{
				Namespace: f.Namespace.Name,
			},
		}, metav1.CreateOptions{})
		framework.ExpectNoError(err, "failed to create test event")

		ginkgo.By("listing all events in all namespaces")
		// get a list of Events in all namespaces to ensure endpoint coverage
		eventsList, err := f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "testevent-constant=true",
		})
		framework.ExpectNoError(err, "failed list all events")

		foundCreatedEvent := false
		var eventCreatedName string
		for _, val := range eventsList.Items {
			if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
				foundCreatedEvent = true
				eventCreatedName = val.ObjectMeta.Name
				break
			}
		}
		framework.ExpectEqual(foundCreatedEvent, true, "unable to find the test event")

		ginkgo.By("patching the test event")
		// patch the event's message
		eventPatchMessage := "This is a test event - patched"
		eventPatch, err := json.Marshal(map[string]interface{}{
			"message": eventPatchMessage,
		})
		framework.ExpectNoError(err, "failed to marshal the patch JSON payload")

		_, err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Patch(context.TODO(), eventTestName, types.StrategicMergePatchType, []byte(eventPatch), metav1.PatchOptions{})
		framework.ExpectNoError(err, "failed to patch the test event")

		ginkgo.By("fetching the test event")
		// get event by name
		event, err := f.ClientSet.CoreV1().Events(f.Namespace.Name).Get(context.TODO(), eventCreatedName, metav1.GetOptions{})
		framework.ExpectNoError(err, "failed to fetch the test event")
		framework.ExpectEqual(event.Message, eventPatchMessage, "test event message does not match patch message")

		ginkgo.By("deleting the test event")
		// delete original event
		err = f.ClientSet.CoreV1().Events(f.Namespace.Name).Delete(context.TODO(), eventCreatedName, metav1.DeleteOptions{})
		framework.ExpectNoError(err, "failed to delete the test event")

		ginkgo.By("listing all events in all namespaces")
		// get a list of Events list namespace
		eventsList, err = f.ClientSet.CoreV1().Events("").List(context.TODO(), metav1.ListOptions{
			LabelSelector: "testevent-constant=true",
		})
		framework.ExpectNoError(err, "fail to list all events")
		foundCreatedEvent = false
		for _, val := range eventsList.Items {
			if val.ObjectMeta.Name == eventTestName && val.ObjectMeta.Namespace == f.Namespace.Name {
				foundCreatedEvent = true
				break
			}
		}
		framework.ExpectEqual(foundCreatedEvent, false, "should not have found test event after deletion")
	})
})

// WaitTimeoutForEvent waits the given timeout duration for an event to occur.
func WaitTimeoutForEvent(c clientset.Interface, namespace, eventSelector, msg string, timeout time.Duration) error {
	interval := 2 * time.Second
	return wait.PollImmediate(interval, timeout, eventOccurred(c, namespace, eventSelector, msg))
}

func eventOccurred(c clientset.Interface, namespace, eventSelector, msg string) wait.ConditionFunc {
	options := metav1.ListOptions{FieldSelector: eventSelector}
	return func() (bool, error) {
		events, err := c.CoreV1().Events(namespace).List(context.TODO(), options)
		if err != nil {
			return false, fmt.Errorf("got error while getting events: %v", err)
		}
		for _, event := range events.Items {
			if strings.Contains(event.Message, msg) {
				return true, nil
			}
		}
		return false, nil
	}
}
<|endoftext|>


<commit_before>package task

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/go-task/task/execext"

	"golang.org/x/sync/errgroup"
)

const (
	// TaskFilePath is the default Taskfile
	TaskFilePath = "Taskfile"
)

// Executor executes a Taskfile
type Executor struct {
	Tasks Tasks
	Force bool
	Watch bool

	watchingFiles map[string]struct{}
}

// Tasks representas a group of tasks
type Tasks map[string]*Task

// Task represents a task
type Task struct {
	Cmds      []string
	Deps      []string
	Desc      string
	Sources   []string
	Generates []string
	Status    []string
	Dir       string
	Vars      map[string]string
	Set       string
	Env       map[string]string
}

// Run runs Task
func (e *Executor) Run(args ...string) error {
	if e.HasCyclicDep() {
		return ErrCyclicDependencyDetected
	}

	// check if given tasks exist
	for _, a := range args {
		if _, ok := e.Tasks[a]; !ok {
			// FIXME: move to the main package
			e.printExistingTasksHelp()
			return &taskNotFoundError{taskName: a}
		}
	}

	if e.Watch {
		if err := e.watchTasks(args...); err != nil {
			return err
		}
		return nil
	}

	for _, a := range args {
		if err := e.RunTask(context.Background(), a); err != nil {
			return err
		}
	}
	return nil
}

// RunTask runs a task by its name
func (e *Executor) RunTask(ctx context.Context, name string) error {
	t, ok := e.Tasks[name]
	if !ok {
		return &taskNotFoundError{name}
	}

	if err := e.runDeps(ctx, name); err != nil {
		return err
	}

	if !e.Force {
		upToDate, err := t.isUpToDate(ctx)
		if err != nil {
			return err
		}
		if upToDate {
			log.Printf(`task: Task "%s" is up to date`, name)
			return nil
		}
	}

	for i := range t.Cmds {
		if err := e.runCommand(ctx, name, i); err != nil {
			return &taskRunError{name, err}
		}
	}
	return nil
}

func (e *Executor) runDeps(ctx context.Context, task string) error {
	g, ctx := errgroup.WithContext(ctx)
	t := e.Tasks[task]

	for _, d := range t.Deps {
		dep := d

		g.Go(func() error {
			dep, err := t.ReplaceVariables(dep)
			if err != nil {
				return err
			}

			if err = e.RunTask(ctx, dep); err != nil {
				return err
			}
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		return err
	}
	return nil
}

func (t *Task) isUpToDate(ctx context.Context) (bool, error) {
	if len(t.Status) > 0 {
		environ, err := t.getEnviron()
		if err != nil {
			return false, err
		}

		for _, s := range t.Status {
			err = execext.RunCommand(&execext.RunCommandOptions{
				Context: ctx,
				Command: s,
				Dir:     t.Dir,
				Env:     environ,
			})
			if err != nil {
				return false, nil
			}
		}
		return true, nil
	}

	if len(t.Sources) == 0 || len(t.Generates) == 0 {
		return false, nil
	}

	sources, err := t.ReplaceSliceVariables(t.Sources)
	if err != nil {
		return false, err
	}
	generates, err := t.ReplaceSliceVariables(t.Generates)
	if err != nil {
		return false, err
	}

	sourcesMaxTime, err := getPatternsMaxTime(sources)
	if err != nil || sourcesMaxTime.IsZero() {
		return false, nil
	}

	generatesMinTime, err := getPatternsMinTime(generates)
	if err != nil || generatesMinTime.IsZero() {
		return false, nil
	}

	return generatesMinTime.After(sourcesMaxTime), nil
}

func (e *Executor) runCommand(ctx context.Context, task string, i int) error {
	t := e.Tasks[task]

	c, err := t.ReplaceVariables(t.Cmds[i])
	if err != nil {
		return err
	}

	if strings.HasPrefix(c, "^") {
		c = strings.TrimPrefix(c, "^")
		if err = e.RunTask(ctx, c); err != nil {
			return err
		}
		return nil
	}

	dir, err := t.ReplaceVariables(t.Dir)
	if err != nil {
		return err
	}

	envs, err := t.getEnviron()
	if err != nil {
		return err
	}
	opts := &execext.RunCommandOptions{
		Context: ctx,
		Command: c,
		Dir:     dir,
		Env:     envs,
		Stdin:   os.Stdin,
		Stderr:  os.Stderr,
	}

	if t.Set == "" {
		log.Println(c)
		opts.Stdout = os.Stdout
		if err = execext.RunCommand(opts); err != nil {
			return err
		}
	} else {
		buff := bytes.NewBuffer(nil)
		opts.Stdout = buff
		if err = execext.RunCommand(opts); err != nil {
			return err
		}
		os.Setenv(t.Set, strings.TrimSpace(buff.String()))
	}
	return nil
}

func (t *Task) getEnviron() ([]string, error) {
	if t.Env == nil {
		return nil, nil
	}

	envs := os.Environ()

	for k, v := range t.Env {
		env, err := t.ReplaceVariables(fmt.Sprintf("%s=%s", k, v))
		if err != nil {
			return nil, err
		}
		envs = append(envs, env)
	}
	return envs, nil
}
<commit_msg>refactor isUpToDate()<commit_after>package task

import (
	"bytes"
	"context"
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/go-task/task/execext"

	"golang.org/x/sync/errgroup"
)

const (
	// TaskFilePath is the default Taskfile
	TaskFilePath = "Taskfile"
)

// Executor executes a Taskfile
type Executor struct {
	Tasks Tasks
	Force bool
	Watch bool

	watchingFiles map[string]struct{}
}

// Tasks representas a group of tasks
type Tasks map[string]*Task

// Task represents a task
type Task struct {
	Cmds      []string
	Deps      []string
	Desc      string
	Sources   []string
	Generates []string
	Status    []string
	Dir       string
	Vars      map[string]string
	Set       string
	Env       map[string]string
}

// Run runs Task
func (e *Executor) Run(args ...string) error {
	if e.HasCyclicDep() {
		return ErrCyclicDependencyDetected
	}

	// check if given tasks exist
	for _, a := range args {
		if _, ok := e.Tasks[a]; !ok {
			// FIXME: move to the main package
			e.printExistingTasksHelp()
			return &taskNotFoundError{taskName: a}
		}
	}

	if e.Watch {
		if err := e.watchTasks(args...); err != nil {
			return err
		}
		return nil
	}

	for _, a := range args {
		if err := e.RunTask(context.Background(), a); err != nil {
			return err
		}
	}
	return nil
}

// RunTask runs a task by its name
func (e *Executor) RunTask(ctx context.Context, name string) error {
	t, ok := e.Tasks[name]
	if !ok {
		return &taskNotFoundError{name}
	}

	if err := e.runDeps(ctx, name); err != nil {
		return err
	}

	if !e.Force {
		upToDate, err := t.isUpToDate(ctx)
		if err != nil {
			return err
		}
		if upToDate {
			log.Printf(`task: Task "%s" is up to date`, name)
			return nil
		}
	}

	for i := range t.Cmds {
		if err := e.runCommand(ctx, name, i); err != nil {
			return &taskRunError{name, err}
		}
	}
	return nil
}

func (e *Executor) runDeps(ctx context.Context, task string) error {
	g, ctx := errgroup.WithContext(ctx)
	t := e.Tasks[task]

	for _, d := range t.Deps {
		dep := d

		g.Go(func() error {
			dep, err := t.ReplaceVariables(dep)
			if err != nil {
				return err
			}

			if err = e.RunTask(ctx, dep); err != nil {
				return err
			}
			return nil
		})
	}

	if err := g.Wait(); err != nil {
		return err
	}
	return nil
}

func (t *Task) isUpToDate(ctx context.Context) (bool, error) {
	if len(t.Status) > 0 {
		return t.isUpToDateStatus(ctx)
	}
	return t.isUpToDateTimestamp(ctx)
}

func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) {
	environ, err := t.getEnviron()
	if err != nil {
		return false, err
	}

	for _, s := range t.Status {
		err = execext.RunCommand(&execext.RunCommandOptions{
			Context: ctx,
			Command: s,
			Dir:     t.Dir,
			Env:     environ,
		})
		if err != nil {
			return false, nil
		}
	}
	return true, nil
}

func (t *Task) isUpToDateTimestamp(ctx context.Context) (bool, error) {
	if len(t.Sources) == 0 || len(t.Generates) == 0 {
		return false, nil
	}

	sources, err := t.ReplaceSliceVariables(t.Sources)
	if err != nil {
		return false, err
	}
	generates, err := t.ReplaceSliceVariables(t.Generates)
	if err != nil {
		return false, err
	}

	sourcesMaxTime, err := getPatternsMaxTime(sources)
	if err != nil || sourcesMaxTime.IsZero() {
		return false, nil
	}

	generatesMinTime, err := getPatternsMinTime(generates)
	if err != nil || generatesMinTime.IsZero() {
		return false, nil
	}

	return generatesMinTime.After(sourcesMaxTime), nil
}

func (e *Executor) runCommand(ctx context.Context, task string, i int) error {
	t := e.Tasks[task]

	c, err := t.ReplaceVariables(t.Cmds[i])
	if err != nil {
		return err
	}

	if strings.HasPrefix(c, "^") {
		c = strings.TrimPrefix(c, "^")
		if err = e.RunTask(ctx, c); err != nil {
			return err
		}
		return nil
	}

	dir, err := t.ReplaceVariables(t.Dir)
	if err != nil {
		return err
	}

	envs, err := t.getEnviron()
	if err != nil {
		return err
	}
	opts := &execext.RunCommandOptions{
		Context: ctx,
		Command: c,
		Dir:     dir,
		Env:     envs,
		Stdin:   os.Stdin,
		Stderr:  os.Stderr,
	}

	if t.Set == "" {
		log.Println(c)
		opts.Stdout = os.Stdout
		if err = execext.RunCommand(opts); err != nil {
			return err
		}
	} else {
		buff := bytes.NewBuffer(nil)
		opts.Stdout = buff
		if err = execext.RunCommand(opts); err != nil {
			return err
		}
		os.Setenv(t.Set, strings.TrimSpace(buff.String()))
	}
	return nil
}

func (t *Task) getEnviron() ([]string, error) {
	if t.Env == nil {
		return nil, nil
	}

	envs := os.Environ()

	for k, v := range t.Env {
		env, err := t.ReplaceVariables(fmt.Sprintf("%s=%s", k, v))
		if err != nil {
			return nil, err
		}
		envs = append(envs, env)
	}
	return envs, nil
}
<|endoftext|>