<commit_before>package middleware

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/helpers"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/pmylund/go-cache"
	"github.com/vifino/golua/lua"
	"github.com/vifino/luar"
	"net/http"
	"time"
)

// Cache
var kvstore *cache.Cache
var cbc *cache.Cache
var cfe *cache.Cache
var LDumper *lua.State

func cacheDump(file string) (string, error, bool) {
	data_tmp, found := cbc.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err, false
		}
		res, err := bcdump(data)
		if err != nil {
			return "", err, true
		}
		cbc.Set(file, res, cache.DefaultExpiration)
		return res, nil, false
	} else {
		//debug("Using Bytecode-cache for " + file)
		return data_tmp.(string), nil, false
	}
}
func bcdump(data string) (string, error) {
	if LDumper.LoadString(data) != 0 {
		return "", errors.New(LDumper.ToString(-1))
	}
	defer LDumper.Pop(1)
	return LDumper.FDump(), nil
}

// FS
var filesystem http.FileSystem

func fileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := physfs.Exists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}

// Preloader/Starter
var jobs int
var Preloaded chan *lua.State

func Preloader() {
	Preloaded = make(chan *lua.State, jobs)
	for {
		//fmt.Println("preloading")
		L := luar.Init()
		Bind(L)
		err := L.DoString(glue.MainGlue())
		if err != nil {
			panic(err)
		}
		err = L.DoString(glue.RouteGlue())
		if err != nil {
			panic(err)
		}
		Preloaded <- L
	}
}
func GetInstance() *lua.State {
	//fmt.Println("grabbing instance")
	L := <-Preloaded
	//fmt.Println("Done")
	return L
}

// Init
func Init(j int, cfe_new *cache.Cache, kvstore_new *cache.Cache) {
	cfe = cfe_new
	kvstore = kvstore_new
	jobs = j
	filesystem = physfs.FileSystem()
	cbc = cache.New(5*time.Minute, 30*time.Second) // Initialize cache with 5 minute lifetime and purge every 30 seconds
	LDumper = luar.Init()
}

// PHP-like lua scripts
func Lua() func(*gin.Context) {
	//LDumper := luar.Init()
	return func(context *gin.Context) {
		file := context.Request.URL.Path
		if fileExists(file) {
			//fmt.Println("start")
			L := GetInstance()
			//fmt.Println("after start")
			defer scheduler.Add(func() {
				L.Close()
			})
			//fmt.Println("after after start")
			BindContext(L, context)
			//fmt.Println("before cache")
			code, err, lerr := cacheDump(file)
			//fmt.Println("after cache")
			if err != nil {
				if lerr == false {
					context.Next()
					return
				} else {
					helpers.HTMLString(context, http.StatusInternalServerError, `
					<title>Syntax Error in `+context.Request.URL.Path+`</title>

Syntax Error in file `+context.Request.URL.Path+`:

						`+string(err.Error())+`

					`)
					context.Abort()
					return
				}
			}
			//fmt.Println("before loadbuffer")
			L.LoadBuffer(code, len(code), file) // This shouldn't error, was checked earlier.
			if L.Pcall(0, 0, 0) != 0 {          // != 0 means error in execution
				helpers.HTMLString(context, http.StatusInternalServerError, `
				<title>Runtime Error in `+context.Request.URL.Path+`</title>

Runtime Error in file `+context.Request.URL.Path+`:

					`+L.ToString(-1)+`

				`)
				context.Abort()
				return
			}
			/*L.DoString("return CONTENT_TO_RETURN")
			v := luar.CopyTableToMap(L, nil, -1)
			m := v.(map[string]interface{})
			i := int(m["code"].(float64))
			if err != nil {
				i = http.StatusOK
			}*/
			//helpers.HTMLString(context, i, m["content"].(string))
		} else {
			context.Next()
		}
	}
}

// Route creation by lua
func DLR_NS(bcode string, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) {
	/*code, err := bcdump(code)
	if err != nil {
		return func(*gin.Context) {}, err
	}*/
	return func(context *gin.Context) {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		defer scheduler.Add(func() {
			L.Close()
		})
		BindContext(L, context)
		//fmt.Println("before loadbuffer")
		/*if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Syntax Error in `+context.Request.URL.Path+`</title>

Syntax Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}*/
		L.LoadBuffer(bcode, len(bcode), "route")
		if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Runtime Error on `+context.Request.URL.Path+`</title>

Runtime Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}
	}, nil
}
func DLR_RUS(bcode string, instances int, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) { // Same as above, but reuses states. Much faster. Higher memory use though, because more states.
	insts := instances
	if instances < 0 {
		insts = 2
		if jobs/2 > 1 {
			insts = jobs
		}
	}
	schan := make(chan *lua.State, insts)
	for i := 0; i < jobs/2; i++ {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			return func(context *gin.Context) {}, errors.New(L.ToString(-1))
		}
		L.PushValue(-1)
		schan <- L
	}
	return func(context *gin.Context) {
		L := <-schan
		BindContext(L, context)
		if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Runtime Error on `+context.Request.URL.Path+`</title>

Runtime Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}
		L.PushValue(-1)
		schan <- L
	}, nil
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

func DLRWS_RUS(bcode string, instances int, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) { // Same as above, but for websockets. Not working because?!
	insts := instances
	if instances < 0 {
		insts = 2
		if jobs/2 > 1 {
			insts = jobs
		}
	}
	schan := make(chan *lua.State, insts)
	for i := 0; i < jobs/2; i++ {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			return func(context *gin.Context) {}, errors.New(L.ToString(-1))
		}
		L.PushValue(-1)
		schan <- L
	}
	return func(context *gin.Context) {
		L := <-schan
		BindContext(L, context)
		r := wshandler(context.Writer, context.Request, L)
		schan <- L
		if r {
			context.Abort()
		}
	}, nil
}

func wshandler(w http.ResponseWriter, r *http.Request, L *lua.State) bool {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return false // silent error.
	}
	luar.Register(L, "ws", luar.Map{
		"BinaryMessage": websocket.BinaryMessage,
		"TextMessage":   websocket.TextMessage,
		//"read":          conn.ReadMessage,
		//"send":          conn.SendMessage,
		"read": (func() (int, string, error) {
			messageType, p, err := conn.ReadMessage()
			if err != nil {
				return -1, "", err
			}
			return messageType, string(p), nil
		}),
		"send": (func(t int, cnt string) error {
			return conn.WriteMessage(t, []byte(cnt))
		}),
	})
	if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
		fmt.Println("Websocket error: " + L.ToString(-1))
		return false
	}
	L.PushValue(-1)
	return true
}
<commit_msg>Blargh.<commit_after>package middleware

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/DeedleFake/Go-PhysicsFS/physfs"
	"github.com/carbonsrv/carbon/modules/glue"
	"github.com/carbonsrv/carbon/modules/helpers"
	"github.com/carbonsrv/carbon/modules/scheduler"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/pmylund/go-cache"
	"github.com/vifino/golua/lua"
	"github.com/vifino/luar"
	"net/http"
	"time"
)

// Cache
var kvstore *cache.Cache
var cbc *cache.Cache
var cfe *cache.Cache
var LDumper *lua.State

func cacheDump(file string) (string, error, bool) {
	data_tmp, found := cbc.Get(file)
	if found == false {
		data, err := fileRead(file)
		if err != nil {
			return "", err, false
		}
		res, err := bcdump(data)
		if err != nil {
			return "", err, true
		}
		cbc.Set(file, res, cache.DefaultExpiration)
		return res, nil, false
	} else {
		//debug("Using Bytecode-cache for " + file)
		return data_tmp.(string), nil, false
	}
}
func bcdump(data string) (string, error) {
	if LDumper.LoadString(data) != 0 {
		return "", errors.New(LDumper.ToString(-1))
	}
	defer LDumper.Pop(1)
	return LDumper.FDump(), nil
}

// FS
var filesystem http.FileSystem

func fileExists(file string) bool {
	data_tmp, found := cfe.Get(file)
	if found == false {
		exists := physfs.Exists(file)
		cfe.Set(file, exists, cache.DefaultExpiration)
		return exists
	} else {
		return data_tmp.(bool)
	}
}

func fileRead(file string) (string, error) {
	f, err := filesystem.Open(file)
	defer f.Close()
	if err != nil {
		return "", err
	}
	fi, err := f.Stat()
	if err != nil {
		return "", err
	}
	r := bufio.NewReader(f)
	buf := make([]byte, fi.Size())
	_, err = r.Read(buf)
	if err != nil {
		return "", err
	}
	return string(buf), err
}

// Preloader/Starter
var jobs int
var Preloaded chan *lua.State

func Preloader() {
	Preloaded = make(chan *lua.State, jobs)
	for {
		//fmt.Println("preloading")
		L := luar.Init()
		Bind(L)
		err := L.DoString(glue.MainGlue())
		if err != nil {
			panic(err)
		}
		err = L.DoString(glue.RouteGlue())
		if err != nil {
			panic(err)
		}
		Preloaded <- L
	}
}
func GetInstance() *lua.State {
	//fmt.Println("grabbing instance")
	L := <-Preloaded
	//fmt.Println("Done")
	return L
}

// Init
func Init(j int, cfe_new *cache.Cache, kvstore_new *cache.Cache) {
	cfe = cfe_new
	kvstore = kvstore_new
	jobs = j
	filesystem = physfs.FileSystem()
	cbc = cache.New(5*time.Minute, 30*time.Second) // Initialize cache with 5 minute lifetime and purge every 30 seconds
	LDumper = luar.Init()
}

// PHP-like lua scripts
func Lua() func(*gin.Context) {
	//LDumper := luar.Init()
	return func(context *gin.Context) {
		file := context.Request.URL.Path
		if fileExists(file) {
			//fmt.Println("start")
			L := GetInstance()
			//fmt.Println("after start")
			defer scheduler.Add(func() {
				L.Close()
			})
			//fmt.Println("after after start")
			BindContext(L, context)
			//fmt.Println("before cache")
			code, err, lerr := cacheDump(file)
			//fmt.Println("after cache")
			if err != nil {
				if lerr == false {
					context.Next()
					return
				} else {
					helpers.HTMLString(context, http.StatusInternalServerError, `
					<title>Syntax Error in `+context.Request.URL.Path+`</title>

Syntax Error in file `+context.Request.URL.Path+`:

						`+string(err.Error())+`

					`)
					context.Abort()
					return
				}
			}
			//fmt.Println("before loadbuffer")
			L.LoadBuffer(code, len(code), file) // This shouldn't error, was checked earlier.
			if L.Pcall(0, 0, 0) != 0 {          // != 0 means error in execution
				helpers.HTMLString(context, http.StatusInternalServerError, `
				<title>Runtime Error in `+context.Request.URL.Path+`</title>

Runtime Error in file `+context.Request.URL.Path+`:

					`+L.ToString(-1)+`

				`)
				context.Abort()
				return
			}
			/*L.DoString("return CONTENT_TO_RETURN")
			v := luar.CopyTableToMap(L, nil, -1)
			m := v.(map[string]interface{})
			i := int(m["code"].(float64))
			if err != nil {
				i = http.StatusOK
			}*/
			//helpers.HTMLString(context, i, m["content"].(string))
		} else {
			context.Next()
		}
	}
}

