<commit_before>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"
)

type Source struct {
	frames          []DataFrame
	frameBytes      uint64
	gone            bool
	input           io.ReadCloser
	nextFrame       uint64 // How many frames have ever been here
	path            string
	sync.RWMutex    // Must be held while changing nextFrame or gone
	*sync.Cond      // Control access to frames other than nextFrame
	statBytesIn     uint64
	statBytesOut    uint64
	statLogInterval time.Duration
}

var sourceMap = make(map[string]*Source)
var sourceMapMutex = sync.RWMutex{}

func NewSource(path string, c Config) (s *Source) {
	s = &Source{}
	s.Cond = sync.NewCond(s.RLocker())
	s.frames = make([]DataFrame, c.SourceBuffer)
	for i := range s.frames {
		s.frames[i] = make(DataFrame, c.FrameBytes)
	}
	s.frameBytes = c.FrameBytes
	s.path = path
	s.statLogInterval = c.StatLogInterval
	return
}

// Read data from the given source into the buffer. If the source
// reaches EOF and cannot be reopened, remove the source from
// sourceMap and return.
func (s *Source) run() {
	var err error
	lastLog := time.Now()
	defer func() {
		if err != nil {
			log.Printf("Source %s error: %s", s.path, err)
		}
		s.Close()
	}()
	if s.input, err = os.Open(s.path); err != nil {
		log.Printf("Source %s error: %s", s.path, err)
		return
	}
	defer s.input.Close()
	for {
		bufPos := s.nextFrame % uint64(cap(s.frames))
		for framePos := uint64(0); framePos < s.frameBytes; {
			var got int
			if got, err = s.input.Read(s.frames[bufPos][framePos:]); err != nil {
				log.Printf("Source %s error: %s", s.path, err)
				return
			}
			framePos += uint64(got)
		}
		s.Lock()
		s.statBytesIn += s.frameBytes
		s.nextFrame += 1
		if s.statLogInterval > 0 && time.Since(lastLog) >= s.statLogInterval {
			log.Printf("Stats: %d in %d out", s.statBytesIn, s.statBytesOut)
			lastLog = time.Now()
		}
		s.Unlock()
		s.Cond.Broadcast()
		runtime.Gosched()
	}
	log.Printf("Input channel closed for %s", s.path)
	// TODO: Reopen
}

// Copy the next data frame into the given buffer and update the
// nextFrame pointer.
//
// Return the number of frames skipped due to buffer underrun. If the
// data source is exhausted, return with err != nil (with frame
// untouched and other return values undefined).
func (s *Source) Next(nextFrame *uint64, frame DataFrame) (nSkipped uint64, err error) {
	s.Cond.L.Lock()
	defer func() {
		s.Cond.L.Unlock()
		if err != nil {
			s.Lock()
			s.statBytesOut += s.frameBytes
			s.Unlock()
			*nextFrame += 1
			runtime.Gosched()
		}
	}()
	for *nextFrame >= s.nextFrame && !s.gone {
		// If we don't Unlock and GoSched here, performance goes awful.
		s.Cond.L.Unlock()
		runtime.Gosched()
		s.Cond.L.Lock()
		// Theoretically, this should be enough:
		s.Cond.Wait()
	}
	if *nextFrame >= s.nextFrame {
		err = errors.New("Read past end of stream")
		return
	}
	lag := s.nextFrame - *nextFrame
	if lag >= uint64(cap(s.frames)-1) {
		*nextFrame = s.nextFrame - 1
		nSkipped = lag
	}
	bufPos := *nextFrame%uint64(cap(s.frames))
	if cap(frame) < len(s.frames[bufPos]) {
		err = errors.New("Caller's frame buffer is too small.")
		return
	}
	copy(frame, s.frames[bufPos])
	return
}

// Remove the source from sourceMap.
func (s *Source) Close() {
	sourceMapMutex.Lock()
	delete(sourceMap, s.path)
	sourceMapMutex.Unlock()
	s.RWMutex.Lock()
	s.gone = true
	s.RWMutex.Unlock()
	s.Broadcast()
}

// Return a Source for the given path (URI) and config (argv). At any
// given time, there is at most one Source for a given path.
func GetSource(path string, c Config) *Source {
	sourceMapMutex.RLock()
	if src, ok := sourceMap[path]; ok {
		sourceMapMutex.RUnlock()
		return src
	}
	sourceMapMutex.RUnlock()
	sourceMapMutex.Lock()
	defer sourceMapMutex.Unlock()
	if src, ok := sourceMap[path]; ok {
		return src
	}
	src := NewSource(path, c)
	sourceMap[path] = src
	go src.run()
	return src
}
<commit_msg>Use a separate lock for updating stats.<commit_after>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"runtime"
	"sync"
	"time"
)

type Source struct {
	frames          []DataFrame
	frameBytes      uint64
	gone            bool
	input           io.ReadCloser
	nextFrame       uint64 // How many frames have ever been here
	path            string
	sync.RWMutex    // Must be held while changing nextFrame or gone
	*sync.Cond      // Control access to frames other than nextFrame
	statBytesIn     uint64
	statBytesOut    uint64
	statLogInterval time.Duration
	statLock        sync.Mutex
}

var sourceMap = make(map[string]*Source)
var sourceMapMutex = sync.RWMutex{}

func NewSource(path string, c Config) (s *Source) {
	s = &Source{}
	s.Cond = sync.NewCond(s.RLocker())
	s.frames = make([]DataFrame, c.SourceBuffer)
	for i := range s.frames {
		s.frames[i] = make(DataFrame, c.FrameBytes)
	}
	s.frameBytes = c.FrameBytes
	s.path = path
	s.statLogInterval = c.StatLogInterval
	return
}

// Read data from the given source into the buffer. If the source
// reaches EOF and cannot be reopened, remove the source from
// sourceMap and return.
func (s *Source) run() {
	var err error
	lastLog := time.Now()
	defer func() {
		if err != nil {
			log.Printf("Source %s error: %s", s.path, err)
		}
		s.Close()
	}()
	if s.input, err = os.Open(s.path); err != nil {
		log.Printf("Source %s error: %s", s.path, err)
		return
	}
	defer s.input.Close()
	for {
		bufPos := s.nextFrame % uint64(cap(s.frames))
		for framePos := uint64(0); framePos < s.frameBytes; {
			var got int
			if got, err = s.input.Read(s.frames[bufPos][framePos:]); err != nil {
				log.Printf("Source %s error: %s", s.path, err)
				return
			}
			framePos += uint64(got)
		}
		s.Lock()
		s.nextFrame += 1
		s.Unlock()
		s.statLock.Lock()
		s.statBytesIn += s.frameBytes
		if s.statLogInterval > 0 && time.Since(lastLog) >= s.statLogInterval {
			log.Printf("Stats: %d in %d out", s.statBytesIn, s.statBytesOut)
			lastLog = time.Now()
		}
		s.statLock.Unlock()
		s.Cond.Broadcast()
		runtime.Gosched()
	}
	log.Printf("Input channel closed for %s", s.path)
	// TODO: Reopen
}

// Copy the next data frame into the given buffer and update the
// nextFrame pointer.
//
// Return the number of frames skipped due to buffer underrun. If the
// data source is exhausted, return with err != nil (with frame
// untouched and other return values undefined).
func (s *Source) Next(nextFrame *uint64, frame DataFrame) (nSkipped uint64, err error) {
	s.Cond.L.Lock()
	defer func() {
		s.Cond.L.Unlock()
		if err != nil {
			s.statLock.Lock()
			s.statBytesOut += s.frameBytes
			s.statLock.Unlock()
			*nextFrame += 1
			runtime.Gosched()
		}
	}()
	for *nextFrame >= s.nextFrame && !s.gone {
		// If we don't Unlock and GoSched here, performance goes awful.
		s.Cond.L.Unlock()
		runtime.Gosched()
		s.Cond.L.Lock()
		// Theoretically, this should be enough:
		s.Cond.Wait()
	}
	if *nextFrame >= s.nextFrame {
		err = errors.New("Read past end of stream")
		return
	}
	lag := s.nextFrame - *nextFrame
	if lag >= uint64(cap(s.frames)-1) {
		*nextFrame = s.nextFrame - 1
		nSkipped = lag
	}
	bufPos := *nextFrame%uint64(cap(s.frames))
	if cap(frame) < len(s.frames[bufPos]) {
		err = errors.New("Caller's frame buffer is too small.")
		return
	}
	copy(frame, s.frames[bufPos])
	return
}

// Remove the source from sourceMap.
func (s *Source) Close() {
	sourceMapMutex.Lock()
	delete(sourceMap, s.path)
	sourceMapMutex.Unlock()
	s.RWMutex.Lock()
	s.gone = true
	s.RWMutex.Unlock()
	s.Broadcast()
}

// Return a Source for the given path (URI) and config (argv). At any
// given time, there is at most one Source for a given path.
func GetSource(path string, c Config) *Source {
	sourceMapMutex.RLock()
	if src, ok := sourceMap[path]; ok {
		sourceMapMutex.RUnlock()
		return src
	}
	sourceMapMutex.RUnlock()
	sourceMapMutex.Lock()
	defer sourceMapMutex.Unlock()
	if src, ok := sourceMap[path]; ok {
		return src
	}
	src := NewSource(path, c)
	sourceMap[path] = src
	go src.run()
	return src
}
<|endoftext|>


<commit_before>package processor

import (
	"archive/tar"
	"compress/gzip"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/gaia-docker/tugbot-collect/log"
	"golang.org/x/net/context"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
        "github.com/gaia-docker/tugbot-parse"
	"encoding/json"
	"bytes"
)

var logger = log.GetLogger("processor")

const resultsTarFile = "results.tar"

//Processor is a struct to hold the Tasks channel
//You should use NewProcessor to allocate new one and the Run func to run it
type Processor struct {
	Tasks              chan string
	dockerClient       *client.Client
	outputDir          string
	publishTarGzTo     string
	publishTestCasesTo string
	resultsDirLabel    string
	dockerRM           bool
}

type results struct {
	testResults         io.ReadCloser
	testResultsPathStat types.ContainerPathStat
	containerInfo       types.ContainerJSON
}

type tugbotData struct {
	ImageName   string
	ContainerId string
	StartedAt   string
	FinishedAt  string
	ExitCode    int
	HostName    string
}

//NewProcessor create new Processor and allocates Tasks buffered channel in size 10 to it
func NewProcessor(pDockerClient *client.Client, pOutputDir, pPublishTarGzTo, pPublishTestCasesTo, pResultsDirLabel string, pDockerRM bool) Processor {
	p := Processor{
		dockerClient:       pDockerClient,
		outputDir:          pOutputDir,
		publishTarGzTo:     pPublishTarGzTo,
		publishTestCasesTo: pPublishTestCasesTo,
		resultsDirLabel:    pResultsDirLabel,
		dockerRM:           pDockerRM,
	}
	p.Tasks = make(chan string, 10)
	return p
}

//Run the processor (listen to the Tasks channel and performs the collection)
func (p Processor) Run() {
	go func() {
		for task := range p.Tasks {

			//We run each task in a separate goroutine to parallel the extraction
			go func(contId string) {
				logger.Info("processor is going to extract results from container with id: ", contId)

				ctx, cancel := context.WithCancel(context.Background())
				defer cancel()

				contResults, err := p.collectResults(ctx, contId)
				if err != nil {
					logger.Error("failed to collect results for container id: ", contId)
					return
				}

				//write the results to disk
				outDirPath, err := writeToDisk(p.outputDir, contId, contResults)
				if err != nil {
					logger.Error("failed to write to disk")
					return
				}

				//publish tar.gz
				if outDirPath != "" {
					err = p.publishTarGz(outDirPath)
					if err != nil {
						logger.Error("failed to publish tar.gz")
						return
					}
				}

				//publish test cases
				if outDirPath != "" {
					err = p.publishTestCases(outDirPath, contId, contResults)
					if err != nil {
						logger.Error("failed to publish test cases")
						return
					}
				}

				//We get here only if no error occurred along the way
				if p.dockerRM {
					err = p.dockerClient.ContainerRemove(ctx, contId, types.ContainerRemoveOptions{})
					if err != nil {
						logger.Error("failed to remove container with id: ", contId, ", why: ", err)
						return
					}

					logger.Info("sucessfully removed (docker rm) container with id: ", contId)
				}

				logger.Info("sucessfully analyzed results for container with id: ", contId)
			}(task)
		}
	}()
}

