<commit_before>// Copyright Project Harbor 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 preheat

import (
	"time"

	"github.com/goharbor/harbor/src/jobservice/job"
	"github.com/goharbor/harbor/src/lib/errors"
	"github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
	pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
	"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider/auth"
)

const (
	// PreheatParamProvider is a parameter keeping the preheating provider instance info.
	PreheatParamProvider = "provider"
	// PreheatParamImage is a parameter keeping the preheating artifact (image) info.
	PreheatParamImage = "image"
	// checkInterval indicates the interval of loop check.
	checkInterval = 10 * time.Second
	// checkTimeout indicates the overall timeout of the loop check.
	checkTimeout = 1801 * time.Second
)

// Job preheats the given artifact(image) to the target preheat provider.
type Job struct{}

// MaxFails of preheat job. Don't need to retry.
func (j *Job) MaxFails() uint {
	return 1
}

// MaxCurrency indicates no limitation to the concurrency of preheat job.
func (j *Job) MaxCurrency() uint {
	return 0
}

// ShouldRetry indicates no need to retry preheat job as it's just for a cache purpose.
func (j *Job) ShouldRetry() bool {
	return false
}

// Validate the parameters of preheat job.
func (j *Job) Validate(params job.Parameters) error {
	_, err := parseParamProvider(params)
	if err != nil {
		return err
	}

	_, err = parseParamImage(params)

	return err
}

// Run the preheat process.
func (j *Job) Run(ctx job.Context, params job.Parameters) error {
	// Get logger
	myLogger := ctx.GetLogger()

	// preheatJobRunningError is an internal error format
	preheatJobRunningError := func(err error) error {
		myLogger.Error(err)
		return errors.Wrap(err, "preheat job running error")
	}

	// shouldStop checks if the job should be stopped
	shouldStop := func() bool {
		if cmd, ok := ctx.OPCommand(); ok && cmd == job.StopCommand {
			return true
		}

		return false
	}

	// Parse parameters, ignore errors as they have been validated already
	p, _ := parseParamProvider(params)
	pi, _ := parseParamImage(params)

	// Print related info to log first
	myLogger.Infof(
		"Preheating image '%s:%s@%s' to the target preheat provider: %s %s:%s\n",
		pi.ImageName,
		pi.Tag,
		pi.Digest,
		p.Vendor,
		p.Name,
		p.Endpoint,
	)

	if shouldStop() {
		return nil
	}

	// Get driver factory for the given provider
	fac, ok := pr.GetProvider(p.Vendor)
	if !ok {
		err := errors.Errorf("No driver registered for provider %s", p.Vendor)
		return preheatJobRunningError(err)
	}

	// Construct driver
	d, err := fac(p)
	if err != nil {
		return preheatJobRunningError(err)
	}

	myLogger.Infof("Get preheat provider driver: %s", p.Vendor)

	// Start the preheat process
	// First, check the health of the provider
	h, err := d.GetHealth()
	if err != nil {
		return preheatJobRunningError(err)
	}

	if h.Status != pr.DriverStatusHealthy {
		err = errors.Errorf("unhealthy target preheat provider: %s", p.Vendor)
		return preheatJobRunningError(err)
	}

	myLogger.Infof("Check health of preheat provider instance: %s", pr.DriverStatusHealthy)

	if shouldStop() {
		return nil
	}

	// Then send the preheat requests to the target provider.
	st, err := d.Preheat(pi)
	if err != nil {
		return preheatJobRunningError(err)
	}

	myLogger.Info("Sending preheat request is successfully done")

	// For some of the drivers, e.g: Kraken, the returned status of preheating request contains the
	// final status info. No need to loop check the status.
	switch st.Status {
	case provider.PreheatingStatusSuccess:
		myLogger.Info("Preheating is completed")
		return nil
	case provider.PreheatingStatusFail:
		err = errors.New("preheating is failed")
		return preheatJobRunningError(err)
	case provider.PreheatingStatusPending,
		provider.PreheatingStatusRunning:
	// do nothing
	default:
		// in case
		err = errors.Errorf("unknown status '%s' returned by the preheat provider %s-%s:%s", st.Status, p.Vendor, p.Name, p.Endpoint)
		return preheatJobRunningError(err)
	}

	if shouldStop() {
		return nil
	}

	myLogger.Info("Start to loop check the preheating status until it's success or timeout(30m)")
	// If process is not completed, loop check the status until it's ready.
	tk := time.NewTicker(checkInterval)
	defer tk.Stop()

	tm := time.NewTimer(checkTimeout)
	defer tm.Stop()

	for {
		select {
		case <-tk.C:
			s, err := d.CheckProgress(st.TaskID)
			if err != nil {
				return preheatJobRunningError(err)
			}

			myLogger.Infof("Check preheat progress: %s", s)

			// Finished
			if s.Status == provider.PreheatingStatusSuccess {
				return nil
			}

			if shouldStop() {
				return nil
			}
		case <-tm.C:
			return preheatJobRunningError(errors.Errorf("status check timeout: %v", checkTimeout))
		}
	}
}

// parseParamProvider parses the provider param.
func parseParamProvider(params job.Parameters) (*provider.Instance, error) {
	data, err := parseStrValue(params, PreheatParamProvider)
	if err != nil {
		return nil, err
	}

	ins := &provider.Instance{}
	if err := ins.FromJSON(data); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	// Validate required info
	if len(ins.Vendor) == 0 {
		return nil, errors.New("missing vendor of preheat provider")
	}

	if ins.AuthMode != auth.AuthModeNone && len(ins.AuthInfo) == 0 {
		return nil, errors.Errorf("missing auth info for '%s' auth mode", ins.AuthMode)
	}

	if len(ins.Endpoint) == 0 {
		return nil, errors.Errorf("missing endpoint of preheat provider")
	}

	return ins, nil
}

// parseParamImage parses the preheating image param.
func parseParamImage(params job.Parameters) (*pr.PreheatImage, error) {
	data, err := parseStrValue(params, PreheatParamImage)
	if err != nil {
		return nil, err
	}

	img := &pr.PreheatImage{}
	if err := img.FromJSON(data); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	if err := img.Validate(); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	return img, nil
}

// parseStrValue parses the string data of the given parameter key from the job parameters.
func parseStrValue(params job.Parameters, key string) (string, error) {
	param, ok := params[key]
	if !ok || param == nil {
		return "", errors.Errorf("missing job parameter '%s'", key)
	}

	data, ok := param.(string)
	if !ok || len(data) == 0 {
		return "", errors.Errorf("bad job parameter '%s'", key)
	}

	return data, nil
}
<commit_msg>fix(preheat):handle fail case of preheat in job<commit_after>// Copyright Project Harbor 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 preheat

import (
	"time"

	"github.com/goharbor/harbor/src/jobservice/job"
	"github.com/goharbor/harbor/src/lib/errors"
	"github.com/goharbor/harbor/src/pkg/p2p/preheat/models/provider"
	pr "github.com/goharbor/harbor/src/pkg/p2p/preheat/provider"
	"github.com/goharbor/harbor/src/pkg/p2p/preheat/provider/auth"
)

const (
	// PreheatParamProvider is a parameter keeping the preheating provider instance info.
	PreheatParamProvider = "provider"
	// PreheatParamImage is a parameter keeping the preheating artifact (image) info.
	PreheatParamImage = "image"
	// checkInterval indicates the interval of loop check.
	checkInterval = 10 * time.Second
	// checkTimeout indicates the overall timeout of the loop check.
	checkTimeout = 1801 * time.Second
)

// Job preheats the given artifact(image) to the target preheat provider.
type Job struct{}

// MaxFails of preheat job. Don't need to retry.
func (j *Job) MaxFails() uint {
	return 1
}

// MaxCurrency indicates no limitation to the concurrency of preheat job.
func (j *Job) MaxCurrency() uint {
	return 0
}

// ShouldRetry indicates no need to retry preheat job as it's just for a cache purpose.
func (j *Job) ShouldRetry() bool {
	return false
}

// Validate the parameters of preheat job.
func (j *Job) Validate(params job.Parameters) error {
	_, err := parseParamProvider(params)
	if err != nil {
		return err
	}

	_, err = parseParamImage(params)

	return err
}

// Run the preheat process.
func (j *Job) Run(ctx job.Context, params job.Parameters) error {
	// Get logger
	myLogger := ctx.GetLogger()

	// preheatJobRunningError is an internal error format
	preheatJobRunningError := func(err error) error {
		myLogger.Error(err)
		return errors.Wrap(err, "preheat job running error")
	}

	// shouldStop checks if the job should be stopped
	shouldStop := func() bool {
		if cmd, ok := ctx.OPCommand(); ok && cmd == job.StopCommand {
			return true
		}

		return false
	}

	// Parse parameters, ignore errors as they have been validated already
	p, _ := parseParamProvider(params)
	pi, _ := parseParamImage(params)

	// Print related info to log first
	myLogger.Infof(
		"Preheating image '%s:%s@%s' to the target preheat provider: %s %s:%s\n",
		pi.ImageName,
		pi.Tag,
		pi.Digest,
		p.Vendor,
		p.Name,
		p.Endpoint,
	)

	if shouldStop() {
		return nil
	}

	// Get driver factory for the given provider
	fac, ok := pr.GetProvider(p.Vendor)
	if !ok {
		err := errors.Errorf("No driver registered for provider %s", p.Vendor)
		return preheatJobRunningError(err)
	}

	// Construct driver
	d, err := fac(p)
	if err != nil {
		return preheatJobRunningError(err)
	}

	myLogger.Infof("Get preheat provider driver: %s", p.Vendor)

	// Start the preheat process
	// First, check the health of the provider
	h, err := d.GetHealth()
	if err != nil {
		return preheatJobRunningError(err)
	}

	if h.Status != pr.DriverStatusHealthy {
		err = errors.Errorf("unhealthy target preheat provider: %s", p.Vendor)
		return preheatJobRunningError(err)
	}

	myLogger.Infof("Check health of preheat provider instance: %s", pr.DriverStatusHealthy)

	if shouldStop() {
		return nil
	}

	// Then send the preheat requests to the target provider.
	st, err := d.Preheat(pi)
	if err != nil {
		return preheatJobRunningError(err)
	}

	myLogger.Info("Sending preheat request is successfully done")

	// For some of the drivers, e.g: Kraken, the returned status of preheating request contains the
	// final status info. No need to loop check the status.
	switch st.Status {
	case provider.PreheatingStatusSuccess:
		myLogger.Info("Preheating is completed")
		return nil
	case provider.PreheatingStatusFail:
		err = errors.New("preheating is failed")
		return preheatJobRunningError(err)
	case provider.PreheatingStatusPending,
		provider.PreheatingStatusRunning:
	// do nothing
	default:
		// in case
		err = errors.Errorf("unknown status '%s' returned by the preheat provider %s-%s:%s", st.Status, p.Vendor, p.Name, p.Endpoint)
		return preheatJobRunningError(err)
	}

	if shouldStop() {
		return nil
	}

	myLogger.Info("Start to loop check the preheating status until it's success or timeout(30m)")
	// If process is not completed, loop check the status until it's ready.
	tk := time.NewTicker(checkInterval)
	defer tk.Stop()

	tm := time.NewTimer(checkTimeout)
	defer tm.Stop()

	for {
		select {
		case <-tk.C:
			s, err := d.CheckProgress(st.TaskID)
			if err != nil {
				return preheatJobRunningError(err)
			}

			myLogger.Infof("Check preheat progress: %s", s)

			switch s.Status {
			case provider.PreheatingStatusFail:
				// Fail
				return preheatJobRunningError(errors.Errorf("preheat failed: %s", s))
			case provider.PreheatingStatusSuccess:
				// Finished
				return nil
			default:
				// do nothing, check again
			}

			if shouldStop() {
				return nil
			}
		case <-tm.C:
			return preheatJobRunningError(errors.Errorf("status check timeout: %v", checkTimeout))
		}
	}
}

