<commit_before>package mcstore

import (
	"fmt"
	"net/http/httptest"
	"time"

	"github.com/emicklei/go-restful"
	"github.com/materials-commons/config"
	"github.com/materials-commons/gohandy/ezhttp"
	c "github.com/materials-commons/mcstore/cmd/pkg/client"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/testutil"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/parnurzeal/gorequest"
	"github.com/willf/bitset"
)

var _ = fmt.Println

var _ = Describe("UploadResource", func() {
	Describe("findStartingBlock method tests", func() {
		var (
			blocks *bitset.BitSet
		)

		BeforeEach(func() {
			blocks = bitset.New(10)
		})

		It("Should return 1 if no blocks have been set", func() {
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 1))
		})

		It("Should return 2 if the first block has been uploaded", func() {
			// BitSet starts a zero. Flowjs starts at 1. So we have to adjust.
			blocks.Set(0)
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 2))
		})

		It("Should return 1 if only the last block as been uploaded", func() {
			blocks.Set(9)
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 1))
		})

		It("Should return 2 if only 2 has not been set (all others set)", func() {
			complement := blocks.Complement()
			complement.Clear(1) // second block
			block := findStartingBlock(complement)
			Expect(block).To(BeNumerically("==", 2))
		})
	})

	Describe("Upload REST API method tests", func() {
		var (
			client        *gorequest.SuperAgent
			server        *httptest.Server
			container     *restful.Container
			rr            *httptest.ResponseRecorder
			uploadRequest CreateUploadRequest
			uploads       dai.Uploads
		)

		BeforeEach(func() {
			client = c.NewGoRequest()
			container = NewServicesContainerForTest()
			server = httptest.NewServer(container)
			rr = httptest.NewRecorder()
			config.Set("mcurl", server.URL)
			uploadRequest = CreateUploadRequest{
				ProjectID:     "test",
				DirectoryID:   "test",
				DirectoryPath: "test/test",
				FileName:      "testreq.txt",
				FileSize:      4,
				ChunkSize:     2,
				FileMTime:     time.Now().Format(time.RFC1123),
				Checksum:      "abc123",
			}
			uploads = dai.NewRUploads(testutil.RSession())
		})

		var (
			createUploadRequest = func(req CreateUploadRequest) (*CreateUploadResponse, error) {
				r, body, errs := client.Post(Api.Url("/upload")).Send(req).End()
				if err := Api.IsError(r, errs); err != nil {
					return nil, err
				}

				var uploadResponse CreateUploadResponse
				if err := Api.ToJSON(body, &uploadResponse); err != nil {
					return nil, err
				}
				return &uploadResponse, nil
			}
		)

		AfterEach(func() {
			server.Close()
		})

		Describe("create upload tests", func() {
			Context("No existing uploads that match request", func() {
				It("Should return an error when the user doesn't have permission", func() {
					// Set apikey for user who doesn't have permission
					config.Set("apikey", "test2")
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 401))
				})

				It("Should return an error when the project doesn't exist", func() {
					config.Set("apikey", "test")
					uploadRequest.ProjectID = "does-not-exist"
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 400))
				})

				It("Should return an error when the directory doesn't exist", func() {
					config.Set("apikey", "test")
					uploadRequest.DirectoryID = "does-not-exist"
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 400))
				})

				It("Should return an error when the apikey doesn't exist", func() {
					config.Set("apikey", "does-not-exist")
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 401))
				})

				It("Should create a new request for a valid submit", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse)
					Expect(err).To(BeNil())
					Expect(uploadResponse.StartingBlock).To(BeNumerically("==", 1))

					uploadEntry, err := uploads.ByID(uploadResponse.RequestID)
					Expect(err).To(BeNil())
					Expect(uploadEntry.ID).To(Equal(uploadResponse.RequestID))
					err = uploads.Delete(uploadEntry.ID)
					Expect(err).To(BeNil())
				})
			})

			Context("Existing uploads that could match", func() {
				var (
					idsToDelete []string
				)

				BeforeEach(func() {
					idsToDelete = []string{}
				})

				AfterEach(func() {
					for _, id := range idsToDelete {
						uploads.Delete(id)
					}
				})

				var addID = func(id string) {
					idsToDelete = append(idsToDelete, id)
				}

				It("Should find an existing upload rather than create a new one", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var firstUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &firstUploadResponse)
					Expect(err).To(BeNil())
					Expect(firstUploadResponse.StartingBlock).To(BeNumerically("==", 1))

					// Resend request - we should get the exact same request id back
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var secondUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &secondUploadResponse)
					Expect(err).To(BeNil())
					Expect(secondUploadResponse.StartingBlock).To(BeNumerically("==", firstUploadResponse.StartingBlock))
					Expect(secondUploadResponse.RequestID).To(Equal(firstUploadResponse.RequestID))
					addID(firstUploadResponse.RequestID)
				})

				It("Should create a new upload when the request has a different checksum", func() {
					// Create two upload requests that are identical except for their checksums. This
					// should result in two different requests.

					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var firstUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &firstUploadResponse)
					Expect(err).To(BeNil())
					Expect(firstUploadResponse.StartingBlock).To(BeNumerically("==", 1))
					addID(firstUploadResponse.RequestID)

					// Send second request with a different checksum
					uploadRequest.Checksum = "def456"
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var secondUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &secondUploadResponse)
					Expect(err).To(BeNil())
					Expect(secondUploadResponse.StartingBlock).To(BeNumerically("==", 1))
					Expect(secondUploadResponse.RequestID).NotTo(Equal(firstUploadResponse.RequestID))
					addID(secondUploadResponse.RequestID)
				})
			})

			Context("Restarting upload requests", func() {
				var (
					idsToDelete []string
				)

				BeforeEach(func() {
					idsToDelete = []string{}
				})

				AfterEach(func() {
					for _, id := range idsToDelete {
						uploads.Delete(id)
					}
				})

				var addID = func(id string) {
					idsToDelete = append(idsToDelete, id)
				}

				It("Should ask for second block after sending first block and then requesting upload again", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse)
					Expect(err).To(BeNil())
					Expect(uploadResponse.StartingBlock).To(BeNumerically("==", 1))
					addID(uploadResponse.RequestID)

					// Second first block
					ezclient := ezhttp.NewClient()
					params := make(map[string]string)
					params["flowChunkNumber"] = "1"
					params["flowTotalChunks"] = "2"
					params["flowChunkSize"] = "2"
					params["flowTotalSize"] = "4"
					params["flowIdentifier"] = uploadResponse.RequestID
					params["flowFileName"] = "testreq.txt"
					params["flowRelativePath"] = "test/testreq.txt"
					params["projectID"] = "test"
					params["directoryID"] = "test"
					params["fileID"] = ""
					sc, err := ezclient.PostFileBytes(Api.Url("/upload/chunk"), "/tmp/test.txt", "chunkData",
						[]byte("ab"), params)
					Expect(err).To(BeNil())
					Expect(sc).To(BeNumerically("==", 200))

					// Now we will request this upload a second time.
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse2 CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse2)
					Expect(err).To(BeNil())
					Expect(uploadResponse2.StartingBlock).To(BeNumerically("==", 2))
					Expect(uploadResponse2.RequestID).To(Equal(uploadResponse.RequestID))
				})

				It("Should return an error when sending a bad id", func() {
					ezclient := ezhttp.NewClient()
					params := make(map[string]string)
					params["flowChunkNumber"] = "1"
					params["flowTotalChunks"] = "2"
					params["flowChunkSize"] = "2"
					params["flowTotalSize"] = "4"
					params["flowIdentifier"] = "i-dont-exist"
					params["flowFileName"] = "testreq.txt"
					params["flowRelativePath"] = "test/testreq.txt"
					params["projectID"] = "test"
					params["directoryID"] = "test"
					params["fileID"] = ""
					_, err := ezclient.PostFileBytes(Api.Url("/upload/chunk"), "/tmp/test.txt", "chunkData",
						[]byte("ab"), params)
					Expect(err).NotTo(BeNil())
				})
			})
		})

		Describe("get uploads tests", func() {
			It("Should return an error on a bad apikey", func() {
				config.Set("apikey", "test")
				resp, err := createUploadRequest(uploadRequest)
				Expect(err).To(BeNil())

				config.Set("apikey", "bad-key")
				r, _, errs := client.Get(Api.Url("/upload/test")).End()
				err = Api.IsError(r, errs)
				Expect(err).ToNot(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 401))

				err = uploads.Delete(resp.RequestID)
				Expect(err).To(BeNil())
			})

			It("Should return an error on a bad project", func() {
				config.Set("apikey", "test")
				r, _, errs := client.Get(Api.Url("/upload/bad-project-id")).End()
				err := Api.IsError(r, errs)
				fmt.Println("err = ", err)
				Expect(err).ToNot(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 400))
			})

			It("Should get existing upload requests for a project", func() {
				config.Set("apikey", "test")
				resp, err := createUploadRequest(uploadRequest)
				Expect(err).To(BeNil())
				r, body, errs := client.Get(Api.Url("/upload/test")).End()
				err = Api.IsError(r, errs)
				Expect(err).To(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 200))
				var entries []UploadEntry
				err = Api.ToJSON(body, &entries)
				Expect(err).To(BeNil())
				Expect(len(entries)).To(BeNumerically("==", 1))
				entry := entries[0]
				Expect(entry.RequestID).To(Equal(resp.RequestID))

				err = uploads.Delete(resp.RequestID)
				Expect(err).To(BeNil())
			})
		})
	})
})
<commit_msg>Remove Println.<commit_after>package mcstore

import (
	"fmt"
	"net/http/httptest"
	"time"

	"github.com/emicklei/go-restful"
	"github.com/materials-commons/config"
	"github.com/materials-commons/gohandy/ezhttp"
	c "github.com/materials-commons/mcstore/cmd/pkg/client"
	"github.com/materials-commons/mcstore/pkg/db/dai"
	"github.com/materials-commons/mcstore/testutil"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/parnurzeal/gorequest"
	"github.com/willf/bitset"
)

var _ = fmt.Println