func (p Processor) collectResults(ctx context.Context, contId string) (contResults *results, err error) {

	contResults = &results{}
	contResults.containerInfo, err = p.dockerClient.ContainerInspect(ctx, contId)
	if err != nil {
		logger.Error("failed to inspect container with id: ", contId, ", cannot determine results dir location - discard processing this container")
		return nil, err
	}

	//copy the result dir from inside the container into memory
	//The result dir will be return in tar format
	resultDir := contResults.containerInfo.Config.Labels[p.resultsDirLabel]
	logger.Info("going to extract results for container with id: ", contId, ", from this location inside the test container: ", resultDir)
	contResults.testResults, contResults.testResultsPathStat, err = p.dockerClient.CopyFromContainer(ctx, contId, resultDir)
	/*buf := new(bytes.Buffer)
	buf.ReadFrom(contResults.testResults)
	s := buf.String()
	logger.Info("tar for cont: ", contId, ", data: ", s) */
	if err != nil {
		logger.Error("failed to copy result dir from container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return nil, err
	}

	//check that the resultdir is really a directory
	if !contResults.testResultsPathStat.Mode.IsDir() {
		logger.Error("results location:", resultDir, ", inside the container with id: ", contId, ", is not a directory - discard processing of this container")
		return nil, err
	}

	return contResults, nil
}

func writeToDisk(outputDir, contId string, contResults *results) (outDirFullPath string, err error) {

	if outputDir == "/dev/null" {
		logger.Info("output dir is ", outputDir, ", skip writing to disk")
		return "", nil
	}

	//create output dir (if dir is already exist MkdirAll will return nil)
	outDirFullPath = filepath.Join(outputDir, strings.Replace(contResults.containerInfo.Config.Image, "/", "_", -1)+"-"+contId[:11])
	err = os.MkdirAll(outDirFullPath, 0777)
	if err != nil {
		logger.Error("failed to create output dir: ", outDirFullPath, ", for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}
	logger.Info("going to write results from container with id: ", contId, ", into this location: ", outDirFullPath)

	// open output file
	resultsTarFullPath := filepath.Join(outDirFullPath, resultsTarFile)
	fo, err := os.Create(resultsTarFullPath)
	if err != nil {
		logger.Error("failed to create tar file on FS for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			logger.Error("failed to close FS tar file for container with id: ", contId, ", error is: ", err)
		}
	}()

	_, err = io.Copy(fo, contResults.testResults)
	if err != nil {
		logger.Error("failed to write to tar file for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}

	if err := fo.Sync(); err != nil {
		logger.Error("failed to flash tar to disk for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}

	//All is good
	return outDirFullPath, nil
}

func (p Processor) publishTestCases(outDirFullPath string, contId string, contResults *results) (err error) {

	if p.publishTestCasesTo == "null" || p.publishTestCasesTo == "NULL" {
		logger.Info("publishTastCasesTo is ", p.publishTestCasesTo, ", skip sending json results")
		return nil
	}

	fullPath := filepath.Join(outDirFullPath, resultsTarFile)
	f, err := os.Open(fullPath)
	if err != nil {
		logger.Error("publishTestCases - failed to open tar file: ", err)
		return err
	}
	defer f.Close()

	tugbotDt := getTugbotData(contResults.containerInfo, contId)
	client := new(http.Client)
	tarReader := tar.NewReader(f)

	i := 0
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			logger.Error("publishTestCases - failed iterate tar file: ", err)
			return err
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			logger.Info("tar (", i, ")", "Name: ", name)
			if strings.HasSuffix(name, "xml") {

				if header.Size <= 0 {
					logger.Warn("cannot process: ", name, " file from the tar, invalid file size: ", header.Size, ". skipping this file")
					continue
				}

				buf := make([]byte, header.Size)
				_, err := tarReader.Read(buf)
				if err != nil {
					logger.Warn("failed to read: ", name, " file from the tar, err: ", err, ". skipping this file")
					continue
				}

				tests, err := parse.ToAnalyticsTests(buf)
				if err != nil {
					logger.Warn("failed to convert xml to tests slice for file: ", name, ", err: ", err, ". skipping this file")
					continue
				}

				for _, test := range tests {
					json, err := json.Marshal(struct {
						TugbotData   *tugbotData
						Test parse.AnalyticsTest

					}{
						TugbotData:  tugbotDt,
						Test: test,
					})

					if err != nil {
						logger.Warn("failed to convert specific AnalyticsTest to json for file: ", name, ", err: ", err, ". skipping this file")
						continue
					}

					logger.Debug("json for file: ", name, ", is: ", string(json))

					r := bytes.NewReader(json)
					request, err := http.NewRequest("POST", p.publishTestCasesTo + "?docker.imagename=" + contResults.containerInfo.Config.Image, r)
					request.Header.Add("Content-Type", "application/json")
					_, err = client.Do(request)
					if err != nil {
						logger.Error("error publish json: ", err)
						return err
					}
				}

			}

		default:
			logger.Infof("%s : %c %s %s\n",
				"Yikes! Unable to figure out type",
				header.Typeflag,
				"in file",
				name,
			)
		}

		i++
	}

	return nil
}

func (p Processor) publishTarGz(outDirFullPath string) (err error) {

	if p.publishTarGzTo == "null" || p.publishTarGzTo == "NULL" {
		logger.Info("publishTarGzTo is ", p.publishTarGzTo, ", skip sending tar.gz results")
		return nil
	}

	tarGzPath, err := gzipFolder(outDirFullPath)

	f, err := os.Open(tarGzPath)
	if err != nil {
		logger.Error("error openning file: ", err)
		return err
	}
	defer f.Close()

	client := new(http.Client)
	request, err := http.NewRequest("POST", p.publishTarGzTo+"?mainfile="+resultsTarFile, f)
	request.Header.Add("Content-Type", "application/gzip")
	_, err = client.Do(request)
	if err != nil {
		logger.Error("error uploading file: ", err)
		return err
	}

	logger.Info("sucessfully sent file: ", tarGzPath, ", to results service")
	return nil
}

//gzip specific files from within "folderpath", "tarGzPath" is the output of the tar.gz file location on disk
func gzipFolder(folderPath string) (tarGzPath string, err error) {
	// set up the output file
	tarGzPath = filepath.Join(folderPath, "output.tar.gz")
	file, err := os.Create(tarGzPath)
	if err != nil {
		logger.Error("failed to create: ", tarGzPath, " on disk, error: ", err)
		return "", err
	}
	defer file.Close()

	// set up the gzip writer
	gw := gzip.NewWriter(file)
	defer gw.Close()
	tw := tar.NewWriter(gw)
	defer tw.Close()
	// grab the paths that need to be added in
	files := []string{
		resultsTarFile,
	}
	// add each file as needed into the current tar archive
	for _, fileName := range files {
		if err := addFileToTar(tw, folderPath, fileName); err != nil {
			logger.Error("failed to add: ", fileName, " to output.tar.gz, error: ", err)
			return "", err
		}
	}

	return tarGzPath, nil
}

func addFileToTar(tw *tar.Writer, folderPath, fileName string) error {
	fullPath := filepath.Join(folderPath, fileName)
	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()
	if stat, err := file.Stat(); err == nil {
		// now lets create the header as needed for this file within the tarball
		header := new(tar.Header)
		header.Name = fileName
		header.Size = stat.Size()
		header.Mode = int64(stat.Mode())
		header.ModTime = stat.ModTime()
		// write the header to the tarball archive
		if err := tw.WriteHeader(header); err != nil {
			return err
		}
		// copy the file data to the tarball
		if _, err := io.Copy(tw, file); err != nil {
			return err
		}
	}
	return nil
}

func getTugbotData(contInfo types.ContainerJSON, contId string) (*tugbotData) {
	return &tugbotData {
		ImageName:  contInfo.Config.Image,
		ContainerId: contId,
		StartedAt: contInfo.State.StartedAt,
		FinishedAt: contInfo.State.FinishedAt,
		ExitCode: contInfo.State.ExitCode,
		HostName: contInfo.Config.Hostname,
	}
}<commit_msg>go fmt processor.go<commit_after>package processor

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"encoding/json"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/gaia-docker/tugbot-collect/log"
	"github.com/gaia-docker/tugbot-parse"
	"golang.org/x/net/context"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var logger = log.GetLogger("processor")

const resultsTarFile = "results.tar"

//Processor is a struct to hold the Tasks channel
//You should use NewProcessor to allocate new one and the Run func to run it
type Processor struct {
	Tasks              chan string
	dockerClient       *client.Client
	outputDir          string
	publishTarGzTo     string
	publishTestCasesTo string
	resultsDirLabel    string
	dockerRM           bool
}

type results struct {
	testResults         io.ReadCloser
	testResultsPathStat types.ContainerPathStat
	containerInfo       types.ContainerJSON
}

type tugbotData struct {
	ImageName   string
	ContainerId string
	StartedAt   string
	FinishedAt  string
	ExitCode    int
	HostName    string
}

//NewProcessor create new Processor and allocates Tasks buffered channel in size 10 to it
func NewProcessor(pDockerClient *client.Client, pOutputDir, pPublishTarGzTo, pPublishTestCasesTo, pResultsDirLabel string, pDockerRM bool) Processor {
	p := Processor{
		dockerClient:       pDockerClient,
		outputDir:          pOutputDir,
		publishTarGzTo:     pPublishTarGzTo,
		publishTestCasesTo: pPublishTestCasesTo,
		resultsDirLabel:    pResultsDirLabel,
		dockerRM:           pDockerRM,
	}
	p.Tasks = make(chan string, 10)
	return p
}

//Run the processor (listen to the Tasks channel and performs the collection)
func (p Processor) Run() {
	go func() {
		for task := range p.Tasks {

			//We run each task in a separate goroutine to parallel the extraction
			go func(contId string) {
				logger.Info("processor is going to extract results from container with id: ", contId)

				ctx, cancel := context.WithCancel(context.Background())
				defer cancel()

				contResults, err := p.collectResults(ctx, contId)
				if err != nil {
					logger.Error("failed to collect results for container id: ", contId)
					return
				}

				//write the results to disk
				outDirPath, err := writeToDisk(p.outputDir, contId, contResults)
				if err != nil {
					logger.Error("failed to write to disk")
					return
				}

				//publish tar.gz
				if outDirPath != "" {
					err = p.publishTarGz(outDirPath)
					if err != nil {
						logger.Error("failed to publish tar.gz")
						return
					}
				}

				//publish test cases
				if outDirPath != "" {
					err = p.publishTestCases(outDirPath, contId, contResults)
					if err != nil {
						logger.Error("failed to publish test cases")
						return
					}
				}

				//We get here only if no error occurred along the way
				if p.dockerRM {
					err = p.dockerClient.ContainerRemove(ctx, contId, types.ContainerRemoveOptions{})
					if err != nil {
						logger.Error("failed to remove container with id: ", contId, ", why: ", err)
						return
					}

					logger.Info("sucessfully removed (docker rm) container with id: ", contId)
				}

				logger.Info("sucessfully analyzed results for container with id: ", contId)
			}(task)
		}
	}()
}

func (p Processor) collectResults(ctx context.Context, contId string) (contResults *results, err error) {

	contResults = &results{}
	contResults.containerInfo, err = p.dockerClient.ContainerInspect(ctx, contId)
	if err != nil {
		logger.Error("failed to inspect container with id: ", contId, ", cannot determine results dir location - discard processing this container")
		return nil, err
	}

	//copy the result dir from inside the container into memory
	//The result dir will be return in tar format
	resultDir := contResults.containerInfo.Config.Labels[p.resultsDirLabel]
	logger.Info("going to extract results for container with id: ", contId, ", from this location inside the test container: ", resultDir)
	contResults.testResults, contResults.testResultsPathStat, err = p.dockerClient.CopyFromContainer(ctx, contId, resultDir)
	/*buf := new(bytes.Buffer)
	buf.ReadFrom(contResults.testResults)
	s := buf.String()
	logger.Info("tar for cont: ", contId, ", data: ", s) */
	if err != nil {
		logger.Error("failed to copy result dir from container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return nil, err
	}

	//check that the resultdir is really a directory
	if !contResults.testResultsPathStat.Mode.IsDir() {
		logger.Error("results location:", resultDir, ", inside the container with id: ", contId, ", is not a directory - discard processing of this container")
		return nil, err
	}

	return contResults, nil
}

func writeToDisk(outputDir, contId string, contResults *results) (outDirFullPath string, err error) {

	if outputDir == "/dev/null" {
		logger.Info("output dir is ", outputDir, ", skip writing to disk")
		return "", nil
	}

	//create output dir (if dir is already exist MkdirAll will return nil)
	outDirFullPath = filepath.Join(outputDir, strings.Replace(contResults.containerInfo.Config.Image, "/", "_", -1)+"-"+contId[:11])
	err = os.MkdirAll(outDirFullPath, 0777)
	if err != nil {
		logger.Error("failed to create output dir: ", outDirFullPath, ", for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}
	logger.Info("going to write results from container with id: ", contId, ", into this location: ", outDirFullPath)

	// open output file
	resultsTarFullPath := filepath.Join(outDirFullPath, resultsTarFile)
	fo, err := os.Create(resultsTarFullPath)
	if err != nil {
		logger.Error("failed to create tar file on FS for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}
	// close fo on exit and check for its returned error
	defer func() {
		if err := fo.Close(); err != nil {
			logger.Error("failed to close FS tar file for container with id: ", contId, ", error is: ", err)
		}
	}()

	_, err = io.Copy(fo, contResults.testResults)
	if err != nil {
		logger.Error("failed to write to tar file for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}

	if err := fo.Sync(); err != nil {
		logger.Error("failed to flash tar to disk for container with id: ", contId, ", error is: ", err, " - discard processing this container")
		return "", err
	}

	//All is good
	return outDirFullPath, nil
}

func (p Processor) publishTestCases(outDirFullPath string, contId string, contResults *results) (err error) {

	if p.publishTestCasesTo == "null" || p.publishTestCasesTo == "NULL" {
		logger.Info("publishTastCasesTo is ", p.publishTestCasesTo, ", skip sending json results")
		return nil
	}

	fullPath := filepath.Join(outDirFullPath, resultsTarFile)
	f, err := os.Open(fullPath)
	if err != nil {
		logger.Error("publishTestCases - failed to open tar file: ", err)
		return err
	}
	defer f.Close()

	tugbotDt := getTugbotData(contResults.containerInfo, contId)
	client := new(http.Client)
	tarReader := tar.NewReader(f)

	i := 0
	for {
		header, err := tarReader.Next()

		if err == io.EOF {
			break
		}

		if err != nil {
			logger.Error("publishTestCases - failed iterate tar file: ", err)
			return err
		}

		name := header.Name

		switch header.Typeflag {
		case tar.TypeDir:
			continue
		case tar.TypeReg:
			logger.Info("tar (", i, ")", "Name: ", name)
			if strings.HasSuffix(name, "xml") {

				if header.Size <= 0 {
					logger.Warn("cannot process: ", name, " file from the tar, invalid file size: ", header.Size, ". skipping this file")
					continue
				}

				buf := make([]byte, header.Size)
				_, err := tarReader.Read(buf)
				if err != nil {
					logger.Warn("failed to read: ", name, " file from the tar, err: ", err, ". skipping this file")
					continue
				}

				tests, err := parse.ToAnalyticsTests(buf)
				if err != nil {
					logger.Warn("failed to convert xml to tests slice for file: ", name, ", err: ", err, ". skipping this file")
					continue
				}

				for _, test := range tests {
					json, err := json.Marshal(struct {
						TugbotData *tugbotData
						Test       parse.AnalyticsTest
					}{
						TugbotData: tugbotDt,
						Test:       test,
					})

					if err != nil {
						logger.Warn("failed to convert specific AnalyticsTest to json for file: ", name, ", err: ", err, ". skipping this file")
						continue
					}

					logger.Debug("json for file: ", name, ", is: ", string(json))

					r := bytes.NewReader(json)
					request, err := http.NewRequest("POST", p.publishTestCasesTo+"?docker.imagename="+contResults.containerInfo.Config.Image, r)
					request.Header.Add("Content-Type", "application/json")
					_, err = client.Do(request)
					if err != nil {
						logger.Error("error publish json: ", err)
						return err
					}
				}

			}

		default:
			logger.Infof("%s : %c %s %s\n",
				"Yikes! Unable to figure out type",
				header.Typeflag,
				"in file",
				name,
			)
		}

		i++
	}

	return nil
}

func (p Processor) publishTarGz(outDirFullPath string) (err error) {

	if p.publishTarGzTo == "null" || p.publishTarGzTo == "NULL" {
		logger.Info("publishTarGzTo is ", p.publishTarGzTo, ", skip sending tar.gz results")
		return nil
	}

	tarGzPath, err := gzipFolder(outDirFullPath)

	f, err := os.Open(tarGzPath)
	if err != nil {
		logger.Error("error openning file: ", err)
		return err
	}
	defer f.Close()

	client := new(http.Client)
	request, err := http.NewRequest("POST", p.publishTarGzTo+"?mainfile="+resultsTarFile, f)
	request.Header.Add("Content-Type", "application/gzip")
	_, err = client.Do(request)
	if err != nil {
		logger.Error("error uploading file: ", err)
		return err
	}

	logger.Info("sucessfully sent file: ", tarGzPath, ", to results service")
	return nil
}

//gzip specific files from within "folderpath", "tarGzPath" is the output of the tar.gz file location on disk
func gzipFolder(folderPath string) (tarGzPath string, err error) {
	// set up the output file
	tarGzPath = filepath.Join(folderPath, "output.tar.gz")
	file, err := os.Create(tarGzPath)
	if err != nil {
		logger.Error("failed to create: ", tarGzPath, " on disk, error: ", err)
		return "", err
	}
	defer file.Close()

	// set up the gzip writer
	gw := gzip.NewWriter(file)
	defer gw.Close()
	tw := tar.NewWriter(gw)
	defer tw.Close()
	// grab the paths that need to be added in
	files := []string{
		resultsTarFile,
	}
	// add each file as needed into the current tar archive
	for _, fileName := range files {
		if err := addFileToTar(tw, folderPath, fileName); err != nil {
			logger.Error("failed to add: ", fileName, " to output.tar.gz, error: ", err)
			return "", err
		}
	}

	return tarGzPath, nil
}

func addFileToTar(tw *tar.Writer, folderPath, fileName string) error {
	fullPath := filepath.Join(folderPath, fileName)
	file, err := os.Open(fullPath)
	if err != nil {
		return err
	}
	defer file.Close()
	if stat, err := file.Stat(); err == nil {
		// now lets create the header as needed for this file within the tarball
		header := new(tar.Header)
		header.Name = fileName
		header.Size = stat.Size()
		header.Mode = int64(stat.Mode())
		header.ModTime = stat.ModTime()
		// write the header to the tarball archive
		if err := tw.WriteHeader(header); err != nil {
			return err
		}
		// copy the file data to the tarball
		if _, err := io.Copy(tw, file); err != nil {
			return err
		}
	}
	return nil
}

func getTugbotData(contInfo types.ContainerJSON, contId string) *tugbotData {
	return &tugbotData{
		ImageName:   contInfo.Config.Image,
		ContainerId: contId,
		StartedAt:   contInfo.State.StartedAt,
		FinishedAt:  contInfo.State.FinishedAt,
		ExitCode:    contInfo.State.ExitCode,
		HostName:    contInfo.Config.Hostname,
	}
}
<|endoftext|>


<commit_before><commit_msg>Use ArgsParser for `hub remote add` extension<commit_after><|endoftext|>


<commit_before>package admin

import (
	"model"
)

func CreateSeasonSchedule(season *model.Season, conferenceCount int, divisionCount int, weeksCount int, playoffs int, players []model.Player) {

}

func CreateConferences(season *model.Season, conferenceCount int, divisionCount int, players []model.Player) {

}
<commit_msg>Added in a function to get player lists by faction<commit_after>package admin

import (
	"model"
)

// Creates and saves the season schedule as passed in.
func CreateSeasonSchedule(season *model.Season, conferenceCount int, divisionCount int, weeksCount int, playoffs int, players []model.Player) {

}

// Creates the season conferences, placing them on the modified season entity.  Note that this does NOT save the conferences or the entity.
func CreateConferences(season *model.Season, conferenceCount int, divisionCount int, players []model.Player) {

}

// Creates a map of the players by faction.
func playersByFaction(players []model.Player) map[string][]model.Player {
	playersByFaction := make(map[string][]model.Player)
	for _, p := range players {
		playersByFaction[p.Faction] = append(playersByFaction[p.Faction], p)
	}
	return playersByFaction
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"context"
	"encoding/json"
	"reflect"

	"github.com/hashicorp/terraform/helper/schema"

	oci_ons "github.com/oracle/oci-go-sdk/ons"
)

func SubscriptionResource() *schema.Resource {
	return &schema.Resource{
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Timeouts: DefaultTimeout,
		Create:   createSubscription,
		Read:     readSubscription,
		Update:   updateSubscription,
		Delete:   deleteSubscription,
		Schema: map[string]*schema.Schema{
			// Required
			"compartment_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"endpoint": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"protocol": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"topic_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			// Optional
			"defined_tags": {
				Type:             schema.TypeMap,
				Optional:         true,
				Computed:         true,
				DiffSuppressFunc: definedTagsDiffSuppressFunction,
				Elem:             schema.TypeString,
			},
			"freeform_tags": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
				Elem:     schema.TypeString,
			},

			// Computed
			"delivery_policy": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				//json string, convert to json to see if they are the same
				DiffSuppressFunc: jsonStringDiffSuppresionFunction,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func createSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return CreateResource(d, sync)
}

func readSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return ReadResource(sync)
}

func updateSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return UpdateResource(d, sync)
}

func deleteSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient
	sync.DisableNotFoundRetries = true

	return DeleteResource(d, sync)
}

type SubscriptionResourceCrud struct {
	BaseCrud
	Client                 *oci_ons.NotificationDataPlaneClient
	Res                    *oci_ons.Subscription
	DisableNotFoundRetries bool
}

func (s *SubscriptionResourceCrud) ID() string {
	return *s.Res.Id
}

func (s *SubscriptionResourceCrud) CreatedPending() []string {
	return []string{}
}

func (s *SubscriptionResourceCrud) CreatedTarget() []string {
	return []string{
		string(oci_ons.SubscriptionLifecycleStatePending),
		string(oci_ons.SubscriptionLifecycleStateActive),
	}
}

func (s *SubscriptionResourceCrud) DeletedPending() []string {
	return []string{}
}

func (s *SubscriptionResourceCrud) DeletedTarget() []string {
	return []string{
		string(oci_ons.SubscriptionLifecycleStateDeleted),
	}
}

func (s *SubscriptionResourceCrud) Create() error {
	request := oci_ons.CreateSubscriptionRequest{}

	if compartmentId, ok := s.D.GetOkExists("compartment_id"); ok {
		tmp := compartmentId.(string)
		request.CompartmentId = &tmp
	}

	if definedTags, ok := s.D.GetOkExists("defined_tags"); ok {
		convertedDefinedTags, err := mapToDefinedTags(definedTags.(map[string]interface{}))
		if err != nil {
			return err
		}
		request.DefinedTags = convertedDefinedTags
	}

	if endpoint, ok := s.D.GetOkExists("endpoint"); ok {
		tmp := endpoint.(string)
		request.Endpoint = &tmp
	}

	if freeformTags, ok := s.D.GetOkExists("freeform_tags"); ok {
		request.FreeformTags = objectMapToStringMap(freeformTags.(map[string]interface{}))
	}

	if protocol, ok := s.D.GetOkExists("protocol"); ok {
		tmp := protocol.(string)
		request.Protocol = &tmp
	}

	if topicId, ok := s.D.GetOkExists("topic_id"); ok {
		tmp := topicId.(string)
		request.TopicId = &tmp
	}

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	response, err := s.Client.CreateSubscription(context.Background(), request)
	if err != nil {
		return err
	}

	s.Res = &response.Subscription
	return nil
}

func (s *SubscriptionResourceCrud) Get() error {
	request := oci_ons.GetSubscriptionRequest{}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	response, err := s.Client.GetSubscription(context.Background(), request)
	if err != nil {
		return err
	}

	s.Res = &response.Subscription
	return nil
}

func (s *SubscriptionResourceCrud) Update() error {
	request := oci_ons.UpdateSubscriptionRequest{}

	if definedTags, ok := s.D.GetOkExists("defined_tags"); ok {
		convertedDefinedTags, err := mapToDefinedTags(definedTags.(map[string]interface{}))
		if err != nil {
			return err
		}
		request.DefinedTags = convertedDefinedTags
	}

	if deliveryPolicy, ok := s.D.GetOkExists("delivery_policy"); ok {
		res := oci_ons.DeliveryPolicy{}
		// due to deliveryPolicy's difference between Update and Get Api
		json.Unmarshal([]byte(deliveryPolicy.(string)), &res)
		request.DeliveryPolicy = &res
	}

	if freeformTags, ok := s.D.GetOkExists("freeform_tags"); ok {
		request.FreeformTags = objectMapToStringMap(freeformTags.(map[string]interface{}))
	}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	_, err := s.Client.UpdateSubscription(context.Background(), request)
	if err == nil {
		return s.Get()
	}

	return err
}

func (s *SubscriptionResourceCrud) Delete() error {
	request := oci_ons.DeleteSubscriptionRequest{}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	_, err := s.Client.DeleteSubscription(context.Background(), request)
	return err
}

func (s *SubscriptionResourceCrud) SetData() error {
	if s.Res.DefinedTags != nil {
		s.D.Set("defined_tags", definedTagsToMap(s.Res.DefinedTags))
	}

	if s.Res.DeliverPolicy != nil {
		s.D.Set("delivery_policy", *s.Res.DeliverPolicy)
	}

	if s.Res.Endpoint != nil {
		s.D.Set("endpoint", *s.Res.Endpoint)
	}

	if s.Res.Etag != nil {
		s.D.Set("etag", *s.Res.Etag)
	}

	s.D.Set("freeform_tags", s.Res.FreeformTags)

	if s.Res.Protocol != nil {
		s.D.Set("protocol", *s.Res.Protocol)
	}

	s.D.Set("state", s.Res.LifecycleState)

	return nil
}

func BackoffRetryPolicyToMap(obj *oci_ons.BackoffRetryPolicy) map[string]interface{} {
	result := map[string]interface{}{}

	if obj.MaxRetryDuration != nil {
		result["max_retry_duration"] = int(*obj.MaxRetryDuration)
	}

	result["policy_type"] = string(obj.PolicyType)

	return result
}

func DeliveryPolicyToMap(obj *oci_ons.DeliveryPolicy) map[string]interface{} {
	result := map[string]interface{}{}

	if obj.BackoffRetryPolicy != nil {
		result["backoff_retry_policy"] = []interface{}{BackoffRetryPolicyToMap(obj.BackoffRetryPolicy)}
	}

	return result
}

// check if the json data are the same
func jsonStringDiffSuppresionFunction(key string, old string, new string, d *schema.ResourceData) bool {
	if old == "" && new == "" {
		return true
	} else if old == "" || new == "" {
		return false
	} else {
		if old == new {
			return true
		}
		oldValue := oci_ons.DeliveryPolicy{}
		newValue := oci_ons.DeliveryPolicy{}
		json.Unmarshal([]byte(old), &oldValue)
		json.Unmarshal([]byte(new), &newValue)
		return reflect.DeepEqual(oldValue, newValue)
	}
}
<commit_msg>fix error check for notification service<commit_after>// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.

package provider

import (
	"context"
	"encoding/json"
	"reflect"

	"github.com/hashicorp/terraform/helper/schema"

	oci_ons "github.com/oracle/oci-go-sdk/ons"
)

func SubscriptionResource() *schema.Resource {
	return &schema.Resource{
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Timeouts: DefaultTimeout,
		Create:   createSubscription,
		Read:     readSubscription,
		Update:   updateSubscription,
		Delete:   deleteSubscription,
		Schema: map[string]*schema.Schema{
			// Required
			"compartment_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"endpoint": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"protocol": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"topic_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},

			// Optional
			"defined_tags": {
				Type:             schema.TypeMap,
				Optional:         true,
				Computed:         true,
				DiffSuppressFunc: definedTagsDiffSuppressFunction,
				Elem:             schema.TypeString,
			},
			"freeform_tags": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
				Elem:     schema.TypeString,
			},

			// Computed
			"delivery_policy": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
				//json string, convert to json to see if they are the same
				DiffSuppressFunc: jsonStringDiffSuppresionFunction,
			},
			"etag": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func createSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return CreateResource(d, sync)
}

func readSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return ReadResource(sync)
}

func updateSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient

	return UpdateResource(d, sync)
}

func deleteSubscription(d *schema.ResourceData, m interface{}) error {
	sync := &SubscriptionResourceCrud{}
	sync.D = d
	sync.Client = m.(*OracleClients).notificationDataPlaneClient
	sync.DisableNotFoundRetries = true

	return DeleteResource(d, sync)
}

type SubscriptionResourceCrud struct {
	BaseCrud
	Client                 *oci_ons.NotificationDataPlaneClient
	Res                    *oci_ons.Subscription
	DisableNotFoundRetries bool
}

func (s *SubscriptionResourceCrud) ID() string {
	return *s.Res.Id
}

func (s *SubscriptionResourceCrud) CreatedPending() []string {
	return []string{}
}

func (s *SubscriptionResourceCrud) CreatedTarget() []string {
	return []string{
		string(oci_ons.SubscriptionLifecycleStatePending),
		string(oci_ons.SubscriptionLifecycleStateActive),
	}
}

func (s *SubscriptionResourceCrud) DeletedPending() []string {
	return []string{}
}

func (s *SubscriptionResourceCrud) DeletedTarget() []string {
	return []string{
		string(oci_ons.SubscriptionLifecycleStateDeleted),
	}
}

func (s *SubscriptionResourceCrud) Create() error {
	request := oci_ons.CreateSubscriptionRequest{}

	if compartmentId, ok := s.D.GetOkExists("compartment_id"); ok {
		tmp := compartmentId.(string)
		request.CompartmentId = &tmp
	}

	if definedTags, ok := s.D.GetOkExists("defined_tags"); ok {
		convertedDefinedTags, err := mapToDefinedTags(definedTags.(map[string]interface{}))
		if err != nil {
			return err
		}
		request.DefinedTags = convertedDefinedTags
	}

	if endpoint, ok := s.D.GetOkExists("endpoint"); ok {
		tmp := endpoint.(string)
		request.Endpoint = &tmp
	}

	if freeformTags, ok := s.D.GetOkExists("freeform_tags"); ok {
		request.FreeformTags = objectMapToStringMap(freeformTags.(map[string]interface{}))
	}

	if protocol, ok := s.D.GetOkExists("protocol"); ok {
		tmp := protocol.(string)
		request.Protocol = &tmp
	}

	if topicId, ok := s.D.GetOkExists("topic_id"); ok {
		tmp := topicId.(string)
		request.TopicId = &tmp
	}

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	response, err := s.Client.CreateSubscription(context.Background(), request)
	if err != nil {
		return err
	}

	s.Res = &response.Subscription
	return nil
}

func (s *SubscriptionResourceCrud) Get() error {
	request := oci_ons.GetSubscriptionRequest{}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	response, err := s.Client.GetSubscription(context.Background(), request)
	if err != nil {
		return err
	}

	s.Res = &response.Subscription
	return nil
}

func (s *SubscriptionResourceCrud) Update() error {
	request := oci_ons.UpdateSubscriptionRequest{}

	if definedTags, ok := s.D.GetOkExists("defined_tags"); ok {
		convertedDefinedTags, err := mapToDefinedTags(definedTags.(map[string]interface{}))
		if err != nil {
			return err
		}
		request.DefinedTags = convertedDefinedTags
	}

	if deliveryPolicy, ok := s.D.GetOkExists("delivery_policy"); ok {
		res := oci_ons.DeliveryPolicy{}
		// due to deliveryPolicy's difference between Update and Get Api
		if err := json.Unmarshal([]byte(deliveryPolicy.(string)), &res); err != nil {
			return err
		}
		request.DeliveryPolicy = &res
	}

	if freeformTags, ok := s.D.GetOkExists("freeform_tags"); ok {
		request.FreeformTags = objectMapToStringMap(freeformTags.(map[string]interface{}))
	}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	_, err := s.Client.UpdateSubscription(context.Background(), request)
	if err == nil {
		return s.Get()
	}

	return err
}

func (s *SubscriptionResourceCrud) Delete() error {
	request := oci_ons.DeleteSubscriptionRequest{}

	tmp := s.D.Id()
	request.SubscriptionId = &tmp

	request.RequestMetadata.RetryPolicy = getRetryPolicy(s.DisableNotFoundRetries, "ons")

	_, err := s.Client.DeleteSubscription(context.Background(), request)
	return err
}

func (s *SubscriptionResourceCrud) SetData() error {
	if s.Res.DefinedTags != nil {
		s.D.Set("defined_tags", definedTagsToMap(s.Res.DefinedTags))
	}

	if s.Res.DeliverPolicy != nil {
		s.D.Set("delivery_policy", *s.Res.DeliverPolicy)
	}

	if s.Res.Endpoint != nil {
		s.D.Set("endpoint", *s.Res.Endpoint)
	}

	if s.Res.Etag != nil {
		s.D.Set("etag", *s.Res.Etag)
	}

	s.D.Set("freeform_tags", s.Res.FreeformTags)

	if s.Res.Protocol != nil {
		s.D.Set("protocol", *s.Res.Protocol)
	}

	s.D.Set("state", s.Res.LifecycleState)

	return nil
}

func BackoffRetryPolicyToMap(obj *oci_ons.BackoffRetryPolicy) map[string]interface{} {
	result := map[string]interface{}{}

	if obj.MaxRetryDuration != nil {
		result["max_retry_duration"] = int(*obj.MaxRetryDuration)
	}

	result["policy_type"] = string(obj.PolicyType)

	return result
}

func DeliveryPolicyToMap(obj *oci_ons.DeliveryPolicy) map[string]interface{} {
	result := map[string]interface{}{}

	if obj.BackoffRetryPolicy != nil {
		result["backoff_retry_policy"] = []interface{}{BackoffRetryPolicyToMap(obj.BackoffRetryPolicy)}
	}

	return result
}

// check if the json data are the same
func jsonStringDiffSuppresionFunction(key string, old string, new string, d *schema.ResourceData) bool {
	if old == "" && new == "" {
		return true
	} else if old == "" || new == "" {
		return false
	} else {
		if old == new {
			return true
		}
		oldValue := oci_ons.DeliveryPolicy{}
		newValue := oci_ons.DeliveryPolicy{}
		if err := json.Unmarshal([]byte(old), &oldValue); err != nil {
			return false
		}
		if err := json.Unmarshal([]byte(new), &newValue); err != nil {
			return false
		}
		return reflect.DeepEqual(oldValue, newValue)
	}
}
<|endoftext|>


<commit_before><commit_msg>handle nil usr<commit_after><|endoftext|>


<commit_before>package app_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/weaveworks/scope/app"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test"
)

func TestCollector(t *testing.T) {
	window := time.Millisecond
	c := app.NewCollector(window)

	r1 := report.MakeReport()
	r1.Endpoint.AddNode("foo", report.MakeNode())

	r2 := report.MakeReport()
	r2.Endpoint.AddNode("bar", report.MakeNode())

	if want, have := report.MakeReport(), c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}

	c.Add(r1)
	if want, have := r1, c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}

	c.Add(r2)

	merged := report.MakeReport()
	merged = merged.Merge(r1)
	merged = merged.Merge(r2)
	if want, have := merged, c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}
}
<commit_msg>Add a little test for collector.go<commit_after>package app_test

import (
	"reflect"
	"testing"
	"time"

	"github.com/weaveworks/scope/app"
	"github.com/weaveworks/scope/report"
	"github.com/weaveworks/scope/test"
)

func TestCollector(t *testing.T) {
	window := time.Millisecond
	c := app.NewCollector(window)

	r1 := report.MakeReport()
	r1.Endpoint.AddNode("foo", report.MakeNode())

	r2 := report.MakeReport()
	r2.Endpoint.AddNode("bar", report.MakeNode())

	if want, have := report.MakeReport(), c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}

	c.Add(r1)
	if want, have := r1, c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}

	c.Add(r2)

	merged := report.MakeReport()
	merged = merged.Merge(r1)
	merged = merged.Merge(r2)
	if want, have := merged, c.Report(); !reflect.DeepEqual(want, have) {
		t.Error(test.Diff(want, have))
	}
}

func TestCollectorWait(t *testing.T) {
	window := time.Millisecond
	c := app.NewCollector(window)

	waiter := make(chan struct{}, 1)
	c.WaitOn(waiter)
	defer c.UnWait(waiter)
	c.(interface {
		Broadcast()
	}).Broadcast()

	select {
	case <-waiter:
	default:
		t.Fatal("Didn't unblock")
	}
}
<|endoftext|>


<commit_before>package renter

import (
	"crypto/rand"
	"errors"
	"io"
	"net"
	"os"
	"sync/atomic"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
)

var (
	downloadAttempts = 5
)

// A Download is a file download that has been queued by the renter.
type Download struct {
	// Implementation note: received is declared first to ensure that it is
	// 64-bit aligned. This is necessary to ensure that atomic operations work
	// correctly on ARM and x86-32.
	received uint64

	complete    bool
	filesize    uint64
	destination string
	nickname    string

	pieces []filePiece
	file   *os.File
}

// Complete returns whether the file is ready to be used.
func (d *Download) Complete() bool {
	return d.complete
}

// Filesize returns the size of the file.
func (d *Download) Filesize() uint64 {
	return d.filesize
}

// Received returns the number of bytes downloaded so far.
func (d *Download) Received() uint64 {
	return d.received
}

// Destination returns the file's location on disk.
func (d *Download) Destination() string {
	return d.destination
}

// Nickname returns the identifier assigned to the file when it was uploaded.
func (d *Download) Nickname() string {
	return d.nickname
}

// Write implements the io.Writer interface. Each write updates the Download's
// received field. This allows download progress to be monitored in real-time.
func (d *Download) Write(b []byte) (int, error) {
	n, err := d.file.Write(b)
	// atomically update d.received
	atomic.AddUint64(&d.received, uint64(n))
	return n, err
}

// downloadPiece attempts to retrieve a file piece from a host.
func (d *Download) downloadPiece(piece filePiece) error {
	conn, err := net.DialTimeout("tcp", string(piece.HostIP), 10e9)
	if err != nil {
		return err
	}
	defer conn.Close()
	err = encoding.WriteObject(conn, [8]byte{'R', 'e', 't', 'r', 'i', 'e', 'v', 'e'})
	if err != nil {
		return err
	}

	// Send the ID of the contract for the file piece we're requesting.
	if err := encoding.WriteObject(conn, piece.ContractID); err != nil {
		return err
	}

	// Simultaneously download, decrypt, and calculate the Merkle root of the file.
	tee := io.TeeReader(
		// Use a LimitedReader to ensure we don't read indefinitely.
		piece.EncryptionKey.NewReader(io.LimitReader(conn, int64(piece.Contract.FileSize))),
		// Each byte we read from tee will also be written to file.
		d,
	)
	merkleRoot, err := crypto.ReaderMerkleRoot(tee)
	if err != nil {
		return err
	}

	if merkleRoot != piece.Contract.FileMerkleRoot {
		return errors.New("host provided a file that's invalid")
	}

	return nil
}

// start initiates the download of a File.
func (d *Download) start() {
	// We only need one piece, so iterate through the hosts until a download
	// succeeds.
	for i := 0; i < downloadAttempts; i++ {
		for _, piece := range d.pieces {
			downloadErr := d.downloadPiece(piece)
			if downloadErr == nil {
				d.complete = true
				d.file.Close()
				return
			}
			// Reset seek, since the file may have been partially written. The
			// next attempt will overwrite these bytes.
			d.file.Seek(0, 0)
		}

		// This iteration failed, no hosts returned the piece. Try again
		// after waiting a random amount of time.
		randSource := make([]byte, 1)
		rand.Read(randSource)
		time.Sleep(time.Second * time.Duration(i*i) * time.Duration(randSource[0]))
	}

	// File could not be downloaded; delete the copy on disk.
	d.file.Close()
	os.Remove(d.destination)
}

// newDownload initializes a new Download object.
func newDownload(file *file, destination string) (*Download, error) {
	// Create the download destination file.
	handle, err := os.Create(destination)
	if err != nil {
		return nil, err
	}

	// Filter out the inactive pieces.
	var activePieces []filePiece
	for _, piece := range file.Pieces {
		if piece.Active {
			activePieces = append(activePieces, piece)
		}
	}
	if len(activePieces) == 0 {
		return nil, errors.New("no active pieces")
	}

	return &Download{
		complete: false,
		// for now, all the pieces are equivalent
		filesize:    file.Pieces[0].Contract.FileSize,
		received:    0,
		destination: destination,
		nickname:    file.Name,

		pieces: activePieces,
		file:   handle,
	}, nil
}

// Download downloads a file, identified by its nickname, to the destination
// specified.
func (r *Renter) Download(nickname, destination string) error {
	lockID := r.mu.Lock()
	defer r.mu.Unlock(lockID)

	// Lookup the File associated with the nickname.
	file, exists := r.files[nickname]
	if !exists {
		return errors.New("no file of that nickname")
	}

	// Create the download object and spawn the download process.
	d, err := newDownload(file, destination)
	if err != nil {
		return err
	}
	go d.start()

	// Add the download to the download queue.
	r.downloadQueue = append(r.downloadQueue, d)
	return nil
}

// DownloadQueue returns the list of downloads in the queue.
func (r *Renter) DownloadQueue() []modules.DownloadInfo {
	lockID := r.mu.RLock()
	defer r.mu.RUnlock(lockID)

	downloads := make([]modules.DownloadInfo, len(r.downloadQueue))
	for i := range r.downloadQueue {
		downloads[i] = r.downloadQueue[i]
	}
	return downloads
}
<commit_msg>fix encryption order in download process<commit_after>package renter

import (
	"crypto/rand"
	"errors"
	"io"
	"net"
	"os"
	"sync/atomic"
	"time"

	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
)

var (
	downloadAttempts = 5
)

// A Download is a file download that has been queued by the renter.
type Download struct {
	// Implementation note: received is declared first to ensure that it is
	// 64-bit aligned. This is necessary to ensure that atomic operations work
	// correctly on ARM and x86-32.
	received uint64

	complete    bool
	filesize    uint64
	destination string
	nickname    string

	pieces []filePiece
	file   *os.File
}

// Complete returns whether the file is ready to be used.
func (d *Download) Complete() bool {
	return d.complete
}

// Filesize returns the size of the file.
func (d *Download) Filesize() uint64 {
	return d.filesize
}

// Received returns the number of bytes downloaded so far.
func (d *Download) Received() uint64 {
	return d.received
}

// Destination returns the file's location on disk.
func (d *Download) Destination() string {
	return d.destination
}

// Nickname returns the identifier assigned to the file when it was uploaded.
func (d *Download) Nickname() string {
	return d.nickname
}

// Write implements the io.Writer interface. Each write updates the Download's
// received field. This allows download progress to be monitored in real-time.
func (d *Download) Write(b []byte) (int, error) {
	n, err := d.file.Write(b)
	// atomically update d.received
	atomic.AddUint64(&d.received, uint64(n))
	return n, err
}

// downloadPiece attempts to retrieve a file piece from a host.
func (d *Download) downloadPiece(piece filePiece) error {
	conn, err := net.DialTimeout("tcp", string(piece.HostIP), 10e9)
	if err != nil {
		return err
	}
	defer conn.Close()
	err = encoding.WriteObject(conn, [8]byte{'R', 'e', 't', 'r', 'i', 'e', 'v', 'e'})
	if err != nil {
		return err
	}

	// Send the ID of the contract for the file piece we're requesting.
	if err := encoding.WriteObject(conn, piece.ContractID); err != nil {
		return err
	}

	// Simultaneously download, decrypt, and calculate the Merkle root of the file.
	tee := io.TeeReader(
		// Use a LimitedReader to ensure we don't read indefinitely.
		io.LimitReader(conn, int64(piece.Contract.FileSize)),
		// Write the decrypted bytes to the file.
		piece.EncryptionKey.NewWriter(d),
	)
	merkleRoot, err := crypto.ReaderMerkleRoot(tee)
	if err != nil {
		return err
	}

	if merkleRoot != piece.Contract.FileMerkleRoot {
		return errors.New("host provided a file that's invalid")
	}

	return nil
}

// start initiates the download of a File.
func (d *Download) start() {
	// We only need one piece, so iterate through the hosts until a download
	// succeeds.
	for i := 0; i < downloadAttempts; i++ {
		for _, piece := range d.pieces {
			downloadErr := d.downloadPiece(piece)
			if downloadErr == nil {
				d.complete = true
				d.file.Close()
				return
			}
			// Reset seek, since the file may have been partially written. The
			// next attempt will overwrite these bytes.
			d.file.Seek(0, 0)
			atomic.SwapUint64(&d.received, 0)
		}

		// This iteration failed, no hosts returned the piece. Try again
		// after waiting a random amount of time.
		randSource := make([]byte, 1)
		rand.Read(randSource)
		time.Sleep(time.Second * time.Duration(i*i) * time.Duration(randSource[0]))
	}

	// File could not be downloaded; delete the copy on disk.
	d.file.Close()
	os.Remove(d.destination)
}

// newDownload initializes a new Download object.
func newDownload(file *file, destination string) (*Download, error) {
	// Create the download destination file.
	handle, err := os.Create(destination)
	if err != nil {
		return nil, err
	}

	// Filter out the inactive pieces.
	var activePieces []filePiece
	for _, piece := range file.Pieces {
		if piece.Active {
			activePieces = append(activePieces, piece)
		}
	}
	if len(activePieces) == 0 {
		return nil, errors.New("no active pieces")
	}

	return &Download{
		complete: false,
		// for now, all the pieces are equivalent
		filesize:    file.Pieces[0].Contract.FileSize,
		received:    0,
		destination: destination,
		nickname:    file.Name,

		pieces: activePieces,
		file:   handle,
	}, nil
}

// Download downloads a file, identified by its nickname, to the destination
// specified.
func (r *Renter) Download(nickname, destination string) error {
	lockID := r.mu.Lock()
	defer r.mu.Unlock(lockID)

	// Lookup the File associated with the nickname.
	file, exists := r.files[nickname]
	if !exists {
		return errors.New("no file of that nickname")
	}

	// Create the download object and spawn the download process.
	d, err := newDownload(file, destination)
	if err != nil {
		return err
	}
	go d.start()

	// Add the download to the download queue.
	r.downloadQueue = append(r.downloadQueue, d)
	return nil
}

// DownloadQueue returns the list of downloads in the queue.
func (r *Renter) DownloadQueue() []modules.DownloadInfo {
	lockID := r.mu.RLock()
	defer r.mu.RUnlock(lockID)

	downloads := make([]modules.DownloadInfo, len(r.downloadQueue))
	for i := range r.downloadQueue {
		downloads[i] = r.downloadQueue[i]
	}
	return downloads
}
<|endoftext|>


<commit_before>package main

import (
	. "fmt"
	. "github.com/PuerkitoBio/goquery"
	"log"
	//	"strconv"
	//	. "strings"
)

var BaseUrls = []string{
	"https://www.indeed.com/jobs?q=%[1]s&l=%[2]s",
}

var KeyWords = []string{}

var Location = []string{}

type TagStructures []string

func GetSearchPages(baseUrls, keyWords, location []string) []*Document {
	var results []*Document
	for _, url := range baseUrls {
		for _, l := range location {
			for _, w := range keyWords {
				doc, err := NewDocument(Sprintf(url, w, l))
				if err != nil {
					log.Fatal(err)
				}
				results = append(results, doc)
			}
		}
	}
	return results
}

// start Concurrency
func GetSearchPages(baseUrl, keyWord, location string) *Document {
	doc, err := NewDocument(Sprintf(baseUrl, keyWord, location))
	if err != nil {
		log.Fatal(err)
	}
	return doc
}

func GetAllNodes(s *Document, tagStructures TagStructures) *Selection {
	temp := s.Find(tagStructures[0])
	for _, v := range tagStructures[1:] {
		temp = temp.Find(v)
	}
	return temp
}

func GetTitleAndLink(s *Selection) {
	s.Each(func(_ int, s *Selection) {
		title := s.Find(".jobtitle").Text()
		if link, ok := s.Find(".jobtitle").Find("a").Attr("href"); ok {
			Println(title)
			Println("https://www.indeed.com" + link)
		}
	})
}

func main() {
	testkeyW := []string{"haskell", "python", "golang", "lisp"}
	testtagStrc := []string{".row.result"}
	testLocation := []string{"Washington%2C+DC", "Chicago%2C+IL", "Boston%2C+MA"}

	a := GetSearchPages(BaseUrls, testkeyW, testLocation)

	for _, doc := range a {
		GetTitleAndLink(GetAllNodes(doc, testtagStrc))
	}
}
<commit_msg>a bit<commit_after>package main

import (
	. "fmt"
	. "github.com/PuerkitoBio/goquery"
	"log"
	//	"strconv"
	//	. "strings"
)

var BaseUrls = []string{
	"https://www.indeed.com/jobs?q=%[1]s&l=%[2]s",
}

var KeyWords = []string{}

var Location = []string{}

type TagStructures []string

func GetSearchPages(baseUrls, keyWords, location []string, a chan *Document) {
	for _, url := range baseUrls {
		for _, l := range location {
			for _, w := range keyWords {
				doc, err := NewDocument(Sprintf(url, w, l))
				if err != nil {
					log.Fatal(err)
				}
				a <- doc
			}
		}
	}
	close(a)
}

func GetAllNodes(s *Document, tagStructures TagStructures) *Selection {
	temp := s.Find(tagStructures[0])
	for _, v := range tagStructures[1:] {
		temp = temp.Find(v)
	}
	return temp
}

func GetTitleAndLink(s *Selection) {
	s.Each(func(_ int, s *Selection) {
		title := s.Find(".jobtitle").Text()
		if link, ok := s.Find(".jobtitle").Find("a").Attr("href"); ok {
			Println(title)
			Println("https://www.indeed.com" + link)
		}
	})
}

func main() {
	testkeyW := []string{"haskell", "python", "golang", "lisp"}
	testtagStrc := []string{".row.result"}
	testLocation := []string{"Washington%2C+DC", "Chicago%2C+IL", "Boston%2C+MA"}

	a := make(chan *Document)

	go GetSearchPages(BaseUrls, testkeyW, testLocation, a)

	for doc := range a {
		Println("here")
		GetTitleAndLink(GetAllNodes(doc, testtagStrc))
	}
}
<|endoftext|>


<commit_before>package activitypub

import (
	"encoding/json"
	"time"
)

type ObjectId string

const (
	ActivityBaseURI          URI                    = URI("https://www.w3.org/ns/activitystreams#")
	ObjectType               ActivityVocabularyType = "Object"
	LinkType                 ActivityVocabularyType = "Link"
	ActivityType             ActivityVocabularyType = "Activity"
	IntransitiveActivityType ActivityVocabularyType = "IntransitiveActivity"
	ActorType                ActivityVocabularyType = "Actor"
	CollectionType           ActivityVocabularyType = "Collection"
	OrderedCollectionType    ActivityVocabularyType = "OrderedCollection"

	// Object Types
	ArticleType      ActivityVocabularyType = "Article"
	AudioType        ActivityVocabularyType = "Audio"
	DocumentType     ActivityVocabularyType = "Document"
	EventType        ActivityVocabularyType = "Event"
	ImageType        ActivityVocabularyType = "Image"
	NoteType         ActivityVocabularyType = "Note"
	PageType         ActivityVocabularyType = "Page"
	PlaceType        ActivityVocabularyType = "Place"
	ProfileType      ActivityVocabularyType = "Profile"
	RelationshipType ActivityVocabularyType = "Relationship"
	TombstoneType    ActivityVocabularyType = "Tombstone"
	VideoType        ActivityVocabularyType = "Video"

	// Link Types
	MentionType ActivityVocabularyType = "Mention"
)

var validGenericObjectTypes = [...]ActivityVocabularyType{
	ActivityType,
	IntransitiveActivityType,
	ObjectType,
	ActorType,
	CollectionType,
	OrderedCollectionType,
}

var validGenericLinkTypes = [...]ActivityVocabularyType{
	LinkType,
}

var validGenericTypes = append(validGenericObjectTypes[:], validGenericLinkTypes[:]...)

var validObjectTypes = [...]ActivityVocabularyType{
	ArticleType,
	AudioType,
	DocumentType,
	EventType,
	ImageType,
	NoteType,
	PageType,
	PlaceType,
	ProfileType,
	RelationshipType,
	TombstoneType,
	VideoType,
}

type (
	ActivityVocabularyType string
	ObjectOrLink           interface{}
	LinkOrUri              interface{}
	ImageOrLink            interface{}
	MimeType               string
	LangRef                string
	NaturalLanguageValue   map[LangRef]string
)

func (o *Object) IsLink() bool {
	return ValidLinkType(o.Type)
}

func (o *Object) IsObject() bool {
	return ValidObjectType(o.Type)
}

func (n NaturalLanguageValue) MarshalJSON() ([]byte, error) {
	if len(n) == 1 {
		for _, v := range n {
			return json.Marshal(v)
		}
	}

	return json.Marshal(map[LangRef]string(n))
}

// Describes an object of any kind.
// The Object type serves as the base type for most of the other kinds of objects defined in the Activity Vocabulary,
//  including other Core types such as Activity, IntransitiveActivity, Collection and OrderedCollection.
type BaseObject struct {
	// Provides the globally unique identifier for an Object or Link.
	Id ObjectId `jsonld:"id,omitempty"`
	//  Identifies the Object or Link type. Multiple values may be specified.
	Type ActivityVocabularyType `jsonld:"type,omitempty"`
	// A simple, human-readable, plain-text name for the object.
	// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values.
	Name NaturalLanguageValue `jsonld:"name,omitempty,collapsible"`
}

type Object struct {
	*BaseObject
	// Identifies a resource attached or related to an object that potentially requires special handling.
	// The intent is to provide a model that is at least semantically similar to attachments in email.
	Attachment ObjectOrLink `jsonld:"attachment,omitempty"`
	// Identifies one or more entities to which this object is attributed. The attributed entities might not be Actors.
	// For instance, an object might be attributed to the completion of another activity.
	AttributedTo ObjectOrLink `jsonld:"attributedTo,omitempty"`
	// Identifies one or more entities that represent the total population of entities
	//  for which the object can considered to be relevant.
	Audience ObjectOrLink `jsonld:"audience,omitempty"`
	// The content or textual representation of the Object encoded as a JSON string.
	// By default, the value of content is HTML.
	// The mediaType property can be used in the object to indicate a different content type.
	// (The content MAY be expressed using multiple language-tagged values.)
	Content NaturalLanguageValue `jsonld:"content,omitempty,collapsible"`
	// Identifies the context within which the object exists or an activity was performed.
	// The notion of "context" used is intentionally vague.
	// The intended function is to serve as a means of grouping objects and activities that share a
	//  common originating context or purpose. An example could be all activities relating to a common project or event.
	Context ObjectOrLink `jsonld:"context,collapsible"`
	// The date and time describing the actual or expected ending time of the object.
	// When used with an Activity object, for instance, the endTime property specifies the moment
	//  the activity concluded or is expected to conclude.
	EndTime time.Time `jsonld:"endTime,omitempty"`
	// Identifies the entity (e.g. an application) that generated the object.
	Generator ObjectOrLink `jsonld:"generator,omitempty"`
	// Indicates an entity that describes an icon for this object.
	// The image should have an aspect ratio of one (horizontal) to one (vertical)
	//  and should be suitable for presentation at a small size.
	Icon ImageOrLink `jsonld:"icon,omitempty"`
	// Indicates an entity that describes an image for this object.
	// Unlike the icon property, there are no aspect ratio or display size limitations assumed.
	Image ImageOrLink `jsonld:"image,omitempty"`
	// Indicates one or more entities for which this object is considered a response.
	InReplyTo ObjectOrLink `jsonld:"inReplyTo,omitempty"`
	// Indicates one or more physical or logical locations associated with the object.
	Location ObjectOrLink `jsonld:"location,omitempty"`
	// Identifies an entity that provides a preview of this object.
	Preview ObjectOrLink `jsonld:"preview,omitempty"`
	// The date and time at which the object was published
	Published time.Time `jsonld:"published,omitempty"`
	// Identifies a Collection containing objects considered to be responses to this object.
	Replies Collection `jsonld:"replies,omitempty"`
	// The date and time describing the actual or expected starting time of the object.
	// When used with an Activity object, for instance, the startTime property specifies
	//  the moment the activity began or is scheduled to begin.
	StartTime time.Time `jsonld:"startTime,omitempty"`
	// A natural language summarization of the object encoded as HTML.
	// *Multiple language tagged summaries may be provided.)
	Summary NaturalLanguageValue `jsonld:"summary,omitempty,collapsible"`
	// One or more "tags" that have been associated with an objects. A tag can be any kind of Object.
	// The key difference between attachment and tag is that the former implies association by inclusion,
	//  while the latter implies associated by reference.
	Tag ObjectOrLink `jsonld:"tag,omitempty"`
	// The date and time at which the object was updated
	Updated time.Time `jsonld:"updated,omitempty"`
	// Identifies one or more links to representations of the object
	Url LinkOrUri `jsonld:"url,omitempty"`
	// Identifies an entity considered to be part of the public primary audience of an Object
	To ObjectOrLink `jsonld:"to,omitempty"`
	// Identifies an Object that is part of the private primary audience of this Object.
	Bto ObjectOrLink `jsonld:"bto,omitempty"`
	// Identifies an Object that is part of the public secondary audience of this Object.
	Cc ObjectOrLink `jsonld:"cc,omitempty"`
	// Identifies one or more Objects that are part of the private secondary audience of this Object.
	Bcc ObjectOrLink `jsonld:"bcc,omitempty"`
	// When the object describes a time-bound resource, such as an audio or video, a meeting, etc,
	//  the duration property indicates the object's approximate duration.
	// The value must be expressed as an xsd:duration as defined by [ xmlschema11-2],
	//  section 3.3.6 (e.g. a period of 5 seconds is represented as "PT5S").
	Duration time.Duration `jsonld:"duration,omitempty"`
}

type ContentType string

type Source struct {
	Content   ContentType
	MediaType string
}

func ValidGenericType(_type ActivityVocabularyType) bool {
	for _, v := range validGenericObjectTypes {
		if v == _type {
			return true
		}
	}
	return false
}

func ValidObjectType(_type ActivityVocabularyType) bool {
	for _, v := range validObjectTypes {
		if v == _type {
			return true
		}
	}
	return ValidActivityType(_type) || ValidActorType(_type) || ValidCollectionType(_type) || ValidGenericType(_type)
}

func ObjectNew(id ObjectId, _type ActivityVocabularyType) *Object {
	if !(ValidObjectType(_type)) {
		_type = ObjectType
	}
	p := BaseObject{Id: id, Type: _type}
	p.Name = make(NaturalLanguageValue, 0)
	o := Object{BaseObject: &p}
	o.Content = make(NaturalLanguageValue, 0)
	o.Summary = make(NaturalLanguageValue, 0)
	return &o
}
<commit_msg>Remove context as a separate property of ActivityPub objects<commit_after>package activitypub

import (
	"encoding/json"
	"time"
)

type ObjectId string

const (
	ActivityBaseURI          URI                    = URI("https://www.w3.org/ns/activitystreams#")
	ObjectType               ActivityVocabularyType = "Object"
	LinkType                 ActivityVocabularyType = "Link"
	ActivityType             ActivityVocabularyType = "Activity"
	IntransitiveActivityType ActivityVocabularyType = "IntransitiveActivity"
	ActorType                ActivityVocabularyType = "Actor"
	CollectionType           ActivityVocabularyType = "Collection"
	OrderedCollectionType    ActivityVocabularyType = "OrderedCollection"

	// Object Types
	ArticleType      ActivityVocabularyType = "Article"
	AudioType        ActivityVocabularyType = "Audio"
	DocumentType     ActivityVocabularyType = "Document"
	EventType        ActivityVocabularyType = "Event"
	ImageType        ActivityVocabularyType = "Image"
	NoteType         ActivityVocabularyType = "Note"
	PageType         ActivityVocabularyType = "Page"
	PlaceType        ActivityVocabularyType = "Place"
	ProfileType      ActivityVocabularyType = "Profile"
	RelationshipType ActivityVocabularyType = "Relationship"
	TombstoneType    ActivityVocabularyType = "Tombstone"
	VideoType        ActivityVocabularyType = "Video"

	// Link Types
	MentionType ActivityVocabularyType = "Mention"
)

var validGenericObjectTypes = [...]ActivityVocabularyType{
	ActivityType,
	IntransitiveActivityType,
	ObjectType,
	ActorType,
	CollectionType,
	OrderedCollectionType,
}

var validGenericLinkTypes = [...]ActivityVocabularyType{
	LinkType,
}

var validGenericTypes = append(validGenericObjectTypes[:], validGenericLinkTypes[:]...)

var validObjectTypes = [...]ActivityVocabularyType{
	ArticleType,
	AudioType,
	DocumentType,
	EventType,
	ImageType,
	NoteType,
	PageType,
	PlaceType,
	ProfileType,
	RelationshipType,
	TombstoneType,
	VideoType,
}

type (
	ActivityVocabularyType string
	ObjectOrLink           interface{}
	LinkOrUri              interface{}
	ImageOrLink            interface{}
	MimeType               string
	LangRef                string
	NaturalLanguageValue   map[LangRef]string
)

func (o *Object) IsLink() bool {
	return ValidLinkType(o.Type)
}

func (o *Object) IsObject() bool {
	return ValidObjectType(o.Type)
}

func (n NaturalLanguageValue) MarshalJSON() ([]byte, error) {
	if len(n) == 1 {
		for _, v := range n {
			return json.Marshal(v)
		}
	}

	return json.Marshal(map[LangRef]string(n))
}

// Describes an object of any kind.
// The Object type serves as the base type for most of the other kinds of objects defined in the Activity Vocabulary,
//  including other Core types such as Activity, IntransitiveActivity, Collection and OrderedCollection.
type BaseObject struct {
	// Provides the globally unique identifier for an Object or Link.
	Id ObjectId `jsonld:"id,omitempty"`
	//  Identifies the Object or Link type. Multiple values may be specified.
	Type ActivityVocabularyType `jsonld:"type,omitempty"`
	// A simple, human-readable, plain-text name for the object.
	// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values.
	Name NaturalLanguageValue `jsonld:"name,omitempty,collapsible"`
}

type Object struct {
	*BaseObject
	// Identifies a resource attached or related to an object that potentially requires special handling.
	// The intent is to provide a model that is at least semantically similar to attachments in email.
	Attachment ObjectOrLink `jsonld:"attachment,omitempty"`
	// Identifies one or more entities to which this object is attributed. The attributed entities might not be Actors.
	// For instance, an object might be attributed to the completion of another activity.
	AttributedTo ObjectOrLink `jsonld:"attributedTo,omitempty"`
	// Identifies one or more entities that represent the total population of entities
	//  for which the object can considered to be relevant.
	Audience ObjectOrLink `jsonld:"audience,omitempty"`
	// The content or textual representation of the Object encoded as a JSON string.
	// By default, the value of content is HTML.
	// The mediaType property can be used in the object to indicate a different content type.
	// (The content MAY be expressed using multiple language-tagged values.)
	Content NaturalLanguageValue `jsonld:"content,omitempty,collapsible"`
	// Identifies the context within which the object exists or an activity was performed.
	// The notion of "context" used is intentionally vague.
	// The intended function is to serve as a means of grouping objects and activities that share a
	//  common originating context or purpose. An example could be all activities relating to a common project or event.
	//Context ObjectOrLink `jsonld:"_"`
	// The date and time describing the actual or expected ending time of the object.
	// When used with an Activity object, for instance, the endTime property specifies the moment
	//  the activity concluded or is expected to conclude.
	EndTime time.Time `jsonld:"endTime,omitempty"`
	// Identifies the entity (e.g. an application) that generated the object.
	Generator ObjectOrLink `jsonld:"generator,omitempty"`
	// Indicates an entity that describes an icon for this object.
	// The image should have an aspect ratio of one (horizontal) to one (vertical)
	//  and should be suitable for presentation at a small size.
	Icon ImageOrLink `jsonld:"icon,omitempty"`
	// Indicates an entity that describes an image for this object.
	// Unlike the icon property, there are no aspect ratio or display size limitations assumed.
	Image ImageOrLink `jsonld:"image,omitempty"`
	// Indicates one or more entities for which this object is considered a response.
	InReplyTo ObjectOrLink `jsonld:"inReplyTo,omitempty"`
	// Indicates one or more physical or logical locations associated with the object.
	Location ObjectOrLink `jsonld:"location,omitempty"`
	// Identifies an entity that provides a preview of this object.
	Preview ObjectOrLink `jsonld:"preview,omitempty"`
	// The date and time at which the object was published
	Published time.Time `jsonld:"published,omitempty"`
	// Identifies a Collection containing objects considered to be responses to this object.
	Replies Collection `jsonld:"replies,omitempty"`
	// The date and time describing the actual or expected starting time of the object.
	// When used with an Activity object, for instance, the startTime property specifies
	//  the moment the activity began or is scheduled to begin.
	StartTime time.Time `jsonld:"startTime,omitempty"`
	// A natural language summarization of the object encoded as HTML.
	// *Multiple language tagged summaries may be provided.)
	Summary NaturalLanguageValue `jsonld:"summary,omitempty,collapsible"`
	// One or more "tags" that have been associated with an objects. A tag can be any kind of Object.
	// The key difference between attachment and tag is that the former implies association by inclusion,
	//  while the latter implies associated by reference.
	Tag ObjectOrLink `jsonld:"tag,omitempty"`
	// The date and time at which the object was updated
	Updated time.Time `jsonld:"updated,omitempty"`
	// Identifies one or more links to representations of the object
	Url LinkOrUri `jsonld:"url,omitempty"`
	// Identifies an entity considered to be part of the public primary audience of an Object
	To ObjectOrLink `jsonld:"to,omitempty"`
	// Identifies an Object that is part of the private primary audience of this Object.
	Bto ObjectOrLink `jsonld:"bto,omitempty"`
	// Identifies an Object that is part of the public secondary audience of this Object.
	Cc ObjectOrLink `jsonld:"cc,omitempty"`
	// Identifies one or more Objects that are part of the private secondary audience of this Object.
	Bcc ObjectOrLink `jsonld:"bcc,omitempty"`
	// When the object describes a time-bound resource, such as an audio or video, a meeting, etc,
	//  the duration property indicates the object's approximate duration.
	// The value must be expressed as an xsd:duration as defined by [ xmlschema11-2],
	//  section 3.3.6 (e.g. a period of 5 seconds is represented as "PT5S").
	Duration time.Duration `jsonld:"duration,omitempty"`
}

type ContentType string

type Source struct {
	Content   ContentType
	MediaType string
}

func ValidGenericType(_type ActivityVocabularyType) bool {
	for _, v := range validGenericObjectTypes {
		if v == _type {
			return true
		}
	}
	return false
}

func ValidObjectType(_type ActivityVocabularyType) bool {
	for _, v := range validObjectTypes {
		if v == _type {
			return true
		}
	}
	return ValidActivityType(_type) || ValidActorType(_type) || ValidCollectionType(_type) || ValidGenericType(_type)
}

func ObjectNew(id ObjectId, _type ActivityVocabularyType) *Object {
	if !(ValidObjectType(_type)) {
		_type = ObjectType
	}
	p := BaseObject{Id: id, Type: _type}
	p.Name = make(NaturalLanguageValue, 0)
	o := Object{BaseObject: &p}
	o.Content = make(NaturalLanguageValue, 0)
	o.Summary = make(NaturalLanguageValue, 0)
	return &o
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"flag"
	"fmt"
	"math/rand"
	"time"

	exec "github.com/mesos/mesos-go/executor"
	mesos "github.com/mesos/mesos-go/mesosproto"
)

var (
	slowTasks = flag.Bool("slow_tasks", false, "When true tasks will take several seconds before responding with TASK_FINISHED; useful for debugging failover")
)

type exampleExecutor struct {
	tasksLaunched int
}

func newExampleExecutor() *exampleExecutor {
	return &exampleExecutor{tasksLaunched: 0}
}

func (exec *exampleExecutor) Registered(driver exec.ExecutorDriver, execInfo *mesos.ExecutorInfo, fwinfo *mesos.FrameworkInfo, slaveInfo *mesos.SlaveInfo) {
	fmt.Println("Registered Executor on slave ", slaveInfo.GetHostname())
}

func (exec *exampleExecutor) Reregistered(driver exec.ExecutorDriver, slaveInfo *mesos.SlaveInfo) {
	fmt.Println("Re-registered Executor on slave ", slaveInfo.GetHostname())
}

func (exec *exampleExecutor) Disconnected(exec.ExecutorDriver) {
	fmt.Println("Executor disconnected.")
}

func (exec *exampleExecutor) LaunchTask(driver exec.ExecutorDriver, taskInfo *mesos.TaskInfo) {
	fmt.Println("Launching task", taskInfo.GetName(), "with command", taskInfo.Command.GetValue())

	runStatus := &mesos.TaskStatus{
		TaskId: taskInfo.GetTaskId(),
		State:  mesos.TaskState_TASK_RUNNING.Enum(),
	}
	_, err := driver.SendStatusUpdate(runStatus)
	if err != nil {
		fmt.Println("Got error", err)
	}

	exec.tasksLaunched++
	fmt.Println("Total tasks launched ", exec.tasksLaunched)
	//
	// this is where one would perform the requested task
	//
	finishTask := func() {
		// finish task
		fmt.Println("Finishing task", taskInfo.GetName())
		finStatus := &mesos.TaskStatus{
			TaskId: taskInfo.GetTaskId(),
			State:  mesos.TaskState_TASK_FINISHED.Enum(),
		}
		if _, err := driver.SendStatusUpdate(finStatus); err != nil {
			fmt.Println("error sending FINISHED", err)
		}
		fmt.Println("Task finished", taskInfo.GetName())
	}
	if *slowTasks {
		starting := &mesos.TaskStatus{
			TaskId: taskInfo.GetTaskId(),
			State:  mesos.TaskState_TASK_STARTING.Enum(),
		}
		if _, err := driver.SendStatusUpdate(starting); err != nil {
			fmt.Println("error sending STARTING", err)
		}
		delay := time.Duration(rand.Intn(90)+10) * time.Second
		go func() {
			time.Sleep(delay) // TODO(jdef) add jitter
			finishTask()
		}()
	} else {
		finishTask()
	}
}

func (exec *exampleExecutor) KillTask(exec.ExecutorDriver, *mesos.TaskID) {
	fmt.Println("Kill task")
}

func (exec *exampleExecutor) FrameworkMessage(driver exec.ExecutorDriver, msg string) {
	fmt.Println("Got framework message: ", msg)
}

func (exec *exampleExecutor) Shutdown(exec.ExecutorDriver) {
	fmt.Println("Shutting down the executor")
}

func (exec *exampleExecutor) Error(driver exec.ExecutorDriver, err string) {
	fmt.Println("Got error message:", err)
}

// -------------------------- func inits () ----------------- //
func init() {
	flag.Parse()
}

func main() {
	fmt.Println("Starting Example Executor (Go)")

	dconfig := exec.DriverConfig{
		Executor: newExampleExecutor(),
	}
	driver, err := exec.NewMesosExecutorDriver(dconfig)

	if err != nil {
		fmt.Println("Unable to create a ExecutorDriver ", err.Error())
	}

	_, err = driver.Start()
	if err != nil {
		fmt.Println("Got error:", err)
		return
	}
	fmt.Println("Executor process has started and running.")
	_, err = driver.Join()
	if err != nil {
		fmt.Println("driver failed:", err)
	}
	fmt.Println("executor terminating")
}
<commit_msg>give a few parameters names (where omitted)<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"flag"
	"fmt"
	"math/rand"
	"time"

	exec "github.com/mesos/mesos-go/executor"
	mesos "github.com/mesos/mesos-go/mesosproto"
)