// parseParamProvider parses the provider param.
func parseParamProvider(params job.Parameters) (*provider.Instance, error) {
	data, err := parseStrValue(params, PreheatParamProvider)
	if err != nil {
		return nil, err
	}

	ins := &provider.Instance{}
	if err := ins.FromJSON(data); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	// Validate required info
	if len(ins.Vendor) == 0 {
		return nil, errors.New("missing vendor of preheat provider")
	}

	if ins.AuthMode != auth.AuthModeNone && len(ins.AuthInfo) == 0 {
		return nil, errors.Errorf("missing auth info for '%s' auth mode", ins.AuthMode)
	}

	if len(ins.Endpoint) == 0 {
		return nil, errors.Errorf("missing endpoint of preheat provider")
	}

	return ins, nil
}

// parseParamImage parses the preheating image param.
func parseParamImage(params job.Parameters) (*pr.PreheatImage, error) {
	data, err := parseStrValue(params, PreheatParamImage)
	if err != nil {
		return nil, err
	}

	img := &pr.PreheatImage{}
	if err := img.FromJSON(data); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	if err := img.Validate(); err != nil {
		return nil, errors.Wrap(err, "parse job parameter error")
	}

	return img, nil
}

// parseStrValue parses the string data of the given parameter key from the job parameters.
func parseStrValue(params job.Parameters, key string) (string, error) {
	param, ok := params[key]
	if !ok || param == nil {
		return "", errors.Errorf("missing job parameter '%s'", key)
	}

	data, ok := param.(string)
	if !ok || len(data) == 0 {
		return "", errors.Errorf("bad job parameter '%s'", key)
	}

	return data, nil
}
<|endoftext|>


<commit_before>// Code generated by protoc-gen-go.
// source: rankserver.proto
// DO NOT EDIT!

/*
Package proto is a generated protocol buffer package.

It is generated from these files:
	rankserver.proto

It has these top-level messages:
	Ranking
*/
package proto

import proto1 "github.com/golang/protobuf/proto"

import (
	context "golang.org/x/net/context"
	grpc "google.golang.org/grpc"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto1.Marshal

type Ranking struct {
}

func (m *Ranking) Reset()         { *m = Ranking{} }
func (m *Ranking) String() string { return proto1.CompactTextString(m) }
func (*Ranking) ProtoMessage()    {}

type Ranking_Nil struct {
}

func (m *Ranking_Nil) Reset()         { *m = Ranking_Nil{} }
func (m *Ranking_Nil) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Nil) ProtoMessage()    {}

type Ranking_SetId struct {
	SetId uint64 `protobuf:"varint,1,opt" json:"SetId,omitempty"`
}

func (m *Ranking_SetId) Reset()         { *m = Ranking_SetId{} }
func (m *Ranking_SetId) String() string { return proto1.CompactTextString(m) }
func (*Ranking_SetId) ProtoMessage()    {}

type Ranking_DeleteUserRequest struct {
	SetId  uint64 `protobuf:"varint,1,opt" json:"SetId,omitempty"`
	UserId int32  `protobuf:"varint,2,opt" json:"UserId,omitempty"`
}

func (m *Ranking_DeleteUserRequest) Reset()         { *m = Ranking_DeleteUserRequest{} }
func (m *Ranking_DeleteUserRequest) String() string { return proto1.CompactTextString(m) }
func (*Ranking_DeleteUserRequest) ProtoMessage()    {}

type Ranking_Change struct {
	UserId int32  `protobuf:"varint,1,opt" json:"UserId,omitempty"`
	Score  int32  `protobuf:"varint,2,opt" json:"Score,omitempty"`
	SetId  uint64 `protobuf:"varint,3,opt" json:"SetId,omitempty"`
}

func (m *Ranking_Change) Reset()         { *m = Ranking_Change{} }
func (m *Ranking_Change) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Change) ProtoMessage()    {}

type Ranking_Range struct {
	A     int32  `protobuf:"varint,1,opt" json:"A,omitempty"`
	B     int32  `protobuf:"varint,2,opt" json:"B,omitempty"`
	SetId uint64 `protobuf:"varint,3,opt" json:"SetId,omitempty"`
}

func (m *Ranking_Range) Reset()         { *m = Ranking_Range{} }
func (m *Ranking_Range) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Range) ProtoMessage()    {}

type Ranking_RankList struct {
	UserIds []int32 `protobuf:"varint,1,rep,packed" json:"UserIds,omitempty"`
	Scores  []int32 `protobuf:"varint,2,rep,packed" json:"Scores,omitempty"`
}

func (m *Ranking_RankList) Reset()         { *m = Ranking_RankList{} }
func (m *Ranking_RankList) String() string { return proto1.CompactTextString(m) }
func (*Ranking_RankList) ProtoMessage()    {}

type Ranking_Users struct {
	UserIds []int32 `protobuf:"varint,1,rep,packed" json:"UserIds,omitempty"`
	SetId   uint64  `protobuf:"varint,2,opt" json:"SetId,omitempty"`
}

func (m *Ranking_Users) Reset()         { *m = Ranking_Users{} }
func (m *Ranking_Users) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Users) ProtoMessage()    {}

type Ranking_UserList struct {
	Ranks  []int32 `protobuf:"varint,1,rep,packed" json:"Ranks,omitempty"`
	Scores []int32 `protobuf:"varint,2,rep,packed" json:"Scores,omitempty"`
}

func (m *Ranking_UserList) Reset()         { *m = Ranking_UserList{} }
func (m *Ranking_UserList) String() string { return proto1.CompactTextString(m) }
func (*Ranking_UserList) ProtoMessage()    {}

func init() {
}

// Client API for RankingService service

type RankingServiceClient interface {
	RankChange(ctx context.Context, in *Ranking_Change, opts ...grpc.CallOption) (*Ranking_Nil, error)
	DeleteSet(ctx context.Context, in *Ranking_SetId, opts ...grpc.CallOption) (*Ranking_Nil, error)
	DeleteUser(ctx context.Context, in *Ranking_DeleteUserRequest, opts ...grpc.CallOption) (*Ranking_Nil, error)
	QueryRankRange(ctx context.Context, in *Ranking_Range, opts ...grpc.CallOption) (*Ranking_RankList, error)
	QueryUsers(ctx context.Context, in *Ranking_Users, opts ...grpc.CallOption) (*Ranking_UserList, error)
}

type rankingServiceClient struct {
	cc *grpc.ClientConn
}

func NewRankingServiceClient(cc *grpc.ClientConn) RankingServiceClient {
	return &rankingServiceClient{cc}
}

