<commit_before>package mcstore

import (
	"time"

	"fmt"

	rethinkdb "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstore/uploads"
	"github.com/willf/bitset"
)

var _ = fmt.Println

// An uploadResource handles all upload requests.
type uploadResource struct {
	log *app.Logger
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	RequestID   string    `json:"request_id"`
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// newUploadResource creates a new upload resource
func newUploadResource() rest.Service {
	return &uploadResource{
		log: app.NewLog("resource", "upload"),
	}
}

// WebService creates an instance of the upload web service.
func (self *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)

	ws.Route(ws.POST("").Filter(projectAccessFilter).Filter(directoryFilter).To(rest.RouteHandler(self.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateUploadRequest{}).
		Writes(CreateUploadResponse{}))

	ws.Route(ws.POST("/chunk").To(rest.RouteHandler(self.uploadFileChunk)).
		Consumes("multipart/form-data").
		Writes(UploadChunkResponse{}).
		Doc("Upload a file chunk"))

	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(self.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))

	ws.Route(ws.GET("{project}").Filter(projectAccessFilter).To(rest.RouteHandler(self.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateUploadRequest struct {
	ProjectID   string `json:"project_id"`
	DirectoryID string `json:"directory_id"`
	FileName    string `json:"filename"`
	FileSize    int64  `json:"filesize"`
	ChunkSize   int32  `json:"chunk_size"`
	FileMTime   string `json:"filemtime"`
	Checksum    string `json: "checksum"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateUploadResponse struct {
	RequestID     string `json:"request_id"`
	StartingBlock uint   `json:"starting_block"`
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (self *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	if cr, err := request2IDRequest(request, user.ID); err != nil {
		app.Log.Debugf("request2IDRequst failed", err)
		return nil, err
	} else {
		session := request.Attribute("session").(*rethinkdb.Session)
		project := request.Attribute("project").(schema.Project)
		directory := request.Attribute("directory").(schema.Directory)
		idService := uploads.NewIDService(session)

		if upload, err := idService.ID(cr, &project, &directory); err != nil {
			app.Log.Debugf("idService.ID failed", err)
			return nil, err
		} else {
			startingBlock := findStartingBlock(upload.File.Blocks)
			resp := CreateUploadResponse{
				RequestID:     upload.ID,
				StartingBlock: startingBlock,
			}
			return &resp, nil
		}
	}
}

// request2IDRequest fills out an id request to send to the idService. It handles request parameter errors.
func request2IDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var (
		req CreateUploadRequest
		cr  uploads.IDRequest
	)

	if err := request.ReadEntity(&req); err != nil {
		app.Log.Debugf("request2IDRequest ReadEntity failed: %s", err)
		return cr, err
	}

	app.Log.Debugf("CreateUploadRequest %#v", req)

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		app.Log.Debugf("request2IDRequest time.Parse failed on %s: %s", req.FileMTime, err)
		return cr, err
	}

	if req.ChunkSize == 0 {
		req.ChunkSize = 1024 * 1024
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: req.DirectoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		ChunkSize:   req.ChunkSize,
		Checksum:    req.Checksum,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil
}

// findStartingBlock returns the block to start at. Blocks start
// at 1, since this is what the flow.js client expects. This
// method takes that into account.
func findStartingBlock(blocks *bitset.BitSet) uint {
	if blocks.None() {
		// Nothing set, start at 1
		return 1
	}

	// Create the complement and return first set.
	complement := blocks.Complement()
	if block, status := complement.NextSet(0); !status {
		// This shouldn't happen, but safest case is to check
		// for it and tell the client to resend everything.
		return 1
	} else {
		return block + 1
	}
}

type UploadChunkResponse struct {
	FileID string `json:"file_id"`
	Done   bool   `json:"done"`
}

// uploadFileChunk uploads a new file chunk.
func (self *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		self.log.Errorf("Error converting form to flow.Request: %s", err)
		return nil, err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}

	uploadService := uploads.NewUploadService(session)
	if uploadStatus, err := uploadService.Upload(&req); err != nil {
		return nil, err
	} else {
		uploadResp := &UploadChunkResponse{
			FileID: uploadStatus.FileID,
			Done:   uploadStatus.Done,
		}
		return uploadResp, nil
	}
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (self *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDService(session)
	uploadID := request.PathParameter("id")
	return idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requester
// has access to the project.
func (self *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDService(session)
	project := request.Attribute("project").(schema.Project)
	entries, err := idService.UploadsForProject(project.ID)
	switch {
	case err == app.ErrNotFound:
		var uploads []UploadEntry
		return uploads, nil
	case err != nil:
		return nil, err
	default:
		return uploads2uploadEntries(entries), nil
	}
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			RequestID:   entry.ID,
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<commit_msg>Remove extra space.<commit_after>package mcstore

import (
	"time"

	"fmt"

	rethinkdb "github.com/dancannon/gorethink"
	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstore/uploads"
	"github.com/willf/bitset"
)

var _ = fmt.Println

// An uploadResource handles all upload requests.
type uploadResource struct {
	log *app.Logger
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	RequestID   string    `json:"request_id"`
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// newUploadResource creates a new upload resource
func newUploadResource() rest.Service {
	return &uploadResource{
		log: app.NewLog("resource", "upload"),
	}
}

// WebService creates an instance of the upload web service.
func (self *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)

	ws.Route(ws.POST("").Filter(projectAccessFilter).Filter(directoryFilter).To(rest.RouteHandler(self.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateUploadRequest{}).
		Writes(CreateUploadResponse{}))

	ws.Route(ws.POST("/chunk").To(rest.RouteHandler(self.uploadFileChunk)).
		Consumes("multipart/form-data").
		Writes(UploadChunkResponse{}).
		Doc("Upload a file chunk"))

	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(self.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))

	ws.Route(ws.GET("{project}").Filter(projectAccessFilter).To(rest.RouteHandler(self.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateUploadRequest struct {
	ProjectID   string `json:"project_id"`
	DirectoryID string `json:"directory_id"`
	FileName    string `json:"filename"`
	FileSize    int64  `json:"filesize"`
	ChunkSize   int32  `json:"chunk_size"`
	FileMTime   string `json:"filemtime"`
	Checksum    string `json:"checksum"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateUploadResponse struct {
	RequestID     string `json:"request_id"`
	StartingBlock uint   `json:"starting_block"`
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (self *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	if cr, err := request2IDRequest(request, user.ID); err != nil {
		app.Log.Debugf("request2IDRequst failed", err)
		return nil, err
	} else {
		session := request.Attribute("session").(*rethinkdb.Session)
		project := request.Attribute("project").(schema.Project)
		directory := request.Attribute("directory").(schema.Directory)
		idService := uploads.NewIDService(session)

		if upload, err := idService.ID(cr, &project, &directory); err != nil {
			app.Log.Debugf("idService.ID failed", err)
			return nil, err
		} else {
			startingBlock := findStartingBlock(upload.File.Blocks)
			resp := CreateUploadResponse{
				RequestID:     upload.ID,
				StartingBlock: startingBlock,
			}
			return &resp, nil
		}
	}
}

// request2IDRequest fills out an id request to send to the idService. It handles request parameter errors.
func request2IDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var (
		req CreateUploadRequest
		cr  uploads.IDRequest
	)

	if err := request.ReadEntity(&req); err != nil {
		app.Log.Debugf("request2IDRequest ReadEntity failed: %s", err)
		return cr, err
	}

	app.Log.Debugf("CreateUploadRequest %#v", req)

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		app.Log.Debugf("request2IDRequest time.Parse failed on %s: %s", req.FileMTime, err)
		return cr, err
	}

	if req.ChunkSize == 0 {
		req.ChunkSize = 1024 * 1024
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: req.DirectoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		ChunkSize:   req.ChunkSize,
		Checksum:    req.Checksum,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil
}

// findStartingBlock returns the block to start at. Blocks start
// at 1, since this is what the flow.js client expects. This
// method takes that into account.
func findStartingBlock(blocks *bitset.BitSet) uint {
	if blocks.None() {
		// Nothing set, start at 1
		return 1
	}

	// Create the complement and return first set.
	complement := blocks.Complement()
	if block, status := complement.NextSet(0); !status {
		// This shouldn't happen, but safest case is to check
		// for it and tell the client to resend everything.
		return 1
	} else {
		return block + 1
	}
}

type UploadChunkResponse struct {
	FileID string `json:"file_id"`
	Done   bool   `json:"done"`
}

// uploadFileChunk uploads a new file chunk.
func (self *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		self.log.Errorf("Error converting form to flow.Request: %s", err)
		return nil, err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}

	uploadService := uploads.NewUploadService(session)
	if uploadStatus, err := uploadService.Upload(&req); err != nil {
		return nil, err
	} else {
		uploadResp := &UploadChunkResponse{
			FileID: uploadStatus.FileID,
			Done:   uploadStatus.Done,
		}
		return uploadResp, nil
	}
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (self *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDService(session)
	uploadID := request.PathParameter("id")
	return idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requester
// has access to the project.
func (self *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	session := request.Attribute("session").(*rethinkdb.Session)
	idService := uploads.NewIDService(session)
	project := request.Attribute("project").(schema.Project)
	entries, err := idService.UploadsForProject(project.ID)
	switch {
	case err == app.ErrNotFound:
		var uploads []UploadEntry
		return uploads, nil
	case err != nil:
		return nil, err
	default:
		return uploads2uploadEntries(entries), nil
	}
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			RequestID:   entry.ID,
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<|endoftext|>


<commit_before>package slack

import "encoding/json"

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// AttachmentAction is a button or menu to be included in the attachment. Required when
// using message buttons or menus and otherwise not useful. A maximum of 5 actions may be
// provided per attachment.
type AttachmentAction struct {
	Name            string                        `json:"name"`                       // Required.
	Text            string                        `json:"text"`                       // Required.
	Style           string                        `json:"style,omitempty"`            // Optional. Allowed values: "default", "primary", "danger".
	Type            string                        `json:"type"`                       // Required. Must be set to "button" or "select".
	Value           string                        `json:"value,omitempty"`            // Optional.
	DataSource      string                        `json:"data_source,omitempty"`      // Optional.
	MinQueryLength  int                           `json:"min_query_length,omitempty"` // Optional. Default value is 1.
	Options         []AttachmentActionOption      `json:"options,omitempty"`          // Optional. Maximum of 100 options can be provided in each menu.
	SelectedOptions []AttachmentActionOption      `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
	OptionGroups    []AttachmentActionOptionGroup `json:"option_groups,omitempty"`    // Optional.
	Confirm         *ConfirmationField            `json:"confirm,omitempty"`          // Optional.
	URL             string                        `json:"url,omitempty"`              // Optional.
}

// AttachmentActionOption the individual option to appear in action menu.
type AttachmentActionOption struct {
	Text        string `json:"text"`                  // Required.
	Value       string `json:"value"`                 // Required.
	Description string `json:"description,omitempty"` // Optional. Up to 30 characters.
}

// AttachmentActionOptionGroup is a semi-hierarchal way to list available options to appear in action menu.
type AttachmentActionOptionGroup struct {
	Text    string                   `json:"text"`    // Required.
	Options []AttachmentActionOption `json:"options"` // Required.
}

// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
type AttachmentActionCallback struct {
	Actions    []AttachmentAction `json:"actions"`
	CallbackID string             `json:"callback_id"`
	Team       Team               `json:"team"`
	Channel    Channel            `json:"channel"`
	User       User               `json:"user"`

	Name  string `json:"name"`
	Value string `json:"value"`

	OriginalMessage Message `json:"original_message"`

	ActionTs     string `json:"action_ts"`
	MessageTs    string `json:"message_ts"`
	AttachmentID string `json:"attachment_id"`
	Token        string `json:"token"`
	ResponseURL  string `json:"response_url"`
	TriggerID    string `json:"trigger_id"`
}

// ConfirmationField are used to ask users to confirm actions
type ConfirmationField struct {
	Title       string `json:"title,omitempty"`        // Optional.
	Text        string `json:"text"`                   // Required.
	OkText      string `json:"ok_text,omitempty"`      // Optional. Defaults to "Okay"
	DismissText string `json:"dismiss_text,omitempty"` // Optional. Defaults to "Cancel"
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Color    string `json:"color,omitempty"`
	Fallback string `json:"fallback"`

	CallbackID string `json:"callback_id,omitempty"`
	ID         int    `json:"id,omitempty"`

	AuthorName    string `json:"author_name,omitempty"`
	AuthorSubname string `json:"author_subname,omitempty"`
	AuthorLink    string `json:"author_link,omitempty"`
	AuthorIcon    string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`
	Pretext   string `json:"pretext,omitempty"`
	Text      string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`
	ThumbURL string `json:"thumb_url,omitempty"`

	Fields     []AttachmentField  `json:"fields,omitempty"`
	Actions    []AttachmentAction `json:"actions,omitempty"`
	MarkdownIn []string           `json:"mrkdwn_in,omitempty"`

	Footer     string `json:"footer,omitempty"`
	FooterIcon string `json:"footer_icon,omitempty"`

	Ts json.Number `json:"ts,omitempty"`
}
<commit_msg>Add AuthorID to Attachment struct<commit_after>package slack

import "encoding/json"

// AttachmentField contains information for an attachment field
// An Attachment can contain multiple of these
type AttachmentField struct {
	Title string `json:"title"`
	Value string `json:"value"`
	Short bool   `json:"short"`
}

// AttachmentAction is a button or menu to be included in the attachment. Required when
// using message buttons or menus and otherwise not useful. A maximum of 5 actions may be
// provided per attachment.
type AttachmentAction struct {
	Name            string                        `json:"name"`                       // Required.
	Text            string                        `json:"text"`                       // Required.
	Style           string                        `json:"style,omitempty"`            // Optional. Allowed values: "default", "primary", "danger".
	Type            string                        `json:"type"`                       // Required. Must be set to "button" or "select".
	Value           string                        `json:"value,omitempty"`            // Optional.
	DataSource      string                        `json:"data_source,omitempty"`      // Optional.
	MinQueryLength  int                           `json:"min_query_length,omitempty"` // Optional. Default value is 1.
	Options         []AttachmentActionOption      `json:"options,omitempty"`          // Optional. Maximum of 100 options can be provided in each menu.
	SelectedOptions []AttachmentActionOption      `json:"selected_options,omitempty"` // Optional. The first element of this array will be set as the pre-selected option for this menu.
	OptionGroups    []AttachmentActionOptionGroup `json:"option_groups,omitempty"`    // Optional.
	Confirm         *ConfirmationField            `json:"confirm,omitempty"`          // Optional.
	URL             string                        `json:"url,omitempty"`              // Optional.
}

// AttachmentActionOption the individual option to appear in action menu.
type AttachmentActionOption struct {
	Text        string `json:"text"`                  // Required.
	Value       string `json:"value"`                 // Required.
	Description string `json:"description,omitempty"` // Optional. Up to 30 characters.
}

// AttachmentActionOptionGroup is a semi-hierarchal way to list available options to appear in action menu.
type AttachmentActionOptionGroup struct {
	Text    string                   `json:"text"`    // Required.
	Options []AttachmentActionOption `json:"options"` // Required.
}

// AttachmentActionCallback is sent from Slack when a user clicks a button in an interactive message (aka AttachmentAction)
type AttachmentActionCallback struct {
	Actions    []AttachmentAction `json:"actions"`
	CallbackID string             `json:"callback_id"`
	Team       Team               `json:"team"`
	Channel    Channel            `json:"channel"`
	User       User               `json:"user"`

	Name  string `json:"name"`
	Value string `json:"value"`

	OriginalMessage Message `json:"original_message"`

	ActionTs     string `json:"action_ts"`
	MessageTs    string `json:"message_ts"`
	AttachmentID string `json:"attachment_id"`
	Token        string `json:"token"`
	ResponseURL  string `json:"response_url"`
	TriggerID    string `json:"trigger_id"`
}

// ConfirmationField are used to ask users to confirm actions
type ConfirmationField struct {
	Title       string `json:"title,omitempty"`        // Optional.
	Text        string `json:"text"`                   // Required.
	OkText      string `json:"ok_text,omitempty"`      // Optional. Defaults to "Okay"
	DismissText string `json:"dismiss_text,omitempty"` // Optional. Defaults to "Cancel"
}

// Attachment contains all the information for an attachment
type Attachment struct {
	Color    string `json:"color,omitempty"`
	Fallback string `json:"fallback"`

	CallbackID string `json:"callback_id,omitempty"`
	ID         int    `json:"id,omitempty"`

	AuthorID      string `json:"author_id,omitempty"`
	AuthorName    string `json:"author_name,omitempty"`
	AuthorSubname string `json:"author_subname,omitempty"`
	AuthorLink    string `json:"author_link,omitempty"`
	AuthorIcon    string `json:"author_icon,omitempty"`

	Title     string `json:"title,omitempty"`
	TitleLink string `json:"title_link,omitempty"`
	Pretext   string `json:"pretext,omitempty"`
	Text      string `json:"text"`

	ImageURL string `json:"image_url,omitempty"`
	ThumbURL string `json:"thumb_url,omitempty"`

	Fields     []AttachmentField  `json:"fields,omitempty"`
	Actions    []AttachmentAction `json:"actions,omitempty"`
	MarkdownIn []string           `json:"mrkdwn_in,omitempty"`

	Footer     string `json:"footer,omitempty"`
	FooterIcon string `json:"footer_icon,omitempty"`

	Ts json.Number `json:"ts,omitempty"`
}
<|endoftext|>


<commit_before>package topgun_test

import (
	_ "github.com/lib/pq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Worker retiring", func() {
	var (
		deployment string
	)

	BeforeEach(func() {
		deployment = "deployments/one-forwarded-worker.yml"
		Deploy(deployment)
	})

	It("[#129726063 #129726083] deletes all containers and volumes when worker is gone", func() {
		By("setting pipeline that creates resource cache")
		fly("set-pipeline", "-n", "-c", "pipelines/get-task.yml", "-p", "worker-retiring-test")

		By("unpausing the pipeline")
		fly("unpause-pipeline", "-p", "worker-retiring-test")

		By("checking resource")
		fly("check-resource", "-r", "worker-retiring-test/tick-tock")

		By("getting the worker containers")
		containersBefore := flyTable("containers")
		Expect(containersBefore).To(HaveLen(1))

		By("getting the worker volumes")
		volumesBefore := flyTable("volumes")
		Expect(volumesBefore).To(HaveLen(2))

		By("retiring the worker")
		Deploy(deployment, "operations/retire-worker.yml")

		By("getting the worker containers")
		containersAfter := flyTable("containers")
		Expect(containersAfter).To(HaveLen(0))

		By("getting the worker volumes")
		volumesAfter := flyTable("volumes")
		Expect(volumesAfter).To(HaveLen(0))
	})
})
<commit_msg>nix magic number<commit_after>package topgun_test

import (
	_ "github.com/lib/pq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Worker retiring", func() {
	var (
		deployment string
	)

	BeforeEach(func() {
		deployment = "deployments/one-forwarded-worker.yml"
		Deploy(deployment)
	})

	It("[#129726063 #129726083] deletes all containers and volumes when worker is gone", func() {
		By("setting pipeline that creates resource cache")
		fly("set-pipeline", "-n", "-c", "pipelines/get-task.yml", "-p", "worker-retiring-test")

		By("unpausing the pipeline")
		fly("unpause-pipeline", "-p", "worker-retiring-test")

		By("checking resource")
		fly("check-resource", "-r", "worker-retiring-test/tick-tock")

		By("getting the worker containers")
		containersBefore := flyTable("containers")
		Expect(containersBefore).To(HaveLen(1))

		By("getting the worker volumes")
		volumesBefore := flyTable("volumes")
		Expect(volumesBefore).ToNot(BeEmpty())

		By("retiring the worker")
		Deploy(deployment, "operations/retire-worker.yml")

		By("getting the worker containers")
		containersAfter := flyTable("containers")
		Expect(containersAfter).To(HaveLen(0))

		By("getting the worker volumes")
		volumesAfter := flyTable("volumes")
		Expect(volumesAfter).To(HaveLen(0))
	})
})
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "log"
    "io/ioutil"
    "net/http"
    "os/exec"
    "os"
    "strings"
    "strconv"

    "github.com/fsouza/go-dockerclient"
    "github.com/gorilla/mux"
)

var allPorts []docker.APIPort
var AWS_LOCAL_HOST_IP = "http://169.254.169.254/latest/meta-data/local-ipv4"
var AWS_PUBLIC_HOST_IP = "http://169.254.169.254/latest/meta-data/public-ipv4"
var hostIP string

func main() {
    hostIP = os.Getenv("HOST_IP")
    if( hostIP == "" ) {
        if( os.Getenv("AWS_EXTERNAL") == "true" ) {
            resp, _ := http.Get(AWS_PUBLIC_HOST_IP)
            defer resp.Body.Close()
            body, _ := ioutil.ReadAll(resp.Body)
            hostIP = string(body)
        } else {
            resp, _ := http.Get(AWS_LOCAL_HOST_IP)
            defer resp.Body.Close()
            body, _ := ioutil.ReadAll(resp.Body)
            hostIP = string(body)
        }
    }
    endpoint := "tcp://"+hostIP+":2376"
    path := os.Getenv("DOCKER_CERT_PATH")
    ca := fmt.Sprintf("%s/ca.pem", path)
    cert := fmt.Sprintf("%s/cert.pem", path)
    key := fmt.Sprintf("%s/key.pem", path)
    client, _ := docker.NewTLSClient(endpoint, cert, key, ca)

    cmd := exec.Command("containerId.sh")
    output, _ := cmd.CombinedOutput()
    cid := strings.Trim(string(output),"\n")
    fmt.Println(cid)

    container, _ := client.InspectContainer(cid)
    allPorts = container.NetworkSettings.PortMappingAPI()
    //fmt.Println( FindPort(9091,ports) )

    router := mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/port/{intPort}", GetPort)
    router.HandleFunc("/hostip", GetHostIP)
    router.HandleFunc("/ping", GetPing)
    log.Fatal(http.ListenAndServe(":1411",router))
}

func GetHostIP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w,hostIP)
}

func FindPort(port int64) int64 {
    for i := 0; i < len(allPorts); i++ {
        if allPorts[i].PrivatePort == port { return allPorts[i].PublicPort }
    }
    return -1
}

func GetPing(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w,"pong")
}

func GetPort(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    foundPort, err := strconv.ParseInt(vars["intPort"], 10, 64)
    if( err != nil ) {
        w.WriteHeader(http.StatusBadRequest)
    } else {
        found := FindPort(foundPort)
        if( found < 0 ) {
            w.WriteHeader(http.StatusNotFound)
        } else {
            fmt.Fprint(w,found)
        }
    }
}
<commit_msg>Kill newlines on returned values<commit_after>package main

import (
    "fmt"
    "log"
    "io/ioutil"
    "net/http"
    "os/exec"
    "os"
    "strings"
    "strconv"

    "github.com/fsouza/go-dockerclient"
    "github.com/gorilla/mux"
)

var allPorts []docker.APIPort
var AWS_LOCAL_HOST_IP = "http://169.254.169.254/latest/meta-data/local-ipv4"
var AWS_PUBLIC_HOST_IP = "http://169.254.169.254/latest/meta-data/public-ipv4"
var hostIP string

func main() {
    hostIP = os.Getenv("HOST_IP")
    if( hostIP == "" ) {
        if( os.Getenv("AWS_EXTERNAL") == "true" ) {
            resp, _ := http.Get(AWS_PUBLIC_HOST_IP)
            defer resp.Body.Close()
            body, _ := ioutil.ReadAll(resp.Body)
            hostIP = string(body)
        } else {
            resp, _ := http.Get(AWS_LOCAL_HOST_IP)
            defer resp.Body.Close()
            body, _ := ioutil.ReadAll(resp.Body)
            hostIP = string(body)
        }
    }
    endpoint := "tcp://"+hostIP+":2376"
    path := os.Getenv("DOCKER_CERT_PATH")
    ca := fmt.Sprintf("%s/ca.pem", path)
    cert := fmt.Sprintf("%s/cert.pem", path)
    key := fmt.Sprintf("%s/key.pem", path)
    client, _ := docker.NewTLSClient(endpoint, cert, key, ca)

    cmd := exec.Command("containerId.sh")
    output, _ := cmd.CombinedOutput()
    cid := strings.Trim(string(output),"\n")
    fmt.Println(cid)

    container, _ := client.InspectContainer(cid)
    allPorts = container.NetworkSettings.PortMappingAPI()
    //fmt.Println( FindPort(9091,ports) )

    router := mux.NewRouter().StrictSlash(true)
    router.HandleFunc("/port/{intPort}", GetPort)
    router.HandleFunc("/hostip", GetHostIP)
    router.HandleFunc("/ping", GetPing)
    log.Fatal(http.ListenAndServe(":1411",router))
}

func GetHostIP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w,hostIP)
}

func FindPort(port int64) int64 {
    for i := 0; i < len(allPorts); i++ {
        if allPorts[i].PrivatePort == port { return allPorts[i].PublicPort }
    }
    return -1
}

func GetPing(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w,"pong")
}

func GetPort(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    foundPort, err := strconv.ParseInt(vars["intPort"], 10, 64)
    if( err != nil ) {
        w.WriteHeader(http.StatusBadRequest)
    } else {
        found := FindPort(foundPort)
        if( found < 0 ) {
            w.WriteHeader(http.StatusNotFound)
        } else {
            fmt.Fprint(w,found)
        }
    }
}
<|endoftext|>


<commit_before>package acceptance

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"gopkg.in/yaml.v2"

	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	pivnet "github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/pivnet-resource/concourse"
	"github.com/pivotal-cf/pivnet-resource/metadata"
	"github.com/pivotal-cf/pivnet-resource/versions"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	executableTimeout = 60 * time.Second
)

var _ = Describe("Out", func() {
	var (
		version string

		releaseType     = pivnet.ReleaseType("Minor Release")
		releaseDate     = "2015-12-17"
		eulaSlug        = "pivotal_beta_eula"
		description     = "this release is for automated-testing only."
		releaseNotesURL = "https://example.com"

		metadataFile = "metadata"

		command         *exec.Cmd
		stdinContents   []byte
		outRequest      concourse.OutRequest
		rootDir         string
		productMetadata metadata.Metadata
	)

	BeforeEach(func() {
		var err error

		By("Creating a temporary root dir")
		rootDir, err = ioutil.TempDir("", "")
		Expect(err).ShouldNot(HaveOccurred())

		By("Generating 'random' product version")
		version = fmt.Sprintf("%d", time.Now().Nanosecond())

		By("Creating a metadata struct")
		productMetadata = metadata.Metadata{
			Release: &metadata.Release{
				ReleaseType:     string(releaseType),
				EULASlug:        eulaSlug,
				ReleaseDate:     releaseDate,
				Description:     description,
				ReleaseNotesURL: releaseNotesURL,
				Version:         version,
			},
		}

		By("Marshaling the metadata to yaml")
		metadataBytes, err := yaml.Marshal(productMetadata)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing the metadata to a file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, metadataFile),
			metadataBytes,
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Creating command object")
		command = exec.Command(outPath, rootDir)

		By("Creating default request")
		outRequest = concourse.OutRequest{
			Source: concourse.Source{
				APIToken:        pivnetAPIToken,
				AccessKeyID:     awsAccessKeyID,
				SecretAccessKey: awsSecretAccessKey,
				ProductSlug:     productSlug,
				Endpoint:        endpoint,
				Bucket:          pivnetBucketName,
				Region:          pivnetRegion,
			},
			Params: concourse.OutParams{
				FileGlob:       "",
				FilepathPrefix: "",
				MetadataFile:   metadataFile,
			},
		}
	})

	JustBeforeEach(func() {
		var err error
		stdinContents, err = json.Marshal(outRequest)
		Expect(err).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Removing local temp files")
		err := os.RemoveAll(rootDir)
		Expect(err).ShouldNot(HaveOccurred())
	})

	Describe("Argument validation", func() {
		Context("when no root directory is provided via args", func() {
			It("exits with error", func() {
				command := exec.Command(outPath)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).ShouldNot(HaveOccurred())

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("usage"))
			})
		})

		Context("when metadata file value is empty", func() {
			BeforeEach(func() {
				outRequest.Params.MetadataFile = ""
			})

			It("exits with error", func() {
				session := run(command, stdinContents)

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("metadata_file"))
			})
		})
	})

	Describe("Creating a new release", func() {
		// We do not delete the release as it causes race conditions with other tests

		It("Successfully creates a release", func() {
			var err error
			stdinContents, err = json.Marshal(outRequest)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the new product version does not yet exist")
			releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			releaseVersions, err := versionsWithFingerprints(releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(versionsWithoutFingerprints(releaseVersions)).NotTo(ContainElement(version))

			By("Running the command")
			session := run(command, stdinContents)
			Eventually(session, executableTimeout).Should(gexec.Exit(0))

			By("Validating new release exists on pivnet")
			releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			releaseVersions, err = versionsWithFingerprints(releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(versionsWithoutFingerprints(releaseVersions)).To(ContainElement(version))

			By("Outputting a valid json response")
			response := concourse.OutResponse{}
			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the release was created correctly")
			release, err := pivnetClient.GetRelease(productSlug, version)
			Expect(err).NotTo(HaveOccurred())

			expectedVersion, err := versions.CombineVersionAndFingerprint(release.Version, release.UpdatedAt)
			Expect(err).NotTo(HaveOccurred())

			Expect(response.Version.ProductVersion).To(Equal(expectedVersion))

			Expect(release.ReleaseType).To(Equal(releaseType))
			Expect(release.ReleaseDate).To(Equal(releaseDate))
			Expect(release.EULA.Slug).To(Equal(eulaSlug))
			Expect(release.Description).To(Equal(description))
			Expect(release.ReleaseNotesURL).To(Equal(releaseNotesURL))

			By("Validing the returned metadata")
			metadataReleaseType, err := metadataValueForKey(response.Metadata, "release_type")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseType).To(Equal(string(releaseType)))

			metadataReleaseDate, err := metadataValueForKey(response.Metadata, "release_date")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseDate).To(Equal(releaseDate))

			metadataDescription, err := metadataValueForKey(response.Metadata, "description")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataDescription).To(Equal(description))

			metadataReleaseNotesURL, err := metadataValueForKey(response.Metadata, "release_notes_url")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseNotesURL).To(Equal(releaseNotesURL))
		})

		Context("When the availability is set to Selected User Groups Only", func() {
			var (
				availability = "Selected User Groups Only"
				userGroupIDs = []string{"6", "8", "54"}
			)

			BeforeEach(func() {
				By("Adding availability to the metadata")
				productMetadata.Release.Availability = availability
				productMetadata.Release.UserGroupIDs = userGroupIDs

				By("Marshaling the metadata to yaml")
				metadataBytes, err := yaml.Marshal(productMetadata)
				Expect(err).ShouldNot(HaveOccurred())

				By("Writing the metadata to a file")
				err = ioutil.WriteFile(
					filepath.Join(rootDir, metadataFile),
					metadataBytes,
					os.ModePerm)
				Expect(err).ShouldNot(HaveOccurred())
			})

			It("Creates a release and updates the availability and user groups", func() {
				var err error
				stdinContents, err = json.Marshal(outRequest)
				Expect(err).ShouldNot(HaveOccurred())

				By("Validating the new product version does not yet exist")
				releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				releaseVersions, err := versionsWithFingerprints(releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(versionsWithoutFingerprints(releaseVersions)).NotTo(ContainElement(version))

				By("Running the command")
				session := run(command, stdinContents)
				Eventually(session, executableTimeout).Should(gexec.Exit(0))

				By("Validating new release exists on pivnet")
				releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				releaseVersions, err = versionsWithFingerprints(releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(versionsWithoutFingerprints(releaseVersions)).To(ContainElement(version))

				By("Outputting a valid json response")
				response := concourse.OutResponse{}
				err = json.Unmarshal(session.Out.Contents(), &response)
				Expect(err).ShouldNot(HaveOccurred())

				Expect(response.Version.ProductVersion).To(ContainSubstring(version))

				By("Validating the release was created correctly")
				release, err := pivnetClient.GetRelease(productSlug, version)
				Expect(err).NotTo(HaveOccurred())

				Expect(release.Availability).To(Equal(availability))

				By("Validing the returned metadata")
				metadataAvailability, err := metadataValueForKey(response.Metadata, "availability")
				Expect(err).ShouldNot(HaveOccurred())
				Expect(metadataAvailability).To(Equal(availability))

				By("Validating the user groups were associated with the release")
				userGroups, err := pivnetClient.UserGroups(productSlug, release.ID)
				Expect(err).NotTo(HaveOccurred())

				userGroupIDs := []int{}
				for _, userGroup := range userGroups {
					userGroupIDs = append(userGroupIDs, userGroup.ID)
				}
				Expect(userGroupIDs).Should(ConsistOf(6, 8, 54))
			})
		})
	})
})

// versionsWithFingerprints adds the release Fingerprints to the release versions
func versionsWithFingerprints(
	releases []pivnet.Release,
) ([]string, error) {
	var allVersions []string
	for _, r := range releases {
		version, err := versions.CombineVersionAndFingerprint(r.Version, r.UpdatedAt)
		if err != nil {
			return nil, err
		}

		allVersions = append(allVersions, version)
	}

	return allVersions, nil
}
<commit_msg>Remove useless (and now broken) integration test. [#136385263]<commit_after>package acceptance

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"gopkg.in/yaml.v2"

	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	pivnet "github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/pivnet-resource/concourse"
	"github.com/pivotal-cf/pivnet-resource/metadata"
	"github.com/pivotal-cf/pivnet-resource/versions"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const (
	executableTimeout = 60 * time.Second
)

var _ = Describe("Out", func() {
	var (
		version string

		releaseType     = pivnet.ReleaseType("Minor Release")
		releaseDate     = "2015-12-17"
		eulaSlug        = "pivotal_beta_eula"
		description     = "this release is for automated-testing only."
		releaseNotesURL = "https://example.com"

		metadataFile = "metadata"

		command         *exec.Cmd
		stdinContents   []byte
		outRequest      concourse.OutRequest
		rootDir         string
		productMetadata metadata.Metadata
	)

	BeforeEach(func() {
		var err error

		By("Creating a temporary root dir")
		rootDir, err = ioutil.TempDir("", "")
		Expect(err).ShouldNot(HaveOccurred())

		By("Generating 'random' product version")
		version = fmt.Sprintf("%d", time.Now().Nanosecond())

		By("Creating a metadata struct")
		productMetadata = metadata.Metadata{
			Release: &metadata.Release{
				ReleaseType:     string(releaseType),
				EULASlug:        eulaSlug,
				ReleaseDate:     releaseDate,
				Description:     description,
				ReleaseNotesURL: releaseNotesURL,
				Version:         version,
			},
		}

		By("Marshaling the metadata to yaml")
		metadataBytes, err := yaml.Marshal(productMetadata)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing the metadata to a file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, metadataFile),
			metadataBytes,
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Creating command object")
		command = exec.Command(outPath, rootDir)

		By("Creating default request")
		outRequest = concourse.OutRequest{
			Source: concourse.Source{
				APIToken:        pivnetAPIToken,
				AccessKeyID:     awsAccessKeyID,
				SecretAccessKey: awsSecretAccessKey,
				ProductSlug:     productSlug,
				Endpoint:        endpoint,
				Bucket:          pivnetBucketName,
				Region:          pivnetRegion,
			},
			Params: concourse.OutParams{
				FileGlob:       "",
				FilepathPrefix: "",
				MetadataFile:   metadataFile,
			},
		}
	})

	JustBeforeEach(func() {
		var err error
		stdinContents, err = json.Marshal(outRequest)
		Expect(err).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Removing local temp files")
		err := os.RemoveAll(rootDir)
		Expect(err).ShouldNot(HaveOccurred())
	})

	Describe("Argument validation", func() {
		Context("when no root directory is provided via args", func() {
			It("exits with error", func() {
				command := exec.Command(outPath)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).ShouldNot(HaveOccurred())

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("usage"))
			})
		})

		Context("when metadata file value is empty", func() {
			BeforeEach(func() {
				outRequest.Params.MetadataFile = ""
			})

			It("exits with error", func() {
				session := run(command, stdinContents)

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("metadata_file"))
			})
		})
	})

	Describe("Creating a new release", func() {
		// We do not delete the release as it causes race conditions with other tests

		It("Successfully creates a release", func() {
			var err error
			stdinContents, err = json.Marshal(outRequest)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the new product version does not yet exist")
			releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			releaseVersions, err := versionsWithFingerprints(releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(versionsWithoutFingerprints(releaseVersions)).NotTo(ContainElement(version))

			By("Running the command")
			session := run(command, stdinContents)
			Eventually(session, executableTimeout).Should(gexec.Exit(0))

			By("Validating new release exists on pivnet")
			releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			releaseVersions, err = versionsWithFingerprints(releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(versionsWithoutFingerprints(releaseVersions)).To(ContainElement(version))

			By("Outputting a valid json response")
			response := concourse.OutResponse{}
			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the release was created correctly")
			release, err := pivnetClient.GetRelease(productSlug, version)
			Expect(err).NotTo(HaveOccurred())

			expectedVersion, err := versions.CombineVersionAndFingerprint(release.Version, release.UpdatedAt)
			Expect(err).NotTo(HaveOccurred())

			Expect(response.Version.ProductVersion).To(Equal(expectedVersion))

			Expect(release.ReleaseType).To(Equal(releaseType))
			Expect(release.ReleaseDate).To(Equal(releaseDate))
			Expect(release.EULA.Slug).To(Equal(eulaSlug))
			Expect(release.Description).To(Equal(description))
			Expect(release.ReleaseNotesURL).To(Equal(releaseNotesURL))

			By("Validing the returned metadata")
			metadataReleaseType, err := metadataValueForKey(response.Metadata, "release_type")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseType).To(Equal(string(releaseType)))

			metadataReleaseDate, err := metadataValueForKey(response.Metadata, "release_date")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseDate).To(Equal(releaseDate))

			metadataDescription, err := metadataValueForKey(response.Metadata, "description")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataDescription).To(Equal(description))

			metadataReleaseNotesURL, err := metadataValueForKey(response.Metadata, "release_notes_url")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseNotesURL).To(Equal(releaseNotesURL))
		})
	})
})

// versionsWithFingerprints adds the release Fingerprints to the release versions
func versionsWithFingerprints(
	releases []pivnet.Release,
) ([]string, error) {
	var allVersions []string
	for _, r := range releases {
		version, err := versions.CombineVersionAndFingerprint(r.Version, r.UpdatedAt)
		if err != nil {
			return nil, err
		}

		allVersions = append(allVersions, version)
	}

	return allVersions, nil
}
<|endoftext|>


<commit_before>package internet

import (
	"crypto/tls"
	"errors"
	"net"
	"sync"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	v2tls "github.com/v2ray/v2ray-core/transport/internet/tls"
)

var (
	ErrClosedConnection = errors.New("Connection already closed.")

	KCPListenFunc    ListenFunc
	TCPListenFunc    ListenFunc
	RawTCPListenFunc ListenFunc
)

type ListenFunc func(address v2net.Address, port v2net.Port) (Listener, error)
type Listener interface {
	Accept() (Connection, error)
	Close() error
	Addr() net.Addr
}

type TCPHub struct {
	sync.Mutex
	listener     Listener
	connCallback ConnectionHandler
	accepting    bool
	tlsConfig    *tls.Config
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamSettings) (*TCPHub, error) {
	var listener Listener
	var err error
	switch {
	case settings.IsCapableOf(StreamConnectionTypeTCP):
		listener, err = TCPListenFunc(address, port)
	case settings.IsCapableOf(StreamConnectionTypeKCP):
		listener, err = KCPListenFunc(address, port)
	case settings.IsCapableOf(StreamConnectionTypeRawTCP):
		listener, err = RawTCPListenFunc(address, port)
	default:
		log.Error("Internet|Listener: Unknown stream type: ", settings.Type)
		err = ErrUnsupportedStreamType
	}

	if err != nil {
		log.Warning("Internet|Listener: Failed to listen on ", address, ":", port)
		return nil, err
	}

	var tlsConfig *tls.Config
	if settings.Security == StreamSecurityTypeTLS {
		tlsConfig = settings.TLSSettings.GetTLSConfig()
	}

	hub := &TCPHub{
		listener:     listener,
		connCallback: callback,
		tlsConfig:    tlsConfig,
	}

	go hub.start()
	return hub, nil
}

func (this *TCPHub) Close() {
	this.accepting = false
	this.listener.Close()
}

func (this *TCPHub) start() {
	this.accepting = true
	for this.accepting {
		conn, err := this.listener.Accept()

		if err != nil {
			if this.accepting {
				log.Warning("Internet|Listener: Failed to accept new TCP connection: ", err)
			}
			continue
		}
		if this.tlsConfig != nil {
			tlsConn := tls.Server(conn, this.tlsConfig)
			conn = v2tls.NewConnection(tlsConn)
		}
		go this.connCallback(conn)
	}
}
<commit_msg>Support Listen WebSocket Connection<commit_after>package internet

import (
	"crypto/tls"
	"errors"
	"net"
	"sync"

	"github.com/v2ray/v2ray-core/common/log"
	v2net "github.com/v2ray/v2ray-core/common/net"
	v2tls "github.com/v2ray/v2ray-core/transport/internet/tls"
)

var (
	ErrClosedConnection = errors.New("Connection already closed.")

	KCPListenFunc    ListenFunc
	TCPListenFunc    ListenFunc
	RawTCPListenFunc ListenFunc
	WSListenFunc     ListenFunc
)

type ListenFunc func(address v2net.Address, port v2net.Port) (Listener, error)
type Listener interface {
	Accept() (Connection, error)
	Close() error
	Addr() net.Addr
}

type TCPHub struct {
	sync.Mutex
	listener     Listener
	connCallback ConnectionHandler
	accepting    bool
	tlsConfig    *tls.Config
}

func ListenTCP(address v2net.Address, port v2net.Port, callback ConnectionHandler, settings *StreamSettings) (*TCPHub, error) {
	var listener Listener
	var err error
	switch {
	case settings.IsCapableOf(StreamConnectionTypeTCP):
		listener, err = TCPListenFunc(address, port)
	case settings.IsCapableOf(StreamConnectionTypeKCP):
		listener, err = KCPListenFunc(address, port)
	case settings.IsCapableOf(StreamConnectionTypeWebSocket):
		listener, err = WSListenFunc(address, port)
	case settings.IsCapableOf(StreamConnectionTypeRawTCP):
		listener, err = RawTCPListenFunc(address, port)
	default:
		log.Error("Internet|Listener: Unknown stream type: ", settings.Type)
		err = ErrUnsupportedStreamType
	}

	if err != nil {
		log.Warning("Internet|Listener: Failed to listen on ", address, ":", port)
		return nil, err
	}

	var tlsConfig *tls.Config
	if settings.Security == StreamSecurityTypeTLS {
		tlsConfig = settings.TLSSettings.GetTLSConfig()
	}

	hub := &TCPHub{
		listener:     listener,
		connCallback: callback,
		tlsConfig:    tlsConfig,
	}

	go hub.start()
	return hub, nil
}

func (this *TCPHub) Close() {
	this.accepting = false
	this.listener.Close()
}

func (this *TCPHub) start() {
	this.accepting = true
	for this.accepting {
		conn, err := this.listener.Accept()

		if err != nil {
			if this.accepting {
				log.Warning("Internet|Listener: Failed to accept new TCP connection: ", err)
			}
			continue
		}
		if this.tlsConfig != nil {
			tlsConn := tls.Server(conn, this.tlsConfig)
			conn = v2tls.NewConnection(tlsConn)
		}
		go this.connCallback(conn)
	}
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"time"
)

// Event represents an event entry (over websocket)
//
// swagger:model
type Event struct {
	// Event type (one of operation, logging or lifecycle)
	// Example: lifecycle
	Type string `yaml:"type" json:"type"`

	// Time at which the event was sent
	// Example: 2021-02-24T19:00:45.452649098-05:00
	Timestamp time.Time `yaml:"timestamp" json:"timestamp"`

	// JSON encoded metadata (see EventLogging, EventLifecycle or Operation)
	// Example: {"action": "instance-started", "source": "/1.0/instances/c1", "context": {}}
	Metadata json.RawMessage `yaml:"metadata" json:"metadata"`

	// Originating cluster member
	// Example: lxd01
	//
	// API extension: event_location
	Location string `yaml:"location,omitempty" json:"location,omitempty"`
}

// ToLogging creates log record for the event
func (event *Event) ToLogging() (EventLogRecord, error) {
	if event.Type == "logging" {
		e := &EventLogging{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  e.Level,
			Msg:  e.Message,
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "lifecycle" {
		e := &EventLifecycle{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("Action: %s, Source: %s", e.Action, e.Source),
			Ctx:  ctx,
		}
		return record, nil
	}

	return EventLogRecord{}, fmt.Errorf("Not supported event type: %s", event.Type)
}

// EventLogRecord represents single log record
type EventLogRecord struct {
	Time time.Time
	Lvl  string
	Msg  string
	Ctx  []interface{}
}

// EventLogging represents a logging type event entry (admin only)
type EventLogging struct {
	Message string            `yaml:"message" json:"message"`
	Level   string            `yaml:"level" json:"level"`
	Context map[string]string `yaml:"context" json:"context"`
}

// EventLifecycle represets a lifecycle type event entry
//
// API extension: event_lifecycle
type EventLifecycle struct {
	Action  string                 `yaml:"action" json:"action"`
	Source  string                 `yaml:"source" json:"source"`
	Context map[string]interface{} `yaml:"context,omitempty" json:"context,omitempty"`

	// API extension: event_lifecycle_requestor
	Requestor *EventLifecycleRequestor `yaml:"requestor,omitempty" json:"requestor,omitempty"`
}

// EventLifecycleRequestor represents the initial requestor for an event
//
// API extension: event_lifecycle_requestor
type EventLifecycleRequestor struct {
	Username string `yaml:"username" json:"username"`
	Protocol string `yaml:"protocol" json:"protocol"`
}
<commit_msg>shared/api: Support for lxc monitor --pretty operation events<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"time"
)

// Event represents an event entry (over websocket)
//
// swagger:model
type Event struct {
	// Event type (one of operation, logging or lifecycle)
	// Example: lifecycle
	Type string `yaml:"type" json:"type"`

	// Time at which the event was sent
	// Example: 2021-02-24T19:00:45.452649098-05:00
	Timestamp time.Time `yaml:"timestamp" json:"timestamp"`

	// JSON encoded metadata (see EventLogging, EventLifecycle or Operation)
	// Example: {"action": "instance-started", "source": "/1.0/instances/c1", "context": {}}
	Metadata json.RawMessage `yaml:"metadata" json:"metadata"`

	// Originating cluster member
	// Example: lxd01
	//
	// API extension: event_location
	Location string `yaml:"location,omitempty" json:"location,omitempty"`
}

// ToLogging creates log record for the event
func (event *Event) ToLogging() (EventLogRecord, error) {
	if event.Type == "logging" {
		e := &EventLogging{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  e.Level,
			Msg:  e.Message,
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "lifecycle" {
		e := &EventLifecycle{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		ctx := []interface{}{}
		for k, v := range e.Context {
			ctx = append(ctx, k)
			ctx = append(ctx, v)
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("Action: %s, Source: %s", e.Action, e.Source),
			Ctx:  ctx,
		}
		return record, nil
	} else if event.Type == "operation" {
		e := &Operation{}
		err := json.Unmarshal(event.Metadata, &e)
		if err != nil {
			return EventLogRecord{}, err
		}

		record := EventLogRecord{
			Time: event.Timestamp,
			Lvl:  "info",
			Msg:  fmt.Sprintf("ID: %s, Class: %s, Description: %s", e.ID, e.Class, e.Description),
			Ctx: []interface{}{
				"CreatedAt", e.CreatedAt,
				"UpdatedAt", e.UpdatedAt,
				"Status", e.Status,
				"StatusCode", e.StatusCode,
				"Resources", e.Resources,
				"Metadata", e.Metadata,
				"MayCancel", e.MayCancel,
				"Err", e.Err,
				"Location", e.Location,
			},
		}
		return record, nil
	}

	return EventLogRecord{}, fmt.Errorf("Not supported event type: %s", event.Type)
}

// EventLogRecord represents single log record
type EventLogRecord struct {
	Time time.Time
	Lvl  string
	Msg  string
	Ctx  []interface{}
}

// EventLogging represents a logging type event entry (admin only)
type EventLogging struct {
	Message string            `yaml:"message" json:"message"`
	Level   string            `yaml:"level" json:"level"`
	Context map[string]string `yaml:"context" json:"context"`
}

// EventLifecycle represets a lifecycle type event entry
//
// API extension: event_lifecycle
type EventLifecycle struct {
	Action  string                 `yaml:"action" json:"action"`
	Source  string                 `yaml:"source" json:"source"`
	Context map[string]interface{} `yaml:"context,omitempty" json:"context,omitempty"`

	// API extension: event_lifecycle_requestor
	Requestor *EventLifecycleRequestor `yaml:"requestor,omitempty" json:"requestor,omitempty"`
}

// EventLifecycleRequestor represents the initial requestor for an event
//
// API extension: event_lifecycle_requestor
type EventLifecycleRequestor struct {
	Username string `yaml:"username" json:"username"`
	Protocol string `yaml:"protocol" json:"protocol"`
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			_, _, errs := gorequest.New().Get(*remoteService).End()
			if errs != nil {
				log.Fatalf("log failed: %v", errs)
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		for msg := range msgs {
			fmt.Println(msg)
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}

					temp = string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00"))
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 || sepIdx+5 > len(msg.Content) {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 || sepIdx+4 > len(msg.Content) {
							sepIdx = len(msg.Content) - 4
						} else {
							sepIdx = len(msg.Content) - 1
						}
					}
					if sepIdx == -1 {
						sepIdx = len(msg.Content)
					}
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
					messages = append(messages, string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00")))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					messages = append(messages, strings.Replace(temp, "$1", fmt.Sprintf("'%s'", string(msg.Content[29:len(msg.Content)-4])), -1))
				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<commit_msg>Update<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"log"
	"os"
	"strings"
	"time"

	"github.com/DimShadoWWW/power-pg/proxy"
	"github.com/parnurzeal/gorequest"
)

var (
	localHost     = flag.String("l", ":9876", "Endereço e porta do listener local")
	remoteHost    = flag.String("r", "localhost:5432", "Endereço e porta do servidor PostgreSQL")
	remoteService = flag.String("s", "", "http://localhost:8080/query")
	messages      = []string{}
)

func main() {
	flag.Parse()
	msgs := make(chan string)
	msgCh := make(chan proxy.Pkg)
	msgOut := make(chan string)
	if *remoteService != "" {
		go func() {
			time.Sleep(time.Second * 3)
			inFile, _ := os.Open("canales_list.txt")
			defer inFile.Close()
			scanner := bufio.NewScanner(inFile)
			scanner.Split(bufio.ScanLines)

			for scanner.Scan() {
				time.Sleep(time.Second * 1)
				messages = []string{}
				fmt.Println(scanner.Text())
				msgOut <- scanner.Text()
				_, _, errs := gorequest.New().Get(fmt.Sprintf("%s%s", *remoteService, scanner.Text())).End()
				if errs != nil {
					log.Fatalf("log failed: %v", errs)
				}
			}
			log.Println("done")
			os.Exit(0)
		}()
	}

	go func() {
		for msg := range msgs {
			fmt.Println(msg)
		}
	}()

	go func() {
		temp := ""
		for msg := range msgCh {
			if msg.Type == 'P' {
				if strings.Contains(string(msg.Content), "$1") {
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
					if sepIdx == -1 {
						sepIdx = len(msg.Content) - 4
					}

					temp = string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00"))
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
				} else {
					temp = ""
					selectIdx := strings.Index(string(msg.Content), string([]byte{83, 69, 76, 69, 67, 84, 32}))
					if selectIdx == -1 {
						selectIdx = 0
					}
					sepIdx := strings.Index(string(msg.Content), string([]byte{0, 0, 1, 0, 0}))
					if sepIdx == -1 || sepIdx+5 > len(msg.Content) {
						sepIdx := strings.Index(string(msg.Content), string([]byte{0, 1, 0, 0}))
						if sepIdx == -1 || sepIdx+4 > len(msg.Content) {
							sepIdx = len(msg.Content) - 4
						} else {
							sepIdx = len(msg.Content) - 1
						}
					}
					if sepIdx == -1 {
						sepIdx = len(msg.Content)
					}
					if selectIdx == -1 {
						selectIdx = 0
					}
					fmt.Printf("SEP index ----->%v\n", sepIdx)
					fmt.Printf("SEP len   ----->%v\n", len(msg.Content))
					fmt.Printf("SEP CONT  ----->%v\n", msg.Content)
					messages = append(messages, string(bytes.Trim(msg.Content[selectIdx:sepIdx], "\x00")))
				}
			} else {
				if msg.Type == 'B' && len(msg.Content) > 28 && temp != "" {
					messages = append(messages, strings.Replace(temp, "$1", fmt.Sprintf("'%s'", string(msg.Content[29:len(msg.Content)-4])), -1))
				}
				temp = ""
			}
			fmt.Printf("---------->%v\n", messages)
			fmt.Printf("---------->%#v\n", messages)
			for _, v := range messages {
				msgOut <- fmt.Sprintf("==> %s", v)
			}
		}
	}()

	proxy.Start(localHost, remoteHost, getQueryModificada, msgs, msgCh)
}

func getQueryModificada(queryOriginal string) string {
	// log.Println("aa")
	// if queryOriginal[:5] != "power" {
	// 	return queryOriginal
	// }

	// log.Println(queryOriginal)
	fmt.Println(queryOriginal)
	return queryOriginal
}
<|endoftext|>


<commit_before>package acceptance

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"gopkg.in/yaml.v2"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
	"github.com/pivotal-cf-experimental/pivnet-resource/metadata"
)

const (
	executableTimeout = 60 * time.Second
)

var _ = Describe("Out", func() {
	var (
		releaseTypeFile = "release_type"
		releaseType     = "Minor Release"

		releaseDateFile = "release_date"
		releaseDate     = "2015-12-17"

		eulaSlugFile = "eula_slug"
		eulaSlug     = "pivotal_beta_eula"

		productVersionFile = "version"
		productVersion     string

		descriptionFile = "description"
		description     = "this release is for automated-testing only."

		metadataFile = "metadata"

		releaseNotesURLFile = "release_notes_url"
		releaseNotesURL     = "https://example.com"

		command       *exec.Cmd
		stdinContents []byte
		outRequest    concourse.OutRequest
		rootDir       string
	)

	BeforeEach(func() {
		var err error

		By("Creating a temporary root dir")
		rootDir, err = ioutil.TempDir("", "")
		Expect(err).ShouldNot(HaveOccurred())

		By("Generating 'random' product version")
		productVersion = fmt.Sprintf("%d", time.Now().Nanosecond())

		By("Creating a metadata struct")
		metadataBytes, err := yaml.Marshal(metadata.Metadata{
			ReleaseType:    releaseType,
			EulaSlug:       eulaSlug,
			ProductVersion: productVersion,
		})
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing the metadata to a file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, metadataFile),
			metadataBytes,
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		// By("Writing product version to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, productVersionFile),
		// 	[]byte(productVersion),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		// By("Writing release type to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, releaseTypeFile),
		// 	[]byte(releaseType),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		// By("Writing release date to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, releaseDateFile),
		// 	[]byte(releaseDate),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		// By("Writing eula slug to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, eulaSlugFile),
		// 	[]byte(eulaSlug),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		// By("Writing description to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, descriptionFile),
		// 	[]byte(description),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		// By("Writing release notes URL to file")
		// err = ioutil.WriteFile(
		// 	filepath.Join(rootDir, releaseNotesURLFile),
		// 	[]byte(releaseNotesURL),
		// 	os.ModePerm)
		// Expect(err).ShouldNot(HaveOccurred())

		By("Creating command object")
		command = exec.Command(outPath, rootDir)

		By("Creating default request")
		outRequest = concourse.OutRequest{
			Source: concourse.Source{
				APIToken:        pivnetAPIToken,
				AccessKeyID:     awsAccessKeyID,
				SecretAccessKey: awsSecretAccessKey,
				ProductSlug:     productSlug,
				Endpoint:        endpoint,
				Bucket:          pivnetBucketName,
				Region:          pivnetRegion,
			},
			Params: concourse.OutParams{
				FileGlob:            "",
				FilepathPrefix:      "",
				VersionFile:         productVersionFile,
				ReleaseTypeFile:     releaseTypeFile,
				ReleaseDateFile:     releaseDateFile,
				EulaSlugFile:        eulaSlugFile,
				DescriptionFile:     descriptionFile,
				ReleaseNotesURLFile: releaseNotesURLFile,
			},
		}

		stdinContents, err = json.Marshal(outRequest)
		Expect(err).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Removing local temp files")
		err := os.RemoveAll(rootDir)
		Expect(err).ShouldNot(HaveOccurred())
	})

	Describe("Argument validation", func() {
		Context("when no root directory is provided via args", func() {
			It("exits with error", func() {
				command := exec.Command(outPath)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("usage"))
			})
		})
	})

	Describe("Creating a new release", func() {
		// We do not delete the release as it causes race conditions with other tests

		It("Successfully creates a release", func() {
			var err error
			stdinContents, err = json.Marshal(outRequest)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the new product version does not yet exist")
			releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			productVersions, err := pivnetClient.ProductVersions(productSlug, releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(productVersionsWithoutETags(productVersions)).NotTo(ContainElement(productVersion))

			By("Running the command")
			session := run(command, stdinContents)
			Eventually(session, executableTimeout).Should(gexec.Exit(0))

			By("Validating new release exists on pivnet")
			releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			productVersions, err = pivnetClient.ProductVersions(productSlug, releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(productVersionsWithoutETags(productVersions)).To(ContainElement(productVersion))

			By("Outputting a valid json response")
			response := concourse.OutResponse{}
			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the release was created correctly")
			release, err := pivnetClient.GetRelease(productSlug, productVersion)
			Expect(err).NotTo(HaveOccurred())

			releaseETag, err := pivnetClient.ReleaseETag(productSlug, release)
			Expect(err).NotTo(HaveOccurred())

			expectedVersion := fmt.Sprintf("%s#%s", productVersion, releaseETag)
			Expect(response.Version.ProductVersion).To(Equal(expectedVersion))

			Expect(release.ReleaseType).To(Equal(releaseType))
			Expect(release.ReleaseDate).To(Equal(releaseDate))
			Expect(release.Eula.Slug).To(Equal(eulaSlug))
			Expect(release.Description).To(Equal(description))
			Expect(release.ReleaseNotesURL).To(Equal(releaseNotesURL))

			By("Validing the returned metadata")
			metadataReleaseType, err := metadataValueForKey(response.Metadata, "release_type")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseType).To(Equal(releaseType))

			metadataReleaseDate, err := metadataValueForKey(response.Metadata, "release_date")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseDate).To(Equal(releaseDate))

			metadataDescription, err := metadataValueForKey(response.Metadata, "description")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataDescription).To(Equal(description))

			metadataReleaseNotesURL, err := metadataValueForKey(response.Metadata, "release_notes_url")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseNotesURL).To(Equal(releaseNotesURL))
		})

		Context("When the availability is set to Selected User Groups Only", func() {
			var (
				availabilityFile = "availability"
				availability     = "Selected User Groups Only"

				userGroupIDsFile = "user_group_ids"
				userGroupIDs     = "6,8,54"
			)

			BeforeEach(func() {
				By("Writing availability to file")
				err := ioutil.WriteFile(
					filepath.Join(rootDir, availabilityFile),
					[]byte(availability),
					os.ModePerm)
				Expect(err).ShouldNot(HaveOccurred())

				By("Writing user group IDs to file")
				err = ioutil.WriteFile(
					filepath.Join(rootDir, userGroupIDsFile),
					[]byte(userGroupIDs),
					os.ModePerm)
				Expect(err).ShouldNot(HaveOccurred())

				outRequest.Params.AvailabilityFile = availabilityFile
				outRequest.Params.UserGroupIDsFile = userGroupIDsFile
			})

			It("Creates a release and updates the availability and user groups", func() {
				var err error
				stdinContents, err = json.Marshal(outRequest)
				Expect(err).ShouldNot(HaveOccurred())

				By("Validating the new product version does not yet exist")
				releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				productVersions, err := pivnetClient.ProductVersions(productSlug, releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(productVersionsWithoutETags(productVersions)).NotTo(ContainElement(productVersion))

				By("Running the command")
				session := run(command, stdinContents)
				Eventually(session, executableTimeout).Should(gexec.Exit(0))

				By("Validating new release exists on pivnet")
				releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				productVersions, err = pivnetClient.ProductVersions(productSlug, releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(productVersionsWithoutETags(productVersions)).To(ContainElement(productVersion))

				By("Outputting a valid json response")
				response := concourse.OutResponse{}
				err = json.Unmarshal(session.Out.Contents(), &response)
				Expect(err).ShouldNot(HaveOccurred())

				Expect(response.Version.ProductVersion).To(ContainSubstring(productVersion))

				By("Validating the release was created correctly")
				release, err := pivnetClient.GetRelease(productSlug, productVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(release.Availability).To(Equal(availability))

				By("Validing the returned metadata")
				metadataAvailability, err := metadataValueForKey(response.Metadata, "availability")
				Expect(err).ShouldNot(HaveOccurred())
				Expect(metadataAvailability).To(Equal(availability))

				By("Validating the user groups were associated with the release")
				userGroups, err := pivnetClient.UserGroups(productSlug, release.ID)
				Expect(err).NotTo(HaveOccurred())

				userGroupIDs := []int{}
				for _, userGroup := range userGroups {
					userGroupIDs = append(userGroupIDs, userGroup.ID)
				}
				Expect(userGroupIDs).Should(ConsistOf(6, 8, 54))
			})
		})
	})
})
<commit_msg>Revert "Start of test"<commit_after>package acceptance

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/onsi/gomega/gexec"
	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
)

const (
	executableTimeout = 60 * time.Second
)

var _ = Describe("Out", func() {
	var (
		releaseTypeFile = "release_type"
		releaseType     = "Minor Release"

		releaseDateFile = "release_date"
		releaseDate     = "2015-12-17"

		eulaSlugFile = "eula_slug"
		eulaSlug     = "pivotal_beta_eula"

		productVersionFile = "version"
		productVersion     string

		descriptionFile = "description"
		description     = "this release is for automated-testing only."

		releaseNotesURLFile = "release_notes_url"
		releaseNotesURL     = "https://example.com"

		command       *exec.Cmd
		stdinContents []byte
		outRequest    concourse.OutRequest
		rootDir       string
	)

	BeforeEach(func() {
		var err error

		By("Creating a temporary root dir")
		rootDir, err = ioutil.TempDir("", "")
		Expect(err).ShouldNot(HaveOccurred())

		By("Generating 'random' product version")
		productVersion = fmt.Sprintf("%d", time.Now().Nanosecond())

		By("Writing product version to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, productVersionFile),
			[]byte(productVersion),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing release type to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, releaseTypeFile),
			[]byte(releaseType),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing release date to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, releaseDateFile),
			[]byte(releaseDate),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing eula slug to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, eulaSlugFile),
			[]byte(eulaSlug),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing description to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, descriptionFile),
			[]byte(description),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Writing release notes URL to file")
		err = ioutil.WriteFile(
			filepath.Join(rootDir, releaseNotesURLFile),
			[]byte(releaseNotesURL),
			os.ModePerm)
		Expect(err).ShouldNot(HaveOccurred())

		By("Creating command object")
		command = exec.Command(outPath, rootDir)

		By("Creating default request")
		outRequest = concourse.OutRequest{
			Source: concourse.Source{
				APIToken:        pivnetAPIToken,
				AccessKeyID:     awsAccessKeyID,
				SecretAccessKey: awsSecretAccessKey,
				ProductSlug:     productSlug,
				Endpoint:        endpoint,
				Bucket:          pivnetBucketName,
				Region:          pivnetRegion,
			},
			Params: concourse.OutParams{
				FileGlob:            "",
				FilepathPrefix:      "",
				VersionFile:         productVersionFile,
				ReleaseTypeFile:     releaseTypeFile,
				ReleaseDateFile:     releaseDateFile,
				EulaSlugFile:        eulaSlugFile,
				DescriptionFile:     descriptionFile,
				ReleaseNotesURLFile: releaseNotesURLFile,
			},
		}

		stdinContents, err = json.Marshal(outRequest)
		Expect(err).ShouldNot(HaveOccurred())
	})

	AfterEach(func() {
		By("Removing local temp files")
		err := os.RemoveAll(rootDir)
		Expect(err).ShouldNot(HaveOccurred())
	})

	Describe("Argument validation", func() {
		Context("when no root directory is provided via args", func() {
			It("exits with error", func() {
				command := exec.Command(outPath)
				session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
				Expect(err).NotTo(HaveOccurred())

				Eventually(session).Should(gexec.Exit(1))
				Expect(session.Err).Should(gbytes.Say("usage"))
			})
		})
	})

	Describe("Creating a new release", func() {
		// We do not delete the release as it causes race conditions with other tests

		It("Successfully creates a release", func() {
			var err error
			stdinContents, err = json.Marshal(outRequest)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the new product version does not yet exist")
			releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			productVersions, err := pivnetClient.ProductVersions(productSlug, releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(productVersionsWithoutETags(productVersions)).NotTo(ContainElement(productVersion))

			By("Running the command")
			session := run(command, stdinContents)
			Eventually(session, executableTimeout).Should(gexec.Exit(0))

			By("Validating new release exists on pivnet")
			releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
			Expect(err).NotTo(HaveOccurred())

			productVersions, err = pivnetClient.ProductVersions(productSlug, releases)
			Expect(err).NotTo(HaveOccurred())

			Expect(productVersionsWithoutETags(productVersions)).To(ContainElement(productVersion))

			By("Outputting a valid json response")
			response := concourse.OutResponse{}
			err = json.Unmarshal(session.Out.Contents(), &response)
			Expect(err).ShouldNot(HaveOccurred())

			By("Validating the release was created correctly")
			release, err := pivnetClient.GetRelease(productSlug, productVersion)
			Expect(err).NotTo(HaveOccurred())

			releaseETag, err := pivnetClient.ReleaseETag(productSlug, release)
			Expect(err).NotTo(HaveOccurred())

			expectedVersion := fmt.Sprintf("%s#%s", productVersion, releaseETag)
			Expect(response.Version.ProductVersion).To(Equal(expectedVersion))

			Expect(release.ReleaseType).To(Equal(releaseType))
			Expect(release.ReleaseDate).To(Equal(releaseDate))
			Expect(release.Eula.Slug).To(Equal(eulaSlug))
			Expect(release.Description).To(Equal(description))
			Expect(release.ReleaseNotesURL).To(Equal(releaseNotesURL))

			By("Validing the returned metadata")
			metadataReleaseType, err := metadataValueForKey(response.Metadata, "release_type")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseType).To(Equal(releaseType))

			metadataReleaseDate, err := metadataValueForKey(response.Metadata, "release_date")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseDate).To(Equal(releaseDate))

			metadataDescription, err := metadataValueForKey(response.Metadata, "description")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataDescription).To(Equal(description))

			metadataReleaseNotesURL, err := metadataValueForKey(response.Metadata, "release_notes_url")
			Expect(err).ShouldNot(HaveOccurred())
			Expect(metadataReleaseNotesURL).To(Equal(releaseNotesURL))
		})

		Context("When the availability is set to Selected User Groups Only", func() {
			var (
				availabilityFile = "availability"
				availability     = "Selected User Groups Only"

				userGroupIDsFile = "user_group_ids"
				userGroupIDs     = "6,8,54"
			)

			BeforeEach(func() {
				By("Writing availability to file")
				err := ioutil.WriteFile(
					filepath.Join(rootDir, availabilityFile),
					[]byte(availability),
					os.ModePerm)
				Expect(err).ShouldNot(HaveOccurred())

				By("Writing user group IDs to file")
				err = ioutil.WriteFile(
					filepath.Join(rootDir, userGroupIDsFile),
					[]byte(userGroupIDs),
					os.ModePerm)
				Expect(err).ShouldNot(HaveOccurred())

				outRequest.Params.AvailabilityFile = availabilityFile
				outRequest.Params.UserGroupIDsFile = userGroupIDsFile
			})

			It("Creates a release and updates the availability and user groups", func() {
				var err error
				stdinContents, err = json.Marshal(outRequest)
				Expect(err).ShouldNot(HaveOccurred())

				By("Validating the new product version does not yet exist")
				releases, err := pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				productVersions, err := pivnetClient.ProductVersions(productSlug, releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(productVersionsWithoutETags(productVersions)).NotTo(ContainElement(productVersion))

				By("Running the command")
				session := run(command, stdinContents)
				Eventually(session, executableTimeout).Should(gexec.Exit(0))

				By("Validating new release exists on pivnet")
				releases, err = pivnetClient.ReleasesForProductSlug(productSlug)
				Expect(err).NotTo(HaveOccurred())

				productVersions, err = pivnetClient.ProductVersions(productSlug, releases)
				Expect(err).NotTo(HaveOccurred())

				Expect(productVersionsWithoutETags(productVersions)).To(ContainElement(productVersion))

				By("Outputting a valid json response")
				response := concourse.OutResponse{}
				err = json.Unmarshal(session.Out.Contents(), &response)
				Expect(err).ShouldNot(HaveOccurred())

				Expect(response.Version.ProductVersion).To(ContainSubstring(productVersion))

				By("Validating the release was created correctly")
				release, err := pivnetClient.GetRelease(productSlug, productVersion)
				Expect(err).NotTo(HaveOccurred())

				Expect(release.Availability).To(Equal(availability))

				By("Validing the returned metadata")
				metadataAvailability, err := metadataValueForKey(response.Metadata, "availability")
				Expect(err).ShouldNot(HaveOccurred())
				Expect(metadataAvailability).To(Equal(availability))

				By("Validating the user groups were associated with the release")
				userGroups, err := pivnetClient.UserGroups(productSlug, release.ID)
				Expect(err).NotTo(HaveOccurred())

				userGroupIDs := []int{}
				for _, userGroup := range userGroups {
					userGroupIDs = append(userGroupIDs, userGroup.ID)
				}
				Expect(userGroupIDs).Should(ConsistOf(6, 8, 54))
			})
		})
	})
})
<|endoftext|>