var (
	slowTasks = flag.Bool("slow_tasks", false, "When true tasks will take several seconds before responding with TASK_FINISHED; useful for debugging failover")
)

type exampleExecutor struct {
	tasksLaunched int
}

func newExampleExecutor() *exampleExecutor {
	return &exampleExecutor{tasksLaunched: 0}
}

func (exec *exampleExecutor) Registered(driver exec.ExecutorDriver, execInfo *mesos.ExecutorInfo, fwinfo *mesos.FrameworkInfo, slaveInfo *mesos.SlaveInfo) {
	fmt.Println("Registered Executor on slave ", slaveInfo.GetHostname())
}

func (exec *exampleExecutor) Reregistered(driver exec.ExecutorDriver, slaveInfo *mesos.SlaveInfo) {
	fmt.Println("Re-registered Executor on slave ", slaveInfo.GetHostname())
}

func (exec *exampleExecutor) Disconnected(driver exec.ExecutorDriver) {
	fmt.Println("Executor disconnected.")
}

func (exec *exampleExecutor) LaunchTask(driver exec.ExecutorDriver, taskInfo *mesos.TaskInfo) {
	fmt.Println("Launching task", taskInfo.GetName(), "with command", taskInfo.Command.GetValue())

	runStatus := &mesos.TaskStatus{
		TaskId: taskInfo.GetTaskId(),
		State:  mesos.TaskState_TASK_RUNNING.Enum(),
	}
	_, err := driver.SendStatusUpdate(runStatus)
	if err != nil {
		fmt.Println("Got error", err)
	}

	exec.tasksLaunched++
	fmt.Println("Total tasks launched ", exec.tasksLaunched)
	//
	// this is where one would perform the requested task
	//
	finishTask := func() {
		// finish task
		fmt.Println("Finishing task", taskInfo.GetName())
		finStatus := &mesos.TaskStatus{
			TaskId: taskInfo.GetTaskId(),
			State:  mesos.TaskState_TASK_FINISHED.Enum(),
		}
		if _, err := driver.SendStatusUpdate(finStatus); err != nil {
			fmt.Println("error sending FINISHED", err)
		}
		fmt.Println("Task finished", taskInfo.GetName())
	}
	if *slowTasks {
		starting := &mesos.TaskStatus{
			TaskId: taskInfo.GetTaskId(),
			State:  mesos.TaskState_TASK_STARTING.Enum(),
		}
		if _, err := driver.SendStatusUpdate(starting); err != nil {
			fmt.Println("error sending STARTING", err)
		}
		delay := time.Duration(rand.Intn(90)+10) * time.Second
		go func() {
			time.Sleep(delay) // TODO(jdef) add jitter
			finishTask()
		}()
	} else {
		finishTask()
	}
}