func (c *rankingServiceClient) RankChange(ctx context.Context, in *Ranking_Change, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/RankChange", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) DeleteSet(ctx context.Context, in *Ranking_SetId, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/DeleteSet", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) DeleteUser(ctx context.Context, in *Ranking_DeleteUserRequest, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/DeleteUser", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) QueryRankRange(ctx context.Context, in *Ranking_Range, opts ...grpc.CallOption) (*Ranking_RankList, error) {
	out := new(Ranking_RankList)
	err := grpc.Invoke(ctx, "/proto.RankingService/QueryRankRange", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) QueryUsers(ctx context.Context, in *Ranking_Users, opts ...grpc.CallOption) (*Ranking_UserList, error) {
	out := new(Ranking_UserList)
	err := grpc.Invoke(ctx, "/proto.RankingService/QueryUsers", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for RankingService service

type RankingServiceServer interface {
	RankChange(context.Context, *Ranking_Change) (*Ranking_Nil, error)
	DeleteSet(context.Context, *Ranking_SetId) (*Ranking_Nil, error)
	DeleteUser(context.Context, *Ranking_DeleteUserRequest) (*Ranking_Nil, error)
	QueryRankRange(context.Context, *Ranking_Range) (*Ranking_RankList, error)
	QueryUsers(context.Context, *Ranking_Users) (*Ranking_UserList, error)
}

func RegisterRankingServiceServer(s *grpc.Server, srv RankingServiceServer) {
	s.RegisterService(&_RankingService_serviceDesc, srv)
}

func _RankingService_RankChange_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
	in := new(Ranking_Change)
	if err := codec.Unmarshal(buf, in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).RankChange(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_DeleteSet_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
	in := new(Ranking_SetId)
	if err := codec.Unmarshal(buf, in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).DeleteSet(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_DeleteUser_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
	in := new(Ranking_DeleteUserRequest)
	if err := codec.Unmarshal(buf, in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).DeleteUser(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_QueryRankRange_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
	in := new(Ranking_Range)
	if err := codec.Unmarshal(buf, in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).QueryRankRange(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_QueryUsers_Handler(srv interface{}, ctx context.Context, codec grpc.Codec, buf []byte) (interface{}, error) {
	in := new(Ranking_Users)
	if err := codec.Unmarshal(buf, in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).QueryUsers(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

var _RankingService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "proto.RankingService",
	HandlerType: (*RankingServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "RankChange",
			Handler:    _RankingService_RankChange_Handler,
		},
		{
			MethodName: "DeleteSet",
			Handler:    _RankingService_DeleteSet_Handler,
		},
		{
			MethodName: "DeleteUser",
			Handler:    _RankingService_DeleteUser_Handler,
		},
		{
			MethodName: "QueryRankRange",
			Handler:    _RankingService_QueryRankRange_Handler,
		},
		{
			MethodName: "QueryUsers",
			Handler:    _RankingService_QueryUsers_Handler,
		},
	},
	Streams: []grpc.StreamDesc{},
}
<commit_msg>update<commit_after>// Code generated by protoc-gen-go.
// source: rankserver.proto
// DO NOT EDIT!

/*
Package proto is a generated protocol buffer package.

It is generated from these files:
	rankserver.proto

It has these top-level messages:
	Ranking
*/
package proto

import proto1 "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"

import (
	context "golang.org/x/net/context"
	grpc "google.golang.org/grpc"
)

// Reference imports to suppress errors if they are not otherwise used.
var _ = proto1.Marshal
var _ = fmt.Errorf
var _ = math.Inf

type Ranking struct {
}

func (m *Ranking) Reset()         { *m = Ranking{} }
func (m *Ranking) String() string { return proto1.CompactTextString(m) }
func (*Ranking) ProtoMessage()    {}

type Ranking_Nil struct {
}

func (m *Ranking_Nil) Reset()         { *m = Ranking_Nil{} }
func (m *Ranking_Nil) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Nil) ProtoMessage()    {}

type Ranking_SetId struct {
	SetId uint64 `protobuf:"varint,1,opt,name=SetId" json:"SetId,omitempty"`
}

func (m *Ranking_SetId) Reset()         { *m = Ranking_SetId{} }
func (m *Ranking_SetId) String() string { return proto1.CompactTextString(m) }
func (*Ranking_SetId) ProtoMessage()    {}

type Ranking_DeleteUserRequest struct {
	SetId  uint64 `protobuf:"varint,1,opt,name=SetId" json:"SetId,omitempty"`
	UserId int32  `protobuf:"varint,2,opt,name=UserId" json:"UserId,omitempty"`
}

func (m *Ranking_DeleteUserRequest) Reset()         { *m = Ranking_DeleteUserRequest{} }
func (m *Ranking_DeleteUserRequest) String() string { return proto1.CompactTextString(m) }
func (*Ranking_DeleteUserRequest) ProtoMessage()    {}

type Ranking_Change struct {
	UserId int32  `protobuf:"varint,1,opt,name=UserId" json:"UserId,omitempty"`
	Score  int32  `protobuf:"varint,2,opt,name=Score" json:"Score,omitempty"`
	SetId  uint64 `protobuf:"varint,3,opt,name=SetId" json:"SetId,omitempty"`
}

func (m *Ranking_Change) Reset()         { *m = Ranking_Change{} }
func (m *Ranking_Change) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Change) ProtoMessage()    {}

type Ranking_Range struct {
	A     int32  `protobuf:"varint,1,opt,name=A" json:"A,omitempty"`
	B     int32  `protobuf:"varint,2,opt,name=B" json:"B,omitempty"`
	SetId uint64 `protobuf:"varint,3,opt,name=SetId" json:"SetId,omitempty"`
}

func (m *Ranking_Range) Reset()         { *m = Ranking_Range{} }
func (m *Ranking_Range) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Range) ProtoMessage()    {}

type Ranking_RankList struct {
	UserIds []int32 `protobuf:"varint,1,rep,packed,name=UserIds" json:"UserIds,omitempty"`
	Scores  []int32 `protobuf:"varint,2,rep,packed,name=Scores" json:"Scores,omitempty"`
}

func (m *Ranking_RankList) Reset()         { *m = Ranking_RankList{} }
func (m *Ranking_RankList) String() string { return proto1.CompactTextString(m) }
func (*Ranking_RankList) ProtoMessage()    {}

type Ranking_Users struct {
	UserIds []int32 `protobuf:"varint,1,rep,packed,name=UserIds" json:"UserIds,omitempty"`
	SetId   uint64  `protobuf:"varint,2,opt,name=SetId" json:"SetId,omitempty"`
}

func (m *Ranking_Users) Reset()         { *m = Ranking_Users{} }
func (m *Ranking_Users) String() string { return proto1.CompactTextString(m) }
func (*Ranking_Users) ProtoMessage()    {}

type Ranking_UserList struct {
	Ranks  []int32 `protobuf:"varint,1,rep,packed,name=Ranks" json:"Ranks,omitempty"`
	Scores []int32 `protobuf:"varint,2,rep,packed,name=Scores" json:"Scores,omitempty"`
}

func (m *Ranking_UserList) Reset()         { *m = Ranking_UserList{} }
func (m *Ranking_UserList) String() string { return proto1.CompactTextString(m) }
func (*Ranking_UserList) ProtoMessage()    {}

// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn

// Client API for RankingService service

type RankingServiceClient interface {
	RankChange(ctx context.Context, in *Ranking_Change, opts ...grpc.CallOption) (*Ranking_Nil, error)
	DeleteSet(ctx context.Context, in *Ranking_SetId, opts ...grpc.CallOption) (*Ranking_Nil, error)
	DeleteUser(ctx context.Context, in *Ranking_DeleteUserRequest, opts ...grpc.CallOption) (*Ranking_Nil, error)
	QueryRankRange(ctx context.Context, in *Ranking_Range, opts ...grpc.CallOption) (*Ranking_RankList, error)
	QueryUsers(ctx context.Context, in *Ranking_Users, opts ...grpc.CallOption) (*Ranking_UserList, error)
}

type rankingServiceClient struct {
	cc *grpc.ClientConn
}

func NewRankingServiceClient(cc *grpc.ClientConn) RankingServiceClient {
	return &rankingServiceClient{cc}
}

func (c *rankingServiceClient) RankChange(ctx context.Context, in *Ranking_Change, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/RankChange", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) DeleteSet(ctx context.Context, in *Ranking_SetId, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/DeleteSet", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) DeleteUser(ctx context.Context, in *Ranking_DeleteUserRequest, opts ...grpc.CallOption) (*Ranking_Nil, error) {
	out := new(Ranking_Nil)
	err := grpc.Invoke(ctx, "/proto.RankingService/DeleteUser", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) QueryRankRange(ctx context.Context, in *Ranking_Range, opts ...grpc.CallOption) (*Ranking_RankList, error) {
	out := new(Ranking_RankList)
	err := grpc.Invoke(ctx, "/proto.RankingService/QueryRankRange", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *rankingServiceClient) QueryUsers(ctx context.Context, in *Ranking_Users, opts ...grpc.CallOption) (*Ranking_UserList, error) {
	out := new(Ranking_UserList)
	err := grpc.Invoke(ctx, "/proto.RankingService/QueryUsers", in, out, c.cc, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Server API for RankingService service

type RankingServiceServer interface {
	RankChange(context.Context, *Ranking_Change) (*Ranking_Nil, error)
	DeleteSet(context.Context, *Ranking_SetId) (*Ranking_Nil, error)
	DeleteUser(context.Context, *Ranking_DeleteUserRequest) (*Ranking_Nil, error)
	QueryRankRange(context.Context, *Ranking_Range) (*Ranking_RankList, error)
	QueryUsers(context.Context, *Ranking_Users) (*Ranking_UserList, error)
}

func RegisterRankingServiceServer(s *grpc.Server, srv RankingServiceServer) {
	s.RegisterService(&_RankingService_serviceDesc, srv)
}

func _RankingService_RankChange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
	in := new(Ranking_Change)
	if err := dec(in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).RankChange(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_DeleteSet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
	in := new(Ranking_SetId)
	if err := dec(in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).DeleteSet(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_DeleteUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
	in := new(Ranking_DeleteUserRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).DeleteUser(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_QueryRankRange_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
	in := new(Ranking_Range)
	if err := dec(in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).QueryRankRange(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func _RankingService_QueryUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error) (interface{}, error) {
	in := new(Ranking_Users)
	if err := dec(in); err != nil {
		return nil, err
	}
	out, err := srv.(RankingServiceServer).QueryUsers(ctx, in)
	if err != nil {
		return nil, err
	}
	return out, nil
}

var _RankingService_serviceDesc = grpc.ServiceDesc{
	ServiceName: "proto.RankingService",
	HandlerType: (*RankingServiceServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "RankChange",
			Handler:    _RankingService_RankChange_Handler,
		},
		{
			MethodName: "DeleteSet",
			Handler:    _RankingService_DeleteSet_Handler,
		},
		{
			MethodName: "DeleteUser",
			Handler:    _RankingService_DeleteUser_Handler,
		},
		{
			MethodName: "QueryRankRange",
			Handler:    _RankingService_QueryRankRange_Handler,
		},
		{
			MethodName: "QueryUsers",
			Handler:    _RankingService_QueryUsers_Handler,
		},
	},
	Streams: []grpc.StreamDesc{},
}
<|endoftext|>


<commit_before>package api

import (
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"registry/layers"
	"registry/logger"
	"registry/storage"
	"strings"
)

func (a *RegistryAPI) RequireCompletion(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		imageID := vars["imageID"]
		if exists, _ := a.Storage.Exists(storage.ImageMarkPath(imageID)); exists {
			a.response(w, "Image is being uploaded, retry later", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
		handler(w, r)
	}
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageLayerHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	reader, err := a.Storage.GetReader(storage.ImageLayerPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	a.response(w, reader, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageLayerHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	jsonContent, err := a.Storage.Get(storage.ImageJsonPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	layerPath := storage.ImageLayerPath(imageID)
	markPath := storage.ImageMarkPath(imageID)
	layerExists, _ := a.Storage.Exists(layerPath)
	markExists, _ := a.Storage.Exists(markPath)
	if layerExists && !markExists {
		a.response(w, "Image already exists", http.StatusConflict, EMPTY_HEADERS)
		return
	}
	// compute checksum while reading. create a TeeReader
	sha256Writer := sha256.New()
	sha256Writer.Write(append(jsonContent, byte('\n'))) // write initial json
	teeReader := io.TeeReader(r.Body, sha256Writer)
	// this will create the checksums for a tar and the json for tar file info
	tarInfo := layers.NewTarInfo(append(jsonContent, byte('\n')))
	// PutReader takes a function that will run after the write finishes:
	err = a.Storage.PutReader(layerPath, teeReader, tarInfo.Load)
	if err != nil {
		a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	checksums := map[string]bool{fmt.Sprintf("sha256:%x", sha256Writer.Sum(nil)): true}
	if tarInfo.Error == nil {
		filesJson, err := tarInfo.TarFilesInfo.Json()
		if err != nil {
			a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
			return
		}
		layers.SetImageFilesCache(a.Storage, imageID, filesJson)
		checksums[tarInfo.TarSum.Compute()] = true
	}

	storedSum, err := a.Storage.Get(storage.ImageChecksumPath(imageID))
	if err != nil {
		cookieString := ""
		for sum, _ := range checksums {
			cookieString += sum + ","
		}
		cookieString = strings.TrimSuffix(cookieString, ",")
		http.SetCookie(w, &http.Cookie{Name: "checksum", Value: cookieString})
		a.response(w, true, http.StatusOK, EMPTY_HEADERS)
		return
	}
	if !checksums[string(storedSum)] {
		logger.Debug("[PutImageLayer][" + imageID + "] Wrong checksum")
		a.response(w, "Checksum mismatch, ignoring the layer", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	if err := a.Storage.Remove(markPath); err != nil {
		logger.Debug("[PutImageLayer]["+imageID+"] Error removing mark path: %s", err.Error())
		a.response(w, "Internal Error", http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	a.response(w, true, http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageJsonHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := a.Storage.Get(storage.ImageJsonPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	size, err := a.Storage.Size(storage.ImageLayerPath(imageID))
	if err == nil {
		headers["X-Docker-Size"] = []string{fmt.Sprintf("%d", size)}
	}
	checksumPath := storage.ImageChecksumPath(imageID)
	if exists, _ := a.Storage.Exists(checksumPath); exists {
		checksum, err := a.Storage.Get(checksumPath)
		if err != nil {
			headers["X-Docker-Checksum"] = []string{string(checksum)}
		}
	}
	a.response(w, data, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageJsonHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	// decode json from request body
	dec := json.NewDecoder(r.Body)
	var data map[string]interface{}
	err := dec.Decode(&data)
	if err != nil {
		a.response(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	logger.Debug("[PutImageJson] body:\n%+v", data)
	if _, exists := data["id"]; !exists {
		a.response(w, "Missing key 'id' in JSON", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	checksum := r.Header.Get("X-Docker-Checksum")
	if checksum == "" {
		// remove the old checksum in case it's a retry after a fail
		a.Storage.Remove(storage.ImageChecksumPath(imageID))
	} else if err := layers.StoreChecksum(a.Storage, imageID, checksum); err != nil {
		a.response(w, err.Error(), http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	dataID, ok := data["id"].(string)
	if !ok {
		a.response(w, "Invalid JSON: 'id' is not a string", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	if imageID != dataID {
		a.response(w, "JSON data contains invalid id", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	var parentID string
	if _, exists := data["parent"]; exists {
		parentID, ok = data["parent"].(string)
		if !ok {
			a.response(w, "Invalid JSON: 'parent' is not a string", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
		if exists, _ := a.Storage.Exists(storage.ImageJsonPath(parentID)); !exists {
			a.response(w, "Image depends on non-existant parent", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
	}
	jsonPath := storage.ImageJsonPath(imageID)
	markPath := storage.ImageMarkPath(imageID)
	if exists, _ := a.Storage.Exists(jsonPath); exists {
		if markExists, _ := a.Storage.Exists(markPath); !markExists {
			a.response(w, "Image already exists", 409, EMPTY_HEADERS)
			return
		}
	}
	err = a.Storage.Put(markPath, []byte("true"))
	if err != nil {
		a.response(w, "Put Mark Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	jsonBytes, err := json.Marshal(&data)
	if err != nil {
		a.response(w, "Marshal Json Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	err = a.Storage.Put(jsonPath, jsonBytes)
	if err != nil {
		a.response(w, "Put Json Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	if err := layers.GenerateAncestry(a.Storage, imageID, parentID); err != nil {
		a.response(w, "Generate Ancestry Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	a.response(w, "true", http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageAncestryHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := a.Storage.Get(storage.ImageAncestryPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	a.response(w, data, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageChecksumHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	// read header
	checksum := r.Header.Get("X-Docker-Checksum")
	if checksum == "" {
		a.response(w, "Missing Image's checksum", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	// read cookie
	checksumCookie, err := r.Cookie("checksum")
	if err != nil {
		a.response(w, "Checksum not found in Cookie", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	// check if image json exists
	if exists, _ := a.Storage.Exists(storage.ImageJsonPath(imageID)); !exists {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	markPath := storage.ImageMarkPath(imageID)
	if exists, _ := a.Storage.Exists(markPath); !exists {
		a.response(w, "Cannot set this image checksum (mark path does not exist)", 409, EMPTY_HEADERS)
		return
	}
	err = layers.StoreChecksum(a.Storage, imageID, checksum)
	// extract checksumCookie JSON
	checksumMap := map[string]bool{}
	for _, checksum := range strings.Split(checksumCookie.Value, ",") {
		checksumMap[checksum] = true
	}
	if !checksumMap[checksum] {
		logger.Debug("[PutImageChecksum][" + imageID + "] Wrong checksum")
		a.response(w, "Checksum mismatch", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	a.Storage.Remove(markPath)
	a.response(w, true, http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageFilesHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := layers.GetImageFilesJson(a.Storage, imageID)
	if err != nil {
		switch err.(type) {
		case layers.TarError:
			a.response(w, "Layer format not supported", http.StatusBadRequest, EMPTY_HEADERS)
			return
		default:
			a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
			return
		}
	}
	a.response(w, data, http.StatusOK, headers)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageDiffHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	diffJson, err := layers.GetImageDiffCache(a.Storage, imageID)
	if err != nil {
		// not cache miss. actual error
		a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	if diffJson == nil {
		// cache miss spawn goroutine to generate the diff and push it to S3
		go layers.GenDiff(a.Storage, imageID)
		diffJson = []byte{}
	}
	a.response(w, diffJson, http.StatusOK, headers)
}
<commit_msg>add detail logging to checksum checking<commit_after>package api

import (
	"crypto/sha256"
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"registry/layers"
	"registry/logger"
	"registry/storage"
	"strings"
)

func (a *RegistryAPI) RequireCompletion(handler http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		vars := mux.Vars(r)
		imageID := vars["imageID"]
		if exists, _ := a.Storage.Exists(storage.ImageMarkPath(imageID)); exists {
			a.response(w, "Image is being uploaded, retry later", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
		handler(w, r)
	}
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageLayerHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	reader, err := a.Storage.GetReader(storage.ImageLayerPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	a.response(w, reader, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageLayerHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	jsonContent, err := a.Storage.Get(storage.ImageJsonPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	layerPath := storage.ImageLayerPath(imageID)
	markPath := storage.ImageMarkPath(imageID)
	layerExists, _ := a.Storage.Exists(layerPath)
	markExists, _ := a.Storage.Exists(markPath)
	if layerExists && !markExists {
		a.response(w, "Image already exists", http.StatusConflict, EMPTY_HEADERS)
		return
	}
	// compute checksum while reading. create a TeeReader
	sha256Writer := sha256.New()
	sha256Writer.Write(append(jsonContent, byte('\n'))) // write initial json
	teeReader := io.TeeReader(r.Body, sha256Writer)
	// this will create the checksums for a tar and the json for tar file info
	tarInfo := layers.NewTarInfo(append(jsonContent, byte('\n')))
	// PutReader takes a function that will run after the write finishes:
	err = a.Storage.PutReader(layerPath, teeReader, tarInfo.Load)
	if err != nil {
		a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	checksums := map[string]bool{fmt.Sprintf("sha256:%x", sha256Writer.Sum(nil)): true}
	if tarInfo.Error == nil {
		filesJson, err := tarInfo.TarFilesInfo.Json()
		if err != nil {
			a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
			return
		}
		layers.SetImageFilesCache(a.Storage, imageID, filesJson)
		checksums[tarInfo.TarSum.Compute()] = true
	}

	storedSum, err := a.Storage.Get(storage.ImageChecksumPath(imageID))
	if err != nil {
		cookieString := ""
		for sum, _ := range checksums {
			cookieString += sum + ","
		}
		cookieString = strings.TrimSuffix(cookieString, ",")
		http.SetCookie(w, &http.Cookie{Name: "checksum", Value: cookieString})
		a.response(w, true, http.StatusOK, EMPTY_HEADERS)
		return
	}
	if !checksums[string(storedSum)] {
		logger.Debug("[PutImageLayer]["+imageID+"] Wrong checksum:"+string(storedSum)+" not in %+v", checksums)
		a.response(w, "Checksum mismatch, ignoring the layer", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	if err := a.Storage.Remove(markPath); err != nil {
		logger.Debug("[PutImageLayer]["+imageID+"] Error removing mark path: %s", err.Error())
		a.response(w, "Internal Error", http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	a.response(w, true, http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageJsonHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := a.Storage.Get(storage.ImageJsonPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	size, err := a.Storage.Size(storage.ImageLayerPath(imageID))
	if err == nil {
		headers["X-Docker-Size"] = []string{fmt.Sprintf("%d", size)}
	}
	checksumPath := storage.ImageChecksumPath(imageID)
	if exists, _ := a.Storage.Exists(checksumPath); exists {
		checksum, err := a.Storage.Get(checksumPath)
		if err != nil {
			headers["X-Docker-Checksum"] = []string{string(checksum)}
		}
	}
	a.response(w, data, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageJsonHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	// decode json from request body
	dec := json.NewDecoder(r.Body)
	var data map[string]interface{}
	err := dec.Decode(&data)
	if err != nil {
		a.response(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	logger.Debug("[PutImageJson] body:\n%+v", data)
	if _, exists := data["id"]; !exists {
		a.response(w, "Missing key 'id' in JSON", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	checksum := r.Header.Get("X-Docker-Checksum")
	if checksum == "" {
		// remove the old checksum in case it's a retry after a fail
		a.Storage.Remove(storage.ImageChecksumPath(imageID))
	} else if err := layers.StoreChecksum(a.Storage, imageID, checksum); err != nil {
		a.response(w, err.Error(), http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	dataID, ok := data["id"].(string)
	if !ok {
		a.response(w, "Invalid JSON: 'id' is not a string", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	if imageID != dataID {
		a.response(w, "JSON data contains invalid id", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	var parentID string
	if _, exists := data["parent"]; exists {
		parentID, ok = data["parent"].(string)
		if !ok {
			a.response(w, "Invalid JSON: 'parent' is not a string", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
		if exists, _ := a.Storage.Exists(storage.ImageJsonPath(parentID)); !exists {
			a.response(w, "Image depends on non-existant parent", http.StatusBadRequest, EMPTY_HEADERS)
			return
		}
	}
	jsonPath := storage.ImageJsonPath(imageID)
	markPath := storage.ImageMarkPath(imageID)
	if exists, _ := a.Storage.Exists(jsonPath); exists {
		if markExists, _ := a.Storage.Exists(markPath); !markExists {
			a.response(w, "Image already exists", 409, EMPTY_HEADERS)
			return
		}
	}
	err = a.Storage.Put(markPath, []byte("true"))
	if err != nil {
		a.response(w, "Put Mark Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	jsonBytes, err := json.Marshal(&data)
	if err != nil {
		a.response(w, "Marshal Json Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	err = a.Storage.Put(jsonPath, jsonBytes)
	if err != nil {
		a.response(w, "Put Json Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	if err := layers.GenerateAncestry(a.Storage, imageID, parentID); err != nil {
		a.response(w, "Generate Ancestry Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	a.response(w, "true", http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageAncestryHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := a.Storage.Get(storage.ImageAncestryPath(imageID))
	if err != nil {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	a.response(w, data, http.StatusOK, headers)
}

func (a *RegistryAPI) PutImageChecksumHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	// read header
	checksum := r.Header.Get("X-Docker-Checksum")
	if checksum == "" {
		a.response(w, "Missing Image's checksum", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	// read cookie
	checksumCookie, err := r.Cookie("checksum")
	if err != nil {
		a.response(w, "Checksum not found in Cookie", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	// check if image json exists
	if exists, _ := a.Storage.Exists(storage.ImageJsonPath(imageID)); !exists {
		a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
		return
	}
	markPath := storage.ImageMarkPath(imageID)
	if exists, _ := a.Storage.Exists(markPath); !exists {
		a.response(w, "Cannot set this image checksum (mark path does not exist)", 409, EMPTY_HEADERS)
		return
	}
	err = layers.StoreChecksum(a.Storage, imageID, checksum)
	// extract checksumCookie JSON
	checksumMap := map[string]bool{}
	for _, checksum := range strings.Split(checksumCookie.Value, ",") {
		checksumMap[checksum] = true
	}
	if !checksumMap[checksum] {
		logger.Debug("[PutImageChecksum]["+imageID+"] Wrong checksum:"+checksum+" not in %+v", checksumMap)
		a.response(w, "Checksum mismatch", http.StatusBadRequest, EMPTY_HEADERS)
		return
	}
	a.Storage.Remove(markPath)
	a.response(w, true, http.StatusOK, EMPTY_HEADERS)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageFilesHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	data, err := layers.GetImageFilesJson(a.Storage, imageID)
	if err != nil {
		switch err.(type) {
		case layers.TarError:
			a.response(w, "Layer format not supported", http.StatusBadRequest, EMPTY_HEADERS)
			return
		default:
			a.response(w, "Image not found", http.StatusNotFound, EMPTY_HEADERS)
			return
		}
	}
	a.response(w, data, http.StatusOK, headers)
}

// RequiresCompletion
// CheckIfModifiedSince
// Sets DefaultCacheHeaders
func (a *RegistryAPI) GetImageDiffHandler(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	imageID := vars["imageID"]
	headers := DefaultCacheHeaders()
	diffJson, err := layers.GetImageDiffCache(a.Storage, imageID)
	if err != nil {
		// not cache miss. actual error
		a.response(w, "Internal Error: "+err.Error(), http.StatusInternalServerError, EMPTY_HEADERS)
		return
	}
	if diffJson == nil {
		// cache miss spawn goroutine to generate the diff and push it to S3
		go layers.GenDiff(a.Storage, imageID)
		diffJson = []byte{}
	}
	a.response(w, diffJson, http.StatusOK, headers)
}
<|endoftext|>


<commit_before>package goselenium

import (
	"errors"
	"fmt"
	"testing"
)

func Test_RemoteElement_IDCanBeRetrieved(t *testing.T) {
	el := newSeleniumElement("test", nil)
	if el.ID() != "test" {
		t.Errorf(correctResponseErrorText)
	}
}

/*
	SELECTED TESTS
*/
func Test_ElementSelected_CommunicationErrorIsReturnedCorrectly(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn:  "",
		errorToReturn: errors.New("An error :<"),
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	_, err := el.Selected()
	if err == nil || !IsCommunicationError(err) {
		t.Errorf(apiCommunicationErrorText)
	}
}

func Test_ElementSelected_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn:  "Invalid JSON!",
		errorToReturn: nil,
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	_, err := el.Selected()
	if err == nil || !IsUnmarshallingError(err) {
		t.Errorf(unmarshallingErrorText)
	}
}

func Test_ElementSelected_CorrectResponseIsReturned(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn: `{
			"state": "success",
			"value": true
		}`,
		errorToReturn: nil,
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	resp, err := el.Selected()
	if err != nil || resp.State != "success" || resp.Selected != true {
		fmt.Println(err)
		fmt.Println(resp)
		t.Errorf(correctResponseErrorText)
	}
}
<commit_msg>Remove rogue fmt.Println<commit_after>package goselenium

import (
	"errors"
	"testing"
)

func Test_RemoteElement_IDCanBeRetrieved(t *testing.T) {
	el := newSeleniumElement("test", nil)
	if el.ID() != "test" {
		t.Errorf(correctResponseErrorText)
	}
}

/*
	SELECTED TESTS
*/
func Test_ElementSelected_CommunicationErrorIsReturnedCorrectly(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn:  "",
		errorToReturn: errors.New("An error :<"),
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	_, err := el.Selected()
	if err == nil || !IsCommunicationError(err) {
		t.Errorf(apiCommunicationErrorText)
	}
}

func Test_ElementSelected_UnmarshallingErrorIsReturnedCorrectly(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn:  "Invalid JSON!",
		errorToReturn: nil,
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	_, err := el.Selected()
	if err == nil || !IsUnmarshallingError(err) {
		t.Errorf(unmarshallingErrorText)
	}
}

func Test_ElementSelected_CorrectResponseIsReturned(t *testing.T) {
	api := &testableAPIService{
		jsonToReturn: `{
			"state": "success",
			"value": true
		}`,
		errorToReturn: nil,
	}

	d := setUpDriver(setUpDefaultCaps(), api)
	d.sessionID = "12345"

	el := newSeleniumElement("0", d)
	resp, err := el.Selected()
	if err != nil || resp.State != "success" || resp.Selected != true {
		t.Errorf(correctResponseErrorText)
	}
}
<|endoftext|>


<commit_before>package goboots

import (
	"bytes"
	"net/http"
	"reflect"
	"text/template"
	//"time"
	"encoding/binary"
)

type IController interface {
	//Setup(app *App)
	Init(app *App)
	Run(w http.ResponseWriter, r *http.Request, params []string) interface{}
	PreFilter(w http.ResponseWriter, r *http.Request, params []string) interface{}
	// rendering
	Render(w http.ResponseWriter, r *http.Request, content interface{})
	RenderNew(w http.ResponseWriter, out *Out)
	registerMethod(name string, method reflect.Value, inKind, outKind int)
	getMethod(name string) (controllerMethod, bool)
}

type PageContent struct {
	Title       string
	Content     string
	UserContent string
	HeadInclude string
	FootInclude string
}

type RedirectPageContent struct {
	Title   string
	Message string
	Path    string
}

type ErrorPageContent struct {
	Title        string
	ErrorTitle   string
	ErrorMessage string
	Content      string
}

type Controller struct {
	App           *App
	Path          string
	Name          string
	LayoutName    string
	Action        string
	LayoutAction  string
	Params        []string
	PageTitle     string
	Layout        string
	ContentType   string
	customMethods map[string]controllerMethod
}

const (
	controllerMethodKindLegacy = iota
	controllerMethodKindNew
)

type controllerMethod struct {
	Val           reflect.Value
	MethodKindIn  int
	MethodKindOut int
}

func (c *Controller) Init(app *App) {
	c.App = app
	c.PageTitle = ""
	c.Layout = "default"
	c.ContentType = "text/html; charset=utf-8"
}

func (c *Controller) Run(w http.ResponseWriter, r *http.Request, params []string) interface{} {
	// www.example.com/params[0]/params[1]/(...)params[n]
	content := &PageContent{
		Title:   c.App.Config.Name + " - " + c.PageTitle,
		Content: "content goes here",
	}
	return content
}

func (c *Controller) PreFilter(w http.ResponseWriter, r *http.Request, params []string) interface{} {
	return nil
}

func (c *Controller) Render(w http.ResponseWriter, r *http.Request, content interface{}) {
	c.render(w, r, content, "")
}

func (c *Controller) RenderWithLayout(w http.ResponseWriter, r *http.Request, content interface{}, customLayout string) {
	c.render(w, r, content, customLayout)
}

func (c *Controller) RenderNew(w http.ResponseWriter, out *Out) {
	out.render(w)
}

func (c *Controller) OutputSoloTpl(in *In, tplPath string, content interface{}) *Out {
	o := &Out{}
	o.kind = outTemplateSolo
	o.contentObj = content
	var tpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		tpl = c.App.GetLocalizedViewTemplate(tplPath, in.W, in.R)
	} else {
		tpl = c.App.GetViewTemplate(tplPath)
	}
	o.tpl = tpl
	return o
}

func (c *Controller) OutputJSON(jobj interface{}) *Out {
	o := &Out{}
	o.kind = outJSON
	o.contentObj = jobj
	return o
}

func (c *Controller) OutputXML(xobj interface{}) *Out {
	o := &Out{}
	o.kind = outXML
	o.contentObj = xobj
	return o
}

func (c *Controller) OutputString(str string) *Out {
	o := &Out{}
	o.kind = outString
	o.contentStr = str
	return o
}

func (c *Controller) render(w http.ResponseWriter, r *http.Request, content interface{}, customLayout string) {
	if len(c.Layout) == 0 && len(customLayout) == 0 {
		// no layout defined!
		var buff bytes.Buffer
		binary.Write(&buff, binary.LittleEndian, content)
		w.Write(buff.Bytes())
		return
	}
	layoutName := c.Layout
	if len(customLayout) > 0 {
		layoutName = customLayout
	}
	var layoutTpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		layoutTpl = c.App.GetLocalizedLayout(layoutName, w, r)
	} else {
		layoutTpl = c.App.GetLayout(layoutName)
	}
	layoutTpl.Execute(w, content)
}

// RenderFromCache renders an entire page to the app's bytecache
func (c *Controller) RenderToCache(w http.ResponseWriter, r *http.Request, name string, content interface{}) {
	if len(c.Layout) == 0 {
		// no layout defined!
		var buff bytes.Buffer
		binary.Write(&buff, binary.LittleEndian, content)
		c.App.ByteCaches.SetCache(name, buff.Bytes())
		return
	}
	var layoutTpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		layoutTpl = c.App.GetLocalizedLayout(c.Layout, w, r)
	} else {
		layoutTpl = c.App.GetLayout(c.Layout)
	}
	var buffer bytes.Buffer
	err := layoutTpl.Execute(&buffer, content)
	__panic(err)
	c.App.ByteCaches.SetCache(name, buffer.Bytes())
}

// RenderFromCache renders an entire page from the app's bytecache
func (c *Controller) RenderFromCache(w http.ResponseWriter, r *http.Request, name string) interface{} {
	w.Write(c.App.ByteCaches.GetCache(name).Content)
	return nil
}

func (c *Controller) ParseContent(w http.ResponseWriter, r *http.Request, locTemplatePath string, content interface{}) string {
	var tpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 && w != nil && r != nil {
		tpl = c.App.GetLocalizedViewTemplate(locTemplatePath, w, r)
	} else {
		tpl = c.App.GetViewTemplate(locTemplatePath)
	}

	//TODO: handle error if template is nil
	var buffer bytes.Buffer
	err := tpl.Execute(&buffer, content)
	__panic(err)
	return buffer.String()
}

func (c *Controller) Redirect(w http.ResponseWriter, path string, title string, message string) interface{} {
	tpl := c.App.GetLayout("redirect")
	//TODO: handle error if template is nil
	content := &RedirectPageContent{
		Title:   title,
		Message: message,
		Path:    path,
	}
	tpl.Execute(w, content)
	return nil
}

func (c *Controller) PageError(w http.ResponseWriter, errorTitle string, errorMessage string, extraHTMLContent string) interface{} {
	tpl := c.App.GetLayout("error")
	//TODO: handle error if template is nil
	content := &ErrorPageContent{
		Title:        c.App.Config.Name + " - Error: " + errorTitle,
		ErrorTitle:   errorTitle,
		ErrorMessage: errorMessage,
		Content:      extraHTMLContent,
	}
	tpl.Execute(w, content)
	return nil
}

func (c *Controller) registerMethod(name string, method reflect.Value, inKind, outKind int) {
	if c.customMethods == nil {
		c.customMethods = make(map[string]controllerMethod, 0)
	}
	c.customMethods[name] = controllerMethod{method, inKind, outKind}
}
func (c *Controller) getMethod(name string) (controllerMethod, bool) {
	if c.customMethods == nil {
		return controllerMethod{}, false
	}
	val, ok := c.customMethods[name]
	return val, ok
}
<commit_msg>checked if output is nil<commit_after>package goboots

import (
	"bytes"
	"net/http"
	"reflect"
	"text/template"
	//"time"
	"encoding/binary"
)

type IController interface {
	//Setup(app *App)
	Init(app *App)
	Run(w http.ResponseWriter, r *http.Request, params []string) interface{}
	PreFilter(w http.ResponseWriter, r *http.Request, params []string) interface{}
	// rendering
	Render(w http.ResponseWriter, r *http.Request, content interface{})
	RenderNew(w http.ResponseWriter, out *Out)
	registerMethod(name string, method reflect.Value, inKind, outKind int)
	getMethod(name string) (controllerMethod, bool)
}

type PageContent struct {
	Title       string
	Content     string
	UserContent string
	HeadInclude string
	FootInclude string
}

type RedirectPageContent struct {
	Title   string
	Message string
	Path    string
}

type ErrorPageContent struct {
	Title        string
	ErrorTitle   string
	ErrorMessage string
	Content      string
}

type Controller struct {
	App           *App
	Path          string
	Name          string
	LayoutName    string
	Action        string
	LayoutAction  string
	Params        []string
	PageTitle     string
	Layout        string
	ContentType   string
	customMethods map[string]controllerMethod
}

const (
	controllerMethodKindLegacy = iota
	controllerMethodKindNew
)

type controllerMethod struct {
	Val           reflect.Value
	MethodKindIn  int
	MethodKindOut int
}

func (c *Controller) Init(app *App) {
	c.App = app
	c.PageTitle = ""
	c.Layout = "default"
	c.ContentType = "text/html; charset=utf-8"
}

func (c *Controller) Run(w http.ResponseWriter, r *http.Request, params []string) interface{} {
	// www.example.com/params[0]/params[1]/(...)params[n]
	content := &PageContent{
		Title:   c.App.Config.Name + " - " + c.PageTitle,
		Content: "content goes here",
	}
	return content
}

func (c *Controller) PreFilter(w http.ResponseWriter, r *http.Request, params []string) interface{} {
	return nil
}

func (c *Controller) Render(w http.ResponseWriter, r *http.Request, content interface{}) {
	c.render(w, r, content, "")
}

func (c *Controller) RenderWithLayout(w http.ResponseWriter, r *http.Request, content interface{}, customLayout string) {
	c.render(w, r, content, customLayout)
}

func (c *Controller) RenderNew(w http.ResponseWriter, out *Out) {
	if out != nil {
		out.render(w)
	}
}

func (c *Controller) OutputSoloTpl(in *In, tplPath string, content interface{}) *Out {
	o := &Out{}
	o.kind = outTemplateSolo
	o.contentObj = content
	var tpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		tpl = c.App.GetLocalizedViewTemplate(tplPath, in.W, in.R)
	} else {
		tpl = c.App.GetViewTemplate(tplPath)
	}
	o.tpl = tpl
	return o
}

func (c *Controller) OutputJSON(jobj interface{}) *Out {
	o := &Out{}
	o.kind = outJSON
	o.contentObj = jobj
	return o
}

func (c *Controller) OutputXML(xobj interface{}) *Out {
	o := &Out{}
	o.kind = outXML
	o.contentObj = xobj
	return o
}

func (c *Controller) OutputString(str string) *Out {
	o := &Out{}
	o.kind = outString
	o.contentStr = str
	return o
}

func (c *Controller) render(w http.ResponseWriter, r *http.Request, content interface{}, customLayout string) {
	if len(c.Layout) == 0 && len(customLayout) == 0 {
		// no layout defined!
		var buff bytes.Buffer
		binary.Write(&buff, binary.LittleEndian, content)
		w.Write(buff.Bytes())
		return
	}
	layoutName := c.Layout
	if len(customLayout) > 0 {
		layoutName = customLayout
	}
	var layoutTpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		layoutTpl = c.App.GetLocalizedLayout(layoutName, w, r)
	} else {
		layoutTpl = c.App.GetLayout(layoutName)
	}
	layoutTpl.Execute(w, content)
}

// RenderFromCache renders an entire page to the app's bytecache
func (c *Controller) RenderToCache(w http.ResponseWriter, r *http.Request, name string, content interface{}) {
	if len(c.Layout) == 0 {
		// no layout defined!
		var buff bytes.Buffer
		binary.Write(&buff, binary.LittleEndian, content)
		c.App.ByteCaches.SetCache(name, buff.Bytes())
		return
	}
	var layoutTpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 {
		layoutTpl = c.App.GetLocalizedLayout(c.Layout, w, r)
	} else {
		layoutTpl = c.App.GetLayout(c.Layout)
	}
	var buffer bytes.Buffer
	err := layoutTpl.Execute(&buffer, content)
	__panic(err)
	c.App.ByteCaches.SetCache(name, buffer.Bytes())
}

// RenderFromCache renders an entire page from the app's bytecache
func (c *Controller) RenderFromCache(w http.ResponseWriter, r *http.Request, name string) interface{} {
	w.Write(c.App.ByteCaches.GetCache(name).Content)
	return nil
}

func (c *Controller) ParseContent(w http.ResponseWriter, r *http.Request, locTemplatePath string, content interface{}) string {
	var tpl *template.Template
	if len(c.App.Config.DefaultLanguage) > 0 && w != nil && r != nil {
		tpl = c.App.GetLocalizedViewTemplate(locTemplatePath, w, r)
	} else {
		tpl = c.App.GetViewTemplate(locTemplatePath)
	}

	//TODO: handle error if template is nil
	var buffer bytes.Buffer
	err := tpl.Execute(&buffer, content)
	__panic(err)
	return buffer.String()
}

func (c *Controller) Redirect(w http.ResponseWriter, path string, title string, message string) interface{} {
	tpl := c.App.GetLayout("redirect")
	//TODO: handle error if template is nil
	content := &RedirectPageContent{
		Title:   title,
		Message: message,
		Path:    path,
	}
	tpl.Execute(w, content)
	return nil
}

func (c *Controller) PageError(w http.ResponseWriter, errorTitle string, errorMessage string, extraHTMLContent string) interface{} {
	tpl := c.App.GetLayout("error")
	//TODO: handle error if template is nil
	content := &ErrorPageContent{
		Title:        c.App.Config.Name + " - Error: " + errorTitle,
		ErrorTitle:   errorTitle,
		ErrorMessage: errorMessage,
		Content:      extraHTMLContent,
	}
	tpl.Execute(w, content)
	return nil
}

func (c *Controller) registerMethod(name string, method reflect.Value, inKind, outKind int) {
	if c.customMethods == nil {
		c.customMethods = make(map[string]controllerMethod, 0)
	}
	c.customMethods[name] = controllerMethod{method, inKind, outKind}
}
func (c *Controller) getMethod(name string) (controllerMethod, bool) {
	if c.customMethods == nil {
		return controllerMethod{}, false
	}
	val, ok := c.customMethods[name]
	return val, ok
}
<|endoftext|>


<commit_before>// errchk $G -e $D/$F.go

// 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.

// Static error messages about interface conversions.

package main

type T struct {
	a int
}

var t *T

type I interface {
	M()
}

var i I

type I2 interface {
	M()
	N()
}

var i2 I2

type E interface{}

var e E

func main() {
	e = t // ok
	t = e // ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t // ERROR "incompatible|missing M method"
	t = i // ERROR "incompatible|need type assertion"

	i = i2 // ok
	i2 = i // ERROR "missing N method"

	i = I(i2)  // ok
	i2 = I2(i) // ERROR "missing N method"

	e = E(t) // ok
	t = T(e) // ERROR "need explicit|need type assertion|incompatible"
}

type M interface { M() }
var m M

var _ = m.(int)	// ERROR "impossible type assertion"

type Int int
func (Int) M(float) {}

var _ = m.(Int)	// ERROR "impossible type assertion"

var ii int
var jj Int

var m1 M = ii	// ERROR "missing"
var m2 M = jj	// ERROR "wrong type for M method"

var m3 = M(ii)	// ERROR "missing"
var m4 = M(jj)	// ERROR "wrong type for M method"
<commit_msg>test: Match gccgo error messages.<commit_after>// errchk $G -e $D/$F.go

// 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.

// Static error messages about interface conversions.

package main

type T struct {
	a int
}

var t *T

type I interface {
	M()
}

var i I

type I2 interface {
	M()
	N()
}

var i2 I2

type E interface{}

var e E

func main() {
	e = t // ok
	t = e // ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t // ERROR "incompatible|missing M method"
	t = i // ERROR "incompatible|need type assertion"

	i = i2 // ok
	i2 = i // ERROR "incompatible|missing N method"

	i = I(i2)  // ok
	i2 = I2(i) // ERROR "invalid|missing N method"

	e = E(t) // ok
	t = T(e) // ERROR "need explicit|need type assertion|incompatible"
}

type M interface { M() }
var m M

var _ = m.(int)	// ERROR "impossible type assertion"

type Int int
func (Int) M(float) {}

var _ = m.(Int)	// ERROR "impossible type assertion"

var ii int
var jj Int

var m1 M = ii	// ERROR "incompatible|missing"
var m2 M = jj	// ERROR "incompatible|wrong type for M method"

var m3 = M(ii)	// ERROR "invalid|missing"
var m4 = M(jj)	// ERROR "invalid|wrong type for M method"
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// 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.

// Static error messages about interface conversions.

package main

type T struct { a int }
var t *T

type I interface { M() }
var i I

type I2 interface { M(); N(); }
var i2 I2

type E interface { }
var e E

func main() {
	e = t;	// ok
	t = e;	// ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t;	// ERROR "missing|incompatible|is not"
	t = i;	// ERROR "missing|incompatible|is not"

	i = i2;	// ok
	i2 = i;	// ERROR "need explicit|need type assertion"

	i = I(i2);	// ok
	i2 = I2(i);	// ERROR "need explicit|need type assertion"

	e = E(t);	// ok
	t = T(e);	// ERROR "need explicit|need type assertion"
}
<commit_msg>Recognize gccgo error message.<commit_after>// errchk $G $D/$F.go

// 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.

// Static error messages about interface conversions.

package main

type T struct { a int }
var t *T

type I interface { M() }
var i I

type I2 interface { M(); N(); }
var i2 I2

type E interface { }
var e E

func main() {
	e = t;	// ok
	t = e;	// ERROR "need explicit|need type assertion"

	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t;	// ERROR "missing|incompatible|is not"
	t = i;	// ERROR "missing|incompatible|is not"

	i = i2;	// ok
	i2 = i;	// ERROR "need explicit|need type assertion"

	i = I(i2);	// ok
	i2 = I2(i);	// ERROR "need explicit|need type assertion"

	e = E(t);	// ok
	t = T(e);	// ERROR "need explicit|need type assertion|incompatible"
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "os"
import "strconv"

type ChessBoard struct {
	columns []bool        // Store available column moves/attacks
	diagonal_up []bool    // Store available diagonal moves/attacks
	diagonal_down []bool
	column_j int          // Stores column to place the next queen in
	placements uint64     // Tracks total number queen placements
	solutions uint64      // Tracks number of solutions
}

func initialize_board(n_size int) *ChessBoard {
	diagonal_size := 2*n_size - 1
	board := &ChessBoard{
		columns: make([]bool, n_size, n_size),
		diagonal_up: make([]bool, diagonal_size, diagonal_size),
		diagonal_down: make([]bool, diagonal_size, diagonal_size),
		column_j: 0,
		placements: 0,
		solutions: 0,
	}
	for i := 0; i < n_size; i++ {
		board.columns[i] = true
	}
	for i := 0; i < (2*n_size - 1); i++ {
		board.diagonal_up[i] = true
	}
	copy(board.diagonal_down, board.diagonal_up)

	return board
}

func (board *ChessBoard) square_is_free(row_i int) bool {
	return board.columns[row_i] &&
		board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] &&
		board.diagonal_down[board.column_j+row_i]
}

func (board *ChessBoard) set_queen(row_i int) {
	board.columns[row_i] = false
	board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] = false
	board.diagonal_down[board.column_j+row_i] = false
	board.column_j++
	board.placements++
}

func (board *ChessBoard) place_next_queen() {
	for row_i := 0; row_i < cap(board.columns); row_i++ {
		if board.square_is_free(row_i) {
			board.set_queen(row_i)
			if board.column_j == cap(board.columns) {
				// Check if chess board is full
				board.solutions++
			} else {
				// Recursive call to find next queen placement on the chess board
				board.place_next_queen()
			}
			// Removes a queen from the chess board in the given column to backtrack
			board.column_j--
			board.diagonal_down[board.column_j+row_i] = true
			board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] = true
			board.columns[row_i] = true
		}
	}
}

func main() {
	var n_size int = 4
	if len(os.Args) != 1 {
		user_input, err := strconv.Atoi(os.Args[1])
		if err == nil {
			n_size = user_input
		}
	}
	board := initialize_board(n_size)
	board.place_next_queen()
	const template string = "The %d-Queens problem required %d queen placements to find all %d solutions"
	fmt.Printf(template, n_size, board.placements, board.solutions)
}
<commit_msg>Extracted other methods and added method docs<commit_after>package main

import "fmt"
import "os"
import "strconv"

// ChessBoard is an abstract representation of an NxN chess board that is used
// to tracking open positions
type ChessBoard struct {
	columns []bool        // Store available column moves/attacks
	diagonal_up []bool    // Store available diagonal moves/attacks
	diagonal_down []bool
	column_j int          // Stores column to place the next queen in
	placements uint64     // Tracks total number queen placements
	solutions uint64      // Tracks number of solutions
}

// InitializeBoard creates an n_size by n_size ChessBoard struct pointer and
// sets initial values for the struct
func InitializeBoard(n_size int) *ChessBoard {
	diagonal_size := 2*n_size - 1
	board := &ChessBoard{
		columns: make([]bool, n_size, n_size),
		diagonal_up: make([]bool, diagonal_size, diagonal_size),
		diagonal_down: make([]bool, diagonal_size, diagonal_size),
		column_j: 0,
		placements: 0,
		solutions: 0,
	}
	for i := 0; i < n_size; i++ {
		board.columns[i] = true
	}
	for i := 0; i < (2*n_size - 1); i++ {
		board.diagonal_up[i] = true
	}
	copy(board.diagonal_down, board.diagonal_up)

	return board
}

// Checks if a queen can be placed in at row 'i' of the current column
func (board *ChessBoard) SquareIsFree(row_i int) bool {
	return board.columns[row_i] &&
		board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] &&
		board.diagonal_down[board.column_j+row_i]
}

// Places a queen on the chess board at row 'i' of the current column
func (board *ChessBoard) SetQueen(row_i int) {
	board.columns[row_i] = false
	board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] = false
	board.diagonal_down[board.column_j+row_i] = false
	board.column_j++
	board.placements++
}

// Removes a queen from the NxN chess board in the given column to backtrack
func (board *ChessBoard) RemoveQueen(row_i int) {
	board.column_j--
	board.diagonal_down[board.column_j+row_i] = true
	board.diagonal_up[cap(board.columns)-1+board.column_j-row_i] = true
	board.columns[row_i] = true
}

// Recursive function for finding valid queen placements on the chess board
func (board *ChessBoard) PlaceNextQueen() {
	for row_i := 0; row_i < cap(board.columns); row_i++ {
		if board.SquareIsFree(row_i) {
			board.SetQueen(row_i)
			if board.column_j == cap(board.columns) {
				board.solutions++
			} else {
				board.PlaceNextQueen()
			}
			board.RemoveQueen(row_i)
		}
	}
}

// Prints the number of queen placements and solutions for the NxN chess board
func (board *ChessBoard) OutputResult() {
	const template string = "The %d-Queens problem " +
		"required %d queen placements " +
		"to find all %d solutions"
	fmt.Printf(template, cap(board.columns), board.placements, board.solutions)
}

// Parses command-line input, if any, and calculates solutions and placements
// for an N-Queens problem. The result is printed to stdout.
func main() {
	var n_size int = 4
	if len(os.Args) != 1 {
		user_input, err := strconv.Atoi(os.Args[1])
		if err == nil {
			n_size = user_input
		}
	}
	board := InitializeBoard(n_size)
	board.PlaceNextQueen()
	board.OutputResult()
}
<|endoftext|>


<commit_before>package discover

import (
	"math/rand"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/flynn/go-etcd/etcd"
)

<<<<<<< HEAD
func deleteService(client *etcd.Client, service string, addr string) {
	client.Delete(fmt.Sprintf("/services/%s/%s", service, addr), true)
=======
func runEtcdServer() func() {
	killCh := make(chan struct{})
	doneCh := make(chan struct{})
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	name := "etcd-test." + strconv.Itoa(r.Int())
	dataDir := "/tmp/" + name
	go func() {
		cmd := exec.Command("etcd", "-name", name, "-data-dir", dataDir)
		if err := cmd.Start(); err != nil {
			panic(err)
		}
		cmdDone := make(chan error)
		go func() {
			cmdDone <- cmd.Wait()
		}()
		select {
		case <-killCh:
			if err := cmd.Process.Kill(); err != nil {
				panic(err)
			}
			<-cmdDone
		case err := <-cmdDone:
			panic(err)
		}
		if err := os.RemoveAll(dataDir); err != nil {
			panic(err)
		}
		doneCh <- struct{}{}
	}()
	return func() {
		close(killCh)
		<-doneCh
	}
>>>>>>> running etcd and discoverd for each test, making sure they have their own data-dir to avoid conflicting state across tests
}

const NoAttrService = "null"

func TestEtcdBackend_RegisterAndUnregister(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}
	serviceName := "test_register"
	serviceAddr := "127.0.0.1"

	client.Delete(KeyPrefix+"/services/"+serviceName+"/"+serviceAddr, true)
	backend.Register(serviceName, serviceAddr, nil)

	servicePath := KeyPrefix + "/services/" + serviceName + "/" + serviceAddr
	response, err := client.Get(servicePath, false, false)
	if err != nil {
		t.Fatal(err)
	}

	// Check if the files the returned values are the same.
	if (response.Key != servicePath) || (response.Value != NoAttrService) {
		t.Fatal("Returned value not equal to sent one")
	}

	backend.Unregister(serviceName, serviceAddr)
	_, err = client.Get(servicePath, false, false)
	if err == nil {
		t.Fatal("Value not deleted after unregister")
	}
}

func TestEtcdBackend_Attributes(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}
	serviceName := "test_attributes"
	serviceAddr := "127.0.0.1"
	serviceAttrs := map[string]string{
		"foo": "bar",
		"baz": "qux",
	}

	client.Delete(KeyPrefix+"/services/"+serviceName+"/"+serviceAddr, true)
	backend.Register(serviceName, serviceAddr, serviceAttrs)
	defer backend.Unregister(serviceName, serviceAddr)

	updates, _ := backend.Subscribe(serviceName)
	runtime.Gosched()

	update := <-updates.Chan()
	if update.Attrs["foo"] != "bar" || update.Attrs["baz"] != "qux" {
		t.Fatal("Attributes received are not attributes registered")
	}
}

func TestEtcdBackend_Subscribe(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}

	err := backend.Register("test_subscribe", "10.0.0.1", nil)
	if err != nil {
		t.Fatal(err)
	}
	defer backend.Unregister("test_subscribe", "10.0.0.1")

	backend.Register("test_subscribe", "10.0.0.2", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.2")

	updates, _ := backend.Subscribe("test_subscribe")
	runtime.Gosched()

	backend.Register("test_subscribe", "10.0.0.3", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.3")

	backend.Register("test_subscribe", "10.0.0.4", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.4")

	for i := 0; i < 5; i++ {
		update := <-updates.Chan()
		if update.Addr == "" && update.Name == "" {
			continue // skip the update that signals "up to current" event
		}
		if update.Online != true {
			t.Fatal("Unexpected offline service update: ", update, i)
		}
		if !strings.Contains("10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4", update.Addr) {
			t.Fatal("Service update of unexected addr: ", update, i)
		}
	}

	backend.Register("test_subscribe", "10.0.0.5", nil)
	backend.Unregister("test_subscribe", "10.0.0.5")

	<-updates.Chan()           // .5 comes online
	update := <-updates.Chan() // .5 goes offline
	if update.Addr != "10.0.0.5" {
		t.Fatal("Unexpected addr: ", update)
	}
	if update.Online != false {
		t.Fatal("Expected service to be offline:", update)
	}
}
<commit_msg>something that keeps coming up after rebasing<commit_after>package discover

import (
	"math/rand"
	"os"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/flynn/go-etcd/etcd"
)

func runEtcdServer() func() {
	killCh := make(chan struct{})
	doneCh := make(chan struct{})
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	name := "etcd-test." + strconv.Itoa(r.Int())
	dataDir := "/tmp/" + name
	go func() {
		cmd := exec.Command("etcd", "-name", name, "-data-dir", dataDir)
		if err := cmd.Start(); err != nil {
			panic(err)
		}
		cmdDone := make(chan error)
		go func() {
			cmdDone <- cmd.Wait()
		}()
		select {
		case <-killCh:
			if err := cmd.Process.Kill(); err != nil {
				panic(err)
			}
			<-cmdDone
		case err := <-cmdDone:
			panic(err)
		}
		if err := os.RemoveAll(dataDir); err != nil {
			panic(err)
		}
		doneCh <- struct{}{}
	}()
	return func() {
		close(killCh)
		<-doneCh
	}
}

const NoAttrService = "null"

func TestEtcdBackend_RegisterAndUnregister(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}
	serviceName := "test_register"
	serviceAddr := "127.0.0.1"

	client.Delete(KeyPrefix+"/services/"+serviceName+"/"+serviceAddr, true)
	backend.Register(serviceName, serviceAddr, nil)

	servicePath := KeyPrefix + "/services/" + serviceName + "/" + serviceAddr
	response, err := client.Get(servicePath, false, false)
	if err != nil {
		t.Fatal(err)
	}

	// Check if the files the returned values are the same.
	if (response.Key != servicePath) || (response.Value != NoAttrService) {
		t.Fatal("Returned value not equal to sent one")
	}

	backend.Unregister(serviceName, serviceAddr)
	_, err = client.Get(servicePath, false, false)
	if err == nil {
		t.Fatal("Value not deleted after unregister")
	}
}

func TestEtcdBackend_Attributes(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}
	serviceName := "test_attributes"
	serviceAddr := "127.0.0.1"
	serviceAttrs := map[string]string{
		"foo": "bar",
		"baz": "qux",
	}

	client.Delete(KeyPrefix+"/services/"+serviceName+"/"+serviceAddr, true)
	backend.Register(serviceName, serviceAddr, serviceAttrs)
	defer backend.Unregister(serviceName, serviceAddr)

	updates, _ := backend.Subscribe(serviceName)
	runtime.Gosched()

	update := <-updates.Chan()
	if update.Attrs["foo"] != "bar" || update.Attrs["baz"] != "qux" {
		t.Fatal("Attributes received are not attributes registered")
	}
}

func TestEtcdBackend_Subscribe(t *testing.T) {
	killServer := runEtcdServer()
	defer killServer()

	client := etcd.NewClient(nil)
	backend := EtcdBackend{Client: client}

	err := backend.Register("test_subscribe", "10.0.0.1", nil)
	if err != nil {
		t.Fatal(err)
	}
	defer backend.Unregister("test_subscribe", "10.0.0.1")

	backend.Register("test_subscribe", "10.0.0.2", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.2")

	updates, _ := backend.Subscribe("test_subscribe")
	runtime.Gosched()

	backend.Register("test_subscribe", "10.0.0.3", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.3")

	backend.Register("test_subscribe", "10.0.0.4", nil)
	defer backend.Unregister("test_subscribe", "10.0.0.4")

	for i := 0; i < 5; i++ {
		update := <-updates.Chan()
		if update.Addr == "" && update.Name == "" {
			continue // skip the update that signals "up to current" event
		}
		if update.Online != true {
			t.Fatal("Unexpected offline service update: ", update, i)
		}
		if !strings.Contains("10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4", update.Addr) {
			t.Fatal("Service update of unexected addr: ", update, i)
		}
	}

	backend.Register("test_subscribe", "10.0.0.5", nil)
	backend.Unregister("test_subscribe", "10.0.0.5")

	<-updates.Chan()           // .5 comes online
	update := <-updates.Chan() // .5 goes offline
	if update.Addr != "10.0.0.5" {
		t.Fatal("Unexpected addr: ", update)
	}
	if update.Online != false {
		t.Fatal("Expected service to be offline:", update)
	}
}
<|endoftext|>


<commit_before>// +build integration

/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

import (
	"testing"
)

func TestFunctional(t *testing.T) {
	p := profileName(t)
	mk := NewMinikubeRunner(t, p)
	stdout, stderr, err := mk.Start()
	if err != nil {
		t.Fatalf("failed to start minikube failed : %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !isTestNoneDriver(t) { // none driver doesn't need to be deleted
		defer mk.TearDown(t)
	}

	// group is needed to make sure tear down runs after parallel runs
	// https://github.com/golang/go/issues/17791#issuecomment-258476786
	t.Run("group", func(t *testing.T) {
		// This one is not parallel, and ensures the cluster comes up
		// before we run any other tests.
		t.Run("Status", testClusterStatus)
		t.Run("ProfileList", testProfileList)
		t.Run("DNS", testClusterDNS)
		t.Run("Logs", testClusterLogs)
		t.Run("Addons", testAddons)
		t.Run("Registry", testRegistry)
		t.Run("Dashboard", testDashboard)
		t.Run("ServicesList", testServicesList)
		t.Run("Provisioning", testProvisioning)
		t.Run("Tunnel", testTunnel)

		if !isTestNoneDriver(t) {
			t.Run("EnvVars", testClusterEnv)
			t.Run("SSH", testClusterSSH)
			t.Run("IngressController", testIngressController)
			t.Run("Mounting", testMounting)
		}

	})

}
<commit_msg>add kubecontext integraiton tests to functional tests as well<commit_after>// +build integration

/*
Copyright 2016 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package integration

import (
	"testing"
)

func TestFunctional(t *testing.T) {
	p := profileName(t)
	mk := NewMinikubeRunner(t, p)
	stdout, stderr, err := mk.Start()
	if err != nil {
		t.Fatalf("failed to start minikube failed : %v\nstdout: %s\nstderr: %s", err, stdout, stderr)
	}
	if !isTestNoneDriver(t) { // none driver doesn't need to be deleted
		defer mk.TearDown(t)
	}

	// group is needed to make sure tear down runs after parallel runs
	// https://github.com/golang/go/issues/17791#issuecomment-258476786
	t.Run("group", func(t *testing.T) {
		// This one is not parallel, and ensures the cluster comes up
		// before we run any other tests.
		t.Run("Status", testClusterStatus)
		t.Run("ProfileList", testProfileList)
		t.Run("DNS", testClusterDNS)
		t.Run("Logs", testClusterLogs)
		t.Run("Addons", testAddons)
		t.Run("Registry", testRegistry)
		t.Run("Dashboard", testDashboard)
		t.Run("ServicesList", testServicesList)
		t.Run("Provisioning", testProvisioning)
		t.Run("Tunnel", testTunnel)
		t.Run("kubecontext", testKubeConfigCurrentCtx)

		if !isTestNoneDriver(t) {
			t.Run("EnvVars", testClusterEnv)
			t.Run("SSH", testClusterSSH)
			t.Run("IngressController", testIngressController)
			t.Run("Mounting", testMounting)
		}

	})

}
<|endoftext|>