var _ = Describe("UploadResource", func() {
	Describe("findStartingBlock method tests", func() {
		var (
			blocks *bitset.BitSet
		)

		BeforeEach(func() {
			blocks = bitset.New(10)
		})

		It("Should return 1 if no blocks have been set", func() {
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 1))
		})

		It("Should return 2 if the first block has been uploaded", func() {
			// BitSet starts a zero. Flowjs starts at 1. So we have to adjust.
			blocks.Set(0)
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 2))
		})

		It("Should return 1 if only the last block as been uploaded", func() {
			blocks.Set(9)
			block := findStartingBlock(blocks)
			Expect(block).To(BeNumerically("==", 1))
		})

		It("Should return 2 if only 2 has not been set (all others set)", func() {
			complement := blocks.Complement()
			complement.Clear(1) // second block
			block := findStartingBlock(complement)
			Expect(block).To(BeNumerically("==", 2))
		})
	})

	Describe("Upload REST API method tests", func() {
		var (
			client        *gorequest.SuperAgent
			server        *httptest.Server
			container     *restful.Container
			rr            *httptest.ResponseRecorder
			uploadRequest CreateUploadRequest
			uploads       dai.Uploads
		)

		BeforeEach(func() {
			client = c.NewGoRequest()
			container = NewServicesContainerForTest()
			server = httptest.NewServer(container)
			rr = httptest.NewRecorder()
			config.Set("mcurl", server.URL)
			uploadRequest = CreateUploadRequest{
				ProjectID:     "test",
				DirectoryID:   "test",
				DirectoryPath: "test/test",
				FileName:      "testreq.txt",
				FileSize:      4,
				ChunkSize:     2,
				FileMTime:     time.Now().Format(time.RFC1123),
				Checksum:      "abc123",
			}
			uploads = dai.NewRUploads(testutil.RSession())
		})

		var (
			createUploadRequest = func(req CreateUploadRequest) (*CreateUploadResponse, error) {
				r, body, errs := client.Post(Api.Url("/upload")).Send(req).End()
				if err := Api.IsError(r, errs); err != nil {
					return nil, err
				}

				var uploadResponse CreateUploadResponse
				if err := Api.ToJSON(body, &uploadResponse); err != nil {
					return nil, err
				}
				return &uploadResponse, nil
			}
		)

		AfterEach(func() {
			server.Close()
		})

		Describe("create upload tests", func() {
			Context("No existing uploads that match request", func() {
				It("Should return an error when the user doesn't have permission", func() {
					// Set apikey for user who doesn't have permission
					config.Set("apikey", "test2")
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 401))
				})

				It("Should return an error when the project doesn't exist", func() {
					config.Set("apikey", "test")
					uploadRequest.ProjectID = "does-not-exist"
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 400))
				})

				It("Should return an error when the directory doesn't exist", func() {
					config.Set("apikey", "test")
					uploadRequest.DirectoryID = "does-not-exist"
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 400))
				})

				It("Should return an error when the apikey doesn't exist", func() {
					config.Set("apikey", "does-not-exist")
					r, _, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).NotTo(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 401))
				})

				It("Should create a new request for a valid submit", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse)
					Expect(err).To(BeNil())
					Expect(uploadResponse.StartingBlock).To(BeNumerically("==", 1))

					uploadEntry, err := uploads.ByID(uploadResponse.RequestID)
					Expect(err).To(BeNil())
					Expect(uploadEntry.ID).To(Equal(uploadResponse.RequestID))
					err = uploads.Delete(uploadEntry.ID)
					Expect(err).To(BeNil())
				})
			})

			Context("Existing uploads that could match", func() {
				var (
					idsToDelete []string
				)

				BeforeEach(func() {
					idsToDelete = []string{}
				})

				AfterEach(func() {
					for _, id := range idsToDelete {
						uploads.Delete(id)
					}
				})

				var addID = func(id string) {
					idsToDelete = append(idsToDelete, id)
				}

				It("Should find an existing upload rather than create a new one", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var firstUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &firstUploadResponse)
					Expect(err).To(BeNil())
					Expect(firstUploadResponse.StartingBlock).To(BeNumerically("==", 1))

					// Resend request - we should get the exact same request id back
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var secondUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &secondUploadResponse)
					Expect(err).To(BeNil())
					Expect(secondUploadResponse.StartingBlock).To(BeNumerically("==", firstUploadResponse.StartingBlock))
					Expect(secondUploadResponse.RequestID).To(Equal(firstUploadResponse.RequestID))
					addID(firstUploadResponse.RequestID)
				})

				It("Should create a new upload when the request has a different checksum", func() {
					// Create two upload requests that are identical except for their checksums. This
					// should result in two different requests.

					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var firstUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &firstUploadResponse)
					Expect(err).To(BeNil())
					Expect(firstUploadResponse.StartingBlock).To(BeNumerically("==", 1))
					addID(firstUploadResponse.RequestID)

					// Send second request with a different checksum
					uploadRequest.Checksum = "def456"
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					var secondUploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &secondUploadResponse)
					Expect(err).To(BeNil())
					Expect(secondUploadResponse.StartingBlock).To(BeNumerically("==", 1))
					Expect(secondUploadResponse.RequestID).NotTo(Equal(firstUploadResponse.RequestID))
					addID(secondUploadResponse.RequestID)
				})
			})

			Context("Restarting upload requests", func() {
				var (
					idsToDelete []string
				)

				BeforeEach(func() {
					idsToDelete = []string{}
				})

				AfterEach(func() {
					for _, id := range idsToDelete {
						uploads.Delete(id)
					}
				})

				var addID = func(id string) {
					idsToDelete = append(idsToDelete, id)
				}

				It("Should ask for second block after sending first block and then requesting upload again", func() {
					config.Set("apikey", "test")
					r, body, errs := client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err := Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse)
					Expect(err).To(BeNil())
					Expect(uploadResponse.StartingBlock).To(BeNumerically("==", 1))
					addID(uploadResponse.RequestID)

					// Second first block
					ezclient := ezhttp.NewClient()
					params := make(map[string]string)
					params["flowChunkNumber"] = "1"
					params["flowTotalChunks"] = "2"
					params["flowChunkSize"] = "2"
					params["flowTotalSize"] = "4"
					params["flowIdentifier"] = uploadResponse.RequestID
					params["flowFileName"] = "testreq.txt"
					params["flowRelativePath"] = "test/testreq.txt"
					params["projectID"] = "test"
					params["directoryID"] = "test"
					params["fileID"] = ""
					sc, err := ezclient.PostFileBytes(Api.Url("/upload/chunk"), "/tmp/test.txt", "chunkData",
						[]byte("ab"), params)
					Expect(err).To(BeNil())
					Expect(sc).To(BeNumerically("==", 200))

					// Now we will request this upload a second time.
					r, body, errs = client.Post(Api.Url("/upload")).Send(uploadRequest).End()
					err = Api.IsError(r, errs)
					Expect(err).To(BeNil())
					Expect(r.StatusCode).To(BeNumerically("==", 200))
					var uploadResponse2 CreateUploadResponse
					err = Api.ToJSON(body, &uploadResponse2)
					Expect(err).To(BeNil())
					Expect(uploadResponse2.StartingBlock).To(BeNumerically("==", 2))
					Expect(uploadResponse2.RequestID).To(Equal(uploadResponse.RequestID))
				})

				It("Should return an error when sending a bad id", func() {
					ezclient := ezhttp.NewClient()
					params := make(map[string]string)
					params["flowChunkNumber"] = "1"
					params["flowTotalChunks"] = "2"
					params["flowChunkSize"] = "2"
					params["flowTotalSize"] = "4"
					params["flowIdentifier"] = "i-dont-exist"
					params["flowFileName"] = "testreq.txt"
					params["flowRelativePath"] = "test/testreq.txt"
					params["projectID"] = "test"
					params["directoryID"] = "test"
					params["fileID"] = ""
					_, err := ezclient.PostFileBytes(Api.Url("/upload/chunk"), "/tmp/test.txt", "chunkData",
						[]byte("ab"), params)
					Expect(err).NotTo(BeNil())
				})
			})
		})

		Describe("get uploads tests", func() {
			It("Should return an error on a bad apikey", func() {
				config.Set("apikey", "test")
				resp, err := createUploadRequest(uploadRequest)
				Expect(err).To(BeNil())

				config.Set("apikey", "bad-key")
				r, _, errs := client.Get(Api.Url("/upload/test")).End()
				err = Api.IsError(r, errs)
				Expect(err).ToNot(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 401))

				err = uploads.Delete(resp.RequestID)
				Expect(err).To(BeNil())
			})

			It("Should return an error on a bad project", func() {
				config.Set("apikey", "test")
				r, _, errs := client.Get(Api.Url("/upload/bad-project-id")).End()
				err := Api.IsError(r, errs)
				Expect(err).ToNot(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 400))
			})

			It("Should get existing upload requests for a project", func() {
				config.Set("apikey", "test")
				resp, err := createUploadRequest(uploadRequest)
				Expect(err).To(BeNil())
				r, body, errs := client.Get(Api.Url("/upload/test")).End()
				err = Api.IsError(r, errs)
				Expect(err).To(BeNil())
				Expect(r.StatusCode).To(BeNumerically("==", 200))
				var entries []UploadEntry
				err = Api.ToJSON(body, &entries)
				Expect(err).To(BeNil())
				Expect(len(entries)).To(BeNumerically("==", 1))
				entry := entries[0]
				Expect(entry.RequestID).To(Equal(resp.RequestID))

				err = uploads.Delete(resp.RequestID)
				Expect(err).To(BeNil())
			})
		})
	})
})
<|endoftext|>


<commit_before>package swgohhelp

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// AuthResponse represents the authentication response data.
type AuthResponse struct {
	TokenType   string `json:"token_type"`
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
}

// Player represents the full player profile information.
type Player struct {
	Name     string `json:"name"`
	AllyCode int    `json:"allyCode"`
	Level    int    `json:"level"`

	GuildName  string `json:"guildName"`
	GuildRefID string `json:"guildRefId"`

	Titles PlayerTitle `json:"titles"`

	Stats  []PlayerStat `json:"stats"`
	Roster Roster       `json:"roster"`

	Arena Arena `json:"arena"`

	UpdatedAt Timestamp `json:"updated"`
}

// PlayerTitle is a list of player unlocked and selected titles.
type PlayerTitle struct {
	Selected string   `json:"selected"`
	Unlocked []string `json:"unlocked"`
}

// PlayerStat is a single player profile statistic info, like how many battles won.
type PlayerStat struct {
	Name  string `json:"nameKey"`
	Value int64  `json:"value"`
	Index int64  `json:"index"`
}

// Roster is a helper collection to manipulate player roster
type Roster []Unit

// FindByID filter the unit collection by the DefID attribute
func (r Roster) FindByID(defID string) (*Unit, bool) {
	for _, unit := range r {
		if unit.DefID == defID {
			return &unit, true
		}
	}
	return nil, false
}

// FindByName filter the unit collection by name attribute
func (r Roster) FindByName(unitName string) (*Unit, bool) {
	for _, unit := range r {
		if strings.ToLower(unit.Name) == strings.ToLower(unitName) {
			return &unit, true
		}
	}
	return nil, false
}