func (exec *exampleExecutor) KillTask(driver exec.ExecutorDriver, taskID *mesos.TaskID) {
	fmt.Println("Kill task")
}

func (exec *exampleExecutor) FrameworkMessage(driver exec.ExecutorDriver, msg string) {
	fmt.Println("Got framework message: ", msg)
}

func (exec *exampleExecutor) Shutdown(driver exec.ExecutorDriver) {
	fmt.Println("Shutting down the executor")
}

func (exec *exampleExecutor) Error(driver exec.ExecutorDriver, err string) {
	fmt.Println("Got error message:", err)
}

// -------------------------- func inits () ----------------- //
func init() {
	flag.Parse()
}

func main() {
	fmt.Println("Starting Example Executor (Go)")

	dconfig := exec.DriverConfig{
		Executor: newExampleExecutor(),
	}
	driver, err := exec.NewMesosExecutorDriver(dconfig)

	if err != nil {
		fmt.Println("Unable to create a ExecutorDriver ", err.Error())
	}

	_, err = driver.Start()
	if err != nil {
		fmt.Println("Got error:", err)
		return
	}
	fmt.Println("Executor process has started and running.")
	_, err = driver.Join()
	if err != nil {
		fmt.Println("driver failed:", err)
	}
	fmt.Println("executor terminating")
}
<|endoftext|>