// Route creation by lua
func DLR_NS(bcode string, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) {
	/*code, err := bcdump(code)
	if err != nil {
		return func(*gin.Context) {}, err
	}*/
	return func(context *gin.Context) {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		defer scheduler.Add(func() {
			L.Close()
		})
		BindContext(L, context)
		//fmt.Println("before loadbuffer")
		/*if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Syntax Error in `+context.Request.URL.Path+`</title>

Syntax Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}*/
		L.LoadBuffer(bcode, len(bcode), "route")
		if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Runtime Error on `+context.Request.URL.Path+`</title>

Runtime Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}
	}, nil
}
func DLR_RUS(bcode string, instances int, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) { // Same as above, but reuses states. Much faster. Higher memory use though, because more states.
	insts := instances
	if instances < 0 {
		insts = 2
		if jobs/2 > 1 {
			insts = jobs
		}
	}
	schan := make(chan *lua.State, insts)
	for i := 0; i < jobs/2; i++ {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			return func(context *gin.Context) {}, errors.New(L.ToString(-1))
		}
		L.PushValue(-1)
		schan <- L
	}
	return func(context *gin.Context) {
		L := <-schan
		BindContext(L, context)
		if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
			helpers.HTMLString(context, http.StatusInternalServerError, `
			<title>Runtime Error on `+context.Request.URL.Path+`</title>

Runtime Error in Lua Route on `+context.Request.URL.Path+`:

				`+L.ToString(-1)+`

			`)
			context.Abort()
			return
		}
		L.PushValue(-1)
		schan <- L
	}, nil
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
}

func DLRWS_RUS(bcode string, instances int, dobind bool, vals map[string]interface{}) (func(*gin.Context), error) { // Same as above, but for websockets. Not working because?!
	insts := instances
	if instances < 0 {
		insts = 2
		if jobs/2 > 1 {
			insts = jobs
		}
	}
	schan := make(chan *lua.State, insts)
	for i := 0; i < jobs/2; i++ {
		L := GetInstance()
		if dobind {
			luar.Register(L, "", vals)
		}
		if L.LoadBuffer(bcode, len(bcode), "route") != 0 {
			return func(context *gin.Context) {}, errors.New(L.ToString(-1))
		}
		L.PushValue(-1)
		schan <- L
	}
	return func(context *gin.Context) {
		L := <-schan
		BindContext(L, context)
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			fmt.Println("Websocket error: " + err.Error()) // silent error.
		}
		luar.Register(L, "ws", luar.Map{
			"BinaryMessage": websocket.BinaryMessage,
			"TextMessage":   websocket.TextMessage,
			//"read":          conn.ReadMessage,
			//"send":          conn.SendMessage,
			"read": (func() (int, string, error) {
				messageType, p, err := conn.ReadMessage()
				if err != nil {
					return -1, "", err
				}
				return messageType, string(p), nil
			}),
			"send": (func(t int, cnt string) error {
				return conn.WriteMessage(t, []byte(cnt))
			}),
		})
		if L.Pcall(0, 0, 0) != 0 { // != 0 means error in execution
			fmt.Println("Websocket error: " + L.ToString(-1))
			context.Abort()
			return
		}
		L.PushValue(-1)
		schan <- L
	}, nil
}
<|endoftext|>


<commit_before>package onbuild

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/golang/glog"
	"github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/build"
	"github.com/openshift/source-to-image/pkg/build/strategies/sti"
	"github.com/openshift/source-to-image/pkg/docker"
	"github.com/openshift/source-to-image/pkg/git"
	"github.com/openshift/source-to-image/pkg/scripts"
	"github.com/openshift/source-to-image/pkg/tar"
	"github.com/openshift/source-to-image/pkg/util"
)

// OnBuild strategy executes the simple Docker build in case the image does not
// support STI scripts but has ONBUILD instructions recorded.
type OnBuild struct {
	docker  docker.Docker
	git     git.Git
	fs      util.FileSystem
	tar     tar.Tar
	source  build.SourceHandler
	garbage build.Cleaner
}

type onBuildSourceHandler struct {
	build.Downloader
	build.Preparer
}

// New returns a new instance of OnBuild builder
func New(config *api.Config) (*OnBuild, error) {
	dockerHandler, err := docker.New(config.DockerConfig, config.PullAuthentication)
	if err != nil {
		return nil, err
	}
	b := &OnBuild{
		docker: dockerHandler,
		git:    git.New(),
		fs:     util.NewFileSystem(),
		tar:    tar.New(),
	}
	// Use STI Prepare() and download the 'run' script optionally.
	s, err := sti.New(config)
	s.SetScripts([]string{}, []string{api.Assemble, api.Run})

	b.source = onBuildSourceHandler{
		&git.Clone{b.git, b.fs},
		s,
	}
	b.garbage = &build.DefaultCleaner{b.fs, b.docker}
	return b, nil
}

// SourceTar produces a tar archive containing application source and stream it
func (b *OnBuild) SourceTar(config *api.Config) (io.ReadCloser, error) {
	uploadDir := filepath.Join(config.WorkingDir, "upload", "src")
	tarFileName, err := b.tar.CreateTarFile(config.WorkingDir, uploadDir)
	if err != nil {
		return nil, err
	}
	return b.fs.Open(tarFileName)
}

// Build executes the ONBUILD kind of build
func (b *OnBuild) Build(config *api.Config) (*api.Result, error) {
	glog.V(2).Info("Preparing the source code for build")
	// Change the installation directory for this config to store scripts inside
	// the application root directory.
	if err := b.source.Prepare(config); err != nil {
		return nil, err
	}

	// If necessary, copy the STI scripts into application root directory
	b.copySTIScripts(config)

	glog.V(2).Info("Creating application Dockerfile")
	if err := b.CreateDockerfile(config); err != nil {
		return nil, err
	}

	glog.V(2).Info("Creating application source code image")
	tarStream, err := b.SourceTar(config)
	if err != nil {
		return nil, err
	}
	defer tarStream.Close()

	opts := docker.BuildImageOptions{
		Name:   config.Tag,
		Stdin:  tarStream,
		Stdout: os.Stdout,
	}

	glog.V(2).Info("Building the application source")
	if err := b.docker.BuildImage(opts); err != nil {
		return nil, err
	}

	glog.V(2).Info("Cleaning up temporary containers")
	b.garbage.Cleanup(config)

	imageID, err := b.docker.GetImageID(opts.Name)
	if err != nil {
		return nil, err
	}

	return &api.Result{
		Success:    true,
		WorkingDir: config.WorkingDir,
		ImageID:    imageID,
	}, nil
}

// CreateDockerfile creates the ONBUILD Dockerfile
func (b *OnBuild) CreateDockerfile(config *api.Config) error {
	buffer := bytes.Buffer{}
	uploadDir := filepath.Join(config.WorkingDir, "upload", "src")
	buffer.WriteString(fmt.Sprintf("FROM %s\n", config.BuilderImage))
	entrypoint, err := GuessEntrypoint(b.fs, uploadDir)
	if err != nil {
		return err
	}
	env, err := scripts.GetEnvironment(config)
	if err != nil {
		glog.V(1).Infof("Environment: %v", err)
	} else {
		buffer.WriteString(scripts.ConvertEnvironmentToDocker(env))
	}
	// If there is an assemble script present, run it as part of the build process
	// as the last thing.
	if b.hasAssembleScript(config) {
		buffer.WriteString(fmt.Sprintf("RUN sh assemble\n"))
	}
	// FIXME: This assumes that the WORKDIR is set to the application source root
	//        directory.
	buffer.WriteString(fmt.Sprintf(`ENTRYPOINT ["./%s"]`+"\n", entrypoint))
	return b.fs.WriteFile(filepath.Join(uploadDir, "Dockerfile"), buffer.Bytes())
}

func (b *OnBuild) copySTIScripts(config *api.Config) {
	scriptsPath := filepath.Join(config.WorkingDir, "upload", "scripts")
	sourcePath := filepath.Join(config.WorkingDir, "upload", "src")
	if _, err := b.fs.Stat(filepath.Join(scriptsPath, api.Run)); err == nil {
		glog.V(3).Infof("Found STI 'run' script, copying to application source dir")
		b.fs.Copy(filepath.Join(scriptsPath, api.Run), sourcePath)
	}
	if _, err := b.fs.Stat(filepath.Join(scriptsPath, api.Assemble)); err == nil {
		glog.V(3).Infof("Found STI 'assemble' script, copying to application source dir")
		b.fs.Copy(filepath.Join(scriptsPath, api.Assemble), sourcePath)
	}
}

// hasAssembleScript checks if the the assemble script is available
func (b *OnBuild) hasAssembleScript(config *api.Config) bool {
	assemblePath := filepath.Join(config.WorkingDir, "upload", "src", "assemble")
	_, err := b.fs.Stat(assemblePath)
	return err == nil
}
<commit_msg>Do not attempt to get image ID when no output image specified<commit_after>package onbuild

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/golang/glog"
	"github.com/openshift/source-to-image/pkg/api"
	"github.com/openshift/source-to-image/pkg/build"
	"github.com/openshift/source-to-image/pkg/build/strategies/sti"
	"github.com/openshift/source-to-image/pkg/docker"
	"github.com/openshift/source-to-image/pkg/git"
	"github.com/openshift/source-to-image/pkg/scripts"
	"github.com/openshift/source-to-image/pkg/tar"
	"github.com/openshift/source-to-image/pkg/util"
)

// OnBuild strategy executes the simple Docker build in case the image does not
// support STI scripts but has ONBUILD instructions recorded.
type OnBuild struct {
	docker  docker.Docker
	git     git.Git
	fs      util.FileSystem
	tar     tar.Tar
	source  build.SourceHandler
	garbage build.Cleaner
}

type onBuildSourceHandler struct {
	build.Downloader
	build.Preparer
}

// New returns a new instance of OnBuild builder
func New(config *api.Config) (*OnBuild, error) {
	dockerHandler, err := docker.New(config.DockerConfig, config.PullAuthentication)
	if err != nil {
		return nil, err
	}
	b := &OnBuild{
		docker: dockerHandler,
		git:    git.New(),
		fs:     util.NewFileSystem(),
		tar:    tar.New(),
	}
	// Use STI Prepare() and download the 'run' script optionally.
	s, err := sti.New(config)
	s.SetScripts([]string{}, []string{api.Assemble, api.Run})

	b.source = onBuildSourceHandler{
		&git.Clone{b.git, b.fs},
		s,
	}
	b.garbage = &build.DefaultCleaner{b.fs, b.docker}
	return b, nil
}

// SourceTar produces a tar archive containing application source and stream it
func (b *OnBuild) SourceTar(config *api.Config) (io.ReadCloser, error) {
	uploadDir := filepath.Join(config.WorkingDir, "upload", "src")
	tarFileName, err := b.tar.CreateTarFile(config.WorkingDir, uploadDir)
	if err != nil {
		return nil, err
	}
	return b.fs.Open(tarFileName)
}