// Mods returns the roster equiped mods
func (r Roster) Mods() (mods []Mod) {
	for i := range r {
		for j := range r[i].Mods {
			if r[i].Mods[j].UnitEquiped == "" {
				r[i].Mods[j].UnitEquiped = r[i].Name
			}
			mods = append(mods, r[i].Mods[j])
		}
	}
	return
}

// Unit is a game unit entity, character or ship.
type Unit struct {
	ID      string          `json:"id"`
	DefID   string          `json:"defId"`
	Name    string          `json:"nameKey"`
	Rarity  int             `json:"rarity"`
	Level   int             `json:"level"`
	XP      int             `json:"xp"`
	Gear    int             `json:"gear"`
	Equiped []UnitEquipment `json:"equipped"`

	CombatType CombatType `json:"combatType"`

	Skills []UnitSkill `json:"skills"`
	Mods   []Mod       `json:"mods"`
	Crew   []Unit      `json:"crew"`

	Stats *UnitStats `json:"stats,omitempty"`
}

// UnitStats unit statis information split by Final and FromMods
type UnitStats struct {
	Final    UnitStatItems `json:"final"`
	FromMods UnitStatItems `json:"mods"`
}

// UnitStatItems is a set of character statistics such as health, speed, etc.
type UnitStatItems struct {
	// Primary attributes
	Strength int `json:"Strength"`
	Agility  int `json:"Agility"`
	Tactics  int `json:"Tactics"`

	// General
	Health         int     `json:"Health"`
	Protection     int     `json:"Protection"`
	Speed          int     `json:"Speed"`
	CriticalDamage float64 `json:"Critical Damage"`
	Potency        float64 `json:"Potency"`
	Tenacity       float64 `json:"Tenacity"`
	HealthSteal    float64 `json:"Helth Steal"`

	// Physical Offense
	PhysicalDamage         int     `json:"Physical Damage"`
	PhysicalCriticalChance float64 `json:"Physical Critical Chance"`
	ArmorPenetration       int     `json:"Armor Penetration"`
	PhysicalAccuracy       float64 `json:"Physical Accuracy"`

	// Physical Survivability
	Armor                     float64 `json:"Armor"`
	DodgeChance               float64 `json:"Dodge Chance"`
	PhysicalCriticalAvoidance float64 `json:"Physical Critical Avoidance"`

	// Special Offense
	SpecialDamage         int     `json:"Special Damage"`
	SpecialCriticalChance float64 `json:"Special Critical Chance"`
	ResistancePenetration int     `json:"Resistance Penetration"`
	SpecialAccuracy       float64 `json:"Special Accuracy"`

	// Special Survivability
	Resistance               float64 `json:"Resistance"`
	DeflectionChance         float64 `json:"Deflection Chance"`
	SpecialCriticalAvoidance float64 `json:"Special Critical Avoidance"`
}

// UnitSkill is a single unit skill, with level and value
type UnitSkill struct {
	ID     string `json:"id"`
	Tier   int    `json:"tier"`
	Name   string `json:"nameKey"`
	IsZeta bool   `json:"isZeta"`
}

// Mod is a character mod detailed value.
type Mod struct {
	ID          string    `json:"id"`
	Level       int       `json:"level"`
	Set         ModSet    `json:"set"`
	Tier        int       `json:"tier"`
	Pips        int       `json:"pips"`
	Slot        ModSlot   `json:"slot"`
	UnitEquiped string    `json:"unit_equiped"`
	Primary     ModStat   `json:"primaryStat"`
	Secondaries []ModStat `json:"secondaryStat"`
}

func (m Mod) String() string {
	var buff bytes.Buffer
	fmt.Fprintf(&buff, "%d* Lv%d %s %s '%s' ", m.Pips, m.Level, m.Set, m.Slot, m.UnitEquiped)
	fmt.Fprintf(&buff, "%s ", m.Primary)
	for _, s := range m.Secondaries {
		fmt.Fprintf(&buff, "%s", s)
	}
	return buff.String()
}

// ModStat is a single mod stat name and value.
// Unit is the key name of the modified character stat attribute,
// and value is always a floating point even when precision is zero.
type ModStat struct {
	Unit  ModUnitStat `json:"unitStat"`
	Value float64     `json:"value"`
	Roll  int         `json:"roll"`
}

func (s ModStat) String() string {
	return fmt.Sprintf("%.02f %s (%d)", s.Value, s.Unit, s.Roll)
}

// UnitEquipment is the unit equiped gear at the current level
type UnitEquipment struct {
	EquipmentID string `json:"equipmentId"`
	Slot        int    `json:"slot"`
	NameKey     string `json:"nameKey"`
}

// Arena wraps both arena rankings for the player.
type Arena struct {
	Char ArenaRanking `json:"char"`
	Ship ArenaRanking `json:"ship"`
}

// ArenaRanking holds player arena ranking.
type ArenaRanking struct {
	Rank  int         `json:"rank"`
	Squad []SquadUnit `json:"squad"`
}

// SquadUnit represents an arena squad unit identifier set.
type SquadUnit struct {
	ID     string        `json:"id"`
	UnitID string        `json:"defId"`
	Type   SquadUnitType `json:"squadUnitType"`
}

// Timestamp is a helper unix timestamp JSON marshaller/unmarshaller.
// Source: https://gist.github.com/alexmcroberts/219127816e7a16c7bd70
type Timestamp time.Time

// MarshalJSON implements the json.Marshaler interface.
func (t Timestamp) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(time.Time(t).Unix()*1000, 10)), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *Timestamp) UnmarshalJSON(s []byte) (err error) {
	r := strings.Replace(string(s), `"`, ``, -1)
	q, err := strconv.ParseInt(r, 10, 64)
	if err != nil {
		return err
	}
	*(*time.Time)(t) = time.Unix(q/1000, 0)
	return
}

// String implements the Stringer interface.
func (t Timestamp) String() string { return time.Time(t).String() }
<commit_msg>update<commit_after>package swgohhelp

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
	"time"
)

// AuthResponse represents the authentication response data.
type AuthResponse struct {
	TokenType   string `json:"token_type"`
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
}

// Player represents the full player profile information.
type Player struct {
	Name     string `json:"name"`
	AllyCode int    `json:"allyCode"`
	Level    int    `json:"level"`

	GuildName  string `json:"guildName"`
	GuildRefID string `json:"guildRefId"`

	Titles PlayerTitle `json:"titles"`

	Stats  []PlayerStat `json:"stats"`
	Roster Roster       `json:"roster"`

	Arena Arena `json:"arena"`

	UpdatedAt Timestamp `json:"updated"`
}

// PlayerTitle is a list of player unlocked and selected titles.
type PlayerTitle struct {
	Selected string   `json:"selected"`
	Unlocked []string `json:"unlocked"`
}

// PlayerStat is a single player profile statistic info, like how many battles won.
type PlayerStat struct {
	Name  string `json:"nameKey"`
	Value int64  `json:"value"`
	Index int64  `json:"index"`
}

// Roster is a helper collection to manipulate player roster
type Roster []Unit

// FindByID filter the unit collection by the DefID attribute
func (r Roster) FindByID(defID string) (*Unit, bool) {
	for _, unit := range r {
		if unit.DefID == defID {
			return &unit, true
		}
	}
	return nil, false
}

// FindByName filter the unit collection by name attribute
func (r Roster) FindByName(unitName string) (*Unit, bool) {
	for _, unit := range r {
		if strings.ToLower(unit.Name) == strings.ToLower(unitName) {
			return &unit, true
		}
	}
	return nil, false
}

// Mods returns the roster equiped mods
func (r Roster) Mods() (mods []Mod) {
	for i := range r {
		for j := range r[i].Mods {
			if r[i].Mods[j].UnitEquiped == "" {
				r[i].Mods[j].UnitEquiped = r[i].Name
			}
			mods = append(mods, r[i].Mods[j])
		}
	}
	return
}

// Unit is a game unit entity, character or ship.
type Unit struct {
	ID      string          `json:"id"`
	DefID   string          `json:"defId"`
	Name    string          `json:"nameKey"`
	Rarity  int             `json:"rarity"`
	Level   int             `json:"level"`
	XP      int             `json:"xp"`
	GalacticPower int	`json:"gp"`
	Gear    int             `json:"gear"`
	Equiped []UnitEquipment `json:"equipped"`

	CombatType CombatType `json:"combatType"`

	Skills []UnitSkill `json:"skills"`
	Mods   []Mod       `json:"mods"`
	Crew   []Unit      `json:"crew"`

	Stats *UnitStats `json:"stats,omitempty"`
}

// UnitStats unit statis information split by Final and FromMods
type UnitStats struct {
	Final    UnitStatItems `json:"final"`
	FromMods UnitStatItems `json:"mods"`
}

// UnitStatItems is a set of character statistics such as health, speed, etc.
type UnitStatItems struct {
	// Primary attributes
	Strength int `json:"Strength"`
	Agility  int `json:"Agility"`
	Tactics  int `json:"Tactics"`

	// General
	Health         int     `json:"Health"`
	Protection     int     `json:"Protection"`
	Speed          int     `json:"Speed"`
	CriticalDamage float64 `json:"Critical Damage"`
	Potency        float64 `json:"Potency"`
	Tenacity       float64 `json:"Tenacity"`
	HealthSteal    float64 `json:"Helth Steal"`

	// Physical Offense
	PhysicalDamage         int     `json:"Physical Damage"`
	PhysicalCriticalChance float64 `json:"Physical Critical Chance"`
	ArmorPenetration       int     `json:"Armor Penetration"`
	PhysicalAccuracy       float64 `json:"Physical Accuracy"`

	// Physical Survivability
	Armor                     float64 `json:"Armor"`
	DodgeChance               float64 `json:"Dodge Chance"`
	PhysicalCriticalAvoidance float64 `json:"Physical Critical Avoidance"`

	// Special Offense
	SpecialDamage         int     `json:"Special Damage"`
	SpecialCriticalChance float64 `json:"Special Critical Chance"`
	ResistancePenetration int     `json:"Resistance Penetration"`
	SpecialAccuracy       float64 `json:"Special Accuracy"`

	// Special Survivability
	Resistance               float64 `json:"Resistance"`
	DeflectionChance         float64 `json:"Deflection Chance"`
	SpecialCriticalAvoidance float64 `json:"Special Critical Avoidance"`
}

// UnitSkill is a single unit skill, with level and value
type UnitSkill struct {
	ID     string `json:"id"`
	Tier   int    `json:"tier"`
	Name   string `json:"nameKey"`
	IsZeta bool   `json:"isZeta"`
}