<commit_before>package splash

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	Reset Property = iota
	Bold
	Dim
	Italic
	Underline
	Blink
	FastBlink
	Reverse
	Hidden
	CrossedOut
)

const (
	Black Property = iota + 30
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	White
)

const (
	BgBlack Property = iota + 40
	BgRed
	BgGreen
	BgYellow
	BgBlue
	BgMagenta
	BgCyan
	BgWhite
)

var attributes map[rune]Property = map[rune]Property{
	'b': Bold,
	'd': Dim,
	'i': Italic,
	'u': Underline,
	'B': Blink,
	'f': FastBlink,
	'r': Reverse,
	'h': Hidden,
	'c': CrossedOut,
}

var colors map[string]Property = map[string]Property{
	"black":   Black,
	"red":     Red,
	"green":   Green,
	"yellow":  Yellow,
	"blue":    Blue,
	"magenta": Magenta,
	"cyan":    Cyan,
	"white":   White,
}

type Property int

func (p Property) String() string {
	return fmt.Sprintf("\u001b[%dm", p)
}

func (p Property) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprint(a...), Reset)
}

func (p Property) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprintf(format, a...), Reset)
}

func (p Property) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", p, fmt.Sprint(a...), Reset)
}

type Style []Property

func NewStyle(props ...Property) Style {
	return props
}