// Build executes the ONBUILD kind of build
func (b *OnBuild) Build(config *api.Config) (*api.Result, error) {
	glog.V(2).Info("Preparing the source code for build")
	// Change the installation directory for this config to store scripts inside
	// the application root directory.
	if err := b.source.Prepare(config); err != nil {
		return nil, err
	}

	// If necessary, copy the STI scripts into application root directory
	b.copySTIScripts(config)

	glog.V(2).Info("Creating application Dockerfile")
	if err := b.CreateDockerfile(config); err != nil {
		return nil, err
	}

	glog.V(2).Info("Creating application source code image")
	tarStream, err := b.SourceTar(config)
	if err != nil {
		return nil, err
	}
	defer tarStream.Close()

	opts := docker.BuildImageOptions{
		Name:   config.Tag,
		Stdin:  tarStream,
		Stdout: os.Stdout,
	}

	glog.V(2).Info("Building the application source")
	if err := b.docker.BuildImage(opts); err != nil {
		return nil, err
	}

	glog.V(2).Info("Cleaning up temporary containers")
	b.garbage.Cleanup(config)

	var imageID string

	if len(opts.Name) > 0 {
		if imageID, err = b.docker.GetImageID(opts.Name); err != nil {
			return nil, err
		}
	}

	return &api.Result{
		Success:    true,
		WorkingDir: config.WorkingDir,
		ImageID:    imageID,
	}, nil
}

// CreateDockerfile creates the ONBUILD Dockerfile
func (b *OnBuild) CreateDockerfile(config *api.Config) error {
	buffer := bytes.Buffer{}
	uploadDir := filepath.Join(config.WorkingDir, "upload", "src")
	buffer.WriteString(fmt.Sprintf("FROM %s\n", config.BuilderImage))
	entrypoint, err := GuessEntrypoint(b.fs, uploadDir)
	if err != nil {
		return err
	}
	env, err := scripts.GetEnvironment(config)
	if err != nil {
		glog.V(1).Infof("Environment: %v", err)
	} else {
		buffer.WriteString(scripts.ConvertEnvironmentToDocker(env))
	}
	// If there is an assemble script present, run it as part of the build process
	// as the last thing.
	if b.hasAssembleScript(config) {
		buffer.WriteString(fmt.Sprintf("RUN sh assemble\n"))
	}
	// FIXME: This assumes that the WORKDIR is set to the application source root
	//        directory.
	buffer.WriteString(fmt.Sprintf(`ENTRYPOINT ["./%s"]`+"\n", entrypoint))
	return b.fs.WriteFile(filepath.Join(uploadDir, "Dockerfile"), buffer.Bytes())
}

func (b *OnBuild) copySTIScripts(config *api.Config) {
	scriptsPath := filepath.Join(config.WorkingDir, "upload", "scripts")
	sourcePath := filepath.Join(config.WorkingDir, "upload", "src")
	if _, err := b.fs.Stat(filepath.Join(scriptsPath, api.Run)); err == nil {
		glog.V(3).Infof("Found STI 'run' script, copying to application source dir")
		b.fs.Copy(filepath.Join(scriptsPath, api.Run), sourcePath)
	}
	if _, err := b.fs.Stat(filepath.Join(scriptsPath, api.Assemble)); err == nil {
		glog.V(3).Infof("Found STI 'assemble' script, copying to application source dir")
		b.fs.Copy(filepath.Join(scriptsPath, api.Assemble), sourcePath)
	}
}