// Mod is a character mod detailed value.
type Mod struct {
	ID          string    `json:"id"`
	Level       int       `json:"level"`
	Set         ModSet    `json:"set"`
	Tier        int       `json:"tier"`
	Pips        int       `json:"pips"`
	Slot        ModSlot   `json:"slot"`
	UnitEquiped string    `json:"unit_equiped"`
	Primary     ModStat   `json:"primaryStat"`
	Secondaries []ModStat `json:"secondaryStat"`
}

func (m Mod) String() string {
	var buff bytes.Buffer
	fmt.Fprintf(&buff, "%d* Lv%d %s %s '%s' ", m.Pips, m.Level, m.Set, m.Slot, m.UnitEquiped)
	fmt.Fprintf(&buff, "%s ", m.Primary)
	for _, s := range m.Secondaries {
		fmt.Fprintf(&buff, "%s", s)
	}
	return buff.String()
}

// ModStat is a single mod stat name and value.
// Unit is the key name of the modified character stat attribute,
// and value is always a floating point even when precision is zero.
type ModStat struct {
	Unit  ModUnitStat `json:"unitStat"`
	Value float64     `json:"value"`
	Roll  int         `json:"roll"`
}

func (s ModStat) String() string {
	return fmt.Sprintf("%.02f %s (%d)", s.Value, s.Unit, s.Roll)
}

// UnitEquipment is the unit equiped gear at the current level
type UnitEquipment struct {
	EquipmentID string `json:"equipmentId"`
	Slot        int    `json:"slot"`
	NameKey     string `json:"nameKey"`
}

// Arena wraps both arena rankings for the player.
type Arena struct {
	Char ArenaRanking `json:"char"`
	Ship ArenaRanking `json:"ship"`
}

// ArenaRanking holds player arena ranking.
type ArenaRanking struct {
	Rank  int         `json:"rank"`
	Squad []SquadUnit `json:"squad"`
}

// SquadUnit represents an arena squad unit identifier set.
type SquadUnit struct {
	ID     string        `json:"id"`
	UnitID string        `json:"defId"`
	Type   SquadUnitType `json:"squadUnitType"`
}

// Timestamp is a helper unix timestamp JSON marshaller/unmarshaller.
// Source: https://gist.github.com/alexmcroberts/219127816e7a16c7bd70
type Timestamp time.Time

// MarshalJSON implements the json.Marshaler interface.
func (t Timestamp) MarshalJSON() ([]byte, error) {
	return []byte(strconv.FormatInt(time.Time(t).Unix()*1000, 10)), nil
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *Timestamp) UnmarshalJSON(s []byte) (err error) {
	r := strings.Replace(string(s), `"`, ``, -1)
	q, err := strconv.ParseInt(r, 10, 64)
	if err != nil {
		return err
	}
	*(*time.Time)(t) = time.Unix(q/1000, 0)
	return
}

// String implements the Stringer interface.
func (t Timestamp) String() string { return time.Time(t).String() }
<|endoftext|>


<commit_before>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package RuntimeTest

import (
	"context"
	"fmt"
	"math/rand"

	"github.com/cilium/cilium/test/helpers"

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

const (
	// MonitorDropNotification represents the DropNotification configuration
	// value for the Cilium monitor
	MonitorDropNotification = "DropNotification"

	// MonitorTraceNotification represents the TraceNotification configuration
	// value for the Cilium monitor
	MonitorTraceNotification = "TraceNotification"

	// MonitorDebug represents the Debug configuration  value for
	// the Cilium monitor
	MonitorDebug = "Debug"
)

var _ = Describe("RuntimeMonitorTest", func() {

	var initialized bool
	var logger *logrus.Entry
	var vm *helpers.SSHMeta

	initialize := func() {
		if initialized == true {
			return
		}
		logger = log.WithFields(logrus.Fields{"testName": "RuntimeMonitorTest"})
		logger.Info("Starting")
		vm = helpers.CreateNewRuntimeHelper(helpers.Runtime, logger)
		vm.WaitUntilReady(100)
		vm.NetworkCreate(helpers.CiliumDockerNetwork, "")

		res := vm.SetPolicyEnforcement(helpers.PolicyEnforcementDefault)
		res.ExpectSuccess()

		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		initialized = true
	}

	BeforeEach(func() {
		initialize()
	})

	AfterEach(func() {
		if CurrentGinkgoTestDescription().Failed {
			vm.ReportFailed()
		}

		vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
	})

	It("Cilium monitor verbose mode", func() {

		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true",
			MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()

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

		res = vm.ExecContext(ctx, "cilium monitor -v")
		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		helpers.Sleep(5)
		cancel()
		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		for k, v := range endpoints {
			filter := fmt.Sprintf("FROM %s DEBUG:", v)
			vm.ContainerExec(k, helpers.Ping(helpers.Httpd1))
			Expect(res.Output().String()).Should(ContainSubstring(filter))
		}
	})

	It("Cilium monitor event types", func() {
		eventTypes := map[string]string{
			"drop":    "DROP:",
			"debug":   "DEBUG:",
			"capture": "DEBUG:",
		}

		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true",
			MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()
		for k, v := range eventTypes {
			By(fmt.Sprintf("Type %s", k))

			ctx, cancel := context.WithCancel(context.Background())
			res := vm.ExecContext(ctx, fmt.Sprintf("cilium monitor --type %s -v", k))
			vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
			helpers.Sleep(5)
			cancel()

			Expect(res.CountLines()).Should(BeNumerically(">", 3))
			Expect(res.Output().String()).Should(ContainSubstring(v))
			vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
		}
	})

	It("cilium monitor check --from", func() {
		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true", MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor --type debug --from %s -v", endpoints[helpers.App1]))
		vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
		helpers.Sleep(5)
		cancel()

		Expect(res.CountLines()).Should(BeNumerically(">", 3))
		filter := fmt.Sprintf("FROM %s DEBUG:", endpoints[helpers.App1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))

		//MonitorDebug mode shouldn't have DROP lines
		Expect(res.Output().String()).ShouldNot(ContainSubstring("DROP"))

	})

	It("cilium monitor check --to", func() {

		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=always", MonitorDebug, MonitorDropNotification, MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		vm.WaitEndpointsReady()
		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor --type drop -v --to %s", endpoints[helpers.Httpd1]))

		vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
		vm.ContainerExec(helpers.App2, helpers.Ping(helpers.Httpd1))
		helpers.Sleep(5)
		cancel()

		Expect(res.CountLines()).Should(BeNumerically(">", 3))
		filter := fmt.Sprintf("FROM %s DROP:", endpoints[helpers.Httpd1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))

	})

	It("cilium monitor check --related-to", func() {

		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=always", MonitorDebug, MonitorDropNotification, MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor -v --type drop --related-to %s", endpoints[helpers.Httpd1]))

		vm.WaitEndpointsReady()
		vm.ContainerExec(helpers.App1, helpers.CurlFail("http://httpd1/public"))

		helpers.Sleep(2)
		cancel()
		Expect(res.CountLines()).Should(BeNumerically(">=", 3))
		filter := fmt.Sprintf("FROM %s DROP:", endpoints[helpers.Httpd1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))
	})

	It("multiple monitors", func() {
		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=default",
			MonitorDebug, MonitorDropNotification,
			MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		var monitorRes []*helpers.CmdRes

		vm.ContainerCreate(helpers.Client, helpers.NetperfImage, helpers.CiliumDockerNetwork, "-l id.client")
		vm.ContainerCreate(helpers.Server, helpers.NetperfImage, helpers.CiliumDockerNetwork, "-l id.server")

		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		defer vm.ContainerRm(helpers.Client)
		defer vm.ContainerRm(helpers.Server)

		ctx, cancelfn := context.WithCancel(context.Background())

		for i := 1; i <= 3; i++ {
			monitorRes = append(monitorRes, vm.ExecContext(ctx, "cilium monitor"))
		}

		vm.ContainerExec(helpers.Client, helpers.Ping(helpers.Server))
		cancelfn()

		Expect(monitorRes[0].CountLines()).Should(BeNumerically(">", 2))

		//Due to the ssh connection, sometimes the result has one line more in
		//any output. So we check at least 5 lines are in the all outputs.
		for i := 0; i < 5; i++ {
			//ln: return a random number in the array len upper than 5 (First 5 lines)
			ln := rand.Intn((len(monitorRes[0].ByLines())-1)-5) + 5
			str := monitorRes[0].ByLines()[ln]
			Expect(monitorRes[1].Output().String()).Should(ContainSubstring(str))
			Expect(monitorRes[2].Output().String()).Should(ContainSubstring(str))
		}
	})
})
<commit_msg>Ginkgo: MonitorTest wait until pods are ready<commit_after>// Copyright 2017 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package RuntimeTest