func ParseStyle(style string) Style {
	props := []Property{}

	style = strings.ToLower(strings.Replace(style, " ", "", -1))
	if style == "" {
		return props
	}

	if style == "reset" {
		return append(props, Reset)
	}

	// Parse attributes
	tokens := strings.Split(style, "+")
	if len(tokens) > 1 {
		for _, attr := range tokens[1] {
			if prop, ok := attributes[attr]; ok {
				props = append(props, prop)
			}
		}
	}

	// Parse colors
	tokens = strings.Split(tokens[0], ":")
	if prop, ok := colors[tokens[0]]; ok {
		props = append(props, prop)
	}

	if len(tokens) > 1 {
		if prop, ok := colors[tokens[1]]; ok {
			// Add property as a background color
			props = append(props, prop+10)
		}
	}

	return props
}

func (s Style) String() string {
	var buffer bytes.Buffer
	for _, prop := range s {
		buffer.WriteString(prop.String())
	}

	return buffer.String()
}

func (s Style) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprint(a...), Reset)
}

func (s Style) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprintf(format, a...), Reset)
}

func (s Style) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", s, fmt.Sprint(a...), Reset)
}
<commit_msg>Add godoc documentation for types and methods<commit_after>package splash

import (
	"bytes"
	"fmt"
	"strings"
)