// hasAssembleScript checks if the the assemble script is available
func (b *OnBuild) hasAssembleScript(config *api.Config) bool {
	assemblePath := filepath.Join(config.WorkingDir, "upload", "src", "assemble")
	_, err := b.fs.Stat(assemblePath)
	return err == nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative 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 kafka

import (
	"context"
	"fmt"
	"math"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/sets"

	"go.uber.org/zap"

	"github.com/Shopify/sarama"
	"knative.dev/pkg/logging"
)

var mutex sync.Mutex

type ClusterAdminFactory func() (sarama.ClusterAdmin, error)

type AdminClient interface {
	// ListConsumerGroups Lists the consumer groups
	ListConsumerGroups() ([]string, error)
}

// AdminClientManager manages a ClusterAdmin connection and recreates one when needed
// it is made to overcome https://github.com/Shopify/sarama/issues/1162
type AdminClientManager struct {
	logger       *zap.SugaredLogger
	adminFactory ClusterAdminFactory
	clusterAdmin sarama.ClusterAdmin
}

func NewAdminClient(ctx context.Context, caFactory ClusterAdminFactory) (AdminClient, error) {
	logger := logging.FromContext(ctx)
	logger.Info("Creating a new AdminClient")
	kafkaClusterAdmin, err := caFactory()
	if err != nil {
		logger.Errorw("error while creating ClusterAdmin", zap.Error(err))
		return nil, err
	}
	return &AdminClientManager{
		logger:       logger,
		adminFactory: caFactory,
		clusterAdmin: kafkaClusterAdmin,
	}, nil
}

// ListConsumerGroups Returns a list of the consumer groups.
//
// In the occasion of errors, there will be a retry with an exponential backoff.
// Due to a known issue in Sarama ClusterAdmin https://github.com/Shopify/sarama/issues/1162,
// a new ClusterAdmin will be created with every retry until the call succeeds or
// the timeout is reached.
func (c *AdminClientManager) ListConsumerGroups() ([]string, error) {
	c.logger.Info("Attempting to list consumer group")
	mutex.Lock()
	defer mutex.Unlock()
	r := 0
	// This gives us around ~13min of exponential backoff
	max := 13
	cgsMap, err := c.clusterAdmin.ListConsumerGroups()
	for err != nil && r <= max {
		// There's on error, let's retry and presume a new ClusterAdmin can fix it

		// Calculate incremental delay following this https://docs.aws.amazon.com/general/latest/gr/api-retries.html
		t := int(math.Pow(2, float64(r)) * 100)
		d := time.Duration(t) * time.Millisecond
		c.logger.Errorw("listing consumer group failed. Refreshing the ClusterAdmin and retrying.",
			zap.Error(err),
			zap.Duration("retry after", d),
			zap.Int("Retry attempt", r),
			zap.Int("Max retries", max),
		)
		time.Sleep(d)

		// let's reconnect and try again
		c.clusterAdmin, err = c.adminFactory()
		r += 1
		if err != nil {
			// skip this attempt
			continue
		}
		cgsMap, err = c.clusterAdmin.ListConsumerGroups()
	}

	if r > max {
		return nil, fmt.Errorf("failed to refresh the culster admin and retry: %v", err)
	}

	return sets.StringKeySet(cgsMap).List(), nil
}
<commit_msg>Removing verbose info logger (#256)<commit_after>/*
Copyright 2020 The Knative 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 kafka

import (
	"context"
	"fmt"
	"math"
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/sets"

	"go.uber.org/zap"

	"github.com/Shopify/sarama"
	"knative.dev/pkg/logging"
)

var mutex sync.Mutex

type ClusterAdminFactory func() (sarama.ClusterAdmin, error)

type AdminClient interface {
	// ListConsumerGroups Lists the consumer groups
	ListConsumerGroups() ([]string, error)
}

// AdminClientManager manages a ClusterAdmin connection and recreates one when needed
// it is made to overcome https://github.com/Shopify/sarama/issues/1162
type AdminClientManager struct {
	logger       *zap.SugaredLogger
	adminFactory ClusterAdminFactory
	clusterAdmin sarama.ClusterAdmin
}

func NewAdminClient(ctx context.Context, caFactory ClusterAdminFactory) (AdminClient, error) {
	logger := logging.FromContext(ctx)
	logger.Debug("Creating a new AdminClient")
	kafkaClusterAdmin, err := caFactory()
	if err != nil {
		logger.Errorw("error while creating ClusterAdmin", zap.Error(err))
		return nil, err
	}
	return &AdminClientManager{
		logger:       logger,
		adminFactory: caFactory,
		clusterAdmin: kafkaClusterAdmin,
	}, nil
}

// ListConsumerGroups Returns a list of the consumer groups.
//
// In the occasion of errors, there will be a retry with an exponential backoff.
// Due to a known issue in Sarama ClusterAdmin https://github.com/Shopify/sarama/issues/1162,
// a new ClusterAdmin will be created with every retry until the call succeeds or
// the timeout is reached.
func (c *AdminClientManager) ListConsumerGroups() ([]string, error) {
	c.logger.Debug("Attempting to list consumer group")
	mutex.Lock()
	defer mutex.Unlock()
	r := 0
	// This gives us around ~13min of exponential backoff
	max := 13
	cgsMap, err := c.clusterAdmin.ListConsumerGroups()
	for err != nil && r <= max {
		// There's on error, let's retry and presume a new ClusterAdmin can fix it

		// Calculate incremental delay following this https://docs.aws.amazon.com/general/latest/gr/api-retries.html
		t := int(math.Pow(2, float64(r)) * 100)
		d := time.Duration(t) * time.Millisecond
		c.logger.Errorw("listing consumer group failed. Refreshing the ClusterAdmin and retrying.",
			zap.Error(err),
			zap.Duration("retry after", d),
			zap.Int("Retry attempt", r),
			zap.Int("Max retries", max),
		)
		time.Sleep(d)

		// let's reconnect and try again
		c.clusterAdmin, err = c.adminFactory()
		r += 1
		if err != nil {
			// skip this attempt
			continue
		}
		cgsMap, err = c.clusterAdmin.ListConsumerGroups()
	}

	if r > max {
		return nil, fmt.Errorf("failed to refresh the culster admin and retry: %v", err)
	}

	return sets.StringKeySet(cgsMap).List(), nil
}
<|endoftext|>


<commit_before>package riemann

import (
	"fmt"
	"testing"
	"time"

	"github.com/amir/raidman"
	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/metric"
	"github.com/influxdata/telegraf/testutil"
	"github.com/stretchr/testify/require"
)

func TestAttributes(t *testing.T) {
	tags := map[string]string{"tag1": "value1", "tag2": "value2"}

	r := &Riemann{}
	require.Equal(t,
		map[string]string{"tag1": "value1", "tag2": "value2"},
		r.attributes("test", tags))

	// enable measurement as attribute, should now be included
	r.MeasurementAsAttribute = true
	require.Equal(t,
		map[string]string{"tag1": "value1", "tag2": "value2", "measurement": "test"},
		r.attributes("test", tags))
}

func TestService(t *testing.T) {
	r := &Riemann{
		Separator: "/",
	}
	require.Equal(t, "test/value", r.service("test", "value"))

	// enable measurement as attribute, should not be part of service name anymore
	r.MeasurementAsAttribute = true
	require.Equal(t, "value", r.service("test", "value"))
}

func TestTags(t *testing.T) {
	tags := map[string]string{"tag1": "value1", "tag2": "value2"}

	// all tag values plus additional tag should be present
	r := &Riemann{
		Tags: []string{"test"},
	}
	require.Equal(t,
		[]string{"test", "value1", "value2"},
		r.tags(tags))

	// only tag2 value plus additional tag should be present
	r.TagKeys = []string{"tag2"}
	require.Equal(t,
		[]string{"test", "value2"},
		r.tags(tags))

	// only tag1 value should be present
	r.Tags = nil
	r.TagKeys = []string{"tag1"}
	require.Equal(t,
		[]string{"value1"},
		r.tags(tags))
}

func TestMetricEvents(t *testing.T) {
	r := &Riemann{
		TTL:                    20.0,
		Separator:              "/",
		MeasurementAsAttribute: false,
		DescriptionText:        "metrics from telegraf",
		Tags:                   []string{"telegraf"},
	}

	// build a single event
	m, _ := metric.New(
		"test1",
		map[string]string{"tag1": "value1", "host": "abc123"},
		map[string]interface{}{"value": 5.6},
		time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
	)

	events := r.buildRiemannEvents(m)
	require.Len(t, events, 1)

	// is event as expected?
	expectedEvent := &raidman.Event{
		Ttl:         20.0,
		Time:        1257894000,
		Tags:        []string{"telegraf", "value1"},
		Host:        "abc123",
		State:       "",
		Service:     "test1/value",
		Metric:      5.6,
		Description: "metrics from telegraf",
		Attributes:  map[string]string{"tag1": "value1"},
	}
	require.Equal(t, expectedEvent, events[0])

	// build 2 events
	m, _ = metric.New(
		"test2",
		map[string]string{"host": "xyz987"},
		map[string]interface{}{"point": 1},
		time.Date(2012, time.November, 2, 3, 0, 0, 0, time.UTC),
	)

	events = append(events, r.buildRiemannEvents(m)...)
	require.Len(t, events, 2)

	// first event should still be the same
	require.Equal(t, expectedEvent, events[0])

	// second event
	expectedEvent = &raidman.Event{
		Ttl:         20.0,
		Time:        1351825200,
		Tags:        []string{"telegraf"},
		Host:        "xyz987",
		State:       "",
		Service:     "test2/point",
		Metric:      int64(1),
		Description: "metrics from telegraf",
		Attributes:  map[string]string{},
	}
	require.Equal(t, expectedEvent, events[1])
}

func TestStateEvents(t *testing.T) {
	r := &Riemann{
		MeasurementAsAttribute: true,
	}

	// string metrics will be skipped unless explicitly enabled
	m, _ := metric.New(
		"test",
		map[string]string{"host": "host"},
		map[string]interface{}{"value": "running"},
		time.Date(2015, time.November, 9, 22, 0, 0, 0, time.UTC),
	)

	events := r.buildRiemannEvents(m)
	// no event should be present
	require.Len(t, events, 0)

	// enable string metrics as event states
	r.StringAsState = true
	events = r.buildRiemannEvents(m)
	require.Len(t, events, 1)

	// is event as expected?
	expectedEvent := &raidman.Event{
		Ttl:         0,
		Time:        1447106400,
		Tags:        nil,
		Host:        "host",
		State:       "running",
		Service:     "value",
		Metric:      nil,
		Description: "",
		Attributes:  map[string]string{"measurement": "test"},
	}
	require.Equal(t, expectedEvent, events[0])
}

func TestConnectAndWrite(t *testing.T) {
	if testing.Short() {
		t.Skip("Skipping integration test in short mode")
	}

	r := &Riemann{
		URL:                    fmt.Sprintf("tcp://%s:5555", testutil.GetLocalHost()),
		TTL:                    15.0,
		Separator:              "/",
		MeasurementAsAttribute: false,
		StringAsState:          true,
		DescriptionText:        "metrics from telegraf",
		Tags:                   []string{"docker"},
	}

	err := r.Connect()
	require.NoError(t, err)

	err = r.Write(testutil.MockMetrics())
	require.NoError(t, err)

	metrics := make([]telegraf.Metric, 0)
	metrics = append(metrics, testutil.TestMetric(2))
	metrics = append(metrics, testutil.TestMetric(3.456789))
	metrics = append(metrics, testutil.TestMetric(uint(0)))
	metrics = append(metrics, testutil.TestMetric("ok"))
	metrics = append(metrics, testutil.TestMetric("running"))
	err = r.Write(metrics)
	require.NoError(t, err)

	// are there any "docker" tagged events in Riemann?
	events, err := r.client.Query(`tagged "docker"`)
	require.NoError(t, err)
	require.NotZero(t, len(events))

	// get Riemann events with state = "running", should be 1 event
	events, err = r.client.Query(`state = "running"`)
	require.NoError(t, err)
	require.Len(t, events, 1)

	// is event as expected?
	require.Equal(t, []string{"docker", "value1"}, events[0].Tags)
	require.Equal(t, "running", events[0].State)
	require.Equal(t, "test1/value", events[0].Service)
	require.Equal(t, "metrics from telegraf", events[0].Description)
	require.Equal(t, map[string]string{"tag1": "value1"}, events[0].Attributes)
}
<commit_msg>Remove timing sensitive riemann test<commit_after>package riemann

import (
	"testing"
	"time"

	"github.com/amir/raidman"
	"github.com/influxdata/telegraf/metric"
	"github.com/stretchr/testify/require"
)

func TestAttributes(t *testing.T) {
	tags := map[string]string{"tag1": "value1", "tag2": "value2"}

	r := &Riemann{}
	require.Equal(t,
		map[string]string{"tag1": "value1", "tag2": "value2"},
		r.attributes("test", tags))

	// enable measurement as attribute, should now be included
	r.MeasurementAsAttribute = true
	require.Equal(t,
		map[string]string{"tag1": "value1", "tag2": "value2", "measurement": "test"},
		r.attributes("test", tags))
}

func TestService(t *testing.T) {
	r := &Riemann{
		Separator: "/",
	}
	require.Equal(t, "test/value", r.service("test", "value"))

	// enable measurement as attribute, should not be part of service name anymore
	r.MeasurementAsAttribute = true
	require.Equal(t, "value", r.service("test", "value"))
}

func TestTags(t *testing.T) {
	tags := map[string]string{"tag1": "value1", "tag2": "value2"}

	// all tag values plus additional tag should be present
	r := &Riemann{
		Tags: []string{"test"},
	}
	require.Equal(t,
		[]string{"test", "value1", "value2"},
		r.tags(tags))

	// only tag2 value plus additional tag should be present
	r.TagKeys = []string{"tag2"}
	require.Equal(t,
		[]string{"test", "value2"},
		r.tags(tags))

	// only tag1 value should be present
	r.Tags = nil
	r.TagKeys = []string{"tag1"}
	require.Equal(t,
		[]string{"value1"},
		r.tags(tags))
}

func TestMetricEvents(t *testing.T) {
	r := &Riemann{
		TTL:                    20.0,
		Separator:              "/",
		MeasurementAsAttribute: false,
		DescriptionText:        "metrics from telegraf",
		Tags:                   []string{"telegraf"},
	}

	// build a single event
	m, _ := metric.New(
		"test1",
		map[string]string{"tag1": "value1", "host": "abc123"},
		map[string]interface{}{"value": 5.6},
		time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC),
	)

	events := r.buildRiemannEvents(m)
	require.Len(t, events, 1)

	// is event as expected?
	expectedEvent := &raidman.Event{
		Ttl:         20.0,
		Time:        1257894000,
		Tags:        []string{"telegraf", "value1"},
		Host:        "abc123",
		State:       "",
		Service:     "test1/value",
		Metric:      5.6,
		Description: "metrics from telegraf",
		Attributes:  map[string]string{"tag1": "value1"},
	}
	require.Equal(t, expectedEvent, events[0])

	// build 2 events
	m, _ = metric.New(
		"test2",
		map[string]string{"host": "xyz987"},
		map[string]interface{}{"point": 1},
		time.Date(2012, time.November, 2, 3, 0, 0, 0, time.UTC),
	)

	events = append(events, r.buildRiemannEvents(m)...)
	require.Len(t, events, 2)

	// first event should still be the same
	require.Equal(t, expectedEvent, events[0])

	// second event
	expectedEvent = &raidman.Event{
		Ttl:         20.0,
		Time:        1351825200,
		Tags:        []string{"telegraf"},
		Host:        "xyz987",
		State:       "",
		Service:     "test2/point",
		Metric:      int64(1),
		Description: "metrics from telegraf",
		Attributes:  map[string]string{},
	}
	require.Equal(t, expectedEvent, events[1])
}

func TestStateEvents(t *testing.T) {
	r := &Riemann{
		MeasurementAsAttribute: true,
	}

	// string metrics will be skipped unless explicitly enabled
	m, _ := metric.New(
		"test",
		map[string]string{"host": "host"},
		map[string]interface{}{"value": "running"},
		time.Date(2015, time.November, 9, 22, 0, 0, 0, time.UTC),
	)

	events := r.buildRiemannEvents(m)
	// no event should be present
	require.Len(t, events, 0)

	// enable string metrics as event states
	r.StringAsState = true
	events = r.buildRiemannEvents(m)
	require.Len(t, events, 1)

	// is event as expected?
	expectedEvent := &raidman.Event{
		Ttl:         0,
		Time:        1447106400,
		Tags:        nil,
		Host:        "host",
		State:       "running",
		Service:     "value",
		Metric:      nil,
		Description: "",
		Attributes:  map[string]string{"measurement": "test"},
	}
	require.Equal(t, expectedEvent, events[0])
}
<|endoftext|>


<commit_before>package ibmmq

/*
  Copyright (c) IBM Corporation 2016

  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.

   Contributors:
     Mark Taylor - Initial Contribution
*/

/*

#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <cmqxc.h>

void freeCCDTUrl(MQCNO *mqcno) {
#if defined(MQCNO_VERSION_6) && MQCNO_CURRENT_VERSION >= MQCNO_VERSION_6
	if (mqcno.CCDTUrlPtr != NULL) {
		free(mqcno.CCDTUrlPtr);
	}
#endif
}

void setCCDTUrl(MQCNO *mqcno, PMQCHAR url, MQLONG length) {
#if defined(MQCNO_VERSION_6) && MQCNO_CURRENT_VERSION == MQCNO_VERSION_6
	mqcno.CCDTUrlOffset = 0;
	mqcno.CCDTUrlPtr = NULL;
	mqcno.CCDTUrlLength = length;
	if (url != NULL) {
		mqcno.CCDTUrlPtr = PMQCHAR(url);
	}
#else
	if (url != NULL) {
		free(url);
	}
#endif
}

*/
import "C"
import "unsafe"

/*
MQCNO is a structure containing the MQ Connection Options (MQCNO)
Note that only a subset of the real structure is exposed in this
version.
*/
type MQCNO struct {
	Version       int32
	Options       int32
	SecurityParms *MQCSP
	CCDTUrl       string
	ClientConn    *MQCD
	SSLConfig     *MQSCO
}

/*
MQCSP is a structure containing the MQ Security Parameters (MQCSP)
*/
type MQCSP struct {
	AuthenticationType int32
	UserId             string
	Password           string
}

/*
NewMQCNO fills in default values for the MQCNO structure
*/
func NewMQCNO() *MQCNO {

	cno := new(MQCNO)
	cno.Version = int32(C.MQCNO_VERSION_1)
	cno.Options = int32(C.MQCNO_NONE)
	cno.SecurityParms = nil
	cno.ClientConn = nil

	return cno
}

/*
NewMQCSP fills in default values for the MQCSP structure
*/
func NewMQCSP() *MQCSP {

	csp := new(MQCSP)
	csp.AuthenticationType = int32(C.MQCSP_AUTH_NONE)
	csp.UserId = ""
	csp.Password = ""

	return csp
}

func copyCNOtoC(mqcno *C.MQCNO, gocno *MQCNO) {
	var i int
	var mqcsp C.PMQCSP
	var mqcd C.PMQCD
	var mqsco C.PMQSCO

	setMQIString((*C.char)(&mqcno.StrucId[0]), "CNO ", 4)
	mqcno.Version = C.MQLONG(gocno.Version)
	mqcno.Options = C.MQLONG(gocno.Options)

	for i = 0; i < C.MQ_CONN_TAG_LENGTH; i++ {
		mqcno.ConnTag[i] = 0
	}
	for i = 0; i < C.MQ_CONNECTION_ID_LENGTH; i++ {
		mqcno.ConnectionId[i] = 0
	}

	mqcno.ClientConnOffset = 0
	if gocno.ClientConn != nil {
		gocd := gocno.ClientConn
		mqcd = C.PMQCD(C.malloc(C.MQCD_LENGTH_11))
		copyCDtoC(mqcd, gocd)
		mqcno.ClientConnPtr = C.MQPTR(mqcd)
		if gocno.Version < 2 {
			mqcno.Version = C.MQCNO_VERSION_2
		}
	} else {
		mqcno.ClientConnPtr = nil
	}

	mqcno.SSLConfigOffset = 0
	if gocno.SSLConfig != nil {
		gosco := gocno.SSLConfig
		mqsco = C.PMQSCO(C.malloc(C.MQSCO_LENGTH_5))
		copySCOtoC(mqsco, gosco)
		mqcno.SSLConfigPtr = C.PMQSCO(mqsco)
		if gocno.Version < 4 {
			mqcno.Version = C.MQCNO_VERSION_4
		}
	} else {
		mqcno.SSLConfigPtr = nil
	}

	mqcno.SecurityParmsOffset = 0
	if gocno.SecurityParms != nil {
		gocsp := gocno.SecurityParms

		mqcsp = C.PMQCSP(C.malloc(C.MQCSP_LENGTH_1))
		setMQIString((*C.char)(&mqcsp.StrucId[0]), "CSP ", 4)
		mqcsp.Version = C.MQCSP_VERSION_1
		mqcsp.AuthenticationType = C.MQLONG(gocsp.AuthenticationType)
		mqcsp.CSPUserIdOffset = 0
		mqcsp.CSPPasswordOffset = 0

		if gocsp.UserId != "" {
			mqcsp.AuthenticationType = C.MQLONG(C.MQCSP_AUTH_USER_ID_AND_PWD)
			mqcsp.CSPUserIdPtr = C.MQPTR(unsafe.Pointer(C.CString(gocsp.UserId)))
			mqcsp.CSPUserIdLength = C.MQLONG(len(gocsp.UserId))
		}
		if gocsp.Password != "" {
			mqcsp.CSPPasswordPtr = C.MQPTR(unsafe.Pointer(C.CString(gocsp.Password)))
			mqcsp.CSPPasswordLength = C.MQLONG(len(gocsp.Password))
		}
		mqcno.SecurityParmsPtr = C.PMQCSP(mqcsp)
		if gocno.Version < 5 {
			mqcno.Version = C.MQCNO_VERSION_5
		}

	} else {
		mqcno.SecurityParmsPtr = nil
	}

	C.setCCDTUrl(mqcno, C.PMQCHAR(C.CString(gocno.CCDTUrl)), C.MQLONG(len(gocno.CCDTUrl)))
	return
}

func copyCNOfromC(mqcno *C.MQCNO, gocno *MQCNO) {

	if mqcno.SecurityParmsPtr != nil {
		if mqcno.SecurityParmsPtr.CSPUserIdPtr != nil {
			C.free(unsafe.Pointer(mqcno.SecurityParmsPtr.CSPUserIdPtr))
		}
		// Set memory to 0 for area that held a password
		if mqcno.SecurityParmsPtr.CSPPasswordPtr != nil {
			C.memset((unsafe.Pointer)(mqcno.SecurityParmsPtr.CSPPasswordPtr), 0, C.size_t(mqcno.SecurityParmsPtr.CSPPasswordLength))
			C.free(unsafe.Pointer(mqcno.SecurityParmsPtr.CSPPasswordPtr))
		}
		C.free(unsafe.Pointer(mqcno.SecurityParmsPtr))
	}

	if mqcno.ClientConnPtr != nil {
		copyCDfromC(C.PMQCD(mqcno.ClientConnPtr), gocno.ClientConn)
		C.free(unsafe.Pointer(mqcno.ClientConnPtr))
	}

	if mqcno.SSLConfigPtr != nil {
		copySCOfromC(C.PMQSCO(mqcno.SSLConfigPtr), gocno.SSLConfig)
		C.free(unsafe.Pointer(mqcno.SSLConfigPtr))
	}

	C.freeCCDTUrl(mqcno)
	return
}
<commit_msg>make it compile with MQv9<commit_after>package ibmmq

/*
  Copyright (c) IBM Corporation 2016

  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.

   Contributors:
     Mark Taylor - Initial Contribution
*/

/*

#include <stdlib.h>
#include <string.h>
#include <cmqc.h>
#include <cmqxc.h>

void freeCCDTUrl(MQCNO *mqcno) {
#if defined(MQCNO_VERSION_6) && MQCNO_CURRENT_VERSION >= MQCNO_VERSION_6
	if (mqcno->CCDTUrlPtr != NULL) {
		free(mqcno->CCDTUrlPtr);
	}
#endif
}

void setCCDTUrl(MQCNO *mqcno, PMQCHAR url, MQLONG length) {
#if defined(MQCNO_VERSION_6) && MQCNO_CURRENT_VERSION >= MQCNO_VERSION_6
	mqcno->CCDTUrlOffset = 0;
	mqcno->CCDTUrlPtr = NULL;
	mqcno->CCDTUrlLength = length;
	if (url != NULL) {
		mqcno->CCDTUrlPtr = url;
	}
#else
	if (url != NULL) {
		free(url);
	}
#endif
}

*/
import "C"
import "unsafe"

/*
MQCNO is a structure containing the MQ Connection Options (MQCNO)
Note that only a subset of the real structure is exposed in this
version.
*/
type MQCNO struct {
	Version       int32
	Options       int32
	SecurityParms *MQCSP
	CCDTUrl       string
	ClientConn    *MQCD
	SSLConfig     *MQSCO
}

/*
MQCSP is a structure containing the MQ Security Parameters (MQCSP)
*/
type MQCSP struct {
	AuthenticationType int32
	UserId             string
	Password           string
}

/*
NewMQCNO fills in default values for the MQCNO structure
*/
func NewMQCNO() *MQCNO {

	cno := new(MQCNO)
	cno.Version = int32(C.MQCNO_VERSION_1)
	cno.Options = int32(C.MQCNO_NONE)
	cno.SecurityParms = nil
	cno.ClientConn = nil

	return cno
}

/*
NewMQCSP fills in default values for the MQCSP structure
*/
func NewMQCSP() *MQCSP {

	csp := new(MQCSP)
	csp.AuthenticationType = int32(C.MQCSP_AUTH_NONE)
	csp.UserId = ""
	csp.Password = ""

	return csp
}

func copyCNOtoC(mqcno *C.MQCNO, gocno *MQCNO) {
	var i int
	var mqcsp C.PMQCSP
	var mqcd C.PMQCD
	var mqsco C.PMQSCO

	setMQIString((*C.char)(&mqcno.StrucId[0]), "CNO ", 4)
	mqcno.Version = C.MQLONG(gocno.Version)
	mqcno.Options = C.MQLONG(gocno.Options)

	for i = 0; i < C.MQ_CONN_TAG_LENGTH; i++ {
		mqcno.ConnTag[i] = 0
	}
	for i = 0; i < C.MQ_CONNECTION_ID_LENGTH; i++ {
		mqcno.ConnectionId[i] = 0
	}

	mqcno.ClientConnOffset = 0
	if gocno.ClientConn != nil {
		gocd := gocno.ClientConn
		mqcd = C.PMQCD(C.malloc(C.MQCD_LENGTH_11))
		copyCDtoC(mqcd, gocd)
		mqcno.ClientConnPtr = C.MQPTR(mqcd)
		if gocno.Version < 2 {
			mqcno.Version = C.MQCNO_VERSION_2
		}
	} else {
		mqcno.ClientConnPtr = nil
	}

	mqcno.SSLConfigOffset = 0
	if gocno.SSLConfig != nil {
		gosco := gocno.SSLConfig
		mqsco = C.PMQSCO(C.malloc(C.MQSCO_LENGTH_5))
		copySCOtoC(mqsco, gosco)
		mqcno.SSLConfigPtr = C.PMQSCO(mqsco)
		if gocno.Version < 4 {
			mqcno.Version = C.MQCNO_VERSION_4
		}
	} else {
		mqcno.SSLConfigPtr = nil
	}

	mqcno.SecurityParmsOffset = 0
	if gocno.SecurityParms != nil {
		gocsp := gocno.SecurityParms

		mqcsp = C.PMQCSP(C.malloc(C.MQCSP_LENGTH_1))
		setMQIString((*C.char)(&mqcsp.StrucId[0]), "CSP ", 4)
		mqcsp.Version = C.MQCSP_VERSION_1
		mqcsp.AuthenticationType = C.MQLONG(gocsp.AuthenticationType)
		mqcsp.CSPUserIdOffset = 0
		mqcsp.CSPPasswordOffset = 0

		if gocsp.UserId != "" {
			mqcsp.AuthenticationType = C.MQLONG(C.MQCSP_AUTH_USER_ID_AND_PWD)
			mqcsp.CSPUserIdPtr = C.MQPTR(unsafe.Pointer(C.CString(gocsp.UserId)))
			mqcsp.CSPUserIdLength = C.MQLONG(len(gocsp.UserId))
		}
		if gocsp.Password != "" {
			mqcsp.CSPPasswordPtr = C.MQPTR(unsafe.Pointer(C.CString(gocsp.Password)))
			mqcsp.CSPPasswordLength = C.MQLONG(len(gocsp.Password))
		}
		mqcno.SecurityParmsPtr = C.PMQCSP(mqcsp)
		if gocno.Version < 5 {
			mqcno.Version = C.MQCNO_VERSION_5
		}

	} else {
		mqcno.SecurityParmsPtr = nil
	}

	C.setCCDTUrl(mqcno, C.PMQCHAR(C.CString(gocno.CCDTUrl)), C.MQLONG(len(gocno.CCDTUrl)))
	return
}

func copyCNOfromC(mqcno *C.MQCNO, gocno *MQCNO) {

	if mqcno.SecurityParmsPtr != nil {
		if mqcno.SecurityParmsPtr.CSPUserIdPtr != nil {
			C.free(unsafe.Pointer(mqcno.SecurityParmsPtr.CSPUserIdPtr))
		}
		// Set memory to 0 for area that held a password
		if mqcno.SecurityParmsPtr.CSPPasswordPtr != nil {
			C.memset((unsafe.Pointer)(mqcno.SecurityParmsPtr.CSPPasswordPtr), 0, C.size_t(mqcno.SecurityParmsPtr.CSPPasswordLength))
			C.free(unsafe.Pointer(mqcno.SecurityParmsPtr.CSPPasswordPtr))
		}
		C.free(unsafe.Pointer(mqcno.SecurityParmsPtr))
	}

	if mqcno.ClientConnPtr != nil {
		copyCDfromC(C.PMQCD(mqcno.ClientConnPtr), gocno.ClientConn)
		C.free(unsafe.Pointer(mqcno.ClientConnPtr))
	}

	if mqcno.SSLConfigPtr != nil {
		copySCOfromC(C.PMQSCO(mqcno.SSLConfigPtr), gocno.SSLConfig)
		C.free(unsafe.Pointer(mqcno.SSLConfigPtr))
	}

	C.freeCCDTUrl(mqcno)
	return
}
<|endoftext|>


<commit_before>package brain

import (
	"io"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
)

// VirtualMachineDefault is a default (or perhaps better, template) for virtual
// machines. They're essentially a VirtualMachineSpec which the panel picks
// details from and populates the virtual machine creation screen with.
// Public defaults can be made by setting Public: true.
type VirtualMachineDefault struct {
	ID             int                `json:"id,omitempty"`
	AccountID      int                `json:"account_id,omitempty"`
	Name           string             `json:"name"`
	Public         bool               `json:"public"`
	ServerSettings VirtualMachineSpec `json:"server_settings"`
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (vmd VirtualMachineDefault) DefaultFields(f output.Format) string {
	return "ID, AccountID, Name, Public, ServerSettings"
}

// PrettyPrint outputs a nice human-readable overview of the VM Default to the given writer.
func (vmd VirtualMachineDefault) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	const template = `{{ define "vmdspec_sgl" }} ▸ {{.Name }} with public => {{.Public }}
{{ prettysprint .ServerSettings }}{{ end }}`
	return prettyprint.Run(wr, template, "vmdspec"+string(detail), vmd)
}

// TotalDiscSize returns the sum of all disc capacities in the VM for the given storage grade.
// Provide the empty string to sum all discs regardless of storage grade.
func (vmd VirtualMachineDefault) TotalDiscSize(storageGrade string) (total int) {
	total = 0
	for _, disc := range vmd.ServerSettings.Discs {
		if storageGrade == "" || storageGrade == disc.StorageGrade {
			total += disc.Size
		}
	}
	return total
}
<commit_msg>Add vmdspec_medium and vmdspec_full templates<commit_after>package brain

import (
	"io"

	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
)

// VirtualMachineDefault is a default (or perhaps better, template) for virtual
// machines. They're essentially a VirtualMachineSpec which the panel picks
// details from and populates the virtual machine creation screen with.
// Public defaults can be made by setting Public: true.
type VirtualMachineDefault struct {
	ID             int                `json:"id,omitempty"`
	AccountID      int                `json:"account_id,omitempty"`
	Name           string             `json:"name"`
	Public         bool               `json:"public"`
	ServerSettings VirtualMachineSpec `json:"server_settings"`
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/row.From for this type.
func (vmd VirtualMachineDefault) DefaultFields(f output.Format) string {
	return "ID, AccountID, Name, Public, ServerSettings"
}

// PrettyPrint outputs a nice human-readable overview of the VM Default to the given writer.
func (vmd VirtualMachineDefault) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	const template = `{{ define "vmdspec_sgl" }} ▸ {{ .Name }} with public => {{ .Public }}{{ end}}
{{ define "vmdspec_medium" }}{{ template "vmdspec_sgl" }}{{ end }}
{{ define "vmdspec_full" }}{{ template "vmdspec_medium" }}
{{ prettysprint .ServerSettings "_full" }}{{ end }}`
	return prettyprint.Run(wr, template, "vmdspec"+string(detail), vmd)
}

// TotalDiscSize returns the sum of all disc capacities in the VM for the given storage grade.
// Provide the empty string to sum all discs regardless of storage grade.
func (vmd VirtualMachineDefault) TotalDiscSize(storageGrade string) (total int) {
	total = 0
	for _, disc := range vmd.ServerSettings.Discs {
		if storageGrade == "" || storageGrade == disc.StorageGrade {
			total += disc.Size
		}
	}
	return total
}
<|endoftext|>


<commit_before>package morningStar

import (
	"../jsonHttp"
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"sync"
	"time"
)

const PERFORMANCE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=1&id=`
const VOLATILITE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=2&id=`
const REFRESH_DELAY = 18
const CONCURRENT_FETCHER = 20

var EMPTY_BYTE = []byte(``)
var ZERO_BYTE = []byte(`0`)
var PERIOD_BYTE = []byte(`.`)
var COMMA_BYTE = []byte(`,`)
var PERCENT_BYTE = []byte(`%`)
var AMP_BYTE = []byte(`&`)
var HTML_AMP_BYTE = []byte(`&`)

var LIST_REQUEST = regexp.MustCompile(`^/list$`)
var PERF_REQUEST = regexp.MustCompile(`^/(.+?)$`)

var ISIN = regexp.MustCompile(`ISIN.:(\S+)`)
var LABEL = regexp.MustCompile(`<h1[^>]*?>((?:.|\n)*?)</h1>`)
var RATING = regexp.MustCompile(`<span\sclass=".*?stars([0-9]).*?">`)
var CATEGORY = regexp.MustCompile(`<span[^>]*?>Catégorie</span>.*?<span[^>]*?>(.*?)</span>`)
var PERF_ONE_MONTH = regexp.MustCompile(`<td[^>]*?>1 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_THREE_MONTH = regexp.MustCompile(`<td[^>]*?>3 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_SIX_MONTH = regexp.MustCompile(`<td[^>]*?>6 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_ONE_YEAR = regexp.MustCompile(`<td[^>]*?>1 an</td><td[^>]*?>(.*?)</td>`)
var VOL_3_YEAR = regexp.MustCompile(`<td[^>]*?>Ecart-type 3 ans.?</td><td[^>]*?>(.*?)</td>`)

type SyncedMap struct {
	sync.RWMutex
	performances map[string]Performance
}

func (m *SyncedMap) get(key string) (Performance, bool) {
	m.RLock()
	defer m.RUnlock()

	performance, ok := m.performances[key]
	return performance, ok
}

func (m *SyncedMap) push(key string, performance Performance) {
	m.Lock()
	defer m.Unlock()

	m.performances[key] = performance
}

var PERFORMANCE_CACHE = SyncedMap{performances: make(map[string]Performance)}

type Performance struct {
	Id            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonth    float64   `json:"3m"`
	SixMonth      float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

type Results struct {
	Results interface{} `json:"results"`
}

func readBody(body io.ReadCloser) ([]byte, error) {
	defer body.Close()
	return ioutil.ReadAll(body)
}

func getBody(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return nil, errors.New(`Error while retrieving data from ` + url)
	}

	if response.StatusCode >= 400 {
		return nil, errors.New(`Got error ` + strconv.Itoa(response.StatusCode) + ` while getting ` + url)
	}

	body, err := readBody(response.Body)
	if err != nil {
		return nil, errors.New(`Error while reading body of ` + url)
	}

	return body, nil
}

func getLabel(extract *regexp.Regexp, body []byte, defaultValue []byte) []byte {
	match := extract.FindSubmatch(body)
	if match == nil {
		return defaultValue
	}

	return bytes.Replace(match[1], HTML_AMP_BYTE, AMP_BYTE, -1)
}

func getPerformance(extract *regexp.Regexp, body []byte) float64 {
	dotResult := bytes.Replace(getLabel(extract, body, EMPTY_BYTE), COMMA_BYTE, PERIOD_BYTE, -1)
	percentageResult := bytes.Replace(dotResult, PERCENT_BYTE, EMPTY_BYTE, -1)
	trimResult := bytes.TrimSpace(percentageResult)

	result, err := strconv.ParseFloat(string(trimResult), 64)
	if err != nil {
		return 0.0
	}
	return result
}

func SinglePerformance(morningStarId []byte) (*Performance, error) {
	cleanId := string(bytes.ToLower(morningStarId))

	performance, ok := PERFORMANCE_CACHE.get(cleanId)

	if ok && time.Now().Add(time.Hour*-REFRESH_DELAY).Before(performance.Update) {
		return &performance, nil
	}

	performanceBody, err := getBody(PERFORMANCE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	volatiliteBody, err := getBody(VOLATILITE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	isin := string(getLabel(ISIN, performanceBody, EMPTY_BYTE))
	label := string(getLabel(LABEL, performanceBody, EMPTY_BYTE))
	rating := string(getLabel(RATING, performanceBody, ZERO_BYTE))
	category := string(getLabel(CATEGORY, performanceBody, EMPTY_BYTE))
	oneMonth := getPerformance(PERF_ONE_MONTH, performanceBody)
	threeMonths := getPerformance(PERF_THREE_MONTH, performanceBody)
	sixMonths := getPerformance(PERF_SIX_MONTH, performanceBody)
	oneYear := getPerformance(PERF_ONE_YEAR, performanceBody)
	volThreeYears := getPerformance(VOL_3_YEAR, volatiliteBody)

	score := (0.25 * oneMonth) + (0.3 * threeMonths) + (0.25 * sixMonths) + (0.2 * oneYear) - (0.1 * volThreeYears)
	scoreTruncated := float64(int(score*100)) / 100

	performance = Performance{cleanId, isin, label, category, rating, oneMonth, threeMonths, sixMonths, oneYear, volThreeYears, scoreTruncated, time.Now()}

	PERFORMANCE_CACHE.push(cleanId, performance)

	return &performance, nil
}

func singlePerformanceHandler(w http.ResponseWriter, morningStarId []byte) {
	performance, err := SinglePerformance(morningStarId)

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		jsonHttp.ResponseJson(w, *performance)
	}
}

func allPerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *Performance) {
	tokens := make(chan struct{}, CONCURRENT_FETCHER)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- struct{}{}

		go func(morningStarId []byte) {
			defer clearSemaphores()
			if performance, err := SinglePerformance(morningStarId); err == nil {
				performances <- performance
			}
		}(id)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	listBody, err := readBody(r.Body)
	if err != nil {
		http.Error(w, `Error while reading body for list`, 500)
		return
	}

	if len(bytes.TrimSpace(listBody)) == 0 {
		jsonHttp.ResponseJson(w, Results{[0]Performance{}})
		return
	}

	var wg sync.WaitGroup
	performances := make(chan *Performance, CONCURRENT_FETCHER)
	ids := bytes.Split(listBody, COMMA_BYTE)

	wg.Add(len(ids))
	go allPerformances(ids, &wg, performances)

	go func() {
		wg.Wait()
		close(performances)
	}()

	results := make([]*Performance, 0, len(ids))
	for performance := range performances {
		results = append(results, performance)
	}

	jsonHttp.ResponseJson(w, Results{results})
}

type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	urlPath := []byte(r.URL.Path)

	if LIST_REQUEST.Match(urlPath) {
		listHandler(w, r)
	} else if PERF_REQUEST.Match(urlPath) {
		singlePerformanceHandler(w, PERF_REQUEST.FindSubmatch(urlPath)[1])
	}
}
<commit_msg>Update morningStar.go<commit_after>package morningStar

import (
	"../jsonHttp"
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"strconv"
	"sync"
	"time"
)

const PERFORMANCE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=1&id=`
const VOLATILITE_URL = `http://www.morningstar.fr/fr/funds/snapshot/snapshot.aspx?tab=2&id=`
const REFRESH_DELAY = 18
const CONCURRENT_FETCHER = 20

var EMPTY_BYTE = []byte(``)
var ZERO_BYTE = []byte(`0`)
var PERIOD_BYTE = []byte(`.`)
var COMMA_BYTE = []byte(`,`)
var PERCENT_BYTE = []byte(`%`)
var AMP_BYTE = []byte(`&`)
var HTML_AMP_BYTE = []byte(`&`)

var LIST_REQUEST = regexp.MustCompile(`^/list$`)
var PERF_REQUEST = regexp.MustCompile(`^/(.+?)$`)

var ISIN = regexp.MustCompile(`ISIN.:(\S+)`)
var LABEL = regexp.MustCompile(`<h1[^>]*?>((?:.|\n)*?)</h1>`)
var RATING = regexp.MustCompile(`<span\sclass=".*?stars([0-9]).*?">`)
var CATEGORY = regexp.MustCompile(`<span[^>]*?>Catégorie</span>.*?<span[^>]*?>(.*?)</span>`)
var PERF_ONE_MONTH = regexp.MustCompile(`<td[^>]*?>1 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_THREE_MONTH = regexp.MustCompile(`<td[^>]*?>3 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_SIX_MONTH = regexp.MustCompile(`<td[^>]*?>6 mois</td><td[^>]*?>(.*?)</td>`)
var PERF_ONE_YEAR = regexp.MustCompile(`<td[^>]*?>1 an</td><td[^>]*?>(.*?)</td>`)
var VOL_3_YEAR = regexp.MustCompile(`<td[^>]*?>Ecart-type 3 ans.?</td><td[^>]*?>(.*?)</td>`)

type SyncedMap struct {
	sync.RWMutex
	performances map[string]Performance
}

func (m *SyncedMap) get(key string) (Performance, bool) {
	m.RLock()
	defer m.RUnlock()

	performance, ok := m.performances[key]
	return performance, ok
}

func (m *SyncedMap) push(key string, performance Performance) {
	m.Lock()
	defer m.Unlock()

	m.performances[key] = performance
}

var PERFORMANCE_CACHE = SyncedMap{performances: make(map[string]Performance)}

type Performance struct {
	Id            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonth    float64   `json:"3m"`
	SixMonth      float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

type Results struct {
	Results interface{} `json:"results"`
}

func readBody(body io.ReadCloser) ([]byte, error) {
	defer body.Close()
	return ioutil.ReadAll(body)
}

func getBody(url string) ([]byte, error) {
	response, err := http.Get(url)
	if err != nil {
		return nil, errors.New(`Error while retrieving data from ` + url)
	}

	if response.StatusCode >= 400 {
		return nil, errors.New(`Got error ` + strconv.Itoa(response.StatusCode) + ` while getting ` + url)
	}

	body, err := readBody(response.Body)
	if err != nil {
		return nil, errors.New(`Error while reading body of ` + url)
	}

	return body, nil
}

func getLabel(extract *regexp.Regexp, body []byte, defaultValue []byte) []byte {
	match := extract.FindSubmatch(body)
	if match == nil {
		return defaultValue
	}

	return bytes.Replace(match[1], HTML_AMP_BYTE, AMP_BYTE, -1)
}

func getPerformance(extract *regexp.Regexp, body []byte) float64 {
	dotResult := bytes.Replace(getLabel(extract, body, EMPTY_BYTE), COMMA_BYTE, PERIOD_BYTE, -1)
	percentageResult := bytes.Replace(dotResult, PERCENT_BYTE, EMPTY_BYTE, -1)
	trimResult := bytes.TrimSpace(percentageResult)

	result, err := strconv.ParseFloat(string(trimResult), 64)
	if err != nil {
		return 0.0
	}
	return result
}

func SinglePerformance(morningStarId []byte) (*Performance, error) {
	cleanId := string(bytes.ToLower(morningStarId))

	performance, ok := PERFORMANCE_CACHE.get(cleanId)

	if ok && time.Now().Add(time.Hour*-REFRESH_DELAY).Before(performance.Update) {
		return &performance, nil
	}

	performanceBody, err := getBody(PERFORMANCE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	volatiliteBody, err := getBody(VOLATILITE_URL + cleanId)
	if err != nil {
		return nil, err
	}

	isin := string(getLabel(ISIN, performanceBody, EMPTY_BYTE))
	label := string(getLabel(LABEL, performanceBody, EMPTY_BYTE))
	rating := string(getLabel(RATING, performanceBody, ZERO_BYTE))
	category := string(getLabel(CATEGORY, performanceBody, EMPTY_BYTE))
	oneMonth := getPerformance(PERF_ONE_MONTH, performanceBody)
	threeMonths := getPerformance(PERF_THREE_MONTH, performanceBody)
	sixMonths := getPerformance(PERF_SIX_MONTH, performanceBody)
	oneYear := getPerformance(PERF_ONE_YEAR, performanceBody)
	volThreeYears := getPerformance(VOL_3_YEAR, volatiliteBody)

	score := (0.25 * oneMonth) + (0.3 * threeMonths) + (0.25 * sixMonths) + (0.2 * oneYear) - (0.1 * volThreeYears)
	scoreTruncated := float64(int(score*100)) / 100

	performance = Performance{cleanId, isin, label, category, rating, oneMonth, threeMonths, sixMonths, oneYear, volThreeYears, scoreTruncated, time.Now()}

	PERFORMANCE_CACHE.push(cleanId, performance)

	return &performance, nil
}

func singlePerformanceHandler(w http.ResponseWriter, morningStarId []byte) {
	performance, err := SinglePerformance(morningStarId)

	if err != nil {
		http.Error(w, err.Error(), 500)
	} else {
		jsonHttp.ResponseJson(w, *performance)
	}
}

func allPerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *Performance) {
	tokens := make(chan struct{}, CONCURRENT_FETCHER)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- struct{}{}

		go func(morningStarId []byte) {
			defer clearSemaphores()
			if performance, err := SinglePerformance(morningStarId); err == nil {
				performances <- performance
			}
		}(id)
	}
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	listBody, err := readBody(r.Body)
	if err != nil {
		http.Error(w, `Error while reading body for list`, 500)
		return
	}

	if len(bytes.TrimSpace(listBody)) == 0 {
		jsonHttp.ResponseJson(w, Results{[0]Performance{}})
		return
	}

	performances := make(chan *Performance, CONCURRENT_FETCHER)
	ids := bytes.Split(listBody, COMMA_BYTE)

	var wg sync.WaitGroup
	wg.Add(len(ids))
	go allPerformances(ids, &wg, performances)

	go func() {
		wg.Wait()
		close(performances)
	}()

	results := make([]*Performance, 0, len(ids))
	for performance := range performances {
		results = append(results, performance)
	}

	jsonHttp.ResponseJson(w, Results{results})
}

type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET, POST`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	urlPath := []byte(r.URL.Path)

	if LIST_REQUEST.Match(urlPath) {
		listHandler(w, r)
	} else if PERF_REQUEST.Match(urlPath) {
		singlePerformanceHandler(w, PERF_REQUEST.FindSubmatch(urlPath)[1])
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes 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 transport

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"testing"
	"time"

	"golang.org/x/oauth2"
)

type testTokenSource struct {
	calls int
	tok   *oauth2.Token
	err   error
}

func (ts *testTokenSource) Token() (*oauth2.Token, error) {
	ts.calls++
	return ts.tok, ts.err
}

func TestCachingTokenSource(t *testing.T) {
	start := time.Now()
	tokA := &oauth2.Token{
		AccessToken: "a",
		Expiry:      start.Add(10 * time.Minute),
	}
	tokB := &oauth2.Token{
		AccessToken: "b",
		Expiry:      start.Add(20 * time.Minute),
	}
	tests := []struct {
		name string

		tok   *oauth2.Token
		tsTok *oauth2.Token
		tsErr error
		wait  time.Duration

		wantTok     *oauth2.Token
		wantErr     bool
		wantTSCalls int
	}{
		{
			name:    "valid token returned from cache",
			tok:     tokA,
			wantTok: tokA,
		},
		{
			name:    "valid token returned from cache 1 minute before scheduled refresh",
			tok:     tokA,
			wait:    8 * time.Minute,
			wantTok: tokA,
		},
		{
			name:        "new token created when cache is empty",
			tsTok:       tokA,
			wantTok:     tokA,
			wantTSCalls: 1,
		},
		{
			name:        "new token created 1 minute after scheduled refresh",
			tok:         tokA,
			tsTok:       tokB,
			wait:        10 * time.Minute,
			wantTok:     tokB,
			wantTSCalls: 1,
		},
		{
			name:        "error on create token returns error",
			tsErr:       fmt.Errorf("error"),
			wantErr:     true,
			wantTSCalls: 1,
		},
	}
	for _, c := range tests {
		t.Run(c.name, func(t *testing.T) {
			tts := &testTokenSource{
				tok: c.tsTok,
				err: c.tsErr,
			}

			ts := &cachingTokenSource{
				base:   tts,
				tok:    c.tok,
				leeway: 1 * time.Minute,
				now:    func() time.Time { return start.Add(c.wait) },
			}

			gotTok, gotErr := ts.Token()
			if got, want := gotTok, c.wantTok; !reflect.DeepEqual(got, want) {
				t.Errorf("unexpected token:\n\tgot:\t%#v\n\twant:\t%#v", got, want)
			}
			if got, want := tts.calls, c.wantTSCalls; got != want {
				t.Errorf("unexpected number of Token() calls: got %d, want %d", got, want)
			}
			if gotErr == nil && c.wantErr {
				t.Errorf("wanted error but got none")
			}
			if gotErr != nil && !c.wantErr {
				t.Errorf("unexpected error: %v", gotErr)
			}
		})
	}
}

func TestCachingTokenSourceRace(t *testing.T) {
	for i := 0; i < 100; i++ {
		tts := &testTokenSource{
			tok: &oauth2.Token{
				AccessToken: "a",
				Expiry:      time.Now().Add(1000 * time.Hour),
			},
		}

		ts := &cachingTokenSource{
			now:    time.Now,
			base:   tts,
			leeway: 1 * time.Minute,
		}

		var wg sync.WaitGroup
		wg.Add(100)

		for i := 0; i < 100; i++ {
			go func() {
				defer wg.Done()
				if _, err := ts.Token(); err != nil {
					t.Fatalf("err: %v", err)
				}
			}()
		}
		wg.Wait()
		if tts.calls != 1 {
			t.Errorf("expected one call to Token() but saw: %d", tts.calls)
		}
	}
}

func TestTokenSourceTransportRoundTrip(t *testing.T) {
	goodToken := &oauth2.Token{
		AccessToken: "good",
		Expiry:      time.Now().Add(1000 * time.Hour),
	}
	badToken := &oauth2.Token{
		AccessToken: "bad",
		Expiry:      time.Now().Add(1000 * time.Hour),
	}
	tests := []struct {
		name        string
		header      http.Header
		token       *oauth2.Token
		cachedToken *oauth2.Token
		wantCalls   int
		wantCaching bool
	}{
		{
			name:   "skip oauth rt if has authorization header",
			header: map[string][]string{"Authorization": {"Bearer TOKEN"}},
			token:  goodToken,
		},
		{
			name:        "authorized on newly acquired good token",
			token:       goodToken,
			wantCalls:   1,
			wantCaching: true,
		},
		{
			name:        "authorized on cached good token",
			token:       goodToken,
			cachedToken: goodToken,
			wantCalls:   0,
			wantCaching: true,
		},
		{
			name:        "unauthorized on newly acquired bad token",
			token:       badToken,
			wantCalls:   1,
			wantCaching: true,
		},
		{
			name:        "unauthorized on cached bad token",
			token:       badToken,
			cachedToken: badToken,
			wantCalls:   0,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			tts := &testTokenSource{
				tok: test.token,
			}
			cachedTokenSource := NewCachedTokenSource(tts)
			cachedTokenSource.tok = test.cachedToken

			rt := ResettableTokenSourceWrapTransport(cachedTokenSource)(&testTransport{})

			rt.RoundTrip(&http.Request{Header: test.header})
			if tts.calls != test.wantCalls {
				t.Errorf("RoundTrip() called Token() = %d times, want %d", tts.calls, test.wantCalls)
			}

			if (cachedTokenSource.tok != nil) != test.wantCaching {
				t.Errorf("Got caching %v, want caching %v", cachedTokenSource != nil, test.wantCaching)
			}
		})
	}
}

type uncancellableRT struct {
	rt http.RoundTripper
}

func (urt *uncancellableRT) RoundTrip(req *http.Request) (*http.Response, error) {
	return urt.rt.RoundTrip(req)
}

func TestTokenSourceTransportCancelRequest(t *testing.T) {
	tests := []struct {
		name          string
		header        http.Header
		wrapTransport func(http.RoundTripper) http.RoundTripper
		expectCancel  bool
	}{
		{
			name:         "cancel req with bearer token skips oauth rt",
			header:       map[string][]string{"Authorization": {"Bearer TOKEN"}},
			expectCancel: true,
		},
		{
			name: "can't cancel request with rts that doesn't implent unwrap or cancel",
			wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
				return &uncancellableRT{rt: rt}
			},
			expectCancel: false,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			baseRecorder := &testTransport{}

			var base http.RoundTripper = baseRecorder
			if test.wrapTransport != nil {
				base = test.wrapTransport(base)
			}

			rt := &tokenSourceTransport{
				base: base,
				ort: &oauth2.Transport{
					Base: base,
				},
			}

			rt.CancelRequest(&http.Request{
				Header: test.header,
			})

			if baseRecorder.canceled != test.expectCancel {
				t.Errorf("unexpected cancel: got=%v, want=%v", baseRecorder.canceled, test.expectCancel)
			}
		})
	}
}

type testTransport struct {
	canceled bool
	base     http.RoundTripper
}

func (rt *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.Header["Authorization"][0] == "Bearer bad" {
		return &http.Response{StatusCode: 401}, nil
	}
	return nil, nil
}

func (rt *testTransport) CancelRequest(req *http.Request) {
	rt.canceled = true
	if rt.base != nil {
		tryCancelRequest(rt.base, req)
	}
}
<commit_msg>Fix staticcheck failures for vendor/k8s.io/client-go/transport (#100429)<commit_after>/*
Copyright 2018 The Kubernetes 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 transport

import (
	"fmt"
	"net/http"
	"reflect"
	"sync"
	"testing"
	"time"

	"golang.org/x/oauth2"
)

type testTokenSource struct {
	calls int
	tok   *oauth2.Token
	err   error
}

func (ts *testTokenSource) Token() (*oauth2.Token, error) {
	ts.calls++
	return ts.tok, ts.err
}

func TestCachingTokenSource(t *testing.T) {
	start := time.Now()
	tokA := &oauth2.Token{
		AccessToken: "a",
		Expiry:      start.Add(10 * time.Minute),
	}
	tokB := &oauth2.Token{
		AccessToken: "b",
		Expiry:      start.Add(20 * time.Minute),
	}
	tests := []struct {
		name string

		tok   *oauth2.Token
		tsTok *oauth2.Token
		tsErr error
		wait  time.Duration

		wantTok     *oauth2.Token
		wantErr     bool
		wantTSCalls int
	}{
		{
			name:    "valid token returned from cache",
			tok:     tokA,
			wantTok: tokA,
		},
		{
			name:    "valid token returned from cache 1 minute before scheduled refresh",
			tok:     tokA,
			wait:    8 * time.Minute,
			wantTok: tokA,
		},
		{
			name:        "new token created when cache is empty",
			tsTok:       tokA,
			wantTok:     tokA,
			wantTSCalls: 1,
		},
		{
			name:        "new token created 1 minute after scheduled refresh",
			tok:         tokA,
			tsTok:       tokB,
			wait:        10 * time.Minute,
			wantTok:     tokB,
			wantTSCalls: 1,
		},
		{
			name:        "error on create token returns error",
			tsErr:       fmt.Errorf("error"),
			wantErr:     true,
			wantTSCalls: 1,
		},
	}
	for _, c := range tests {
		t.Run(c.name, func(t *testing.T) {
			tts := &testTokenSource{
				tok: c.tsTok,
				err: c.tsErr,
			}

			ts := &cachingTokenSource{
				base:   tts,
				tok:    c.tok,
				leeway: 1 * time.Minute,
				now:    func() time.Time { return start.Add(c.wait) },
			}

			gotTok, gotErr := ts.Token()
			if got, want := gotTok, c.wantTok; !reflect.DeepEqual(got, want) {
				t.Errorf("unexpected token:\n\tgot:\t%#v\n\twant:\t%#v", got, want)
			}
			if got, want := tts.calls, c.wantTSCalls; got != want {
				t.Errorf("unexpected number of Token() calls: got %d, want %d", got, want)
			}
			if gotErr == nil && c.wantErr {
				t.Errorf("wanted error but got none")
			}
			if gotErr != nil && !c.wantErr {
				t.Errorf("unexpected error: %v", gotErr)
			}
		})
	}
}

func TestCachingTokenSourceRace(t *testing.T) {
	for i := 0; i < 100; i++ {
		tts := &testTokenSource{
			tok: &oauth2.Token{
				AccessToken: "a",
				Expiry:      time.Now().Add(1000 * time.Hour),
			},
		}

		ts := &cachingTokenSource{
			now:    time.Now,
			base:   tts,
			leeway: 1 * time.Minute,
		}

		var wg sync.WaitGroup
		wg.Add(100)
		errc := make(chan error, 100)

		for i := 0; i < 100; i++ {
			go func() {
				defer wg.Done()
				if _, err := ts.Token(); err != nil {
					errc <- err
				}
			}()
		}
		go func() {
			wg.Wait()
			close(errc)
		}()
		if err, ok := <-errc; ok {
			t.Fatalf("err: %v", err)
		}
		if tts.calls != 1 {
			t.Errorf("expected one call to Token() but saw: %d", tts.calls)
		}
	}
}

func TestTokenSourceTransportRoundTrip(t *testing.T) {
	goodToken := &oauth2.Token{
		AccessToken: "good",
		Expiry:      time.Now().Add(1000 * time.Hour),
	}
	badToken := &oauth2.Token{
		AccessToken: "bad",
		Expiry:      time.Now().Add(1000 * time.Hour),
	}
	tests := []struct {
		name        string
		header      http.Header
		token       *oauth2.Token
		cachedToken *oauth2.Token
		wantCalls   int
		wantCaching bool
	}{
		{
			name:   "skip oauth rt if has authorization header",
			header: map[string][]string{"Authorization": {"Bearer TOKEN"}},
			token:  goodToken,
		},
		{
			name:        "authorized on newly acquired good token",
			token:       goodToken,
			wantCalls:   1,
			wantCaching: true,
		},
		{
			name:        "authorized on cached good token",
			token:       goodToken,
			cachedToken: goodToken,
			wantCalls:   0,
			wantCaching: true,
		},
		{
			name:        "unauthorized on newly acquired bad token",
			token:       badToken,
			wantCalls:   1,
			wantCaching: true,
		},
		{
			name:        "unauthorized on cached bad token",
			token:       badToken,
			cachedToken: badToken,
			wantCalls:   0,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			tts := &testTokenSource{
				tok: test.token,
			}
			cachedTokenSource := NewCachedTokenSource(tts)
			cachedTokenSource.tok = test.cachedToken

			rt := ResettableTokenSourceWrapTransport(cachedTokenSource)(&testTransport{})

			rt.RoundTrip(&http.Request{Header: test.header})
			if tts.calls != test.wantCalls {
				t.Errorf("RoundTrip() called Token() = %d times, want %d", tts.calls, test.wantCalls)
			}

			if (cachedTokenSource.tok != nil) != test.wantCaching {
				t.Errorf("Got caching %v, want caching %v", cachedTokenSource != nil, test.wantCaching)
			}
		})
	}
}

type uncancellableRT struct {
	rt http.RoundTripper
}

func (urt *uncancellableRT) RoundTrip(req *http.Request) (*http.Response, error) {
	return urt.rt.RoundTrip(req)
}

func TestTokenSourceTransportCancelRequest(t *testing.T) {
	tests := []struct {
		name          string
		header        http.Header
		wrapTransport func(http.RoundTripper) http.RoundTripper
		expectCancel  bool
	}{
		{
			name:         "cancel req with bearer token skips oauth rt",
			header:       map[string][]string{"Authorization": {"Bearer TOKEN"}},
			expectCancel: true,
		},
		{
			name: "can't cancel request with rts that doesn't implent unwrap or cancel",
			wrapTransport: func(rt http.RoundTripper) http.RoundTripper {
				return &uncancellableRT{rt: rt}
			},
			expectCancel: false,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			baseRecorder := &testTransport{}

			var base http.RoundTripper = baseRecorder
			if test.wrapTransport != nil {
				base = test.wrapTransport(base)
			}

			rt := &tokenSourceTransport{
				base: base,
				ort: &oauth2.Transport{
					Base: base,
				},
			}

			rt.CancelRequest(&http.Request{
				Header: test.header,
			})

			if baseRecorder.canceled != test.expectCancel {
				t.Errorf("unexpected cancel: got=%v, want=%v", baseRecorder.canceled, test.expectCancel)
			}
		})
	}
}

type testTransport struct {
	canceled bool
	base     http.RoundTripper
}

func (rt *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	if req.Header["Authorization"][0] == "Bearer bad" {
		return &http.Response{StatusCode: 401}, nil
	}
	return nil, nil
}

func (rt *testTransport) CancelRequest(req *http.Request) {
	rt.canceled = true
	if rt.base != nil {
		tryCancelRequest(rt.base, req)
	}
}
<|endoftext|>