import (
	"context"
	"fmt"
	"math/rand"

	"github.com/cilium/cilium/test/helpers"

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

const (
	// MonitorDropNotification represents the DropNotification configuration
	// value for the Cilium monitor
	MonitorDropNotification = "DropNotification"

	// MonitorTraceNotification represents the TraceNotification configuration
	// value for the Cilium monitor
	MonitorTraceNotification = "TraceNotification"

	// MonitorDebug represents the Debug configuration  value for
	// the Cilium monitor
	MonitorDebug = "Debug"
)

var _ = Describe("RuntimeMonitorTest", func() {

	var initialized bool
	var logger *logrus.Entry
	var vm *helpers.SSHMeta

	initialize := func() {
		if initialized == true {
			return
		}
		logger = log.WithFields(logrus.Fields{"testName": "RuntimeMonitorTest"})
		logger.Info("Starting")
		vm = helpers.CreateNewRuntimeHelper(helpers.Runtime, logger)
		vm.WaitUntilReady(100)
		vm.NetworkCreate(helpers.CiliumDockerNetwork, "")

		res := vm.SetPolicyEnforcement(helpers.PolicyEnforcementDefault)
		res.ExpectSuccess()

		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		initialized = true
	}

	BeforeEach(func() {
		initialize()
	})

	AfterEach(func() {
		if CurrentGinkgoTestDescription().Failed {
			vm.ReportFailed()
		}

		vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
	})

	It("Cilium monitor verbose mode", func() {

		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true",
			MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()

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

		res = vm.ExecContext(ctx, "cilium monitor -v")
		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		helpers.Sleep(5)
		cancel()
		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		for k, v := range endpoints {
			filter := fmt.Sprintf("FROM %s DEBUG:", v)
			vm.ContainerExec(k, helpers.Ping(helpers.Httpd1))
			Expect(res.Output().String()).Should(ContainSubstring(filter))
		}
	})

	It("Cilium monitor event types", func() {
		eventTypes := map[string]string{
			"drop":    "DROP:",
			"debug":   "DEBUG:",
			"capture": "DEBUG:",
		}

		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true",
			MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()
		for k, v := range eventTypes {
			By(fmt.Sprintf("Type %s", k))

			ctx, cancel := context.WithCancel(context.Background())
			res := vm.ExecContext(ctx, fmt.Sprintf("cilium monitor --type %s -v", k))
			vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
			areEndpointsReady := vm.WaitEndpointsReady()
			Expect(areEndpointsReady).Should(BeTrue())

			vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
			helpers.Sleep(5)
			cancel()

			Expect(res.CountLines()).Should(BeNumerically(">", 3))
			Expect(res.Output().String()).Should(ContainSubstring(v))
			vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
		}
	})

	It("cilium monitor check --from", func() {
		res := vm.ExecCilium(fmt.Sprintf("config %s=true %s=true %s=true", MonitorDebug, MonitorDropNotification, MonitorTraceNotification))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor --type debug --from %s -v", endpoints[helpers.App1]))
		vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
		helpers.Sleep(5)
		cancel()

		Expect(res.CountLines()).Should(BeNumerically(">", 3))
		filter := fmt.Sprintf("FROM %s DEBUG:", endpoints[helpers.App1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))

		//MonitorDebug mode shouldn't have DROP lines
		Expect(res.Output().String()).ShouldNot(ContainSubstring("DROP"))

	})

	It("cilium monitor check --to", func() {

		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=always", MonitorDebug, MonitorDropNotification, MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		vm.WaitEndpointsReady()
		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor --type drop -v --to %s", endpoints[helpers.Httpd1]))

		vm.ContainerExec(helpers.App1, helpers.Ping(helpers.Httpd1))
		vm.ContainerExec(helpers.App2, helpers.Ping(helpers.Httpd1))
		helpers.Sleep(5)
		cancel()

		Expect(res.CountLines()).Should(BeNumerically(">", 3))
		filter := fmt.Sprintf("FROM %s DROP:", endpoints[helpers.Httpd1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))

	})

	It("cilium monitor check --related-to", func() {

		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=always", MonitorDebug, MonitorDropNotification, MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		endpoints, err := vm.GetEndpointsIds()
		Expect(err).Should(BeNil())

		ctx, cancel := context.WithCancel(context.Background())
		res = vm.ExecContext(ctx, fmt.Sprintf(
			"cilium monitor -v --type drop --related-to %s", endpoints[helpers.Httpd1]))

		vm.WaitEndpointsReady()
		vm.ContainerExec(helpers.App1, helpers.CurlFail("http://httpd1/public"))

		helpers.Sleep(2)
		cancel()
		Expect(res.CountLines()).Should(BeNumerically(">=", 3))
		filter := fmt.Sprintf("FROM %s DROP:", endpoints[helpers.Httpd1])
		Expect(res.Output().String()).Should(ContainSubstring(filter))
	})

	It("multiple monitors", func() {
		res := vm.ExecCilium(fmt.Sprintf(
			"config %s=true %s=true %s=true %s=default",
			MonitorDebug, MonitorDropNotification,
			MonitorTraceNotification, helpers.PolicyEnforcement))
		res.ExpectSuccess()

		var monitorRes []*helpers.CmdRes

		vm.ContainerCreate(helpers.Client, helpers.NetperfImage, helpers.CiliumDockerNetwork, "-l id.client")
		vm.ContainerCreate(helpers.Server, helpers.NetperfImage, helpers.CiliumDockerNetwork, "-l id.server")

		areEndpointsReady := vm.WaitEndpointsReady()
		Expect(areEndpointsReady).Should(BeTrue())

		defer vm.ContainerRm(helpers.Client)
		defer vm.ContainerRm(helpers.Server)

		ctx, cancelfn := context.WithCancel(context.Background())

		for i := 1; i <= 3; i++ {
			monitorRes = append(monitorRes, vm.ExecContext(ctx, "cilium monitor"))
		}

		vm.ContainerExec(helpers.Client, helpers.Ping(helpers.Server))
		cancelfn()

		Expect(monitorRes[0].CountLines()).Should(BeNumerically(">", 2))

		//Due to the ssh connection, sometimes the result has one line more in
		//any output. So we check at least 5 lines are in the all outputs.
		for i := 0; i < 5; i++ {
			//ln: return a random number in the array len upper than 5 (First 5 lines)
			ln := rand.Intn((len(monitorRes[0].ByLines())-1)-5) + 5
			str := monitorRes[0].ByLines()[ln]
			Expect(monitorRes[1].Output().String()).Should(ContainSubstring(str))
			Expect(monitorRes[2].Output().String()).Should(ContainSubstring(str))
		}
	})
})
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/config"
)

// BuiltinEvalContext is an EvalContext implementation that is used by
// Terraform by default.
type BuiltinEvalContext struct {
	// PathValue is the Path that this context is operating within.
	PathValue []string

	// Interpolater setting below affect the interpolation of variables.
	//
	// The InterpolaterVars are the exact value for ${var.foo} values.
	// The map is shared between all contexts and is a mapping of
	// PATH to KEY to VALUE. Because it is shared by all contexts as well
	// as the Interpolater itself, it is protected by InterpolaterVarLock
	// which must be locked during any access to the map.
	Interpolater        *Interpolater
	InterpolaterVars    map[string]map[string]interface{}
	InterpolaterVarLock *sync.Mutex

	Components          contextComponentFactory
	Hooks               []Hook
	InputValue          UIInput
	ProviderCache       map[string]ResourceProvider
	ProviderConfigCache map[string]*ResourceConfig
	ProviderInputConfig map[string]map[string]interface{}
	ProviderLock        *sync.Mutex
	ProvisionerCache    map[string]ResourceProvisioner
	ProvisionerLock     *sync.Mutex
	DiffValue           *Diff
	DiffLock            *sync.RWMutex
	StateValue          *State
	StateLock           *sync.RWMutex

	once sync.Once
}

func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
	for _, h := range ctx.Hooks {
		action, err := fn(h)
		if err != nil {
			return err
		}

		switch action {
		case HookActionContinue:
			continue
		case HookActionHalt:
			// Return an early exit error to trigger an early exit
			log.Printf("[WARN] Early exit triggered by hook: %T", h)
			return EvalEarlyExitError{}
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) Input() UIInput {
	return ctx.InputValue
}

func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
	ctx.once.Do(ctx.init)

	// If we already initialized, it is an error
	if p := ctx.Provider(n); p != nil {
		return nil, fmt.Errorf("Provider '%s' already initialized", n)
	}

	// Warning: make sure to acquire these locks AFTER the call to Provider
	// above, since it also acquires locks.
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	typeName := strings.SplitN(n, ".", 2)[0]
	uid := n

	p, err := ctx.Components.ResourceProvider(typeName, uid)
	if err != nil {
		return nil, err
	}

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	ctx.ProviderCache[PathCacheKey(providerPath)] = p
	return p, nil
}

func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
	ctx.once.Do(ctx.init)

	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	return ctx.ProviderCache[PathCacheKey(providerPath)]
}

func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
	ctx.once.Do(ctx.init)

	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	var provider interface{}
	provider = ctx.ProviderCache[PathCacheKey(providerPath)]
	if provider != nil {
		if p, ok := provider.(ResourceProviderCloser); ok {
			delete(ctx.ProviderCache, PathCacheKey(providerPath))
			return p.Close()
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) ConfigureProvider(
	n string, cfg *ResourceConfig) error {
	p := ctx.Provider(n)
	if p == nil {
		return fmt.Errorf("Provider '%s' not initialized", n)
	}

	if err := ctx.SetProviderConfig(n, cfg); err != nil {
		return nil
	}

	return p.Configure(cfg)
}

func (ctx *BuiltinEvalContext) SetProviderConfig(
	n string, cfg *ResourceConfig) error {
	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	// Save the configuration
	ctx.ProviderLock.Lock()
	ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
	ctx.ProviderLock.Unlock()

	return nil
}

func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	// Make a copy of the path so we can safely edit it
	path := ctx.Path()
	pathCopy := make([]string, len(path)+1)
	copy(pathCopy, path)

	// Go up the tree.
	for i := len(path) - 1; i >= 0; i-- {
		pathCopy[i+1] = n
		k := PathCacheKey(pathCopy[:i+2])
		if v, ok := ctx.ProviderInputConfig[k]; ok {
			return v
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	// Save the configuration
	ctx.ProviderLock.Lock()
	ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c
	ctx.ProviderLock.Unlock()
}

func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	// Make a copy of the path so we can safely edit it
	path := ctx.Path()
	pathCopy := make([]string, len(path)+1)
	copy(pathCopy, path)

	// Go up the tree.
	for i := len(path) - 1; i >= 0; i-- {
		pathCopy[i+1] = n
		k := PathCacheKey(pathCopy[:i+2])
		if v, ok := ctx.ProviderConfigCache[k]; ok {
			return v
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) InitProvisioner(
	n string) (ResourceProvisioner, error) {
	ctx.once.Do(ctx.init)

	// If we already initialized, it is an error
	if p := ctx.Provisioner(n); p != nil {
		return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
	}

	// Warning: make sure to acquire these locks AFTER the call to Provisioner
	// above, since it also acquires locks.
	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	p, err := ctx.Components.ResourceProvisioner(n, n)
	if err != nil {
		return nil, err
	}

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n

	ctx.ProvisionerCache[PathCacheKey(provPath)] = p
	return p, nil
}

func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
	ctx.once.Do(ctx.init)

	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n

	return ctx.ProvisionerCache[PathCacheKey(provPath)]
}

func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
	ctx.once.Do(ctx.init)

	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n

	var prov interface{}
	prov = ctx.ProvisionerCache[PathCacheKey(provPath)]
	if prov != nil {
		if p, ok := prov.(ResourceProvisionerCloser); ok {
			delete(ctx.ProvisionerCache, PathCacheKey(provPath))
			return p.Close()
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) Interpolate(
	cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
	if cfg != nil {
		scope := &InterpolationScope{
			Path:     ctx.Path(),
			Resource: r,
		}

		vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
		if err != nil {
			return nil, err
		}

		// Do the interpolation
		if err := cfg.Interpolate(vs); err != nil {
			return nil, err
		}
	}

	result := NewResourceConfig(cfg)
	result.interpolateForce()
	return result, nil
}

func (ctx *BuiltinEvalContext) Path() []string {
	return ctx.PathValue
}

func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) {
	ctx.InterpolaterVarLock.Lock()
	defer ctx.InterpolaterVarLock.Unlock()

	path := make([]string, len(ctx.Path())+1)
	copy(path, ctx.Path())
	path[len(path)-1] = n
	key := PathCacheKey(path)

	vars := ctx.InterpolaterVars[key]
	if vars == nil {
		vars = make(map[string]interface{})
		ctx.InterpolaterVars[key] = vars
	}

	for k, v := range vs {
		vars[k] = v
	}
}

func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
	return ctx.DiffValue, ctx.DiffLock
}

func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
	return ctx.StateValue, ctx.StateLock
}

