<commit_before>package http

import (
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	fm "github.com/hacdias/filemanager"
	"github.com/hacdias/fileutils"
	"github.com/mholt/archiver"
)

// downloadHandler creates an archive in one of the supported formats (zip, tar,
// tar.gz or tar.bz2) and sends it to be downloaded.
func downloadHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// If the file isn't a directory, serve it using http.ServeFile. We display it
	// inline if it is requested.
	if !c.File.IsDir {
		return downloadFileHandler(c, w, r)
	}

	query := r.URL.Query().Get("format")
	files := []string{}
	names := strings.Split(r.URL.Query().Get("files"), ",")

	// If there are files in the query, sanitize their names.
	// Otherwise, just append the current path.
	if len(names) != 0 {
		for _, name := range names {
			// Unescape the name.
			name, err := url.QueryUnescape(name)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Clean the slashes.
			name = fileutils.SlashClean(name)
			files = append(files, filepath.Join(c.File.Path, name))
		}
	} else {
		files = append(files, c.File.Path)
	}

	var (
		extension string
		ar        archiver.Archiver
	)

	switch query {
	// If the format is true, just set it to "zip".
	case "zip", "true", "":
		extension, ar = ".zip", archiver.Zip
	case "tar":
		extension, ar = ".tar", archiver.Tar
	case "targz":
		extension, ar = ".tar.gz", archiver.TarGz
	case "tarbz2":
		extension, ar = ".tar.bz2", archiver.TarBz2
	case "tarxz":
		extension, ar = ".tar.xz", archiver.TarXZ
	default:
		return http.StatusNotImplemented, nil
	}

	// Defines the file name.
	name := c.File.Name
	if name == "." || name == "" {
		name = "archive"
	}
	name += extension

	w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.QueryEscape(name))
	err := ar.Write(w, files)

	return 0, err
}

func downloadFileHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	file, err := os.Open(c.File.Path)
	defer file.Close()

	if err != nil {
		return http.StatusInternalServerError, err
	}

	stat, err := file.Stat()
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if r.URL.Query().Get("inline") == "true" {
		w.Header().Set("Content-Disposition", "inline")
	} else {
		// As per RFC6266 section 4.3
		w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.QueryEscape(c.File.Name))
	}

	http.ServeContent(w, r, stat.Name(), stat.ModTime(), file)

	return 0, nil
}
<commit_msg>http: download: use PathEscape instead of QueryEscape (#340)<commit_after>package http

import (
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	fm "github.com/hacdias/filemanager"
	"github.com/hacdias/fileutils"
	"github.com/mholt/archiver"
)

// downloadHandler creates an archive in one of the supported formats (zip, tar,
// tar.gz or tar.bz2) and sends it to be downloaded.
func downloadHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	// If the file isn't a directory, serve it using http.ServeFile. We display it
	// inline if it is requested.
	if !c.File.IsDir {
		return downloadFileHandler(c, w, r)
	}

	query := r.URL.Query().Get("format")
	files := []string{}
	names := strings.Split(r.URL.Query().Get("files"), ",")

	// If there are files in the query, sanitize their names.
	// Otherwise, just append the current path.
	if len(names) != 0 {
		for _, name := range names {
			// Unescape the name.
			name, err := url.QueryUnescape(name)
			if err != nil {
				return http.StatusInternalServerError, err
			}

			// Clean the slashes.
			name = fileutils.SlashClean(name)
			files = append(files, filepath.Join(c.File.Path, name))
		}
	} else {
		files = append(files, c.File.Path)
	}

	var (
		extension string
		ar        archiver.Archiver
	)

	switch query {
	// If the format is true, just set it to "zip".
	case "zip", "true", "":
		extension, ar = ".zip", archiver.Zip
	case "tar":
		extension, ar = ".tar", archiver.Tar
	case "targz":
		extension, ar = ".tar.gz", archiver.TarGz
	case "tarbz2":
		extension, ar = ".tar.bz2", archiver.TarBz2
	case "tarxz":
		extension, ar = ".tar.xz", archiver.TarXZ
	default:
		return http.StatusNotImplemented, nil
	}

	// Defines the file name.
	name := c.File.Name
	if name == "." || name == "" {
		name = "archive"
	}
	name += extension

	w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
	err := ar.Write(w, files)

	return 0, err
}

func downloadFileHandler(c *fm.Context, w http.ResponseWriter, r *http.Request) (int, error) {
	file, err := os.Open(c.File.Path)
	defer file.Close()

	if err != nil {
		return http.StatusInternalServerError, err
	}

	stat, err := file.Stat()
	if err != nil {
		return http.StatusInternalServerError, err
	}

	if r.URL.Query().Get("inline") == "true" {
		w.Header().Set("Content-Disposition", "inline")
	} else {
		// As per RFC6266 section 4.3
		w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(c.File.Name))
	}

	http.ServeContent(w, r, stat.Name(), stat.ModTime(), file)

	return 0, nil
}
<|endoftext|>


<commit_before>package git_pipeline_test

import (
	"time"

	"github.com/concourse/testflight/guidserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A job with a git resource", func() {
	It("triggers when it updates", func() {
		guid1 := gitServer.Commit()

		By("building the initial commit")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid1))

		guid2 := gitServer.Commit()

		By("building another commit")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid2))
	})

	It("performs output conditions correctly", func() {
		committedGuid := gitServer.Commit()

		masterSHA := gitServer.RevParse("master")
		Ω(masterSHA).ShouldNot(BeEmpty())

		By("executing the build")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(committedGuid))

		By("performing on: [success] outputs on success")
		Eventually(func() string {
			return successGitServer.RevParse("success")
		}, 10*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("performing on: [failure] outputs on failure")
		Eventually(func() string {
			return failureGitServer.RevParse("failure")
		}, 10*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("not performing on: [success] outputs on failure")
		Consistently(func() string {
			return noUpdateGitServer.RevParse("no-update")
		}, 10*time.Second, 1*time.Second).Should(BeEmpty())
	})
})
<commit_msg>bump git pipeline timeouts<commit_after>package git_pipeline_test

import (
	"time"

	"github.com/concourse/testflight/guidserver"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("A job with a git resource", func() {
	It("triggers when it updates", func() {
		guid1 := gitServer.Commit()

		By("building the initial commit")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid1))

		guid2 := gitServer.Commit()

		By("building another commit")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(guid2))
	})

	It("performs output conditions correctly", func() {
		committedGuid := gitServer.Commit()

		masterSHA := gitServer.RevParse("master")
		Ω(masterSHA).ShouldNot(BeEmpty())

		By("executing the build")
		Eventually(guidserver.ReportingGuids, 5*time.Minute, 10*time.Second).Should(ContainElement(committedGuid))

		By("performing on: [success] outputs on success")
		Eventually(func() string {
			return successGitServer.RevParse("success")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("performing on: [failure] outputs on failure")
		Eventually(func() string {
			return failureGitServer.RevParse("failure")
		}, 30*time.Second, 1*time.Second).Should(Equal(masterSHA))

		By("not performing on: [success] outputs on failure")
		Consistently(func() string {
			return noUpdateGitServer.RevParse("no-update")
		}, 30*time.Second, 1*time.Second).Should(BeEmpty())
	})
})
<|endoftext|>


<commit_before>package files

