<commit_before>package web

import (
	"net/http"
	"reflect"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/log"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
)

// 参数类型方法
type ParamTypeFunc func(context *Context, name string, t reflect.Type) interface{}

// HttpProcessor 用于协调http连接器和路由,并管理Http应用的所有内容
type HttpProcessor struct {
	Root             router.Router            //根路由
	Config           *HttpConfig              //http配置
	Logger           log.Logger               //日志记录
	SessionContainer session.SessionContainer //Session容器
	CSRFContainer    session.SessionContainer //Csrf容器
	Funcs            map[string]ParamTypeFunc //参数生成方法
	DefaultFunc      ParamTypeFunc            //当Funcs中不存在指定类型的方法时,使用该方法处理
	Event            HttpProcessorEvent       //处理器事件
}

// NewHttpProcessor 创建Http处理器
func NewHttpProcessor(root router.Router, config *HttpConfig) *HttpProcessor {
	var processor = new(HttpProcessor)
	processor.Root = root
	processor.Config = config
	//日志
	if config.Log {
		var logger, err = log.NewLogger(config.LogType, config.LogPath)
		if err != nil {
			panic(err)
		}
		logger.SetAsync(config.LogAsync)
		processor.Logger = logger
	}
	//session
	if config.Session {
		var container, err = session.NewSessionContainer(config.SessionType, config.SessionExpire, config.SessionSource)
		if err != nil {
			panic(err)
		}
		processor.SessionContainer = container
	}
	//CSRF,过期时间与Session相同,CSRFExpire用于设置CSRF token的过期时间
	if config.CSRF {
		var container, err = session.NewSessionContainer(config.CSRFType, config.SessionExpire, config.CSRFSource)
		if err != nil {
			panic(err)
		}
		processor.CSRFContainer = container
	}

	processor.Funcs = make(map[string]ParamTypeFunc)
	register(processor.Funcs)
	processor.DefaultFunc = DefaultFunc
	processor.Event = new(DefaultHttpProcessorEvent)
	return processor
}

// ParamFunc 根据类型全名获取指定的生成方法
func (this *HttpProcessor) ParamFunc(t string) ParamTypeFunc {
	var f, ok = this.Funcs[t]
	if !ok {
		f = this.DefaultFunc
	}
	return f
}

// createCookie 创建cookie(有效期为1天)
func (this *HttpProcessor) createCookie(name string, id string) *http.Cookie {
	var cookieValue = new(http.Cookie)
	cookieValue.Name = name
	cookieValue.Value = id
	cookieValue.Path = "/"
	cookieValue.MaxAge = 24 * 3600
	cookieValue.Expires = time.Now().AddDate(0, 0, 1)
	cookieValue.HttpOnly = true
	return cookieValue
}

// addCookie 添加cookie
func (this *HttpProcessor) addCookie(context *Context, cookie *http.Cookie) {
	context.HttpContext.ResponseWriter.Header().Add("Set-Cookie", cookie.String())
}

// ResolveSession 处理会话相关内容
func (this *HttpProcessor) ResolveSession(context *Context) {
	if this.SessionContainer != nil {
		//添加Session信息
		var cookieValue, err = context.HttpContext.Request.Cookie(this.Config.SessionCookieName)
		var ss session.Session
		var ok bool = false
		if err == nil {
			ss, ok = this.SessionContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.SessionContainer.CreateSession()
			this.addCookie(context, this.createCookie(this.Config.SessionCookieName, ss.SessionId()))
		}
		if ok {
			context.Session = ss
		}
	}

	if this.CSRFContainer != nil {
		//添加CSRF Session信息,CSRF的过期时间和Session相同,使用SessionExpire设置Cookie过期时间
		var cookieValue, err = context.HttpContext.Request.Cookie(this.Config.CSRFCookieName)
		var ss session.Session
		var ok bool = false
		if err == nil {
			ss, ok = this.CSRFContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.CSRFContainer.CreateSession()
			this.addCookie(context, this.createCookie(this.Config.CSRFCookieName, ss.SessionId()))
		}
		if ok {
			context.Csrf = ss
		}
	}
}

// Dispatch 将接收到的请求进行分发
//  segments:用于进行分发的路径段信息
//  data:连接携带的数据
func (this *HttpProcessor) Dispatch(segments []string, data interface{}) {
	var ct = data.(*connector.HttpContext)
	var context, err = NewContext(segments, ct)
	if err == nil {
		context.Processor = this
		this.ResolveSession(context)
		if this.Event != nil {
			this.Event.Request(this, context)
		}
		var executor, ok = this.Root.Match(context)
		if ok {
			var result = executor.Execute()
			if this.Event != nil {
				this.Event.RequestFinish(this, context, result)
			}
		} else {
			if this.Event != nil {
				this.Event.RouterNotFound(this, context)
			}
		}
	} else {
		if this.Event != nil {
			this.Event.Error(this, context, err)
		}
	}

}

// HttpProcessor事件接口
type HttpProcessorEvent interface {
	// 每次出现一个新请求的时候触发
	Request(processor *HttpProcessor, context *Context)
	// 每次请求执行完成的时候触发
	RequestFinish(processor *HttpProcessor, context *Context, result interface{})
	// 路由未匹配时触发
	RouterNotFound(processor *HttpProcessor, context *Context)
	// 出现错误时触发,出现错误时context需要检查是否为nil后才能使用
	Error(processor *HttpProcessor, context *Context, err error)
}

// 默认事件
type DefaultHttpProcessorEvent struct {
}

// 每次出现一个新请求的时候触发
func (this *DefaultHttpProcessorEvent) Request(processor *HttpProcessor, context *Context) {

}

// 每次请求执行完成的时候触发
func (this *DefaultHttpProcessorEvent) RequestFinish(processor *HttpProcessor, context *Context, result interface{}) {
	var rs, ok = result.([]interface{})
	if ok && len(rs) > 0 {
		var r, ok2 = rs[0].(Result)
		if ok2 {
			r.WriteTo(context.HttpContext.ResponseWriter)
		}
	}

}

// 路由未匹配时触发
func (this *DefaultHttpProcessorEvent) RouterNotFound(processor *HttpProcessor, context *Context) {

}

// 出现错误时触发
func (this *DefaultHttpProcessorEvent) Error(processor *HttpProcessor, context *Context, err error) {

}
<commit_msg>提高Cookie创建性能<commit_after>package web

import (
	"net/http"
	"reflect"
	"time"

	"github.com/kdada/tinygo/connector"
	"github.com/kdada/tinygo/log"
	"github.com/kdada/tinygo/router"
	"github.com/kdada/tinygo/session"
)

// 参数类型方法
type ParamTypeFunc func(context *Context, name string, t reflect.Type) interface{}

// HttpProcessor 用于协调http连接器和路由,并管理Http应用的所有内容
type HttpProcessor struct {
	Root             router.Router            //根路由
	Config           *HttpConfig              //http配置
	Logger           log.Logger               //日志记录
	SessionContainer session.SessionContainer //Session容器
	CSRFContainer    session.SessionContainer //Csrf容器
	Funcs            map[string]ParamTypeFunc //参数生成方法
	DefaultFunc      ParamTypeFunc            //当Funcs中不存在指定类型的方法时,使用该方法处理
	Event            HttpProcessorEvent       //处理器事件
}

// NewHttpProcessor 创建Http处理器
func NewHttpProcessor(root router.Router, config *HttpConfig) *HttpProcessor {
	var processor = new(HttpProcessor)
	processor.Root = root
	processor.Config = config
	//日志
	if config.Log {
		var logger, err = log.NewLogger(config.LogType, config.LogPath)
		if err != nil {
			panic(err)
		}
		logger.SetAsync(config.LogAsync)
		processor.Logger = logger
	}
	//session
	if config.Session {
		var container, err = session.NewSessionContainer(config.SessionType, config.SessionExpire, config.SessionSource)
		if err != nil {
			panic(err)
		}
		processor.SessionContainer = container
	}
	//CSRF,过期时间与Session相同,CSRFExpire用于设置CSRF token的过期时间
	if config.CSRF {
		var container, err = session.NewSessionContainer(config.CSRFType, config.SessionExpire, config.CSRFSource)
		if err != nil {
			panic(err)
		}
		processor.CSRFContainer = container
	}

	processor.Funcs = make(map[string]ParamTypeFunc)
	register(processor.Funcs)
	processor.DefaultFunc = DefaultFunc
	processor.Event = new(DefaultHttpProcessorEvent)
	return processor
}

// ParamFunc 根据类型全名获取指定的生成方法
func (this *HttpProcessor) ParamFunc(t string) ParamTypeFunc {
	var f, ok = this.Funcs[t]
	if !ok {
		f = this.DefaultFunc
	}
	return f
}

// createCookie 创建cookie(有效期为1天)
func (this *HttpProcessor) createCookie(name string, id string) *http.Cookie {
	var cookieValue = new(http.Cookie)
	cookieValue.Name = name
	cookieValue.Value = id
	cookieValue.Path = "/"
	cookieValue.MaxAge = 24 * 3600
	cookieValue.Expires = time.Now().Add(time.Hour * 24)
	cookieValue.HttpOnly = true
	return cookieValue
}

// addCookie 添加cookie
func (this *HttpProcessor) addCookie(context *Context, cookie *http.Cookie) {
	context.HttpContext.ResponseWriter.Header().Add("Set-Cookie", cookie.String())
}