func (ctx *BuiltinEvalContext) init() {
}
<commit_msg>terraform: component uid includes the path<commit_after>package terraform

import (
	"fmt"
	"log"
	"strings"
	"sync"

	"github.com/hashicorp/terraform/config"
)

// BuiltinEvalContext is an EvalContext implementation that is used by
// Terraform by default.
type BuiltinEvalContext struct {
	// PathValue is the Path that this context is operating within.
	PathValue []string

	// Interpolater setting below affect the interpolation of variables.
	//
	// The InterpolaterVars are the exact value for ${var.foo} values.
	// The map is shared between all contexts and is a mapping of
	// PATH to KEY to VALUE. Because it is shared by all contexts as well
	// as the Interpolater itself, it is protected by InterpolaterVarLock
	// which must be locked during any access to the map.
	Interpolater        *Interpolater
	InterpolaterVars    map[string]map[string]interface{}
	InterpolaterVarLock *sync.Mutex

	Components          contextComponentFactory
	Hooks               []Hook
	InputValue          UIInput
	ProviderCache       map[string]ResourceProvider
	ProviderConfigCache map[string]*ResourceConfig
	ProviderInputConfig map[string]map[string]interface{}
	ProviderLock        *sync.Mutex
	ProvisionerCache    map[string]ResourceProvisioner
	ProvisionerLock     *sync.Mutex
	DiffValue           *Diff
	DiffLock            *sync.RWMutex
	StateValue          *State
	StateLock           *sync.RWMutex

	once sync.Once
}