const (
	Reset Property = iota
	Bold
	Dim
	Italic
	Underline
	Blink
	FastBlink
	Reverse
	Hidden
	CrossedOut
)

const (
	Black Property = iota + 30
	Red
	Green
	Yellow
	Blue
	Magenta
	Cyan
	White
)

const (
	BgBlack Property = iota + 40
	BgRed
	BgGreen
	BgYellow
	BgBlue
	BgMagenta
	BgCyan
	BgWhite
)

var attributes map[rune]Property = map[rune]Property{
	'b': Bold,
	'd': Dim,
	'i': Italic,
	'u': Underline,
	'B': Blink,
	'f': FastBlink,
	'r': Reverse,
	'h': Hidden,
	'c': CrossedOut,
}

var colors map[string]Property = map[string]Property{
	"black":   Black,
	"red":     Red,
	"green":   Green,
	"yellow":  Yellow,
	"blue":    Blue,
	"magenta": Magenta,
	"cyan":    Cyan,
	"white":   White,
}

// Property represents either a color (foreground or background) or a text
// attribute (bold, underline, etc.).
type Property int

// String returns the printable string representation of the property.
func (p Property) String() string {
	return fmt.Sprintf("\u001b[%dm", p)
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the property and
// a reset is applied at end.
func (p Property) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the property
// and a style is applied at end.
func (p Property) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", p, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the
// property and a reset is applied at end.
func (p Property) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", p, fmt.Sprint(a...), Reset)
}