// ResolveSession 处理会话相关内容
func (this *HttpProcessor) ResolveSession(context *Context) {
	if this.SessionContainer != nil {
		//添加Session信息
		var cookieValue, err = context.HttpContext.Request.Cookie(this.Config.SessionCookieName)
		var ss session.Session
		var ok bool = false
		if err == nil {
			ss, ok = this.SessionContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.SessionContainer.CreateSession()
			this.addCookie(context, this.createCookie(this.Config.SessionCookieName, ss.SessionId()))
		}
		if ok {
			context.Session = ss
		}
	}
	if this.CSRFContainer != nil {
		//添加CSRF Session信息,CSRF的过期时间和Session相同,使用SessionExpire设置Cookie过期时间
		var cookieValue, err = context.HttpContext.Request.Cookie(this.Config.CSRFCookieName)
		var ss session.Session
		var ok bool = false
		if err == nil {
			ss, ok = this.CSRFContainer.Session(cookieValue.Value)
		}
		if !ok {
			ss, ok = this.CSRFContainer.CreateSession()
			this.addCookie(context, this.createCookie(this.Config.CSRFCookieName, ss.SessionId()))
		}
		if ok {
			context.Csrf = ss
		}
	}
}

// Dispatch 将接收到的请求进行分发
//  segments:用于进行分发的路径段信息
//  data:连接携带的数据
func (this *HttpProcessor) Dispatch(segments []string, data interface{}) {
	var ct = data.(*connector.HttpContext)
	var context, err = NewContext(segments, ct)
	if err == nil {
		context.Processor = this
		this.ResolveSession(context)
		if this.Event != nil {
			this.Event.Request(this, context)
		}
		var executor, ok = this.Root.Match(context)
		if ok {
			var result = executor.Execute()
			if this.Event != nil {
				this.Event.RequestFinish(this, context, result)
			}
		} else {
			if this.Event != nil {
				this.Event.RouterNotFound(this, context)
			}
		}
	} else {
		if this.Event != nil {
			this.Event.Error(this, context, err)
		}
	}
}

// HttpProcessor事件接口
type HttpProcessorEvent interface {
	// 每次出现一个新请求的时候触发
	Request(processor *HttpProcessor, context *Context)
	// 每次请求执行完成的时候触发
	RequestFinish(processor *HttpProcessor, context *Context, result interface{})
	// 路由未匹配时触发
	RouterNotFound(processor *HttpProcessor, context *Context)
	// 出现错误时触发,出现错误时context需要检查是否为nil后才能使用
	Error(processor *HttpProcessor, context *Context, err error)
}

// 默认事件
type DefaultHttpProcessorEvent struct {
}

// 每次出现一个新请求的时候触发
func (this *DefaultHttpProcessorEvent) Request(processor *HttpProcessor, context *Context) {

}

// 每次请求执行完成的时候触发
func (this *DefaultHttpProcessorEvent) RequestFinish(processor *HttpProcessor, context *Context, result interface{}) {
	var rs, ok = result.([]interface{})
	if ok && len(rs) > 0 {
		var r, ok2 = rs[0].(Result)
		if ok2 {
			r.WriteTo(context.HttpContext.ResponseWriter)
		}
	}

}

// 路由未匹配时触发
func (this *DefaultHttpProcessorEvent) RouterNotFound(processor *HttpProcessor, context *Context) {

}

// 出现错误时触发
func (this *DefaultHttpProcessorEvent) Error(processor *HttpProcessor, context *Context, err error) {

}
<|endoftext|>


<commit_before>package images

import (
	"context"
	"fmt"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	k8simage "k8s.io/kubernetes/test/utils/image"

	authorizationv1 "github.com/openshift/api/authorization/v1"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/image"
)