func (ctx *BuiltinEvalContext) Hook(fn func(Hook) (HookAction, error)) error {
	for _, h := range ctx.Hooks {
		action, err := fn(h)
		if err != nil {
			return err
		}

		switch action {
		case HookActionContinue:
			continue
		case HookActionHalt:
			// Return an early exit error to trigger an early exit
			log.Printf("[WARN] Early exit triggered by hook: %T", h)
			return EvalEarlyExitError{}
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) Input() UIInput {
	return ctx.InputValue
}

func (ctx *BuiltinEvalContext) InitProvider(n string) (ResourceProvider, error) {
	ctx.once.Do(ctx.init)

	// If we already initialized, it is an error
	if p := ctx.Provider(n); p != nil {
		return nil, fmt.Errorf("Provider '%s' already initialized", n)
	}

	// Warning: make sure to acquire these locks AFTER the call to Provider
	// above, since it also acquires locks.
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n
	key := PathCacheKey(providerPath)

	typeName := strings.SplitN(n, ".", 2)[0]
	p, err := ctx.Components.ResourceProvider(typeName, key)
	if err != nil {
		return nil, err
	}

	ctx.ProviderCache[key] = p
	return p, nil
}

func (ctx *BuiltinEvalContext) Provider(n string) ResourceProvider {
	ctx.once.Do(ctx.init)

	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	return ctx.ProviderCache[PathCacheKey(providerPath)]
}

func (ctx *BuiltinEvalContext) CloseProvider(n string) error {
	ctx.once.Do(ctx.init)

	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	var provider interface{}
	provider = ctx.ProviderCache[PathCacheKey(providerPath)]
	if provider != nil {
		if p, ok := provider.(ResourceProviderCloser); ok {
			delete(ctx.ProviderCache, PathCacheKey(providerPath))
			return p.Close()
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) ConfigureProvider(
	n string, cfg *ResourceConfig) error {
	p := ctx.Provider(n)
	if p == nil {
		return fmt.Errorf("Provider '%s' not initialized", n)
	}

	if err := ctx.SetProviderConfig(n, cfg); err != nil {
		return nil
	}

	return p.Configure(cfg)
}

func (ctx *BuiltinEvalContext) SetProviderConfig(
	n string, cfg *ResourceConfig) error {
	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	// Save the configuration
	ctx.ProviderLock.Lock()
	ctx.ProviderConfigCache[PathCacheKey(providerPath)] = cfg
	ctx.ProviderLock.Unlock()

	return nil
}

func (ctx *BuiltinEvalContext) ProviderInput(n string) map[string]interface{} {
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	// Make a copy of the path so we can safely edit it
	path := ctx.Path()
	pathCopy := make([]string, len(path)+1)
	copy(pathCopy, path)

	// Go up the tree.
	for i := len(path) - 1; i >= 0; i-- {
		pathCopy[i+1] = n
		k := PathCacheKey(pathCopy[:i+2])
		if v, ok := ctx.ProviderInputConfig[k]; ok {
			return v
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) SetProviderInput(n string, c map[string]interface{}) {
	providerPath := make([]string, len(ctx.Path())+1)
	copy(providerPath, ctx.Path())
	providerPath[len(providerPath)-1] = n

	// Save the configuration
	ctx.ProviderLock.Lock()
	ctx.ProviderInputConfig[PathCacheKey(providerPath)] = c
	ctx.ProviderLock.Unlock()
}

func (ctx *BuiltinEvalContext) ParentProviderConfig(n string) *ResourceConfig {
	ctx.ProviderLock.Lock()
	defer ctx.ProviderLock.Unlock()

	// Make a copy of the path so we can safely edit it
	path := ctx.Path()
	pathCopy := make([]string, len(path)+1)
	copy(pathCopy, path)

	// Go up the tree.
	for i := len(path) - 1; i >= 0; i-- {
		pathCopy[i+1] = n
		k := PathCacheKey(pathCopy[:i+2])
		if v, ok := ctx.ProviderConfigCache[k]; ok {
			return v
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) InitProvisioner(
	n string) (ResourceProvisioner, error) {
	ctx.once.Do(ctx.init)

	// If we already initialized, it is an error
	if p := ctx.Provisioner(n); p != nil {
		return nil, fmt.Errorf("Provisioner '%s' already initialized", n)
	}

	// Warning: make sure to acquire these locks AFTER the call to Provisioner
	// above, since it also acquires locks.
	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n
	key := PathCacheKey(provPath)

	p, err := ctx.Components.ResourceProvisioner(n, key)
	if err != nil {
		return nil, err
	}

	ctx.ProvisionerCache[key] = p
	return p, nil
}

func (ctx *BuiltinEvalContext) Provisioner(n string) ResourceProvisioner {
	ctx.once.Do(ctx.init)

	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n

	return ctx.ProvisionerCache[PathCacheKey(provPath)]
}

func (ctx *BuiltinEvalContext) CloseProvisioner(n string) error {
	ctx.once.Do(ctx.init)

	ctx.ProvisionerLock.Lock()
	defer ctx.ProvisionerLock.Unlock()

	provPath := make([]string, len(ctx.Path())+1)
	copy(provPath, ctx.Path())
	provPath[len(provPath)-1] = n

	var prov interface{}
	prov = ctx.ProvisionerCache[PathCacheKey(provPath)]
	if prov != nil {
		if p, ok := prov.(ResourceProvisionerCloser); ok {
			delete(ctx.ProvisionerCache, PathCacheKey(provPath))
			return p.Close()
		}
	}

	return nil
}

func (ctx *BuiltinEvalContext) Interpolate(
	cfg *config.RawConfig, r *Resource) (*ResourceConfig, error) {
	if cfg != nil {
		scope := &InterpolationScope{
			Path:     ctx.Path(),
			Resource: r,
		}

		vs, err := ctx.Interpolater.Values(scope, cfg.Variables)
		if err != nil {
			return nil, err
		}

		// Do the interpolation
		if err := cfg.Interpolate(vs); err != nil {
			return nil, err
		}
	}

	result := NewResourceConfig(cfg)
	result.interpolateForce()
	return result, nil
}

func (ctx *BuiltinEvalContext) Path() []string {
	return ctx.PathValue
}

func (ctx *BuiltinEvalContext) SetVariables(n string, vs map[string]interface{}) {
	ctx.InterpolaterVarLock.Lock()
	defer ctx.InterpolaterVarLock.Unlock()

	path := make([]string, len(ctx.Path())+1)
	copy(path, ctx.Path())
	path[len(path)-1] = n
	key := PathCacheKey(path)

	vars := ctx.InterpolaterVars[key]
	if vars == nil {
		vars = make(map[string]interface{})
		ctx.InterpolaterVars[key] = vars
	}

	for k, v := range vs {
		vars[k] = v
	}
}

func (ctx *BuiltinEvalContext) Diff() (*Diff, *sync.RWMutex) {
	return ctx.DiffValue, ctx.DiffLock
}

func (ctx *BuiltinEvalContext) State() (*State, *sync.RWMutex) {
	return ctx.StateValue, ctx.StateLock
}

func (ctx *BuiltinEvalContext) init() {
}
<|endoftext|>


<commit_before>package github

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"sync"

	"github.com/google/go-github/v28/github"
	"github.com/reviewdog/reviewdog"
	"github.com/reviewdog/reviewdog/service/github/githubutils"
	"github.com/reviewdog/reviewdog/service/serviceutil"
)

var _ reviewdog.CommentService = &GitHubPullRequest{}
var _ reviewdog.DiffService = &GitHubPullRequest{}

const maxCommentsPerRequest = 30

// GitHubPullRequest is a comment and diff service for GitHub PullRequest.
//
// API:
//	https://developer.github.com/v3/pulls/comments/#create-a-comment
//	POST /repos/:owner/:repo/pulls/:number/comments
type GitHubPullRequest struct {
	cli   *github.Client
	owner string
	repo  string
	pr    int
	sha   string

	muComments   sync.Mutex
	postComments []*reviewdog.Comment

	postedcs serviceutil.PostedComments

	// wd is working directory relative to root of repository.
	wd string
}

// NewGitHubPullRequest returns a new GitHubPullRequest service.
// GitHubPullRequest service needs git command in $PATH.
func NewGitHubPullRequest(cli *github.Client, owner, repo string, pr int, sha string) (*GitHubPullRequest, error) {
	workDir, err := serviceutil.GitRelWorkdir()
	if err != nil {
		return nil, fmt.Errorf("GitHubPullRequest needs 'git' command: %v", err)
	}
	return &GitHubPullRequest{
		cli:   cli,
		owner: owner,
		repo:  repo,
		pr:    pr,
		sha:   sha,
		wd:    workDir,
	}, nil
}

// Post accepts a comment and holds it. Flush method actually posts comments to
// GitHub in parallel.
func (g *GitHubPullRequest) Post(_ context.Context, c *reviewdog.Comment) error {
	c.Path = filepath.ToSlash(filepath.Join(g.wd, c.Path))
	g.muComments.Lock()
	defer g.muComments.Unlock()
	g.postComments = append(g.postComments, c)
	return nil
}

// Flush posts comments which has not been posted yet.
func (g *GitHubPullRequest) Flush(ctx context.Context) error {
	g.muComments.Lock()
	defer g.muComments.Unlock()

	if err := g.setPostedComment(ctx); err != nil {
		return err
	}
	return g.postAsReviewComment(ctx)
}

func (g *GitHubPullRequest) postAsReviewComment(ctx context.Context) error {
	comments := make([]*github.DraftReviewComment, 0, len(g.postComments))
	remaining := make([]*reviewdog.Comment, 0)
	for _, c := range g.postComments {
		if g.postedcs.IsPosted(c, c.LnumDiff) {
			continue
		}
		// Only posts maxCommentsPerRequest comments per 1 request to avoid spammy
		// review comments. An example GitHub error if we don't limit the # of
		// review comments.
		//
		// > 403 You have triggered an abuse detection mechanism and have been
		// > temporarily blocked from content creation. Please retry your request
		// > again later.
		// https://developer.github.com/v3/#abuse-rate-limits
		if len(comments) >= maxCommentsPerRequest {
			remaining = append(remaining, c)
			continue
		}
		cbody := serviceutil.CommentBody(c)
		comments = append(comments, &github.DraftReviewComment{
			Path:     &c.Path,
			Position: &c.LnumDiff,
			Body:     &cbody,
		})
	}

	if len(comments) == 0 {
		return nil
	}

	// TODO(haya14busa): it might be useful to report overview results by "body"
	// field.
	review := &github.PullRequestReviewRequest{
		CommitID: &g.sha,
		Event:    github.String("COMMENT"),
		Comments: comments,
		Body:     github.String(g.remainingCommentsSummary(remaining)),
	}
	_, _, err := g.cli.PullRequests.CreateReview(ctx, g.owner, g.repo, g.pr, review)
	return err
}

func (g *GitHubPullRequest) remainingCommentsSummary(remaining []*reviewdog.Comment) string {
	perTool := make(map[string][]*reviewdog.Comment)
	for _, c := range remaining {
		perTool[c.ToolName] = append(perTool[c.ToolName], c)
	}
	var sb strings.Builder
	for tool, comments := range perTool {
		sb.WriteString("Remaining comments which cannot be posted as a review comment to avoid GitHub abuse Rate Limit\n")
		sb.WriteString("\n")
		sb.WriteString("\n")
		sb.WriteString(fmt.Sprintf("%s\n", tool))
		sb.WriteString("\n")
		for _, c := range comments {
			sb.WriteString(githubutils.LinkedMarkdownCheckResult(g.owner, g.repo, g.sha, c.CheckResult))
			sb.WriteString("\n")
		}
		sb.WriteString("\n")
	}
	return sb.String()
}

func (g *GitHubPullRequest) setPostedComment(ctx context.Context) error {
	g.postedcs = make(serviceutil.PostedComments)
	cs, err := g.comment(ctx)
	if err != nil {
		return err
	}
	for _, c := range cs {
		if c.Position == nil || c.Path == nil || c.Body == nil {
			// skip resolved comments. Or comments which do not have "path" nor
			// "body".
			continue
		}
		g.postedcs.AddPostedComment(c.GetPath(), c.GetPosition(), c.GetBody())
	}
	return nil
}

// Diff returns a diff of PullRequest.
func (g *GitHubPullRequest) Diff(ctx context.Context) ([]byte, error) {
	opt := github.RawOptions{Type: github.Diff}
	d, _, err := g.cli.PullRequests.GetRaw(ctx, g.owner, g.repo, g.pr, opt)
	if err != nil {
		return nil, err
	}
	return []byte(d), nil
}

// Strip returns 1 as a strip of git diff.
func (g *GitHubPullRequest) Strip() int {
	return 1
}

func (g *GitHubPullRequest) comment(ctx context.Context) ([]*github.PullRequestComment, error) {
	// https://developer.github.com/v3/guides/traversing-with-pagination/
	opts := &github.PullRequestListCommentsOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}
	comments, err := listAllPullRequestsComments(ctx, g.cli, g.owner, g.repo, g.pr, opts)
	if err != nil {
		return nil, err
	}
	return comments, nil
}

func listAllPullRequestsComments(ctx context.Context, cli *github.Client,
	owner, repo string, pr int, opts *github.PullRequestListCommentsOptions) ([]*github.PullRequestComment, error) {
	comments, resp, err := cli.PullRequests.ListComments(ctx, owner, repo, pr, opts)
	if err != nil {
		return nil, err
	}
	if resp.NextPage == 0 {
		return comments, nil
	}
	newOpts := &github.PullRequestListCommentsOptions{
		ListOptions: github.ListOptions{
			Page:    resp.NextPage,
			PerPage: opts.PerPage,
		},
	}
	restComments, err := listAllPullRequestsComments(ctx, cli, owner, repo, pr, newOpts)
	if err != nil {
		return nil, err
	}
	return append(comments, restComments...), nil
}
<commit_msg>github-pr-review: remove `abuse` from a summary comment<commit_after>package github

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"sync"

	"github.com/google/go-github/v28/github"
	"github.com/reviewdog/reviewdog"
	"github.com/reviewdog/reviewdog/service/github/githubutils"
	"github.com/reviewdog/reviewdog/service/serviceutil"
)

var _ reviewdog.CommentService = &GitHubPullRequest{}
var _ reviewdog.DiffService = &GitHubPullRequest{}

const maxCommentsPerRequest = 30

// GitHubPullRequest is a comment and diff service for GitHub PullRequest.
//
// API:
//	https://developer.github.com/v3/pulls/comments/#create-a-comment
//	POST /repos/:owner/:repo/pulls/:number/comments
type GitHubPullRequest struct {
	cli   *github.Client
	owner string
	repo  string
	pr    int
	sha   string

	muComments   sync.Mutex
	postComments []*reviewdog.Comment

	postedcs serviceutil.PostedComments

	// wd is working directory relative to root of repository.
	wd string
}

// NewGitHubPullRequest returns a new GitHubPullRequest service.
// GitHubPullRequest service needs git command in $PATH.
func NewGitHubPullRequest(cli *github.Client, owner, repo string, pr int, sha string) (*GitHubPullRequest, error) {
	workDir, err := serviceutil.GitRelWorkdir()
	if err != nil {
		return nil, fmt.Errorf("GitHubPullRequest needs 'git' command: %v", err)
	}
	return &GitHubPullRequest{
		cli:   cli,
		owner: owner,
		repo:  repo,
		pr:    pr,
		sha:   sha,
		wd:    workDir,
	}, nil
}

// Post accepts a comment and holds it. Flush method actually posts comments to
// GitHub in parallel.
func (g *GitHubPullRequest) Post(_ context.Context, c *reviewdog.Comment) error {
	c.Path = filepath.ToSlash(filepath.Join(g.wd, c.Path))
	g.muComments.Lock()
	defer g.muComments.Unlock()
	g.postComments = append(g.postComments, c)
	return nil
}

// Flush posts comments which has not been posted yet.
func (g *GitHubPullRequest) Flush(ctx context.Context) error {
	g.muComments.Lock()
	defer g.muComments.Unlock()

	if err := g.setPostedComment(ctx); err != nil {
		return err
	}
	return g.postAsReviewComment(ctx)
}

func (g *GitHubPullRequest) postAsReviewComment(ctx context.Context) error {
	comments := make([]*github.DraftReviewComment, 0, len(g.postComments))
	remaining := make([]*reviewdog.Comment, 0)
	for _, c := range g.postComments {
		if g.postedcs.IsPosted(c, c.LnumDiff) {
			continue
		}
		// Only posts maxCommentsPerRequest comments per 1 request to avoid spammy
		// review comments. An example GitHub error if we don't limit the # of
		// review comments.
		//
		// > 403 You have triggered an abuse detection mechanism and have been
		// > temporarily blocked from content creation. Please retry your request
		// > again later.
		// https://developer.github.com/v3/#abuse-rate-limits
		if len(comments) >= maxCommentsPerRequest {
			remaining = append(remaining, c)
			continue
		}
		cbody := serviceutil.CommentBody(c)
		comments = append(comments, &github.DraftReviewComment{
			Path:     &c.Path,
			Position: &c.LnumDiff,
			Body:     &cbody,
		})
	}

	if len(comments) == 0 {
		return nil
	}

	// TODO(haya14busa): it might be useful to report overview results by "body"
	// field.
	review := &github.PullRequestReviewRequest{
		CommitID: &g.sha,
		Event:    github.String("COMMENT"),
		Comments: comments,
		Body:     github.String(g.remainingCommentsSummary(remaining)),
	}
	_, _, err := g.cli.PullRequests.CreateReview(ctx, g.owner, g.repo, g.pr, review)
	return err
}

func (g *GitHubPullRequest) remainingCommentsSummary(remaining []*reviewdog.Comment) string {
	perTool := make(map[string][]*reviewdog.Comment)
	for _, c := range remaining {
		perTool[c.ToolName] = append(perTool[c.ToolName], c)
	}
	var sb strings.Builder
	for tool, comments := range perTool {
		sb.WriteString("Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit\n")
		sb.WriteString("\n")
		sb.WriteString("\n")
		sb.WriteString(fmt.Sprintf("%s\n", tool))
		sb.WriteString("\n")
		for _, c := range comments {
			sb.WriteString(githubutils.LinkedMarkdownCheckResult(g.owner, g.repo, g.sha, c.CheckResult))
			sb.WriteString("\n")
		}
		sb.WriteString("\n")
	}
	return sb.String()
}

func (g *GitHubPullRequest) setPostedComment(ctx context.Context) error {
	g.postedcs = make(serviceutil.PostedComments)
	cs, err := g.comment(ctx)
	if err != nil {
		return err
	}
	for _, c := range cs {
		if c.Position == nil || c.Path == nil || c.Body == nil {
			// skip resolved comments. Or comments which do not have "path" nor
			// "body".
			continue
		}
		g.postedcs.AddPostedComment(c.GetPath(), c.GetPosition(), c.GetBody())
	}
	return nil
}

// Diff returns a diff of PullRequest.
func (g *GitHubPullRequest) Diff(ctx context.Context) ([]byte, error) {
	opt := github.RawOptions{Type: github.Diff}
	d, _, err := g.cli.PullRequests.GetRaw(ctx, g.owner, g.repo, g.pr, opt)
	if err != nil {
		return nil, err
	}
	return []byte(d), nil
}

// Strip returns 1 as a strip of git diff.
func (g *GitHubPullRequest) Strip() int {
	return 1
}

func (g *GitHubPullRequest) comment(ctx context.Context) ([]*github.PullRequestComment, error) {
	// https://developer.github.com/v3/guides/traversing-with-pagination/
	opts := &github.PullRequestListCommentsOptions{
		ListOptions: github.ListOptions{
			PerPage: 100,
		},
	}
	comments, err := listAllPullRequestsComments(ctx, g.cli, g.owner, g.repo, g.pr, opts)
	if err != nil {
		return nil, err
	}
	return comments, nil
}

func listAllPullRequestsComments(ctx context.Context, cli *github.Client,
	owner, repo string, pr int, opts *github.PullRequestListCommentsOptions) ([]*github.PullRequestComment, error) {
	comments, resp, err := cli.PullRequests.ListComments(ctx, owner, repo, pr, opts)
	if err != nil {
		return nil, err
	}
	if resp.NextPage == 0 {
		return comments, nil
	}
	newOpts := &github.PullRequestListCommentsOptions{
		ListOptions: github.ListOptions{
			Page:    resp.NextPage,
			PerPage: opts.PerPage,
		},
	}
	restComments, err := listAllPullRequestsComments(ctx, cli, owner, repo, pr, newOpts)
	if err != nil {
		return nil, err
	}
	return append(comments, restComments...), nil
}
<|endoftext|>


<commit_before>package gitmediaclient

import (
	"bytes"
	"fmt"
	"net/url"
	"os/exec"
	"strings"
)

func credentials(u *url.URL) (Creds, error) {
	creds := Creds{"protocol": u.Scheme, "host": u.Host}
	cmd, err := execCreds(creds, "fill")
	if err != nil {
		return nil, err
	}
	return cmd.Credentials(), nil
}

func execCreds(input Creds, subCommand string) (*CredentialCmd, error) {
	cmd := NewCommand(input, subCommand)
	err := cmd.Start()
	if err == nil {
		err = cmd.Wait()
	}

	if err != nil {
		return cmd, fmt.Errorf("'git credential %s' error: %s\n%s", cmd.SubCommand, err.Error(), cmd.StderrString())
	}

	return cmd, nil
}

type CredentialCmd struct {
	output     *bytes.Buffer
	err        *bytes.Buffer
	SubCommand string
	*exec.Cmd
}

func NewCommand(input Creds, subCommand string) *CredentialCmd {
	buf1 := new(bytes.Buffer)
	buf2 := new(bytes.Buffer)
	cmd := exec.Command("git", "credential", subCommand)
	cmd.Stdin = input.Buffer()
	cmd.Stdout = buf1
	cmd.Stderr = buf2
	return &CredentialCmd{buf1, buf2, subCommand, cmd}
}

func (c *CredentialCmd) StderrString() string {
	return c.err.String()
}

func (c *CredentialCmd) StdoutString() string {
	return c.output.String()
}

func (c *CredentialCmd) Credentials() Creds {
	creds := make(Creds)

	for _, line := range strings.Split(c.StdoutString(), "\n") {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		creds[pieces[0]] = pieces[1]
	}

	return creds
}

type Creds map[string]string

func (c Creds) Buffer() *bytes.Buffer {
	buf := new(bytes.Buffer)

	for k, v := range c {
		buf.Write([]byte(k))
		buf.Write([]byte("="))
		buf.Write([]byte(v))
		buf.Write([]byte("\n"))
	}

	return buf
}
<commit_msg>ンンー ンンンン ンーンン<commit_after>package gitmediaclient

import (
	"bytes"
	"fmt"
	"net/url"
	"os/exec"
	"strings"
)

func credentials(u *url.URL) (Creds, error) {
	creds := Creds{"protocol": u.Scheme, "host": u.Host}
	cmd, err := execCreds(creds, "fill")
	if err != nil {
		return nil, err
	}
	return cmd.Credentials(), nil
}

func execCreds(input Creds, subCommand string) (*CredentialCmd, error) {
	cmd := NewCommand(input, subCommand)
	err := cmd.Start()
	if err == nil {
		err = cmd.Wait()
	}

	if err != nil {
		return cmd, fmt.Errorf("'git credential %s' error: %s\n", cmd.SubCommand, err.Error())
	}

	return cmd, nil
}

type CredentialCmd struct {
	output     *bytes.Buffer
	SubCommand string
	*exec.Cmd
}

func NewCommand(input Creds, subCommand string) *CredentialCmd {
	buf1 := new(bytes.Buffer)
	cmd := exec.Command("git", "credential", subCommand)

	cmd.Stdin = input.Buffer()
	cmd.Stdout = buf1
	/*
		There is a reason we don't hook up stderr here:
		Git's credential cache daemon helper does not close its stderr, so if this
		process is the process that fires up the daemon, it will wait forever
		(until the daemon exits, really) trying to read from stderr.

		See https://github.com/github/git-media/issues/117 for more details.
	*/

	return &CredentialCmd{buf1, subCommand, cmd}
}

func (c *CredentialCmd) StdoutString() string {
	return c.output.String()
}

func (c *CredentialCmd) Credentials() Creds {
	creds := make(Creds)

	for _, line := range strings.Split(c.StdoutString(), "\n") {
		pieces := strings.SplitN(line, "=", 2)
		if len(pieces) < 2 {
			continue
		}
		creds[pieces[0]] = pieces[1]
	}

	return creds
}

type Creds map[string]string

func (c Creds) Buffer() *bytes.Buffer {
	buf := new(bytes.Buffer)

	for k, v := range c {
		buf.Write([]byte(k))
		buf.Write([]byte("="))
		buf.Write([]byte(v))
		buf.Write([]byte("\n"))
	}

	return buf
}
<|endoftext|>


<commit_before>package mockery

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type Walker struct {
	BaseDir   string
	Recursive bool
	Filter    *regexp.Regexp
	LimitOne  bool
}

type WalkerVisitor interface {
	VisitWalk(*Interface) error
}

func (this *Walker) Walk(visitor WalkerVisitor) (generated bool) {
	return this.doWalk(this.BaseDir, visitor)
}

func (this *Walker) doWalk(dir string, visitor WalkerVisitor) (generated bool) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}

	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") {
			continue
		}

		path := filepath.Join(dir, file.Name())

		if file.IsDir() {
			if this.Recursive {
				generated = this.doWalk(path, visitor) || generated
				if generated && this.LimitOne {
					return
				}
			}
			continue
		}

		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			continue
		}

		p := NewParser()

		err = p.Parse(path)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error parsing file: ", err)
			continue
		}
		for _, iface := range p.Interfaces() {
			if !this.Filter.MatchString(iface.Name) {
				continue
			}
			err := visitor.VisitWalk(iface)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error walking %s: %s\n", iface.Name, err)
				os.Exit(1)
			}
			generated = true
			if this.LimitOne {
				return
			}
		}
	}

	return
}

