<commit_before>/*
Hugocs or Hugo Comment Script is a script that processes POST requests
in a specific format and converts them into JSON files that are saved
to the filesystem. These files can then be processed using Hugo's
readDir and getJSON functions and displayed as comments.

Copyright 2016 Juha Auvinen.

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 main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Comment struct {
	Name       string `json:"name"`
	Email      string `json:"email"`
	Website    string `json:"website"`
	GravatarID string `json:"gravatar_id"`
	IPAddress  string `json:"ipv4_address"`
	PageID     string `json:"page_id"`
	Body       string `json:"body"`
	Timestamp  string `json:"timestamp"`
}

type Config struct {
	BaseDir     string
	CommentsDir string
	ContentDir  string
	TouchFile   string
}

type Response struct {
	Message string `json:"message"`
	IsError bool   `json:"is_error"`
}

var config = Config{
	BaseDir:     ".",
	CommentsDir: "comments",
	ContentDir:  "content",
	TouchFile:   ".comment"}

// Used for testing
func newComment(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("new.html")
	t.Execute(w, "")
}

func saveComment(w http.ResponseWriter, r *http.Request) {
	// TODO: Allow other content-types for javascript-disabled clients.
	// Maybe render a HTML template?
	w.Header().Set("Content-Type", "application/json")
	response := Response{
		Message: "",
		IsError: true}

	if r.Method == "POST" {
		r.ParseForm()
		_, err := validateComment(r, config.ContentDir)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			response = Response{
				Message: err.Error(),
				IsError: true}
		} else {
			now := time.Now()
			comment := Comment{
				Name:       r.Form.Get("name"),
				Email:      r.Form.Get("email"),
				Website:    r.Form.Get("website"),
				GravatarID: getGravatarId(r.Form.Get("email")),
				IPAddress:  getIPAddress(r),
				PageID:     r.Form.Get("page_id"),
				Body:       processBody(r.Form.Get("body")),
				Timestamp:  now.Format(time.RFC3339)}

			// FIXME: Needs error handling
			jsonData, _ := json.Marshal(comment)
			filename := buildFilename(comment.Email, buildTimestamp(now))
			writePath := filepath.Join(config.CommentsDir, comment.PageID)
			writeCommentToDisk(jsonData, writePath, filename)

			//str := fmt.Sprintf( "%#v", r )

			w.WriteHeader(http.StatusOK)
			response = Response{
				Message: "Thank you for the comment",
				IsError: false}
		}
	} else {
		w.WriteHeader(http.StatusBadRequest)
		response = Response{
			Message: "Must be POST",
			IsError: true}
	}
	// FIXME: Needs error handling
	responseJSON, _ := json.Marshal(response)
	w.Write(responseJSON)
}

// TODO: Check field lengths to prevent too large comment files
// TODO: Error messages should be read from a config file
func validateComment(r *http.Request, contentDir string) (string, error) {
	form := r.Form
	if form.Get("last_name") != "" {
		return "last_name", errors.New("You appear to be a spammer, or your browser auto-fills this form.")
	}
	if regexp.MustCompile(`(?i)[\w\s\d]+`).MatchString(form.Get("name")) != true {
		return "name", errors.New("Name is not valid")
	}
	if regexp.MustCompile(`^[a-z0-9_.\-\+]+@[a-z0-9_.\-\+]+$`).MatchString(form.Get("email")) != true {
		return "email", errors.New("Email address is not valid")
	}
	if form.Get("website") != "" && regexp.MustCompile(`(https?:\/\/)?[a-z0-9\-\.]+`).MatchString(form.Get("website")) != true {
		return "website", errors.New("Website is not valid")
	}
	if regexp.MustCompile(`[a-z0-9\-]+(\/[a-z0-9\-]+)*`).MatchString(form.Get("page_id")) != true {
		return "page_id", errors.New("page_id is not valid")
	}
	if regexp.MustCompile(`[a-z]+`).MatchString(form.Get("content_type")) != true {
		return "content_type", errors.New("Content type is not valid")
	}
	if form.Get("body") == "" {
		return "body", errors.New("You forgot to write the actual comment!")
	}
	if !postExists(form.Get("page_id"), form.Get("content_type"), contentDir) {
		return "page_id", errors.New("Specified post does not exist")
	}
	return "", nil
}

func isValidPath(path string) bool {
	if _, err := os.Stat(path); err == nil {
		return true
	}
	//panic(path)
	return false
}

func postExists(pageID string, extension string, contentDir string) bool {
	return isValidPath(filepath.Join(contentDir, pageID+"."+extension))
}

func processBody(body string) string {
	body = strings.Replace(body, `"""`, "%quote%", -1)
	body = template.HTMLEscapeString(body)
	body = strings.Replace(body, "%quote%", ">", -1)
	return body
}

func getGravatarId(email string) string {
	hash := md5.Sum([]byte(email))
	return hex.EncodeToString(hash[:])
}

func getIPAddress(r *http.Request) string {
	if r.RemoteAddr != "" {
		return r.RemoteAddr
	}
	// FIXME: This should probably look at other fields instead
	return "Unknown"
}

func debugJSON(comment Comment) string {
	str := fmt.Sprintf("%#v", comment)
	return str
}

func buildFilename(email string, timestamp string) string {
	return timestamp + "-" + email + ".json"
}

/*
BuildTimestamp builds a timestamp string to be used as part of the
filename for JSON comment files.
*/
func buildTimestamp(now time.Time) string {
	dateString := fmt.Sprintf("%d-%02d-%02d-%02d%02d%02d",
		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
	return dateString
}

func writeCommentToDisk(JSON []byte, path string, filename string) error {
	dirCreated := false
	// If path doesn't exist, create it
	if !isValidPath(path) {
		os.MkdirAll(path, os.ModePerm)
		dirCreated = true
	}
	fn := filepath.Join(path, filename)
	// Write file to disk
	// FIXME: Needs error handling
	ioutil.WriteFile(fn, JSON, 0600)
	if dirCreated == true && config.TouchFile != "" {
		updateChangeFile(filepath.Join(config.TouchFile))
	}
	return nil
}

func updateChangeFile(touchFile string) {
	// FIXME: Needs error handling
	ioutil.WriteFile(touchFile, []byte("."), 0600)
}

func main() {
	sourceFlag := flag.String("src", ".", "Full path to the sources dir.")
	contentFlag := flag.String("content", "content", "The content directory. Relative to source dir.")
	commentsFlag := flag.String("comments", "comments", "The directory to save comments. Relative to source dir.")
	touchFlag := flag.String("touch", "", "File to update when a new comment directory is created. Relative to source dir. Some watch scripts may require this.")
	addressFlag := flag.String("address", "", "IP address to use for incoming requests. Defaults to any address.")
	portFlag := flag.String("port", "8080", "Port to listen to for incoming requests.")
	pathFlag := flag.String("path", "/comment", "The url path that is used for comment processing.")

	flag.Parse()

	config.BaseDir = *sourceFlag
	config.ContentDir = filepath.Join(*sourceFlag, *contentFlag)
	config.CommentsDir = filepath.Join(*sourceFlag, *commentsFlag)
	if *touchFlag != "" {
		config.TouchFile = filepath.Join(*sourceFlag, *touchFlag)
	} else {
		config.TouchFile = ""
	}

	// FIXME: Add
	serverAddress := *addressFlag + ":" + *portFlag

	http.HandleFunc(*pathFlag, saveComment)
	//http.HandleFunc("/new", newComment );
	http.ListenAndServe(serverAddress, nil)
}
<commit_msg>Change Comment field tags back to camelCase<commit_after>/*
Hugocs or Hugo Comment Script is a script that processes POST requests
in a specific format and converts them into JSON files that are saved
to the filesystem. These files can then be processed using Hugo's
readDir and getJSON functions and displayed as comments.

Copyright 2016 Juha Auvinen.

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 main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"flag"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

type Comment struct {
	Name       string `json:"name"`
	Email      string `json:"email"`
	Website    string `json:"website"`
	GravatarID string `json:"gravatarId"`
	IPAddress  string `json:"ipv4Address"`
	PageID     string `json:"pageId"`
	Body       string `json:"body"`
	Timestamp  string `json:"timestamp"`
}

type Config struct {
	BaseDir     string
	CommentsDir string
	ContentDir  string
	TouchFile   string
}

type Response struct {
	Message string `json:"message"`
	IsError bool   `json:"isError"`
}

var config = Config{
	BaseDir:     ".",
	CommentsDir: "comments",
	ContentDir:  "content",
	TouchFile:   ".comment"}

// Used for testing
func newComment(w http.ResponseWriter, r *http.Request) {
	t, _ := template.ParseFiles("new.html")
	t.Execute(w, "")
}

func saveComment(w http.ResponseWriter, r *http.Request) {
	// TODO: Allow other content-types for javascript-disabled clients.
	// Maybe render a HTML template?
	w.Header().Set("Content-Type", "application/json")
	response := Response{
		Message: "",
		IsError: true}

	if r.Method == "POST" {
		r.ParseForm()
		_, err := validateComment(r, config.ContentDir)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			response = Response{
				Message: err.Error(),
				IsError: true}
		} else {
			now := time.Now()
			comment := Comment{
				Name:       r.Form.Get("name"),
				Email:      r.Form.Get("email"),
				Website:    r.Form.Get("website"),
				GravatarID: getGravatarId(r.Form.Get("email")),
				IPAddress:  getIPAddress(r),
				PageID:     r.Form.Get("page_id"),
				Body:       processBody(r.Form.Get("body")),
				Timestamp:  now.Format(time.RFC3339)}

			// FIXME: Needs error handling
			jsonData, _ := json.Marshal(comment)
			filename := buildFilename(comment.Email, buildTimestamp(now))
			writePath := filepath.Join(config.CommentsDir, comment.PageID)
			writeCommentToDisk(jsonData, writePath, filename)

			//str := fmt.Sprintf( "%#v", r )

			w.WriteHeader(http.StatusOK)
			response = Response{
				Message: "Thank you for the comment",
				IsError: false}
		}
	} else {
		w.WriteHeader(http.StatusBadRequest)
		response = Response{
			Message: "Must be POST",
			IsError: true}
	}
	// FIXME: Needs error handling
	responseJSON, _ := json.Marshal(response)
	w.Write(responseJSON)
}

// TODO: Check field lengths to prevent too large comment files
// TODO: Error messages should be read from a config file
func validateComment(r *http.Request, contentDir string) (string, error) {
	form := r.Form
	if form.Get("last_name") != "" {
		return "last_name", errors.New("You appear to be a spammer, or your browser auto-fills this form.")
	}
	if regexp.MustCompile(`(?i)[\w\s\d]+`).MatchString(form.Get("name")) != true {
		return "name", errors.New("Name is not valid")
	}
	if regexp.MustCompile(`^[a-z0-9_.\-\+]+@[a-z0-9_.\-\+]+$`).MatchString(form.Get("email")) != true {
		return "email", errors.New("Email address is not valid")
	}
	if form.Get("website") != "" && regexp.MustCompile(`(https?:\/\/)?[a-z0-9\-\.]+`).MatchString(form.Get("website")) != true {
		return "website", errors.New("Website is not valid")
	}
	if regexp.MustCompile(`[a-z0-9\-]+(\/[a-z0-9\-]+)*`).MatchString(form.Get("page_id")) != true {
		return "page_id", errors.New("page_id is not valid")
	}
	if regexp.MustCompile(`[a-z]+`).MatchString(form.Get("content_type")) != true {
		return "content_type", errors.New("Content type is not valid")
	}
	if form.Get("body") == "" {
		return "body", errors.New("You forgot to write the actual comment!")
	}
	if !postExists(form.Get("page_id"), form.Get("content_type"), contentDir) {
		return "page_id", errors.New("Specified post does not exist")
	}
	return "", nil
}

func isValidPath(path string) bool {
	if _, err := os.Stat(path); err == nil {
		return true
	}
	//panic(path)
	return false
}

func postExists(pageID string, extension string, contentDir string) bool {
	return isValidPath(filepath.Join(contentDir, pageID+"."+extension))
}

func processBody(body string) string {
	body = strings.Replace(body, `"""`, "%quote%", -1)
	body = template.HTMLEscapeString(body)
	body = strings.Replace(body, "%quote%", ">", -1)
	return body
}

func getGravatarId(email string) string {
	hash := md5.Sum([]byte(email))
	return hex.EncodeToString(hash[:])
}

func getIPAddress(r *http.Request) string {
	if r.RemoteAddr != "" {
		return r.RemoteAddr
	}
	// FIXME: This should probably look at other fields instead
	return "Unknown"
}

func debugJSON(comment Comment) string {
	str := fmt.Sprintf("%#v", comment)
	return str
}

func buildFilename(email string, timestamp string) string {
	return timestamp + "-" + email + ".json"
}

/*
BuildTimestamp builds a timestamp string to be used as part of the
filename for JSON comment files.
*/
func buildTimestamp(now time.Time) string {
	dateString := fmt.Sprintf("%d-%02d-%02d-%02d%02d%02d",
		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
	return dateString
}

func writeCommentToDisk(JSON []byte, path string, filename string) error {
	dirCreated := false
	// If path doesn't exist, create it
	if !isValidPath(path) {
		os.MkdirAll(path, os.ModePerm)
		dirCreated = true
	}
	fn := filepath.Join(path, filename)
	// Write file to disk
	// FIXME: Needs error handling
	ioutil.WriteFile(fn, JSON, 0600)
	if dirCreated == true && config.TouchFile != "" {
		updateChangeFile(filepath.Join(config.TouchFile))
	}
	return nil
}

func updateChangeFile(touchFile string) {
	// FIXME: Needs error handling
	ioutil.WriteFile(touchFile, []byte("."), 0600)
}

func main() {
	sourceFlag := flag.String("src", ".", "Full path to the sources dir.")
	contentFlag := flag.String("content", "content", "The content directory. Relative to source dir.")
	commentsFlag := flag.String("comments", "comments", "The directory to save comments. Relative to source dir.")
	touchFlag := flag.String("touch", "", "File to update when a new comment directory is created. Relative to source dir. Some watch scripts may require this.")
	addressFlag := flag.String("address", "", "IP address to use for incoming requests. Defaults to any address.")
	portFlag := flag.String("port", "8080", "Port to listen to for incoming requests.")
	pathFlag := flag.String("path", "/comment", "The url path that is used for comment processing.")

	flag.Parse()

	config.BaseDir = *sourceFlag
	config.ContentDir = filepath.Join(*sourceFlag, *contentFlag)
	config.CommentsDir = filepath.Join(*sourceFlag, *commentsFlag)
	if *touchFlag != "" {
		config.TouchFile = filepath.Join(*sourceFlag, *touchFlag)
	} else {
		config.TouchFile = ""
	}

	// FIXME: Add
	serverAddress := *addressFlag + ":" + *portFlag

	http.HandleFunc(*pathFlag, saveComment)
	//http.HandleFunc("/new", newComment );
	http.ListenAndServe(serverAddress, nil)
}
<|endoftext|>


<commit_before>package resource

import (
	"net/http"

	"encoding/json"

	"github.com/ReneGa/tweetcount-microservices/stopwordfilter/gateway"
	"github.com/ReneGa/tweetcount-microservices/stopwordfilter/service"
	"github.com/julienschmidt/httprouter"
)

// TweetWords is a resource serving tweets passed through a stopword filter
type Tweets struct {
	Gateway gateway.Tweets
	Service service.StopWordFilter
}

// GET writes a stream of filtered tweets to the response
func (t *Tweets) GET(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	query := r.URL.Query().Get("q")
	tweets := t.Gateway.Tweets(query)
	filteredTweets := t.Service.Filter(tweets)

	go func() {
		filteredTweets.Stop <- <-w.(http.CloseNotifier).CloseNotify()
	}()

	je := json.NewEncoder(w)
	for tweetWords := range filteredTweets.Data {
		je.Encode(tweetWords)
		w.(http.Flusher).Flush()
	}
}
<commit_msg>format<commit_after>package resource

import (
	"net/http"

	"encoding/json"

	"github.com/ReneGa/tweetcount-microservices/stopwordfilter/gateway"
	"github.com/ReneGa/tweetcount-microservices/stopwordfilter/service"
	"github.com/julienschmidt/httprouter"
)

// Tweets is a resource serving tweets passed through a stopword filter
type Tweets struct {
	Gateway gateway.Tweets
	Service service.StopWordFilter
}

// GET writes a stream of filtered tweets to the response
func (t *Tweets) GET(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
	query := r.URL.Query().Get("q")
	tweets := t.Gateway.Tweets(query)
	filteredTweets := t.Service.Filter(tweets)

	go func() {
		filteredTweets.Stop <- <-w.(http.CloseNotifier).CloseNotify()
	}()

	je := json.NewEncoder(w)
	for tweetWords := range filteredTweets.Data {
		je.Encode(tweetWords)
		w.(http.Flusher).Flush()
	}
}
<|endoftext|>


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

package libsacloud

// Version バージョン
const Version = "2.3.0"
<commit_msg>Bump to v2.4.0<commit_after>// Copyright 2016-2020 The Libsacloud Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package libsacloud

// Version バージョン
const Version = "2.4.0"
<|endoftext|>


<commit_before>package monit

import (
	"net/http"
	"time"

	boshhttp "github.com/cloudfoundry/bosh-utils/http"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	"github.com/pivotal-golang/clock"
)

type monitRetryClient struct {
	delegate               boshhttp.Client
	maxUnavailableAttempts uint
	maxOtherAttempts       uint
	retryDelay             time.Duration
	logger                 boshlog.Logger
}

func NewMonitRetryClient(
	delegate boshhttp.Client,
	maxUnavailableAttempts uint,
	maxOtherAttempts uint,
	retryDelay time.Duration,
	logger boshlog.Logger,
) boshhttp.Client {
	return &monitRetryClient{
		delegate:               delegate,
		maxUnavailableAttempts: maxUnavailableAttempts,
		maxOtherAttempts:       maxOtherAttempts,
		retryDelay:             retryDelay,
		logger:                 logger,
	}
}

func (r *monitRetryClient) Do(req *http.Request) (*http.Response, error) {
	requestRetryable := boshhttp.NewRequestRetryable(req, r.delegate, r.logger)
	timeService := clock.NewClock()
	retryStrategy := NewMonitRetryStrategy(
		requestRetryable,
		r.maxUnavailableAttempts,
		r.maxOtherAttempts,
		r.retryDelay,
		timeService,
	)

	err := retryStrategy.Try()

	return requestRetryable.Response(), err
}
<commit_msg>Update monit_retry_client to reflect bosh-utils changes<commit_after>package monit

import (
	"net/http"
	"time"

	boshhttp "github.com/cloudfoundry/bosh-utils/http"
	boshlog "github.com/cloudfoundry/bosh-utils/logger"
	"github.com/pivotal-golang/clock"
)

type monitRetryClient struct {
	delegate               boshhttp.Client
	maxUnavailableAttempts uint
	maxOtherAttempts       uint
	retryDelay             time.Duration
	logger                 boshlog.Logger
}

func NewMonitRetryClient(
	delegate boshhttp.Client,
	maxUnavailableAttempts uint,
	maxOtherAttempts uint,
	retryDelay time.Duration,
	logger boshlog.Logger,
) boshhttp.Client {
	return &monitRetryClient{
		delegate:               delegate,
		maxUnavailableAttempts: maxUnavailableAttempts,
		maxOtherAttempts:       maxOtherAttempts,
		retryDelay:             retryDelay,
		logger:                 logger,
	}
}

func (r *monitRetryClient) Do(req *http.Request) (*http.Response, error) {
	requestRetryable := boshhttp.NewRequestRetryable(req, r.delegate, r.logger, nil)
	timeService := clock.NewClock()
	retryStrategy := NewMonitRetryStrategy(
		requestRetryable,
		r.maxUnavailableAttempts,
		r.maxOtherAttempts,
		r.retryDelay,
		timeService,
	)

	err := retryStrategy.Try()

	return requestRetryable.Response(), err
}
<|endoftext|>


<commit_before>/*
Copyright © 2014–5 Brad Ackerman.

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 api

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

	"github.com/backerman/evego"
	"github.com/zenazn/goji/web"
)

type queryItem struct {
	Quantity int    `json:"quantity"`
	ItemName string `json:"itemName"`
}

// priceFloat overrides the marshalling of our prices to JSON,
// ensuring we get 00000000000.00 rather than 0000e+00.
type priceFloat float64

func (p priceFloat) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("%.2f", p)), nil
}

type responseItem struct {
	// The EVE database ID of the item.
	ItemID int `json:"itemID"`
	// The name of the item.
	ItemName string `json:"itemName"`
	// The quantity of that item available for purchase in
	// the returned location.
	QuantityAvailable int `json:"quantityAvailable"`
	// The best buy price per unit found in the system/region.
	BestBuy priceFloat `json:"bestBuy"`
	// The best sell price per unit found in the system/region.
	BestSell priceFloat `json:"bestSell"`
	// Details of the best buy order
	BuyInfo orderInfo `json:"buyInfo"`
	// Details of the best sell order
	SellInfo orderInfo `json:"sellInfo"`
}

type orderInfo struct {
	// The quantity available to buy/sell on this order
	Quantity int `json:"quantity"`
	// This order's minimum quantity to buy (1 for sell)
	MinQuantity int `json:"minQuantity"`
	// Where the order is available
	Station station `json:"station"`
	// One of: station, system, region, or a number of jumps
	Within string `json:"within"`
}

func makeRangeString(order *evego.Order) string {
	var rangeStr string
	switch order.JumpRange {
	case evego.BuyRegion:
		rangeStr = "region"
	case evego.BuySystem:
		rangeStr = "system"
	case evego.BuyStation:
		rangeStr = "station"
	default:
		// fix upstream
		if order.NumJumps > 100 {
			rangeStr = "region"
		} else {
			rangeStr = fmt.Sprintf("%d jumps", order.NumJumps)
		}
	}
	return rangeStr
}

func makeOrderInfo(db evego.Database, order *evego.Order) orderInfo {
	return orderInfo{
		Quantity:    order.Quantity,
		MinQuantity: order.MinQuantity,
		// Cheat — it's an outpost if it has no reprocessing efficiency reported.
		Station: stationFromAPI(db, order.Station,
			order.Station.ReprocessingEfficiency == 0.0),
		Within: makeRangeString(order),
	}
}

// summarizeOrders takes as input the orders for a given type in a region/station
// and returns the corresponding responseItem struct filled out.
func summarizeOrders(db evego.Database, orders []evego.Order, dbItem *evego.Item) responseItem {
	var (
		quantity          int
		bestBuy, bestSell float64
		buyInfo, sellInfo orderInfo
	)

	for _, ord := range orders {
		if ord.Type == evego.Buy && ord.Price > bestBuy {
			bestBuy = ord.Price
			buyInfo = makeOrderInfo(db, &ord)
		} else if ord.Type == evego.Sell && (bestSell == 0 || ord.Price < bestSell) {
			bestSell = ord.Price
			sellInfo = makeOrderInfo(db, &ord)
		}
		if ord.Type == evego.Sell {
			quantity += ord.Quantity
		}
	}
	result := responseItem{
		ItemID:            dbItem.ID,
		ItemName:          dbItem.Name,
		QuantityAvailable: quantity,
		BestBuy:           priceFloat(bestBuy),
		BuyInfo:           buyInfo,
		BestSell:          priceFloat(bestSell),
		SellInfo:          sellInfo,
	}
	return result
}

// ItemsMarketValue returns a handler that takes as input a JSON
// array of items and their quantities, plus a specified station
// or region, and computes the items' value.
//
// FIXME: Should return all buy orders within range for the queried system.
func ItemsMarketValue(db evego.Database, mkt evego.Market, xmlAPI evego.XMLAPI) web.HandlerFunc {
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		contentType := r.Header.Get("Content-Type")
		contentType, _, err := mime.ParseMediaType(contentType)
		if err != nil {
			http.Error(w, `{"status": "Error", "error": "Bad request content type"}`,
				http.StatusBadRequest)
			return
		}
		if contentType != "application/json" {
			http.Error(w, `{"status": "Error", "error": "Request must be of type application/json"}`,
				http.StatusUnsupportedMediaType)
			return
		}
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, `{"status": "Error", "error": "Unable to process request body"}`,
				http.StatusBadRequest)
			return
		}
		var req []queryItem
		err = json.Unmarshal(reqBody, &req)
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to process request JSON: %v", err), http.StatusBadRequest)
			w.Write([]byte(`{"status": "Error"}`))
			return
		}
		respItems := make(map[string]responseItem)
		loc := c.URLParams["location"]
		stationIDStr, isStation := c.URLParams["id"]
		var station *evego.Station
		if isStation {
			// Get station / outpost object.
			stationID, _ := strconv.Atoi(stationIDStr)
			station, err = db.StationForID(stationID)
			if err != nil {
				// Not a station; should be an outpost.
				station, err = xmlAPI.OutpostForID(stationID)
				if err != nil {
					http.Error(w, `{"status": "Error", "error": "Unable to identify location"}`,
						http.StatusBadRequest)
					return
				}
			}
		}
		for _, i := range req {
			dbItem, err := db.ItemForName(i.ItemName)
			if err != nil {
				continue
			}
			var (
				item   responseItem
				orders *[]evego.Order
			)
			if isStation {
				orders, err = mkt.OrdersInStation(dbItem, station)
			} else {
				orders, err = mkt.OrdersForItem(dbItem, loc, evego.AllOrders)
			}
			if err != nil {
				http.Error(w, `{"status": "Error", "error": "Unable to retrieve order information"}`,
					http.StatusInternalServerError)
				return
			}
			item = summarizeOrders(db, *orders, dbItem)
			respItems[item.ItemName] = item
		}
		respJSON, _ := json.Marshal(respItems)
		w.Write(respJSON)
	}
}
<commit_msg>Factor out item-looping code.<commit_after>/*
Copyright © 2014–5 Brad Ackerman.

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 api

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

	"github.com/backerman/evego"
	"github.com/zenazn/goji/web"
)

type queryItem struct {
	Quantity int    `json:"quantity"`
	ItemName string `json:"itemName"`
}

// priceFloat overrides the marshalling of our prices to JSON,
// ensuring we get 00000000000.00 rather than 0000e+00.
type priceFloat float64

func (p priceFloat) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("%.2f", p)), nil
}

type responseItem struct {
	// The EVE database ID of the item.
	ItemID int `json:"itemID"`
	// The name of the item.
	ItemName string `json:"itemName"`
	// The quantity of that item available for purchase in
	// the returned location.
	QuantityAvailable int `json:"quantityAvailable"`
	// The best buy price per unit found in the system/region.
	BestBuy priceFloat `json:"bestBuy"`
	// The best sell price per unit found in the system/region.
	BestSell priceFloat `json:"bestSell"`
	// Details of the best buy order
	BuyInfo orderInfo `json:"buyInfo"`
	// Details of the best sell order
	SellInfo orderInfo `json:"sellInfo"`
}

type orderInfo struct {
	// The quantity available to buy/sell on this order
	Quantity int `json:"quantity"`
	// This order's minimum quantity to buy (1 for sell)
	MinQuantity int `json:"minQuantity"`
	// Where the order is available
	Station station `json:"station"`
	// One of: station, system, region, or a number of jumps
	Within string `json:"within"`
}

func makeRangeString(order *evego.Order) string {
	var rangeStr string
	switch order.JumpRange {
	case evego.BuyRegion:
		rangeStr = "region"
	case evego.BuySystem:
		rangeStr = "system"
	case evego.BuyStation:
		rangeStr = "station"
	default:
		// fix upstream
		if order.NumJumps > 100 {
			rangeStr = "region"
		} else {
			rangeStr = fmt.Sprintf("%d jumps", order.NumJumps)
		}
	}
	return rangeStr
}

func makeOrderInfo(db evego.Database, order *evego.Order) orderInfo {
	return orderInfo{
		Quantity:    order.Quantity,
		MinQuantity: order.MinQuantity,
		// Cheat — it's an outpost if it has no reprocessing efficiency reported.
		Station: stationFromAPI(db, order.Station,
			order.Station.ReprocessingEfficiency == 0.0),
		Within: makeRangeString(order),
	}
}

// summarizeOrders takes as input the orders for a given type in a region/station
// and returns the corresponding responseItem struct filled out.
func summarizeOrders(db evego.Database, orders []evego.Order, dbItem *evego.Item) responseItem {
	var (
		quantity          int
		bestBuy, bestSell float64
		buyInfo, sellInfo orderInfo
	)

	for _, ord := range orders {
		if ord.Type == evego.Buy && ord.Price > bestBuy {
			bestBuy = ord.Price
			buyInfo = makeOrderInfo(db, &ord)
		} else if ord.Type == evego.Sell && (bestSell == 0 || ord.Price < bestSell) {
			bestSell = ord.Price
			sellInfo = makeOrderInfo(db, &ord)
		}
		if ord.Type == evego.Sell {
			quantity += ord.Quantity
		}
	}
	result := responseItem{
		ItemID:            dbItem.ID,
		ItemName:          dbItem.Name,
		QuantityAvailable: quantity,
		BestBuy:           priceFloat(bestBuy),
		BuyInfo:           buyInfo,
		BestSell:          priceFloat(bestSell),
		SellInfo:          sellInfo,
	}
	return result
}

func getItemPrices(
	db evego.Database,
	mkt evego.Market,
	req *[]queryItem,
	station *evego.Station,
	loc string) (*map[string]responseItem, error) {
	respItems := make(map[string]responseItem)
	for _, i := range *req {
		dbItem, err := db.ItemForName(i.ItemName)
		if err != nil {
			continue
		}
		var (
			item   responseItem
			orders *[]evego.Order
		)
		if station != nil {
			orders, err = mkt.OrdersInStation(dbItem, station)
		} else {
			orders, err = mkt.OrdersForItem(dbItem, loc, evego.AllOrders)
		}
		if err != nil {
			return nil, fmt.Errorf("Unable to retrieve order information")
		}
		item = summarizeOrders(db, *orders, dbItem)
		respItems[item.ItemName] = item
	}
	return &respItems, nil
}

// ItemsMarketValue returns a handler that takes as input a JSON
// array of items and their quantities, plus a specified station
// or region, and computes the items' value.
//
// FIXME: Should return all buy orders within range for the queried system.
func ItemsMarketValue(db evego.Database, mkt evego.Market, xmlAPI evego.XMLAPI) web.HandlerFunc {
	return func(c web.C, w http.ResponseWriter, r *http.Request) {
		contentType := r.Header.Get("Content-Type")
		contentType, _, err := mime.ParseMediaType(contentType)
		if err != nil {
			http.Error(w, `{"status": "Error", "error": "Bad request content type"}`,
				http.StatusBadRequest)
			return
		}
		if contentType != "application/json" {
			http.Error(w, `{"status": "Error", "error": "Request must be of type application/json"}`,
				http.StatusUnsupportedMediaType)
			return
		}
		reqBody, err := ioutil.ReadAll(r.Body)
		if err != nil {
			http.Error(w, `{"status": "Error", "error": "Unable to process request body"}`,
				http.StatusBadRequest)
			return
		}
		var req []queryItem
		err = json.Unmarshal(reqBody, &req)
		if err != nil {
			http.Error(w, fmt.Sprintf("Unable to process request JSON: %v", err), http.StatusBadRequest)
			w.Write([]byte(`{"status": "Error"}`))
			return
		}
		loc := c.URLParams["location"]
		stationIDStr, isStation := c.URLParams["id"]
		var station *evego.Station
		if isStation {
			// Get station / outpost object.
			stationID, _ := strconv.Atoi(stationIDStr)
			station, err = db.StationForID(stationID)
			if err != nil {
				// Not a station; should be an outpost.
				station, err = xmlAPI.OutpostForID(stationID)
				if err != nil {
					http.Error(w, `{"status": "Error", "error": "Unable to identify location"}`,
						http.StatusBadRequest)
					return
				}
			}
		}
		respItems, err := getItemPrices(db, mkt, &req, station, loc)
		if err != nil {
			http.Error(w, `{"status": "Error", "error": "Unable to retrieve order information"}`,
				http.StatusBadRequest)
			return
		}
		respJSON, _ := json.Marshal(respItems)
		w.Write(respJSON)
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC 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 crane

import (
	"log"

	"net/http"

	"github.com/google/go-containerregistry/pkg/v1/remote"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/spf13/cobra"
)

func init() { Root.AddCommand(NewCmdCopy()) }

func NewCmdCopy() *cobra.Command {
	return &cobra.Command{
		Use:   "copy",
		Short: "Efficiently copy a remote image from src to dst",
		Args:  cobra.ExactArgs(2),
		Run:   doCopy,
	}
}

func doCopy(_ *cobra.Command, args []string) {
	src, dst := args[0], args[1]
	srcRef, err := name.ParseReference(src, name.WeakValidation)
	if err != nil {
		log.Fatalf("parsing reference %q: %v", src, err)
	}
	log.Printf("Pulling %v", srcRef)

	img, err := remote.Image(srcRef, remote.WithAuthFromKeychain(authn.DefaultKeychain))
	if err != nil {
		log.Fatalf("reading image %q: %v", srcRef, err)
	}

	dstRef, err := name.ParseReference(dst, name.WeakValidation)
	if err != nil {
		log.Fatalf("parsing reference %q: %v", dst, err)
	}
	log.Printf("Pushing %v", dstRef)

	dstAuth, err := authn.DefaultKeychain.Resolve(dstRef.Context().Registry)
	if err != nil {
		log.Fatalf("getting creds for %q: %v", dstRef, err)
	}

	wo := remote.WriteOptions{}
	if err := remote.Write(dstRef, img, dstAuth, http.DefaultTransport, wo); err != nil {
		log.Fatalf("writing image %q: %v", dstRef, err)
	}
}
<commit_msg>Add cp alias for `crane copy` (#256)<commit_after>// Copyright 2018 Google LLC 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 crane

import (
	"log"

	"net/http"

	"github.com/google/go-containerregistry/pkg/v1/remote"

	"github.com/google/go-containerregistry/pkg/authn"
	"github.com/google/go-containerregistry/pkg/name"
	"github.com/spf13/cobra"
)

func init() { Root.AddCommand(NewCmdCopy()) }

func NewCmdCopy() *cobra.Command {
	return &cobra.Command{
		Use:     "copy",
		Aliases: []string{"cp"},
		Short:   "Efficiently copy a remote image from src to dst",
		Args:    cobra.ExactArgs(2),
		Run:     doCopy,
	}
}

func doCopy(_ *cobra.Command, args []string) {
	src, dst := args[0], args[1]
	srcRef, err := name.ParseReference(src, name.WeakValidation)
	if err != nil {
		log.Fatalf("parsing reference %q: %v", src, err)
	}
	log.Printf("Pulling %v", srcRef)

	img, err := remote.Image(srcRef, remote.WithAuthFromKeychain(authn.DefaultKeychain))
	if err != nil {
		log.Fatalf("reading image %q: %v", srcRef, err)
	}

	dstRef, err := name.ParseReference(dst, name.WeakValidation)
	if err != nil {
		log.Fatalf("parsing reference %q: %v", dst, err)
	}
	log.Printf("Pushing %v", dstRef)

	dstAuth, err := authn.DefaultKeychain.Resolve(dstRef.Context().Registry)
	if err != nil {
		log.Fatalf("getting creds for %q: %v", dstRef, err)
	}

	wo := remote.WriteOptions{}
	if err := remote.Write(dstRef, img, dstAuth, http.DefaultTransport, wo); err != nil {
		log.Fatalf("writing image %q: %v", dstRef, err)
	}
}
<|endoftext|>


<commit_before>package eval

import (
	"errors"
	"fmt"
	"reflect"
	"unsafe"

	"github.com/xiaq/persistent/hash"
	"src.elv.sh/pkg/eval/errs"
	"src.elv.sh/pkg/eval/vals"
)

var (
	// ErrArgs is thrown when a Go function gets erroneous arguments.
	//
	// TODO(xiaq): Replace this single error type with multiple types that carry
	// richer error information.
	ErrArgs = errors.New("args error")
	// ErrNoOptAccepted is thrown when a Go function that does not accept any
	// options gets passed options.
	ErrNoOptAccepted = errors.New("function does not accept any options")
)

type goFn struct {
	name string
	impl interface{}

	// Type information of impl.

	// If true, pass the frame as a *Frame argument.
	frame bool
	// If true, pass options as a RawOptions argument.
	rawOptions bool
	// If not nil, type of the parameter that gets options via RawOptions.Scan.
	options reflect.Type
	// If not nil, pass the inputs as an Input-typed last argument.
	inputs bool
	// Type of "normal" (non-frame, non-options, non-variadic) arguments.
	normalArgs []reflect.Type
	// If not nil, type of variadic arguments.
	variadicArg reflect.Type
}

// An interface to be implemented by pointers to structs that should hold
// scanned options.
type optionsPtr interface {
	SetDefaultOptions()
}

// Inputs is the type that the last parameter of a Go-native function can take.
// When that is the case, it is a callback to get inputs. See the doc of GoFn
// for details.
type Inputs func(func(interface{}))

var (
	frameType      = reflect.TypeOf((*Frame)(nil))
	rawOptionsType = reflect.TypeOf(RawOptions(nil))
	optionsPtrType = reflect.TypeOf((*optionsPtr)(nil)).Elem()
	inputsType     = reflect.TypeOf(Inputs(nil))
)

// NewGoFn wraps a Go function into an Elvish function using reflection.
//
// Parameters are passed following these rules:
//
// 1. If the first parameter of function has type *Frame, it gets the current
// call frame.
//
// 2. After the potential *Frame argument, the first parameter has type
// RawOptions, it gets a map of option names to their values.
//
// Alternatively, this parameter may be a (non-pointer) struct whose pointer
// type implements a SetDefaultOptions method that takes no arguments and has no
// return value. In this case, a new instance of the struct is constructed, the
// SetDefaultOptions method is called, and any option passed to the Elvish
// function is used to populate the fields of the struct. Field names are mapped
// to option names using strutil.CamelToDashed, unless they have a field tag
// "name", in which case the tag is preferred.
//
// If the function does not declare that it accepts options via either method
// described above, it accepts no options.
//
// 3. If the last parameter is non-variadic and has type Inputs, it represents
// an optional parameter that contains the input to this function. If the
// argument is not supplied, the input channel of the Frame will be used to
// supply the inputs.
//
// 4. Other parameters are converted using vals.ScanToGo.
//
// Return values go to the channel part of the stdout port, after being
// converted using vals.FromGo. If the last return value has type error and is
// not nil, it is turned into an exception and no outputting happens. If the
// last return value is a nil error, it is ignored.
func NewGoFn(name string, impl interface{}) Callable {
	implType := reflect.TypeOf(impl)
	b := &goFn{name: name, impl: impl}

	i := 0
	if i < implType.NumIn() && implType.In(i) == frameType {
		b.frame = true
		i++
	}
	if i < implType.NumIn() && implType.In(i) == rawOptionsType {
		b.rawOptions = true
		i++
	}
	if i < implType.NumIn() && reflect.PtrTo(implType.In(i)).Implements(optionsPtrType) {
		if b.rawOptions {
			panic("Function declares both RawOptions and Options parameters")
		}
		b.options = implType.In(i)
		i++
	}
	for ; i < implType.NumIn(); i++ {
		paramType := implType.In(i)
		if i == implType.NumIn()-1 {
			if implType.IsVariadic() {
				b.variadicArg = paramType.Elem()
				break
			} else if paramType == inputsType {
				b.inputs = true
				break
			}
		}
		b.normalArgs = append(b.normalArgs, paramType)
	}
	return b
}

// Kind returns "fn".
func (*goFn) Kind() string {
	return "fn"
}

// Equal compares identity.
func (b *goFn) Equal(rhs interface{}) bool {
	return b == rhs
}

// Hash hashes the address.
func (b *goFn) Hash() uint32 {
	return hash.Pointer(unsafe.Pointer(b))
}

// Repr returns an opaque representation "<builtin $name>".
func (b *goFn) Repr(int) string {
	return "<builtin " + b.name + ">"
}

// error(nil) is treated as nil by reflect.TypeOf, so we first get the type of
// *error and use Elem to obtain type of error.
var errorType = reflect.TypeOf((*error)(nil)).Elem()

var errNoOptions = errors.New("function does not accept any options")

// Call calls the implementation using reflection.
func (b *goFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error {
	if b.variadicArg != nil {
		if len(args) < len(b.normalArgs) {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(b.normalArgs), ValidHigh: -1, Actual: len(args)}
		}
	} else if b.inputs {
		if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(b.normalArgs), ValidHigh: len(b.normalArgs) + 1, Actual: len(args)}
		}
	} else if len(args) != len(b.normalArgs) {
		return errs.ArityMismatch{
			What:     "arguments here",
			ValidLow: len(b.normalArgs), ValidHigh: len(b.normalArgs), Actual: len(args)}
	}
	if !b.rawOptions && b.options == nil && len(opts) > 0 {
		return ErrNoOptAccepted
	}

	var in []reflect.Value
	if b.frame {
		in = append(in, reflect.ValueOf(f))
	}
	if b.rawOptions {
		in = append(in, reflect.ValueOf(opts))
	}
	if b.options != nil {
		ptrValue := reflect.New(b.options)
		ptr := ptrValue.Interface()
		ptr.(optionsPtr).SetDefaultOptions()
		err := scanOptions(opts, ptr)
		if err != nil {
			return err
		}
		in = append(in, ptrValue.Elem())
	}
	for i, arg := range args {
		var typ reflect.Type
		if i < len(b.normalArgs) {
			typ = b.normalArgs[i]
		} else if b.variadicArg != nil {
			typ = b.variadicArg
		} else if b.inputs {
			break // Handled after the loop
		} else {
			panic("impossible")
		}
		ptr := reflect.New(typ)
		err := vals.ScanToGo(arg, ptr.Interface())
		if err != nil {
			return fmt.Errorf("wrong type of %d'th argument: %v", i+1, err)
		}
		in = append(in, ptr.Elem())
	}

	if b.inputs {
		var inputs Inputs
		if len(args) == len(b.normalArgs) {
			inputs = f.IterateInputs
		} else {
			// Wrap an iterable argument in Inputs.
			iterable := args[len(args)-1]
			if !vals.CanIterate(iterable) {
				return fmt.Errorf("%s cannot be iterated", vals.Kind(iterable))
			}
			inputs = func(f func(interface{})) {
				// CanIterate(iterable) is true
				_ = vals.Iterate(iterable, func(v interface{}) bool {
					f(v)
					return true
				})
			}
		}
		in = append(in, reflect.ValueOf(inputs))
	}

	outs := reflect.ValueOf(b.impl).Call(in)

	if len(outs) > 0 && outs[len(outs)-1].Type() == errorType {
		err := outs[len(outs)-1].Interface()
		if err != nil {
			return err.(error)
		}
		outs = outs[:len(outs)-1]
	}

	for _, out := range outs {
		f.OutputChan() <- vals.FromGo(out.Interface())
	}
	return nil
}
<commit_msg>Improve readability of argument mismatch error message.<commit_after>package eval

import (
	"errors"
	"fmt"
	"reflect"
	"unsafe"

	"github.com/xiaq/persistent/hash"
	"src.elv.sh/pkg/eval/errs"
	"src.elv.sh/pkg/eval/vals"
)

var (
	// ErrArgs is thrown when a Go function gets erroneous arguments.
	//
	// TODO(xiaq): Replace this single error type with multiple types that carry
	// richer error information.
	ErrArgs = errors.New("args error")
	// ErrNoOptAccepted is thrown when a Go function that does not accept any
	// options gets passed options.
	ErrNoOptAccepted = errors.New("function does not accept any options")
)

type goFn struct {
	name string
	impl interface{}

	// Type information of impl.

	// If true, pass the frame as a *Frame argument.
	frame bool
	// If true, pass options as a RawOptions argument.
	rawOptions bool
	// If not nil, type of the parameter that gets options via RawOptions.Scan.
	options reflect.Type
	// If not nil, pass the inputs as an Input-typed last argument.
	inputs bool
	// Type of "normal" (non-frame, non-options, non-variadic) arguments.
	normalArgs []reflect.Type
	// If not nil, type of variadic arguments.
	variadicArg reflect.Type
}

// An interface to be implemented by pointers to structs that should hold
// scanned options.
type optionsPtr interface {
	SetDefaultOptions()
}

// Inputs is the type that the last parameter of a Go-native function can take.
// When that is the case, it is a callback to get inputs. See the doc of GoFn
// for details.
type Inputs func(func(interface{}))

var (
	frameType      = reflect.TypeOf((*Frame)(nil))
	rawOptionsType = reflect.TypeOf(RawOptions(nil))
	optionsPtrType = reflect.TypeOf((*optionsPtr)(nil)).Elem()
	inputsType     = reflect.TypeOf(Inputs(nil))
)

// NewGoFn wraps a Go function into an Elvish function using reflection.
//
// Parameters are passed following these rules:
//
// 1. If the first parameter of function has type *Frame, it gets the current
// call frame.
//
// 2. After the potential *Frame argument, the first parameter has type
// RawOptions, it gets a map of option names to their values.
//
// Alternatively, this parameter may be a (non-pointer) struct whose pointer
// type implements a SetDefaultOptions method that takes no arguments and has no
// return value. In this case, a new instance of the struct is constructed, the
// SetDefaultOptions method is called, and any option passed to the Elvish
// function is used to populate the fields of the struct. Field names are mapped
// to option names using strutil.CamelToDashed, unless they have a field tag
// "name", in which case the tag is preferred.
//
// If the function does not declare that it accepts options via either method
// described above, it accepts no options.
//
// 3. If the last parameter is non-variadic and has type Inputs, it represents
// an optional parameter that contains the input to this function. If the
// argument is not supplied, the input channel of the Frame will be used to
// supply the inputs.
//
// 4. Other parameters are converted using vals.ScanToGo.
//
// Return values go to the channel part of the stdout port, after being
// converted using vals.FromGo. If the last return value has type error and is
// not nil, it is turned into an exception and no outputting happens. If the
// last return value is a nil error, it is ignored.
func NewGoFn(name string, impl interface{}) Callable {
	implType := reflect.TypeOf(impl)
	b := &goFn{name: name, impl: impl}

	i := 0
	if i < implType.NumIn() && implType.In(i) == frameType {
		b.frame = true
		i++
	}
	if i < implType.NumIn() && implType.In(i) == rawOptionsType {
		b.rawOptions = true
		i++
	}
	if i < implType.NumIn() && reflect.PtrTo(implType.In(i)).Implements(optionsPtrType) {
		if b.rawOptions {
			panic("Function declares both RawOptions and Options parameters")
		}
		b.options = implType.In(i)
		i++
	}
	for ; i < implType.NumIn(); i++ {
		paramType := implType.In(i)
		if i == implType.NumIn()-1 {
			if implType.IsVariadic() {
				b.variadicArg = paramType.Elem()
				break
			} else if paramType == inputsType {
				b.inputs = true
				break
			}
		}
		b.normalArgs = append(b.normalArgs, paramType)
	}
	return b
}

// Kind returns "fn".
func (*goFn) Kind() string {
	return "fn"
}

// Equal compares identity.
func (b *goFn) Equal(rhs interface{}) bool {
	return b == rhs
}

// Hash hashes the address.
func (b *goFn) Hash() uint32 {
	return hash.Pointer(unsafe.Pointer(b))
}

// Repr returns an opaque representation "<builtin $name>".
func (b *goFn) Repr(int) string {
	return "<builtin " + b.name + ">"
}

// error(nil) is treated as nil by reflect.TypeOf, so we first get the type of
// *error and use Elem to obtain type of error.
var errorType = reflect.TypeOf((*error)(nil)).Elem()

var errNoOptions = errors.New("function does not accept any options")

// Call calls the implementation using reflection.
func (b *goFn) Call(f *Frame, args []interface{}, opts map[string]interface{}) error {
	if b.variadicArg != nil {
		if len(args) < len(b.normalArgs) {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(b.normalArgs), ValidHigh: -1, Actual: len(args)}
		}
	} else if b.inputs {
		if len(args) != len(b.normalArgs) && len(args) != len(b.normalArgs)+1 {
			return errs.ArityMismatch{
				What:     "arguments here",
				ValidLow: len(b.normalArgs), ValidHigh: len(b.normalArgs) + 1, Actual: len(args)}
		}
	} else if len(args) != len(b.normalArgs) {
		return errs.ArityMismatch{
			What:     "arguments here",
			ValidLow: len(b.normalArgs), ValidHigh: len(b.normalArgs), Actual: len(args)}
	}
	if !b.rawOptions && b.options == nil && len(opts) > 0 {
		return ErrNoOptAccepted
	}

	var in []reflect.Value
	if b.frame {
		in = append(in, reflect.ValueOf(f))
	}
	if b.rawOptions {
		in = append(in, reflect.ValueOf(opts))
	}
	if b.options != nil {
		ptrValue := reflect.New(b.options)
		ptr := ptrValue.Interface()
		ptr.(optionsPtr).SetDefaultOptions()
		err := scanOptions(opts, ptr)
		if err != nil {
			return err
		}
		in = append(in, ptrValue.Elem())
	}
	for i, arg := range args {
		var typ reflect.Type
		if i < len(b.normalArgs) {
			typ = b.normalArgs[i]
		} else if b.variadicArg != nil {
			typ = b.variadicArg
		} else if b.inputs {
			break // Handled after the loop
		} else {
			panic("impossible")
		}
		ptr := reflect.New(typ)
		err := vals.ScanToGo(arg, ptr.Interface())
		if err != nil {
			return fmt.Errorf("wrong type of argument %d: %v", i, err)
		}
		in = append(in, ptr.Elem())
	}

	if b.inputs {
		var inputs Inputs
		if len(args) == len(b.normalArgs) {
			inputs = f.IterateInputs
		} else {
			// Wrap an iterable argument in Inputs.
			iterable := args[len(args)-1]
			if !vals.CanIterate(iterable) {
				return fmt.Errorf("%s cannot be iterated", vals.Kind(iterable))
			}
			inputs = func(f func(interface{})) {
				// CanIterate(iterable) is true
				_ = vals.Iterate(iterable, func(v interface{}) bool {
					f(v)
					return true
				})
			}
		}
		in = append(in, reflect.ValueOf(inputs))
	}

	outs := reflect.ValueOf(b.impl).Call(in)

	if len(outs) > 0 && outs[len(outs)-1].Type() == errorType {
		err := outs[len(outs)-1].Interface()
		if err != nil {
			return err.(error)
		}
		outs = outs[:len(outs)-1]
	}

	for _, out := range outs {
		f.OutputChan() <- vals.FromGo(out.Interface())
	}
	return nil
}
<|endoftext|>


<commit_before>package mongo

import (
	"encoding/json"
	apperrors "github.com/vardius/go-api-boilerplate/pkg/errors"
	"go.mongodb.org/mongo-driver/bson"
)

type JSONRawMessage json.RawMessage

func (m JSONRawMessage) MarshalBSON() ([]byte, error) {
	v, err := bson.Marshal(string(m))
	if err != nil {
		return nil, apperrors.Wrap(err)
	}
	return v, nil
}

func (m *JSONRawMessage) UnmarshalBSON(data []byte) error {
	if len(data) == 0 {
		return nil
	}

	var v string
	if err := bson.Unmarshal(data, &v); err != nil {
		return apperrors.Wrap(err)
	}

	if v != "" {
		*m = JSONRawMessage(v)
	}

	return nil
}
<commit_msg>Update JSONRawMessage<commit_after>package mongo

import (
	"encoding/json"
	"fmt"
	apperrors "github.com/vardius/go-api-boilerplate/pkg/errors"
	"go.mongodb.org/mongo-driver/bson/bsontype"
	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

type JSONRawMessage json.RawMessage

func (m JSONRawMessage) MarshalBSONValue() (bsontype.Type, []byte, error) {
	return bsontype.String, bsoncore.AppendString(nil, string(m)), nil
}

func (m *JSONRawMessage) UnmarshalBSONValue(t bsontype.Type, data []byte) error {
	if len(data) == 0 {
		return nil
	}
	if t != bsontype.String {
		return apperrors.Wrap(fmt.Errorf("invalid type: %s", t))
	}

	str, _, ok := bsoncore.ReadString(data)
	if ok {
		*m = JSONRawMessage(str)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kedge Authors 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 spec

import (
	os_deploy_v1 "github.com/openshift/origin/pkg/apps/apis/apps/v1"
	build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1"
	image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1"
	os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	api_v1 "k8s.io/kubernetes/pkg/api/v1"
	batch_v1 "k8s.io/kubernetes/pkg/apis/batch/v1"
	ext_v1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)

// VolumeClaim is used to define Persistent Volumes for app
// kedgeSpec: io.kedge.VolumeClaim
type VolumeClaim struct {
	// Data from the kubernetes persistent volume claim spec
	// k8s: io.k8s.kubernetes.pkg.api.v1.PersistentVolumeClaimSpec
	api_v1.PersistentVolumeClaimSpec `json:",inline"`
	// Size of persistent volume
	Size string `json:"size"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// ServicePortMod is used to define Kubernetes service's port
// kedgeSpec: io.kedge.ServicePort
type ServicePortMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ServicePort
	api_v1.ServicePort `json:",inline"`
	// Host to create ingress automatically. Endpoint allows specifying an
	// ingress resource in the format '<Host>/<Path>'
	// +optional
	Endpoint string `json:"endpoint"`
}

// ServiceSpecMod is used to define Kubernetes service
// kedgeSpec: io.kedge.ServiceSpec
type ServiceSpecMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ServiceSpec
	api_v1.ServiceSpec `json:",inline"`
	// The list of ports that are exposed by this service. More info:
	// https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
	// ref: io.kedge.ServicePort
	// +optional
	Ports []ServicePortMod `json:"ports,conflicting"`
	// The list of portMappings, where each portMapping allows specifying port,
	// targetPort and protocol in the format '<port>:<targetPort>/<protocol>'
	// +optional
	PortMappings []intstr.IntOrString `json:"portMappings,omitempty"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// IngressSpecMod defines Kubernetes Ingress object
// kedgeSpec: io.kedge.IngressSpec
type IngressSpecMod struct {
	// k8s: io.k8s.kubernetes.pkg.apis.extensions.v1beta1.IngressSpec
	ext_v1beta1.IngressSpec `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// kedgeSpec: io.kedge.RouteSpec
type RouteSpecMod struct {
	// k8s: v1.RouteSpec
	os_route_v1.RouteSpec `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// Container defines a single application container that you want to run within a pod.
// kedgeSpec: io.kedge.ContainerSpec
type Container struct {
	// One common definitions for 'livenessProbe' and 'readinessProbe'
	// this allows to have only one place to define both probes (if they are the same)
	// Periodic probe of container liveness and readiness. Container will be restarted
	// if the probe fails. Cannot be updated. More info:
	// https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
	// ref: io.k8s.kubernetes.pkg.api.v1.Probe
	// +optional
	Health *api_v1.Probe `json:"health,omitempty"`
	// k8s: io.k8s.kubernetes.pkg.api.v1.Container
	api_v1.Container `json:",inline"`
}

// ConfigMapMod holds configuration data for pods to consume.
// kedgeSpec: io.kedge.ConfigMap
type ConfigMapMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ConfigMap
	api_v1.ConfigMap `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	// We need ObjectMeta here, even though it is present in api.ConfigMap
	// because the one upstream has a JSON tag "metadata" due to which it
	// cannot be merged at ConfigMap's root level. The ObjectMeta here
	// overwrites the one in upstream and lets us merge ObjectMeta at
	// ConfigMap's root YAML syntax
	meta_v1.ObjectMeta `json:",inline"`
}

// PodSpecMod is a description of a pod
type PodSpecMod struct {
	// List of containers belonging to the pod. Containers cannot currently be
	// added or removed. There must be at least one container in a Pod. Cannot be updated.
	// ref: io.kedge.ContainerSpec
	Containers []Container `json:"containers,conflicting,omitempty"`
	// List of initialization containers belonging to the pod. Init containers are
	// executed in order prior to containers being started. If any init container
	// fails, the pod is considered to have failed and is handled according to its
	// restartPolicy. The name for an init container or normal container must be
	// unique among all containers.
	// ref: io.kedge.ContainerSpec
	// +optional
	InitContainers []Container `json:"initContainers,conflicting,omitempty"`
	// k8s: io.k8s.kubernetes.pkg.api.v1.PodSpec
	api_v1.PodSpec `json:",inline"`
}

// SecretMod defines secret that will be consumed by application
// kedgeSpec: io.kedge.SecretSpec
type SecretMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.Secret
	api_v1.Secret `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	// We need ObjectMeta here, even though it is present in api.Secret
	// because the one upstream has a JSON tag "metadata" due to which it
	// cannot be merged at Secret's root level. The ObjectMeta here
	// overwrites the one in upstream and lets us merge ObjectMeta at
	// Secret's root YAML syntax
	meta_v1.ObjectMeta `json:",inline"`
}

// ImageStreamSpec defines OpenShift ImageStream Object
// kedgeSpec: io.kedge.ImageStreamSpec
type ImageStreamSpecMod struct {
	// k8s: v1.ImageStreamSpec
	image_v1.ImageStreamSpec `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// BuildConfigSpecMod defines OpenShift BuildConfig object
// kedgeSpec: io.kedge.BuildConfigSpec
type BuildConfigSpecMod struct {
	// k8s: v1.BuildConfigSpec
	build_v1.BuildConfigSpec `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// ControllerFields are the common fields in every controller Kedge supports
type ControllerFields struct {
	// Field to specify the version of application
	// +optional
	Appversion string `json:"appversion,omitempty"`

	Controller string `json:"controller,omitempty"`
	// List of volume that should be mounted on the pod.
	// ref: io.kedge.VolumeClaim
	// +optional
	VolumeClaims []VolumeClaim `json:"volumeClaims,omitempty"`
	// List of configMaps
	// ref: io.kedge.ConfigMap
	// +optional
	ConfigMaps []ConfigMapMod `json:"configMaps,omitempty"`
	// List of Kubernetes Services
	// ref: io.kedge.ServiceSpec
	// +optional
	Services []ServiceSpecMod `json:"services,omitempty"`
	// List of Kubernetes Ingress
	// ref: io.kedge.IngressSpec
	// +optional
	Ingresses []IngressSpecMod `json:"ingresses,omitempty"`
	// List of OpenShift Routes
	// ref: io.kedge.RouteSpec
	// +optional
	Routes []RouteSpecMod `json:"routes,omitempty"`
	// List of Kubernetes Secrets
	// ref: io.kedge.SecretSpec
	// +optional
	Secrets []SecretMod `json:"secrets,omitempty"`
	// List of OpenShift ImageStreams
	// ref: io.kedge.ImageStreamSpec
	// +optional
	ImageStreams []ImageStreamSpecMod `json:"imageStreams,omitempty"`
	// List of OpenShift BuildConfigs
	// ref: io.kedge.BuildConfigSpec
	// +optional
	BuildConfigs []BuildConfigSpecMod `json:"buildConfigs,omitempty"`
	// List of Kubernetes resource files, that can be directly given to Kubernetes
	// +optional
	IncludeResources []string `json:"includeResources,omitempty"`

	// k8s: io.k8s.kubernetes.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
	PodSpecMod         `json:",inline"`
}

type Controller struct {
	Controller string `json:"controller,omitempty"`
}

// DeploymentSpecMod is Kedge's extension of Kubernetes DeploymentSpec and allows
// defining a complete kedge application
// kedgeSpec: io.kedge.DeploymentSpecMod
type DeploymentSpecMod struct {
	ControllerFields `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.apps.v1beta1.DeploymentSpec
	ext_v1beta1.DeploymentSpec `json:",inline"`
}

// JobSpecMod is Kedge's extension of Kubernetes JobSpec and allows
// defining a complete kedge application
// kedgeSpec: io.kedge.JobSpecMod
type JobSpecMod struct {
	ControllerFields `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.batch.v1.JobSpec
	batch_v1.JobSpec `json:",inline"`
	// Optional duration in seconds relative to the startTime that the job may be active
	// before the system tries to terminate it; value must be positive integer
	// This only sets ActiveDeadlineSeconds in JobSpec, not PodSpec
	// +optional
	ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,conflicting,omitempty"`
}

// Ochestrator: OpenShift
// DeploymentConfigSpecMod is Kedge's extension of OpenShift DeploymentConfig in order to define and allow
// a complete kedge app based on OpenShift
// kedgeSpec: io.kedge.DeploymentConfigSpecMod
type DeploymentConfigSpecMod struct {
	ControllerFields                  `json:",inline"`
	os_deploy_v1.DeploymentConfigSpec `json:",inline"`

	// Replicas is the number of desired replicas.
	// We need to add this field here despite being in v1.DeploymentConfigSpec
	// because the one in v1.DeploymentConfigSpec has the type as int32, which
	// does not let us check if the set value is 0, is it set by the user or not
	// since this field's value with default to 0. We need the default value as
	// 1. Hence, we need to check if the user has set it or not.Making the type
	// *int32 helps us do it, followed by substitution later on.
	Replicas *int32 `json:"replicas,omitempty"`
}
<commit_msg>Fix references to ObjectMeta<commit_after>/*
Copyright 2017 The Kedge Authors 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 spec

import (
	os_deploy_v1 "github.com/openshift/origin/pkg/apps/apis/apps/v1"
	build_v1 "github.com/openshift/origin/pkg/build/apis/build/v1"
	image_v1 "github.com/openshift/origin/pkg/image/apis/image/v1"
	os_route_v1 "github.com/openshift/origin/pkg/route/apis/route/v1"
	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	api_v1 "k8s.io/kubernetes/pkg/api/v1"
	batch_v1 "k8s.io/kubernetes/pkg/apis/batch/v1"
	ext_v1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
)

// VolumeClaim is used to define Persistent Volumes for app
// kedgeSpec: io.kedge.VolumeClaim
type VolumeClaim struct {
	// Data from the kubernetes persistent volume claim spec
	// k8s: io.k8s.kubernetes.pkg.api.v1.PersistentVolumeClaimSpec
	api_v1.PersistentVolumeClaimSpec `json:",inline"`
	// Size of persistent volume
	Size string `json:"size"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// ServicePortMod is used to define Kubernetes service's port
// kedgeSpec: io.kedge.ServicePort
type ServicePortMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ServicePort
	api_v1.ServicePort `json:",inline"`
	// Host to create ingress automatically. Endpoint allows specifying an
	// ingress resource in the format '<Host>/<Path>'
	// +optional
	Endpoint string `json:"endpoint"`
}

// ServiceSpecMod is used to define Kubernetes service
// kedgeSpec: io.kedge.ServiceSpec
type ServiceSpecMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ServiceSpec
	api_v1.ServiceSpec `json:",inline"`
	// The list of ports that are exposed by this service. More info:
	// https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
	// ref: io.kedge.ServicePort
	// +optional
	Ports []ServicePortMod `json:"ports,conflicting"`
	// The list of portMappings, where each portMapping allows specifying port,
	// targetPort and protocol in the format '<port>:<targetPort>/<protocol>'
	// +optional
	PortMappings []intstr.IntOrString `json:"portMappings,omitempty"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// IngressSpecMod defines Kubernetes Ingress object
// kedgeSpec: io.kedge.IngressSpec
type IngressSpecMod struct {
	// k8s: io.k8s.kubernetes.pkg.apis.extensions.v1beta1.IngressSpec
	ext_v1beta1.IngressSpec `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// kedgeSpec: io.kedge.RouteSpec
type RouteSpecMod struct {
	// k8s: v1.RouteSpec
	os_route_v1.RouteSpec `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// Container defines a single application container that you want to run within a pod.
// kedgeSpec: io.kedge.ContainerSpec
type Container struct {
	// One common definitions for 'livenessProbe' and 'readinessProbe'
	// this allows to have only one place to define both probes (if they are the same)
	// Periodic probe of container liveness and readiness. Container will be restarted
	// if the probe fails. Cannot be updated. More info:
	// https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
	// ref: io.k8s.kubernetes.pkg.api.v1.Probe
	// +optional
	Health *api_v1.Probe `json:"health,omitempty"`
	// k8s: io.k8s.kubernetes.pkg.api.v1.Container
	api_v1.Container `json:",inline"`
}

// ConfigMapMod holds configuration data for pods to consume.
// kedgeSpec: io.kedge.ConfigMap
type ConfigMapMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.ConfigMap
	api_v1.ConfigMap `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	// We need ObjectMeta here, even though it is present in api.ConfigMap
	// because the one upstream has a JSON tag "metadata" due to which it
	// cannot be merged at ConfigMap's root level. The ObjectMeta here
	// overwrites the one in upstream and lets us merge ObjectMeta at
	// ConfigMap's root YAML syntax
	meta_v1.ObjectMeta `json:",inline"`
}

// PodSpecMod is a description of a pod
type PodSpecMod struct {
	// List of containers belonging to the pod. Containers cannot currently be
	// added or removed. There must be at least one container in a Pod. Cannot be updated.
	// ref: io.kedge.ContainerSpec
	Containers []Container `json:"containers,conflicting,omitempty"`
	// List of initialization containers belonging to the pod. Init containers are
	// executed in order prior to containers being started. If any init container
	// fails, the pod is considered to have failed and is handled according to its
	// restartPolicy. The name for an init container or normal container must be
	// unique among all containers.
	// ref: io.kedge.ContainerSpec
	// +optional
	InitContainers []Container `json:"initContainers,conflicting,omitempty"`
	// k8s: io.k8s.kubernetes.pkg.api.v1.PodSpec
	api_v1.PodSpec `json:",inline"`
}

// SecretMod defines secret that will be consumed by application
// kedgeSpec: io.kedge.SecretSpec
type SecretMod struct {
	// k8s: io.k8s.kubernetes.pkg.api.v1.Secret
	api_v1.Secret `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	// We need ObjectMeta here, even though it is present in api.Secret
	// because the one upstream has a JSON tag "metadata" due to which it
	// cannot be merged at Secret's root level. The ObjectMeta here
	// overwrites the one in upstream and lets us merge ObjectMeta at
	// Secret's root YAML syntax
	meta_v1.ObjectMeta `json:",inline"`
}

// ImageStreamSpec defines OpenShift ImageStream Object
// kedgeSpec: io.kedge.ImageStreamSpec
type ImageStreamSpecMod struct {
	// k8s: v1.ImageStreamSpec
	image_v1.ImageStreamSpec `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// BuildConfigSpecMod defines OpenShift BuildConfig object
// kedgeSpec: io.kedge.BuildConfigSpec
type BuildConfigSpecMod struct {
	// k8s: v1.BuildConfigSpec
	build_v1.BuildConfigSpec `json:",inline"`
	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
}

// ControllerFields are the common fields in every controller Kedge supports
type ControllerFields struct {
	// Field to specify the version of application
	// +optional
	Appversion string `json:"appversion,omitempty"`

	Controller string `json:"controller,omitempty"`
	// List of volume that should be mounted on the pod.
	// ref: io.kedge.VolumeClaim
	// +optional
	VolumeClaims []VolumeClaim `json:"volumeClaims,omitempty"`
	// List of configMaps
	// ref: io.kedge.ConfigMap
	// +optional
	ConfigMaps []ConfigMapMod `json:"configMaps,omitempty"`
	// List of Kubernetes Services
	// ref: io.kedge.ServiceSpec
	// +optional
	Services []ServiceSpecMod `json:"services,omitempty"`
	// List of Kubernetes Ingress
	// ref: io.kedge.IngressSpec
	// +optional
	Ingresses []IngressSpecMod `json:"ingresses,omitempty"`
	// List of OpenShift Routes
	// ref: io.kedge.RouteSpec
	// +optional
	Routes []RouteSpecMod `json:"routes,omitempty"`
	// List of Kubernetes Secrets
	// ref: io.kedge.SecretSpec
	// +optional
	Secrets []SecretMod `json:"secrets,omitempty"`
	// List of OpenShift ImageStreams
	// ref: io.kedge.ImageStreamSpec
	// +optional
	ImageStreams []ImageStreamSpecMod `json:"imageStreams,omitempty"`
	// List of OpenShift BuildConfigs
	// ref: io.kedge.BuildConfigSpec
	// +optional
	BuildConfigs []BuildConfigSpecMod `json:"buildConfigs,omitempty"`
	// List of Kubernetes resource files, that can be directly given to Kubernetes
	// +optional
	IncludeResources []string `json:"includeResources,omitempty"`

	// k8s: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
	meta_v1.ObjectMeta `json:",inline"`
	PodSpecMod         `json:",inline"`
}

type Controller struct {
	Controller string `json:"controller,omitempty"`
}

// DeploymentSpecMod is Kedge's extension of Kubernetes DeploymentSpec and allows
// defining a complete kedge application
// kedgeSpec: io.kedge.DeploymentSpecMod
type DeploymentSpecMod struct {
	ControllerFields `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.apps.v1beta1.DeploymentSpec
	ext_v1beta1.DeploymentSpec `json:",inline"`
}

// JobSpecMod is Kedge's extension of Kubernetes JobSpec and allows
// defining a complete kedge application
// kedgeSpec: io.kedge.JobSpecMod
type JobSpecMod struct {
	ControllerFields `json:",inline"`
	// k8s: io.k8s.kubernetes.pkg.apis.batch.v1.JobSpec
	batch_v1.JobSpec `json:",inline"`
	// Optional duration in seconds relative to the startTime that the job may be active
	// before the system tries to terminate it; value must be positive integer
	// This only sets ActiveDeadlineSeconds in JobSpec, not PodSpec
	// +optional
	ActiveDeadlineSeconds *int64 `json:"activeDeadlineSeconds,conflicting,omitempty"`
}

// Ochestrator: OpenShift
// DeploymentConfigSpecMod is Kedge's extension of OpenShift DeploymentConfig in order to define and allow
// a complete kedge app based on OpenShift
// kedgeSpec: io.kedge.DeploymentConfigSpecMod
type DeploymentConfigSpecMod struct {
	ControllerFields                  `json:",inline"`
	os_deploy_v1.DeploymentConfigSpec `json:",inline"`

	// Replicas is the number of desired replicas.
	// We need to add this field here despite being in v1.DeploymentConfigSpec
	// because the one in v1.DeploymentConfigSpec has the type as int32, which
	// does not let us check if the set value is 0, is it set by the user or not
	// since this field's value with default to 0. We need the default value as
	// 1. Hence, we need to check if the user has set it or not.Making the type
	// *int32 helps us do it, followed by substitution later on.
	Replicas *int32 `json:"replicas,omitempty"`
}
<|endoftext|>


<commit_before>// Copyright 2018 the LinuxBoot 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 uefi

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"sort"
)

// FlashSignature is the sequence of bytes that a Flash image is expected to
// start with.
var (
	FlashSignature = []byte{0x5a, 0xa5, 0xf0, 0x0f}
)

const (
	// FlashDescriptorLength represents the size of the descriptor region.
	FlashDescriptorLength = 0x1000
	// FlashSignatureLength represents the size of the flash signature
	FlashSignatureLength = 4
)

// FlashDescriptor is the main structure that represents an Intel Flash Descriptor.
type FlashDescriptor struct {
	// Holds the raw buffer
	buf                []byte
	DescriptorMapStart uint
	RegionStart        uint
	MasterStart        uint
	DescriptorMap      *FlashDescriptorMap
	Region             *FlashRegionSection
	Master             *FlashMasterSection

	//Metadata for extraction and recovery
	ExtractPath string
}

// FindSignature searches for an Intel flash signature.
func FindSignature(buf []byte) (int, error) {
	if bytes.Equal(buf[16:16+FlashSignatureLength], FlashSignature) {
		// 16 + 4 since the descriptor starts after the signature
		return 20, nil
	}
	if bytes.Equal(buf[:FlashSignatureLength], FlashSignature) {
		// + 4 since the descriptor starts after the signature
		return FlashSignatureLength, nil
	}
	return -1, fmt.Errorf("Flash signature not found: first 20 bytes are:\n%s",
		hex.Dump(buf[:20]))
}

// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fd *FlashDescriptor) Buf() []byte {
	return fd.buf
}

// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fd *FlashDescriptor) SetBuf(buf []byte) {
	fd.buf = buf
}

// Apply calls the visitor on the FlashDescriptor.
func (fd *FlashDescriptor) Apply(v Visitor) error {
	return v.Visit(fd)
}

// ApplyChildren calls the visitor on each child node of FlashDescriptor.
func (fd *FlashDescriptor) ApplyChildren(v Visitor) error {
	return nil
}

// ParseFlashDescriptor parses the ifd from the buffer
func (fd *FlashDescriptor) ParseFlashDescriptor() error {
	if buflen := len(fd.buf); buflen != FlashDescriptorLength {
		return fmt.Errorf("flash descriptor length not %#x, was %#x", FlashDescriptorLength, buflen)
	}

	descriptorMapStart, err := FindSignature(fd.buf)
	if err != nil {
		return err
	}
	fd.DescriptorMapStart = uint(descriptorMapStart)

	// Descriptor Map
	desc, err := NewFlashDescriptorMap(fd.buf[fd.DescriptorMapStart : fd.DescriptorMapStart+FlashDescriptorMapSize])
	if err != nil {
		return err
	}
	fd.DescriptorMap = desc

	// Region
	fd.RegionStart = uint(fd.DescriptorMap.RegionBase) * 0x10
	region, err := NewFlashRegionSection(fd.buf[fd.RegionStart : fd.RegionStart+uint(FlashRegionSectionSize)])
	if err != nil {
		return err
	}
	fd.Region = region

	// Master
	fd.MasterStart = uint(fd.DescriptorMap.MasterBase) * 0x10
	master, err := NewFlashMasterSection(fd.buf[fd.MasterStart : fd.MasterStart+uint(FlashMasterSectionSize)])
	if err != nil {
		return err
	}
	fd.Master = master

	return nil
}

// FlashImage is the main structure that represents an Intel Flash image. It
// implements the Firmware interface.
type FlashImage struct {
	// Holds the raw buffer
	buf []byte
	// Holds the Flash Descriptor
	IFD FlashDescriptor
	// Actual regions
	Regions []Region `json:",omitempty"`

	// Metadata for extraction and recovery
	ExtractPath string
	FlashSize   uint64
}

// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (f *FlashImage) Buf() []byte {
	return f.buf
}

// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (f *FlashImage) SetBuf(buf []byte) {
	f.buf = buf
}

// Apply calls the visitor on the FlashImage.
func (f *FlashImage) Apply(v Visitor) error {
	return v.Visit(f)
}

// ApplyChildren calls the visitor on each child node of FlashImage.
func (f *FlashImage) ApplyChildren(v Visitor) error {
	if err := f.IFD.Apply(v); err != nil {
		return err
	}
	for _, r := range f.Regions {
		if err := r.Apply(v); err != nil {
			return err
		}
	}
	return nil
}

// IsPCH returns whether the flash image has the more recent PCH format, or not.
// PCH images have the first 16 bytes reserved, and the 4-bytes signature starts
// immediately after. Older images (ICH8/9/10) have the signature at the
// beginning.
// TODO: Check this. What if we have the signature in both places? I feel like the check
// should be IsICH because I expect the ICH to override PCH if the signature exists in 0:4
// since in that case 16:20 should be data. If that's the case, FindSignature needs to
// be fixed as well
func (f *FlashImage) IsPCH() bool {
	return bytes.Equal(f.buf[16:16+len(FlashSignature)], FlashSignature)
}

// FindSignature looks for the Intel flash signature, and returns its offset
// from the start of the image. The PCH images are located at offset 16, while
// in ICH8/9/10 they start at 0. If no signature is found, it returns -1.
func (f *FlashImage) FindSignature() (int, error) {
	return FindSignature(f.buf)
}

func (f *FlashImage) String() string {
	return fmt.Sprintf("FlashImage{Size=%v, Descriptor=%v, Region=%v, Master=%v}",
		len(f.buf),
		f.IFD.DescriptorMap.String(),
		f.IFD.Region.String(),
		f.IFD.Master.String(),
	)
}

func (f *FlashImage) fillRegionGaps() error {
	// Search for gaps and fill in with unknown regions
	offset := uint64(FlashDescriptorLength)
	var newRegions []Region
	for _, r := range f.Regions {
		nextBase := uint64(r.FlashRegion().BaseOffset())
		if nextBase < offset {
			// Something is wrong, overlapping regions
			// TODO: print a better error message describing what it overlaps with
			return fmt.Errorf("overlapping regions! region type %s overlaps with the previous region",
				r.Type().String())
		}
		if nextBase > offset {
			// There is a gap, create an unknown region
			tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
				Limit: uint16(nextBase/RegionBlockSize) - 1}
			newRegions = append(newRegions, &RawRegion{buf: f.buf[offset:nextBase],
				flashRegion: tempFR,
				RegionType:  RegionTypeUnknown})
		}
		offset = uint64(r.FlashRegion().EndOffset())
		newRegions = append(newRegions, r)
	}
	// check for the last region
	if offset != f.FlashSize {
		tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
			Limit: uint16(f.FlashSize/RegionBlockSize) - 1}
		newRegions = append(newRegions, &RawRegion{buf: f.buf[offset:f.FlashSize],
			flashRegion: tempFR,
			RegionType:  RegionTypeUnknown})
	}
	f.Regions = newRegions
	return nil
}

// NewFlashImage tries to create a FlashImage structure, and returns a FlashImage
// and an error if any. This only works with images that operate in Descriptor
// mode.
func NewFlashImage(buf []byte) (*FlashImage, error) {
	if len(buf) < FlashDescriptorMapSize {
		return nil, fmt.Errorf("Flash Descriptor Map size too small: expected %v bytes, got %v",
			FlashDescriptorMapSize,
			len(buf),
		)
	}
	f := FlashImage{buf: buf, FlashSize: uint64(len(buf))}
	f.IFD.buf = buf[:FlashDescriptorLength]
	if err := f.IFD.ParseFlashDescriptor(); err != nil {
		return nil, err
	}

	// FlashRegions
	frs := f.IFD.Region.FlashRegions

	// BIOS region has to be valid
	if !frs[RegionTypeBIOS].Valid() {
		return nil, fmt.Errorf("no BIOS region: invalid region parameters %v", frs[RegionTypeBIOS])
	}

	nr := int(f.IFD.DescriptorMap.NumberOfRegions)
	// Parse all the regions
	for i, fr := range frs {
		// Parse only a smaller number of regions if number of regions isn't 0
		// Number of regions is deprecated in newer IFDs and is just 0, older IFDs report
		// the number of regions and have falsely "valid" regions after that number.
		if nr != 0 && i >= nr {
			break
		}
		if !fr.Valid() {
			continue
		}
		if o := uint64(fr.BaseOffset()); o > f.FlashSize {
			return nil, fmt.Errorf("region %d, %v out of bounds: BaseOffset %#x, Flash size %#x",
				i, fr, o, f.FlashSize)
		}
		if o := uint64(fr.EndOffset()); o > f.FlashSize {
			return nil, fmt.Errorf("region %d, %v out of bounds: EndOffset %#x, Flash size %#x",
				i, fr, o, f.FlashSize)
		}
		if c, ok := regionConstructors[FlashRegionType(i)]; ok {
			r, err := c(buf[fr.BaseOffset():fr.EndOffset()], &frs[i], FlashRegionType(i))
			if err != nil {
				return nil, err
			}
			f.Regions = append(f.Regions, r)
		}
	}

	// Sort the regions by offset so we can look for gaps
	sort.Slice(f.Regions, func(i, j int) bool {
		return f.Regions[i].FlashRegion().Base < f.Regions[j].FlashRegion().Base
	})

	if err := f.fillRegionGaps(); err != nil {
		return nil, err
	}
	return &f, nil
}
<commit_msg>uefi: Warn and skip regions that are outside the firmware<commit_after>// Copyright 2018 the LinuxBoot 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 uefi

import (
	"bytes"
	"encoding/hex"
	"fmt"
	"sort"
)

// FlashSignature is the sequence of bytes that a Flash image is expected to
// start with.
var (
	FlashSignature = []byte{0x5a, 0xa5, 0xf0, 0x0f}
)

const (
	// FlashDescriptorLength represents the size of the descriptor region.
	FlashDescriptorLength = 0x1000
	// FlashSignatureLength represents the size of the flash signature
	FlashSignatureLength = 4
)

// FlashDescriptor is the main structure that represents an Intel Flash Descriptor.
type FlashDescriptor struct {
	// Holds the raw buffer
	buf                []byte
	DescriptorMapStart uint
	RegionStart        uint
	MasterStart        uint
	DescriptorMap      *FlashDescriptorMap
	Region             *FlashRegionSection
	Master             *FlashMasterSection

	//Metadata for extraction and recovery
	ExtractPath string
}

// FindSignature searches for an Intel flash signature.
func FindSignature(buf []byte) (int, error) {
	if bytes.Equal(buf[16:16+FlashSignatureLength], FlashSignature) {
		// 16 + 4 since the descriptor starts after the signature
		return 20, nil
	}
	if bytes.Equal(buf[:FlashSignatureLength], FlashSignature) {
		// + 4 since the descriptor starts after the signature
		return FlashSignatureLength, nil
	}
	return -1, fmt.Errorf("Flash signature not found: first 20 bytes are:\n%s",
		hex.Dump(buf[:20]))
}

// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fd *FlashDescriptor) Buf() []byte {
	return fd.buf
}

// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (fd *FlashDescriptor) SetBuf(buf []byte) {
	fd.buf = buf
}

// Apply calls the visitor on the FlashDescriptor.
func (fd *FlashDescriptor) Apply(v Visitor) error {
	return v.Visit(fd)
}

// ApplyChildren calls the visitor on each child node of FlashDescriptor.
func (fd *FlashDescriptor) ApplyChildren(v Visitor) error {
	return nil
}

// ParseFlashDescriptor parses the ifd from the buffer
func (fd *FlashDescriptor) ParseFlashDescriptor() error {
	if buflen := len(fd.buf); buflen != FlashDescriptorLength {
		return fmt.Errorf("flash descriptor length not %#x, was %#x", FlashDescriptorLength, buflen)
	}

	descriptorMapStart, err := FindSignature(fd.buf)
	if err != nil {
		return err
	}
	fd.DescriptorMapStart = uint(descriptorMapStart)

	// Descriptor Map
	desc, err := NewFlashDescriptorMap(fd.buf[fd.DescriptorMapStart : fd.DescriptorMapStart+FlashDescriptorMapSize])
	if err != nil {
		return err
	}
	fd.DescriptorMap = desc

	// Region
	fd.RegionStart = uint(fd.DescriptorMap.RegionBase) * 0x10
	region, err := NewFlashRegionSection(fd.buf[fd.RegionStart : fd.RegionStart+uint(FlashRegionSectionSize)])
	if err != nil {
		return err
	}
	fd.Region = region

	// Master
	fd.MasterStart = uint(fd.DescriptorMap.MasterBase) * 0x10
	master, err := NewFlashMasterSection(fd.buf[fd.MasterStart : fd.MasterStart+uint(FlashMasterSectionSize)])
	if err != nil {
		return err
	}
	fd.Master = master

	return nil
}

// FlashImage is the main structure that represents an Intel Flash image. It
// implements the Firmware interface.
type FlashImage struct {
	// Holds the raw buffer
	buf []byte
	// Holds the Flash Descriptor
	IFD FlashDescriptor
	// Actual regions
	Regions []Region `json:",omitempty"`

	// Metadata for extraction and recovery
	ExtractPath string
	FlashSize   uint64
}

// Buf returns the buffer.
// Used mostly for things interacting with the Firmware interface.
func (f *FlashImage) Buf() []byte {
	return f.buf
}

// SetBuf sets the buffer.
// Used mostly for things interacting with the Firmware interface.
func (f *FlashImage) SetBuf(buf []byte) {
	f.buf = buf
}

// Apply calls the visitor on the FlashImage.
func (f *FlashImage) Apply(v Visitor) error {
	return v.Visit(f)
}

// ApplyChildren calls the visitor on each child node of FlashImage.
func (f *FlashImage) ApplyChildren(v Visitor) error {
	if err := f.IFD.Apply(v); err != nil {
		return err
	}
	for _, r := range f.Regions {
		if err := r.Apply(v); err != nil {
			return err
		}
	}
	return nil
}

// IsPCH returns whether the flash image has the more recent PCH format, or not.
// PCH images have the first 16 bytes reserved, and the 4-bytes signature starts
// immediately after. Older images (ICH8/9/10) have the signature at the
// beginning.
// TODO: Check this. What if we have the signature in both places? I feel like the check
// should be IsICH because I expect the ICH to override PCH if the signature exists in 0:4
// since in that case 16:20 should be data. If that's the case, FindSignature needs to
// be fixed as well
func (f *FlashImage) IsPCH() bool {
	return bytes.Equal(f.buf[16:16+len(FlashSignature)], FlashSignature)
}

// FindSignature looks for the Intel flash signature, and returns its offset
// from the start of the image. The PCH images are located at offset 16, while
// in ICH8/9/10 they start at 0. If no signature is found, it returns -1.
func (f *FlashImage) FindSignature() (int, error) {
	return FindSignature(f.buf)
}

func (f *FlashImage) String() string {
	return fmt.Sprintf("FlashImage{Size=%v, Descriptor=%v, Region=%v, Master=%v}",
		len(f.buf),
		f.IFD.DescriptorMap.String(),
		f.IFD.Region.String(),
		f.IFD.Master.String(),
	)
}

func (f *FlashImage) fillRegionGaps() error {
	// Search for gaps and fill in with unknown regions
	offset := uint64(FlashDescriptorLength)
	var newRegions []Region
	for _, r := range f.Regions {
		nextBase := uint64(r.FlashRegion().BaseOffset())
		if nextBase < offset {
			// Something is wrong, overlapping regions
			// TODO: print a better error message describing what it overlaps with
			return fmt.Errorf("overlapping regions! region type %s overlaps with the previous region",
				r.Type().String())
		}
		if nextBase > offset {
			// There is a gap, create an unknown region
			tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
				Limit: uint16(nextBase/RegionBlockSize) - 1}
			newRegions = append(newRegions, &RawRegion{buf: f.buf[offset:nextBase],
				flashRegion: tempFR,
				RegionType:  RegionTypeUnknown})
		}
		offset = uint64(r.FlashRegion().EndOffset())
		newRegions = append(newRegions, r)
	}
	// check for the last region
	if offset != f.FlashSize {
		tempFR := &FlashRegion{Base: uint16(offset / RegionBlockSize),
			Limit: uint16(f.FlashSize/RegionBlockSize) - 1}
		newRegions = append(newRegions, &RawRegion{buf: f.buf[offset:f.FlashSize],
			flashRegion: tempFR,
			RegionType:  RegionTypeUnknown})
	}
	f.Regions = newRegions
	return nil
}

// NewFlashImage tries to create a FlashImage structure, and returns a FlashImage
// and an error if any. This only works with images that operate in Descriptor
// mode.
func NewFlashImage(buf []byte) (*FlashImage, error) {
	if len(buf) < FlashDescriptorMapSize {
		return nil, fmt.Errorf("Flash Descriptor Map size too small: expected %v bytes, got %v",
			FlashDescriptorMapSize,
			len(buf),
		)
	}
	f := FlashImage{buf: buf, FlashSize: uint64(len(buf))}
	f.IFD.buf = buf[:FlashDescriptorLength]
	if err := f.IFD.ParseFlashDescriptor(); err != nil {
		return nil, err
	}

	// FlashRegions
	frs := f.IFD.Region.FlashRegions

	// BIOS region has to be valid
	if !frs[RegionTypeBIOS].Valid() {
		return nil, fmt.Errorf("no BIOS region: invalid region parameters %v", frs[RegionTypeBIOS])
	}

	nr := int(f.IFD.DescriptorMap.NumberOfRegions)
	// Parse all the regions
	for i, fr := range frs {
		// Parse only a smaller number of regions if number of regions isn't 0
		// Number of regions is deprecated in newer IFDs and is just 0, older IFDs report
		// the number of regions and have falsely "valid" regions after that number.
		if nr != 0 && i >= nr {
			break
		}
		if !fr.Valid() {
			continue
		}
		if o := uint64(fr.BaseOffset()); o >= f.FlashSize {
			fmt.Printf("region %s (%d, %v) out of bounds: BaseOffset %#x, Flash size %#x, skipping...\n",
				flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize)
			continue
		}
		if o := uint64(fr.EndOffset()); o > f.FlashSize {
			fmt.Printf("region %s (%d, %v) out of bounds: EndOffset %#x, Flash size %#x, skipping...\n",
				flashRegionTypeNames[FlashRegionType(i)], i, fr, o, f.FlashSize)
			continue
		}
		if c, ok := regionConstructors[FlashRegionType(i)]; ok {
			r, err := c(buf[fr.BaseOffset():fr.EndOffset()], &frs[i], FlashRegionType(i))
			if err != nil {
				return nil, err
			}
			f.Regions = append(f.Regions, r)
		}
	}

	// Sort the regions by offset so we can look for gaps
	sort.Slice(f.Regions, func(i, j int) bool {
		return f.Regions[i].FlashRegion().Base < f.Regions[j].FlashRegion().Base
	})

	if err := f.fillRegionGaps(); err != nil {
		return nil, err
	}
	return &f, nil
}
<|endoftext|>


<commit_before>package treehash

import (
	"bytes"
	"crypto/sha256"
	"hash"
)

const ChunkSize = 1024 * 1024

type digest struct {
	buf *bytes.Buffer
}

// New creates a new hash.Hash computing the Tree Hash checksum.
func New() hash.Hash {
	return &digest{
		new(bytes.Buffer),
	}
}

func (d *digest) Size() int { return sha256.Size }

func (d *digest) BlockSize() int { return sha256.BlockSize }

func (d *digest) Reset() {
	d.buf = new(bytes.Buffer)
}

func chunk(buffer *bytes.Buffer) [][]byte {
	sha := sha256.New()
	chunks := [][]byte{}
	chunk := make([]byte, ChunkSize)
	for {
		read, err := buffer.Read(chunk)
		if err != nil {
			break // It never returns an error.
		}
		sha.Reset()
		sha.Write(chunk[:read])
		chunks = append(chunks, sha.Sum(nil))
	}
	return chunks
}

func compute(chunks [][]byte) []byte {
	sha := sha256.New()
	previousLevel := chunks
	for {
		if len(previousLevel) == 1 {
			break
		}

		length := len(previousLevel) / 2
		if len(previousLevel)%2 != 0 {
			length++
		}

		currentLevel := make([][]byte, length)
		for i, j := 0, 0; i < len(previousLevel); i, j = i+2, j+1 {
			if len(previousLevel)-i > 1 {
				sha.Reset()
				sha.Write(previousLevel[i])
				sha.Write(previousLevel[i+1])
				currentLevel[j] = sha.Sum(nil)
			} else {
				currentLevel[j] = previousLevel[i]
			}
		}

		previousLevel = currentLevel
	}
	return previousLevel[0]
}

func (d *digest) Sum(in []byte) []byte {
	chunks := chunk(d.buf)
	return compute(chunks)
}

func (d *digest) Write(p []byte) (n int, err error) {
	return d.buf.Write(p)
}
<commit_msg>make const private<commit_after>package treehash

import (
	"bytes"
	"crypto/sha256"
	"hash"
)

const chunkSize = 1024 * 1024

type digest struct {
	buf *bytes.Buffer
}

// New creates a new hash.Hash computing the Tree Hash checksum.
func New() hash.Hash {
	return &digest{
		new(bytes.Buffer),
	}
}

func (d *digest) Size() int { return sha256.Size }

func (d *digest) BlockSize() int { return sha256.BlockSize }

func (d *digest) Reset() {
	d.buf = new(bytes.Buffer)
}

func chunk(buffer *bytes.Buffer) [][]byte {
	sha := sha256.New()
	chunks := [][]byte{}
	chunk := make([]byte, chunkSize)
	for {
		read, err := buffer.Read(chunk)
		if err != nil {
			break // It never returns an error.
		}
		sha.Reset()
		sha.Write(chunk[:read])
		chunks = append(chunks, sha.Sum(nil))
	}
	return chunks
}

func compute(chunks [][]byte) []byte {
	sha := sha256.New()
	previousLevel := chunks
	for {
		if len(previousLevel) == 1 {
			break
		}

		length := len(previousLevel) / 2
		if len(previousLevel)%2 != 0 {
			length++
		}

		currentLevel := make([][]byte, length)
		for i, j := 0, 0; i < len(previousLevel); i, j = i+2, j+1 {
			if len(previousLevel)-i > 1 {
				sha.Reset()
				sha.Write(previousLevel[i])
				sha.Write(previousLevel[i+1])
				currentLevel[j] = sha.Sum(nil)
			} else {
				currentLevel[j] = previousLevel[i]
			}
		}

		previousLevel = currentLevel
	}
	return previousLevel[0]
}

func (d *digest) Sum(in []byte) []byte {
	chunks := chunk(d.buf)
	return compute(chunks)
}

func (d *digest) Write(p []byte) (n int, err error) {
	return d.buf.Write(p)
}
<|endoftext|>