var _ = g.Describe("[sig-imageregistry][Feature:Image] oc tag", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("image-oc-tag")
	ctx := context.Background()

	g.It("should preserve image reference for external images", func() {
		var (
			externalImage = k8simage.GetE2EImage(k8simage.BusyBox)
			isName        = "busybox"
			isName2       = "busybox2"
		)

		externalRepository := externalImage
		if i := strings.LastIndex(externalRepository, ":"); i != -1 {
			externalRepository = externalRepository[:i]
		}

		g.By("import an external image")

		err := oc.Run("tag").Args("--source=docker", externalImage, isName+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the created image stream references the external registry
		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag1 := is.Status.Tags[0]
		o.Expect(tag1.Tag).To(o.Equal("latest"))
		o.Expect(tag1.Items).To(o.HaveLen(1))
		o.Expect(tag1.Items[0].DockerImageReference).To(o.HavePrefix(externalRepository + "@"))

		g.By("copy the image to another image stream")

		err = oc.Run("tag").Args("--source=istag", isName+":latest", isName2+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName2, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the new image stream references the still uses the external registry
		is, err = oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName2, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag2 := is.Status.Tags[0]
		o.Expect(tag2.Tag).To(o.Equal("latest"))
		o.Expect(tag2.Items).To(o.HaveLen(1))
		o.Expect(tag2.Items[0].DockerImageReference).To(o.Equal(tag1.Items[0].DockerImageReference))
	})

	g.It("should change image reference for internal images", func() {
		var (
			isName     = "localimage"
			isName2    = "localimage2"
			dockerfile = fmt.Sprintf(`FROM %s
RUN touch /test-image
`, image.ShellImage())
		)

		g.By("determine the name of the integrated registry")

		registryHost, err := oc.Run("registry").Args("info", "--internal").Output()
		o.Expect(err).NotTo(o.HaveOccurred())

		g.By("build an image")

		err = oc.Run("new-build").Args("-D", "-", "--to", isName+":latest").InputString(dockerfile).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), isName+"-1", nil, nil, nil)
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the created image stream references the integrated registry
		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag := is.Status.Tags[0]
		o.Expect(tag.Tag).To(o.Equal("latest"))
		o.Expect(tag.Items).To(o.HaveLen(1))
		o.Expect(tag.Items[0].DockerImageReference).To(o.HavePrefix(fmt.Sprintf("%s/%s/%s@", registryHost, oc.Namespace(), isName)))

		// extract the image digest
		ref := tag.Items[0].DockerImageReference
		digest := ref[strings.Index(ref, "@")+1:]
		o.Expect(digest).To(o.HavePrefix("sha256:"))

		g.By("copy the image to another image stream")

		err = oc.Run("tag").Args("--source=istag", isName+":latest", isName2+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName2, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the new image stream uses its own name in the image reference
		is, err = oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName2, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag = is.Status.Tags[0]
		o.Expect(tag.Tag).To(o.Equal("latest"))
		o.Expect(tag.Items).To(o.HaveLen(1))
		o.Expect(tag.Items[0].DockerImageReference).To(o.Equal(fmt.Sprintf("%s/%s/%s@%s", registryHost, oc.Namespace(), isName2, digest)))
	})

	g.It("should work when only imagestreams api is available", func() {
		err := oc.Run("tag").Args("--source=docker", image.ShellImage(), "testis:latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), "testis", "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("create").Args("serviceaccount", "testsa").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		e2e.Logf("Creating a role that allows to work with imagestreams, but not imagestreamtags...")

		_, err = oc.AdminAuthorizationClient().AuthorizationV1().Roles(oc.Namespace()).Create(ctx, &authorizationv1.Role{
			ObjectMeta: metav1.ObjectMeta{
				Name: "testrole",
			},
			Rules: []authorizationv1.PolicyRule{
				{
					Verbs:     []string{"get", "update"},
					APIGroups: []string{"image.openshift.io"},
					Resources: []string{"imagestreams"},
				},
			},
		}, metav1.CreateOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("policy").Args("add-role-to-user", "testrole", "-z", "testsa", "--role-namespace="+oc.Namespace()).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForServiceAccount(oc.AdminKubeClient().CoreV1().ServiceAccounts(oc.Namespace()), "testsa")
		o.Expect(err).NotTo(o.HaveOccurred())

		token, err := oc.Run("serviceaccounts").Args("get-token", "testsa").Output()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("login").Args("--token=" + token).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("whoami").Args().Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("tag").Args("testis:latest", "testis:copy").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		e2e.Logf("Checking that the imagestream is updated...")

		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, "testis", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		var tags []string
		for _, t := range is.Spec.Tags {
			tags = append(tags, t.Name)
		}
		o.Expect(tags).To(o.ContainElement("copy"), "testis spec.tags should contain the tag copy")
	})
})
<commit_msg>Bug 2081018: fix token request in oc tag test<commit_after>package images

import (
	"context"
	"fmt"
	"strings"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	k8simage "k8s.io/kubernetes/test/utils/image"

	authorizationv1 "github.com/openshift/api/authorization/v1"

	exutil "github.com/openshift/origin/test/extended/util"
	"github.com/openshift/origin/test/extended/util/image"
)

var _ = g.Describe("[sig-imageregistry][Feature:Image] oc tag", func() {
	defer g.GinkgoRecover()
	oc := exutil.NewCLI("image-oc-tag")
	ctx := context.Background()

	g.It("should preserve image reference for external images", func() {
		var (
			externalImage = k8simage.GetE2EImage(k8simage.BusyBox)
			isName        = "busybox"
			isName2       = "busybox2"
		)

		externalRepository := externalImage
		if i := strings.LastIndex(externalRepository, ":"); i != -1 {
			externalRepository = externalRepository[:i]
		}

		g.By("import an external image")

		err := oc.Run("tag").Args("--source=docker", externalImage, isName+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the created image stream references the external registry
		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag1 := is.Status.Tags[0]
		o.Expect(tag1.Tag).To(o.Equal("latest"))
		o.Expect(tag1.Items).To(o.HaveLen(1))
		o.Expect(tag1.Items[0].DockerImageReference).To(o.HavePrefix(externalRepository + "@"))

		g.By("copy the image to another image stream")

		err = oc.Run("tag").Args("--source=istag", isName+":latest", isName2+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName2, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the new image stream references the still uses the external registry
		is, err = oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName2, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag2 := is.Status.Tags[0]
		o.Expect(tag2.Tag).To(o.Equal("latest"))
		o.Expect(tag2.Items).To(o.HaveLen(1))
		o.Expect(tag2.Items[0].DockerImageReference).To(o.Equal(tag1.Items[0].DockerImageReference))
	})

	g.It("should change image reference for internal images", func() {
		var (
			isName     = "localimage"
			isName2    = "localimage2"
			dockerfile = fmt.Sprintf(`FROM %s
RUN touch /test-image
`, image.ShellImage())
		)

		g.By("determine the name of the integrated registry")

		registryHost, err := oc.Run("registry").Args("info", "--internal").Output()
		o.Expect(err).NotTo(o.HaveOccurred())

		g.By("build an image")

		err = oc.Run("new-build").Args("-D", "-", "--to", isName+":latest").InputString(dockerfile).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForABuild(oc.BuildClient().BuildV1().Builds(oc.Namespace()), isName+"-1", nil, nil, nil)
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the created image stream references the integrated registry
		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag := is.Status.Tags[0]
		o.Expect(tag.Tag).To(o.Equal("latest"))
		o.Expect(tag.Items).To(o.HaveLen(1))
		o.Expect(tag.Items[0].DockerImageReference).To(o.HavePrefix(fmt.Sprintf("%s/%s/%s@", registryHost, oc.Namespace(), isName)))

		// extract the image digest
		ref := tag.Items[0].DockerImageReference
		digest := ref[strings.Index(ref, "@")+1:]
		o.Expect(digest).To(o.HavePrefix("sha256:"))

		g.By("copy the image to another image stream")

		err = oc.Run("tag").Args("--source=istag", isName+":latest", isName2+":latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), isName2, "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		// check that the new image stream uses its own name in the image reference
		is, err = oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, isName2, metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())
		o.Expect(is.Status.Tags).To(o.HaveLen(1))
		tag = is.Status.Tags[0]
		o.Expect(tag.Tag).To(o.Equal("latest"))
		o.Expect(tag.Items).To(o.HaveLen(1))
		o.Expect(tag.Items[0].DockerImageReference).To(o.Equal(fmt.Sprintf("%s/%s/%s@%s", registryHost, oc.Namespace(), isName2, digest)))
	})

	g.It("should work when only imagestreams api is available", func() {
		err := oc.Run("tag").Args("--source=docker", image.ShellImage(), "testis:latest").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = exutil.WaitForAnImageStreamTag(oc, oc.Namespace(), "testis", "latest")
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("create").Args("serviceaccount", "testsa").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		e2e.Logf("Creating a role that allows to work with imagestreams, but not imagestreamtags...")

		_, err = oc.AdminAuthorizationClient().AuthorizationV1().Roles(oc.Namespace()).Create(ctx, &authorizationv1.Role{
			ObjectMeta: metav1.ObjectMeta{
				Name: "testrole",
			},
			Rules: []authorizationv1.PolicyRule{
				{
					Verbs:     []string{"get", "update"},
					APIGroups: []string{"image.openshift.io"},
					Resources: []string{"imagestreams"},
				},
			},
		}, metav1.CreateOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("policy").Args("add-role-to-user", "testrole", "-z", "testsa", "--role-namespace="+oc.Namespace()).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		// TODO (soltysh): after k8s 1.24 lands we should be able to replace this with:
		// token, err := oc.Run("create").Args("token", "testsa").Output()
		token, _, err := oc.Run("serviceaccounts").Args("new-token", "testsa").Outputs()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("login").Args("--token=" + token).Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("whoami").Args().Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		err = oc.Run("tag").Args("testis:latest", "testis:copy").Execute()
		o.Expect(err).NotTo(o.HaveOccurred())

		e2e.Logf("Checking that the imagestream is updated...")

		is, err := oc.ImageClient().ImageV1().ImageStreams(oc.Namespace()).Get(ctx, "testis", metav1.GetOptions{})
		o.Expect(err).NotTo(o.HaveOccurred())

		var tags []string
		for _, t := range is.Spec.Tags {
			tags = append(tags, t.Name)
		}
		o.Expect(tags).To(o.ContainElement("copy"), "testis spec.tags should contain the tag copy")
	})
})
<|endoftext|>


<commit_before>/*
 * reporter-cli/sql-pdf/go/src/reporter_controller.go
 * ============================================================================
 * Reporter Multilang. Version 0.1
 * ============================================================================
 * A tool to generate human-readable reports based on data from various sources
 * with the focus on its implementation using a series of programming languages
 * ============================================================================
 * Written by Radislav (Radicchio) Golubtsov, 2016-2017
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import "database/sql"
import "reflect"
import "fmt"
import "strconv"
import "time"
import "strings"
import "github.com/jung-kurt/gofpdf"

/**
 * Constant: The start date to retrieve data set.
 *     TODO: Move to cli args.
 */
const FROM string = "2016-06-01"

/**
 * Constant: The end   date to retrieve data set.
 *     TODO: Move to cli args.
 */
const TO   string = "2016-12-01"

/**
 * Constant: The PDF report output location.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_DIR string = "lib/data"

/**
 * Constant: The PDF report filename.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_FILENAME string = "packages.pdf"

/** Constant: The maximum number of data rows displayed in a page. */
const MAX_ROWS_IN_A_PAGE uint = 40

/** Constant: The PDF basic measurement unit -- PostScript point. */
const PT float64 =   1

/** Constant: The one inch       (in PDF measurement terms). */
const IN float64 = ( 1   / 72)

/** Constant: The one millimeter (in PDF measurement terms). */
const MM float64 = (25.4 / 72)

/** Constant: The vertical coordinate flipping normalizer. */
const ZZ float64 = 297

/* Various string literals. */
const _REPORT_ORIENTATION_P string = "Portrait"
const _REPORT_UNITS_MM      string = "mm"
const _REPORT_PAGE_SIZE_A4  string = "A4"
// ----------------------------------------------------------------------------
const _RECT_STYLE_DRAW      string = "D"
const _RECT_STYLE_FILL      string = "F"
const _FONT_STYLE_BOLD      string = "B"
const _HELVETICA_FONT       string = "Helvetica"
const _TIMES_FONT           string = "Times"
// ----------------------------------------------------------------------------
const _ARCH_HEADER          string = "Arch"
const _REPO_HEADER          string = "Repo"
const _NAME_HEADER          string = "Name"
const _VERSION_HEADER       string = "Version"
const _LAST_UPDATED_HEADER  string = "Last Updated"
const _FLAG_DATE_HEADER     string = "Flag Date"
// ----------------------------------------------------------------------------
const _ROWS_IN_SET_FOOTER   string = " rows in set"
const _ROWS_SHOWN_FOOTER_1  string = "  ("
const _ROWS_SHOWN_FOOTER_2  string = " rows shown)"
const _PDF_REPORT_SAVED_MSG string = "PDF report saved"

/** The controller class of the application. */
type ReporterController struct {
    /*
     * Attached methods:
     *   - pdf_report_generate(cnx      *sql.DB,
     *                         postgres  bool,
     *                         exec      string) int
     *
     *   - _page_body_draw(report      *gofpdf.Fpdf,
     *                     hdr_set  [  ]string,
     *                     row_set  [][]string,
     *                     num_hdrs     uint,
     *                     num_rows     uint) int
     *
     *   - _get_pdf_report_path(exec string) string
     */
}

/**
 * Generates the PDF report.
 *
 * @param cnx      The database connection object.
 * @param postgres The indicator that shows whether the database connection
 *                 object is PostgreSQL connection.
 * @param exec     The executable path.
 *
 * @return The exit code indicating the status of generating the PDF report.
 */
func (ReporterController) pdf_report_generate(cnx      *sql.DB,
                                              postgres  bool,
                                              exec      string) int {

    var ret int = _EXIT_SUCCESS

    var class___ ReporterController
    var __name__ string = reflect.TypeOf(class___).Name()

    __file__ := exec

    var e error

    // Instantiating the model class.
    model := new(ReporterModel)

    // Retrieving a list of all data items stored in the database.
//  hdr_set, row_set := model.get_all_data_items(cnx)

    // Retrieving a list of data items for a given date period.
    hdr_set, row_set := model.get_data_items_by_date(FROM, TO, cnx, postgres)

    num_hdrs := uint(len(hdr_set))
    num_rows := uint(len(row_set))

    // In case of getting an empty result set, informing the user.
    if (num_hdrs == 0) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + _ERROR_NO_DATA +
                                                         _NEW_LINE)

        return ret
    }

    // ------------------------------------------------------------------------
    // --- Debug output - Begin -----------------------------------------------
    // ------------------------------------------------------------------------
    dbg_output := new(TabularDisplay)

    fmt.Printf(_S_FMT, dbg_output.render(hdr_set, row_set))

    fmt.Printf(_S_FMT, strconv.Itoa(int(num_rows)) + _ROWS_IN_SET_FOOTER +
                                         _NEW_LINE + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Debug output - End -------------------------------------------------
    // ------------------------------------------------------------------------

                            //     Waiting one second...
    time.Sleep(time.Second) // <== Just for fun...:-)...
                            //     Please!   --   OK.

    // ------------------------------------------------------------------------
    // --- Generating the PDF report - Begin ----------------------------------
    // ------------------------------------------------------------------------
    pdf_report_path := class___._get_pdf_report_path(__file__)

    report := gofpdf.New(_REPORT_ORIENTATION_P, // <== "Portrait".
                         _REPORT_UNITS_MM,
                         _REPORT_PAGE_SIZE_A4,  // <== 210 x 297 mm.
                         _EMPTY_STRING)

    report.AddPage()

    // --- Page body (data) ---------------------------------------------------
    ret=class___._page_body_draw(report, hdr_set, row_set, num_hdrs, num_rows)

    // Trying to save the report.
    e = report.OutputFileAndClose(pdf_report_path)

    if (e != nil) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + e.Error() + _NEW_LINE)

        return ret
    }

    fmt.Printf(_S_FMT, _PDF_REPORT_SAVED_MSG + _COLON_SPACE_SEP +
                        pdf_report_path      + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Generating the PDF report - End ------------------------------------
    // ------------------------------------------------------------------------

    return ret
}

/* Draws the PDF report page body (data). */
func (ReporterController) _page_body_draw(report      *gofpdf.Fpdf,
                                          hdr_set  [  ]string,
                                          row_set  [][]string,
                                          num_hdrs     uint,
                                          num_rows     uint) int {

    var ret int = _EXIT_SUCCESS

    var table_headers map[string]string

    // --- Border -------------------------------------------------------------

    report.SetDrawColor(34, 68, 136)        // <== _RAINY_NIGHT_COLOR (#224488)

    report.SetLineWidth(0.7)

    report.Rect(16, 19, 178, 259, _RECT_STYLE_DRAW)

    // --- Headers bar --------------------------------------------------------

    report.SetFillColor(34, 68, 136)        // <== _RAINY_NIGHT_COLOR (#224488)

    report.Rect(17, ((ZZ - 10) - 267), 176, 10, _RECT_STYLE_FILL)

    // --- Headers txt --------------------------------------------------------

    report.SetFont(_HELVETICA_FONT, _FONT_STYLE_BOLD, (16 / PT))

    report.SetTextColor(255, 255, 255)            // <== _WHITE_COLOR (#ffffff)

    table_headers = make(map[string]string)

    table_headers[hdr_set[0]] = _ARCH_HEADER
    table_headers[hdr_set[1]] = _REPO_HEADER
    table_headers[hdr_set[2]] = _NAME_HEADER
    table_headers[hdr_set[3]] = _VERSION_HEADER
    table_headers[hdr_set[4]] = _LAST_UPDATED_HEADER
    table_headers[hdr_set[5]] = _FLAG_DATE_HEADER

    var x float64 = 0

    // Printing table headers.
    for i := uint(0); i < num_hdrs; i++ {
               if (i == 1) {
            x =  17
        } else if (i == 2) {
            x =  40
        } else if (i == 3) {
            x =  78
        } else if (i == 4) {
            x = 107
        } else if (i == 5) {
            x = 146
        } else { // <== Includes (i == 0).
            x =   0
        }

        report.Text((20 + x), (ZZ - 270), table_headers[hdr_set[i]])
    }

    // --- Table rows ---------------------------------------------------------

    report.SetFont(_HELVETICA_FONT, _EMPTY_STRING, (11 / PT))

    report.SetTextColor(0, 0, 0)                  // <== _BLACK_COLOR (#000000)

    var y float64 = 0

    // Printing table rows.
//  for i := uint(0); i <           num_rows; i++ {
    for i := uint(0); i < MAX_ROWS_IN_A_PAGE; i++ {
        if ((i & 1) == 1) {
            report.SetFillColor(221, 221, 221) // <== _RAINY_DAY_COLOR(#dddddd)
            report.Rect(17, ((ZZ - 6) - (260 - y)), 176, 6, _RECT_STYLE_FILL)
        }

        for j := uint(0); j < num_hdrs; j++ {
                   if (j == 1) {
                x =  17
            } else if (j == 2) {
                x =  40
            } else if (j == 3) {
                x =  78
            } else if (j == 4) {
                x = 123
            } else if (j == 5) {
                x = 148
            } else { // <== Includes (j == 0).
                x =   0
            }

            report.Text((20 + x), (ZZ - (262 - y)), row_set[i][j])
        }

        y += 6
    }

    // --- Footer bar ---------------------------------------------------------

    report.SetFillColor(170, 170, 170) // <== _VERY_LIGHT_COBALT_COLOR(#aaaaaa)

    report.Rect(17, ((ZZ - 6) - 20), 176, 6, _RECT_STYLE_FILL)

    // --- Footer txt ---------------------------------------------------------

    report.SetFont(_TIMES_FONT, _FONT_STYLE_BOLD, (12 / PT))

    report.SetTextColor(238, 238, 238)    // <== _YET_NOT_WHITE_COLOR (#eeeeee)

    report.Text(20, (ZZ - 22), strconv.Itoa(int(num_rows))+_ROWS_IN_SET_FOOTER+
        _ROWS_SHOWN_FOOTER_1 + strconv.Itoa(int(MAX_ROWS_IN_A_PAGE)) +
        _ROWS_SHOWN_FOOTER_2)

    // ------------------------------------------------------------------------

    return ret
}

/*
 * Helper method.
 * Returns the generated PDF report output path,
 * relative to the executable's location.
 * TODO: Remove this method when the report output location
 *       will be passed through cli args.
 */
func (ReporterController) _get_pdf_report_path(exec string) string {
    exec_path := strings.Split(exec, _SLASH)

    var exec_path_len uint = uint(len(exec_path))

//  for i := uint(0); i < exec_path_len; i++ {
//      fmt.Printf(_S_FMT, exec_path[i] + _NEW_LINE)
//  }

    exec_path_new := make([]string, exec_path_len - 1)
    copy(exec_path_new, exec_path)
    exec_path_new[exec_path_len - 2] = PDF_REPORT_DIR

//  for i := uint(0); i < (exec_path_len - 1); i++ {
//      fmt.Printf(_S_FMT, exec_path_new[i] + _NEW_LINE)
//  }

    pdf_report_path := strings.Join(exec_path_new, _SLASH) + _SLASH +
                                                PDF_REPORT_FILENAME

    return pdf_report_path
}

// vim:set nu:et:ts=4:sw=4:
<commit_msg>SQL-PDF-Go: Create complete 20-pages PDF report using Kurt Jung's gofpdf Go package.<commit_after>/*
 * reporter-cli/sql-pdf/go/src/reporter_controller.go
 * ============================================================================
 * Reporter Multilang. Version 0.1
 * ============================================================================
 * A tool to generate human-readable reports based on data from various sources
 * with the focus on its implementation using a series of programming languages
 * ============================================================================
 * Written by Radislav (Radicchio) Golubtsov, 2016-2017
 *
 * This is free and unencumbered software released into the public domain.
 *
 * Anyone is free to copy, modify, publish, use, compile, sell, or
 * distribute this software, either in source code form or as a compiled
 * binary, for any purpose, commercial or non-commercial, and by any
 * means.
 *
 * (See the LICENSE file at the top of the source tree.)
 */

package main

import "database/sql"
import "reflect"
import "fmt"
import "strconv"
import "time"
import "strings"
import "github.com/jung-kurt/gofpdf"

/**
 * Constant: The start date to retrieve data set.
 *     TODO: Move to cli args.
 */
const FROM string = "2016-06-01"

/**
 * Constant: The end   date to retrieve data set.
 *     TODO: Move to cli args.
 */
const TO   string = "2016-12-01"

/**
 * Constant: The PDF report output location.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_DIR string = "lib/data"

/**
 * Constant: The PDF report filename.
 *     TODO: Move to cli args.
 */
const PDF_REPORT_FILENAME string = "packages.pdf"

/**
 * Constant: The number of pages generated in a PDF report.
 *     TODO: Move to cli args.
 */
const MAX_PAGES uint = 20

/** Constant: The maximum number of data rows displayed in a page. */
const MAX_ROWS_IN_A_PAGE uint = 40

/** Constant: The PDF basic measurement unit -- PostScript point. */
const PT float64 =   1

/** Constant: The one inch       (in PDF measurement terms). */
const IN float64 = ( 1   / 72)

/** Constant: The one millimeter (in PDF measurement terms). */
const MM float64 = (25.4 / 72)

/** Constant: The vertical coordinate flipping normalizer. */
const ZZ float64 = 297

/* Various string literals. */
const _REPORT_ORIENTATION_P string = "Portrait"
const _REPORT_UNITS_MM      string = "mm"
const _REPORT_PAGE_SIZE_A4  string = "A4"
// ----------------------------------------------------------------------------
const _RECT_STYLE_DRAW      string = "D"
const _RECT_STYLE_FILL      string = "F"
const _FONT_STYLE_BOLD      string = "B"
const _HELVETICA_FONT       string = "Helvetica"
const _TIMES_FONT           string = "Times"
// ----------------------------------------------------------------------------
const _ARCH_HEADER          string = "Arch"
const _REPO_HEADER          string = "Repo"
const _NAME_HEADER          string = "Name"
const _VERSION_HEADER       string = "Version"
const _LAST_UPDATED_HEADER  string = "Last Updated"
const _FLAG_DATE_HEADER     string = "Flag Date"
// ----------------------------------------------------------------------------
const _ROWS_IN_SET_FOOTER   string = " rows in set"
const _ROWS_SHOWN_FOOTER_1  string = "  ("
const _ROWS_SHOWN_FOOTER_2  string = " rows shown)"
const _PDF_REPORT_SAVED_MSG string = "PDF report saved"

/** The controller class of the application. */
type ReporterController struct {
    /*
     * Attached methods:
     *   - pdf_report_generate(cnx      *sql.DB,
     *                         postgres  bool,
     *                         exec      string) int
     *
     *   - _page_body_draw(report      *gofpdf.Fpdf,
     *                     hdr_set  [  ]string,
     *                     row_set  [][]string,
     *                     num_hdrs     uint,
     *                     num_rows     uint) int
     *
     *   - _get_pdf_report_path(exec string) string
     */
}

/**
 * Generates the PDF report.
 *
 * @param cnx      The database connection object.
 * @param postgres The indicator that shows whether the database connection
 *                 object is PostgreSQL connection.
 * @param exec     The executable path.
 *
 * @return The exit code indicating the status of generating the PDF report.
 */
func (ReporterController) pdf_report_generate(cnx      *sql.DB,
                                              postgres  bool,
                                              exec      string) int {

    var ret int = _EXIT_SUCCESS

    var class___ ReporterController
    var __name__ string = reflect.TypeOf(class___).Name()

    __file__ := exec

    var e error

    // Instantiating the model class.
    model := new(ReporterModel)

    // Retrieving a list of all data items stored in the database.
//  hdr_set, row_set := model.get_all_data_items(cnx)

    // Retrieving a list of data items for a given date period.
    hdr_set, row_set := model.get_data_items_by_date(FROM, TO, cnx, postgres)

    num_hdrs := uint(len(hdr_set))
    num_rows := uint(len(row_set))

    // In case of getting an empty result set, informing the user.
    if (num_hdrs == 0) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + _ERROR_NO_DATA +
                                                         _NEW_LINE)

        return ret
    }

    // ------------------------------------------------------------------------
    // --- Debug output - Begin -----------------------------------------------
    // ------------------------------------------------------------------------
    dbg_output := new(TabularDisplay)

    fmt.Printf(_S_FMT, dbg_output.render(hdr_set, row_set))

    fmt.Printf(_S_FMT, strconv.Itoa(int(num_rows)) + _ROWS_IN_SET_FOOTER +
                                         _NEW_LINE + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Debug output - End -------------------------------------------------
    // ------------------------------------------------------------------------

                            //     Waiting one second...
    time.Sleep(time.Second) // <== Just for fun...:-)...
                            //     Please!   --   OK.

    // ------------------------------------------------------------------------
    // --- Generating the PDF report - Begin ----------------------------------
    // ------------------------------------------------------------------------
    pdf_report_path := class___._get_pdf_report_path(__file__)

    report := gofpdf.New(_REPORT_ORIENTATION_P, // <== "Portrait".
                         _REPORT_UNITS_MM,
                         _REPORT_PAGE_SIZE_A4,  // <== 210 x 297 mm.
                         _EMPTY_STRING)

    // --- Page body (data) x MAX_PAGES ---------------------------------------
    for i := uint(0); i < MAX_PAGES; i++ {
        report.AddPage()

        ret=class___._page_body_draw(report,hdr_set,row_set,num_hdrs,num_rows)

        if (ret == _EXIT_FAILURE) {
            fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                       _COLON_SPACE_SEP + _ERROR_NO_REPORT_GEN + _NEW_LINE)

            return ret
        }
    }

    // Trying to save the report.
    e = report.OutputFileAndClose(pdf_report_path)

    if (e != nil) {
        ret = _EXIT_FAILURE

        fmt.Printf(_S_FMT, __name__ + _COLON_SPACE_SEP + _ERROR_PREFIX +
                                      _COLON_SPACE_SEP + e.Error() + _NEW_LINE)

        return ret
    }

    fmt.Printf(_S_FMT, _PDF_REPORT_SAVED_MSG + _COLON_SPACE_SEP +
                        pdf_report_path      + _NEW_LINE)
    // ------------------------------------------------------------------------
    // --- Generating the PDF report - End ------------------------------------
    // ------------------------------------------------------------------------

    return ret
}

/* Draws the PDF report page body (data). */
func (ReporterController) _page_body_draw(report      *gofpdf.Fpdf,
                                          hdr_set  [  ]string,
                                          row_set  [][]string,
                                          num_hdrs     uint,
                                          num_rows     uint) int {

    var ret int = _EXIT_SUCCESS

    var table_headers map[string]string

    // --- Border -------------------------------------------------------------

    report.SetDrawColor(34, 68, 136)        // <== _RAINY_NIGHT_COLOR (#224488)

    report.SetLineWidth(0.7)

    report.Rect(16, 19, 178, 259, _RECT_STYLE_DRAW)

    // --- Headers bar --------------------------------------------------------

    report.SetFillColor(34, 68, 136)        // <== _RAINY_NIGHT_COLOR (#224488)

    report.Rect(17, ((ZZ - 10) - 267), 176, 10, _RECT_STYLE_FILL)

    // --- Headers txt --------------------------------------------------------

    report.SetFont(_HELVETICA_FONT, _FONT_STYLE_BOLD, (16 / PT))

    report.SetTextColor(255, 255, 255)            // <== _WHITE_COLOR (#ffffff)

    table_headers = make(map[string]string)

    table_headers[hdr_set[0]] = _ARCH_HEADER
    table_headers[hdr_set[1]] = _REPO_HEADER
    table_headers[hdr_set[2]] = _NAME_HEADER
    table_headers[hdr_set[3]] = _VERSION_HEADER
    table_headers[hdr_set[4]] = _LAST_UPDATED_HEADER
    table_headers[hdr_set[5]] = _FLAG_DATE_HEADER

    var x float64 = 0

    // Printing table headers.
    for i := uint(0); i < num_hdrs; i++ {
               if (i == 1) {
            x =  17
        } else if (i == 2) {
            x =  40
        } else if (i == 3) {
            x =  78
        } else if (i == 4) {
            x = 107
        } else if (i == 5) {
            x = 146
        } else { // <== Includes (i == 0).
            x =   0
        }

        report.Text((20 + x), (ZZ - 270), table_headers[hdr_set[i]])
    }

    // --- Table rows ---------------------------------------------------------

    report.SetFont(_HELVETICA_FONT, _EMPTY_STRING, (11 / PT))

    report.SetTextColor(0, 0, 0)                  // <== _BLACK_COLOR (#000000)

    var y float64 = 0

    // Printing table rows.
//  for i := uint(0); i <           num_rows; i++ {
    for i := uint(0); i < MAX_ROWS_IN_A_PAGE; i++ {
        if ((i & 1) == 1) {
            report.SetFillColor(221, 221, 221) // <== _RAINY_DAY_COLOR(#dddddd)
            report.Rect(17, ((ZZ - 6) - (260 - y)), 176, 6, _RECT_STYLE_FILL)
        }

        for j := uint(0); j < num_hdrs; j++ {
                   if (j == 1) {
                x =  17
            } else if (j == 2) {
                x =  40
            } else if (j == 3) {
                x =  78
            } else if (j == 4) {
                x = 123
            } else if (j == 5) {
                x = 148
            } else { // <== Includes (j == 0).
                x =   0
            }

            report.Text((20 + x), (ZZ - (262 - y)), row_set[i][j])
        }

        y += 6
    }

    // --- Footer bar ---------------------------------------------------------

    report.SetFillColor(170, 170, 170) // <== _VERY_LIGHT_COBALT_COLOR(#aaaaaa)

    report.Rect(17, ((ZZ - 6) - 20), 176, 6, _RECT_STYLE_FILL)

    // --- Footer txt ---------------------------------------------------------

    report.SetFont(_TIMES_FONT, _FONT_STYLE_BOLD, (12 / PT))

    report.SetTextColor(238, 238, 238)    // <== _YET_NOT_WHITE_COLOR (#eeeeee)

    report.Text(20, (ZZ - 22), strconv.Itoa(int(num_rows))+_ROWS_IN_SET_FOOTER+
        _ROWS_SHOWN_FOOTER_1 + strconv.Itoa(int(MAX_ROWS_IN_A_PAGE)) +
        _ROWS_SHOWN_FOOTER_2)

    // ------------------------------------------------------------------------

    return ret
}

/*
 * Helper method.
 * Returns the generated PDF report output path,
 * relative to the executable's location.
 * TODO: Remove this method when the report output location
 *       will be passed through cli args.
 */
func (ReporterController) _get_pdf_report_path(exec string) string {
    exec_path := strings.Split(exec, _SLASH)

    var exec_path_len uint = uint(len(exec_path))

//  for i := uint(0); i < exec_path_len; i++ {
//      fmt.Printf(_S_FMT, exec_path[i] + _NEW_LINE)
//  }

    exec_path_new := make([]string, exec_path_len - 1)
    copy(exec_path_new, exec_path)
    exec_path_new[exec_path_len - 2] = PDF_REPORT_DIR

//  for i := uint(0); i < (exec_path_len - 1); i++ {
//      fmt.Printf(_S_FMT, exec_path_new[i] + _NEW_LINE)
//  }

    pdf_report_path := strings.Join(exec_path_new, _SLASH) + _SLASH +
                                                PDF_REPORT_FILENAME

    return pdf_report_path
}

// vim:set nu:et:ts=4:sw=4:
<|endoftext|>


<commit_before>package hdf5

// #include <stdlib.h>
// #include <hdf5.h>
import "C"

import (
	"unsafe"
)

const (
	flagReference = 1 << iota
	flagOwned
)

type object struct {
	data unsafe.Pointer
	flag uint8
	tid  C.hid_t
	deps []*object
}

func newObject() *object {
	return &object{
		tid: -1,
	}
}

func (o *object) free() {
	for i := range o.deps {
		o.deps[i].free()
	}
	if o.tid >= 0 {
		_ = C.H5Tclose(o.tid)
	}
	if o.data != nil && o.flag&flagOwned != 0 {
		C.free(o.data)
	}
}
<commit_msg>A cosmetic change<commit_after>package hdf5

// #include <stdlib.h>
// #include <hdf5.h>
import "C"

import (
	"unsafe"
)

const (
	flagReference = 1 << iota
	flagOwned
)

type object struct {
	data unsafe.Pointer
	flag uint8
	tid  C.hid_t
	deps []*object
}

func newObject() *object {
	return &object{
		tid: -1,
	}
}

func (o *object) free() {
	for i := range o.deps {
		o.deps[i].free()
	}
	if o.tid >= 0 {
		C.H5Tclose(o.tid)
	}
	if o.data != nil && o.flag&flagOwned != 0 {
		C.free(o.data)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"testing"

	"github.com/lxc/lxd/lxd/db"
)

func Test_removing_a_profile_deletes_associated_configuration_entries(t *testing.T) {
	cluster, cleanup := db.NewTestCluster(t)
	defer cleanup()
	tx, err := cluster.DB().Begin()
	if err != nil {
		t.Fatal(err)
	}

	// Insert a container and a related profile. Dont't forget that the profile
	// we insert is profile ID 2 (there is a default profile already).
	statements := `
    INSERT INTO containers (node_id, name, architecture, type, project_id) VALUES (1, 'thename', 1, 1, 1);
    INSERT INTO profiles (name, project_id) VALUES ('theprofile', 1);
    INSERT INTO containers_profiles (container_id, profile_id) VALUES (1, 2);
    INSERT INTO profiles_devices (name, profile_id) VALUES ('somename', 2);
    INSERT INTO profiles_config (key, value, profile_id) VALUES ('thekey', 'thevalue', 2);
    INSERT INTO profiles_devices_config (profile_device_id, key, value) VALUES (1, 'something', 'boring');`

	_, err = tx.Exec(statements)
	if err != nil {
		t.Fatal(err)
	}
	err = tx.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Delete the profile we just created with dbapi.ProfileDelete
	err = cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.ProfileDelete("default", "theprofile")
	})
	if err != nil {
		t.Fatal(err)
	}

	// Make sure there are 0 profiles_devices entries left.
	devices, err := cluster.Devices("default", "theprofile", true)
	if err != nil {
		t.Fatal(err)
	}
	if len(devices) != 0 {
		t.Errorf("Deleting a profile didn't delete the related profiles_devices! There are %d left", len(devices))
	}

	// Make sure there are 0 profiles_config entries left.
	config, err := cluster.ProfileConfig("default", "theprofile")
	if err == nil {
		t.Fatal("found the profile!")
	}

	if len(config) != 0 {
		t.Errorf("Deleting a profile didn't delete the related profiles_config! There are %d left", len(config))
	}
}
<commit_msg>Update top-level unit-tests<commit_after>package main