type GeneratorVisitor struct {
	InPackage bool
	Note      string
	Osp       OutputStreamProvider
	// The name of the output package, if InPackage is false (defaults to "mocks")
	PackageName string
}

func (this *GeneratorVisitor) VisitWalk(iface *Interface) error {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Unable to generated mock for '%s': %s\n", iface.Name, r)
			return
		}
	}()

	var out io.Writer
	var pkg string

	if this.InPackage {
		pkg = iface.File.Name.String()
	} else {
		pkg = this.PackageName
	}

	out, err, closer := this.Osp.GetWriter(iface, pkg)
	if err != nil {
		fmt.Printf("Unable to get writer for %s: %s", iface.Name, err)
		os.Exit(1)
	}
	defer closer()

	gen := NewGenerator(iface, pkg, this.InPackage)
	gen.GeneratePrologueNote(this.Note)
	gen.GeneratePrologue(pkg)

	err = gen.Generate()
	if err != nil {
		return err
	}

	err = gen.Write(out)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>Skipping ignored files<commit_after>package mockery

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type Walker struct {
	BaseDir   string
	Recursive bool
	Filter    *regexp.Regexp
	LimitOne  bool
}

type WalkerVisitor interface {
	VisitWalk(*Interface) error
}

func (this *Walker) Walk(visitor WalkerVisitor) (generated bool) {
	return this.doWalk(this.BaseDir, visitor)
}

func (this *Walker) doWalk(dir string, visitor WalkerVisitor) (generated bool) {
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return
	}

	for _, file := range files {
		if strings.HasPrefix(file.Name(), ".") || strings.HasPrefix(file.Name(), "_") {
			continue
		}

		path := filepath.Join(dir, file.Name())

		if file.IsDir() {
			if this.Recursive {
				generated = this.doWalk(path, visitor) || generated
				if generated && this.LimitOne {
					return
				}
			}
			continue
		}

		if !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
			continue
		}

		p := NewParser()

		err = p.Parse(path)
		if err != nil {
			fmt.Fprintln(os.Stderr, "Error parsing file: ", err)
			continue
		}
		for _, iface := range p.Interfaces() {
			if !this.Filter.MatchString(iface.Name) {
				continue
			}
			err := visitor.VisitWalk(iface)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Error walking %s: %s\n", iface.Name, err)
				os.Exit(1)
			}
			generated = true
			if this.LimitOne {
				return
			}
		}
	}

	return
}

type GeneratorVisitor struct {
	InPackage bool
	Note      string
	Osp       OutputStreamProvider
	// The name of the output package, if InPackage is false (defaults to "mocks")
	PackageName string
}

func (this *GeneratorVisitor) VisitWalk(iface *Interface) error {
	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Unable to generated mock for '%s': %s\n", iface.Name, r)
			return
		}
	}()

	var out io.Writer
	var pkg string

	if this.InPackage {
		pkg = iface.File.Name.String()
	} else {
		pkg = this.PackageName
	}

	out, err, closer := this.Osp.GetWriter(iface, pkg)
	if err != nil {
		fmt.Printf("Unable to get writer for %s: %s", iface.Name, err)
		os.Exit(1)
	}
	defer closer()

	gen := NewGenerator(iface, pkg, this.InPackage)
	gen.GeneratePrologueNote(this.Note)
	gen.GeneratePrologue(pkg)

	err = gen.Generate()
	if err != nil {
		return err
	}

	err = gen.Write(out)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>