// Style is a reusable collection of properties.
type Style []Property

// NewStyle returns a new style from property arguments passed in.
func NewStyle(props ...Property) Style {
	return props
}

// ParseStyle returns a new style from the properties specified in the style
// parameter.
func ParseStyle(style string) Style {
	props := []Property{}

	style = strings.ToLower(strings.Replace(style, " ", "", -1))
	if style == "" {
		return props
	}

	if style == "reset" {
		return append(props, Reset)
	}

	// Parse attributes
	tokens := strings.Split(style, "+")
	if len(tokens) > 1 {
		for _, attr := range tokens[1] {
			if prop, ok := attributes[attr]; ok {
				props = append(props, prop)
			}
		}
	}

	// Parse colors
	tokens = strings.Split(tokens[0], ":")
	if prop, ok := colors[tokens[0]]; ok {
		props = append(props, prop)
	}

	if len(tokens) > 1 {
		if prop, ok := colors[tokens[1]]; ok {
			// Add property as a background color
			props = append(props, prop+10)
		}
	}

	return props
}

// String returns the printable string representation of the style.
func (s Style) String() string {
	var buffer bytes.Buffer
	for _, prop := range s {
		buffer.WriteString(prop.String())
	}

	return buffer.String()
}

// Sprint formats using the default formats for its operands and returns the
// resulting string. Spaces are added between operands when neither is a
// string. The output string is wrapped in the value of the style and a reset
// is applied at end.
func (s Style) Sprint(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprint(a...), Reset)
}

// Sprintf formats according to a format specifier and returns the
// resulting string. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintf(format string, a ...interface{}) string {
	return fmt.Sprintf("%s%s%s", s, fmt.Sprintf(format, a...), Reset)
}

// Sprintln formats using the default formats for its operands and returns
// the resulting string. Spaces are always added between operands and a
// newline is appended. The output string is wrapped in the value of the style
// and a reset is applied at end.
func (s Style) Sprintln(a ...interface{}) string {
	return fmt.Sprintf("%s%s%s\n", s, fmt.Sprint(a...), Reset)
}
<|endoftext|>