import (
	"testing"

	"github.com/lxc/lxd/lxd/db"
)

func Test_removing_a_profile_deletes_associated_configuration_entries(t *testing.T) {
	cluster, cleanup := db.NewTestCluster(t)
	defer cleanup()
	tx, err := cluster.DB().Begin()
	if err != nil {
		t.Fatal(err)
	}

	// Insert a container and a related profile. Dont't forget that the profile
	// we insert is profile ID 2 (there is a default profile already).
	statements := `
    INSERT INTO instances (node_id, name, architecture, type, project_id) VALUES (1, 'thename', 1, 1, 1);
    INSERT INTO profiles (name, project_id) VALUES ('theprofile', 1);
    INSERT INTO instances_profiles (instance_id, profile_id) VALUES (1, 2);
    INSERT INTO profiles_devices (name, profile_id) VALUES ('somename', 2);
    INSERT INTO profiles_config (key, value, profile_id) VALUES ('thekey', 'thevalue', 2);
    INSERT INTO profiles_devices_config (profile_device_id, key, value) VALUES (1, 'something', 'boring');`

	_, err = tx.Exec(statements)
	if err != nil {
		t.Fatal(err)
	}
	err = tx.Commit()
	if err != nil {
		t.Fatal(err)
	}

	// Delete the profile we just created with dbapi.ProfileDelete
	err = cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.ProfileDelete("default", "theprofile")
	})
	if err != nil {
		t.Fatal(err)
	}

	// Make sure there are 0 profiles_devices entries left.
	devices, err := cluster.Devices("default", "theprofile", true)
	if err != nil {
		t.Fatal(err)
	}
	if len(devices) != 0 {
		t.Errorf("Deleting a profile didn't delete the related profiles_devices! There are %d left", len(devices))
	}

	// Make sure there are 0 profiles_config entries left.
	config, err := cluster.ProfileConfig("default", "theprofile")
	if err == nil {
		t.Fatal("found the profile!")
	}

	if len(config) != 0 {
		t.Errorf("Deleting a profile didn't delete the related profiles_config! There are %d left", len(config))
	}
}
<|endoftext|>


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