import (
	"bytes"
	"crypto/md5" // #nosec
	"encoding/json"
	"io"
	"net/http"
	"path"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

type fileAttributes struct {
	Name       string    `json:"name"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
	Size       int64     `json:"size,string"`
	Tags       []string  `json:"tags"`
	MD5Sum     []byte    `json:"md5sum"`
	Executable bool      `json:"executable"`
	Class      string    `json:"class"`
	Mime       string    `json:"mime"`
}

// FileDoc is a struct containing all the informations about a file.
// It implements the couchdb.Doc and jsonapi.JSONApier interfaces.
type FileDoc struct {
	// Qualified file identifier
	QID string `json:"_id"`
	// File revision
	FRev string `json:"_rev,omitempty"`
	// File attributes
	Attrs *fileAttributes `json:"attributes"`
	// Parent folder identifier
	FolderID string `json:"folderID"`
	// File path on VFS
	Path string `json:"path"`
}

// ID returns the file qualified identifier (part of couchdb.Doc
// interface)
func (f *FileDoc) ID() string {
	return f.QID
}

// Rev returns the file revision (part of couchdb.Doc interface)
func (f *FileDoc) Rev() string {
	return f.FRev
}

// DocType returns the file document type (part of couchdb.Doc
// interface)
func (f *FileDoc) DocType() string {
	return string(FileDocType)
}

// SetID is used to change the file qualified identifier (part of
// couchdb.Doc interface)
func (f *FileDoc) SetID(id string) {
	f.QID = id
}

// SetRev is used to change the file revision (part of couchdb.Doc
// interface)
func (f *FileDoc) SetRev(rev string) {
	f.FRev = rev
}

// ToJSONApi implements temporary interface JSONApier to serialize
// the file document
func (f *FileDoc) ToJSONApi() ([]byte, error) {
	qid := f.QID
	data := map[string]interface{}{
		"id":         qid[strings.Index(qid, "/")+1:],
		"type":       f.DocType(),
		"rev":        f.Rev(),
		"attributes": f.Attrs,
	}
	m := map[string]interface{}{
		"data": data,
	}
	return json.Marshal(m)
}

// GetFileDoc is used to fetch file document information form our
// database.
func GetFileDoc(fileID, dbPrefix string) (doc *FileDoc, err error) {
	doc = &FileDoc{}
	err = couchdb.GetDoc(dbPrefix, string(FileDocType), fileID, doc)
	return
}

// ServeFileContent replies to a http request using the content of a
// file given its FileDoc.
//
// It uses internally http.ServeContent and benefits from it by
// offering support to Range, If-Modified-Since and If-None-Match
// requests. It uses the revision of the file as the Etag value for
// non-ranged requests
//
// The content disposition is inlined.
func ServeFileContent(fileDoc *FileDoc, req *http.Request, w http.ResponseWriter, fs afero.Fs) (err error) {
	attrs := fileDoc.Attrs
	header := w.Header()
	header.Set("Content-Type", attrs.Mime)
	header.Set("Content-Disposition", "inline; filename="+attrs.Name+"")

	if header.Get("Range") == "" {
		header.Set("Etag", fileDoc.Rev())
	}

	serveContent(req, w, fs, fileDoc.Path, attrs.Name, attrs.UpdatedAt)
	return
}

// ServeFileContentByPath replies to a http request using the content
// of a file identified by its full path on the VFS. Unlike
// ServeFileContent, this method does not require the full file
// document but only its path.
//
// It also uses internally http.ServeContent but does not provide an
// Etag.
//
// The content disposition is attached
func ServeFileContentByPath(pth string, req *http.Request, w http.ResponseWriter, fs afero.Fs) (err error) {
	fileInfo, err := fs.Stat(pth)
	if err != nil {
		return
	}

	name := path.Base(pth)
	w.Header().Set("Content-Disposition", "attachment; filename="+name+"")

	serveContent(req, w, fs, pth, name, fileInfo.ModTime())
	return
}

func serveContent(req *http.Request, w http.ResponseWriter, fs afero.Fs, pth, name string, modtime time.Time) (err error) {
	content, err := fs.Open(pth)
	if err != nil {
		return
	}

	defer content.Close()
	http.ServeContent(w, req, name, modtime, content)
	return
}

// CreateFileAndUpload is the method for uploading a file onto the filesystem.
func CreateFileAndUpload(m *DocMetadata, fs afero.Fs, contentType string, contentLength int64, dbPrefix string, body io.ReadCloser) (doc *FileDoc, err error) {
	if m.Type != FileDocType {
		err = errDocTypeInvalid
		return
	}

	pth, _, err := createNewFilePath(m, fs, dbPrefix)
	if err != nil {
		return
	}

	mime, class := extractMimeAndClass(contentType)
	createDate := time.Now()
	attrs := &fileAttributes{
		Name:       m.Name,
		CreatedAt:  createDate,
		UpdatedAt:  createDate,
		Size:       contentLength,
		Tags:       m.Tags,
		MD5Sum:     m.GivenMD5,
		Executable: m.Executable,
		Class:      class,
		Mime:       mime,
	}

	doc = &FileDoc{
		Attrs:    attrs,
		FolderID: m.FolderID,
		Path:     pth,
	}

	// Error handling to make sure the steps of uploading the file and
	// creating the corresponding are both rollbacked in case of an
	// error. This should preserve our VFS coherency a little.
	defer func() {
		if err != nil {
			fs.Remove(pth)
		}
	}()

	var written int64
	if written, err = copyOnFsAndCheckIntegrity(m, fs, pth, body); err != nil {
		return
	}

	if contentLength >= 0 && written != contentLength {
		err = errContentLengthMismatch
		return
	}

	if contentLength < 0 {
		attrs.Size = written
	}

	if err = couchdb.CreateDoc(dbPrefix, doc.DocType(), doc); err != nil {
		return
	}

	return
}

func copyOnFsAndCheckIntegrity(m *DocMetadata, fs afero.Fs, pth string, r io.ReadCloser) (written int64, err error) {
	f, err := fs.Create(pth)
	if err != nil {
		return
	}

	defer f.Close()
	defer r.Close()

	md5H := md5.New() // #nosec
	written, err = io.Copy(f, io.TeeReader(r, md5H))
	if err != nil {
		return
	}

	calcMD5 := md5H.Sum(nil)
	if !bytes.Equal(m.GivenMD5, calcMD5) {
		err = errInvalidHash
		return
	}

	return
}

func extractMimeAndClass(contentType string) (mime, class string) {
	if contentType == "" {
		contentType = DefaultContentType
	}

	charsetIndex := strings.Index(contentType, ";")
	if charsetIndex >= 0 {
		mime = contentType[:charsetIndex]
	} else {
		mime = contentType
	}

	// @TODO improve for specific mime types
	slashIndex := strings.Index(contentType, "/")
	if slashIndex >= 0 {
		class = contentType[:slashIndex]
	} else {
		class = contentType
	}

	return
}
<commit_msg>Remove unnecessary quotes<commit_after>package files

import (
	"bytes"
	"crypto/md5" // #nosec
	"encoding/json"
	"io"
	"net/http"
	"path"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/couchdb"
	"github.com/spf13/afero"
)

// DefaultContentType is used for files uploaded with no content-type
const DefaultContentType = "application/octet-stream"

type fileAttributes struct {
	Name       string    `json:"name"`
	CreatedAt  time.Time `json:"created_at"`
	UpdatedAt  time.Time `json:"updated_at"`
	Size       int64     `json:"size,string"`
	Tags       []string  `json:"tags"`
	MD5Sum     []byte    `json:"md5sum"`
	Executable bool      `json:"executable"`
	Class      string    `json:"class"`
	Mime       string    `json:"mime"`
}

// FileDoc is a struct containing all the informations about a file.
// It implements the couchdb.Doc and jsonapi.JSONApier interfaces.
type FileDoc struct {
	// Qualified file identifier
	QID string `json:"_id"`
	// File revision
	FRev string `json:"_rev,omitempty"`
	// File attributes
	Attrs *fileAttributes `json:"attributes"`
	// Parent folder identifier
	FolderID string `json:"folderID"`
	// File path on VFS
	Path string `json:"path"`
}

// ID returns the file qualified identifier (part of couchdb.Doc
// interface)
func (f *FileDoc) ID() string {
	return f.QID
}

// Rev returns the file revision (part of couchdb.Doc interface)
func (f *FileDoc) Rev() string {
	return f.FRev
}

// DocType returns the file document type (part of couchdb.Doc
// interface)
func (f *FileDoc) DocType() string {
	return string(FileDocType)
}

// SetID is used to change the file qualified identifier (part of
// couchdb.Doc interface)
func (f *FileDoc) SetID(id string) {
	f.QID = id
}

// SetRev is used to change the file revision (part of couchdb.Doc
// interface)
func (f *FileDoc) SetRev(rev string) {
	f.FRev = rev
}

// ToJSONApi implements temporary interface JSONApier to serialize
// the file document
func (f *FileDoc) ToJSONApi() ([]byte, error) {
	qid := f.QID
	data := map[string]interface{}{
		"id":         qid[strings.Index(qid, "/")+1:],
		"type":       f.DocType(),
		"rev":        f.Rev(),
		"attributes": f.Attrs,
	}
	m := map[string]interface{}{
		"data": data,
	}
	return json.Marshal(m)
}

// GetFileDoc is used to fetch file document information form our
// database.
func GetFileDoc(fileID, dbPrefix string) (doc *FileDoc, err error) {
	doc = &FileDoc{}
	err = couchdb.GetDoc(dbPrefix, string(FileDocType), fileID, doc)
	return
}

// ServeFileContent replies to a http request using the content of a
// file given its FileDoc.
//
// It uses internally http.ServeContent and benefits from it by
// offering support to Range, If-Modified-Since and If-None-Match
// requests. It uses the revision of the file as the Etag value for
// non-ranged requests
//
// The content disposition is inlined.
func ServeFileContent(fileDoc *FileDoc, req *http.Request, w http.ResponseWriter, fs afero.Fs) (err error) {
	attrs := fileDoc.Attrs
	header := w.Header()
	header.Set("Content-Type", attrs.Mime)
	header.Set("Content-Disposition", "inline; filename="+attrs.Name)

	if header.Get("Range") == "" {
		header.Set("Etag", fileDoc.Rev())
	}

	serveContent(req, w, fs, fileDoc.Path, attrs.Name, attrs.UpdatedAt)
	return
}

// ServeFileContentByPath replies to a http request using the content
// of a file identified by its full path on the VFS. Unlike
// ServeFileContent, this method does not require the full file
// document but only its path.
//
// It also uses internally http.ServeContent but does not provide an
// Etag.
//
// The content disposition is attached
func ServeFileContentByPath(pth string, req *http.Request, w http.ResponseWriter, fs afero.Fs) (err error) {
	fileInfo, err := fs.Stat(pth)
	if err != nil {
		return
	}

	name := path.Base(pth)
	w.Header().Set("Content-Disposition", "attachment; filename="+name)

	serveContent(req, w, fs, pth, name, fileInfo.ModTime())
	return
}

func serveContent(req *http.Request, w http.ResponseWriter, fs afero.Fs, pth, name string, modtime time.Time) (err error) {
	content, err := fs.Open(pth)
	if err != nil {
		return
	}

	defer content.Close()
	http.ServeContent(w, req, name, modtime, content)
	return
}

// CreateFileAndUpload is the method for uploading a file onto the filesystem.
func CreateFileAndUpload(m *DocMetadata, fs afero.Fs, contentType string, contentLength int64, dbPrefix string, body io.ReadCloser) (doc *FileDoc, err error) {
	if m.Type != FileDocType {
		err = errDocTypeInvalid
		return
	}

	pth, _, err := createNewFilePath(m, fs, dbPrefix)
	if err != nil {
		return
	}

	mime, class := extractMimeAndClass(contentType)
	createDate := time.Now()
	attrs := &fileAttributes{
		Name:       m.Name,
		CreatedAt:  createDate,
		UpdatedAt:  createDate,
		Size:       contentLength,
		Tags:       m.Tags,
		MD5Sum:     m.GivenMD5,
		Executable: m.Executable,
		Class:      class,
		Mime:       mime,
	}

	doc = &FileDoc{
		Attrs:    attrs,
		FolderID: m.FolderID,
		Path:     pth,
	}

	// Error handling to make sure the steps of uploading the file and
	// creating the corresponding are both rollbacked in case of an
	// error. This should preserve our VFS coherency a little.
	defer func() {
		if err != nil {
			fs.Remove(pth)
		}
	}()

	var written int64
	if written, err = copyOnFsAndCheckIntegrity(m, fs, pth, body); err != nil {
		return
	}

	if contentLength >= 0 && written != contentLength {
		err = errContentLengthMismatch
		return
	}

	if contentLength < 0 {
		attrs.Size = written
	}

	if err = couchdb.CreateDoc(dbPrefix, doc.DocType(), doc); err != nil {
		return
	}

	return
}

func copyOnFsAndCheckIntegrity(m *DocMetadata, fs afero.Fs, pth string, r io.ReadCloser) (written int64, err error) {
	f, err := fs.Create(pth)
	if err != nil {
		return
	}

	defer f.Close()
	defer r.Close()

	md5H := md5.New() // #nosec
	written, err = io.Copy(f, io.TeeReader(r, md5H))
	if err != nil {
		return
	}

	calcMD5 := md5H.Sum(nil)
	if !bytes.Equal(m.GivenMD5, calcMD5) {
		err = errInvalidHash
		return
	}

	return
}

func extractMimeAndClass(contentType string) (mime, class string) {
	if contentType == "" {
		contentType = DefaultContentType
	}

	charsetIndex := strings.Index(contentType, ";")
	if charsetIndex >= 0 {
		mime = contentType[:charsetIndex]
	} else {
		mime = contentType
	}

	// @TODO improve for specific mime types
	slashIndex := strings.Index(contentType, "/")
	if slashIndex >= 0 {
		class = contentType[:slashIndex]
	} else {
		class = contentType
	}

	return
}
<|endoftext|>


<commit_before>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hooks

import (
	"golang.org/x/tools/internal/lsp/source"
	"honnef.co/go/tools/simple"
	"honnef.co/go/tools/staticcheck"
	"honnef.co/go/tools/stylecheck"
)

func updateAnalyzers(options *source.Options) {
	if options.StaticCheck {
		for _, a := range simple.Analyzers {
			options.Analyzers[a.Name] = a
		}
		for _, a := range staticcheck.Analyzers {
			options.Analyzers[a.Name] = a
		}
		for _, a := range stylecheck.Analyzers {
			options.Analyzers[a.Name] = a
		}
	}
}
<commit_msg>gopls/internal/hooks: ignore a duplicate analysis from staticcheck<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hooks

import (
	"golang.org/x/tools/internal/lsp/source"
	"honnef.co/go/tools/simple"
	"honnef.co/go/tools/staticcheck"
	"honnef.co/go/tools/stylecheck"
)

func updateAnalyzers(options *source.Options) {
	if options.StaticCheck {
		for _, a := range simple.Analyzers {
			options.Analyzers[a.Name] = a
		}
		for _, a := range staticcheck.Analyzers {
			// This check conflicts with the vet printf check (golang/go#34494).
			if a.Name == "SA5009" {
				continue
			}
			options.Analyzers[a.Name] = a
		}
		for _, a := range stylecheck.Analyzers {
			options.Analyzers[a.Name] = a
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 service

import (
	"sync"

	"github.com/hyperledger/fabric/core/committer"
	"github.com/hyperledger/fabric/core/deliverservice"
	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
	"github.com/hyperledger/fabric/gossip/api"
	gossipCommon "github.com/hyperledger/fabric/gossip/common"
	"github.com/hyperledger/fabric/gossip/election"
	"github.com/hyperledger/fabric/gossip/gossip"
	"github.com/hyperledger/fabric/gossip/identity"
	"github.com/hyperledger/fabric/gossip/integration"
	"github.com/hyperledger/fabric/gossip/state"
	"github.com/hyperledger/fabric/gossip/util"
	"github.com/hyperledger/fabric/protos/common"
	proto "github.com/hyperledger/fabric/protos/gossip"
	"github.com/spf13/viper"
	"google.golang.org/grpc"
)

var (
	gossipServiceInstance *gossipServiceImpl
	once                  sync.Once
)

type gossipSvc gossip.Gossip

// GossipService encapsulates gossip and state capabilities into single interface
type GossipService interface {
	gossip.Gossip

	// NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
	NewConfigEventer() ConfigProcessor
	// InitializeChannel allocates the state provider and should be invoked once per channel per execution
	InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
	// GetBlock returns block for given chain
	GetBlock(chainID string, index uint64) *common.Block
	// AddPayload appends message payload to for given chain
	AddPayload(chainID string, payload *proto.Payload) error
}

// DeliveryServiceFactory factory to create and initialize delivery service instance
type DeliveryServiceFactory interface {
	// Returns an instance of delivery client
	Service(g GossipService, endpoints []string, msc api.MessageCryptoService) (deliverclient.DeliverService, error)
}

type deliveryFactoryImpl struct {
}

// Returns an instance of delivery client
func (*deliveryFactoryImpl) Service(g GossipService, endpoints []string, mcs api.MessageCryptoService) (deliverclient.DeliverService, error) {
	return deliverclient.NewDeliverService(&deliverclient.Config{
		CryptoSvc:   mcs,
		Gossip:      g,
		Endpoints:   endpoints,
		ConnFactory: deliverclient.DefaultConnectionFactory,
		ABCFactory:  deliverclient.DefaultABCFactory,
	})
}

type gossipServiceImpl struct {
	gossipSvc
	chains          map[string]state.GossipStateProvider
	leaderElection  map[string]election.LeaderElectionService
	deliveryService deliverclient.DeliverService
	deliveryFactory DeliveryServiceFactory
	lock            sync.RWMutex
	idMapper        identity.Mapper
	mcs             api.MessageCryptoService
	peerIdentity    []byte
	secAdv          api.SecurityAdvisor
}

// This is an implementation of api.JoinChannelMessage.
type joinChannelMessage struct {
	seqNum              uint64
	members2AnchorPeers map[string][]api.AnchorPeer
}

func (jcm *joinChannelMessage) SequenceNumber() uint64 {
	return jcm.seqNum
}

// Members returns the organizations of the channel
func (jcm *joinChannelMessage) Members() []api.OrgIdentityType {
	members := make([]api.OrgIdentityType, len(jcm.members2AnchorPeers))
	i := 0
	for org := range jcm.members2AnchorPeers {
		members[i] = api.OrgIdentityType(org)
		i++
	}
	return members
}

// AnchorPeersOf returns the anchor peers of the given organization
func (jcm *joinChannelMessage) AnchorPeersOf(org api.OrgIdentityType) []api.AnchorPeer {
	return jcm.members2AnchorPeers[string(org)]
}

var logger = util.GetLogger(util.LoggingServiceModule, "")

// InitGossipService initialize gossip service
func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,
	secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) {
	// TODO: Remove this.
	// TODO: This is a temporary work-around to make the gossip leader election module load its logger at startup
	// TODO: in order for the flogging package to register this logger in time so it can set the log levels as requested in the config
	util.GetLogger(util.LoggingElectionModule, "")
	InitGossipServiceCustomDeliveryFactory(peerIdentity, endpoint, s, &deliveryFactoryImpl{},
		mcs, secAdv, secureDialOpts, bootPeers...)
}

// InitGossipServiceCustomDeliveryFactory initialize gossip service with customize delivery factory
// implementation, might be useful for testing and mocking purposes
func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string, s *grpc.Server,
	factory DeliveryServiceFactory, mcs api.MessageCryptoService, secAdv api.SecurityAdvisor,
	secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) {
	once.Do(func() {
		if overrideEndpoint := viper.GetString("peer.gossip.endpoint"); overrideEndpoint != "" {
			endpoint = overrideEndpoint
		}

		logger.Info("Initialize gossip with endpoint", endpoint, "and bootstrap set", bootPeers)

		idMapper := identity.NewIdentityMapper(mcs, peerIdentity)
		gossip := integration.NewGossipComponent(peerIdentity, endpoint, s, secAdv,
			mcs, idMapper, secureDialOpts, bootPeers...)
		gossipServiceInstance = &gossipServiceImpl{
			mcs:             mcs,
			gossipSvc:       gossip,
			chains:          make(map[string]state.GossipStateProvider),
			leaderElection:  make(map[string]election.LeaderElectionService),
			deliveryFactory: factory,
			idMapper:        idMapper,
			peerIdentity:    peerIdentity,
			secAdv:          secAdv,
		}
	})
}

// GetGossipService returns an instance of gossip service
func GetGossipService() GossipService {
	return gossipServiceInstance
}

// NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
func (g *gossipServiceImpl) NewConfigEventer() ConfigProcessor {
	return newConfigEventer(g)
}

// InitializeChannel allocates the state provider and should be invoked once per channel per execution
func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string) {
	g.lock.Lock()
	defer g.lock.Unlock()
	// Initialize new state provider for given committer
	logger.Debug("Creating state provider for chainID", chainID)
	g.chains[chainID] = state.NewGossipStateProvider(chainID, g, committer, g.mcs)
	if g.deliveryService == nil {
		var err error
		g.deliveryService, err = g.deliveryFactory.Service(gossipServiceInstance, endpoints, g.mcs)
		if err != nil {
			logger.Warning("Cannot create delivery client, due to", err)
		}
	}

	// Delivery service might be nil only if it was not able to get connected
	// to the ordering service
	if g.deliveryService != nil {
		// Parameters:
		//              - peer.gossip.useLeaderElection
		//              - peer.gossip.orgLeader
		//
		// are mutual exclusive, setting both to true is not defined, hence
		// peer will panic and terminate
		leaderElection := viper.GetBool("peer.gossip.useLeaderElection")
		isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader")

		if leaderElection && isStaticOrgLeader {
			logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
		}

		if leaderElection {
			logger.Debug("Delivery uses dynamic leader election mechanism, channel", chainID)
			g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
		} else if isStaticOrgLeader {
			logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", chainID)
			g.deliveryService.StartDeliverForChannel(chainID, committer)
		} else {
			logger.Debug("This peer is not configured to connect to ordering service for blocks delivery, channel", chainID)
		}
	} else {
		logger.Warning("Delivery client is down won't be able to pull blocks for chain", chainID)
	}
}

// configUpdated constructs a joinChannelMessage and sends it to the gossipSvc
func (g *gossipServiceImpl) configUpdated(config Config) {
	myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity)))
	if !g.amIinChannel(myOrg, config) {
		logger.Error("Tried joining channel", config.ChainID(), "but our org(", myOrg, "), isn't "+
			"among the orgs of the channel:", orgListFromConfig(config), ", aborting.")
		return
	}
	jcm := &joinChannelMessage{seqNum: config.Sequence(), members2AnchorPeers: map[string][]api.AnchorPeer{}}
	for _, appOrg := range config.Organizations() {
		logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
		jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
		for _, ap := range appOrg.AnchorPeers() {
			anchorPeer := api.AnchorPeer{
				Host: ap.Host,
				Port: int(ap.Port),
			}
			jcm.members2AnchorPeers[appOrg.MSPID()] = append(jcm.members2AnchorPeers[appOrg.MSPID()], anchorPeer)
		}
	}

	// Initialize new state provider for given committer
	logger.Debug("Creating state provider for chainID", config.ChainID())
	g.JoinChan(jcm, gossipCommon.ChainID(config.ChainID()))
}

// GetBlock returns block for given chain
func (g *gossipServiceImpl) GetBlock(chainID string, index uint64) *common.Block {
	g.lock.RLock()
	defer g.lock.RUnlock()
	return g.chains[chainID].GetBlock(index)
}

// AddPayload appends message payload to for given chain
func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error {
	g.lock.RLock()
	defer g.lock.RUnlock()
	return g.chains[chainID].AddPayload(payload)
}

// Stop stops the gossip component
func (g *gossipServiceImpl) Stop() {
	g.lock.Lock()
	defer g.lock.Unlock()
	for _, ch := range g.chains {
		logger.Info("Stopping chain", ch)
		ch.Stop()
	}

	for chainID, electionService := range g.leaderElection {
		logger.Info("Stopping leader election for %s", chainID)
		electionService.Stop()
	}
	g.gossipSvc.Stop()
	if g.deliveryService != nil {
		g.deliveryService.Stop()
	}
}

func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService {
	PKIid := g.idMapper.GetPKIidOfCert(g.peerIdentity)
	adapter := election.NewAdapter(g, PKIid, gossipCommon.ChainID(chainID))
	return election.NewLeaderElectionService(adapter, string(PKIid), callback)
}

func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool {
	for _, orgName := range orgListFromConfig(config) {
		if orgName == myOrg {
			return true
		}
	}
	return false
}

func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool) {
	return func(isLeader bool) {
		if isLeader {
			if err := g.deliveryService.StartDeliverForChannel(chainID, committer); err != nil {
				logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
			}
		} else {
			if err := g.deliveryService.StopDeliverForChannel(chainID); err != nil {
				logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
			}

		}

	}
}

func orgListFromConfig(config Config) []string {
	var orgList []string
	for _, appOrg := range config.Organizations() {
		orgList = append(orgList, appOrg.MSPID())
	}
	return orgList
}
<commit_msg>[FAB-4512] Add leader log entry in gossip<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 service

import (
	"sync"

	"github.com/hyperledger/fabric/core/committer"
	"github.com/hyperledger/fabric/core/deliverservice"
	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
	"github.com/hyperledger/fabric/gossip/api"
	gossipCommon "github.com/hyperledger/fabric/gossip/common"
	"github.com/hyperledger/fabric/gossip/election"
	"github.com/hyperledger/fabric/gossip/gossip"
	"github.com/hyperledger/fabric/gossip/identity"
	"github.com/hyperledger/fabric/gossip/integration"
	"github.com/hyperledger/fabric/gossip/state"
	"github.com/hyperledger/fabric/gossip/util"
	"github.com/hyperledger/fabric/protos/common"
	proto "github.com/hyperledger/fabric/protos/gossip"
	"github.com/spf13/viper"
	"google.golang.org/grpc"
)

var (
	gossipServiceInstance *gossipServiceImpl
	once                  sync.Once
)

type gossipSvc gossip.Gossip

// GossipService encapsulates gossip and state capabilities into single interface
type GossipService interface {
	gossip.Gossip

	// NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
	NewConfigEventer() ConfigProcessor
	// InitializeChannel allocates the state provider and should be invoked once per channel per execution
	InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
	// GetBlock returns block for given chain
	GetBlock(chainID string, index uint64) *common.Block
	// AddPayload appends message payload to for given chain
	AddPayload(chainID string, payload *proto.Payload) error
}

// DeliveryServiceFactory factory to create and initialize delivery service instance
type DeliveryServiceFactory interface {
	// Returns an instance of delivery client
	Service(g GossipService, endpoints []string, msc api.MessageCryptoService) (deliverclient.DeliverService, error)
}

type deliveryFactoryImpl struct {
}

// Returns an instance of delivery client
func (*deliveryFactoryImpl) Service(g GossipService, endpoints []string, mcs api.MessageCryptoService) (deliverclient.DeliverService, error) {
	return deliverclient.NewDeliverService(&deliverclient.Config{
		CryptoSvc:   mcs,
		Gossip:      g,
		Endpoints:   endpoints,
		ConnFactory: deliverclient.DefaultConnectionFactory,
		ABCFactory:  deliverclient.DefaultABCFactory,
	})
}

type gossipServiceImpl struct {
	gossipSvc
	chains          map[string]state.GossipStateProvider
	leaderElection  map[string]election.LeaderElectionService
	deliveryService deliverclient.DeliverService
	deliveryFactory DeliveryServiceFactory
	lock            sync.RWMutex
	idMapper        identity.Mapper
	mcs             api.MessageCryptoService
	peerIdentity    []byte
	secAdv          api.SecurityAdvisor
}

// This is an implementation of api.JoinChannelMessage.
type joinChannelMessage struct {
	seqNum              uint64
	members2AnchorPeers map[string][]api.AnchorPeer
}

func (jcm *joinChannelMessage) SequenceNumber() uint64 {
	return jcm.seqNum
}

// Members returns the organizations of the channel
func (jcm *joinChannelMessage) Members() []api.OrgIdentityType {
	members := make([]api.OrgIdentityType, len(jcm.members2AnchorPeers))
	i := 0
	for org := range jcm.members2AnchorPeers {
		members[i] = api.OrgIdentityType(org)
		i++
	}
	return members
}

// AnchorPeersOf returns the anchor peers of the given organization
func (jcm *joinChannelMessage) AnchorPeersOf(org api.OrgIdentityType) []api.AnchorPeer {
	return jcm.members2AnchorPeers[string(org)]
}

var logger = util.GetLogger(util.LoggingServiceModule, "")

// InitGossipService initialize gossip service
func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,
	secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) {
	// TODO: Remove this.
	// TODO: This is a temporary work-around to make the gossip leader election module load its logger at startup
	// TODO: in order for the flogging package to register this logger in time so it can set the log levels as requested in the config
	util.GetLogger(util.LoggingElectionModule, "")
	InitGossipServiceCustomDeliveryFactory(peerIdentity, endpoint, s, &deliveryFactoryImpl{},
		mcs, secAdv, secureDialOpts, bootPeers...)
}

// InitGossipServiceCustomDeliveryFactory initialize gossip service with customize delivery factory
// implementation, might be useful for testing and mocking purposes
func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string, s *grpc.Server,
	factory DeliveryServiceFactory, mcs api.MessageCryptoService, secAdv api.SecurityAdvisor,
	secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) {
	once.Do(func() {
		if overrideEndpoint := viper.GetString("peer.gossip.endpoint"); overrideEndpoint != "" {
			endpoint = overrideEndpoint
		}

		logger.Info("Initialize gossip with endpoint", endpoint, "and bootstrap set", bootPeers)

		idMapper := identity.NewIdentityMapper(mcs, peerIdentity)
		gossip := integration.NewGossipComponent(peerIdentity, endpoint, s, secAdv,
			mcs, idMapper, secureDialOpts, bootPeers...)
		gossipServiceInstance = &gossipServiceImpl{
			mcs:             mcs,
			gossipSvc:       gossip,
			chains:          make(map[string]state.GossipStateProvider),
			leaderElection:  make(map[string]election.LeaderElectionService),
			deliveryFactory: factory,
			idMapper:        idMapper,
			peerIdentity:    peerIdentity,
			secAdv:          secAdv,
		}
	})
}

// GetGossipService returns an instance of gossip service
func GetGossipService() GossipService {
	return gossipServiceInstance
}

// NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
func (g *gossipServiceImpl) NewConfigEventer() ConfigProcessor {
	return newConfigEventer(g)
}

// InitializeChannel allocates the state provider and should be invoked once per channel per execution
func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string) {
	g.lock.Lock()
	defer g.lock.Unlock()
	// Initialize new state provider for given committer
	logger.Debug("Creating state provider for chainID", chainID)
	g.chains[chainID] = state.NewGossipStateProvider(chainID, g, committer, g.mcs)
	if g.deliveryService == nil {
		var err error
		g.deliveryService, err = g.deliveryFactory.Service(gossipServiceInstance, endpoints, g.mcs)
		if err != nil {
			logger.Warning("Cannot create delivery client, due to", err)
		}
	}

	// Delivery service might be nil only if it was not able to get connected
	// to the ordering service
	if g.deliveryService != nil {
		// Parameters:
		//              - peer.gossip.useLeaderElection
		//              - peer.gossip.orgLeader
		//
		// are mutual exclusive, setting both to true is not defined, hence
		// peer will panic and terminate
		leaderElection := viper.GetBool("peer.gossip.useLeaderElection")
		isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader")

		if leaderElection && isStaticOrgLeader {
			logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
		}

		if leaderElection {
			logger.Debug("Delivery uses dynamic leader election mechanism, channel", chainID)
			g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
		} else if isStaticOrgLeader {
			logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", chainID)
			g.deliveryService.StartDeliverForChannel(chainID, committer)
		} else {
			logger.Debug("This peer is not configured to connect to ordering service for blocks delivery, channel", chainID)
		}
	} else {
		logger.Warning("Delivery client is down won't be able to pull blocks for chain", chainID)
	}
}

// configUpdated constructs a joinChannelMessage and sends it to the gossipSvc
func (g *gossipServiceImpl) configUpdated(config Config) {
	myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity)))
	if !g.amIinChannel(myOrg, config) {
		logger.Error("Tried joining channel", config.ChainID(), "but our org(", myOrg, "), isn't "+
			"among the orgs of the channel:", orgListFromConfig(config), ", aborting.")
		return
	}
	jcm := &joinChannelMessage{seqNum: config.Sequence(), members2AnchorPeers: map[string][]api.AnchorPeer{}}
	for _, appOrg := range config.Organizations() {
		logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
		jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
		for _, ap := range appOrg.AnchorPeers() {
			anchorPeer := api.AnchorPeer{
				Host: ap.Host,
				Port: int(ap.Port),
			}
			jcm.members2AnchorPeers[appOrg.MSPID()] = append(jcm.members2AnchorPeers[appOrg.MSPID()], anchorPeer)
		}
	}

	// Initialize new state provider for given committer
	logger.Debug("Creating state provider for chainID", config.ChainID())
	g.JoinChan(jcm, gossipCommon.ChainID(config.ChainID()))
}

// GetBlock returns block for given chain
func (g *gossipServiceImpl) GetBlock(chainID string, index uint64) *common.Block {
	g.lock.RLock()
	defer g.lock.RUnlock()
	return g.chains[chainID].GetBlock(index)
}

// AddPayload appends message payload to for given chain
func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error {
	g.lock.RLock()
	defer g.lock.RUnlock()
	return g.chains[chainID].AddPayload(payload)
}

// Stop stops the gossip component
func (g *gossipServiceImpl) Stop() {
	g.lock.Lock()
	defer g.lock.Unlock()
	for _, ch := range g.chains {
		logger.Info("Stopping chain", ch)
		ch.Stop()
	}

	for chainID, electionService := range g.leaderElection {
		logger.Info("Stopping leader election for %s", chainID)
		electionService.Stop()
	}
	g.gossipSvc.Stop()
	if g.deliveryService != nil {
		g.deliveryService.Stop()
	}
}

func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService {
	PKIid := g.idMapper.GetPKIidOfCert(g.peerIdentity)
	adapter := election.NewAdapter(g, PKIid, gossipCommon.ChainID(chainID))
	return election.NewLeaderElectionService(adapter, string(PKIid), callback)
}

func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool {
	for _, orgName := range orgListFromConfig(config) {
		if orgName == myOrg {
			return true
		}
	}
	return false
}

func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool) {
	return func(isLeader bool) {
		if isLeader {
			logger.Info("Elected as a leader, starting delivery service for channel", chainID)
			if err := g.deliveryService.StartDeliverForChannel(chainID, committer); err != nil {
				logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
			}
		} else {
			logger.Info("Renounced leadership, stopping delivery service for channel", chainID)
			if err := g.deliveryService.StopDeliverForChannel(chainID); err != nil {
				logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
			}

		}

	}
}

func orgListFromConfig(config Config) []string {
	var orgList []string
	for _, appOrg := range config.Organizations() {
		orgList = append(orgList, appOrg.MSPID())
	}
	return orgList
}
<|endoftext|>


<commit_before>package lisp

import (
	"path"
	"reflect"
)

// The map of available imports
var _go_imports = map[string]map[string]interface{}{}

func ExposeImport(name string, pkg map[string]interface{}) {
	_go_imports[name] = pkg
}

// Expose an identifier globally.
func ExposeGlobal(id string, x interface{}) {
	global.define(sym(id), wrapGo(x))
}

func builtinImport(sc *scope, ss []sexpr) sexpr {
	if len(ss) != 1 {
		panic("Invalid number of arguments")
	}

	pkgPath, ok := ss[0].(string)
	if !ok {
		panic("Invalid argument")
	}

	pkgName := path.Base(pkgPath)

	// find the package in _go_imports
	pkg, found := _go_imports[pkgPath]
	if !found {
		panic("Package not found")
	}

	// import each item
	for name, _go := range pkg {
		sc.define(sym(pkgName+"."+name), wrapGo(_go))
	}
	return Nil
}

func wrapGo(_go interface{}) sexpr {
	return wrapGoval(reflect.ValueOf(_go))
}

func wrapGoval(r reflect.Value) sexpr {
	typ := r.Type()
	kind := typ.Kind()
	switch kind {
	case reflect.Bool:
		b := r.Bool()
		if b {
			return float64(1)
		} else {
			return Nil
		}
	case reflect.Int:
		return float64(r.Int())
	case reflect.Int8:
		return float64(r.Int())
	case reflect.Int16:
		return float64(r.Int())
	case reflect.Int32:
		return float64(r.Int())
	case reflect.Int64:
		return float64(r.Int())
	case reflect.Uint:
		return float64(r.Uint())
	case reflect.Uint8:
		return float64(r.Uint())
	case reflect.Uint16:
		return float64(r.Uint())
	case reflect.Uint32:
		return float64(r.Uint())
	case reflect.Uint64:
		return float64(r.Uint())
	case reflect.Uintptr:
		return Nil // TODO
	case reflect.Float32:
		return float64(r.Float())
	case reflect.Float64:
		return float64(r.Float())
	case reflect.Complex64:
		return Nil // TODO
	case reflect.Complex128:
		return Nil // TODO
	case reflect.Array:
		return Nil // TODO
	case reflect.Chan:
		return Nil // TODO
	case reflect.Func:
		return wrapFunc(r.Interface())
	case reflect.Interface:
		return Nil // TODO
	case reflect.Map:
		return Nil // TODO
	case reflect.Ptr:
		return Nil // TODO
	case reflect.Slice:
		return Nil // TODO
	case reflect.String:
		return r.String()
	case reflect.Struct:
		return Nil // TODO
	case reflect.UnsafePointer:
		return Nil // can't handle this
	}
	return Nil
}

func wrapFunc(f interface{}) function {
	// TODO patch reflect so we can do type compatibility-checking
	return func(sc *scope, ss []sexpr) sexpr {
		fun := reflect.ValueOf(f)

		t := fun.Type()
		ni := t.NumIn()
		if ni != len(ss) && !t.IsVariadic() {
			panic("Invalid number of arguments")
		}

		vs := make([]reflect.Value, len(ss))
		for i, s := range ss {
			// TODO convert any cons and function arguments
			vs[i] = reflect.ValueOf(s)
		}
		r := fun.Call(vs)
		if len(r) == 0 {
			return Nil
		}
		return wrapGoval(r[0])
	}
}
<commit_msg>Various compat.go improvements<commit_after>package lisp

import (
	"path"
	"reflect"
)

// The map of available imports
var _go_imports = map[string]map[string]interface{}{}

func ExposeImport(name string, pkg map[string]interface{}) {
	_go_imports[name] = pkg
}

// Expose an identifier globally.
func ExposeGlobal(id string, x interface{}) {
	global.define(sym(id), wrapGo(x))
}

func builtinImport(sc *scope, ss []sexpr) sexpr {
	if len(ss) != 1 {
		panic("Invalid number of arguments")
	}

	pkgPath, ok := ss[0].(string)
	if !ok {
		panic("Invalid argument")
	}

	pkgName := path.Base(pkgPath)

	// find the package in _go_imports
	pkg, found := _go_imports[pkgPath]
	if !found {
		panic("Package not found")
	}

	// import each item
	for name, _go := range pkg {
		sc.define(sym(pkgName+"."+name), wrapGo(_go))
	}
	return Nil
}

func wrapGo(_go interface{}) sexpr {
	return wrapGoval(reflect.ValueOf(_go))
}

func wrapGoval(r reflect.Value) sexpr {
	typ := r.Type()
	kind := typ.Kind()
	switch kind {
	case reflect.Bool:
		b := r.Bool()
		if b {
			return float64(1)
		} else {
			return Nil
		}
	case reflect.Int:
		return float64(r.Int())
	case reflect.Int8:
		return float64(r.Int())
	case reflect.Int16:
		return float64(r.Int())
	case reflect.Int32:
		return float64(r.Int())
	case reflect.Int64:
		return float64(r.Int())
	case reflect.Uint:
		return float64(r.Uint())
	case reflect.Uint8:
		return float64(r.Uint())
	case reflect.Uint16:
		return float64(r.Uint())
	case reflect.Uint32:
		return float64(r.Uint())
	case reflect.Uint64:
		return float64(r.Uint())
	case reflect.Uintptr:
		return Nil // TODO
	case reflect.Float32:
		return float64(r.Float())
	case reflect.Float64:
		return float64(r.Float())
	case reflect.Complex64:
		return Nil // TODO
	case reflect.Complex128:
		return Nil // TODO
	case reflect.Array:
		return Nil // TODO
	case reflect.Chan:
		return Nil // TODO
	case reflect.Func:
		return wrapFunc(r.Interface())
	case reflect.Interface:
		return Nil // TODO
	case reflect.Map:
		return Nil // TODO
	case reflect.Ptr:
		return Nil // TODO
	case reflect.Slice:
		return Nil // TODO
	case reflect.String:
		return r.String()
	case reflect.Struct:
		return Nil // TODO
	case reflect.UnsafePointer:
		return Nil // can't handle this
	}
	return Nil
}

func forGo(v sexpr, typ reflect.Type) reflect.Value {
	kind := typ.Kind()
	switch kind {
	case reflect.Bool:
		return reflect.ValueOf(v != Nil)
	case reflect.Int:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(int(f))
	case reflect.Int8:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(int8(f))
	case reflect.Int16:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(int16(f))
	case reflect.Int32:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(int32(f))
	case reflect.Int64:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(int64(f))
	case reflect.Uint:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(uint(f))
	case reflect.Uint8:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(uint8(f))
	case reflect.Uint16:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(uint16(f))
	case reflect.Uint32:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(uint32(f))
	case reflect.Uint64:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(uint64(f))
	case reflect.Uintptr:
		panic("Invalid argument") // TODO
	case reflect.Float32:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(float32(f))
	case reflect.Float64:
		f, ok := v.(float64)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(f)
	case reflect.Complex64:
		panic("Invalid argument") // TODO
	case reflect.Complex128:
		panic("Invalid argument") // TODO
	case reflect.Array:
		panic("Invalid argument") // TODO
	case reflect.Chan:
		panic("Invalid argument") // TODO
	case reflect.Func:
		panic("Cannot do callbacks yet, sorry") // XXX TODO
	case reflect.Interface:
		// TODO do some checks
		reflect.ValueOf(v)
	case reflect.Map:
		panic("Invalid argument") // TODO
	case reflect.Ptr:
		panic("Invalid argument") // TODO
	case reflect.Slice:
		panic("Invalid argument") // TODO
	case reflect.String:
		s, ok := v.(string)
		if !ok {
			panic("Invalid argument")
		}
		return reflect.ValueOf(s)
	case reflect.Struct:
		panic("Invalid argument") // TODO
	case reflect.UnsafePointer:
		panic("Invalid argument") // can't handle this
	}
	return reflect.ValueOf(v)
}

func wrapFunc(f interface{}) function {
	// TODO patch reflect so we can do type compatibility-checking
	return func(sc *scope, ss []sexpr) sexpr {
		fun := reflect.ValueOf(f)

		t := fun.Type()
		ni := t.NumIn()
		if ni != len(ss) && !t.IsVariadic() {
			panic("Invalid number of arguments")
		}

		vs := make([]reflect.Value, len(ss))
		for i, s := range ss {
			// get argument type
			var at reflect.Type
			if t.IsVariadic() && i >= ni-1 {
				st := t.In(ni-1)
				at = st.Elem()
			} else {
				at = t.In(i)
			}
			// TODO convert any cons and function arguments
			vs[i] = forGo(s, at)
		}
		r := fun.Call(vs)
		if len(r) == 0 {
			return Nil
		}
		return wrapGoval(r[0])
	}
}
<|endoftext|>


<commit_before>package in

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
	"github.com/pivotal-cf-experimental/pivnet-resource/downloader"
	"github.com/pivotal-cf-experimental/pivnet-resource/filter"
	"github.com/pivotal-cf-experimental/pivnet-resource/logger"
	"github.com/pivotal-cf-experimental/pivnet-resource/md5"
	"github.com/pivotal-cf-experimental/pivnet-resource/metadata"
	"github.com/pivotal-cf-experimental/pivnet-resource/pivnet"
	"github.com/pivotal-cf-experimental/pivnet-resource/versions"
)

type InCommand struct {
	logger       logger.Logger
	downloadDir  string
	pivnetClient pivnet.Client
	filter       filter.Filter
	downloader   downloader.Downloader
}

func NewInCommand(
	logger logger.Logger,
	downloadDir string,
	pivnetClient pivnet.Client,
	filter filter.Filter,
	downloader downloader.Downloader,
) *InCommand {
	return &InCommand{
		logger:       logger,
		downloadDir:  downloadDir,
		pivnetClient: pivnetClient,
		filter:       filter,
		downloader:   downloader,
	}
}

func (c *InCommand) Run(input concourse.InRequest) (concourse.InResponse, error) {
	c.logger.Debugf("Received input: %+v\n", input)

	productSlug := input.Source.ProductSlug

	c.logger.Debugf("Creating download directory: %s\n", c.downloadDir)
	err := os.MkdirAll(c.downloadDir, os.ModePerm)
	if err != nil {
		return concourse.InResponse{}, err
	}

	productVersion, etag, err := versions.SplitIntoVersionAndETag(input.Version.ProductVersion)
	if err != nil {
		c.logger.Debugf("Parsing of etag failed; continuing without it\n")
		productVersion = input.Version.ProductVersion
	}

	c.logger.Debugf(
		"Getting release: {product_slug: %s, product_version: %s, etag: %s}\n",
		productSlug,
		productVersion,
		etag,
	)

	release, err := c.pivnetClient.GetRelease(productSlug, productVersion)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Release: %+v\n",
		release,
	)

	c.logger.Debugf(
		"Accepting EULA: {product_slug: %s, release_id: %d}\n",
		productSlug,
		release.ID,
	)

	err = c.pivnetClient.AcceptEULA(productSlug, release.ID)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Getting product files: {release_id: %d}\n",
		release.ID,
	)

	productFiles, err := c.pivnetClient.GetProductFiles(release)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Getting download links: {product_files: %+v}\n",
		productFiles,
	)

	downloadLinksMD5 := map[string]string{}
	for _, p := range productFiles.ProductFiles {
		productFile, err := c.pivnetClient.GetProductFile(
			productSlug,
			release.ID,
			p.ID,
		)
		if err != nil {
			return concourse.InResponse{}, err
		}

		parts := strings.Split(productFile.AWSObjectKey, "/")
		fileName := parts[len(parts)-1]

		downloadLinksMD5[fileName] = productFile.MD5
	}

	downloadLinks := c.filter.DownloadLinks(productFiles)

	if len(input.Params.Globs) > 0 {
		c.logger.Debugf(
			"Filtering download links with globs: {globs: %+v}\n",
			input.Params.Globs,
		)

		var err error
		downloadLinks, err = c.filter.DownloadLinksByGlob(downloadLinks, input.Params.Globs)
		if err != nil {
			return concourse.InResponse{}, err
		}

		c.logger.Debugf(
			"Downloading files: {download_links: %+v, download_dir: %s}\n",
			downloadLinks,
			c.downloadDir,
		)

		files, err := c.downloader.Download(c.downloadDir, downloadLinks, input.Source.APIToken)
		if err != nil {
			log.Fatalf("Failed to Download Files: %s\n", err.Error())
		}

		for _, f := range files {
			downloadPath := filepath.Join(c.downloadDir, f)

			c.logger.Debugf(
				"Calcuating MD5 for downloaded file: %s\n",
				downloadPath,
			)
			md5, err := md5.NewFileContentsSummer(downloadPath).Sum()
			if err != nil {
				log.Fatalf("Failed to calculate MD5: %s\n", err.Error())
			}

			expectedMD5 := downloadLinksMD5[f]
			if md5 != expectedMD5 {
				log.Fatalf(
					"Failed MD5 comparison for file: %s. Expected %s, got %s\n",
					f,
					expectedMD5,
					md5,
				)
			}

			c.logger.Debugf(
				"MD5 for downloaded file: %s matched expected: %s\n",
				downloadPath,
				md5,
			)
		}
	}

	versionFilepath := filepath.Join(c.downloadDir, "version")

	versionWithETag, err := versions.CombineVersionAndETag(productVersion, etag)
	if err != nil {
		// Untested as it is too hard to force versions.CombineVersionAndETag
		// to return an error
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Writing version to file: {version: %s, version_filepath: %s}\n",
		versionWithETag,
		versionFilepath,
	)

	err = ioutil.WriteFile(versionFilepath, []byte(versionWithETag), os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return concourse.InResponse{}, err
	}

	mdata := metadata.Metadata{
		Release: &metadata.Release{
			Version:               release.Version,
			ReleaseType:           release.ReleaseType,
			ReleaseDate:           release.ReleaseDate,
			Description:           release.Description,
			ReleaseNotesURL:       release.ReleaseNotesURL,
			Availability:          release.Availability,
			Controlled:            release.Controlled,
			ECCN:                  release.ECCN,
			LicenseException:      release.LicenseException,
			EndOfSupportDate:      release.EndOfSupportDate,
			EndOfGuidanceDate:     release.EndOfGuidanceDate,
			EndOfAvailabilityDate: release.EndOfAvailabilityDate,
		},
	}

	for _, pf := range productFiles.ProductFiles {
		mdata.ProductFiles = append(mdata.ProductFiles, metadata.ProductFile{
			ID:           pf.ID,
			File:         pf.Name,
			Description:  pf.Description,
			AWSObjectKey: pf.AWSObjectKey,
			FileType:     pf.FileType,
			FileVersion:  pf.FileVersion,
			MD5:          pf.MD5,
		})
	}

	yamlMetadataFilepath := filepath.Join(c.downloadDir, "metadata.yaml")
	c.logger.Debugf(
		"Writing metadata to yaml file: {metadata: %+v, metadata_filepath: %s}\n",
		mdata,
		yamlMetadataFilepath,
	)

	yamlMetadata, err := yaml.Marshal(mdata)
	if err != nil {
		// Untested as it is too hard to force yaml.Marshal to return an error
		return concourse.InResponse{}, err
	}

	err = ioutil.WriteFile(yamlMetadataFilepath, yamlMetadata, os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return concourse.InResponse{}, err
	}

	jsonMetadataFilepath := filepath.Join(c.downloadDir, "metadata.json")
	c.logger.Debugf(
		"Writing metadata to json file: {metadata: %+v, metadata_filepath: %s}\n",
		mdata,
		jsonMetadataFilepath,
	)

	jsonMetadata, err := json.Marshal(mdata)
	if err != nil {
		// Untested as it is too hard to force json.Marshal to return an error
		return concourse.InResponse{}, err
	}

	err = ioutil.WriteFile(jsonMetadataFilepath, jsonMetadata, os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return concourse.InResponse{}, err
	}

	concourseMetadata := []concourse.Metadata{
		{Name: "version", Value: release.Version},
		{Name: "release_type", Value: release.ReleaseType},
		{Name: "release_date", Value: release.ReleaseDate},
		{Name: "description", Value: release.Description},
		{Name: "release_notes_url", Value: release.ReleaseNotesURL},
		{Name: "availability", Value: release.Availability},
		{Name: "controlled", Value: fmt.Sprintf("%t", release.Controlled)},
		{Name: "eccn", Value: release.ECCN},
		{Name: "license_exception", Value: release.LicenseException},
		{Name: "end_of_support_date", Value: release.EndOfSupportDate},
		{Name: "end_of_guidance_date", Value: release.EndOfGuidanceDate},
		{Name: "end_of_availability_date", Value: release.EndOfAvailabilityDate},
	}
	if release.EULA != nil {
		concourseMetadata = append(concourseMetadata, concourse.Metadata{Name: "eula_slug", Value: release.EULA.Slug})
	}

	if release.EULA != nil {
		concourseMetadata = append(concourseMetadata,
			concourse.Metadata{Name: "eula_slug", Value: release.EULA.Slug},
		)
	}

	out := concourse.InResponse{
		Version: concourse.Version{
			ProductVersion: versionWithETag,
		},
		Metadata: concourseMetadata,
	}

	return out, nil
}
<commit_msg>Refactor in command to write meatadata files from separate methods.<commit_after>package in

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/pivotal-cf-experimental/pivnet-resource/concourse"
	"github.com/pivotal-cf-experimental/pivnet-resource/downloader"
	"github.com/pivotal-cf-experimental/pivnet-resource/filter"
	"github.com/pivotal-cf-experimental/pivnet-resource/logger"
	"github.com/pivotal-cf-experimental/pivnet-resource/md5"
	"github.com/pivotal-cf-experimental/pivnet-resource/metadata"
	"github.com/pivotal-cf-experimental/pivnet-resource/pivnet"
	"github.com/pivotal-cf-experimental/pivnet-resource/versions"
)

type InCommand struct {
	logger       logger.Logger
	downloadDir  string
	pivnetClient pivnet.Client
	filter       filter.Filter
	downloader   downloader.Downloader
}

func NewInCommand(
	logger logger.Logger,
	downloadDir string,
	pivnetClient pivnet.Client,
	filter filter.Filter,
	downloader downloader.Downloader,
) *InCommand {
	return &InCommand{
		logger:       logger,
		downloadDir:  downloadDir,
		pivnetClient: pivnetClient,
		filter:       filter,
		downloader:   downloader,
	}
}

func (c *InCommand) Run(input concourse.InRequest) (concourse.InResponse, error) {
	c.logger.Debugf("Received input: %+v\n", input)

	productSlug := input.Source.ProductSlug

	c.logger.Debugf("Creating download directory: %s\n", c.downloadDir)
	err := os.MkdirAll(c.downloadDir, os.ModePerm)
	if err != nil {
		return concourse.InResponse{}, err
	}

	productVersion, etag, err := versions.SplitIntoVersionAndETag(input.Version.ProductVersion)
	if err != nil {
		c.logger.Debugf("Parsing of etag failed; continuing without it\n")
		productVersion = input.Version.ProductVersion
	}

	c.logger.Debugf(
		"Getting release: {product_slug: %s, product_version: %s, etag: %s}\n",
		productSlug,
		productVersion,
		etag,
	)

	release, err := c.pivnetClient.GetRelease(productSlug, productVersion)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Release: %+v\n",
		release,
	)

	c.logger.Debugf(
		"Accepting EULA: {product_slug: %s, release_id: %d}\n",
		productSlug,
		release.ID,
	)

	err = c.pivnetClient.AcceptEULA(productSlug, release.ID)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Getting product files: {release_id: %d}\n",
		release.ID,
	)

	productFiles, err := c.pivnetClient.GetProductFiles(release)
	if err != nil {
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Getting download links: {product_files: %+v}\n",
		productFiles,
	)

	downloadLinksMD5 := map[string]string{}
	for _, p := range productFiles.ProductFiles {
		productFile, err := c.pivnetClient.GetProductFile(
			productSlug,
			release.ID,
			p.ID,
		)
		if err != nil {
			return concourse.InResponse{}, err
		}

		parts := strings.Split(productFile.AWSObjectKey, "/")
		fileName := parts[len(parts)-1]

		downloadLinksMD5[fileName] = productFile.MD5
	}

	downloadLinks := c.filter.DownloadLinks(productFiles)

	if len(input.Params.Globs) > 0 {
		c.logger.Debugf(
			"Filtering download links with globs: {globs: %+v}\n",
			input.Params.Globs,
		)

		var err error
		downloadLinks, err = c.filter.DownloadLinksByGlob(downloadLinks, input.Params.Globs)
		if err != nil {
			return concourse.InResponse{}, err
		}

		c.logger.Debugf(
			"Downloading files: {download_links: %+v, download_dir: %s}\n",
			downloadLinks,
			c.downloadDir,
		)

		files, err := c.downloader.Download(c.downloadDir, downloadLinks, input.Source.APIToken)
		if err != nil {
			log.Fatalf("Failed to Download Files: %s\n", err.Error())
		}

		for _, f := range files {
			downloadPath := filepath.Join(c.downloadDir, f)

			c.logger.Debugf(
				"Calcuating MD5 for downloaded file: %s\n",
				downloadPath,
			)
			md5, err := md5.NewFileContentsSummer(downloadPath).Sum()
			if err != nil {
				log.Fatalf("Failed to calculate MD5: %s\n", err.Error())
			}

			expectedMD5 := downloadLinksMD5[f]
			if md5 != expectedMD5 {
				log.Fatalf(
					"Failed MD5 comparison for file: %s. Expected %s, got %s\n",
					f,
					expectedMD5,
					md5,
				)
			}

			c.logger.Debugf(
				"MD5 for downloaded file: %s matched expected: %s\n",
				downloadPath,
				md5,
			)
		}
	}

	versionFilepath := filepath.Join(c.downloadDir, "version")

	versionWithETag, err := versions.CombineVersionAndETag(productVersion, etag)
	if err != nil {
		// Untested as it is too hard to force versions.CombineVersionAndETag
		// to return an error
		return concourse.InResponse{}, err
	}

	c.logger.Debugf(
		"Writing version to file: {version: %s, version_filepath: %s}\n",
		versionWithETag,
		versionFilepath,
	)

	err = ioutil.WriteFile(versionFilepath, []byte(versionWithETag), os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return concourse.InResponse{}, err
	}

	mdata := metadata.Metadata{
		Release: &metadata.Release{
			Version:               release.Version,
			ReleaseType:           release.ReleaseType,
			ReleaseDate:           release.ReleaseDate,
			Description:           release.Description,
			ReleaseNotesURL:       release.ReleaseNotesURL,
			Availability:          release.Availability,
			Controlled:            release.Controlled,
			ECCN:                  release.ECCN,
			LicenseException:      release.LicenseException,
			EndOfSupportDate:      release.EndOfSupportDate,
			EndOfGuidanceDate:     release.EndOfGuidanceDate,
			EndOfAvailabilityDate: release.EndOfAvailabilityDate,
		},
	}

	for _, pf := range productFiles.ProductFiles {
		mdata.ProductFiles = append(mdata.ProductFiles, metadata.ProductFile{
			ID:           pf.ID,
			File:         pf.Name,
			Description:  pf.Description,
			AWSObjectKey: pf.AWSObjectKey,
			FileType:     pf.FileType,
			FileVersion:  pf.FileVersion,
			MD5:          pf.MD5,
		})
	}

	err = c.writeMetadataYAML(mdata)
	if err != nil {
		return concourse.InResponse{}, err
	}

	err = c.writeMetadataJSON(mdata)
	if err != nil {
		return concourse.InResponse{}, err
	}

	concourseMetadata := []concourse.Metadata{
		{Name: "version", Value: release.Version},
		{Name: "release_type", Value: release.ReleaseType},
		{Name: "release_date", Value: release.ReleaseDate},
		{Name: "description", Value: release.Description},
		{Name: "release_notes_url", Value: release.ReleaseNotesURL},
		{Name: "availability", Value: release.Availability},
		{Name: "controlled", Value: fmt.Sprintf("%t", release.Controlled)},
		{Name: "eccn", Value: release.ECCN},
		{Name: "license_exception", Value: release.LicenseException},
		{Name: "end_of_support_date", Value: release.EndOfSupportDate},
		{Name: "end_of_guidance_date", Value: release.EndOfGuidanceDate},
		{Name: "end_of_availability_date", Value: release.EndOfAvailabilityDate},
	}
	if release.EULA != nil {
		concourseMetadata = append(concourseMetadata, concourse.Metadata{Name: "eula_slug", Value: release.EULA.Slug})
	}

	if release.EULA != nil {
		concourseMetadata = append(concourseMetadata,
			concourse.Metadata{Name: "eula_slug", Value: release.EULA.Slug},
		)
	}

	out := concourse.InResponse{
		Version: concourse.Version{
			ProductVersion: versionWithETag,
		},
		Metadata: concourseMetadata,
	}

	return out, nil
}

func (c InCommand) writeMetadataJSON(mdata metadata.Metadata) error {
	jsonMetadataFilepath := filepath.Join(c.downloadDir, "metadata.json")
	c.logger.Debugf(
		"Writing metadata to json file: {metadata: %+v, metadata_filepath: %s}\n",
		mdata,
		jsonMetadataFilepath,
	)

	jsonMetadata, err := json.Marshal(mdata)
	if err != nil {
		// Untested as it is too hard to force json.Marshal to return an error
		return err
	}

	err = ioutil.WriteFile(jsonMetadataFilepath, jsonMetadata, os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return err
	}

	return nil
}

func (c InCommand) writeMetadataYAML(mdata metadata.Metadata) error {
	yamlMetadataFilepath := filepath.Join(c.downloadDir, "metadata.yaml")
	c.logger.Debugf(
		"Writing metadata to yaml file: {metadata: %+v, metadata_filepath: %s}\n",
		mdata,
		yamlMetadataFilepath,
	)

	yamlMetadata, err := yaml.Marshal(mdata)
	if err != nil {
		// Untested as it is too hard to force yaml.Marshal to return an error
		return err
	}

	err = ioutil.WriteFile(yamlMetadataFilepath, yamlMetadata, os.ModePerm)
	if err != nil {
		// Untested as it is too hard to force io.WriteFile to return an error
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package rdbms

import (
	"github.com/graniticio/granitic/v2/ioc"
	"github.com/graniticio/granitic/v2/logging"
	"github.com/graniticio/granitic/v2/rdbms"
	"testing"
)

func TestDecorator(t *testing.T) {

	cm := new(rdbms.GraniticRdbmsClientManager)

	tar := new(mockTarget)

	c := ioc.NewComponent("", tar)

	d := new(clientManagerDecorator)
	d.fieldNameManager = map[string]rdbms.ClientManager{"ManagedClient": cm}
	d.log = new(logging.ConsoleErrorLogger)

	if !d.OfInterest(c) {
		t.FailNow()
	}

	d.DecorateComponent(c, nil)

	if tar.Client == nil {
		t.FailNow()
	}

}

type mockTarget struct {
	Client rdbms.ClientManager
}
<commit_msg>Fixed broken RDBMS client decorator test<commit_after>package rdbms

import (
	"github.com/graniticio/granitic/v2/ioc"
	"github.com/graniticio/granitic/v2/logging"
	"github.com/graniticio/granitic/v2/rdbms"
	"testing"
)

func TestDecorator(t *testing.T) {

	cm := new(rdbms.GraniticRdbmsClientManager)

	tar := new(mockTarget)

	c := ioc.NewComponent("", tar)

	d := new(clientManagerDecorator)
	d.fieldNameManager = map[string]rdbms.ClientManager{"sClient": cm}
	d.log = new(logging.ConsoleErrorLogger)

	if !d.OfInterest(c) {
		t.FailNow()
	}

	d.DecorateComponent(c, nil)

	if tar.Client == nil {
		t.FailNow()
	}

}

type mockTarget struct {
	Client rdbms.ClientManager
}
<|endoftext|>


<commit_before>// Copyright 2016 Google Inc. All rights reserved.
//
// 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 rest

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/google/zoekt"
	"github.com/google/zoekt/query"
)

const jsonContentType = "application/json; charset=utf-8"

type httpError struct {
	msg    string
	status int
}

func (e *httpError) Error() string { return fmt.Sprintf("%d: %s", e.status, e.msg) }

func Search(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) {
	if err := serveSearchAPIErr(s, w, r); err != nil {
		if e, ok := err.(*httpError); ok {
			http.Error(w, e.msg, e.status)
		}
		http.Error(w, err.Error(), http.StatusTeapot)
	}
}

func serveSearchAPIErr(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodPost {
		return &httpError{"must use POST", http.StatusMethodNotAllowed}
	}

	if got := r.Header.Get("Content-Type"); got != jsonContentType {
		return &httpError{"must use " + jsonContentType, http.StatusNotAcceptable}

	}

	content, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return &httpError{err.Error(), http.StatusBadRequest}
	}

	var req SearchRequest
	if err := json.Unmarshal(content, &req); err != nil {
		return &httpError{err.Error(), http.StatusBadRequest}
	}

	rep, err := serveSearchAPIStructured(s, &req)
	if err != nil {
		return err
	}
	content, err = json.Marshal(rep)
	if err != nil {
		return &httpError{err.Error(), http.StatusInternalServerError}

	}

	w.Header().Set("Content-Type", jsonContentType)
	if _, err := w.Write(content); err != nil {
		return &httpError{err.Error(), http.StatusInternalServerError}
	}
	return nil
}

func serveSearchAPIStructured(searcher zoekt.Searcher, req *SearchRequest) (*SearchResponse, error) {
	q, err := query.Parse(req.Query)
	if err != nil {
		msg := "parse error: " + err.Error()
		return &SearchResponse{Error: &msg}, nil
	}

	var restrictions []query.Q
	for _, r := range req.Restrict {
		var branchQs []query.Q
		for _, b := range r.Branches {
			branchQs = append(branchQs, &query.Branch{b})
		}

		restrictions = append(restrictions,
			query.NewAnd(&query.Repo{r.Repo}, query.NewOr(branchQs...)))
	}

	finalQ := query.NewAnd(q, query.NewOr(restrictions...))
	var options zoekt.SearchOptions
	options.SetDefaults()

	ctx := context.Background()
	result, err := searcher.Search(ctx, finalQ, &options)
	if err != nil {
		return nil, &httpError{err.Error(), http.StatusInternalServerError}
	}

	// TODO - make this tunable. Use a query param or a JSON struct?
	num := 50
	if len(result.Files) > num {
		result.Files = result.Files[:num]
	}
	var resp SearchResponse
	for _, f := range result.Files {
		srf := SearchResponseFile{
			Repo:     f.Repository,
			Branches: f.Branches,
			FileName: f.FileName,
			// TODO - set version
		}
		for _, m := range f.LineMatches {
			srl := &SearchResponseLine{
				LineNumber: m.LineNumber,
				Line:       string(m.Line),
			}

			// Convert to unicode indices.
			charOffsets := make([]int, len(m.Line), len(m.Line)+1)
			j := 0
			for i := range srl.Line {
				charOffsets[i] = j
				j++
			}
			charOffsets = append(charOffsets, j)

			for _, fr := range m.LineFragments {
				srfr := SearchResponseMatch{
					Start: charOffsets[fr.LineOffset],
					End:   charOffsets[fr.LineOffset+fr.MatchLength],
				}

				srl.Matches = append(srl.Matches, &srfr)
			}
			srf.Lines = append(srf.Lines, srl)
		}
		resp.Files = append(resp.Files, &srf)
	}

	return &resp, nil
}
<commit_msg>Use pre-1.7 import path for "context" package.<commit_after>// Copyright 2016 Google Inc. All rights reserved.
//
// 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 rest

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/google/zoekt"
	"github.com/google/zoekt/query"
	"golang.org/x/net/context"
)

const jsonContentType = "application/json; charset=utf-8"

type httpError struct {
	msg    string
	status int
}

func (e *httpError) Error() string { return fmt.Sprintf("%d: %s", e.status, e.msg) }

func Search(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) {
	if err := serveSearchAPIErr(s, w, r); err != nil {
		if e, ok := err.(*httpError); ok {
			http.Error(w, e.msg, e.status)
		}
		http.Error(w, err.Error(), http.StatusTeapot)
	}
}

func serveSearchAPIErr(s zoekt.Searcher, w http.ResponseWriter, r *http.Request) error {
	if r.Method != http.MethodPost {
		return &httpError{"must use POST", http.StatusMethodNotAllowed}
	}

	if got := r.Header.Get("Content-Type"); got != jsonContentType {
		return &httpError{"must use " + jsonContentType, http.StatusNotAcceptable}

	}

	content, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return &httpError{err.Error(), http.StatusBadRequest}
	}

	var req SearchRequest
	if err := json.Unmarshal(content, &req); err != nil {
		return &httpError{err.Error(), http.StatusBadRequest}
	}

	rep, err := serveSearchAPIStructured(s, &req)
	if err != nil {
		return err
	}
	content, err = json.Marshal(rep)
	if err != nil {
		return &httpError{err.Error(), http.StatusInternalServerError}

	}

	w.Header().Set("Content-Type", jsonContentType)
	if _, err := w.Write(content); err != nil {
		return &httpError{err.Error(), http.StatusInternalServerError}
	}
	return nil
}

func serveSearchAPIStructured(searcher zoekt.Searcher, req *SearchRequest) (*SearchResponse, error) {
	q, err := query.Parse(req.Query)
	if err != nil {
		msg := "parse error: " + err.Error()
		return &SearchResponse{Error: &msg}, nil
	}

	var restrictions []query.Q
	for _, r := range req.Restrict {
		var branchQs []query.Q
		for _, b := range r.Branches {
			branchQs = append(branchQs, &query.Branch{b})
		}

		restrictions = append(restrictions,
			query.NewAnd(&query.Repo{r.Repo}, query.NewOr(branchQs...)))
	}

	finalQ := query.NewAnd(q, query.NewOr(restrictions...))
	var options zoekt.SearchOptions
	options.SetDefaults()

	ctx := context.Background()
	result, err := searcher.Search(ctx, finalQ, &options)
	if err != nil {
		return nil, &httpError{err.Error(), http.StatusInternalServerError}
	}

	// TODO - make this tunable. Use a query param or a JSON struct?
	num := 50
	if len(result.Files) > num {
		result.Files = result.Files[:num]
	}
	var resp SearchResponse
	for _, f := range result.Files {
		srf := SearchResponseFile{
			Repo:     f.Repository,
			Branches: f.Branches,
			FileName: f.FileName,
			// TODO - set version
		}
		for _, m := range f.LineMatches {
			srl := &SearchResponseLine{
				LineNumber: m.LineNumber,
				Line:       string(m.Line),
			}

			// Convert to unicode indices.
			charOffsets := make([]int, len(m.Line), len(m.Line)+1)
			j := 0
			for i := range srl.Line {
				charOffsets[i] = j
				j++
			}
			charOffsets = append(charOffsets, j)

			for _, fr := range m.LineFragments {
				srfr := SearchResponseMatch{
					Start: charOffsets[fr.LineOffset],
					End:   charOffsets[fr.LineOffset+fr.MatchLength],
				}

				srl.Matches = append(srl.Matches, &srfr)
			}
			srf.Lines = append(srf.Lines, srl)
		}
		resp.Files = append(resp.Files, &srf)
	}

	return &resp, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gg

import (
	"sort"

	"github.com/aclements/go-gg/gg/layout"
)

// textLeading is the height of a line of text.
//
// TODO: Make this real. Chrome's default font-size is 16px, so 20px
// is a fairly comfortable leading.
const textLeading = 20

type eltType int

const (
	eltSubplot eltType = 1 + iota
	eltHLabel
	eltVLabel
)

// A plotElt is a high-level element of a plot layout. It is either a
// subplot body, or a facet label.
//
// plotElts are arranged in a 2D grid. Coordinates in the grid are
// specified by a pair of "paths" rather than a simple pair of
// indexes. For example, element A is to the left of element B if A's
// X path is less than B's X path, where paths are compared in tuple
// order. This makes it easy to, for example, place an element to the
// right of another element without having to renumber all of the
// elements that are already to its right.
type plotElt struct {
	typ            eltType
	xPath, yPath   eltPath // Top left coordinate.
	x2Path, y2Path eltPath // Bottom right. If nil, same as xPath, yPath.

	// For subplot elements.
	subplot *subplot
	marks   []plotMark
	scales  map[string]map[Scaler]bool

	// For label elements.
	label string

	// x, y, xSpan, and ySpan are the global position and span of
	// this element. These are computed by layoutPlotElts.
	x, y         int
	xSpan, ySpan int

	layout *layout.Leaf
}

func newPlotElt(s *subplot) *plotElt {
	return &plotElt{
		typ:     eltSubplot,
		subplot: s,
		scales:  make(map[string]map[Scaler]bool),
		xPath:   eltPath{s.x, 0},
		yPath:   eltPath{s.y, 0},
		layout:  new(layout.Leaf).SetFlex(true, true),
	}
}

func addSubplotLabels(elts []*plotElt) []*plotElt {
	// Find the regions covered by each subplot band.
	type region struct{ x1, x2, y1, y2, level int }
	update := func(r *region, s *subplot) {
		if s.x < r.x1 {
			r.x1 = s.x
		} else if s.x > r.x2 {
			r.x2 = s.x
		}
		if s.y < r.y1 {
			r.y1 = s.y
		} else if s.y > r.y2 {
			r.y2 = s.y
		}
	}

	vBands := make(map[*subplotBand]region)
	hBands := make(map[*subplotBand]region)
	for _, elt := range elts {
		if elt.typ != eltSubplot {
			continue
		}
		s := elt.subplot

		level := 1
		for vBand := s.vBand; vBand != nil; vBand = vBand.parent {
			r, ok := vBands[vBand]
			if !ok {
				r = region{s.x, s.x, s.y, s.y, level}
			} else {
				update(&r, s)
			}
			vBands[vBand] = r
			level++
		}

		level = 1
		for hBand := s.hBand; hBand != nil; hBand = hBand.parent {
			r, ok := hBands[hBand]
			if !ok {
				r = region{s.x, s.x, s.y, s.y, level}
			} else {
				update(&r, s)
			}
			hBands[hBand] = r
			level++
		}
	}

	// Create labels.
	for vBand, r := range vBands {
		elts = append(elts, &plotElt{
			typ:    eltHLabel,
			label:  vBand.label,
			xPath:  eltPath{r.x1, 0},
			yPath:  eltPath{r.y1, -r.level},
			x2Path: eltPath{r.x2, 0},
			layout: new(layout.Leaf).SetMin(0, textLeading).SetFlex(true, false),
		})
	}
	for hBand, r := range hBands {
		elts = append(elts, &plotElt{
			typ:    eltVLabel,
			label:  hBand.label,
			xPath:  eltPath{r.x2, r.level},
			yPath:  eltPath{r.y1, 0},
			y2Path: eltPath{r.y2, 0},
			layout: new(layout.Leaf).SetMin(textLeading, 0).SetFlex(false, true),
		})
	}
	return elts
}

type eltPath []int

func (a eltPath) cmp(b eltPath) int {
	for k := 0; k < len(a) && k < len(b); k++ {
		if a[k] != b[k] {
			if a[k] < b[k] {
				return -1
			} else {
				return 1
			}
		}
	}
	if len(a) < len(b) {
		return -1
	} else if len(a) > len(b) {
		return 1
	}
	return 0
}

type eltPaths []eltPath

func (s eltPaths) Len() int {
	return len(s)
}

func (s eltPaths) Less(i, j int) bool {
	return s[i].cmp(s[j]) < 0
}

func (s eltPaths) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s eltPaths) nub() eltPaths {
	var i, o int
	for i, o = 1, 1; i < len(s); i++ {
		if s[i].cmp(s[i-1]) != 0 {
			s[o] = s[i]
			o++
		}
	}
	return s[:o]
}

func (s eltPaths) find(p eltPath) int {
	return sort.Search(len(s), func(i int) bool {
		return s[i].cmp(p) >= 0
	})
}

// layoutPlotElts returns a layout containing all of the elements in
// elts.
//
// layoutPlotElts flattens the X and Y paths of elts into simple
// coordinate indexes and constructs a layout.Grid.
func layoutPlotElts(elts []*plotElt) layout.Element {
	// Construct the global element grid from coordinate paths by
	// sorting the sets of X paths and Y paths to each leaf and
	// computing a global (x,y) for each leaf from these orders.
	dir := func(get func(*plotElt) (p, p2 eltPath, pos, span *int)) {
		var paths eltPaths
		for _, elt := range elts {
			p, p2, _, _ := get(elt)
			paths = append(paths, p)
			if p2 != nil {
				paths = append(paths, p2)
			}
		}
		sort.Sort(paths)
		paths = paths.nub()
		for _, elt := range elts {
			p, p2, pos, span := get(elt)
			*pos = paths.find(p)
			if p2 == nil {
				*span = 1
			} else {
				*span = paths.find(p2) - *pos + 1
			}
		}
	}
	dir(func(e *plotElt) (p, p2 eltPath, pos, span *int) {
		return e.xPath, e.x2Path, &e.x, &e.xSpan
	})
	dir(func(e *plotElt) (p, p2 eltPath, pos, span *int) {
		return e.yPath, e.y2Path, &e.y, &e.ySpan
	})

	// Construct the grid layout.
	l := new(layout.Grid)
	for _, si := range elts {
		l.Add(si.layout, si.x, si.y, si.xSpan, si.ySpan)
	}
	return l
}
<commit_msg>gg: document layout hierarchy<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gg

import (
	"sort"

	"github.com/aclements/go-gg/gg/layout"
)

// textLeading is the height of a line of text.
//
// TODO: Make this real. Chrome's default font-size is 16px, so 20px
// is a fairly comfortable leading.
const textLeading = 20

type eltType int

const (
	eltSubplot eltType = 1 + iota
	eltHLabel
	eltVLabel
)

// A plotElt is a high-level element of a plot layout. It is either a
// subplot body, or a facet label.
//
// plotElts are arranged in a 2D grid. Coordinates in the grid are
// specified by a pair of "paths" rather than a simple pair of
// indexes. For example, element A is to the left of element B if A's
// X path is less than B's X path, where paths are compared in tuple
// order. This makes it easy to, for example, place an element to the
// right of another element without having to renumber all of the
// elements that are already to its right.
//
// The first level of the hierarchy is simply the coordinate of the
// plot in the grid. Within this, we layout plot elements as follows:
//
//    +----------------------+
//    | label (x/0, y/-2)    |
//    +----------------------+
//    | label (x/0, y/-1)    |
//    +----------------------+------------+
//    |                      | label      |
//    | body (x/0, y/0)      | (x/1, y/0) |
//    |                      |            |
//    +----------------------+------------+
type plotElt struct {
	typ            eltType
	xPath, yPath   eltPath // Top left coordinate.
	x2Path, y2Path eltPath // Bottom right. If nil, same as xPath, yPath.

	// For subplot elements.
	subplot *subplot
	marks   []plotMark
	scales  map[string]map[Scaler]bool

	// For label elements.
	label string

	// x, y, xSpan, and ySpan are the global position and span of
	// this element. These are computed by layoutPlotElts.
	x, y         int
	xSpan, ySpan int

	layout *layout.Leaf
}

func newPlotElt(s *subplot) *plotElt {
	return &plotElt{
		typ:     eltSubplot,
		subplot: s,
		scales:  make(map[string]map[Scaler]bool),
		xPath:   eltPath{s.x, 0},
		yPath:   eltPath{s.y, 0},
		layout:  new(layout.Leaf).SetFlex(true, true),
	}
}

func addSubplotLabels(elts []*plotElt) []*plotElt {
	// Find the regions covered by each subplot band.
	type region struct{ x1, x2, y1, y2, level int }
	update := func(r *region, s *subplot) {
		if s.x < r.x1 {
			r.x1 = s.x
		} else if s.x > r.x2 {
			r.x2 = s.x
		}
		if s.y < r.y1 {
			r.y1 = s.y
		} else if s.y > r.y2 {
			r.y2 = s.y
		}
	}

	vBands := make(map[*subplotBand]region)
	hBands := make(map[*subplotBand]region)
	for _, elt := range elts {
		if elt.typ != eltSubplot {
			continue
		}
		s := elt.subplot

		level := 1
		for vBand := s.vBand; vBand != nil; vBand = vBand.parent {
			r, ok := vBands[vBand]
			if !ok {
				r = region{s.x, s.x, s.y, s.y, level}
			} else {
				update(&r, s)
			}
			vBands[vBand] = r
			level++
		}

		level = 1
		for hBand := s.hBand; hBand != nil; hBand = hBand.parent {
			r, ok := hBands[hBand]
			if !ok {
				r = region{s.x, s.x, s.y, s.y, level}
			} else {
				update(&r, s)
			}
			hBands[hBand] = r
			level++
		}
	}

	// Create labels.
	for vBand, r := range vBands {
		elts = append(elts, &plotElt{
			typ:    eltHLabel,
			label:  vBand.label,
			xPath:  eltPath{r.x1, 0},
			yPath:  eltPath{r.y1, -r.level},
			x2Path: eltPath{r.x2, 0},
			layout: new(layout.Leaf).SetMin(0, textLeading).SetFlex(true, false),
		})
	}
	for hBand, r := range hBands {
		elts = append(elts, &plotElt{
			typ:    eltVLabel,
			label:  hBand.label,
			xPath:  eltPath{r.x2, r.level},
			yPath:  eltPath{r.y1, 0},
			y2Path: eltPath{r.y2, 0},
			layout: new(layout.Leaf).SetMin(textLeading, 0).SetFlex(false, true),
		})
	}
	return elts
}

type eltPath []int

func (a eltPath) cmp(b eltPath) int {
	for k := 0; k < len(a) && k < len(b); k++ {
		if a[k] != b[k] {
			if a[k] < b[k] {
				return -1
			} else {
				return 1
			}
		}
	}
	if len(a) < len(b) {
		return -1
	} else if len(a) > len(b) {
		return 1
	}
	return 0
}

type eltPaths []eltPath

func (s eltPaths) Len() int {
	return len(s)
}

func (s eltPaths) Less(i, j int) bool {
	return s[i].cmp(s[j]) < 0
}

func (s eltPaths) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s eltPaths) nub() eltPaths {
	var i, o int
	for i, o = 1, 1; i < len(s); i++ {
		if s[i].cmp(s[i-1]) != 0 {
			s[o] = s[i]
			o++
		}
	}
	return s[:o]
}

func (s eltPaths) find(p eltPath) int {
	return sort.Search(len(s), func(i int) bool {
		return s[i].cmp(p) >= 0
	})
}

// layoutPlotElts returns a layout containing all of the elements in
// elts.
//
// layoutPlotElts flattens the X and Y paths of elts into simple
// coordinate indexes and constructs a layout.Grid.
func layoutPlotElts(elts []*plotElt) layout.Element {
	// Construct the global element grid from coordinate paths by
	// sorting the sets of X paths and Y paths to each leaf and
	// computing a global (x,y) for each leaf from these orders.
	dir := func(get func(*plotElt) (p, p2 eltPath, pos, span *int)) {
		var paths eltPaths
		for _, elt := range elts {
			p, p2, _, _ := get(elt)
			paths = append(paths, p)
			if p2 != nil {
				paths = append(paths, p2)
			}
		}
		sort.Sort(paths)
		paths = paths.nub()
		for _, elt := range elts {
			p, p2, pos, span := get(elt)
			*pos = paths.find(p)
			if p2 == nil {
				*span = 1
			} else {
				*span = paths.find(p2) - *pos + 1
			}
		}
	}
	dir(func(e *plotElt) (p, p2 eltPath, pos, span *int) {
		return e.xPath, e.x2Path, &e.x, &e.xSpan
	})
	dir(func(e *plotElt) (p, p2 eltPath, pos, span *int) {
		return e.yPath, e.y2Path, &e.y, &e.ySpan
	})

	// Construct the grid layout.
	l := new(layout.Grid)
	for _, si := range elts {
		l.Add(si.layout, si.x, si.y, si.xSpan, si.ySpan)
	}
	return l
}
<|endoftext|>


<commit_before>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/oglematchers"
)

// AssertEq(e, a) is equivalent to AssertThat(a, oglematchers.Equals(e)).
func AssertEq(expected, actual interface{}, errorParts ...interface{}) {
	res := expectThat(actual, oglematchers.Equals(expected), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertNe(e, a) is equivalent to AssertThat(a, oglematchers.Not(oglematchers.Equals(e))).
func AssertNe(expected, actual interface{}, errorParts ...interface{}) {
	res := expectThat(
		actual,
		oglematchers.Not(oglematchers.Equals(expected)),
		errorParts...)

	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertLt(x, y) is equivalent to AssertThat(x, oglematchers.LessThan(y)).
func AssertLt(x, y interface{}, errorParts ...interface{}) {
	res := expectThat(x, oglematchers.LessThan(y), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertLe(x, y) is equivalent to AssertThat(x, oglematchers.LessOrEqual(y)).
func AssertLe(x, y interface{}, errorParts ...interface{}) {
	res := expectThat(x, oglematchers.LessOrEqual(y), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertGt(x, y) is equivalent to AssertThat(x, oglematchers.GreaterThan(y)).
func AssertGt(x, y interface{}, errorParts ...interface{}) {
	res := expectThat(x, oglematchers.GreaterThan(y), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertGe(x, y) is equivalent to AssertThat(x, oglematchers.GreaterOrEqual(y)).
func AssertGe(x, y interface{}, errorParts ...interface{}) {
	res := expectThat(x, oglematchers.GreaterOrEqual(y), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertTrue(b) is equivalent to AssertThat(b, oglematchers.Equals(true)).
func AssertTrue(b interface{}, errorParts ...interface{}) {
	res := expectThat(b, oglematchers.Equals(true), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}

// AssertFalse(b) is equivalent to AssertThat(b, oglematchers.Equals(false)).
func AssertFalse(b interface{}, errorParts ...interface{}) {
	res := expectThat(b, oglematchers.Equals(false), errorParts...)
	res.SetCaller(getCallerForAlias())

	matcherErr := res.MatchResult()
	if matcherErr != nil {
		panic(&assertThatError{})
	}
}
<commit_msg>Fixed assert aliases.<commit_after>// Copyright 2011 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 ogletest

import (
	"github.com/jacobsa/oglematchers"
)

// AssertEq(e, a) is equivalent to AssertThat(a, oglematchers.Equals(e)).
func AssertEq(expected, actual interface{}, errorParts ...interface{}) {
	assertThat(actual, oglematchers.Equals(expected), 1, errorParts)
}

// AssertNe(e, a) is equivalent to AssertThat(a, oglematchers.Not(oglematchers.Equals(e))).
func AssertNe(expected, actual interface{}, errorParts ...interface{}) {
	assertThat(
		actual,
		oglematchers.Not(oglematchers.Equals(expected)),
		1,
		errorParts)
}

// AssertLt(x, y) is equivalent to AssertThat(x, oglematchers.LessThan(y)).
func AssertLt(x, y interface{}, errorParts ...interface{}) {
	assertThat(x, oglematchers.LessThan(y), 1, errorParts)
}

// AssertLe(x, y) is equivalent to AssertThat(x, oglematchers.LessOrEqual(y)).
func AssertLe(x, y interface{}, errorParts ...interface{}) {
	assertThat(x, oglematchers.LessOrEqual(y), 1, errorParts)
}

// AssertGt(x, y) is equivalent to AssertThat(x, oglematchers.GreaterThan(y)).
func AssertGt(x, y interface{}, errorParts ...interface{}) {
	assertThat(x, oglematchers.GreaterThan(y), 1, errorParts)
}

// AssertGe(x, y) is equivalent to AssertThat(x, oglematchers.GreaterOrEqual(y)).
func AssertGe(x, y interface{}, errorParts ...interface{}) {
	assertThat(x, oglematchers.GreaterOrEqual(y), 1, errorParts)
}

// AssertTrue(b) is equivalent to AssertThat(b, oglematchers.Equals(true)).
func AssertTrue(b interface{}, errorParts ...interface{}) {
	assertThat(b, oglematchers.Equals(true), 1, errorParts)
}

// AssertFalse(b) is equivalent to AssertThat(b, oglematchers.Equals(false)).
func AssertFalse(b interface{}, errorParts ...interface{}) {
	assertThat(b, oglematchers.Equals(false), 1, errorParts)
}
<|endoftext|>