// HTTP file system request handler

package http

import (
	"fmt"
	"io"
	"mime"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	"utf8"
)

// Heuristic: b is text if it is valid UTF-8 and doesn't
// contain any unprintable ASCII or Unicode characters.
func isText(b []byte) bool {
	for len(b) > 0 && utf8.FullRune(b) {
		rune, size := utf8.DecodeRune(b)
		if size == 1 && rune == utf8.RuneError {
			// decoding error
			return false
		}
		if 0x7F <= rune && rune <= 0x9F {
			return false
		}
		if rune < ' ' {
			switch rune {
			case '\n', '\r', '\t':
				// okay
			default:
				// binary garbage
				return false
			}
		}
		b = b[size:]
	}
	return true
}

func dirList(w ResponseWriter, f *os.File) {
	fmt.Fprintf(w, "
\n")
	for {
		dirs, err := f.Readdir(100)
		if err != nil || len(dirs) == 0 {
			break
		}
		for _, d := range dirs {
			name := d.Name
			if d.IsDirectory() {
				name += "/"
			}
			// TODO htmlescape
			fmt.Fprintf(w, "%s\n", name, name)
		}
	}
	fmt.Fprintf(w, "
\n")
}

func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
	const indexPage = "/index.html"

	// redirect .../index.html to .../
	if strings.HasSuffix(r.URL.Path, indexPage) {
		Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently)
		return
	}

	f, err := os.Open(name, os.O_RDONLY, 0)
	if err != nil {
		// TODO expose actual error?
		NotFound(w, r)
		return
	}
	defer f.Close()

	d, err1 := f.Stat()
	if err1 != nil {
		// TODO expose actual error?
		NotFound(w, r)
		return
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path
		if d.IsDirectory() {
			if url[len(url)-1] != '/' {
				Redirect(w, r, url+"/", StatusMovedPermanently)
				return
			}
		} else {
			if url[len(url)-1] == '/' {
				Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently)
				return
			}
		}
	}

	if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t != nil && d.Mtime_ns/1e9 <= t.Seconds() {
		w.WriteHeader(StatusNotModified)
		return
	}
	w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))

	// use contents of index.html for directory, if present
	if d.IsDirectory() {
		index := name + filepath.FromSlash(indexPage)
		ff, err := os.Open(index, os.O_RDONLY, 0)
		if err == nil {
			defer ff.Close()
			dd, err := ff.Stat()
			if err == nil {
				name = index
				d = dd
				f = ff
			}
		}
	}

	if d.IsDirectory() {
		dirList(w, f)
		return
	}

	// serve file
	size := d.Size
	code := StatusOK

	// use extension to find content type.
	ext := filepath.Ext(name)
	if ctype := mime.TypeByExtension(ext); ctype != "" {
		w.Header().Set("Content-Type", ctype)
	} else {
		// read first chunk to decide between utf-8 text and binary
		var buf [1024]byte
		n, _ := io.ReadFull(f, buf[:])
		b := buf[:n]
		if isText(b) {
			w.Header().Set("Content-Type", "text-plain; charset=utf-8")
		} else {
			w.Header().Set("Content-Type", "application/octet-stream") // generic binary
		}
		f.Seek(0, 0) // rewind to output whole file
	}

	// handle Content-Range header.
	// TODO(adg): handle multiple ranges
	ranges, err := parseRange(r.Header.Get("Range"), size)
	if err != nil || len(ranges) > 1 {
		Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
		return
	}
	if len(ranges) == 1 {
		ra := ranges[0]
		if _, err := f.Seek(ra.start, 0); err != nil {
			Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
			return
		}
		size = ra.length
		code = StatusPartialContent
		w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
	}

	w.Header().Set("Accept-Ranges", "bytes")
	w.Header().Set("Content-Length", strconv.Itoa64(size))

	w.WriteHeader(code)

	if r.Method != "HEAD" {
		io.Copyn(w, f, size)
	}
}

// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w ResponseWriter, r *Request, name string) {
	serveFile(w, r, name, false)
}

type fileHandler struct {
	root   string
	prefix string
}

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
// It strips prefix from the incoming requests before
// looking up the file name in the file system.
func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} }

func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
	path := r.URL.Path
	if !strings.HasPrefix(path, f.prefix) {
		NotFound(w, r)
		return
	}
	path = path[len(f.prefix):]
	serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true)
}

// httpRange specifies the byte range to be sent to the client.
type httpRange struct {
	start, length int64
}

// parseRange parses a Range header string as per RFC 2616.
func parseRange(s string, size int64) ([]httpRange, os.Error) {
	if s == "" {
		return nil, nil // header not present
	}
	const b = "bytes="
	if !strings.HasPrefix(s, b) {
		return nil, os.NewError("invalid range")
	}
	var ranges []httpRange
	for _, ra := range strings.Split(s[len(b):], ",", -1) {
		i := strings.Index(ra, "-")
		if i < 0 {
			return nil, os.NewError("invalid range")
		}
		start, end := ra[:i], ra[i+1:]
		var r httpRange
		if start == "" {
			// If no start is specified, end specifies the
			// range start relative to the end of the file.
			i, err := strconv.Atoi64(end)
			if err != nil {
				return nil, os.NewError("invalid range")
			}
			if i > size {
				i = size
			}
			r.start = size - i
			r.length = size - r.start
		} else {
			i, err := strconv.Atoi64(start)
			if err != nil || i > size || i < 0 {
				return nil, os.NewError("invalid range")
			}
			r.start = i
			if end == "" {
				// If no end is specified, range extends to end of the file.
				r.length = size - r.start
			} else {
				i, err := strconv.Atoi64(end)
				if err != nil || r.start > i {
					return nil, os.NewError("invalid range")
				}
				if i >= size {
					i = size - 1
				}
				r.length = i - r.start + 1
			}
		}
		ranges = append(ranges, r)
	}
	return ranges, nil
}
<commit_msg>http: avoid crash when asked for multiple file ranges<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// HTTP file system request handler

package http

import (
	"fmt"
	"io"
	"mime"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
	"utf8"
)

// Heuristic: b is text if it is valid UTF-8 and doesn't
// contain any unprintable ASCII or Unicode characters.
func isText(b []byte) bool {
	for len(b) > 0 && utf8.FullRune(b) {
		rune, size := utf8.DecodeRune(b)
		if size == 1 && rune == utf8.RuneError {
			// decoding error
			return false
		}
		if 0x7F <= rune && rune <= 0x9F {
			return false
		}
		if rune < ' ' {
			switch rune {
			case '\n', '\r', '\t':
				// okay
			default:
				// binary garbage
				return false
			}
		}
		b = b[size:]
	}
	return true
}

func dirList(w ResponseWriter, f *os.File) {
	fmt.Fprintf(w, "
\n")
	for {
		dirs, err := f.Readdir(100)
		if err != nil || len(dirs) == 0 {
			break
		}
		for _, d := range dirs {
			name := d.Name
			if d.IsDirectory() {
				name += "/"
			}
			// TODO htmlescape
			fmt.Fprintf(w, "%s\n", name, name)
		}
	}
	fmt.Fprintf(w, "
\n")
}

func serveFile(w ResponseWriter, r *Request, name string, redirect bool) {
	const indexPage = "/index.html"

	// redirect .../index.html to .../
	if strings.HasSuffix(r.URL.Path, indexPage) {
		Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], StatusMovedPermanently)
		return
	}

	f, err := os.Open(name, os.O_RDONLY, 0)
	if err != nil {
		// TODO expose actual error?
		NotFound(w, r)
		return
	}
	defer f.Close()

	d, err1 := f.Stat()
	if err1 != nil {
		// TODO expose actual error?
		NotFound(w, r)
		return
	}

	if redirect {
		// redirect to canonical path: / at end of directory url
		// r.URL.Path always begins with /
		url := r.URL.Path
		if d.IsDirectory() {
			if url[len(url)-1] != '/' {
				Redirect(w, r, url+"/", StatusMovedPermanently)
				return
			}
		} else {
			if url[len(url)-1] == '/' {
				Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently)
				return
			}
		}
	}

	if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t != nil && d.Mtime_ns/1e9 <= t.Seconds() {
		w.WriteHeader(StatusNotModified)
		return
	}
	w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))

	// use contents of index.html for directory, if present
	if d.IsDirectory() {
		index := name + filepath.FromSlash(indexPage)
		ff, err := os.Open(index, os.O_RDONLY, 0)
		if err == nil {
			defer ff.Close()
			dd, err := ff.Stat()
			if err == nil {
				name = index
				d = dd
				f = ff
			}
		}
	}

	if d.IsDirectory() {
		dirList(w, f)
		return
	}

	// serve file
	size := d.Size
	code := StatusOK

	// use extension to find content type.
	ext := filepath.Ext(name)
	if ctype := mime.TypeByExtension(ext); ctype != "" {
		w.Header().Set("Content-Type", ctype)
	} else {
		// read first chunk to decide between utf-8 text and binary
		var buf [1024]byte
		n, _ := io.ReadFull(f, buf[:])
		b := buf[:n]
		if isText(b) {
			w.Header().Set("Content-Type", "text-plain; charset=utf-8")
		} else {
			w.Header().Set("Content-Type", "application/octet-stream") // generic binary
		}
		f.Seek(0, 0) // rewind to output whole file
	}

	// handle Content-Range header.
	// TODO(adg): handle multiple ranges
	ranges, err := parseRange(r.Header.Get("Range"), size)
	if err == nil && len(ranges) > 1 {
		err = os.ErrorString("multiple ranges not supported")
	}
	if err != nil {
		Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
		return
	}
	if len(ranges) == 1 {
		ra := ranges[0]
		if _, err := f.Seek(ra.start, 0); err != nil {
			Error(w, err.String(), StatusRequestedRangeNotSatisfiable)
			return
		}
		size = ra.length
		code = StatusPartialContent
		w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", ra.start, ra.start+ra.length-1, d.Size))
	}

	w.Header().Set("Accept-Ranges", "bytes")
	w.Header().Set("Content-Length", strconv.Itoa64(size))

	w.WriteHeader(code)

	if r.Method != "HEAD" {
		io.Copyn(w, f, size)
	}
}

// ServeFile replies to the request with the contents of the named file or directory.
func ServeFile(w ResponseWriter, r *Request, name string) {
	serveFile(w, r, name, false)
}

type fileHandler struct {
	root   string
	prefix string
}

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
// It strips prefix from the incoming requests before
// looking up the file name in the file system.
func FileServer(root, prefix string) Handler { return &fileHandler{root, prefix} }

func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
	path := r.URL.Path
	if !strings.HasPrefix(path, f.prefix) {
		NotFound(w, r)
		return
	}
	path = path[len(f.prefix):]
	serveFile(w, r, filepath.Join(f.root, filepath.FromSlash(path)), true)
}

// httpRange specifies the byte range to be sent to the client.
type httpRange struct {
	start, length int64
}

// parseRange parses a Range header string as per RFC 2616.
func parseRange(s string, size int64) ([]httpRange, os.Error) {
	if s == "" {
		return nil, nil // header not present
	}
	const b = "bytes="
	if !strings.HasPrefix(s, b) {
		return nil, os.NewError("invalid range")
	}
	var ranges []httpRange
	for _, ra := range strings.Split(s[len(b):], ",", -1) {
		i := strings.Index(ra, "-")
		if i < 0 {
			return nil, os.NewError("invalid range")
		}
		start, end := ra[:i], ra[i+1:]
		var r httpRange
		if start == "" {
			// If no start is specified, end specifies the
			// range start relative to the end of the file.
			i, err := strconv.Atoi64(end)
			if err != nil {
				return nil, os.NewError("invalid range")
			}
			if i > size {
				i = size
			}
			r.start = size - i
			r.length = size - r.start
		} else {
			i, err := strconv.Atoi64(start)
			if err != nil || i > size || i < 0 {
				return nil, os.NewError("invalid range")
			}
			r.start = i
			if end == "" {
				// If no end is specified, range extends to end of the file.
				r.length = size - r.start
			} else {
				i, err := strconv.Atoi64(end)
				if err != nil || r.start > i {
					return nil, os.NewError("invalid range")
				}
				if i >= size {
					i = size - 1
				}
				r.length = i - r.start + 1
			}
		}
		ranges = append(ranges, r)
	}
	return ranges, nil
}
<|endoftext|>


<commit_before>package gophercloud

import (
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

/*
MaybeString is an internal function to be used by request methods in individual
resource packages.

It takes a string that might be a zero value and returns either a pointer to its
address or nil. This is useful for allowing users to conveniently omit values
from an options struct, but still pass nil to the JSON serializer to omit them
from a request body.
*/
func MaybeString(original string) *string {
	if original != "" {
		return &original
	}
	return nil
}

/*
MaybeInt is an internal function to be used by request methods in individual
resource packages.

Like MaybeString, it accepts an int that may be a zero value and returns either
a pointer to its address or nil to hint to the JSON serializer to omit its
field.
*/
func MaybeInt(original int) *int {
	if original != 0 {
		return &original
	}
	return nil
}

var t time.Time

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Func, reflect.Map, reflect.Slice:
		return v.IsNil()
	case reflect.Array:
		z := true
		for i := 0; i < v.Len(); i++ {
			z = z && isZero(v.Index(i))
		}
		return z
	case reflect.Struct:
		if v.Type() == reflect.TypeOf(t) {
			if v.Interface().(time.Time).IsZero() {
				return true
			}
			return false
		}
		z := true
		for i := 0; i < v.NumField(); i++ {
			z = z && isZero(v.Field(i))
		}
		return z
	}
	// Compare other types directly:
	z := reflect.Zero(v.Type())
	return v.Interface() == z.Interface()
}

/*
BuildQueryString is an internal function to be used by request methods in
individual resource packages.

It accepts a tagged structure and expands it into a URL struct. Field names are
converted into query parameters based on a "q" tag. For example:

	type struct Something {
	   Bar string `q:"x_bar"`
	   Baz int    `q:"lorem_ipsum"`
	}

	instance := Something{
	   Bar: "AAA",
	   Baz: "BBB",
	}

will be converted into "?x_bar=AAA&lorem_ipsum=BBB".

The struct's fields may be strings, integers, or boolean values. Fields left at
their type's zero value will be omittted from the query.
*/
func BuildQueryString(opts interface{}) (*url.URL, error) {
	optsValue := reflect.ValueOf(opts)
	if optsValue.Kind() == reflect.Ptr {
		optsValue = optsValue.Elem()
	}

	optsType := reflect.TypeOf(opts)
	if optsType.Kind() == reflect.Ptr {
		optsType = optsType.Elem()
	}

	var optsSlice []string
	if optsValue.Kind() == reflect.Struct {
		for i := 0; i < optsValue.NumField(); i++ {
			v := optsValue.Field(i)
			f := optsType.Field(i)
			qTag := f.Tag.Get("q")

			// if the field has a 'q' tag, it goes in the query string
			if qTag != "" {
				tags := strings.Split(qTag, ",")

				// if the field is set, add it to the slice of query pieces
				if !isZero(v) {
					switch v.Kind() {
					case reflect.String:
						optsSlice = append(optsSlice, tags[0]+"="+v.String())
					case reflect.Int:
						optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatInt(v.Int(), 10))
					case reflect.Bool:
						optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatBool(v.Bool()))
					}
				} else {
					// Otherwise, the field is not set.
					if len(tags) == 2 && tags[1] == "required" {
						// And the field is required. Return an error.
						return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name)
					}
				}
			}

		}
		// URL encode the string for safety.
		s := strings.Join(optsSlice, "&")
		if s != "" {
			s = "?" + s
		}
		u, err := url.Parse(s)
		if err != nil {
			return nil, err
		}
		return u, nil
	}
	// Return an error if the underlying type of 'opts' isn't a struct.
	return nil, fmt.Errorf("Options type is not a struct.")
}

/*
BuildHeaders is an internal function to be used by request methods in
individual resource packages.

It accepts a arbitrary tagged structure and produces a string map that's
suitable for use as the HTTP headers of an outgoing request. Field names are
mapped to header names based in "h" tags.

  type struct Something {
    Bar string `h:"x_bar"`
    Baz int    `h:"lorem_ipsum"`
  }

  instance := Something{
    Bar: "AAA",
    Baz: "BBB",
  }

will be converted into:

  map[string]string{
    "x_bar": "AAA",
    "lorem_ipsum": "BBB",
  }

Untagged fields and fields left at their zero values are skipped. Integers,
booleans and string values are supported.
*/
func BuildHeaders(opts interface{}) (map[string]string, error) {
	optsValue := reflect.ValueOf(opts)
	if optsValue.Kind() == reflect.Ptr {
		optsValue = optsValue.Elem()
	}

	optsType := reflect.TypeOf(opts)
	if optsType.Kind() == reflect.Ptr {
		optsType = optsType.Elem()
	}

	optsMap := make(map[string]string)
	if optsValue.Kind() == reflect.Struct {
		for i := 0; i < optsValue.NumField(); i++ {
			v := optsValue.Field(i)
			f := optsType.Field(i)
			hTag := f.Tag.Get("h")

			// if the field has a 'h' tag, it goes in the header
			if hTag != "" {
				tags := strings.Split(hTag, ",")

				// if the field is set, add it to the slice of query pieces
				if !isZero(v) {
					switch v.Kind() {
					case reflect.String:
						optsMap[tags[0]] = v.String()
					case reflect.Int:
						optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
					case reflect.Bool:
						optsMap[tags[0]] = strconv.FormatBool(v.Bool())
					}
				} else {
					// Otherwise, the field is not set.
					if len(tags) == 2 && tags[1] == "required" {
						// And the field is required. Return an error.
						return optsMap, fmt.Errorf("Required header not set.")
					}
				}
			}

		}
		return optsMap, nil
	}
	// Return an error if the underlying type of 'opts' isn't a struct.
	return optsMap, fmt.Errorf("Options type is not a struct.")
}
<commit_msg>A second read on the wording there.<commit_after>package gophercloud

import (
	"fmt"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

/*
MaybeString is an internal function to be used by request methods in individual
resource packages.

It takes a string that might be a zero value and returns either a pointer to its
address or nil. This is useful for allowing users to conveniently omit values
from an options struct by leaving them zeroed, but still pass nil to the JSON
serializer so they'll be omitted from the request body.
*/
func MaybeString(original string) *string {
	if original != "" {
		return &original
	}
	return nil
}

/*
MaybeInt is an internal function to be used by request methods in individual
resource packages.

Like MaybeString, it accepts an int that may or may not be a zero value, and
returns either a pointer to its address or nil. It's intended to hint that the
JSON serializer should omit its field.
*/
func MaybeInt(original int) *int {
	if original != 0 {
		return &original
	}
	return nil
}

var t time.Time

func isZero(v reflect.Value) bool {
	switch v.Kind() {
	case reflect.Func, reflect.Map, reflect.Slice:
		return v.IsNil()
	case reflect.Array:
		z := true
		for i := 0; i < v.Len(); i++ {
			z = z && isZero(v.Index(i))
		}
		return z
	case reflect.Struct:
		if v.Type() == reflect.TypeOf(t) {
			if v.Interface().(time.Time).IsZero() {
				return true
			}
			return false
		}
		z := true
		for i := 0; i < v.NumField(); i++ {
			z = z && isZero(v.Field(i))
		}
		return z
	}
	// Compare other types directly:
	z := reflect.Zero(v.Type())
	return v.Interface() == z.Interface()
}

/*
BuildQueryString is an internal function to be used by request methods in
individual resource packages.

It accepts a tagged structure and expands it into a URL struct. Field names are
converted into query parameters based on a "q" tag. For example:

	type struct Something {
	   Bar string `q:"x_bar"`
	   Baz int    `q:"lorem_ipsum"`
	}

	instance := Something{
	   Bar: "AAA",
	   Baz: "BBB",
	}

will be converted into "?x_bar=AAA&lorem_ipsum=BBB".

The struct's fields may be strings, integers, or boolean values. Fields left at
their type's zero value will be omittted from the query.
*/
func BuildQueryString(opts interface{}) (*url.URL, error) {
	optsValue := reflect.ValueOf(opts)
	if optsValue.Kind() == reflect.Ptr {
		optsValue = optsValue.Elem()
	}

	optsType := reflect.TypeOf(opts)
	if optsType.Kind() == reflect.Ptr {
		optsType = optsType.Elem()
	}

	var optsSlice []string
	if optsValue.Kind() == reflect.Struct {
		for i := 0; i < optsValue.NumField(); i++ {
			v := optsValue.Field(i)
			f := optsType.Field(i)
			qTag := f.Tag.Get("q")

			// if the field has a 'q' tag, it goes in the query string
			if qTag != "" {
				tags := strings.Split(qTag, ",")

				// if the field is set, add it to the slice of query pieces
				if !isZero(v) {
					switch v.Kind() {
					case reflect.String:
						optsSlice = append(optsSlice, tags[0]+"="+v.String())
					case reflect.Int:
						optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatInt(v.Int(), 10))
					case reflect.Bool:
						optsSlice = append(optsSlice, tags[0]+"="+strconv.FormatBool(v.Bool()))
					}
				} else {
					// Otherwise, the field is not set.
					if len(tags) == 2 && tags[1] == "required" {
						// And the field is required. Return an error.
						return nil, fmt.Errorf("Required query parameter [%s] not set.", f.Name)
					}
				}
			}

		}
		// URL encode the string for safety.
		s := strings.Join(optsSlice, "&")
		if s != "" {
			s = "?" + s
		}
		u, err := url.Parse(s)
		if err != nil {
			return nil, err
		}
		return u, nil
	}
	// Return an error if the underlying type of 'opts' isn't a struct.
	return nil, fmt.Errorf("Options type is not a struct.")
}

/*
BuildHeaders is an internal function to be used by request methods in
individual resource packages.

It accepts a arbitrary tagged structure and produces a string map that's
suitable for use as the HTTP headers of an outgoing request. Field names are
mapped to header names based in "h" tags.

  type struct Something {
    Bar string `h:"x_bar"`
    Baz int    `h:"lorem_ipsum"`
  }

  instance := Something{
    Bar: "AAA",
    Baz: "BBB",
  }

will be converted into:

  map[string]string{
    "x_bar": "AAA",
    "lorem_ipsum": "BBB",
  }

Untagged fields and fields left at their zero values are skipped. Integers,
booleans and string values are supported.
*/
func BuildHeaders(opts interface{}) (map[string]string, error) {
	optsValue := reflect.ValueOf(opts)
	if optsValue.Kind() == reflect.Ptr {
		optsValue = optsValue.Elem()
	}

	optsType := reflect.TypeOf(opts)
	if optsType.Kind() == reflect.Ptr {
		optsType = optsType.Elem()
	}

	optsMap := make(map[string]string)
	if optsValue.Kind() == reflect.Struct {
		for i := 0; i < optsValue.NumField(); i++ {
			v := optsValue.Field(i)
			f := optsType.Field(i)
			hTag := f.Tag.Get("h")

			// if the field has a 'h' tag, it goes in the header
			if hTag != "" {
				tags := strings.Split(hTag, ",")

				// if the field is set, add it to the slice of query pieces
				if !isZero(v) {
					switch v.Kind() {
					case reflect.String:
						optsMap[tags[0]] = v.String()
					case reflect.Int:
						optsMap[tags[0]] = strconv.FormatInt(v.Int(), 10)
					case reflect.Bool:
						optsMap[tags[0]] = strconv.FormatBool(v.Bool())
					}
				} else {
					// Otherwise, the field is not set.
					if len(tags) == 2 && tags[1] == "required" {
						// And the field is required. Return an error.
						return optsMap, fmt.Errorf("Required header not set.")
					}
				}
			}

		}
		return optsMap, nil
	}
	// Return an error if the underlying type of 'opts' isn't a struct.
	return optsMap, fmt.Errorf("Options type is not a struct.")
}
<|endoftext|>